sfc_ef100: implement MCDI transport
authorEdward Cree <ecree@solarflare.com>
Mon, 27 Jul 2020 11:57:09 +0000 (12:57 +0100)
committerDavid S. Miller <davem@davemloft.net>
Mon, 27 Jul 2020 19:26:55 +0000 (12:26 -0700)
Signed-off-by: Edward Cree <ecree@solarflare.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/ethernet/sfc/ef100_nic.c
drivers/net/ethernet/sfc/ef100_nic.h

index 802ab0ce00fe0de1b380ba4c0de74a5df132d7ce..189a8a706c0e8c60dd9fef29236fa66309b4b38d 100644 (file)
 #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;
 }
index 643111aebba5ec794b40a9619f387a830cdc0d8e..a4290d18387921f7e248cf1605ee2de5017adff6 100644 (file)
@@ -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;
 };