Skip to content

Commit

Permalink
[realtek] Use ID word to detect EEPROM presence
Browse files Browse the repository at this point in the history
Some onboard RTL8169 NICs seem to leave the EEPROM pins disconnected.
The existing is_valid_ether_addr() test will not necessarily catch
this, since it expects a missing EEPROM to show up as a MAC address of
00:00:00:00:00:00 or ff:ff:ff:ff:ff:ff.  When the EEPROM pins are
floating the MAC address may read as e.g. 00:00:00:00:0f:00, which
will not be detected as invalid.

Check the ID word in the first two bytes of the EEPROM (which should
have the value 0x8129 for all RTL8139 and RTL8169 chips), and use this
to determine whether or not an EEPROM is present.

Reported-by: Carl Karsten <carl@nextdayvideo.com>
Tested-by: Carl Karsten <carl@nextdayvideo.com>
Signed-off-by: Michael Brown <mcb30@ipxe.org>
  • Loading branch information
mcb30 committed Apr 28, 2013
1 parent 1aca99f commit d90fc31
Show file tree
Hide file tree
Showing 2 changed files with 42 additions and 16 deletions.
52 changes: 36 additions & 16 deletions src/drivers/net/realtek.c
Expand Up @@ -144,9 +144,12 @@ static struct bit_basher_operations realtek_basher_ops = {
* Initialise EEPROM
*
* @v netdev Network device
* @ret rc Return status code
*/
static void realtek_init_eeprom ( struct net_device *netdev ) {
static int realtek_init_eeprom ( struct net_device *netdev ) {
struct realtek_nic *rtl = netdev->priv;
uint16_t id;
int rc;

/* Initialise SPI bit-bashing interface */
rtl->spibit.basher.op = &realtek_basher_ops;
Expand All @@ -163,6 +166,22 @@ static void realtek_init_eeprom ( struct net_device *netdev ) {
}
rtl->eeprom.bus = &rtl->spibit.bus;

/* Check for EEPROM presence. Some onboard NICs will have no
* EEPROM connected, with the BIOS being responsible for
* programming the initial register values.
*/
if ( ( rc = nvs_read ( &rtl->eeprom.nvs, RTL_EEPROM_ID,
&id, sizeof ( id ) ) ) != 0 ) {
DBGC ( rtl, "REALTEK %p could not read EEPROM ID: %s\n",
rtl, strerror ( rc ) );
return rc;
}
if ( id != cpu_to_le16 ( RTL_EEPROM_ID_MAGIC ) ) {
DBGC ( rtl, "REALTEK %p EEPROM ID incorrect (%#04x); assuming "
"no EEPROM\n", rtl, le16_to_cpu ( id ) );
return -ENODEV;
}

/* Initialise space for non-volatile options, if available
*
* We use offset 0x40 (i.e. address 0x20), length 0x40. This
Expand All @@ -176,6 +195,8 @@ static void realtek_init_eeprom ( struct net_device *netdev ) {
nvo_init ( &rtl->nvo, &rtl->eeprom.nvs, RTL_EEPROM_VPD,
RTL_EEPROM_VPD_LEN, NULL, &netdev->refcnt );
}

return 0;
}

/******************************************************************************
Expand Down Expand Up @@ -1045,23 +1066,22 @@ static int realtek_probe ( struct pci_device *pci ) {
realtek_detect ( rtl );

/* Initialise EEPROM */
realtek_init_eeprom ( netdev );
if ( ( rc = realtek_init_eeprom ( netdev ) ) == 0 ) {

/* Read MAC address from EEPROM */
if ( ( rc = nvs_read ( &rtl->eeprom.nvs, RTL_EEPROM_MAC,
netdev->hw_addr, ETH_ALEN ) ) != 0 ) {
DBGC ( rtl, "REALTEK %p could not read MAC address: "
"%s\n", rtl, strerror ( rc ) );
goto err_nvs_read;
}

/* Read MAC address from EEPROM */
if ( ( rc = nvs_read ( &rtl->eeprom.nvs, RTL_EEPROM_MAC,
netdev->hw_addr, ETH_ALEN ) ) != 0 ) {
DBGC ( rtl, "REALTEK %p could not read MAC address: %s\n",
rtl, strerror ( rc ) );
goto err_nvs_read;
}
} else {

/* The EEPROM may not be present for onboard NICs. Fall back
* to reading the current ID register value, which will
* hopefully have been programmed by the platform firmware.
*/
if ( ! is_valid_ether_addr ( netdev->hw_addr ) ) {
DBGC ( rtl, "REALTEK %p seems to have no EEPROM (MAC %s)\n",
rtl, eth_ntoa ( netdev->hw_addr ) );
/* EEPROM not present. Fall back to reading the
* current ID register value, which will hopefully
* have been programmed by the platform firmware.
*/
for ( i = 0 ; i < ETH_ALEN ; i++ )
netdev->hw_addr[i] = readb ( rtl->regs + RTL_IDR0 + i );
}
Expand Down
6 changes: 6 additions & 0 deletions src/drivers/net/realtek.h
Expand Up @@ -166,6 +166,12 @@ enum realtek_legacy_status {
#define RTL_9346CR_EEDI 0x02 /**< Data in */
#define RTL_9346CR_EEDO 0x01 /**< Data out */

/** Word offset of ID code word within EEPROM */
#define RTL_EEPROM_ID ( 0x00 / 2 )

/** EEPROM code word magic value */
#define RTL_EEPROM_ID_MAGIC 0x8129

/** Word offset of MAC address within EEPROM */
#define RTL_EEPROM_MAC ( 0x0e / 2 )

Expand Down

0 comments on commit d90fc31

Please sign in to comment.