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

[cdi-devel] [PATCH v2] 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.

This is the second version of this patch, and fixes the "unused
variable" error that comes up for some environments.

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)
+{
+    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