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