@@ -1343,30 +1343,39 @@ static int ice_dpll_sma_direction_set(struct ice_dpll_pin *p,
enum dpll_pin_direction direction,
struct netlink_ext_ack *extack)
{
+ struct ice_dpll_pin_config peer_config;
struct ice_dplls *d = &p->pf->dplls;
+ int peer_dpll_idx, restore_err, ret;
+ struct ice_dpll_pin *target = NULL;
+ enum ice_dpll_pin_type type = 0;
struct ice_dpll_pin *peer;
- u8 data;
- int ret;
+ u8 data, old_data = 0;
if (p->direction == direction && p->active)
return 0;
ret = ice_read_sma_ctrl(&p->pf->hw, &data);
if (ret)
return ret;
+ old_data = data;
switch (p->idx) {
case ICE_DPLL_PIN_SW_1_IDX:
- data &= ~ICE_SMA1_MASK;
- if (direction == DPLL_PIN_DIRECTION_OUTPUT)
+ if (direction == DPLL_PIN_DIRECTION_OUTPUT) {
+ data &= ~ICE_SMA1_TX_EN;
data |= ICE_SMA1_DIR_EN;
+ } else {
+ data &= ~ICE_SMA1_DIR_EN;
+ data &= ~ICE_SMA1_TX_EN;
+ }
break;
case ICE_DPLL_PIN_SW_2_IDX:
if (direction == DPLL_PIN_DIRECTION_INPUT) {
data &= ~ICE_SMA2_DIR_EN;
- data |= ICE_SMA2_UFL2_RX_DIS;
+ data &= ~ICE_SMA2_TX_EN;
} else {
- data &= ~(ICE_SMA2_TX_EN | ICE_SMA2_UFL2_RX_DIS);
data |= ICE_SMA2_DIR_EN;
+ data &= ~ICE_SMA2_UFL2_RX_DIS;
+ data &= ~ICE_SMA2_TX_EN;
}
break;
default:
@@ -1378,7 +1387,7 @@ static int ice_dpll_sma_direction_set(struct ice_dpll_pin *p,
ICE_DPLL_PIN_TYPE_SOFTWARE,
extack);
if (ret)
- return ret;
+ goto restore_sma;
/* When a direction change activates the paired U.FL pin, enable
* its backing CGU pin so the pin reports as connected. Without
@@ -1389,27 +1398,44 @@ static int ice_dpll_sma_direction_set(struct ice_dpll_pin *p,
*/
peer = p->muxed;
if (peer->active) {
- struct ice_dpll_pin *target;
- enum ice_dpll_pin_type type;
- int peer_ret;
-
if (peer->output) {
target = peer->output;
type = ICE_DPLL_PIN_TYPE_OUTPUT;
- } else {
+ } else if (peer->input) {
target = peer->input;
type = ICE_DPLL_PIN_TYPE_INPUT;
+ } else {
+ ret = -EINVAL;
+ goto restore_sma;
}
- 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);
+
+ ret = ice_dpll_pin_config_get(p->pf, target, type,
+ &peer_config, extack);
+ if (ret)
+ goto restore_sma;
+
+ peer_dpll_idx = ice_dpll_get_fallback_idx(d, target);
+ if (peer_dpll_idx < 0)
+ peer_dpll_idx = d->eec.dpll_idx;
+ ret = ice_dpll_pin_enable(&p->pf->hw, target, peer_dpll_idx,
+ type, extack);
+ if (ret)
+ goto restore_peer;
+ ret = ice_dpll_pin_state_update(p->pf, target, type, extack);
+ if (ret)
+ goto restore_peer;
}
+ return 0;
+
+restore_peer:
+ restore_err =
+ ice_dpll_pin_config_restore(p->pf, target, type, &peer_config,
+ extack, ret);
+ if (restore_err)
+ ret = restore_err;
+restore_sma:
+ ice_dpll_restore_sma_ctrl(p->pf, old_data, ret);
return ret;
}
@@ -1435,18 +1461,18 @@ ice_dpll_ufl_pin_state_set(const struct dpll_pin *pin, void *pin_priv,
enum dpll_pin_state state,
struct netlink_ext_ack *extack)
{
+ enum ice_dpll_pin_type peer_type = 0, type;
struct ice_dpll_pin *p = pin_priv, *target;
struct ice_dpll_pin *peer_target = NULL;
+ struct ice_dpll_pin_config peer_config;
+ struct ice_dpll_pin_config old_config;
+ int peer_dpll_idx, restore_err, ret;
struct ice_dpll *d = dpll_priv;
- 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 peer_dpll_idx, restore_err, ret;
if (ice_dpll_is_reset(pf, extack))
return -EBUSY;
@@ -1666,12 +1692,16 @@ ice_dpll_sma_pin_state_set(const struct dpll_pin *pin, void *pin_priv,
enum dpll_pin_state state,
struct netlink_ext_ack *extack)
{
+ int peer_dpll_idx, restore_err, ret = -EINVAL;
struct ice_dpll_pin *sma = pin_priv, *target;
+ enum ice_dpll_pin_type peer_type = 0, type;
+ struct ice_dpll_pin *peer_target = NULL;
+ struct ice_dpll_pin_config peer_config;
+ struct ice_dpll_pin_config old_config;
struct ice_dpll *d = dpll_priv;
struct ice_pf *pf = sma->pf;
- enum ice_dpll_pin_type type;
+ u8 old_data = 0;
bool enable;
- int ret;
if (ice_dpll_is_reset(pf, extack))
return -EBUSY;
@@ -1679,6 +1709,10 @@ ice_dpll_sma_pin_state_set(const struct dpll_pin *pin, void *pin_priv,
mutex_lock(&pf->dplls.lock);
switch (state) {
case DPLL_PIN_STATE_SELECTABLE:
+ /* Reject direction-mismatched requests: SELECTABLE + OUTPUT
+ * is an invalid combination, return -EINVAL in this case
+ * immediately instead of disabling the pin.
+ */
if (sma->direction == DPLL_PIN_DIRECTION_OUTPUT) {
enable = false;
ret = -EINVAL;
@@ -1687,6 +1721,10 @@ ice_dpll_sma_pin_state_set(const struct dpll_pin *pin, void *pin_priv,
enable = true;
break;
case DPLL_PIN_STATE_CONNECTED:
+ /* Reject direction-mismatched requests: CONNECTED + INPUT
+ * is an invalid combination, return -EINVAL in this case
+ * immediately instead of disabling the pin.
+ */
if (sma->direction == DPLL_PIN_DIRECTION_INPUT) {
enable = false;
ret = -EINVAL;
@@ -1722,13 +1760,95 @@ ice_dpll_sma_pin_state_set(const struct dpll_pin *pin, void *pin_priv,
goto unlock;
}
- if (enable)
+ if (enable) {
+ u8 data;
+
+ ret = ice_read_sma_ctrl(&pf->hw, &data);
+ if (ret)
+ goto unlock;
+ old_data = data;
+ switch (sma->idx) {
+ case ICE_DPLL_PIN_SW_1_IDX:
+ data &= ~ICE_SMA1_TX_EN;
+ break;
+ case ICE_DPLL_PIN_SW_2_IDX:
+ data &= ~ICE_SMA2_UFL2_RX_DIS;
+ data &= ~ICE_SMA2_TX_EN;
+ break;
+ default:
+ ret = -EINVAL;
+ goto unlock;
+ }
+ ret = ice_write_sma_ctrl(&pf->hw, data);
+ if (ret)
+ goto restore_sma_ctrl;
+ ret = ice_dpll_sw_pins_update(pf);
+ if (ret)
+ goto restore_sma_ctrl;
+
+ ret = ice_dpll_pin_config_get(pf, target, type, &old_config,
+ extack);
+ if (ret)
+ goto restore_sma_ctrl;
ret = ice_dpll_pin_enable(&pf->hw, target, d->dpll_idx, type,
extack);
- else
- ret = ice_dpll_pin_disable(&pf->hw, target, type, extack);
- if (!ret)
+ if (ret)
+ goto restore_target;
+ /* Refresh target state before peer-side operations. */
ret = ice_dpll_pin_state_update(pf, target, type, extack);
+ if (ret)
+ goto restore_target;
+
+ if (sma->muxed && sma->muxed->active) {
+ ice_dpll_get_peer_target(sma->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;
+ }
+ } else {
+ ret = ice_dpll_pin_disable(&pf->hw, target, type, extack);
+ if (!ret)
+ ret = ice_dpll_pin_state_update(pf, target, type,
+ extack);
+ }
+ 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);