From 2200e6d92e05ed00adda1a58bc438dbfc5a7b8e2 Mon Sep 17 00:00:00 2001 From: Edward Cree Date: Mon, 27 Jul 2020 12:57:09 +0100 Subject: sfc_ef100: implement MCDI transport Signed-off-by: Edward Cree Signed-off-by: David S. Miller --- drivers/net/ethernet/sfc/ef100_nic.c | 106 +++++++++++++++++++++++++++++++++++ drivers/net/ethernet/sfc/ef100_nic.h | 1 + 2 files changed, 107 insertions(+) (limited to 'drivers/net/ethernet/sfc') diff --git a/drivers/net/ethernet/sfc/ef100_nic.c b/drivers/net/ethernet/sfc/ef100_nic.c index 802ab0ce00fe..189a8a706c0e 100644 --- a/drivers/net/ethernet/sfc/ef100_nic.c +++ b/drivers/net/ethernet/sfc/ef100_nic.c @@ -25,11 +25,23 @@ #include "ef100_netdev.h" #define EF100_MAX_VIS 4096 +#define EF100_NUM_MCDI_BUFFERS 1 +#define MCDI_BUF_LEN (8 + MCDI_CTL_SDU_LEN_MAX) #define EF100_RESET_PORT ((ETH_RESET_MAC | ETH_RESET_PHY) << ETH_RESET_SHARED_SHIFT) /* MCDI */ +static u8 *ef100_mcdi_buf(struct efx_nic *efx, u8 bufid, dma_addr_t *dma_addr) +{ + struct ef100_nic_data *nic_data = efx->nic_data; + + if (dma_addr) + *dma_addr = nic_data->mcdi_buf.dma_addr + + bufid * ALIGN(MCDI_BUF_LEN, 256); + return nic_data->mcdi_buf.addr + bufid * ALIGN(MCDI_BUF_LEN, 256); +} + static int ef100_get_warm_boot_count(struct efx_nic *efx) { efx_dword_t reg; @@ -46,6 +58,72 @@ static int ef100_get_warm_boot_count(struct efx_nic *efx) } } +static void ef100_mcdi_request(struct efx_nic *efx, + const efx_dword_t *hdr, size_t hdr_len, + const efx_dword_t *sdu, size_t sdu_len) +{ + dma_addr_t dma_addr; + u8 *pdu = ef100_mcdi_buf(efx, 0, &dma_addr); + + memcpy(pdu, hdr, hdr_len); + memcpy(pdu + hdr_len, sdu, sdu_len); + wmb(); + + /* The hardware provides 'low' and 'high' (doorbell) registers + * for passing the 64-bit address of an MCDI request to + * firmware. However the dwords are swapped by firmware. The + * least significant bits of the doorbell are then 0 for all + * MCDI requests due to alignment. + */ + _efx_writed(efx, cpu_to_le32((u64)dma_addr >> 32), efx_reg(efx, ER_GZ_MC_DB_LWRD)); + _efx_writed(efx, cpu_to_le32((u32)dma_addr), efx_reg(efx, ER_GZ_MC_DB_HWRD)); +} + +static bool ef100_mcdi_poll_response(struct efx_nic *efx) +{ + const efx_dword_t hdr = + *(const efx_dword_t *)(ef100_mcdi_buf(efx, 0, NULL)); + + rmb(); + return EFX_DWORD_FIELD(hdr, MCDI_HEADER_RESPONSE); +} + +static void ef100_mcdi_read_response(struct efx_nic *efx, + efx_dword_t *outbuf, size_t offset, + size_t outlen) +{ + const u8 *pdu = ef100_mcdi_buf(efx, 0, NULL); + + memcpy(outbuf, pdu + offset, outlen); +} + +static int ef100_mcdi_poll_reboot(struct efx_nic *efx) +{ + struct ef100_nic_data *nic_data = efx->nic_data; + int rc; + + rc = ef100_get_warm_boot_count(efx); + if (rc < 0) { + /* The firmware is presumably in the process of + * rebooting. However, we are supposed to report each + * reboot just once, so we must only do that once we + * can read and store the updated warm boot count. + */ + return 0; + } + + if (rc == nic_data->warm_boot_count) + return 0; + + nic_data->warm_boot_count = rc; + + return -EIO; +} + +static void ef100_mcdi_reboot_detected(struct efx_nic *efx) +{ +} + /* Event handling */ static int ef100_ev_probe(struct efx_channel *channel) @@ -139,6 +217,11 @@ const struct efx_nic_type ef100_pf_nic_type = { .is_vf = false, .probe = ef100_probe_pf, .mcdi_max_ver = 2, + .mcdi_request = ef100_mcdi_request, + .mcdi_poll_response = ef100_mcdi_poll_response, + .mcdi_read_response = ef100_mcdi_read_response, + .mcdi_poll_reboot = ef100_mcdi_poll_reboot, + .mcdi_reboot_detected = ef100_mcdi_reboot_detected, .irq_enable_master = efx_port_dummy_op_void, .irq_disable_non_ev = efx_port_dummy_op_void, .push_irq_moderation = efx_channel_dummy_op_void, @@ -178,6 +261,15 @@ static int ef100_probe_main(struct efx_nic *efx) net_dev->features |= efx->type->offload_features; net_dev->hw_features |= efx->type->offload_features; + /* we assume later that we can copy from this buffer in dwords */ + BUILD_BUG_ON(MCDI_CTL_SDU_LEN_MAX_V2 % 4); + + /* MCDI buffers must be 256 byte aligned. */ + rc = efx_nic_alloc_buffer(efx, &nic_data->mcdi_buf, MCDI_BUF_LEN, + GFP_KERNEL); + if (rc) + goto fail; + /* Get the MC's warm boot count. In case it's rebooting right * now, be prepared to retry. */ @@ -201,6 +293,16 @@ static int ef100_probe_main(struct efx_nic *efx) /* Post-IO section. */ + rc = efx_mcdi_init(efx); + if (!rc && efx->mcdi->fn_flags & + (1 << MC_CMD_DRV_ATTACH_EXT_OUT_FLAG_NO_ACTIVE_PORT)) { + netif_info(efx, probe, efx->net_dev, + "No network port on this PCI function"); + rc = -ENODEV; + } + if (rc) + goto fail; + efx->max_vis = EF100_MAX_VIS; rc = ef100_phy_probe(efx); @@ -233,6 +335,10 @@ void ef100_remove(struct efx_nic *efx) efx_fini_channels(efx); kfree(efx->phy_data); efx->phy_data = NULL; + efx_mcdi_detach(efx); + efx_mcdi_fini(efx); + if (nic_data) + efx_nic_free_buffer(efx, &nic_data->mcdi_buf); kfree(nic_data); efx->nic_data = NULL; } diff --git a/drivers/net/ethernet/sfc/ef100_nic.h b/drivers/net/ethernet/sfc/ef100_nic.h index 643111aebba5..a4290d183879 100644 --- a/drivers/net/ethernet/sfc/ef100_nic.h +++ b/drivers/net/ethernet/sfc/ef100_nic.h @@ -19,6 +19,7 @@ void ef100_remove(struct efx_nic *efx); struct ef100_nic_data { struct efx_nic *efx; + struct efx_buffer mcdi_buf; u16 warm_boot_count; }; -- cgit v1.2.3