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 <bhutchings@solarflare.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
Ben Hutchings
2009-11-29 15:15:41 +00:00
committed by David S. Miller
parent afd4aea03f
commit 8880f4ec21
14 changed files with 414 additions and 29 deletions

View File

@@ -8,6 +8,7 @@
* 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>
@@ -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;
}