[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[cdi-devel] [PATCH] Make e1000 work in VirtualBox



Hi,

This patch makes the e1000 driver work in VirtualBox. I'm working towards
getting the driver working in VMWare as well, but VMWare emulates a
different chip (82545 rather than 82540). Those fixes and adjustments
will come in a different patch, at a later date.

The biggest change here is that the driver now supports the microwire
interface for reading from the EEPROM as well as the EERD.

Cheers,

Matt

Signed-off-by: Matthew Iselin <matthew@xxxxxxxxxxxxxx>
---
 e1000/device.c    |  224 +++++++++++++++++++++++++++++++++++++++++++++++++----
 e1000/device.h    |   22 +++++-
 3 files changed, 232 insertions(+), 18 deletions(-)

diff --git a/e1000/device.c b/e1000/device.c
index 1b31987..f1caa1f 100644
--- a/e1000/device.c
+++ b/e1000/device.c
@@ -48,14 +48,210 @@
 static void e1000_handle_interrupt(struct cdi_device* device);
 static uint64_t get_mac_address(struct e1000_device* device);
 
-static uint16_t e1000_eeprom_read(struct e1000_device* device, uint16_t offset)
+static uint32_t e1000_reg_read(struct e1000_device *device, uint16_t offset)
+{
+    return reg_inl(device, offset);
+}
+
+static void e1000_reg_write(struct e1000_device *device, uint16_t offset, uint32_t value)
+{
+    reg_outl(device, offset, value);
+}
+
+static void e1000_write_flush(struct e1000_device *device)
+{
+    uint32_t temp = e1000_reg_read(device, REG_STATUS);
+}
+
+static void e1000_raise_eeprom_clock(struct e1000_device *device, uint32_t *eecd)
+{
+    *eecd |= E1000_EECD_SK;
+    e1000_reg_write(device, REG_EECD, *eecd);
+    e1000_write_flush(device);
+    cdi_sleep_ms(5);
+}
+
+static void e1000_lower_eeprom_clock(struct e1000_device *device, uint32_t *eecd)
+{
+    *eecd &= ~E1000_EECD_SK;
+    e1000_reg_write(device, REG_EECD, *eecd);
+    e1000_write_flush(device);
+    cdi_sleep_ms(5);
+}
+
+static void e1000_write_eeprom_bits(struct e1000_device *device, uint16_t data, uint16_t bitcount)
+{
+    uint32_t eecd, mask;
+    
+    mask = 0x1 << (bitcount - 1);
+    eecd = e1000_reg_read(device, REG_EECD);
+    eecd &= ~(E1000_EECD_DO | E1000_EECD_DI);
+    do
+    {
+        eecd &= ~E1000_EECD_DI;
+        if(data & mask)
+            eecd |= E1000_EECD_DI;
+        e1000_reg_write(device, REG_EECD, eecd);
+        e1000_write_flush(device);
+        
+        cdi_sleep_ms(5);
+        
+        e1000_raise_eeprom_clock(device, &eecd);
+        e1000_lower_eeprom_clock(device, &eecd);
+        
+        mask >>= 1;
+    }
+    while(mask);
+    
+    eecd &= ~E1000_EECD_DI;
+    e1000_reg_write(device, REG_EECD, eecd);
+}
+
+static uint16_t e1000_read_eeprom_bits(struct e1000_device *device)
+{
+    uint32_t eecd, i;
+    uint16_t data;
+    
+    eecd = e1000_reg_read(device, REG_EECD);
+    eecd &= ~(E1000_EECD_DO | E1000_EECD_DI);
+    data = 0;
+    
+    for(i = 0; i < 16; i++)
+    {
+        data <<= 1;
+        e1000_raise_eeprom_clock(device, &eecd);
+        
+        eecd = e1000_reg_read(device, REG_EECD);
+        
+        eecd &= ~E1000_EECD_DI;
+        if(eecd & E1000_EECD_DO)
+            data |= 1;
+        
+        e1000_lower_eeprom_clock(device, &eecd);
+    }
+    
+    return data;
+}
+
+static void e1000_prep_eeprom(struct e1000_device *device)
 {
-    uint32_t eerd;
+    uint32_t eecd = e1000_reg_read(device, REG_EECD);
+    
+    eecd &= ~(E1000_EECD_SK | E1000_EECD_DI);
+    e1000_reg_write(device, REG_EECD, eecd);
+    
+    eecd |= E1000_EECD_CS;
+    e1000_reg_write(device, REG_EECD, eecd);
+}
+
+static void e1000_standby_eeprom(struct e1000_device *device)
+{
+    uint32_t eecd = e1000_reg_read(device, REG_EECD);
+    
+    eecd &= ~(E1000_EECD_CS | E1000_EECD_SK);
+    e1000_reg_write(device, REG_EECD, eecd);
+    e1000_write_flush(device);
+    cdi_sleep_ms(5);
+    
+    eecd |= E1000_EECD_SK;
+    e1000_reg_write(device, REG_EECD, eecd);
+    e1000_write_flush(device);
+    cdi_sleep_ms(5);
+    
+    eecd |= E1000_EECD_CS;
+    e1000_reg_write(device, REG_EECD, eecd);
+    e1000_write_flush(device);
+    cdi_sleep_ms(5);
+    
+    eecd &= ~(E1000_EECD_SK);
+    e1000_reg_write(device, REG_EECD, eecd);
+    e1000_write_flush(device);
+    cdi_sleep_ms(5);
+}
+
+static uint32_t e1000_read_uwire(struct e1000_device *device, uint16_t offset)
+{
+    uint32_t eecd, i = 0;
+    int large_eeprom = 0;
+    
+    // TODO: check for post-82544 chip, only run this handling if so
+    eecd = e1000_reg_read(device, REG_EECD);
+    if(eecd & E1000_EECD_SIZE)
+        large_eeprom = 1;
+    eecd |= E1000_EECD_REQ;
+    e1000_reg_write(device, REG_EECD, eecd);
+    eecd = e1000_reg_read(device, REG_EECD);
+    while((!(eecd & E1000_EECD_GNT)) && (i++ < 100))
+    {
+        cdi_sleep_ms(1);
+        eecd = e1000_reg_read(device, REG_EECD);
+    }
+    if(!(eecd & E1000_EECD_GNT))
+    {
+        eecd &= ~E1000_EECD_REQ;
+        e1000_reg_write(device, REG_EECD, eecd);
+        return (uint32_t) -1;
+    }
+    
+    e1000_prep_eeprom(device);
+    
+    e1000_write_eeprom_bits(device, EEPROM_READ_OPCODE, 3);
+    if(large_eeprom)
+        e1000_write_eeprom_bits(device, offset, 8);
+    else
+        e1000_write_eeprom_bits(device, offset, 6);
+    
+    uint32_t data = e1000_read_eeprom_bits(device);
+    
+    e1000_standby_eeprom(device);
+    
+    // TODO: check for post-82544 chip
+    eecd = e1000_reg_read(device, REG_EECD);
+    eecd &= ~E1000_EECD_REQ;
+    e1000_reg_write(device, REG_EECD, eecd);
+    
+    return data;
+}
+
+static uint32_t e1000_read_eerd(struct e1000_device *device, uint16_t offset)
+{
+    uint32_t eerd, i;
 
     reg_outl(device, REG_EEPROM_READ, (offset << 8) | EERD_START);
-    while (((eerd = reg_inl(device, REG_EEPROM_READ)) & EERD_DONE) == 0);
-    printf("eerd = %8x\n", eerd);
-    return (eerd >> 16);
+    for(i = 0; i < 100; i++)
+    {
+        eerd = reg_inl(device, REG_EEPROM_READ);
+        if(eerd & EERD_DONE)
+            break;
+        cdi_sleep_ms(1);
+    }
+
+    if(eerd & EERD_DONE)
+        return (eerd >> 16) & 0xFFFF;
+    else
+        return (uint32_t) -1;
+}
+
+static uint16_t e1000_eeprom_read(struct e1000_device* device, uint16_t offset)
+{
+    static int eerd_safe = 1;
+    
+    uint32_t data = 0;
+    if(eerd_safe)
+        data = e1000_read_eerd(device, offset);
+    if(!eerd_safe || (data == ((uint32_t) -1)))
+    {
+        eerd_safe = 0;
+        
+        data = e1000_read_uwire(device, offset);
+        if(data == ((uint32_t) -1))
+        {
+            printf("e1000: couldn't read eeprom via EERD or uwire!");
+            return 0;
+        }
+    }
+    
+    return (uint16_t) (data & 0xFFFF);
 }
 
 static void reset_nic(struct e1000_device* netcard)
@@ -106,6 +302,9 @@ static void reset_nic(struct e1000_device* netcard)
     reg_outl(netcard, REG_RECV_ADDR_LIST + 4,
         ((mac >> 32) & 0xFFFF) | RAH_VALID);
 
+    netcard->net.mac = mac;
+    printf("e1000: MAC-Adresse: %012llx\n", (uint64_t) netcard->net.mac);
+
     // Rx-Deskriptoren aufsetzen
     for (i = 0; i < RX_BUFFER_NUM; i++) {
         netcard->rx_desc[i].length = RX_BUFFER_SIZE;
@@ -123,10 +322,6 @@ static void reset_nic(struct e1000_device* netcard)
     netcard->tx_cur_buffer = 0;
     netcard->rx_cur_buffer = 0;
 
-    // Interrupts aktivieren
-    reg_outl(netcard, REG_INTR_MASK_CLR, 0xFFFF);
-    reg_outl(netcard, REG_INTR_MASK, 0xFFFF);
-
     // Rx/Tx aktivieren
     reg_outl(netcard, REG_RX_CTL, RCTL_ENABLE | RCTL_BROADCAST);
     reg_outl(netcard, REG_TX_CTL, TCTL_ENABLE | TCTL_PADDING
@@ -153,7 +348,7 @@ struct cdi_device* e1000_init_device(struct cdi_bus_data* bus_data)
     struct e1000_device* netcard;
     struct cdi_mem_area* buf;
 
-    if (!((pci->vendor_id == 0x8086) && (pci->device_id == 0x100e))) {
+    if (!((pci->vendor_id == 0x8086) && ((pci->device_id == 0x100e) || (pci->device_id == 0x100f)))) {
         return NULL;
     }
 
@@ -191,11 +386,12 @@ struct cdi_device* e1000_init_device(struct cdi_bus_data* bus_data)
     printf("e1000: Fuehre Reset der Karte durch\n");
     reset_nic(netcard);
 
-    netcard->net.mac = get_mac_address(netcard);
-    printf("e1000: MAC-Adresse: %012llx\n", (uint64_t) netcard->net.mac);
-
     cdi_net_device_init(&netcard->net);
 
+    // Interrupts aktivieren
+    reg_outl(netcard, REG_INTR_MASK_CLR, 0xFFFF);
+    reg_outl(netcard, REG_INTR_MASK, 0xFFFF);
+
     return &netcard->net.dev;
 }
 
diff --git a/e1000/device.h b/e1000/device.h
index 2a10365..428edad 100644
--- a/e1000/device.h
+++ b/e1000/device.h
@@ -26,8 +26,8 @@
  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-#ifndef _SIS900_DEVICES_H_
-#define _SIS900_DEVICES_H_
+#ifndef _E1000_DEVICE_H_
+#define _E1000_DEVICE_H_
 
 #include <stdint.h>
 
@@ -38,6 +38,8 @@
 
 enum {
     REG_CTL             =    0x0,
+    REG_STATUS          =    0x8,
+    REG_EECD            =   0x10, /* EEPROM Control */
     REG_EEPROM_READ     =   0x14, /* EERD */
     REG_VET             =   0x38, /* VLAN */
 
@@ -104,6 +106,22 @@ enum {
 #define EERD_START  (1 <<  0)
 #define EERD_DONE   (1 <<  4)
 
+/* EEPROM/Flash Control */
+#define E1000_EECD_SK        0x00000001 /* EEPROM Clock */
+#define E1000_EECD_CS        0x00000002 /* EEPROM Chip Select */
+#define E1000_EECD_DI        0x00000004 /* EEPROM Data In */
+#define E1000_EECD_DO        0x00000008 /* EEPROM Data Out */
+#define E1000_EECD_FWE_MASK  0x00000030 
+#define E1000_EECD_FWE_DIS   0x00000010 /* Disable FLASH writes */
+#define E1000_EECD_FWE_EN    0x00000020 /* Enable FLASH writes */
+#define E1000_EECD_FWE_SHIFT 4
+#define E1000_EECD_SIZE      0x00000200 /* EEPROM Size (0=64 word 1=256 word) */
+#define E1000_EECD_REQ       0x00000040 /* EEPROM Access Request */
+#define E1000_EECD_GNT       0x00000080 /* EEPROM Access Grant */
+#define E1000_EECD_PRES      0x00000100 /* EEPROM Present */
+
+#define EEPROM_READ_OPCODE  0x6
+
 /* Allgemeine Definitionen */
 
 #define TX_BUFFER_SIZE  2048
-- 
1.6.4.msysgit.0