Re: WOL with forcedeth broken since f55c21fd9a92a444e55ad1ca4e4732d56661bf2e

From: Yinghai Lu
Date: Fri Jan 30 2009 - 03:35:47 EST


On Thu, Jan 29, 2009 at 10:53 PM, Yinghai Lu <yhlu.kernel@xxxxxxxxx> wrote:
> On Wed, Jan 28, 2009 at 10:31 PM, Philipp Matthias Hahn
> <pmhahn@xxxxxxxxxxxxx> wrote:
>> Hello!
>>
>> Somewhere between 2.6.26 and 2.6.27 WOL stopped working on my MS-7350
>> motherboard with an "nVidia Corporation MCP51 Ethernet Controller (rev a3)".
>> "etherwake 00:19:db:f2:e1:35" did not work
>> "etherwake 35:e1:f2:db:19:00" did work

looks like your BIOS, already reverse the MAC addr...

please check attached ... Rafael, wonder if will break suspend/resume...

YH
[PATCH] forcedeth: keep mac addr to orignal

Impact: fix mac reverse for wol

some BIOS may already reverse that, try to don't touch the mac addr, so don't
confuse the WOL

Signed-off-by: Yinghai Lu <yinghai@xxxxxxxxxx>

---
drivers/net/forcedeth.c | 50 +++++++++++++++++++++++++++++-------------------
1 file changed, 31 insertions(+), 19 deletions(-)

Index: linux-2.6/drivers/net/forcedeth.c
===================================================================
--- linux-2.6.orig/drivers/net/forcedeth.c
+++ linux-2.6/drivers/net/forcedeth.c
@@ -748,6 +748,8 @@ struct fe_priv {
/* General data: RO fields */
dma_addr_t ring_addr;
struct pci_dev *pci_dev;
+ /* wonder if bios already reverse that */
+ int orig_mac_status;
u32 orig_mac[2];
u32 irqmask;
u32 desc_ver;
@@ -5639,14 +5641,17 @@ static int __devinit nv_probe(struct pci
/* check the workaround bit for correct mac address order */
txreg = readl(base + NvRegTransmitPoll);
if (id->driver_data & DEV_HAS_CORRECT_MACADDR) {
- /* mac address is already in correct order */
- dev->dev_addr[0] = (np->orig_mac[0] >> 0) & 0xff;
- dev->dev_addr[1] = (np->orig_mac[0] >> 8) & 0xff;
- dev->dev_addr[2] = (np->orig_mac[0] >> 16) & 0xff;
- dev->dev_addr[3] = (np->orig_mac[0] >> 24) & 0xff;
- dev->dev_addr[4] = (np->orig_mac[1] >> 0) & 0xff;
- dev->dev_addr[5] = (np->orig_mac[1] >> 8) & 0xff;
+ np->orig_mac_status = -1;
} else if (txreg & NVREG_TRANSMITPOLL_MAC_ADDR_REV) {
+ np->orig_mac_status = 1;
+ printk(KERN_DEBUG "nv_probe: workaround bit for reversed mac addr is set\n");
+ } else {
+ np->orig_mac_status = 0;
+ writel(txreg|NVREG_TRANSMITPOLL_MAC_ADDR_REV,
+ base + NvRegTransmitPoll);
+ printk(KERN_DEBUG "nv_probe: set workaround bit for reversed mac addr\n");
+ }
+ if (np->orig_mac_status) {
/* mac address is already in correct order */
dev->dev_addr[0] = (np->orig_mac[0] >> 0) & 0xff;
dev->dev_addr[1] = (np->orig_mac[0] >> 8) & 0xff;
@@ -5654,14 +5659,6 @@ static int __devinit nv_probe(struct pci
dev->dev_addr[3] = (np->orig_mac[0] >> 24) & 0xff;
dev->dev_addr[4] = (np->orig_mac[1] >> 0) & 0xff;
dev->dev_addr[5] = (np->orig_mac[1] >> 8) & 0xff;
- /*
- * Set orig mac address back to the reversed version.
- * This flag will be cleared during low power transition.
- * Therefore, we should always put back the reversed address.
- */
- np->orig_mac[0] = (dev->dev_addr[5] << 0) + (dev->dev_addr[4] << 8) +
- (dev->dev_addr[3] << 16) + (dev->dev_addr[2] << 24);
- np->orig_mac[1] = (dev->dev_addr[1] << 0) + (dev->dev_addr[0] << 8);
} else {
/* need to reverse mac address to correct order */
dev->dev_addr[0] = (np->orig_mac[1] >> 8) & 0xff;
@@ -5670,8 +5667,6 @@ static int __devinit nv_probe(struct pci
dev->dev_addr[3] = (np->orig_mac[0] >> 16) & 0xff;
dev->dev_addr[4] = (np->orig_mac[0] >> 8) & 0xff;
dev->dev_addr[5] = (np->orig_mac[0] >> 0) & 0xff;
- writel(txreg|NVREG_TRANSMITPOLL_MAC_ADDR_REV, base + NvRegTransmitPoll);
- printk(KERN_DEBUG "nv_probe: set workaround bit for reversed mac addr\n");
}
memcpy(dev->perm_addr, dev->dev_addr, dev->addr_len);

@@ -5923,14 +5918,31 @@ static void nv_restore_mac_addr(struct p
{
struct net_device *dev = pci_get_drvdata(pci_dev);
struct fe_priv *np = netdev_priv(dev);
- u8 __iomem *base = get_hwbase(dev);
+ u8 __iomem *base;
+ u32 txreg;
+
+ if (np->orig_mac_status == -1)
+ return;
+
+ base = get_hwbase(dev);
+ txreg = readl(base + NvRegTransmitPoll);
+ if (np->orig_mac_status == 1) {
+ /*
+ * Set orig mac address back to the reversed version.
+ * This flag will be cleared during low power transition.
+ * Therefore, set that bit don't confuse other...
+ */
+ writel(txreg | NVREG_TRANSMITPOLL_MAC_ADDR_REV,
+ base + NvRegTransmitPoll);
+ return;
+ }

/* special op: write back the misordered MAC address - otherwise
* the next nv_probe would see a wrong address.
*/
writel(np->orig_mac[0], base + NvRegMacAddrA);
writel(np->orig_mac[1], base + NvRegMacAddrB);
- writel(readl(base + NvRegTransmitPoll) & ~NVREG_TRANSMITPOLL_MAC_ADDR_REV,
+ writel(txreg & ~NVREG_TRANSMITPOLL_MAC_ADDR_REV,
base + NvRegTransmitPoll);
}