diff --git a/drivers/dpll/dpll_netlink.c b/drivers/dpll/dpll_netlink.c index 09a6c2a1ea92..a6dc3997bf5c 100644 --- a/drivers/dpll/dpll_netlink.c +++ b/drivers/dpll/dpll_netlink.c @@ -615,8 +615,10 @@ static int dpll_pin_freq_set(struct dpll_pin *pin, struct nlattr *a, struct netlink_ext_ack *extack) { - u64 freq = nla_get_u64(a); - struct dpll_pin_ref *ref; + u64 freq = nla_get_u64(a), old_freq; + struct dpll_pin_ref *ref, *failed; + const struct dpll_pin_ops *ops; + struct dpll_device *dpll; unsigned long i; int ret; @@ -626,19 +628,51 @@ dpll_pin_freq_set(struct dpll_pin *pin, struct nlattr *a, } xa_for_each(&pin->dpll_refs, i, ref) { - const struct dpll_pin_ops *ops = dpll_pin_ops(ref); - struct dpll_device *dpll = ref->dpll; - - if (!ops->frequency_set) + ops = dpll_pin_ops(ref); + if (!ops->frequency_set || !ops->frequency_get) { + NL_SET_ERR_MSG(extack, "frequency set not supported by the device"); return -EOPNOTSUPP; + } + } + ref = dpll_xa_ref_dpll_first(&pin->dpll_refs); + ops = dpll_pin_ops(ref); + dpll = ref->dpll; + ret = ops->frequency_get(pin, dpll_pin_on_dpll_priv(dpll, pin), dpll, + dpll_priv(dpll), &old_freq, extack); + if (ret) { + NL_SET_ERR_MSG(extack, "unable to get old frequency value"); + return ret; + } + if (freq == old_freq) + return 0; + + xa_for_each(&pin->dpll_refs, i, ref) { + ops = dpll_pin_ops(ref); + dpll = ref->dpll; ret = ops->frequency_set(pin, dpll_pin_on_dpll_priv(dpll, pin), dpll, dpll_priv(dpll), freq, extack); - if (ret) - return ret; + if (ret) { + failed = ref; + NL_SET_ERR_MSG_FMT(extack, "frequency set failed for dpll_id:%u", + dpll->id); + goto rollback; + } } __dpll_pin_change_ntf(pin); return 0; + +rollback: + xa_for_each(&pin->dpll_refs, i, ref) { + if (ref == failed) + break; + ops = dpll_pin_ops(ref); + dpll = ref->dpll; + if (ops->frequency_set(pin, dpll_pin_on_dpll_priv(dpll, pin), + dpll, dpll_priv(dpll), old_freq, extack)) + NL_SET_ERR_MSG(extack, "set frequency rollback failed"); + } + return ret; } static int