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

[cdi-devel] [PATCH 6/7] e1000: Implement support for 8086:10f5 e1000e chip



This adds support for the e1000e in my Thinkpad T500.

The main difference between the already supported e1000 cards and the
8086:10f5 is that the EEPROM can't be accessed using EERD, but must use
the flash interface. Apart from that, only the settings in TCTL differ.

Signed-off-by: Kevin Wolf <kevin@xxxxxxxxxx>
---
 e1000/device.c   | 79 +++++++++++++++++++++++++++++++++++++++++++++++++++++++-
 e1000/device.h   | 32 +++++++++++++++++++++++
 e1000/e1000_io.h | 29 +++++++++++++++++++++
 3 files changed, 139 insertions(+), 1 deletion(-)

diff --git a/e1000/device.c b/e1000/device.c
index 6356721..d9e65c5 100644
--- a/e1000/device.c
+++ b/e1000/device.c
@@ -247,6 +247,63 @@ static uint32_t e1000e_read_eerd(struct e1000_device *device, uint16_t offset)
 }
 #endif
 
+static uint32_t e1000e_read_flash(struct e1000_device *device, uint16_t offset)
+{
+    uint16_t hsfsts;
+    uint32_t linear_address;
+
+    /* Convert from words to bytes */
+    offset *= 2;
+
+    if (device->flash_base == NULL) {
+        printf("e1000: Cannot read from flash without flash BAR\n");
+        return (uint32_t) -1;
+    }
+
+    if (offset + 2 > device->flash_size) {
+        printf("e1000: Cannot read from flash offset %d (size %d)\n",
+               offset, device->flash_size);
+        return (uint32_t) -1;
+    }
+
+    /* Check that hardware sequencing is supported */
+    hsfsts = flash_reg_inw(device, FLASH_REG_HSFSTS);
+    if ((hsfsts & HSFSTS_FDV) == 0) {
+        /* TODO Implement access using Software Sequencing registers */
+        printf("e1000: Cannot read from flash without hardware sequencing\n");
+        return (uint32_t) -1;
+    }
+
+    /* Check that no SPI cycle is in progress */
+    if (hsfsts & HSFSTS_SCIP) {
+        printf("e1000: Cannot read from flash while SPI cycle in progress\n");
+        return (uint32_t) -1;
+    }
+
+    /* Start a new cycle */
+    linear_address = device->flash_base_offset + offset;
+    flash_reg_outl(device, FLASH_REG_FADDR, linear_address);
+
+    flash_reg_outw(device, FLASH_REG_HSFSTS,
+                   HSFSTS_FDONE | HSFSTS_FCERR | HSFSTS_AEL);
+    flash_reg_outw(device, FLASH_REG_HSFCTL,
+                   HSFCTL_FGO | HSFCTL_FCYCLE_READ | HSFCTL_FDBC_WORD);
+
+    CDI_CONDITION_WAIT_SLEEP(
+        flash_reg_inw(device, FLASH_REG_HSFSTS) & HSFSTS_FDONE, 100, 1);
+
+    hsfsts = flash_reg_inw(device, FLASH_REG_HSFSTS);
+    if ((hsfsts & (HSFSTS_FDONE | HSFSTS_FCERR)) == HSFSTS_FDONE) {
+        return flash_reg_inl(device, FLASH_REG_FDATA0);
+    } else if ((hsfsts & HSFSTS_FDONE) == 0){
+        printf("e1000: Timeout while reading from flash\n");
+    } else {
+        printf("e1000: Error while reading from flash\n");
+    }
+
+    return (uint32_t) -1;
+}
+
 static uint16_t e1000_eeprom_read(struct e1000_device* device, uint16_t offset)
 {
     static int eerd_safe = 1;
@@ -287,6 +344,18 @@ static void reset_nic(struct e1000_device* netcard)
     cdi_sleep_ms(10);
     while (reg_inl(netcard, REG_CTL) & CTL_RESET);
 
+    // Flash-Basisadresse
+    if (netcard->flash_base) {
+        uint32_t glfpr = flash_reg_inl(netcard, FLASH_REG_GLFPR);
+        uint32_t base = (glfpr & 0x1fff) << 12;
+        uint32_t limit = ((glfpr >> 16) & 0x1fff) << 12;
+
+        netcard->flash_base_offset = base;
+        netcard->flash_size = limit - base;
+
+        printf("e1000: Flash base offset %x; size %x\n", base, limit - base);
+    }
+
     // Kontrollregister initialisieren
     reg_outl(netcard, REG_CTL, CTL_AUTO_SPEED | CTL_LINK_UP);
 
@@ -377,6 +446,11 @@ static struct e1000_model models[] = {
         .device_id      = 0x100f,
         .tctl_flags     = TCTL_COLL_DIST_E1000,
         .eeprom_read    = e1000_read_eerd,
+    }, {
+        .vendor_id      = 0x8086,
+        .device_id      = 0x10f5,
+        .tctl_flags     = TCTL_COLL_DIST_E1000E | TCTL_RRTHRESH,
+        .eeprom_read    = e1000e_read_flash,
     }
 };
 
@@ -418,9 +492,12 @@ found:
     cdi_list_t reslist = pci->resources;
     struct cdi_pci_resource* res;
     for (i = 0; (res = cdi_list_get(reslist, i)); i++) {
-        if (res->type == CDI_PCI_MEMORY) {
+        if (res->type == CDI_PCI_MEMORY && res->index == 0) {
             struct cdi_mem_area* mmio = cdi_mem_map(res->start, res->length);
             netcard->mem_base = mmio->vaddr;
+        } else if (res->type == CDI_PCI_MEMORY && res->index == 1) {
+            struct cdi_mem_area* mmio = cdi_mem_map(res->start, res->length);
+            netcard->flash_base = mmio->vaddr;
         }
     }
 
diff --git a/e1000/device.h b/e1000/device.h
index 2eebf8a..638e6f9 100644
--- a/e1000/device.h
+++ b/e1000/device.h
@@ -93,6 +93,10 @@ enum {
 
     /* e1000 specific values */
     TCTL_COLL_DIST_E1000    = (0x40 << 12), /* COLD - Collision Distance */
+
+    /* e1000e specific values */
+    TCTL_COLL_DIST_E1000E   = (0x3f << 12), /* COLD - Collision Distance */
+    TCTL_RRTHRESH           = (0x03 << 28), /* Read Request Threshold */
 };
 
 enum {
@@ -101,6 +105,31 @@ enum {
 };
 
 enum {
+    FLASH_REG_GLFPR     =   0x0, /* Gigabit LAN Flash Primary Region */
+    FLASH_REG_HSFSTS    =   0x4, /* Hardware Sequencing Flash Status */
+    FLASH_REG_HSFCTL    =   0x6, /* Hardware Sequencing Flash Control */
+    FLASH_REG_FADDR     =   0x8, /* Flash Address Register */
+    FLASH_REG_FDATA0    =  0x10, /* Flash Data 0 Register */
+};
+
+enum {
+    HSFSTS_FDONE        = (1 <<  0),
+    HSFSTS_FCERR        = (1 <<  1),
+    HSFSTS_AEL          = (1 <<  2),
+    HSFSTS_SCIP         = (1 <<  5),
+    HSFSTS_FDV          = (1 << 14),
+};
+
+enum {
+    HSFCTL_FGO          = (1 <<  0),
+    HSFCTL_FCYCLE_READ  = (0 <<  1),
+    /*HSFCTL_FCYCLE_WRITE = (2 <<  1),*/
+    HSFCTL_FDBC_BYTE    = (0 <<  8),
+    HSFCTL_FDBC_WORD    = (1 <<  8),
+    HSFCTL_FDBC_DWORD   = (3 <<  8),
+};
+
+enum {
     EEPROM_OFS_MAC      = 0x0,
 };
 
@@ -190,6 +219,9 @@ struct e1000_device {
     uint32_t                    rx_cur_buffer;
 
     void*                       mem_base;
+    void*                       flash_base;
+    uint32_t                    flash_base_offset;
+    uint32_t                    flash_size;
     uint8_t                     revision;
 
     struct e1000_model*         model;
diff --git a/e1000/e1000_io.h b/e1000/e1000_io.h
index 68937a9..2432b1c 100644
--- a/e1000/e1000_io.h
+++ b/e1000/e1000_io.h
@@ -74,4 +74,33 @@ static inline uint32_t reg_inl(struct e1000_device* netcard, uint16_t reg)
     return *mmio;
 }
 
+
+/* Flash register access */
+
+static inline void flash_reg_outw
+    (struct e1000_device* netcard, uint16_t reg, uint16_t value)
+{
+    volatile uint16_t* mmio = (uint16_t*) (((char*)netcard->flash_base) + reg);
+    *mmio = value;
+}
+
+static inline void flash_reg_outl
+    (struct e1000_device* netcard, uint16_t reg, uint32_t value)
+{
+    volatile uint32_t* mmio = (uint32_t*) (((char*)netcard->flash_base) + reg);
+    *mmio = value;
+}
+
+static inline uint16_t flash_reg_inw(struct e1000_device* netcard, uint16_t reg)
+{
+    volatile uint16_t* mmio = (uint16_t*) (((char*)netcard->flash_base) + reg);
+    return *mmio;
+}
+
+static inline uint32_t flash_reg_inl(struct e1000_device* netcard, uint16_t reg)
+{
+    volatile uint32_t* mmio = (uint32_t*) (((char*)netcard->flash_base) + reg);
+    return *mmio;
+}
+
 #endif
-- 
2.1.4