wifi: iwlwifi: mvm: add support for PTP HW clock (PHC)
Add support to enable/disable PHC clock. The PHC clock includes support for fetching the cross timestamp i.e. a non-atomic snapshot of the current time from the hardware (WiFi device) clock and system clock (wall-clock) simultaneously. Signed-off-by: Krishnanand Prabhu <krishnanand.prabhu@intel.com> Signed-off-by: Luca Coelho <luciano.coelho@intel.com> Signed-off-by: Gregory Greenman <gregory.greenman@intel.com> Link: https://lore.kernel.org/r/20230320122330.ae1d64f513b9.Ib3b6ad61c9fa2fc5908f1e0d6f59f4af6eec1a77@changeid Signed-off-by: Johannes Berg <johannes.berg@intel.com>
This commit is contained in:
parent
70664495e3
commit
1595ecce1c
@ -8,6 +8,7 @@ iwlmvm-y += tt.o offloading.o tdls.o
|
||||
iwlmvm-y += ftm-responder.o ftm-initiator.o
|
||||
iwlmvm-y += rfi.o
|
||||
iwlmvm-y += mld-key.o mld-mac.o link.o mld-sta.o mld-mac80211.o
|
||||
iwlmvm-y += ptp.o
|
||||
iwlmvm-$(CONFIG_IWLWIFI_DEBUGFS) += debugfs.o debugfs-vif.o
|
||||
iwlmvm-$(CONFIG_IWLWIFI_LEDS) += led.o
|
||||
iwlmvm-$(CONFIG_PM) += d3.o
|
||||
|
@ -1669,6 +1669,9 @@ int iwl_mvm_up(struct iwl_mvm *mvm)
|
||||
if (test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status))
|
||||
iwl_mvm_send_recovery_cmd(mvm, ERROR_RECOVERY_UPDATE_DB);
|
||||
|
||||
if (!mvm->ptp_data.ptp_clock)
|
||||
iwl_mvm_ptp_init(mvm);
|
||||
|
||||
if (iwl_acpi_get_eckv(mvm->dev, &mvm->ext_clock_valid))
|
||||
IWL_DEBUG_INFO(mvm, "ECKV table doesn't exist in BIOS\n");
|
||||
|
||||
|
@ -16,6 +16,8 @@
|
||||
#include <linux/thermal.h>
|
||||
#endif
|
||||
|
||||
#include <linux/ptp_clock_kernel.h>
|
||||
|
||||
#include <linux/ktime.h>
|
||||
|
||||
#include "iwl-op-mode.h"
|
||||
@ -770,6 +772,15 @@ struct iwl_mvm_dqa_txq_info {
|
||||
enum iwl_mvm_queue_status status;
|
||||
};
|
||||
|
||||
struct ptp_data {
|
||||
struct ptp_clock *ptp_clock;
|
||||
struct ptp_clock_info ptp_clock_info;
|
||||
/* keeps track of GP2 wrap-around */
|
||||
u32 last_gp2;
|
||||
u32 wrap_counter;
|
||||
struct delayed_work dwork;
|
||||
};
|
||||
|
||||
struct iwl_mvm {
|
||||
/* for logger access */
|
||||
struct device *dev;
|
||||
@ -1080,6 +1091,8 @@ struct iwl_mvm {
|
||||
|
||||
struct list_head resp_pasn_list;
|
||||
|
||||
struct ptp_data ptp_data;
|
||||
|
||||
struct {
|
||||
u8 range_resp;
|
||||
} cmd_ver;
|
||||
@ -2121,6 +2134,8 @@ void iwl_mvm_event_frame_timeout_callback(struct iwl_mvm *mvm,
|
||||
const struct ieee80211_sta *sta,
|
||||
u16 tid);
|
||||
|
||||
void iwl_mvm_ptp_init(struct iwl_mvm *mvm);
|
||||
void iwl_mvm_ptp_remove(struct iwl_mvm *mvm);
|
||||
int iwl_mvm_sar_select_profile(struct iwl_mvm *mvm, int prof_a, int prof_b);
|
||||
int iwl_mvm_get_sar_geo_profile(struct iwl_mvm *mvm);
|
||||
int iwl_mvm_ppag_send_cmd(struct iwl_mvm *mvm);
|
||||
|
@ -1435,6 +1435,8 @@ static void iwl_op_mode_mvm_stop(struct iwl_op_mode *op_mode)
|
||||
kfree(mvm->error_recovery_buf);
|
||||
mvm->error_recovery_buf = NULL;
|
||||
|
||||
iwl_mvm_ptp_remove(mvm);
|
||||
|
||||
iwl_trans_op_mode_leave(mvm->trans);
|
||||
|
||||
iwl_phy_db_free(mvm->phy_db);
|
||||
|
131
drivers/net/wireless/intel/iwlwifi/mvm/ptp.c
Normal file
131
drivers/net/wireless/intel/iwlwifi/mvm/ptp.c
Normal file
@ -0,0 +1,131 @@
|
||||
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
|
||||
/*
|
||||
* Copyright (C) 2021 - 2023 Intel Corporation
|
||||
*/
|
||||
|
||||
#include "mvm.h"
|
||||
#include "iwl-debug.h"
|
||||
#include <linux/timekeeping.h>
|
||||
|
||||
#define IWL_PTP_GP2_WRAP 0x100000000ULL
|
||||
#define IWL_PTP_WRAP_TIME (3600 * HZ)
|
||||
|
||||
static void iwl_mvm_ptp_update_new_read(struct iwl_mvm *mvm, u32 gp2)
|
||||
{
|
||||
if (gp2 < mvm->ptp_data.last_gp2) {
|
||||
mvm->ptp_data.wrap_counter++;
|
||||
IWL_DEBUG_INFO(mvm,
|
||||
"PTP: wraparound detected (new counter=%u)\n",
|
||||
mvm->ptp_data.wrap_counter);
|
||||
}
|
||||
|
||||
mvm->ptp_data.last_gp2 = gp2;
|
||||
schedule_delayed_work(&mvm->ptp_data.dwork, IWL_PTP_WRAP_TIME);
|
||||
}
|
||||
|
||||
static int
|
||||
iwl_mvm_phc_get_crosstimestamp(struct ptp_clock_info *ptp,
|
||||
struct system_device_crosststamp *xtstamp)
|
||||
{
|
||||
struct iwl_mvm *mvm = container_of(ptp, struct iwl_mvm,
|
||||
ptp_data.ptp_clock_info);
|
||||
/* Raw value read from GP2 register in usec */
|
||||
u32 gp2;
|
||||
/* GP2 value in ns*/
|
||||
s64 gp2_ns;
|
||||
/* System (wall) time */
|
||||
ktime_t sys_time;
|
||||
|
||||
memset(xtstamp, 0, sizeof(struct system_device_crosststamp));
|
||||
|
||||
if (!mvm->ptp_data.ptp_clock) {
|
||||
IWL_ERR(mvm, "No PHC clock registered\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
iwl_mvm_get_sync_time(mvm, CLOCK_REALTIME, &gp2, NULL, &sys_time);
|
||||
|
||||
iwl_mvm_ptp_update_new_read(mvm, gp2);
|
||||
|
||||
gp2_ns = (gp2 + (mvm->ptp_data.wrap_counter * IWL_PTP_GP2_WRAP)) *
|
||||
NSEC_PER_USEC;
|
||||
|
||||
IWL_INFO(mvm, "Got Sync Time: GP2:%u, last_GP2: %u, GP2_ns: %lld, sys_time: %lld\n",
|
||||
gp2, mvm->ptp_data.last_gp2, gp2_ns, (s64)sys_time);
|
||||
|
||||
/* System monotonic raw time is not used */
|
||||
xtstamp->device = (ktime_t)gp2_ns;
|
||||
xtstamp->sys_realtime = sys_time;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void iwl_mvm_ptp_work(struct work_struct *wk)
|
||||
{
|
||||
struct iwl_mvm *mvm = container_of(wk, struct iwl_mvm,
|
||||
ptp_data.dwork.work);
|
||||
u32 gp2;
|
||||
|
||||
mutex_lock(&mvm->mutex);
|
||||
gp2 = iwl_mvm_get_systime(mvm);
|
||||
iwl_mvm_ptp_update_new_read(mvm, gp2);
|
||||
mutex_unlock(&mvm->mutex);
|
||||
}
|
||||
|
||||
/* iwl_mvm_ptp_init - initialize PTP for devices which support it.
|
||||
* @mvm: internal mvm structure, see &struct iwl_mvm.
|
||||
*
|
||||
* Performs the required steps for enabling PTP support.
|
||||
*/
|
||||
void iwl_mvm_ptp_init(struct iwl_mvm *mvm)
|
||||
{
|
||||
/* Warn if the interface already has a ptp_clock defined */
|
||||
if (WARN_ON(mvm->ptp_data.ptp_clock))
|
||||
return;
|
||||
|
||||
mvm->ptp_data.ptp_clock_info.owner = THIS_MODULE;
|
||||
mvm->ptp_data.ptp_clock_info.max_adj = 0x7fffffff;
|
||||
mvm->ptp_data.ptp_clock_info.getcrosststamp =
|
||||
iwl_mvm_phc_get_crosstimestamp;
|
||||
|
||||
/* Give a short 'friendly name' to identify the PHC clock */
|
||||
snprintf(mvm->ptp_data.ptp_clock_info.name,
|
||||
sizeof(mvm->ptp_data.ptp_clock_info.name),
|
||||
"%s", "iwlwifi-PTP");
|
||||
|
||||
INIT_DELAYED_WORK(&mvm->ptp_data.dwork, iwl_mvm_ptp_work);
|
||||
|
||||
mvm->ptp_data.ptp_clock =
|
||||
ptp_clock_register(&mvm->ptp_data.ptp_clock_info, mvm->dev);
|
||||
|
||||
if (IS_ERR(mvm->ptp_data.ptp_clock)) {
|
||||
IWL_ERR(mvm, "Failed to register PHC clock (%ld)\n",
|
||||
PTR_ERR(mvm->ptp_data.ptp_clock));
|
||||
mvm->ptp_data.ptp_clock = NULL;
|
||||
} else if (mvm->ptp_data.ptp_clock) {
|
||||
IWL_INFO(mvm, "Registered PHC clock: %s, with index: %d\n",
|
||||
mvm->ptp_data.ptp_clock_info.name,
|
||||
ptp_clock_index(mvm->ptp_data.ptp_clock));
|
||||
}
|
||||
}
|
||||
|
||||
/* iwl_mvm_ptp_remove - disable PTP device.
|
||||
* @mvm: internal mvm structure, see &struct iwl_mvm.
|
||||
*
|
||||
* Disable PTP support.
|
||||
*/
|
||||
void iwl_mvm_ptp_remove(struct iwl_mvm *mvm)
|
||||
{
|
||||
if (mvm->ptp_data.ptp_clock) {
|
||||
IWL_INFO(mvm, "Unregistering PHC clock: %s, with index: %d\n",
|
||||
mvm->ptp_data.ptp_clock_info.name,
|
||||
ptp_clock_index(mvm->ptp_data.ptp_clock));
|
||||
|
||||
ptp_clock_unregister(mvm->ptp_data.ptp_clock);
|
||||
mvm->ptp_data.ptp_clock = NULL;
|
||||
memset(&mvm->ptp_data.ptp_clock_info, 0,
|
||||
sizeof(mvm->ptp_data.ptp_clock_info));
|
||||
mvm->ptp_data.last_gp2 = 0;
|
||||
cancel_delayed_work_sync(&mvm->ptp_data.dwork);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user