summaryrefslogtreecommitdiffstats
path: root/drivers/net/e1000/mtd.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/e1000/mtd.c')
-rw-r--r--drivers/net/e1000/mtd.c835
1 files changed, 835 insertions, 0 deletions
diff --git a/drivers/net/e1000/mtd.c b/drivers/net/e1000/mtd.c
new file mode 100644
index 0000000000..93595cc88d
--- /dev/null
+++ b/drivers/net/e1000/mtd.c
@@ -0,0 +1,835 @@
+#include <common.h>
+#include <init.h>
+#include <malloc.h>
+#include <linux/math64.h>
+#include <linux/sizes.h>
+#include <of_device.h>
+#include <linux/pci.h>
+#include <linux/mtd/spi-nor.h>
+
+#include "e1000.h"
+
+static int32_t e1000_acquire_eeprom(struct e1000_hw *hw)
+{
+ if (hw->eeprom.acquire)
+ return hw->eeprom.acquire(hw);
+ else
+ return E1000_SUCCESS;
+}
+
+static void e1000_release_eeprom(struct e1000_hw *hw)
+{
+ if (hw->eeprom.release)
+ hw->eeprom.release(hw);
+}
+
+static int e1000_flash_mode_wait_for_idle(struct e1000_hw *hw)
+{
+ const int ret = e1000_poll_reg(hw, E1000_FLSWCTL, E1000_FLSWCTL_DONE,
+ E1000_FLSWCTL_DONE, SECOND);
+ if (ret < 0)
+ dev_err(hw->dev,
+ "Timeout waiting for FLSWCTL.DONE to be set (wait)\n");
+ return ret;
+}
+
+static int e1000_flash_mode_check_command_valid(struct e1000_hw *hw)
+{
+ const uint32_t flswctl = e1000_read_reg(hw, E1000_FLSWCTL);
+ if (!(flswctl & E1000_FLSWCTL_CMDV)) {
+ dev_err(hw->dev, "FLSWCTL.CMDV was cleared\n");
+ return -EIO;
+ }
+
+ return E1000_SUCCESS;
+}
+
+static void e1000_flash_cmd(struct e1000_hw *hw,
+ uint32_t cmd, uint32_t offset)
+{
+ uint32_t flswctl = e1000_read_reg(hw, E1000_FLSWCTL);
+ flswctl &= ~E1000_FLSWCTL_CMD_ADDR_MASK;
+ flswctl |= E1000_FLSWCTL_CMD(cmd) | E1000_FLSWCTL_ADDR(offset);
+ e1000_write_reg(hw, E1000_FLSWCTL, flswctl);
+}
+
+static int e1000_flash_mode_read_chunk(struct e1000_hw *hw, loff_t offset,
+ size_t size, void *data)
+{
+ int ret;
+ size_t chunk, residue = size;
+ uint32_t flswdata;
+
+ DEBUGFUNC();
+
+ if (size > SZ_4K ||
+ E1000_FLSWCTL_ADDR(offset) != offset)
+ return -EINVAL;
+
+ ret = e1000_flash_mode_wait_for_idle(hw);
+ if (ret < 0)
+ return ret;
+
+ e1000_write_reg(hw, E1000_FLSWCNT, size);
+ e1000_flash_cmd(hw, E1000_FLSWCTL_CMD_READ, offset);
+
+ do {
+ ret = e1000_flash_mode_check_command_valid(hw);
+ if (ret < 0)
+ return -EIO;
+
+ chunk = min(sizeof(flswdata), residue);
+
+ ret = e1000_poll_reg(hw, E1000_FLSWCTL,
+ E1000_FLSWCTL_DONE, E1000_FLSWCTL_DONE,
+ SECOND);
+ if (ret < 0) {
+ dev_err(hw->dev,
+ "Timeout waiting for FLSWCTL.DONE to be set (read)\n");
+ return ret;
+ }
+
+ flswdata = e1000_read_reg(hw, E1000_FLSWDATA);
+ /*
+ * Readl does le32_to_cpu, so we need to undo that
+ */
+ flswdata = cpu_to_le32(flswdata);
+ memcpy(data, &flswdata, chunk);
+
+ data += chunk;
+ residue -= chunk;
+ } while (residue);
+
+ return E1000_SUCCESS;
+}
+
+static int e1000_flash_mode_write_chunk(struct e1000_hw *hw, loff_t offset,
+ size_t size, const void *data)
+{
+ int ret;
+ size_t chunk, residue = size;
+ uint32_t flswdata;
+
+ if (size > 256 ||
+ E1000_FLSWCTL_ADDR(offset) != offset)
+ return -EINVAL;
+
+ ret = e1000_flash_mode_wait_for_idle(hw);
+ if (ret < 0)
+ return ret;
+
+
+ e1000_write_reg(hw, E1000_FLSWCNT, size);
+ e1000_flash_cmd(hw, E1000_FLSWCTL_CMD_WRITE, offset);
+
+ do {
+ chunk = min(sizeof(flswdata), residue);
+ memcpy(&flswdata, data, chunk);
+ /*
+ * writel does cpu_to_le32, so we do the inverse in
+ * order to account for that
+ */
+ flswdata = le32_to_cpu(flswdata);
+ e1000_write_reg(hw, E1000_FLSWDATA, flswdata);
+
+ ret = e1000_flash_mode_check_command_valid(hw);
+ if (ret < 0)
+ return -EIO;
+
+ ret = e1000_poll_reg(hw, E1000_FLSWCTL,
+ E1000_FLSWCTL_DONE, E1000_FLSWCTL_DONE,
+ SECOND);
+ if (ret < 0) {
+ dev_err(hw->dev,
+ "Timeout waiting for FLSWCTL.DONE to be set (write)\n");
+ return ret;
+ }
+
+ data += chunk;
+ residue -= chunk;
+
+ } while (residue);
+
+ return E1000_SUCCESS;
+}
+
+static int e1000_flash_mode_erase_chunk(struct e1000_hw *hw, loff_t offset,
+ size_t size)
+{
+ int ret;
+
+ ret = e1000_flash_mode_wait_for_idle(hw);
+ if (ret < 0)
+ return ret;
+
+ if (!size && !offset)
+ e1000_flash_cmd(hw, E1000_FLSWCTL_CMD_ERASE_DEVICE, 0);
+ else
+ e1000_flash_cmd(hw, E1000_FLSWCTL_CMD_ERASE_SECTOR, offset);
+
+ ret = e1000_flash_mode_check_command_valid(hw);
+ if (ret < 0)
+ return -EIO;
+
+ ret = e1000_poll_reg(hw, E1000_FLSWCTL,
+ E1000_FLSWCTL_DONE | E1000_FLSWCTL_FLBUSY,
+ E1000_FLSWCTL_DONE,
+ 40 * SECOND);
+ if (ret < 0) {
+ dev_err(hw->dev,
+ "Timeout waiting for FLSWCTL.DONE to be set (erase)\n");
+ return ret;
+ }
+
+ return E1000_SUCCESS;
+}
+
+enum {
+ E1000_FLASH_MODE_OP_READ = 0,
+ E1000_FLASH_MODE_OP_WRITE = 1,
+ E1000_FLASH_MODE_OP_ERASE = 2,
+};
+
+
+static int e1000_flash_mode_io(struct e1000_hw *hw, int op, size_t granularity,
+ loff_t offset, size_t size, void *data)
+{
+ int ret;
+ size_t residue = size;
+
+ do {
+ const size_t chunk = min(granularity, residue);
+
+ switch (op) {
+ case E1000_FLASH_MODE_OP_READ:
+ ret = e1000_flash_mode_read_chunk(hw, offset,
+ chunk, data);
+ break;
+ case E1000_FLASH_MODE_OP_WRITE:
+ ret = e1000_flash_mode_write_chunk(hw, offset,
+ chunk, data);
+ break;
+ case E1000_FLASH_MODE_OP_ERASE:
+ ret = e1000_flash_mode_erase_chunk(hw, offset,
+ chunk);
+ break;
+ default:
+ return -ENOTSUPP;
+ }
+
+ if (ret < 0)
+ return ret;
+
+ offset += chunk;
+ residue -= chunk;
+ data += chunk;
+ } while (residue);
+
+ return E1000_SUCCESS;
+}
+
+static int e1000_flash_mode_read(struct e1000_hw *hw, loff_t offset,
+ size_t size, void *data)
+{
+ return e1000_flash_mode_io(hw,
+ E1000_FLASH_MODE_OP_READ, SZ_4K,
+ offset, size, data);
+}
+
+static int e1000_flash_mode_write(struct e1000_hw *hw, loff_t offset,
+ size_t size, const void *data)
+{
+ int ret;
+
+ ret = e1000_flash_mode_io(hw,
+ E1000_FLASH_MODE_OP_WRITE, 256,
+ offset, size, (void *)data);
+ if (ret < 0)
+ return ret;
+
+ ret = e1000_poll_reg(hw, E1000_FLSWCTL,
+ E1000_FLSWCTL_FLBUSY,
+ 0, SECOND);
+ if (ret < 0)
+ dev_err(hw->dev, "Timout while waiting for FLSWCTL.FLBUSY\n");
+
+ return ret;
+}
+
+static int e1000_flash_mode_erase(struct e1000_hw *hw, loff_t offset,
+ size_t size)
+{
+ return e1000_flash_mode_io(hw,
+ E1000_FLASH_MODE_OP_ERASE, SZ_4K,
+ offset, size, NULL);
+}
+
+static ssize_t e1000_invm_cdev_read(struct cdev *cdev, void *buf,
+ size_t count, loff_t offset, unsigned long flags)
+{
+ uint8_t n, bnr;
+ uint32_t line;
+ size_t chunk, residue = count;
+ struct e1000_hw *hw = container_of(cdev, struct e1000_hw, invm.cdev);
+
+ n = offset / sizeof(line);
+ if (n > E1000_INVM_DATA_MAX_N)
+ return -EINVAL;
+
+ bnr = offset % sizeof(line);
+ if (bnr) {
+ /*
+ * if bnr in not zero it means we have a non 4-byte
+ * aligned start and need to do a partial read
+ */
+ const uint8_t *bptr;
+
+ bptr = (uint8_t *)&line + bnr;
+ chunk = min(bnr - sizeof(line), count);
+ line = e1000_read_reg(hw, E1000_INVM_DATA(n));
+ line = cpu_to_le32(line); /* to account for readl */
+ memcpy(buf, bptr, chunk);
+
+ goto start_adjusted;
+ }
+
+ do {
+ if (n > E1000_INVM_DATA_MAX_N)
+ return -EINVAL;
+
+ chunk = min(sizeof(line), residue);
+ line = e1000_read_reg(hw, E1000_INVM_DATA(n));
+ line = cpu_to_le32(line); /* to account for readl */
+
+ /*
+ * by using memcpy in conjunction with min should get
+ * dangling tail reads as well as aligned reads
+ */
+ memcpy(buf, &line, chunk);
+
+ start_adjusted:
+ residue -= chunk;
+ buf += chunk;
+ n++;
+ } while (residue);
+
+ return count;
+}
+
+static int e1000_invm_program(struct e1000_hw *hw, u32 offset, u32 value,
+ unsigned int delay)
+{
+ int retries = 400;
+ do {
+ if ((e1000_read_reg(hw, offset) & value) == value)
+ return E1000_SUCCESS;
+
+ e1000_write_reg(hw, offset, value);
+
+ if (delay) {
+ udelay(delay);
+ } else {
+ int ret;
+
+ if (e1000_read_reg(hw, E1000_INVM_PROTECT) &
+ E1000_INVM_PROTECT_WRITE_ERROR) {
+ dev_err(hw->dev, "Error while writing to %x\n", offset);
+ return -EIO;
+ }
+
+ ret = e1000_poll_reg(hw, E1000_INVM_PROTECT,
+ E1000_INVM_PROTECT_BUSY,
+ 0, SECOND);
+ if (ret < 0) {
+ dev_err(hw->dev,
+ "Timeout while waiting for INVM_PROTECT.BUSY\n");
+ return ret;
+ }
+ }
+ } while (retries--);
+
+ return -ETIMEDOUT;
+}
+
+static int e1000_invm_set_lock(struct param_d *param, void *priv)
+{
+ struct e1000_hw *hw = priv;
+
+ if (hw->invm.line > 31)
+ return -EINVAL;
+
+ return e1000_invm_program(hw,
+ E1000_INVM_LOCK(hw->invm.line),
+ E1000_INVM_LOCK_BIT,
+ 10);
+}
+
+static int e1000_invm_unlock(struct e1000_hw *hw)
+{
+ e1000_write_reg(hw, E1000_INVM_PROTECT, E1000_INVM_PROTECT_CODE);
+ /*
+ * If we were successful at unlocking iNVM for programming we
+ * should see ALLOW_WRITE bit toggle to 1
+ */
+ if (!(e1000_read_reg(hw, E1000_INVM_PROTECT) &
+ E1000_INVM_PROTECT_ALLOW_WRITE))
+ return -EIO;
+ else
+ return E1000_SUCCESS;
+}
+
+static void e1000_invm_lock(struct e1000_hw *hw)
+{
+ e1000_write_reg(hw, E1000_INVM_PROTECT, 0);
+}
+
+static int e1000_invm_write_prepare(struct e1000_hw *hw)
+{
+ int ret;
+ /*
+ * This needs to be done accorging to the datasheet p. 541 and
+ * p. 79
+ */
+ e1000_write_reg(hw, E1000_PCIEMISC,
+ E1000_PCIEMISC_RESERVED_PATTERN1 |
+ E1000_PCIEMISC_DMA_IDLE |
+ E1000_PCIEMISC_RESERVED_PATTERN2);
+
+ /*
+ * Needed for programming iNVM on devices with Flash with valid
+ * contents attached
+ */
+ ret = e1000_poll_reg(hw, E1000_EEMNGCTL,
+ E1000_EEMNGCTL_CFG_DONE,
+ E1000_EEMNGCTL_CFG_DONE, SECOND);
+ if (ret < 0) {
+ dev_err(hw->dev,
+ "Timeout while waiting for EEMNGCTL.CFG_DONE\n");
+ return ret;
+ }
+
+ udelay(15);
+
+ return E1000_SUCCESS;
+}
+
+static ssize_t e1000_invm_cdev_write(struct cdev *cdev, const void *buf,
+ size_t count, loff_t offset, unsigned long flags)
+{
+ int ret;
+ uint8_t n, bnr;
+ uint32_t line;
+ size_t chunk, residue = count;
+ struct e1000_hw *hw = container_of(cdev, struct e1000_hw, invm.cdev);
+
+ ret = e1000_invm_write_prepare(hw);
+ if (ret < 0)
+ return ret;
+
+ ret = e1000_invm_unlock(hw);
+ if (ret < 0)
+ goto exit;
+
+ n = offset / sizeof(line);
+ if (n > E1000_INVM_DATA_MAX_N) {
+ ret = -EINVAL;
+ goto exit;
+ }
+
+ bnr = offset % sizeof(line);
+ if (bnr) {
+ uint8_t *bptr;
+ /*
+ * if bnr in not zero it means we have a non 4-byte
+ * aligned start and need to do a read-modify-write
+ * sequence
+ */
+
+ /* Read */
+ line = e1000_read_reg(hw, E1000_INVM_DATA(n));
+
+ /* Modify */
+ /*
+ * We need to ensure that line is LE32 in order for
+ * memcpy to copy byte from least significant to most
+ * significant, since that's how i210 will write the
+ * 32-bit word out to OTP
+ */
+ line = cpu_to_le32(line);
+ bptr = (uint8_t *)&line + bnr;
+ chunk = min(sizeof(line) - bnr, count);
+ memcpy(bptr, buf, chunk);
+ line = le32_to_cpu(line);
+
+ /* Jumping inside of the loop to take care of the
+ * Write */
+ goto start_adjusted;
+ }
+
+ do {
+ if (n > E1000_INVM_DATA_MAX_N) {
+ ret = -EINVAL;
+ goto exit;
+ }
+
+ chunk = min(sizeof(line), residue);
+ if (chunk != sizeof(line)) {
+ /*
+ * If chunk is smaller that sizeof(line), which
+ * should be 4 bytes, we have a "dangling"
+ * chunk and we should read the unchanged
+ * portion of the 4-byte word from iNVM and do
+ * a read-modify-write sequence
+ */
+ line = e1000_read_reg(hw, E1000_INVM_DATA(n));
+ }
+
+ line = cpu_to_le32(line);
+ memcpy(&line, buf, chunk);
+ line = le32_to_cpu(line);
+
+ start_adjusted:
+ /*
+ * iNVM is organized in 32 64-bit lines and each of
+ * those lines can be locked to prevent any further
+ * modification, so for every i-th 32-bit word we need
+ * to check INVM_LINE[i/2] register to see if that word
+ * can be modified
+ */
+ if (e1000_read_reg(hw, E1000_INVM_LOCK(n / 2)) &
+ E1000_INVM_LOCK_BIT) {
+ dev_err(hw->dev, "line %d is locked\n", n / 2);
+ ret = -EIO;
+ goto exit;
+ }
+
+ ret = e1000_invm_program(hw,
+ E1000_INVM_DATA(n),
+ line,
+ 0);
+ if (ret < 0)
+ goto exit;
+
+ residue -= chunk;
+ buf += chunk;
+ n++;
+ } while (residue);
+
+ ret = E1000_SUCCESS;
+exit:
+ e1000_invm_lock(hw);
+ return ret;
+}
+
+static struct cdev_operations e1000_invm_ops = {
+ .read = e1000_invm_cdev_read,
+ .write = e1000_invm_cdev_write,
+};
+
+static ssize_t e1000_eeprom_cdev_read(struct cdev *cdev, void *buf,
+ size_t count, loff_t offset, unsigned long flags)
+{
+ struct e1000_hw *hw = container_of(cdev, struct e1000_hw, eepromcdev);
+ int32_t ret;
+
+ /*
+ * The eeprom interface works on 16 bit words which gives a nice excuse
+ * for being lazy and not implementing unaligned reads.
+ */
+ if (offset & 1 || count == 1)
+ return -EIO;
+
+ ret = e1000_read_eeprom(hw, offset / 2, count / 2, buf);
+ if (ret)
+ return -EIO;
+ else
+ return (count / 2) * 2;
+};
+
+static struct cdev_operations e1000_eeprom_ops = {
+ .read = e1000_eeprom_cdev_read,
+};
+
+static int e1000_mtd_read_or_write(bool read,
+ struct mtd_info *mtd, loff_t off, size_t len,
+ size_t *retlen, u_char *buf)
+{
+ int ret;
+ struct e1000_hw *hw = container_of(mtd, struct e1000_hw, mtd);
+
+ DEBUGFUNC();
+
+ if (e1000_acquire_eeprom(hw) == E1000_SUCCESS) {
+ if (read)
+ ret = e1000_flash_mode_read(hw, off,
+ len, buf);
+ else
+ ret = e1000_flash_mode_write(hw, off,
+ len, buf);
+ if (ret == E1000_SUCCESS)
+ *retlen = len;
+
+ e1000_release_eeprom(hw);
+ } else {
+ ret = -E1000_ERR_EEPROM;
+ }
+
+ return ret;
+
+}
+
+static int e1000_mtd_read(struct mtd_info *mtd, loff_t from, size_t len,
+ size_t *retlen, u_char *buf)
+{
+ return e1000_mtd_read_or_write(true,
+ mtd, from, len, retlen, buf);
+}
+
+static int e1000_mtd_write(struct mtd_info *mtd, loff_t to, size_t len,
+ size_t *retlen, const u_char *buf)
+{
+ return e1000_mtd_read_or_write(false,
+ mtd, to, len, retlen, (u_char *)buf);
+}
+
+static int e1000_mtd_erase(struct mtd_info *mtd, struct erase_info *instr)
+{
+ uint32_t rem;
+ struct e1000_hw *hw = container_of(mtd, struct e1000_hw, mtd);
+ int ret;
+
+ div_u64_rem(instr->len, mtd->erasesize, &rem);
+ if (rem)
+ return -EINVAL;
+
+ ret = e1000_acquire_eeprom(hw);
+ if (ret != E1000_SUCCESS)
+ goto fail;
+
+ /*
+ * If mtd->size is 4096 it means we are dealing with
+ * unprogrammed flash and we don't really know its size to
+ * make an informed decision wheither to erase the whole chip or
+ * just a number of its sectors
+ */
+ if (mtd->size > SZ_4K &&
+ instr->len == mtd->size)
+ ret = e1000_flash_mode_erase(hw, 0, 0);
+ else
+ ret = e1000_flash_mode_erase(hw,
+ instr->addr, instr->len);
+
+ e1000_release_eeprom(hw);
+
+ if (ret < 0)
+ goto fail;
+
+ return 0;
+
+fail:
+ return ret;
+}
+
+static int e1000_mtd_sr_rmw(struct mtd_info *mtd, u8 mask, u8 val)
+{
+ struct e1000_hw *hw = container_of(mtd, struct e1000_hw, mtd);
+ uint32_t flswdata;
+ int ret;
+
+ ret = e1000_flash_mode_wait_for_idle(hw);
+ if (ret < 0)
+ return ret;
+
+ e1000_write_reg(hw, E1000_FLSWCNT, 1);
+ e1000_flash_cmd(hw, E1000_FLSWCTL_CMD_RDSR, 0);
+
+ ret = e1000_flash_mode_check_command_valid(hw);
+ if (ret < 0)
+ return -EIO;
+
+ ret = e1000_poll_reg(hw, E1000_FLSWCTL,
+ E1000_FLSWCTL_DONE, E1000_FLSWCTL_DONE,
+ SECOND);
+ if (ret < 0) {
+ dev_err(hw->dev,
+ "Timeout waiting for FLSWCTL.DONE to be set (RDSR)\n");
+ return ret;
+ }
+
+ flswdata = e1000_read_reg(hw, E1000_FLSWDATA);
+
+ flswdata = (flswdata & ~mask) | val;
+
+ e1000_write_reg(hw, E1000_FLSWCNT, 1);
+ e1000_flash_cmd(hw, E1000_FLSWCTL_CMD_WRSR, 0);
+
+ ret = e1000_flash_mode_check_command_valid(hw);
+ if (ret < 0)
+ return -EIO;
+
+ e1000_write_reg(hw, E1000_FLSWDATA, flswdata);
+
+ ret = e1000_poll_reg(hw, E1000_FLSWCTL,
+ E1000_FLSWCTL_DONE, E1000_FLSWCTL_DONE,
+ SECOND);
+ if (ret < 0) {
+ dev_err(hw->dev,
+ "Timeout waiting for FLSWCTL.DONE to be set (WRSR)\n");
+ }
+
+ return ret;
+}
+
+/*
+ * The available spi nor devices are very different in how the block protection
+ * bits affect which sectors to be protected. So take the simple approach and
+ * only use BP[012] = b000 (unprotected) and BP[012] = b111 (protected).
+ */
+#define SR_BPALL (SR_BP0 | SR_BP1 | SR_BP2)
+
+static int e1000_mtd_lock(struct mtd_info *mtd, loff_t ofs, size_t len)
+{
+ return e1000_mtd_sr_rmw(mtd, SR_BPALL, SR_BPALL);
+}
+
+static int e1000_mtd_unlock(struct mtd_info *mtd, loff_t ofs, size_t len)
+{
+ return e1000_mtd_sr_rmw(mtd, SR_BPALL, 0x0);
+}
+
+static int e1000_register_invm(struct e1000_hw *hw)
+{
+ int ret;
+ u16 word;
+ struct param_d *p;
+
+ if (e1000_eeprom_valid(hw)) {
+ ret = e1000_read_eeprom(hw, 0x0a, 1, &word);
+ if (ret < 0)
+ return ret;
+
+ if (word & (1 << 15))
+ dev_warn(hw->dev, "iNVM lockout mechanism is active\n");
+ }
+
+ hw->invm.cdev.dev = hw->dev;
+ hw->invm.cdev.ops = &e1000_invm_ops;
+ hw->invm.cdev.priv = hw;
+ hw->invm.cdev.name = xasprintf("e1000-invm%d", hw->dev->id);
+ hw->invm.cdev.size = 4 * (E1000_INVM_DATA_MAX_N + 1);
+
+ ret = devfs_create(&hw->invm.cdev);
+ if (ret < 0)
+ return ret;
+
+ dev_set_name(&hw->invm.dev, "invm");
+ hw->invm.dev.id = hw->dev->id;
+ hw->invm.dev.parent = hw->dev;
+ ret = register_device(&hw->invm.dev);
+ if (ret < 0) {
+ devfs_remove(&hw->invm.cdev);
+ return ret;
+ }
+
+ p = dev_add_param_int(&hw->invm.dev, "lock", e1000_invm_set_lock,
+ NULL, &hw->invm.line, "%u", hw);
+ if (IS_ERR(p)) {
+ unregister_device(&hw->invm.dev);
+ devfs_remove(&hw->invm.cdev);
+ ret = PTR_ERR(p);
+ }
+
+ return ret;
+}
+
+/*
+ * This function has a wrong name for historic reasons, it doesn't add an
+ * eeprom, but the flash (if available) that is used to simulate the eeprom.
+ * Also a device that represents the invm is registered here (if available).
+ */
+int e1000_register_eeprom(struct e1000_hw *hw)
+{
+ struct e1000_eeprom_info *eeprom = &hw->eeprom;
+ uint32_t eecd;
+ int ret;
+
+ if (hw->mac_type != e1000_igb)
+ return E1000_SUCCESS;
+
+ eecd = e1000_read_reg(hw, E1000_EECD);
+
+ if (eecd & E1000_EECD_AUTO_RD) {
+ if (eecd & E1000_EECD_EE_PRES) {
+ if (eecd & E1000_EECD_FLASH_IN_USE) {
+ uint32_t fla = e1000_read_reg(hw, E1000_FLA);
+ dev_info(hw->dev,
+ "Hardware programmed from flash (%ssecure)\n",
+ fla & E1000_FLA_LOCKED ? "" : "un");
+ } else {
+ dev_info(hw->dev, "Hardware programmed from iNVM\n");
+ }
+ } else {
+ dev_warn(hw->dev, "Shadow RAM invalid\n");
+ }
+ } else {
+ /*
+ * I never saw this case in practise and I'm unsure how
+ * to handle that. Maybe just wait until the hardware is
+ * up enough that this bit is set?
+ */
+ dev_err(hw->dev, "Flash Auto-Read not done\n");
+ }
+
+ if (e1000_eeprom_valid(hw)) {
+ hw->eepromcdev.dev = hw->dev;
+ hw->eepromcdev.ops = &e1000_eeprom_ops;
+ hw->eepromcdev.name = xasprintf("e1000-eeprom%d",
+ hw->dev->id);
+ hw->eepromcdev.size = 0x1000;
+
+ ret = devfs_create(&hw->eepromcdev);
+ if (ret < 0)
+ return ret;
+ }
+
+ if (eecd & E1000_EECD_I210_FLASH_DETECTED) {
+ hw->mtd.dev.parent = hw->dev;
+ hw->mtd._read = e1000_mtd_read;
+ hw->mtd._write = e1000_mtd_write;
+ hw->mtd._erase = e1000_mtd_erase;
+ hw->mtd._lock = e1000_mtd_lock;
+ hw->mtd._unlock = e1000_mtd_unlock;
+ hw->mtd.size = eeprom->word_size * 2;
+ hw->mtd.writesize = 1;
+ hw->mtd.subpage_sft = 0;
+
+ hw->mtd.eraseregions = xzalloc(sizeof(struct mtd_erase_region_info));
+ hw->mtd.erasesize = SZ_4K;
+ hw->mtd.eraseregions[0].erasesize = SZ_4K;
+ hw->mtd.eraseregions[0].numblocks = hw->mtd.size / SZ_4K;
+ hw->mtd.numeraseregions = 1;
+
+ hw->mtd.flags = MTD_CAP_NORFLASH;
+ hw->mtd.type = MTD_NORFLASH;
+
+ ret = add_mtd_device(&hw->mtd, "e1000-nor",
+ DEVICE_ID_DYNAMIC);
+ if (ret)
+ goto out_eeprom;
+ }
+
+ ret = e1000_register_invm(hw);
+ if (ret < 0)
+ goto out_mtd;
+
+ return E1000_SUCCESS;
+
+out_mtd:
+ if (eecd & E1000_EECD_I210_FLASH_DETECTED)
+ del_mtd_device(&hw->mtd);
+out_eeprom:
+ if (e1000_eeprom_valid(hw))
+ devfs_remove(&hw->eepromcdev);
+
+ return ret;
+}