From 76884835684411264cda2f15585261eb02183541 Mon Sep 17 00:00:00 2001 From: Ben Hutchings Date: Sun, 29 Nov 2009 15:10:44 +0000 Subject: sfc: Extend MTD driver for use with new NICs In new NICs flash is managed by firmware and we will use high-level operations on partitions rather than direct SPI commands. Add support for multiple MTD partitions per flash device and remove the direct link between MTD and SPI devices. Maintain a list of MTD partitions in struct efx_nic. Signed-off-by: Ben Hutchings Signed-off-by: David S. Miller --- drivers/net/sfc/mtd.c | 312 +++++++++++++++++++++++++++++++++++--------------- 1 file changed, 219 insertions(+), 93 deletions(-) (limited to 'drivers/net/sfc/mtd.c') diff --git a/drivers/net/sfc/mtd.c b/drivers/net/sfc/mtd.c index 820c233c3ea..3121e242d82 100644 --- a/drivers/net/sfc/mtd.c +++ b/drivers/net/sfc/mtd.c @@ -11,26 +11,58 @@ #include #include #include +#include #define EFX_DRIVER_NAME "sfc_mtd" #include "net_driver.h" #include "spi.h" #include "efx.h" +#include "falcon.h" #define EFX_SPI_VERIFY_BUF_LEN 16 -struct efx_mtd { - const struct efx_spi_device *spi; +struct efx_mtd_partition { struct mtd_info mtd; + size_t offset; + const char *type_name; char name[IFNAMSIZ + 20]; }; +struct efx_mtd_ops { + 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); +}; + +struct efx_mtd { + struct list_head node; + struct efx_nic *efx; + const struct efx_spi_device *spi; + const char *name; + const struct efx_mtd_ops *ops; + size_t n_parts; + struct efx_mtd_partition part[0]; +}; + +#define efx_for_each_partition(part, efx_mtd) \ + for ((part) = &(efx_mtd)->part[0]; \ + (part) != &(efx_mtd)->part[(efx_mtd)->n_parts]; \ + (part)++) + +#define to_efx_mtd_partition(mtd) \ + container_of(mtd, struct efx_mtd_partition, mtd) + +static int falcon_mtd_probe(struct efx_nic *efx); + /* SPI utilities */ static int efx_spi_slow_wait(struct efx_mtd *efx_mtd, bool uninterruptible) { const struct efx_spi_device *spi = efx_mtd->spi; - struct efx_nic *efx = spi->efx; + struct efx_nic *efx = efx_mtd->efx; u8 status; int rc, i; @@ -39,7 +71,7 @@ static int efx_spi_slow_wait(struct efx_mtd *efx_mtd, bool uninterruptible) __set_current_state(uninterruptible ? TASK_UNINTERRUPTIBLE : TASK_INTERRUPTIBLE); schedule_timeout(HZ / 10); - rc = falcon_spi_cmd(spi, SPI_RDSR, -1, NULL, + rc = falcon_spi_cmd(efx, spi, SPI_RDSR, -1, NULL, &status, sizeof(status)); if (rc) return rc; @@ -52,32 +84,35 @@ static int efx_spi_slow_wait(struct efx_mtd *efx_mtd, bool uninterruptible) return -ETIMEDOUT; } -static int efx_spi_unlock(const struct efx_spi_device *spi) +static int +efx_spi_unlock(struct efx_nic *efx, const struct efx_spi_device *spi) { const u8 unlock_mask = (SPI_STATUS_BP2 | SPI_STATUS_BP1 | SPI_STATUS_BP0); u8 status; int rc; - rc = falcon_spi_cmd(spi, SPI_RDSR, -1, NULL, &status, sizeof(status)); + 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(spi, SPI_WREN, -1, NULL, NULL, 0); + rc = falcon_spi_cmd(efx, spi, SPI_WREN, -1, NULL, NULL, 0); if (rc) return rc; - rc = falcon_spi_cmd(spi, SPI_SST_EWSR, -1, NULL, NULL, 0); + rc = falcon_spi_cmd(efx, spi, SPI_SST_EWSR, -1, NULL, NULL, 0); if (rc) return rc; status &= ~unlock_mask; - rc = falcon_spi_cmd(spi, SPI_WRSR, -1, &status, NULL, sizeof(status)); + rc = falcon_spi_cmd(efx, spi, SPI_WRSR, -1, &status, + NULL, sizeof(status)); if (rc) return rc; - rc = falcon_spi_wait_write(spi); + rc = falcon_spi_wait_write(efx, spi); if (rc) return rc; @@ -87,6 +122,7 @@ static int efx_spi_unlock(const struct efx_spi_device *spi) static int efx_spi_erase(struct efx_mtd *efx_mtd, loff_t start, size_t len) { const struct efx_spi_device *spi = efx_mtd->spi; + struct efx_nic *efx = efx_mtd->efx; unsigned pos, block_len; u8 empty[EFX_SPI_VERIFY_BUF_LEN]; u8 buffer[EFX_SPI_VERIFY_BUF_LEN]; @@ -98,13 +134,14 @@ static int efx_spi_erase(struct efx_mtd *efx_mtd, loff_t start, size_t len) if (spi->erase_command == 0) return -EOPNOTSUPP; - rc = efx_spi_unlock(spi); + rc = efx_spi_unlock(efx, spi); if (rc) return rc; - rc = falcon_spi_cmd(spi, SPI_WREN, -1, NULL, NULL, 0); + rc = falcon_spi_cmd(efx, spi, SPI_WREN, -1, NULL, NULL, 0); if (rc) return rc; - rc = falcon_spi_cmd(spi, spi->erase_command, start, NULL, NULL, 0); + rc = falcon_spi_cmd(efx, spi, spi->erase_command, start, NULL, + NULL, 0); if (rc) return rc; rc = efx_spi_slow_wait(efx_mtd, false); @@ -113,7 +150,8 @@ static int efx_spi_erase(struct efx_mtd *efx_mtd, loff_t start, size_t len) memset(empty, 0xff, sizeof(empty)); for (pos = 0; pos < len; pos += block_len) { block_len = min(len - pos, sizeof(buffer)); - rc = falcon_spi_read(spi, start + pos, block_len, NULL, buffer); + rc = falcon_spi_read(efx, spi, start + pos, block_len, + NULL, buffer); if (rc) return rc; if (memcmp(empty, buffer, block_len)) @@ -130,140 +168,228 @@ static int efx_spi_erase(struct efx_mtd *efx_mtd, loff_t start, size_t len) /* MTD interface */ -static int efx_mtd_read(struct mtd_info *mtd, loff_t start, size_t len, - size_t *retlen, u8 *buffer) +static int efx_mtd_erase(struct mtd_info *mtd, struct erase_info *erase) +{ + struct efx_mtd *efx_mtd = mtd->priv; + int rc; + + rc = efx_mtd->ops->erase(mtd, erase->addr, erase->len); + if (rc == 0) { + erase->state = MTD_ERASE_DONE; + } else { + erase->state = MTD_ERASE_FAILED; + erase->fail_addr = 0xffffffff; + } + mtd_erase_callback(erase); + return rc; +} + +static void efx_mtd_sync(struct mtd_info *mtd) +{ + struct efx_mtd *efx_mtd = mtd->priv; + struct efx_nic *efx = efx_mtd->efx; + int rc; + + rc = efx_mtd->ops->sync(mtd); + if (rc) + EFX_ERR(efx, "%s sync failed (%d)\n", efx_mtd->name, rc); +} + +static void efx_mtd_remove_partition(struct efx_mtd_partition *part) +{ + int rc; + + for (;;) { + rc = del_mtd_device(&part->mtd); + if (rc != -EBUSY) + break; + ssleep(1); + } + WARN_ON(rc); +} + +static void efx_mtd_remove_device(struct efx_mtd *efx_mtd) +{ + struct efx_mtd_partition *part; + + efx_for_each_partition(part, efx_mtd) + efx_mtd_remove_partition(part); + list_del(&efx_mtd->node); + kfree(efx_mtd); +} + +static void efx_mtd_rename_device(struct efx_mtd *efx_mtd) +{ + struct efx_mtd_partition *part; + + efx_for_each_partition(part, efx_mtd) + snprintf(part->name, sizeof(part->name), + "%s %s", efx_mtd->efx->name, + part->type_name); +} + +static int efx_mtd_probe_device(struct efx_nic *efx, struct efx_mtd *efx_mtd) +{ + struct efx_mtd_partition *part; + + efx_mtd->efx = efx; + + efx_mtd_rename_device(efx_mtd); + + efx_for_each_partition(part, efx_mtd) { + part->mtd.writesize = 1; + + part->mtd.owner = THIS_MODULE; + part->mtd.priv = efx_mtd; + 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.sync = efx_mtd_sync; + + if (add_mtd_device(&part->mtd)) + goto fail; + } + + list_add(&efx_mtd->node, &efx->mtd_list); + return 0; + +fail: + while (part != &efx_mtd->part[0]) { + --part; + efx_mtd_remove_partition(part); + } + /* add_mtd_device() returns 1 if the MTD table is full */ + return -ENOMEM; +} + +void efx_mtd_remove(struct efx_nic *efx) { + struct efx_mtd *efx_mtd, *next; + + WARN_ON(efx_dev_registered(efx)); + + list_for_each_entry_safe(efx_mtd, next, &efx->mtd_list, node) + efx_mtd_remove_device(efx_mtd); +} + +void efx_mtd_rename(struct efx_nic *efx) +{ + struct efx_mtd *efx_mtd; + + ASSERT_RTNL(); + + list_for_each_entry(efx_mtd, &efx->mtd_list, node) + efx_mtd_rename_device(efx_mtd); +} + +int efx_mtd_probe(struct efx_nic *efx) +{ + return falcon_mtd_probe(efx); +} + +/* Implementation of MTD operations for Falcon */ + +static int falcon_mtd_read(struct mtd_info *mtd, loff_t start, + size_t len, size_t *retlen, u8 *buffer) +{ + struct efx_mtd_partition *part = to_efx_mtd_partition(mtd); struct efx_mtd *efx_mtd = mtd->priv; const struct efx_spi_device *spi = efx_mtd->spi; - struct efx_nic *efx = spi->efx; + struct efx_nic *efx = efx_mtd->efx; int rc; rc = mutex_lock_interruptible(&efx->spi_lock); if (rc) return rc; - rc = falcon_spi_read(spi, FALCON_FLASH_BOOTCODE_START + start, - len, retlen, buffer); + rc = falcon_spi_read(efx, spi, part->offset + start, len, + retlen, buffer); mutex_unlock(&efx->spi_lock); return rc; } -static int efx_mtd_erase(struct mtd_info *mtd, struct erase_info *erase) +static int falcon_mtd_erase(struct mtd_info *mtd, loff_t start, size_t len) { + struct efx_mtd_partition *part = to_efx_mtd_partition(mtd); struct efx_mtd *efx_mtd = mtd->priv; - struct efx_nic *efx = efx_mtd->spi->efx; + struct efx_nic *efx = efx_mtd->efx; int rc; rc = mutex_lock_interruptible(&efx->spi_lock); if (rc) return rc; - rc = efx_spi_erase(efx_mtd, FALCON_FLASH_BOOTCODE_START + erase->addr, - erase->len); + rc = efx_spi_erase(efx_mtd, part->offset + start, len); mutex_unlock(&efx->spi_lock); - - if (rc == 0) { - erase->state = MTD_ERASE_DONE; - } else { - erase->state = MTD_ERASE_FAILED; - erase->fail_addr = 0xffffffff; - } - mtd_erase_callback(erase); return rc; } -static int efx_mtd_write(struct mtd_info *mtd, loff_t start, - size_t len, size_t *retlen, const u8 *buffer) +static int falcon_mtd_write(struct mtd_info *mtd, loff_t start, + size_t len, size_t *retlen, const u8 *buffer) { + struct efx_mtd_partition *part = to_efx_mtd_partition(mtd); struct efx_mtd *efx_mtd = mtd->priv; const struct efx_spi_device *spi = efx_mtd->spi; - struct efx_nic *efx = spi->efx; + struct efx_nic *efx = efx_mtd->efx; int rc; rc = mutex_lock_interruptible(&efx->spi_lock); if (rc) return rc; - rc = falcon_spi_write(spi, FALCON_FLASH_BOOTCODE_START + start, - len, retlen, buffer); + rc = falcon_spi_write(efx, spi, part->offset + start, len, + retlen, buffer); mutex_unlock(&efx->spi_lock); return rc; } -static void efx_mtd_sync(struct mtd_info *mtd) +static int falcon_mtd_sync(struct mtd_info *mtd) { struct efx_mtd *efx_mtd = mtd->priv; - struct efx_nic *efx = efx_mtd->spi->efx; + struct efx_nic *efx = efx_mtd->efx; int rc; mutex_lock(&efx->spi_lock); rc = efx_spi_slow_wait(efx_mtd, true); mutex_unlock(&efx->spi_lock); - - if (rc) - EFX_ERR(efx, "%s sync failed (%d)\n", efx_mtd->name, rc); - return; -} - -void efx_mtd_remove(struct efx_nic *efx) -{ - if (efx->spi_flash && efx->spi_flash->mtd) { - struct efx_mtd *efx_mtd = efx->spi_flash->mtd; - int rc; - - for (;;) { - rc = del_mtd_device(&efx_mtd->mtd); - if (rc != -EBUSY) - break; - ssleep(1); - } - WARN_ON(rc); - kfree(efx_mtd); - } + return rc; } -void efx_mtd_rename(struct efx_nic *efx) -{ - if (efx->spi_flash && efx->spi_flash->mtd) { - struct efx_mtd *efx_mtd = efx->spi_flash->mtd; - snprintf(efx_mtd->name, sizeof(efx_mtd->name), - "%s sfc_flash_bootrom", efx->name); - } -} +static struct efx_mtd_ops falcon_mtd_ops = { + .read = falcon_mtd_read, + .erase = falcon_mtd_erase, + .write = falcon_mtd_write, + .sync = falcon_mtd_sync, +}; -int efx_mtd_probe(struct efx_nic *efx) +static int falcon_mtd_probe(struct efx_nic *efx) { struct efx_spi_device *spi = efx->spi_flash; struct efx_mtd *efx_mtd; + int rc; + + ASSERT_RTNL(); if (!spi || spi->size <= FALCON_FLASH_BOOTCODE_START) return -ENODEV; - efx_mtd = kzalloc(sizeof(*efx_mtd), GFP_KERNEL); + efx_mtd = kzalloc(sizeof(*efx_mtd) + sizeof(efx_mtd->part[0]), + GFP_KERNEL); if (!efx_mtd) return -ENOMEM; efx_mtd->spi = spi; - spi->mtd = efx_mtd; - - efx_mtd->mtd.type = MTD_NORFLASH; - efx_mtd->mtd.flags = MTD_CAP_NORFLASH; - efx_mtd->mtd.size = spi->size - FALCON_FLASH_BOOTCODE_START; - efx_mtd->mtd.erasesize = spi->erase_size; - efx_mtd->mtd.writesize = 1; - efx_mtd_rename(efx); - - efx_mtd->mtd.owner = THIS_MODULE; - efx_mtd->mtd.priv = efx_mtd; - efx_mtd->mtd.name = efx_mtd->name; - efx_mtd->mtd.erase = efx_mtd_erase; - efx_mtd->mtd.read = efx_mtd_read; - efx_mtd->mtd.write = efx_mtd_write; - efx_mtd->mtd.sync = efx_mtd_sync; - - if (add_mtd_device(&efx_mtd->mtd)) { + efx_mtd->name = "flash"; + efx_mtd->ops = &falcon_mtd_ops; + + efx_mtd->n_parts = 1; + efx_mtd->part[0].mtd.type = MTD_NORFLASH; + efx_mtd->part[0].mtd.flags = MTD_CAP_NORFLASH; + efx_mtd->part[0].mtd.size = spi->size - FALCON_FLASH_BOOTCODE_START; + efx_mtd->part[0].mtd.erasesize = spi->erase_size; + efx_mtd->part[0].offset = FALCON_FLASH_BOOTCODE_START; + efx_mtd->part[0].type_name = "sfc_flash_bootrom"; + + rc = efx_mtd_probe_device(efx, efx_mtd); + if (rc) kfree(efx_mtd); - spi->mtd = NULL; - /* add_mtd_device() returns 1 if the MTD table is full */ - return -ENOMEM; - } - - return 0; + return rc; } -- cgit From 744093c98363f8a65853aed39708c9effc80f8ff Mon Sep 17 00:00:00 2001 From: Ben Hutchings Date: Sun, 29 Nov 2009 15:12:08 +0000 Subject: sfc: Rename falcon.h to nic.h nic.h is no longer specific to Falcon. Signed-off-by: Ben Hutchings Signed-off-by: David S. Miller --- drivers/net/sfc/mtd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net/sfc/mtd.c') diff --git a/drivers/net/sfc/mtd.c b/drivers/net/sfc/mtd.c index 3121e242d82..65a22f193f9 100644 --- a/drivers/net/sfc/mtd.c +++ b/drivers/net/sfc/mtd.c @@ -17,7 +17,7 @@ #include "net_driver.h" #include "spi.h" #include "efx.h" -#include "falcon.h" +#include "nic.h" #define EFX_SPI_VERIFY_BUF_LEN 16 -- cgit From 8880f4ec21e668dcab3c6d387524a887e5bcbf73 Mon Sep 17 00:00:00 2001 From: Ben Hutchings Date: Sun, 29 Nov 2009 15:15:41 +0000 Subject: sfc: Add support for SFC9000 family (2) This integrates support for the SFC9000 family of 10G Ethernet controllers and LAN-on-motherboard chips, starting with the SFL9021 'Siena' and SFC9020 'Bethpage'. Credit for this code is largely due to my colleagues at Solarflare: Guido Barzini Steve Hodgson Kieran Mansley Matthew Slattery Neil Turton Signed-off-by: Ben Hutchings Signed-off-by: David S. Miller --- drivers/net/sfc/mtd.c | 267 +++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 262 insertions(+), 5 deletions(-) (limited to 'drivers/net/sfc/mtd.c') diff --git a/drivers/net/sfc/mtd.c b/drivers/net/sfc/mtd.c index 65a22f193f9..ef561f8af4d 100644 --- a/drivers/net/sfc/mtd.c +++ b/drivers/net/sfc/mtd.c @@ -8,6 +8,7 @@ * by the Free Software Foundation, incorporated herein by reference. */ +#include #include #include #include @@ -18,12 +19,22 @@ #include "spi.h" #include "efx.h" #include "nic.h" +#include "mcdi.h" +#include "mcdi_pcol.h" #define EFX_SPI_VERIFY_BUF_LEN 16 +#define EFX_MCDI_CHUNK_LEN 128 struct efx_mtd_partition { struct mtd_info mtd; - size_t offset; + union { + struct { + bool updating; + u8 nvram_type; + u16 fw_subtype; + } mcdi; + size_t offset; + }; const char *type_name; char name[IFNAMSIZ + 20]; }; @@ -56,6 +67,7 @@ struct efx_mtd { container_of(mtd, struct efx_mtd_partition, mtd) static int falcon_mtd_probe(struct efx_nic *efx); +static int siena_mtd_probe(struct efx_nic *efx); /* SPI utilities */ @@ -223,9 +235,14 @@ static void efx_mtd_rename_device(struct efx_mtd *efx_mtd) struct efx_mtd_partition *part; efx_for_each_partition(part, efx_mtd) - snprintf(part->name, sizeof(part->name), - "%s %s", efx_mtd->efx->name, - part->type_name); + if (efx_nic_rev(efx_mtd->efx) >= EFX_REV_SIENA_A0) + snprintf(part->name, sizeof(part->name), + "%s %s:%02x", efx_mtd->efx->name, + part->type_name, part->mcdi.fw_subtype); + else + snprintf(part->name, sizeof(part->name), + "%s %s", efx_mtd->efx->name, + part->type_name); } static int efx_mtd_probe_device(struct efx_nic *efx, struct efx_mtd *efx_mtd) @@ -285,7 +302,10 @@ void efx_mtd_rename(struct efx_nic *efx) int efx_mtd_probe(struct efx_nic *efx) { - return falcon_mtd_probe(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 */ @@ -393,3 +413,240 @@ static int falcon_mtd_probe(struct efx_nic *efx) kfree(efx_mtd); return rc; } + +/* Implementation of MTD operations for Siena */ + +static int siena_mtd_read(struct mtd_info *mtd, loff_t start, + size_t len, size_t *retlen, u8 *buffer) +{ + struct efx_mtd_partition *part = to_efx_mtd_partition(mtd); + struct efx_mtd *efx_mtd = mtd->priv; + struct efx_nic *efx = efx_mtd->efx; + 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_CHUNK_LEN); + rc = efx_mcdi_nvram_read(efx, part->mcdi.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_mtd_partition *part = to_efx_mtd_partition(mtd); + struct efx_mtd *efx_mtd = mtd->priv; + struct efx_nic *efx = efx_mtd->efx; + loff_t offset = start & ~((loff_t)(mtd->erasesize - 1)); + loff_t end = min_t(loff_t, start + len, mtd->size); + size_t chunk = part->mtd.erasesize; + int rc = 0; + + if (!part->mcdi.updating) { + rc = efx_mcdi_nvram_update_start(efx, part->mcdi.nvram_type); + if (rc) + goto out; + part->mcdi.updating = 1; + } + + /* 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->mcdi.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_mtd_partition *part = to_efx_mtd_partition(mtd); + struct efx_mtd *efx_mtd = mtd->priv; + struct efx_nic *efx = efx_mtd->efx; + loff_t offset = start; + loff_t end = min_t(loff_t, start + len, mtd->size); + size_t chunk; + int rc = 0; + + if (!part->mcdi.updating) { + rc = efx_mcdi_nvram_update_start(efx, part->mcdi.nvram_type); + if (rc) + goto out; + part->mcdi.updating = 1; + } + + while (offset < end) { + chunk = min_t(size_t, end - offset, EFX_MCDI_CHUNK_LEN); + rc = efx_mcdi_nvram_write(efx, part->mcdi.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_mtd_partition *part = to_efx_mtd_partition(mtd); + struct efx_mtd *efx_mtd = mtd->priv; + struct efx_nic *efx = efx_mtd->efx; + int rc = 0; + + if (part->mcdi.updating) { + part->mcdi.updating = 0; + rc = efx_mcdi_nvram_update_finish(efx, part->mcdi.nvram_type); + } + + return rc; +} + +static struct efx_mtd_ops siena_mtd_ops = { + .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 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" }, +}; + +static int siena_mtd_probe_partition(struct efx_nic *efx, + struct efx_mtd *efx_mtd, + unsigned int part_id, + unsigned int type) +{ + struct efx_mtd_partition *part = &efx_mtd->part[part_id]; + struct siena_nvram_type_info *info; + size_t size, erase_size; + bool protected; + int rc; + + if (type >= ARRAY_SIZE(siena_nvram_types)) + 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->mcdi.nvram_type = type; + part->type_name = info->name; + + part->mtd.type = MTD_NORFLASH; + part->mtd.flags = MTD_CAP_NORFLASH; + part->mtd.size = size; + part->mtd.erasesize = erase_size; + + return 0; +} + +static int siena_mtd_get_fw_subtypes(struct efx_nic *efx, + struct efx_mtd *efx_mtd) +{ + struct efx_mtd_partition *part; + uint16_t fw_subtype_list[MC_CMD_GET_BOARD_CFG_OUT_FW_SUBTYPE_LIST_LEN / + sizeof(uint16_t)]; + int rc; + + rc = efx_mcdi_get_board_cfg(efx, NULL, fw_subtype_list); + if (rc) + return rc; + + efx_for_each_partition(part, efx_mtd) + part->mcdi.fw_subtype = fw_subtype_list[part->mcdi.nvram_type]; + + return 0; +} + +static int siena_mtd_probe(struct efx_nic *efx) +{ + struct efx_mtd *efx_mtd; + int rc = -ENODEV; + u32 nvram_types; + unsigned int type; + + ASSERT_RTNL(); + + rc = efx_mcdi_nvram_types(efx, &nvram_types); + if (rc) + return rc; + + efx_mtd = kzalloc(sizeof(*efx_mtd) + + hweight32(nvram_types) * sizeof(efx_mtd->part[0]), + GFP_KERNEL); + if (!efx_mtd) + return -ENOMEM; + + efx_mtd->name = "Siena NVRAM manager"; + + efx_mtd->ops = &siena_mtd_ops; + + type = 0; + efx_mtd->n_parts = 0; + + while (nvram_types != 0) { + if (nvram_types & 1) { + rc = siena_mtd_probe_partition(efx, efx_mtd, + efx_mtd->n_parts, type); + if (rc == 0) + efx_mtd->n_parts++; + else if (rc != -ENODEV) + goto fail; + } + type++; + nvram_types >>= 1; + } + + rc = siena_mtd_get_fw_subtypes(efx, efx_mtd); + if (rc) + goto fail; + + rc = efx_mtd_probe_device(efx, efx_mtd); +fail: + if (rc) + kfree(efx_mtd); + return rc; +} + -- cgit From 906bb26c0624d87df74e6642f2d74cde176fcc12 Mon Sep 17 00:00:00 2001 From: Ben Hutchings Date: Sun, 29 Nov 2009 15:16:19 +0000 Subject: sfc: Update version, copyright dates, authors This driver has been mostly rewritten since Michael Brown's initial work, so swap the order of the authors. Signed-off-by: Ben Hutchings Signed-off-by: David S. Miller --- drivers/net/sfc/mtd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net/sfc/mtd.c') diff --git a/drivers/net/sfc/mtd.c b/drivers/net/sfc/mtd.c index ef561f8af4d..3a464529a46 100644 --- a/drivers/net/sfc/mtd.c +++ b/drivers/net/sfc/mtd.c @@ -1,7 +1,7 @@ /**************************************************************************** * Driver for Solarflare Solarstorm network controllers and boards * Copyright 2005-2006 Fen Systems Ltd. - * Copyright 2006-2008 Solarflare Communications Inc. + * Copyright 2006-2009 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 -- cgit