diff --git a/drivers/dpll/dpll_netlink.c b/drivers/dpll/dpll_netlink.c index 0ff1658c2dc1..75e3ae0c16d0 100644 --- a/drivers/dpll/dpll_netlink.c +++ b/drivers/dpll/dpll_netlink.c @@ -829,12 +829,21 @@ int dpll_device_delete_ntf(struct dpll_device *dpll) return dpll_device_event_send(DPLL_CMD_DEVICE_DELETE_NTF, dpll); } -static int -__dpll_device_change_ntf(struct dpll_device *dpll) +/** + * __dpll_device_change_ntf - notify that the dpll device has been changed + * @dpll: registered dpll pointer + * + * Context: caller must hold dpll_lock. Suitable for use inside device + * callbacks which are already invoked under dpll_lock. + * Return: 0 if succeeds, error code otherwise. + */ +int __dpll_device_change_ntf(struct dpll_device *dpll) { + lockdep_assert_held(&dpll_lock); dpll_device_notify(dpll, DPLL_DEVICE_CHANGED); return dpll_device_event_send(DPLL_CMD_DEVICE_CHANGE_NTF, dpll); } +EXPORT_SYMBOL_GPL(__dpll_device_change_ntf); /** * dpll_device_change_ntf - notify that the dpll device has been changed diff --git a/drivers/dpll/zl3073x/core.c b/drivers/dpll/zl3073x/core.c index 5f1e70f3e40a..0a133b0f2d97 100644 --- a/drivers/dpll/zl3073x/core.c +++ b/drivers/dpll/zl3073x/core.c @@ -762,18 +762,15 @@ zl3073x_dev_periodic_work(struct kthread_work *work) dev_warn(zldev->dev, "Failed to update phase offsets: %pe\n", ERR_PTR(rc)); - /* Update measured input reference frequencies if any DPLL has - * frequency monitoring enabled. + /* Update measured input reference frequencies if frequency + * monitoring is enabled. */ - list_for_each_entry(zldpll, &zldev->dplls, list) { - if (zldpll->freq_monitor) { - rc = zl3073x_ref_freq_meas_update(zldev); - if (rc) - dev_warn(zldev->dev, - "Failed to update measured frequencies: %pe\n", - ERR_PTR(rc)); - break; - } + if (zldev->freq_monitor) { + rc = zl3073x_ref_freq_meas_update(zldev); + if (rc) + dev_warn(zldev->dev, + "Failed to update measured frequencies: %pe\n", + ERR_PTR(rc)); } /* Update references' fractional frequency offsets */ diff --git a/drivers/dpll/zl3073x/core.h b/drivers/dpll/zl3073x/core.h index 99440620407d..addba378b0df 100644 --- a/drivers/dpll/zl3073x/core.h +++ b/drivers/dpll/zl3073x/core.h @@ -57,6 +57,7 @@ struct zl3073x_chip_info { * @work: periodic work * @clock_id: clock id of the device * @phase_avg_factor: phase offset measurement averaging factor + * @freq_monitor: is frequency monitor enabled */ struct zl3073x_dev { struct device *dev; @@ -77,9 +78,10 @@ struct zl3073x_dev { struct kthread_worker *kworker; struct kthread_delayed_work work; - /* Devlink parameters */ + /* Per-chip parameters */ u64 clock_id; u8 phase_avg_factor; + bool freq_monitor; }; extern const struct regmap_config zl3073x_regmap_config; diff --git a/drivers/dpll/zl3073x/dpll.c b/drivers/dpll/zl3073x/dpll.c index 64b4e9e3e8fe..0bfcbae2109f 100644 --- a/drivers/dpll/zl3073x/dpll.c +++ b/drivers/dpll/zl3073x/dpll.c @@ -1079,15 +1079,6 @@ zl3073x_dpll_phase_offset_avg_factor_get(const struct dpll_device *dpll, return 0; } -static void -zl3073x_dpll_change_work(struct work_struct *work) -{ - struct zl3073x_dpll *zldpll; - - zldpll = container_of(work, struct zl3073x_dpll, change_work); - dpll_device_change_ntf(zldpll->dpll_dev); -} - static int zl3073x_dpll_phase_offset_avg_factor_set(const struct dpll_device *dpll, void *dpll_priv, u32 factor, @@ -1113,8 +1104,10 @@ zl3073x_dpll_phase_offset_avg_factor_set(const struct dpll_device *dpll, * we have to send a notification for other DPLL devices. */ list_for_each_entry(item, &zldpll->dev->dplls, list) { - if (item != zldpll) - schedule_work(&item->change_work); + struct dpll_device *dpll_dev = READ_ONCE(item->dpll_dev); + + if (item != zldpll && dpll_dev) + __dpll_device_change_ntf(dpll_dev); } return 0; @@ -1219,7 +1212,7 @@ zl3073x_dpll_freq_monitor_get(const struct dpll_device *dpll, { struct zl3073x_dpll *zldpll = dpll_priv; - if (zldpll->freq_monitor) + if (zldpll->dev->freq_monitor) *state = DPLL_FEATURE_STATE_ENABLE; else *state = DPLL_FEATURE_STATE_DISABLE; @@ -1233,9 +1226,19 @@ zl3073x_dpll_freq_monitor_set(const struct dpll_device *dpll, enum dpll_feature_state state, struct netlink_ext_ack *extack) { - struct zl3073x_dpll *zldpll = dpll_priv; + struct zl3073x_dpll *item, *zldpll = dpll_priv; - zldpll->freq_monitor = (state == DPLL_FEATURE_STATE_ENABLE); + zldpll->dev->freq_monitor = (state == DPLL_FEATURE_STATE_ENABLE); + + /* The frequency monitoring is common for all DPLL channels so after + * change we have to send a notification for other DPLL devices. + */ + list_for_each_entry(item, &zldpll->dev->dplls, list) { + struct dpll_device *dpll_dev = READ_ONCE(item->dpll_dev); + + if (item != zldpll && dpll_dev) + __dpll_device_change_ntf(dpll_dev); + } return 0; } @@ -1627,13 +1630,13 @@ zl3073x_dpll_device_register(struct zl3073x_dpll *zldpll) static void zl3073x_dpll_device_unregister(struct zl3073x_dpll *zldpll) { - WARN(!zldpll->dpll_dev, "DPLL device is not registered\n"); + struct dpll_device *dpll_dev = READ_ONCE(zldpll->dpll_dev); - cancel_work_sync(&zldpll->change_work); + WARN(!dpll_dev, "DPLL device is not registered\n"); - dpll_device_unregister(zldpll->dpll_dev, &zldpll->ops, zldpll); - dpll_device_put(zldpll->dpll_dev, &zldpll->tracker); - zldpll->dpll_dev = NULL; + WRITE_ONCE(zldpll->dpll_dev, NULL); + dpll_device_unregister(dpll_dev, &zldpll->ops, zldpll); + dpll_device_put(dpll_dev, &zldpll->tracker); } /** @@ -1752,7 +1755,7 @@ zl3073x_dpll_pin_measured_freq_check(struct zl3073x_dpll_pin *pin) u8 ref_id; u32 freq; - if (!zldpll->freq_monitor) + if (!zldpll->dev->freq_monitor) return false; ref_id = zl3073x_input_pin_ref_get(pin->id); @@ -1785,10 +1788,8 @@ zl3073x_dpll_changes_check(struct zl3073x_dpll *zldpll) struct zl3073x_dev *zldev = zldpll->dev; enum dpll_lock_status lock_status; struct device *dev = zldev->dev; - const struct zl3073x_chan *chan; struct zl3073x_dpll_pin *pin; int rc; - u8 mode; zldpll->check_count++; @@ -1807,15 +1808,6 @@ zl3073x_dpll_changes_check(struct zl3073x_dpll *zldpll) dpll_device_change_ntf(zldpll->dpll_dev); } - /* Input pin monitoring does make sense only in automatic - * or forced reference modes. - */ - chan = zl3073x_chan_state_get(zldev, zldpll->id); - mode = zl3073x_chan_mode_get(chan); - if (mode != ZL_DPLL_MODE_REFSEL_MODE_AUTO && - mode != ZL_DPLL_MODE_REFSEL_MODE_REFLOCK) - return; - /* Update phase offset latch registers for this DPLL if the phase * offset monitor feature is enabled. */ @@ -1926,7 +1918,6 @@ zl3073x_dpll_alloc(struct zl3073x_dev *zldev, u8 ch) zldpll->dev = zldev; zldpll->id = ch; INIT_LIST_HEAD(&zldpll->pins); - INIT_WORK(&zldpll->change_work, zl3073x_dpll_change_work); return zldpll; } diff --git a/drivers/dpll/zl3073x/dpll.h b/drivers/dpll/zl3073x/dpll.h index 434c32a7db12..21adcc18e45e 100644 --- a/drivers/dpll/zl3073x/dpll.h +++ b/drivers/dpll/zl3073x/dpll.h @@ -15,13 +15,11 @@ * @id: DPLL index * @check_count: periodic check counter * @phase_monitor: is phase offset monitor enabled - * @freq_monitor: is frequency monitor enabled * @ops: DPLL device operations for this instance * @dpll_dev: pointer to registered DPLL device * @tracker: tracking object for the acquired reference * @lock_status: last saved DPLL lock status * @pins: list of pins - * @change_work: device change notification work */ struct zl3073x_dpll { struct list_head list; @@ -29,13 +27,11 @@ struct zl3073x_dpll { u8 id; u8 check_count; bool phase_monitor; - bool freq_monitor; struct dpll_device_ops ops; struct dpll_device *dpll_dev; dpll_tracker tracker; enum dpll_lock_status lock_status; struct list_head pins; - struct work_struct change_work; }; struct zl3073x_dpll *zl3073x_dpll_alloc(struct zl3073x_dev *zldev, u8 ch); diff --git a/include/linux/dpll.h b/include/linux/dpll.h index f8037f1ab20b..2dbe8567eafc 100644 --- a/include/linux/dpll.h +++ b/include/linux/dpll.h @@ -284,6 +284,7 @@ void dpll_pin_on_pin_unregister(struct dpll_pin *parent, struct dpll_pin *pin, int dpll_pin_ref_sync_pair_add(struct dpll_pin *pin, struct dpll_pin *ref_sync_pin); +int __dpll_device_change_ntf(struct dpll_device *dpll); int dpll_device_change_ntf(struct dpll_device *dpll); int __dpll_pin_change_ntf(struct dpll_pin *pin);