Commit 2200e6d9 authored by Edward Cree's avatar Edward Cree Committed by David S. Miller
Browse files

sfc_ef100: implement MCDI transport

parent 35a36af8
Loading
Loading
Loading
Loading
+106 −0
Original line number Diff line number Diff line
@@ -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;
}
+1 −0
Original line number Diff line number Diff line
@@ -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;
};