mirror of
				git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
				synced 2025-09-04 20:19:47 +08:00 
			
		
		
		
	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 += ftm-responder.o ftm-initiator.o | ||||||
| iwlmvm-y += rfi.o | iwlmvm-y += rfi.o | ||||||
| iwlmvm-y += mld-key.o mld-mac.o link.o mld-sta.o mld-mac80211.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_DEBUGFS) += debugfs.o debugfs-vif.o | ||||||
| iwlmvm-$(CONFIG_IWLWIFI_LEDS) += led.o | iwlmvm-$(CONFIG_IWLWIFI_LEDS) += led.o | ||||||
| iwlmvm-$(CONFIG_PM) += d3.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)) | 	if (test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)) | ||||||
| 		iwl_mvm_send_recovery_cmd(mvm, ERROR_RECOVERY_UPDATE_DB); | 		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)) | 	if (iwl_acpi_get_eckv(mvm->dev, &mvm->ext_clock_valid)) | ||||||
| 		IWL_DEBUG_INFO(mvm, "ECKV table doesn't exist in BIOS\n"); | 		IWL_DEBUG_INFO(mvm, "ECKV table doesn't exist in BIOS\n"); | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -16,6 +16,8 @@ | |||||||
| #include <linux/thermal.h> | #include <linux/thermal.h> | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
|  | #include <linux/ptp_clock_kernel.h> | ||||||
|  | 
 | ||||||
| #include <linux/ktime.h> | #include <linux/ktime.h> | ||||||
| 
 | 
 | ||||||
| #include "iwl-op-mode.h" | #include "iwl-op-mode.h" | ||||||
| @ -770,6 +772,15 @@ struct iwl_mvm_dqa_txq_info { | |||||||
| 	enum iwl_mvm_queue_status status; | 	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 { | struct iwl_mvm { | ||||||
| 	/* for logger access */ | 	/* for logger access */ | ||||||
| 	struct device *dev; | 	struct device *dev; | ||||||
| @ -1080,6 +1091,8 @@ struct iwl_mvm { | |||||||
| 
 | 
 | ||||||
| 	struct list_head resp_pasn_list; | 	struct list_head resp_pasn_list; | ||||||
| 
 | 
 | ||||||
|  | 	struct ptp_data ptp_data; | ||||||
|  | 
 | ||||||
| 	struct { | 	struct { | ||||||
| 		u8 range_resp; | 		u8 range_resp; | ||||||
| 	} cmd_ver; | 	} cmd_ver; | ||||||
| @ -2121,6 +2134,8 @@ void iwl_mvm_event_frame_timeout_callback(struct iwl_mvm *mvm, | |||||||
| 					  const struct ieee80211_sta *sta, | 					  const struct ieee80211_sta *sta, | ||||||
| 					  u16 tid); | 					  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_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_get_sar_geo_profile(struct iwl_mvm *mvm); | ||||||
| int iwl_mvm_ppag_send_cmd(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); | 	kfree(mvm->error_recovery_buf); | ||||||
| 	mvm->error_recovery_buf = NULL; | 	mvm->error_recovery_buf = NULL; | ||||||
| 
 | 
 | ||||||
|  | 	iwl_mvm_ptp_remove(mvm); | ||||||
|  | 
 | ||||||
| 	iwl_trans_op_mode_leave(mvm->trans); | 	iwl_trans_op_mode_leave(mvm->trans); | ||||||
| 
 | 
 | ||||||
| 	iwl_phy_db_free(mvm->phy_db); | 	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
	 Krishnanand Prabhu
						Krishnanand Prabhu