Merge changes I87205d59,I91332da9 into wlan-cld3.driver.lnx.2.0-dev

* changes:
  qcacld-3.0: fix a potential spinlock lockup issue
  qcacld-3.0: add device attribute 'tsf'
diff --git a/core/hdd/src/wlan_hdd_tsf.c b/core/hdd/src/wlan_hdd_tsf.c
index 8965893..edb8a75 100644
--- a/core/hdd/src/wlan_hdd_tsf.c
+++ b/core/hdd/src/wlan_hdd_tsf.c
@@ -422,11 +422,21 @@
 	if (!adapter)
 		return;
 
+	/* host time is updated in IRQ context, it's always before target time,
+	 * and so no need to try update last_host_time at present;
+	 * since the interval of capturing TSF
+	 * (WLAN_HDD_CAPTURE_TSF_INTERVAL_SEC) is long enough, host and target
+	 * time are updated in pairs, and one by one, we can return here to
+	 * avoid requiring spin lock, and to speed up the IRQ processing.
+	 */
+	if (host_time > 0) {
+		adapter->cur_host_time = host_time;
+		return;
+	}
+
 	qdf_spin_lock_bh(&adapter->host_target_sync_lock);
 	if (target_time > 0)
 		adapter->cur_target_time = target_time;
-	if (host_time > 0)
-		adapter->cur_host_time = host_time;
 
 	sync_status = hdd_check_timestamp_status(adapter->last_target_time,
 						 adapter->last_host_time,
@@ -494,6 +504,34 @@
 	return 0;
 }
 
+static inline int hdd_uint64_plus(uint64_t x, uint64_t y, uint64_t *ret)
+{
+	if (!ret)
+		return -EINVAL;
+
+	if (x > (U64_MAX - y)) {
+		*ret = 0;
+		return -EINVAL;
+	}
+
+	*ret = x + y;
+	return 0;
+}
+
+static inline int hdd_uint64_minus(uint64_t x, uint64_t y, uint64_t *ret)
+{
+	if (!ret)
+		return -EINVAL;
+
+	if (x < y) {
+		*ret = 0;
+		return -EINVAL;
+	}
+
+	*ret = x - y;
+	return 0;
+}
+
 static inline int32_t hdd_get_hosttime_from_targettime(
 	hdd_adapter_t *adapter, uint64_t target_time,
 	uint64_t *host_time)
@@ -532,6 +570,37 @@
 	return ret;
 }
 
+static inline int32_t hdd_get_targettime_from_hosttime(
+	hdd_adapter_t *adapter, uint64_t host_time,
+	uint64_t *target_time)
+{
+	int32_t ret = -EINVAL;
+	bool in_cap_state;
+
+	if (!adapter || host_time == 0)
+		return ret;
+
+	in_cap_state = hdd_tsf_is_in_cap(adapter);
+	if (in_cap_state)
+		qdf_spin_lock_bh(&adapter->host_target_sync_lock);
+
+	if (host_time < adapter->last_host_time)
+		ret = hdd_uint64_minus(adapter->last_target_time,
+				       (adapter->last_host_time - host_time) /
+					HOST_TO_TARGET_TIME_RATIO,
+				       target_time);
+	else
+		ret = hdd_uint64_plus(adapter->last_target_time,
+				      (host_time - adapter->last_host_time) /
+					HOST_TO_TARGET_TIME_RATIO,
+				      target_time);
+
+	if (in_cap_state)
+		qdf_spin_unlock_bh(&adapter->host_target_sync_lock);
+
+	return ret;
+}
+
 static inline uint64_t hdd_get_monotonic_host_time(void)
 {
 	struct timespec ts;
@@ -540,6 +609,53 @@
 	return timespec_to_ns(&ts);
 }
 
+static ssize_t __hdd_wlan_tsf_show(struct device *dev,
+				   struct device_attribute *attr, char *buf)
+{
+	hdd_station_ctx_t *hdd_sta_ctx;
+	hdd_adapter_t *adapter;
+	ssize_t size;
+	uint64_t host_time, target_time;
+
+	struct net_device *net_dev = container_of(dev, struct net_device, dev);
+
+	adapter = (hdd_adapter_t *)(netdev_priv(net_dev));
+	if (adapter->magic != WLAN_HDD_ADAPTER_MAGIC)
+		return scnprintf(buf, PAGE_SIZE, "Invalid device\n");
+
+	if (!hdd_get_th_sync_status(adapter))
+		return scnprintf(buf, PAGE_SIZE,
+				 "TSF sync is not initialized\n");
+
+	hdd_sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(adapter);
+	if (eConnectionState_Associated != hdd_sta_ctx->conn_info.connState)
+		return scnprintf(buf, PAGE_SIZE, "NOT connected\n");
+
+	host_time = hdd_get_monotonic_host_time();
+	if (hdd_get_targettime_from_hosttime(adapter, host_time,
+					     &target_time))
+		size = scnprintf(buf, PAGE_SIZE, "Invalid timestamp\n");
+	else
+		size = scnprintf(buf, PAGE_SIZE, "%s%llu %llu %pM\n",
+				 buf, target_time, host_time,
+				 hdd_sta_ctx->conn_info.bssId.bytes);
+	return size;
+}
+
+static ssize_t hdd_wlan_tsf_show(struct device *dev,
+				 struct device_attribute *attr, char *buf)
+{
+	ssize_t ret;
+
+	cds_ssr_protect(__func__);
+	ret = __hdd_wlan_tsf_show(dev, attr, buf);
+	cds_ssr_unprotect(__func__);
+
+	return ret;
+}
+
+static DEVICE_ATTR(tsf, 0400, hdd_wlan_tsf_show, NULL);
+
 static void hdd_capture_tsf_timer_expired_handler(void *arg)
 {
 	uint32_t tsf_op_resp;
@@ -589,6 +705,7 @@
 {
 	QDF_STATUS ret;
 	hdd_context_t *hddctx;
+	struct net_device *net_dev;
 
 	if (!adapter)
 		return HDD_TSF_OP_FAIL;
@@ -622,6 +739,9 @@
 		goto fail;
 	}
 
+	net_dev = adapter->dev;
+		if (net_dev)
+			device_create_file(&net_dev->dev, &dev_attr_tsf);
 	hdd_set_th_sync_status(adapter, true);
 
 	return HDD_TSF_OP_SUCC;
@@ -634,6 +754,7 @@
 {
 	QDF_STATUS ret;
 	hdd_context_t *hddctx;
+	struct net_device *net_dev;
 
 	if (!adapter)
 		return HDD_TSF_OP_FAIL;
@@ -645,6 +766,13 @@
 
 	hdd_set_th_sync_status(adapter, false);
 
+	net_dev = adapter->dev;
+	if (net_dev) {
+		struct device *dev = &net_dev->dev;
+
+		device_remove_file(dev, &dev_attr_tsf);
+	}
+
 	ret = qdf_mc_timer_destroy(&adapter->host_target_sync_timer);
 	if (ret != QDF_STATUS_SUCCESS)
 		hdd_err("Failed to destroy timer, ret: %d", ret);