diff mbox

[U-Boot,4/5] e1000: New "e1000" commands for SPI EEPROM management

Message ID 1297467482-14864-5-git-send-email-Kyle.D.Moffett@boeing.com
State Superseded, archived
Headers show

Commit Message

Kyle Moffett Feb. 11, 2011, 11:38 p.m. UTC
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(-)

Comments

Wolfgang Denk April 12, 2011, 8:24 p.m. UTC | #1
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
Kyle Moffett April 12, 2011, 11:26 p.m. UTC | #2
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
Wolfgang Denk April 13, 2011, 5:23 a.m. UTC | #3
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
Kyle Moffett April 13, 2011, 2:54 p.m. UTC | #4
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
diff mbox

Patch

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;