162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2020-2021 Intel Corporation. 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci#include <linux/wwan.h> 762306a36Sopenharmony_ci#include "iosm_ipc_trace.h" 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci/* sub buffer size and number of sub buffer */ 1062306a36Sopenharmony_ci#define IOSM_TRC_SUB_BUFF_SIZE 131072 1162306a36Sopenharmony_ci#define IOSM_TRC_N_SUB_BUFF 32 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci#define IOSM_TRC_FILE_PERM 0600 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci#define IOSM_TRC_DEBUGFS_TRACE "trace" 1662306a36Sopenharmony_ci#define IOSM_TRC_DEBUGFS_TRACE_CTRL "trace_ctrl" 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci/** 1962306a36Sopenharmony_ci * ipc_trace_port_rx - Receive trace packet from cp and write to relay buffer 2062306a36Sopenharmony_ci * @ipc_imem: Pointer to iosm_imem structure 2162306a36Sopenharmony_ci * @skb: Pointer to struct sk_buff 2262306a36Sopenharmony_ci */ 2362306a36Sopenharmony_civoid ipc_trace_port_rx(struct iosm_imem *ipc_imem, struct sk_buff *skb) 2462306a36Sopenharmony_ci{ 2562306a36Sopenharmony_ci struct iosm_trace *ipc_trace = ipc_imem->trace; 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci if (ipc_trace->ipc_rchan) 2862306a36Sopenharmony_ci relay_write(ipc_trace->ipc_rchan, skb->data, skb->len); 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci dev_kfree_skb(skb); 3162306a36Sopenharmony_ci} 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci/* Creates relay file in debugfs. */ 3462306a36Sopenharmony_cistatic struct dentry * 3562306a36Sopenharmony_ciipc_trace_create_buf_file_handler(const char *filename, 3662306a36Sopenharmony_ci struct dentry *parent, 3762306a36Sopenharmony_ci umode_t mode, 3862306a36Sopenharmony_ci struct rchan_buf *buf, 3962306a36Sopenharmony_ci int *is_global) 4062306a36Sopenharmony_ci{ 4162306a36Sopenharmony_ci *is_global = 1; 4262306a36Sopenharmony_ci return debugfs_create_file(filename, mode, parent, buf, 4362306a36Sopenharmony_ci &relay_file_operations); 4462306a36Sopenharmony_ci} 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci/* Removes relay file from debugfs. */ 4762306a36Sopenharmony_cistatic int ipc_trace_remove_buf_file_handler(struct dentry *dentry) 4862306a36Sopenharmony_ci{ 4962306a36Sopenharmony_ci debugfs_remove(dentry); 5062306a36Sopenharmony_ci return 0; 5162306a36Sopenharmony_ci} 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_cistatic int ipc_trace_subbuf_start_handler(struct rchan_buf *buf, void *subbuf, 5462306a36Sopenharmony_ci void *prev_subbuf, 5562306a36Sopenharmony_ci size_t prev_padding) 5662306a36Sopenharmony_ci{ 5762306a36Sopenharmony_ci if (relay_buf_full(buf)) { 5862306a36Sopenharmony_ci pr_err_ratelimited("Relay_buf full dropping traces"); 5962306a36Sopenharmony_ci return 0; 6062306a36Sopenharmony_ci } 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci return 1; 6362306a36Sopenharmony_ci} 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci/* Relay interface callbacks */ 6662306a36Sopenharmony_cistatic struct rchan_callbacks relay_callbacks = { 6762306a36Sopenharmony_ci .subbuf_start = ipc_trace_subbuf_start_handler, 6862306a36Sopenharmony_ci .create_buf_file = ipc_trace_create_buf_file_handler, 6962306a36Sopenharmony_ci .remove_buf_file = ipc_trace_remove_buf_file_handler, 7062306a36Sopenharmony_ci}; 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci/* Copy the trace control mode to user buffer */ 7362306a36Sopenharmony_cistatic ssize_t ipc_trace_ctrl_file_read(struct file *filp, char __user *buffer, 7462306a36Sopenharmony_ci size_t count, loff_t *ppos) 7562306a36Sopenharmony_ci{ 7662306a36Sopenharmony_ci struct iosm_trace *ipc_trace = filp->private_data; 7762306a36Sopenharmony_ci char buf[16]; 7862306a36Sopenharmony_ci int len; 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci mutex_lock(&ipc_trace->trc_mutex); 8162306a36Sopenharmony_ci len = snprintf(buf, sizeof(buf), "%d\n", ipc_trace->mode); 8262306a36Sopenharmony_ci mutex_unlock(&ipc_trace->trc_mutex); 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci return simple_read_from_buffer(buffer, count, ppos, buf, len); 8562306a36Sopenharmony_ci} 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci/* Open and close the trace channel depending on user input */ 8862306a36Sopenharmony_cistatic ssize_t ipc_trace_ctrl_file_write(struct file *filp, 8962306a36Sopenharmony_ci const char __user *buffer, 9062306a36Sopenharmony_ci size_t count, loff_t *ppos) 9162306a36Sopenharmony_ci{ 9262306a36Sopenharmony_ci struct iosm_trace *ipc_trace = filp->private_data; 9362306a36Sopenharmony_ci unsigned long val; 9462306a36Sopenharmony_ci int ret; 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci ret = kstrtoul_from_user(buffer, count, 10, &val); 9762306a36Sopenharmony_ci if (ret) 9862306a36Sopenharmony_ci return ret; 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci mutex_lock(&ipc_trace->trc_mutex); 10162306a36Sopenharmony_ci if (val == TRACE_ENABLE && ipc_trace->mode != TRACE_ENABLE) { 10262306a36Sopenharmony_ci ipc_trace->channel = ipc_imem_sys_port_open(ipc_trace->ipc_imem, 10362306a36Sopenharmony_ci ipc_trace->chl_id, 10462306a36Sopenharmony_ci IPC_HP_CDEV_OPEN); 10562306a36Sopenharmony_ci if (!ipc_trace->channel) { 10662306a36Sopenharmony_ci ret = -EIO; 10762306a36Sopenharmony_ci goto unlock; 10862306a36Sopenharmony_ci } 10962306a36Sopenharmony_ci ipc_trace->mode = TRACE_ENABLE; 11062306a36Sopenharmony_ci } else if (val == TRACE_DISABLE && ipc_trace->mode != TRACE_DISABLE) { 11162306a36Sopenharmony_ci ipc_trace->mode = TRACE_DISABLE; 11262306a36Sopenharmony_ci /* close trace channel */ 11362306a36Sopenharmony_ci ipc_imem_sys_port_close(ipc_trace->ipc_imem, 11462306a36Sopenharmony_ci ipc_trace->channel); 11562306a36Sopenharmony_ci relay_flush(ipc_trace->ipc_rchan); 11662306a36Sopenharmony_ci } 11762306a36Sopenharmony_ci ret = count; 11862306a36Sopenharmony_ciunlock: 11962306a36Sopenharmony_ci mutex_unlock(&ipc_trace->trc_mutex); 12062306a36Sopenharmony_ci return ret; 12162306a36Sopenharmony_ci} 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_cistatic const struct file_operations ipc_trace_fops = { 12462306a36Sopenharmony_ci .open = simple_open, 12562306a36Sopenharmony_ci .write = ipc_trace_ctrl_file_write, 12662306a36Sopenharmony_ci .read = ipc_trace_ctrl_file_read, 12762306a36Sopenharmony_ci}; 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci/** 13062306a36Sopenharmony_ci * ipc_trace_init - Create trace interface & debugfs entries 13162306a36Sopenharmony_ci * @ipc_imem: Pointer to iosm_imem structure 13262306a36Sopenharmony_ci * 13362306a36Sopenharmony_ci * Returns: Pointer to trace instance on success else NULL 13462306a36Sopenharmony_ci */ 13562306a36Sopenharmony_cistruct iosm_trace *ipc_trace_init(struct iosm_imem *ipc_imem) 13662306a36Sopenharmony_ci{ 13762306a36Sopenharmony_ci struct ipc_chnl_cfg chnl_cfg = { 0 }; 13862306a36Sopenharmony_ci struct iosm_trace *ipc_trace; 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci ipc_chnl_cfg_get(&chnl_cfg, IPC_MEM_CTRL_CHL_ID_3); 14162306a36Sopenharmony_ci ipc_imem_channel_init(ipc_imem, IPC_CTYPE_CTRL, chnl_cfg, 14262306a36Sopenharmony_ci IRQ_MOD_OFF); 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci ipc_trace = kzalloc(sizeof(*ipc_trace), GFP_KERNEL); 14562306a36Sopenharmony_ci if (!ipc_trace) 14662306a36Sopenharmony_ci return NULL; 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci ipc_trace->mode = TRACE_DISABLE; 14962306a36Sopenharmony_ci ipc_trace->dev = ipc_imem->dev; 15062306a36Sopenharmony_ci ipc_trace->ipc_imem = ipc_imem; 15162306a36Sopenharmony_ci ipc_trace->chl_id = IPC_MEM_CTRL_CHL_ID_3; 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci mutex_init(&ipc_trace->trc_mutex); 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci ipc_trace->ctrl_file = debugfs_create_file(IOSM_TRC_DEBUGFS_TRACE_CTRL, 15662306a36Sopenharmony_ci IOSM_TRC_FILE_PERM, 15762306a36Sopenharmony_ci ipc_imem->debugfs_dir, 15862306a36Sopenharmony_ci ipc_trace, &ipc_trace_fops); 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci ipc_trace->ipc_rchan = relay_open(IOSM_TRC_DEBUGFS_TRACE, 16162306a36Sopenharmony_ci ipc_imem->debugfs_dir, 16262306a36Sopenharmony_ci IOSM_TRC_SUB_BUFF_SIZE, 16362306a36Sopenharmony_ci IOSM_TRC_N_SUB_BUFF, 16462306a36Sopenharmony_ci &relay_callbacks, NULL); 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci return ipc_trace; 16762306a36Sopenharmony_ci} 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci/** 17062306a36Sopenharmony_ci * ipc_trace_deinit - Closing relayfs, removing debugfs entries 17162306a36Sopenharmony_ci * @ipc_trace: Pointer to the iosm_trace data struct 17262306a36Sopenharmony_ci */ 17362306a36Sopenharmony_civoid ipc_trace_deinit(struct iosm_trace *ipc_trace) 17462306a36Sopenharmony_ci{ 17562306a36Sopenharmony_ci if (!ipc_trace) 17662306a36Sopenharmony_ci return; 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci debugfs_remove(ipc_trace->ctrl_file); 17962306a36Sopenharmony_ci relay_close(ipc_trace->ipc_rchan); 18062306a36Sopenharmony_ci mutex_destroy(&ipc_trace->trc_mutex); 18162306a36Sopenharmony_ci kfree(ipc_trace); 18262306a36Sopenharmony_ci} 183