/* MTD */
 #ifdef CONFIG_SFC_MTD
-extern int efx_mtd_probe(struct efx_nic *efx);
+extern int efx_mtd_add(struct efx_nic *efx, struct efx_mtd_partition *parts,
+                      size_t n_parts, size_t sizeof_part);
+static inline int efx_mtd_probe(struct efx_nic *efx)
+{
+       return efx->type->mtd_probe(efx);
+}
 extern void efx_mtd_rename(struct efx_nic *efx);
 extern void efx_mtd_remove(struct efx_nic *efx);
 #else
 
 #include "net_driver.h"
 #include "bitfield.h"
 #include "efx.h"
-#include "spi.h"
 #include "nic.h"
 #include "farch_regs.h"
 #include "io.h"
 
 /**************************************************************************
  *
- * Non-volatile configuration
+ * Basic SPI command set and bit definitions
+ *
+ *************************************************************************/
+
+#define SPI_WRSR 0x01          /* Write status register */
+#define SPI_WRITE 0x02         /* Write data to memory array */
+#define SPI_READ 0x03          /* Read data from memory array */
+#define SPI_WRDI 0x04          /* Reset write enable latch */
+#define SPI_RDSR 0x05          /* Read status register */
+#define SPI_WREN 0x06          /* Set write enable latch */
+#define SPI_SST_EWSR 0x50      /* SST: Enable write to status register */
+
+#define SPI_STATUS_WPEN 0x80   /* Write-protect pin enabled */
+#define SPI_STATUS_BP2 0x10    /* Block protection bit 2 */
+#define SPI_STATUS_BP1 0x08    /* Block protection bit 1 */
+#define SPI_STATUS_BP0 0x04    /* Block protection bit 0 */
+#define SPI_STATUS_WEN 0x02    /* State of the write enable latch */
+#define SPI_STATUS_NRDY 0x01   /* Device busy flag */
+
+/**************************************************************************
+ *
+ * Non-volatile memory layout
  *
  **************************************************************************
  */
 
+/* SFC4000 flash is partitioned into:
+ *     0-0x400       chip and board config (see struct falcon_nvconfig)
+ *     0x400-0x8000  unused (or may contain VPD if EEPROM not present)
+ *     0x8000-end    boot code (mapped to PCI expansion ROM)
+ * SFC4000 small EEPROM (size < 0x400) is used for VPD only.
+ * SFC4000 large EEPROM (size >= 0x400) is partitioned into:
+ *     0-0x400       chip and board config
+ *     configurable  VPD
+ *     0x800-0x1800  boot config
+ * Aside from the chip and board config, all of these are optional and may
+ * be absent or truncated depending on the devices used.
+ */
+#define FALCON_NVCONFIG_END 0x400U
+#define FALCON_FLASH_BOOTCODE_START 0x8000U
+#define FALCON_EEPROM_BOOTCONFIG_START 0x800U
+#define FALCON_EEPROM_BOOTCONFIG_END 0x1800U
+
 /* Board configuration v2 (v1 is obsolete; later versions are compatible) */
 struct falcon_nvconfig_board_v2 {
        __le16 nports;
        }
 }
 
-int falcon_spi_cmd(struct efx_nic *efx, const struct falcon_spi_device *spi,
-                  unsigned int command, int address,
-                  const void *in, void *out, size_t len)
+static int
+falcon_spi_cmd(struct efx_nic *efx, const struct falcon_spi_device *spi,
+              unsigned int command, int address,
+              const void *in, void *out, size_t len)
 {
        bool addressed = (address >= 0);
        bool reading = (out != NULL);
        return 0;
 }
 
-static size_t
-falcon_spi_write_limit(const struct falcon_spi_device *spi, size_t start)
-{
-       return min(FALCON_SPI_MAX_LEN,
-                  (spi->block_size - (start & (spi->block_size - 1))));
-}
-
 static inline u8
 falcon_spi_munge_command(const struct falcon_spi_device *spi,
                         const u8 command, const unsigned int address)
        return command | (((address >> 8) & spi->munge_address) << 3);
 }
 
-/* Wait up to 10 ms for buffered write completion */
-int
-falcon_spi_wait_write(struct efx_nic *efx, const struct falcon_spi_device *spi)
-{
-       unsigned long timeout = jiffies + 1 + DIV_ROUND_UP(HZ, 100);
-       u8 status;
-       int rc;
-
-       for (;;) {
-               rc = falcon_spi_cmd(efx, spi, SPI_RDSR, -1, NULL,
-                                   &status, sizeof(status));
-               if (rc)
-                       return rc;
-               if (!(status & SPI_STATUS_NRDY))
-                       return 0;
-               if (time_after_eq(jiffies, timeout)) {
-                       netif_err(efx, hw, efx->net_dev,
-                                 "SPI write timeout on device %d"
-                                 " last status=0x%02x\n",
-                                 spi->device_id, status);
-                       return -ETIMEDOUT;
-               }
-               schedule_timeout_uninterruptible(1);
-       }
-}
-
-int falcon_spi_read(struct efx_nic *efx, const struct falcon_spi_device *spi,
-                   loff_t start, size_t len, size_t *retlen, u8 *buffer)
+static int
+falcon_spi_read(struct efx_nic *efx, const struct falcon_spi_device *spi,
+               loff_t start, size_t len, size_t *retlen, u8 *buffer)
 {
        size_t block_len, pos = 0;
        unsigned int command;
        return rc;
 }
 
-int
+#ifdef CONFIG_SFC_MTD
+
+struct falcon_mtd_partition {
+       struct efx_mtd_partition common;
+       const struct falcon_spi_device *spi;
+       size_t offset;
+};
+
+#define to_falcon_mtd_partition(mtd)                           \
+       container_of(mtd, struct falcon_mtd_partition, common.mtd)
+
+static size_t
+falcon_spi_write_limit(const struct falcon_spi_device *spi, size_t start)
+{
+       return min(FALCON_SPI_MAX_LEN,
+                  (spi->block_size - (start & (spi->block_size - 1))));
+}
+
+/* Wait up to 10 ms for buffered write completion */
+static int
+falcon_spi_wait_write(struct efx_nic *efx, const struct falcon_spi_device *spi)
+{
+       unsigned long timeout = jiffies + 1 + DIV_ROUND_UP(HZ, 100);
+       u8 status;
+       int rc;
+
+       for (;;) {
+               rc = falcon_spi_cmd(efx, spi, SPI_RDSR, -1, NULL,
+                                   &status, sizeof(status));
+               if (rc)
+                       return rc;
+               if (!(status & SPI_STATUS_NRDY))
+                       return 0;
+               if (time_after_eq(jiffies, timeout)) {
+                       netif_err(efx, hw, efx->net_dev,
+                                 "SPI write timeout on device %d"
+                                 " last status=0x%02x\n",
+                                 spi->device_id, status);
+                       return -ETIMEDOUT;
+               }
+               schedule_timeout_uninterruptible(1);
+       }
+}
+
+static int
 falcon_spi_write(struct efx_nic *efx, const struct falcon_spi_device *spi,
                 loff_t start, size_t len, size_t *retlen, const u8 *buffer)
 {
        return rc;
 }
 
+static int
+falcon_spi_slow_wait(struct falcon_mtd_partition *part, bool uninterruptible)
+{
+       const struct falcon_spi_device *spi = part->spi;
+       struct efx_nic *efx = part->common.mtd.priv;
+       u8 status;
+       int rc, i;
+
+       /* Wait up to 4s for flash/EEPROM to finish a slow operation. */
+       for (i = 0; i < 40; i++) {
+               __set_current_state(uninterruptible ?
+                                   TASK_UNINTERRUPTIBLE : TASK_INTERRUPTIBLE);
+               schedule_timeout(HZ / 10);
+               rc = falcon_spi_cmd(efx, spi, SPI_RDSR, -1, NULL,
+                                   &status, sizeof(status));
+               if (rc)
+                       return rc;
+               if (!(status & SPI_STATUS_NRDY))
+                       return 0;
+               if (signal_pending(current))
+                       return -EINTR;
+       }
+       pr_err("%s: timed out waiting for %s\n",
+              part->common.name, part->common.dev_type_name);
+       return -ETIMEDOUT;
+}
+
+static int
+falcon_spi_unlock(struct efx_nic *efx, const struct falcon_spi_device *spi)
+{
+       const u8 unlock_mask = (SPI_STATUS_BP2 | SPI_STATUS_BP1 |
+                               SPI_STATUS_BP0);
+       u8 status;
+       int rc;
+
+       rc = falcon_spi_cmd(efx, spi, SPI_RDSR, -1, NULL,
+                           &status, sizeof(status));
+       if (rc)
+               return rc;
+
+       if (!(status & unlock_mask))
+               return 0; /* already unlocked */
+
+       rc = falcon_spi_cmd(efx, spi, SPI_WREN, -1, NULL, NULL, 0);
+       if (rc)
+               return rc;
+       rc = falcon_spi_cmd(efx, spi, SPI_SST_EWSR, -1, NULL, NULL, 0);
+       if (rc)
+               return rc;
+
+       status &= ~unlock_mask;
+       rc = falcon_spi_cmd(efx, spi, SPI_WRSR, -1, &status,
+                           NULL, sizeof(status));
+       if (rc)
+               return rc;
+       rc = falcon_spi_wait_write(efx, spi);
+       if (rc)
+               return rc;
+
+       return 0;
+}
+
+#define FALCON_SPI_VERIFY_BUF_LEN 16
+
+static int
+falcon_spi_erase(struct falcon_mtd_partition *part, loff_t start, size_t len)
+{
+       const struct falcon_spi_device *spi = part->spi;
+       struct efx_nic *efx = part->common.mtd.priv;
+       unsigned pos, block_len;
+       u8 empty[FALCON_SPI_VERIFY_BUF_LEN];
+       u8 buffer[FALCON_SPI_VERIFY_BUF_LEN];
+       int rc;
+
+       if (len != spi->erase_size)
+               return -EINVAL;
+
+       if (spi->erase_command == 0)
+               return -EOPNOTSUPP;
+
+       rc = falcon_spi_unlock(efx, spi);
+       if (rc)
+               return rc;
+       rc = falcon_spi_cmd(efx, spi, SPI_WREN, -1, NULL, NULL, 0);
+       if (rc)
+               return rc;
+       rc = falcon_spi_cmd(efx, spi, spi->erase_command, start, NULL,
+                           NULL, 0);
+       if (rc)
+               return rc;
+       rc = falcon_spi_slow_wait(part, false);
+
+       /* Verify the entire region has been wiped */
+       memset(empty, 0xff, sizeof(empty));
+       for (pos = 0; pos < len; pos += block_len) {
+               block_len = min(len - pos, sizeof(buffer));
+               rc = falcon_spi_read(efx, spi, start + pos, block_len,
+                                    NULL, buffer);
+               if (rc)
+                       return rc;
+               if (memcmp(empty, buffer, block_len))
+                       return -EIO;
+
+               /* Avoid locking up the system */
+               cond_resched();
+               if (signal_pending(current))
+                       return -EINTR;
+       }
+
+       return rc;
+}
+
+static void falcon_mtd_rename(struct efx_mtd_partition *part)
+{
+       struct efx_nic *efx = part->mtd.priv;
+
+       snprintf(part->name, sizeof(part->name), "%s %s",
+                efx->name, part->type_name);
+}
+
+static int falcon_mtd_read(struct mtd_info *mtd, loff_t start,
+                          size_t len, size_t *retlen, u8 *buffer)
+{
+       struct falcon_mtd_partition *part = to_falcon_mtd_partition(mtd);
+       struct efx_nic *efx = mtd->priv;
+       struct falcon_nic_data *nic_data = efx->nic_data;
+       int rc;
+
+       rc = mutex_lock_interruptible(&nic_data->spi_lock);
+       if (rc)
+               return rc;
+       rc = falcon_spi_read(efx, part->spi, part->offset + start,
+                            len, retlen, buffer);
+       mutex_unlock(&nic_data->spi_lock);
+       return rc;
+}
+
+static int falcon_mtd_erase(struct mtd_info *mtd, loff_t start, size_t len)
+{
+       struct falcon_mtd_partition *part = to_falcon_mtd_partition(mtd);
+       struct efx_nic *efx = mtd->priv;
+       struct falcon_nic_data *nic_data = efx->nic_data;
+       int rc;
+
+       rc = mutex_lock_interruptible(&nic_data->spi_lock);
+       if (rc)
+               return rc;
+       rc = falcon_spi_erase(part, part->offset + start, len);
+       mutex_unlock(&nic_data->spi_lock);
+       return rc;
+}
+
+static int falcon_mtd_write(struct mtd_info *mtd, loff_t start,
+                           size_t len, size_t *retlen, const u8 *buffer)
+{
+       struct falcon_mtd_partition *part = to_falcon_mtd_partition(mtd);
+       struct efx_nic *efx = mtd->priv;
+       struct falcon_nic_data *nic_data = efx->nic_data;
+       int rc;
+
+       rc = mutex_lock_interruptible(&nic_data->spi_lock);
+       if (rc)
+               return rc;
+       rc = falcon_spi_write(efx, part->spi, part->offset + start,
+                             len, retlen, buffer);
+       mutex_unlock(&nic_data->spi_lock);
+       return rc;
+}
+
+static int falcon_mtd_sync(struct mtd_info *mtd)
+{
+       struct falcon_mtd_partition *part = to_falcon_mtd_partition(mtd);
+       struct efx_nic *efx = mtd->priv;
+       struct falcon_nic_data *nic_data = efx->nic_data;
+       int rc;
+
+       mutex_lock(&nic_data->spi_lock);
+       rc = falcon_spi_slow_wait(part, true);
+       mutex_unlock(&nic_data->spi_lock);
+       return rc;
+}
+
+static int falcon_mtd_probe(struct efx_nic *efx)
+{
+       struct falcon_nic_data *nic_data = efx->nic_data;
+       struct falcon_mtd_partition *parts;
+       struct falcon_spi_device *spi;
+       size_t n_parts;
+       int rc = -ENODEV;
+
+       ASSERT_RTNL();
+
+       /* Allocate space for maximum number of partitions */
+       parts = kcalloc(2, sizeof(*parts), GFP_KERNEL);
+       n_parts = 0;
+
+       spi = &nic_data->spi_flash;
+       if (falcon_spi_present(spi) && spi->size > FALCON_FLASH_BOOTCODE_START) {
+               parts[n_parts].spi = spi;
+               parts[n_parts].offset = FALCON_FLASH_BOOTCODE_START;
+               parts[n_parts].common.dev_type_name = "flash";
+               parts[n_parts].common.type_name = "sfc_flash_bootrom";
+               parts[n_parts].common.mtd.type = MTD_NORFLASH;
+               parts[n_parts].common.mtd.flags = MTD_CAP_NORFLASH;
+               parts[n_parts].common.mtd.size = spi->size - FALCON_FLASH_BOOTCODE_START;
+               parts[n_parts].common.mtd.erasesize = spi->erase_size;
+               n_parts++;
+       }
+
+       spi = &nic_data->spi_eeprom;
+       if (falcon_spi_present(spi) && spi->size > FALCON_EEPROM_BOOTCONFIG_START) {
+               parts[n_parts].spi = spi;
+               parts[n_parts].offset = FALCON_EEPROM_BOOTCONFIG_START;
+               parts[n_parts].common.dev_type_name = "EEPROM";
+               parts[n_parts].common.type_name = "sfc_bootconfig";
+               parts[n_parts].common.mtd.type = MTD_RAM;
+               parts[n_parts].common.mtd.flags = MTD_CAP_RAM;
+               parts[n_parts].common.mtd.size =
+                       min(spi->size, FALCON_EEPROM_BOOTCONFIG_END) -
+                       FALCON_EEPROM_BOOTCONFIG_START;
+               parts[n_parts].common.mtd.erasesize = spi->erase_size;
+               n_parts++;
+       }
+
+       rc = efx_mtd_add(efx, &parts[0].common, n_parts, sizeof(*parts));
+       if (rc)
+               kfree(parts);
+       return rc;
+}
+
+#endif /* CONFIG_SFC_MTD */
+
 /**************************************************************************
  *
  * XMAC operations
        .filter_get_rx_id_limit = efx_farch_filter_get_rx_id_limit,
        .filter_get_rx_ids = efx_farch_filter_get_rx_ids,
 
+#ifdef CONFIG_SFC_MTD
+       .mtd_probe = falcon_mtd_probe,
+       .mtd_rename = falcon_mtd_rename,
+       .mtd_read = falcon_mtd_read,
+       .mtd_erase = falcon_mtd_erase,
+       .mtd_write = falcon_mtd_write,
+       .mtd_sync = falcon_mtd_sync,
+#endif
+
        .revision = EFX_REV_FALCON_A1,
        .txd_ptr_tbl_base = FR_AA_TX_DESC_PTR_TBL_KER,
        .rxd_ptr_tbl_base = FR_AA_RX_DESC_PTR_TBL_KER,
        .filter_rfs_insert = efx_farch_filter_rfs_insert,
        .filter_rfs_expire_one = efx_farch_filter_rfs_expire_one,
 #endif
+#ifdef CONFIG_SFC_MTD
+       .mtd_probe = falcon_mtd_probe,
+       .mtd_rename = falcon_mtd_rename,
+       .mtd_read = falcon_mtd_read,
+       .mtd_erase = falcon_mtd_erase,
+       .mtd_write = falcon_mtd_write,
+       .mtd_sync = falcon_mtd_sync,
+#endif
 
        .revision = EFX_REV_FALCON_B0,
        .txd_ptr_tbl_base = FR_BZ_TX_DESC_PTR_TBL,
 
        return rc;
 }
 
-int efx_mcdi_nvram_update_start(struct efx_nic *efx, unsigned int type)
-{
-       MCDI_DECLARE_BUF(inbuf, MC_CMD_NVRAM_UPDATE_START_IN_LEN);
-       int rc;
-
-       MCDI_SET_DWORD(inbuf, NVRAM_UPDATE_START_IN_TYPE, type);
-
-       BUILD_BUG_ON(MC_CMD_NVRAM_UPDATE_START_OUT_LEN != 0);
-
-       rc = efx_mcdi_rpc(efx, MC_CMD_NVRAM_UPDATE_START, inbuf, sizeof(inbuf),
-                         NULL, 0, NULL);
-       if (rc)
-               goto fail;
-
-       return 0;
-
-fail:
-       netif_err(efx, hw, efx->net_dev, "%s: failed rc=%d\n", __func__, rc);
-       return rc;
-}
-
-int efx_mcdi_nvram_read(struct efx_nic *efx, unsigned int type,
-                       loff_t offset, u8 *buffer, size_t length)
-{
-       MCDI_DECLARE_BUF(inbuf, MC_CMD_NVRAM_READ_IN_LEN);
-       MCDI_DECLARE_BUF(outbuf,
-                        MC_CMD_NVRAM_READ_OUT_LEN(EFX_MCDI_NVRAM_LEN_MAX));
-       size_t outlen;
-       int rc;
-
-       MCDI_SET_DWORD(inbuf, NVRAM_READ_IN_TYPE, type);
-       MCDI_SET_DWORD(inbuf, NVRAM_READ_IN_OFFSET, offset);
-       MCDI_SET_DWORD(inbuf, NVRAM_READ_IN_LENGTH, length);
-
-       rc = efx_mcdi_rpc(efx, MC_CMD_NVRAM_READ, inbuf, sizeof(inbuf),
-                         outbuf, sizeof(outbuf), &outlen);
-       if (rc)
-               goto fail;
-
-       memcpy(buffer, MCDI_PTR(outbuf, NVRAM_READ_OUT_READ_BUFFER), length);
-       return 0;
-
-fail:
-       netif_err(efx, hw, efx->net_dev, "%s: failed rc=%d\n", __func__, rc);
-       return rc;
-}
-
-int efx_mcdi_nvram_write(struct efx_nic *efx, unsigned int type,
-                          loff_t offset, const u8 *buffer, size_t length)
-{
-       MCDI_DECLARE_BUF(inbuf,
-                        MC_CMD_NVRAM_WRITE_IN_LEN(EFX_MCDI_NVRAM_LEN_MAX));
-       int rc;
-
-       MCDI_SET_DWORD(inbuf, NVRAM_WRITE_IN_TYPE, type);
-       MCDI_SET_DWORD(inbuf, NVRAM_WRITE_IN_OFFSET, offset);
-       MCDI_SET_DWORD(inbuf, NVRAM_WRITE_IN_LENGTH, length);
-       memcpy(MCDI_PTR(inbuf, NVRAM_WRITE_IN_WRITE_BUFFER), buffer, length);
-
-       BUILD_BUG_ON(MC_CMD_NVRAM_WRITE_OUT_LEN != 0);
-
-       rc = efx_mcdi_rpc(efx, MC_CMD_NVRAM_WRITE, inbuf,
-                         ALIGN(MC_CMD_NVRAM_WRITE_IN_LEN(length), 4),
-                         NULL, 0, NULL);
-       if (rc)
-               goto fail;
-
-       return 0;
-
-fail:
-       netif_err(efx, hw, efx->net_dev, "%s: failed rc=%d\n", __func__, rc);
-       return rc;
-}
-
-int efx_mcdi_nvram_erase(struct efx_nic *efx, unsigned int type,
-                        loff_t offset, size_t length)
-{
-       MCDI_DECLARE_BUF(inbuf, MC_CMD_NVRAM_ERASE_IN_LEN);
-       int rc;
-
-       MCDI_SET_DWORD(inbuf, NVRAM_ERASE_IN_TYPE, type);
-       MCDI_SET_DWORD(inbuf, NVRAM_ERASE_IN_OFFSET, offset);
-       MCDI_SET_DWORD(inbuf, NVRAM_ERASE_IN_LENGTH, length);
-
-       BUILD_BUG_ON(MC_CMD_NVRAM_ERASE_OUT_LEN != 0);
-
-       rc = efx_mcdi_rpc(efx, MC_CMD_NVRAM_ERASE, inbuf, sizeof(inbuf),
-                         NULL, 0, NULL);
-       if (rc)
-               goto fail;
-
-       return 0;
-
-fail:
-       netif_err(efx, hw, efx->net_dev, "%s: failed rc=%d\n", __func__, rc);
-       return rc;
-}
-
-int efx_mcdi_nvram_update_finish(struct efx_nic *efx, unsigned int type)
-{
-       MCDI_DECLARE_BUF(inbuf, MC_CMD_NVRAM_UPDATE_FINISH_IN_LEN);
-       int rc;
-
-       MCDI_SET_DWORD(inbuf, NVRAM_UPDATE_FINISH_IN_TYPE, type);
-
-       BUILD_BUG_ON(MC_CMD_NVRAM_UPDATE_FINISH_OUT_LEN != 0);
-
-       rc = efx_mcdi_rpc(efx, MC_CMD_NVRAM_UPDATE_FINISH, inbuf, sizeof(inbuf),
-                         NULL, 0, NULL);
-       if (rc)
-               goto fail;
-
-       return 0;
-
-fail:
-       netif_err(efx, hw, efx->net_dev, "%s: failed rc=%d\n", __func__, rc);
-       return rc;
-}
-
 static int efx_mcdi_nvram_test(struct efx_nic *efx, unsigned int type)
 {
        MCDI_DECLARE_BUF(inbuf, MC_CMD_NVRAM_TEST_IN_LEN);
        return rc;
 }
 
+#ifdef CONFIG_SFC_MTD
+
+#define EFX_MCDI_NVRAM_LEN_MAX 128
+
+static int efx_mcdi_nvram_update_start(struct efx_nic *efx, unsigned int type)
+{
+       MCDI_DECLARE_BUF(inbuf, MC_CMD_NVRAM_UPDATE_START_IN_LEN);
+       int rc;
+
+       MCDI_SET_DWORD(inbuf, NVRAM_UPDATE_START_IN_TYPE, type);
+
+       BUILD_BUG_ON(MC_CMD_NVRAM_UPDATE_START_OUT_LEN != 0);
+
+       rc = efx_mcdi_rpc(efx, MC_CMD_NVRAM_UPDATE_START, inbuf, sizeof(inbuf),
+                         NULL, 0, NULL);
+       if (rc)
+               goto fail;
+
+       return 0;
+
+fail:
+       netif_err(efx, hw, efx->net_dev, "%s: failed rc=%d\n", __func__, rc);
+       return rc;
+}
+
+static int efx_mcdi_nvram_read(struct efx_nic *efx, unsigned int type,
+                              loff_t offset, u8 *buffer, size_t length)
+{
+       MCDI_DECLARE_BUF(inbuf, MC_CMD_NVRAM_READ_IN_LEN);
+       MCDI_DECLARE_BUF(outbuf,
+                        MC_CMD_NVRAM_READ_OUT_LEN(EFX_MCDI_NVRAM_LEN_MAX));
+       size_t outlen;
+       int rc;
+
+       MCDI_SET_DWORD(inbuf, NVRAM_READ_IN_TYPE, type);
+       MCDI_SET_DWORD(inbuf, NVRAM_READ_IN_OFFSET, offset);
+       MCDI_SET_DWORD(inbuf, NVRAM_READ_IN_LENGTH, length);
+
+       rc = efx_mcdi_rpc(efx, MC_CMD_NVRAM_READ, inbuf, sizeof(inbuf),
+                         outbuf, sizeof(outbuf), &outlen);
+       if (rc)
+               goto fail;
+
+       memcpy(buffer, MCDI_PTR(outbuf, NVRAM_READ_OUT_READ_BUFFER), length);
+       return 0;
+
+fail:
+       netif_err(efx, hw, efx->net_dev, "%s: failed rc=%d\n", __func__, rc);
+       return rc;
+}
+
+static int efx_mcdi_nvram_write(struct efx_nic *efx, unsigned int type,
+                               loff_t offset, const u8 *buffer, size_t length)
+{
+       MCDI_DECLARE_BUF(inbuf,
+                        MC_CMD_NVRAM_WRITE_IN_LEN(EFX_MCDI_NVRAM_LEN_MAX));
+       int rc;
+
+       MCDI_SET_DWORD(inbuf, NVRAM_WRITE_IN_TYPE, type);
+       MCDI_SET_DWORD(inbuf, NVRAM_WRITE_IN_OFFSET, offset);
+       MCDI_SET_DWORD(inbuf, NVRAM_WRITE_IN_LENGTH, length);
+       memcpy(MCDI_PTR(inbuf, NVRAM_WRITE_IN_WRITE_BUFFER), buffer, length);
+
+       BUILD_BUG_ON(MC_CMD_NVRAM_WRITE_OUT_LEN != 0);
+
+       rc = efx_mcdi_rpc(efx, MC_CMD_NVRAM_WRITE, inbuf,
+                         ALIGN(MC_CMD_NVRAM_WRITE_IN_LEN(length), 4),
+                         NULL, 0, NULL);
+       if (rc)
+               goto fail;
+
+       return 0;
+
+fail:
+       netif_err(efx, hw, efx->net_dev, "%s: failed rc=%d\n", __func__, rc);
+       return rc;
+}
+
+static int efx_mcdi_nvram_erase(struct efx_nic *efx, unsigned int type,
+                               loff_t offset, size_t length)
+{
+       MCDI_DECLARE_BUF(inbuf, MC_CMD_NVRAM_ERASE_IN_LEN);
+       int rc;
+
+       MCDI_SET_DWORD(inbuf, NVRAM_ERASE_IN_TYPE, type);
+       MCDI_SET_DWORD(inbuf, NVRAM_ERASE_IN_OFFSET, offset);
+       MCDI_SET_DWORD(inbuf, NVRAM_ERASE_IN_LENGTH, length);
+
+       BUILD_BUG_ON(MC_CMD_NVRAM_ERASE_OUT_LEN != 0);
+
+       rc = efx_mcdi_rpc(efx, MC_CMD_NVRAM_ERASE, inbuf, sizeof(inbuf),
+                         NULL, 0, NULL);
+       if (rc)
+               goto fail;
+
+       return 0;
+
+fail:
+       netif_err(efx, hw, efx->net_dev, "%s: failed rc=%d\n", __func__, rc);
+       return rc;
+}
+
+static int efx_mcdi_nvram_update_finish(struct efx_nic *efx, unsigned int type)
+{
+       MCDI_DECLARE_BUF(inbuf, MC_CMD_NVRAM_UPDATE_FINISH_IN_LEN);
+       int rc;
+
+       MCDI_SET_DWORD(inbuf, NVRAM_UPDATE_FINISH_IN_TYPE, type);
+
+       BUILD_BUG_ON(MC_CMD_NVRAM_UPDATE_FINISH_OUT_LEN != 0);
+
+       rc = efx_mcdi_rpc(efx, MC_CMD_NVRAM_UPDATE_FINISH, inbuf, sizeof(inbuf),
+                         NULL, 0, NULL);
+       if (rc)
+               goto fail;
+
+       return 0;
+
+fail:
+       netif_err(efx, hw, efx->net_dev, "%s: failed rc=%d\n", __func__, rc);
+       return rc;
+}
+
+int efx_mcdi_mtd_read(struct mtd_info *mtd, loff_t start,
+                     size_t len, size_t *retlen, u8 *buffer)
+{
+       struct efx_mcdi_mtd_partition *part = to_efx_mcdi_mtd_partition(mtd);
+       struct efx_nic *efx = mtd->priv;
+       loff_t offset = start;
+       loff_t end = min_t(loff_t, start + len, mtd->size);
+       size_t chunk;
+       int rc = 0;
+
+       while (offset < end) {
+               chunk = min_t(size_t, end - offset, EFX_MCDI_NVRAM_LEN_MAX);
+               rc = efx_mcdi_nvram_read(efx, part->nvram_type, offset,
+                                        buffer, chunk);
+               if (rc)
+                       goto out;
+               offset += chunk;
+               buffer += chunk;
+       }
+out:
+       *retlen = offset - start;
+       return rc;
+}
+
+int efx_mcdi_mtd_erase(struct mtd_info *mtd, loff_t start, size_t len)
+{
+       struct efx_mcdi_mtd_partition *part = to_efx_mcdi_mtd_partition(mtd);
+       struct efx_nic *efx = mtd->priv;
+       loff_t offset = start & ~((loff_t)(mtd->erasesize - 1));
+       loff_t end = min_t(loff_t, start + len, mtd->size);
+       size_t chunk = part->common.mtd.erasesize;
+       int rc = 0;
+
+       if (!part->updating) {
+               rc = efx_mcdi_nvram_update_start(efx, part->nvram_type);
+               if (rc)
+                       goto out;
+               part->updating = true;
+       }
+
+       /* The MCDI interface can in fact do multiple erase blocks at once;
+        * but erasing may be slow, so we make multiple calls here to avoid
+        * tripping the MCDI RPC timeout. */
+       while (offset < end) {
+               rc = efx_mcdi_nvram_erase(efx, part->nvram_type, offset,
+                                         chunk);
+               if (rc)
+                       goto out;
+               offset += chunk;
+       }
+out:
+       return rc;
+}
+
+int efx_mcdi_mtd_write(struct mtd_info *mtd, loff_t start,
+                      size_t len, size_t *retlen, const u8 *buffer)
+{
+       struct efx_mcdi_mtd_partition *part = to_efx_mcdi_mtd_partition(mtd);
+       struct efx_nic *efx = mtd->priv;
+       loff_t offset = start;
+       loff_t end = min_t(loff_t, start + len, mtd->size);
+       size_t chunk;
+       int rc = 0;
+
+       if (!part->updating) {
+               rc = efx_mcdi_nvram_update_start(efx, part->nvram_type);
+               if (rc)
+                       goto out;
+               part->updating = true;
+       }
+
+       while (offset < end) {
+               chunk = min_t(size_t, end - offset, EFX_MCDI_NVRAM_LEN_MAX);
+               rc = efx_mcdi_nvram_write(efx, part->nvram_type, offset,
+                                         buffer, chunk);
+               if (rc)
+                       goto out;
+               offset += chunk;
+               buffer += chunk;
+       }
+out:
+       *retlen = offset - start;
+       return rc;
+}
+
+int efx_mcdi_mtd_sync(struct mtd_info *mtd)
+{
+       struct efx_mcdi_mtd_partition *part = to_efx_mcdi_mtd_partition(mtd);
+       struct efx_nic *efx = mtd->priv;
+       int rc = 0;
+
+       if (part->updating) {
+               part->updating = false;
+               rc = efx_mcdi_nvram_update_finish(efx, part->nvram_type);
+       }
+
+       return rc;
+}
+
+void efx_mcdi_mtd_rename(struct efx_mtd_partition *part)
+{
+       struct efx_mcdi_mtd_partition *mcdi_part =
+               container_of(part, struct efx_mcdi_mtd_partition, common);
+       struct efx_nic *efx = part->mtd.priv;
+
+       snprintf(part->name, sizeof(part->name), "%s %s:%02x",
+                efx->name, part->type_name, mcdi_part->fw_subtype);
+}
+
+#endif /* CONFIG_SFC_MTD */
 
        unsigned int n_attrs;
 };
 
+struct efx_mcdi_mtd_partition {
+       struct efx_mtd_partition common;
+       bool updating;
+       u8 nvram_type;
+       u16 fw_subtype;
+};
+
+#define to_efx_mcdi_mtd_partition(mtd)                         \
+       container_of(mtd, struct efx_mcdi_mtd_partition, common.mtd)
+
 /**
  * struct efx_mcdi_data - extra state for NICs that implement MCDI
  * @iface: Interface/protocol state
 extern int efx_mcdi_nvram_info(struct efx_nic *efx, unsigned int type,
                               size_t *size_out, size_t *erase_size_out,
                               bool *protected_out);
-extern int efx_mcdi_nvram_update_start(struct efx_nic *efx,
-                                      unsigned int type);
-extern int efx_mcdi_nvram_read(struct efx_nic *efx, unsigned int type,
-                              loff_t offset, u8 *buffer, size_t length);
-extern int efx_mcdi_nvram_write(struct efx_nic *efx, unsigned int type,
-                               loff_t offset, const u8 *buffer,
-                               size_t length);
-#define EFX_MCDI_NVRAM_LEN_MAX 128
-extern int efx_mcdi_nvram_erase(struct efx_nic *efx, unsigned int type,
-                               loff_t offset, size_t length);
-extern int efx_mcdi_nvram_update_finish(struct efx_nic *efx,
-                                       unsigned int type);
 extern int efx_mcdi_nvram_test_all(struct efx_nic *efx);
 extern int efx_mcdi_handle_assertion(struct efx_nic *efx);
 extern void efx_mcdi_set_id_led(struct efx_nic *efx, enum efx_led_mode mode);
 static inline void efx_mcdi_mon_remove(struct efx_nic *efx) {}
 #endif
 
+#ifdef CONFIG_SFC_MTD
+extern int efx_mcdi_mtd_read(struct mtd_info *mtd, loff_t start,
+                            size_t len, size_t *retlen, u8 *buffer);
+extern int efx_mcdi_mtd_erase(struct mtd_info *mtd, loff_t start, size_t len);
+extern int efx_mcdi_mtd_write(struct mtd_info *mtd, loff_t start,
+                             size_t len, size_t *retlen, const u8 *buffer);
+extern int efx_mcdi_mtd_sync(struct mtd_info *mtd);
+extern void efx_mcdi_mtd_rename(struct efx_mtd_partition *part);
+#endif
+
 #endif /* EFX_MCDI_H */
 
  * by the Free Software Foundation, incorporated herein by reference.
  */
 
-#include <linux/bitops.h>
 #include <linux/module.h>
 #include <linux/mtd/mtd.h>
-#include <linux/delay.h>
 #include <linux/slab.h>
 #include <linux/rtnetlink.h>
 
 #include "net_driver.h"
-#include "spi.h"
 #include "efx.h"
-#include "nic.h"
-#include "mcdi.h"
-#include "mcdi_pcol.h"
-
-#define FALCON_SPI_VERIFY_BUF_LEN 16
-
-struct efx_mtd_partition {
-       struct list_head node;
-       struct mtd_info mtd;
-       const char *dev_type_name;
-       const char *type_name;
-       char name[IFNAMSIZ + 20];
-};
-
-struct falcon_mtd_partition {
-       struct efx_mtd_partition common;
-       const struct falcon_spi_device *spi;
-       size_t offset;
-};
-
-struct efx_mtd_ops {
-       void (*rename)(struct efx_mtd_partition *part);
-       int (*read)(struct mtd_info *mtd, loff_t start, size_t len,
-                   size_t *retlen, u8 *buffer);
-       int (*erase)(struct mtd_info *mtd, loff_t start, size_t len);
-       int (*write)(struct mtd_info *mtd, loff_t start, size_t len,
-                    size_t *retlen, const u8 *buffer);
-       int (*sync)(struct mtd_info *mtd);
-};
 
 #define to_efx_mtd_partition(mtd)                              \
        container_of(mtd, struct efx_mtd_partition, mtd)
 
-#define to_falcon_mtd_partition(mtd)                           \
-       container_of(mtd, struct falcon_mtd_partition, common.mtd)
-
-static int falcon_mtd_probe(struct efx_nic *efx);
-static int siena_mtd_probe(struct efx_nic *efx);
-
-/* SPI utilities */
-
-static int
-falcon_spi_slow_wait(struct falcon_mtd_partition *part, bool uninterruptible)
-{
-       const struct falcon_spi_device *spi = part->spi;
-       struct efx_nic *efx = part->common.mtd.priv;
-       u8 status;
-       int rc, i;
-
-       /* Wait up to 4s for flash/EEPROM to finish a slow operation. */
-       for (i = 0; i < 40; i++) {
-               __set_current_state(uninterruptible ?
-                                   TASK_UNINTERRUPTIBLE : TASK_INTERRUPTIBLE);
-               schedule_timeout(HZ / 10);
-               rc = falcon_spi_cmd(efx, spi, SPI_RDSR, -1, NULL,
-                                   &status, sizeof(status));
-               if (rc)
-                       return rc;
-               if (!(status & SPI_STATUS_NRDY))
-                       return 0;
-               if (signal_pending(current))
-                       return -EINTR;
-       }
-       pr_err("%s: timed out waiting for %s\n",
-              part->common.name, part->common.dev_type_name);
-       return -ETIMEDOUT;
-}
-
-static int
-falcon_spi_unlock(struct efx_nic *efx, const struct falcon_spi_device *spi)
-{
-       const u8 unlock_mask = (SPI_STATUS_BP2 | SPI_STATUS_BP1 |
-                               SPI_STATUS_BP0);
-       u8 status;
-       int rc;
-
-       rc = falcon_spi_cmd(efx, spi, SPI_RDSR, -1, NULL,
-                           &status, sizeof(status));
-       if (rc)
-               return rc;
-
-       if (!(status & unlock_mask))
-               return 0; /* already unlocked */
-
-       rc = falcon_spi_cmd(efx, spi, SPI_WREN, -1, NULL, NULL, 0);
-       if (rc)
-               return rc;
-       rc = falcon_spi_cmd(efx, spi, SPI_SST_EWSR, -1, NULL, NULL, 0);
-       if (rc)
-               return rc;
-
-       status &= ~unlock_mask;
-       rc = falcon_spi_cmd(efx, spi, SPI_WRSR, -1, &status,
-                           NULL, sizeof(status));
-       if (rc)
-               return rc;
-       rc = falcon_spi_wait_write(efx, spi);
-       if (rc)
-               return rc;
-
-       return 0;
-}
-
-static int
-falcon_spi_erase(struct falcon_mtd_partition *part, loff_t start, size_t len)
-{
-       const struct falcon_spi_device *spi = part->spi;
-       struct efx_nic *efx = part->common.mtd.priv;
-       unsigned pos, block_len;
-       u8 empty[FALCON_SPI_VERIFY_BUF_LEN];
-       u8 buffer[FALCON_SPI_VERIFY_BUF_LEN];
-       int rc;
-
-       if (len != spi->erase_size)
-               return -EINVAL;
-
-       if (spi->erase_command == 0)
-               return -EOPNOTSUPP;
-
-       rc = falcon_spi_unlock(efx, spi);
-       if (rc)
-               return rc;
-       rc = falcon_spi_cmd(efx, spi, SPI_WREN, -1, NULL, NULL, 0);
-       if (rc)
-               return rc;
-       rc = falcon_spi_cmd(efx, spi, spi->erase_command, start, NULL,
-                           NULL, 0);
-       if (rc)
-               return rc;
-       rc = falcon_spi_slow_wait(part, false);
-
-       /* Verify the entire region has been wiped */
-       memset(empty, 0xff, sizeof(empty));
-       for (pos = 0; pos < len; pos += block_len) {
-               block_len = min(len - pos, sizeof(buffer));
-               rc = falcon_spi_read(efx, spi, start + pos, block_len,
-                                    NULL, buffer);
-               if (rc)
-                       return rc;
-               if (memcmp(empty, buffer, block_len))
-                       return -EIO;
-
-               /* Avoid locking up the system */
-               cond_resched();
-               if (signal_pending(current))
-                       return -EINTR;
-       }
-
-       return rc;
-}
-
 /* MTD interface */
 
 static int efx_mtd_erase(struct mtd_info *mtd, struct erase_info *erase)
        struct efx_nic *efx = mtd->priv;
        int rc;
 
-       rc = efx->mtd_ops->erase(mtd, erase->addr, erase->len);
+       rc = efx->type->mtd_erase(mtd, erase->addr, erase->len);
        if (rc == 0) {
                erase->state = MTD_ERASE_DONE;
        } else {
        struct efx_nic *efx = mtd->priv;
        int rc;
 
-       rc = efx->mtd_ops->sync(mtd);
+       rc = efx->type->mtd_sync(mtd);
        if (rc)
                pr_err("%s: %s sync failed (%d)\n",
                       part->name, part->dev_type_name, rc);
        list_del(&part->node);
 }
 
-static void efx_mtd_rename_partition(struct efx_mtd_partition *part)
-{
-       struct efx_nic *efx = part->mtd.priv;
-
-       efx->mtd_ops->rename(part);
-}
-
-static int efx_mtd_add(struct efx_nic *efx, struct efx_mtd_partition *parts,
-                      size_t n_parts, size_t sizeof_part)
+int efx_mtd_add(struct efx_nic *efx, struct efx_mtd_partition *parts,
+               size_t n_parts, size_t sizeof_part)
 {
        struct efx_mtd_partition *part;
        size_t i;
                part->mtd.priv = efx;
                part->mtd.name = part->name;
                part->mtd._erase = efx_mtd_erase;
-               part->mtd._read = efx->mtd_ops->read;
-               part->mtd._write = efx->mtd_ops->write;
+               part->mtd._read = efx->type->mtd_read;
+               part->mtd._write = efx->type->mtd_write;
                part->mtd._sync = efx_mtd_sync;
 
-               efx_mtd_rename_partition(part);
+               efx->type->mtd_rename(part);
 
                if (mtd_device_register(&part->mtd, NULL, 0))
                        goto fail;
        ASSERT_RTNL();
 
        list_for_each_entry(part, &efx->mtd_list, node)
-               efx_mtd_rename_partition(part);
-}
-
-int efx_mtd_probe(struct efx_nic *efx)
-{
-       if (efx_nic_rev(efx) >= EFX_REV_SIENA_A0)
-               return siena_mtd_probe(efx);
-       else
-               return falcon_mtd_probe(efx);
-}
-
-/* Implementation of MTD operations for Falcon */
-
-static void falcon_mtd_rename(struct efx_mtd_partition *part)
-{
-       struct efx_nic *efx = part->mtd.priv;
-
-       snprintf(part->name, sizeof(part->name), "%s %s",
-                efx->name, part->type_name);
-}
-
-static int falcon_mtd_read(struct mtd_info *mtd, loff_t start,
-                          size_t len, size_t *retlen, u8 *buffer)
-{
-       struct falcon_mtd_partition *part = to_falcon_mtd_partition(mtd);
-       struct efx_nic *efx = mtd->priv;
-       struct falcon_nic_data *nic_data = efx->nic_data;
-       int rc;
-
-       rc = mutex_lock_interruptible(&nic_data->spi_lock);
-       if (rc)
-               return rc;
-       rc = falcon_spi_read(efx, part->spi, part->offset + start,
-                            len, retlen, buffer);
-       mutex_unlock(&nic_data->spi_lock);
-       return rc;
-}
-
-static int falcon_mtd_erase(struct mtd_info *mtd, loff_t start, size_t len)
-{
-       struct falcon_mtd_partition *part = to_falcon_mtd_partition(mtd);
-       struct efx_nic *efx = mtd->priv;
-       struct falcon_nic_data *nic_data = efx->nic_data;
-       int rc;
-
-       rc = mutex_lock_interruptible(&nic_data->spi_lock);
-       if (rc)
-               return rc;
-       rc = falcon_spi_erase(part, part->offset + start, len);
-       mutex_unlock(&nic_data->spi_lock);
-       return rc;
-}
-
-static int falcon_mtd_write(struct mtd_info *mtd, loff_t start,
-                           size_t len, size_t *retlen, const u8 *buffer)
-{
-       struct falcon_mtd_partition *part = to_falcon_mtd_partition(mtd);
-       struct efx_nic *efx = mtd->priv;
-       struct falcon_nic_data *nic_data = efx->nic_data;
-       int rc;
-
-       rc = mutex_lock_interruptible(&nic_data->spi_lock);
-       if (rc)
-               return rc;
-       rc = falcon_spi_write(efx, part->spi, part->offset + start,
-                             len, retlen, buffer);
-       mutex_unlock(&nic_data->spi_lock);
-       return rc;
-}
-
-static int falcon_mtd_sync(struct mtd_info *mtd)
-{
-       struct falcon_mtd_partition *part = to_falcon_mtd_partition(mtd);
-       struct efx_nic *efx = mtd->priv;
-       struct falcon_nic_data *nic_data = efx->nic_data;
-       int rc;
-
-       mutex_lock(&nic_data->spi_lock);
-       rc = falcon_spi_slow_wait(part, true);
-       mutex_unlock(&nic_data->spi_lock);
-       return rc;
-}
-
-static const struct efx_mtd_ops falcon_mtd_ops = {
-       .rename = falcon_mtd_rename,
-       .read   = falcon_mtd_read,
-       .erase  = falcon_mtd_erase,
-       .write  = falcon_mtd_write,
-       .sync   = falcon_mtd_sync,
-};
-
-static int falcon_mtd_probe(struct efx_nic *efx)
-{
-       struct falcon_nic_data *nic_data = efx->nic_data;
-       struct falcon_mtd_partition *parts;
-       struct falcon_spi_device *spi;
-       size_t n_parts;
-       int rc = -ENODEV;
-
-       ASSERT_RTNL();
-
-       efx->mtd_ops = &falcon_mtd_ops;
-
-       /* Allocate space for maximum number of partitions */
-       parts = kcalloc(2, sizeof(*parts), GFP_KERNEL);
-       n_parts = 0;
-
-       spi = &nic_data->spi_flash;
-       if (falcon_spi_present(spi) && spi->size > FALCON_FLASH_BOOTCODE_START) {
-               parts[n_parts].spi = spi;
-               parts[n_parts].offset = FALCON_FLASH_BOOTCODE_START;
-               parts[n_parts].common.dev_type_name = "flash";
-               parts[n_parts].common.type_name = "sfc_flash_bootrom";
-               parts[n_parts].common.mtd.type = MTD_NORFLASH;
-               parts[n_parts].common.mtd.flags = MTD_CAP_NORFLASH;
-               parts[n_parts].common.mtd.size = spi->size - FALCON_FLASH_BOOTCODE_START;
-               parts[n_parts].common.mtd.erasesize = spi->erase_size;
-               n_parts++;
-       }
-
-       spi = &nic_data->spi_eeprom;
-       if (falcon_spi_present(spi) && spi->size > FALCON_EEPROM_BOOTCONFIG_START) {
-               parts[n_parts].spi = spi;
-               parts[n_parts].offset = FALCON_EEPROM_BOOTCONFIG_START;
-               parts[n_parts].common.dev_type_name = "EEPROM";
-               parts[n_parts].common.type_name = "sfc_bootconfig";
-               parts[n_parts].common.mtd.type = MTD_RAM;
-               parts[n_parts].common.mtd.flags = MTD_CAP_RAM;
-               parts[n_parts].common.mtd.size =
-                       min(spi->size, FALCON_EEPROM_BOOTCONFIG_END) -
-                       FALCON_EEPROM_BOOTCONFIG_START;
-               parts[n_parts].common.mtd.erasesize = spi->erase_size;
-               n_parts++;
-       }
-
-       rc = efx_mtd_add(efx, &parts[0].common, n_parts, sizeof(*parts));
-       if (rc)
-               kfree(parts);
-       return rc;
-}
-
-/* Implementation of MTD operations for Siena */
-
-struct efx_mcdi_mtd_partition {
-       struct efx_mtd_partition common;
-       bool updating;
-       u8 nvram_type;
-       u16 fw_subtype;
-};
-
-#define to_efx_mcdi_mtd_partition(mtd)                         \
-       container_of(mtd, struct efx_mcdi_mtd_partition, common.mtd)
-
-static void siena_mtd_rename(struct efx_mtd_partition *part)
-{
-       struct efx_mcdi_mtd_partition *mcdi_part =
-               container_of(part, struct efx_mcdi_mtd_partition, common);
-       struct efx_nic *efx = part->mtd.priv;
-
-       snprintf(part->name, sizeof(part->name), "%s %s:%02x",
-                efx->name, part->type_name, mcdi_part->fw_subtype);
-}
-
-static int siena_mtd_read(struct mtd_info *mtd, loff_t start,
-                         size_t len, size_t *retlen, u8 *buffer)
-{
-       struct efx_mcdi_mtd_partition *part = to_efx_mcdi_mtd_partition(mtd);
-       struct efx_nic *efx = mtd->priv;
-       loff_t offset = start;
-       loff_t end = min_t(loff_t, start + len, mtd->size);
-       size_t chunk;
-       int rc = 0;
-
-       while (offset < end) {
-               chunk = min_t(size_t, end - offset, EFX_MCDI_NVRAM_LEN_MAX);
-               rc = efx_mcdi_nvram_read(efx, part->nvram_type, offset,
-                                        buffer, chunk);
-               if (rc)
-                       goto out;
-               offset += chunk;
-               buffer += chunk;
-       }
-out:
-       *retlen = offset - start;
-       return rc;
-}
-
-static int siena_mtd_erase(struct mtd_info *mtd, loff_t start, size_t len)
-{
-       struct efx_mcdi_mtd_partition *part = to_efx_mcdi_mtd_partition(mtd);
-       struct efx_nic *efx = mtd->priv;
-       loff_t offset = start & ~((loff_t)(mtd->erasesize - 1));
-       loff_t end = min_t(loff_t, start + len, mtd->size);
-       size_t chunk = part->common.mtd.erasesize;
-       int rc = 0;
-
-       if (!part->updating) {
-               rc = efx_mcdi_nvram_update_start(efx, part->nvram_type);
-               if (rc)
-                       goto out;
-               part->updating = true;
-       }
-
-       /* The MCDI interface can in fact do multiple erase blocks at once;
-        * but erasing may be slow, so we make multiple calls here to avoid
-        * tripping the MCDI RPC timeout. */
-       while (offset < end) {
-               rc = efx_mcdi_nvram_erase(efx, part->nvram_type, offset,
-                                         chunk);
-               if (rc)
-                       goto out;
-               offset += chunk;
-       }
-out:
-       return rc;
-}
-
-static int siena_mtd_write(struct mtd_info *mtd, loff_t start,
-                          size_t len, size_t *retlen, const u8 *buffer)
-{
-       struct efx_mcdi_mtd_partition *part = to_efx_mcdi_mtd_partition(mtd);
-       struct efx_nic *efx = mtd->priv;
-       loff_t offset = start;
-       loff_t end = min_t(loff_t, start + len, mtd->size);
-       size_t chunk;
-       int rc = 0;
-
-       if (!part->updating) {
-               rc = efx_mcdi_nvram_update_start(efx, part->nvram_type);
-               if (rc)
-                       goto out;
-               part->updating = true;
-       }
-
-       while (offset < end) {
-               chunk = min_t(size_t, end - offset, EFX_MCDI_NVRAM_LEN_MAX);
-               rc = efx_mcdi_nvram_write(efx, part->nvram_type, offset,
-                                         buffer, chunk);
-               if (rc)
-                       goto out;
-               offset += chunk;
-               buffer += chunk;
-       }
-out:
-       *retlen = offset - start;
-       return rc;
-}
-
-static int siena_mtd_sync(struct mtd_info *mtd)
-{
-       struct efx_mcdi_mtd_partition *part = to_efx_mcdi_mtd_partition(mtd);
-       struct efx_nic *efx = mtd->priv;
-       int rc = 0;
-
-       if (part->updating) {
-               part->updating = false;
-               rc = efx_mcdi_nvram_update_finish(efx, part->nvram_type);
-       }
-
-       return rc;
+               efx->type->mtd_rename(part);
 }
-
-static const struct efx_mtd_ops siena_mtd_ops = {
-       .rename = siena_mtd_rename,
-       .read   = siena_mtd_read,
-       .erase  = siena_mtd_erase,
-       .write  = siena_mtd_write,
-       .sync   = siena_mtd_sync,
-};
-
-struct siena_nvram_type_info {
-       int port;
-       const char *name;
-};
-
-static const struct siena_nvram_type_info siena_nvram_types[] = {
-       [MC_CMD_NVRAM_TYPE_DISABLED_CALLISTO]   = { 0, "sfc_dummy_phy" },
-       [MC_CMD_NVRAM_TYPE_MC_FW]               = { 0, "sfc_mcfw" },
-       [MC_CMD_NVRAM_TYPE_MC_FW_BACKUP]        = { 0, "sfc_mcfw_backup" },
-       [MC_CMD_NVRAM_TYPE_STATIC_CFG_PORT0]    = { 0, "sfc_static_cfg" },
-       [MC_CMD_NVRAM_TYPE_STATIC_CFG_PORT1]    = { 1, "sfc_static_cfg" },
-       [MC_CMD_NVRAM_TYPE_DYNAMIC_CFG_PORT0]   = { 0, "sfc_dynamic_cfg" },
-       [MC_CMD_NVRAM_TYPE_DYNAMIC_CFG_PORT1]   = { 1, "sfc_dynamic_cfg" },
-       [MC_CMD_NVRAM_TYPE_EXP_ROM]             = { 0, "sfc_exp_rom" },
-       [MC_CMD_NVRAM_TYPE_EXP_ROM_CFG_PORT0]   = { 0, "sfc_exp_rom_cfg" },
-       [MC_CMD_NVRAM_TYPE_EXP_ROM_CFG_PORT1]   = { 1, "sfc_exp_rom_cfg" },
-       [MC_CMD_NVRAM_TYPE_PHY_PORT0]           = { 0, "sfc_phy_fw" },
-       [MC_CMD_NVRAM_TYPE_PHY_PORT1]           = { 1, "sfc_phy_fw" },
-       [MC_CMD_NVRAM_TYPE_FPGA]                = { 0, "sfc_fpga" },
-};
-
-static int siena_mtd_probe_partition(struct efx_nic *efx,
-                                    struct efx_mcdi_mtd_partition *part,
-                                    unsigned int type)
-{
-       const struct siena_nvram_type_info *info;
-       size_t size, erase_size;
-       bool protected;
-       int rc;
-
-       if (type >= ARRAY_SIZE(siena_nvram_types) ||
-           siena_nvram_types[type].name == NULL)
-               return -ENODEV;
-
-       info = &siena_nvram_types[type];
-
-       if (info->port != efx_port_num(efx))
-               return -ENODEV;
-
-       rc = efx_mcdi_nvram_info(efx, type, &size, &erase_size, &protected);
-       if (rc)
-               return rc;
-       if (protected)
-               return -ENODEV; /* hide it */
-
-       part->nvram_type = type;
-       part->common.dev_type_name = "Siena NVRAM manager";
-       part->common.type_name = info->name;
-
-       part->common.mtd.type = MTD_NORFLASH;
-       part->common.mtd.flags = MTD_CAP_NORFLASH;
-       part->common.mtd.size = size;
-       part->common.mtd.erasesize = erase_size;
-
-       return 0;
-}
-
-static int siena_mtd_get_fw_subtypes(struct efx_nic *efx,
-                                    struct efx_mcdi_mtd_partition *parts,
-                                    size_t n_parts)
-{
-       uint16_t fw_subtype_list[
-               MC_CMD_GET_BOARD_CFG_OUT_FW_SUBTYPE_LIST_MAXNUM];
-       size_t i;
-       int rc;
-
-       rc = efx_mcdi_get_board_cfg(efx, NULL, fw_subtype_list, NULL);
-       if (rc)
-               return rc;
-
-       for (i = 0; i < n_parts; i++)
-               parts[i].fw_subtype = fw_subtype_list[parts[i].nvram_type];
-
-       return 0;
-}
-
-static int siena_mtd_probe(struct efx_nic *efx)
-{
-       struct efx_mcdi_mtd_partition *parts;
-       u32 nvram_types;
-       unsigned int type;
-       size_t n_parts;
-       int rc;
-
-       ASSERT_RTNL();
-
-       efx->mtd_ops = &siena_mtd_ops;
-
-       rc = efx_mcdi_nvram_types(efx, &nvram_types);
-       if (rc)
-               return rc;
-
-       parts = kcalloc(hweight32(nvram_types), sizeof(*parts), GFP_KERNEL);
-       if (!parts)
-               return -ENOMEM;
-
-       type = 0;
-       n_parts = 0;
-
-       while (nvram_types != 0) {
-               if (nvram_types & 1) {
-                       rc = siena_mtd_probe_partition(efx, &parts[n_parts],
-                                                      type);
-                       if (rc == 0)
-                               n_parts++;
-                       else if (rc != -ENODEV)
-                               goto fail;
-               }
-               type++;
-               nvram_types >>= 1;
-       }
-
-       rc = siena_mtd_get_fw_subtypes(efx, parts, n_parts);
-       if (rc)
-               goto fail;
-
-       rc = efx_mtd_add(efx, &parts[0].common, n_parts, sizeof(*parts));
-fail:
-       if (rc)
-               kfree(parts);
-       return rc;
-}
-
 
 #include <linux/mutex.h>
 #include <linux/vmalloc.h>
 #include <linux/i2c.h>
+#include <linux/mtd/mtd.h>
 
 #include "enum.h"
 #include "bitfield.h"
        struct delayed_work selftest_work;
 
 #ifdef CONFIG_SFC_MTD
-       const struct efx_mtd_ops *mtd_ops;
        struct list_head mtd_list;
 #endif
 
        return efx->port_num;
 }
 
+struct efx_mtd_partition {
+       struct list_head node;
+       struct mtd_info mtd;
+       const char *dev_type_name;
+       const char *type_name;
+       char name[IFNAMSIZ + 20];
+};
+
 /**
  * struct efx_nic_type - Efx device type definition
  * @mem_map_size: Get memory BAR mapped size
  * @filter_rfs_expire_one: Consider expiring a filter inserted for RFS.
  *     This must check whether the specified table entry is used by RFS
  *     and that rps_may_expire_flow() returns true for it.
+ * @mtd_probe: Probe and add MTD partitions associated with this net device,
+ *      using efx_mtd_add()
+ * @mtd_rename: Set an MTD partition name using the net device name
+ * @mtd_read: Read from an MTD partition
+ * @mtd_erase: Erase part of an MTD partition
+ * @mtd_write: Write to an MTD partition
+ * @mtd_sync: Wait for write-back to complete on MTD partition.  This
+ *     also notifies the driver that a writer has finished using this
+ *     partition.
  * @revision: Hardware architecture revision
  * @txd_ptr_tbl_base: TX descriptor ring base address
  * @rxd_ptr_tbl_base: RX descriptor ring base address
        bool (*filter_rfs_expire_one)(struct efx_nic *efx, u32 flow_id,
                                      unsigned int index);
 #endif
+#ifdef CONFIG_SFC_MTD
+       int (*mtd_probe)(struct efx_nic *efx);
+       void (*mtd_rename)(struct efx_mtd_partition *part);
+       int (*mtd_read)(struct mtd_info *mtd, loff_t start, size_t len,
+                       size_t *retlen, u8 *buffer);
+       int (*mtd_erase)(struct mtd_info *mtd, loff_t start, size_t len);
+       int (*mtd_write)(struct mtd_info *mtd, loff_t start, size_t len,
+                        size_t *retlen, const u8 *buffer);
+       int (*mtd_sync)(struct mtd_info *mtd);
+#endif
 
        int revision;
        unsigned int txd_ptr_tbl_base;
 
 #include "net_driver.h"
 #include "efx.h"
 #include "mcdi.h"
-#include "spi.h"
 
 /*
  * Falcon hardware control
        struct i2c_client *hwmon_client, *ioexp_client;
 };
 
+/**
+ * struct falcon_spi_device - a Falcon SPI (Serial Peripheral Interface) device
+ * @device_id:         Controller's id for the device
+ * @size:              Size (in bytes)
+ * @addr_len:          Number of address bytes in read/write commands
+ * @munge_address:     Flag whether addresses should be munged.
+ *     Some devices with 9-bit addresses (e.g. AT25040A EEPROM)
+ *     use bit 3 of the command byte as address bit A8, rather
+ *     than having a two-byte address.  If this flag is set, then
+ *     commands should be munged in this way.
+ * @erase_command:     Erase command (or 0 if sector erase not needed).
+ * @erase_size:                Erase sector size (in bytes)
+ *     Erase commands affect sectors with this size and alignment.
+ *     This must be a power of two.
+ * @block_size:                Write block size (in bytes).
+ *     Write commands are limited to blocks with this size and alignment.
+ */
+struct falcon_spi_device {
+       int device_id;
+       unsigned int size;
+       unsigned int addr_len;
+       unsigned int munge_address:1;
+       u8 erase_command;
+       unsigned int erase_size;
+       unsigned int block_size;
+};
+
+static inline bool falcon_spi_present(const struct falcon_spi_device *spi)
+{
+       return spi->size != 0;
+}
+
 /**
  * struct falcon_nic_data - Falcon NIC state
  * @pci_dev2: Secondary function of Falcon A
 
 #include "bitfield.h"
 #include "efx.h"
 #include "nic.h"
-#include "spi.h"
 #include "farch_regs.h"
 #include "io.h"
 #include "phy.h"
                return -EIO;
 }
 
+/**************************************************************************
+ *
+ * MTD
+ *
+ **************************************************************************
+ */
+
+#ifdef CONFIG_SFC_MTD
+
+struct siena_nvram_type_info {
+       int port;
+       const char *name;
+};
+
+static const struct siena_nvram_type_info siena_nvram_types[] = {
+       [MC_CMD_NVRAM_TYPE_DISABLED_CALLISTO]   = { 0, "sfc_dummy_phy" },
+       [MC_CMD_NVRAM_TYPE_MC_FW]               = { 0, "sfc_mcfw" },
+       [MC_CMD_NVRAM_TYPE_MC_FW_BACKUP]        = { 0, "sfc_mcfw_backup" },
+       [MC_CMD_NVRAM_TYPE_STATIC_CFG_PORT0]    = { 0, "sfc_static_cfg" },
+       [MC_CMD_NVRAM_TYPE_STATIC_CFG_PORT1]    = { 1, "sfc_static_cfg" },
+       [MC_CMD_NVRAM_TYPE_DYNAMIC_CFG_PORT0]   = { 0, "sfc_dynamic_cfg" },
+       [MC_CMD_NVRAM_TYPE_DYNAMIC_CFG_PORT1]   = { 1, "sfc_dynamic_cfg" },
+       [MC_CMD_NVRAM_TYPE_EXP_ROM]             = { 0, "sfc_exp_rom" },
+       [MC_CMD_NVRAM_TYPE_EXP_ROM_CFG_PORT0]   = { 0, "sfc_exp_rom_cfg" },
+       [MC_CMD_NVRAM_TYPE_EXP_ROM_CFG_PORT1]   = { 1, "sfc_exp_rom_cfg" },
+       [MC_CMD_NVRAM_TYPE_PHY_PORT0]           = { 0, "sfc_phy_fw" },
+       [MC_CMD_NVRAM_TYPE_PHY_PORT1]           = { 1, "sfc_phy_fw" },
+       [MC_CMD_NVRAM_TYPE_FPGA]                = { 0, "sfc_fpga" },
+};
+
+static int siena_mtd_probe_partition(struct efx_nic *efx,
+                                    struct efx_mcdi_mtd_partition *part,
+                                    unsigned int type)
+{
+       const struct siena_nvram_type_info *info;
+       size_t size, erase_size;
+       bool protected;
+       int rc;
+
+       if (type >= ARRAY_SIZE(siena_nvram_types) ||
+           siena_nvram_types[type].name == NULL)
+               return -ENODEV;
+
+       info = &siena_nvram_types[type];
+
+       if (info->port != efx_port_num(efx))
+               return -ENODEV;
+
+       rc = efx_mcdi_nvram_info(efx, type, &size, &erase_size, &protected);
+       if (rc)
+               return rc;
+       if (protected)
+               return -ENODEV; /* hide it */
+
+       part->nvram_type = type;
+       part->common.dev_type_name = "Siena NVRAM manager";
+       part->common.type_name = info->name;
+
+       part->common.mtd.type = MTD_NORFLASH;
+       part->common.mtd.flags = MTD_CAP_NORFLASH;
+       part->common.mtd.size = size;
+       part->common.mtd.erasesize = erase_size;
+
+       return 0;
+}
+
+static int siena_mtd_get_fw_subtypes(struct efx_nic *efx,
+                                    struct efx_mcdi_mtd_partition *parts,
+                                    size_t n_parts)
+{
+       uint16_t fw_subtype_list[
+               MC_CMD_GET_BOARD_CFG_OUT_FW_SUBTYPE_LIST_MAXNUM];
+       size_t i;
+       int rc;
+
+       rc = efx_mcdi_get_board_cfg(efx, NULL, fw_subtype_list, NULL);
+       if (rc)
+               return rc;
+
+       for (i = 0; i < n_parts; i++)
+               parts[i].fw_subtype = fw_subtype_list[parts[i].nvram_type];
+
+       return 0;
+}
+
+static int siena_mtd_probe(struct efx_nic *efx)
+{
+       struct efx_mcdi_mtd_partition *parts;
+       u32 nvram_types;
+       unsigned int type;
+       size_t n_parts;
+       int rc;
+
+       ASSERT_RTNL();
+
+       rc = efx_mcdi_nvram_types(efx, &nvram_types);
+       if (rc)
+               return rc;
+
+       parts = kcalloc(hweight32(nvram_types), sizeof(*parts), GFP_KERNEL);
+       if (!parts)
+               return -ENOMEM;
+
+       type = 0;
+       n_parts = 0;
+
+       while (nvram_types != 0) {
+               if (nvram_types & 1) {
+                       rc = siena_mtd_probe_partition(efx, &parts[n_parts],
+                                                      type);
+                       if (rc == 0)
+                               n_parts++;
+                       else if (rc != -ENODEV)
+                               goto fail;
+               }
+               type++;
+               nvram_types >>= 1;
+       }
+
+       rc = siena_mtd_get_fw_subtypes(efx, parts, n_parts);
+       if (rc)
+               goto fail;
+
+       rc = efx_mtd_add(efx, &parts[0].common, n_parts, sizeof(*parts));
+fail:
+       if (rc)
+               kfree(parts);
+       return rc;
+}
+
+#endif /* CONFIG_SFC_MTD */
+
 /**************************************************************************
  *
  * Revision-dependent attributes used by efx.c and nic.c
        .filter_rfs_insert = efx_farch_filter_rfs_insert,
        .filter_rfs_expire_one = efx_farch_filter_rfs_expire_one,
 #endif
+#ifdef CONFIG_SFC_MTD
+       .mtd_probe = siena_mtd_probe,
+       .mtd_rename = efx_mcdi_mtd_rename,
+       .mtd_read = efx_mcdi_mtd_read,
+       .mtd_erase = efx_mcdi_mtd_erase,
+       .mtd_write = efx_mcdi_mtd_write,
+       .mtd_sync = efx_mcdi_mtd_sync,
+#endif
 
        .revision = EFX_REV_SIENA_A0,
        .txd_ptr_tbl_base = FR_BZ_TX_DESC_PTR_TBL,
 
+++ /dev/null
-/****************************************************************************
- * Driver for Solarflare Solarstorm network controllers and boards
- * Copyright 2005 Fen Systems Ltd.
- * Copyright 2006-2010 Solarflare Communications Inc.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 as published
- * by the Free Software Foundation, incorporated herein by reference.
- */
-
-#ifndef EFX_SPI_H
-#define EFX_SPI_H
-
-#include "net_driver.h"
-
-/**************************************************************************
- *
- * Basic SPI command set and bit definitions
- *
- *************************************************************************/
-
-#define SPI_WRSR 0x01          /* Write status register */
-#define SPI_WRITE 0x02         /* Write data to memory array */
-#define SPI_READ 0x03          /* Read data from memory array */
-#define SPI_WRDI 0x04          /* Reset write enable latch */
-#define SPI_RDSR 0x05          /* Read status register */
-#define SPI_WREN 0x06          /* Set write enable latch */
-#define SPI_SST_EWSR 0x50      /* SST: Enable write to status register */
-
-#define SPI_STATUS_WPEN 0x80   /* Write-protect pin enabled */
-#define SPI_STATUS_BP2 0x10    /* Block protection bit 2 */
-#define SPI_STATUS_BP1 0x08    /* Block protection bit 1 */
-#define SPI_STATUS_BP0 0x04    /* Block protection bit 0 */
-#define SPI_STATUS_WEN 0x02    /* State of the write enable latch */
-#define SPI_STATUS_NRDY 0x01   /* Device busy flag */
-
-/**
- * struct falcon_spi_device - a Falcon SPI (Serial Peripheral Interface) device
- * @device_id:         Controller's id for the device
- * @size:              Size (in bytes)
- * @addr_len:          Number of address bytes in read/write commands
- * @munge_address:     Flag whether addresses should be munged.
- *     Some devices with 9-bit addresses (e.g. AT25040A EEPROM)
- *     use bit 3 of the command byte as address bit A8, rather
- *     than having a two-byte address.  If this flag is set, then
- *     commands should be munged in this way.
- * @erase_command:     Erase command (or 0 if sector erase not needed).
- * @erase_size:                Erase sector size (in bytes)
- *     Erase commands affect sectors with this size and alignment.
- *     This must be a power of two.
- * @block_size:                Write block size (in bytes).
- *     Write commands are limited to blocks with this size and alignment.
- */
-struct falcon_spi_device {
-       int device_id;
-       unsigned int size;
-       unsigned int addr_len;
-       unsigned int munge_address:1;
-       u8 erase_command;
-       unsigned int erase_size;
-       unsigned int block_size;
-};
-
-static inline bool falcon_spi_present(const struct falcon_spi_device *spi)
-{
-       return spi->size != 0;
-}
-
-int falcon_spi_cmd(struct efx_nic *efx,
-                  const struct falcon_spi_device *spi, unsigned int command,
-                  int address, const void *in, void *out, size_t len);
-int falcon_spi_wait_write(struct efx_nic *efx,
-                         const struct falcon_spi_device *spi);
-int falcon_spi_read(struct efx_nic *efx,
-                   const struct falcon_spi_device *spi, loff_t start,
-                   size_t len, size_t *retlen, u8 *buffer);
-int falcon_spi_write(struct efx_nic *efx,
-                    const struct falcon_spi_device *spi, loff_t start,
-                    size_t len, size_t *retlen, const u8 *buffer);
-
-/*
- * SFC4000 flash is partitioned into:
- *     0-0x400       chip and board config (see falcon_hwdefs.h)
- *     0x400-0x8000  unused (or may contain VPD if EEPROM not present)
- *     0x8000-end    boot code (mapped to PCI expansion ROM)
- * SFC4000 small EEPROM (size < 0x400) is used for VPD only.
- * SFC4000 large EEPROM (size >= 0x400) is partitioned into:
- *     0-0x400       chip and board config
- *     configurable  VPD
- *     0x800-0x1800  boot config
- * Aside from the chip and board config, all of these are optional and may
- * be absent or truncated depending on the devices used.
- */
-#define FALCON_NVCONFIG_END 0x400U
-#define FALCON_FLASH_BOOTCODE_START 0x8000U
-#define FALCON_EEPROM_BOOTCONFIG_START 0x800U
-#define FALCON_EEPROM_BOOTCONFIG_END 0x1800U
-
-#endif /* EFX_SPI_H */