xref: /OK3568_Linux_fs/external/rkwifibt/drivers/bcmdhd/dhd_timesync.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1 /**
2  * @file Broadcom Dongle Host Driver (DHD), time sync protocol handler
3  *
4  * timesync mesasages are exchanged between the host and device to synchronize the source time
5  * for ingress and egress packets.
6  *
7  * Copyright (C) 2020, Broadcom.
8  *
9  *      Unless you and Broadcom execute a separate written software license
10  * agreement governing use of this software, this software is licensed to you
11  * under the terms of the GNU General Public License version 2 (the "GPL"),
12  * available at http://www.broadcom.com/licenses/GPLv2.php, with the
13  * following added to such license:
14  *
15  *      As a special exception, the copyright holders of this software give you
16  * permission to link this software with independent modules, and to copy and
17  * distribute the resulting executable under terms of your choice, provided that
18  * you also meet, for each linked independent module, the terms and conditions of
19  * the license of that module.  An independent module is a module which is not
20  * derived from this software.  The special exception does not apply to any
21  * modifications of the software.
22  *
23  *
24  * <<Broadcom-WL-IPTag/Open:>>
25  *
26  * $Id$:
27  */
28 
29 #include <typedefs.h>
30 #include <bcmutils.h>
31 #include <bcmendian.h>
32 #include <bcmdevs.h>
33 #include <dngl_stats.h>
34 #include <dhd.h>
35 #include <dhd_bus.h>
36 #include <dhd_proto.h>
37 #include <dhd_dbg.h>
38 #include <dhd_timesync.h>
39 #include <bcmpcie.h>
40 #include <bcmmsgbuf.h>
41 
42 extern void dhd_msgbuf_delay_post_ts_bufs(dhd_pub_t *dhd);
43 
44 #define MAX_FW_CLKINFO_TYPES		8
45 #define MAX_SIZE_FW_CLKINFO_TYPE	(MAX_FW_CLKINFO_TYPES * sizeof(ts_fw_clock_info_t))
46 
47 #define MAX_FW_TS_LOG_SAMPLES		64
48 
49 #define BCMMSGBUF_HOST_TS_BADTAG	0xF0
50 
51 #define DHD_DEFAULT_TIMESYNC_TIMER_VALUE 20	/* ms */
52 #define DHD_DEFAULT_TIMESYNC_TIMER_VALUE_MAX 9000	/* ms */
53 
54 #define MAX_TS_LOG_SAMPLES_DATA		128
55 #define TS_NODROP_CONFIG_TO		1
56 #define TS_DROP_CONFIG_TO		5
57 
58 typedef struct clksrc_ts_log {
59 	uchar name[4];
60 	uint32 inuse;
61 	ts_timestamp_srcid_t log[MAX_FW_TS_LOG_SAMPLES];
62 } clksrc_ts_log_t;
63 
64 typedef struct clk_ts_log {
65 	uint32	clk_ts_inited;
66 	uint32 cur_idx;
67 	uint32	seqnum[MAX_FW_TS_LOG_SAMPLES];
68 	clksrc_ts_log_t ts_log[MAX_CLKSRC_ID+1];
69 } clk_ts_log_t;
70 
71 typedef struct dhd_ts_xt_id {
72 	uint16	host_timestamping_config;
73 	uint16	host_clock_selection;
74 	uint16	host_clk_info;
75 	uint16	d2h_clk_correction;
76 } dhd_ts_xt_id_t;
77 
78 typedef struct dhd_ts_log_ts_item {
79 	uint16	flowid; /* interface, Flow ID */
80 	uint8	intf; /* interface */
81 	uint8	rsvd;
82 	uint32	ts_low; /* time stamp values */
83 	uint32	ts_high; /* time stamp values */
84 	uint32	proto;
85 	uint32	t1;
86 	uint32	t2;
87 } dhd_ts_log_ts_item_t;
88 
89 typedef struct dhd_ts_log_ts {
90 	uint32	max_idx;
91 	uint32	cur_idx;
92 	dhd_ts_log_ts_item_t	ts_log[MAX_TS_LOG_SAMPLES_DATA];
93 } dhd_ts_log_ts_t;
94 
95 #define MAX_BUF_SIZE_HOST_CLOCK_INFO	512
96 
97 #define HOST_TS_CONFIG_FW_TIMESTAMP_PERIOD_DEFAULT	1000
98 
99 struct dhd_ts {
100 	dhd_pub_t *dhdp;
101 	osl_t	*osh;
102 	uint32	xt_id;
103 	uint16	host_ts_capture_cnt;
104 	uint32	fw_ts_capture_cnt;
105 	uint32	fw_ts_disc_cnt;
106 	uint32	h_clkid_min;
107 	uint32	h_clkid_max;
108 	uint32	h_tsconf_period;
109 
110 	/* sould these be per clock source */
111 	ts_correction_m_t	correction_m;
112 	ts_correction_m_t	correction_b;
113 
114 	ts_fw_clock_info_t	fw_tlv[MAX_FW_CLKINFO_TYPES];
115 	uint32			fw_tlv_len;
116 	clk_ts_log_t 	fw_ts_log;
117 	uchar		host_ts_host_clk_info_buffer[MAX_BUF_SIZE_HOST_CLOCK_INFO];
118 	bool		host_ts_host_clk_info_buffer_in_use;
119 	dhd_ts_xt_id_t	xt_ids;
120 	uint32	active_ipc_version;
121 
122 	uint32 	fwts2hsts_delay;
123 	uint32 	fwts2hsts_delay_wdcount;
124 	uint32	ts_watchdog_calls;
125 	uint64  last_ts_watchdog_time;
126 	uint32	pending_requests;
127 
128 	dhd_ts_log_ts_t	tx_timestamps;
129 	dhd_ts_log_ts_t	rx_timestamps;
130 	/* outside modules could stop timesync independent of the user config */
131 	bool timesync_disabled;
132 	uint32	host_reset_cnt;
133 	bool nodrop_config;
134 
135 	uint32	suspend_req;
136 	uint32	resume_req;
137 };
138 struct dhd_ts *g_dhd_ts;
139 static uint32 dhd_timesync_send_D2H_clk_correction(dhd_ts_t *ts);
140 static uint32 dhd_timesync_send_host_clk_info(dhd_ts_t *ts);
141 static uint32 dhd_timesync_send_host_clock_selection(dhd_ts_t *ts);
142 static uint32 dhd_timesync_send_host_timestamping_config(dhd_ts_t *ts, bool inject_err);
143 static void dhd_timesync_ts_log_dump_item(dhd_ts_log_ts_t *tsl, struct bcmstrbuf *b);
144 
145 /* Check for and handle local prot-specific iovar commands */
146 
147 enum {
148 	IOV_TS_INFO_DUMP,
149 	IOV_TS_TX_TS_DUMP,
150 	IOV_TS_RX_TS_DUMP,
151 	IOV_TS_FW_CLKINFO_DUMP,
152 	IOV_TS_HCLK_CLKID_MIN,
153 	IOV_TS_HCLK_CLKID_MAX,
154 	IOV_TS_HTSCONF_PERIOD,
155 	IOV_TS_SEND_TSCONFIG,
156 	IOV_TS_SEND_HCLK_SEL,
157 	IOV_TS_SEND_HCLK_INFO,
158 	IOV_TS_SEND_D2H_CRCT,
159 	IOV_TS_TXS_LOG,
160 	IOV_TS_RXS_LOG,
161 	IOV_TS_INJECT_BAD_XTID,
162 	IOV_TS_INJECT_BAD_TAG,
163 	IOV_TS_FWTS2HSTS_DELAY,
164 	IOV_TS_NODROP_CONFIG,
165 	IOV_TS_CLEAR_LOGS,
166 	IOV_TS_NO_RETRY,
167 	IOV_TS_NO_AGGR,
168 	IOV_TS_FIXED_RATE,
169 	IOV_LAST
170 };
171 const bcm_iovar_t dhd_ts_iovars[] = {
172 	{"ts_info_dump", IOV_TS_INFO_DUMP,	0,	0,	IOVT_BUFFER,	DHD_IOCTL_MAXLEN },
173 	{"ts_tx_ts_dump", IOV_TS_TX_TS_DUMP,	0,	0,	IOVT_BUFFER,	DHD_IOCTL_MAXLEN },
174 	{"ts_rx_ts_dump", IOV_TS_RX_TS_DUMP,	0,	0,	IOVT_BUFFER,	DHD_IOCTL_MAXLEN },
175 	{"ts_fw_clkinfo_dump", IOV_TS_FW_CLKINFO_DUMP, 0, 0, IOVT_BUFFER, DHD_IOCTL_MAXLEN },
176 	{"ts_hclk_clkid_min",	IOV_TS_HCLK_CLKID_MIN,	0,	0,	IOVT_UINT32,	0 },
177 	{"ts_hclk_clkid_max",	IOV_TS_HCLK_CLKID_MAX,	0,	0,	IOVT_UINT32,	0 },
178 	{"ts_htsconf_period",	IOV_TS_HTSCONF_PERIOD,	0,	0,	IOVT_UINT32,	0 },
179 	{"ts_send_tsconfig",	IOV_TS_SEND_TSCONFIG,	0,	0,	IOVT_UINT32,	0 },
180 	{"ts_send_hostclk_sel",	IOV_TS_SEND_HCLK_SEL,	0,	0,	IOVT_UINT32,	0 },
181 	{"ts_send_hostclk_info", IOV_TS_SEND_HCLK_INFO,	0,	0,	IOVT_UINT32,	0 },
182 	{"ts_send_d2h_corect ",	IOV_TS_SEND_D2H_CRCT,	0,	0,	IOVT_UINT32,	0 },
183 	{"ts_txs_log",		IOV_TS_TXS_LOG,		0,	0,	IOVT_UINT32,	0 },
184 	{"ts_rxs_log",		IOV_TS_RXS_LOG,		0,	0,	IOVT_UINT32,	0 },
185 
186 	/* error injection cases */
187 	{"ts_inject_bad_xtid",	IOV_TS_INJECT_BAD_XTID,	0,	0,	IOVT_UINT32,	0 },
188 	{"ts_inject_bad_tag",	IOV_TS_INJECT_BAD_TAG,	0,	0,	IOVT_UINT32,	0 },
189 	{"ts_fwts2hsts_delay",	IOV_TS_FWTS2HSTS_DELAY,	0,	0,	IOVT_UINT32,	0 },
190 	{"ts_nodrop_config",	IOV_TS_NODROP_CONFIG,	0,	0,	IOVT_UINT32,	0 },
191 	{"ts_clear_logs",	IOV_TS_CLEAR_LOGS,	0,	0,	IOVT_UINT32,	0 },
192 	{"ts_set_no_retry",	IOV_TS_NO_RETRY,	0,	0,	IOVT_UINT32,	0 },
193 	{"ts_set_no_aggr",	IOV_TS_NO_AGGR,		0,	0,	IOVT_UINT32,	0 },
194 	{"ts_set_fixed_rate",	IOV_TS_FIXED_RATE,	0,	0,	IOVT_UINT32,	0 },
195 
196 	{NULL, 0, 0, 0,	0, 0 }
197 };
198 
199 static int dhd_ts_fw_clksrc_dump(dhd_ts_t *ts, char *buf, int buflen);
200 #ifdef CONFIG_PROC_FS
201 static int dhd_open_proc_ts_fw_clk_dump(struct inode *inode, struct file *file);
202 ssize_t dhd_read_proc_ts_fw_clk_dump(struct file *file, char *user_buf, size_t count, loff_t *loff);
203 static int dhd_open_proc_ts_tx_dump(struct inode *inode, struct file *file);
204 ssize_t dhd_read_proc_ts_tx_dump(struct file *file, char *user_buf, size_t count, loff_t *loff);
205 static int dhd_open_proc_ts_rx_dump(struct inode *inode, struct file *file);
206 ssize_t dhd_read_proc_ts_rx_dump(struct file *file, char *user_buf, size_t count, loff_t *loff);
207 
208 static int
dhd_open_proc_ts_fw_clk_dump(struct inode * inode,struct file * file)209 dhd_open_proc_ts_fw_clk_dump(struct inode *inode, struct file *file)
210 {
211 	return single_open(file, 0, NULL);
212 }
213 ssize_t
dhd_read_proc_ts_fw_clk_dump(struct file * file,char * user_buf,size_t count,loff_t * loff)214 dhd_read_proc_ts_fw_clk_dump(struct file *file, char *user_buf, size_t count, loff_t *loff)
215 {
216 	dhd_ts_t *ts;
217 	char *buf;
218 	ssize_t ret = 0;
219 
220 	ts = g_dhd_ts;
221 	if (ts == NULL) {
222 		return -EAGAIN;
223 	}
224 	if (DHD_BUS_CHECK_SUSPEND_OR_SUSPEND_IN_PROGRESS(ts->dhdp)) {
225 		DHD_INFO(("%s bus is in suspend or suspend in progress\n", __func__));
226 		return -EAGAIN;
227 	}
228 	if (DHD_BUS_CHECK_DOWN_OR_DOWN_IN_PROGRESS(ts->dhdp)) {
229 		DHD_ERROR(("%s rmmod in progress\n", __func__));
230 		return -ENOENT;
231 	}
232 	buf = kmalloc(count, GFP_KERNEL);
233 	if (buf == NULL) {
234 		DHD_ERROR(("%s failed to allocate buf with size %zu\n", __func__, count));
235 		return -ENOMEM;
236 	}
237 	ret = dhd_ts_fw_clksrc_dump(ts, buf, count);
238 	if (ret < 0) {
239 		return 0;
240 	}
241 	ret = simple_read_from_buffer(user_buf, count, loff, buf, (count - ret));
242 	kfree(buf);
243 	return ret;
244 }
dhd_open_proc_ts_tx_dump(struct inode * inode,struct file * file)245 static int dhd_open_proc_ts_tx_dump(struct inode *inode, struct file *file)
246 {
247 	return single_open(file, 0, NULL);
248 }
249 ssize_t
dhd_read_proc_ts_tx_dump(struct file * file,char * user_buf,size_t count,loff_t * loff)250 dhd_read_proc_ts_tx_dump(struct file *file, char *user_buf, size_t count, loff_t *loff)
251 {
252 	dhd_ts_t *ts;
253 	char *buf;
254 	ssize_t ret = 0;
255 	struct bcmstrbuf strbuf;
256 
257 	ts = g_dhd_ts;
258 	if (ts == NULL) {
259 		return -EAGAIN;
260 	}
261 	if (DHD_BUS_CHECK_SUSPEND_OR_SUSPEND_IN_PROGRESS(ts->dhdp)) {
262 		DHD_INFO(("%s bus is in suspend or suspend in progress\n", __func__));
263 		return -EAGAIN;
264 	}
265 	if (DHD_BUS_CHECK_DOWN_OR_DOWN_IN_PROGRESS(ts->dhdp)) {
266 		DHD_ERROR(("%s rmmod in progress\n", __func__));
267 		return -ENOENT;
268 	}
269 	buf = kmalloc(count, GFP_KERNEL);
270 	if (buf == NULL) {
271 		DHD_ERROR(("%s failed to allocate buf with size %zu\n", __func__, count));
272 		return -ENOMEM;
273 	}
274 	bcm_binit(&strbuf, buf, count);
275 	bcm_bprintf(&strbuf, "Tx Log dump\n");
276 	dhd_timesync_ts_log_dump_item(&ts->tx_timestamps, &strbuf);
277 	ret = simple_read_from_buffer(user_buf, count, loff, buf, (count - strbuf.size));
278 	kfree(buf);
279 	return ret;
280 }
281 
dhd_open_proc_ts_rx_dump(struct inode * inode,struct file * file)282 static int dhd_open_proc_ts_rx_dump(struct inode *inode, struct file *file)
283 {
284 	return single_open(file, 0, NULL);
285 }
286 
287 ssize_t
dhd_read_proc_ts_rx_dump(struct file * file,char * user_buf,size_t count,loff_t * loff)288 dhd_read_proc_ts_rx_dump(struct file *file, char *user_buf, size_t count, loff_t *loff)
289 {
290 	dhd_ts_t *ts;
291 	char *buf;
292 	ssize_t ret = 0;
293 	struct bcmstrbuf strbuf;
294 
295 	ts = g_dhd_ts;
296 	if (ts == NULL) {
297 		return -EAGAIN;
298 	}
299 	if (DHD_BUS_CHECK_SUSPEND_OR_SUSPEND_IN_PROGRESS(ts->dhdp)) {
300 		DHD_INFO(("%s bus is in suspend or suspend in progress\n", __func__));
301 		return -EAGAIN;
302 	}
303 	if (DHD_BUS_CHECK_DOWN_OR_DOWN_IN_PROGRESS(ts->dhdp)) {
304 		DHD_ERROR(("%s rmmod in progress\n", __func__));
305 		return -ENOENT;
306 	}
307 	buf = kmalloc(count, GFP_KERNEL);
308 	if (buf == NULL) {
309 		DHD_ERROR(("%s failed to allocate buf with size %zu\n", __func__, count));
310 		return -ENOMEM;
311 	}
312 	bcm_binit(&strbuf, buf, count);
313 	bcm_bprintf(&strbuf, "Rx Log dump\n");
314 	dhd_timesync_ts_log_dump_item(&ts->rx_timestamps, &strbuf);
315 	ret = simple_read_from_buffer(user_buf, count, loff, buf, (count - strbuf.size));
316 	kfree(buf);
317 	return ret;
318 }
319 
320 static const struct file_operations proc_fops_ts_fw_clk_dump = {
321 	.read = dhd_read_proc_ts_fw_clk_dump,
322 	.open = dhd_open_proc_ts_fw_clk_dump,
323 	.release = seq_release,
324 };
325 static const struct file_operations proc_fops_ts_tx_dump = {
326 	.read = dhd_read_proc_ts_tx_dump,
327 	.open = dhd_open_proc_ts_tx_dump,
328 	.release = seq_release,
329 };
330 static const struct file_operations proc_fops_ts_rx_dump = {
331 	.read = dhd_read_proc_ts_rx_dump,
332 	.open = dhd_open_proc_ts_rx_dump,
333 	.release = seq_release,
334 };
335 #endif /* CONFIG_PROC_FS */
336 
337 int
dhd_timesync_detach(dhd_pub_t * dhdp)338 dhd_timesync_detach(dhd_pub_t *dhdp)
339 {
340 	dhd_ts_t *ts;
341 
342 	DHD_TRACE(("%s: %d\n", __FUNCTION__, __LINE__));
343 
344 	if (!dhdp) {
345 		return BCME_OK;
346 	}
347 	ts = dhdp->ts;
348 #ifdef CONFIG_PROC_FS
349 	remove_proc_entry("ts_fw_clk_dump", NULL);
350 	remove_proc_entry("ts_tx_dump", NULL);
351 	remove_proc_entry("ts_rx_dump", NULL);
352 #endif /* CONFIG_PROC_FS */
353 #ifndef CONFIG_DHD_USE_STATIC_BUF
354 		MFREE(dhdp->osh, ts, sizeof(dhd_ts_t));
355 #endif /* CONFIG_DHD_USE_STATIC_BUF */
356 	g_dhd_ts = NULL;
357 	dhdp->ts = NULL;
358 	DHD_INFO(("Deallocated DHD TS\n"));
359 	return BCME_OK;
360 }
361 int
dhd_timesync_attach(dhd_pub_t * dhdp)362 dhd_timesync_attach(dhd_pub_t *dhdp)
363 {
364 	dhd_ts_t *ts;
365 
366 	DHD_TRACE(("%s: %d\n", __FUNCTION__, __LINE__));
367 	/* Allocate prot structure */
368 	if (!(ts = (dhd_ts_t *)DHD_OS_PREALLOC(dhdp, DHD_PREALLOC_PROT,
369 		sizeof(dhd_ts_t)))) {
370 		DHD_ERROR(("%s: kmalloc failed\n", __FUNCTION__));
371 		goto fail;
372 	}
373 	memset(ts, 0, sizeof(*ts));
374 
375 	g_dhd_ts = ts;
376 	ts->osh = dhdp->osh;
377 	dhdp->ts = ts;
378 	ts->dhdp = dhdp;
379 
380 	ts->correction_m.low = 1;
381 	ts->correction_m.high = 1;
382 
383 	ts->correction_b.low = 0;
384 	ts->correction_m.high = 0;
385 
386 	ts->fwts2hsts_delay = DHD_DEFAULT_TIMESYNC_TIMER_VALUE;
387 	ts->fwts2hsts_delay_wdcount = 0;
388 
389 	ts->tx_timestamps.max_idx = MAX_TS_LOG_SAMPLES_DATA;
390 	ts->rx_timestamps.max_idx = MAX_TS_LOG_SAMPLES_DATA;
391 
392 	ts->xt_id = 1;
393 
394 	DHD_INFO(("allocated DHD TS\n"));
395 
396 #ifdef CONFIG_PROC_FS
397 	if (proc_create("ts_fw_clk_dump", S_IRUSR, NULL, &proc_fops_ts_fw_clk_dump) == NULL) {
398 		DHD_ERROR(("Failed to create /proc/ts_fw_clk_dump procfs interface\n"));
399 	}
400 	if (proc_create("ts_tx_dump", S_IRUSR, NULL, &proc_fops_ts_tx_dump) == NULL) {
401 		DHD_ERROR(("Failed to create /proc/ts_tx_dump procfs interface\n"));
402 	}
403 	if (proc_create("ts_rx_dump", S_IRUSR, NULL, &proc_fops_ts_rx_dump) == NULL) {
404 		DHD_ERROR(("Failed to create /proc/ts_rx_dump procfs interface\n"));
405 	}
406 #endif /* CONFIG_PROC_FS */
407 
408 	return BCME_OK;
409 
410 fail:
411 	if (dhdp->ts != NULL) {
412 		dhd_timesync_detach(dhdp);
413 	}
414 	return BCME_NOMEM;
415 }
416 
417 static void
dhd_timesync_ts_log_dump_item(dhd_ts_log_ts_t * tsl,struct bcmstrbuf * b)418 dhd_timesync_ts_log_dump_item(dhd_ts_log_ts_t *tsl, struct bcmstrbuf *b)
419 {
420 	uint32 i = 0;
421 
422 	bcm_bprintf(b, "Max_idx: %d, cur_idx %d\n", tsl->max_idx, tsl->cur_idx);
423 	for (i = 0; i < tsl->max_idx; i++) {
424 		bcm_bprintf(b, "\t idx: %03d, (%d: %d) timestamp: 0x%08x:0x%08x "
425 				" proto: %02d, t1: 0x%08x t2: 0x%08x\n",
426 				i, tsl->ts_log[i].intf, tsl->ts_log[i].flowid,
427 				tsl->ts_log[i].ts_high, tsl->ts_log[i].ts_low,
428 				tsl->ts_log[i].proto, tsl->ts_log[i].t1,
429 				tsl->ts_log[i].t2);
430 	}
431 }
432 
433 static int
dhd_timesync_ts_log_dump(dhd_ts_t * ts,char * buf,int buflen,bool tx)434 dhd_timesync_ts_log_dump(dhd_ts_t *ts, char *buf, int buflen, bool tx)
435 {
436 	struct bcmstrbuf b;
437 	struct bcmstrbuf *strbuf = &b;
438 
439 	bcm_binit(strbuf, buf, buflen);
440 
441 	if (tx) {
442 		bcm_bprintf(strbuf, "Tx Log dump\t");
443 		dhd_timesync_ts_log_dump_item(&ts->tx_timestamps, strbuf);
444 	}
445 	else {
446 		bcm_bprintf(strbuf, "Rx Log dump\n");
447 		dhd_timesync_ts_log_dump_item(&ts->rx_timestamps, strbuf);
448 	}
449 	return BCME_OK;
450 }
451 
452 static void
dhd_timesync_clear_logs(dhd_ts_t * ts)453 dhd_timesync_clear_logs(dhd_ts_t *ts)
454 {
455 	dhd_ts_log_ts_t *tsl;
456 
457 	tsl = &ts->rx_timestamps;
458 	tsl->cur_idx = 0;
459 	memset(tsl->ts_log, 0, sizeof(dhd_ts_log_ts_item_t) *
460 			MAX_TS_LOG_SAMPLES_DATA);
461 
462 	tsl = &ts->tx_timestamps;
463 	tsl->cur_idx = 0;
464 	memset(tsl->ts_log, 0, sizeof(dhd_ts_log_ts_item_t) *
465 			MAX_TS_LOG_SAMPLES_DATA);
466 
467 	return;
468 }
469 
470 void
dhd_timesync_debug_info_print(dhd_pub_t * dhdp)471 dhd_timesync_debug_info_print(dhd_pub_t *dhdp)
472 {
473 	dhd_ts_t *ts = dhdp->ts;
474 	uint64 current_time;
475 
476 	if (!ts) {
477 		DHD_ERROR(("%s: %d ts is NULL\n", __FUNCTION__, __LINE__));
478 		return;
479 	}
480 
481 	DHD_ERROR(("\nts info dump: active_ipc_version %d\n", ts->active_ipc_version));
482 	current_time = OSL_LOCALTIME_NS();
483 	DHD_ERROR(("current_time="SEC_USEC_FMT" last_ts_watchdog_time="SEC_USEC_FMT"\n",
484 		GET_SEC_USEC(current_time), GET_SEC_USEC(ts->last_ts_watchdog_time)));
485 	DHD_ERROR(("timesync disabled %d\n", ts->timesync_disabled));
486 	DHD_ERROR(("Host TS dump cnt %d, fw TS dump cnt %d, descrepency %d\n",
487 		ts->host_ts_capture_cnt, ts->fw_ts_capture_cnt, ts->fw_ts_disc_cnt));
488 	DHD_ERROR(("ts_watchdog calls %d reset cnt %d\n",
489 		ts->ts_watchdog_calls, ts->host_reset_cnt));
490 	DHD_ERROR(("xt_ids tag/ID %d/%d, %d/%d, %d/%d, %d/%d\n",
491 		BCMMSGBUF_HOST_TIMESTAMPING_CONFIG_TAG, ts->xt_ids.host_timestamping_config,
492 		BCMMSGBUF_HOST_CLOCK_SELECT_TAG, ts->xt_ids.host_clock_selection,
493 		BCMMSGBUF_HOST_CLOCK_INFO_TAG, ts->xt_ids.host_clk_info,
494 		BCMMSGBUF_D2H_CLOCK_CORRECTION_TAG, ts->xt_ids.d2h_clk_correction));
495 	DHD_ERROR(("pending requests %d suspend req %d resume req %d\n",
496 		ts->pending_requests, ts->suspend_req, ts->resume_req));
497 
498 }
499 
500 static int
dhd_timesync_dump(dhd_ts_t * ts,char * buf,int buflen)501 dhd_timesync_dump(dhd_ts_t *ts, char *buf, int buflen)
502 {
503 	struct bcmstrbuf b;
504 	struct bcmstrbuf *strbuf = &b;
505 
506 	bcm_binit(strbuf, buf, buflen);
507 
508 	bcm_bprintf(strbuf, "ts info dump: active_ipc_version %d\n", ts->active_ipc_version);
509 	bcm_bprintf(strbuf, "timesync disabled %d\n", ts->timesync_disabled);
510 	bcm_bprintf(strbuf, "Host TS dump cnt %d, fw TS dump cnt %d, descrepency %d\n",
511 		ts->host_ts_capture_cnt, ts->fw_ts_capture_cnt, ts->fw_ts_disc_cnt);
512 	bcm_bprintf(strbuf, "ts_watchdog calls %d reset cnt %d\n",
513 		ts->ts_watchdog_calls, ts->host_reset_cnt);
514 	bcm_bprintf(strbuf, "xt_ids tag/ID %d/%d, %d/%d, %d/%d, %d/%d\n",
515 		BCMMSGBUF_HOST_TIMESTAMPING_CONFIG_TAG, ts->xt_ids.host_timestamping_config,
516 		BCMMSGBUF_HOST_CLOCK_SELECT_TAG, ts->xt_ids.host_clock_selection,
517 		BCMMSGBUF_HOST_CLOCK_INFO_TAG, ts->xt_ids.host_clk_info,
518 		BCMMSGBUF_D2H_CLOCK_CORRECTION_TAG, ts->xt_ids.d2h_clk_correction);
519 	bcm_bprintf(strbuf, "pending requests %d suspend req %d resume req %d\n",
520 		ts->pending_requests, ts->suspend_req, ts->resume_req);
521 
522 	return BCME_OK;
523 }
524 
525 static int
dhd_timesync_doiovar(dhd_ts_t * ts,const bcm_iovar_t * vi,uint32 actionid,const char * name,void * params,uint plen,void * arg,uint len,int val_size)526 dhd_timesync_doiovar(dhd_ts_t *ts, const bcm_iovar_t *vi, uint32 actionid, const char *name,
527             void *params, uint plen, void *arg, uint len, int val_size)
528 {
529 	int bcmerror = 0;
530 	int32 int_val = 0;
531 
532 	DHD_TRACE(("%s: Enter\n", __FUNCTION__));
533 	DHD_TRACE(("%s: actionid = %d; name %s\n", __FUNCTION__, actionid, name));
534 
535 	if ((bcmerror = bcm_iovar_lencheck(vi, arg, len, IOV_ISSET(actionid))) != 0)
536 		goto exit;
537 
538 	if (plen >= sizeof(int_val))
539 		bcopy(params, &int_val, sizeof(int_val));
540 
541 	switch (actionid) {
542 	case IOV_GVAL(IOV_TS_INFO_DUMP):
543 		dhd_timesync_dump(ts, arg, len);
544 		break;
545 	case IOV_GVAL(IOV_TS_TX_TS_DUMP):
546 		dhd_timesync_ts_log_dump(ts, arg, len, TRUE);
547 		break;
548 	case IOV_GVAL(IOV_TS_RX_TS_DUMP):
549 		dhd_timesync_ts_log_dump(ts, arg, len, FALSE);
550 		break;
551 	case IOV_GVAL(IOV_TS_FW_CLKINFO_DUMP):
552 		dhd_ts_fw_clksrc_dump(ts, arg, len);
553 		break;
554 	case IOV_SVAL(IOV_TS_SEND_TSCONFIG):
555 		if (ts->active_ipc_version < 7) {
556 			bcmerror = BCME_ERROR;
557 			break;
558 		}
559 		bcmerror = dhd_timesync_send_host_timestamping_config(ts, FALSE);
560 		break;
561 	case IOV_SVAL(IOV_TS_SEND_HCLK_SEL):
562 		if (ts->active_ipc_version < 7) {
563 			bcmerror = BCME_ERROR;
564 			break;
565 		}
566 		bcmerror = dhd_timesync_send_host_clock_selection(ts);
567 		break;
568 	case IOV_SVAL(IOV_TS_SEND_HCLK_INFO):
569 		if (ts->active_ipc_version < 7) {
570 			bcmerror = BCME_ERROR;
571 			break;
572 		}
573 		bcmerror = dhd_timesync_send_host_clk_info(ts);
574 		break;
575 	case IOV_SVAL(IOV_TS_SEND_D2H_CRCT):
576 		if (ts->active_ipc_version < 7) {
577 			bcmerror = BCME_ERROR;
578 			break;
579 		}
580 		bcmerror = dhd_timesync_send_D2H_clk_correction(ts);
581 		break;
582 	case IOV_SVAL(IOV_TS_INJECT_BAD_TAG):
583 		if (ts->active_ipc_version < 7) {
584 			bcmerror = BCME_ERROR;
585 			break;
586 		}
587 		bcmerror = dhd_timesync_send_host_timestamping_config(ts, TRUE);
588 		break;
589 	case IOV_SVAL(IOV_TS_INJECT_BAD_XTID): {
590 		uint16 old_xt_id;
591 
592 		if (ts->active_ipc_version < 7) {
593 			bcmerror = BCME_ERROR;
594 			break;
595 		}
596 		old_xt_id = ts->xt_id;
597 		ts->xt_id += 10; /* will cause the error now */
598 		DHD_ERROR(("generating bad XTID transaction for the device exp %d, sending %d",
599 			old_xt_id, ts->xt_id));
600 		bcmerror = dhd_timesync_send_host_timestamping_config(ts, FALSE);
601 		ts->xt_id = old_xt_id;
602 		break;
603 	}
604 	case IOV_GVAL(IOV_TS_FWTS2HSTS_DELAY):
605 		bcopy(&ts->fwts2hsts_delay, arg, val_size);
606 		break;
607 	case IOV_SVAL(IOV_TS_FWTS2HSTS_DELAY):
608 		if (ts->active_ipc_version < 7) {
609 			bcmerror = BCME_ERROR;
610 			break;
611 		}
612 		if (int_val > DHD_DEFAULT_TIMESYNC_TIMER_VALUE_MAX) {
613 			bcmerror = BCME_RANGE;
614 			break;
615 		}
616 		if (int_val <= DHD_DEFAULT_TIMESYNC_TIMER_VALUE) {
617 			bcmerror = BCME_RANGE;
618 			break;
619 		}
620 		ts->fwts2hsts_delay = int_val;
621 		break;
622 	case IOV_GVAL(IOV_TS_NODROP_CONFIG):
623 		bcopy(&ts->nodrop_config, arg, val_size);
624 		break;
625 	case IOV_SVAL(IOV_TS_NODROP_CONFIG):
626 		ts->nodrop_config = int_val;
627 		break;
628 	case IOV_GVAL(IOV_TS_NO_RETRY):
629 		int_val = dhd_prot_pkt_noretry(ts->dhdp, 0, FALSE);
630 		bcopy(&int_val, arg, val_size);
631 		break;
632 	case IOV_SVAL(IOV_TS_NO_RETRY):
633 		dhd_prot_pkt_noretry(ts->dhdp, int_val, TRUE);
634 		break;
635 	case IOV_GVAL(IOV_TS_NO_AGGR):
636 		int_val = dhd_prot_pkt_noaggr(ts->dhdp, 0, FALSE);
637 		bcopy(&int_val, arg, val_size);
638 		break;
639 	case IOV_SVAL(IOV_TS_NO_AGGR):
640 		dhd_prot_pkt_noaggr(ts->dhdp, int_val, TRUE);
641 		break;
642 	case IOV_GVAL(IOV_TS_FIXED_RATE):
643 		int_val = dhd_prot_pkt_fixed_rate(ts->dhdp, 0, FALSE);
644 		bcopy(&int_val, arg, val_size);
645 		break;
646 	case IOV_SVAL(IOV_TS_FIXED_RATE):
647 		dhd_prot_pkt_fixed_rate(ts->dhdp, int_val, TRUE);
648 		break;
649 	case IOV_SVAL(IOV_TS_CLEAR_LOGS):
650 		dhd_timesync_clear_logs(ts);
651 		break;
652 	case IOV_GVAL(IOV_TS_TXS_LOG):
653 		int_val = dhd_prot_data_path_tx_timestamp_logging(ts->dhdp, 0,  FALSE);
654 		bcopy(&int_val, arg, val_size);
655 		break;
656 	case IOV_SVAL(IOV_TS_TXS_LOG):
657 		dhd_prot_data_path_tx_timestamp_logging(ts->dhdp, int_val,  TRUE);
658 		break;
659 	case IOV_GVAL(IOV_TS_RXS_LOG):
660 		int_val = dhd_prot_data_path_rx_timestamp_logging(ts->dhdp, 0,  FALSE);
661 		bcopy(&int_val, arg, val_size);
662 		break;
663 	case IOV_SVAL(IOV_TS_RXS_LOG):
664 		dhd_prot_data_path_rx_timestamp_logging(ts->dhdp, int_val,  TRUE);
665 		break;
666 	case IOV_SVAL(IOV_TS_HTSCONF_PERIOD):
667 		if (ts->active_ipc_version < 7) {
668 			bcmerror = BCME_ERROR;
669 			break;
670 		}
671 		ts->h_tsconf_period = int_val;
672 		break;
673 	case IOV_GVAL(IOV_TS_HTSCONF_PERIOD):
674 		if (ts->active_ipc_version < 7) {
675 			bcmerror = BCME_ERROR;
676 			break;
677 		}
678 		bcopy(&ts->h_tsconf_period, arg, val_size);
679 		break;
680 	case IOV_SVAL(IOV_TS_HCLK_CLKID_MAX):
681 		if (ts->active_ipc_version < 7) {
682 			bcmerror = BCME_ERROR;
683 			break;
684 		}
685 		ts->h_clkid_max = int_val;
686 		break;
687 	case IOV_GVAL(IOV_TS_HCLK_CLKID_MAX):
688 		if (ts->active_ipc_version < 7) {
689 			bcmerror = BCME_ERROR;
690 			break;
691 		}
692 		bcopy(&ts->h_clkid_max, arg, val_size);
693 		break;
694 
695 	case IOV_SVAL(IOV_TS_HCLK_CLKID_MIN):
696 		if (ts->active_ipc_version < 7) {
697 			bcmerror = BCME_ERROR;
698 			break;
699 		}
700 		ts->h_clkid_min = int_val;
701 		break;
702 	case IOV_GVAL(IOV_TS_HCLK_CLKID_MIN):
703 		if (ts->active_ipc_version < 7) {
704 			bcmerror = BCME_ERROR;
705 			break;
706 		}
707 		bcopy(&ts->h_clkid_min, arg, val_size);
708 		break;
709 	default:
710 		bcmerror = BCME_UNSUPPORTED;
711 		break;
712 	}
713 exit:
714 	DHD_TRACE(("%s: actionid %d, bcmerror %d\n", __FUNCTION__, actionid, bcmerror));
715 	return bcmerror;
716 }
717 
718 int
dhd_timesync_iovar_op(dhd_ts_t * ts,const char * name,void * params,int plen,void * arg,int len,bool set)719 dhd_timesync_iovar_op(dhd_ts_t *ts, const char *name,
720 	void *params, int plen, void *arg, int len, bool set)
721 {
722 	int bcmerror = 0;
723 	int val_size;
724 	const bcm_iovar_t *vi = NULL;
725 	uint32 actionid;
726 
727 	DHD_TRACE(("%s: Enter\n", __FUNCTION__));
728 
729 	ASSERT(name);
730 	ASSERT(len >= 0);
731 
732 	/* Get MUST have return space */
733 	ASSERT(set || (arg && len));
734 
735 	/* Set does NOT take qualifiers */
736 	ASSERT(!set || (!params && !plen));
737 
738 	if ((vi = bcm_iovar_lookup(dhd_ts_iovars, name)) == NULL) {
739 		DHD_TRACE(("%s: not ours\n", name));
740 		bcmerror = BCME_UNSUPPORTED;
741 		goto exit;
742 	}
743 
744 	DHD_CTL(("%s: %s %s, len %d plen %d\n", __FUNCTION__,
745 		name, (set ? "set" : "get"), len, plen));
746 
747 	/* set up 'params' pointer in case this is a set command so that
748 	 * the convenience int and bool code can be common to set and get
749 	 */
750 	if (params == NULL) {
751 		params = arg;
752 		plen = len;
753 	}
754 
755 	if (vi->type == IOVT_VOID)
756 		val_size = 0;
757 	else if (vi->type == IOVT_BUFFER)
758 		val_size = len;
759 	else
760 		/* all other types are integer sized */
761 		val_size = sizeof(int);
762 
763 	actionid = set ? IOV_SVAL(vi->varid) : IOV_GVAL(vi->varid);
764 
765 	bcmerror = dhd_timesync_doiovar(ts, vi, actionid, name, params, plen, arg, len, val_size);
766 
767 exit:
768 	return bcmerror;
769 }
770 
771 void
dhd_timesync_handle_host_ts_complete(dhd_ts_t * ts,uint16 xt_id,uint16 status)772 dhd_timesync_handle_host_ts_complete(dhd_ts_t *ts, uint16 xt_id, uint16 status)
773 {
774 	if (ts == NULL) {
775 		DHD_ERROR(("%s: called with ts null\n", __FUNCTION__));
776 		return;
777 	}
778 	DHD_INFO(("Host send TS complete, for ID %d, status %d\n", xt_id, status));
779 	if (xt_id == ts->xt_ids.host_clk_info) {
780 		if (ts->host_ts_host_clk_info_buffer_in_use != TRUE) {
781 			DHD_ERROR(("same ID as the host clock info, but buffer not in use: %d\n",
782 				ts->xt_ids.host_clk_info));
783 			return;
784 		}
785 		ts->host_ts_host_clk_info_buffer_in_use = FALSE;
786 	}
787 	ts->pending_requests--;
788 }
789 
790 void
dhd_timesync_notify_ipc_rev(dhd_ts_t * ts,uint32 ipc_rev)791 dhd_timesync_notify_ipc_rev(dhd_ts_t *ts, uint32 ipc_rev)
792 {
793 	if (ts != NULL)
794 		ts->active_ipc_version = ipc_rev;
795 }
796 
797 static int
dhd_ts_fw_clksrc_dump(dhd_ts_t * ts,char * buf,int buflen)798 dhd_ts_fw_clksrc_dump(dhd_ts_t *ts, char *buf, int buflen)
799 {
800 	struct bcmstrbuf b;
801 	struct bcmstrbuf *strbuf = &b;
802 	clk_ts_log_t *fw_ts_log;
803 	uint32 i = 0, j = 0;
804 	clksrc_ts_log_t *clk_src;
805 
806 	fw_ts_log = &ts->fw_ts_log;
807 
808 	bcm_binit(strbuf, buf, buflen);
809 
810 	while (i <= MAX_CLKSRC_ID) {
811 		clk_src = &fw_ts_log->ts_log[i];
812 		if (clk_src->inuse == FALSE) {
813 			bcm_bprintf(strbuf, "clkID %d: not in use\n", i);
814 		}
815 		else {
816 			bcm_bprintf(strbuf, "clkID %d: name %s Max idx %d, cur_idx %d\n",
817 				i, clk_src->name, MAX_FW_TS_LOG_SAMPLES, fw_ts_log->cur_idx);
818 			j = 0;
819 			while (j < MAX_FW_TS_LOG_SAMPLES) {
820 				bcm_bprintf(strbuf, "%03d: %03d: 0x%08x-0x%08x\n", j,
821 					fw_ts_log->seqnum[j], clk_src->log[j].ts_high,
822 					clk_src->log[j].ts_low);
823 				j++;
824 			}
825 		}
826 		i++;
827 	}
828 	return b.size;
829 }
830 
831 static void
dhd_ts_fw_clksrc_log(dhd_ts_t * ts,uchar * tlvs,uint32 tlv_len,uint32 seqnum)832 dhd_ts_fw_clksrc_log(dhd_ts_t *ts, uchar *tlvs, uint32 tlv_len, uint32 seqnum)
833 {
834 	ts_fw_clock_info_t *fw_clock_info;
835 	clksrc_ts_log_t	*clk_src;
836 	clk_ts_log_t *fw_ts_log;
837 
838 	fw_ts_log = &ts->fw_ts_log;
839 
840 	fw_ts_log->seqnum[fw_ts_log->cur_idx] = seqnum;
841 	while (tlv_len) {
842 		fw_clock_info = (ts_fw_clock_info_t *)tlvs;
843 		clk_src = &fw_ts_log->ts_log[(fw_clock_info->ts.ts_high >> 28) & 0xF];
844 		if (clk_src->inuse == FALSE) {
845 			bcopy(fw_clock_info->clk_src, clk_src->name, sizeof(clk_src->name));
846 			clk_src->inuse = TRUE;
847 		}
848 		clk_src->log[fw_ts_log->cur_idx].ts_low = fw_clock_info->ts.ts_low;
849 		clk_src->log[fw_ts_log->cur_idx].ts_high = fw_clock_info->ts.ts_high;
850 
851 		tlvs += sizeof(*fw_clock_info);
852 		tlv_len -= sizeof(*fw_clock_info);
853 	}
854 	fw_ts_log->cur_idx++;
855 	if (fw_ts_log->cur_idx >= MAX_FW_TS_LOG_SAMPLES)
856 		fw_ts_log->cur_idx = 0;
857 }
858 
859 void
dhd_timesync_handle_fw_timestamp(dhd_ts_t * ts,uchar * tlvs,uint32 tlv_len,uint32 seqnum)860 dhd_timesync_handle_fw_timestamp(dhd_ts_t *ts, uchar *tlvs, uint32 tlv_len, uint32 seqnum)
861 {
862 	ts_fw_clock_info_t *fw_clock_info;
863 	uint16 tag_id;
864 
865 	DHD_INFO(("FW sent timestamp message, tlv_len %d, seqnum %d\n", tlv_len, seqnum));
866 	bcm_print_bytes("fw ts", tlvs, tlv_len);
867 	/* we are expecting only one TLV type from the firmware side */
868 	/* BCMMSGBUF_FW_CLOCK_INFO_TAG */
869 	/* Validate the tag ID */
870 	if (ts == NULL) {
871 		DHD_ERROR(("%s:  NULL TS \n", __FUNCTION__));
872 		return;
873 	}
874 	if (tlvs == NULL) {
875 		DHD_ERROR(("%s:  NULL TLV \n", __FUNCTION__));
876 		return;
877 	}
878 	if (tlv_len < BCM_XTLV_HDR_SIZE) {
879 		DHD_ERROR(("%s:  bad length %d\n", __FUNCTION__, tlv_len));
880 		return;
881 	}
882 	if (tlv_len > MAX_SIZE_FW_CLKINFO_TYPE) {
883 		DHD_ERROR(("tlv_len %d more than what is supported in Host %d\n", tlv_len,
884 			(uint32)MAX_SIZE_FW_CLKINFO_TYPE));
885 		return;
886 	}
887 	if (tlv_len % (sizeof(*fw_clock_info))) {
888 		DHD_ERROR(("bad tlv_len for the packet %d, needs to be multiple of %d\n", tlv_len,
889 			(uint32)(sizeof(*fw_clock_info))));
890 		return;
891 	}
892 
893 	/* validate the tag for all the include tag IDs */
894 	{
895 		uint32 check_len = 0;
896 		uchar *tag_ptr = (uchar *)(tlvs);
897 		while (check_len < tlv_len) {
898 			bcopy(tag_ptr+check_len, &tag_id, sizeof(uint16));
899 			DHD_INFO(("FWTS: tag_id %d, offset %d \n",
900 					tag_id, check_len));
901 			if (tag_id != BCMMSGBUF_FW_CLOCK_INFO_TAG) {
902 				DHD_ERROR(("Fatal: invalid tag from FW in TS: %d, offset %d \n",
903 					tag_id, check_len));
904 				return;
905 			}
906 			check_len += sizeof(*fw_clock_info);
907 		}
908 	}
909 
910 	if (seqnum != (ts->fw_ts_capture_cnt + 1)) {
911 		DHD_ERROR(("FW TS descrepency: out of sequence exp %d, got %d, resyncing %d\n",
912 			ts->fw_ts_capture_cnt + 1, seqnum, seqnum));
913 		ts->fw_ts_disc_cnt++;
914 	}
915 	ts->fw_ts_capture_cnt = seqnum;
916 
917 	/* copy it into local info */
918 	bcopy(tlvs, &ts->fw_tlv[0], tlv_len);
919 	ts->fw_tlv_len = tlv_len;
920 
921 	dhd_ts_fw_clksrc_log(ts, tlvs, tlv_len, seqnum);
922 	/* launch the watchdog to send the host time stamp as per the delay programmed */
923 	if (ts->fwts2hsts_delay_wdcount != 0) {
924 		DHD_ERROR(("FATAL: Last Host sync is not sent out yet\n"));
925 		return;
926 	}
927 	if (dhd_watchdog_ms == 0) {
928 		DHD_ERROR(("FATAL: WATCHDOG is set to 0, timesync can't work properly \n"));
929 		return;
930 	}
931 	/* schedule sending host time sync values to device */
932 	ts->fwts2hsts_delay_wdcount = ts->fwts2hsts_delay / dhd_watchdog_ms;
933 	if (ts->fwts2hsts_delay_wdcount == 0)
934 		ts->fwts2hsts_delay_wdcount = 1;
935 }
936 
937 static uint32
dhd_timesync_send_host_timestamping_config(dhd_ts_t * ts,bool inject_err)938 dhd_timesync_send_host_timestamping_config(dhd_ts_t *ts, bool inject_err)
939 {
940 	ts_host_timestamping_config_t ts_config;
941 	int ret_val;
942 
943 	if (ts->timesync_disabled) {
944 		DHD_ERROR(("Timesync Disabled: Cannot send HOST TS config msg\n"));
945 		return BCME_ERROR;
946 	}
947 	bzero(&ts_config, sizeof(ts_config));
948 
949 	ts_config.xtlv.id = BCMMSGBUF_HOST_TIMESTAMPING_CONFIG_TAG;
950 	if (inject_err)
951 		ts_config.xtlv.id = BCMMSGBUF_HOST_TS_BADTAG;
952 
953 	ts_config.xtlv.len = sizeof(ts_config) - sizeof(_bcm_xtlv_t);
954 	ts_config.period_ms = ts->h_tsconf_period;
955 
956 	if (ts_config.period_ms) {
957 		ts_config.flags |= FLAG_HOST_RESET;
958 		ts_config.reset_cnt = ts->host_reset_cnt + 1;
959 	}
960 
961 	if (ts->nodrop_config) {
962 		ts_config.flags |= FLAG_CONFIG_NODROP;
963 		ts_config.post_delay = TS_NODROP_CONFIG_TO;
964 	} else {
965 		ts_config.post_delay = TS_DROP_CONFIG_TO;
966 	}
967 
968 	DHD_ERROR(("sending Host Timestamping Config: TLV (ID %d, LEN %d), period %d, seq %d\n",
969 		ts_config.xtlv.id, ts_config.xtlv.len, ts_config.period_ms,
970 		ts->host_ts_capture_cnt));
971 	ret_val = dhd_prot_send_host_timestamp(ts->dhdp, (uchar *)&ts_config, sizeof(ts_config),
972 		ts->host_ts_capture_cnt, ts->xt_id);
973 	if (ret_val != 0) {
974 		DHD_ERROR(("Fatal: Error sending HOST TS config msg to device: %d\n", ret_val));
975 		return BCME_ERROR;
976 	}
977 
978 	if (ts_config.period_ms) {
979 		ts->host_reset_cnt++;
980 	}
981 
982 	ts->pending_requests++;
983 	ts->xt_ids.host_timestamping_config = ts->xt_id;
984 	ts->xt_id++;
985 	return BCME_OK;
986 }
987 
988 static uint32
dhd_timesync_send_host_clock_selection(dhd_ts_t * ts)989 dhd_timesync_send_host_clock_selection(dhd_ts_t *ts)
990 {
991 	ts_host_clock_sel_t ts_clk_sel;
992 	int ret_val;
993 
994 	if (ts->timesync_disabled) {
995 		DHD_ERROR(("Timesync Disabled: Cannot send HOST clock sel  msg\n"));
996 		return BCME_ERROR;
997 	}
998 
999 	bzero(&ts_clk_sel, sizeof(ts_clk_sel));
1000 
1001 	ts_clk_sel.xtlv.id = BCMMSGBUF_HOST_CLOCK_SELECT_TAG;
1002 	ts_clk_sel.xtlv.len = sizeof(ts_clk_sel) - sizeof(_bcm_xtlv_t);
1003 	ts_clk_sel.min_clk_idx = ts->h_clkid_min;
1004 	ts_clk_sel.max_clk_idx = ts->h_clkid_max;
1005 	DHD_INFO(("sending Host ClockSel Config: TLV (ID %d, LEN %d), min %d, max %d, seq %d\n",
1006 		ts_clk_sel.xtlv.id, ts_clk_sel.xtlv.len, ts_clk_sel.min_clk_idx,
1007 		ts_clk_sel.max_clk_idx,
1008 		ts->host_ts_capture_cnt));
1009 	ret_val = dhd_prot_send_host_timestamp(ts->dhdp, (uchar *)&ts_clk_sel, sizeof(ts_clk_sel),
1010 		ts->host_ts_capture_cnt, ts->xt_id);
1011 	if (ret_val != 0) {
1012 		DHD_ERROR(("Fatal: Error sending HOST ClockSel msg to device: %d\n", ret_val));
1013 		return BCME_ERROR;
1014 	}
1015 	ts->xt_ids.host_clock_selection = ts->xt_id;
1016 	ts->xt_id++;
1017 	ts->pending_requests++;
1018 	return BCME_OK;
1019 }
1020 
1021 static uint32
dhd_timesync_send_host_clk_info(dhd_ts_t * ts)1022 dhd_timesync_send_host_clk_info(dhd_ts_t *ts)
1023 {
1024 	ts_host_clock_info_t *host_clock_info;
1025 	uchar *clk_info_buffer;
1026 	uint32 clk_info_bufsize;
1027 	int ret_val;
1028 
1029 	if (ts->timesync_disabled) {
1030 		DHD_ERROR(("Timesync Disabled: Cannot send HOST clock config msg\n"));
1031 		return BCME_ERROR;
1032 	}
1033 	if (ts->host_ts_host_clk_info_buffer_in_use == TRUE) {
1034 		DHD_ERROR(("Host Ts Clock info buffer in Use\n"));
1035 		return BCME_ERROR;
1036 	}
1037 	clk_info_buffer = &ts->host_ts_host_clk_info_buffer[0];
1038 	clk_info_bufsize = sizeof(ts->host_ts_host_clk_info_buffer);
1039 
1040 	DHD_INFO(("clk_info_buf size %d, tlv_len %d, host clk_info_len %d\n",
1041 		clk_info_bufsize, ts->fw_tlv_len, (uint32)sizeof(*host_clock_info)));
1042 
1043 	if (clk_info_bufsize < sizeof(*host_clock_info)) {
1044 		DHD_ERROR(("clock_info_buf_size too small to fit host clock info %d, %d\n",
1045 			clk_info_bufsize, (uint32)sizeof(*host_clock_info)));
1046 		return BCME_ERROR;
1047 	}
1048 
1049 	host_clock_info = (ts_host_clock_info_t *)clk_info_buffer;
1050 	host_clock_info->xtlv.id = BCMMSGBUF_HOST_CLOCK_INFO_TAG;
1051 	host_clock_info->xtlv.len = sizeof(*host_clock_info) - sizeof(_bcm_xtlv_t);
1052 	/* OSL_GET_CYCLES */
1053 	host_clock_info->ticks.low = 0;
1054 	host_clock_info->ticks.high = 0;
1055 	/* OSL_SYS_UPTIME?? */
1056 	host_clock_info->ns.low = 0;
1057 	host_clock_info->ns.high = 0;
1058 	clk_info_buffer += (sizeof(*host_clock_info));
1059 	clk_info_bufsize -= sizeof(*host_clock_info);
1060 
1061 	/* copy the device clk config as that is the reference for this */
1062 	if (clk_info_bufsize < ts->fw_tlv_len) {
1063 		DHD_ERROR(("clock info buffer is small to fit dev clk info %d, %d\n",
1064 			clk_info_bufsize, ts->fw_tlv_len));
1065 		return BCME_ERROR;
1066 	}
1067 	bcopy(ts->fw_tlv, clk_info_buffer, ts->fw_tlv_len);
1068 	clk_info_bufsize -= ts->fw_tlv_len;
1069 
1070 	DHD_INFO(("sending Host TS msg Len %d, xt_id %d, host_ts_capture_count %d\n",
1071 		(uint32)(sizeof(ts->host_ts_host_clk_info_buffer) - clk_info_bufsize),
1072 		ts->xt_id, ts->host_ts_capture_cnt));
1073 
1074 	bcm_print_bytes("host ts",  (uchar *)ts->host_ts_host_clk_info_buffer,
1075 		sizeof(ts->host_ts_host_clk_info_buffer) - clk_info_bufsize);
1076 
1077 	ret_val = dhd_prot_send_host_timestamp(ts->dhdp, (uchar *)ts->host_ts_host_clk_info_buffer,
1078 		sizeof(ts->host_ts_host_clk_info_buffer) - clk_info_bufsize,
1079 		ts->host_ts_capture_cnt, ts->xt_id);
1080 	if (ret_val != 0) {
1081 		DHD_ERROR(("Fatal: Error sending HOST ClockSel msg to device: %d\n", ret_val));
1082 		return BCME_ERROR;
1083 	}
1084 	ts->host_ts_host_clk_info_buffer_in_use = TRUE;
1085 	ts->xt_ids.host_clk_info = ts->xt_id;
1086 	ts->xt_id++;
1087 	ts->pending_requests++;
1088 	return BCME_OK;
1089 }
1090 
1091 static uint32
dhd_timesync_send_D2H_clk_correction(dhd_ts_t * ts)1092 dhd_timesync_send_D2H_clk_correction(dhd_ts_t *ts)
1093 {
1094 	ts_d2h_clock_correction_t ts_clk_crtion;
1095 	int ret_val;
1096 
1097 	if (ts->timesync_disabled) {
1098 		DHD_ERROR(("Timesync Disabled: Cannot send d2h clock correction msg\n"));
1099 		return BCME_ERROR;
1100 	}
1101 
1102 	bzero(&ts_clk_crtion, sizeof(ts_clk_crtion));
1103 
1104 	/* XXX: should this be sending for all the clock sources */
1105 
1106 	ts_clk_crtion.xtlv.id = BCMMSGBUF_D2H_CLOCK_CORRECTION_TAG;
1107 	ts_clk_crtion.xtlv.len = sizeof(ts_clk_crtion) - sizeof(_bcm_xtlv_t);
1108 	ts_clk_crtion.clk_id = ts->h_clkid_max;
1109 	ts_clk_crtion.m.low = ts->correction_m.low;
1110 	ts_clk_crtion.m.high = ts->correction_m.high;
1111 	ts_clk_crtion.b.low = ts->correction_b.low;
1112 	ts_clk_crtion.b.high = ts->correction_b.high;
1113 
1114 	DHD_INFO(("sending D2H Correction: ID %d, LEN %d, clkid %d, m %d:%d, b %d:%d, seq %d\n",
1115 		ts_clk_crtion.xtlv.id, ts_clk_crtion.xtlv.len, ts_clk_crtion.clk_id,
1116 		ts_clk_crtion.m.high,
1117 		ts_clk_crtion.m.low,
1118 		ts_clk_crtion.b.high,
1119 		ts_clk_crtion.b.low,
1120 		ts->host_ts_capture_cnt));
1121 
1122 	ret_val = dhd_prot_send_host_timestamp(ts->dhdp, (uchar *)&ts_clk_crtion,
1123 		sizeof(ts_clk_crtion), ts->host_ts_capture_cnt, ts->xt_id);
1124 	if (ret_val != 0) {
1125 		DHD_ERROR(("Fatal: Error sending HOST ClockSel msg to device: %d\n", ret_val));
1126 		return BCME_ERROR;
1127 	}
1128 	ts->xt_ids.d2h_clk_correction = ts->xt_id;
1129 	ts->xt_id++;
1130 	ts->pending_requests++;
1131 	return BCME_OK;
1132 }
1133 
1134 bool
dhd_timesync_delay_post_bufs(dhd_pub_t * dhdp)1135 dhd_timesync_delay_post_bufs(dhd_pub_t *dhdp)
1136 {
1137 	return (dhdp->ts->fwts2hsts_delay != 0);
1138 }
1139 
1140 bool
dhd_timesync_watchdog(dhd_pub_t * dhdp)1141 dhd_timesync_watchdog(dhd_pub_t *dhdp)
1142 {
1143 	dhd_ts_t *ts = dhdp->ts;
1144 
1145 	if (ts == NULL)
1146 		return FALSE;
1147 
1148 	ts->last_ts_watchdog_time = OSL_LOCALTIME_NS();
1149 	ts->ts_watchdog_calls++;
1150 
1151 	/* XXX: this is relying the watchdog to be running..which may not hold good */
1152 	if (ts->fwts2hsts_delay_wdcount) {
1153 		ts->fwts2hsts_delay_wdcount--;
1154 		if (ts->fwts2hsts_delay != 0 && dhdp->busstate == DHD_BUS_DATA &&
1155 			(ts->fwts2hsts_delay_wdcount == 0)) {
1156 			/* see if we need to send the host clock info */
1157 			dhd_timesync_send_host_clk_info(ts);
1158 			dhd_msgbuf_delay_post_ts_bufs(dhdp);
1159 		}
1160 	}
1161 	return FALSE;
1162 }
1163 
1164 static void
dhd_timesync_log_timestamp_item(dhd_ts_log_ts_t * tsl,uint16 flowid,uint8 intf,uint32 ts_low,uint32 ts_high,dhd_pkt_parse_t * pkt)1165 dhd_timesync_log_timestamp_item(dhd_ts_log_ts_t *tsl, uint16 flowid, uint8 intf,
1166 	uint32 ts_low, uint32 ts_high, dhd_pkt_parse_t *pkt)
1167 {
1168 	tsl->ts_log[tsl->cur_idx].ts_low = ts_low;
1169 	tsl->ts_log[tsl->cur_idx].ts_high = ts_high;
1170 	tsl->ts_log[tsl->cur_idx].intf = intf;
1171 	tsl->ts_log[tsl->cur_idx].proto = pkt->proto;
1172 	tsl->ts_log[tsl->cur_idx].t1 = pkt->t1;
1173 	tsl->ts_log[tsl->cur_idx].t2 = pkt->t2;
1174 	tsl->cur_idx++;
1175 	if (tsl->cur_idx == tsl->max_idx)
1176 		tsl->cur_idx = 0;
1177 }
1178 
1179 void
dhd_timesync_log_tx_timestamp(dhd_ts_t * ts,uint16 flowid,uint8 intf,uint32 ts_low,uint32 ts_high,dhd_pkt_parse_t * pkt)1180 dhd_timesync_log_tx_timestamp(dhd_ts_t *ts, uint16 flowid, uint8 intf,
1181 	uint32 ts_low, uint32 ts_high, dhd_pkt_parse_t *pkt)
1182 {
1183 	if (ts != NULL) {
1184 		dhd_timesync_log_timestamp_item(&ts->tx_timestamps, flowid, intf,
1185 			ts_low, ts_high, pkt);
1186 	}
1187 }
1188 
1189 void
dhd_timesync_log_rx_timestamp(dhd_ts_t * ts,uint8 intf,uint32 ts_low,uint32 ts_high,dhd_pkt_parse_t * pkt)1190 dhd_timesync_log_rx_timestamp(dhd_ts_t *ts, uint8 intf, uint32 ts_low, uint32 ts_high,
1191 	dhd_pkt_parse_t *pkt)
1192 {
1193 	if (ts != NULL) {
1194 		dhd_timesync_log_timestamp_item(&ts->rx_timestamps, 0, intf,
1195 			ts_low, ts_high, pkt);
1196 	}
1197 }
1198 
1199 void
dhd_timesync_control(dhd_pub_t * dhdp,bool disabled)1200 dhd_timesync_control(dhd_pub_t *dhdp, bool disabled)
1201 {
1202 	dhd_ts_t *ts;
1203 	if (dhdp == NULL)
1204 		return;
1205 
1206 	ts = dhdp->ts;
1207 	if (ts != NULL) {
1208 		if (disabled) {
1209 			DHD_ERROR(("resetting the timesync counter, current(%d)\n",
1210 				ts->fw_ts_capture_cnt));
1211 
1212 			ts->fw_ts_capture_cnt = 0;
1213 
1214 			/* Suspend case: Disable timesync after the config message */
1215 			if ((ts->active_ipc_version >= 7) && (ts->h_tsconf_period != 0)) {
1216 				uint32	tsconf_period;
1217 
1218 				tsconf_period = ts->h_tsconf_period;
1219 				ts->h_tsconf_period = 0;
1220 
1221 				dhd_timesync_send_host_timestamping_config(ts, FALSE);
1222 				ts->h_tsconf_period = tsconf_period;
1223 			}
1224 			ts->timesync_disabled = TRUE;
1225 			ts->suspend_req++;
1226 		} else {
1227 			/* Resume case: Enable timesync before the config message */
1228 			DHD_ERROR(("enabling the timesync counter, current(%d)\n",
1229 				ts->fw_ts_capture_cnt));
1230 
1231 			ts->timesync_disabled = FALSE;
1232 			ts->resume_req++;
1233 
1234 			if ((ts->active_ipc_version >= 7) && (ts->h_tsconf_period != 0))
1235 				dhd_timesync_send_host_timestamping_config(ts, FALSE);
1236 		}
1237 	}
1238 	/* XXX: may be all the other internal iovar calls should check for disabled state */
1239 }
1240