On Mon, 12 May 2014 11:52:24 +0200 Ricardo Ribalda Delgado ricardo.ribalda@gmail.com wrote:
On Sun, May 11, 2014 at 11:14 PM, Stefan Tauner stefan.tauner@alumni.tuwien.ac.at wrote:
On Thu, 5 Sep 2013 15:45:07 +0200 Ricardo Ribalda Delgado ricardo.ribalda@gmail.com wrote:
This probably means that we want to copy parts of the copyright lines from those files here. I don't care too much about this, but Carl-Daniel probably does and it would be the right thing™ to do. The least effort would be to copy them all... Carl-Daniel, what do you think? For now I have only added myself for the opaque programmer implementation in ichspi.c and my changes today.
That depends how you handle copyright on your project. The "based on" declaration it is usually enough. The author line it is generally used to specify who to cc on a bug/patch.
It's totally OK for me, but Carl-Daniel is rather picky about these things.
+/* EEPROM/Flash Control & Data Register */ +#define EECD 0x10
Where does the D in EECD comes from? My datasheet denotes it EEC only.
To be consistent with the names of the other parts of flashrom. Just in case in the future you want to add a big define for all the intel cards. The kernel does this:
driver/ethernet/intel/e1000/e1000_hw.h
Ah! I did not notice that the registers of the older cards are almost the same (although the supported eeproms are different). I'll use the names of the respective datasheet nevertheless in this case.
+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"},
Should we include more than this? First of all the devices that were tested by others, but also untested devices that should actually work.
I would recommend only adding tested devices, but it is your call.
That would require the user to recompile flashrom without gain. We use the OK, NT etc. enum values to indicate if the device has been tested. I have added the other device IDs mentioned in the datasheet.
{0},
+};
+static int nicintel_ee_probe(struct flashctx *flash) +{
if (nicintel_pci->device_id == UNPROG_DEVICE)
flash->chip->total_size = 16;
Why is this a special case? AFAICS this default ID is given if no EEPROM is attached although I guess one could store exactly this ID in the EEPROM too? UNPROG seems to indicate that this is also set for empty EEPROMs. Isn't this dead code in case no EEPROM is attached due o the check for EE_PRES in the init function? And why doesn't the probe fail but defaults to 16kB instead? I seem to neither understand Intel's auto-detection of the EEPROM size nor your interpretation.
The intel card recognizes the eeprom size based on the eeprom content: Word 0x12, bit 13:10
A device shows itself as UNPROG_DEVICE if the eeprom content is wrong or empty.
In that case we have to default to the smallest size supported by the card : 16 Kbytes
OK, thanks, that makes sense. I have added an appropriate comments to the probe function and a reference to 3.3.1.5. OTOH, I am not convinced yet, that an unset EE_PRES bit is a complete show stopper yet. The description in 3.3.1.5 is either unrelated to EE_PRES or at least one of the descriptions (EE_PRES or 3.3.15) is wrong, because they contradict themselves IMHO.
While viewing the datasheet I have also noticed 4.7 which seems to have been ignored so far. I'll assume our module works nevertheless, but I have added a FIXME comment.
As fas as I know it is impossible to auto detect the size of an eeprom. (unless you write to it)
At least for those in questions this seems to be true.
+#define RETRIES_EERD 10000000
How did you obtain all retry values?
Trial and error.
Fair enough but it should be documented :)
/* 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.
*/
This is actually not true. We can of course store the state of the bits we want to restore and use that in a shutdown function that only restores these bits. I have implemented this in my patch for the bit banging output pins/bits and EE_REQ.
Then please also do the same on nicintel_spi.c :P
Well, the comment in nicintel_spi.c is referring to the pci_*r*mmio_writel functions that tidy up after themselves. The "semi-automatic" approach using registered shutdown functions works of course (and that's what implemented there too already). The only difference is that they unconditionally disable flash access there while we restored exactly the original state of the bits in question.
if (dev->device_id != UNPROG_DEVICE) {
And why this distinction again?
When the device does not have an eeprom/checksum is wrong, the EE_PRES bit is not reliable
Understood, thank you.
I have made some trivial changes on the code and added the manpage entry.
I had to revert a number of them again. We do use a line length of 112 characters, and I personally detest c89 variable declarations w/o good reason but prefer tighter scopes. Some of them made sense nevertheless, thanks.
I have also tested the code on my board and it looks ok. I have sended the patch with git send-email
Great, thanks. As the first EEPROM-accessing flashrom module it deserves its own section in the manpage and I also want to warn about possible breakage if EEPROM values become invalid for the NIC. I'll add a dedicated section to the manpage and commit it sometime this week if Carl-Daniel does not object. Thanks for all your efforts and patience.
Hello Stefan
Need help on this?
Cheers
On Tue, May 13, 2014 at 12:26 AM, Stefan Tauner stefan.tauner@alumni.tuwien.ac.at wrote:
On Mon, 12 May 2014 11:52:24 +0200 Ricardo Ribalda Delgado ricardo.ribalda@gmail.com wrote:
On Sun, May 11, 2014 at 11:14 PM, Stefan Tauner stefan.tauner@alumni.tuwien.ac.at wrote:
On Thu, 5 Sep 2013 15:45:07 +0200 Ricardo Ribalda Delgado ricardo.ribalda@gmail.com wrote:
This probably means that we want to copy parts of the copyright lines from those files here. I don't care too much about this, but Carl-Daniel probably does and it would be the right thing™ to do. The least effort would be to copy them all... Carl-Daniel, what do you think? For now I have only added myself for the opaque programmer implementation in ichspi.c and my changes today.
That depends how you handle copyright on your project. The "based on" declaration it is usually enough. The author line it is generally used to specify who to cc on a bug/patch.
It's totally OK for me, but Carl-Daniel is rather picky about these things.
+/* EEPROM/Flash Control & Data Register */ +#define EECD 0x10
Where does the D in EECD comes from? My datasheet denotes it EEC only.
To be consistent with the names of the other parts of flashrom. Just in case in the future you want to add a big define for all the intel cards. The kernel does this:
driver/ethernet/intel/e1000/e1000_hw.h
Ah! I did not notice that the registers of the older cards are almost the same (although the supported eeproms are different). I'll use the names of the respective datasheet nevertheless in this case.
+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"},
Should we include more than this? First of all the devices that were tested by others, but also untested devices that should actually work.
I would recommend only adding tested devices, but it is your call.
That would require the user to recompile flashrom without gain. We use the OK, NT etc. enum values to indicate if the device has been tested. I have added the other device IDs mentioned in the datasheet.
{0},
+};
+static int nicintel_ee_probe(struct flashctx *flash) +{
if (nicintel_pci->device_id == UNPROG_DEVICE)
flash->chip->total_size = 16;
Why is this a special case? AFAICS this default ID is given if no EEPROM is attached although I guess one could store exactly this ID in the EEPROM too? UNPROG seems to indicate that this is also set for empty EEPROMs. Isn't this dead code in case no EEPROM is attached due o the check for EE_PRES in the init function? And why doesn't the probe fail but defaults to 16kB instead? I seem to neither understand Intel's auto-detection of the EEPROM size nor your interpretation.
The intel card recognizes the eeprom size based on the eeprom content: Word 0x12, bit 13:10
A device shows itself as UNPROG_DEVICE if the eeprom content is wrong or empty.
In that case we have to default to the smallest size supported by the card : 16 Kbytes
OK, thanks, that makes sense. I have added an appropriate comments to the probe function and a reference to 3.3.1.5. OTOH, I am not convinced yet, that an unset EE_PRES bit is a complete show stopper yet. The description in 3.3.1.5 is either unrelated to EE_PRES or at least one of the descriptions (EE_PRES or 3.3.15) is wrong, because they contradict themselves IMHO.
While viewing the datasheet I have also noticed 4.7 which seems to have been ignored so far. I'll assume our module works nevertheless, but I have added a FIXME comment.
As fas as I know it is impossible to auto detect the size of an eeprom. (unless you write to it)
At least for those in questions this seems to be true.
+#define RETRIES_EERD 10000000
How did you obtain all retry values?
Trial and error.
Fair enough but it should be documented :)
/* 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.
*/
This is actually not true. We can of course store the state of the bits we want to restore and use that in a shutdown function that only restores these bits. I have implemented this in my patch for the bit banging output pins/bits and EE_REQ.
Then please also do the same on nicintel_spi.c :P
Well, the comment in nicintel_spi.c is referring to the pci_*r*mmio_writel functions that tidy up after themselves. The "semi-automatic" approach using registered shutdown functions works of course (and that's what implemented there too already). The only difference is that they unconditionally disable flash access there while we restored exactly the original state of the bits in question.
if (dev->device_id != UNPROG_DEVICE) {
And why this distinction again?
When the device does not have an eeprom/checksum is wrong, the EE_PRES bit is not reliable
Understood, thank you.
I have made some trivial changes on the code and added the manpage entry.
I had to revert a number of them again. We do use a line length of 112 characters, and I personally detest c89 variable declarations w/o good reason but prefer tighter scopes. Some of them made sense nevertheless, thanks.
I have also tested the code on my board and it looks ok. I have sended the patch with git send-email
Great, thanks. As the first EEPROM-accessing flashrom module it deserves its own section in the manpage and I also want to warn about possible breakage if EEPROM values become invalid for the NIC. I'll add a dedicated section to the manpage and commit it sometime this week if Carl-Daniel does not object. Thanks for all your efforts and patience. -- Kind regards/Mit freundlichen Grüßen, Stefan Tauner
This patch lets you read and write the EEPROM on 82580-based gigabit NIC cards. So far it has been tested on copper NICs only, but other variants employing this controller should work too. It is a nice substitution for the official 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 Signed-off-by: Stefan Tauner stefan.tauner@alumni.tuwien.ac.at Acked-by: Stefan Tauner stefan.tauner@alumni.tuwien.ac.at --- Makefile | 9 ++ flashrom.8.tmpl | 13 ++- flashrom.c | 12 ++ nicintel_eeprom.c | 334 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ programmer.h | 9 ++ 5 files changed, 376 insertions(+), 1 deletion(-) create mode 100644 nicintel_eeprom.c
diff --git a/Makefile b/Makefile index 7a48889..b47b644 100644 --- a/Makefile +++ b/Makefile @@ -426,6 +426,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
@@ -626,6 +629,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 0b4867d..d3000c2 100644 --- a/flashrom.8.tmpl +++ b/flashrom.8.tmpl @@ -225,6 +225,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 @@ -591,7 +593,7 @@ 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" , " atahpt" ", " atavia " and " it8212 " programmers These programmers have an option to specify the PCI address of the card @@ -625,6 +627,14 @@ For more information please see .nh .B http://flashrom.org/VT6421A .SS +.BR "nicintel_eeprom " programmer +This is the first programmer module in flashrom that does not provide access to NOR flash chips but EEPROMs +mounted on gigabit Ethernet cards based on Intel's 82580 NIC. Because EEPROMs normally do not announce their +size nor allow to be identified, the controller relies on correct size values written to predefined addresses +within the chip. Flashrom follows this scheme but assumes the minimum size of 16 kB (128 kb) if an unprogrammed +EEPROM/card is detected. Intel specifies following EEPROMs to be compatible: Atmel AT25128, AT25256, Micron (ST) +M95128, M95256 and OnSemi (Catalyst) CAT25CS128. +.SS .BR "ft2232_spi " programmer An optional parameter specifies the controller type and channel/interface/port it should support. For that you have to use the @@ -903,6 +913,7 @@ flashrom exits with 0 on success, 1 on most failures but with 3 if a call to mma .SH REQUIREMENTS flashrom needs different access permissions for different programmers. .sp +." FIXME: nicintel* .B internal needs raw memory access, PCI configuration space access, raw I/O port access (x86) and MSR access (x86). diff --git a/flashrom.c b/flashrom.c index 408c555..7375b2e 100644 --- a/flashrom.c +++ b/flashrom.c @@ -297,6 +297,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..48fac4a --- /dev/null +++ b/nicintel_eeprom.c @@ -0,0 +1,334 @@ +/* + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. + */ + +/* + * Datasheet: Intel 82580 Quad/Dual Gigabit Ethernet LAN Controller Datasheet + * 3.3.1.4: General EEPROM Software Access + * 4.7: Access to shared resources (FIXME: we should probably use this semaphore interface) + * 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 Gigabit Ethernet Controller (Copper)"}, + {PCI_VENDOR_ID_INTEL, 0x150f, NT , "Intel", "82580 Quad Gigabit Ethernet Controller (Fiber)"}, + {PCI_VENDOR_ID_INTEL, 0x1510, NT , "Intel", "82580 Quad Gigabit Ethernet Controller (Backplane)"}, + {PCI_VENDOR_ID_INTEL, 0x1511, NT , "Intel", "82580 Quad Gigabit Ethernet Controller (Ext. PHY)"}, + {PCI_VENDOR_ID_INTEL, 0x1511, NT , "Intel", "82580 Dual Gigabit Ethernet Controller (Copper)"}, + {PCI_VENDOR_ID_INTEL, UNPROG_DEVICE, OK, "Intel", "Unprogrammed 82580 Quad/Dual Gigabit Ethernet Controller"}, + {0}, +}; + +static int nicintel_ee_probe(struct flashctx *flash) +{ + if (nicintel_pci->device_id == UNPROG_DEVICE) + flash->chip->total_size = 16; /* Fall back to minimum supported size. */ + 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); + pci_mmio_writel(tmp, nicintel_eebar + EERD); + + /* Poll done flag. 10.000.000 cycles seem to be enough. */ + uint32_t i; + 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; +} + +/* Shifts one byte out while receiving another one by bitbanging (denoted "direct access" in the datasheet). */ +static int nicintel_ee_bitbang(uint8_t mosi, uint8_t *miso) +{ + uint8_t out = 0x0; + + int i; + 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; +} + +/* Polls the WIP bit of the status register of the attached EEPROM via bitbanging. */ +static int nicintel_ee_ready(void) +{ + unsigned int i; + for (i = 0; i < 1000; i++) { + nicintel_ee_bitset(EEC, EE_CS, 0); + + nicintel_ee_bitbang(JEDEC_RDSR, NULL); + uint8_t rdsr; + nicintel_ee_bitbang(0x00, &rdsr); + + nicintel_ee_bitset(EEC, EE_CS, 1); + programmer_delay(1); + if (!(rdsr & SPI_SR_WIP)) { + return 0; + } + } + return -1; +} + +/* Requests direct access to the SPI pins. */ +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_bitbang(JEDEC_WREN, NULL); + nicintel_ee_bitset(EEC, EE_CS, 1); + programmer_delay(1); + + /* data */ + nicintel_ee_bitset(EEC, EE_CS, 0); + nicintel_ee_bitbang(JEDEC_BYTE_PROGRAM, NULL); + nicintel_ee_bitbang((addr >> 8) & 0xff, NULL); + nicintel_ee_bitbang(addr & 0xff, NULL); + while (len > 0) { + nicintel_ee_bitbang((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); /* Give up direct access. */ + 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) +{ + 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. */ + int 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)); + /* REQ will be cleared by hardware anyway after 2 seconds of inactivity on the SPI pins (3.3.2.1). */ + ret |= nicintel_ee_bitset(EEC, EE_REQ, old_eec & BIT(EE_REQ)); + + free(eecp); + return ret; +} + +int nicintel_ee_init(void) +{ + if (rget_io_perms()) + return 1; + + struct pci_dev *dev = pcidev_init(nics_intel_ee, PCI_BASE_ADDRESS_0); + if (!dev) + return 1; + + uint32_t 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); + + /* C.f. 3.3.1.5 for the detection mechanism (maybe? contradicting the EE_PRES definition), + * and 3.3.1.7 for possible recovery. */ + if (!(eec & BIT(EE_PRES))) { + msg_perr("Controller reports no EEPROM is present.\n"); + return 1; + } + + uint32_t *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 b0df2ba..83a05b9 100644 --- a/programmer.h +++ b/programmer.h @@ -84,6 +84,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 @@ -416,6 +419,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);