Skip to content

Commit d90fc31

Browse files
committed
[realtek] Use ID word to detect EEPROM presence
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>
1 parent 1aca99f commit d90fc31

File tree

2 files changed

+42
-16
lines changed

2 files changed

+42
-16
lines changed

src/drivers/net/realtek.c

Lines changed: 36 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -144,9 +144,12 @@ static struct bit_basher_operations realtek_basher_ops = {
144144
* Initialise EEPROM
145145
*
146146
* @v netdev Network device
147+
* @ret rc Return status code
147148
*/
148-
static void realtek_init_eeprom ( struct net_device *netdev ) {
149+
static int realtek_init_eeprom ( struct net_device *netdev ) {
149150
struct realtek_nic *rtl = netdev->priv;
151+
uint16_t id;
152+
int rc;
150153

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

169+
/* Check for EEPROM presence. Some onboard NICs will have no
170+
* EEPROM connected, with the BIOS being responsible for
171+
* programming the initial register values.
172+
*/
173+
if ( ( rc = nvs_read ( &rtl->eeprom.nvs, RTL_EEPROM_ID,
174+
&id, sizeof ( id ) ) ) != 0 ) {
175+
DBGC ( rtl, "REALTEK %p could not read EEPROM ID: %s\n",
176+
rtl, strerror ( rc ) );
177+
return rc;
178+
}
179+
if ( id != cpu_to_le16 ( RTL_EEPROM_ID_MAGIC ) ) {
180+
DBGC ( rtl, "REALTEK %p EEPROM ID incorrect (%#04x); assuming "
181+
"no EEPROM\n", rtl, le16_to_cpu ( id ) );
182+
return -ENODEV;
183+
}
184+
166185
/* Initialise space for non-volatile options, if available
167186
*
168187
* We use offset 0x40 (i.e. address 0x20), length 0x40. This
@@ -176,6 +195,8 @@ static void realtek_init_eeprom ( struct net_device *netdev ) {
176195
nvo_init ( &rtl->nvo, &rtl->eeprom.nvs, RTL_EEPROM_VPD,
177196
RTL_EEPROM_VPD_LEN, NULL, &netdev->refcnt );
178197
}
198+
199+
return 0;
179200
}
180201

181202
/******************************************************************************
@@ -1045,23 +1066,22 @@ static int realtek_probe ( struct pci_device *pci ) {
10451066
realtek_detect ( rtl );
10461067

10471068
/* Initialise EEPROM */
1048-
realtek_init_eeprom ( netdev );
1069+
if ( ( rc = realtek_init_eeprom ( netdev ) ) == 0 ) {
1070+
1071+
/* Read MAC address from EEPROM */
1072+
if ( ( rc = nvs_read ( &rtl->eeprom.nvs, RTL_EEPROM_MAC,
1073+
netdev->hw_addr, ETH_ALEN ) ) != 0 ) {
1074+
DBGC ( rtl, "REALTEK %p could not read MAC address: "
1075+
"%s\n", rtl, strerror ( rc ) );
1076+
goto err_nvs_read;
1077+
}
10491078

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

1058-
/* The EEPROM may not be present for onboard NICs. Fall back
1059-
* to reading the current ID register value, which will
1060-
* hopefully have been programmed by the platform firmware.
1061-
*/
1062-
if ( ! is_valid_ether_addr ( netdev->hw_addr ) ) {
1063-
DBGC ( rtl, "REALTEK %p seems to have no EEPROM (MAC %s)\n",
1064-
rtl, eth_ntoa ( netdev->hw_addr ) );
1081+
/* EEPROM not present. Fall back to reading the
1082+
* current ID register value, which will hopefully
1083+
* have been programmed by the platform firmware.
1084+
*/
10651085
for ( i = 0 ; i < ETH_ALEN ; i++ )
10661086
netdev->hw_addr[i] = readb ( rtl->regs + RTL_IDR0 + i );
10671087
}

src/drivers/net/realtek.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,12 @@ enum realtek_legacy_status {
166166
#define RTL_9346CR_EEDI 0x02 /**< Data in */
167167
#define RTL_9346CR_EEDO 0x01 /**< Data out */
168168

169+
/** Word offset of ID code word within EEPROM */
170+
#define RTL_EEPROM_ID ( 0x00 / 2 )
171+
172+
/** EEPROM code word magic value */
173+
#define RTL_EEPROM_ID_MAGIC 0x8129
174+
169175
/** Word offset of MAC address within EEPROM */
170176
#define RTL_EEPROM_MAC ( 0x0e / 2 )
171177

0 commit comments

Comments
 (0)