Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit b1352e2a authored by Skylar Chang's avatar Skylar Chang
Browse files

msm: gsi: data path statistics



Add capabilities to GSI driver to collect data path statistics
in order to help debugging throughput issues.
This change also exposes the a debugfs interface to print stats.

CRs-Fixed: 975119
Change-Id: If97512af034bd419cf4f679b1c19419605529fa6
Acked by: Ady Abraham <adya@qti.qualcomm.com>
Signed-off-by: default avatarSkylar Chang <chiaweic@codeaurora.org>
parent ef78af1b
Loading
Loading
Loading
Loading
+42 −8
Original line number Diff line number Diff line
@@ -315,7 +315,7 @@ static void gsi_incr_ring_rp(struct gsi_ring_ctx *ctx)
		ctx->rp_local = ctx->base;
}

static uint16_t gsi_find_idx_from_addr(struct gsi_ring_ctx *ctx, uint64_t addr)
uint16_t gsi_find_idx_from_addr(struct gsi_ring_ctx *ctx, uint64_t addr)
{
	BUG_ON(addr < ctx->base || addr >= ctx->end);

@@ -1538,10 +1538,12 @@ int gsi_alloc_channel(struct gsi_chan_props *props, unsigned long dev_hdl,

	spin_lock_init(&ctx->ring.slock);
	gsi_init_chan_ring(props, &ctx->ring);

	if (!props->max_re_expected)
		ctx->props.max_re_expected = ctx->ring.max_num_elem;
	ctx->user_data = user_data;
	*chan_hdl = props->ch_id;
	ctx->allocated = true;
	ctx->stats.dp.last_timestamp = jiffies_to_msecs(jiffies);
	atomic_inc(&gsi_ctx->num_chan);

	return GSI_STATUS_SUCCESS;
@@ -1955,35 +1957,67 @@ int gsi_dealloc_channel(unsigned long chan_hdl)
}
EXPORT_SYMBOL(gsi_dealloc_channel);

void gsi_update_ch_dp_stats(struct gsi_chan_ctx *ctx, uint16_t used)
{
	unsigned long now = jiffies_to_msecs(jiffies);
	unsigned long elapsed;

	if (used == 0) {
		elapsed = now - ctx->stats.dp.last_timestamp;
		if (ctx->stats.dp.empty_time < elapsed)
			ctx->stats.dp.empty_time = elapsed;
	}

	if (used <= ctx->props.max_re_expected / 3)
		++ctx->stats.dp.ch_below_lo;
	else if (used <= 2 * ctx->props.max_re_expected / 3)
		++ctx->stats.dp.ch_below_hi;
	else
		++ctx->stats.dp.ch_above_hi;
	ctx->stats.dp.last_timestamp = now;
}

static void __gsi_query_channel_free_re(struct gsi_chan_ctx *ctx,
		uint16_t *num_free_re)
{
	uint16_t start;
	uint16_t start_hw;
	uint16_t end;
	uint64_t rp;
	uint64_t rp_hw;
	int ee = gsi_ctx->per.ee;
	uint16_t used;
	uint16_t used_hw;

	if (!ctx->evtr) {
		rp = gsi_readl(gsi_ctx->base +
	rp_hw = gsi_readl(gsi_ctx->base +
		GSI_EE_n_GSI_CH_k_CNTXT_4_OFFS(ctx->props.ch_id, ee));
		rp |= ((uint64_t)gsi_readl(gsi_ctx->base +
	rp_hw |= ((uint64_t)gsi_readl(gsi_ctx->base +
		GSI_EE_n_GSI_CH_k_CNTXT_5_OFFS(ctx->props.ch_id, ee)))
		<< 32;

	if (!ctx->evtr) {
		rp = rp_hw;
		ctx->ring.rp = rp;
	} else {
		rp = ctx->ring.rp_local;
	}

	start = gsi_find_idx_from_addr(&ctx->ring, rp);
	start_hw = gsi_find_idx_from_addr(&ctx->ring, rp_hw);
	end = gsi_find_idx_from_addr(&ctx->ring, ctx->ring.wp_local);

	if (end >= start)
		used = end - start;
	else
		used = ctx->ring.max_num_elem + 1 - (end - start);
		used = ctx->ring.max_num_elem + 1 - (start - end);

	if (end >= start_hw)
		used_hw = end - start_hw;
	else
		used_hw = ctx->ring.max_num_elem + 1 - (start_hw - end);

	*num_free_re = ctx->ring.max_num_elem - used;
	gsi_update_ch_dp_stats(ctx, used_hw);
}

int gsi_query_channel_info(unsigned long chan_hdl,
+16 −1
Original line number Diff line number Diff line
/* Copyright (c) 2015, The Linux Foundation. All rights reserved.
/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 and
@@ -60,6 +60,14 @@ struct gsi_ring_ctx {
	uint64_t end;
};

struct gsi_chan_dp_stats {
	unsigned long ch_below_lo;
	unsigned long ch_below_hi;
	unsigned long ch_above_hi;
	unsigned long empty_time;
	unsigned long last_timestamp;
};

struct gsi_chan_stats {
	unsigned long queued;
	unsigned long completed;
@@ -68,6 +76,7 @@ struct gsi_chan_stats {
	unsigned long invalid_tre_error;
	unsigned long poll_ok;
	unsigned long poll_empty;
	struct gsi_chan_dp_stats dp;
};

struct gsi_chan_ctx {
@@ -82,6 +91,8 @@ struct gsi_chan_ctx {
	atomic_t poll_mode;
	union __packed gsi_channel_scratch scratch;
	struct gsi_chan_stats stats;
	bool enable_dp_stats;
	bool print_dp_stats;
};

struct gsi_evt_stats {
@@ -128,6 +139,8 @@ struct gsi_ctx {
	atomic_t num_chan;
	atomic_t num_evt_ring;
	struct gsi_ee_scratch scratch;
	int num_ch_dp_stats;
	struct workqueue_struct *dp_stat_wq;
};

enum gsi_re_type {
@@ -203,5 +216,7 @@ enum gsi_evt_ch_cmd_opcode {

extern struct gsi_ctx *gsi_ctx;
void gsi_debugfs_init(void);
uint16_t gsi_find_idx_from_addr(struct gsi_ring_ctx *ctx, uint64_t addr);
void gsi_update_ch_dp_stats(struct gsi_chan_ctx *ctx, uint16_t used);

#endif
+422 −36
Original line number Diff line number Diff line
/* Copyright (c) 2015, The Linux Foundation. All rights reserved.
/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 and
@@ -24,15 +24,17 @@
		pr_err("%s:%d " fmt, __func__, __LINE__, ## args)
#define TDBG(fmt, args...) \
		pr_debug("%s:%d " fmt, __func__, __LINE__, ## args)
#define PRT_STAT(fmt, args...) \
		pr_err(fmt, ## args)

static struct dentry *dent;
static struct dentry *dfile_gsi_ev_dump;
static struct dentry *dfile_gsi_ch_dump;
static struct dentry *dfile_gsi_ee_dump;
static struct dentry *dfile_gsi_map;
static struct dentry *dfile_gsi_stats;
static char dbg_buff[4096];

static void gsi_wq_print_dp_stats(struct work_struct *work);
static DECLARE_DELAYED_WORK(gsi_print_dp_stats_work, gsi_wq_print_dp_stats);
static void gsi_wq_update_dp_stats(struct work_struct *work);
static DECLARE_DELAYED_WORK(gsi_update_dp_stats_work, gsi_wq_update_dp_stats);

static ssize_t gsi_dump_evt(struct file *file,
		const char __user *buf, size_t count, loff_t *ppos)
{
@@ -354,35 +356,374 @@ static ssize_t gsi_dump_map(struct file *file,
	return count;
}

static ssize_t gsi_dump_stats(struct file *file,
		const char __user *buf, size_t count, loff_t *ppos)
static void gsi_dump_ch_stats(struct gsi_chan_ctx *ctx)
{
	struct gsi_chan_ctx *ctx;
	int i;

	for (i = 0; i < GSI_MAX_CHAN; i++) {
		ctx = &gsi_ctx->chan[i];
	if (!ctx->allocated)
		return;

		if (ctx->allocated) {
			TERR("CH%2d:\n", ctx->props.ch_id);
			TERR("queued=%lu compl=%lu\n",
	PRT_STAT("CH%2d:\n", ctx->props.ch_id);
	PRT_STAT("queued=%lu compl=%lu\n",
		ctx->stats.queued,
		ctx->stats.completed);
			TERR("cb->poll=%lu poll->cb=%lu\n",
	PRT_STAT("cb->poll=%lu poll->cb=%lu\n",
		ctx->stats.callback_to_poll,
		ctx->stats.poll_to_callback);
			TERR("invalid_tre_error=%lu\n",
	PRT_STAT("invalid_tre_error=%lu\n",
		ctx->stats.invalid_tre_error);
			TERR("poll_ok=%lu poll_empty=%lu\n",
	PRT_STAT("poll_ok=%lu poll_empty=%lu\n",
		ctx->stats.poll_ok, ctx->stats.poll_empty);
	if (ctx->evtr)
				TERR("compl_evt=%lu\n",
		PRT_STAT("compl_evt=%lu\n",
			ctx->evtr->stats.completed);

	PRT_STAT("ch_below_lo=%lu\n", ctx->stats.dp.ch_below_lo);
	PRT_STAT("ch_below_hi=%lu\n", ctx->stats.dp.ch_below_hi);
	PRT_STAT("ch_above_hi=%lu\n", ctx->stats.dp.ch_above_hi);
	PRT_STAT("time_empty=%lums\n", ctx->stats.dp.empty_time);
	PRT_STAT("\n");
}

static ssize_t gsi_dump_stats(struct file *file,
		const char __user *buf, size_t count, loff_t *ppos)
{
	int ch_id;
	int min, max;

	if (sizeof(dbg_buff) < count + 1)
		goto error;

	if (copy_from_user(dbg_buff, buf, count))
		goto error;

	dbg_buff[count] = '\0';

	if (kstrtos32(dbg_buff, 0, &ch_id))
		goto error;

	if (ch_id == -1) {
		min = 0;
		max = GSI_MAX_CHAN;
	} else if (ch_id < 0 || ch_id >= GSI_MAX_CHAN ||
		   !gsi_ctx->chan[ch_id].allocated) {
		goto error;
	} else {
		min = ch_id;
		max = ch_id + 1;
	}

	for (ch_id = min; ch_id < max; ch_id++)
		gsi_dump_ch_stats(&gsi_ctx->chan[ch_id]);

	return count;
error:
	TERR("Usage: echo ch_id > stats. Use -1 for all\n");
	return -EFAULT;
}

static int gsi_dbg_create_stats_wq(void)
{
	gsi_ctx->dp_stat_wq =
		create_singlethread_workqueue("gsi_stat");
	if (!gsi_ctx->dp_stat_wq) {
		TERR("failed create workqueue\n");
		return -ENOMEM;
	}

	return 0;
}

static void gsi_dbg_destroy_stats_wq(void)
{
	cancel_delayed_work_sync(&gsi_update_dp_stats_work);
	cancel_delayed_work_sync(&gsi_print_dp_stats_work);
	flush_workqueue(gsi_ctx->dp_stat_wq);
	destroy_workqueue(gsi_ctx->dp_stat_wq);
	gsi_ctx->dp_stat_wq = NULL;
}

static ssize_t gsi_enable_dp_stats(struct file *file,
	const char __user *buf, size_t count, loff_t *ppos)
{
	int ch_id;
	bool enable;
	int ret;

	if (sizeof(dbg_buff) < count + 1)
		goto error;

	if (copy_from_user(dbg_buff, buf, count))
		goto error;

	dbg_buff[count] = '\0';

	if (dbg_buff[0] != '+' && dbg_buff[0] != '-')
		goto error;

	enable = (dbg_buff[0] == '+');

	if (kstrtos32(dbg_buff + 1, 0, &ch_id))
		goto error;

	if (ch_id < 0 || ch_id >= GSI_MAX_CHAN ||
	    !gsi_ctx->chan[ch_id].allocated) {
		goto error;
	}

	if (gsi_ctx->chan[ch_id].props.prot == GSI_CHAN_PROT_GPI) {
		TERR("valid for non GPI channels only\n");
		goto error;
	}

	if (gsi_ctx->chan[ch_id].enable_dp_stats == enable) {
		TERR("ch_%d: already enabled/disabled\n", ch_id);
		return -EFAULT;
	}
	gsi_ctx->chan[ch_id].enable_dp_stats = enable;

	if (enable)
		gsi_ctx->num_ch_dp_stats++;
	else
		gsi_ctx->num_ch_dp_stats--;

	if (enable) {
		if (gsi_ctx->num_ch_dp_stats == 1) {
			ret = gsi_dbg_create_stats_wq();
			if (ret)
				return ret;
		}
		cancel_delayed_work_sync(&gsi_update_dp_stats_work);
		queue_delayed_work(gsi_ctx->dp_stat_wq,
			&gsi_update_dp_stats_work, msecs_to_jiffies(10));
	} else if (!enable && gsi_ctx->num_ch_dp_stats == 0) {
		gsi_dbg_destroy_stats_wq();
	}

	return count;
error:
	TERR("Usage: echo [+-]ch_id > enable_dp_stats\n");
	return -EFAULT;
}

static ssize_t gsi_set_max_elem_dp_stats(struct file *file,
		const char __user *buf, size_t count, loff_t *ppos)
{
	u32 ch_id;
	u32 max_elem;
	unsigned long missing;
	char *sptr, *token;


	if (sizeof(dbg_buff) < count + 1)
		goto error;

	missing = copy_from_user(dbg_buff, buf, count);
	if (missing)
		goto error;

	dbg_buff[count] = '\0';

	sptr = dbg_buff;

	token = strsep(&sptr, " ");
	if (!token) {
		TERR("\n");
		goto error;
	}

	if (kstrtou32(token, 0, &ch_id)) {
		TERR("\n");
		goto error;
	}

	token = strsep(&sptr, " ");
	if (!token) {
		/* get */
		if (kstrtou32(dbg_buff, 0, &ch_id))
			goto error;
		if (ch_id >= GSI_MAX_CHAN)
			goto error;
		PRT_STAT("ch %d: max_re_expected=%d\n", ch_id,
			gsi_ctx->chan[ch_id].props.max_re_expected);
		return count;
	}
	if (kstrtou32(token, 0, &max_elem)) {
		TERR("\n");
		goto error;
	}

	TDBG("ch_id=%u max_elem=%u\n", ch_id, max_elem);

	if (ch_id >= GSI_MAX_CHAN) {
		TERR("invalid chan id %u\n", ch_id);
		goto error;
	}

	gsi_ctx->chan[ch_id].props.max_re_expected = max_elem;

	return count;

error:
	TERR("Usage: (set) echo <ch_id> <max_elem> > max_elem_dp_stats\n");
	TERR("Usage: (get) echo <ch_id> > max_elem_dp_stats\n");
	return -EFAULT;
}

static void gsi_wq_print_dp_stats(struct work_struct *work)
{
	int ch_id;

	for (ch_id = 0; ch_id < GSI_MAX_CHAN; ch_id++) {
		if (gsi_ctx->chan[ch_id].print_dp_stats)
			gsi_dump_ch_stats(&gsi_ctx->chan[ch_id]);
	}

	queue_delayed_work(gsi_ctx->dp_stat_wq, &gsi_print_dp_stats_work,
		msecs_to_jiffies(1000));
}

static void gsi_dbg_update_ch_dp_stats(struct gsi_chan_ctx *ctx)
{
	uint16_t start_hw;
	uint16_t end_hw;
	uint64_t rp_hw;
	uint64_t wp_hw;
	int ee = gsi_ctx->per.ee;
	uint16_t used_hw;

	rp_hw = gsi_readl(gsi_ctx->base +
		GSI_EE_n_GSI_CH_k_CNTXT_4_OFFS(ctx->props.ch_id, ee));
	rp_hw |= ((uint64_t)gsi_readl(gsi_ctx->base +
		GSI_EE_n_GSI_CH_k_CNTXT_5_OFFS(ctx->props.ch_id, ee)))
		<< 32;

	wp_hw = gsi_readl(gsi_ctx->base +
		GSI_EE_n_GSI_CH_k_CNTXT_6_OFFS(ctx->props.ch_id, ee));
	wp_hw |= ((uint64_t)gsi_readl(gsi_ctx->base +
		GSI_EE_n_GSI_CH_k_CNTXT_7_OFFS(ctx->props.ch_id, ee)))
		<< 32;

	start_hw = gsi_find_idx_from_addr(&ctx->ring, rp_hw);
	end_hw = gsi_find_idx_from_addr(&ctx->ring, wp_hw);

	if (end_hw >= start_hw)
		used_hw = end_hw - start_hw;
	else
		used_hw = ctx->ring.max_num_elem + 1 - (start_hw - end_hw);

	TERR("ch %d used %d\n", ctx->props.ch_id, used_hw);
	gsi_update_ch_dp_stats(ctx, used_hw);
}

static void gsi_wq_update_dp_stats(struct work_struct *work)
{
	int ch_id;

	for (ch_id = 0; ch_id < GSI_MAX_CHAN; ch_id++) {
		if (gsi_ctx->chan[ch_id].allocated &&
		    gsi_ctx->chan[ch_id].props.prot != GSI_CHAN_PROT_GPI &&
		    gsi_ctx->chan[ch_id].enable_dp_stats)
			gsi_dbg_update_ch_dp_stats(&gsi_ctx->chan[ch_id]);
	}

	queue_delayed_work(gsi_ctx->dp_stat_wq, &gsi_update_dp_stats_work,
		msecs_to_jiffies(10));
}


static ssize_t gsi_rst_stats(struct file *file,
		const char __user *buf, size_t count, loff_t *ppos)
{
	int ch_id;
	int min, max;

	if (sizeof(dbg_buff) < count + 1)
		goto error;

	if (copy_from_user(dbg_buff, buf, count))
		goto error;

	dbg_buff[count] = '\0';

	if (kstrtos32(dbg_buff, 0, &ch_id))
		goto error;

	if (ch_id == -1) {
		min = 0;
		max = GSI_MAX_CHAN;
	} else if (ch_id < 0 || ch_id >= GSI_MAX_CHAN ||
		   !gsi_ctx->chan[ch_id].allocated) {
		goto error;
	}

	min = ch_id;
	max = ch_id + 1;

	for (ch_id = min; ch_id < max; ch_id++)
		memset(&gsi_ctx->chan[ch_id].stats, 0,
			sizeof(gsi_ctx->chan[ch_id].stats));

	return count;
error:
	TERR("Usage: echo ch_id > rst_stats. Use -1 for all\n");
	return -EFAULT;
}

static ssize_t gsi_print_dp_stats(struct file *file,
	const char __user *buf, size_t count, loff_t *ppos)
{
	int ch_id;
	bool enable;
	int ret;

	if (sizeof(dbg_buff) < count + 1)
		goto error;

	if (copy_from_user(dbg_buff, buf, count))
		goto error;

	dbg_buff[count] = '\0';

	if (dbg_buff[0] != '+' && dbg_buff[0] != '-')
		goto error;

	enable = (dbg_buff[0] == '+');

	if (kstrtos32(dbg_buff + 1, 0, &ch_id))
		goto error;

	if (ch_id < 0 || ch_id >= GSI_MAX_CHAN ||
	    !gsi_ctx->chan[ch_id].allocated) {
		goto error;
	}

	if (gsi_ctx->chan[ch_id].print_dp_stats == enable) {
		TERR("ch_%d: already enabled/disabled\n", ch_id);
		return -EFAULT;
	}
	gsi_ctx->chan[ch_id].print_dp_stats = enable;

	if (enable)
		gsi_ctx->num_ch_dp_stats++;
	else
		gsi_ctx->num_ch_dp_stats--;

	if (enable) {
		if (gsi_ctx->num_ch_dp_stats == 1) {
			ret = gsi_dbg_create_stats_wq();
			if (ret)
				return ret;
		}
		cancel_delayed_work_sync(&gsi_print_dp_stats_work);
		queue_delayed_work(gsi_ctx->dp_stat_wq,
			&gsi_print_dp_stats_work, msecs_to_jiffies(10));
	} else if (!enable && gsi_ctx->num_ch_dp_stats == 0) {
		gsi_dbg_destroy_stats_wq();
	}

	return count;
error:
	TERR("Usage: echo [+-]ch_id > print_dp_stats\n");
	return -EFAULT;
}

const struct file_operations gsi_ev_dump_ops = {
@@ -405,8 +746,25 @@ const struct file_operations gsi_stats_ops = {
	.write = gsi_dump_stats,
};

const struct file_operations gsi_enable_dp_stats_ops = {
	.write = gsi_enable_dp_stats,
};

const struct file_operations gsi_max_elem_dp_stats_ops = {
	.write = gsi_set_max_elem_dp_stats,
};

const struct file_operations gsi_rst_stats_ops = {
	.write = gsi_rst_stats,
};

const struct file_operations gsi_print_dp_stats_ops = {
	.write = gsi_print_dp_stats,
};

void gsi_debugfs_init(void)
{
	static struct dentry *dfile;
	const mode_t read_only_mode = S_IRUSR | S_IRGRP | S_IROTH;
	const mode_t write_only_mode = S_IWUSR | S_IWGRP;

@@ -416,37 +774,65 @@ void gsi_debugfs_init(void)
		return;
	}

	dfile_gsi_ev_dump = debugfs_create_file("ev_dump", write_only_mode,
	dfile = debugfs_create_file("ev_dump", write_only_mode,
			dent, 0, &gsi_ev_dump_ops);
	if (!dfile_gsi_ev_dump || IS_ERR(dfile_gsi_ev_dump)) {
	if (!dfile || IS_ERR(dfile)) {
		TERR("fail to create ev_dump file\n");
		goto fail;
	}

	dfile_gsi_ch_dump = debugfs_create_file("ch_dump", write_only_mode,
	dfile = debugfs_create_file("ch_dump", write_only_mode,
			dent, 0, &gsi_ch_dump_ops);
	if (!dfile_gsi_ch_dump || IS_ERR(dfile_gsi_ch_dump)) {
	if (!dfile || IS_ERR(dfile)) {
		TERR("fail to create ch_dump file\n");
		goto fail;
	}

	dfile_gsi_ee_dump = debugfs_create_file("ee_dump", read_only_mode, dent,
	dfile = debugfs_create_file("ee_dump", read_only_mode, dent,
			0, &gsi_ee_dump_ops);
	if (!dfile_gsi_ee_dump || IS_ERR(dfile_gsi_ee_dump)) {
	if (!dfile || IS_ERR(dfile)) {
		TERR("fail to create ee_dump file\n");
		goto fail;
	}

	dfile_gsi_map = debugfs_create_file("map", read_only_mode, dent,
	dfile = debugfs_create_file("map", read_only_mode, dent,
			0, &gsi_map_ops);
	if (!dfile_gsi_map || IS_ERR(dfile_gsi_map)) {
	if (!dfile || IS_ERR(dfile)) {
		TERR("fail to create map file\n");
		goto fail;
	}

	dfile_gsi_stats = debugfs_create_file("stats", read_only_mode, dent,
	dfile = debugfs_create_file("stats", write_only_mode, dent,
			0, &gsi_stats_ops);
	if (!dfile_gsi_stats || IS_ERR(dfile_gsi_stats)) {
	if (!dfile || IS_ERR(dfile)) {
		TERR("fail to create stats file\n");
		goto fail;
	}

	dfile = debugfs_create_file("enable_dp_stats", write_only_mode, dent,
			0, &gsi_enable_dp_stats_ops);
	if (!dfile || IS_ERR(dfile)) {
		TERR("fail to create stats file\n");
		goto fail;
	}

	dfile = debugfs_create_file("max_elem_dp_stats", write_only_mode,
		dent, 0, &gsi_max_elem_dp_stats_ops);
	if (!dfile || IS_ERR(dfile)) {
		TERR("fail to create stats file\n");
		goto fail;
	}

	dfile = debugfs_create_file("rst_stats", write_only_mode,
		dent, 0, &gsi_rst_stats_ops);
	if (!dfile || IS_ERR(dfile)) {
		TERR("fail to create stats file\n");
		goto fail;
	}

	dfile = debugfs_create_file("print_dp_stats",
		write_only_mode, dent, 0, &gsi_print_dp_stats_ops);
	if (!dfile || IS_ERR(dfile)) {
		TERR("fail to create stats file\n");
		goto fail;
	}
+4 −2
Original line number Diff line number Diff line
@@ -3570,10 +3570,12 @@ static int ipa_gsi_setup_channel(struct ipa3_ep_context *ep)

	memset(&gsi_channel_props, 0, sizeof(gsi_channel_props));
	gsi_channel_props.prot = GSI_CHAN_PROT_GPI;
	if (IPA_CLIENT_IS_PROD(ep->client))
	if (IPA_CLIENT_IS_PROD(ep->client)) {
		gsi_channel_props.dir = GSI_CHAN_DIR_TO_GSI;
	else
	} else {
		gsi_channel_props.dir = GSI_CHAN_DIR_FROM_GSI;
		gsi_channel_props.max_re_expected = ep->sys->rx_pool_sz;
	}

	gsi_ep_info = ipa3_get_gsi_ep_info(ipa3_get_ep_mapping(ep->client));
	if (!gsi_ep_info) {
+5 −1
Original line number Diff line number Diff line
/* Copyright (c) 2015, The Linux Foundation. All rights reserved.
/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 and
@@ -280,6 +280,9 @@ enum gsi_chan_use_db_eng {
 * @re_size:         size of channel ring element
 * @ring_len:        length of ring in bytes (must be integral multiple of
 *                   re_size)
 * @max_re_expected: maximal number of ring elements expected to be queued.
 *                   used for data path statistics gathering. if 0 provided
 *                   ring_len / re_size will be used.
 * @ring_base_addr:  physical base address of ring. Address must be aligned to
 *                   ring_len rounded to power of two
 * @ring_base_vaddr: virtual base address of ring (set to NULL when not
@@ -333,6 +336,7 @@ struct gsi_chan_props {
	unsigned long evt_ring_hdl;
	enum gsi_chan_ring_elem_size re_size;
	uint16_t ring_len;
	uint16_t max_re_expected;
	uint64_t ring_base_addr;
	void *ring_base_vaddr;
	enum gsi_chan_use_db_eng use_db_eng;