Merge tag 'zonefs-6.9-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/dlemoal...
[sfrench/cifs-2.6.git] / drivers / net / ethernet / mellanox / mlx5 / core / dpll.c
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         dpll_netdev_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         dpll_netdev_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_sync(&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");