in hw/hfi1/chip.c [10666:10965]
int set_link_state(struct hfi1_pportdata *ppd, u32 state)
{
struct hfi1_devdata *dd = ppd->dd;
struct ib_event event = {.device = NULL};
int ret1, ret = 0;
int orig_new_state, poll_bounce;
mutex_lock(&ppd->hls_lock);
orig_new_state = state;
if (state == HLS_DN_DOWNDEF)
state = HLS_DEFAULT;
/* interpret poll -> poll as a link bounce */
poll_bounce = ppd->host_link_state == HLS_DN_POLL &&
state == HLS_DN_POLL;
dd_dev_info(dd, "%s: current %s, new %s %s%s\n", __func__,
link_state_name(ppd->host_link_state),
link_state_name(orig_new_state),
poll_bounce ? "(bounce) " : "",
link_state_reason_name(ppd, state));
/*
* If we're going to a (HLS_*) link state that implies the logical
* link state is neither of (IB_PORT_ARMED, IB_PORT_ACTIVE), then
* reset is_sm_config_started to 0.
*/
if (!(state & (HLS_UP_ARMED | HLS_UP_ACTIVE)))
ppd->is_sm_config_started = 0;
/*
* Do nothing if the states match. Let a poll to poll link bounce
* go through.
*/
if (ppd->host_link_state == state && !poll_bounce)
goto done;
switch (state) {
case HLS_UP_INIT:
if (ppd->host_link_state == HLS_DN_POLL &&
(quick_linkup || dd->icode == ICODE_FUNCTIONAL_SIMULATOR)) {
/*
* Quick link up jumps from polling to here.
*
* Whether in normal or loopback mode, the
* simulator jumps from polling to link up.
* Accept that here.
*/
/* OK */
} else if (ppd->host_link_state != HLS_GOING_UP) {
goto unexpected;
}
/*
* Wait for Link_Up physical state.
* Physical and Logical states should already be
* be transitioned to LinkUp and LinkInit respectively.
*/
ret = wait_physical_linkstate(ppd, PLS_LINKUP, 1000);
if (ret) {
dd_dev_err(dd,
"%s: physical state did not change to LINK-UP\n",
__func__);
break;
}
ret = wait_logical_linkstate(ppd, IB_PORT_INIT, 1000);
if (ret) {
dd_dev_err(dd,
"%s: logical state did not change to INIT\n",
__func__);
break;
}
/* clear old transient LINKINIT_REASON code */
if (ppd->linkinit_reason >= OPA_LINKINIT_REASON_CLEAR)
ppd->linkinit_reason =
OPA_LINKINIT_REASON_LINKUP;
/* enable the port */
add_rcvctrl(dd, RCV_CTRL_RCV_PORT_ENABLE_SMASK);
handle_linkup_change(dd, 1);
pio_kernel_linkup(dd);
/*
* After link up, a new link width will have been set.
* Update the xmit counters with regards to the new
* link width.
*/
update_xmit_counters(ppd, ppd->link_width_active);
ppd->host_link_state = HLS_UP_INIT;
update_statusp(ppd, IB_PORT_INIT);
break;
case HLS_UP_ARMED:
if (ppd->host_link_state != HLS_UP_INIT)
goto unexpected;
if (!data_vls_operational(ppd)) {
dd_dev_err(dd,
"%s: Invalid data VL credits or mtu\n",
__func__);
ret = -EINVAL;
break;
}
set_logical_state(dd, LSTATE_ARMED);
ret = wait_logical_linkstate(ppd, IB_PORT_ARMED, 1000);
if (ret) {
dd_dev_err(dd,
"%s: logical state did not change to ARMED\n",
__func__);
break;
}
ppd->host_link_state = HLS_UP_ARMED;
update_statusp(ppd, IB_PORT_ARMED);
/*
* The simulator does not currently implement SMA messages,
* so neighbor_normal is not set. Set it here when we first
* move to Armed.
*/
if (dd->icode == ICODE_FUNCTIONAL_SIMULATOR)
ppd->neighbor_normal = 1;
break;
case HLS_UP_ACTIVE:
if (ppd->host_link_state != HLS_UP_ARMED)
goto unexpected;
set_logical_state(dd, LSTATE_ACTIVE);
ret = wait_logical_linkstate(ppd, IB_PORT_ACTIVE, 1000);
if (ret) {
dd_dev_err(dd,
"%s: logical state did not change to ACTIVE\n",
__func__);
} else {
/* tell all engines to go running */
sdma_all_running(dd);
ppd->host_link_state = HLS_UP_ACTIVE;
update_statusp(ppd, IB_PORT_ACTIVE);
/* Signal the IB layer that the port has went active */
event.device = &dd->verbs_dev.rdi.ibdev;
event.element.port_num = ppd->port;
event.event = IB_EVENT_PORT_ACTIVE;
}
break;
case HLS_DN_POLL:
if ((ppd->host_link_state == HLS_DN_DISABLE ||
ppd->host_link_state == HLS_DN_OFFLINE) &&
dd->dc_shutdown)
dc_start(dd);
/* Hand LED control to the DC */
write_csr(dd, DCC_CFG_LED_CNTRL, 0);
if (ppd->host_link_state != HLS_DN_OFFLINE) {
u8 tmp = ppd->link_enabled;
ret = goto_offline(ppd, ppd->remote_link_down_reason);
if (ret) {
ppd->link_enabled = tmp;
break;
}
ppd->remote_link_down_reason = 0;
if (ppd->driver_link_ready)
ppd->link_enabled = 1;
}
set_all_slowpath(ppd->dd);
ret = set_local_link_attributes(ppd);
if (ret)
break;
ppd->port_error_action = 0;
if (quick_linkup) {
/* quick linkup does not go into polling */
ret = do_quick_linkup(dd);
} else {
ret1 = set_physical_link_state(dd, PLS_POLLING);
if (!ret1)
ret1 = wait_phys_link_out_of_offline(ppd,
3000);
if (ret1 != HCMD_SUCCESS) {
dd_dev_err(dd,
"Failed to transition to Polling link state, return 0x%x\n",
ret1);
ret = -EINVAL;
}
}
/*
* Change the host link state after requesting DC8051 to
* change its physical state so that we can ignore any
* interrupt with stale LNI(XX) error, which will not be
* cleared until DC8051 transitions to Polling state.
*/
ppd->host_link_state = HLS_DN_POLL;
ppd->offline_disabled_reason =
HFI1_ODR_MASK(OPA_LINKDOWN_REASON_NONE);
/*
* If an error occurred above, go back to offline. The
* caller may reschedule another attempt.
*/
if (ret)
goto_offline(ppd, 0);
else
log_physical_state(ppd, PLS_POLLING);
break;
case HLS_DN_DISABLE:
/* link is disabled */
ppd->link_enabled = 0;
/* allow any state to transition to disabled */
/* must transition to offline first */
if (ppd->host_link_state != HLS_DN_OFFLINE) {
ret = goto_offline(ppd, ppd->remote_link_down_reason);
if (ret)
break;
ppd->remote_link_down_reason = 0;
}
if (!dd->dc_shutdown) {
ret1 = set_physical_link_state(dd, PLS_DISABLED);
if (ret1 != HCMD_SUCCESS) {
dd_dev_err(dd,
"Failed to transition to Disabled link state, return 0x%x\n",
ret1);
ret = -EINVAL;
break;
}
ret = wait_physical_linkstate(ppd, PLS_DISABLED, 10000);
if (ret) {
dd_dev_err(dd,
"%s: physical state did not change to DISABLED\n",
__func__);
break;
}
dc_shutdown(dd);
}
ppd->host_link_state = HLS_DN_DISABLE;
break;
case HLS_DN_OFFLINE:
if (ppd->host_link_state == HLS_DN_DISABLE)
dc_start(dd);
/* allow any state to transition to offline */
ret = goto_offline(ppd, ppd->remote_link_down_reason);
if (!ret)
ppd->remote_link_down_reason = 0;
break;
case HLS_VERIFY_CAP:
if (ppd->host_link_state != HLS_DN_POLL)
goto unexpected;
ppd->host_link_state = HLS_VERIFY_CAP;
log_physical_state(ppd, PLS_CONFIGPHY_VERIFYCAP);
break;
case HLS_GOING_UP:
if (ppd->host_link_state != HLS_VERIFY_CAP)
goto unexpected;
ret1 = set_physical_link_state(dd, PLS_LINKUP);
if (ret1 != HCMD_SUCCESS) {
dd_dev_err(dd,
"Failed to transition to link up state, return 0x%x\n",
ret1);
ret = -EINVAL;
break;
}
ppd->host_link_state = HLS_GOING_UP;
break;
case HLS_GOING_OFFLINE: /* transient within goto_offline() */
case HLS_LINK_COOLDOWN: /* transient within goto_offline() */
default:
dd_dev_info(dd, "%s: state 0x%x: not supported\n",
__func__, state);
ret = -EINVAL;
break;
}
goto done;
unexpected:
dd_dev_err(dd, "%s: unexpected state transition from %s to %s\n",
__func__, link_state_name(ppd->host_link_state),
link_state_name(state));
ret = -EINVAL;
done:
mutex_unlock(&ppd->hls_lock);
if (event.device)
ib_dispatch_event(&event);
return ret;
}