GNU Linux-libre 6.8.9-gnu
[releases.git] / drivers / net / wwan / iosm / iosm_ipc_trace.c
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Copyright (C) 2020-2021 Intel Corporation.
4  */
5
6 #include <linux/wwan.h>
7 #include "iosm_ipc_trace.h"
8
9 /* sub buffer size and number of sub buffer */
10 #define IOSM_TRC_SUB_BUFF_SIZE 131072
11 #define IOSM_TRC_N_SUB_BUFF 32
12
13 #define IOSM_TRC_FILE_PERM 0600
14
15 #define IOSM_TRC_DEBUGFS_TRACE "trace"
16 #define IOSM_TRC_DEBUGFS_TRACE_CTRL "trace_ctrl"
17
18 /**
19  * ipc_trace_port_rx - Receive trace packet from cp and write to relay buffer
20  * @ipc_imem:   Pointer to iosm_imem structure
21  * @skb:        Pointer to struct sk_buff
22  */
23 void ipc_trace_port_rx(struct iosm_imem *ipc_imem, struct sk_buff *skb)
24 {
25         struct iosm_trace *ipc_trace = ipc_imem->trace;
26
27         if (ipc_trace->ipc_rchan)
28                 relay_write(ipc_trace->ipc_rchan, skb->data, skb->len);
29
30         dev_kfree_skb(skb);
31 }
32
33 /* Creates relay file in debugfs. */
34 static struct dentry *
35 ipc_trace_create_buf_file_handler(const char *filename,
36                                   struct dentry *parent,
37                                   umode_t mode,
38                                   struct rchan_buf *buf,
39                                   int *is_global)
40 {
41         *is_global = 1;
42         return debugfs_create_file(filename, mode, parent, buf,
43                                    &relay_file_operations);
44 }
45
46 /* Removes relay file from debugfs. */
47 static int ipc_trace_remove_buf_file_handler(struct dentry *dentry)
48 {
49         debugfs_remove(dentry);
50         return 0;
51 }
52
53 static int ipc_trace_subbuf_start_handler(struct rchan_buf *buf, void *subbuf,
54                                           void *prev_subbuf,
55                                           size_t prev_padding)
56 {
57         if (relay_buf_full(buf)) {
58                 pr_err_ratelimited("Relay_buf full dropping traces");
59                 return 0;
60         }
61
62         return 1;
63 }
64
65 /* Relay interface callbacks */
66 static struct rchan_callbacks relay_callbacks = {
67         .subbuf_start = ipc_trace_subbuf_start_handler,
68         .create_buf_file = ipc_trace_create_buf_file_handler,
69         .remove_buf_file = ipc_trace_remove_buf_file_handler,
70 };
71
72 /* Copy the trace control mode to user buffer */
73 static ssize_t ipc_trace_ctrl_file_read(struct file *filp, char __user *buffer,
74                                         size_t count, loff_t *ppos)
75 {
76         struct iosm_trace *ipc_trace = filp->private_data;
77         char buf[16];
78         int len;
79
80         mutex_lock(&ipc_trace->trc_mutex);
81         len = snprintf(buf, sizeof(buf), "%d\n", ipc_trace->mode);
82         mutex_unlock(&ipc_trace->trc_mutex);
83
84         return simple_read_from_buffer(buffer, count, ppos, buf, len);
85 }
86
87 /* Open and close the trace channel depending on user input */
88 static ssize_t ipc_trace_ctrl_file_write(struct file *filp,
89                                          const char __user *buffer,
90                                          size_t count, loff_t *ppos)
91 {
92         struct iosm_trace *ipc_trace = filp->private_data;
93         unsigned long val;
94         int ret;
95
96         ret = kstrtoul_from_user(buffer, count, 10, &val);
97         if (ret)
98                 return ret;
99
100         mutex_lock(&ipc_trace->trc_mutex);
101         if (val == TRACE_ENABLE && ipc_trace->mode != TRACE_ENABLE) {
102                 ipc_trace->channel = ipc_imem_sys_port_open(ipc_trace->ipc_imem,
103                                                             ipc_trace->chl_id,
104                                                             IPC_HP_CDEV_OPEN);
105                 if (!ipc_trace->channel) {
106                         ret = -EIO;
107                         goto unlock;
108                 }
109                 ipc_trace->mode = TRACE_ENABLE;
110         } else if (val == TRACE_DISABLE && ipc_trace->mode != TRACE_DISABLE) {
111                 ipc_trace->mode = TRACE_DISABLE;
112                 /* close trace channel */
113                 ipc_imem_sys_port_close(ipc_trace->ipc_imem,
114                                         ipc_trace->channel);
115                 relay_flush(ipc_trace->ipc_rchan);
116         }
117         ret = count;
118 unlock:
119         mutex_unlock(&ipc_trace->trc_mutex);
120         return ret;
121 }
122
123 static const struct file_operations ipc_trace_fops = {
124         .open = simple_open,
125         .write = ipc_trace_ctrl_file_write,
126         .read  = ipc_trace_ctrl_file_read,
127 };
128
129 /**
130  * ipc_trace_init - Create trace interface & debugfs entries
131  * @ipc_imem:   Pointer to iosm_imem structure
132  *
133  * Returns: Pointer to trace instance on success else NULL
134  */
135 struct iosm_trace *ipc_trace_init(struct iosm_imem *ipc_imem)
136 {
137         struct ipc_chnl_cfg chnl_cfg = { 0 };
138         struct iosm_trace *ipc_trace;
139
140         ipc_chnl_cfg_get(&chnl_cfg, IPC_MEM_CTRL_CHL_ID_3);
141         ipc_imem_channel_init(ipc_imem, IPC_CTYPE_CTRL, chnl_cfg,
142                               IRQ_MOD_OFF);
143
144         ipc_trace = kzalloc(sizeof(*ipc_trace), GFP_KERNEL);
145         if (!ipc_trace)
146                 return NULL;
147
148         ipc_trace->mode = TRACE_DISABLE;
149         ipc_trace->dev = ipc_imem->dev;
150         ipc_trace->ipc_imem = ipc_imem;
151         ipc_trace->chl_id = IPC_MEM_CTRL_CHL_ID_3;
152
153         mutex_init(&ipc_trace->trc_mutex);
154
155         ipc_trace->ctrl_file = debugfs_create_file(IOSM_TRC_DEBUGFS_TRACE_CTRL,
156                                                    IOSM_TRC_FILE_PERM,
157                                                    ipc_imem->debugfs_dir,
158                                                    ipc_trace, &ipc_trace_fops);
159
160         ipc_trace->ipc_rchan = relay_open(IOSM_TRC_DEBUGFS_TRACE,
161                                           ipc_imem->debugfs_dir,
162                                           IOSM_TRC_SUB_BUFF_SIZE,
163                                           IOSM_TRC_N_SUB_BUFF,
164                                           &relay_callbacks, NULL);
165
166         return ipc_trace;
167 }
168
169 /**
170  * ipc_trace_deinit - Closing relayfs, removing debugfs entries
171  * @ipc_trace: Pointer to the iosm_trace data struct
172  */
173 void ipc_trace_deinit(struct iosm_trace *ipc_trace)
174 {
175         if (!ipc_trace)
176                 return;
177
178         debugfs_remove(ipc_trace->ctrl_file);
179         relay_close(ipc_trace->ipc_rchan);
180         mutex_destroy(&ipc_trace->trc_mutex);
181         kfree(ipc_trace);
182 }