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:

committed by
David S. Miller

parent
afd4aea03f
commit
8880f4ec21
@@ -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;
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user