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