Rework multiplexed pin control to match the state change listed in the design requirements. Signed-off-by: Sergey Temerkhanov Reviewed-by: Aleksandr Loktionov Reviewed-by: Przemyslaw Korba --- drivers/net/ethernet/intel/ice/ice_dpll.c | 191 +++++++++++++++++++++- 1 file changed, 183 insertions(+), 8 deletions(-) diff --git a/drivers/net/ethernet/intel/ice/ice_dpll.c b/drivers/net/ethernet/intel/ice/ice_dpll.c index d0225097cd10..0081544b769c 100644 --- a/drivers/net/ethernet/intel/ice/ice_dpll.c +++ b/drivers/net/ethernet/intel/ice/ice_dpll.c @@ -469,6 +469,40 @@ ice_dpll_sw_pin_frequency_get(const struct dpll_pin *pin, void *pin_priv, return ret; } +static int ice_dpll_get_fallback_idx(struct ice_dplls *d, struct ice_dpll_pin *target) +{ + if (d->pps.dpll_idx < ICE_DPLL_RCLK_NUM_MAX && + target->state[d->pps.dpll_idx] == DPLL_PIN_STATE_CONNECTED) + return d->pps.dpll_idx; + + if (d->eec.dpll_idx < ICE_DPLL_RCLK_NUM_MAX && + target->state[d->eec.dpll_idx] == DPLL_PIN_STATE_CONNECTED) + return d->eec.dpll_idx; + + return -EINVAL; +} + +static void ice_dpll_get_peer_target(struct ice_dpll_pin *peer, + struct ice_dpll_pin **peer_target, + enum ice_dpll_pin_type *peer_type) +{ + *peer_target = NULL; + *peer_type = 0; + + switch (peer->direction) { + case DPLL_PIN_DIRECTION_OUTPUT: + *peer_target = peer->output; + *peer_type = ICE_DPLL_PIN_TYPE_OUTPUT; + break; + case DPLL_PIN_DIRECTION_INPUT: + *peer_target = peer->input; + *peer_type = ICE_DPLL_PIN_TYPE_INPUT; + break; + default: + break; + } +} + /** * ice_dpll_pin_enable - enable a pin on dplls * @hw: board private hw structure @@ -1225,6 +1259,87 @@ ice_dpll_input_state_get(const struct dpll_pin *pin, void *pin_priv, extack, ICE_DPLL_PIN_TYPE_INPUT); } +struct ice_dpll_pin_config { + bool enabled; + u8 dpll_idx; +}; + +static int +ice_dpll_pin_config_get(struct ice_pf *pf, struct ice_dpll_pin *target, + enum ice_dpll_pin_type type, + struct ice_dpll_pin_config *config, + struct netlink_ext_ack *extack) +{ + int ret; + + ret = ice_dpll_pin_state_update(pf, target, type, extack); + if (ret) + return ret; + + switch (type) { + case ICE_DPLL_PIN_TYPE_INPUT: + config->enabled = target->flags[0] & + ICE_AQC_GET_CGU_IN_CFG_FLG2_INPUT_EN; + config->dpll_idx = 0; + return 0; + case ICE_DPLL_PIN_TYPE_OUTPUT: + config->enabled = target->flags[0] & + ICE_AQC_GET_CGU_OUT_CFG_OUT_EN; + if (!config->enabled) { + config->dpll_idx = 0; + return 0; + } + + ret = ice_dpll_get_fallback_idx(&pf->dplls, target); + if (ret < 0) + return ret; + config->dpll_idx = ret; + return 0; + default: + return -EINVAL; + } +} + +static int +ice_dpll_pin_config_restore(struct ice_pf *pf, struct ice_dpll_pin *target, + enum ice_dpll_pin_type type, + const struct ice_dpll_pin_config *config, + struct netlink_ext_ack *extack, int init_err) +{ + int restore_err, update_err; + + if (config->enabled) + restore_err = ice_dpll_pin_enable(&pf->hw, target, + config->dpll_idx, type, extack); + else + restore_err = ice_dpll_pin_disable(&pf->hw, target, type, extack); + update_err = ice_dpll_pin_state_update(pf, target, type, extack); + if (restore_err || update_err) { + dev_warn(ice_pf_to_dev(pf), + "Failed to rollback pin state: err %d, restore err %d, update err %d\n", + init_err, restore_err, update_err); + return restore_err ? restore_err : update_err; + } + + return 0; +} + +static int ice_dpll_restore_sma_ctrl(struct ice_pf *pf, u8 old_data, + int init_err) +{ + int restore_err; + + restore_err = ice_write_sma_ctrl(&pf->hw, old_data); + if (!restore_err) + restore_err = ice_dpll_sw_pins_update(pf); + if (restore_err) + dev_warn(ice_pf_to_dev(pf), + "Failed to restore SMA control: err %d, restore err %d\n", + init_err, restore_err); + + return restore_err; +} + /** * ice_dpll_sma_direction_set - set direction of SMA pin * @p: pointer to a pin @@ -1290,6 +1405,7 @@ static int ice_dpll_sma_direction_set(struct ice_dpll_pin *p, if (peer->active) { struct ice_dpll_pin *target; enum ice_dpll_pin_type type; + int peer_ret; if (peer->output) { target = peer->output; @@ -1298,8 +1414,11 @@ static int ice_dpll_sma_direction_set(struct ice_dpll_pin *p, target = peer->input; type = ICE_DPLL_PIN_TYPE_INPUT; } - ret = ice_dpll_pin_enable(&p->pf->hw, target, - d->eec.dpll_idx, type, extack); + peer_ret = ice_dpll_get_fallback_idx(d, target); + if (peer_ret < 0) + peer_ret = d->eec.dpll_idx; + ret = ice_dpll_pin_enable(&p->pf->hw, target, peer_ret, type, + extack); if (!ret) ret = ice_dpll_pin_state_update(p->pf, target, type, extack); @@ -1331,13 +1450,17 @@ ice_dpll_ufl_pin_state_set(const struct dpll_pin *pin, void *pin_priv, struct netlink_ext_ack *extack) { struct ice_dpll_pin *p = pin_priv, *target; + struct ice_dpll_pin *peer_target = NULL; struct ice_dpll *d = dpll_priv; - enum ice_dpll_pin_type type; + enum ice_dpll_pin_type peer_type = 0, type; struct ice_pf *pf = p->pf; + struct ice_dpll_pin_config old_config; + struct ice_dpll_pin_config peer_config; struct ice_hw *hw; + u8 old_data = 0; bool enable; u8 data; - int ret; + int peer_dpll_idx, restore_err, ret; if (ice_dpll_is_reset(pf, extack)) return -EBUSY; @@ -1347,6 +1470,7 @@ ice_dpll_ufl_pin_state_set(const struct dpll_pin *pin, void *pin_priv, ret = ice_read_sma_ctrl(hw, &data); if (ret) goto unlock; + old_data = data; ret = -EINVAL; switch (p->idx) { @@ -1406,18 +1530,69 @@ ice_dpll_ufl_pin_state_set(const struct dpll_pin *pin, void *pin_priv, ret = ice_write_sma_ctrl(hw, data); if (ret) - goto unlock; + goto restore_sma_ctrl; ret = ice_dpll_pin_state_update(pf, p, ICE_DPLL_PIN_TYPE_SOFTWARE, extack); if (ret) - goto unlock; + goto restore_sma_ctrl; + + ret = ice_dpll_pin_config_get(pf, target, type, &old_config, extack); + if (ret) + goto restore_sma_ctrl; if (enable) ret = ice_dpll_pin_enable(hw, target, d->dpll_idx, type, extack); else ret = ice_dpll_pin_disable(hw, target, type, extack); - if (!ret) - ret = ice_dpll_pin_state_update(pf, target, type, extack); + if (ret) + goto restore_target; + ret = ice_dpll_pin_state_update(pf, target, type, extack); + if (ret) + goto restore_target; + + if (enable && p->muxed && p->muxed->active) { + ice_dpll_get_peer_target(p->muxed, &peer_target, &peer_type); + if (!peer_target) { + ret = -EINVAL; + goto restore_target; + } + ret = ice_dpll_pin_config_get(pf, peer_target, peer_type, + &peer_config, extack); + if (ret) + goto restore_target; + + peer_dpll_idx = ice_dpll_get_fallback_idx(&pf->dplls, + peer_target); + if (peer_dpll_idx < 0) + peer_dpll_idx = d->dpll_idx; + ret = ice_dpll_pin_enable(&pf->hw, peer_target, peer_dpll_idx, + peer_type, extack); + if (ret) + goto restore_peer; + ret = ice_dpll_pin_state_update(pf, peer_target, peer_type, + extack); + if (ret) + goto restore_peer; + } + + goto unlock; + +restore_peer: + restore_err = + ice_dpll_pin_config_restore(pf, peer_target, peer_type, + &peer_config, extack, ret); + if (restore_err) + ret = restore_err; +restore_target: + restore_err = + ice_dpll_pin_config_restore(pf, target, type, &old_config, + extack, ret); + if (restore_err) + ret = restore_err; +restore_sma_ctrl: + restore_err = ice_dpll_restore_sma_ctrl(pf, old_data, ret); + if (restore_err) + ret = restore_err; unlock: mutex_unlock(&pf->dplls.lock); -- 2.53.0