Commit 8880f4ec authored by Ben Hutchings's avatar Ben Hutchings Committed by David S. Miller

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: default avatarBen Hutchings <bhutchings@solarflare.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent afd4aea0
config SFC config SFC
tristate "Solarflare Solarstorm SFC4000 support" tristate "Solarflare Solarstorm SFC4000/SFC9000-family support"
depends on PCI && INET depends on PCI && INET
select MDIO select MDIO
select CRC32 select CRC32
...@@ -7,15 +7,16 @@ config SFC ...@@ -7,15 +7,16 @@ config SFC
select I2C_ALGOBIT select I2C_ALGOBIT
help help
This driver supports 10-gigabit Ethernet cards based on 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 To compile this driver as a module, choose M here. The module
will be called sfc. will be called sfc.
config SFC_MTD 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) depends on SFC && MTD && !(SFC=y && MTD=m)
default y default y
help help
This exposes the on-board flash memory as an MTD device (e.g. This exposes the on-board flash memory as MTD devices (e.g.
/dev/mtd1). This makes it possible to upload new boot code /dev/mtd1). This makes it possible to upload new firmware
to the NIC. to the NIC.
sfc-y += efx.o nic.o falcon.o tx.o rx.o falcon_gmac.o \ sfc-y += efx.o nic.o falcon.o siena.o tx.o rx.o \
falcon_xmac.o selftest.o ethtool.o qt202x_phy.o \ falcon_gmac.o falcon_xmac.o mcdi_mac.o \
mdio_10g.o tenxpress.o falcon_boards.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 sfc-$(CONFIG_SFC_MTD) += mtd.o
obj-$(CONFIG_SFC) += sfc.o obj-$(CONFIG_SFC) += sfc.o
...@@ -37,6 +37,8 @@ ...@@ -37,6 +37,8 @@
#define EFX_DWORD_2_WIDTH 32 #define EFX_DWORD_2_WIDTH 32
#define EFX_DWORD_3_LBN 96 #define EFX_DWORD_3_LBN 96
#define EFX_DWORD_3_WIDTH 32 #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 */ /* Specified attribute (e.g. LBN) of the specified field */
#define EFX_VAL(field, attribute) field ## _ ## attribute #define EFX_VAL(field, attribute) field ## _ ## attribute
......
...@@ -25,6 +25,8 @@ ...@@ -25,6 +25,8 @@
#include "mdio_10g.h" #include "mdio_10g.h"
#include "nic.h" #include "nic.h"
#include "mcdi.h"
/************************************************************************** /**************************************************************************
* *
* Type name strings * Type name strings
...@@ -84,6 +86,7 @@ const char *efx_reset_type_names[] = { ...@@ -84,6 +86,7 @@ const char *efx_reset_type_names[] = {
[RESET_TYPE_RX_DESC_FETCH] = "RX_DESC_FETCH", [RESET_TYPE_RX_DESC_FETCH] = "RX_DESC_FETCH",
[RESET_TYPE_TX_DESC_FETCH] = "TX_DESC_FETCH", [RESET_TYPE_TX_DESC_FETCH] = "TX_DESC_FETCH",
[RESET_TYPE_TX_SKIP] = "TX_SKIP", [RESET_TYPE_TX_SKIP] = "TX_SKIP",
[RESET_TYPE_MC_FAILURE] = "MC_FAILURE",
}; };
#define EFX_MAX_MTU (9 * 1024) #define EFX_MAX_MTU (9 * 1024)
...@@ -1191,6 +1194,15 @@ static void efx_start_all(struct efx_nic *efx) ...@@ -1191,6 +1194,15 @@ static void efx_start_all(struct efx_nic *efx)
efx_nic_enable_interrupts(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 /* 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 * event driven), we have to poll the PHY because after an event queue
* flush, we could have a missed a link state change */ * flush, we could have a missed a link state change */
...@@ -1242,6 +1254,9 @@ static void efx_stop_all(struct efx_nic *efx) ...@@ -1242,6 +1254,9 @@ static void efx_stop_all(struct efx_nic *efx)
efx->type->stop_stats(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 */ /* Disable interrupts and wait for ISR to complete */
efx_nic_disable_interrupts(efx); efx_nic_disable_interrupts(efx);
if (efx->legacy_irq) if (efx->legacy_irq)
...@@ -1445,6 +1460,8 @@ static int efx_net_open(struct net_device *net_dev) ...@@ -1445,6 +1460,8 @@ static int efx_net_open(struct net_device *net_dev)
return -EIO; return -EIO;
if (efx->phy_mode & PHY_MODE_SPECIAL) if (efx->phy_mode & PHY_MODE_SPECIAL)
return -EBUSY; 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, /* Notify the kernel of the link state polled during driver load,
* before the monitor starts running */ * before the monitor starts running */
...@@ -1895,6 +1912,7 @@ void efx_schedule_reset(struct efx_nic *efx, enum reset_type type) ...@@ -1895,6 +1912,7 @@ void efx_schedule_reset(struct efx_nic *efx, enum reset_type type)
case RESET_TYPE_TX_SKIP: case RESET_TYPE_TX_SKIP:
method = RESET_TYPE_INVISIBLE; method = RESET_TYPE_INVISIBLE;
break; break;
case RESET_TYPE_MC_FAILURE:
default: default:
method = RESET_TYPE_ALL; method = RESET_TYPE_ALL;
break; break;
...@@ -1908,6 +1926,10 @@ void efx_schedule_reset(struct efx_nic *efx, enum reset_type type) ...@@ -1908,6 +1926,10 @@ void efx_schedule_reset(struct efx_nic *efx, enum reset_type type)
efx->reset_pending = method; 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); queue_work(reset_workqueue, &efx->reset_work);
} }
...@@ -1923,6 +1945,10 @@ static struct pci_device_id efx_pci_table[] __devinitdata = { ...@@ -1923,6 +1945,10 @@ static struct pci_device_id efx_pci_table[] __devinitdata = {
.driver_data = (unsigned long) &falcon_a1_nic_type}, .driver_data = (unsigned long) &falcon_a1_nic_type},
{PCI_DEVICE(EFX_VENDID_SFC, FALCON_B_P_DEVID), {PCI_DEVICE(EFX_VENDID_SFC, FALCON_B_P_DEVID),
.driver_data = (unsigned long) &falcon_b0_nic_type}, .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 */ {0} /* end of list */
}; };
......
...@@ -18,6 +18,8 @@ ...@@ -18,6 +18,8 @@
#define FALCON_A_P_DEVID 0x0703 #define FALCON_A_P_DEVID 0x0703
#define FALCON_A_S_DEVID 0x6703 #define FALCON_A_S_DEVID 0x6703
#define FALCON_B_P_DEVID 0x0710 #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 */ /* Solarstorm controllers use BAR 0 for I/O space and BAR 2(&3) for memory */
#define EFX_MEM_BAR 2 #define EFX_MEM_BAR 2
......
...@@ -144,6 +144,7 @@ enum efx_loopback_mode { ...@@ -144,6 +144,7 @@ enum efx_loopback_mode {
* @RESET_TYPE_RX_DESC_FETCH: pcie error during rx descriptor fetch * @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_DESC_FETCH: pcie error during tx descriptor fetch
* @RESET_TYPE_TX_SKIP: hardware completed empty tx descriptors * @RESET_TYPE_TX_SKIP: hardware completed empty tx descriptors
* @RESET_TYPE_MC_FAILURE: MC reboot/assertion
*/ */
enum reset_type { enum reset_type {
RESET_TYPE_NONE = -1, RESET_TYPE_NONE = -1,
...@@ -158,6 +159,7 @@ enum reset_type { ...@@ -158,6 +159,7 @@ enum reset_type {
RESET_TYPE_RX_DESC_FETCH, RESET_TYPE_RX_DESC_FETCH,
RESET_TYPE_TX_DESC_FETCH, RESET_TYPE_TX_DESC_FETCH,
RESET_TYPE_TX_SKIP, RESET_TYPE_TX_SKIP,
RESET_TYPE_MC_FAILURE,
RESET_TYPE_MAX, RESET_TYPE_MAX,
}; };
......
...@@ -236,6 +236,9 @@ static void efx_ethtool_get_drvinfo(struct net_device *net_dev, ...@@ -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->driver, EFX_DRIVER_NAME, sizeof(info->driver));
strlcpy(info->version, EFX_DRIVER_VERSION, sizeof(info->version)); 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)); strlcpy(info->bus_info, pci_name(efx->pci_dev), sizeof(info->bus_info));
} }
......
...@@ -15,6 +15,9 @@ ...@@ -15,6 +15,9 @@
extern struct efx_mac_operations falcon_gmac_operations; extern struct efx_mac_operations falcon_gmac_operations;
extern struct efx_mac_operations falcon_xmac_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 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 #endif
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
* by the Free Software Foundation, incorporated herein by reference. * by the Free Software Foundation, incorporated herein by reference.
*/ */
#include <linux/bitops.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/mtd/mtd.h> #include <linux/mtd/mtd.h>
#include <linux/delay.h> #include <linux/delay.h>
...@@ -18,12 +19,22 @@ ...@@ -18,12 +19,22 @@
#include "spi.h" #include "spi.h"
#include "efx.h" #include "efx.h"
#include "nic.h" #include "nic.h"
#include "mcdi.h"
#include "mcdi_pcol.h"
#define EFX_SPI_VERIFY_BUF_LEN 16 #define EFX_SPI_VERIFY_BUF_LEN 16
#define EFX_MCDI_CHUNK_LEN 128
struct efx_mtd_partition { struct efx_mtd_partition {
struct mtd_info mtd; struct mtd_info mtd;
union {
struct {
bool updating;
u8 nvram_type;
u16 fw_subtype;
} mcdi;
size_t offset; size_t offset;
};
const char *type_name; const char *type_name;
char name[IFNAMSIZ + 20]; char name[IFNAMSIZ + 20];
}; };
...@@ -56,6 +67,7 @@ struct efx_mtd { ...@@ -56,6 +67,7 @@ struct efx_mtd {
container_of(mtd, struct efx_mtd_partition, mtd) container_of(mtd, struct efx_mtd_partition, mtd)
static int falcon_mtd_probe(struct efx_nic *efx); static int falcon_mtd_probe(struct efx_nic *efx);
static int siena_mtd_probe(struct efx_nic *efx);
/* SPI utilities */ /* SPI utilities */
...@@ -223,6 +235,11 @@ static void efx_mtd_rename_device(struct efx_mtd *efx_mtd) ...@@ -223,6 +235,11 @@ static void efx_mtd_rename_device(struct efx_mtd *efx_mtd)
struct efx_mtd_partition *part; struct efx_mtd_partition *part;
efx_for_each_partition(part, efx_mtd) efx_for_each_partition(part, efx_mtd)
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), snprintf(part->name, sizeof(part->name),
"%s %s", efx_mtd->efx->name, "%s %s", efx_mtd->efx->name,
part->type_name); part->type_name);
...@@ -285,6 +302,9 @@ void efx_mtd_rename(struct efx_nic *efx) ...@@ -285,6 +302,9 @@ void efx_mtd_rename(struct efx_nic *efx)
int efx_mtd_probe(struct efx_nic *efx) 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); return falcon_mtd_probe(efx);
} }
...@@ -393,3 +413,240 @@ static int falcon_mtd_probe(struct efx_nic *efx) ...@@ -393,3 +413,240 @@ static int falcon_mtd_probe(struct efx_nic *efx)
kfree(efx_mtd); kfree(efx_mtd);
return rc; 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 { ...@@ -706,6 +706,7 @@ union efx_multicast_hash {
* @phy_op: PHY interface * @phy_op: PHY interface
* @phy_data: PHY private data (including PHY-specific stats) * @phy_data: PHY private data (including PHY-specific stats)
* @mdio: PHY MDIO interface * @mdio: PHY MDIO interface
* @mdio_bus: PHY MDIO bus ID (only used by Siena)
* @phy_mode: PHY operating mode. Serialised by @mac_lock. * @phy_mode: PHY operating mode. Serialised by @mac_lock.
* @xmac_poll_required: XMAC link state needs polling * @xmac_poll_required: XMAC link state needs polling
* @link_advertising: Autonegotiation advertising flags * @link_advertising: Autonegotiation advertising flags
...@@ -756,6 +757,7 @@ struct efx_nic { ...@@ -756,6 +757,7 @@ struct efx_nic {
struct efx_buffer irq_status; struct efx_buffer irq_status;
volatile signed int last_irq_cpu; volatile signed int last_irq_cpu;
unsigned long irq_zero_count;
struct efx_spi_device *spi_flash; struct efx_spi_device *spi_flash;
struct efx_spi_device *spi_eeprom; struct efx_spi_device *spi_eeprom;
...@@ -766,7 +768,7 @@ struct efx_nic { ...@@ -766,7 +768,7 @@ struct efx_nic {
unsigned n_rx_nodesc_drop_cnt; unsigned n_rx_nodesc_drop_cnt;
struct falcon_nic_data *nic_data; void *nic_data;
struct mutex mac_lock; struct mutex mac_lock;
struct work_struct mac_work; struct work_struct mac_work;
...@@ -792,6 +794,7 @@ struct efx_nic { ...@@ -792,6 +794,7 @@ struct efx_nic {
struct efx_phy_operations *phy_op; struct efx_phy_operations *phy_op;
void *phy_data; void *phy_data;
struct mdio_if_info mdio; struct mdio_if_info mdio;
unsigned int mdio_bus;
enum efx_phy_mode phy_mode; enum efx_phy_mode phy_mode;
bool xmac_poll_required; bool xmac_poll_required;
...@@ -824,6 +827,11 @@ static inline const char *efx_dev_name(struct efx_nic *efx) ...@@ -824,6 +827,11 @@ static inline const char *efx_dev_name(struct efx_nic *efx)
return efx_dev_registered(efx) ? efx->name : ""; 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 * struct efx_nic_type - Efx device type definition
* @probe: Probe the controller * @probe: Probe the controller
......
...@@ -997,6 +997,9 @@ int efx_nic_process_eventq(struct efx_channel *channel, int rx_quota) ...@@ -997,6 +997,9 @@ int efx_nic_process_eventq(struct efx_channel *channel, int rx_quota)
case FSE_AZ_EV_CODE_DRIVER_EV: case FSE_AZ_EV_CODE_DRIVER_EV:
efx_handle_driver_event(channel, &event); efx_handle_driver_event(channel, &event);
break; break;
case FSE_CZ_EV_CODE_MCDI_EV:
efx_mcdi_process_event(channel, &event);
break;
default: default:
EFX_ERR(channel->efx, "channel %d unknown event type %d" EFX_ERR(channel->efx, "channel %d unknown event type %d"
" (data " EFX_QWORD_FMT ")\n", channel->channel, " (data " EFX_QWORD_FMT ")\n", channel->channel,
...@@ -1025,13 +1028,21 @@ int efx_nic_probe_eventq(struct efx_channel *channel) ...@@ -1025,13 +1028,21 @@ int efx_nic_probe_eventq(struct efx_channel *channel)
void efx_nic_init_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; struct efx_nic *efx = channel->efx;
EFX_LOG(efx, "channel %d event queue in special buffers %d-%d\n", EFX_LOG(efx, "channel %d event queue in special buffers %d-%d\n",
channel->channel, channel->eventq.index, channel->channel, channel->eventq.index,
channel->eventq.index + channel->eventq.entries - 1); 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, &reg, FR_BZ_TIMER_TBL, channel->channel);
}
/* Pin event queue buffer */ /* Pin event queue buffer */
efx_init_special_buffer(efx, &channel->eventq); efx_init_special_buffer(efx, &channel->eventq);
...@@ -1039,11 +1050,11 @@ void efx_nic_init_eventq(struct efx_channel *channel) ...@@ -1039,11 +1050,11 @@ void efx_nic_init_eventq(struct efx_channel *channel)
memset(channel->eventq.addr, 0xff, channel->eventq.len); memset(channel->eventq.addr, 0xff, channel->eventq.len);
/* Push event queue to card */ /* Push event queue to card */
EFX_POPULATE_OWORD_3(evq_ptr, EFX_POPULATE_OWORD_3(reg,
FRF_AZ_EVQ_EN, 1, FRF_AZ_EVQ_EN, 1,
FRF_AZ_EVQ_SIZE, __ffs(channel->eventq.entries), FRF_AZ_EVQ_SIZE, __ffs(channel->eventq.entries),
FRF_AZ_EVQ_BUF_BASE_ID, channel->eventq.index); 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, &reg, efx->type->evq_ptr_tbl_base,
channel->channel); channel->channel);
efx->type->push_irq_moderation(channel); efx->type->push_irq_moderation(channel);
...@@ -1051,13 +1062,15 @@ void efx_nic_init_eventq(struct efx_channel *channel) ...@@ -1051,13 +1062,15 @@ void efx_nic_init_eventq(struct efx_channel *channel)
void efx_nic_fini_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; struct efx_nic *efx = channel->efx;
/* Remove event queue from card */ /* Remove event queue from card */
EFX_ZERO_OWORD(eventq_ptr); EFX_ZERO_OWORD(reg);
efx_writeo_table(efx, &eventq_ptr, efx->type->evq_ptr_tbl_base, efx_writeo_table(efx, &reg, efx->type->evq_ptr_tbl_base,
channel->channel); channel->channel);
if (efx_nic_rev(efx) >= EFX_REV_SIENA_A0)
efx_writeo_table(efx, &reg, FR_BZ_TIMER_TBL, channel->channel);
/* Unpin event queue */ /* Unpin event queue */
efx_fini_special_buffer(efx, &channel->eventq); efx_fini_special_buffer(efx, &channel->eventq);
...@@ -1220,8 +1233,15 @@ static inline void efx_nic_interrupts(struct efx_nic *efx, ...@@ -1220,8 +1233,15 @@ static inline void efx_nic_interrupts(struct efx_nic *efx,
bool enabled, bool force) bool enabled, bool force)
{ {
efx_oword_t int_en_reg_ker; efx_oword_t int_en_reg_ker;
unsigned int level = 0;
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_2(int_en_reg_ker, EFX_POPULATE_OWORD_3(int_en_reg_ker,
FRF_AZ_KER_INT_LEVE_SEL, level,
FRF_AZ_KER_INT_KER, force, FRF_AZ_KER_INT_KER, force,
FRF_AZ_DRV_INT_EN_KER, enabled); FRF_AZ_DRV_INT_EN_KER, enabled);
efx_writeo(efx, &int_en_reg_ker, FR_AZ_INT_EN_KER); 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) ...@@ -1334,15 +1354,30 @@ static irqreturn_t efx_legacy_interrupt(int irq, void *dev_id)
if (unlikely(syserr)) if (unlikely(syserr))
return efx_nic_fatal_interrupt(efx); return efx_nic_fatal_interrupt(efx);
if (queues != 0) {
if (EFX_WORKAROUND_15783(efx))
efx->irq_zero_count = 0;
/* Schedule processing of any interrupting queues */ /* Schedule processing of any interrupting queues */
efx_for_each_channel(channel, efx) { efx_for_each_channel(channel, efx) {
if ((queues & 1) || if (queues & 1)
efx_event_present(
efx_event(channel, channel->eventq_read_ptr))) {
efx_schedule_channel(channel); efx_schedule_channel(channel);
queues >>= 1;
}
result = IRQ_HANDLED; 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);
} }
queues >>= 1;
result = IRQ_HANDLED;
} }
if (result == IRQ_HANDLED) { if (result == IRQ_HANDLED) {
......
...@@ -14,6 +14,7 @@ ...@@ -14,6 +14,7 @@
#include <linux/i2c-algo-bit.h> #include <linux/i2c-algo-bit.h>
#include "net_driver.h" #include "net_driver.h"
#include "efx.h" #include "efx.h"
#include "mcdi.h"
/* /*
* Falcon hardware control * Falcon hardware control
...@@ -23,6 +24,7 @@ enum { ...@@ -23,6 +24,7 @@ enum {
EFX_REV_FALCON_A0 = 0, EFX_REV_FALCON_A0 = 0,
EFX_REV_FALCON_A1 = 1, EFX_REV_FALCON_A1 = 1,
EFX_REV_FALCON_B0 = 2, EFX_REV_FALCON_B0 = 2,
EFX_REV_SIENA_A0 = 3,
}; };
static inline int efx_nic_rev(struct efx_nic *efx) static inline int efx_nic_rev(struct efx_nic *efx)
...@@ -32,6 +34,10 @@ 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); 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. */ /* NIC has two interlinked PCI functions for the same port. */
static inline bool efx_nic_is_dual_func(struct efx_nic *efx) 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) ...@@ -123,8 +129,25 @@ static inline struct falcon_board *falcon_board(struct efx_nic *efx)
return &data->board; 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_a1_nic_type;
extern struct efx_nic_type falcon_b0_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; ...@@ -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); 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 #endif
...@@ -18,6 +18,7 @@ ...@@ -18,6 +18,7 @@
#define EFX_WORKAROUND_ALWAYS(efx) 1 #define EFX_WORKAROUND_ALWAYS(efx) 1
#define EFX_WORKAROUND_FALCON_A(efx) (efx_nic_rev(efx) <= EFX_REV_FALCON_A1) #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_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_10G(efx) EFX_IS10G(efx)
#define EFX_WORKAROUND_SFT9001(efx) ((efx)->phy_type == PHY_TYPE_SFT9001A || \ #define EFX_WORKAROUND_SFT9001(efx) ((efx)->phy_type == PHY_TYPE_SFT9001A || \
(efx)->phy_type == PHY_TYPE_SFT9001B) (efx)->phy_type == PHY_TYPE_SFT9001B)
...@@ -35,6 +36,10 @@ ...@@ -35,6 +36,10 @@
#define EFX_WORKAROUND_11482 EFX_WORKAROUND_FALCON_AB #define EFX_WORKAROUND_11482 EFX_WORKAROUND_FALCON_AB
/* Truncated IPv4 packets can confuse the TX packet parser */ /* Truncated IPv4 packets can confuse the TX packet parser */
#define EFX_WORKAROUND_15592 EFX_WORKAROUND_FALCON_AB #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 */ /* Spurious parity errors in TSORT buffers */
#define EFX_WORKAROUND_5129 EFX_WORKAROUND_FALCON_A #define EFX_WORKAROUND_5129 EFX_WORKAROUND_FALCON_A
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment