Commit f3ad5003 authored by Ben Hutchings's avatar Ben Hutchings
Browse files

sfc: Make MCDI independent of Siena



Move the lowest layer (transport) of the current MCDI code to
per-NIC-type operations.

Introduce a new structure and efx_nic member for MCDI-specific data.

Signed-off-by: default avatarBen Hutchings <bhutchings@solarflare.com>
parent f073dde0
Loading
Loading
Loading
Loading
+26 −61
Original line number Diff line number Diff line
@@ -24,13 +24,6 @@

#define MCDI_RPC_TIMEOUT       (10 * HZ)

#define MCDI_PDU(efx)							\
	(efx_port_num(efx) ? MC_SMEM_P1_PDU_OFST : MC_SMEM_P0_PDU_OFST)
#define MCDI_DOORBELL(efx)						\
	(efx_port_num(efx) ? MC_SMEM_P1_DOORBELL_OFST : MC_SMEM_P0_DOORBELL_OFST)
#define MCDI_STATUS(efx)						\
	(efx_port_num(efx) ? MC_SMEM_P1_STATUS_OFST : MC_SMEM_P0_STATUS_OFST)

/* A reboot/assertion causes the MCDI status word to be set after the
 * command word is set or a REBOOT event is sent. If we notice a reboot
 * via these mechanisms then wait 10ms for the status word to be set. */
@@ -44,16 +37,18 @@

static inline struct efx_mcdi_iface *efx_mcdi(struct efx_nic *efx)
{
	struct siena_nic_data *nic_data;
	EFX_BUG_ON_PARANOID(efx_nic_rev(efx) < EFX_REV_SIENA_A0);
	nic_data = efx->nic_data;
	return &nic_data->mcdi;
	EFX_BUG_ON_PARANOID(!efx->mcdi);
	return &efx->mcdi->iface;
}

int efx_mcdi_init(struct efx_nic *efx)
{
	struct efx_mcdi_iface *mcdi;

	efx->mcdi = kzalloc(sizeof(*efx->mcdi), GFP_KERNEL);
	if (!efx->mcdi)
		return -ENOMEM;

	mcdi = efx_mcdi(efx);
	init_waitqueue_head(&mcdi->wq);
	spin_lock_init(&mcdi->iface_lock);
@@ -66,16 +61,19 @@ int efx_mcdi_init(struct efx_nic *efx)
	return efx_mcdi_handle_assertion(efx);
}

void efx_mcdi_fini(struct efx_nic *efx)
{
	BUG_ON(efx->mcdi &&
	       atomic_read(&efx->mcdi->iface.state) != MCDI_STATE_QUIESCENT);
	kfree(efx->mcdi);
}

static void efx_mcdi_copyin(struct efx_nic *efx, unsigned cmd,
			    const efx_dword_t *inbuf, size_t inlen)
{
	struct efx_mcdi_iface *mcdi = efx_mcdi(efx);
	unsigned pdu = FR_CZ_MC_TREG_SMEM + MCDI_PDU(efx);
	unsigned doorbell = FR_CZ_MC_TREG_SMEM + MCDI_DOORBELL(efx);
	unsigned int i;
	efx_dword_t hdr;
	u32 xflags, seqno;
	unsigned int inlen_dw = DIV_ROUND_UP(inlen, 4);

	BUG_ON(atomic_read(&mcdi->state) == MCDI_STATE_QUIESCENT);
	BUG_ON(inlen > MCDI_CTL_SDU_LEN_MAX_V1);
@@ -93,31 +91,18 @@ static void efx_mcdi_copyin(struct efx_nic *efx, unsigned cmd,
			     MCDI_HEADER_SEQ, seqno,
			     MCDI_HEADER_XFLAGS, xflags);

	efx_writed(efx, &hdr, pdu);

	for (i = 0; i < inlen_dw; i++)
		efx_writed(efx, &inbuf[i], pdu + 4 + 4 * i);

	/* Ensure the payload is written out before the header */
	wmb();

	/* ring the doorbell with a distinctive value */
	_efx_writed(efx, (__force __le32) 0x45789abc, doorbell);
	efx->type->mcdi_request(efx, &hdr, 4, inbuf, inlen);
}

static void
efx_mcdi_copyout(struct efx_nic *efx, efx_dword_t *outbuf, size_t outlen)
{
	struct efx_mcdi_iface *mcdi = efx_mcdi(efx);
	unsigned int pdu = FR_CZ_MC_TREG_SMEM + MCDI_PDU(efx);
	unsigned int outlen_dw = DIV_ROUND_UP(outlen, 4);
	int i;

	BUG_ON(atomic_read(&mcdi->state) == MCDI_STATE_QUIESCENT);
	BUG_ON(outlen > MCDI_CTL_SDU_LEN_MAX_V1);

	for (i = 0; i < outlen_dw; i++)
		efx_readd(efx, &outbuf[i], pdu + 4 + 4 * i);
	efx->type->mcdi_read_response(efx, outbuf, 4, outlen);
}

static int efx_mcdi_poll(struct efx_nic *efx)
@@ -125,7 +110,6 @@ static int efx_mcdi_poll(struct efx_nic *efx)
	struct efx_mcdi_iface *mcdi = efx_mcdi(efx);
	unsigned long time, finish;
	unsigned int respseq, respcmd, error;
	unsigned int pdu = FR_CZ_MC_TREG_SMEM + MCDI_PDU(efx);
	unsigned int rc, spins;
	efx_dword_t reg;

@@ -152,19 +136,14 @@ static int efx_mcdi_poll(struct efx_nic *efx)
		time = jiffies;

		rmb();
		efx_readd(efx, &reg, pdu);

		/* All 1's indicates that shared memory is in reset (and is
		 * not a valid header). Wait for it to come out reset before
		 * completing the command */
		if (EFX_DWORD_FIELD(reg, EFX_DWORD_0) != 0xffffffff &&
		    EFX_DWORD_FIELD(reg, MCDI_HEADER_RESPONSE))
		if (efx->type->mcdi_poll_response(efx))
			break;

		if (time_after(time, finish))
			return -ETIMEDOUT;
	}

	efx->type->mcdi_read_response(efx, &reg, 0, 4);
	mcdi->resplen = EFX_DWORD_FIELD(reg, MCDI_HEADER_DATALEN);
	respseq = EFX_DWORD_FIELD(reg, MCDI_HEADER_SEQ);
	respcmd = EFX_DWORD_FIELD(reg, MCDI_HEADER_CODE);
@@ -179,7 +158,7 @@ static int efx_mcdi_poll(struct efx_nic *efx)
			  respseq, mcdi->seqno);
		rc = EIO;
	} else if (error) {
		efx_readd(efx, &reg, pdu + 4);
		efx->type->mcdi_read_response(efx, &reg, 4, 4);
		switch (EFX_DWORD_FIELD(reg, EFX_DWORD_0)) {
#define TRANSLATE_ERROR(name)					\
		case MC_CMD_ERR_ ## name:			\
@@ -215,17 +194,13 @@ out:
 */
int efx_mcdi_poll_reboot(struct efx_nic *efx)
{
	unsigned int addr = FR_CZ_MC_TREG_SMEM + MCDI_STATUS(efx);
	efx_dword_t reg;
	uint32_t value;

	if (efx_nic_rev(efx) < EFX_REV_SIENA_A0)
		return false;
	int rc;

	efx_readd(efx, &reg, addr);
	value = EFX_DWORD_FIELD(reg, EFX_DWORD_0);
	if (!efx->mcdi)
		return 0;

	if (value == 0)
	rc = efx->type->mcdi_poll_reboot(efx);
	if (!rc)
		return 0;

	/* MAC statistics have been cleared on the NIC; clear our copy
@@ -233,13 +208,7 @@ int efx_mcdi_poll_reboot(struct efx_nic *efx)
	 */
	memset(&efx->mac_stats, 0, sizeof(efx->mac_stats));

	EFX_ZERO_DWORD(reg);
	efx_writed(efx, &reg, addr);

	if (value == MC_STATUS_DWORD_ASSERT)
		return -EINTR;
	else
		return -EIO;
	return rc;
}

static void efx_mcdi_acquire(struct efx_mcdi_iface *mcdi)
@@ -345,8 +314,6 @@ void efx_mcdi_rpc_start(struct efx_nic *efx, unsigned cmd,
{
	struct efx_mcdi_iface *mcdi = efx_mcdi(efx);

	BUG_ON(efx_nic_rev(efx) < EFX_REV_SIENA_A0);

	efx_mcdi_acquire(mcdi);

	/* Serialise with efx_mcdi_ev_cpl() and efx_mcdi_ev_death() */
@@ -364,8 +331,6 @@ int efx_mcdi_rpc_finish(struct efx_nic *efx, unsigned cmd, size_t inlen,
	struct efx_mcdi_iface *mcdi = efx_mcdi(efx);
	int rc;

	BUG_ON(efx_nic_rev(efx) < EFX_REV_SIENA_A0);

	if (mcdi->mode == MCDI_MODE_POLL)
		rc = efx_mcdi_poll(efx);
	else
@@ -426,7 +391,7 @@ void efx_mcdi_mode_poll(struct efx_nic *efx)
{
	struct efx_mcdi_iface *mcdi;

	if (efx_nic_rev(efx) < EFX_REV_SIENA_A0)
	if (!efx->mcdi)
		return;

	mcdi = efx_mcdi(efx);
@@ -450,7 +415,7 @@ void efx_mcdi_mode_event(struct efx_nic *efx)
{
	struct efx_mcdi_iface *mcdi;

	if (efx_nic_rev(efx) < EFX_REV_SIENA_A0)
	if (!efx->mcdi)
		return;

	mcdi = efx_mcdi(efx);
+21 −0
Original line number Diff line number Diff line
@@ -65,7 +65,28 @@ struct efx_mcdi_mon {
	unsigned int n_attrs;
};

/**
 * struct efx_mcdi_data - extra state for NICs that implement MCDI
 * @iface: Interface/protocol state
 * @hwmon: Hardware monitor state
 */
struct efx_mcdi_data {
	struct efx_mcdi_iface iface;
#ifdef CONFIG_SFC_MCDI_MON
	struct efx_mcdi_mon hwmon;
#endif
};

#ifdef CONFIG_SFC_MCDI_MON
static inline struct efx_mcdi_mon *efx_mcdi_mon(struct efx_nic *efx)
{
	EFX_BUG_ON_PARANOID(!efx->mcdi);
	return &efx->mcdi->hwmon;
}
#endif

extern int efx_mcdi_init(struct efx_nic *efx);
extern void efx_mcdi_fini(struct efx_nic *efx);

extern int efx_mcdi_rpc(struct efx_nic *efx, unsigned cmd,
			const efx_dword_t *inbuf, size_t inlen,
+20 −0
Original line number Diff line number Diff line
@@ -718,6 +718,7 @@ struct vfdi_status;
 * @selftest_work: Work item for asynchronous self-test
 * @mtd_list: List of MTDs attached to the NIC
 * @nic_data: Hardware dependent state
 * @mcdi: Management-Controller-to-Driver Interface state
 * @mac_lock: MAC access lock. Protects @port_enabled, @phy_mode,
 *	efx_monitor() and efx_reconfigure_port()
 * @port_enabled: Port enabled indicator.
@@ -847,6 +848,7 @@ struct efx_nic {
#endif

	void *nic_data;
	struct efx_mcdi_data *mcdi;

	struct mutex mac_lock;
	struct work_struct mac_work;
@@ -956,6 +958,17 @@ static inline unsigned int efx_port_num(struct efx_nic *efx)
 * @test_chip: Test registers.  Should use efx_nic_test_registers(), and is
 *	expected to reset the NIC.
 * @test_nvram: Test validity of NVRAM contents
 * @mcdi_request: Send an MCDI request with the given header and SDU.
 *	The SDU length may be any value from 0 up to the protocol-
 *	defined maximum, but its buffer will be padded to a multiple
 *	of 4 bytes.
 * @mcdi_poll_response: Test whether an MCDI response is available.
 * @mcdi_read_response: Read the MCDI response PDU.  The offset will
 *	be a multiple of 4.  The length may not be, but the buffer
 *	will be padded so it is safe to round up.
 * @mcdi_poll_reboot: Test whether the MCDI has rebooted.  If so,
 *	return an appropriate error code for aborting any current
 *	request; otherwise return 0.
 * @revision: Hardware architecture revision
 * @mem_map_size: Memory BAR mapped size
 * @txd_ptr_tbl_base: TX descriptor ring base address
@@ -1004,6 +1017,13 @@ struct efx_nic_type {
	void (*resume_wol)(struct efx_nic *efx);
	int (*test_chip)(struct efx_nic *efx, struct efx_self_tests *tests);
	int (*test_nvram)(struct efx_nic *efx);
	void (*mcdi_request)(struct efx_nic *efx,
			     const efx_dword_t *hdr, size_t hdr_len,
			     const efx_dword_t *sdu, size_t sdu_len);
	bool (*mcdi_poll_response)(struct efx_nic *efx);
	void (*mcdi_read_response)(struct efx_nic *efx, efx_dword_t *pdu,
				   size_t pdu_offset, size_t pdu_len);
	int (*mcdi_poll_reboot)(struct efx_nic *efx);

	int revision;
	unsigned int mem_map_size;
+0 −16
Original line number Diff line number Diff line
@@ -140,28 +140,12 @@ static inline struct falcon_board *falcon_board(struct efx_nic *efx)

/**
 * struct siena_nic_data - Siena NIC state
 * @mcdi: Management-Controller-to-Driver Interface
 * @wol_filter_id: Wake-on-LAN packet filter id
 * @hwmon: Hardware monitor state
 */
struct siena_nic_data {
	struct efx_mcdi_iface mcdi;
	int wol_filter_id;
#ifdef CONFIG_SFC_MCDI_MON
	struct efx_mcdi_mon hwmon;
#endif
};

#ifdef CONFIG_SFC_MCDI_MON
static inline struct efx_mcdi_mon *efx_mcdi_mon(struct efx_nic *efx)
{
	struct siena_nic_data *nic_data;
	EFX_BUG_ON_PARANOID(efx_nic_rev(efx) < EFX_REV_SIENA_A0);
	nic_data = efx->nic_data;
	return &nic_data->hwmon;
}
#endif

/*
 * On the SFC9000 family each port is associated with 1 PCI physical
 * function (PF) handled by sfc and a configurable number of virtual
+90 −0
Original line number Diff line number Diff line
@@ -274,6 +274,7 @@ fail4:
fail3:
	efx_mcdi_drv_attach(efx, false, NULL);
fail2:
	efx_mcdi_fini(efx);
fail1:
	kfree(efx->nic_data);
	return rc;
@@ -367,6 +368,8 @@ static void siena_remove_nic(struct efx_nic *efx)
	/* Tear down the private nic state */
	kfree(efx->nic_data);
	efx->nic_data = NULL;

	efx_mcdi_fini(efx);
}

static int siena_try_update_nic_stats(struct efx_nic *efx)
@@ -574,6 +577,89 @@ static void siena_init_wol(struct efx_nic *efx)
	}
}

/**************************************************************************
 *
 * MCDI
 *
 **************************************************************************
 */

#define MCDI_PDU(efx)							\
	(efx_port_num(efx) ? MC_SMEM_P1_PDU_OFST : MC_SMEM_P0_PDU_OFST)
#define MCDI_DOORBELL(efx)						\
	(efx_port_num(efx) ? MC_SMEM_P1_DOORBELL_OFST : MC_SMEM_P0_DOORBELL_OFST)
#define MCDI_STATUS(efx)						\
	(efx_port_num(efx) ? MC_SMEM_P1_STATUS_OFST : MC_SMEM_P0_STATUS_OFST)

static void siena_mcdi_request(struct efx_nic *efx,
			       const efx_dword_t *hdr, size_t hdr_len,
			       const efx_dword_t *sdu, size_t sdu_len)
{
	unsigned pdu = FR_CZ_MC_TREG_SMEM + MCDI_PDU(efx);
	unsigned doorbell = FR_CZ_MC_TREG_SMEM + MCDI_DOORBELL(efx);
	unsigned int i;
	unsigned int inlen_dw = DIV_ROUND_UP(sdu_len, 4);

	EFX_BUG_ON_PARANOID(hdr_len != 4);

	efx_writed(efx, hdr, pdu);

	for (i = 0; i < inlen_dw; i++)
		efx_writed(efx, &sdu[i], pdu + hdr_len + 4 * i);

	/* Ensure the request is written out before the doorbell */
	wmb();

	/* ring the doorbell with a distinctive value */
	_efx_writed(efx, (__force __le32) 0x45789abc, doorbell);
}

static bool siena_mcdi_poll_response(struct efx_nic *efx)
{
	unsigned int pdu = FR_CZ_MC_TREG_SMEM + MCDI_PDU(efx);
	efx_dword_t hdr;

	efx_readd(efx, &hdr, pdu);

	/* All 1's indicates that shared memory is in reset (and is
	 * not a valid hdr). Wait for it to come out reset before
	 * completing the command
	 */
	return EFX_DWORD_FIELD(hdr, EFX_DWORD_0) != 0xffffffff &&
		EFX_DWORD_FIELD(hdr, MCDI_HEADER_RESPONSE);
}

static void siena_mcdi_read_response(struct efx_nic *efx, efx_dword_t *outbuf,
				     size_t offset, size_t outlen)
{
	unsigned int pdu = FR_CZ_MC_TREG_SMEM + MCDI_PDU(efx);
	unsigned int outlen_dw = DIV_ROUND_UP(outlen, 4);
	int i;

	for (i = 0; i < outlen_dw; i++)
		efx_readd(efx, &outbuf[i], pdu + offset + 4 * i);
}

static int siena_mcdi_poll_reboot(struct efx_nic *efx)
{
	unsigned int addr = FR_CZ_MC_TREG_SMEM + MCDI_STATUS(efx);
	efx_dword_t reg;
	u32 value;

	efx_readd(efx, &reg, addr);
	value = EFX_DWORD_FIELD(reg, EFX_DWORD_0);

	if (value == 0)
		return 0;

	EFX_ZERO_DWORD(reg);
	efx_writed(efx, &reg, addr);

	if (value == MC_STATUS_DWORD_ASSERT)
		return -EINTR;
	else
		return -EIO;
}

/**************************************************************************
 *
@@ -613,6 +699,10 @@ const struct efx_nic_type siena_a0_nic_type = {
	.resume_wol = siena_init_wol,
	.test_chip = siena_test_chip,
	.test_nvram = efx_mcdi_nvram_test_all,
	.mcdi_request = siena_mcdi_request,
	.mcdi_poll_response = siena_mcdi_poll_response,
	.mcdi_read_response = siena_mcdi_read_response,
	.mcdi_poll_reboot = siena_mcdi_poll_reboot,

	.revision = EFX_REV_SIENA_A0,
	.mem_map_size = (FR_CZ_MC_TREG_SMEM +