From 5b4b9775a00c20ade1b1ac8aa25e0e4059d6243e Mon Sep 17 00:00:00 2001 From: Michael Buesch Date: Mon, 1 May 2006 22:43:00 +0200 Subject: [PATCH] bcm43xx: fix iwmode crash when down This fixes a crash when iwconfig ethX mode foo is done before ifconfig ethX up or after ifconfig ethX down Signed-off-by: Michael Buesch Signed-off-by: John W. Linville --- drivers/net/wireless/bcm43xx/bcm43xx_wx.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/bcm43xx/bcm43xx_wx.c b/drivers/net/wireless/bcm43xx/bcm43xx_wx.c index 3edbb481a0a..b45063974ae 100644 --- a/drivers/net/wireless/bcm43xx/bcm43xx_wx.c +++ b/drivers/net/wireless/bcm43xx/bcm43xx_wx.c @@ -182,8 +182,11 @@ static int bcm43xx_wx_set_mode(struct net_device *net_dev, mode = BCM43xx_INITIAL_IWMODE; bcm43xx_lock_mmio(bcm, flags); - if (bcm->ieee->iw_mode != mode) - bcm43xx_set_iwmode(bcm, mode); + if (bcm->initialized) { + if (bcm->ieee->iw_mode != mode) + bcm43xx_set_iwmode(bcm, mode); + } else + bcm->ieee->iw_mode = mode; bcm43xx_unlock_mmio(bcm, flags); return 0; -- cgit v1.2.3 From f9f7b9602ecb66f55718d6d1afa3e2b1e721b22d Mon Sep 17 00:00:00 2001 From: Stefano Brivio Date: Fri, 5 May 2006 01:26:29 +0200 Subject: [PATCH] bcm43xx: check for valid MAC address in SPROM Check for valid MAC address in SPROM fields instead of relying on PHY type while setting the MAC address in the networking subsystem, as some devices have multiple PHYs. Signed-off-by: Stefano Brivio Signed-off-by: John W. Linville --- drivers/net/wireless/bcm43xx/bcm43xx_main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/bcm43xx/bcm43xx_main.c b/drivers/net/wireless/bcm43xx/bcm43xx_main.c index 9a06e61df0a..4be2d9b5749 100644 --- a/drivers/net/wireless/bcm43xx/bcm43xx_main.c +++ b/drivers/net/wireless/bcm43xx/bcm43xx_main.c @@ -3482,7 +3482,7 @@ static int bcm43xx_attach_board(struct bcm43xx_private *bcm) bcm43xx_pctl_set_crystal(bcm, 0); /* Set the MAC address in the networking subsystem */ - if (bcm43xx_current_phy(bcm)->type == BCM43xx_PHYTYPE_A) + if (is_valid_ether_addr(bcm->sprom.et1macaddr)) memcpy(bcm->net_dev->dev_addr, bcm->sprom.et1macaddr, 6); else memcpy(bcm->net_dev->dev_addr, bcm->sprom.il0macaddr, 6); -- cgit v1.2.3 From 869aaab1812c4212e65fb181e94b824cf49f9509 Mon Sep 17 00:00:00 2001 From: Michael Buesch Date: Fri, 5 May 2006 17:23:51 +0200 Subject: [PATCH] bcm43xx: Fix array overrun in bcm43xx_geo_init The problem here is that the bcm34xx driver and the ieee80211 stack do not agree on what channels are possible for 802.11a. The ieee80211 stack only wants channels between 34 and 165, while the bcm43xx driver accepts anything from 0 to 200. I made the bcm43xx driver comply with the ieee80211 stack expectations, by using the proper constants. Signed-off-by: Jean Delvare [mb]: Reduce stack usage by kzalloc-ing ieee80211_geo Signed-off-by: Michael Buesch Signed-off-by: John W. Linville --- drivers/net/wireless/bcm43xx/bcm43xx_main.c | 43 +++++++++++++++++------------ drivers/net/wireless/bcm43xx/bcm43xx_main.h | 6 ++-- 2 files changed, 30 insertions(+), 19 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/bcm43xx/bcm43xx_main.c b/drivers/net/wireless/bcm43xx/bcm43xx_main.c index 4be2d9b5749..e2982a83ae4 100644 --- a/drivers/net/wireless/bcm43xx/bcm43xx_main.c +++ b/drivers/net/wireless/bcm43xx/bcm43xx_main.c @@ -939,9 +939,9 @@ static int bcm43xx_sprom_extract(struct bcm43xx_private *bcm) return 0; } -static void bcm43xx_geo_init(struct bcm43xx_private *bcm) +static int bcm43xx_geo_init(struct bcm43xx_private *bcm) { - struct ieee80211_geo geo; + struct ieee80211_geo *geo; struct ieee80211_channel *chan; int have_a = 0, have_bg = 0; int i; @@ -949,7 +949,10 @@ static void bcm43xx_geo_init(struct bcm43xx_private *bcm) struct bcm43xx_phyinfo *phy; const char *iso_country; - memset(&geo, 0, sizeof(geo)); + geo = kzalloc(sizeof(*geo), GFP_KERNEL); + if (!geo) + return -ENOMEM; + for (i = 0; i < bcm->nr_80211_available; i++) { phy = &(bcm->core_80211_ext[i].phy); switch (phy->type) { @@ -967,31 +970,36 @@ static void bcm43xx_geo_init(struct bcm43xx_private *bcm) iso_country = bcm43xx_locale_iso(bcm->sprom.locale); if (have_a) { - for (i = 0, channel = 0; channel < 201; channel++) { - chan = &geo.a[i++]; + for (i = 0, channel = IEEE80211_52GHZ_MIN_CHANNEL; + channel <= IEEE80211_52GHZ_MAX_CHANNEL; channel++) { + chan = &geo->a[i++]; chan->freq = bcm43xx_channel_to_freq_a(channel); chan->channel = channel; } - geo.a_channels = i; + geo->a_channels = i; } if (have_bg) { - for (i = 0, channel = 1; channel < 15; channel++) { - chan = &geo.bg[i++]; + for (i = 0, channel = IEEE80211_24GHZ_MIN_CHANNEL; + channel <= IEEE80211_24GHZ_MAX_CHANNEL; channel++) { + chan = &geo->bg[i++]; chan->freq = bcm43xx_channel_to_freq_bg(channel); chan->channel = channel; } - geo.bg_channels = i; + geo->bg_channels = i; } - memcpy(geo.name, iso_country, 2); + memcpy(geo->name, iso_country, 2); if (0 /*TODO: Outdoor use only */) - geo.name[2] = 'O'; + geo->name[2] = 'O'; else if (0 /*TODO: Indoor use only */) - geo.name[2] = 'I'; + geo->name[2] = 'I'; else - geo.name[2] = ' '; - geo.name[3] = '\0'; + geo->name[2] = ' '; + geo->name[3] = '\0'; + + ieee80211_set_geo(bcm->ieee, geo); + kfree(geo); - ieee80211_set_geo(bcm->ieee, &geo); + return 0; } /* DummyTransmission function, as documented on @@ -3479,6 +3487,9 @@ static int bcm43xx_attach_board(struct bcm43xx_private *bcm) goto err_80211_unwind; bcm43xx_wireless_core_disable(bcm); } + err = bcm43xx_geo_init(bcm); + if (err) + goto err_80211_unwind; bcm43xx_pctl_set_crystal(bcm, 0); /* Set the MAC address in the networking subsystem */ @@ -3487,8 +3498,6 @@ static int bcm43xx_attach_board(struct bcm43xx_private *bcm) else memcpy(bcm->net_dev->dev_addr, bcm->sprom.il0macaddr, 6); - bcm43xx_geo_init(bcm); - snprintf(bcm->nick, IW_ESSID_MAX_SIZE, "Broadcom %04X", bcm->chip_id); diff --git a/drivers/net/wireless/bcm43xx/bcm43xx_main.h b/drivers/net/wireless/bcm43xx/bcm43xx_main.h index eca79a38594..30a202b258b 100644 --- a/drivers/net/wireless/bcm43xx/bcm43xx_main.h +++ b/drivers/net/wireless/bcm43xx/bcm43xx_main.h @@ -118,12 +118,14 @@ int bcm43xx_channel_to_freq(struct bcm43xx_private *bcm, static inline int bcm43xx_is_valid_channel_a(u8 channel) { - return (channel <= 200); + return (channel >= IEEE80211_52GHZ_MIN_CHANNEL + && channel <= IEEE80211_52GHZ_MAX_CHANNEL); } static inline int bcm43xx_is_valid_channel_bg(u8 channel) { - return (channel >= 1 && channel <= 14); + return (channel >= IEEE80211_24GHZ_MIN_CHANNEL + && channel <= IEEE80211_24GHZ_MAX_CHANNEL); } static inline int bcm43xx_is_valid_channel(struct bcm43xx_private *bcm, -- cgit v1.2.3 From 178e0cc5ff249965c6cfbd78b1af6a5e614d837c Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Fri, 5 May 2006 18:19:37 +0100 Subject: [PATCH] bcm43xx: Fix access to non-existent PHY registers Fix the conditions under which we poke at the APHY registers in bcm43xx_phy_initg() to avoid a machine check on chips where they don't exist. Signed-off-by: David Woodhouse Signed-off-by: John W. Linville --- drivers/net/wireless/bcm43xx/bcm43xx_phy.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/bcm43xx/bcm43xx_phy.c b/drivers/net/wireless/bcm43xx/bcm43xx_phy.c index 33137165727..b0abac51553 100644 --- a/drivers/net/wireless/bcm43xx/bcm43xx_phy.c +++ b/drivers/net/wireless/bcm43xx/bcm43xx_phy.c @@ -1287,7 +1287,7 @@ static void bcm43xx_phy_initg(struct bcm43xx_private *bcm) if (radio->revision == 8) bcm43xx_phy_write(bcm, 0x0805, 0x3230); bcm43xx_phy_init_pctl(bcm); - if (bcm->chip_id == 0x4306 && bcm->chip_package != 2) { + if (bcm->chip_id == 0x4306 && bcm->chip_package == 2) { bcm43xx_phy_write(bcm, 0x0429, bcm43xx_phy_read(bcm, 0x0429) & 0xBFFF); bcm43xx_phy_write(bcm, 0x04C3, -- cgit v1.2.3 From d324031245abbb54e4e0321004430826052b6c37 Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Mon, 8 May 2006 15:11:26 -0700 Subject: sky2: backout NAPI reschedule This is a backout of earlier patch. The whole rescheduling hack was a bad idea. It doesn't really solve the problem and it makes the code more complicated for no good reason. Signed-off-by: Stephen Hemminger --- drivers/net/sky2.c | 20 ++++---------------- 1 file changed, 4 insertions(+), 16 deletions(-) (limited to 'drivers') diff --git a/drivers/net/sky2.c b/drivers/net/sky2.c index 227df9876a2..76da74fbe85 100644 --- a/drivers/net/sky2.c +++ b/drivers/net/sky2.c @@ -2105,7 +2105,6 @@ static int sky2_poll(struct net_device *dev0, int *budget) int work_done = 0; u32 status = sky2_read32(hw, B0_Y2_SP_EISR); - restart_poll: if (unlikely(status & ~Y2_IS_STAT_BMU)) { if (status & Y2_IS_HW_ERR) sky2_hw_intr(hw); @@ -2136,7 +2135,7 @@ static int sky2_poll(struct net_device *dev0, int *budget) } if (status & Y2_IS_STAT_BMU) { - work_done += sky2_status_intr(hw, work_limit - work_done); + work_done = sky2_status_intr(hw, work_limit); *budget -= work_done; dev0->quota -= work_done; @@ -2148,22 +2147,9 @@ static int sky2_poll(struct net_device *dev0, int *budget) mod_timer(&hw->idle_timer, jiffies + HZ); - local_irq_disable(); - __netif_rx_complete(dev0); + netif_rx_complete(dev0); status = sky2_read32(hw, B0_Y2_SP_LISR); - - if (unlikely(status)) { - /* More work pending, try and keep going */ - if (__netif_rx_schedule_prep(dev0)) { - __netif_rx_reschedule(dev0, work_done); - status = sky2_read32(hw, B0_Y2_SP_EISR); - local_irq_enable(); - goto restart_poll; - } - } - - local_irq_enable(); return 0; } @@ -2181,6 +2167,8 @@ static irqreturn_t sky2_intr(int irq, void *dev_id, struct pt_regs *regs) prefetch(&hw->st_le[hw->st_idx]); if (likely(__netif_rx_schedule_prep(dev0))) __netif_rx_schedule(dev0); + else + printk(KERN_DEBUG PFX "irq race detected\n"); return IRQ_HANDLED; } -- cgit v1.2.3 From 1e5f1283a2aed429f4457e2eb875b1928a6643df Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Mon, 8 May 2006 15:11:27 -0700 Subject: sky2: status irq hang fix The status interrupt flag should be cleared before processing, not afterwards to avoid race. Need to process in poll routine even if no new interrupt status. This is a normal occurrence when more than 64 frames (NAPI weight) are processed in one poll routine. Signed-off-by: Stephen Hemminger --- drivers/net/sky2.c | 53 +++++++++++++++++++++++++---------------------------- 1 file changed, 25 insertions(+), 28 deletions(-) (limited to 'drivers') diff --git a/drivers/net/sky2.c b/drivers/net/sky2.c index 76da74fbe85..552aca76b28 100644 --- a/drivers/net/sky2.c +++ b/drivers/net/sky2.c @@ -2105,45 +2105,42 @@ static int sky2_poll(struct net_device *dev0, int *budget) int work_done = 0; u32 status = sky2_read32(hw, B0_Y2_SP_EISR); - if (unlikely(status & ~Y2_IS_STAT_BMU)) { - if (status & Y2_IS_HW_ERR) - sky2_hw_intr(hw); + if (status & Y2_IS_HW_ERR) + sky2_hw_intr(hw); - if (status & Y2_IS_IRQ_PHY1) - sky2_phy_intr(hw, 0); + if (status & Y2_IS_IRQ_PHY1) + sky2_phy_intr(hw, 0); - if (status & Y2_IS_IRQ_PHY2) - sky2_phy_intr(hw, 1); + if (status & Y2_IS_IRQ_PHY2) + sky2_phy_intr(hw, 1); - if (status & Y2_IS_IRQ_MAC1) - sky2_mac_intr(hw, 0); + if (status & Y2_IS_IRQ_MAC1) + sky2_mac_intr(hw, 0); - if (status & Y2_IS_IRQ_MAC2) - sky2_mac_intr(hw, 1); + if (status & Y2_IS_IRQ_MAC2) + sky2_mac_intr(hw, 1); - if (status & Y2_IS_CHK_RX1) - sky2_descriptor_error(hw, 0, "receive", Y2_IS_CHK_RX1); + if (status & Y2_IS_CHK_RX1) + sky2_descriptor_error(hw, 0, "receive", Y2_IS_CHK_RX1); - if (status & Y2_IS_CHK_RX2) - sky2_descriptor_error(hw, 1, "receive", Y2_IS_CHK_RX2); + if (status & Y2_IS_CHK_RX2) + sky2_descriptor_error(hw, 1, "receive", Y2_IS_CHK_RX2); - if (status & Y2_IS_CHK_TXA1) - sky2_descriptor_error(hw, 0, "transmit", Y2_IS_CHK_TXA1); + if (status & Y2_IS_CHK_TXA1) + sky2_descriptor_error(hw, 0, "transmit", Y2_IS_CHK_TXA1); - if (status & Y2_IS_CHK_TXA2) - sky2_descriptor_error(hw, 1, "transmit", Y2_IS_CHK_TXA2); - } + if (status & Y2_IS_CHK_TXA2) + sky2_descriptor_error(hw, 1, "transmit", Y2_IS_CHK_TXA2); - if (status & Y2_IS_STAT_BMU) { - work_done = sky2_status_intr(hw, work_limit); - *budget -= work_done; - dev0->quota -= work_done; + if (status & Y2_IS_STAT_BMU) + sky2_write32(hw, STAT_CTRL, SC_STAT_CLR_IRQ); - if (work_done >= work_limit) - return 1; + work_done = sky2_status_intr(hw, work_limit); + *budget -= work_done; + dev0->quota -= work_done; - sky2_write32(hw, STAT_CTRL, SC_STAT_CLR_IRQ); - } + if (work_done >= work_limit) + return 1; mod_timer(&hw->idle_timer, jiffies + HZ); -- cgit v1.2.3 From f55925d7eb04f936ab4c001f10e3e9c74c1297ae Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Mon, 8 May 2006 15:11:28 -0700 Subject: sky2: tx ring index mask fix Mask for transmit ring status was picking up bits from the unused sync ring. They were always zero, so far... Also, make sure to remind self not to make tx ring too big. Signed-off-by: Stephen Hemminger --- drivers/net/sky2.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/sky2.c b/drivers/net/sky2.c index 552aca76b28..4bb6ea13efd 100644 --- a/drivers/net/sky2.c +++ b/drivers/net/sky2.c @@ -1927,7 +1927,8 @@ static int sky2_status_intr(struct sky2_hw *hw, int to_do) case OP_TXINDEXLE: /* TX index reports status for both ports */ - sky2_tx_done(hw->dev[0], status & 0xffff); + BUILD_BUG_ON(TX_RING_SIZE > 0x1000); + sky2_tx_done(hw->dev[0], status & 0xfff); if (hw->dev[1]) sky2_tx_done(hw->dev[1], ((status >> 24) & 0xff) -- cgit v1.2.3 From cb5d9547307f44f210f88c829bad4249eeb24bc3 Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Mon, 8 May 2006 15:11:29 -0700 Subject: sky2: use mask instead of modulo operation Gcc isn't smart enough to know that it can do a modulo operation with power of 2 constant by doing a mask. So add macro to do it for us. Signed-off-by: Stephen Hemminger --- drivers/net/sky2.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/net/sky2.c b/drivers/net/sky2.c index 4bb6ea13efd..d9cce50cffd 100644 --- a/drivers/net/sky2.c +++ b/drivers/net/sky2.c @@ -79,6 +79,8 @@ #define NAPI_WEIGHT 64 #define PHY_RETRIES 1000 +#define RING_NEXT(x,s) (((x)+1) & ((s)-1)) + static const u32 default_msg = NETIF_MSG_DRV | NETIF_MSG_PROBE | NETIF_MSG_LINK | NETIF_MSG_TIMER | NETIF_MSG_TX_ERR | NETIF_MSG_RX_ERR @@ -719,7 +721,7 @@ static inline struct sky2_tx_le *get_tx_le(struct sky2_port *sky2) { struct sky2_tx_le *le = sky2->tx_le + sky2->tx_prod; - sky2->tx_prod = (sky2->tx_prod + 1) % TX_RING_SIZE; + sky2->tx_prod = RING_NEXT(sky2->tx_prod, TX_RING_SIZE); return le; } @@ -735,7 +737,7 @@ static inline void sky2_put_idx(struct sky2_hw *hw, unsigned q, u16 idx) static inline struct sky2_rx_le *sky2_next_rx(struct sky2_port *sky2) { struct sky2_rx_le *le = sky2->rx_le + sky2->rx_put; - sky2->rx_put = (sky2->rx_put + 1) % RX_LE_SIZE; + sky2->rx_put = RING_NEXT(sky2->rx_put, RX_LE_SIZE); return le; } @@ -1078,7 +1080,7 @@ err_out: /* Modular subtraction in ring */ static inline int tx_dist(unsigned tail, unsigned head) { - return (head - tail) % TX_RING_SIZE; + return (head - tail) & (TX_RING_SIZE - 1); } /* Number of list elements available for next tx */ @@ -1255,7 +1257,7 @@ static int sky2_xmit_frame(struct sk_buff *skb, struct net_device *dev) le->opcode = OP_BUFFER | HW_OWNER; fre = sky2->tx_ring - + ((re - sky2->tx_ring) + i + 1) % TX_RING_SIZE; + + RING_NEXT((re - sky2->tx_ring) + i, TX_RING_SIZE); pci_unmap_addr_set(fre, mapaddr, mapping); } @@ -1315,7 +1317,7 @@ static void sky2_tx_complete(struct sky2_port *sky2, u16 done) for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { struct tx_ring_info *fre; - fre = sky2->tx_ring + (put + i + 1) % TX_RING_SIZE; + fre = sky2->tx_ring + RING_NEXT(put + i, TX_RING_SIZE); pci_unmap_page(pdev, pci_unmap_addr(fre, mapaddr), skb_shinfo(skb)->frags[i].size, PCI_DMA_TODEVICE); @@ -1876,7 +1878,7 @@ static int sky2_status_intr(struct sky2_hw *hw, int to_do) break; opcode &= ~HW_OWNER; - hw->st_idx = (hw->st_idx + 1) % STATUS_RING_SIZE; + hw->st_idx = RING_NEXT(hw->st_idx, STATUS_RING_SIZE); le->opcode = 0; link = le->link; -- cgit v1.2.3 From 01bd75645f94d49cb7ffd61022890166ce00ec2a Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Mon, 8 May 2006 15:11:30 -0700 Subject: sky2: edge triggered workaround enhancement Need to make the edge-triggered workaround timer faster to get marginally better peformance. The test_and_set_bit in schedule_prep() acts as a barrier already. Make it a module parameter so that laptops who are concerned about power can set it to 0; and user's stuck with broken BIOS's can turn the driver into pure polling. Signed-off-by: Stephen Hemminger --- drivers/net/sky2.c | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/net/sky2.c b/drivers/net/sky2.c index d9cce50cffd..940ed699a70 100644 --- a/drivers/net/sky2.c +++ b/drivers/net/sky2.c @@ -98,6 +98,10 @@ static int disable_msi = 0; module_param(disable_msi, int, 0); MODULE_PARM_DESC(disable_msi, "Disable Message Signaled Interrupt (MSI)"); +static int idle_timeout = 100; +module_param(idle_timeout, int, 0); +MODULE_PARM_DESC(idle_timeout, "Idle timeout workaround for lost interrupts (ms)"); + static const struct pci_device_id sky2_id_table[] = { { PCI_DEVICE(PCI_VENDOR_ID_SYSKONNECT, 0x9000) }, { PCI_DEVICE(PCI_VENDOR_ID_SYSKONNECT, 0x9E00) }, @@ -2092,12 +2096,13 @@ static void sky2_descriptor_error(struct sky2_hw *hw, unsigned port, */ static void sky2_idle(unsigned long arg) { - struct net_device *dev = (struct net_device *) arg; + struct sky2_hw *hw = (struct sky2_hw *) arg; + struct net_device *dev = hw->dev[0]; - local_irq_disable(); if (__netif_rx_schedule_prep(dev)) __netif_rx_schedule(dev); - local_irq_enable(); + + mod_timer(&hw->idle_timer, jiffies + msecs_to_jiffies(idle_timeout)); } @@ -2145,8 +2150,6 @@ static int sky2_poll(struct net_device *dev0, int *budget) if (work_done >= work_limit) return 1; - mod_timer(&hw->idle_timer, jiffies + HZ); - netif_rx_complete(dev0); status = sky2_read32(hw, B0_Y2_SP_LISR); @@ -2167,8 +2170,6 @@ static irqreturn_t sky2_intr(int irq, void *dev_id, struct pt_regs *regs) prefetch(&hw->st_le[hw->st_idx]); if (likely(__netif_rx_schedule_prep(dev0))) __netif_rx_schedule(dev0); - else - printk(KERN_DEBUG PFX "irq race detected\n"); return IRQ_HANDLED; } @@ -3290,7 +3291,10 @@ static int __devinit sky2_probe(struct pci_dev *pdev, sky2_write32(hw, B0_IMSK, Y2_IS_BASE); - setup_timer(&hw->idle_timer, sky2_idle, (unsigned long) dev); + setup_timer(&hw->idle_timer, sky2_idle, (unsigned long) hw); + if (idle_timeout > 0) + mod_timer(&hw->idle_timer, + jiffies + msecs_to_jiffies(idle_timeout)); pci_set_drvdata(pdev, hw); -- cgit v1.2.3 From e71ebd73276cc21efc74aba4118ef037cd32e50a Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Mon, 8 May 2006 15:11:31 -0700 Subject: sky2: dont write status ring It is more efficient not to write the status ring from the processor and just read the active portion. Signed-off-by: Stephen Hemminger --- drivers/net/sky2.c | 21 +++++++-------------- 1 file changed, 7 insertions(+), 14 deletions(-) (limited to 'drivers') diff --git a/drivers/net/sky2.c b/drivers/net/sky2.c index 940ed699a70..ea23da53677 100644 --- a/drivers/net/sky2.c +++ b/drivers/net/sky2.c @@ -1865,35 +1865,28 @@ static inline void sky2_tx_done(struct net_device *dev, u16 last) static int sky2_status_intr(struct sky2_hw *hw, int to_do) { int work_done = 0; + u16 hwidx = sky2_read16(hw, STAT_PUT_IDX); rmb(); - for(;;) { + while (hw->st_idx != hwidx) { struct sky2_status_le *le = hw->st_le + hw->st_idx; struct net_device *dev; struct sky2_port *sky2; struct sk_buff *skb; u32 status; u16 length; - u8 link, opcode; - - opcode = le->opcode; - if (!opcode) - break; - opcode &= ~HW_OWNER; hw->st_idx = RING_NEXT(hw->st_idx, STATUS_RING_SIZE); - le->opcode = 0; - link = le->link; - BUG_ON(link >= 2); - dev = hw->dev[link]; + BUG_ON(le->link >= 2); + dev = hw->dev[le->link]; sky2 = netdev_priv(dev); length = le->length; status = le->status; - switch (opcode) { + switch (le->opcode & ~HW_OWNER) { case OP_RXSTAT: skb = sky2_receive(sky2, length, status); if (!skb) @@ -1944,8 +1937,8 @@ static int sky2_status_intr(struct sky2_hw *hw, int to_do) default: if (net_ratelimit()) printk(KERN_WARNING PFX - "unknown status opcode 0x%x\n", opcode); - break; + "unknown status opcode 0x%x\n", le->opcode); + goto exit_loop; } } -- cgit v1.2.3 From 72cb8529208020484cecd69bbf87719b50ee6313 Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Mon, 8 May 2006 15:11:32 -0700 Subject: sky2: synchronize irq on remove Need to make sure interrupt is not racing with unregister of network device. Signed-off-by: Stephen Hemminger --- drivers/net/sky2.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers') diff --git a/drivers/net/sky2.c b/drivers/net/sky2.c index ea23da53677..9b16c2a899e 100644 --- a/drivers/net/sky2.c +++ b/drivers/net/sky2.c @@ -3327,6 +3327,8 @@ static void __devexit sky2_remove(struct pci_dev *pdev) del_timer_sync(&hw->idle_timer); sky2_write32(hw, B0_IMSK, 0); + synchronize_irq(hw->pdev->irq); + dev0 = hw->dev[0]; dev1 = hw->dev[1]; if (dev1) -- cgit v1.2.3 From ed6d32c7a927bfccf921d15a3e25160f4528c3eb Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Mon, 8 May 2006 15:11:33 -0700 Subject: Add more support for the Yukon Ultra chip found in dual core centino laptops. The newest Yukon Ultra chipset's require more special tweaks. They seem to be like the Yukon XL chipsets. This code is transliterated from the latest SysKonnect driver; I don't have any Ultra hardware. Signed-off-by: Stephe Hemminger Signed-off-by: Stephen Hemminger --- drivers/net/sky2.c | 88 ++++++++++++++++++++++++++++++++++++------------------ drivers/net/sky2.h | 3 ++ 2 files changed, 62 insertions(+), 29 deletions(-) (limited to 'drivers') diff --git a/drivers/net/sky2.c b/drivers/net/sky2.c index 9b16c2a899e..16a99f1ed17 100644 --- a/drivers/net/sky2.c +++ b/drivers/net/sky2.c @@ -304,7 +304,8 @@ static void sky2_phy_init(struct sky2_hw *hw, unsigned port) struct sky2_port *sky2 = netdev_priv(hw->dev[port]); u16 ctrl, ct1000, adv, pg, ledctrl, ledover; - if (sky2->autoneg == AUTONEG_ENABLE && hw->chip_id != CHIP_ID_YUKON_XL) { + if (sky2->autoneg == AUTONEG_ENABLE && + (hw->chip_id != CHIP_ID_YUKON_XL || hw->chip_id == CHIP_ID_YUKON_EC_U)) { u16 ectrl = gm_phy_read(hw, port, PHY_MARV_EXT_CTRL); ectrl &= ~(PHY_M_EC_M_DSC_MSK | PHY_M_EC_S_DSC_MSK | @@ -332,7 +333,7 @@ static void sky2_phy_init(struct sky2_hw *hw, unsigned port) ctrl |= PHY_M_PC_MDI_XMODE(PHY_M_PC_ENA_AUTO); if (sky2->autoneg == AUTONEG_ENABLE && - hw->chip_id == CHIP_ID_YUKON_XL) { + (hw->chip_id == CHIP_ID_YUKON_XL || hw->chip_id == CHIP_ID_YUKON_EC_U)) { ctrl &= ~PHY_M_PC_DSC_MSK; ctrl |= PHY_M_PC_DSC(2) | PHY_M_PC_DOWN_S_ENA; } @@ -448,10 +449,11 @@ static void sky2_phy_init(struct sky2_hw *hw, unsigned port) gm_phy_write(hw, port, PHY_MARV_EXT_ADR, 3); /* set LED Function Control register */ - gm_phy_write(hw, port, PHY_MARV_PHY_CTRL, (PHY_M_LEDC_LOS_CTRL(1) | /* LINK/ACT */ - PHY_M_LEDC_INIT_CTRL(7) | /* 10 Mbps */ - PHY_M_LEDC_STA1_CTRL(7) | /* 100 Mbps */ - PHY_M_LEDC_STA0_CTRL(7))); /* 1000 Mbps */ + gm_phy_write(hw, port, PHY_MARV_PHY_CTRL, + (PHY_M_LEDC_LOS_CTRL(1) | /* LINK/ACT */ + PHY_M_LEDC_INIT_CTRL(7) | /* 10 Mbps */ + PHY_M_LEDC_STA1_CTRL(7) | /* 100 Mbps */ + PHY_M_LEDC_STA0_CTRL(7))); /* 1000 Mbps */ /* set Polarity Control register */ gm_phy_write(hw, port, PHY_MARV_PHY_STAT, @@ -465,6 +467,25 @@ static void sky2_phy_init(struct sky2_hw *hw, unsigned port) /* restore page register */ gm_phy_write(hw, port, PHY_MARV_EXT_ADR, pg); break; + case CHIP_ID_YUKON_EC_U: + pg = gm_phy_read(hw, port, PHY_MARV_EXT_ADR); + + /* select page 3 to access LED control register */ + gm_phy_write(hw, port, PHY_MARV_EXT_ADR, 3); + + /* set LED Function Control register */ + gm_phy_write(hw, port, PHY_MARV_PHY_CTRL, + (PHY_M_LEDC_LOS_CTRL(1) | /* LINK/ACT */ + PHY_M_LEDC_INIT_CTRL(8) | /* 10 Mbps */ + PHY_M_LEDC_STA1_CTRL(7) | /* 100 Mbps */ + PHY_M_LEDC_STA0_CTRL(7)));/* 1000 Mbps */ + + /* set Blink Rate in LED Timer Control Register */ + gm_phy_write(hw, port, PHY_MARV_INT_MASK, + ledctrl | PHY_M_LED_BLINK_RT(BLINK_84MS)); + /* restore page register */ + gm_phy_write(hw, port, PHY_MARV_EXT_ADR, pg); + break; default: /* set Tx LED (LED_TX) to blink mode on Rx OR Tx activity */ @@ -473,19 +494,21 @@ static void sky2_phy_init(struct sky2_hw *hw, unsigned port) ledover |= PHY_M_LED_MO_RX(MO_LED_OFF); } - if (hw->chip_id == CHIP_ID_YUKON_EC_U && hw->chip_rev >= 2) { + if (hw->chip_id == CHIP_ID_YUKON_EC_U && hw->chip_rev == CHIP_REV_YU_EC_A1) { /* apply fixes in PHY AFE */ - gm_phy_write(hw, port, 22, 255); + pg = gm_phy_read(hw, port, PHY_MARV_EXT_ADR); + gm_phy_write(hw, port, PHY_MARV_EXT_ADR, 255); + /* increase differential signal amplitude in 10BASE-T */ - gm_phy_write(hw, port, 24, 0xaa99); - gm_phy_write(hw, port, 23, 0x2011); + gm_phy_write(hw, port, 0x18, 0xaa99); + gm_phy_write(hw, port, 0x17, 0x2011); /* fix for IEEE A/B Symmetry failure in 1000BASE-T */ - gm_phy_write(hw, port, 24, 0xa204); - gm_phy_write(hw, port, 23, 0x2002); + gm_phy_write(hw, port, 0x18, 0xa204); + gm_phy_write(hw, port, 0x17, 0x2002); /* set page register to 0 */ - gm_phy_write(hw, port, 22, 0); + gm_phy_write(hw, port, PHY_MARV_EXT_ADR, pg); } else { gm_phy_write(hw, port, PHY_MARV_LED_CTRL, ledctrl); @@ -559,6 +582,11 @@ static void sky2_mac_init(struct sky2_hw *hw, unsigned port) if (sky2->duplex == DUPLEX_FULL) reg |= GM_GPCR_DUP_FULL; + + /* turn off pause in 10/100mbps half duplex */ + else if (sky2->speed != SPEED_1000 && + hw->chip_id != CHIP_ID_YUKON_EC_U) + sky2->tx_pause = sky2->rx_pause = 0; } else reg = GM_GPCR_SPEED_1000 | GM_GPCR_SPEED_100 | GM_GPCR_DUP_FULL; @@ -1504,17 +1532,26 @@ static void sky2_link_up(struct sky2_port *sky2) sky2_write8(hw, SK_REG(port, LNK_LED_REG), LINKLED_ON | LINKLED_BLINK_OFF | LINKLED_LINKSYNC_OFF); - if (hw->chip_id == CHIP_ID_YUKON_XL) { + if (hw->chip_id == CHIP_ID_YUKON_XL || hw->chip_id == CHIP_ID_YUKON_EC_U) { u16 pg = gm_phy_read(hw, port, PHY_MARV_EXT_ADR); + u16 led = PHY_M_LEDC_LOS_CTRL(1); /* link active */ + + switch(sky2->speed) { + case SPEED_10: + led |= PHY_M_LEDC_INIT_CTRL(7); + break; + + case SPEED_100: + led |= PHY_M_LEDC_STA1_CTRL(7); + break; + + case SPEED_1000: + led |= PHY_M_LEDC_STA0_CTRL(7); + break; + } gm_phy_write(hw, port, PHY_MARV_EXT_ADR, 3); - gm_phy_write(hw, port, PHY_MARV_PHY_CTRL, PHY_M_LEDC_LOS_CTRL(1) | /* LINK/ACT */ - PHY_M_LEDC_INIT_CTRL(sky2->speed == - SPEED_10 ? 7 : 0) | - PHY_M_LEDC_STA1_CTRL(sky2->speed == - SPEED_100 ? 7 : 0) | - PHY_M_LEDC_STA0_CTRL(sky2->speed == - SPEED_1000 ? 7 : 0)); + gm_phy_write(hw, port, PHY_MARV_PHY_CTRL, led); gm_phy_write(hw, port, PHY_MARV_EXT_ADR, pg); } @@ -1589,7 +1626,7 @@ static int sky2_autoneg_done(struct sky2_port *sky2, u16 aux) sky2->speed = sky2_phy_speed(hw, aux); /* Pause bits are offset (9..8) */ - if (hw->chip_id == CHIP_ID_YUKON_XL) + if (hw->chip_id == CHIP_ID_YUKON_XL || hw->chip_id == CHIP_ID_YUKON_EC_U) aux >>= 6; sky2->rx_pause = (aux & PHY_M_PS_RX_P_EN) != 0; @@ -2226,13 +2263,6 @@ static int __devinit sky2_reset(struct sky2_hw *hw) return -EOPNOTSUPP; } - /* This chip is new and not tested yet */ - if (hw->chip_id == CHIP_ID_YUKON_EC_U) { - pr_info(PFX "%s: is a version of Yukon 2 chipset that has not been tested yet.\n", - pci_name(hw->pdev)); - pr_info("Please report success/failure to maintainer \n"); - } - /* disable ASF */ if (hw->chip_id <= CHIP_ID_YUKON_EC) { sky2_write8(hw, B28_Y2_ASF_STAT_CMD, Y2_ASF_RESET); diff --git a/drivers/net/sky2.h b/drivers/net/sky2.h index b026f5653f0..8012994c9b9 100644 --- a/drivers/net/sky2.h +++ b/drivers/net/sky2.h @@ -378,6 +378,9 @@ enum { CHIP_REV_YU_EC_A1 = 0, /* Chip Rev. for Yukon-EC A1/A0 */ CHIP_REV_YU_EC_A2 = 1, /* Chip Rev. for Yukon-EC A2 */ CHIP_REV_YU_EC_A3 = 2, /* Chip Rev. for Yukon-EC A3 */ + + CHIP_REV_YU_EC_U_A0 = 0, + CHIP_REV_YU_EC_U_A1 = 1, }; /* B2_Y2_CLK_GATE 8 bit Clock Gating (Yukon-2 only) */ -- cgit v1.2.3 From 6d4b0f617d577975108ccc7e3b0c7dbe50144df6 Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Mon, 8 May 2006 15:11:34 -0700 Subject: sky2: version 1.3 Update version number, to track changes. Signed-off-by: Stephen Hemminger --- drivers/net/sky2.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/sky2.c b/drivers/net/sky2.c index 16a99f1ed17..60cdfcabe1f 100644 --- a/drivers/net/sky2.c +++ b/drivers/net/sky2.c @@ -51,7 +51,7 @@ #include "sky2.h" #define DRV_NAME "sky2" -#define DRV_VERSION "1.2" +#define DRV_VERSION "1.3" #define PFX DRV_NAME " " /* -- cgit v1.2.3 From 8cd35da094bed8a41eb722c1d03eab24d57bf706 Mon Sep 17 00:00:00 2001 From: Herbert Valerio Riedel Date: Mon, 1 May 2006 15:37:09 +0200 Subject: au1000_eth.c: use ether_crc() from since the au1000 driver already selects the CRC32 routines, simply replace the internal ether_crc() implementation with the semantically equivalent one from Signed-off-by: Herbert Valerio Riedel Signed-off-by: Stephen Hemminger --- drivers/net/au1000_eth.c | 18 +----------------- 1 file changed, 1 insertion(+), 17 deletions(-) (limited to 'drivers') diff --git a/drivers/net/au1000_eth.c b/drivers/net/au1000_eth.c index 1363083b4d8..14dbad14afb 100644 --- a/drivers/net/au1000_eth.c +++ b/drivers/net/au1000_eth.c @@ -52,6 +52,7 @@ #include #include #include +#include #include #include #include @@ -2070,23 +2071,6 @@ static void au1000_tx_timeout(struct net_device *dev) netif_wake_queue(dev); } - -static unsigned const ethernet_polynomial = 0x04c11db7U; -static inline u32 ether_crc(int length, unsigned char *data) -{ - int crc = -1; - - while(--length >= 0) { - unsigned char current_octet = *data++; - int bit; - for (bit = 0; bit < 8; bit++, current_octet >>= 1) - crc = (crc << 1) ^ - ((crc < 0) ^ (current_octet & 1) ? - ethernet_polynomial : 0); - } - return crc; -} - static void set_rx_mode(struct net_device *dev) { struct au1000_private *aup = (struct au1000_private *) dev->priv; -- cgit v1.2.3 From aedc0e520e5ae9ba1342c25c4604d18fb236c2bc Mon Sep 17 00:00:00 2001 From: Sergei Shtylyov Date: Tue, 9 May 2006 00:58:28 +0400 Subject: Fix RTL8019AS init for Toshiba RBTX49xx boards Ensure that 8-bit mode is selected for the on-board Realtek RTL8019AS chip on Toshiba RBHMA4x00, get rid of the duplicate #ifdef's when setting ei_status.word16. The chip's datasheet says that the PSTOP register shouldn't exceed 0x60 in 8-bit mode -- ensure this too. Signed-off-by: Sergei Shtylyov Signed-off-by: Stephen Hemminger --- drivers/net/ne.c | 31 +++++++++++++++++++------------ 1 file changed, 19 insertions(+), 12 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ne.c b/drivers/net/ne.c index 93c494bcd18..b32765215f7 100644 --- a/drivers/net/ne.c +++ b/drivers/net/ne.c @@ -139,8 +139,9 @@ bad_clone_list[] __initdata = { #if defined(CONFIG_PLAT_MAPPI) # define DCR_VAL 0x4b -#elif defined(CONFIG_PLAT_OAKS32R) -# define DCR_VAL 0x48 +#elif defined(CONFIG_PLAT_OAKS32R) || \ + defined(CONFIG_TOSHIBA_RBTX4927) || defined(CONFIG_TOSHIBA_RBTX4938) +# define DCR_VAL 0x48 /* 8-bit mode */ #else # define DCR_VAL 0x49 #endif @@ -396,10 +397,22 @@ static int __init ne_probe1(struct net_device *dev, int ioaddr) /* We must set the 8390 for word mode. */ outb_p(DCR_VAL, ioaddr + EN0_DCFG); start_page = NESM_START_PG; - stop_page = NESM_STOP_PG; + + /* + * Realtek RTL8019AS datasheet says that the PSTOP register + * shouldn't exceed 0x60 in 8-bit mode. + * This chip can be identified by reading the signature from + * the remote byte count registers (otherwise write-only)... + */ + if ((DCR_VAL & 0x01) == 0 && /* 8-bit mode */ + inb(ioaddr + EN0_RCNTLO) == 0x50 && + inb(ioaddr + EN0_RCNTHI) == 0x70) + stop_page = 0x60; + else + stop_page = NESM_STOP_PG; } else { start_page = NE1SM_START_PG; - stop_page = NE1SM_STOP_PG; + stop_page = NE1SM_STOP_PG; } #if defined(CONFIG_PLAT_MAPPI) || defined(CONFIG_PLAT_OAKS32R) @@ -509,15 +522,9 @@ static int __init ne_probe1(struct net_device *dev, int ioaddr) ei_status.name = name; ei_status.tx_start_page = start_page; ei_status.stop_page = stop_page; -#if defined(CONFIG_TOSHIBA_RBTX4927) || defined(CONFIG_TOSHIBA_RBTX4938) - wordlength = 1; -#endif -#ifdef CONFIG_PLAT_OAKS32R - ei_status.word16 = 0; -#else - ei_status.word16 = (wordlength == 2); -#endif + /* Use 16-bit mode only if this wasn't overridden by DCR_VAL */ + ei_status.word16 = (wordlength == 2 && (DCR_VAL & 0x01)); ei_status.rx_start_page = start_page + TX_PAGES; #ifdef PACKETBUF_MEMSIZE -- cgit v1.2.3 From b636d17a3bee8ba988e78e4bc8262f0dc3fad8ab Mon Sep 17 00:00:00 2001 From: Jens Osterkamp Date: Thu, 4 May 2006 05:59:56 -0400 Subject: spidernet: introduce new setting We found a new chip setting that we need in order to make the driver work more reliable. Signed-off-by: Arnd Bergmann Signed-off-by: Stephen Hemminger --- drivers/net/spider_net.c | 2 ++ drivers/net/spider_net.h | 2 ++ 2 files changed, 4 insertions(+) (limited to 'drivers') diff --git a/drivers/net/spider_net.c b/drivers/net/spider_net.c index 43f5e86fc55..b36838bfb8d 100644 --- a/drivers/net/spider_net.c +++ b/drivers/net/spider_net.c @@ -1652,6 +1652,8 @@ spider_net_enable_card(struct spider_net_card *card) { SPIDER_NET_GFTRESTRT, SPIDER_NET_RESTART_VALUE }, { SPIDER_NET_GMRWOLCTRL, 0 }, + { SPIDER_NET_GTESTMD, 0x10000000 }, + { SPIDER_NET_GTTQMSK, 0x00400040 }, { SPIDER_NET_GTESTMD, 0 }, { SPIDER_NET_GMACINTEN, 0 }, diff --git a/drivers/net/spider_net.h b/drivers/net/spider_net.h index 5922b529a04..3b8d951cf73 100644 --- a/drivers/net/spider_net.h +++ b/drivers/net/spider_net.h @@ -120,6 +120,8 @@ extern char spider_net_driver_name[]; #define SPIDER_NET_GMRUAFILnR 0x00000500 #define SPIDER_NET_GMRUA0FIL15R 0x00000578 +#define SPIDER_NET_GTTQMSK 0x00000934 + /* RX DMA controller registers, all 0x00000a.. are for DMA controller A, * 0x00000b.. for DMA controller B, etc. */ #define SPIDER_NET_GDADCHA 0x00000a00 -- cgit v1.2.3 From 8ec93459655a3618dedec6360bb28d63f0010ef6 Mon Sep 17 00:00:00 2001 From: Jens Osterkamp Date: Thu, 4 May 2006 05:59:41 -0400 Subject: spidernet: enable support for bcm5461 ethernet phy A newer board revision changed the type of ethernet phy. Moreover, this generalizes the way that a phy gets switched into fiber mode when autodetection is not available. Signed-off-by: Jens Osterkamp Signed-off-by: Arnd Bergmann Signed-off-by: Stephen Hemminger --- drivers/net/spider_net.c | 10 +--------- drivers/net/sungem_phy.c | 45 +++++++++++++++++++++++++++++++++++++++++++++ drivers/net/sungem_phy.h | 1 + 3 files changed, 47 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/net/spider_net.c b/drivers/net/spider_net.c index b36838bfb8d..394339d5e87 100644 --- a/drivers/net/spider_net.c +++ b/drivers/net/spider_net.c @@ -1794,15 +1794,7 @@ spider_net_setup_phy(struct spider_net_card *card) if (phy->def->ops->setup_forced) phy->def->ops->setup_forced(phy, SPEED_1000, DUPLEX_FULL); - /* the following two writes could be moved to sungem_phy.c */ - /* enable fiber mode */ - spider_net_write_phy(card->netdev, 1, MII_NCONFIG, 0x9020); - /* LEDs active in both modes, autosense prio = fiber */ - spider_net_write_phy(card->netdev, 1, MII_NCONFIG, 0x945f); - - /* switch off fibre autoneg */ - spider_net_write_phy(card->netdev, 1, MII_NCONFIG, 0xfc01); - spider_net_write_phy(card->netdev, 1, 0x0b, 0x0004); + phy->def->ops->enable_fiber(phy); phy->def->ops->read_link(phy); pr_info("Found %s with %i Mbps, %s-duplex.\n", phy->def->name, diff --git a/drivers/net/sungem_phy.c b/drivers/net/sungem_phy.c index 046371ee5bb..b2ddd5e7930 100644 --- a/drivers/net/sungem_phy.c +++ b/drivers/net/sungem_phy.c @@ -329,6 +329,30 @@ static int bcm5421_init(struct mii_phy* phy) return 0; } +static int bcm5421_enable_fiber(struct mii_phy* phy) +{ + /* enable fiber mode */ + phy_write(phy, MII_NCONFIG, 0x9020); + /* LEDs active in both modes, autosense prio = fiber */ + phy_write(phy, MII_NCONFIG, 0x945f); + + /* switch off fibre autoneg */ + phy_write(phy, MII_NCONFIG, 0xfc01); + phy_write(phy, 0x0b, 0x0004); + + return 0; +} + +static int bcm5461_enable_fiber(struct mii_phy* phy) +{ + phy_write(phy, MII_NCONFIG, 0xfc0c); + phy_write(phy, MII_BMCR, 0x4140); + phy_write(phy, MII_NCONFIG, 0xfc0b); + phy_write(phy, MII_BMCR, 0x0140); + + return 0; +} + static int bcm54xx_setup_aneg(struct mii_phy *phy, u32 advertise) { u16 ctl, adv; @@ -762,6 +786,7 @@ static struct mii_phy_ops bcm5421_phy_ops = { .setup_forced = bcm54xx_setup_forced, .poll_link = genmii_poll_link, .read_link = bcm54xx_read_link, + .enable_fiber = bcm5421_enable_fiber, }; static struct mii_phy_def bcm5421_phy_def = { @@ -792,6 +817,25 @@ static struct mii_phy_def bcm5421k2_phy_def = { .ops = &bcm5421k2_phy_ops }; +static struct mii_phy_ops bcm5461_phy_ops = { + .init = bcm5421_init, + .suspend = generic_suspend, + .setup_aneg = bcm54xx_setup_aneg, + .setup_forced = bcm54xx_setup_forced, + .poll_link = genmii_poll_link, + .read_link = bcm54xx_read_link, + .enable_fiber = bcm5461_enable_fiber, +}; + +static struct mii_phy_def bcm5461_phy_def = { + .phy_id = 0x002060c0, + .phy_id_mask = 0xfffffff0, + .name = "BCM5461", + .features = MII_GBIT_FEATURES, + .magic_aneg = 1, + .ops = &bcm5461_phy_ops +}; + /* Broadcom BCM 5462 built-in Vesta */ static struct mii_phy_ops bcm5462V_phy_ops = { .init = bcm5421_init, @@ -857,6 +901,7 @@ static struct mii_phy_def* mii_phy_table[] = { &bcm5411_phy_def, &bcm5421_phy_def, &bcm5421k2_phy_def, + &bcm5461_phy_def, &bcm5462V_phy_def, &marvell_phy_def, &genmii_phy_def, diff --git a/drivers/net/sungem_phy.h b/drivers/net/sungem_phy.h index 430544496c5..69e125197fc 100644 --- a/drivers/net/sungem_phy.h +++ b/drivers/net/sungem_phy.h @@ -12,6 +12,7 @@ struct mii_phy_ops int (*setup_forced)(struct mii_phy *phy, int speed, int fd); int (*poll_link)(struct mii_phy *phy); int (*read_link)(struct mii_phy *phy); + int (*enable_fiber)(struct mii_phy *phy); }; /* Structure used to statically define an mii/gii based PHY */ -- cgit v1.2.3