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