xref: /openbmc/linux/drivers/net/wireless/intel/iwlwifi/mvm/ptp.c (revision 1595ecce1cf32688760281f40b58b7a1d4a69aa9)
1 // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
2 /*
3  * Copyright (C) 2021 - 2023 Intel Corporation
4  */
5 
6 #include "mvm.h"
7 #include "iwl-debug.h"
8 #include <linux/timekeeping.h>
9 
10 #define IWL_PTP_GP2_WRAP	0x100000000ULL
11 #define IWL_PTP_WRAP_TIME	(3600 * HZ)
12 
13 static void iwl_mvm_ptp_update_new_read(struct iwl_mvm *mvm, u32 gp2)
14 {
15 	if (gp2 < mvm->ptp_data.last_gp2) {
16 		mvm->ptp_data.wrap_counter++;
17 		IWL_DEBUG_INFO(mvm,
18 			       "PTP: wraparound detected (new counter=%u)\n",
19 			       mvm->ptp_data.wrap_counter);
20 	}
21 
22 	mvm->ptp_data.last_gp2 = gp2;
23 	schedule_delayed_work(&mvm->ptp_data.dwork, IWL_PTP_WRAP_TIME);
24 }
25 
26 static int
27 iwl_mvm_phc_get_crosstimestamp(struct ptp_clock_info *ptp,
28 			       struct system_device_crosststamp *xtstamp)
29 {
30 	struct iwl_mvm *mvm = container_of(ptp, struct iwl_mvm,
31 					   ptp_data.ptp_clock_info);
32 	/* Raw value read from GP2 register in usec */
33 	u32 gp2;
34 	/* GP2 value in ns*/
35 	s64 gp2_ns;
36 	/* System (wall) time */
37 	ktime_t sys_time;
38 
39 	memset(xtstamp, 0, sizeof(struct system_device_crosststamp));
40 
41 	if (!mvm->ptp_data.ptp_clock) {
42 		IWL_ERR(mvm, "No PHC clock registered\n");
43 		return -ENODEV;
44 	}
45 
46 	iwl_mvm_get_sync_time(mvm, CLOCK_REALTIME, &gp2, NULL, &sys_time);
47 
48 	iwl_mvm_ptp_update_new_read(mvm, gp2);
49 
50 	gp2_ns = (gp2 + (mvm->ptp_data.wrap_counter * IWL_PTP_GP2_WRAP)) *
51 		NSEC_PER_USEC;
52 
53 	IWL_INFO(mvm, "Got Sync Time: GP2:%u, last_GP2: %u, GP2_ns: %lld, sys_time: %lld\n",
54 		 gp2, mvm->ptp_data.last_gp2, gp2_ns, (s64)sys_time);
55 
56 	/* System monotonic raw time is not used */
57 	xtstamp->device = (ktime_t)gp2_ns;
58 	xtstamp->sys_realtime = sys_time;
59 
60 	return 0;
61 }
62 
63 static void iwl_mvm_ptp_work(struct work_struct *wk)
64 {
65 	struct iwl_mvm *mvm = container_of(wk, struct iwl_mvm,
66 					   ptp_data.dwork.work);
67 	u32 gp2;
68 
69 	mutex_lock(&mvm->mutex);
70 	gp2 = iwl_mvm_get_systime(mvm);
71 	iwl_mvm_ptp_update_new_read(mvm, gp2);
72 	mutex_unlock(&mvm->mutex);
73 }
74 
75 /* iwl_mvm_ptp_init - initialize PTP for devices which support it.
76  * @mvm: internal mvm structure, see &struct iwl_mvm.
77  *
78  * Performs the required steps for enabling PTP support.
79  */
80 void iwl_mvm_ptp_init(struct iwl_mvm *mvm)
81 {
82 	/* Warn if the interface already has a ptp_clock defined */
83 	if (WARN_ON(mvm->ptp_data.ptp_clock))
84 		return;
85 
86 	mvm->ptp_data.ptp_clock_info.owner = THIS_MODULE;
87 	mvm->ptp_data.ptp_clock_info.max_adj = 0x7fffffff;
88 	mvm->ptp_data.ptp_clock_info.getcrosststamp =
89 					iwl_mvm_phc_get_crosstimestamp;
90 
91 	/* Give a short 'friendly name' to identify the PHC clock */
92 	snprintf(mvm->ptp_data.ptp_clock_info.name,
93 		 sizeof(mvm->ptp_data.ptp_clock_info.name),
94 		 "%s", "iwlwifi-PTP");
95 
96 	INIT_DELAYED_WORK(&mvm->ptp_data.dwork, iwl_mvm_ptp_work);
97 
98 	mvm->ptp_data.ptp_clock =
99 		ptp_clock_register(&mvm->ptp_data.ptp_clock_info, mvm->dev);
100 
101 	if (IS_ERR(mvm->ptp_data.ptp_clock)) {
102 		IWL_ERR(mvm, "Failed to register PHC clock (%ld)\n",
103 			PTR_ERR(mvm->ptp_data.ptp_clock));
104 		mvm->ptp_data.ptp_clock = NULL;
105 	} else if (mvm->ptp_data.ptp_clock) {
106 		IWL_INFO(mvm, "Registered PHC clock: %s, with index: %d\n",
107 			 mvm->ptp_data.ptp_clock_info.name,
108 			 ptp_clock_index(mvm->ptp_data.ptp_clock));
109 	}
110 }
111 
112 /* iwl_mvm_ptp_remove - disable PTP device.
113  * @mvm: internal mvm structure, see &struct iwl_mvm.
114  *
115  * Disable PTP support.
116  */
117 void iwl_mvm_ptp_remove(struct iwl_mvm *mvm)
118 {
119 	if (mvm->ptp_data.ptp_clock) {
120 		IWL_INFO(mvm, "Unregistering PHC clock: %s, with index: %d\n",
121 			 mvm->ptp_data.ptp_clock_info.name,
122 			 ptp_clock_index(mvm->ptp_data.ptp_clock));
123 
124 		ptp_clock_unregister(mvm->ptp_data.ptp_clock);
125 		mvm->ptp_data.ptp_clock = NULL;
126 		memset(&mvm->ptp_data.ptp_clock_info, 0,
127 		       sizeof(mvm->ptp_data.ptp_clock_info));
128 		mvm->ptp_data.last_gp2 = 0;
129 		cancel_delayed_work_sync(&mvm->ptp_data.dwork);
130 	}
131 }
132