diff options
author | Ben Hutchings <bhutchings@solarflare.com> | 2009-11-29 18:15:41 +0300 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2009-11-30 04:23:57 +0300 |
commit | 8880f4ec21e668dcab3c6d387524a887e5bcbf73 (patch) | |
tree | d2f34eec2fba31f3b3141c2e580846e92c4e554c /drivers/net | |
parent | afd4aea03f597f29421dc5767e7d1f754730ec23 (diff) | |
download | linux-8880f4ec21e668dcab3c6d387524a887e5bcbf73.tar.xz |
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>
Diffstat (limited to 'drivers/net')
-rw-r--r-- | drivers/net/sfc/Kconfig | 13 | ||||
-rw-r--r-- | drivers/net/sfc/Makefile | 7 | ||||
-rw-r--r-- | drivers/net/sfc/bitfield.h | 2 | ||||
-rw-r--r-- | drivers/net/sfc/efx.c | 26 | ||||
-rw-r--r-- | drivers/net/sfc/efx.h | 2 | ||||
-rw-r--r-- | drivers/net/sfc/enum.h | 2 | ||||
-rw-r--r-- | drivers/net/sfc/ethtool.c | 3 | ||||
-rw-r--r-- | drivers/net/sfc/mac.h | 3 | ||||
-rw-r--r-- | drivers/net/sfc/mtd.c | 267 | ||||
-rw-r--r-- | drivers/net/sfc/net_driver.h | 10 | ||||
-rw-r--r-- | drivers/net/sfc/nic.c | 63 | ||||
-rw-r--r-- | drivers/net/sfc/nic.h | 23 | ||||
-rw-r--r-- | drivers/net/sfc/phy.h | 17 | ||||
-rw-r--r-- | drivers/net/sfc/workarounds.h | 5 |
14 files changed, 414 insertions, 29 deletions
diff --git a/drivers/net/sfc/Kconfig b/drivers/net/sfc/Kconfig index 260aafaac235..a65c98638398 100644 --- a/drivers/net/sfc/Kconfig +++ b/drivers/net/sfc/Kconfig @@ -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. diff --git a/drivers/net/sfc/Makefile b/drivers/net/sfc/Makefile index 223106b93447..1047b19c60a5 100644 --- a/drivers/net/sfc/Makefile +++ b/drivers/net/sfc/Makefile @@ -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 diff --git a/drivers/net/sfc/bitfield.h b/drivers/net/sfc/bitfield.h index 6ad909bba957..bb5de4fe9257 100644 --- a/drivers/net/sfc/bitfield.h +++ b/drivers/net/sfc/bitfield.h @@ -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 diff --git a/drivers/net/sfc/efx.c b/drivers/net/sfc/efx.c index 97a6ebdcaf2b..4b5c786f0e2c 100644 --- a/drivers/net/sfc/efx.c +++ b/drivers/net/sfc/efx.c @@ -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 */ }; diff --git a/drivers/net/sfc/efx.h b/drivers/net/sfc/efx.h index fa40c7b66d7b..b4470da23860 100644 --- a/drivers/net/sfc/efx.h +++ b/drivers/net/sfc/efx.h @@ -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 diff --git a/drivers/net/sfc/enum.h b/drivers/net/sfc/enum.h index 7a9e79ab9313..b1f7a40ab15f 100644 --- a/drivers/net/sfc/enum.h +++ b/drivers/net/sfc/enum.h @@ -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, }; diff --git a/drivers/net/sfc/ethtool.c b/drivers/net/sfc/ethtool.c index 012ee31db0c2..5492b6336602 100644 --- a/drivers/net/sfc/ethtool.c +++ b/drivers/net/sfc/ethtool.c @@ -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)); } diff --git a/drivers/net/sfc/mac.h b/drivers/net/sfc/mac.h index d2af50f1747b..c733863fa41e 100644 --- a/drivers/net/sfc/mac.h +++ b/drivers/net/sfc/mac.h @@ -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 diff --git a/drivers/net/sfc/mtd.c b/drivers/net/sfc/mtd.c index 65a22f193f92..ef561f8af4d1 100644 --- a/drivers/net/sfc/mtd.c +++ b/drivers/net/sfc/mtd.c @@ -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; +} + diff --git a/drivers/net/sfc/net_driver.h b/drivers/net/sfc/net_driver.h index 96d3f00df645..ec132038b26d 100644 --- a/drivers/net/sfc/net_driver.h +++ b/drivers/net/sfc/net_driver.h @@ -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 diff --git a/drivers/net/sfc/nic.c b/drivers/net/sfc/nic.c index 55dbd7994b64..5ac4b1af8391 100644 --- a/drivers/net/sfc/nic.c +++ b/drivers/net/sfc/nic.c @@ -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; + + 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_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); + 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; } - queues >>= 1; + + result = IRQ_HANDLED; } if (result == IRQ_HANDLED) { diff --git a/drivers/net/sfc/nic.h b/drivers/net/sfc/nic.h index e7eb30488c15..57c510d8c34d 100644 --- a/drivers/net/sfc/nic.h +++ b/drivers/net/sfc/nic.h @@ -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; /************************************************************************** * diff --git a/drivers/net/sfc/phy.h b/drivers/net/sfc/phy.h index 2ad1cec2c720..64dff2d59522 100644 --- a/drivers/net/sfc/phy.h +++ b/drivers/net/sfc/phy.h @@ -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 diff --git a/drivers/net/sfc/workarounds.h b/drivers/net/sfc/workarounds.h index 021d0d2d97f3..ecee8f57d7f3 100644 --- a/drivers/net/sfc/workarounds.h +++ b/drivers/net/sfc/workarounds.h @@ -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 |