brintos

brintos / linux-shallow public Read only

0
0
Text · 12.9 KiB · 904e08d Raw
472 lines · c
1// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB2/* 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 created8 * per mdev instance.9 */10struct 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 25static 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 39struct 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 48static int49mlx5_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 69static int70mlx5_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 86static enum dpll_lock_status87mlx5_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 104static enum dpll_lock_status_error105mlx5_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 124static enum dpll_pin_state125mlx5_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 133static int134mlx5_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 143static int144mlx5_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 161static 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 169static 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 174static 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 185static 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 203static 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 218static 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 232static 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 239static 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 246static 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 252static 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 275invalid_out:276	mdpll->last.lock_status = lock_status;277	mdpll->last.pin_state = pin_state;278	mdpll->last.valid = true;279err_out:280	mlx5_dpll_periodic_work_queue(mdpll);281}282 283static 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 292static 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 300static 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		else311			mlx5_dpll_netdev_dpll_pin_clear(mdpll);312		break;313	default:314		return NOTIFY_DONE;315	}316 317	return NOTIFY_OK;318}319 320static 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 328static 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 335static 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 397err_unregister_dpll_pin:398	dpll_pin_unregister(mdpll->dpll, mdpll->dpll_pin,399			    &mlx5_dpll_pins_ops, mdpll);400err_put_dpll_pin:401	dpll_pin_put(mdpll->dpll_pin);402err_unregister_dpll_device:403	dpll_device_unregister(mdpll->dpll, &mlx5_dpll_device_ops, mdpll);404err_put_dpll_device:405	dpll_device_put(mdpll->dpll);406err_free_mdpll:407	kfree(mdpll);408	return err;409}410 411static 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 430static int mlx5_dpll_suspend(struct auxiliary_device *adev, pm_message_t state)431{432	return 0;433}434 435static int mlx5_dpll_resume(struct auxiliary_device *adev)436{437	return 0;438}439 440static const struct auxiliary_device_id mlx5_dpll_id_table[] = {441	{ .name = MLX5_ADEV_NAME ".dpll", },442	{},443};444 445MODULE_DEVICE_TABLE(auxiliary, mlx5_dpll_id_table);446 447static 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 456static int __init mlx5_dpll_init(void)457{458	return auxiliary_driver_register(&mlx5_dpll_driver);459}460 461static void __exit mlx5_dpll_exit(void)462{463	auxiliary_driver_unregister(&mlx5_dpll_driver);464}465 466module_init(mlx5_dpll_init);467module_exit(mlx5_dpll_exit);468 469MODULE_AUTHOR("Jiri Pirko <jiri@nvidia.com>");470MODULE_DESCRIPTION("Mellanox 5th generation network adapters (ConnectX series) DPLL driver");471MODULE_LICENSE("Dual BSD/GPL");472