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@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@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@gmail.com --- Makefile | 9 ++ flashrom.c | 12 ++ nicintel_eeprom.c | 329 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ programmer.h | 9 ++ 4 files changed, 359 insertions(+) create mode 100644 nicintel_eeprom.c
diff --git a/Makefile b/Makefile index 70ef640..e9d5fbf 100644 --- a/Makefile +++ b/Makefile @@ -396,6 +396,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
@@ -560,6 +563,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.c b/flashrom.c index a00347e..0cb948b 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..9719c24 --- /dev/null +++ b/nicintel_eeprom.c @@ -0,0 +1,329 @@ +/* + * This file is part of the flashrom project. + * + * Copyright (C) 2013 Ricardo Ribalda - Qtechnology A/S + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* + * Datasheet: Intel 82580 Quad/Dual Gigabit Ethernet LAN Controller Datasheet + */ + +#include <stdlib.h> +#include <unistd.h> +#include "flash.h" +#include "programmer.h" +#include "hwaccess.h" + +#define PCI_VENDOR_ID_INTEL 0x8086 +#define MEMMAP_SIZE getpagesize() + +/* EEPROM/Flash Control & Data Register */ +#define EECD 0x10 +#define EERD 0x14 + +/* Eeprom Access register bits +* Section 8.4.1 +*/ +#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 + +#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 + +uint8_t *nicintel_eebar; +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; + tmp = pci_mmio_readl(nicintel_eebar + EECD); + 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("Non supported chip size 0x%x\n", tmp); + return 0; + } + } + + flash->chip->page_size = PAGE_MASK + 1; + flash->chip->tested = TEST_OK_PREW; + 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; +} + +#define RETRIES_EERD 10000000 +static int nicintel_ee_read_word(unsigned int daddr, uint16_t * word) +{ + uint32_t tmp; + int i; + + tmp = BIT(EERD_START) + (daddr << EERD_ADDR); + pci_mmio_writel(tmp, nicintel_eebar + EERD); + + for (i = 0; i < RETRIES_EERD; 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; + + if (addr & 1) { + if (nicintel_ee_read_word(addr / 2, &word)) + return -1; + *buf++ = word & 0xff; + addr++; + len--; + } + + while (len) { + if (nicintel_ee_read_word(addr / 2, &word)) + return -1; + *buf++ = word & 0xff; + addr++; + len--; + if (len) { + *buf++ = (word >> 8) & 0xff; + addr++; + len--; + } + } + + return 0; +} + +static int nicintel_ee_bitset(int reg, int bit, int 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(EECD, EE_SI, mosi & BIT(i)); + nicintel_ee_bitset(EECD, EE_SCK, 1); + if (miso) { + uint32_t tmp = pci_mmio_readl(nicintel_eebar + EECD); + if (tmp & BIT(EE_SO)) + out |= BIT(i); + } + nicintel_ee_bitset(EECD, EE_SCK, 0); + } + + if (miso) + *miso = out; + + return 0; +} + +#define OPCODE_RDSR 0x5 +#define RETRIES_RDSR 100 +#define RDSR_WIP 0 +static int nicintel_ee_ready() +{ + int i; + uint8_t rdsr; + + nicintel_ee_bitset(EECD, EE_CS, 0); + nicintel_ee_byte(OPCODE_RDSR, NULL); + for (i = 0; i < RETRIES_RDSR; i++) { + nicintel_ee_byte(0x00, &rdsr); + if (!(rdsr & BIT(RDSR_WIP))) { + nicintel_ee_bitset(EECD, EE_CS, 1); + programmer_delay(1); + return 0; + } + } + + nicintel_ee_bitset(EECD, EE_CS, 1); + programmer_delay(1); + + return -1; +} + +static int nicintel_ee_req() +{ + uint32_t tmp; + nicintel_ee_bitset(EECD, EE_REQ, 1); + + tmp = pci_mmio_readl(nicintel_eebar + EECD); + if (!(tmp & BIT(EE_GNT))) { + msg_perr("Enabling eeprom access failed.\n"); + return 1; + } + + nicintel_ee_bitset(EECD, EE_SCK, 0); + return 0; +} + +#define DELAY_WRITE 1000 +#define OPCODE_WRITE 0x2 +#define OPCODE_WREN 0x6 +static int nicintel_ee_write(struct flashctx *flash, uint8_t * buf, + unsigned int addr, unsigned int len) +{ + if (nicintel_ee_req()) + return -1; + + if (nicintel_ee_ready()) { + nicintel_ee_bitset(EECD, EE_REQ, 0); + return -1; + } + + while (len) { + //wren + nicintel_ee_bitset(EECD, EE_CS, 0); + nicintel_ee_byte(OPCODE_WREN, NULL); + nicintel_ee_bitset(EECD, EE_CS, 1); + programmer_delay(1); + + //data + nicintel_ee_bitset(EECD, EE_CS, 0); + nicintel_ee_byte(OPCODE_WRITE, NULL); + nicintel_ee_byte((addr >> 8) & 0xff, NULL); + nicintel_ee_byte(addr & 0xff, NULL); + while (len) { + nicintel_ee_byte((buf) ? *buf++ : 0xff, NULL); + len--; + addr++; + if (!(addr & PAGE_MASK)) + break; + } + nicintel_ee_bitset(EECD, EE_CS, 1); + programmer_delay(1); + if (nicintel_ee_ready()) { + nicintel_ee_bitset(EECD, EE_REQ, 0); + return -1; + } + } + + nicintel_ee_bitset(EECD, EE_REQ, 0); + return 0; +} + +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 *data) +{ + + /* See the comment about EECD in nicintel_ee_init() for details. */ + + return 0; +} + +int nicintel_ee_init(void) +{ + struct pci_dev *dev = NULL; + uint32_t tmp; + + 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; + /* Automatic restore of EECD on shutdown is not possible because EECD + * does not only contain EE_REQ but other bits with side effects as well. + * Those other bits must be left untouched. + */ + if (dev->device_id != UNPROG_DEVICE) { + tmp = pci_mmio_readl(nicintel_eebar + EECD); + + if (!(tmp & BIT(EE_PRES))) { + msg_perr("EEprom not present.\n"); + return 1; + } + } + + if (register_shutdown(nicintel_spi_shutdown, NULL)) + return 1; + + return register_opaque_programmer(&opaque_programmer_nicintel_ee); +} diff --git a/programmer.h b/programmer.h index f792d9a..dcf9911 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 @@ -406,6 +409,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);