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 dpll_tracker dpll_tracker;
13 struct dpll_pin *dpll_pin;
14 dpll_tracker pin_tracker;
15 struct mlx5_core_dev *mdev;
16 struct workqueue_struct *wq;
17 struct delayed_work work;
18 struct {
19 bool valid;
20 enum dpll_lock_status lock_status;
21 enum dpll_pin_state pin_state;
22 } last;
23 struct notifier_block mdev_nb;
24 struct net_device *tracking_netdev;
25 };
26
mlx5_dpll_clock_id_get(struct mlx5_core_dev * mdev,u64 * clock_id)27 static int mlx5_dpll_clock_id_get(struct mlx5_core_dev *mdev, u64 *clock_id)
28 {
29 u32 out[MLX5_ST_SZ_DW(msecq_reg)] = {};
30 u32 in[MLX5_ST_SZ_DW(msecq_reg)] = {};
31 int err;
32
33 err = mlx5_core_access_reg(mdev, in, sizeof(in), out, sizeof(out),
34 MLX5_REG_MSECQ, 0, 0);
35 if (err)
36 return err;
37 *clock_id = MLX5_GET64(msecq_reg, out, local_clock_identity);
38 return 0;
39 }
40
41 struct mlx5_dpll_synce_status {
42 enum mlx5_msees_admin_status admin_status;
43 enum mlx5_msees_oper_status oper_status;
44 bool ho_acq;
45 bool oper_freq_measure;
46 enum mlx5_msees_failure_reason failure_reason;
47 s32 frequency_diff;
48 };
49
50 static int
mlx5_dpll_synce_status_get(struct mlx5_core_dev * mdev,struct mlx5_dpll_synce_status * synce_status)51 mlx5_dpll_synce_status_get(struct mlx5_core_dev *mdev,
52 struct mlx5_dpll_synce_status *synce_status)
53 {
54 u32 out[MLX5_ST_SZ_DW(msees_reg)] = {};
55 u32 in[MLX5_ST_SZ_DW(msees_reg)] = {};
56 int err;
57
58 err = mlx5_core_access_reg(mdev, in, sizeof(in), out, sizeof(out),
59 MLX5_REG_MSEES, 0, 0);
60 if (err)
61 return err;
62 synce_status->admin_status = MLX5_GET(msees_reg, out, admin_status);
63 synce_status->oper_status = MLX5_GET(msees_reg, out, oper_status);
64 synce_status->ho_acq = MLX5_GET(msees_reg, out, ho_acq);
65 synce_status->oper_freq_measure = MLX5_GET(msees_reg, out, oper_freq_measure);
66 synce_status->failure_reason = MLX5_GET(msees_reg, out, failure_reason);
67 synce_status->frequency_diff = MLX5_GET(msees_reg, out, frequency_diff);
68 return 0;
69 }
70
71 static int
mlx5_dpll_synce_status_set(struct mlx5_core_dev * mdev,enum mlx5_msees_admin_status admin_status)72 mlx5_dpll_synce_status_set(struct mlx5_core_dev *mdev,
73 enum mlx5_msees_admin_status admin_status)
74 {
75 u32 out[MLX5_ST_SZ_DW(msees_reg)] = {};
76 u32 in[MLX5_ST_SZ_DW(msees_reg)] = {};
77
78 MLX5_SET(msees_reg, in, field_select,
79 MLX5_MSEES_FIELD_SELECT_ENABLE |
80 MLX5_MSEES_FIELD_SELECT_ADMIN_FREQ_MEASURE |
81 MLX5_MSEES_FIELD_SELECT_ADMIN_STATUS);
82 MLX5_SET(msees_reg, in, admin_status, admin_status);
83 MLX5_SET(msees_reg, in, admin_freq_measure, true);
84 return mlx5_core_access_reg(mdev, in, sizeof(in), out, sizeof(out),
85 MLX5_REG_MSEES, 0, 1);
86 }
87
88 static enum dpll_lock_status
mlx5_dpll_lock_status_get(struct mlx5_dpll_synce_status * synce_status)89 mlx5_dpll_lock_status_get(struct mlx5_dpll_synce_status *synce_status)
90 {
91 switch (synce_status->oper_status) {
92 case MLX5_MSEES_OPER_STATUS_SELF_TRACK:
93 fallthrough;
94 case MLX5_MSEES_OPER_STATUS_OTHER_TRACK:
95 return synce_status->ho_acq ? DPLL_LOCK_STATUS_LOCKED_HO_ACQ :
96 DPLL_LOCK_STATUS_LOCKED;
97 case MLX5_MSEES_OPER_STATUS_HOLDOVER:
98 fallthrough;
99 case MLX5_MSEES_OPER_STATUS_FAIL_HOLDOVER:
100 return DPLL_LOCK_STATUS_HOLDOVER;
101 default:
102 return DPLL_LOCK_STATUS_UNLOCKED;
103 }
104 }
105
106 static enum dpll_lock_status_error
mlx5_dpll_lock_status_error_get(struct mlx5_dpll_synce_status * synce_status)107 mlx5_dpll_lock_status_error_get(struct mlx5_dpll_synce_status *synce_status)
108 {
109 switch (synce_status->oper_status) {
110 case MLX5_MSEES_OPER_STATUS_FAIL_HOLDOVER:
111 fallthrough;
112 case MLX5_MSEES_OPER_STATUS_FAIL_FREE_RUNNING:
113 switch (synce_status->failure_reason) {
114 case MLX5_MSEES_FAILURE_REASON_PORT_DOWN:
115 return DPLL_LOCK_STATUS_ERROR_MEDIA_DOWN;
116 case MLX5_MSEES_FAILURE_REASON_TOO_HIGH_FREQUENCY_DIFF:
117 return DPLL_LOCK_STATUS_ERROR_FRACTIONAL_FREQUENCY_OFFSET_TOO_HIGH;
118 default:
119 return DPLL_LOCK_STATUS_ERROR_UNDEFINED;
120 }
121 default:
122 return DPLL_LOCK_STATUS_ERROR_NONE;
123 }
124 }
125
126 static enum dpll_pin_state
mlx5_dpll_pin_state_get(struct mlx5_dpll_synce_status * synce_status)127 mlx5_dpll_pin_state_get(struct mlx5_dpll_synce_status *synce_status)
128 {
129 return (synce_status->admin_status == MLX5_MSEES_ADMIN_STATUS_TRACK &&
130 (synce_status->oper_status == MLX5_MSEES_OPER_STATUS_SELF_TRACK ||
131 synce_status->oper_status == MLX5_MSEES_OPER_STATUS_OTHER_TRACK)) ?
132 DPLL_PIN_STATE_CONNECTED : DPLL_PIN_STATE_DISCONNECTED;
133 }
134
135 static int
mlx5_dpll_pin_ffo_get(struct mlx5_dpll_synce_status * synce_status,s64 * ffo)136 mlx5_dpll_pin_ffo_get(struct mlx5_dpll_synce_status *synce_status,
137 s64 *ffo)
138 {
139 if (!synce_status->oper_freq_measure)
140 return -ENODATA;
141 *ffo = 1000000LL * synce_status->frequency_diff;
142 return 0;
143 }
144
145 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)146 mlx5_dpll_device_lock_status_get(const struct dpll_device *dpll, void *priv,
147 enum dpll_lock_status *status,
148 enum dpll_lock_status_error *status_error,
149 struct netlink_ext_ack *extack)
150 {
151 struct mlx5_dpll_synce_status synce_status;
152 struct mlx5_dpll *mdpll = priv;
153 int err;
154
155 err = mlx5_dpll_synce_status_get(mdpll->mdev, &synce_status);
156 if (err)
157 return err;
158 *status = mlx5_dpll_lock_status_get(&synce_status);
159 *status_error = mlx5_dpll_lock_status_error_get(&synce_status);
160 return 0;
161 }
162
mlx5_dpll_device_mode_get(const struct dpll_device * dpll,void * priv,enum dpll_mode * mode,struct netlink_ext_ack * extack)163 static int mlx5_dpll_device_mode_get(const struct dpll_device *dpll,
164 void *priv, enum dpll_mode *mode,
165 struct netlink_ext_ack *extack)
166 {
167 *mode = DPLL_MODE_MANUAL;
168 return 0;
169 }
170
171 enum {
172 MLX5_DPLL_SSM_CODE_PRC = 0b0010,
173 MLX5_DPLL_SSM_CODE_SSU_A = 0b0100,
174 MLX5_DPLL_SSM_CODE_SSU_B = 0b1000,
175 MLX5_DPLL_SSM_CODE_EEC1 = 0b1011,
176 MLX5_DPLL_SSM_CODE_PRTC = 0b0010,
177 MLX5_DPLL_SSM_CODE_EPRTC = 0b0010,
178 MLX5_DPLL_SSM_CODE_EEEC = 0b1011,
179 MLX5_DPLL_SSM_CODE_EPRC = 0b0010,
180 };
181
182 enum {
183 MLX5_DPLL_ENHANCED_SSM_CODE_PRC = 0xff,
184 MLX5_DPLL_ENHANCED_SSM_CODE_SSU_A = 0xff,
185 MLX5_DPLL_ENHANCED_SSM_CODE_SSU_B = 0xff,
186 MLX5_DPLL_ENHANCED_SSM_CODE_EEC1 = 0xff,
187 MLX5_DPLL_ENHANCED_SSM_CODE_PRTC = 0x20,
188 MLX5_DPLL_ENHANCED_SSM_CODE_EPRTC = 0x21,
189 MLX5_DPLL_ENHANCED_SSM_CODE_EEEC = 0x22,
190 MLX5_DPLL_ENHANCED_SSM_CODE_EPRC = 0x23,
191 };
192
193 #define __MLX5_DPLL_SSM_COMBINED_CODE(ssm_code, enhanced_ssm_code) \
194 ((ssm_code) | ((enhanced_ssm_code) << 8))
195
196 #define MLX5_DPLL_SSM_COMBINED_CODE(type) \
197 __MLX5_DPLL_SSM_COMBINED_CODE(MLX5_DPLL_SSM_CODE_##type, \
198 MLX5_DPLL_ENHANCED_SSM_CODE_##type)
199
mlx5_dpll_clock_quality_level_get(const struct dpll_device * dpll,void * priv,unsigned long * qls,struct netlink_ext_ack * extack)200 static int mlx5_dpll_clock_quality_level_get(const struct dpll_device *dpll,
201 void *priv, unsigned long *qls,
202 struct netlink_ext_ack *extack)
203 {
204 u8 network_option, ssm_code, enhanced_ssm_code;
205 u32 out[MLX5_ST_SZ_DW(msecq_reg)] = {};
206 u32 in[MLX5_ST_SZ_DW(msecq_reg)] = {};
207 struct mlx5_dpll *mdpll = priv;
208 int err;
209
210 err = mlx5_core_access_reg(mdpll->mdev, in, sizeof(in),
211 out, sizeof(out), MLX5_REG_MSECQ, 0, 0);
212 if (err)
213 return err;
214 network_option = MLX5_GET(msecq_reg, out, network_option);
215 if (network_option != 1)
216 goto errout;
217 ssm_code = MLX5_GET(msecq_reg, out, local_ssm_code);
218 enhanced_ssm_code = MLX5_GET(msecq_reg, out, local_enhanced_ssm_code);
219
220 switch (__MLX5_DPLL_SSM_COMBINED_CODE(ssm_code, enhanced_ssm_code)) {
221 case MLX5_DPLL_SSM_COMBINED_CODE(PRC):
222 __set_bit(DPLL_CLOCK_QUALITY_LEVEL_ITU_OPT1_PRC, qls);
223 return 0;
224 case MLX5_DPLL_SSM_COMBINED_CODE(SSU_A):
225 __set_bit(DPLL_CLOCK_QUALITY_LEVEL_ITU_OPT1_SSU_A, qls);
226 return 0;
227 case MLX5_DPLL_SSM_COMBINED_CODE(SSU_B):
228 __set_bit(DPLL_CLOCK_QUALITY_LEVEL_ITU_OPT1_SSU_B, qls);
229 return 0;
230 case MLX5_DPLL_SSM_COMBINED_CODE(EEC1):
231 __set_bit(DPLL_CLOCK_QUALITY_LEVEL_ITU_OPT1_EEC1, qls);
232 return 0;
233 case MLX5_DPLL_SSM_COMBINED_CODE(PRTC):
234 __set_bit(DPLL_CLOCK_QUALITY_LEVEL_ITU_OPT1_PRTC, qls);
235 return 0;
236 case MLX5_DPLL_SSM_COMBINED_CODE(EPRTC):
237 __set_bit(DPLL_CLOCK_QUALITY_LEVEL_ITU_OPT1_EPRTC, qls);
238 return 0;
239 case MLX5_DPLL_SSM_COMBINED_CODE(EEEC):
240 __set_bit(DPLL_CLOCK_QUALITY_LEVEL_ITU_OPT1_EEEC, qls);
241 return 0;
242 case MLX5_DPLL_SSM_COMBINED_CODE(EPRC):
243 __set_bit(DPLL_CLOCK_QUALITY_LEVEL_ITU_OPT1_EPRC, qls);
244 return 0;
245 }
246 errout:
247 NL_SET_ERR_MSG_MOD(extack, "Invalid clock quality level obtained from firmware");
248 return -EINVAL;
249 }
250
251 static const struct dpll_device_ops mlx5_dpll_device_ops = {
252 .lock_status_get = mlx5_dpll_device_lock_status_get,
253 .mode_get = mlx5_dpll_device_mode_get,
254 .clock_quality_level_get = mlx5_dpll_clock_quality_level_get,
255 };
256
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)257 static int mlx5_dpll_pin_direction_get(const struct dpll_pin *pin,
258 void *pin_priv,
259 const struct dpll_device *dpll,
260 void *dpll_priv,
261 enum dpll_pin_direction *direction,
262 struct netlink_ext_ack *extack)
263 {
264 *direction = DPLL_PIN_DIRECTION_INPUT;
265 return 0;
266 }
267
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)268 static int mlx5_dpll_state_on_dpll_get(const struct dpll_pin *pin,
269 void *pin_priv,
270 const struct dpll_device *dpll,
271 void *dpll_priv,
272 enum dpll_pin_state *state,
273 struct netlink_ext_ack *extack)
274 {
275 struct mlx5_dpll_synce_status synce_status;
276 struct mlx5_dpll *mdpll = pin_priv;
277 int err;
278
279 err = mlx5_dpll_synce_status_get(mdpll->mdev, &synce_status);
280 if (err)
281 return err;
282 *state = mlx5_dpll_pin_state_get(&synce_status);
283 return 0;
284 }
285
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)286 static int mlx5_dpll_state_on_dpll_set(const struct dpll_pin *pin,
287 void *pin_priv,
288 const struct dpll_device *dpll,
289 void *dpll_priv,
290 enum dpll_pin_state state,
291 struct netlink_ext_ack *extack)
292 {
293 struct mlx5_dpll *mdpll = pin_priv;
294
295 return mlx5_dpll_synce_status_set(mdpll->mdev,
296 state == DPLL_PIN_STATE_CONNECTED ?
297 MLX5_MSEES_ADMIN_STATUS_TRACK :
298 MLX5_MSEES_ADMIN_STATUS_FREE_RUNNING);
299 }
300
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)301 static int mlx5_dpll_ffo_get(const struct dpll_pin *pin, void *pin_priv,
302 const struct dpll_device *dpll, void *dpll_priv,
303 s64 *ffo, struct netlink_ext_ack *extack)
304 {
305 struct mlx5_dpll_synce_status synce_status;
306 struct mlx5_dpll *mdpll = pin_priv;
307 int err;
308
309 err = mlx5_dpll_synce_status_get(mdpll->mdev, &synce_status);
310 if (err)
311 return err;
312 return mlx5_dpll_pin_ffo_get(&synce_status, ffo);
313 }
314
315 static const struct dpll_pin_ops mlx5_dpll_pins_ops = {
316 .direction_get = mlx5_dpll_pin_direction_get,
317 .state_on_dpll_get = mlx5_dpll_state_on_dpll_get,
318 .state_on_dpll_set = mlx5_dpll_state_on_dpll_set,
319 .ffo_get = mlx5_dpll_ffo_get,
320 };
321
322 static const struct dpll_pin_properties mlx5_dpll_pin_properties = {
323 .type = DPLL_PIN_TYPE_SYNCE_ETH_PORT,
324 .capabilities = DPLL_PIN_CAPABILITIES_STATE_CAN_CHANGE,
325 };
326
327 #define MLX5_DPLL_PERIODIC_WORK_INTERVAL 500 /* ms */
328
mlx5_dpll_periodic_work_queue(struct mlx5_dpll * mdpll)329 static void mlx5_dpll_periodic_work_queue(struct mlx5_dpll *mdpll)
330 {
331 queue_delayed_work(mdpll->wq, &mdpll->work,
332 msecs_to_jiffies(MLX5_DPLL_PERIODIC_WORK_INTERVAL));
333 }
334
mlx5_dpll_periodic_work(struct work_struct * work)335 static void mlx5_dpll_periodic_work(struct work_struct *work)
336 {
337 struct mlx5_dpll *mdpll = container_of(work, struct mlx5_dpll,
338 work.work);
339 struct mlx5_dpll_synce_status synce_status;
340 enum dpll_lock_status lock_status;
341 enum dpll_pin_state pin_state;
342 int err;
343
344 err = mlx5_dpll_synce_status_get(mdpll->mdev, &synce_status);
345 if (err)
346 goto err_out;
347 lock_status = mlx5_dpll_lock_status_get(&synce_status);
348 pin_state = mlx5_dpll_pin_state_get(&synce_status);
349
350 if (!mdpll->last.valid)
351 goto invalid_out;
352
353 if (mdpll->last.lock_status != lock_status)
354 dpll_device_change_ntf(mdpll->dpll);
355 if (mdpll->last.pin_state != pin_state)
356 dpll_pin_change_ntf(mdpll->dpll_pin);
357
358 invalid_out:
359 mdpll->last.lock_status = lock_status;
360 mdpll->last.pin_state = pin_state;
361 mdpll->last.valid = true;
362 err_out:
363 mlx5_dpll_periodic_work_queue(mdpll);
364 }
365
mlx5_dpll_netdev_dpll_pin_set(struct mlx5_dpll * mdpll,struct net_device * netdev)366 static void mlx5_dpll_netdev_dpll_pin_set(struct mlx5_dpll *mdpll,
367 struct net_device *netdev)
368 {
369 if (mdpll->tracking_netdev)
370 return;
371 dpll_netdev_pin_set(netdev, mdpll->dpll_pin);
372 mdpll->tracking_netdev = netdev;
373 }
374
mlx5_dpll_netdev_dpll_pin_clear(struct mlx5_dpll * mdpll)375 static void mlx5_dpll_netdev_dpll_pin_clear(struct mlx5_dpll *mdpll)
376 {
377 if (!mdpll->tracking_netdev)
378 return;
379 dpll_netdev_pin_clear(mdpll->tracking_netdev);
380 mdpll->tracking_netdev = NULL;
381 }
382
mlx5_dpll_mdev_notifier_event(struct notifier_block * nb,unsigned long event,void * data)383 static int mlx5_dpll_mdev_notifier_event(struct notifier_block *nb,
384 unsigned long event, void *data)
385 {
386 struct mlx5_dpll *mdpll = container_of(nb, struct mlx5_dpll, mdev_nb);
387 struct net_device *netdev = data;
388
389 switch (event) {
390 case MLX5_DRIVER_EVENT_UPLINK_NETDEV:
391 if (netdev)
392 mlx5_dpll_netdev_dpll_pin_set(mdpll, netdev);
393 else
394 mlx5_dpll_netdev_dpll_pin_clear(mdpll);
395 break;
396 default:
397 return NOTIFY_DONE;
398 }
399
400 return NOTIFY_OK;
401 }
402
mlx5_dpll_mdev_netdev_track(struct mlx5_dpll * mdpll,struct mlx5_core_dev * mdev)403 static void mlx5_dpll_mdev_netdev_track(struct mlx5_dpll *mdpll,
404 struct mlx5_core_dev *mdev)
405 {
406 mdpll->mdev_nb.notifier_call = mlx5_dpll_mdev_notifier_event;
407 mlx5_blocking_notifier_register(mdev, &mdpll->mdev_nb);
408 mlx5_core_uplink_netdev_event_replay(mdev);
409 }
410
mlx5_dpll_mdev_netdev_untrack(struct mlx5_dpll * mdpll,struct mlx5_core_dev * mdev)411 static void mlx5_dpll_mdev_netdev_untrack(struct mlx5_dpll *mdpll,
412 struct mlx5_core_dev *mdev)
413 {
414 mlx5_blocking_notifier_unregister(mdev, &mdpll->mdev_nb);
415 mlx5_dpll_netdev_dpll_pin_clear(mdpll);
416 }
417
mlx5_dpll_probe(struct auxiliary_device * adev,const struct auxiliary_device_id * id)418 static int mlx5_dpll_probe(struct auxiliary_device *adev,
419 const struct auxiliary_device_id *id)
420 {
421 struct mlx5_adev *edev = container_of(adev, struct mlx5_adev, adev);
422 struct mlx5_core_dev *mdev = edev->mdev;
423 struct mlx5_dpll *mdpll;
424 u64 clock_id;
425 int err;
426
427 err = mlx5_dpll_synce_status_set(mdev,
428 MLX5_MSEES_ADMIN_STATUS_FREE_RUNNING);
429 if (err)
430 return err;
431
432 err = mlx5_dpll_clock_id_get(mdev, &clock_id);
433 if (err)
434 return err;
435
436 mdpll = kzalloc_obj(*mdpll);
437 if (!mdpll)
438 return -ENOMEM;
439 mdpll->mdev = mdev;
440 auxiliary_set_drvdata(adev, mdpll);
441
442 /* Multiple mdev instances might share one DPLL device. */
443 mdpll->dpll = dpll_device_get(clock_id, 0, THIS_MODULE,
444 &mdpll->dpll_tracker);
445 if (IS_ERR(mdpll->dpll)) {
446 err = PTR_ERR(mdpll->dpll);
447 goto err_free_mdpll;
448 }
449
450 err = dpll_device_register(mdpll->dpll, DPLL_TYPE_EEC,
451 &mlx5_dpll_device_ops, mdpll);
452 if (err)
453 goto err_put_dpll_device;
454
455 /* Multiple mdev instances might share one DPLL pin. */
456 mdpll->dpll_pin = dpll_pin_get(clock_id, mlx5_get_dev_index(mdev),
457 THIS_MODULE, &mlx5_dpll_pin_properties,
458 &mdpll->pin_tracker);
459 if (IS_ERR(mdpll->dpll_pin)) {
460 err = PTR_ERR(mdpll->dpll_pin);
461 goto err_unregister_dpll_device;
462 }
463
464 err = dpll_pin_register(mdpll->dpll, mdpll->dpll_pin,
465 &mlx5_dpll_pins_ops, mdpll);
466 if (err)
467 goto err_put_dpll_pin;
468
469 mdpll->wq = create_singlethread_workqueue("mlx5_dpll");
470 if (!mdpll->wq) {
471 err = -ENOMEM;
472 goto err_unregister_dpll_pin;
473 }
474
475 mlx5_dpll_mdev_netdev_track(mdpll, mdev);
476
477 INIT_DELAYED_WORK(&mdpll->work, &mlx5_dpll_periodic_work);
478 mlx5_dpll_periodic_work_queue(mdpll);
479
480 return 0;
481
482 err_unregister_dpll_pin:
483 dpll_pin_unregister(mdpll->dpll, mdpll->dpll_pin,
484 &mlx5_dpll_pins_ops, mdpll);
485 err_put_dpll_pin:
486 dpll_pin_put(mdpll->dpll_pin, &mdpll->pin_tracker);
487 err_unregister_dpll_device:
488 dpll_device_unregister(mdpll->dpll, &mlx5_dpll_device_ops, mdpll);
489 err_put_dpll_device:
490 dpll_device_put(mdpll->dpll, &mdpll->dpll_tracker);
491 err_free_mdpll:
492 kfree(mdpll);
493 return err;
494 }
495
mlx5_dpll_remove(struct auxiliary_device * adev)496 static void mlx5_dpll_remove(struct auxiliary_device *adev)
497 {
498 struct mlx5_dpll *mdpll = auxiliary_get_drvdata(adev);
499 struct mlx5_core_dev *mdev = mdpll->mdev;
500
501 cancel_delayed_work_sync(&mdpll->work);
502 mlx5_dpll_mdev_netdev_untrack(mdpll, mdev);
503 destroy_workqueue(mdpll->wq);
504 dpll_pin_unregister(mdpll->dpll, mdpll->dpll_pin,
505 &mlx5_dpll_pins_ops, mdpll);
506 dpll_pin_put(mdpll->dpll_pin, &mdpll->pin_tracker);
507 dpll_device_unregister(mdpll->dpll, &mlx5_dpll_device_ops, mdpll);
508 dpll_device_put(mdpll->dpll, &mdpll->dpll_tracker);
509 kfree(mdpll);
510
511 mlx5_dpll_synce_status_set(mdev,
512 MLX5_MSEES_ADMIN_STATUS_FREE_RUNNING);
513 }
514
mlx5_dpll_suspend(struct auxiliary_device * adev,pm_message_t state)515 static int mlx5_dpll_suspend(struct auxiliary_device *adev, pm_message_t state)
516 {
517 return 0;
518 }
519
mlx5_dpll_resume(struct auxiliary_device * adev)520 static int mlx5_dpll_resume(struct auxiliary_device *adev)
521 {
522 return 0;
523 }
524
525 static const struct auxiliary_device_id mlx5_dpll_id_table[] = {
526 { .name = MLX5_ADEV_NAME ".dpll", },
527 {},
528 };
529
530 MODULE_DEVICE_TABLE(auxiliary, mlx5_dpll_id_table);
531
532 static struct auxiliary_driver mlx5_dpll_driver = {
533 .name = "dpll",
534 .probe = mlx5_dpll_probe,
535 .remove = mlx5_dpll_remove,
536 .suspend = mlx5_dpll_suspend,
537 .resume = mlx5_dpll_resume,
538 .id_table = mlx5_dpll_id_table,
539 };
540
mlx5_dpll_init(void)541 static int __init mlx5_dpll_init(void)
542 {
543 return auxiliary_driver_register(&mlx5_dpll_driver);
544 }
545
mlx5_dpll_exit(void)546 static void __exit mlx5_dpll_exit(void)
547 {
548 auxiliary_driver_unregister(&mlx5_dpll_driver);
549 }
550
551 module_init(mlx5_dpll_init);
552 module_exit(mlx5_dpll_exit);
553
554 MODULE_AUTHOR("Jiri Pirko <jiri@nvidia.com>");
555 MODULE_DESCRIPTION("Mellanox 5th generation network adapters (ConnectX series) DPLL driver");
556 MODULE_LICENSE("Dual BSD/GPL");
557