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 #include <linux/math64.h> 10 11 #define IWL_PTP_GP2_WRAP 0x100000000ULL 12 #define IWL_PTP_WRAP_TIME (3600 * HZ) 13 14 /* The scaled_ppm parameter is ppm (parts per million) with a 16-bit fractional 15 * part, which means that a value of 1 in one of those fields actually means 16 * 2^-16 ppm, and 2^16=65536 is 1 ppm. 17 */ 18 #define SCALE_FACTOR 65536000000ULL 19 #define IWL_PTP_WRAP_THRESHOLD_USEC (5000) 20 21 #define IWL_PTP_GET_CROSS_TS_NUM 5 22 23 static void iwl_mvm_ptp_update_new_read(struct iwl_mvm *mvm, u32 gp2) 24 { 25 /* If the difference is above the threshold, assume it's a wraparound. 26 * Otherwise assume it's an old read and ignore it. 27 */ 28 if (gp2 < mvm->ptp_data.last_gp2 && 29 mvm->ptp_data.last_gp2 - gp2 < IWL_PTP_WRAP_THRESHOLD_USEC) { 30 IWL_DEBUG_INFO(mvm, 31 "PTP: ignore old read (gp2=%u, last_gp2=%u)\n", 32 gp2, mvm->ptp_data.last_gp2); 33 return; 34 } 35 36 if (gp2 < mvm->ptp_data.last_gp2) { 37 mvm->ptp_data.wrap_counter++; 38 IWL_DEBUG_INFO(mvm, 39 "PTP: wraparound detected (new counter=%u)\n", 40 mvm->ptp_data.wrap_counter); 41 } 42 43 mvm->ptp_data.last_gp2 = gp2; 44 schedule_delayed_work(&mvm->ptp_data.dwork, IWL_PTP_WRAP_TIME); 45 } 46 47 u64 iwl_mvm_ptp_get_adj_time(struct iwl_mvm *mvm, u64 base_time_ns) 48 { 49 struct ptp_data *data = &mvm->ptp_data; 50 u64 last_gp2_ns = mvm->ptp_data.scale_update_gp2 * NSEC_PER_USEC; 51 u64 res; 52 u64 diff; 53 54 iwl_mvm_ptp_update_new_read(mvm, 55 div64_u64(base_time_ns, NSEC_PER_USEC)); 56 57 IWL_DEBUG_INFO(mvm, "base_time_ns=%llu, wrap_counter=%u\n", 58 (unsigned long long)base_time_ns, data->wrap_counter); 59 60 base_time_ns = base_time_ns + 61 (data->wrap_counter * IWL_PTP_GP2_WRAP * NSEC_PER_USEC); 62 63 /* It is possible that a GP2 timestamp was received from fw before the 64 * last scale update. Since we don't know how to scale - ignore it. 65 */ 66 if (base_time_ns < last_gp2_ns) { 67 IWL_DEBUG_INFO(mvm, "Time before scale update - ignore\n"); 68 return 0; 69 } 70 71 diff = base_time_ns - last_gp2_ns; 72 IWL_DEBUG_INFO(mvm, "diff ns=%llu\n", (unsigned long long)diff); 73 74 diff = mul_u64_u64_div_u64(diff, data->scaled_freq, 75 SCALE_FACTOR); 76 IWL_DEBUG_INFO(mvm, "scaled diff ns=%llu\n", (unsigned long long)diff); 77 78 res = data->scale_update_adj_time_ns + data->delta + diff; 79 80 IWL_DEBUG_INFO(mvm, "base=%llu delta=%lld adj=%llu\n", 81 (unsigned long long)base_time_ns, (long long)data->delta, 82 (unsigned long long)res); 83 return res; 84 } 85 86 static int 87 iwl_mvm_get_crosstimestamp_fw(struct iwl_mvm *mvm, u32 *gp2, u64 *sys_time) 88 { 89 struct iwl_synced_time_cmd synced_time_cmd = { 90 .operation = cpu_to_le32(IWL_SYNCED_TIME_OPERATION_READ_BOTH) 91 }; 92 struct iwl_host_cmd cmd = { 93 .id = WIDE_ID(DATA_PATH_GROUP, WNM_PLATFORM_PTM_REQUEST_CMD), 94 .flags = CMD_WANT_SKB, 95 .data[0] = &synced_time_cmd, 96 .len[0] = sizeof(synced_time_cmd), 97 }; 98 struct iwl_synced_time_rsp *resp; 99 struct iwl_rx_packet *pkt; 100 int ret; 101 u64 gp2_10ns; 102 103 ret = iwl_mvm_send_cmd(mvm, &cmd); 104 if (ret) 105 return ret; 106 107 pkt = cmd.resp_pkt; 108 109 if (iwl_rx_packet_payload_len(pkt) != sizeof(*resp)) { 110 IWL_ERR(mvm, "PTP: Invalid command response\n"); 111 iwl_free_resp(&cmd); 112 return -EIO; 113 } 114 115 resp = (void *)pkt->data; 116 117 gp2_10ns = (u64)le32_to_cpu(resp->gp2_timestamp_hi) << 32 | 118 le32_to_cpu(resp->gp2_timestamp_lo); 119 *gp2 = div_u64(gp2_10ns, 100); 120 121 *sys_time = (u64)le32_to_cpu(resp->platform_timestamp_hi) << 32 | 122 le32_to_cpu(resp->platform_timestamp_lo); 123 124 return ret; 125 } 126 127 static void iwl_mvm_phc_get_crosstimestamp_loop(struct iwl_mvm *mvm, 128 ktime_t *sys_time, u32 *gp2) 129 { 130 u64 diff = 0, new_diff; 131 u64 tmp_sys_time; 132 u32 tmp_gp2; 133 int i; 134 135 for (i = 0; i < IWL_PTP_GET_CROSS_TS_NUM; i++) { 136 iwl_mvm_get_sync_time(mvm, CLOCK_REALTIME, &tmp_gp2, NULL, 137 &tmp_sys_time); 138 new_diff = tmp_sys_time - ((u64)tmp_gp2 * NSEC_PER_USEC); 139 if (!diff || new_diff < diff) { 140 *sys_time = tmp_sys_time; 141 *gp2 = tmp_gp2; 142 diff = new_diff; 143 IWL_DEBUG_INFO(mvm, "PTP: new times: gp2=%u sys=%lld\n", 144 *gp2, *sys_time); 145 } 146 } 147 } 148 149 static int 150 iwl_mvm_phc_get_crosstimestamp(struct ptp_clock_info *ptp, 151 struct system_device_crosststamp *xtstamp) 152 { 153 struct iwl_mvm *mvm = container_of(ptp, struct iwl_mvm, 154 ptp_data.ptp_clock_info); 155 int ret = 0; 156 /* Raw value read from GP2 register in usec */ 157 u32 gp2; 158 /* GP2 value in ns*/ 159 s64 gp2_ns; 160 /* System (wall) time */ 161 ktime_t sys_time; 162 163 memset(xtstamp, 0, sizeof(struct system_device_crosststamp)); 164 165 if (!mvm->ptp_data.ptp_clock) { 166 IWL_ERR(mvm, "No PHC clock registered\n"); 167 return -ENODEV; 168 } 169 170 mutex_lock(&mvm->mutex); 171 if (fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_SYNCED_TIME)) { 172 ret = iwl_mvm_get_crosstimestamp_fw(mvm, &gp2, &sys_time); 173 174 if (ret) 175 goto out; 176 } else { 177 iwl_mvm_phc_get_crosstimestamp_loop(mvm, &sys_time, &gp2); 178 } 179 180 gp2_ns = iwl_mvm_ptp_get_adj_time(mvm, (u64)gp2 * NSEC_PER_USEC); 181 182 IWL_INFO(mvm, "Got Sync Time: GP2:%u, last_GP2: %u, GP2_ns: %lld, sys_time: %lld\n", 183 gp2, mvm->ptp_data.last_gp2, gp2_ns, (s64)sys_time); 184 185 /* System monotonic raw time is not used */ 186 xtstamp->device = (ktime_t)gp2_ns; 187 xtstamp->sys_realtime = sys_time; 188 189 out: 190 mutex_unlock(&mvm->mutex); 191 return ret; 192 } 193 194 static void iwl_mvm_ptp_work(struct work_struct *wk) 195 { 196 struct iwl_mvm *mvm = container_of(wk, struct iwl_mvm, 197 ptp_data.dwork.work); 198 u32 gp2; 199 200 mutex_lock(&mvm->mutex); 201 gp2 = iwl_mvm_get_systime(mvm); 202 iwl_mvm_ptp_update_new_read(mvm, gp2); 203 mutex_unlock(&mvm->mutex); 204 } 205 206 static int iwl_mvm_ptp_gettime(struct ptp_clock_info *ptp, 207 struct timespec64 *ts) 208 { 209 struct iwl_mvm *mvm = container_of(ptp, struct iwl_mvm, 210 ptp_data.ptp_clock_info); 211 u64 gp2; 212 u64 ns; 213 214 mutex_lock(&mvm->mutex); 215 gp2 = iwl_mvm_get_systime(mvm); 216 ns = iwl_mvm_ptp_get_adj_time(mvm, gp2 * NSEC_PER_USEC); 217 mutex_unlock(&mvm->mutex); 218 219 *ts = ns_to_timespec64(ns); 220 return 0; 221 } 222 223 static int iwl_mvm_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta) 224 { 225 struct iwl_mvm *mvm = container_of(ptp, struct iwl_mvm, 226 ptp_data.ptp_clock_info); 227 struct ptp_data *data = container_of(ptp, struct ptp_data, 228 ptp_clock_info); 229 230 mutex_lock(&mvm->mutex); 231 data->delta += delta; 232 IWL_DEBUG_INFO(mvm, "delta=%lld, new delta=%lld\n", (long long)delta, 233 (long long)data->delta); 234 mutex_unlock(&mvm->mutex); 235 return 0; 236 } 237 238 static int iwl_mvm_ptp_adjfine(struct ptp_clock_info *ptp, long scaled_ppm) 239 { 240 struct iwl_mvm *mvm = container_of(ptp, struct iwl_mvm, 241 ptp_data.ptp_clock_info); 242 struct ptp_data *data = &mvm->ptp_data; 243 u32 gp2; 244 245 mutex_lock(&mvm->mutex); 246 247 /* Must call _iwl_mvm_ptp_get_adj_time() before updating 248 * data->scale_update_gp2 or data->scaled_freq since 249 * scale_update_adj_time_ns should reflect the previous scaled_freq. 250 */ 251 gp2 = iwl_mvm_get_systime(mvm); 252 data->scale_update_adj_time_ns = 253 iwl_mvm_ptp_get_adj_time(mvm, gp2 * NSEC_PER_USEC); 254 data->scale_update_gp2 = gp2; 255 data->wrap_counter = 0; 256 data->delta = 0; 257 258 data->scaled_freq = SCALE_FACTOR + scaled_ppm; 259 IWL_DEBUG_INFO(mvm, "adjfine: scaled_ppm=%ld new=%llu\n", 260 scaled_ppm, (unsigned long long)data->scaled_freq); 261 262 mutex_unlock(&mvm->mutex); 263 return 0; 264 } 265 266 /* iwl_mvm_ptp_init - initialize PTP for devices which support it. 267 * @mvm: internal mvm structure, see &struct iwl_mvm. 268 * 269 * Performs the required steps for enabling PTP support. 270 */ 271 void iwl_mvm_ptp_init(struct iwl_mvm *mvm) 272 { 273 /* Warn if the interface already has a ptp_clock defined */ 274 if (WARN_ON(mvm->ptp_data.ptp_clock)) 275 return; 276 277 mvm->ptp_data.ptp_clock_info.owner = THIS_MODULE; 278 mvm->ptp_data.ptp_clock_info.max_adj = 0x7fffffff; 279 mvm->ptp_data.ptp_clock_info.getcrosststamp = 280 iwl_mvm_phc_get_crosstimestamp; 281 mvm->ptp_data.ptp_clock_info.adjfine = iwl_mvm_ptp_adjfine; 282 mvm->ptp_data.ptp_clock_info.adjtime = iwl_mvm_ptp_adjtime; 283 mvm->ptp_data.ptp_clock_info.gettime64 = iwl_mvm_ptp_gettime; 284 mvm->ptp_data.scaled_freq = SCALE_FACTOR; 285 286 /* Give a short 'friendly name' to identify the PHC clock */ 287 snprintf(mvm->ptp_data.ptp_clock_info.name, 288 sizeof(mvm->ptp_data.ptp_clock_info.name), 289 "%s", "iwlwifi-PTP"); 290 291 INIT_DELAYED_WORK(&mvm->ptp_data.dwork, iwl_mvm_ptp_work); 292 293 mvm->ptp_data.ptp_clock = 294 ptp_clock_register(&mvm->ptp_data.ptp_clock_info, mvm->dev); 295 296 if (IS_ERR(mvm->ptp_data.ptp_clock)) { 297 IWL_ERR(mvm, "Failed to register PHC clock (%ld)\n", 298 PTR_ERR(mvm->ptp_data.ptp_clock)); 299 mvm->ptp_data.ptp_clock = NULL; 300 } else if (mvm->ptp_data.ptp_clock) { 301 IWL_INFO(mvm, "Registered PHC clock: %s, with index: %d\n", 302 mvm->ptp_data.ptp_clock_info.name, 303 ptp_clock_index(mvm->ptp_data.ptp_clock)); 304 } 305 } 306 307 /* iwl_mvm_ptp_remove - disable PTP device. 308 * @mvm: internal mvm structure, see &struct iwl_mvm. 309 * 310 * Disable PTP support. 311 */ 312 void iwl_mvm_ptp_remove(struct iwl_mvm *mvm) 313 { 314 if (mvm->ptp_data.ptp_clock) { 315 IWL_INFO(mvm, "Unregistering PHC clock: %s, with index: %d\n", 316 mvm->ptp_data.ptp_clock_info.name, 317 ptp_clock_index(mvm->ptp_data.ptp_clock)); 318 319 ptp_clock_unregister(mvm->ptp_data.ptp_clock); 320 mvm->ptp_data.ptp_clock = NULL; 321 memset(&mvm->ptp_data.ptp_clock_info, 0, 322 sizeof(mvm->ptp_data.ptp_clock_info)); 323 mvm->ptp_data.last_gp2 = 0; 324 cancel_delayed_work_sync(&mvm->ptp_data.dwork); 325 } 326 } 327