aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/ethernet/sfc/mtd.c
diff options
context:
space:
mode:
authorDavid S. Miller <davem@davemloft.net>2013-08-29 01:56:01 -0400
committerDavid S. Miller <davem@davemloft.net>2013-08-29 01:56:01 -0400
commit4c9d546f6c522f541dfb01e192ab7101eca0053b (patch)
tree4def585cba646c5825abbeb9d965a962b3c74b5e /drivers/net/ethernet/sfc/mtd.c
parentMerge tag 'batman-adv-for-davem' of git://git.open-mesh.org/linux-merge (diff)
parentsfc: Use extended MC_CMD_SENSOR_INFO and MC_CMD_READ_SENSORS (diff)
downloadlinux-dev-4c9d546f6c522f541dfb01e192ab7101eca0053b.tar.xz
linux-dev-4c9d546f6c522f541dfb01e192ab7101eca0053b.zip
Merge branch 'for-davem' of git://git.kernel.org/pub/scm/linux/kernel/git/bwh/sfc-next
Ben Hutchings says: ==================== 1. Further cleanup and refactoring in preparation for EF10. 2. Remove ethtool stats that are always zero on Falcon boards. 3. Add an ethtool stat for merged TX completions. 4. Prepare to support merged RX completions. 5. Prepare to support more hwmon sensors. 6. Add support for new events that are generated by EF10 firmware. 7. Update MC reboot detection for EF10. ==================== Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net/ethernet/sfc/mtd.c')
-rw-r--r--drivers/net/ethernet/sfc/mtd.c551
1 files changed, 15 insertions, 536 deletions
diff --git a/drivers/net/ethernet/sfc/mtd.c b/drivers/net/ethernet/sfc/mtd.c
index ba6c87a73d86..8be9a69a61e1 100644
--- a/drivers/net/ethernet/sfc/mtd.c
+++ b/drivers/net/ethernet/sfc/mtd.c
@@ -8,168 +8,17 @@
* 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;
- union {
- struct {
- bool updating;
- u8 nvram_type;
- u16 fw_subtype;
- } mcdi;
- struct {
- const struct falcon_spi_device *spi;
- size_t offset;
- } falcon;
- };
- const char *dev_type_name;
- 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);
-};
#define to_efx_mtd_partition(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 */
-
-static int
-falcon_spi_slow_wait(struct efx_mtd_partition *part, bool uninterruptible)
-{
- const struct falcon_spi_device *spi = part->falcon.spi;
- struct efx_nic *efx = part->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->name, part->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 efx_mtd_partition *part, loff_t start, size_t len)
-{
- const struct falcon_spi_device *spi = part->falcon.spi;
- struct efx_nic *efx = part->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)
@@ -177,7 +26,7 @@ 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 {
@@ -194,7 +43,7 @@ static void efx_mtd_sync(struct mtd_info *mtd)
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);
@@ -214,26 +63,15 @@ static void efx_mtd_remove_partition(struct efx_mtd_partition *part)
list_del(&part->node);
}
-static void efx_mtd_rename_partition(struct efx_mtd_partition *part)
-{
- struct efx_nic *efx = part->mtd.priv;
-
- if (efx_nic_rev(efx) >= EFX_REV_SIENA_A0)
- snprintf(part->name, sizeof(part->name), "%s %s:%02x",
- efx->name, part->type_name, part->mcdi.fw_subtype);
- else
- snprintf(part->name, sizeof(part->name), "%s %s",
- efx->name, part->type_name);
-}
-
-static int efx_mtd_add(struct efx_nic *efx,
- struct efx_mtd_partition *parts, size_t n_parts)
+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;
for (i = 0; i < n_parts; i++) {
- part = &parts[i];
+ part = (struct efx_mtd_partition *)((char *)parts +
+ i * sizeof_part);
part->mtd.writesize = 1;
@@ -241,11 +79,11 @@ static int efx_mtd_add(struct efx_nic *efx,
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;
@@ -257,8 +95,11 @@ static int efx_mtd_add(struct efx_nic *efx,
return 0;
fail:
- while (i--)
- efx_mtd_remove_partition(&parts[i]);
+ while (i--) {
+ part = (struct efx_mtd_partition *)((char *)parts +
+ i * sizeof_part);
+ efx_mtd_remove_partition(part);
+ }
/* Failure is unlikely here, but probably means we're out of memory */
return -ENOMEM;
}
@@ -288,367 +129,5 @@ void efx_mtd_rename(struct efx_nic *efx)
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 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_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->falcon.spi, part->falcon.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 efx_mtd_partition *part = to_efx_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->falcon.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 efx_mtd_partition *part = to_efx_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->falcon.spi,
- part->falcon.offset + start, len, retlen, buffer);
- mutex_unlock(&nic_data->spi_lock);
- return rc;
-}
-
-static int falcon_mtd_sync(struct mtd_info *mtd)
-{
- struct efx_mtd_partition *part = to_efx_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 = {
- .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 efx_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].falcon.spi = spi;
- parts[n_parts].falcon.offset = FALCON_FLASH_BOOTCODE_START;
- parts[n_parts].dev_type_name = "flash";
- parts[n_parts].type_name = "sfc_flash_bootrom";
- parts[n_parts].mtd.type = MTD_NORFLASH;
- parts[n_parts].mtd.flags = MTD_CAP_NORFLASH;
- parts[n_parts].mtd.size = spi->size - FALCON_FLASH_BOOTCODE_START;
- parts[n_parts].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].falcon.spi = spi;
- parts[n_parts].falcon.offset = FALCON_EEPROM_BOOTCONFIG_START;
- parts[n_parts].dev_type_name = "EEPROM";
- parts[n_parts].type_name = "sfc_bootconfig";
- parts[n_parts].mtd.type = MTD_RAM;
- parts[n_parts].mtd.flags = MTD_CAP_RAM;
- parts[n_parts].mtd.size =
- min(spi->size, FALCON_EEPROM_BOOTCONFIG_END) -
- FALCON_EEPROM_BOOTCONFIG_START;
- parts[n_parts].mtd.erasesize = spi->erase_size;
- n_parts++;
- }
-
- rc = efx_mtd_add(efx, parts, n_parts);
- if (rc)
- kfree(parts);
- 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_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->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_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->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 = 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->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_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->mcdi.updating) {
- rc = efx_mcdi_nvram_update_start(efx, part->mcdi.nvram_type);
- if (rc)
- goto out;
- part->mcdi.updating = true;
- }
-
- while (offset < end) {
- chunk = min_t(size_t, end - offset, EFX_MCDI_NVRAM_LEN_MAX);
- 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_nic *efx = mtd->priv;
- int rc = 0;
-
- if (part->mcdi.updating) {
- part->mcdi.updating = false;
- rc = efx_mcdi_nvram_update_finish(efx, part->mcdi.nvram_type);
- }
-
- return rc;
+ efx->type->mtd_rename(part);
}
-
-static const 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 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_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->mcdi.nvram_type = type;
- part->dev_type_name = "Siena NVRAM manager";
- 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_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].mcdi.fw_subtype =
- fw_subtype_list[parts[i].mcdi.nvram_type];
-
- return 0;
-}
-
-static int siena_mtd_probe(struct efx_nic *efx)
-{
- struct efx_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, n_parts);
-fail:
- if (rc)
- kfree(parts);
- return rc;
-}
-