Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit b02821f4 authored by Yue Ma's avatar Yue Ma
Browse files

cnss2: Add host and device time sync support



Driver will get host time ticks and device time ticks at the same
time and calculate the timestamp offset in microsecond which will
be sent back to device periodically so that device time can be
synced with host time.

Change-Id: I44d5e3f8f48115c00920099da04b996cad8b7e3f
Signed-off-by: default avatarYue Ma <yuem@codeaurora.org>
parent 76b7b507
Loading
Loading
Loading
Loading
+23 −0
Original line number Diff line number Diff line
@@ -4,6 +4,7 @@
#ifndef _CNSS_MAIN_H
#define _CNSS_MAIN_H

#include <asm/arch_timer.h>
#include <linux/esoc_client.h>
#include <linux/etherdevice.h>
#include <linux/msm-bus.h>
@@ -19,6 +20,7 @@
#define QMI_WLFW_MAX_NUM_MEM_SEG	32
#define CNSS_RDDM_TIMEOUT_MS		20000
#define RECOVERY_TIMEOUT		60000
#define TIME_CLOCK_FREQ_HZ		19200000

#define CNSS_EVENT_SYNC   BIT(0)
#define CNSS_EVENT_UNINTERRUPTIBLE BIT(1)
@@ -302,6 +304,7 @@ struct cnss_plat_data {
	struct completion power_up_complete;
	struct completion cal_complete;
	struct mutex dev_lock; /* mutex for register access through debugfs */
	u32 device_freq_hz;
	u32 diag_reg_read_addr;
	u32 diag_reg_read_mem_type;
	u32 diag_reg_read_len;
@@ -318,6 +321,26 @@ struct cnss_plat_data {
	struct qmi_handle coex_qmi;
};

#ifdef CONFIG_ARCH_QCOM
static inline u64 cnss_get_host_timestamp(struct cnss_plat_data *plat_priv)
{
	u64 ticks = arch_counter_get_cntvct();

	do_div(ticks, TIME_CLOCK_FREQ_HZ / 100000);

	return ticks * 10;
}
#else
static inline u64 cnss_get_host_timestamp(struct cnss_plat_data *plat_priv)
{
	struct timespec ts;

	ktime_get_ts(&ts);

	return ((u64)ts.tv_sec * 1000000) + (ts.tv_nsec / 1000);
}
#endif

struct cnss_plat_data *cnss_get_plat_priv(struct platform_device *plat_dev);
int cnss_driver_event_post(struct cnss_plat_data *plat_priv,
			   enum cnss_driver_event_type type,
+154 −0
Original line number Diff line number Diff line
@@ -50,7 +50,10 @@
#define EMULATION_HW			0
#endif

#define TIME_SYNC_PERIOD_JF		msecs_to_jiffies(900000)

static DEFINE_SPINLOCK(pci_link_down_lock);
static DEFINE_SPINLOCK(pci_reg_window_lock);

#define MHI_TIMEOUT_OVERWRITE_MS	(plat_priv->ctrl_params.mhi_timeout)

@@ -96,7 +99,11 @@ static DEFINE_SPINLOCK(pci_link_down_lock);

#define SHADOW_REG_COUNT			36
#define QCA6390_PCIE_SHADOW_REG_VALUE_0		0x1E03024
#define QCA6390_PCIE_SHADOW_REG_VALUE_34	0x1E030AC
#define QCA6390_PCIE_SHADOW_REG_VALUE_35	0x1E030B0
#define QCA6390_WLAON_GLOBAL_COUNTER_CTRL3	0x1F80118
#define QCA6390_WLAON_GLOBAL_COUNTER_CTRL4	0x1F8011C
#define QCA6390_WLAON_GLOBAL_COUNTER_CTRL5	0x1F80120

#define SHADOW_REG_INTER_COUNT			43
#define QCA6390_PCIE_SHADOW_REG_INTER_0		0x1E05000
@@ -116,6 +123,9 @@ static DEFINE_SPINLOCK(pci_link_down_lock);
#define WINDOW_START				MAX_UNWINDOWED_ADDRESS
#define WINDOW_RANGE_MASK			0x7FFFF

#define QCA6390_TIME_SYNC_ENABLE		0x80000000
#define QCA6390_TIME_SYNC_CLEAR			0x0

static struct cnss_pci_reg ce_src[] = {
	{ "SRC_RING_BASE_LSB", QCA6390_CE_SRC_RING_BASE_LSB_OFFSET },
	{ "SRC_RING_BASE_MSB", QCA6390_CE_SRC_RING_BASE_MSB_OFFSET },
@@ -206,6 +216,7 @@ static int cnss_pci_reg_read(struct cnss_pci_data *pci_priv,
			     u32 offset, u32 *val)
{
	int ret;
	unsigned long flags;

	ret = cnss_pci_check_link_status(pci_priv);
	if (ret)
@@ -217,10 +228,39 @@ static int cnss_pci_reg_read(struct cnss_pci_data *pci_priv,
		return 0;
	}

	spin_lock_irqsave(&pci_reg_window_lock, flags);
	cnss_pci_select_window(pci_priv, offset);

	*val = readl_relaxed(pci_priv->bar + WINDOW_START +
			     (offset & WINDOW_RANGE_MASK));
	spin_unlock_irqrestore(&pci_reg_window_lock, flags);

	return 0;
}

static int cnss_pci_reg_write(struct cnss_pci_data *pci_priv, u32 offset,
			      u32 val)
{
	int ret;
	unsigned long flags;

	ret = cnss_pci_check_link_status(pci_priv);
	if (ret)
		return ret;

	if (pci_priv->pci_dev->device == QCA6174_DEVICE_ID ||
	    offset < MAX_UNWINDOWED_ADDRESS) {
		writel_relaxed(val, pci_priv->bar + offset);
		return 0;
	}

	spin_lock_irqsave(&pci_reg_window_lock, flags);
	cnss_pci_select_window(pci_priv, offset);

	writel_relaxed(val, pci_priv->bar + WINDOW_START +
		       (offset & WINDOW_RANGE_MASK));
	spin_unlock_irqrestore(&pci_reg_window_lock, flags);

	return 0;
}

@@ -415,6 +455,114 @@ int cnss_pci_is_device_down(struct device *dev)
}
EXPORT_SYMBOL(cnss_pci_is_device_down);

static int cnss_pci_get_device_timestamp(struct cnss_pci_data *pci_priv,
					 u64 *time_us)
{
	struct cnss_plat_data *plat_priv = pci_priv->plat_priv;
	u32 low, high;
	u64 device_ticks;

	if (!plat_priv->device_freq_hz) {
		cnss_pr_err("Device time clock frequency is not valid\n");
		return -EINVAL;
	}

	cnss_pci_reg_write(pci_priv, QCA6390_WLAON_GLOBAL_COUNTER_CTRL5,
			   QCA6390_TIME_SYNC_CLEAR);
	cnss_pci_reg_write(pci_priv, QCA6390_WLAON_GLOBAL_COUNTER_CTRL5,
			   QCA6390_TIME_SYNC_ENABLE);

	cnss_pci_reg_read(pci_priv, QCA6390_WLAON_GLOBAL_COUNTER_CTRL3, &low);
	cnss_pci_reg_read(pci_priv, QCA6390_WLAON_GLOBAL_COUNTER_CTRL4, &high);

	device_ticks = (u64)high << 32 | low;
	do_div(device_ticks, plat_priv->device_freq_hz / 100000);
	*time_us = device_ticks * 10;

	return 0;
}

static int cnss_pci_update_timestamp(struct cnss_pci_data *pci_priv)
{
	struct cnss_plat_data *plat_priv = pci_priv->plat_priv;
	u64 host_time_us, device_time_us, offset;
	u32 low, high;
	int ret;

	ret = cnss_pci_is_device_down(&pci_priv->pci_dev->dev);
	if (ret)
		return ret;

	ret = cnss_pci_check_link_status(pci_priv);
	if (ret)
		return ret;

	host_time_us = cnss_get_host_timestamp(plat_priv);
	ret = cnss_pci_get_device_timestamp(pci_priv, &device_time_us);
	if (ret)
		return ret;

	if (host_time_us < device_time_us) {
		cnss_pr_err("Host time (%llu us) is smaller than device time (%llu us), stop\n");
		return -EINVAL;
	}

	offset = host_time_us - device_time_us;
	cnss_pr_dbg("Host time = %llu us, device time = %llu us, offset = %llu us\n",
		    host_time_us, device_time_us, offset);

	low = offset & 0xFFFFFFFF;
	high = offset >> 32;

	cnss_pci_reg_write(pci_priv, QCA6390_PCIE_SHADOW_REG_VALUE_34, low);
	cnss_pci_reg_write(pci_priv, QCA6390_PCIE_SHADOW_REG_VALUE_35, high);

	return 0;
}

static void cnss_pci_time_sync_work_hdlr(struct work_struct *work)
{
	struct cnss_pci_data *pci_priv =
		container_of(work, struct cnss_pci_data, time_sync_work.work);

	cnss_pci_update_timestamp(pci_priv);

	schedule_delayed_work(&pci_priv->time_sync_work, TIME_SYNC_PERIOD_JF);
}

static int cnss_pci_start_time_sync_update(struct cnss_pci_data *pci_priv)
{
	struct cnss_plat_data *plat_priv = pci_priv->plat_priv;

	switch (pci_priv->device_id) {
	case QCA6390_DEVICE_ID:
		break;
	default:
		return -EOPNOTSUPP;
	}

	if (!plat_priv->device_freq_hz) {
		cnss_pr_dbg("Device time clock frequency is not valid, skip time sync\n");
		return -EINVAL;
	}

	cnss_pci_time_sync_work_hdlr(&pci_priv->time_sync_work.work);

	return 0;
}

static void cnss_pci_stop_time_sync_update(struct cnss_pci_data *pci_priv)
{
	switch (pci_priv->device_id) {
	case QCA6390_DEVICE_ID:
		break;
	default:
		return;
	}

	cancel_delayed_work_sync(&pci_priv->time_sync_work);
}

int cnss_pci_call_driver_probe(struct cnss_pci_data *pci_priv)
{
	int ret = 0;
@@ -475,6 +623,8 @@ int cnss_pci_call_driver_probe(struct cnss_pci_data *pci_priv)
		complete(&plat_priv->power_up_complete);
	}

	cnss_pci_start_time_sync_update(pci_priv);

	return 0;

out:
@@ -490,6 +640,8 @@ int cnss_pci_call_driver_remove(struct cnss_pci_data *pci_priv)

	plat_priv = pci_priv->plat_priv;

	cnss_pci_stop_time_sync_update(pci_priv);

	if (test_bit(CNSS_COLD_BOOT_CAL, &plat_priv->driver_state) ||
	    test_bit(CNSS_FW_BOOT_RECOVERY, &plat_priv->driver_state) ||
	    test_bit(CNSS_DRIVER_DEBUG, &plat_priv->driver_state)) {
@@ -2905,6 +3057,8 @@ static int cnss_pci_probe(struct pci_dev *pci_dev,
	case QCA6390_DEVICE_ID:
		timer_setup(&pci_priv->dev_rddm_timer,
			    cnss_dev_rddm_timeout_hdlr, 0);
		INIT_DELAYED_WORK(&pci_priv->time_sync_work,
				  cnss_pci_time_sync_work_hdlr);

		ret = cnss_pci_enable_msi(pci_priv);
		if (ret)
+1 −0
Original line number Diff line number Diff line
@@ -76,6 +76,7 @@ struct cnss_pci_data {
	unsigned long mhi_state;
	u32 remap_window;
	struct timer_list dev_rddm_timer;
	struct delayed_work time_sync_work;
	u8 disable_pc;
	struct cnss_pci_debug_reg *debug_reg;
};
+5 −0
Original line number Diff line number Diff line
@@ -395,6 +395,11 @@ int cnss_wlfw_tgt_cap_send_sync(struct cnss_plat_data *plat_priv)
			    plat_priv->cpr_info.voltage);
		cnss_update_cpr_info(plat_priv);
	}
	if (resp->time_freq_hz_valid) {
		plat_priv->device_freq_hz = resp->time_freq_hz;
		cnss_pr_dbg("Device frequency is %d HZ\n",
			    plat_priv->device_freq_hz);
	}
	if (resp->otp_version_valid)
		plat_priv->otp_version = resp->otp_version;