[flashrom] [PATCH v2] nicintel_eeprom: New programmer for Intel Nics eeproms

Ricardo Ribalda Delgado ricardo.ribalda at gmail.com
Mon May 12 11:51:15 CEST 2014


This patch let you read and write the eeprom on your gigagit nic card.
So far it has been tested on the 82580 NIC, but there should be other
boards also compatibles. It is a nice substitution for the eeupdate tool.

Speed is quite decent

root at qt5022:~# time ./flashrom -p nicintel_eeprom:pci=03:00.0 -E
flashrom v0.9.7-r1622 on Linux 3.11.0-qtec-standard (x86_64)
flashrom is free software, get the source code at http://www.flashrom.org

Calibrating delay loop... OK.
Found Programmer flash chip "Opaque flash chip" (32 kB, Programmer-specific) on nicintel_eeprom.
Erasing and writing flash chip... Erase/write done.

real	0m4.699s
user	0m4.676s
sys	0m0.024s
root at qt5022:~# time ./flashrom -p nicintel_eeprom:pci=03:00.0 -w original.kk
flashrom v0.9.7-r1622 on Linux 3.11.0-qtec-standard (x86_64)
flashrom is free software, get the source code at http://www.flashrom.org

Calibrating delay loop... OK.
Found Programmer flash chip "Opaque flash chip" (32 kB, Programmer-specific) on nicintel_eeprom.
Reading old flash chip contents... done.
Erasing and writing flash chip... Erase/write done.
Verifying flash... VERIFIED.

real	0m3.533s
user	0m3.516s
sys	0m0.018s

Signed-off-by: Ricardo Ribalda Delgado <ricardo.ribalda at gmail.com>
---
v2: -Changes by Stefan Tauner
    -Manpage


 Makefile          |   9 ++
 flashrom.8.tmpl   |   6 +-
 flashrom.c        |  12 ++
 nicintel_eeprom.c | 336 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 programmer.h      |   9 ++
 5 files changed, 370 insertions(+), 2 deletions(-)
 create mode 100644 nicintel_eeprom.c

diff --git a/Makefile b/Makefile
index 346d46a..c412bdc 100644
--- a/Makefile
+++ b/Makefile
@@ -408,6 +408,9 @@ CONFIG_NICINTEL ?= yes
 # Always enable SPI on Intel NICs for now.
 CONFIG_NICINTEL_SPI ?= yes
 
+# Always enable EEPROM on Intel NICs for now.
+CONFIG_NICINTEL_EEPROM ?= yes
+
 # Always enable SPI on OGP cards for now.
 CONFIG_OGP_SPI ?= yes
 
@@ -592,6 +595,12 @@ PROGRAMMER_OBJS += nicintel_spi.o
 NEED_PCI := yes
 endif
 
+ifeq ($(CONFIG_NICINTEL_EEPROM), yes)
+FEATURE_CFLAGS += -D'CONFIG_NICINTEL_EEPROM=1'
+PROGRAMMER_OBJS += nicintel_eeprom.o
+NEED_PCI := yes
+endif
+
 ifeq ($(CONFIG_OGP_SPI), yes)
 FEATURE_CFLAGS += -D'CONFIG_OGP_SPI=1'
 PROGRAMMER_OBJS += ogp_spi.o
diff --git a/flashrom.8.tmpl b/flashrom.8.tmpl
index 8d46ffe..6d400b4 100644
--- a/flashrom.8.tmpl
+++ b/flashrom.8.tmpl
@@ -221,6 +221,8 @@ bitbanging adapter)
 .sp
 .BR "* usbblaster_spi" " (for SPI flash ROMs attached to an Altera USB-Blaster compatible cable)"
 .sp
+.BR "* nicintel_eeprom" " (for SPI EEPROMs on Intel Gigabit network cards)"
+.sp
 Some programmers have optional or mandatory parameters which are described
 in detail in the
 .B PROGRAMMER SPECIFIC INFO
@@ -566,9 +568,9 @@ syntax where
 .B content
 is an 8-bit hexadecimal value.
 .SS
-.BR "nic3com" , " nicrealtek" , " nicnatsemi" , " nicintel\
+.BR "nic3com" , " nicrealtek" , " nicnatsemi" , " nicintel", " nicintel_eeprom\
 " , " nicintel_spi" , " gfxnvidia" , " ogp_spi" , " drkaiser" , " satasii\
-" , " satamv" ", and " atahpt " programmers
+" , " satamv", ", and " atahpt " programmers
 These programmers have an option to specify the PCI address of the card
 your want to use, which must be specified if more than one card supported
 by the selected programmer is installed in your system. The syntax is
diff --git a/flashrom.c b/flashrom.c
index b3661c1..1b0a687 100644
--- a/flashrom.c
+++ b/flashrom.c
@@ -273,6 +273,18 @@ const struct programmer_entry programmer_table[] = {
 	},
 #endif
 
+#if CONFIG_NICINTEL_EEPROM == 1
+	{
+		.name			= "nicintel_eeprom",
+		.type			= PCI,
+		.devs.dev		= nics_intel_ee,
+		.init			= nicintel_ee_init,
+		.map_flash_region	= fallback_map,
+		.unmap_flash_region	= fallback_unmap,
+		.delay			= internal_delay,
+	},
+#endif
+
 #if CONFIG_OGP_SPI == 1
 	{
 		.name			= "ogp_spi",
diff --git a/nicintel_eeprom.c b/nicintel_eeprom.c
new file mode 100644
index 0000000..c80be37
--- /dev/null
+++ b/nicintel_eeprom.c
@@ -0,0 +1,336 @@
+/*
+ * This file is part of the flashrom project.
+ *
+ * Copyright (C) 2013 Ricardo Ribalda - Qtechnology A/S
+ * Copyright (C) 2011, 2014 Stefan Tauner
+ *
+ * Based on nicinctel_spi.c and ichspi.c
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+/*
+ * Datasheet: Intel 82580 Quad/Dual Gigabit Ethernet LAN Controller Datasheet
+ * Section 7.4: Register Descriptions
+ */
+
+#include <stdlib.h>
+#include <unistd.h>
+#include "flash.h"
+#include "spi.h"
+#include "programmer.h"
+#include "hwaccess.h"
+
+#define PCI_VENDOR_ID_INTEL 0x8086
+#define MEMMAP_SIZE (0x14 + 3) /* Only EEC and EERD are needed. */
+
+#define EEC	0x10 /* EEPROM/Flash Control Register */
+#define EERD	0x14 /* EEPROM Read Register */
+
+/* EPROM/Flash Control Register bits */
+#define EE_SCK	0
+#define EE_CS	1
+#define EE_SI	2
+#define EE_SO	3
+#define EE_REQ	6
+#define EE_GNT	7
+#define EE_PRES	8
+#define EE_SIZE 11
+#define EE_SIZE_MASK 0xf
+
+/* EEPROM Read Register bits */
+#define EERD_START 0
+#define EERD_DONE 1
+#define EERD_ADDR 2
+#define EERD_DATA 16
+
+#define BIT(x) (1<<x)
+#define PAGE_MASK 0x3f
+
+static uint8_t *nicintel_eebar;
+static struct pci_dev *nicintel_pci;
+
+#define UNPROG_DEVICE 0x1509
+
+const struct dev_entry nics_intel_ee[] = {
+	{PCI_VENDOR_ID_INTEL, 0x150e, OK, "Intel",
+		"82580 Quad/Dual Gigabit Ethernet LAN Controller"},
+	{PCI_VENDOR_ID_INTEL, UNPROG_DEVICE, OK, "Intel",
+		"Unprogrammed 82580 Quad/Dual "
+		"Gigabit Ethernet LAN Controller"},
+	{0},
+};
+
+static int nicintel_ee_probe(struct flashctx *flash)
+{
+	if (nicintel_pci->device_id == UNPROG_DEVICE)
+		flash->chip->total_size = 16;
+	else{
+		uint32_t tmp = pci_mmio_readl(nicintel_eebar + EEC);
+		tmp = ((tmp >> EE_SIZE) & EE_SIZE_MASK);
+		switch (tmp) {
+		case 7:
+			flash->chip->total_size = 16;
+			break;
+		case 8:
+			flash->chip->total_size = 32;
+			break;
+		default:
+			msg_cerr("Unsupported chip size 0x%x\n", tmp);
+			return 0;
+		}
+	}
+
+	flash->chip->page_size = PAGE_MASK + 1;
+	flash->chip->tested = TEST_OK_PREW;
+	/* FIXME: Due to the write_granularity set below we do 0xFF dummy
+	 * writes to fake erases on every write which is completely pointless
+	 * and potentially even harmful for the hardware. Programmers/devices
+	 * should be able to indicate that erases are not needed for writes. */
+	flash->chip->gran = write_gran_1byte;
+	flash->chip->block_erasers->eraseblocks[0].size = (PAGE_MASK + 1);
+	flash->chip->block_erasers->eraseblocks[0].count =
+	    (flash->chip->total_size * 1024) / (PAGE_MASK + 1);
+
+	return 1;
+}
+
+static int nicintel_ee_read_word(unsigned int addr, uint16_t *word)
+{
+	uint32_t tmp = BIT(EERD_START) | (addr << EERD_ADDR);
+	uint32_t i;
+
+	pci_mmio_writel(tmp, nicintel_eebar + EERD);
+
+	/* Poll done flag */
+	for (i = 0; i < 10000000; i++) {
+		tmp = pci_mmio_readl(nicintel_eebar + EERD);
+		if (tmp & BIT(EERD_DONE)) {
+			*word = (tmp >> EERD_DATA) & 0xffff;
+			return 0;
+		}
+	}
+
+	return -1;
+}
+
+static int nicintel_ee_read(struct flashctx *flash, uint8_t *buf,
+				unsigned int addr, unsigned int len)
+{
+	uint16_t word;
+
+	/* The NIC interface always reads 16 b words so we need to convert the
+	 * address and handle odd address explicitly at the start (and also
+	 * at the end in the loop below). */
+	if (addr & 1) {
+		if (nicintel_ee_read_word(addr / 2, &word))
+			return -1;
+		*buf++ = word & 0xff;
+		addr++;
+		len--;
+	}
+
+	while (len > 0) {
+		if (nicintel_ee_read_word(addr / 2, &word))
+			return -1;
+		*buf++ = word & 0xff;
+		addr++;
+		len--;
+		if (len > 0) {
+			*buf++ = (word >> 8) & 0xff;
+			addr++;
+			len--;
+		}
+	}
+
+	return 0;
+}
+
+static int nicintel_ee_bitset(int reg, int bit, bool val)
+{
+	uint32_t tmp;
+
+	tmp = pci_mmio_readl(nicintel_eebar + reg);
+	if (val)
+		tmp |= BIT(bit);
+	else
+		tmp &= ~BIT(bit);
+	pci_mmio_writel(tmp, nicintel_eebar + reg);
+
+	return -1;
+}
+
+static int nicintel_ee_byte(uint8_t mosi, uint8_t *miso)
+{
+	int i;
+	uint8_t out = 0x0;
+
+	for (i = 7; i >= 0; i--) {
+		nicintel_ee_bitset(EEC, EE_SI, mosi & BIT(i));
+		nicintel_ee_bitset(EEC, EE_SCK, 1);
+		if (miso != NULL) {
+			uint32_t tmp = pci_mmio_readl(nicintel_eebar + EEC);
+			if (tmp & BIT(EE_SO))
+				out |= BIT(i);
+		}
+		nicintel_ee_bitset(EEC, EE_SCK, 0);
+	}
+
+	if (miso != NULL)
+		*miso = out;
+
+	return 0;
+}
+
+static int nicintel_ee_ready(void)
+{
+	unsigned int i;
+
+	for (i = 0; i < 1000; i++) {
+		uint8_t rdsr;
+
+		nicintel_ee_bitset(EEC, EE_CS, 0);
+		nicintel_ee_byte(JEDEC_RDSR, NULL);
+		nicintel_ee_byte(0x00, &rdsr);
+		nicintel_ee_bitset(EEC, EE_CS, 1);
+		programmer_delay(1);
+		if (!(rdsr & SPI_SR_WIP))
+			return 0;
+	}
+
+	return -1;
+}
+
+static int nicintel_ee_req(void)
+{
+	uint32_t tmp;
+	nicintel_ee_bitset(EEC, EE_REQ, 1);
+
+	tmp = pci_mmio_readl(nicintel_eebar + EEC);
+	if (!(tmp & BIT(EE_GNT))) {
+		msg_perr("Enabling eeprom access failed.\n");
+		return 1;
+	}
+
+	nicintel_ee_bitset(EEC, EE_SCK, 0);
+	return 0;
+}
+
+static int nicintel_ee_write(struct flashctx *flash, const uint8_t *buf,
+					unsigned int addr, unsigned int len)
+{
+	if (nicintel_ee_req())
+		return -1;
+
+	int ret = -1;
+	if (nicintel_ee_ready())
+		goto out;
+
+	while (len > 0) {
+		/*wren*/
+		nicintel_ee_bitset(EEC, EE_CS, 0);
+		nicintel_ee_byte(JEDEC_WREN, NULL);
+		nicintel_ee_bitset(EEC, EE_CS, 1);
+		programmer_delay(1);
+
+		/*data*/
+		nicintel_ee_bitset(EEC, EE_CS, 0);
+		nicintel_ee_byte(JEDEC_BYTE_PROGRAM, NULL);
+		nicintel_ee_byte((addr >> 8) & 0xff, NULL);
+		nicintel_ee_byte(addr & 0xff, NULL);
+		while (len > 0) {
+			nicintel_ee_byte((buf) ? *buf++ : 0xff, NULL);
+			len--;
+			addr++;
+			if (!(addr & PAGE_MASK))
+				break;
+		}
+		nicintel_ee_bitset(EEC, EE_CS, 1);
+		programmer_delay(1);
+		if (nicintel_ee_ready())
+			goto out;
+	}
+	ret = 0;
+out:
+	nicintel_ee_bitset(EEC, EE_REQ, 0);
+	return ret;
+}
+
+static int nicintel_ee_erase(struct flashctx *flash, unsigned int addr,
+							unsigned int len)
+{
+	return nicintel_ee_write(flash, NULL, addr, len);
+}
+
+static const struct opaque_programmer opaque_programmer_nicintel_ee = {
+	.probe = nicintel_ee_probe,
+	.read = nicintel_ee_read,
+	.write = nicintel_ee_write,
+	.erase = nicintel_ee_erase,
+};
+
+static int nicintel_spi_shutdown(void *eecp)
+{
+	int ret;
+	uint32_t old_eec = *(uint32_t *)eecp;
+
+	/* Request bitbanging and unselect the chip first to be safe. */
+	if (nicintel_ee_req() || nicintel_ee_bitset(EEC, EE_CS, 1))
+		return -1;
+
+	/* Try to restore individual bits we care about. */
+	ret = nicintel_ee_bitset(EEC, EE_SCK, old_eec & BIT(EE_SCK));
+	ret |= nicintel_ee_bitset(EEC, EE_SI, old_eec & BIT(EE_SI));
+	ret |= nicintel_ee_bitset(EEC, EE_CS, old_eec & BIT(EE_CS));
+	ret |= nicintel_ee_bitset(EEC, EE_REQ, old_eec & BIT(EE_REQ));
+
+	free(eecp);
+	return ret;
+}
+
+int nicintel_ee_init(void)
+{
+	struct pci_dev *dev;
+
+	if (rget_io_perms())
+		return 1;
+
+	dev = pcidev_init(nics_intel_ee, PCI_BASE_ADDRESS_0);
+	if (!dev)
+		return 1;
+
+	io_base_addr = pcidev_readbar(dev, PCI_BASE_ADDRESS_0);
+	if (!io_base_addr)
+		return 1;
+
+	nicintel_eebar = rphysmap("Intel Gigabit NIC w/ SPI EEPROM",
+						io_base_addr, MEMMAP_SIZE);
+	nicintel_pci = dev;
+	if (dev->device_id != UNPROG_DEVICE) {
+		uint32_t eec = pci_mmio_readl(nicintel_eebar + EEC);
+		uint32_t *eecp;
+
+		if (!(eec & BIT(EE_PRES))) {
+			msg_perr("EEPROM not present.\n");
+			return 1;
+		}
+
+		eecp = malloc(sizeof(uint32_t));
+		if (eecp == NULL)
+			return 1;
+		*eecp = eec;
+
+		if (register_shutdown(nicintel_spi_shutdown, eecp))
+			return 1;
+	}
+
+	return register_opaque_programmer(&opaque_programmer_nicintel_ee);
+}
diff --git a/programmer.h b/programmer.h
index 7670784..84a4c68 100644
--- a/programmer.h
+++ b/programmer.h
@@ -78,6 +78,9 @@ enum programmer {
 #if CONFIG_NICINTEL_SPI == 1
 	PROGRAMMER_NICINTEL_SPI,
 #endif
+#if CONFIG_NICINTEL_EEPROM == 1
+	PROGRAMMER_NICINTEL_EEPROM,
+#endif
 #if CONFIG_OGP_SPI == 1
 	PROGRAMMER_OGP_SPI,
 #endif
@@ -410,6 +413,12 @@ int nicintel_spi_init(void);
 extern const struct dev_entry nics_intel_spi[];
 #endif
 
+/* nicintel_eeprom.c */
+#if CONFIG_NICINTEL_EEPROM == 1
+int nicintel_ee_init(void);
+extern const struct dev_entry nics_intel_ee[];
+#endif
+
 /* ogp_spi.c */
 #if CONFIG_OGP_SPI == 1
 int ogp_spi_init(void);
-- 
2.0.0.rc0





More information about the flashrom mailing list