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