1*4882a593Smuzhiyun /******************************************************************************
2*4882a593Smuzhiyun *
3*4882a593Smuzhiyun * This file is provided under a dual BSD/GPLv2 license. When using or
4*4882a593Smuzhiyun * redistributing this file, you may do so under either license.
5*4882a593Smuzhiyun *
6*4882a593Smuzhiyun * GPL LICENSE SUMMARY
7*4882a593Smuzhiyun *
8*4882a593Smuzhiyun * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
9*4882a593Smuzhiyun * Copyright(c) 2016 - 2017 Intel Deutschland GmbH
10*4882a593Smuzhiyun * Copyright(c) 2012 - 2014, 2018 - 2020 Intel Corporation
11*4882a593Smuzhiyun *
12*4882a593Smuzhiyun * This program is free software; you can redistribute it and/or modify
13*4882a593Smuzhiyun * it under the terms of version 2 of the GNU General Public License as
14*4882a593Smuzhiyun * published by the Free Software Foundation.
15*4882a593Smuzhiyun *
16*4882a593Smuzhiyun * This program is distributed in the hope that it will be useful, but
17*4882a593Smuzhiyun * WITHOUT ANY WARRANTY; without even the implied warranty of
18*4882a593Smuzhiyun * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19*4882a593Smuzhiyun * General Public License for more details.
20*4882a593Smuzhiyun *
21*4882a593Smuzhiyun * The full GNU General Public License is included in this distribution
22*4882a593Smuzhiyun * in the file called COPYING.
23*4882a593Smuzhiyun *
24*4882a593Smuzhiyun * Contact Information:
25*4882a593Smuzhiyun * Intel Linux Wireless <linuxwifi@intel.com>
26*4882a593Smuzhiyun * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
27*4882a593Smuzhiyun *
28*4882a593Smuzhiyun * BSD LICENSE
29*4882a593Smuzhiyun *
30*4882a593Smuzhiyun * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
31*4882a593Smuzhiyun * Copyright(c) 2016 - 2017 Intel Deutschland GmbH
32*4882a593Smuzhiyun * Copyright(c) 2012 - 2014, 2018 - 2020 Intel Corporation
33*4882a593Smuzhiyun * All rights reserved.
34*4882a593Smuzhiyun *
35*4882a593Smuzhiyun * Redistribution and use in source and binary forms, with or without
36*4882a593Smuzhiyun * modification, are permitted provided that the following conditions
37*4882a593Smuzhiyun * are met:
38*4882a593Smuzhiyun *
39*4882a593Smuzhiyun * * Redistributions of source code must retain the above copyright
40*4882a593Smuzhiyun * notice, this list of conditions and the following disclaimer.
41*4882a593Smuzhiyun * * Redistributions in binary form must reproduce the above copyright
42*4882a593Smuzhiyun * notice, this list of conditions and the following disclaimer in
43*4882a593Smuzhiyun * the documentation and/or other materials provided with the
44*4882a593Smuzhiyun * distribution.
45*4882a593Smuzhiyun * * Neither the name Intel Corporation nor the names of its
46*4882a593Smuzhiyun * contributors may be used to endorse or promote products derived
47*4882a593Smuzhiyun * from this software without specific prior written permission.
48*4882a593Smuzhiyun *
49*4882a593Smuzhiyun * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
50*4882a593Smuzhiyun * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
51*4882a593Smuzhiyun * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
52*4882a593Smuzhiyun * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
53*4882a593Smuzhiyun * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
54*4882a593Smuzhiyun * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
55*4882a593Smuzhiyun * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
56*4882a593Smuzhiyun * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
57*4882a593Smuzhiyun * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
58*4882a593Smuzhiyun * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
59*4882a593Smuzhiyun * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
60*4882a593Smuzhiyun *
61*4882a593Smuzhiyun *****************************************************************************/
62*4882a593Smuzhiyun #include "api/commands.h"
63*4882a593Smuzhiyun #include "debugfs.h"
64*4882a593Smuzhiyun #include "dbg.h"
65*4882a593Smuzhiyun #include <linux/seq_file.h>
66*4882a593Smuzhiyun
67*4882a593Smuzhiyun #define FWRT_DEBUGFS_OPEN_WRAPPER(name, buflen, argtype) \
68*4882a593Smuzhiyun struct dbgfs_##name##_data { \
69*4882a593Smuzhiyun argtype *arg; \
70*4882a593Smuzhiyun bool read_done; \
71*4882a593Smuzhiyun ssize_t rlen; \
72*4882a593Smuzhiyun char rbuf[buflen]; \
73*4882a593Smuzhiyun }; \
74*4882a593Smuzhiyun static int _iwl_dbgfs_##name##_open(struct inode *inode, \
75*4882a593Smuzhiyun struct file *file) \
76*4882a593Smuzhiyun { \
77*4882a593Smuzhiyun struct dbgfs_##name##_data *data; \
78*4882a593Smuzhiyun \
79*4882a593Smuzhiyun data = kzalloc(sizeof(*data), GFP_KERNEL); \
80*4882a593Smuzhiyun if (!data) \
81*4882a593Smuzhiyun return -ENOMEM; \
82*4882a593Smuzhiyun \
83*4882a593Smuzhiyun data->read_done = false; \
84*4882a593Smuzhiyun data->arg = inode->i_private; \
85*4882a593Smuzhiyun file->private_data = data; \
86*4882a593Smuzhiyun \
87*4882a593Smuzhiyun return 0; \
88*4882a593Smuzhiyun }
89*4882a593Smuzhiyun
90*4882a593Smuzhiyun #define FWRT_DEBUGFS_READ_WRAPPER(name) \
91*4882a593Smuzhiyun static ssize_t _iwl_dbgfs_##name##_read(struct file *file, \
92*4882a593Smuzhiyun char __user *user_buf, \
93*4882a593Smuzhiyun size_t count, loff_t *ppos) \
94*4882a593Smuzhiyun { \
95*4882a593Smuzhiyun struct dbgfs_##name##_data *data = file->private_data; \
96*4882a593Smuzhiyun \
97*4882a593Smuzhiyun if (!data->read_done) { \
98*4882a593Smuzhiyun data->read_done = true; \
99*4882a593Smuzhiyun data->rlen = iwl_dbgfs_##name##_read(data->arg, \
100*4882a593Smuzhiyun sizeof(data->rbuf),\
101*4882a593Smuzhiyun data->rbuf); \
102*4882a593Smuzhiyun } \
103*4882a593Smuzhiyun \
104*4882a593Smuzhiyun if (data->rlen < 0) \
105*4882a593Smuzhiyun return data->rlen; \
106*4882a593Smuzhiyun return simple_read_from_buffer(user_buf, count, ppos, \
107*4882a593Smuzhiyun data->rbuf, data->rlen); \
108*4882a593Smuzhiyun }
109*4882a593Smuzhiyun
_iwl_dbgfs_release(struct inode * inode,struct file * file)110*4882a593Smuzhiyun static int _iwl_dbgfs_release(struct inode *inode, struct file *file)
111*4882a593Smuzhiyun {
112*4882a593Smuzhiyun kfree(file->private_data);
113*4882a593Smuzhiyun
114*4882a593Smuzhiyun return 0;
115*4882a593Smuzhiyun }
116*4882a593Smuzhiyun
117*4882a593Smuzhiyun #define _FWRT_DEBUGFS_READ_FILE_OPS(name, buflen, argtype) \
118*4882a593Smuzhiyun FWRT_DEBUGFS_OPEN_WRAPPER(name, buflen, argtype) \
119*4882a593Smuzhiyun FWRT_DEBUGFS_READ_WRAPPER(name) \
120*4882a593Smuzhiyun static const struct file_operations iwl_dbgfs_##name##_ops = { \
121*4882a593Smuzhiyun .read = _iwl_dbgfs_##name##_read, \
122*4882a593Smuzhiyun .open = _iwl_dbgfs_##name##_open, \
123*4882a593Smuzhiyun .llseek = generic_file_llseek, \
124*4882a593Smuzhiyun .release = _iwl_dbgfs_release, \
125*4882a593Smuzhiyun }
126*4882a593Smuzhiyun
127*4882a593Smuzhiyun #define FWRT_DEBUGFS_WRITE_WRAPPER(name, buflen, argtype) \
128*4882a593Smuzhiyun static ssize_t _iwl_dbgfs_##name##_write(struct file *file, \
129*4882a593Smuzhiyun const char __user *user_buf, \
130*4882a593Smuzhiyun size_t count, loff_t *ppos) \
131*4882a593Smuzhiyun { \
132*4882a593Smuzhiyun argtype *arg = \
133*4882a593Smuzhiyun ((struct dbgfs_##name##_data *)file->private_data)->arg;\
134*4882a593Smuzhiyun char buf[buflen] = {}; \
135*4882a593Smuzhiyun size_t buf_size = min(count, sizeof(buf) - 1); \
136*4882a593Smuzhiyun \
137*4882a593Smuzhiyun if (copy_from_user(buf, user_buf, buf_size)) \
138*4882a593Smuzhiyun return -EFAULT; \
139*4882a593Smuzhiyun \
140*4882a593Smuzhiyun return iwl_dbgfs_##name##_write(arg, buf, buf_size); \
141*4882a593Smuzhiyun }
142*4882a593Smuzhiyun
143*4882a593Smuzhiyun #define _FWRT_DEBUGFS_READ_WRITE_FILE_OPS(name, buflen, argtype) \
144*4882a593Smuzhiyun FWRT_DEBUGFS_OPEN_WRAPPER(name, buflen, argtype) \
145*4882a593Smuzhiyun FWRT_DEBUGFS_WRITE_WRAPPER(name, buflen, argtype) \
146*4882a593Smuzhiyun FWRT_DEBUGFS_READ_WRAPPER(name) \
147*4882a593Smuzhiyun static const struct file_operations iwl_dbgfs_##name##_ops = { \
148*4882a593Smuzhiyun .write = _iwl_dbgfs_##name##_write, \
149*4882a593Smuzhiyun .read = _iwl_dbgfs_##name##_read, \
150*4882a593Smuzhiyun .open = _iwl_dbgfs_##name##_open, \
151*4882a593Smuzhiyun .llseek = generic_file_llseek, \
152*4882a593Smuzhiyun .release = _iwl_dbgfs_release, \
153*4882a593Smuzhiyun }
154*4882a593Smuzhiyun
155*4882a593Smuzhiyun #define _FWRT_DEBUGFS_WRITE_FILE_OPS(name, buflen, argtype) \
156*4882a593Smuzhiyun FWRT_DEBUGFS_OPEN_WRAPPER(name, buflen, argtype) \
157*4882a593Smuzhiyun FWRT_DEBUGFS_WRITE_WRAPPER(name, buflen, argtype) \
158*4882a593Smuzhiyun static const struct file_operations iwl_dbgfs_##name##_ops = { \
159*4882a593Smuzhiyun .write = _iwl_dbgfs_##name##_write, \
160*4882a593Smuzhiyun .open = _iwl_dbgfs_##name##_open, \
161*4882a593Smuzhiyun .llseek = generic_file_llseek, \
162*4882a593Smuzhiyun .release = _iwl_dbgfs_release, \
163*4882a593Smuzhiyun }
164*4882a593Smuzhiyun
165*4882a593Smuzhiyun #define FWRT_DEBUGFS_READ_FILE_OPS(name, bufsz) \
166*4882a593Smuzhiyun _FWRT_DEBUGFS_READ_FILE_OPS(name, bufsz, struct iwl_fw_runtime)
167*4882a593Smuzhiyun
168*4882a593Smuzhiyun #define FWRT_DEBUGFS_WRITE_FILE_OPS(name, bufsz) \
169*4882a593Smuzhiyun _FWRT_DEBUGFS_WRITE_FILE_OPS(name, bufsz, struct iwl_fw_runtime)
170*4882a593Smuzhiyun
171*4882a593Smuzhiyun #define FWRT_DEBUGFS_READ_WRITE_FILE_OPS(name, bufsz) \
172*4882a593Smuzhiyun _FWRT_DEBUGFS_READ_WRITE_FILE_OPS(name, bufsz, struct iwl_fw_runtime)
173*4882a593Smuzhiyun
174*4882a593Smuzhiyun #define FWRT_DEBUGFS_ADD_FILE_ALIAS(alias, name, parent, mode) do { \
175*4882a593Smuzhiyun debugfs_create_file(alias, mode, parent, fwrt, \
176*4882a593Smuzhiyun &iwl_dbgfs_##name##_ops); \
177*4882a593Smuzhiyun } while (0)
178*4882a593Smuzhiyun #define FWRT_DEBUGFS_ADD_FILE(name, parent, mode) \
179*4882a593Smuzhiyun FWRT_DEBUGFS_ADD_FILE_ALIAS(#name, name, parent, mode)
180*4882a593Smuzhiyun
iwl_fw_send_timestamp_marker_cmd(struct iwl_fw_runtime * fwrt)181*4882a593Smuzhiyun static int iwl_fw_send_timestamp_marker_cmd(struct iwl_fw_runtime *fwrt)
182*4882a593Smuzhiyun {
183*4882a593Smuzhiyun struct iwl_mvm_marker marker = {
184*4882a593Smuzhiyun .dw_len = sizeof(struct iwl_mvm_marker) / 4,
185*4882a593Smuzhiyun .marker_id = MARKER_ID_SYNC_CLOCK,
186*4882a593Smuzhiyun
187*4882a593Smuzhiyun /* the real timestamp is taken from the ftrace clock
188*4882a593Smuzhiyun * this is for finding the match between fw and kernel logs
189*4882a593Smuzhiyun */
190*4882a593Smuzhiyun .timestamp = cpu_to_le64(fwrt->timestamp.seq++),
191*4882a593Smuzhiyun };
192*4882a593Smuzhiyun
193*4882a593Smuzhiyun struct iwl_host_cmd hcmd = {
194*4882a593Smuzhiyun .id = MARKER_CMD,
195*4882a593Smuzhiyun .flags = CMD_ASYNC,
196*4882a593Smuzhiyun .data[0] = &marker,
197*4882a593Smuzhiyun .len[0] = sizeof(marker),
198*4882a593Smuzhiyun };
199*4882a593Smuzhiyun
200*4882a593Smuzhiyun return iwl_trans_send_cmd(fwrt->trans, &hcmd);
201*4882a593Smuzhiyun }
202*4882a593Smuzhiyun
iwl_fw_timestamp_marker_wk(struct work_struct * work)203*4882a593Smuzhiyun static void iwl_fw_timestamp_marker_wk(struct work_struct *work)
204*4882a593Smuzhiyun {
205*4882a593Smuzhiyun int ret;
206*4882a593Smuzhiyun struct iwl_fw_runtime *fwrt =
207*4882a593Smuzhiyun container_of(work, struct iwl_fw_runtime, timestamp.wk.work);
208*4882a593Smuzhiyun unsigned long delay = fwrt->timestamp.delay;
209*4882a593Smuzhiyun
210*4882a593Smuzhiyun ret = iwl_fw_send_timestamp_marker_cmd(fwrt);
211*4882a593Smuzhiyun if (!ret && delay)
212*4882a593Smuzhiyun schedule_delayed_work(&fwrt->timestamp.wk,
213*4882a593Smuzhiyun round_jiffies_relative(delay));
214*4882a593Smuzhiyun else
215*4882a593Smuzhiyun IWL_INFO(fwrt,
216*4882a593Smuzhiyun "stopping timestamp_marker, ret: %d, delay: %u\n",
217*4882a593Smuzhiyun ret, jiffies_to_msecs(delay) / 1000);
218*4882a593Smuzhiyun }
219*4882a593Smuzhiyun
iwl_fw_trigger_timestamp(struct iwl_fw_runtime * fwrt,u32 delay)220*4882a593Smuzhiyun void iwl_fw_trigger_timestamp(struct iwl_fw_runtime *fwrt, u32 delay)
221*4882a593Smuzhiyun {
222*4882a593Smuzhiyun IWL_INFO(fwrt,
223*4882a593Smuzhiyun "starting timestamp_marker trigger with delay: %us\n",
224*4882a593Smuzhiyun delay);
225*4882a593Smuzhiyun
226*4882a593Smuzhiyun iwl_fw_cancel_timestamp(fwrt);
227*4882a593Smuzhiyun
228*4882a593Smuzhiyun fwrt->timestamp.delay = msecs_to_jiffies(delay * 1000);
229*4882a593Smuzhiyun
230*4882a593Smuzhiyun schedule_delayed_work(&fwrt->timestamp.wk,
231*4882a593Smuzhiyun round_jiffies_relative(fwrt->timestamp.delay));
232*4882a593Smuzhiyun }
233*4882a593Smuzhiyun
iwl_dbgfs_timestamp_marker_write(struct iwl_fw_runtime * fwrt,char * buf,size_t count)234*4882a593Smuzhiyun static ssize_t iwl_dbgfs_timestamp_marker_write(struct iwl_fw_runtime *fwrt,
235*4882a593Smuzhiyun char *buf, size_t count)
236*4882a593Smuzhiyun {
237*4882a593Smuzhiyun int ret;
238*4882a593Smuzhiyun u32 delay;
239*4882a593Smuzhiyun
240*4882a593Smuzhiyun ret = kstrtou32(buf, 10, &delay);
241*4882a593Smuzhiyun if (ret < 0)
242*4882a593Smuzhiyun return ret;
243*4882a593Smuzhiyun
244*4882a593Smuzhiyun iwl_fw_trigger_timestamp(fwrt, delay);
245*4882a593Smuzhiyun
246*4882a593Smuzhiyun return count;
247*4882a593Smuzhiyun }
248*4882a593Smuzhiyun
iwl_dbgfs_timestamp_marker_read(struct iwl_fw_runtime * fwrt,size_t size,char * buf)249*4882a593Smuzhiyun static ssize_t iwl_dbgfs_timestamp_marker_read(struct iwl_fw_runtime *fwrt,
250*4882a593Smuzhiyun size_t size, char *buf)
251*4882a593Smuzhiyun {
252*4882a593Smuzhiyun u32 delay_secs = jiffies_to_msecs(fwrt->timestamp.delay) / 1000;
253*4882a593Smuzhiyun
254*4882a593Smuzhiyun return scnprintf(buf, size, "%d\n", delay_secs);
255*4882a593Smuzhiyun }
256*4882a593Smuzhiyun
257*4882a593Smuzhiyun FWRT_DEBUGFS_READ_WRITE_FILE_OPS(timestamp_marker, 16);
258*4882a593Smuzhiyun
259*4882a593Smuzhiyun struct hcmd_write_data {
260*4882a593Smuzhiyun __be32 cmd_id;
261*4882a593Smuzhiyun __be32 flags;
262*4882a593Smuzhiyun __be16 length;
263*4882a593Smuzhiyun u8 data[];
264*4882a593Smuzhiyun } __packed;
265*4882a593Smuzhiyun
iwl_dbgfs_send_hcmd_write(struct iwl_fw_runtime * fwrt,char * buf,size_t count)266*4882a593Smuzhiyun static ssize_t iwl_dbgfs_send_hcmd_write(struct iwl_fw_runtime *fwrt, char *buf,
267*4882a593Smuzhiyun size_t count)
268*4882a593Smuzhiyun {
269*4882a593Smuzhiyun size_t header_size = (sizeof(u32) * 2 + sizeof(u16)) * 2;
270*4882a593Smuzhiyun size_t data_size = (count - 1) / 2;
271*4882a593Smuzhiyun int ret;
272*4882a593Smuzhiyun struct hcmd_write_data *data;
273*4882a593Smuzhiyun struct iwl_host_cmd hcmd = {
274*4882a593Smuzhiyun .len = { 0, },
275*4882a593Smuzhiyun .data = { NULL, },
276*4882a593Smuzhiyun };
277*4882a593Smuzhiyun
278*4882a593Smuzhiyun if (fwrt->ops && fwrt->ops->fw_running &&
279*4882a593Smuzhiyun !fwrt->ops->fw_running(fwrt->ops_ctx))
280*4882a593Smuzhiyun return -EIO;
281*4882a593Smuzhiyun
282*4882a593Smuzhiyun if (count < header_size + 1 || count > 1024 * 4)
283*4882a593Smuzhiyun return -EINVAL;
284*4882a593Smuzhiyun
285*4882a593Smuzhiyun data = kmalloc(data_size, GFP_KERNEL);
286*4882a593Smuzhiyun if (!data)
287*4882a593Smuzhiyun return -ENOMEM;
288*4882a593Smuzhiyun
289*4882a593Smuzhiyun ret = hex2bin((u8 *)data, buf, data_size);
290*4882a593Smuzhiyun if (ret)
291*4882a593Smuzhiyun goto out;
292*4882a593Smuzhiyun
293*4882a593Smuzhiyun hcmd.id = be32_to_cpu(data->cmd_id);
294*4882a593Smuzhiyun hcmd.flags = be32_to_cpu(data->flags);
295*4882a593Smuzhiyun hcmd.len[0] = be16_to_cpu(data->length);
296*4882a593Smuzhiyun hcmd.data[0] = data->data;
297*4882a593Smuzhiyun
298*4882a593Smuzhiyun if (count != header_size + hcmd.len[0] * 2 + 1) {
299*4882a593Smuzhiyun IWL_ERR(fwrt,
300*4882a593Smuzhiyun "host command data size does not match header length\n");
301*4882a593Smuzhiyun ret = -EINVAL;
302*4882a593Smuzhiyun goto out;
303*4882a593Smuzhiyun }
304*4882a593Smuzhiyun
305*4882a593Smuzhiyun if (fwrt->ops && fwrt->ops->send_hcmd)
306*4882a593Smuzhiyun ret = fwrt->ops->send_hcmd(fwrt->ops_ctx, &hcmd);
307*4882a593Smuzhiyun else
308*4882a593Smuzhiyun ret = -EPERM;
309*4882a593Smuzhiyun
310*4882a593Smuzhiyun if (ret < 0)
311*4882a593Smuzhiyun goto out;
312*4882a593Smuzhiyun
313*4882a593Smuzhiyun if (hcmd.flags & CMD_WANT_SKB)
314*4882a593Smuzhiyun iwl_free_resp(&hcmd);
315*4882a593Smuzhiyun out:
316*4882a593Smuzhiyun kfree(data);
317*4882a593Smuzhiyun return ret ?: count;
318*4882a593Smuzhiyun }
319*4882a593Smuzhiyun
320*4882a593Smuzhiyun FWRT_DEBUGFS_WRITE_FILE_OPS(send_hcmd, 512);
321*4882a593Smuzhiyun
iwl_dbgfs_fw_dbg_domain_read(struct iwl_fw_runtime * fwrt,size_t size,char * buf)322*4882a593Smuzhiyun static ssize_t iwl_dbgfs_fw_dbg_domain_read(struct iwl_fw_runtime *fwrt,
323*4882a593Smuzhiyun size_t size, char *buf)
324*4882a593Smuzhiyun {
325*4882a593Smuzhiyun return scnprintf(buf, size, "0x%08x\n",
326*4882a593Smuzhiyun fwrt->trans->dbg.domains_bitmap);
327*4882a593Smuzhiyun }
328*4882a593Smuzhiyun
329*4882a593Smuzhiyun FWRT_DEBUGFS_READ_FILE_OPS(fw_dbg_domain, 20);
330*4882a593Smuzhiyun
331*4882a593Smuzhiyun struct iwl_dbgfs_fw_info_priv {
332*4882a593Smuzhiyun struct iwl_fw_runtime *fwrt;
333*4882a593Smuzhiyun };
334*4882a593Smuzhiyun
335*4882a593Smuzhiyun struct iwl_dbgfs_fw_info_state {
336*4882a593Smuzhiyun loff_t pos;
337*4882a593Smuzhiyun };
338*4882a593Smuzhiyun
iwl_dbgfs_fw_info_seq_next(struct seq_file * seq,void * v,loff_t * pos)339*4882a593Smuzhiyun static void *iwl_dbgfs_fw_info_seq_next(struct seq_file *seq,
340*4882a593Smuzhiyun void *v, loff_t *pos)
341*4882a593Smuzhiyun {
342*4882a593Smuzhiyun struct iwl_dbgfs_fw_info_state *state = v;
343*4882a593Smuzhiyun struct iwl_dbgfs_fw_info_priv *priv = seq->private;
344*4882a593Smuzhiyun const struct iwl_fw *fw = priv->fwrt->fw;
345*4882a593Smuzhiyun
346*4882a593Smuzhiyun *pos = ++state->pos;
347*4882a593Smuzhiyun if (*pos >= fw->ucode_capa.n_cmd_versions)
348*4882a593Smuzhiyun return NULL;
349*4882a593Smuzhiyun
350*4882a593Smuzhiyun return state;
351*4882a593Smuzhiyun }
352*4882a593Smuzhiyun
iwl_dbgfs_fw_info_seq_stop(struct seq_file * seq,void * v)353*4882a593Smuzhiyun static void iwl_dbgfs_fw_info_seq_stop(struct seq_file *seq,
354*4882a593Smuzhiyun void *v)
355*4882a593Smuzhiyun {
356*4882a593Smuzhiyun kfree(v);
357*4882a593Smuzhiyun }
358*4882a593Smuzhiyun
iwl_dbgfs_fw_info_seq_start(struct seq_file * seq,loff_t * pos)359*4882a593Smuzhiyun static void *iwl_dbgfs_fw_info_seq_start(struct seq_file *seq, loff_t *pos)
360*4882a593Smuzhiyun {
361*4882a593Smuzhiyun struct iwl_dbgfs_fw_info_priv *priv = seq->private;
362*4882a593Smuzhiyun const struct iwl_fw *fw = priv->fwrt->fw;
363*4882a593Smuzhiyun struct iwl_dbgfs_fw_info_state *state;
364*4882a593Smuzhiyun
365*4882a593Smuzhiyun if (*pos >= fw->ucode_capa.n_cmd_versions)
366*4882a593Smuzhiyun return NULL;
367*4882a593Smuzhiyun
368*4882a593Smuzhiyun state = kzalloc(sizeof(*state), GFP_KERNEL);
369*4882a593Smuzhiyun if (!state)
370*4882a593Smuzhiyun return NULL;
371*4882a593Smuzhiyun state->pos = *pos;
372*4882a593Smuzhiyun return state;
373*4882a593Smuzhiyun };
374*4882a593Smuzhiyun
iwl_dbgfs_fw_info_seq_show(struct seq_file * seq,void * v)375*4882a593Smuzhiyun static int iwl_dbgfs_fw_info_seq_show(struct seq_file *seq, void *v)
376*4882a593Smuzhiyun {
377*4882a593Smuzhiyun struct iwl_dbgfs_fw_info_state *state = v;
378*4882a593Smuzhiyun struct iwl_dbgfs_fw_info_priv *priv = seq->private;
379*4882a593Smuzhiyun const struct iwl_fw *fw = priv->fwrt->fw;
380*4882a593Smuzhiyun const struct iwl_fw_cmd_version *ver;
381*4882a593Smuzhiyun u32 cmd_id;
382*4882a593Smuzhiyun
383*4882a593Smuzhiyun if (!state->pos)
384*4882a593Smuzhiyun seq_puts(seq, "fw_api_ver:\n");
385*4882a593Smuzhiyun
386*4882a593Smuzhiyun ver = &fw->ucode_capa.cmd_versions[state->pos];
387*4882a593Smuzhiyun
388*4882a593Smuzhiyun cmd_id = iwl_cmd_id(ver->cmd, ver->group, 0);
389*4882a593Smuzhiyun
390*4882a593Smuzhiyun seq_printf(seq, " 0x%04x:\n", cmd_id);
391*4882a593Smuzhiyun seq_printf(seq, " name: %s\n",
392*4882a593Smuzhiyun iwl_get_cmd_string(priv->fwrt->trans, cmd_id));
393*4882a593Smuzhiyun seq_printf(seq, " cmd_ver: %d\n", ver->cmd_ver);
394*4882a593Smuzhiyun seq_printf(seq, " notif_ver: %d\n", ver->notif_ver);
395*4882a593Smuzhiyun return 0;
396*4882a593Smuzhiyun }
397*4882a593Smuzhiyun
398*4882a593Smuzhiyun static const struct seq_operations iwl_dbgfs_info_seq_ops = {
399*4882a593Smuzhiyun .start = iwl_dbgfs_fw_info_seq_start,
400*4882a593Smuzhiyun .next = iwl_dbgfs_fw_info_seq_next,
401*4882a593Smuzhiyun .stop = iwl_dbgfs_fw_info_seq_stop,
402*4882a593Smuzhiyun .show = iwl_dbgfs_fw_info_seq_show,
403*4882a593Smuzhiyun };
404*4882a593Smuzhiyun
iwl_dbgfs_fw_info_open(struct inode * inode,struct file * filp)405*4882a593Smuzhiyun static int iwl_dbgfs_fw_info_open(struct inode *inode, struct file *filp)
406*4882a593Smuzhiyun {
407*4882a593Smuzhiyun struct iwl_dbgfs_fw_info_priv *priv;
408*4882a593Smuzhiyun
409*4882a593Smuzhiyun priv = __seq_open_private(filp, &iwl_dbgfs_info_seq_ops,
410*4882a593Smuzhiyun sizeof(*priv));
411*4882a593Smuzhiyun
412*4882a593Smuzhiyun if (!priv)
413*4882a593Smuzhiyun return -ENOMEM;
414*4882a593Smuzhiyun
415*4882a593Smuzhiyun priv->fwrt = inode->i_private;
416*4882a593Smuzhiyun return 0;
417*4882a593Smuzhiyun }
418*4882a593Smuzhiyun
419*4882a593Smuzhiyun static const struct file_operations iwl_dbgfs_fw_info_ops = {
420*4882a593Smuzhiyun .owner = THIS_MODULE,
421*4882a593Smuzhiyun .open = iwl_dbgfs_fw_info_open,
422*4882a593Smuzhiyun .read = seq_read,
423*4882a593Smuzhiyun .llseek = seq_lseek,
424*4882a593Smuzhiyun .release = seq_release_private,
425*4882a593Smuzhiyun };
426*4882a593Smuzhiyun
iwl_fwrt_dbgfs_register(struct iwl_fw_runtime * fwrt,struct dentry * dbgfs_dir)427*4882a593Smuzhiyun void iwl_fwrt_dbgfs_register(struct iwl_fw_runtime *fwrt,
428*4882a593Smuzhiyun struct dentry *dbgfs_dir)
429*4882a593Smuzhiyun {
430*4882a593Smuzhiyun INIT_DELAYED_WORK(&fwrt->timestamp.wk, iwl_fw_timestamp_marker_wk);
431*4882a593Smuzhiyun FWRT_DEBUGFS_ADD_FILE(timestamp_marker, dbgfs_dir, 0200);
432*4882a593Smuzhiyun FWRT_DEBUGFS_ADD_FILE(fw_info, dbgfs_dir, 0200);
433*4882a593Smuzhiyun FWRT_DEBUGFS_ADD_FILE(send_hcmd, dbgfs_dir, 0200);
434*4882a593Smuzhiyun FWRT_DEBUGFS_ADD_FILE(fw_dbg_domain, dbgfs_dir, 0400);
435*4882a593Smuzhiyun }
436