FreeBSD/sys/dev/cxgbe/common/t4_hw.c
/**
 *	t4_wr_mbox_meat_timeout - send a command to FW through the given mailbox
 *	@adap: the adapter
 *	@mbox: index of the mailbox to use
 *	@cmd: the command to write
 *	@size: command length in bytes
 *	@rpl: where to optionally store the reply
 *	@sleep_ok: if true we may sleep while awaiting command completion
 *	@timeout: time to wait for command to finish before timing out
 *		(negative implies @sleep_ok=false)
 *
 *	Sends the given command to FW through the selected mailbox and waits
 *	for the FW to execute the command.  If @rpl is not %NULL it is used to
 *	store the FW's reply to the command.  The command and its optional
 *	reply are of the same length.  Some FW commands like RESET and
 *	INITIALIZE can take a considerable amount of time to execute.
 *	@sleep_ok determines whether we may sleep while awaiting the response.
 *	If sleeping is allowed we use progressive backoff otherwise we spin.
 *	Note that passing in a negative @timeout is an alternate mechanism
 *	for specifying @sleep_ok=false.  This is useful when a higher level
 *	interface allows for specification of @timeout but not @sleep_ok ...
 *
 *	The return value is 0 on success or a negative errno on failure.  A
 *	failure can happen either because we are not able to execute the
 *	command or FW executes it but signals an error.  In the latter case
 *	the return value is the error code indicated by FW (negated).
 */


Sends the given command to FW through the selected mailbox and waits
for the FW to execute the command.  If @rpl is not %NULL it is used to
store the FW's reply to the command.  The command and its optional
reply are of the same length.  Some FW commands like RESET and
INITIALIZE can take a considerable amount of time to execute.
@sleep_ok determines whether we may sleep while awaiting the response.
If sleeping is allowed we use progressive backoff otherwise we spin.
Note that passing in a negative @timeout is an alternate mechanism
for specifying @sleep_ok=false.  This is useful when a higher level
interface allows for specification of @timeout but not @sleep_ok

The return value is 0 on success or a negative errno on failure.  A
failure can happen either because we are not able to execute the
command or FW executes it but signals an error.  In the latter case
the return value is the error code indicated by FW (negated).
Sends the given command to FW through the selected mailbox and waits for the FW to execute the command . If @rpl is not %NULL it is used to store the FW ' s reply to the command . The command and its optional reply are of the same length . Some FW commands like RESET and INITIALIZE can take a considerable amount of time to execute . @sleep_ok determines whether we may sleep while awaiting the response . If sleeping is allowed we use progressive backoff otherwise we spin . Note that passing in a negative @timeout is an alternate mechanism for specifying @sleep_ok = false . This is useful when a higher level interface allows for specification of @timeout but not @sleep_ok The return value is 0 on success or a negative errno on failure . A failure can happen either because we are not able to execute the command or FW executes it but signals an error . In the latter case the return value is the error code indicated by FW ( negated ) .
int t4_wr_mbox_meat_timeout(struct adapter *adap, int mbox, const void *cmd,
			    int size, void *rpl, bool sleep_ok, int timeout)
{
	static const int delay[] = {
		1, 1, 3, 5, 10, 10, 20, 50, 100
	};
	u32 v;
	u64 res;
	int i, ms, delay_idx, ret;
	const __be64 *p = cmd;
	u32 data_reg = PF_REG(mbox, A_CIM_PF_MAILBOX_DATA);
	u32 ctl_reg = PF_REG(mbox, A_CIM_PF_MAILBOX_CTRL);
	u32 ctl;
	__be64 cmd_rpl[MBOX_LEN/8];
	u32 pcie_fw;
	if ((size & 15) || size > MBOX_LEN)
		return -EINVAL;
	if (timeout < 0) {
		sleep_ok = false;
		timeout = -timeout;
	}
	for (i = 0; i < 4; i++) {
		ctl = t4_read_reg(adap, ctl_reg);
		v = G_MBOWNER(ctl);
		if (v != X_MBOWNER_NONE)
			break;
	}
	if (v != X_MBOWNER_PL) {
		t4_report_fw_error(adap);
		ret = (v == X_MBOWNER_FW) ? -EBUSY : -ETIMEDOUT;
		return ret;
	}
	if (ctl & F_MBMSGVALID) {
		CH_ERR(adap, "found VALID command in mbox %u: "
		       "%llx %llx %llx %llx %llx %llx %llx %llx\n", mbox,
		       (unsigned long long)t4_read_reg64(adap, data_reg),
		       (unsigned long long)t4_read_reg64(adap, data_reg + 8),
		       (unsigned long long)t4_read_reg64(adap, data_reg + 16),
		       (unsigned long long)t4_read_reg64(adap, data_reg + 24),
		       (unsigned long long)t4_read_reg64(adap, data_reg + 32),
		       (unsigned long long)t4_read_reg64(adap, data_reg + 40),
		       (unsigned long long)t4_read_reg64(adap, data_reg + 48),
		       (unsigned long long)t4_read_reg64(adap, data_reg + 56));
	}
	for (i = 0; i < size; i += 8, p++)
		t4_write_reg64(adap, data_reg + i, be64_to_cpu(*p));
	CH_DUMP_MBOX(adap, mbox, data_reg);
	t4_write_reg(adap, ctl_reg, F_MBMSGVALID | V_MBOWNER(X_MBOWNER_FW));
	t4_read_reg(adap, ctl_reg);	
	delay_idx = 0;
	ms = delay[0];
	for (i = 0;
	     !((pcie_fw = t4_read_reg(adap, A_PCIE_FW)) & F_PCIE_FW_ERR) &&
	     i < timeout;
	     i += ms) {
		if (sleep_ok) {
			ms = delay[delay_idx];  
			if (delay_idx < ARRAY_SIZE(delay) - 1)
				delay_idx++;
			msleep(ms);
		} else {
			mdelay(ms);
		}
		v = t4_read_reg(adap, ctl_reg);
		if (v == X_CIM_PF_NOACCESS)
			continue;
		if (G_MBOWNER(v) == X_MBOWNER_PL) {
			if (!(v & F_MBMSGVALID)) {
				t4_write_reg(adap, ctl_reg,
					     V_MBOWNER(X_MBOWNER_NONE));
				continue;
			}
			get_mbox_rpl(adap, cmd_rpl, MBOX_LEN/8, data_reg);
			t4_write_reg(adap, ctl_reg, V_MBOWNER(X_MBOWNER_NONE));
			CH_DUMP_MBOX(adap, mbox, data_reg);
			res = be64_to_cpu(cmd_rpl[0]);
			if (G_FW_CMD_OP(res >> 32) == FW_DEBUG_CMD) {
				fw_asrt(adap, (struct fw_debug_cmd *)cmd_rpl);
				res = V_FW_CMD_RETVAL(EIO);
			} else if (rpl)
				memcpy(rpl, cmd_rpl, size);
			return -G_FW_CMD_RETVAL((int)res);
		}
	}
	ret = (pcie_fw & F_PCIE_FW_ERR) ? -ENXIO : -ETIMEDOUT;
	CH_ERR(adap, "command %#x in mailbox %d timed out\n",
	       *(const u8 *)cmd, mbox);
	t4_report_fw_error(adap);
	t4_fatal_err(adap);
	return ret;
}
int t4_wr_mbox_meat_timeout ( struct adapter * adap , int mbox , const void * cmd , int size , void * rpl , bool sleep_ok , int timeout ) { static const int delay [ ] = { 1 , 1 , 3 , 5 , 10 , 10 , 20 , 50 , 100 } ; u32 v ; u64 res ; int i , ms , delay_idx , ret ; const __be64 * p = cmd ; u32 data_reg = PF_REG ( mbox , A_CIM_PF_MAILBOX_DATA ) ; u32 ctl_reg = PF_REG ( mbox , A_CIM_PF_MAILBOX_CTRL ) ; u32 ctl ; __be64 cmd_rpl [ MBOX_LEN / 8 ] ; u32 pcie_fw ; if ( ( size & 15 ) || size > MBOX_LEN ) return - EINVAL ; if ( timeout < 0 ) { sleep_ok = false ; timeout = - timeout ; } for ( i = 0 ; i < 4 ; i ++ ) { ctl = t4_read_reg ( adap , ctl_reg ) ; v = G_MBOWNER ( ctl ) ; if ( v != X_MBOWNER_NONE ) break ; } if ( v != X_MBOWNER_PL ) { t4_report_fw_error ( adap ) ; ret = ( v == X_MBOWNER_FW ) ? - EBUSY : - ETIMEDOUT ; return ret ; } if ( ctl & F_MBMSGVALID ) { CH_ERR ( adap , " " " \n " , mbox , ( unsigned long long ) t4_read_reg64 ( adap , data_reg ) , ( unsigned long long ) t4_read_reg64 ( adap , data_reg + 8 ) , ( unsigned long long ) t4_read_reg64 ( adap , data_reg + 16 ) , ( unsigned long long ) t4_read_reg64 ( adap , data_reg + 24 ) , ( unsigned long long ) t4_read_reg64 ( adap , data_reg + 32 ) , ( unsigned long long ) t4_read_reg64 ( adap , data_reg + 40 ) , ( unsigned long long ) t4_read_reg64 ( adap , data_reg + 48 ) , ( unsigned long long ) t4_read_reg64 ( adap , data_reg + 56 ) ) ; } for ( i = 0 ; i < size ; i += 8 , p ++ ) t4_write_reg64 ( adap , data_reg + i , be64_to_cpu ( * p ) ) ; CH_DUMP_MBOX ( adap , mbox , data_reg ) ; t4_write_reg ( adap , ctl_reg , F_MBMSGVALID | V_MBOWNER ( X_MBOWNER_FW ) ) ; t4_read_reg ( adap , ctl_reg ) ; delay_idx = 0 ; ms = delay [ 0 ] ; for ( i = 0 ; ! ( ( pcie_fw = t4_read_reg ( adap , A_PCIE_FW ) ) & F_PCIE_FW_ERR ) && i < timeout ; i += ms ) { if ( sleep_ok ) { ms = delay [ delay_idx ] ; if ( delay_idx < ARRAY_SIZE ( delay ) - 1 ) delay_idx ++ ; msleep ( ms ) ; } else { mdelay ( ms ) ; } v = t4_read_reg ( adap , ctl_reg ) ; if ( v == X_CIM_PF_NOACCESS ) continue ; if ( G_MBOWNER ( v ) == X_MBOWNER_PL ) { if ( ! ( v & F_MBMSGVALID ) ) { t4_write_reg ( adap , ctl_reg , V_MBOWNER ( X_MBOWNER_NONE ) ) ; continue ; } get_mbox_rpl ( adap , cmd_rpl , MBOX_LEN / 8 , data_reg ) ; t4_write_reg ( adap , ctl_reg , V_MBOWNER ( X_MBOWNER_NONE ) ) ; CH_DUMP_MBOX ( adap , mbox , data_reg ) ; res = be64_to_cpu ( cmd_rpl [ 0 ] ) ; if ( G_FW_CMD_OP ( res >> 32 ) == FW_DEBUG_CMD ) { fw_asrt ( adap , ( struct fw_debug_cmd * ) cmd_rpl ) ; res = V_FW_CMD_RETVAL ( EIO ) ; } else if ( rpl ) memcpy ( rpl , cmd_rpl , size ) ; return - G_FW_CMD_RETVAL ( ( int ) res ) ; } } ret = ( pcie_fw & F_PCIE_FW_ERR ) ? - ENXIO : - ETIMEDOUT ; CH_ERR ( adap , " \n " , * ( const u8 * ) cmd , mbox ) ; t4_report_fw_error ( adap ) ; t4_fatal_err ( adap ) ; return ret ; }
t4_wr_mbox_meat_timeout - send a command to FW through the given mailbox
      @adap: the adapter
      @mbox: index of the mailbox to use
      @cmd: the command to write
      @size: command length in bytes
      @rpl: where to optionally store the reply
      @sleep_ok: if true we may sleep while awaiting command completion
      @timeout: time to wait for command to finish before timing out
              (negative implies @sleep_ok=false)
t4_wr_mbox_meat_timeout - send a command to FW through the given mailbox @adap : the adapter @mbox : index of the mailbox to use @cmd : the command to write @size : command length in bytes @rpl : where to optionally store the reply @sleep_ok : if true we may sleep while awaiting command completion @timeout : time to wait for command to finish before timing out ( negative implies @sleep_ok = false )
/*
	 * We delay in small increments at first in an effort to maintain
	 * responsiveness for simple, fast executing commands but then back
	 * off to larger delays to a maximum retry delay.
	 */ /*
	 * If we have a negative timeout, that implies that we can't sleep.
	 */ /*
	 * Attempt to gain access to the mailbox.
	 */ /*
	 * If we were unable to gain access, dequeue ourselves from the
	 * mailbox atomic access list and report the error to our caller.
	 */ /*
	 * If we gain ownership of the mailbox and there's a "valid" message
	 * in it, this is likely an asynchronous error message from the
	 * firmware.  So we'll report that and then proceed on with attempting
	 * to issue our own command ... which may well fail if the error
	 * presaged the firmware crashing ...
	 */ /*
	 * Copy in the new mailbox command and send it on its way ...
	 */ /* flush write */ /*
	 * Loop waiting for the reply; bail out if we time out or the firmware
	 * reports an error.
	 */ /* last element may repeat */ /*
			 * Retrieve the command reply and release the mailbox.
			 */ /*
	 * We timed out waiting for a reply to our mailbox command.  Report
	 * the error and also check to see if the firmware reported any
	 * errors ...
	 */