Skip to content

Commit

Permalink
r8169: read MAC address from EEPROM on init (2nd attempt)
Browse files Browse the repository at this point in the history
This is 2nd attempt to implement the initialization/reading of MAC address
from EEPROM. The first used PCI's VPD and there were some problems, some
devices are not able to read EEPROM content by VPD. The 2nd one uses direct
access to EEPROM through bit-banging interface and my testing results seem
to be much better.

I tested 5 systems each with different Realtek NICs and I didn't find any
problem. AFAIK Francois's NICs also works fine.

Original description:
This fixes the problem when MAC address is set by ifconfig or by
ip link commands and this address is stored in the device after
reboot. The power-off is needed to get right MAC address.
This is problem when Xen daemon is running because it renames the device
name from ethX to pethX and sets its MAC address to FE:FF:FF:FF:FF:FF.
After reboot the device is still using FE:FF:FF:FF:FF:FF.

Signed-off-by: Ivan Vecera <ivecera@redhat.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
Ivan Vecera authored and David S. Miller committed Mar 2, 2009
1 parent 9ec06ff commit 6709fe9
Showing 1 changed file with 112 additions and 2 deletions.
114 changes: 112 additions & 2 deletions drivers/net/r8169.c
Original file line number Diff line number Diff line change
Expand Up @@ -81,9 +81,9 @@ static const int multicast_filter_limit = 32;
#define RTL8169_TX_TIMEOUT (6*HZ)
#define RTL8169_PHY_TIMEOUT (10*HZ)

#define RTL_EEPROM_SIG cpu_to_le32(0x8129)
#define RTL_EEPROM_SIG_MASK cpu_to_le32(0xffff)
#define RTL_EEPROM_SIG 0x8129
#define RTL_EEPROM_SIG_ADDR 0x0000
#define RTL_EEPROM_MAC_ADDR 0x0007

/* write/read MMIO register */
#define RTL_W8(reg, val8) writeb ((val8), ioaddr + (reg))
Expand Down Expand Up @@ -293,6 +293,11 @@ enum rtl_register_content {
/* Cfg9346Bits */
Cfg9346_Lock = 0x00,
Cfg9346_Unlock = 0xc0,
Cfg9346_Program = 0x80, /* Programming mode */
Cfg9346_EECS = 0x08, /* Chip select */
Cfg9346_EESK = 0x04, /* Serial data clock */
Cfg9346_EEDI = 0x02, /* Data input */
Cfg9346_EEDO = 0x01, /* Data output */

/* rx_mode_bits */
AcceptErr = 0x20,
Expand All @@ -305,6 +310,7 @@ enum rtl_register_content {
/* RxConfigBits */
RxCfgFIFOShift = 13,
RxCfgDMAShift = 8,
RxCfg9356SEL = 6, /* EEPROM type: 0 = 9346, 1 = 9356 */

/* TxConfigBits */
TxInterFrameGapShift = 24,
Expand Down Expand Up @@ -1963,6 +1969,108 @@ static const struct net_device_ops rtl8169_netdev_ops = {

};

/* Delay between EEPROM clock transitions. Force out buffered PCI writes. */
#define RTL_EEPROM_DELAY() RTL_R8(Cfg9346)
#define RTL_EEPROM_READ_CMD 6

/* read 16bit word stored in EEPROM. EEPROM is addressed by words. */
static u16 rtl_eeprom_read(void __iomem *ioaddr, int addr)
{
u16 result = 0;
int cmd, cmd_len, i;

/* check for EEPROM address size (in bits) */
if (RTL_R32(RxConfig) & (1 << RxCfg9356SEL)) {
/* EEPROM is 93C56 */
cmd_len = 3 + 8; /* 3 bits for command id and 8 for address */
cmd = (RTL_EEPROM_READ_CMD << 8) | (addr & 0xff);
} else {
/* EEPROM is 93C46 */
cmd_len = 3 + 6; /* 3 bits for command id and 6 for address */
cmd = (RTL_EEPROM_READ_CMD << 6) | (addr & 0x3f);
}

/* enter programming mode */
RTL_W8(Cfg9346, Cfg9346_Program | Cfg9346_EECS);
RTL_EEPROM_DELAY();

/* write command and requested address */
while (cmd_len--) {
u8 x = Cfg9346_Program | Cfg9346_EECS;

x |= (cmd & (1 << cmd_len)) ? Cfg9346_EEDI : 0;

/* write a bit */
RTL_W8(Cfg9346, x);
RTL_EEPROM_DELAY();

/* raise clock */
RTL_W8(Cfg9346, x | Cfg9346_EESK);
RTL_EEPROM_DELAY();
}

/* lower clock */
RTL_W8(Cfg9346, Cfg9346_Program | Cfg9346_EECS);
RTL_EEPROM_DELAY();

/* read back 16bit value */
for (i = 16; i > 0; i--) {
/* raise clock */
RTL_W8(Cfg9346, Cfg9346_Program | Cfg9346_EECS | Cfg9346_EESK);
RTL_EEPROM_DELAY();

result <<= 1;
result |= (RTL_R8(Cfg9346) & Cfg9346_EEDO) ? 1 : 0;

/* lower clock */
RTL_W8(Cfg9346, Cfg9346_Program | Cfg9346_EECS);
RTL_EEPROM_DELAY();
}

RTL_W8(Cfg9346, Cfg9346_Program);
/* leave programming mode */
RTL_W8(Cfg9346, Cfg9346_Lock);

return result;
}

static void rtl_init_mac_address(struct rtl8169_private *tp,
void __iomem *ioaddr)
{
struct pci_dev *pdev = tp->pci_dev;
u16 x;
u8 mac[8];

/* read EEPROM signature */
x = rtl_eeprom_read(ioaddr, RTL_EEPROM_SIG_ADDR);

if (x != RTL_EEPROM_SIG) {
dev_info(&pdev->dev, "Missing EEPROM signature: %04x\n", x);
return;
}

/* read MAC address */
x = rtl_eeprom_read(ioaddr, RTL_EEPROM_MAC_ADDR);
mac[0] = x & 0xff;
mac[1] = x >> 8;
x = rtl_eeprom_read(ioaddr, RTL_EEPROM_MAC_ADDR + 1);
mac[2] = x & 0xff;
mac[3] = x >> 8;
x = rtl_eeprom_read(ioaddr, RTL_EEPROM_MAC_ADDR + 2);
mac[4] = x & 0xff;
mac[5] = x >> 8;

if (netif_msg_probe(tp)) {
DECLARE_MAC_BUF(buf);

dev_info(&pdev->dev, "MAC address found in EEPROM: %s\n",
print_mac(buf, mac));
}

if (is_valid_ether_addr(mac))
rtl_rar_set(tp, mac);
}

static int __devinit
rtl8169_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
{
Expand Down Expand Up @@ -2141,6 +2249,8 @@ rtl8169_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)

tp->mmio_addr = ioaddr;

rtl_init_mac_address(tp, ioaddr);

/* Get MAC address */
for (i = 0; i < MAC_ADDR_LEN; i++)
dev->dev_addr[i] = RTL_R8(MAC0 + i);
Expand Down

0 comments on commit 6709fe9

Please sign in to comment.