1  // SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
2  /* Copyright (c) 2023, NVIDIA CORPORATION & AFFILIATES. All rights reserved. */
3  
4  #include <linux/dpll.h>
5  #include <linux/mlx5/driver.h>
6  
7  /* This structure represents a reference to DPLL, one is created
8   * per mdev instance.
9   */
10  struct mlx5_dpll {
11  	struct dpll_device *dpll;
12  	struct dpll_pin *dpll_pin;
13  	struct mlx5_core_dev *mdev;
14  	struct workqueue_struct *wq;
15  	struct delayed_work work;
16  	struct {
17  		bool valid;
18  		enum dpll_lock_status lock_status;
19  		enum dpll_pin_state pin_state;
20  	} last;
21  	struct notifier_block mdev_nb;
22  	struct net_device *tracking_netdev;
23  };
24  
mlx5_dpll_clock_id_get(struct mlx5_core_dev * mdev,u64 * clock_id)25  static int mlx5_dpll_clock_id_get(struct mlx5_core_dev *mdev, u64 *clock_id)
26  {
27  	u32 out[MLX5_ST_SZ_DW(msecq_reg)] = {};
28  	u32 in[MLX5_ST_SZ_DW(msecq_reg)] = {};
29  	int err;
30  
31  	err = mlx5_core_access_reg(mdev, in, sizeof(in), out, sizeof(out),
32  				   MLX5_REG_MSECQ, 0, 0);
33  	if (err)
34  		return err;
35  	*clock_id = MLX5_GET64(msecq_reg, out, local_clock_identity);
36  	return 0;
37  }
38  
39  struct mlx5_dpll_synce_status {
40  	enum mlx5_msees_admin_status admin_status;
41  	enum mlx5_msees_oper_status oper_status;
42  	bool ho_acq;
43  	bool oper_freq_measure;
44  	enum mlx5_msees_failure_reason failure_reason;
45  	s32 frequency_diff;
46  };
47  
48  static int
mlx5_dpll_synce_status_get(struct mlx5_core_dev * mdev,struct mlx5_dpll_synce_status * synce_status)49  mlx5_dpll_synce_status_get(struct mlx5_core_dev *mdev,
50  			   struct mlx5_dpll_synce_status *synce_status)
51  {
52  	u32 out[MLX5_ST_SZ_DW(msees_reg)] = {};
53  	u32 in[MLX5_ST_SZ_DW(msees_reg)] = {};
54  	int err;
55  
56  	err = mlx5_core_access_reg(mdev, in, sizeof(in), out, sizeof(out),
57  				   MLX5_REG_MSEES, 0, 0);
58  	if (err)
59  		return err;
60  	synce_status->admin_status = MLX5_GET(msees_reg, out, admin_status);
61  	synce_status->oper_status = MLX5_GET(msees_reg, out, oper_status);
62  	synce_status->ho_acq = MLX5_GET(msees_reg, out, ho_acq);
63  	synce_status->oper_freq_measure = MLX5_GET(msees_reg, out, oper_freq_measure);
64  	synce_status->failure_reason = MLX5_GET(msees_reg, out, failure_reason);
65  	synce_status->frequency_diff = MLX5_GET(msees_reg, out, frequency_diff);
66  	return 0;
67  }
68  
69  static int
mlx5_dpll_synce_status_set(struct mlx5_core_dev * mdev,enum mlx5_msees_admin_status admin_status)70  mlx5_dpll_synce_status_set(struct mlx5_core_dev *mdev,
71  			   enum mlx5_msees_admin_status admin_status)
72  {
73  	u32 out[MLX5_ST_SZ_DW(msees_reg)] = {};
74  	u32 in[MLX5_ST_SZ_DW(msees_reg)] = {};
75  
76  	MLX5_SET(msees_reg, in, field_select,
77  		 MLX5_MSEES_FIELD_SELECT_ENABLE |
78  		 MLX5_MSEES_FIELD_SELECT_ADMIN_FREQ_MEASURE |
79  		 MLX5_MSEES_FIELD_SELECT_ADMIN_STATUS);
80  	MLX5_SET(msees_reg, in, admin_status, admin_status);
81  	MLX5_SET(msees_reg, in, admin_freq_measure, true);
82  	return mlx5_core_access_reg(mdev, in, sizeof(in), out, sizeof(out),
83  				    MLX5_REG_MSEES, 0, 1);
84  }
85  
86  static enum dpll_lock_status
mlx5_dpll_lock_status_get(struct mlx5_dpll_synce_status * synce_status)87  mlx5_dpll_lock_status_get(struct mlx5_dpll_synce_status *synce_status)
88  {
89  	switch (synce_status->oper_status) {
90  	case MLX5_MSEES_OPER_STATUS_SELF_TRACK:
91  		fallthrough;
92  	case MLX5_MSEES_OPER_STATUS_OTHER_TRACK:
93  		return synce_status->ho_acq ? DPLL_LOCK_STATUS_LOCKED_HO_ACQ :
94  					      DPLL_LOCK_STATUS_LOCKED;
95  	case MLX5_MSEES_OPER_STATUS_HOLDOVER:
96  		fallthrough;
97  	case MLX5_MSEES_OPER_STATUS_FAIL_HOLDOVER:
98  		return DPLL_LOCK_STATUS_HOLDOVER;
99  	default:
100  		return DPLL_LOCK_STATUS_UNLOCKED;
101  	}
102  }
103  
104  static enum dpll_lock_status_error
mlx5_dpll_lock_status_error_get(struct mlx5_dpll_synce_status * synce_status)105  mlx5_dpll_lock_status_error_get(struct mlx5_dpll_synce_status *synce_status)
106  {
107  	switch (synce_status->oper_status) {
108  	case MLX5_MSEES_OPER_STATUS_FAIL_HOLDOVER:
109  		fallthrough;
110  	case MLX5_MSEES_OPER_STATUS_FAIL_FREE_RUNNING:
111  		switch (synce_status->failure_reason) {
112  		case MLX5_MSEES_FAILURE_REASON_PORT_DOWN:
113  			return DPLL_LOCK_STATUS_ERROR_MEDIA_DOWN;
114  		case MLX5_MSEES_FAILURE_REASON_TOO_HIGH_FREQUENCY_DIFF:
115  			return DPLL_LOCK_STATUS_ERROR_FRACTIONAL_FREQUENCY_OFFSET_TOO_HIGH;
116  		default:
117  			return DPLL_LOCK_STATUS_ERROR_UNDEFINED;
118  		}
119  	default:
120  		return DPLL_LOCK_STATUS_ERROR_NONE;
121  	}
122  }
123  
124  static enum dpll_pin_state
mlx5_dpll_pin_state_get(struct mlx5_dpll_synce_status * synce_status)125  mlx5_dpll_pin_state_get(struct mlx5_dpll_synce_status *synce_status)
126  {
127  	return (synce_status->admin_status == MLX5_MSEES_ADMIN_STATUS_TRACK &&
128  		(synce_status->oper_status == MLX5_MSEES_OPER_STATUS_SELF_TRACK ||
129  		 synce_status->oper_status == MLX5_MSEES_OPER_STATUS_OTHER_TRACK)) ?
130  	       DPLL_PIN_STATE_CONNECTED : DPLL_PIN_STATE_DISCONNECTED;
131  }
132  
133  static int
mlx5_dpll_pin_ffo_get(struct mlx5_dpll_synce_status * synce_status,s64 * ffo)134  mlx5_dpll_pin_ffo_get(struct mlx5_dpll_synce_status *synce_status,
135  		      s64 *ffo)
136  {
137  	if (!synce_status->oper_freq_measure)
138  		return -ENODATA;
139  	*ffo = synce_status->frequency_diff;
140  	return 0;
141  }
142  
143  static int
mlx5_dpll_device_lock_status_get(const struct dpll_device * dpll,void * priv,enum dpll_lock_status * status,enum dpll_lock_status_error * status_error,struct netlink_ext_ack * extack)144  mlx5_dpll_device_lock_status_get(const struct dpll_device *dpll, void *priv,
145  				 enum dpll_lock_status *status,
146  				 enum dpll_lock_status_error *status_error,
147  				 struct netlink_ext_ack *extack)
148  {
149  	struct mlx5_dpll_synce_status synce_status;
150  	struct mlx5_dpll *mdpll = priv;
151  	int err;
152  
153  	err = mlx5_dpll_synce_status_get(mdpll->mdev, &synce_status);
154  	if (err)
155  		return err;
156  	*status = mlx5_dpll_lock_status_get(&synce_status);
157  	*status_error = mlx5_dpll_lock_status_error_get(&synce_status);
158  	return 0;
159  }
160  
mlx5_dpll_device_mode_get(const struct dpll_device * dpll,void * priv,enum dpll_mode * mode,struct netlink_ext_ack * extack)161  static int mlx5_dpll_device_mode_get(const struct dpll_device *dpll,
162  				     void *priv, enum dpll_mode *mode,
163  				     struct netlink_ext_ack *extack)
164  {
165  	*mode = DPLL_MODE_MANUAL;
166  	return 0;
167  }
168  
169  static const struct dpll_device_ops mlx5_dpll_device_ops = {
170  	.lock_status_get = mlx5_dpll_device_lock_status_get,
171  	.mode_get = mlx5_dpll_device_mode_get,
172  };
173  
mlx5_dpll_pin_direction_get(const struct dpll_pin * pin,void * pin_priv,const struct dpll_device * dpll,void * dpll_priv,enum dpll_pin_direction * direction,struct netlink_ext_ack * extack)174  static int mlx5_dpll_pin_direction_get(const struct dpll_pin *pin,
175  				       void *pin_priv,
176  				       const struct dpll_device *dpll,
177  				       void *dpll_priv,
178  				       enum dpll_pin_direction *direction,
179  				       struct netlink_ext_ack *extack)
180  {
181  	*direction = DPLL_PIN_DIRECTION_INPUT;
182  	return 0;
183  }
184  
mlx5_dpll_state_on_dpll_get(const struct dpll_pin * pin,void * pin_priv,const struct dpll_device * dpll,void * dpll_priv,enum dpll_pin_state * state,struct netlink_ext_ack * extack)185  static int mlx5_dpll_state_on_dpll_get(const struct dpll_pin *pin,
186  				       void *pin_priv,
187  				       const struct dpll_device *dpll,
188  				       void *dpll_priv,
189  				       enum dpll_pin_state *state,
190  				       struct netlink_ext_ack *extack)
191  {
192  	struct mlx5_dpll_synce_status synce_status;
193  	struct mlx5_dpll *mdpll = pin_priv;
194  	int err;
195  
196  	err = mlx5_dpll_synce_status_get(mdpll->mdev, &synce_status);
197  	if (err)
198  		return err;
199  	*state = mlx5_dpll_pin_state_get(&synce_status);
200  	return 0;
201  }
202  
mlx5_dpll_state_on_dpll_set(const struct dpll_pin * pin,void * pin_priv,const struct dpll_device * dpll,void * dpll_priv,enum dpll_pin_state state,struct netlink_ext_ack * extack)203  static int mlx5_dpll_state_on_dpll_set(const struct dpll_pin *pin,
204  				       void *pin_priv,
205  				       const struct dpll_device *dpll,
206  				       void *dpll_priv,
207  				       enum dpll_pin_state state,
208  				       struct netlink_ext_ack *extack)
209  {
210  	struct mlx5_dpll *mdpll = pin_priv;
211  
212  	return mlx5_dpll_synce_status_set(mdpll->mdev,
213  					  state == DPLL_PIN_STATE_CONNECTED ?
214  					  MLX5_MSEES_ADMIN_STATUS_TRACK :
215  					  MLX5_MSEES_ADMIN_STATUS_FREE_RUNNING);
216  }
217  
mlx5_dpll_ffo_get(const struct dpll_pin * pin,void * pin_priv,const struct dpll_device * dpll,void * dpll_priv,s64 * ffo,struct netlink_ext_ack * extack)218  static int mlx5_dpll_ffo_get(const struct dpll_pin *pin, void *pin_priv,
219  			     const struct dpll_device *dpll, void *dpll_priv,
220  			     s64 *ffo, struct netlink_ext_ack *extack)
221  {
222  	struct mlx5_dpll_synce_status synce_status;
223  	struct mlx5_dpll *mdpll = pin_priv;
224  	int err;
225  
226  	err = mlx5_dpll_synce_status_get(mdpll->mdev, &synce_status);
227  	if (err)
228  		return err;
229  	return mlx5_dpll_pin_ffo_get(&synce_status, ffo);
230  }
231  
232  static const struct dpll_pin_ops mlx5_dpll_pins_ops = {
233  	.direction_get = mlx5_dpll_pin_direction_get,
234  	.state_on_dpll_get = mlx5_dpll_state_on_dpll_get,
235  	.state_on_dpll_set = mlx5_dpll_state_on_dpll_set,
236  	.ffo_get = mlx5_dpll_ffo_get,
237  };
238  
239  static const struct dpll_pin_properties mlx5_dpll_pin_properties = {
240  	.type = DPLL_PIN_TYPE_SYNCE_ETH_PORT,
241  	.capabilities = DPLL_PIN_CAPABILITIES_STATE_CAN_CHANGE,
242  };
243  
244  #define MLX5_DPLL_PERIODIC_WORK_INTERVAL 500 /* ms */
245  
mlx5_dpll_periodic_work_queue(struct mlx5_dpll * mdpll)246  static void mlx5_dpll_periodic_work_queue(struct mlx5_dpll *mdpll)
247  {
248  	queue_delayed_work(mdpll->wq, &mdpll->work,
249  			   msecs_to_jiffies(MLX5_DPLL_PERIODIC_WORK_INTERVAL));
250  }
251  
mlx5_dpll_periodic_work(struct work_struct * work)252  static void mlx5_dpll_periodic_work(struct work_struct *work)
253  {
254  	struct mlx5_dpll *mdpll = container_of(work, struct mlx5_dpll,
255  					       work.work);
256  	struct mlx5_dpll_synce_status synce_status;
257  	enum dpll_lock_status lock_status;
258  	enum dpll_pin_state pin_state;
259  	int err;
260  
261  	err = mlx5_dpll_synce_status_get(mdpll->mdev, &synce_status);
262  	if (err)
263  		goto err_out;
264  	lock_status = mlx5_dpll_lock_status_get(&synce_status);
265  	pin_state = mlx5_dpll_pin_state_get(&synce_status);
266  
267  	if (!mdpll->last.valid)
268  		goto invalid_out;
269  
270  	if (mdpll->last.lock_status != lock_status)
271  		dpll_device_change_ntf(mdpll->dpll);
272  	if (mdpll->last.pin_state != pin_state)
273  		dpll_pin_change_ntf(mdpll->dpll_pin);
274  
275  invalid_out:
276  	mdpll->last.lock_status = lock_status;
277  	mdpll->last.pin_state = pin_state;
278  	mdpll->last.valid = true;
279  err_out:
280  	mlx5_dpll_periodic_work_queue(mdpll);
281  }
282  
mlx5_dpll_netdev_dpll_pin_set(struct mlx5_dpll * mdpll,struct net_device * netdev)283  static void mlx5_dpll_netdev_dpll_pin_set(struct mlx5_dpll *mdpll,
284  					  struct net_device *netdev)
285  {
286  	if (mdpll->tracking_netdev)
287  		return;
288  	dpll_netdev_pin_set(netdev, mdpll->dpll_pin);
289  	mdpll->tracking_netdev = netdev;
290  }
291  
mlx5_dpll_netdev_dpll_pin_clear(struct mlx5_dpll * mdpll)292  static void mlx5_dpll_netdev_dpll_pin_clear(struct mlx5_dpll *mdpll)
293  {
294  	if (!mdpll->tracking_netdev)
295  		return;
296  	dpll_netdev_pin_clear(mdpll->tracking_netdev);
297  	mdpll->tracking_netdev = NULL;
298  }
299  
mlx5_dpll_mdev_notifier_event(struct notifier_block * nb,unsigned long event,void * data)300  static int mlx5_dpll_mdev_notifier_event(struct notifier_block *nb,
301  					 unsigned long event, void *data)
302  {
303  	struct mlx5_dpll *mdpll = container_of(nb, struct mlx5_dpll, mdev_nb);
304  	struct net_device *netdev = data;
305  
306  	switch (event) {
307  	case MLX5_DRIVER_EVENT_UPLINK_NETDEV:
308  		if (netdev)
309  			mlx5_dpll_netdev_dpll_pin_set(mdpll, netdev);
310  		else
311  			mlx5_dpll_netdev_dpll_pin_clear(mdpll);
312  		break;
313  	default:
314  		return NOTIFY_DONE;
315  	}
316  
317  	return NOTIFY_OK;
318  }
319  
mlx5_dpll_mdev_netdev_track(struct mlx5_dpll * mdpll,struct mlx5_core_dev * mdev)320  static void mlx5_dpll_mdev_netdev_track(struct mlx5_dpll *mdpll,
321  					struct mlx5_core_dev *mdev)
322  {
323  	mdpll->mdev_nb.notifier_call = mlx5_dpll_mdev_notifier_event;
324  	mlx5_blocking_notifier_register(mdev, &mdpll->mdev_nb);
325  	mlx5_core_uplink_netdev_event_replay(mdev);
326  }
327  
mlx5_dpll_mdev_netdev_untrack(struct mlx5_dpll * mdpll,struct mlx5_core_dev * mdev)328  static void mlx5_dpll_mdev_netdev_untrack(struct mlx5_dpll *mdpll,
329  					  struct mlx5_core_dev *mdev)
330  {
331  	mlx5_blocking_notifier_unregister(mdev, &mdpll->mdev_nb);
332  	mlx5_dpll_netdev_dpll_pin_clear(mdpll);
333  }
334  
mlx5_dpll_probe(struct auxiliary_device * adev,const struct auxiliary_device_id * id)335  static int mlx5_dpll_probe(struct auxiliary_device *adev,
336  			   const struct auxiliary_device_id *id)
337  {
338  	struct mlx5_adev *edev = container_of(adev, struct mlx5_adev, adev);
339  	struct mlx5_core_dev *mdev = edev->mdev;
340  	struct mlx5_dpll *mdpll;
341  	u64 clock_id;
342  	int err;
343  
344  	err = mlx5_dpll_synce_status_set(mdev,
345  					 MLX5_MSEES_ADMIN_STATUS_FREE_RUNNING);
346  	if (err)
347  		return err;
348  
349  	err = mlx5_dpll_clock_id_get(mdev, &clock_id);
350  	if (err)
351  		return err;
352  
353  	mdpll = kzalloc(sizeof(*mdpll), GFP_KERNEL);
354  	if (!mdpll)
355  		return -ENOMEM;
356  	mdpll->mdev = mdev;
357  	auxiliary_set_drvdata(adev, mdpll);
358  
359  	/* Multiple mdev instances might share one DPLL device. */
360  	mdpll->dpll = dpll_device_get(clock_id, 0, THIS_MODULE);
361  	if (IS_ERR(mdpll->dpll)) {
362  		err = PTR_ERR(mdpll->dpll);
363  		goto err_free_mdpll;
364  	}
365  
366  	err = dpll_device_register(mdpll->dpll, DPLL_TYPE_EEC,
367  				   &mlx5_dpll_device_ops, mdpll);
368  	if (err)
369  		goto err_put_dpll_device;
370  
371  	/* Multiple mdev instances might share one DPLL pin. */
372  	mdpll->dpll_pin = dpll_pin_get(clock_id, mlx5_get_dev_index(mdev),
373  				       THIS_MODULE, &mlx5_dpll_pin_properties);
374  	if (IS_ERR(mdpll->dpll_pin)) {
375  		err = PTR_ERR(mdpll->dpll_pin);
376  		goto err_unregister_dpll_device;
377  	}
378  
379  	err = dpll_pin_register(mdpll->dpll, mdpll->dpll_pin,
380  				&mlx5_dpll_pins_ops, mdpll);
381  	if (err)
382  		goto err_put_dpll_pin;
383  
384  	mdpll->wq = create_singlethread_workqueue("mlx5_dpll");
385  	if (!mdpll->wq) {
386  		err = -ENOMEM;
387  		goto err_unregister_dpll_pin;
388  	}
389  
390  	mlx5_dpll_mdev_netdev_track(mdpll, mdev);
391  
392  	INIT_DELAYED_WORK(&mdpll->work, &mlx5_dpll_periodic_work);
393  	mlx5_dpll_periodic_work_queue(mdpll);
394  
395  	return 0;
396  
397  err_unregister_dpll_pin:
398  	dpll_pin_unregister(mdpll->dpll, mdpll->dpll_pin,
399  			    &mlx5_dpll_pins_ops, mdpll);
400  err_put_dpll_pin:
401  	dpll_pin_put(mdpll->dpll_pin);
402  err_unregister_dpll_device:
403  	dpll_device_unregister(mdpll->dpll, &mlx5_dpll_device_ops, mdpll);
404  err_put_dpll_device:
405  	dpll_device_put(mdpll->dpll);
406  err_free_mdpll:
407  	kfree(mdpll);
408  	return err;
409  }
410  
mlx5_dpll_remove(struct auxiliary_device * adev)411  static void mlx5_dpll_remove(struct auxiliary_device *adev)
412  {
413  	struct mlx5_dpll *mdpll = auxiliary_get_drvdata(adev);
414  	struct mlx5_core_dev *mdev = mdpll->mdev;
415  
416  	cancel_delayed_work_sync(&mdpll->work);
417  	mlx5_dpll_mdev_netdev_untrack(mdpll, mdev);
418  	destroy_workqueue(mdpll->wq);
419  	dpll_pin_unregister(mdpll->dpll, mdpll->dpll_pin,
420  			    &mlx5_dpll_pins_ops, mdpll);
421  	dpll_pin_put(mdpll->dpll_pin);
422  	dpll_device_unregister(mdpll->dpll, &mlx5_dpll_device_ops, mdpll);
423  	dpll_device_put(mdpll->dpll);
424  	kfree(mdpll);
425  
426  	mlx5_dpll_synce_status_set(mdev,
427  				   MLX5_MSEES_ADMIN_STATUS_FREE_RUNNING);
428  }
429  
mlx5_dpll_suspend(struct auxiliary_device * adev,pm_message_t state)430  static int mlx5_dpll_suspend(struct auxiliary_device *adev, pm_message_t state)
431  {
432  	return 0;
433  }
434  
mlx5_dpll_resume(struct auxiliary_device * adev)435  static int mlx5_dpll_resume(struct auxiliary_device *adev)
436  {
437  	return 0;
438  }
439  
440  static const struct auxiliary_device_id mlx5_dpll_id_table[] = {
441  	{ .name = MLX5_ADEV_NAME ".dpll", },
442  	{},
443  };
444  
445  MODULE_DEVICE_TABLE(auxiliary, mlx5_dpll_id_table);
446  
447  static struct auxiliary_driver mlx5_dpll_driver = {
448  	.name = "dpll",
449  	.probe = mlx5_dpll_probe,
450  	.remove = mlx5_dpll_remove,
451  	.suspend = mlx5_dpll_suspend,
452  	.resume = mlx5_dpll_resume,
453  	.id_table = mlx5_dpll_id_table,
454  };
455  
mlx5_dpll_init(void)456  static int __init mlx5_dpll_init(void)
457  {
458  	return auxiliary_driver_register(&mlx5_dpll_driver);
459  }
460  
mlx5_dpll_exit(void)461  static void __exit mlx5_dpll_exit(void)
462  {
463  	auxiliary_driver_unregister(&mlx5_dpll_driver);
464  }
465  
466  module_init(mlx5_dpll_init);
467  module_exit(mlx5_dpll_exit);
468  
469  MODULE_AUTHOR("Jiri Pirko <jiri@nvidia.com>");
470  MODULE_DESCRIPTION("Mellanox 5th generation network adapters (ConnectX series) DPLL driver");
471  MODULE_LICENSE("Dual BSD/GPL");
472