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