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