[U-Boot] e1000: Driver additions for eXMeritus HWW-1U-1A

Sorry for taking so long to update and re-post these; I've had a lot of other things on my plate for the last several months. I've re-based them against the latest U-Boot master branch as of today.
The following 5 patches are a series to provide the necessary support for the onboard Intel 82571 E1000E chips on our eXMeritus HWW-1U-1A boards.
There are a few incidental fixups for dual-port NICs, but the main body of these patches is an SPI driver and "e1000 eeprom" commands for programming the configuration EEPROM attached to the 82571 chips.
Our manufacturing process directly attaches the unprogrammed EEPROMs to the boards. We then use this code to load the Intel EEPROM and update the MAC address.
The overall diffstat is: drivers/net/e1000.c | 848 ++++++++++++++++++++++++++++++++++++++++++++------- drivers/net/e1000.h | 18 +- 2 files changed, 751 insertions(+), 115 deletions(-)
Most of the new code is hidden behind a couple of config options, so it won't affect other users of e1000 chips unless they explicitly request it.
Cheers, Kyle Moffett

Consolidate the test for a dual-port NIC to one location for easy modification, then fix support for the dual-port 82571.
Signed-off-by: Kyle Moffett Kyle.D.Moffett@boeing.com --- drivers/net/e1000.c | 66 +++++++++++++++++++++++++------------------------- drivers/net/e1000.h | 6 ---- 2 files changed, 33 insertions(+), 39 deletions(-)
diff --git a/drivers/net/e1000.c b/drivers/net/e1000.c index 5f390bd..5383064 100644 --- a/drivers/net/e1000.c +++ b/drivers/net/e1000.c @@ -1096,6 +1096,20 @@ e1000_swfw_sync_acquire(struct e1000_hw *hw, uint16_t mask) return E1000_SUCCESS; }
+static boolean_t e1000_is_second_port(struct e1000_hw *hw) +{ + switch (hw->mac_type) { + case e1000_80003es2lan: + case e1000_82546: + case e1000_82571: + if (E1000_READ_REG(hw, STATUS) & E1000_STATUS_FUNC_1) + return TRUE; + /* Fallthrough */ + default: + return FALSE; + } +} + /****************************************************************************** * Reads the adapter's MAC address from the EEPROM and inverts the LSB for the * second function of dual function devices @@ -1122,11 +1136,11 @@ e1000_read_mac_addr(struct eth_device *nic) nic->enetaddr[i] = eeprom_data & 0xff; nic->enetaddr[i + 1] = (eeprom_data >> 8) & 0xff; } - if ((hw->mac_type == e1000_82546) && - (E1000_READ_REG(hw, STATUS) & E1000_STATUS_FUNC_1)) { - /* Invert the last bit if this is the second device */ - nic->enetaddr[5] += 1; - } + + /* Invert the last bit if this is the second device */ + if (e1000_is_second_port(hw)) + nic->enetaddr[5] ^= 1; + #ifdef CONFIG_E1000_FALLBACK_MAC if ( *(u32*)(nic->enetaddr) == 0 || *(u32*)(nic->enetaddr) == ~0 ) { unsigned char fb_mac[NODE_ADDRESS_SIZE] = CONFIG_E1000_FALLBACK_MAC; @@ -2528,16 +2542,13 @@ e1000_check_mng_mode(struct e1000_hw *hw) static int32_t e1000_write_kmrn_reg(struct e1000_hw *hw, uint32_t reg_addr, uint16_t data) { + uint16_t swfw = E1000_SWFW_PHY0_SM; uint32_t reg_val; - uint16_t swfw; DEBUGFUNC();
- if ((hw->mac_type == e1000_80003es2lan) && - (E1000_READ_REG(hw, STATUS) & E1000_STATUS_FUNC_1)) { + if (e1000_is_second_port(hw)) swfw = E1000_SWFW_PHY1_SM; - } else { - swfw = E1000_SWFW_PHY0_SM; - } + if (e1000_swfw_sync_acquire(hw, swfw)) return -E1000_ERR_SWFW_SYNC;
@@ -2552,16 +2563,13 @@ e1000_write_kmrn_reg(struct e1000_hw *hw, uint32_t reg_addr, uint16_t data) static int32_t e1000_read_kmrn_reg(struct e1000_hw *hw, uint32_t reg_addr, uint16_t *data) { + uint16_t swfw = E1000_SWFW_PHY0_SM; uint32_t reg_val; - uint16_t swfw; DEBUGFUNC();
- if ((hw->mac_type == e1000_80003es2lan) && - (E1000_READ_REG(hw, STATUS) & E1000_STATUS_FUNC_1)) { + if (e1000_is_second_port(hw)) swfw = E1000_SWFW_PHY1_SM; - } else { - swfw = E1000_SWFW_PHY0_SM; - } + if (e1000_swfw_sync_acquire(hw, swfw)) return -E1000_ERR_SWFW_SYNC;
@@ -4259,11 +4267,13 @@ e1000_get_phy_cfg_done(struct e1000_hw *hw) default: mdelay(10); break; + case e1000_80003es2lan: /* Separate *_CFG_DONE_* bit for each port */ - if (E1000_READ_REG(hw, STATUS) & E1000_STATUS_FUNC_1) + if (e1000_is_second_port(hw)) cfg_mask = E1000_EEPROM_CFG_DONE_PORT_1; - /* Fall Through */ + /* Fall Through */ + case e1000_82571: case e1000_82572: while (timeout) { @@ -4292,10 +4302,10 @@ e1000_get_phy_cfg_done(struct e1000_hw *hw) int32_t e1000_phy_hw_reset(struct e1000_hw *hw) { + uint16_t swfw = E1000_SWFW_PHY0_SM; uint32_t ctrl, ctrl_ext; uint32_t led_ctrl; int32_t ret_val; - uint16_t swfw;
DEBUGFUNC();
@@ -4308,16 +4318,14 @@ e1000_phy_hw_reset(struct e1000_hw *hw) DEBUGOUT("Resetting Phy...\n");
if (hw->mac_type > e1000_82543) { - if ((hw->mac_type == e1000_80003es2lan) && - (E1000_READ_REG(hw, STATUS) & E1000_STATUS_FUNC_1)) { + if (e1000_is_second_port(hw)) swfw = E1000_SWFW_PHY1_SM; - } else { - swfw = E1000_SWFW_PHY0_SM; - } + if (e1000_swfw_sync_acquire(hw, swfw)) { DEBUGOUT("Unable to acquire swfw sync\n"); return -E1000_ERR_SWFW_SYNC; } + /* Read the device control register and assert the E1000_CTRL_PHY_RST * bit. Then, take it out of reset. */ @@ -4771,14 +4779,6 @@ e1000_sw_init(struct eth_device *nic, int cardnum) break; }
- /* lan a vs. lan b settings */ - if (hw->mac_type == e1000_82546) - /*this also works w/ multiple 82546 cards */ - /*but not if they're intermingled /w other e1000s */ - hw->lan_loc = (cardnum % 2) ? e1000_lan_b : e1000_lan_a; - else - hw->lan_loc = e1000_lan_a; - /* flow control settings */ hw->fc_high_water = E1000_FC_HIGH_THRESH; hw->fc_low_water = E1000_FC_LOW_THRESH; diff --git a/drivers/net/e1000.h b/drivers/net/e1000.h index eb0804b..8597e23 100644 --- a/drivers/net/e1000.h +++ b/drivers/net/e1000.h @@ -111,11 +111,6 @@ typedef enum { e1000_100_full = 3 } e1000_speed_duplex_type;
-typedef enum { - e1000_lan_a = 0, - e1000_lan_b = 1 -} e1000_lan_loc; - /* Flow Control Settings */ typedef enum { e1000_fc_none = 0, @@ -1055,7 +1050,6 @@ struct e1000_hw { uint32_t phy_init_script; uint32_t txd_cmd; e1000_media_type media_type; - e1000_lan_loc lan_loc; e1000_fc_type fc; e1000_bus_type bus_type; #if 0

By allocating the e1000 device structures much earlier, we can easily generate better error messages and siginficantly clean things up.
The only user-visable change (aside from reworded error messages) is that a detected e1000 device which fails to initialize due to software or hardware error will still be allocated a device number.
As one example, consider a system with 2 e1000 PCI devices where the first controller has a corrupted EEPROM. Using the old code the second controller would be "e1000#0", while with this change it would be "e1000#1".
This change should hopefully make such EEPROM errors much more straightforward to handle correctly in boot scripts and the like.
It is also necessary for a followup patch which allows SPI programming of an e1000 controller's EEPROM even if the checksum is invalid.
Signed-off-by: Kyle Moffett Kyle.D.Moffett@boeing.com --- drivers/net/e1000.c | 117 +++++++++++++++++++++++++-------------------------- drivers/net/e1000.h | 3 + 2 files changed, 60 insertions(+), 60 deletions(-)
diff --git a/drivers/net/e1000.c b/drivers/net/e1000.c index 5383064..ddf29c8 100644 --- a/drivers/net/e1000.c +++ b/drivers/net/e1000.c @@ -65,7 +65,7 @@ static struct e1000_rx_desc *rx_base; static int tx_tail; static int rx_tail, rx_last;
-static struct pci_device_id supported[] = { +static struct pci_device_id e1000_supported[] = { {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82542}, {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82543GC_FIBER}, {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82543GC_COPPER}, @@ -4746,7 +4746,7 @@ e1000_set_media_type(struct e1000_hw *hw) **/
static int -e1000_sw_init(struct eth_device *nic, int cardnum) +e1000_sw_init(struct eth_device *nic) { struct e1000_hw *hw = (typeof(hw)) nic->priv; int result; @@ -5146,57 +5146,59 @@ You should omit the last argument struct pci_device * for a non-PCI NIC int e1000_initialize(bd_t * bis) { + unsigned int i; pci_dev_t devno; - int card_number = 0; - struct eth_device *nic = NULL; - struct e1000_hw *hw = NULL; - u32 iobase; - int idx = 0; - u32 PciCommandWord;
DEBUGFUNC();
- while (1) { /* Find PCI device(s) */ - if ((devno = pci_find_devices(supported, idx++)) < 0) { - break; - } - - pci_read_config_dword(devno, PCI_BASE_ADDRESS_0, &iobase); - iobase &= ~0xf; /* Mask the bits that say "this is an io addr" */ - DEBUGOUT("e1000#%d: iobase 0x%08x\n", card_number, iobase); - - pci_write_config_dword(devno, PCI_COMMAND, - PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER); - /* Check if I/O accesses and Bus Mastering are enabled. */ - pci_read_config_dword(devno, PCI_COMMAND, &PciCommandWord); - if (!(PciCommandWord & PCI_COMMAND_MEMORY)) { - printf("Error: Can not enable MEM access.\n"); - continue; - } else if (!(PciCommandWord & PCI_COMMAND_MASTER)) { - printf("Error: Can not enable Bus Mastering.\n"); - continue; - } - - nic = (struct eth_device *) malloc(sizeof (*nic)); - if (!nic) { - printf("Error: e1000 - Can not alloc memory\n"); - return 0; - } + /* Find and probe all the matching PCI devices */ + for (i = 0; (devno = pci_find_devices(e1000_supported, i)) >= 0; i++) { + u32 val;
- hw = (struct e1000_hw *) malloc(sizeof (*hw)); - if (!hw) { + /* + * These will never get freed due to errors, this allows us to + * perform SPI EEPROM programming from U-boot, for example. + */ + struct eth_device *nic = malloc(sizeof(*nic)); + struct e1000_hw *hw = malloc(sizeof(*hw)); + if (!nic || !hw) { + printf("e1000#%u: Out of Memory!\n", i); free(nic); - printf("Error: e1000 - Can not alloc memory\n"); - return 0; + free(hw); + continue; }
+ /* Make sure all of the fields are initially zeroed */ memset(nic, 0, sizeof(*nic)); memset(hw, 0, sizeof(*hw));
+ /* Assign the passed-in values */ + hw->cardnum = i; hw->pdev = devno; + hw->nic = nic; nic->priv = hw;
- sprintf(nic->name, "e1000#%d", card_number); + /* Generate a card name */ + sprintf(nic->name, "e1000#%u", hw->cardnum); + + /* Print a debug message with the IO base address */ + pci_read_config_dword(devno, PCI_BASE_ADDRESS_0, &val); + DEBUGOUT("%s: iobase 0x%08x\n", nic->name, val & 0xfffffff0); + + /* Try to enable I/O accesses and bus-mastering */ + val = PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER; + pci_write_config_dword(devno, PCI_COMMAND, val); + + /* Make sure it worked */ + pci_read_config_dword(devno, PCI_COMMAND, &val); + if (!(val & PCI_COMMAND_MEMORY)) { + printf("%s: ERROR: Can't enable I/O memory\n", nic->name); + continue; + } + if (!(val & PCI_COMMAND_MASTER)) { + printf("%s: ERROR: Can't enable bus-mastering\n", nic->name); + continue; + }
/* Are these variables needed? */ hw->fc = e1000_fc_default; @@ -5204,50 +5206,45 @@ e1000_initialize(bd_t * bis) hw->autoneg_failed = 0; hw->autoneg = 1; hw->get_link_status = TRUE; - hw->hw_addr = - pci_map_bar(devno, PCI_BASE_ADDRESS_0, PCI_REGION_MEM); + hw->hw_addr = pci_map_bar(devno, PCI_BASE_ADDRESS_0, PCI_REGION_MEM); hw->mac_type = e1000_undefined;
/* MAC and Phy settings */ - if (e1000_sw_init(nic, card_number) < 0) { - free(hw); - free(nic); - return 0; + if (e1000_sw_init(nic) < 0) { + printf("%s: ERROR: Software init failed\n", nic->name); + continue; } if (e1000_check_phy_reset_block(hw)) - printf("phy reset block error \n"); + printf("%s: ERROR: PHY Reset is blocked!\n", nic->name); + + /* Basic init was OK, reset the hardware */ e1000_reset_hw(hw); + + /* Validate the EEPROM and get chipset information */ #if !(defined(CONFIG_AP1000) || defined(CONFIG_MVBC_1G)) if (e1000_init_eeprom_params(hw)) { - printf("The EEPROM Checksum Is Not Valid\n"); - free(hw); - free(nic); - return 0; + printf("%s: ERROR: EEPROM is invalid!\n", nic->name); + continue; } if (e1000_validate_eeprom_checksum(nic) < 0) { - printf("The EEPROM Checksum Is Not Valid\n"); - free(hw); - free(nic); - return 0; + printf("%s: ERROR: EEPROM checksum is bad!\n", nic->name); + continue; } #endif e1000_read_mac_addr(nic); - - /* get the bus type information */ e1000_get_bus_type(hw);
printf("e1000: %02x:%02x:%02x:%02x:%02x:%02x\n", nic->enetaddr[0], nic->enetaddr[1], nic->enetaddr[2], nic->enetaddr[3], nic->enetaddr[4], nic->enetaddr[5]);
+ /* Set up the function pointers and register the device */ nic->init = e1000_init; nic->recv = e1000_poll; nic->send = e1000_transmit; nic->halt = e1000_disable; - eth_register(nic); - - card_number++; } - return card_number; + + return i; } diff --git a/drivers/net/e1000.h b/drivers/net/e1000.h index 8597e23..8573511 100644 --- a/drivers/net/e1000.h +++ b/drivers/net/e1000.h @@ -1043,6 +1043,9 @@ typedef enum {
/* Structure containing variables used by the shared code (e1000_hw.c) */ struct e1000_hw { + struct eth_device *nic; + unsigned int cardnum; + pci_dev_t pdev; uint8_t *hw_addr; e1000_mac_type mac_type;

Dear Kyle Moffett,
In message 1297467482-14864-3-git-send-email-Kyle.D.Moffett@boeing.com you wrote:
By allocating the e1000 device structures much earlier, we can easily generate better error messages and siginficantly clean things up.
The only user-visable change (aside from reworded error messages) is that a detected e1000 device which fails to initialize due to software or hardware error will still be allocated a device number.
As one example, consider a system with 2 e1000 PCI devices where the first controller has a corrupted EEPROM. Using the old code the second controller would be "e1000#0", while with this change it would be "e1000#1".
This change should hopefully make such EEPROM errors much more straightforward to handle correctly in boot scripts and the like.
It is also necessary for a followup patch which allows SPI programming of an e1000 controller's EEPROM even if the checksum is invalid.
This patch has a number of overlong lines. Please globally fix the line length.
Thanks.
Best regards,
Wolfgang Denk

On Apr 12, 2011, at 16:17, Wolfgang Denk wrote:
In message 1297467482-14864-3-git-send-email-Kyle.D.Moffett@boeing.com you wrote:
By allocating the e1000 device structures much earlier, we can easily generate better error messages and siginficantly clean things up.
The only user-visable change (aside from reworded error messages) is that a detected e1000 device which fails to initialize due to software or hardware error will still be allocated a device number.
As one example, consider a system with 2 e1000 PCI devices where the first controller has a corrupted EEPROM. Using the old code the second controller would be "e1000#0", while with this change it would be "e1000#1".
This change should hopefully make such EEPROM errors much more straightforward to handle correctly in boot scripts and the like.
It is also necessary for a followup patch which allows SPI programming of an e1000 controller's EEPROM even if the checksum is invalid.
This patch has a number of overlong lines. Please globally fix the line length.
The only lines longer than 80 characters in this patch are these 4 when indented by 3 levels:
hw->hw_addr = pci_map_bar(devno, PCI_BASE_ADDRESS_0, PCI_REGION_MEM); printf("%s: ERROR: Can't enable I/O memory\n", nic->name); printf("%s: ERROR: Can't enable bus-mastering\n", nic->name); printf("%s: ERROR: EEPROM checksum is bad!\n", nic->name);
According to Documentation/CodingStyle:
Statements longer than 80 columns will be broken into sensible chunks. Descendants are always substantially shorter than the parent and are placed substantially to the right. The same applies to function headers with a long argument list. Long strings are as well broken into shorter strings. The only exception to this is where exceeding 80 columns significantly increases readability and does not hide information.
Wrapping any of those lines will in fact make them much harder to read and dissimilar to the other nearly identical printf() calls in that piece of code; I strongly disagree that it is necessary.
Cheers, Kyle Moffett

Dear "Moffett, Kyle D",
In message 85579850-725C-46CC-B6AF-9DE6D6683C82@boeing.com you wrote:
indented by 3 levels:
hw->hw_addr = pci_map_bar(devno, PCI_BASE_ADDRESS_0, PCI_REGION_MEM); printf("%s: ERROR: Can't enable I/O memory\n", nic->name); printf("%s: ERROR: Can't enable bus-mastering\n", nic->name); printf("%s: ERROR: EEPROM checksum is bad!\n", nic->name);
...
Wrapping any of those lines will in fact make them much harder to read and dissimilar to the other nearly identical printf() calls in that piece of code; I strongly disagree that it is necessary.
What makes you think that
printf("%s: ERROR: Can't enable I/O memory\n", nic->name);
was "much harder to read" than
printf("%s: ERROR: Can't enable I/O memory\n", nic->name);
?
Please fix these!
Best regards,
Wolfgang Denk

As an aide to debugging, we should print out the expected value of the EEPROM checksum in addition to just saying that it is wrong.
Signed-off-by: Kyle Moffett Kyle.D.Moffett@boeing.com --- drivers/net/e1000.c | 48 ++++++++++++++++++++++++++++++------------------ 1 files changed, 30 insertions(+), 18 deletions(-)
diff --git a/drivers/net/e1000.c b/drivers/net/e1000.c index ddf29c8..c4cedc6 100644 --- a/drivers/net/e1000.c +++ b/drivers/net/e1000.c @@ -873,29 +873,43 @@ e1000_read_eeprom(struct e1000_hw *hw, uint16_t offset, * If the the sum of the 64 16 bit words is 0xBABA, the EEPROM's checksum is * valid. *****************************************************************************/ -static int -e1000_validate_eeprom_checksum(struct eth_device *nic) +static int e1000_validate_eeprom_checksum(struct e1000_hw *hw) { - struct e1000_hw *hw = nic->priv; - uint16_t checksum = 0; - uint16_t i, eeprom_data; + uint16_t i, checksum, checksum_reg, *buf;
DEBUGFUNC();
- for (i = 0; i < (EEPROM_CHECKSUM_REG + 1); i++) { - if (e1000_read_eeprom(hw, i, 1, &eeprom_data) < 0) { - DEBUGOUT("EEPROM Read Error\n"); - return -E1000_ERR_EEPROM; - } - checksum += eeprom_data; + /* Allocate a temporary buffer */ + buf = malloc(sizeof(buf[0]) * (EEPROM_CHECKSUM_REG + 1)); + if (!buf) { + printf("%s: ERROR: Unable to allocate EEPROM buffer!\n", + hw->nic->name); + return -E1000_ERR_EEPROM; }
- if (checksum == (uint16_t) EEPROM_SUM) { - return 0; - } else { - DEBUGOUT("EEPROM Checksum Invalid\n"); + /* Read the EEPROM */ + if (e1000_read_eeprom(hw, 0, EEPROM_CHECKSUM_REG + 1, buf) < 0) { + printf("%s: ERROR: Unable to read EEPROM!\n", + hw->nic->name); return -E1000_ERR_EEPROM; } + + /* Compute the checksum */ + for (i = 0; i < EEPROM_CHECKSUM_REG; i++) + checksum += buf[i]; + checksum = ((uint16_t)EEPROM_SUM) - checksum; + checksum_reg = buf[i]; + + /* Verify it! */ + if (checksum == checksum_reg) + return 0; + + /* Hrm, verification failed, print an error */ + printf("%s: ERROR: EEPROM checksum is incorrect!\n", hw->nic->name); + printf("%s: ERROR: ...register was 0x%04hx, calculated 0x%04hx\n", + hw->nic->name, checksum_reg, checksum); + + return -E1000_ERR_EEPROM; }
/***************************************************************************** @@ -5226,10 +5240,8 @@ e1000_initialize(bd_t * bis) printf("%s: ERROR: EEPROM is invalid!\n", nic->name); continue; } - if (e1000_validate_eeprom_checksum(nic) < 0) { - printf("%s: ERROR: EEPROM checksum is bad!\n", nic->name); + if (e1000_validate_eeprom_checksum(hw)) continue; - } #endif e1000_read_mac_addr(nic); e1000_get_bus_type(hw);

For our new board ports, we are programming the EEPROMs attached to our Intel 82571EB controllers from software (using U-Boot and Linux).
This code provides a helpful set of "e1000" subcommands for performing EEPROM manipulation on e1000 devices, including displaying a hex-dump, copying to and from main memory, and verifying/updating of the software checksum.
The following commands work for programming the EEPROM from USB: usb start fatload usb 0 $loadaddr 82571EB_No_Mgmt_Discrete-LOM.bin e1000 0 eeprom program $loadaddr 0 1024 e1000 0 eeprom checksum update
Please keep in mind that the Intel-provided .eep files are organized as 16-bit words. When converting them to binary form for programming you must byteswap each 16-bit word so that it is in little-endian form.
This means that when reading and writing words to the SPI EEPROM, the bit ordering for each word looks like this on the wire:
Time >>> ----------------------------------------------------------------- ... [7, 6, 5, 4, 3, 2, 1, 0, 15, 14, 13, 12, 11, 10, 9, 8], ... ----------------------------------------------------------------- (MSB is 15, LSB is 0).
Signed-off-by: Kyle Moffett Kyle.D.Moffett@boeing.com --- drivers/net/e1000.c | 533 ++++++++++++++++++++++++++++++++++++++++++++++++++- drivers/net/e1000.h | 2 + 2 files changed, 534 insertions(+), 1 deletions(-)
diff --git a/drivers/net/e1000.c b/drivers/net/e1000.c index c4cedc6..b8bc27f 100644 --- a/drivers/net/e1000.c +++ b/drivers/net/e1000.c @@ -5153,6 +5153,8 @@ void e1000_get_bus_type(struct e1000_hw *hw) } }
+static LIST_HEAD(e1000_hw_list); + /************************************************************************** PROBE - Look for an adapter, this routine's visible to the outside You should omit the last argument struct pci_device * for a non-PCI NIC @@ -5231,8 +5233,9 @@ e1000_initialize(bd_t * bis) if (e1000_check_phy_reset_block(hw)) printf("%s: ERROR: PHY Reset is blocked!\n", nic->name);
- /* Basic init was OK, reset the hardware */ + /* Basic init was OK, reset the hardware and allow SPI access */ e1000_reset_hw(hw); + list_add_tail(&hw->list_node, &e1000_hw_list);
/* Validate the EEPROM and get chipset information */ #if !(defined(CONFIG_AP1000) || defined(CONFIG_MVBC_1G)) @@ -5260,3 +5263,531 @@ e1000_initialize(bd_t * bis)
return i; } + +#ifdef CONFIG_CMD_E1000 +static struct e1000_hw *e1000_find_card(unsigned int cardnum) +{ + struct e1000_hw *hw; + + list_for_each_entry(hw, &e1000_hw_list, list_node) + if (hw->cardnum == cardnum) + return hw; + + return NULL; +} + +/*----------------------------------------------------------------------- + * SPI transfer + * + * This writes "bitlen" bits out the SPI MOSI port and simultaneously clocks + * "bitlen" bits in the SPI MISO port. That's just the way SPI works. + * + * The source of the outgoing bits is the "dout" parameter and the + * destination of the input bits is the "din" parameter. Note that "dout" + * and "din" can point to the same memory location, in which case the + * input data overwrites the output data (since both are buffered by + * temporary variables, this is OK). + * + * This may be interrupted with Ctrl-C if "intr" is true, otherwise it will + * never return an error. + */ +static int e1000_spi_xfer(struct e1000_hw *hw, unsigned int bitlen, + const void *dout_mem, void *din_mem, boolean_t intr) +{ + const uint8_t *dout = dout_mem; + uint8_t *din = din_mem; + + uint8_t mask = 0; + uint32_t eecd; + unsigned long i; + + /* Pre-read the control register */ + eecd = E1000_READ_REG(hw, EECD); + + /* Iterate over each bit */ + for (i = 0, mask = 0x80; i < bitlen; i++, mask = (mask >> 1)?:0x80) { + /* Check for interrupt */ + if (intr && ctrlc()) + return -1; + + /* Determine the output bit */ + if (dout && dout[i >> 3] & mask) + eecd |= E1000_EECD_DI; + else + eecd &= ~E1000_EECD_DI; + + /* Write the output bit and wait 50us */ + E1000_WRITE_REG(hw, EECD, eecd); + E1000_WRITE_FLUSH(hw); + udelay(50); + + /* Poke the clock (waits 50us) */ + e1000_raise_ee_clk(hw, &eecd); + + /* Now read the input bit */ + eecd = E1000_READ_REG(hw, EECD); + if (din) { + if (eecd & E1000_EECD_DO) + din[i >> 3] |= mask; + else + din[i >> 3] &= ~mask; + } + + /* Poke the clock again (waits 50us) */ + e1000_lower_ee_clk(hw, &eecd); + } + + /* Now clear any remaining bits of the input */ + if (din && (i & 7)) + din[i >> 3] &= ~((mask << 1) - 1); + + return 0; +} + +/* The EEPROM opcodes */ +#define SPI_EEPROM_ENABLE_WR 0x06 +#define SPI_EEPROM_DISABLE_WR 0x04 +#define SPI_EEPROM_WRITE_STATUS 0x01 +#define SPI_EEPROM_READ_STATUS 0x05 +#define SPI_EEPROM_WRITE_PAGE 0x02 +#define SPI_EEPROM_READ_PAGE 0x03 + +/* The EEPROM status bits */ +#define SPI_EEPROM_STATUS_BUSY 0x01 +#define SPI_EEPROM_STATUS_WREN 0x02 + +static int e1000_spi_eeprom_enable_wr(struct e1000_hw *hw, boolean_t intr) +{ + u8 op[] = { SPI_EEPROM_ENABLE_WR }; + e1000_standby_eeprom(hw); + return e1000_spi_xfer(hw, 8*sizeof(op), op, NULL, intr); +} + +#if 0 +static int e1000_spi_eeprom_disable_wr(struct e1000_hw *hw, boolean_t intr) +{ + u8 op[] = { SPI_EEPROM_DISABLE_WR }; + e1000_standby_eeprom(hw); + return e1000_spi_xfer(hw, 8*sizeof(op), op, NULL, intr); +} + +static int e1000_spi_eeprom_write_status(struct e1000_hw *hw, + u8 status, boolean_t intr) +{ + u8 op[] = { SPI_EEPROM_WRITE_STATUS, status }; + e1000_standby_eeprom(hw); + return e1000_spi_xfer(hw, 8*sizeof(op), op, NULL, intr); +} +#endif + +static int e1000_spi_eeprom_read_status(struct e1000_hw *hw, boolean_t intr) +{ + u8 op[] = { SPI_EEPROM_READ_STATUS, 0 }; + e1000_standby_eeprom(hw); + if (e1000_spi_xfer(hw, 8*sizeof(op), op, op, intr)) + return -1; + return op[1]; +} + +static int e1000_spi_eeprom_write_page(struct e1000_hw *hw, + const void *data, u16 off, u16 len, boolean_t intr) +{ + u8 op[] = { + SPI_EEPROM_WRITE_PAGE, + (off >> (hw->eeprom.address_bits - 8)) & 0xff, off & 0xff + }; + + e1000_standby_eeprom(hw); + printf("%s: Write Page @0x%04hx (0x%04hx bytes)\n", + hw->nic->name, off, len); + + if (e1000_spi_xfer(hw, 8 + hw->eeprom.address_bits, op, NULL, intr)) + return -1; + if (e1000_spi_xfer(hw, len << 3, data, NULL, intr)) + return -1; + + printf(" => Done!\n"); + return 0; +} + +static int e1000_spi_eeprom_read_page(struct e1000_hw *hw, + void *data, u16 off, u16 len, boolean_t intr) +{ + u8 op[] = { + SPI_EEPROM_READ_PAGE, + (off >> (hw->eeprom.address_bits - 8)) & 0xff, off & 0xff + }; + + e1000_standby_eeprom(hw); + printf("%s: Read Page @0x%04hx (0x%04hx bytes)\n", + hw->nic->name, off, len); + + if (e1000_spi_xfer(hw, 8 + hw->eeprom.address_bits, op, NULL, intr)) + return -1; + if (e1000_spi_xfer(hw, len << 3, NULL, data, intr)) + return -1; + + printf(" => Done!\n"); + return 0; +} + +static int e1000_spi_eeprom_poll_ready(struct e1000_hw *hw, boolean_t intr) +{ + int status; + while ((status = e1000_spi_eeprom_read_status(hw, intr)) >= 0) { + if (!(status & SPI_EEPROM_STATUS_BUSY)) + return 0; + } + return -1; +} + +int e1000_spi_eeprom_dump(struct e1000_hw *hw, + void *data, u16 off, unsigned int len, boolean_t intr) +{ + /* Interruptibly wait for the EEPROM to be ready */ + if (e1000_spi_eeprom_poll_ready(hw, intr)) + return -1; + + /* Dump each page in sequence */ + while (len) { + /* Calculate the data bytes on this page */ + u16 pg_off = off & (hw->eeprom.page_size - 1); + u16 pg_len = hw->eeprom.page_size - pg_off; + if (pg_len > len) + pg_len = len; + + /* Now dump the page */ + if (e1000_spi_eeprom_read_page(hw, data, off, pg_len, intr)) + return -1; + + /* Otherwise go on to the next page */ + len -= pg_len; + off += pg_len; + data += pg_len; + } + + /* We're done! */ + return 0; +} + +int e1000_spi_eeprom_program(struct e1000_hw *hw, + const void *data, u16 off, u16 len, boolean_t intr) +{ + /* Program each page in sequence */ + while (len) { + /* Calculate the data bytes on this page */ + u16 pg_off = off & (hw->eeprom.page_size - 1); + u16 pg_len = hw->eeprom.page_size - pg_off; + if (pg_len > len) + pg_len = len; + + /* Interruptibly wait for the EEPROM to be ready */ + if (e1000_spi_eeprom_poll_ready(hw, intr)) + return -1; + + /* Enable write access */ + if (e1000_spi_eeprom_enable_wr(hw, intr)) + return -1; + + /* Now program the page */ + if (e1000_spi_eeprom_write_page(hw, data, off, pg_len, intr)) + return -1; + + /* Otherwise go on to the next page */ + len -= pg_len; + off += pg_len; + data += pg_len; + } + + /* Wait for the last write to complete */ + if (e1000_spi_eeprom_poll_ready(hw, intr)) + return -1; + + /* We're done! */ + return 0; +} + +static int do_e1000_eeprom_show(cmd_tbl_t *cmdtp, struct e1000_hw *hw, + int argc, char *argv[]) +{ + unsigned int length = 0; + u16 i, offset = 0; + u8 *buffer; + int err; + + if (argc > 3) { + cmd_usage(cmdtp); + return 1; + } + + /* Parse the offset and length */ + if (argc >= 2) + offset = simple_strtoul(argv[1], NULL, 0); + if (argc == 3) + length = simple_strtoul(argv[2], NULL, 0); + else if (offset < (hw->eeprom.word_size << 1)) + length = (hw->eeprom.word_size << 1) - offset; + + /* Extra sanity checks */ + if (!length) { + printf("%s: ERROR: Requested zero-sized dump!\n", + hw->nic->name); + return 1; + } + if ((0x10000 < length) || (0x10000 - length < offset)) { + printf("%s: ERROR: Can't dump past 0xFFFF!\n", hw->nic->name); + return 1; + } + + /* Allocate a buffer to hold stuff */ + buffer = malloc(length); + if (!buffer) { + printf("%s: ERROR: Out of Memory!\n", hw->nic->name); + return 1; + } + + /* Acquire the EEPROM and perform the dump */ + if (e1000_acquire_eeprom(hw)) { + printf("%s: EEPROM SPI cannot be acquired!", hw->nic->name); + free(buffer); + return 1; + } + err = e1000_spi_eeprom_dump(hw, buffer, offset, length, TRUE); + e1000_release_eeprom(hw); + if (err) { + printf("%s: Interrupted!\n", hw->nic->name); + free(buffer); + return 1; + } + + /* Now hexdump the result */ + printf("%s: ===== Intel e1000 EEPROM (0x%04hX - 0x%04hX) =====", + hw->nic->name, offset, offset + length - 1); + for (i = 0; i < length; i++) { + if ((i & 0xF) == 0) + printf("\n%s: %04hX: ", hw->nic->name, offset + i); + else if ((i & 0xF) == 0x8) + printf(" "); + printf(" %02hx", buffer[i]); + } + printf("\n"); + + /* Success! */ + free(buffer); + return 0; +} + +static int do_e1000_eeprom_dump(cmd_tbl_t *cmdtp, struct e1000_hw *hw, + int argc, char *argv[]) +{ + unsigned int length; + u16 offset; + void *dest; + + if (argc != 4) { + cmd_usage(cmdtp); + return 1; + } + + /* Parse the arguments */ + dest = (void *)simple_strtoul(argv[1], NULL, 16); + offset = simple_strtoul(argv[2], NULL, 0); + length = simple_strtoul(argv[3], NULL, 0); + + /* Extra sanity checks */ + if (!length) { + printf("%s: ERROR: Requested zero-sized dump!\n", + hw->nic->name); + return 1; + } + if ((0x10000 < length) || (0x10000 - length < offset)) { + printf("%s: ERROR: Can't dump past 0xFFFF!\n", hw->nic->name); + return 1; + } + + /* Acquire the EEPROM */ + if (e1000_acquire_eeprom(hw)) { + printf("%s: EEPROM SPI cannot be acquired!", hw->nic->name); + return 1; + } + + /* Perform the programming operation */ + if (e1000_spi_eeprom_dump(hw, dest, offset, length, TRUE) < 0) { + printf("%s: Interrupted!\n", hw->nic->name); + e1000_release_eeprom(hw); + return 1; + } + + e1000_release_eeprom(hw); + printf("%s: ===== EEPROM DUMP COMPLETE =====\n", hw->nic->name); + return 0; +} + +static int do_e1000_eeprom_program(cmd_tbl_t *cmdtp, struct e1000_hw *hw, + int argc, char *argv[]) +{ + unsigned int length; + const void *source; + u16 offset; + + if (argc != 4) { + cmd_usage(cmdtp); + return 1; + } + + /* Parse the arguments */ + source = (const void *)simple_strtoul(argv[1], NULL, 16); + offset = simple_strtoul(argv[2], NULL, 0); + length = simple_strtoul(argv[3], NULL, 0); + + /* Acquire the EEPROM */ + if (e1000_acquire_eeprom(hw)) { + printf("%s: EEPROM SPI cannot be acquired!", hw->nic->name); + return 1; + } + + /* Perform the programming operation */ + if (e1000_spi_eeprom_program(hw, source, offset, length, TRUE) < 0) { + printf("%s: Interrupted!\n", hw->nic->name); + e1000_release_eeprom(hw); + return 1; + } + + e1000_release_eeprom(hw); + printf("%s: ===== EEPROM PROGRAMMED =====\n", hw->nic->name); + return 0; +} + +static int do_e1000_eeprom_checksum(cmd_tbl_t *cmdtp, struct e1000_hw *hw, + int argc, char *argv[]) +{ + uint16_t i, length, checksum, checksum_reg; + uint16_t *buffer; + boolean_t upd; + + if (argc == 1) + upd = 0; + else if ((argc == 2) && !strcmp(argv[1], "update")) + upd = 1; + else { + cmd_usage(cmdtp); + return 1; + } + + /* Allocate a temporary buffer */ + length = sizeof(uint16_t) * (EEPROM_CHECKSUM_REG + 1); + buffer = malloc(length); + if (!buffer) { + printf("%s: ERROR: Unable to allocate EEPROM buffer!\n", + hw->nic->name); + return 1; + } + + /* Acquire the EEPROM */ + if (e1000_acquire_eeprom(hw)) { + printf("%s: EEPROM SPI cannot be acquired!", hw->nic->name); + return 1; + } + + /* Read the EEPROM */ + if (e1000_spi_eeprom_dump(hw, buffer, 0, length, TRUE) < 0) { + printf("%s: Interrupted!\n", hw->nic->name); + e1000_release_eeprom(hw); + return 1; + } + + /* Compute the checksum and read the expected value */ + for (i = 0; i < EEPROM_CHECKSUM_REG; i++) + checksum += le16_to_cpu(buffer[i]); + checksum = ((uint16_t)EEPROM_SUM) - checksum; + checksum_reg = le16_to_cpu(buffer[i]); + + /* Verify it! */ + if (checksum_reg == checksum) { + printf("%s: INFO: EEPROM checksum is correct! (0x%04hx)\n", + hw->nic->name, checksum); + e1000_release_eeprom(hw); + return 0; + } + + /* Hrm, verification failed, print an error */ + printf("%s: ERROR: EEPROM checksum is incorrect!\n", hw->nic->name); + printf("%s: ERROR: ...register was 0x%04hx, calculated 0x%04hx\n", + hw->nic->name, checksum_reg, checksum); + + /* If they didn't ask us to update it, just return an error */ + if (!upd) { + e1000_release_eeprom(hw); + return 1; + } + + /* Ok, correct it! */ + printf("%s: Reprogramming the EEPROM checksum...\n", hw->nic->name); + buffer[i] = cpu_to_le16(checksum); + if (e1000_spi_eeprom_program(hw, &buffer[i], i * sizeof(uint16_t), + sizeof(uint16_t), TRUE)) { + printf("%s: Interrupted!\n", hw->nic->name); + e1000_release_eeprom(hw); + return 1; + } + + e1000_release_eeprom(hw); + return 0; +} + +int do_e1000(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) +{ + struct e1000_hw *hw; + + if (argc < 4) { + cmd_usage(cmdtp); + return 1; + } + + /* Make sure we can find the requested e1000 card */ + hw = e1000_find_card(simple_strtoul(argv[1], NULL, 10)); + if (!hw) { + printf("e1000: ERROR: No such device: e1000#%s\n", argv[1]); + return 1; + } + + /* We only support an "eeprom" sub-command right now */ + if (strcmp(argv[2], "eeprom")) { + cmd_usage(cmdtp); + return 1; + } + + /* Make sure it has an SPI chip */ + if (hw->eeprom.type != e1000_eeprom_spi) { + printf("%s: No attached SPI EEPROM found!\n", hw->nic->name); + return 1; + } + + /* Check the eeprom sub-sub-command arguments */ + if (!strcmp(argv[3], "show")) + return do_e1000_eeprom_show(cmdtp, hw, argc - 3, argv + 3); + + if (!strcmp(argv[3], "dump")) + return do_e1000_eeprom_dump(cmdtp, hw, argc - 3, argv + 3); + + if (!strcmp(argv[3], "program")) + return do_e1000_eeprom_program(cmdtp, hw, argc - 3, argv + 3); + + if (!strcmp(argv[3], "checksum")) + return do_e1000_eeprom_checksum(cmdtp, hw, argc - 3, argv + 3); + + cmd_usage(cmdtp); + return 1; +} + +U_BOOT_CMD( + e1000, 7, 0, do_e1000, + "Intel e1000 controller management", + /* */"<card#> eeprom show [<offset> [<length>]]\n" + "e1000 <card#> eeprom dump <addr> <offset> <length>\n" + "e1000 <card#> eeprom program <addr> <offset> <length>\n" + "e1000 <card#> eeprom checksum [update]\n" + " - Manage the e1000 card's SPI EEPROM" +); + +#endif /* CONFIG_CMD_E1000 */ diff --git a/drivers/net/e1000.h b/drivers/net/e1000.h index 8573511..68a3409 100644 --- a/drivers/net/e1000.h +++ b/drivers/net/e1000.h @@ -34,6 +34,7 @@ #define _E1000_HW_H_
#include <common.h> +#include <linux/list.h> #include <malloc.h> #include <net.h> #include <netdev.h> @@ -1043,6 +1044,7 @@ typedef enum {
/* Structure containing variables used by the shared code (e1000_hw.c) */ struct e1000_hw { + struct list_head list_node; struct eth_device *nic; unsigned int cardnum;

Dear Kyle Moffett,
In message 1297467482-14864-5-git-send-email-Kyle.D.Moffett@boeing.com you wrote:
For our new board ports, we are programming the EEPROMs attached to our Intel 82571EB controllers from software (using U-Boot and Linux).
This code provides a helpful set of "e1000" subcommands for performing EEPROM manipulation on e1000 devices, including displaying a hex-dump, copying to and from main memory, and verifying/updating of the software checksum.
The following commands work for programming the EEPROM from USB: usb start fatload usb 0 $loadaddr 82571EB_No_Mgmt_Discrete-LOM.bin e1000 0 eeprom program $loadaddr 0 1024 e1000 0 eeprom checksum update
Please keep in mind that the Intel-provided .eep files are organized as 16-bit words. When converting them to binary form for programming you must byteswap each 16-bit word so that it is in little-endian form.
This means that when reading and writing words to the SPI EEPROM, the bit ordering for each word looks like this on the wire:
I don't like the idea of having this in the driver code itself. It is a separate function, which should be implemented in a separate source file.
Eventually this should not even be linked with U-Boot, but kept separate as lodable module, like eepro100_eeprom.c, smc911x_eeprom.c and smc91111_eeprom.c.
What do you think?
Best regards,
Wolfgang Denk

On Apr 12, 2011, at 16:24, Wolfgang Denk wrote:
In message 1297467482-14864-5-git-send-email-Kyle.D.Moffett@boeing.com you wrote:
For our new board ports, we are programming the EEPROMs attached to our Intel 82571EB controllers from software (using U-Boot and Linux).
This code provides a helpful set of "e1000" subcommands for performing EEPROM manipulation on e1000 devices, including displaying a hex-dump, copying to and from main memory, and verifying/updating of the software checksum.
The following commands work for programming the EEPROM from USB: usb start fatload usb 0 $loadaddr 82571EB_No_Mgmt_Discrete-LOM.bin e1000 0 eeprom program $loadaddr 0 1024 e1000 0 eeprom checksum update
Please keep in mind that the Intel-provided .eep files are organized as 16-bit words. When converting them to binary form for programming you must byteswap each 16-bit word so that it is in little-endian form.
This means that when reading and writing words to the SPI EEPROM, the bit ordering for each word looks like this on the wire:
I don't like the idea of having this in the driver code itself. It is a separate function, which should be implemented in a separate source file.
Eventually this should not even be linked with U-Boot, but kept separate as lodable module, like eepro100_eeprom.c, smc911x_eeprom.c and smc91111_eeprom.c.
Hmm, there seem to be some fairly significant functionality differences between this e1000 driver and those other EEPROM drivers.
In particular, those other eeprom drivers simply have a single hardcoded I/O base address that they assume is properly mapped, IE:
struct eth_device dev; dev.iobase = CONFIG_SMC911X_BASE;
That won't really work with the way the existing E1000 driver is set up; it expects to run with full PCI device access using the standard U-Boot PCI calls. It seems to get very confused if you poke at the hardware behind its back.
Also, I can't see any way to allow CONFIG_CMD_SPI to link against an external "app" (the functionality provided by patch 5/5).
Finally, from an actual operational standpoint, this EEPROM driver is designed to allow the user to program up to 64kB of information to the E1000 EEPROM, including manageability firmware and other stuff; speed and copy-to/from-memory operations are very important.
The other EEPROM "apps" only really support changing individual bytes one-at-a-time and reprogramming the MAC address, which is insufficient for initial hardware load.
If you agree then I will modify the patch to move the new code into a separate e1000_eeprom.c file which is conditionally compiled, but still linked into the main U-Boot image.
Thanks for your comments!
Cheers, Kyle Moffett

Dear "Moffett, Kyle D",
In message 0EF7E520-FF4A-435F-AF2A-0D47C0951B34@boeing.com you wrote:
Eventually this should not even be linked with U-Boot, but kept separate as lodable module, like eepro100_eeprom.c, smc911x_eeprom.c and smc91111_eeprom.c.
Hmm, there seem to be some fairly significant functionality differences between this e1000 driver and those other EEPROM drivers.
I did not claim otherwise.
In particular, those other eeprom drivers simply have a single hardcoded I/O base address that they assume is properly mapped, IE:
struct eth_device dev; dev.iobase = CONFIG_SMC911X_BASE;
That won't really work with the way the existing E1000 driver is set up; it expects to run with full PCI device access using the standard U-Boot PCI calls. It seems to get very confused if you poke at the hardware behind its back.
I'm not really sure how this is related to the EEPROM access part of the code. This is functionally separate from the network driver, isn't it?
And I don't really see where the EEPROM part needs to be PCI aware.
Finally, from an actual operational standpoint, this EEPROM driver is designed to allow the user to program up to 64kB of information to the E1000 EEPROM, including manageability firmware and other stuff; speed and copy-to/from-memory operations are very important.
These should be important to all kinds of drivers.
The other EEPROM "apps" only really support changing individual bytes one-at-a-time and reprogramming the MAC address, which is insufficient for initial hardware load.
I wonder if we could / should unify all this code.
If you agree then I will modify the patch to move the new code into a separate e1000_eeprom.c file which is conditionally compiled, but still linked into the main U-Boot image.
That's OK with me. [As long as it remains optional.]
Best regards,
Wolfgang Denk

On Apr 13, 2011, at 01:23, Wolfgang Denk wrote:
In message 0EF7E520-FF4A-435F-AF2A-0D47C0951B34@boeing.com you wrote:
In particular, those other eeprom drivers simply have a single hardcoded I/O base address that they assume is properly mapped, IE:
struct eth_device dev; dev.iobase = CONFIG_SMC911X_BASE;
That won't really work with the way the existing E1000 driver is set up; it expects to run with full PCI device access using the standard U-Boot PCI calls. It seems to get very confused if you poke at the hardware behind its back.
I'm not really sure how this is related to the EEPROM access part of the code. This is functionally separate from the network driver, isn't it?
And I don't really see where the EEPROM part needs to be PCI aware.
No, the EEPROM driver relies upon the network driver having initialized the hardware correctly (including mapping PCI BARs, etc). The SPI bus to the EEPROM is also shared with the network hardware and firmware, and requires the use of other shared arbitration register bits.
Given that the E1000 is always a PCI device, I can't see why you would ever build an E1000 EEPROM programmer which was *not* PCI aware.
I honestly have no clue what IO address the E1000 BARs actually get mapped at, nor do I really care; that is (and should always be) entirely abstracted away by the U-Boot PCI layer.
Finally, from an actual operational standpoint, this EEPROM driver is designed to allow the user to program up to 64kB of information to the E1000 EEPROM, including manageability firmware and other stuff; speed and copy-to/from-memory operations are very important.
These should be important to all kinds of drivers.
I agree, but for the other ethernet eeprom drivers I don't think it matters. From what I understand the existing programmers support at most 256-byte EEPROMs (because that is what the hardware supports). The E1000 is unique among the ethernet eeprom drivers in U-Boot because of the size of the chip.
The other EEPROM "apps" only really support changing individual bytes one-at-a-time and reprogramming the MAC address, which is insufficient for initial hardware load.
I wonder if we could / should unify all this code.
It looks like most of the programmers right now use SPI, although from what I remember of the Linux code some of the E1000 chipsets use a different register interface to program other kinds of Flash memory (I think the embedded E1000 in the ICH/PCH chipsets do this).
The only real obstacle right now is that there can only be one SPI host driver in U-Boot at compile-time. To fix this we would need to create a "struct spi_host" abstraction with function pointers to allow multiple different hosts to be registered as bus-0, bus-1, etc.
Then the "EEPROM" commands could just talk to the appropriate device on each SPI bus.
If you agree then I will modify the patch to move the new code into a separate e1000_eeprom.c file which is conditionally compiled, but still linked into the main U-Boot image.
That's OK with me. [As long as it remains optional.]
Ok, I'll make the necessary changes and resubmit.
Cheers, Kyle Moffett

To make it possible to use the "sspi" command with the e1000 firmware EEPROM we add a small "generic SPI" driver wrapper around the existing e1000 SPI backend.
Signed-off-by: Kyle Moffett Kyle.D.Moffett@boeing.com --- drivers/net/e1000.c | 92 ++++++++++++++++++++++++++++++++++++++++++++++++++- drivers/net/e1000.h | 7 ++++ 2 files changed, 98 insertions(+), 1 deletions(-)
diff --git a/drivers/net/e1000.c b/drivers/net/e1000.c index b8bc27f..e841bc9 100644 --- a/drivers/net/e1000.c +++ b/drivers/net/e1000.c @@ -5264,7 +5264,7 @@ e1000_initialize(bd_t * bis) return i; }
-#ifdef CONFIG_CMD_E1000 +#if defined(CONFIG_E1000_SPI) || defined(CONFIG_CMD_E1000) static struct e1000_hw *e1000_find_card(unsigned int cardnum) { struct e1000_hw *hw; @@ -5343,6 +5343,96 @@ static int e1000_spi_xfer(struct e1000_hw *hw, unsigned int bitlen,
return 0; } +#endif /* defined(CONFIG_E1000_SPI) || defined(CONFIG_CMD_E1000) */ + +#ifdef CONFIG_E1000_SPI +static inline struct e1000_hw *e1000_hw_from_spi(struct spi_slave *spi) +{ + return container_of(spi, struct e1000_hw, spi); +} + +/* Not sure why all of these are necessary */ +void spi_init_r(void) { /* Nothing to do */ } +void spi_init_f(void) { /* Nothing to do */ } +void spi_init(void) { /* Nothing to do */ } + +struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs, + unsigned int max_hz, unsigned int mode) +{ + /* Find the right PCI device */ + struct e1000_hw *hw = e1000_find_card(bus); + if (!hw) { + printf("ERROR: No such e1000 device: e1000#%u\n", bus); + return NULL; + } + + /* Make sure it has an SPI chip */ + if (hw->eeprom.type != e1000_eeprom_spi) { + printf("%s: No attached SPI EEPROM found!\n", hw->nic->name); + return NULL; + } + + /* Argument sanity checks */ + if (cs != 0) { + printf("%s: No such SPI chip: %u\n", hw->nic->name, cs); + return NULL; + } + if (mode != SPI_MODE_0) { + printf("%s: Cannot support SPI modes other than MODE-0\n", + hw->nic->name); + return NULL; + } + + /* TODO: Use max_hz somehow */ + printf("%s: EEPROM SPI access requested\n", hw->nic->name); + return &hw->spi; +} + +void spi_free_slave(struct spi_slave *spi) +{ + struct e1000_hw *hw = e1000_hw_from_spi(spi); + printf("%s: EEPROM SPI access released\n", hw->nic->name); +} + +int spi_claim_bus(struct spi_slave *spi) +{ + struct e1000_hw *hw = e1000_hw_from_spi(spi); + + if (e1000_acquire_eeprom(hw)) { + printf("%s: EEPROM SPI cannot be acquired!", hw->nic->name); + return -1; + } + + return 0; +} + +void spi_release_bus(struct spi_slave *spi) +{ + struct e1000_hw *hw = e1000_hw_from_spi(spi); + e1000_release_eeprom(hw); +} + +/* Skinny wrapper around e1000_spi_xfer */ +int spi_xfer(struct spi_slave *spi, unsigned int bitlen, + const void *dout_mem, void *din_mem, unsigned long flags) +{ + struct e1000_hw *hw = e1000_hw_from_spi(spi); + int ret; + + if (flags & SPI_XFER_BEGIN) + e1000_standby_eeprom(hw); + + ret = e1000_spi_xfer(hw, bitlen, dout_mem, din_mem, TRUE); + + if (flags & SPI_XFER_END) + e1000_standby_eeprom(hw); + + return ret; +} + +#endif /* CONFIG_E1000_SPI */ + +#ifdef CONFIG_CMD_E1000
/* The EEPROM opcodes */ #define SPI_EEPROM_ENABLE_WR 0x06 diff --git a/drivers/net/e1000.h b/drivers/net/e1000.h index 68a3409..f504c90 100644 --- a/drivers/net/e1000.h +++ b/drivers/net/e1000.h @@ -41,6 +41,10 @@ #include <asm/io.h> #include <pci.h>
+#ifdef CONFIG_E1000_SPI +#include <spi.h> +#endif + #define E1000_ERR(args...) printf("e1000: " args)
#ifdef E1000_DEBUG @@ -1046,6 +1050,9 @@ typedef enum { struct e1000_hw { struct list_head list_node; struct eth_device *nic; +#ifdef CONFIG_E1000_SPI + struct spi_slave spi; +#endif unsigned int cardnum;
pci_dev_t pdev;

Dear Kyle Moffett,
In message 1297467482-14864-6-git-send-email-Kyle.D.Moffett@boeing.com you wrote:
To make it possible to use the "sspi" command with the e1000 firmware EEPROM we add a small "generic SPI" driver wrapper around the existing e1000 SPI backend.
Signed-off-by: Kyle Moffett Kyle.D.Moffett@boeing.com
Again, this should be a separate driver file.
Best regards,
Wolfgang Denk
participants (3)
-
Kyle Moffett
-
Moffett, Kyle D
-
Wolfgang Denk