FILE
ptp.c
File preview with syntax highlighting.
linux-6.19.14/drivers/net/wireless/intel/iwlwifi/mld/ptp.c
1// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause 2/* 3 * Copyright (C) 2025 Intel Corporation 4 */ 5 6#include "mld.h" 7#include "iwl-debug.h" 8#include "hcmd.h" 9#include "ptp.h" 10#include <linux/timekeeping.h> 11 12/* The scaled_ppm parameter is ppm (parts per million) with a 16-bit fractional 13 * part, which means that a value of 1 in one of those fields actually means 14 * 2^-16 ppm, and 2^16=65536 is 1 ppm. 15 */ 16#define PTP_SCALE_FACTOR 65536000000ULL 17 18#define IWL_PTP_GP2_WRAP 0x100000000ULL 19#define IWL_PTP_WRAP_TIME (3600 * HZ) 20#define IWL_PTP_WRAP_THRESHOLD_USEC (5000) 21 22static int iwl_mld_get_systime(struct iwl_mld *mld, u32 *gp2) 23{ 24 *gp2 = iwl_read_prph(mld->trans, mld->trans->mac_cfg->base->gp2_reg_addr); 25 26 if (*gp2 == 0x5a5a5a5a) 27 return -EINVAL; 28 29 return 0; 30} 31 32static void iwl_mld_ptp_update_new_read(struct iwl_mld *mld, u32 gp2) 33{ 34 IWL_DEBUG_PTP(mld, "PTP: last_gp2=%u, new gp2 read=%u\n", 35 mld->ptp_data.last_gp2, gp2); 36 37 /* If the difference is above the threshold, assume it's a wraparound. 38 * Otherwise assume it's an old read and ignore it. 39 */ 40 if (gp2 < mld->ptp_data.last_gp2) { 41 if (mld->ptp_data.last_gp2 - gp2 < 42 IWL_PTP_WRAP_THRESHOLD_USEC) { 43 IWL_DEBUG_PTP(mld, 44 "PTP: ignore old read (gp2=%u, last_gp2=%u)\n", 45 gp2, mld->ptp_data.last_gp2); 46 return; 47 } 48 49 mld->ptp_data.wrap_counter++; 50 IWL_DEBUG_PTP(mld, 51 "PTP: wraparound detected (new counter=%u)\n", 52 mld->ptp_data.wrap_counter); 53 } 54 55 mld->ptp_data.last_gp2 = gp2; 56 schedule_delayed_work(&mld->ptp_data.dwork, IWL_PTP_WRAP_TIME); 57} 58 59u64 iwl_mld_ptp_get_adj_time(struct iwl_mld *mld, u64 base_time_ns) 60{ 61 struct ptp_data *data = &mld->ptp_data; 62 u64 scale_time_gp2_ns = mld->ptp_data.scale_update_gp2 * NSEC_PER_USEC; 63 u64 res; 64 u64 diff; 65 s64 scaled_diff; 66 67 lockdep_assert_held(&data->lock); 68 69 iwl_mld_ptp_update_new_read(mld, 70 div64_u64(base_time_ns, NSEC_PER_USEC)); 71 72 base_time_ns = base_time_ns + 73 (data->wrap_counter * IWL_PTP_GP2_WRAP * NSEC_PER_USEC); 74 75 /* It is possible that a GP2 timestamp was received from fw before the 76 * last scale update. 77 */ 78 if (base_time_ns < scale_time_gp2_ns) { 79 diff = scale_time_gp2_ns - base_time_ns; 80 scaled_diff = -mul_u64_u64_div_u64(diff, 81 data->scaled_freq, 82 PTP_SCALE_FACTOR); 83 } else { 84 diff = base_time_ns - scale_time_gp2_ns; 85 scaled_diff = mul_u64_u64_div_u64(diff, 86 data->scaled_freq, 87 PTP_SCALE_FACTOR); 88 } 89 90 IWL_DEBUG_PTP(mld, "base_time=%llu diff ns=%llu scaled_diff_ns=%lld\n", 91 (unsigned long long)base_time_ns, 92 (unsigned long long)diff, (long long)scaled_diff); 93 94 res = data->scale_update_adj_time_ns + data->delta + scaled_diff; 95 96 IWL_DEBUG_PTP(mld, "scale_update_ns=%llu delta=%lld adj=%llu\n", 97 (unsigned long long)data->scale_update_adj_time_ns, 98 (long long)data->delta, (unsigned long long)res); 99 return res; 100} 101 102static int iwl_mld_ptp_gettime(struct ptp_clock_info *ptp, 103 struct timespec64 *ts) 104{ 105 struct iwl_mld *mld = container_of(ptp, struct iwl_mld, 106 ptp_data.ptp_clock_info); 107 struct ptp_data *data = &mld->ptp_data; 108 u32 gp2; 109 u64 ns; 110 111 if (iwl_mld_get_systime(mld, &gp2)) { 112 IWL_DEBUG_PTP(mld, "PTP: gettime: failed to read systime\n"); 113 return -EIO; 114 } 115 116 spin_lock_bh(&data->lock); 117 ns = iwl_mld_ptp_get_adj_time(mld, (u64)gp2 * NSEC_PER_USEC); 118 spin_unlock_bh(&data->lock); 119 120 *ts = ns_to_timespec64(ns); 121 return 0; 122} 123 124static int iwl_mld_ptp_settime(struct ptp_clock_info *ptp, 125 const struct timespec64 *ts) 126{ 127 return -EOPNOTSUPP; 128} 129 130static int iwl_mld_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta) 131{ 132 struct iwl_mld *mld = container_of(ptp, struct iwl_mld, 133 ptp_data.ptp_clock_info); 134 struct ptp_data *data = &mld->ptp_data; 135 136 spin_lock_bh(&data->lock); 137 data->delta += delta; 138 IWL_DEBUG_PTP(mld, "delta=%lld, new delta=%lld\n", (long long)delta, 139 (long long)data->delta); 140 spin_unlock_bh(&data->lock); 141 return 0; 142} 143 144static int iwl_mld_ptp_adjfine(struct ptp_clock_info *ptp, long scaled_ppm) 145{ 146 struct iwl_mld *mld = container_of(ptp, struct iwl_mld, 147 ptp_data.ptp_clock_info); 148 struct ptp_data *data = &mld->ptp_data; 149 u32 gp2; 150 151 /* Must call iwl_mld_ptp_get_adj_time() before updating 152 * data->scale_update_gp2 or data->scaled_freq since 153 * scale_update_adj_time_ns should reflect the previous scaled_freq. 154 */ 155 if (iwl_mld_get_systime(mld, &gp2)) { 156 IWL_DEBUG_PTP(mld, "adjfine: failed to read systime\n"); 157 return -EBUSY; 158 } 159 160 spin_lock_bh(&data->lock); 161 data->scale_update_adj_time_ns = 162 iwl_mld_ptp_get_adj_time(mld, gp2 * NSEC_PER_USEC); 163 data->scale_update_gp2 = gp2; 164 165 /* scale_update_adj_time_ns now relects the configured delta, the 166 * wrap_counter and the previous scaled frequency. Thus delta and 167 * wrap_counter should be reset, and the scale frequency is updated 168 * to the new frequency. 169 */ 170 data->delta = 0; 171 data->wrap_counter = 0; 172 data->scaled_freq = PTP_SCALE_FACTOR + scaled_ppm; 173 IWL_DEBUG_PTP(mld, "adjfine: scaled_ppm=%ld new=%llu\n", 174 scaled_ppm, (unsigned long long)data->scaled_freq); 175 spin_unlock_bh(&data->lock); 176 return 0; 177} 178 179static void iwl_mld_ptp_work(struct work_struct *wk) 180{ 181 struct iwl_mld *mld = container_of(wk, struct iwl_mld, 182 ptp_data.dwork.work); 183 struct ptp_data *data = &mld->ptp_data; 184 u32 gp2; 185 186 spin_lock_bh(&data->lock); 187 if (!iwl_mld_get_systime(mld, &gp2)) 188 iwl_mld_ptp_update_new_read(mld, gp2); 189 else 190 IWL_DEBUG_PTP(mld, "PTP work: failed to read GP2\n"); 191 spin_unlock_bh(&data->lock); 192} 193 194static int 195iwl_mld_get_crosstimestamp_fw(struct iwl_mld *mld, u32 *gp2, u64 *sys_time) 196{ 197 struct iwl_synced_time_cmd synced_time_cmd = { 198 .operation = cpu_to_le32(IWL_SYNCED_TIME_OPERATION_READ_BOTH) 199 }; 200 struct iwl_host_cmd cmd = { 201 .id = WIDE_ID(DATA_PATH_GROUP, WNM_PLATFORM_PTM_REQUEST_CMD), 202 .flags = CMD_WANT_SKB, 203 .data[0] = &synced_time_cmd, 204 .len[0] = sizeof(synced_time_cmd), 205 }; 206 struct iwl_synced_time_rsp *resp; 207 struct iwl_rx_packet *pkt; 208 int ret; 209 u64 gp2_10ns; 210 211 wiphy_lock(mld->wiphy); 212 ret = iwl_mld_send_cmd(mld, &cmd); 213 wiphy_unlock(mld->wiphy); 214 if (ret) 215 return ret; 216 217 pkt = cmd.resp_pkt; 218 219 if (iwl_rx_packet_payload_len(pkt) != sizeof(*resp)) { 220 IWL_DEBUG_PTP(mld, "PTP: Invalid PTM command response\n"); 221 iwl_free_resp(&cmd); 222 return -EIO; 223 } 224 225 resp = (void *)pkt->data; 226 227 gp2_10ns = (u64)le32_to_cpu(resp->gp2_timestamp_hi) << 32 | 228 le32_to_cpu(resp->gp2_timestamp_lo); 229 *gp2 = div_u64(gp2_10ns, 100); 230 231 *sys_time = (u64)le32_to_cpu(resp->platform_timestamp_hi) << 32 | 232 le32_to_cpu(resp->platform_timestamp_lo); 233 234 iwl_free_resp(&cmd); 235 return ret; 236} 237 238static int 239iwl_mld_phc_get_crosstimestamp(struct ptp_clock_info *ptp, 240 struct system_device_crosststamp *xtstamp) 241{ 242 struct iwl_mld *mld = container_of(ptp, struct iwl_mld, 243 ptp_data.ptp_clock_info); 244 struct ptp_data *data = &mld->ptp_data; 245 int ret = 0; 246 /* Raw value read from GP2 register in usec */ 247 u32 gp2; 248 /* GP2 value in ns*/ 249 s64 gp2_ns; 250 /* System (wall) time */ 251 ktime_t sys_time; 252 253 memset(xtstamp, 0, sizeof(struct system_device_crosststamp)); 254 255 ret = iwl_mld_get_crosstimestamp_fw(mld, &gp2, &sys_time); 256 if (ret) { 257 IWL_DEBUG_PTP(mld, 258 "PTP: fw get_crosstimestamp failed (ret=%d)\n", 259 ret); 260 return ret; 261 } 262 263 spin_lock_bh(&data->lock); 264 gp2_ns = iwl_mld_ptp_get_adj_time(mld, (u64)gp2 * NSEC_PER_USEC); 265 spin_unlock_bh(&data->lock); 266 267 IWL_DEBUG_PTP(mld, 268 "Got Sync Time: GP2:%u, last_GP2: %u, GP2_ns: %lld, sys_time: %lld\n", 269 gp2, mld->ptp_data.last_gp2, gp2_ns, (s64)sys_time); 270 271 /* System monotonic raw time is not used */ 272 xtstamp->device = ns_to_ktime(gp2_ns); 273 xtstamp->sys_realtime = sys_time; 274 275 return ret; 276} 277 278void iwl_mld_ptp_init(struct iwl_mld *mld) 279{ 280 if (WARN_ON(mld->ptp_data.ptp_clock)) 281 return; 282 283 spin_lock_init(&mld->ptp_data.lock); 284 INIT_DELAYED_WORK(&mld->ptp_data.dwork, iwl_mld_ptp_work); 285 286 mld->ptp_data.ptp_clock_info.owner = THIS_MODULE; 287 mld->ptp_data.ptp_clock_info.gettime64 = iwl_mld_ptp_gettime; 288 mld->ptp_data.ptp_clock_info.settime64 = iwl_mld_ptp_settime; 289 mld->ptp_data.ptp_clock_info.max_adj = 0x7fffffff; 290 mld->ptp_data.ptp_clock_info.adjtime = iwl_mld_ptp_adjtime; 291 mld->ptp_data.ptp_clock_info.adjfine = iwl_mld_ptp_adjfine; 292 mld->ptp_data.scaled_freq = PTP_SCALE_FACTOR; 293 mld->ptp_data.ptp_clock_info.getcrosststamp = 294 iwl_mld_phc_get_crosstimestamp; 295 296 /* Give a short 'friendly name' to identify the PHC clock */ 297 snprintf(mld->ptp_data.ptp_clock_info.name, 298 sizeof(mld->ptp_data.ptp_clock_info.name), 299 "%s", "iwlwifi-PTP"); 300 301 mld->ptp_data.ptp_clock = 302 ptp_clock_register(&mld->ptp_data.ptp_clock_info, mld->dev); 303 304 if (IS_ERR_OR_NULL(mld->ptp_data.ptp_clock)) { 305 IWL_ERR(mld, "Failed to register PHC clock (%ld)\n", 306 PTR_ERR(mld->ptp_data.ptp_clock)); 307 mld->ptp_data.ptp_clock = NULL; 308 } else { 309 IWL_DEBUG_INFO(mld, "Registered PHC clock: %s, with index: %d\n", 310 mld->ptp_data.ptp_clock_info.name, 311 ptp_clock_index(mld->ptp_data.ptp_clock)); 312 } 313} 314 315void iwl_mld_ptp_remove(struct iwl_mld *mld) 316{ 317 if (mld->ptp_data.ptp_clock) { 318 IWL_DEBUG_INFO(mld, "Unregistering PHC clock: %s, with index: %d\n", 319 mld->ptp_data.ptp_clock_info.name, 320 ptp_clock_index(mld->ptp_data.ptp_clock)); 321 322 ptp_clock_unregister(mld->ptp_data.ptp_clock); 323 mld->ptp_data.ptp_clock = NULL; 324 mld->ptp_data.last_gp2 = 0; 325 mld->ptp_data.wrap_counter = 0; 326 cancel_delayed_work_sync(&mld->ptp_data.dwork); 327 } 328}
~/
Read-only file preview.