summaryrefslogtreecommitdiffstats
path: root/src/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'src/drivers')
-rw-r--r--src/drivers/net/ath/ath5k/ath5k_eeprom.c3
-rw-r--r--src/drivers/net/bnxt/bnxt.c68
-rw-r--r--src/drivers/net/bnxt/bnxt.h137
-rw-r--r--src/drivers/net/intel.c12
-rw-r--r--src/drivers/net/rdc.c694
-rw-r--r--src/drivers/net/rdc.h194
-rw-r--r--src/drivers/net/realtek.c10
7 files changed, 977 insertions, 141 deletions
diff --git a/src/drivers/net/ath/ath5k/ath5k_eeprom.c b/src/drivers/net/ath/ath5k/ath5k_eeprom.c
index 12519bc59..46f33d1e8 100644
--- a/src/drivers/net/ath/ath5k/ath5k_eeprom.c
+++ b/src/drivers/net/ath/ath5k/ath5k_eeprom.c
@@ -39,6 +39,9 @@ static int ath5k_hw_eeprom_read(struct ath5k_hw *ah, u32 offset, u16 *data)
{
u32 status, timeout;
+ /* Avoid returning uninitialised data on error */
+ *data = 0xffff;
+
/*
* Initialize EEPROM access
*/
diff --git a/src/drivers/net/bnxt/bnxt.c b/src/drivers/net/bnxt/bnxt.c
index b8663c00a..e3876503f 100644
--- a/src/drivers/net/bnxt/bnxt.c
+++ b/src/drivers/net/bnxt/bnxt.c
@@ -23,16 +23,74 @@ static void bnxt_adv_cq_index ( struct bnxt *bp, u16 cnt );
static int bnxt_rx_complete ( struct net_device *dev, struct rx_pkt_cmpl *rx );
void bnxt_link_evt ( struct bnxt *bp, struct hwrm_async_event_cmpl *evt );
+static struct pci_device_id bnxt_nics[] = {
+ PCI_ROM( 0x14e4, 0x16c0, "14e4-16C0", "14e4-16C0", 0 ),
+ PCI_ROM( 0x14e4, 0x16c1, "14e4-16C1", "14e4-16C1", BNXT_FLAG_PCI_VF ),
+ PCI_ROM( 0x14e4, 0x16c8, "14e4-16C8", "14e4-16C8", 0 ),
+ PCI_ROM( 0x14e4, 0x16c9, "14e4-16C9", "14e4-16C9", 0 ),
+ PCI_ROM( 0x14e4, 0x16ca, "14e4-16CA", "14e4-16CA", 0 ),
+ PCI_ROM( 0x14e4, 0x16cc, "14e4-16CC", "14e4-16CC", 0 ),
+ PCI_ROM( 0x14e4, 0x16cd, "14e4-16CD", "14e4-16CD", 0 ),
+ PCI_ROM( 0x14e4, 0x16ce, "14e4-16CE", "14e4-16CE", 0 ),
+ PCI_ROM( 0x14e4, 0x16cf, "14e4-16CF", "14e4-16CF", 0 ),
+ PCI_ROM( 0x14e4, 0x16d0, "14e4-16D0", "14e4-16D0", 0 ),
+ PCI_ROM( 0x14e4, 0x16d1, "14e4-16D1", "14e4-16D1", 0 ),
+ PCI_ROM( 0x14e4, 0x16d2, "14e4-16D2", "14e4-16D2", 0 ),
+ PCI_ROM( 0x14e4, 0x16d4, "14e4-16D4", "14e4-16D4", 0 ),
+ PCI_ROM( 0x14e4, 0x16d5, "14e4-16D5", "14e4-16D5", 0 ),
+ PCI_ROM( 0x14e4, 0x16d6, "14e4-16D6", "14e4-16D6", 0 ),
+ PCI_ROM( 0x14e4, 0x16d7, "14e4-16D7", "14e4-16D7", 0 ),
+ PCI_ROM( 0x14e4, 0x16d8, "14e4-16D8", "14e4-16D8", 0 ),
+ PCI_ROM( 0x14e4, 0x16d9, "14e4-16D9", "14e4-16D9", 0 ),
+ PCI_ROM( 0x14e4, 0x16da, "14e4-16DA", "14e4-16DA", 0 ),
+ PCI_ROM( 0x14e4, 0x16db, "14e4-16DB", "14e4-16DB", 0 ),
+ PCI_ROM( 0x14e4, 0x16dc, "14e4-16DC", "14e4-16DC", BNXT_FLAG_PCI_VF ),
+ PCI_ROM( 0x14e4, 0x16de, "14e4-16DE", "14e4-16DE", 0 ),
+ PCI_ROM( 0x14e4, 0x16df, "14e4-16DF", "14e4-16DF", 0 ),
+ PCI_ROM( 0x14e4, 0x16e0, "14e4-16E0", "14e4-16E0", 0 ),
+ PCI_ROM( 0x14e4, 0x16e2, "14e4-16E2", "14e4-16E2", 0 ),
+ PCI_ROM( 0x14e4, 0x16e3, "14e4-16E3", "14e4-16E3", 0 ),
+ PCI_ROM( 0x14e4, 0x16e4, "14e4-16E4", "14e4-16E4", 0 ),
+ PCI_ROM( 0x14e4, 0x16e7, "14e4-16E7", "14e4-16E7", 0 ),
+ PCI_ROM( 0x14e4, 0x16e8, "14e4-16E8", "14e4-16E8", 0 ),
+ PCI_ROM( 0x14e4, 0x16e9, "14e4-16E9", "14e4-16E9", 0 ),
+ PCI_ROM( 0x14e4, 0x16ea, "14e4-16EA", "14e4-16EA", 0 ),
+ PCI_ROM( 0x14e4, 0x16eb, "14e4-16EB", "14e4-16EB", 0 ),
+ PCI_ROM( 0x14e4, 0x16ec, "14e4-16EC", "14e4-16EC", 0 ),
+ PCI_ROM( 0x14e4, 0x16ed, "14e4-16ED", "14e4-16ED", 0 ),
+ PCI_ROM( 0x14e4, 0x16ee, "14e4-16EE", "14e4-16EE", 0 ),
+ PCI_ROM( 0x14e4, 0x16ef, "14e4-16EF", "14e4-16EF", 0 ),
+ PCI_ROM( 0x14e4, 0x16f0, "14e4-16F0", "14e4-16F0", 0 ),
+ PCI_ROM( 0x14e4, 0x16f1, "14e4-16F1", "14e4-16F1", 0 ),
+ PCI_ROM( 0x14e4, 0x1604, "14e4-1604", "14e4-1604", 0 ),
+ PCI_ROM( 0x14e4, 0x1605, "14e4-1605", "14e4-1605", 0 ),
+ PCI_ROM( 0x14e4, 0x1606, "14e4-1606", "14e4-1606", 0 ),
+ PCI_ROM( 0x14e4, 0x1609, "14e4-1609", "14e4-1609", 0 ),
+ PCI_ROM( 0x14e4, 0x1614, "14e4-1614", "14e4-1614", 0 ),
+ PCI_ROM( 0x14e4, 0xd802, "14e4-D802", "14e4-D802", 0 ),
+ PCI_ROM( 0x14e4, 0xd804, "14e4-D804", "14e4-D804", 0 ),
+ PCI_ROM( 0x14e4, 0x1750, "14e4-1750", "14e4-1750", 0 ),
+ PCI_ROM( 0x14e4, 0x1802, "14e4-1802", "14e4-1802", 0 ),
+ PCI_ROM( 0x14e4, 0x1805, "14e4-1805", "14e4-1805", 0 ),
+ PCI_ROM( 0x14e4, 0x1751, "14e4-1751", "14e4-1751", 0 ),
+ PCI_ROM( 0x14e4, 0x1801, "14e4-1801", "14e4-1801", 0 ),
+ PCI_ROM( 0x14e4, 0x1804, "14e4-1804", "14e4-1804", 0 ),
+ PCI_ROM( 0x14e4, 0x1752, "14e4-1752", "14e4-1752", 0 ),
+ PCI_ROM( 0x14e4, 0x1800, "14e4-1800", "14e4-1800", 0 ),
+ PCI_ROM( 0x14e4, 0x1803, "14e4-1803", "14e4-1803", 0 ),
+ PCI_ROM( 0x14e4, 0x1806, "14e4-1806", "14e4-1806", BNXT_FLAG_PCI_VF ),
+ PCI_ROM( 0x14e4, 0x1807, "14e4-1807", "14e4-1807", BNXT_FLAG_PCI_VF ),
+ PCI_ROM( 0x14e4, 0x1808, "14e4-1808", "14e4-1808", BNXT_FLAG_PCI_VF ),
+ PCI_ROM( 0x14e4, 0x1809, "14e4-1809", "14e4-1809", BNXT_FLAG_PCI_VF ),
+};
+
/**
* Check if Virtual Function
*/
u8 bnxt_is_pci_vf ( struct pci_device *pdev )
{
- u16 i;
-
- for ( i = 0; i < ARRAY_SIZE ( bnxt_vf_nics ); i++ ) {
- if ( pdev->device == bnxt_vf_nics[i] )
- return 1;
+ if ( FLAG_TEST ( pdev->id->driver_data, BNXT_FLAG_PCI_VF ) ) {
+ return 1;
}
return 0;
}
diff --git a/src/drivers/net/bnxt/bnxt.h b/src/drivers/net/bnxt/bnxt.h
index 4cca07bd3..2cbaec5e5 100644
--- a/src/drivers/net/bnxt/bnxt.h
+++ b/src/drivers/net/bnxt/bnxt.h
@@ -51,6 +51,7 @@ union dma_addr64_t {
#define BNXT_FLAG_MULTI_HOST 0x0008
#define BNXT_FLAG_NPAR_MODE 0x0010
#define BNXT_FLAG_ATOMICS_ENABLE 0x0020
+#define BNXT_FLAG_PCI_VF 0x0040
/*******************************************************************************
* Status codes.
******************************************************************************/
@@ -867,140 +868,4 @@ struct bnxt {
FUNC_VF_CFG_REQ_ENABLES_ASYNC_EVENT_CR | \
FUNC_VF_CFG_REQ_ENABLES_DFLT_MAC_ADDR)
-/* Device ID's */
-#define PCI_VID_BCOM 0x14e4
#define CHIP_NUM_57500 0x1750
-
-#define DID_57508 0x1750
-#define DID_57508_MF 0x1802
-#define DID_57508_MF_RDMA 0x1805
-#define DID_57504 0x1751
-#define DID_57504_MF 0x1801
-#define DID_57504_MF_RDMA 0x1804
-#define DID_57502 0x1752
-#define DID_57502_MF 0x1800
-#define DID_57502_MF_RDMA 0x1803
-#define DID_57508_VF 0x1806
-#define DID_57508_VF_RDMA 0x1807
-#define DID_57508_VF_HV 0x1806
-#define DID_57508_VF_RDMA_HV 0x1807
-/* Stratus Device IDs */
-#define DID_57320_1 0x16F0
-#define DID_57320_2 0x16F1
-#define DID_57454_MHB 0x1604
-#define DID_57454_MHB_RDMA 0x1605
-#define DID_57454_VF_RDMA 0x1606
-#define DID_57454_VF 0x1609
-#define DID_57454 0x1614
-#define DID_58802 0xD802
-#define DID_58804 0xD804
-
-#define DID_57417_RDMA_MF 0x16C0
-#define DID_57417_VF_RDMA 0x16c1
-#define DID_57301 0x16C8
-#define DID_57302 0x16C9
-#define DID_57304 0x16CA
-#define DID_57417_MF 0x16CC
-#define DID_58700 0x16CD
-#define DID_57311 0x16CE
-#define DID_57312 0x16CF
-#define DID_57402 0x16D0
-#define DID_57404 0x16D1
-#define DID_57406 0x16D2
-#define DID_57402_MF 0x16D4
-#define DID_57407C 0x16D5
-#define DID_57412 0x16D6
-#define DID_57414 0x16D7
-#define DID_57416C 0x16D8
-#define DID_57417C 0x16D9
-#define DID_57402L 0x16DA
-#define DID_57404L 0x16DB
-#define DID_57417_VF 0x16dc
-#define DID_57412_MF 0x16DE
-#define DID_57314 0x16DF
-#define DID_57317C 0x16E0
-#define DID_57417F 0x16E2
-#define DID_57416F 0x16E3
-#define DID_57317F 0x16E4
-#define DID_57404_MF 0x16E7
-#define DID_57406_MF 0x16E8
-#define DID_57407F 0x16E9
-#define DID_57407_MF 0x16EA
-#define DID_57412_RDMA_MF 0x16EB
-#define DID_57414_MF 0x16EC
-#define DID_57414_RDMA_MF 0x16ED
-#define DID_57416_MF 0x16EE
-#define DID_57416_RDMA_MF 0x16EF
-
-static struct pci_device_id bnxt_nics[] = {
- PCI_ROM(PCI_VID_BCOM, DID_57417_RDMA_MF, "14e4-16C0", "14e4-16C0", 0),
- PCI_ROM(PCI_VID_BCOM, DID_57417_VF_RDMA, "14e4-16C1", "14e4-16C1", 0),
- PCI_ROM(PCI_VID_BCOM, DID_57301, "14e4-16C8", "14e4-16C8", 0),
- PCI_ROM(PCI_VID_BCOM, DID_57302, "14e4-16C9", "14e4-16C9", 0),
- PCI_ROM(PCI_VID_BCOM, DID_57304, "14e4-16CA", "14e4-16CA", 0),
- PCI_ROM(PCI_VID_BCOM, DID_57417_MF, "14e4-16CC", "14e4-16CC", 0),
- PCI_ROM(PCI_VID_BCOM, DID_58700, "14e4-16CD", "14e4-16CD", 0),
- PCI_ROM(PCI_VID_BCOM, DID_57311, "14e4-16CE", "14e4-16CE", 0),
- PCI_ROM(PCI_VID_BCOM, DID_57312, "14e4-16CF", "14e4-16CF", 0),
- PCI_ROM(PCI_VID_BCOM, DID_57402, "14e4-16D0", "14e4-16D0", 0),
- PCI_ROM(PCI_VID_BCOM, DID_57404, "14e4-16D1", "14e4-16D1", 0),
- PCI_ROM(PCI_VID_BCOM, DID_57406, "14e4-16D2", "14e4-16D2", 0),
- PCI_ROM(PCI_VID_BCOM, DID_57402_MF, "14e4-16D4", "14e4-16D4", 0),
- PCI_ROM(PCI_VID_BCOM, DID_57407C, "14e4-16D5", "14e4-16D5", 0),
- PCI_ROM(PCI_VID_BCOM, DID_57412, "14e4-16D6", "14e4-16D6", 0),
- PCI_ROM(PCI_VID_BCOM, DID_57414, "14e4-16D7", "14e4-16D7", 0),
- PCI_ROM(PCI_VID_BCOM, DID_57416C, "14e4-16D8", "14e4-16D8", 0),
- PCI_ROM(PCI_VID_BCOM, DID_57417C, "14e4-16D9", "14e4-16D9", 0),
- PCI_ROM(PCI_VID_BCOM, DID_57402L, "14e4-16DA", "14e4-16DA", 0),
- PCI_ROM(PCI_VID_BCOM, DID_57404L, "14e4-16DB", "14e4-16DB", 0),
- PCI_ROM(PCI_VID_BCOM, DID_57417_VF, "14e4-16DC", "14e4-16DC", 0),
- PCI_ROM(PCI_VID_BCOM, DID_57412_MF, "14e4-16DE", "14e4-16DE", 0),
- PCI_ROM(PCI_VID_BCOM, DID_57314, "14e4-16DF", "14e4-16DF", 0),
- PCI_ROM(PCI_VID_BCOM, DID_57317C, "14e4-16E0", "14e4-16E0", 0),
- PCI_ROM(PCI_VID_BCOM, DID_57417F, "14e4-16E2", "14e4-16E2", 0),
- PCI_ROM(PCI_VID_BCOM, DID_57416F, "14e4-16E3", "14e4-16E3", 0),
- PCI_ROM(PCI_VID_BCOM, DID_57317F, "14e4-16E4", "14e4-16E4", 0),
- PCI_ROM(PCI_VID_BCOM, DID_57404_MF, "14e4-16E7", "14e4-16E7", 0),
- PCI_ROM(PCI_VID_BCOM, DID_57406_MF, "14e4-16E8", "14e4-16E8", 0),
- PCI_ROM(PCI_VID_BCOM, DID_57407F, "14e4-16E9", "14e4-16E9", 0),
- PCI_ROM(PCI_VID_BCOM, DID_57407_MF, "14e4-16EA", "14e4-16EA", 0),
- PCI_ROM(PCI_VID_BCOM, DID_57412_RDMA_MF, "14e4-16EB", "14e4-16EB", 0),
- PCI_ROM(PCI_VID_BCOM, DID_57414_MF, "14e4-16EC", "14e4-16EC", 0),
- PCI_ROM(PCI_VID_BCOM, DID_57414_RDMA_MF, "14e4-16ED", "14e4-16ED", 0),
- PCI_ROM(PCI_VID_BCOM, DID_57416_MF, "14e4-16EE", "14e4-16EE", 0),
- PCI_ROM(PCI_VID_BCOM, DID_57416_RDMA_MF, "14e4-16EF", "14e4-16EF", 0),
-
- PCI_ROM(PCI_VID_BCOM, DID_57320_1, "14e4-16F0", "14e4-16F0", 0),
- PCI_ROM(PCI_VID_BCOM, DID_57320_2, "14e4-16F1", "14e4-16F1", 0),
- PCI_ROM(PCI_VID_BCOM, DID_57454_MHB, "14e4-1604", "14e4-1604", 0),
- PCI_ROM(PCI_VID_BCOM, DID_57454_MHB_RDMA, "14e4-1605", "14e4-1605", 0),
- PCI_ROM(PCI_VID_BCOM, DID_57454_VF_RDMA, "14e4-1606", "14e4-1606", 0),
- PCI_ROM(PCI_VID_BCOM, DID_57454_VF, "14e4-1609", "14e4-1609", 0),
- PCI_ROM(PCI_VID_BCOM, DID_57454, "14e4-1614", "14e4-1614", 0),
- PCI_ROM(PCI_VID_BCOM, DID_58802, "14e4-D802", "14e4-D802", 0),
- PCI_ROM(PCI_VID_BCOM, DID_58804, "14e4-D804", "14e4-D804", 0),
-
- PCI_ROM(PCI_VID_BCOM, DID_57508, "14e4-1750", "14e4-1750", 0),
- PCI_ROM(PCI_VID_BCOM, DID_57508_MF, "14e4-1802", "14e4-1802", 0),
- PCI_ROM(PCI_VID_BCOM, DID_57508_MF_RDMA, "14e4-1805", "14e4-1805", 0),
- PCI_ROM(PCI_VID_BCOM, DID_57504, "14e4-1751", "14e4-1751", 0),
- PCI_ROM(PCI_VID_BCOM, DID_57504_MF, "14e4-1801", "14e4-1801", 0),
- PCI_ROM(PCI_VID_BCOM, DID_57504_MF_RDMA, "14e4-1804", "14e4-1804", 0),
- PCI_ROM(PCI_VID_BCOM, DID_57502, "14e4-1752", "14e4-1752", 0),
- PCI_ROM(PCI_VID_BCOM, DID_57502_MF, "14e4-1800", "14e4-1800", 0),
- PCI_ROM(PCI_VID_BCOM, DID_57502_MF_RDMA, "14e4-1803", "14e4-1803", 0),
- PCI_ROM(PCI_VID_BCOM, DID_57508_VF, "14e4-1806", "14e4-1806", 0),
- PCI_ROM(PCI_VID_BCOM, DID_57508_VF_RDMA, "14e4-1807", "14e4-1807", 0),
- PCI_ROM(PCI_VID_BCOM, DID_57508_VF_HV, "14e4-1808", "14e4-1808", 0),
- PCI_ROM(PCI_VID_BCOM,
- DID_57508_VF_RDMA_HV, "14e4-1809", "14e4-1809", 0),
-};
-
-static u16 bnxt_vf_nics[] = {
- DID_57508_VF,
- DID_57508_VF_RDMA,
- DID_57508_VF_HV,
- DID_57508_VF_RDMA_HV,
- DID_57417_VF,
- DID_57417_VF_RDMA,
-};
diff --git a/src/drivers/net/intel.c b/src/drivers/net/intel.c
index 83492961f..ea3ebf68d 100644
--- a/src/drivers/net/intel.c
+++ b/src/drivers/net/intel.c
@@ -1025,6 +1025,12 @@ static struct pci_device_id intel_nics[] = {
PCI_ROM ( 0x8086, 0x043a, "dh8900cc-f", "DH8900CC Fiber", 0 ),
PCI_ROM ( 0x8086, 0x043c, "dh8900cc-b", "DH8900CC Backplane", 0 ),
PCI_ROM ( 0x8086, 0x0440, "dh8900cc-s", "DH8900CC SFP", 0 ),
+ PCI_ROM ( 0x8086, 0x0d4c, "i219lm-11", "I219-LM (11)", INTEL_I219 ),
+ PCI_ROM ( 0x8086, 0x0d4d, "i219v-11", "I219-V (11)", INTEL_I219 ),
+ PCI_ROM ( 0x8086, 0x0d4e, "i219lm-10", "I219-LM (10)", INTEL_I219 ),
+ PCI_ROM ( 0x8086, 0x0d4f, "i219v-10", "I219-V (10)", INTEL_I219 ),
+ PCI_ROM ( 0x8086, 0x0d53, "i219lm-12", "I219-LM (12)", INTEL_I219 ),
+ PCI_ROM ( 0x8086, 0x0d55, "i219v-12", "I219-V (12)", INTEL_I219 ),
PCI_ROM ( 0x8086, 0x1000, "82542-f", "82542 (Fiber)", 0 ),
PCI_ROM ( 0x8086, 0x1001, "82543gc-f", "82543GC (Fiber)", 0 ),
PCI_ROM ( 0x8086, 0x1004, "82543gc", "82543GC (Copper)", 0 ),
@@ -1161,6 +1167,12 @@ static struct pci_device_id intel_nics[] = {
PCI_ROM ( 0x8086, 0x15e1, "i219lm-9", "I219-LM (9)", INTEL_I219 ),
PCI_ROM ( 0x8086, 0x15e2, "i219v-9", "I219-V (9)", INTEL_I219 ),
PCI_ROM ( 0x8086, 0x15e3, "i219lm-5", "I219-LM (5)", INTEL_I219 ),
+ PCI_ROM ( 0x8086, 0x15f4, "i219lm-15", "I219-LM (15)", INTEL_I219 ),
+ PCI_ROM ( 0x8086, 0x15f5, "i219v-15", "I219-V (15)", INTEL_I219 ),
+ PCI_ROM ( 0x8086, 0x15f9, "i219lm-14", "I219-LM (14)", INTEL_I219 ),
+ PCI_ROM ( 0x8086, 0x15fa, "i219v-14", "I219-V (14)", INTEL_I219 ),
+ PCI_ROM ( 0x8086, 0x15fb, "i219lm-13", "I219-LM (13)", INTEL_I219 ),
+ PCI_ROM ( 0x8086, 0x15fc, "i219v-13", "I219-V (13)", INTEL_I219 ),
PCI_ROM ( 0x8086, 0x1f41, "i354", "I354", INTEL_NO_ASDE ),
PCI_ROM ( 0x8086, 0x294c, "82566dc-2", "82566DC-2", 0 ),
PCI_ROM ( 0x8086, 0x2e6e, "cemedia", "CE Media Processor", 0 ),
diff --git a/src/drivers/net/rdc.c b/src/drivers/net/rdc.c
new file mode 100644
index 000000000..c3239c002
--- /dev/null
+++ b/src/drivers/net/rdc.c
@@ -0,0 +1,694 @@
+/*
+ * Copyright (C) 2021 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * You can also choose to distribute this program under the terms of
+ * the Unmodified Binary Distribution Licence (as given in the file
+ * COPYING.UBDL), provided that you have satisfied its requirements.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+#include <stdint.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <byteswap.h>
+#include <ipxe/netdevice.h>
+#include <ipxe/ethernet.h>
+#include <ipxe/if_ether.h>
+#include <ipxe/iobuf.h>
+#include <ipxe/malloc.h>
+#include <ipxe/pci.h>
+#include "rdc.h"
+
+/** @file
+ *
+ * RDC R6040 network driver
+ *
+ */
+
+/******************************************************************************
+ *
+ * Device reset
+ *
+ ******************************************************************************
+ */
+
+/**
+ * Reset hardware
+ *
+ * @v rdc RDC device
+ * @ret rc Return status code
+ */
+static int rdc_reset ( struct rdc_nic *rdc ) {
+ unsigned int i;
+
+ /* Reset NIC */
+ writew ( RDC_MCR1_RST, rdc->regs + RDC_MCR1 );
+
+ /* Wait for reset to complete */
+ for ( i = 0 ; i < RDC_RESET_MAX_WAIT_MS ; i++ ) {
+
+ /* Check for reset completion */
+ if ( readw ( rdc->regs + RDC_MCR1 ) & RDC_MCR1_RST ) {
+ mdelay ( 1 );
+ continue;
+ }
+
+ /* Reset internal state machine */
+ writew ( RDC_MACSM_RST, rdc->regs + RDC_MACSM );
+ writew ( 0, rdc->regs + RDC_MACSM );
+ mdelay ( RDC_MACSM_RESET_DELAY_MS );
+
+ return 0;
+ }
+
+ DBGC ( rdc, "RDC %p timed out waiting for reset\n", rdc );
+ return -ETIMEDOUT;
+}
+
+/******************************************************************************
+ *
+ * MII interface
+ *
+ ******************************************************************************
+ */
+
+/**
+ * Read from MII register
+ *
+ * @v mdio MII interface
+ * @v phy PHY address
+ * @v reg Register address
+ * @ret value Data read, or negative error
+ */
+static int rdc_mii_read ( struct mii_interface *mdio, unsigned int phy,
+ unsigned int reg ) {
+ struct rdc_nic *rdc = container_of ( mdio, struct rdc_nic, mdio );
+ uint16_t mmdio;
+ unsigned int i;
+
+ /* Initiate read */
+ mmdio = ( RDC_MMDIO_MIIRD | RDC_MMDIO_PHYAD ( phy ) |
+ RDC_MMDIO_REGAD ( reg ) );
+ writew ( mmdio, rdc->regs + RDC_MMDIO );
+
+ /* Wait for read to complete */
+ for ( i = 0 ; i < RDC_MII_MAX_WAIT_US ; i++ ) {
+
+ /* Check for read completion */
+ if ( readw ( rdc->regs + RDC_MMDIO ) & RDC_MMDIO_MIIRD ) {
+ udelay ( 1 );
+ continue;
+ }
+
+ /* Return register value */
+ return ( readw ( rdc->regs + RDC_MMRD ) );
+ }
+
+ DBGC ( rdc, "RDC %p timed out waiting for MII read\n", rdc );
+ return -ETIMEDOUT;
+}
+
+/**
+ * Write to MII register
+ *
+ * @v mdio MII interface
+ * @v phy PHY address
+ * @v reg Register address
+ * @v data Data to write
+ * @ret rc Return status code
+ */
+static int rdc_mii_write ( struct mii_interface *mdio, unsigned int phy,
+ unsigned int reg, unsigned int data ) {
+ struct rdc_nic *rdc = container_of ( mdio, struct rdc_nic, mdio );
+ uint16_t mmdio;
+ unsigned int i;
+
+ /* Initiate write */
+ mmdio = ( RDC_MMDIO_MIIWR | RDC_MMDIO_PHYAD ( phy ) |
+ RDC_MMDIO_REGAD ( reg ) );
+ writew ( data, rdc->regs + RDC_MMWD );
+ writew ( mmdio, rdc->regs + RDC_MMDIO );
+
+ /* Wait for write to complete */
+ for ( i = 0 ; i < RDC_MII_MAX_WAIT_US ; i++ ) {
+
+ /* Check for write completion */
+ if ( readw ( rdc->regs + RDC_MMDIO ) & RDC_MMDIO_MIIWR ) {
+ udelay ( 1 );
+ continue;
+ }
+
+ return 0;
+ }
+
+ DBGC ( rdc, "RDC %p timed out waiting for MII write\n", rdc );
+ return -ETIMEDOUT;
+}
+
+/** RDC MII operations */
+static struct mii_operations rdc_mii_operations = {
+ .read = rdc_mii_read,
+ .write = rdc_mii_write,
+};
+
+/******************************************************************************
+ *
+ * Link state
+ *
+ ******************************************************************************
+ */
+
+/**
+ * Initialise PHY
+ *
+ * @v rdc RDC device
+ * @ret rc Return status code
+ */
+static int rdc_init_phy ( struct rdc_nic *rdc ) {
+ int rc;
+
+ /* Find PHY address */
+ if ( ( rc = mii_find ( &rdc->mii ) ) != 0 ) {
+ DBGC ( rdc, "RDC %p could not find PHY address: %s\n",
+ rdc, strerror ( rc ) );
+ return rc;
+ }
+
+ /* Reset PHY */
+ if ( ( rc = mii_reset ( &rdc->mii ) ) != 0 ) {
+ DBGC ( rdc, "RDC %p could not reset PHY: %s\n",
+ rdc, strerror ( rc ) );
+ return rc;
+ }
+
+ return 0;
+}
+
+/**
+ * Check link state
+ *
+ * @v netdev Network device
+ * @ret rc Return status code
+ */
+static int rdc_check_link ( struct net_device *netdev ) {
+ struct rdc_nic *rdc = netdev->priv;
+ int rc;
+
+ /* Check link state */
+ if ( ( rc = mii_check_link ( &rdc->mii, netdev ) ) != 0 ) {
+ DBGC ( rdc, "RDC %p could not check link: %s\n",
+ rdc, strerror ( rc ) );
+ return rc;
+ }
+
+ return 0;
+}
+
+/******************************************************************************
+ *
+ * Network device interface
+ *
+ ******************************************************************************
+ */
+
+/**
+ * Create descriptor ring
+ *
+ * @v rdc RDC device
+ * @v ring Descriptor ring
+ * @ret rc Return status code
+ */
+static int rdc_create_ring ( struct rdc_nic *rdc, struct rdc_ring *ring ) {
+ size_t len = ( ring->count * sizeof ( ring->desc[0] ) );
+ struct rdc_descriptor *desc;
+ struct rdc_descriptor *next;
+ physaddr_t start;
+ unsigned int i;
+
+ /* Allocate descriptor ring */
+ ring->desc = dma_alloc ( rdc->dma, &ring->map, len, len );
+ if ( ! ring->desc )
+ return -ENOMEM;
+
+ /* Initialise descriptor ring */
+ memset ( ring->desc, 0, len );
+ for ( i = 0 ; i < ring->count ; i++ ) {
+ desc = &ring->desc[i];
+ next = &ring->desc[ ( i + 1 ) & ( ring->count - 1 ) ];
+ desc->next = cpu_to_le32 ( dma ( &ring->map, next ) );
+ }
+
+ /* Program ring address */
+ start = dma ( &ring->map, ring->desc );
+ writew ( ( start >> 0 ), ( rdc->regs + ring->reg + RDC_MxDSA_LO ) );
+ writew ( ( start >> 16 ), ( rdc->regs + ring->reg + RDC_MxDSA_HI ) );
+
+ DBGC ( rdc, "RDC %p ring %#02x is at [%08lx,%08lx)\n",
+ rdc, ring->reg, virt_to_phys ( ring->desc ),
+ ( virt_to_phys ( ring->desc ) + len ) );
+ return 0;
+}
+
+/**
+ * Destroy descriptor ring
+ *
+ * @v rdc RDC device
+ * @v ring Descriptor ring
+ */
+static void rdc_destroy_ring ( struct rdc_nic *rdc, struct rdc_ring *ring ) {
+ size_t len = ( ring->count * sizeof ( ring->desc[0] ) );
+
+ /* Clear ring address */
+ writew ( 0, ( rdc->regs + ring->reg + RDC_MxDSA_LO ) );
+ writew ( 0, ( rdc->regs + ring->reg + RDC_MxDSA_HI ) );
+
+ /* Free descriptors */
+ dma_free ( &ring->map, ring->desc, len );
+ ring->desc = NULL;
+
+ /* Reset ring */
+ ring->prod = 0;
+ ring->cons = 0;
+}
+
+/**
+ * Refill receive descriptor ring
+ *
+ * @v rdc RDC device
+ */
+static void rdc_refill_rx ( struct rdc_nic *rdc ) {
+ struct rdc_descriptor *rx;
+ struct io_buffer *iobuf;
+ unsigned int rx_idx;
+
+ /* Refill ring */
+ while ( ( rdc->rx.prod - rdc->rx.cons ) < RDC_NUM_RX_DESC ) {
+
+ /* Allocate I/O buffer */
+ iobuf = alloc_rx_iob ( RDC_RX_MAX_LEN, rdc->dma );
+ if ( ! iobuf ) {
+ /* Wait for next refill */
+ break;
+ }
+
+ /* Get next receive descriptor */
+ rx_idx = ( rdc->rx.prod++ % RDC_NUM_RX_DESC );
+ rx = &rdc->rx.desc[rx_idx];
+
+ /* Populate receive descriptor */
+ rx->len = cpu_to_le16 ( RDC_RX_MAX_LEN );
+ rx->addr = cpu_to_le32 ( iob_dma ( iobuf ) );
+ wmb();
+ rx->flags = cpu_to_le16 ( RDC_FL_OWNED );
+
+ /* Record I/O buffer */
+ assert ( rdc->rx_iobuf[rx_idx] == NULL );
+ rdc->rx_iobuf[rx_idx] = iobuf;
+
+ DBGC2 ( rdc, "RDC %p RX %d is [%lx,%lx)\n",
+ rdc, rx_idx, virt_to_phys ( iobuf->data ),
+ ( virt_to_phys ( iobuf->data ) + RDC_RX_MAX_LEN ) );
+ }
+}
+
+/**
+ * Open network device
+ *
+ * @v netdev Network device
+ * @ret rc Return status code
+ */
+static int rdc_open ( struct net_device *netdev ) {
+ struct rdc_nic *rdc = netdev->priv;
+ int rc;
+
+ /* Create transmit descriptor ring */
+ if ( ( rc = rdc_create_ring ( rdc, &rdc->tx ) ) != 0 )
+ goto err_create_tx;
+
+ /* Create receive descriptor ring */
+ if ( ( rc = rdc_create_ring ( rdc, &rdc->rx ) ) != 0 )
+ goto err_create_rx;
+
+ /* Program receive buffer length */
+ writew ( RDC_RX_MAX_LEN, rdc->regs + RDC_MRBSR );
+
+ /* Enable transmit and receive */
+ writew ( ( RDC_MCR0_FD | RDC_MCR0_TXEN | RDC_MCR0_PROMISC |
+ RDC_MCR0_RXEN ),
+ rdc->regs + RDC_MCR0 );
+
+ /* Enable PHY status polling */
+ writew ( ( RDC_MPSCCR_EN | RDC_MPSCCR_PHYAD ( rdc->mii.address ) |
+ RDC_MPSCCR_SLOW ),
+ rdc->regs + RDC_MPSCCR );
+
+ /* Fill receive ring */
+ rdc_refill_rx ( rdc );
+
+ /* Update link state */
+ rdc_check_link ( netdev );
+
+ return 0;
+
+ rdc_destroy_ring ( rdc, &rdc->rx );
+ err_create_rx:
+ rdc_destroy_ring ( rdc, &rdc->tx );
+ err_create_tx:
+ return rc;
+}
+
+/**
+ * Close network device
+ *
+ * @v netdev Network device
+ */
+static void rdc_close ( struct net_device *netdev ) {
+ struct rdc_nic *rdc = netdev->priv;
+ unsigned int i;
+
+ /* Disable NIC */
+ writew ( 0, rdc->regs + RDC_MCR0 );
+
+ /* Destroy receive descriptor ring */
+ rdc_destroy_ring ( rdc, &rdc->rx );
+
+ /* Discard any unused receive buffers */
+ for ( i = 0 ; i < RDC_NUM_RX_DESC ; i++ ) {
+ if ( rdc->rx_iobuf[i] )
+ free_rx_iob ( rdc->rx_iobuf[i] );
+ rdc->rx_iobuf[i] = NULL;
+ }
+
+ /* Destroy transmit descriptor ring */
+ rdc_destroy_ring ( rdc, &rdc->tx );
+}
+
+/**
+ * Transmit packet
+ *
+ * @v netdev Network device
+ * @v iobuf I/O buffer
+ * @ret rc Return status code
+ */
+static int rdc_transmit ( struct net_device *netdev, struct io_buffer *iobuf ) {
+ struct rdc_nic *rdc = netdev->priv;
+ struct rdc_descriptor *tx;
+ unsigned int tx_idx;
+ int rc;
+
+ /* Get next transmit descriptor */
+ if ( ( rdc->tx.prod - rdc->tx.cons ) >= RDC_NUM_TX_DESC ) {
+ DBGC ( rdc, "RDC %p out of transmit descriptors\n", rdc );
+ return -ENOBUFS;
+ }
+ tx_idx = ( rdc->tx.prod % RDC_NUM_TX_DESC );
+ tx = &rdc->tx.desc[tx_idx];
+
+ /* Pad to minimum length */
+ iob_pad ( iobuf, ETH_ZLEN );
+
+ /* Map I/O buffer */
+ if ( ( rc = iob_map_tx ( iobuf, rdc->dma ) ) != 0 )
+ return rc;
+
+ /* Update producer index */
+ rdc->tx.prod++;
+
+ /* Populate transmit descriptor */
+ tx->len = cpu_to_le16 ( iob_len ( iobuf ) );
+ tx->addr = cpu_to_le32 ( iob_dma ( iobuf ) );
+ wmb();
+ tx->flags = cpu_to_le16 ( RDC_FL_OWNED );
+ wmb();
+
+ /* Notify card that there are packets ready to transmit */
+ writew ( RDC_MTPR_TM2TX, rdc->regs + RDC_MTPR );
+
+ return 0;
+}
+
+/**
+ * Poll for completed packets
+ *
+ * @v netdev Network device
+ */
+static void rdc_poll_tx ( struct net_device *netdev ) {
+ struct rdc_nic *rdc = netdev->priv;
+ struct rdc_descriptor *tx;
+ unsigned int tx_idx;
+
+ /* Check for completed packets */
+ while ( rdc->tx.cons != rdc->tx.prod ) {
+
+ /* Get next transmit descriptor */
+ tx_idx = ( rdc->tx.cons % RDC_NUM_TX_DESC );
+ tx = &rdc->tx.desc[tx_idx];
+
+ /* Stop if descriptor is still in use */
+ if ( tx->flags & cpu_to_le16 ( RDC_FL_OWNED ) )
+ return;
+ DBGC2 ( rdc, "RDC %p TX %d complete\n", rdc, tx_idx );
+
+ /* Complete transmit descriptor */
+ rdc->tx.cons++;
+ netdev_tx_complete_next ( netdev );
+ }
+}
+
+/**
+ * Poll for received packets
+ *
+ * @v netdev Network device
+ */
+static void rdc_poll_rx ( struct net_device *netdev ) {
+ struct rdc_nic *rdc = netdev->priv;
+ struct rdc_descriptor *rx;
+ struct io_buffer *iobuf;
+ unsigned int rx_idx;
+ size_t len;
+
+ /* Check for received packets */
+ while ( rdc->rx.cons != rdc->rx.prod ) {
+
+ /* Get next receive descriptor */
+ rx_idx = ( rdc->rx.cons % RDC_NUM_RX_DESC );
+ rx = &rdc->rx.desc[rx_idx];
+
+ /* Stop if descriptor is still in use */
+ if ( rx->flags & cpu_to_le16 ( RDC_FL_OWNED ) )
+ return;
+
+ /* Populate I/O buffer */
+ iobuf = rdc->rx_iobuf[rx_idx];
+ rdc->rx_iobuf[rx_idx] = NULL;
+ len = le16_to_cpu ( rx->len );
+ iob_put ( iobuf, len );
+ iob_unput ( iobuf, 4 /* strip CRC */ );
+
+ /* Hand off to network stack */
+ if ( rx->flags & cpu_to_le16 ( RDC_FL_OK ) ) {
+ DBGC2 ( rdc, "RDC %p RX %d complete (length %zd)\n",
+ rdc, rx_idx, len );
+ netdev_rx ( netdev, iobuf );
+ } else {
+ DBGC2 ( rdc, "RDC %p RX %d error (length %zd, "
+ "flags %#04x)\n", rdc, rx_idx, len,
+ le16_to_cpu ( rx->flags ) );
+ netdev_rx_err ( netdev, iobuf, -EIO );
+ }
+ rdc->rx.cons++;
+ }
+}
+
+/**
+ * Poll for completed and received packets
+ *
+ * @v netdev Network device
+ */
+static void rdc_poll ( struct net_device *netdev ) {
+ struct rdc_nic *rdc = netdev->priv;
+ uint16_t misr;
+
+ /* Check for (and acknowledge) interrupts */
+ misr = readw ( rdc->regs + RDC_MISR );
+
+ /* Poll for TX completions, if applicable */
+ if ( misr & RDC_MIRQ_TX )
+ rdc_poll_tx ( netdev );
+
+ /* Poll for RX completions, if applicable */
+ if ( misr & RDC_MIRQ_RX )
+ rdc_poll_rx ( netdev );
+
+ /* Check link state, if applicable */
+ if ( misr & RDC_MIRQ_LINK )
+ rdc_check_link ( netdev );
+
+ /* Check for unexpected interrupts */
+ if ( misr & ~( RDC_MIRQ_LINK | RDC_MIRQ_TX | RDC_MIRQ_RX_EARLY |
+ RDC_MIRQ_RX_EMPTY | RDC_MIRQ_RX ) ) {
+ DBGC ( rdc, "RDC %p unexpected MISR %#04x\n", rdc, misr );
+ /* Report as a TX error */
+ netdev_tx_err ( netdev, NULL, -ENOTSUP );
+ }
+
+ /* Refill receive ring */
+ rdc_refill_rx ( rdc );
+}
+
+/**
+ * Enable or disable interrupts
+ *
+ * @v netdev Network device
+ * @v enable Interrupts should be enabled
+ */
+static void rdc_irq ( struct net_device *netdev, int enable ) {
+ struct rdc_nic *rdc = netdev->priv;
+ uint16_t mier;
+
+ /* Enable/disable interrupts */
+ mier = ( enable ? ( RDC_MIRQ_LINK | RDC_MIRQ_TX | RDC_MIRQ_RX ) : 0 );
+ writew ( mier, rdc->regs + RDC_MIER );
+}
+
+/** RDC network device operations */
+static struct net_device_operations rdc_operations = {
+ .open = rdc_open,
+ .close = rdc_close,
+ .transmit = rdc_transmit,
+ .poll = rdc_poll,
+ .irq = rdc_irq,
+};
+
+/******************************************************************************
+ *
+ * PCI interface
+ *
+ ******************************************************************************
+ */
+
+/**
+ * Probe PCI device
+ *
+ * @v pci PCI device
+ * @ret rc Return status code
+ */
+static int rdc_probe ( struct pci_device *pci ) {
+ struct net_device *netdev;
+ struct rdc_nic *rdc;
+ union rdc_mac mac;
+ int rc;
+
+ /* Allocate and initialise net device */
+ netdev = alloc_etherdev ( sizeof ( *rdc ) );
+ if ( ! netdev ) {
+ rc = -ENOMEM;
+ goto err_alloc;
+ }
+ netdev_init ( netdev, &rdc_operations );
+ rdc = netdev->priv;
+ pci_set_drvdata ( pci, netdev );
+ netdev->dev = &pci->dev;
+ memset ( rdc, 0, sizeof ( *rdc ) );
+ rdc->dma = &pci->dma;
+ mdio_init ( &rdc->mdio, &rdc_mii_operations );
+ mii_init ( &rdc->mii, &rdc->mdio, 0 );
+ rdc_init_ring ( &rdc->tx, RDC_NUM_TX_DESC, RDC_MTDSA );
+ rdc_init_ring ( &rdc->rx, RDC_NUM_RX_DESC, RDC_MRDSA );
+
+ /* Fix up PCI device */
+ adjust_pci_device ( pci );
+
+ /* Map registers */
+ rdc->regs = pci_ioremap ( pci, pci->membase, RDC_BAR_SIZE );
+ if ( ! rdc->regs ) {
+ rc = -ENODEV;
+ goto err_ioremap;
+ }
+
+ /* Fetch MAC address */
+ mac.mid[0] = cpu_to_le16 ( readw ( rdc->regs + RDC_MID0 ) );
+ mac.mid[1] = cpu_to_le16 ( readw ( rdc->regs + RDC_MID1 ) );
+ mac.mid[2] = cpu_to_le16 ( readw ( rdc->regs + RDC_MID2 ) );
+ memcpy ( netdev->hw_addr, mac.raw, ETH_ALEN );
+
+ /* Reset the NIC */
+ if ( ( rc = rdc_reset ( rdc ) ) != 0 )
+ goto err_reset;
+
+ /* Initialise PHY */
+ if ( ( rc = rdc_init_phy ( rdc ) ) != 0 )
+ goto err_init_phy;
+
+ /* Register network device */
+ if ( ( rc = register_netdev ( netdev ) ) != 0 )
+ goto err_register_netdev;
+
+ /* Set initial link state */
+ rdc_check_link ( netdev );
+
+ return 0;
+
+ unregister_netdev ( netdev );
+ err_register_netdev:
+ err_init_phy:
+ rdc_reset ( rdc );
+ err_reset:
+ iounmap ( rdc->regs );
+ err_ioremap:
+ netdev_nullify ( netdev );
+ netdev_put ( netdev );
+ err_alloc:
+ return rc;
+}
+
+/**
+ * Remove PCI device
+ *
+ * @v pci PCI device
+ */
+static void rdc_remove ( struct pci_device *pci ) {
+ struct net_device *netdev = pci_get_drvdata ( pci );
+ struct rdc_nic *rdc = netdev->priv;
+
+ /* Unregister network device */
+ unregister_netdev ( netdev );
+
+ /* Reset card */
+ rdc_reset ( rdc );
+
+ /* Free network device */
+ iounmap ( rdc->regs );
+ netdev_nullify ( netdev );
+ netdev_put ( netdev );
+}
+
+/** RDC PCI device IDs */
+static struct pci_device_id rdc_nics[] = {
+ PCI_ROM ( 0x17f3, 0x6040, "r6040", "RDC R6040", 0 ),
+};
+
+/** RDC PCI driver */
+struct pci_driver rdc_driver __pci_driver = {
+ .ids = rdc_nics,
+ .id_count = ( sizeof ( rdc_nics ) / sizeof ( rdc_nics[0] ) ),
+ .probe = rdc_probe,
+ .remove = rdc_remove,
+};
diff --git a/src/drivers/net/rdc.h b/src/drivers/net/rdc.h
new file mode 100644
index 000000000..ee1671fdd
--- /dev/null
+++ b/src/drivers/net/rdc.h
@@ -0,0 +1,194 @@
+#ifndef _RDC_H
+#define _RDC_H
+
+/** @file
+ *
+ * RDC R6040 network driver
+ *
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+#include <stdint.h>
+#include <ipxe/if_ether.h>
+#include <ipxe/mii.h>
+
+/** RDC BAR size */
+#define RDC_BAR_SIZE 256
+
+/** An RDC descriptor */
+struct rdc_descriptor {
+ /** Flags */
+ uint16_t flags;
+ /** Length */
+ uint16_t len;
+ /** Address */
+ uint32_t addr;
+ /** Next descriptor */
+ uint32_t next;
+ /** Reserved */
+ uint32_t reserved;
+} __attribute__ (( packed ));
+
+/** Descriptor is owned by NIC */
+#define RDC_FL_OWNED 0x8000
+
+/** Packet OK */
+#define RDC_FL_OK 0x4000
+
+/** MAC control register 0 */
+#define RDC_MCR0 0x00
+#define RDC_MCR0_FD 0x8000 /**< Full duplex */
+#define RDC_MCR0_TXEN 0x1000 /**< Transmit enable */
+#define RDC_MCR0_PROMISC 0x0020 /**< Promiscuous mode */
+#define RDC_MCR0_RXEN 0x0002 /**< Receive enable */
+
+/** MAC control register 1 */
+#define RDC_MCR1 0x04
+#define RDC_MCR1_RST 0x0001 /**< MAC reset */
+
+/** Maximum time to wait for reset */
+#define RDC_RESET_MAX_WAIT_MS 10
+
+/** MAC transmit poll command register */
+#define RDC_MTPR 0x14
+#define RDC_MTPR_TM2TX 0x0001 /**< Trigger MAC to transmit */
+
+/** MAC receive buffer size register */
+#define RDC_MRBSR 0x18
+
+/** MAC MDIO control register */
+#define RDC_MMDIO 0x20
+#define RDC_MMDIO_MIIWR 0x4000 /**< MDIO write */
+#define RDC_MMDIO_MIIRD 0x2000 /**< MDIO read */
+#define RDC_MMDIO_PHYAD(x) ( (x) << 8 ) /**< PHY address */
+#define RDC_MMDIO_REGAD(x) ( (x) << 0 ) /**< Register address */
+
+/** Maximum time to wait for an MII read or write */
+#define RDC_MII_MAX_WAIT_US 2048
+
+/** MAC MDIO read data register */
+#define RDC_MMRD 0x24
+
+/** MAC MDIO write data register */
+#define RDC_MMWD 0x28
+
+/** MAC transmit descriptor start address */
+#define RDC_MTDSA 0x2c
+
+/** MAC receive descriptor start address */
+#define RDC_MRDSA 0x34
+
+/** MAC descriptor start address low half */
+#define RDC_MxDSA_LO 0x0
+
+/** MAC descriptor start address low half */
+#define RDC_MxDSA_HI 0x4
+
+/** MAC interrupt status register */
+#define RDC_MISR 0x3c
+#define RDC_MIRQ_LINK 0x0200 /**< Link status changed */
+#define RDC_MIRQ_TX 0x0010 /**< Transmit complete */
+#define RDC_MIRQ_RX_EARLY 0x0008 /**< Receive early interrupt */
+#define RDC_MIRQ_RX_EMPTY 0x0002 /**< Receive descriptor unavailable */
+#define RDC_MIRQ_RX 0x0001 /**< Receive complete */
+
+/** MAC interrupt enable register */
+#define RDC_MIER 0x40
+
+/** MAC address word 0 */
+#define RDC_MID0 0x68
+
+/** MAC address word 1 */
+#define RDC_MID1 0x6a
+
+/** MAC address word 2 */
+#define RDC_MID2 0x6c
+
+/** MAC PHY status change configuration register */
+#define RDC_MPSCCR 0x88
+#define RDC_MPSCCR_EN 0x8000 /**< PHY status change enable */
+#define RDC_MPSCCR_PHYAD(x) ( (x) << 8 ) /**< PHY address */
+#define RDC_MPSCCR_SLOW 0x0007 /**< Poll slowly */
+
+/** MAC state machine register */
+#define RDC_MACSM 0xac
+#define RDC_MACSM_RST 0x0002 /**< Reset state machine */
+
+/** Time to wait after resetting MAC state machine */
+#define RDC_MACSM_RESET_DELAY_MS 10
+
+/** A MAC address */
+union rdc_mac {
+ /** Raw bytes */
+ uint8_t raw[ETH_ALEN];
+ /** MIDx registers */
+ uint16_t mid[ ETH_ALEN / 2 ];
+};
+
+/** A descriptor ring */
+struct rdc_ring {
+ /** Descriptors */
+ struct rdc_descriptor *desc;
+ /** Descriptor ring DMA mapping */
+ struct dma_mapping map;
+ /** Producer index */
+ unsigned int prod;
+ /** Consumer index */
+ unsigned int cons;
+
+ /** Number of descriptors */
+ unsigned int count;
+ /** Start address register 0 */
+ unsigned int reg;
+};
+
+/**
+ * Initialise descriptor ring
+ *
+ * @v ring Descriptor ring
+ * @v count Number of descriptors
+ * @v reg Start address register 0
+ */
+static inline __attribute__ (( always_inline )) void
+rdc_init_ring ( struct rdc_ring *ring, unsigned int count, unsigned int reg ) {
+
+ ring->count = count;
+ ring->reg = reg;
+}
+
+/** Number of transmit descriptors
+ *
+ * This is a policy decision.
+ */
+#define RDC_NUM_TX_DESC 16
+
+/** Number of receive descriptors
+ *
+ * This is a policy decision.
+ */
+#define RDC_NUM_RX_DESC 8
+
+/** Receive buffer length */
+#define RDC_RX_MAX_LEN ( ETH_FRAME_LEN + 4 /* VLAN */ + 4 /* CRC */ )
+
+/** An RDC network card */
+struct rdc_nic {
+ /** Registers */
+ void *regs;
+ /** DMA device */
+ struct dma_device *dma;
+ /** MII interface */
+ struct mii_interface mdio;
+ /** MII device */
+ struct mii_device mii;
+
+ /** Transmit descriptor ring */
+ struct rdc_ring tx;
+ /** Receive descriptor ring */
+ struct rdc_ring rx;
+ /** Receive I/O buffers */
+ struct io_buffer *rx_iobuf[RDC_NUM_RX_DESC];
+};
+
+#endif /* _RDC_H */
diff --git a/src/drivers/net/realtek.c b/src/drivers/net/realtek.c
index 0af3416d5..a43efb68b 100644
--- a/src/drivers/net/realtek.c
+++ b/src/drivers/net/realtek.c
@@ -420,6 +420,16 @@ static int realtek_phy_reset ( struct realtek_nic *rtl ) {
*/
}
+ /* Some cards (e.g. RTL8211B) have a hardware errata that
+ * requires the MII_MMD_DATA register to be cleared before the
+ * link will come up.
+ */
+ if ( ( rc = mii_write ( &rtl->mii, MII_MMD_DATA, 0 ) ) != 0 ) {
+ /* Ignore failures, since the register may not be
+ * present on all PHYs.
+ */
+ }
+
/* Restart autonegotiation */
if ( ( rc = mii_restart ( &rtl->mii ) ) != 0 ) {
DBGC ( rtl, "REALTEK %p could not restart MII: %s\n",