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:
parent
afd4aea03f
commit
8880f4ec21
|
@ -1,5 +1,5 @@
|
|||
config SFC
|
||||
tristate "Solarflare Solarstorm SFC4000 support"
|
||||
tristate "Solarflare Solarstorm SFC4000/SFC9000-family support"
|
||||
depends on PCI && INET
|
||||
select MDIO
|
||||
select CRC32
|
||||
|
@ -7,15 +7,16 @@ config SFC
|
|||
select I2C_ALGOBIT
|
||||
help
|
||||
This driver supports 10-gigabit Ethernet cards based on
|
||||
the Solarflare Communications Solarstorm SFC4000 controller.
|
||||
the Solarflare Communications Solarstorm SFC4000 and
|
||||
SFC9000-family controllers.
|
||||
|
||||
To compile this driver as a module, choose M here. The module
|
||||
will be called sfc.
|
||||
config SFC_MTD
|
||||
bool "Solarflare Solarstorm SFC4000 flash MTD support"
|
||||
bool "Solarflare Solarstorm SFC4000/SFC9000-family MTD support"
|
||||
depends on SFC && MTD && !(SFC=y && MTD=m)
|
||||
default y
|
||||
help
|
||||
This exposes the on-board flash memory as an MTD device (e.g.
|
||||
/dev/mtd1). This makes it possible to upload new boot code
|
||||
to the NIC.
|
||||
This exposes the on-board flash memory as MTD devices (e.g.
|
||||
/dev/mtd1). This makes it possible to upload new firmware
|
||||
to the NIC.
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
sfc-y += efx.o nic.o falcon.o tx.o rx.o falcon_gmac.o \
|
||||
falcon_xmac.o selftest.o ethtool.o qt202x_phy.o \
|
||||
mdio_10g.o tenxpress.o falcon_boards.o
|
||||
sfc-y += efx.o nic.o falcon.o siena.o tx.o rx.o \
|
||||
falcon_gmac.o falcon_xmac.o mcdi_mac.o \
|
||||
selftest.o ethtool.o qt202x_phy.o mdio_10g.o \
|
||||
tenxpress.o falcon_boards.o mcdi.o mcdi_phy.o
|
||||
sfc-$(CONFIG_SFC_MTD) += mtd.o
|
||||
|
||||
obj-$(CONFIG_SFC) += sfc.o
|
||||
|
|
|
@ -37,6 +37,8 @@
|
|||
#define EFX_DWORD_2_WIDTH 32
|
||||
#define EFX_DWORD_3_LBN 96
|
||||
#define EFX_DWORD_3_WIDTH 32
|
||||
#define EFX_QWORD_0_LBN 0
|
||||
#define EFX_QWORD_0_WIDTH 64
|
||||
|
||||
/* Specified attribute (e.g. LBN) of the specified field */
|
||||
#define EFX_VAL(field, attribute) field ## _ ## attribute
|
||||
|
|
|
@ -25,6 +25,8 @@
|
|||
#include "mdio_10g.h"
|
||||
#include "nic.h"
|
||||
|
||||
#include "mcdi.h"
|
||||
|
||||
/**************************************************************************
|
||||
*
|
||||
* Type name strings
|
||||
|
@ -84,6 +86,7 @@ const char *efx_reset_type_names[] = {
|
|||
[RESET_TYPE_RX_DESC_FETCH] = "RX_DESC_FETCH",
|
||||
[RESET_TYPE_TX_DESC_FETCH] = "TX_DESC_FETCH",
|
||||
[RESET_TYPE_TX_SKIP] = "TX_SKIP",
|
||||
[RESET_TYPE_MC_FAILURE] = "MC_FAILURE",
|
||||
};
|
||||
|
||||
#define EFX_MAX_MTU (9 * 1024)
|
||||
|
@ -1191,6 +1194,15 @@ static void efx_start_all(struct efx_nic *efx)
|
|||
|
||||
efx_nic_enable_interrupts(efx);
|
||||
|
||||
/* Switch to event based MCDI completions after enabling interrupts.
|
||||
* If a reset has been scheduled, then we need to stay in polled mode.
|
||||
* Rather than serialising efx_mcdi_mode_event() [which sleeps] and
|
||||
* reset_pending [modified from an atomic context], we instead guarantee
|
||||
* that efx_mcdi_mode_poll() isn't reverted erroneously */
|
||||
efx_mcdi_mode_event(efx);
|
||||
if (efx->reset_pending != RESET_TYPE_NONE)
|
||||
efx_mcdi_mode_poll(efx);
|
||||
|
||||
/* Start the hardware monitor if there is one. Otherwise (we're link
|
||||
* event driven), we have to poll the PHY because after an event queue
|
||||
* flush, we could have a missed a link state change */
|
||||
|
@ -1242,6 +1254,9 @@ static void efx_stop_all(struct efx_nic *efx)
|
|||
|
||||
efx->type->stop_stats(efx);
|
||||
|
||||
/* Switch to MCDI polling on Siena before disabling interrupts */
|
||||
efx_mcdi_mode_poll(efx);
|
||||
|
||||
/* Disable interrupts and wait for ISR to complete */
|
||||
efx_nic_disable_interrupts(efx);
|
||||
if (efx->legacy_irq)
|
||||
|
@ -1445,6 +1460,8 @@ static int efx_net_open(struct net_device *net_dev)
|
|||
return -EIO;
|
||||
if (efx->phy_mode & PHY_MODE_SPECIAL)
|
||||
return -EBUSY;
|
||||
if (efx_mcdi_poll_reboot(efx) && efx_reset(efx, RESET_TYPE_ALL))
|
||||
return -EIO;
|
||||
|
||||
/* Notify the kernel of the link state polled during driver load,
|
||||
* before the monitor starts running */
|
||||
|
@ -1895,6 +1912,7 @@ void efx_schedule_reset(struct efx_nic *efx, enum reset_type type)
|
|||
case RESET_TYPE_TX_SKIP:
|
||||
method = RESET_TYPE_INVISIBLE;
|
||||
break;
|
||||
case RESET_TYPE_MC_FAILURE:
|
||||
default:
|
||||
method = RESET_TYPE_ALL;
|
||||
break;
|
||||
|
@ -1908,6 +1926,10 @@ void efx_schedule_reset(struct efx_nic *efx, enum reset_type type)
|
|||
|
||||
efx->reset_pending = method;
|
||||
|
||||
/* efx_process_channel() will no longer read events once a
|
||||
* reset is scheduled. So switch back to poll'd MCDI completions. */
|
||||
efx_mcdi_mode_poll(efx);
|
||||
|
||||
queue_work(reset_workqueue, &efx->reset_work);
|
||||
}
|
||||
|
||||
|
@ -1923,6 +1945,10 @@ static struct pci_device_id efx_pci_table[] __devinitdata = {
|
|||
.driver_data = (unsigned long) &falcon_a1_nic_type},
|
||||
{PCI_DEVICE(EFX_VENDID_SFC, FALCON_B_P_DEVID),
|
||||
.driver_data = (unsigned long) &falcon_b0_nic_type},
|
||||
{PCI_DEVICE(EFX_VENDID_SFC, BETHPAGE_A_P_DEVID),
|
||||
.driver_data = (unsigned long) &siena_a0_nic_type},
|
||||
{PCI_DEVICE(EFX_VENDID_SFC, SIENA_A_P_DEVID),
|
||||
.driver_data = (unsigned long) &siena_a0_nic_type},
|
||||
{0} /* end of list */
|
||||
};
|
||||
|
||||
|
|
|
@ -18,6 +18,8 @@
|
|||
#define FALCON_A_P_DEVID 0x0703
|
||||
#define FALCON_A_S_DEVID 0x6703
|
||||
#define FALCON_B_P_DEVID 0x0710
|
||||
#define BETHPAGE_A_P_DEVID 0x0803
|
||||
#define SIENA_A_P_DEVID 0x0813
|
||||
|
||||
/* Solarstorm controllers use BAR 0 for I/O space and BAR 2(&3) for memory */
|
||||
#define EFX_MEM_BAR 2
|
||||
|
|
|
@ -144,6 +144,7 @@ enum efx_loopback_mode {
|
|||
* @RESET_TYPE_RX_DESC_FETCH: pcie error during rx descriptor fetch
|
||||
* @RESET_TYPE_TX_DESC_FETCH: pcie error during tx descriptor fetch
|
||||
* @RESET_TYPE_TX_SKIP: hardware completed empty tx descriptors
|
||||
* @RESET_TYPE_MC_FAILURE: MC reboot/assertion
|
||||
*/
|
||||
enum reset_type {
|
||||
RESET_TYPE_NONE = -1,
|
||||
|
@ -158,6 +159,7 @@ enum reset_type {
|
|||
RESET_TYPE_RX_DESC_FETCH,
|
||||
RESET_TYPE_TX_DESC_FETCH,
|
||||
RESET_TYPE_TX_SKIP,
|
||||
RESET_TYPE_MC_FAILURE,
|
||||
RESET_TYPE_MAX,
|
||||
};
|
||||
|
||||
|
|
|
@ -236,6 +236,9 @@ static void efx_ethtool_get_drvinfo(struct net_device *net_dev,
|
|||
|
||||
strlcpy(info->driver, EFX_DRIVER_NAME, sizeof(info->driver));
|
||||
strlcpy(info->version, EFX_DRIVER_VERSION, sizeof(info->version));
|
||||
if (efx_nic_rev(efx) >= EFX_REV_SIENA_A0)
|
||||
siena_print_fwver(efx, info->fw_version,
|
||||
sizeof(info->fw_version));
|
||||
strlcpy(info->bus_info, pci_name(efx->pci_dev), sizeof(info->bus_info));
|
||||
}
|
||||
|
||||
|
|
|
@ -15,6 +15,9 @@
|
|||
|
||||
extern struct efx_mac_operations falcon_gmac_operations;
|
||||
extern struct efx_mac_operations falcon_xmac_operations;
|
||||
extern struct efx_mac_operations efx_mcdi_mac_operations;
|
||||
extern void falcon_reconfigure_xmac_core(struct efx_nic *efx);
|
||||
extern int efx_mcdi_mac_stats(struct efx_nic *efx, dma_addr_t dma_addr,
|
||||
u32 dma_len, int enable, int clear);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -706,6 +706,7 @@ union efx_multicast_hash {
|
|||
* @phy_op: PHY interface
|
||||
* @phy_data: PHY private data (including PHY-specific stats)
|
||||
* @mdio: PHY MDIO interface
|
||||
* @mdio_bus: PHY MDIO bus ID (only used by Siena)
|
||||
* @phy_mode: PHY operating mode. Serialised by @mac_lock.
|
||||
* @xmac_poll_required: XMAC link state needs polling
|
||||
* @link_advertising: Autonegotiation advertising flags
|
||||
|
@ -756,6 +757,7 @@ struct efx_nic {
|
|||
|
||||
struct efx_buffer irq_status;
|
||||
volatile signed int last_irq_cpu;
|
||||
unsigned long irq_zero_count;
|
||||
|
||||
struct efx_spi_device *spi_flash;
|
||||
struct efx_spi_device *spi_eeprom;
|
||||
|
@ -766,7 +768,7 @@ struct efx_nic {
|
|||
|
||||
unsigned n_rx_nodesc_drop_cnt;
|
||||
|
||||
struct falcon_nic_data *nic_data;
|
||||
void *nic_data;
|
||||
|
||||
struct mutex mac_lock;
|
||||
struct work_struct mac_work;
|
||||
|
@ -792,6 +794,7 @@ struct efx_nic {
|
|||
struct efx_phy_operations *phy_op;
|
||||
void *phy_data;
|
||||
struct mdio_if_info mdio;
|
||||
unsigned int mdio_bus;
|
||||
enum efx_phy_mode phy_mode;
|
||||
|
||||
bool xmac_poll_required;
|
||||
|
@ -824,6 +827,11 @@ static inline const char *efx_dev_name(struct efx_nic *efx)
|
|||
return efx_dev_registered(efx) ? efx->name : "";
|
||||
}
|
||||
|
||||
static inline unsigned int efx_port_num(struct efx_nic *efx)
|
||||
{
|
||||
return PCI_FUNC(efx->pci_dev->devfn);
|
||||
}
|
||||
|
||||
/**
|
||||
* struct efx_nic_type - Efx device type definition
|
||||
* @probe: Probe the controller
|
||||
|
|
|
@ -997,6 +997,9 @@ int efx_nic_process_eventq(struct efx_channel *channel, int rx_quota)
|
|||
case FSE_AZ_EV_CODE_DRIVER_EV:
|
||||
efx_handle_driver_event(channel, &event);
|
||||
break;
|
||||
case FSE_CZ_EV_CODE_MCDI_EV:
|
||||
efx_mcdi_process_event(channel, &event);
|
||||
break;
|
||||
default:
|
||||
EFX_ERR(channel->efx, "channel %d unknown event type %d"
|
||||
" (data " EFX_QWORD_FMT ")\n", channel->channel,
|
||||
|
@ -1025,13 +1028,21 @@ int efx_nic_probe_eventq(struct efx_channel *channel)
|
|||
|
||||
void efx_nic_init_eventq(struct efx_channel *channel)
|
||||
{
|
||||
efx_oword_t evq_ptr;
|
||||
efx_oword_t reg;
|
||||
struct efx_nic *efx = channel->efx;
|
||||
|
||||
EFX_LOG(efx, "channel %d event queue in special buffers %d-%d\n",
|
||||
channel->channel, channel->eventq.index,
|
||||
channel->eventq.index + channel->eventq.entries - 1);
|
||||
|
||||
if (efx_nic_rev(efx) >= EFX_REV_SIENA_A0) {
|
||||
EFX_POPULATE_OWORD_3(reg,
|
||||
FRF_CZ_TIMER_Q_EN, 1,
|
||||
FRF_CZ_HOST_NOTIFY_MODE, 0,
|
||||
FRF_CZ_TIMER_MODE, FFE_CZ_TIMER_MODE_DIS);
|
||||
efx_writeo_table(efx, ®, FR_BZ_TIMER_TBL, channel->channel);
|
||||
}
|
||||
|
||||
/* Pin event queue buffer */
|
||||
efx_init_special_buffer(efx, &channel->eventq);
|
||||
|
||||
|
@ -1039,11 +1050,11 @@ void efx_nic_init_eventq(struct efx_channel *channel)
|
|||
memset(channel->eventq.addr, 0xff, channel->eventq.len);
|
||||
|
||||
/* Push event queue to card */
|
||||
EFX_POPULATE_OWORD_3(evq_ptr,
|
||||
EFX_POPULATE_OWORD_3(reg,
|
||||
FRF_AZ_EVQ_EN, 1,
|
||||
FRF_AZ_EVQ_SIZE, __ffs(channel->eventq.entries),
|
||||
FRF_AZ_EVQ_BUF_BASE_ID, channel->eventq.index);
|
||||
efx_writeo_table(efx, &evq_ptr, efx->type->evq_ptr_tbl_base,
|
||||
efx_writeo_table(efx, ®, efx->type->evq_ptr_tbl_base,
|
||||
channel->channel);
|
||||
|
||||
efx->type->push_irq_moderation(channel);
|
||||
|
@ -1051,13 +1062,15 @@ void efx_nic_init_eventq(struct efx_channel *channel)
|
|||
|
||||
void efx_nic_fini_eventq(struct efx_channel *channel)
|
||||
{
|
||||
efx_oword_t eventq_ptr;
|
||||
efx_oword_t reg;
|
||||
struct efx_nic *efx = channel->efx;
|
||||
|
||||
/* Remove event queue from card */
|
||||
EFX_ZERO_OWORD(eventq_ptr);
|
||||
efx_writeo_table(efx, &eventq_ptr, efx->type->evq_ptr_tbl_base,
|
||||
EFX_ZERO_OWORD(reg);
|
||||
efx_writeo_table(efx, ®, efx->type->evq_ptr_tbl_base,
|
||||
channel->channel);
|
||||
if (efx_nic_rev(efx) >= EFX_REV_SIENA_A0)
|
||||
efx_writeo_table(efx, ®, FR_BZ_TIMER_TBL, channel->channel);
|
||||
|
||||
/* Unpin event queue */
|
||||
efx_fini_special_buffer(efx, &channel->eventq);
|
||||
|
@ -1220,8 +1233,15 @@ static inline void efx_nic_interrupts(struct efx_nic *efx,
|
|||
bool enabled, bool force)
|
||||
{
|
||||
efx_oword_t int_en_reg_ker;
|
||||
unsigned int level = 0;
|
||||
|
||||
EFX_POPULATE_OWORD_2(int_en_reg_ker,
|
||||
if (EFX_WORKAROUND_17213(efx) && !EFX_INT_MODE_USE_MSI(efx))
|
||||
/* Set the level always even if we're generating a test
|
||||
* interrupt, because our legacy interrupt handler is safe */
|
||||
level = 0x1f;
|
||||
|
||||
EFX_POPULATE_OWORD_3(int_en_reg_ker,
|
||||
FRF_AZ_KER_INT_LEVE_SEL, level,
|
||||
FRF_AZ_KER_INT_KER, force,
|
||||
FRF_AZ_DRV_INT_EN_KER, enabled);
|
||||
efx_writeo(efx, &int_en_reg_ker, FR_AZ_INT_EN_KER);
|
||||
|
@ -1334,15 +1354,30 @@ static irqreturn_t efx_legacy_interrupt(int irq, void *dev_id)
|
|||
if (unlikely(syserr))
|
||||
return efx_nic_fatal_interrupt(efx);
|
||||
|
||||
/* Schedule processing of any interrupting queues */
|
||||
efx_for_each_channel(channel, efx) {
|
||||
if ((queues & 1) ||
|
||||
efx_event_present(
|
||||
efx_event(channel, channel->eventq_read_ptr))) {
|
||||
if (queues != 0) {
|
||||
if (EFX_WORKAROUND_15783(efx))
|
||||
efx->irq_zero_count = 0;
|
||||
|
||||
/* Schedule processing of any interrupting queues */
|
||||
efx_for_each_channel(channel, efx) {
|
||||
if (queues & 1)
|
||||
efx_schedule_channel(channel);
|
||||
result = IRQ_HANDLED;
|
||||
queues >>= 1;
|
||||
}
|
||||
queues >>= 1;
|
||||
result = IRQ_HANDLED;
|
||||
|
||||
} else if (EFX_WORKAROUND_15783(efx) &&
|
||||
efx->irq_zero_count++ == 0) {
|
||||
efx_qword_t *event;
|
||||
|
||||
/* Ensure we rearm all event queues */
|
||||
efx_for_each_channel(channel, efx) {
|
||||
event = efx_event(channel, channel->eventq_read_ptr);
|
||||
if (efx_event_present(event))
|
||||
efx_schedule_channel(channel);
|
||||
}
|
||||
|
||||
result = IRQ_HANDLED;
|
||||
}
|
||||
|
||||
if (result == IRQ_HANDLED) {
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
#include <linux/i2c-algo-bit.h>
|
||||
#include "net_driver.h"
|
||||
#include "efx.h"
|
||||
#include "mcdi.h"
|
||||
|
||||
/*
|
||||
* Falcon hardware control
|
||||
|
@ -23,6 +24,7 @@ enum {
|
|||
EFX_REV_FALCON_A0 = 0,
|
||||
EFX_REV_FALCON_A1 = 1,
|
||||
EFX_REV_FALCON_B0 = 2,
|
||||
EFX_REV_SIENA_A0 = 3,
|
||||
};
|
||||
|
||||
static inline int efx_nic_rev(struct efx_nic *efx)
|
||||
|
@ -32,6 +34,10 @@ static inline int efx_nic_rev(struct efx_nic *efx)
|
|||
|
||||
extern u32 efx_nic_fpga_ver(struct efx_nic *efx);
|
||||
|
||||
static inline bool efx_nic_has_mc(struct efx_nic *efx)
|
||||
{
|
||||
return efx_nic_rev(efx) >= EFX_REV_SIENA_A0;
|
||||
}
|
||||
/* NIC has two interlinked PCI functions for the same port. */
|
||||
static inline bool efx_nic_is_dual_func(struct efx_nic *efx)
|
||||
{
|
||||
|
@ -123,8 +129,25 @@ static inline struct falcon_board *falcon_board(struct efx_nic *efx)
|
|||
return &data->board;
|
||||
}
|
||||
|
||||
/**
|
||||
* struct siena_nic_data - Siena NIC state
|
||||
* @fw_version: Management controller firmware version
|
||||
* @fw_build: Firmware build number
|
||||
* @mcdi: Management-Controller-to-Driver Interface
|
||||
* @wol_filter_id: Wake-on-LAN packet filter id
|
||||
*/
|
||||
struct siena_nic_data {
|
||||
u64 fw_version;
|
||||
u32 fw_build;
|
||||
struct efx_mcdi_iface mcdi;
|
||||
int wol_filter_id;
|
||||
};
|
||||
|
||||
extern void siena_print_fwver(struct efx_nic *efx, char *buf, size_t len);
|
||||
|
||||
extern struct efx_nic_type falcon_a1_nic_type;
|
||||
extern struct efx_nic_type falcon_b0_nic_type;
|
||||
extern struct efx_nic_type siena_a0_nic_type;
|
||||
|
||||
/**************************************************************************
|
||||
*
|
||||
|
|
|
@ -41,4 +41,21 @@ extern struct efx_phy_operations falcon_qt202x_phy_ops;
|
|||
|
||||
extern void falcon_qt202x_set_led(struct efx_nic *p, int led, int state);
|
||||
|
||||
/****************************************************************************
|
||||
* Siena managed PHYs
|
||||
*/
|
||||
extern struct efx_phy_operations efx_mcdi_phy_ops;
|
||||
|
||||
extern int efx_mcdi_mdio_read(struct efx_nic *efx, unsigned int bus,
|
||||
unsigned int prtad, unsigned int devad,
|
||||
u16 addr, u16 *value_out, u32 *status_out);
|
||||
extern int efx_mcdi_mdio_write(struct efx_nic *efx, unsigned int bus,
|
||||
unsigned int prtad, unsigned int devad,
|
||||
u16 addr, u16 value, u32 *status_out);
|
||||
extern void efx_mcdi_phy_decode_link(struct efx_nic *efx,
|
||||
struct efx_link_state *link_state,
|
||||
u32 speed, u32 flags, u32 fcntl);
|
||||
extern int efx_mcdi_phy_reconfigure(struct efx_nic *efx);
|
||||
extern void efx_mcdi_phy_check_fcntl(struct efx_nic *efx, u32 lpa);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
#define EFX_WORKAROUND_ALWAYS(efx) 1
|
||||
#define EFX_WORKAROUND_FALCON_A(efx) (efx_nic_rev(efx) <= EFX_REV_FALCON_A1)
|
||||
#define EFX_WORKAROUND_FALCON_AB(efx) (efx_nic_rev(efx) <= EFX_REV_FALCON_B0)
|
||||
#define EFX_WORKAROUND_SIENA(efx) (efx_nic_rev(efx) == EFX_REV_SIENA_A0)
|
||||
#define EFX_WORKAROUND_10G(efx) EFX_IS10G(efx)
|
||||
#define EFX_WORKAROUND_SFT9001(efx) ((efx)->phy_type == PHY_TYPE_SFT9001A || \
|
||||
(efx)->phy_type == PHY_TYPE_SFT9001B)
|
||||
|
@ -35,6 +36,10 @@
|
|||
#define EFX_WORKAROUND_11482 EFX_WORKAROUND_FALCON_AB
|
||||
/* Truncated IPv4 packets can confuse the TX packet parser */
|
||||
#define EFX_WORKAROUND_15592 EFX_WORKAROUND_FALCON_AB
|
||||
/* Legacy ISR read can return zero once */
|
||||
#define EFX_WORKAROUND_15783 EFX_WORKAROUND_SIENA
|
||||
/* Legacy interrupt storm when interrupt fifo fills */
|
||||
#define EFX_WORKAROUND_17213 EFX_WORKAROUND_SIENA
|
||||
|
||||
/* Spurious parity errors in TSORT buffers */
|
||||
#define EFX_WORKAROUND_5129 EFX_WORKAROUND_FALCON_A
|
||||
|
|
Loading…
Reference in a new issue