diff options
Diffstat (limited to 'drivers/net/qlge/qlge_mpi.c')
| -rw-r--r-- | drivers/net/qlge/qlge_mpi.c | 328 | 
1 files changed, 286 insertions, 42 deletions
diff --git a/drivers/net/qlge/qlge_mpi.c b/drivers/net/qlge/qlge_mpi.c index c2e43073047..e2b2286102d 100644 --- a/drivers/net/qlge/qlge_mpi.c +++ b/drivers/net/qlge/qlge_mpi.c @@ -1,25 +1,5 @@  #include "qlge.h" -static void ql_display_mb_sts(struct ql_adapter *qdev, -						struct mbox_params *mbcp) -{ -	int i; -	static char *err_sts[] = { -		"Command Complete", -		"Command Not Supported", -		"Host Interface Error", -		"Checksum Error", -		"Unused Completion Status", -		"Test Failed", -		"Command Parameter Error"}; - -	QPRINTK(qdev, DRV, DEBUG, "%s.\n", -		err_sts[mbcp->mbox_out[0] & 0x0000000f]); -	for (i = 0; i < mbcp->out_count; i++) -		QPRINTK(qdev, DRV, DEBUG, "mbox_out[%d] = 0x%.08x.\n", -				i, mbcp->mbox_out[i]); -} -  int ql_read_mpi_reg(struct ql_adapter *qdev, u32 reg, u32 *data)  {  	int status; @@ -317,6 +297,7 @@ static void ql_init_fw_done(struct ql_adapter *qdev, struct mbox_params *mbcp)  	} else {  		QPRINTK(qdev, DRV, ERR, "Firmware Revision  = 0x%.08x.\n",  			mbcp->mbox_out[1]); +		qdev->fw_rev_id = mbcp->mbox_out[1];  		status = ql_cam_route_initialize(qdev);  		if (status)  			QPRINTK(qdev, IFUP, ERR, @@ -446,6 +427,9 @@ static int ql_mpi_handler(struct ql_adapter *qdev, struct mbox_params *mbcp)  		ql_aen_lost(qdev, mbcp);  		break; +	case AEN_DCBX_CHG: +		/* Need to support AEN 8110 */ +		break;  	default:  		QPRINTK(qdev, DRV, ERR,  			"Unsupported AE %.08x.\n", mbcp->mbox_out[0]); @@ -470,7 +454,8 @@ end:   */  static int ql_mailbox_command(struct ql_adapter *qdev, struct mbox_params *mbcp)  { -	int status, count; +	int status; +	unsigned long count;  	/* Begin polled mode for MPI */ @@ -491,14 +476,14 @@ static int ql_mailbox_command(struct ql_adapter *qdev, struct mbox_params *mbcp)  	/* Wait for the command to complete. We loop  	 * here because some AEN might arrive while  	 * we're waiting for the mailbox command to -	 * complete. If more than 5 arrive then we can +	 * complete. If more than 5 seconds expire we can  	 * assume something is wrong. */ -	count = 5; +	count = jiffies + HZ * MAILBOX_TIMEOUT;  	do {  		/* Wait for the interrupt to come in. */  		status = ql_wait_mbx_cmd_cmplt(qdev);  		if (status) -			goto end; +			continue;  		/* Process the event.  If it's an AEN, it  		 * will be handled in-line or a worker @@ -517,15 +502,15 @@ static int ql_mailbox_command(struct ql_adapter *qdev, struct mbox_params *mbcp)  					MB_CMD_STS_GOOD) ||  			((mbcp->mbox_out[0] & 0x0000f000) ==  					MB_CMD_STS_INTRMDT)) -			break; -	} while (--count); +			goto done; +	} while (time_before(jiffies, count)); -	if (!count) { -		QPRINTK(qdev, DRV, ERR, -			"Timed out waiting for mailbox complete.\n"); -		status = -ETIMEDOUT; -		goto end; -	} +	QPRINTK(qdev, DRV, ERR, +		"Timed out waiting for mailbox complete.\n"); +	status = -ETIMEDOUT; +	goto end; + +done:  	/* Now we can clear the interrupt condition  	 * and look at our status. @@ -536,7 +521,6 @@ static int ql_mailbox_command(struct ql_adapter *qdev, struct mbox_params *mbcp)  					MB_CMD_STS_GOOD) &&  		((mbcp->mbox_out[0] & 0x0000f000) !=  					MB_CMD_STS_INTRMDT)) { -		ql_display_mb_sts(qdev, mbcp);  		status = -EIO;  	}  end: @@ -654,7 +638,7 @@ int ql_mb_idc_ack(struct ql_adapter *qdev)   * for the current port.   * Most likely will block.   */ -static int ql_mb_set_port_cfg(struct ql_adapter *qdev) +int ql_mb_set_port_cfg(struct ql_adapter *qdev)  {  	struct mbox_params mbc;  	struct mbox_params *mbcp = &mbc; @@ -689,7 +673,7 @@ static int ql_mb_set_port_cfg(struct ql_adapter *qdev)   * for the current port.   * Most likely will block.   */ -static int ql_mb_get_port_cfg(struct ql_adapter *qdev) +int ql_mb_get_port_cfg(struct ql_adapter *qdev)  {  	struct mbox_params mbc;  	struct mbox_params *mbcp = &mbc; @@ -719,6 +703,76 @@ static int ql_mb_get_port_cfg(struct ql_adapter *qdev)  	return status;  } +int ql_mb_wol_mode(struct ql_adapter *qdev, u32 wol) +{ +	struct mbox_params mbc; +	struct mbox_params *mbcp = &mbc; +	int status; + +	memset(mbcp, 0, sizeof(struct mbox_params)); + +	mbcp->in_count = 2; +	mbcp->out_count = 1; + +	mbcp->mbox_in[0] = MB_CMD_SET_WOL_MODE; +	mbcp->mbox_in[1] = wol; + + +	status = ql_mailbox_command(qdev, mbcp); +	if (status) +		return status; + +	if (mbcp->mbox_out[0] != MB_CMD_STS_GOOD) { +		QPRINTK(qdev, DRV, ERR, +			"Failed to set WOL mode.\n"); +		status = -EIO; +	} +	return status; +} + +int ql_mb_wol_set_magic(struct ql_adapter *qdev, u32 enable_wol) +{ +	struct mbox_params mbc; +	struct mbox_params *mbcp = &mbc; +	int status; +	u8 *addr = qdev->ndev->dev_addr; + +	memset(mbcp, 0, sizeof(struct mbox_params)); + +	mbcp->in_count = 8; +	mbcp->out_count = 1; + +	mbcp->mbox_in[0] = MB_CMD_SET_WOL_MAGIC; +	if (enable_wol) { +		mbcp->mbox_in[1] = (u32)addr[0]; +		mbcp->mbox_in[2] = (u32)addr[1]; +		mbcp->mbox_in[3] = (u32)addr[2]; +		mbcp->mbox_in[4] = (u32)addr[3]; +		mbcp->mbox_in[5] = (u32)addr[4]; +		mbcp->mbox_in[6] = (u32)addr[5]; +		mbcp->mbox_in[7] = 0; +	} else { +		mbcp->mbox_in[1] = 0; +		mbcp->mbox_in[2] = 1; +		mbcp->mbox_in[3] = 1; +		mbcp->mbox_in[4] = 1; +		mbcp->mbox_in[5] = 1; +		mbcp->mbox_in[6] = 1; +		mbcp->mbox_in[7] = 0; +	} + +	status = ql_mailbox_command(qdev, mbcp); +	if (status) +		return status; + +	if (mbcp->mbox_out[0] != MB_CMD_STS_GOOD) { +		QPRINTK(qdev, DRV, ERR, +			"Failed to set WOL mode.\n"); +		status = -EIO; +	} +	return status; +} +  /* IDC - Inter Device Communication...   * Some firmware commands require consent of adjacent FCOE   * function.  This function waits for the OK, or a @@ -768,6 +822,150 @@ static int ql_idc_wait(struct ql_adapter *qdev)  	return status;  } +int ql_mb_set_led_cfg(struct ql_adapter *qdev, u32 led_config) +{ +	struct mbox_params mbc; +	struct mbox_params *mbcp = &mbc; +	int status; + +	memset(mbcp, 0, sizeof(struct mbox_params)); + +	mbcp->in_count = 2; +	mbcp->out_count = 1; + +	mbcp->mbox_in[0] = MB_CMD_SET_LED_CFG; +	mbcp->mbox_in[1] = led_config; + + +	status = ql_mailbox_command(qdev, mbcp); +	if (status) +		return status; + +	if (mbcp->mbox_out[0] != MB_CMD_STS_GOOD) { +		QPRINTK(qdev, DRV, ERR, +			"Failed to set LED Configuration.\n"); +		status = -EIO; +	} + +	return status; +} + +int ql_mb_get_led_cfg(struct ql_adapter *qdev) +{ +	struct mbox_params mbc; +	struct mbox_params *mbcp = &mbc; +	int status; + +	memset(mbcp, 0, sizeof(struct mbox_params)); + +	mbcp->in_count = 1; +	mbcp->out_count = 2; + +	mbcp->mbox_in[0] = MB_CMD_GET_LED_CFG; + +	status = ql_mailbox_command(qdev, mbcp); +	if (status) +		return status; + +	if (mbcp->mbox_out[0] != MB_CMD_STS_GOOD) { +		QPRINTK(qdev, DRV, ERR, +			"Failed to get LED Configuration.\n"); +		status = -EIO; +	} else +		qdev->led_config = mbcp->mbox_out[1]; + +	return status; +} + +int ql_mb_set_mgmnt_traffic_ctl(struct ql_adapter *qdev, u32 control) +{ +	struct mbox_params mbc; +	struct mbox_params *mbcp = &mbc; +	int status; + +	memset(mbcp, 0, sizeof(struct mbox_params)); + +	mbcp->in_count = 1; +	mbcp->out_count = 2; + +	mbcp->mbox_in[0] = MB_CMD_SET_MGMNT_TFK_CTL; +	mbcp->mbox_in[1] = control; + +	status = ql_mailbox_command(qdev, mbcp); +	if (status) +		return status; + +	if (mbcp->mbox_out[0] == MB_CMD_STS_GOOD) +		return status; + +	if (mbcp->mbox_out[0] == MB_CMD_STS_INVLD_CMD) { +		QPRINTK(qdev, DRV, ERR, +			"Command not supported by firmware.\n"); +		status = -EINVAL; +	} else if (mbcp->mbox_out[0] == MB_CMD_STS_ERR) { +		/* This indicates that the firmware is +		 * already in the state we are trying to +		 * change it to. +		 */ +		QPRINTK(qdev, DRV, ERR, +			"Command parameters make no change.\n"); +	} +	return status; +} + +/* Returns a negative error code or the mailbox command status. */ +static int ql_mb_get_mgmnt_traffic_ctl(struct ql_adapter *qdev, u32 *control) +{ +	struct mbox_params mbc; +	struct mbox_params *mbcp = &mbc; +	int status; + +	memset(mbcp, 0, sizeof(struct mbox_params)); +	*control = 0; + +	mbcp->in_count = 1; +	mbcp->out_count = 1; + +	mbcp->mbox_in[0] = MB_CMD_GET_MGMNT_TFK_CTL; + +	status = ql_mailbox_command(qdev, mbcp); +	if (status) +		return status; + +	if (mbcp->mbox_out[0] == MB_CMD_STS_GOOD) { +		*control = mbcp->mbox_in[1]; +		return status; +	} + +	if (mbcp->mbox_out[0] == MB_CMD_STS_INVLD_CMD) { +		QPRINTK(qdev, DRV, ERR, +			"Command not supported by firmware.\n"); +		status = -EINVAL; +	} else if (mbcp->mbox_out[0] == MB_CMD_STS_ERR) { +		QPRINTK(qdev, DRV, ERR, +			"Failed to get MPI traffic control.\n"); +		status = -EIO; +	} +	return status; +} + +int ql_wait_fifo_empty(struct ql_adapter *qdev) +{ +	int count = 5; +	u32 mgmnt_fifo_empty; +	u32 nic_fifo_empty; + +	do { +		nic_fifo_empty = ql_read32(qdev, STS) & STS_NFE; +		ql_mb_get_mgmnt_traffic_ctl(qdev, &mgmnt_fifo_empty); +		mgmnt_fifo_empty &= MB_GET_MPI_TFK_FIFO_EMPTY; +		if (nic_fifo_empty && mgmnt_fifo_empty) +			return 0; +		msleep(100); +	} while (count-- > 0); +	return -ETIMEDOUT; +} +  /* API called in work thread context to set new TX/RX   * maximum frame size values to match MTU.   */ @@ -840,8 +1038,11 @@ void ql_mpi_idc_work(struct work_struct *work)  	int status;  	struct mbox_params *mbcp = &qdev->idc_mbc;  	u32 aen; +	int timeout; +	rtnl_lock();  	aen = mbcp->mbox_out[1] >> 16; +	timeout = (mbcp->mbox_out[1] >> 8) & 0xf;  	switch (aen) {  	default: @@ -849,22 +1050,61 @@ void ql_mpi_idc_work(struct work_struct *work)  			"Bug: Unhandled IDC action.\n");  		break;  	case MB_CMD_PORT_RESET: -	case MB_CMD_SET_PORT_CFG:  	case MB_CMD_STOP_FW:  		ql_link_off(qdev); +	case MB_CMD_SET_PORT_CFG:  		/* Signal the resulting link up AEN  		 * that the frame routing and mac addr  		 * needs to be set.  		 * */  		set_bit(QL_CAM_RT_SET, &qdev->flags); -		rtnl_lock(); -		status = ql_mb_idc_ack(qdev); -		rtnl_unlock(); -		if (status) { -			QPRINTK(qdev, DRV, ERR, -			"Bug: No pending IDC!\n"); +		/* Do ACK if required */ +		if (timeout) { +			status = ql_mb_idc_ack(qdev); +			if (status) +				QPRINTK(qdev, DRV, ERR, +					"Bug: No pending IDC!\n"); +		} else { +			QPRINTK(qdev, DRV, DEBUG, +				    "IDC ACK not required\n"); +			status = 0; /* success */ +		} +		break; + +	/* These sub-commands issued by another (FCoE) +	 * function are requesting to do an operation +	 * on the shared resource (MPI environment). +	 * We currently don't issue these so we just +	 * ACK the request. +	 */ +	case MB_CMD_IOP_RESTART_MPI: +	case MB_CMD_IOP_PREP_LINK_DOWN: +		/* Drop the link, reload the routing +		 * table when link comes up. +		 */ +		ql_link_off(qdev); +		set_bit(QL_CAM_RT_SET, &qdev->flags); +		/* Fall through. */ +	case MB_CMD_IOP_DVR_START: +	case MB_CMD_IOP_FLASH_ACC: +	case MB_CMD_IOP_CORE_DUMP_MPI: +	case MB_CMD_IOP_PREP_UPDATE_MPI: +	case MB_CMD_IOP_COMP_UPDATE_MPI: +	case MB_CMD_IOP_NONE:	/*  an IDC without params */ +		/* Do ACK if required */ +		if (timeout) { +			status = ql_mb_idc_ack(qdev); +			if (status) +				QPRINTK(qdev, DRV, ERR, +				    "Bug: No pending IDC!\n"); +		} else { +			QPRINTK(qdev, DRV, DEBUG, +			    "IDC ACK not required\n"); +			status = 0; /* success */  		} +		break;  	} +	rtnl_unlock();  }  void ql_mpi_work(struct work_struct *work) @@ -876,6 +1116,8 @@ void ql_mpi_work(struct work_struct *work)  	int err = 0;  	rtnl_lock(); +	/* Begin polled mode for MPI */ +	ql_write32(qdev, INTR_MASK, (INTR_MASK_PI << 16));  	while (ql_read32(qdev, STS) & STS_PI) {  		memset(mbcp, 0, sizeof(struct mbox_params)); @@ -888,6 +1130,8 @@ void ql_mpi_work(struct work_struct *work)  			break;  	} +	/* End polled mode for MPI */ +	ql_write32(qdev, INTR_MASK, (INTR_MASK_PI << 16) | INTR_MASK_PI);  	rtnl_unlock();  	ql_enable_completion_interrupt(qdev, 0);  }  |