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