diff options
Diffstat (limited to 'drivers')
41 files changed, 2346 insertions, 1313 deletions
diff --git a/drivers/isdn/hardware/mISDN/hfc_multi.h b/drivers/isdn/hardware/mISDN/hfc_multi.h index a33d87afc84..7bbf7300593 100644 --- a/drivers/isdn/hardware/mISDN/hfc_multi.h +++ b/drivers/isdn/hardware/mISDN/hfc_multi.h @@ -162,8 +162,8 @@ struct hfc_multi { 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 1eac03f39d0..c63e2f49da8 100644 --- a/drivers/isdn/hardware/mISDN/hfcmulti.c +++ b/drivers/isdn/hardware/mISDN/hfcmulti.c @@ -171,9 +171,8 @@ static int (*unregister_interrupt)(void); 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 @@ HFC_wait_debug(struct hfc_multi *hc, const char *function, int line) #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_regio(struct hfc_multi *hc, u_char *data, int len) } } /* 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 @@ write_fifo_pcimem(struct hfc_multi *hc, u_char *data, int len) } } /* 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_regio(struct hfc_multi *hc, u_char *data, int len) } /* 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 @@ vpm_out(struct hfc_multi *c, int which, unsigned short addr, } -void +static void vpm_init(struct hfc_multi *wc) { unsigned char reg; @@ -789,7 +788,8 @@ vpm_init(struct hfc_multi *wc) } } -void +#ifdef UNUSED +static void vpm_check(struct hfc_multi *hctmp) { unsigned char gpi2; @@ -799,6 +799,7 @@ vpm_check(struct hfc_multi *hctmp) if ((gpi2 & 0x3) != 0x3) printk(KERN_DEBUG "Got interrupt 0x%x from VPM!\n", gpi2); } +#endif /* UNUSED */ /* @@ -812,7 +813,7 @@ vpm_check(struct hfc_multi *hctmp) * */ -void +static void vpm_echocan_on(struct hfc_multi *hc, int ch, int taps) { unsigned int timeslot; @@ -844,7 +845,7 @@ vpm_echocan_on(struct hfc_multi *hc, int ch, int taps) 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 @@ vpm_echocan_off(struct hfc_multi *hc, int ch) 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 @@ hfcmulti_resync(struct hfc_multi *locked, struct hfc_multi *newmaster, int rm) /* 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 @@ hfcmulti_resync(struct hfc_multi *locked, struct hfc_multi *newmaster, int rm) 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 @@ hfcmulti_resync(struct hfc_multi *locked, struct hfc_multi *newmaster, int rm) "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 @@ plxsd_checksync(struct hfc_multi *hc, int rm) 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 @@ release_io_hfcmulti(struct hfc_multi *hc) 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 @@ release_io_hfcmulti(struct hfc_multi *hc) 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 @@ init_chip(struct hfc_multi *hc) 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 @@ init_chip(struct hfc_multi *hc) 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 @@ init_chip(struct hfc_multi *hc) "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 @@ init_chip(struct hfc_multi *hc) /* 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 @@ controller_fail: /* 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 @@ controller_fail: 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 @@ hfcmulti_interrupt(int intno, void *dev_id) 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 @@ hfcmulti_interrupt(int intno, void *dev_id) 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 @@ init_card(struct hfc_multi *hc) { 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 @@ init_card(struct hfc_multi *hc) 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 @@ init_card(struct hfc_multi *hc) 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 33068177b7c..751665c448d 100644 --- a/drivers/isdn/mISDN/core.c +++ b/drivers/isdn/mISDN/core.c @@ -26,12 +26,12 @@ MODULE_LICENSE("GPL"); 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 @@ mISDN_unregister_Bprotocol(struct Bprotocol *bp) } EXPORT_SYMBOL(mISDN_unregister_Bprotocol); -int +static int mISDNInit(void) { int err; @@ -224,7 +224,7 @@ error: 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 1c2dd569477..de3795e3f43 100644 --- a/drivers/isdn/mISDN/dsp_audio.c +++ b/drivers/isdn/mISDN/dsp_audio.c @@ -30,7 +30,7 @@ EXPORT_SYMBOL(dsp_audio_s16_to_law); /* 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 c2f51cc5076..c884511e2d4 100644 --- a/drivers/isdn/mISDN/dsp_cmx.c +++ b/drivers/isdn/mISDN/dsp_cmx.c @@ -1540,11 +1540,13 @@ send_packet: 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 2f10ed82c0d..1dc21d80341 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 @@ dsp_function(struct mISDNchannel *ch, struct sk_buff *skb) int ret = 0; u8 *digits; int cont; - struct sk_buff *nskb; u_long flags; hh = mISDN_HEAD_P(skb); @@ -690,6 +689,7 @@ dsp_function(struct mISDNchannel *ch, struct sk_buff *skb) 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 850260ab57d..5ee6651b45b 100644 --- a/drivers/isdn/mISDN/dsp_pipeline.c +++ b/drivers/isdn/mISDN/dsp_pipeline.c @@ -249,7 +249,7 @@ int dsp_pipeline_build(struct dsp_pipeline *pipeline, const char *cfg) 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 23dd0dd2152..7a9af66f4b1 100644 --- a/drivers/isdn/mISDN/dsp_tones.c +++ b/drivers/isdn/mISDN/dsp_tones.c @@ -231,120 +231,120 @@ dsp_audio_generate_ulaw_samples(void) * 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 @@ dsp_tone_timeout(void *arg) /* 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 a2dc4570ef4..2ec4b28d9ed 100644 --- a/drivers/isdn/mISDN/l1oip_codec.c +++ b/drivers/isdn/mISDN/l1oip_codec.c @@ -49,6 +49,7 @@ NOTE: The bytes are handled as they are law-encoded. #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 e42150a5778..0884dd6892f 100644 --- a/drivers/isdn/mISDN/l1oip_core.c +++ b/drivers/isdn/mISDN/l1oip_core.c @@ -469,7 +469,7 @@ l1oip_socket_recv(struct l1oip *hc, u8 remotecodec, u8 channel, u16 timebase, 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 @@ l1oip_socket_parse(struct l1oip *hc, struct sockaddr_in *sin, u8 *buf, int len) } /* get id flag */ - id = (*buf>>4)&1; + packet_id = (*buf>>4)&1; /* check coding */ remotecodec = (*buf) & 0x0f; @@ -520,11 +520,11 @@ l1oip_socket_parse(struct l1oip *hc, struct sockaddr_in *sin, u8 *buf, int len) 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 @@ l1oip_socket_parse(struct l1oip *hc, struct sockaddr_in *sin, u8 *buf, int len) "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 fced1a2755f..b73e952d12c 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 a7915a156c0..d6e2863f224 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 @@ IsRNR(u_char *data, struct layer2 *l2) 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 @@ iframe_error(struct layer2 *l2, struct sk_buff *skb) return 0; } -int +static int super_error(struct layer2 *l2, struct sk_buff *skb) { if (skb->len != l2addrsize(l2) + @@ -492,7 +494,7 @@ super_error(struct layer2 *l2, struct sk_buff *skb) 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 @@ unnum_error(struct layer2 *l2, struct sk_buff *skb, int wantrsp) return 0; } -int +static int UI_error(struct layer2 *l2, struct sk_buff *skb) { int rsp = *skb->data & 0x2; @@ -518,7 +520,7 @@ UI_error(struct layer2 *l2, struct sk_buff *skb) return 0; } -int +static int FRMR_error(struct layer2 *l2, struct sk_buff *skb) { u_int headers = l2addrsize(l2) + 1; @@ -1065,7 +1067,7 @@ l2_st6_dm_release(struct FsmInst *fi, int event, void *arg) } } -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 e5a20f9542d..37a2de18cfd 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 54cfddcc478..d55b14ae4e9 100644 --- a/drivers/isdn/mISDN/stack.c +++ b/drivers/isdn/mISDN/stack.c @@ -36,7 +36,7 @@ _queue_message(struct mISDNstack *st, struct sk_buff *skb) } } -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 6fbae42127b..5c43d19e7c1 100644 --- a/drivers/isdn/mISDN/tei.c +++ b/drivers/isdn/mISDN/tei.c @@ -393,7 +393,7 @@ dl_unit_data(struct manager *mgr, struct sk_buff *skb) return 0; } -unsigned int +static unsigned int random_ri(void) { u16 x; @@ -1287,7 +1287,7 @@ create_teimanager(struct mISDNdevice *dev) 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 875fabe16e3..f2b32186d4a 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 @@ mISDN_close(struct inode *ino, struct file *filep) } 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 @@ mISDN_read(struct file *filep, char *buf, size_t count, loff_t *off) 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 @@ static struct miscdevice mISDNtimer = { }; 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 e507daa4f0e..b89f9be3cb1 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 edf026280be..64903496aa9 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 @@ static int sfe4002_init(struct efx_nic *efx) /* 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; - } - - 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); - } + efx->board_info.type = BOARD_TYPE(revision_info); + efx->board_info.major = BOARD_MAJOR(revision_info); + efx->board_info.minor = BOARD_MINOR(revision_info); - data = &board_data[efx->board_info.type]; + for (i = 0; i < ARRAY_SIZE(board_data); i++) + if (board_data[i].type == efx->board_info.type) + data = &board_data[i]; - /* 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 c6e01b64bfb..d93c6c6a754 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 e5024bb8a46..086629c0fe5 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 @@ MODULE_PARM_DESC(lro, "Large receive offload acceleration"); /* * 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 @@ static unsigned int rss_cpus; 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 int efx_probe_channel(struct efx_channel *channel) } +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 @@ static void efx_link_status_changed(struct efx_nic *efx) /* 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 @@ void __efx_reconfigure_port(struct efx_nic *efx) 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 @@ void efx_reconfigure_port(struct efx_nic *efx) /* 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 @@ static void efx_reconfigure_work(struct work_struct *data) 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 @@ static int efx_probe_port(struct efx_nic *efx) 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 @@ static int efx_init_port(struct efx_nic *efx) 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 @@ static void efx_start_port(struct efx_nic *efx) 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 @@ static void efx_fini_port(struct efx_nic *efx) 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 @@ static void efx_probe_interrupts(struct efx_nic *efx) 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 @@ static void efx_probe_interrupts(struct efx_nic *efx) /* 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 @@ static void efx_probe_interrupts(struct efx_nic *efx) /* 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 @@ static void efx_set_channels(struct efx_nic *efx) 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 @@ static int efx_probe_all(struct efx_nic *efx) goto fail3; } } + efx_set_channel_names(efx); return 0; @@ -1043,7 +1104,8 @@ static void efx_flush_all(struct efx_nic *efx) 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 @@ static void efx_stop_all(struct efx_nic *efx) * 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 @@ static void efx_monitor(struct work_struct *data) { 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 @@ static struct net_device_stats *efx_net_stats(struct net_device *net_dev) 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 @@ static void efx_set_multicast_list(struct net_device *net_dev) 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 @@ static const struct net_device_ops efx_netdev_ops = { #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 @@ static struct notifier_block efx_netdev_notifier = { .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 @@ static int efx_register_netdev(struct efx_nic *efx) 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 @@ static int efx_register_netdev(struct efx_nic *efx) 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 @@ static void efx_unregister_netdev(struct efx_nic *efx) 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 @@ static void efx_unregister_netdev(struct efx_nic *efx) * 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 @@ void efx_reset_down(struct efx_nic *efx, struct ethtool_cmd *ecmd) 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 @@ int efx_reset_up(struct efx_nic *efx, struct ethtool_cmd *ecmd, bool ok) 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 @@ void efx_schedule_reset(struct efx_nic *efx, enum reset_type type) efx->reset_pending = method; - queue_work(efx->reset_workqueue, &efx->reset_work); + queue_work(reset_workqueue, &efx->reset_work); } /************************************************************************** @@ -1731,10 +1821,16 @@ int efx_port_dummy_op_int(struct efx_nic *efx) 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 @@ static struct efx_board efx_dummy_board_info = { .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 @@ static int efx_init_struct(struct efx_nic *efx, struct efx_nic_type *type, 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 @@ static int efx_init_struct(struct efx_nic *efx, struct efx_nic_type *type, 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 @@ static int efx_init_struct(struct efx_nic *efx, struct efx_nic_type *type, 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 @@ static void efx_pci_remove(struct pci_dev *pci_dev) 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 @@ static void efx_pci_remove(struct pci_dev *pci_dev) 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 @@ static int efx_pci_probe_main(struct efx_nic *efx) efx_fini_port(efx); fail5: fail4: + efx->board_info.fini(efx); fail3: efx_fini_napi(efx); fail2: @@ -2036,14 +2117,23 @@ static int __devinit efx_pci_probe(struct pci_dev *pci_dev, * 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 @@ static int __devinit efx_pci_probe(struct pci_dev *pci_dev, /* 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 @@ static int __init efx_init_module(void) 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 @@ static int __init efx_init_module(void) 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 @@ static void __exit efx_exit_module(void) 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 41e758e8fdb..60cbc6e1e66 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 @@ extern const char *efx_loopback_mode_names[]; 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 cd92c4d8dbc..3aaece6b12c 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 @@ static struct efx_ethtool_stat efx_ethtool_stats[] = { */ /* 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 @@ int efx_ethtool_get_settings(struct net_device *net_dev, 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 @@ int efx_ethtool_set_settings(struct net_device *net_dev, 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 @@ static void efx_ethtool_get_drvinfo(struct net_device *net_dev, * @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 @@ static void efx_fill_test(unsigned int test_index, /* 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 @@ static int efx_fill_loopback_test(struct efx_nic *efx, } 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 @@ static int efx_ethtool_fill_self_tests(struct efx_nic *efx, 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 @@ static int efx_ethtool_get_eeprom(struct net_device *net_dev, 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 @@ static int efx_ethtool_set_eeprom(struct net_device *net_dev, 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 @@ static int efx_ethtool_set_pauseparam(struct net_device *net_dev, 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; + + wanted_fc = ((pause->rx_pause ? EFX_FC_RX : 0) | + (pause->tx_pause ? EFX_FC_TX : 0) | + (pause->autoneg ? EFX_FC_AUTO : 0)); - 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; + 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); - mutex_unlock(&efx->mac_lock); - if (!rc) - efx_reconfigure_port(efx); + efx->wanted_fc = wanted_fc; + mdio_clause45_set_pause(efx); + __efx_reconfigure_port(efx); - return rc; + mutex_unlock(&efx->mac_lock); + + return 0; } static void efx_ethtool_get_pauseparam(struct net_device *net_dev, @@ -691,9 +740,9 @@ static void efx_ethtool_get_pauseparam(struct net_device *net_dev, { 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 71e0bed6061..6884dc8c1f8 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 @@ static int disable_dma_stats; #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 @@ static void falcon_handle_rx_not_ok(struct efx_rx_queue *rx_queue, 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 @@ static void falcon_handle_rx_not_ok(struct efx_rx_queue *rx_queue, #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 @@ static void falcon_handle_global_event(struct efx_channel *channel, 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 @@ void falcon_generate_test_event(struct efx_channel *channel, unsigned int magic) 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 @@ int falcon_init_interrupt(struct efx_nic *efx) 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 @@ void falcon_fini_interrupt(struct efx_nic *efx) ************************************************************************** */ -#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, ®, 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, ®, 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 @@ int falcon_spi_cmd(const struct efx_spi_device *spi, 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 @@ int falcon_spi_cmd(const struct efx_spi_device *spi, 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 @@ efx_spi_munge_command(const struct efx_spi_device *spi, 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; - - /* Wait up to 1000us for flash/EEPROM to finish a fast operation. */ - for (i = 0; i < 50; i++) { - udelay(20); + int rc; + 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 @@ int falcon_spi_write(const struct efx_spi_device *spi, loff_t start, 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 @@ int falcon_spi_write(const struct efx_spi_device *spi, loff_t start, 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 @@ int falcon_spi_write(const struct efx_spi_device *spi, loff_t start, if (rc) break; - rc = falcon_spi_fast_wait(spi); + rc = falcon_spi_wait_write(spi); if (rc) break; @@ -1805,40 +1850,61 @@ int falcon_spi_write(const struct efx_spi_device *spi, loff_t start, * ************************************************************************** */ -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, ®, GM_CFG1_REG); + udelay(1000); + + EFX_POPULATE_OWORD_1(reg, GM_SW_RST, 0); + falcon_write(efx, ®, GM_CFG1_REG); + udelay(1000); + return 0; + } else { + EFX_POPULATE_OWORD_1(reg, XM_CORE_RST, 1); + falcon_write(efx, ®, XM_GLB_CFG_REG); + + for (count = 0; count < 10000; count++) { + falcon_read(efx, ®, XM_GLB_CFG_REG); + if (EFX_OWORD_FIELD(reg, XM_CORE_RST) == 0) + return 0; + udelay(10); + } - 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_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, ®, MAC0_CTRL_REG_KER); + EFX_SET_OWORD_FIELD(reg, TXFIFO_DRAIN_EN_B0, 1); + falcon_write(efx, ®, 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, ®, 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, ®, 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, ®, 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 @@ void falcon_drain_tx_fifo(struct efx_nic *efx) /* 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, ®, 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, ®, RX_CFG_REG_KER); + EFX_SET_OWORD_FIELD(reg, RX_INGR_EN_B0, 0); + falcon_write(efx, ®, RX_CFG_REG_KER); if (!efx->link_up) falcon_drain_tx_fifo(efx); @@ -1881,14 +1965,12 @@ void falcon_reconfigure_mac_wrapper(struct efx_nic *efx) 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 @@ void falcon_reconfigure_mac_wrapper(struct efx_nic *efx) /* 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, ®, RX_CFG_REG_KER); EFX_SET_OWORD_FIELD_VER(efx, reg, RX_XOFF_MAC_EN, tx_fc); @@ -1998,7 +2080,8 @@ static int falcon_gmii_wait(struct efx_nic *efx) 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 void falcon_init_mdio(struct mii_if_info *gmii) 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_SFT9001A: + case PHY_TYPE_SFT9001B: + efx->phy_op = &falcon_sft9001_phy_ops; break; - case PHY_TYPE_XFP: + case PHY_TYPE_QT2022C2: efx->phy_op = &falcon_xfp_phy_ops; break; default: @@ -2174,10 +2261,59 @@ static int falcon_probe_phy(struct efx_nic *efx) 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 @@ int falcon_probe_port(struct efx_nic *efx) /* 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 @@ int falcon_read_nvram(struct efx_nic *efx, struct falcon_nvconfig *nvconfig_out) __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 @@ static struct { 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 @@ static int falcon_spi_device_init(struct efx_nic *efx, 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_nvconfig(struct efx_nic *efx) 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 @@ static int falcon_probe_nic_variant(struct efx_nic *efx) 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 @@ static int falcon_probe_nic_variant(struct efx_nic *efx) return -ENODEV; } + /* Initial assumed speed */ + efx->link_speed = EFX_OWORD_FIELD(nic_stat, STRAP_10G) ? 10000 : 1000; + return 0; } @@ -2696,80 +2836,37 @@ static int falcon_probe_nic_variant(struct efx_nic *efx) 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 (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 (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 (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 @@ int falcon_probe_nic(struct efx_nic *efx) 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 @@ int falcon_init_nic(struct efx_nic *efx) 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 be025ba7a6c..7869c3d7438 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 int falcon_probe_port(struct efx_nic *efx); 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 int falcon_init_interrupt(struct efx_nic *efx); 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 00000000000..8865eae20ac --- /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, ®, 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, ®, 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, ®, 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, ®, GMF_CFG0_REG); + udelay(10); + + /* FIFO configuration register 1 */ + EFX_POPULATE_OWORD_2(reg, + GMF_CFGFRTH, 0x12, + GMF_CFGXOFFRTX, 0xffff); + falcon_write(efx, ®, GMF_CFG1_REG); + udelay(10); + + /* FIFO configuration register 2 */ + EFX_POPULATE_OWORD_2(reg, + GMF_CFGHWM, 0x3f, + GMF_CFGLWM, 0xa); + falcon_write(efx, ®, GMF_CFG2_REG); + udelay(10); + + /* FIFO configuration register 3 */ + EFX_POPULATE_OWORD_2(reg, + GMF_CFGHWMFT, 0x1c, + GMF_CFGFTTH, 0x08); + falcon_write(efx, ®, GMF_CFG3_REG); + udelay(10); + + /* FIFO configuration register 4 */ + EFX_POPULATE_OWORD_1(reg, GMF_HSTFLTRFRM_PAUSE, 1); + falcon_write(efx, ®, GMF_CFG4_REG); + udelay(10); + + /* FIFO configuration register 5 */ + falcon_read(efx, ®, 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, ®, 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, ®, 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, ®, 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 040e70ed4ec..bda8d5bb72e 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 d4012314dd0..5a03713685a 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, ®, XM_GLB_CFG_REG); - - for (count = 0; count < 10000; count++) { /* wait upto 100ms */ - falcon_read(efx, ®, 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 @@ int falcon_reset_xaui(struct efx_nic *efx) return -ETIMEDOUT; } -static bool falcon_xgmii_status(struct efx_nic *efx) +static void falcon_mask_status_intr(struct efx_nic *efx, bool enable) { 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, ®, XM_MGT_INT_REG_B0); - falcon_read(efx, ®, 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; -} + if ((falcon_rev(efx) != FALCON_REV_B0) || LOOPBACK_INTERNAL(efx)) + return; -static void falcon_mask_status_intr(struct efx_nic *efx, bool enable) -{ - efx_oword_t reg; + /* We expect xgmii faults if the wireside link is up */ + if (!EFX_WORKAROUND_5147(efx) || !efx->link_up) + return; - if ((falcon_rev(efx) < FALCON_REV_B0) || LOOPBACK_INTERNAL(efx)) + /* 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 @@ static void falcon_mask_status_intr(struct efx_nic *efx, bool enable) falcon_write(efx, ®, 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 @@ bool falcon_xaui_link_ok(struct efx_nic *efx) EFX_SET_OWORD_FIELD(reg, XX_DISPERR, XX_DISPERR_RESET); falcon_write(efx, ®, 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 @@ static void falcon_reconfigure_xmac_core(struct efx_nic *efx) { 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 @@ static void falcon_reconfigure_xgxs_core(struct efx_nic *efx) /* 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; - - while (tries) { - if (falcon_xaui_link_ok(efx)) - return true; + /* XAUI link is expected to be down */ + return; - 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); -} - -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); + falcon_check_xaui_link_up(efx, 5); + falcon_mask_status_intr(efx, true); } -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 @@ void falcon_update_stats_xmac(struct efx_nic *efx) 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; - - if ((efx->loopback_mode == LOOPBACK_NETWORK) || - efx_phy_mode_disabled(efx->phy_mode)) - return 0; - - 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; -} - -/* 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); + /* 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; } -int falcon_xmac_get_settings(struct efx_nic *efx, struct ethtool_cmd *ecmd) +static void falcon_poll_xmac(struct efx_nic *efx) { - 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; -} + if (!EFX_WORKAROUND_5147(efx) || !efx->link_up || efx->mac_up) + return; -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); + falcon_mask_status_intr(efx, false); + falcon_check_xaui_link_up(efx, 1); + falcon_mask_status_intr(efx, true); } - -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 d25bbd1297f..dfccaa7b573 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 a31571c6913..4e7074278fe 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 19e25210b68..037601e0b9d 100644 --- a/drivers/net/sfc/mdio_10g.c +++ b/drivers/net/sfc/mdio_10g.c @@ -47,13 +47,16 @@ static int mdio_clause45_check_mmd(struct efx_nic *efx, int mmd, 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_wait_reset_mmds(struct efx_nic *efx, 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 @@ bool mdio_clause45_links_ok(struct efx_nic *efx, unsigned int mmd_mask) 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 @@ bool mdio_clause45_links_ok(struct efx_nic *efx, unsigned int mmd_mask) 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 @@ static void mdio_clause45_set_mmd_lpower(struct efx_nic *efx, { 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 @@ void mdio_clause45_set_mmds_lpower(struct efx_nic *efx, 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,101 +259,314 @@ void mdio_clause45_set_mmds_lpower(struct efx_nic *efx, } } +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; - ecmd->port = PORT_FIBRE; - ecmd->supported = SUPPORTED_FIBRE; - ecmd->advertising = ADVERTISED_FIBRE; - return; - } +/** + * 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; - pma_type = mdio_clause45_read(efx, efx->mii.phy_id, - MDIO_MMD_PMAPMD, MDIO_MMDREG_CTRL2); - pma_type &= MDIO_PMAPMD_CTRL2_TYPE_MASK; + ecmd->transceiver = XCVR_INTERNAL; + ecmd->phy_address = phy_id; - 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 */ + 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: - 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; + 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; - /* All the other defined modes are flavours of - * 10G optical */ + + /* 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->speed = SPEED_10000; ecmd->port = PORT_FIBRE; ecmd->supported = SUPPORTED_FIBRE; ecmd->advertising = ADVERTISED_FIBRE; break; } + + 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; + + /* 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); + } } /** * 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 db9f358349c..409118228b1 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_set_mmds_lpower(struct efx_nic *efx, 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 a1e6c2875fc..665cafb88d6 100644 --- a/drivers/net/sfc/mtd.c +++ b/drivers/net/sfc/mtd.c @@ -76,7 +76,7 @@ static int efx_spi_unlock(const struct efx_spi_device *spi) 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 e596c9a6a4c..03feaee04ac 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 @@ enum efx_rx_alloc_method { * * @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 @@ enum efx_rx_alloc_method { 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 efx_int_mode { 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 @@ enum efx_fc_type { 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 @@ enum efx_fc_type { * @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 @@ union efx_multicast_hash { * @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 @@ union efx_multicast_hash { * @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 @@ union efx_multicast_hash { * @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 @@ union efx_multicast_hash { * @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 @@ union efx_multicast_hash { * @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 @@ struct efx_nic { 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_nic { 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 efx_nic { 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 @@ struct efx_nic { 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 f746536f4ff..58c493ef81b 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 362956e3fe1..6bb09f263b3 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 @@ static int efx_test_mii(struct efx_nic *efx, struct efx_self_tests *tests) 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 @@ static int efx_test_loopbacks(struct efx_nic *efx, struct ethtool_cmd ecmd, 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 @@ static int efx_test_loopbacks(struct efx_nic *efx, struct ethtool_cmd ecmd, 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 @@ int efx_offline_test(struct efx_nic *efx, */ 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 @@ int efx_offline_test(struct efx_nic *efx, 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 @@ int efx_offline_test(struct efx_nic *efx, 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 fc15df15d76..252f7d71724 100644 --- a/drivers/net/sfc/selftest.h +++ b/drivers/net/sfc/selftest.h @@ -39,8 +39,6 @@ struct efx_self_tests { /* 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 aa576c559ec..16b80acb999 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 @@ fail_on: 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, ®, GPIO_CTL_REG_KER); + EFX_SET_OWORD_FIELD(reg, GPIO2_OEN, true); + EFX_SET_OWORD_FIELD(reg, GPIO2_OUT, false); + falcon_write(efx, ®, 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, ®, 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 @@ static ssize_t set_phy_flash_cfg(struct device *dev, 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 @@ static void sfe4001_fini(struct efx_nic *efx) 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 @@ fail_hwmon: 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 c4aca132348..1b1ceb41167 100644 --- a/drivers/net/sfc/spi.h +++ b/drivers/net/sfc/spi.h @@ -68,8 +68,8 @@ struct efx_spi_device { }; 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 8d41c29b9d7..b3ca2dc8040 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) - -#define TENXPRESS_LOOPBACKS ((1 << LOOPBACK_PHYXS) | \ - (1 << LOOPBACK_PCS) | \ - (1 << LOOPBACK_PMAPMD) | \ - (1 << LOOPBACK_NETWORK)) +/* 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 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) - -/* Special Software reset register */ -#define PMA_PMD_EXT_CTRL_REG 49152 -#define PMA_PMD_EXT_SSR_LBN 15 - -/* Misc register defines */ -#define PCS_CLOCK_CTRL_REG 0xd801 +#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 + +/* 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 - 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 @@ void tenxpress_crc_err(struct efx_nic *efx) 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_phy_check(struct efx_nic *efx) 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 @@ static int tenxpress_phy_init(struct efx_nic *efx) 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 @@ static int tenxpress_phy_init(struct efx_nic *efx) 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 @@ static int tenxpress_special_reset(struct efx_nic *efx) 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); + /* 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; + } +} - 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"); +static bool sfx7101_link_ok(struct efx_nic *efx) +{ + return mdio_clause45_links_ok(efx, + MDIO_MMDREG_DEVS_PMAPMD | + MDIO_MMDREG_DEVS_PCS | + MDIO_MMDREG_DEVS_PHYXS); +} + +static bool sft9001_link_ok(struct efx_nic *efx, struct ethtool_cmd *ecmd) +{ + int phy_id = efx->mii.phy_id; + u32 reg; + + 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 |= (PMA_PMD_LED_OFF << PMA_PMD_LED_RX_LBN); + reg = mdio_clause45_read(efx, phy_id, MDIO_MMD_C22EXT, + C22EXT_STATUS_REG); + return reg & (1 << C22EXT_STATUS_LINK_LBN); } - 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 void tenxpress_ext_loopback(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; - } + int phy_id = efx->mii.phy_id; - /* 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; + 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_phyxs_loopback(struct efx_nic *efx) +static void tenxpress_low_power(struct efx_nic *efx) { int phy_id = efx->mii.phy_id; - int ctrl1, ctrl2; - 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->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); - - /* 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 */ + 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 */ + } kfree(efx->phy_data); efx->phy_data = NULL; @@ -452,13 +657,134 @@ static int tenxpress_phy_test(struct efx_nic *efx) 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, + .poll = tenxpress_phy_poll, + .fini = tenxpress_phy_fini, + .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 = SFX7101_LOOPBACKS, +}; + +struct efx_phy_operations falcon_sft9001_phy_ops = { + .macs = EFX_GMAC | 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 = sft9001_get_settings, + .set_settings = sft9001_set_settings, + .set_xnp_advertise = sft9001_set_xnp_advertise, .mmds = TENXPRESS_REQUIRED_DEVS, - .loopbacks = TENXPRESS_LOOPBACKS, + .loopbacks = SFT9001_LOOPBACKS, }; diff --git a/drivers/net/sfc/workarounds.h b/drivers/net/sfc/workarounds.h index ec50b90f428..82e03e1d737 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 91f02466210..2d50b6ecf5f 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 @@ static int xfp_reset_phy(struct efx_nic *efx) /* 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 @@ static int xfp_link_ok(struct efx_nic *efx) 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 @@ static void xfp_phy_reconfigure(struct efx_nic *efx) 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 @@ static void xfp_phy_fini(struct efx_nic *efx) } 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, }; |