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

Commit 6e8eff88 authored by qctecmdr's avatar qctecmdr Committed by Gerrit - the friendly Code Review server
Browse files

Merge "icnss2: Add platform driver code for QDSS on moselle"

parents 70f32294 049053ce
Loading
Loading
Loading
Loading
+1 −0
Original line number Original line Diff line number Diff line
@@ -6,4 +6,5 @@ obj-$(CONFIG_ICNSS2) += icnss2.o
icnss2-y := main.o
icnss2-y := main.o
icnss2-y += debug.o
icnss2-y += debug.o
icnss2-y += power.o
icnss2-y += power.o
icnss2-y += genl.o
icnss2-$(CONFIG_ICNSS2_QMI) += qmi.o
icnss2-$(CONFIG_ICNSS2_QMI) += qmi.o
+213 −0
Original line number Original line Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0-only
/* Copyright (c) 2020, The Linux Foundation. All rights reserved. */

#define pr_fmt(fmt) "cnss_genl: " fmt

#include <linux/err.h>
#include <linux/module.h>
#include <linux/delay.h>
#include <net/netlink.h>
#include <net/genetlink.h>

#include "main.h"
#include "debug.h"

#define ICNSS_GENL_FAMILY_NAME "cnss-genl"
#define ICNSS_GENL_MCAST_GROUP_NAME "cnss-genl-grp"
#define ICNSS_GENL_VERSION 1
#define ICNSS_GENL_DATA_LEN_MAX (15 * 1024)
#define ICNSS_GENL_STR_LEN_MAX 16

enum {
	ICNSS_GENL_ATTR_MSG_UNSPEC,
	ICNSS_GENL_ATTR_MSG_TYPE,
	ICNSS_GENL_ATTR_MSG_FILE_NAME,
	ICNSS_GENL_ATTR_MSG_TOTAL_SIZE,
	ICNSS_GENL_ATTR_MSG_SEG_ID,
	ICNSS_GENL_ATTR_MSG_END,
	ICNSS_GENL_ATTR_MSG_DATA_LEN,
	ICNSS_GENL_ATTR_MSG_DATA,
	__ICNSS_GENL_ATTR_MAX,
};

#define ICNSS_GENL_ATTR_MAX (__ICNSS_GENL_ATTR_MAX - 1)

enum {
	ICNSS_GENL_CMD_UNSPEC,
	ICNSS_GENL_CMD_MSG,
	__ICNSS_GENL_CMD_MAX,
};

#define ICNSS_GENL_CMD_MAX (__ICNSS_GENL_CMD_MAX - 1)

static struct nla_policy icnss_genl_msg_policy[ICNSS_GENL_ATTR_MAX + 1] = {
	[ICNSS_GENL_ATTR_MSG_TYPE] = { .type = NLA_U8 },
	[ICNSS_GENL_ATTR_MSG_FILE_NAME] = { .type = NLA_NUL_STRING,
					   .len = ICNSS_GENL_STR_LEN_MAX },
	[ICNSS_GENL_ATTR_MSG_TOTAL_SIZE] = { .type = NLA_U32 },
	[ICNSS_GENL_ATTR_MSG_SEG_ID] = { .type = NLA_U32 },
	[ICNSS_GENL_ATTR_MSG_END] = { .type = NLA_U8 },
	[ICNSS_GENL_ATTR_MSG_DATA_LEN] = { .type = NLA_U32 },
	[ICNSS_GENL_ATTR_MSG_DATA] = { .type = NLA_BINARY,
				      .len = ICNSS_GENL_DATA_LEN_MAX },
};

static int icnss_genl_process_msg(struct sk_buff *skb, struct genl_info *info)
{
	return 0;
}

static struct genl_ops icnss_genl_ops[] = {
	{
		.cmd = ICNSS_GENL_CMD_MSG,
		.policy = icnss_genl_msg_policy,
		.doit = icnss_genl_process_msg,
	},
};

static struct genl_multicast_group icnss_genl_mcast_grp[] = {
	{
		.name = ICNSS_GENL_MCAST_GROUP_NAME,
	},
};

static struct genl_family icnss_genl_family = {
	.id = 0,
	.hdrsize = 0,
	.name = ICNSS_GENL_FAMILY_NAME,
	.version = ICNSS_GENL_VERSION,
	.maxattr = ICNSS_GENL_ATTR_MAX,
	.module = THIS_MODULE,
	.ops = icnss_genl_ops,
	.n_ops = ARRAY_SIZE(icnss_genl_ops),
	.mcgrps = icnss_genl_mcast_grp,
	.n_mcgrps = ARRAY_SIZE(icnss_genl_mcast_grp),
};

static int icnss_genl_send_data(u8 type, char *file_name, u32 total_size,
				u32 seg_id, u8 end, u32 data_len, u8 *msg_buff)
{
	struct sk_buff *skb = NULL;
	void *msg_header = NULL;
	int ret = 0;
	char filename[ICNSS_GENL_STR_LEN_MAX + 1];

	icnss_pr_dbg("type: %u, file_name %s, total_size: %x, seg_id %u, end %u, data_len %u\n",
		     type, file_name, total_size, seg_id, end, data_len);

	if (!file_name)
		strlcpy(filename, "default", sizeof(filename));
	else
		strlcpy(filename, file_name, sizeof(filename));

	skb = genlmsg_new(NLMSG_HDRLEN +
			  nla_total_size(sizeof(type)) +
			  nla_total_size(strlen(filename) + 1) +
			  nla_total_size(sizeof(total_size)) +
			  nla_total_size(sizeof(seg_id)) +
			  nla_total_size(sizeof(end)) +
			  nla_total_size(sizeof(data_len)) +
			  nla_total_size(data_len), GFP_KERNEL);
	if (!skb)
		return -ENOMEM;

	msg_header = genlmsg_put(skb, 0, 0,
				 &icnss_genl_family, 0,
				 ICNSS_GENL_CMD_MSG);
	if (!msg_header) {
		ret = -ENOMEM;
		goto fail;
	}

	ret = nla_put_u8(skb, ICNSS_GENL_ATTR_MSG_TYPE, type);
	if (ret < 0)
		goto fail;
	ret = nla_put_string(skb, ICNSS_GENL_ATTR_MSG_FILE_NAME, filename);
	if (ret < 0)
		goto fail;
	ret = nla_put_u32(skb, ICNSS_GENL_ATTR_MSG_TOTAL_SIZE, total_size);
	if (ret < 0)
		goto fail;
	ret = nla_put_u32(skb, ICNSS_GENL_ATTR_MSG_SEG_ID, seg_id);
	if (ret < 0)
		goto fail;
	ret = nla_put_u8(skb, ICNSS_GENL_ATTR_MSG_END, end);
	if (ret < 0)
		goto fail;
	ret = nla_put_u32(skb, ICNSS_GENL_ATTR_MSG_DATA_LEN, data_len);
	if (ret < 0)
		goto fail;
	ret = nla_put(skb, ICNSS_GENL_ATTR_MSG_DATA, data_len, msg_buff);
	if (ret < 0)
		goto fail;

	genlmsg_end(skb, msg_header);
	ret = genlmsg_multicast(&icnss_genl_family, skb, 0, 0, GFP_KERNEL);
	if (ret < 0)
		icnss_pr_err("Fail to send genl msg: %d\n", ret);

	return ret;
fail:
	icnss_pr_err("Fail to generate genl msg: %d\n", ret);
	if (skb)
		nlmsg_free(skb);
	return ret;
}

int icnss_genl_send_msg(void *buff, u8 type, char *file_name, u32 total_size)
{
	int ret = 0;
	u8 *msg_buff = buff;
	u32 remaining = total_size;
	u32 seg_id = 0;
	u32 data_len = 0;
	u8 end = 0;
	u8 retry;

	icnss_pr_dbg("type: %u, total_size: %x\n", type, total_size);

	while (remaining) {
		if (remaining > ICNSS_GENL_DATA_LEN_MAX) {
			data_len = ICNSS_GENL_DATA_LEN_MAX;
		} else {
			data_len = remaining;
			end = 1;
		}

		for (retry = 0; retry < 2; retry++) {
			ret = icnss_genl_send_data(type, file_name, total_size,
						   seg_id, end, data_len,
						   msg_buff);
			if (ret >= 0)
				break;
			msleep(100);
		}

		if (ret < 0) {
			icnss_pr_err("fail to send genl data, ret %d\n", ret);
			return ret;
		}

		remaining -= data_len;
		msg_buff += data_len;
		seg_id++;
	}

	return ret;
}

int icnss_genl_init(void)
{
	int ret = 0;

	ret = genl_register_family(&icnss_genl_family);
	if (ret != 0)
		icnss_pr_err("genl_register_family fail: %d\n", ret);

	return ret;
}

void icnss_genl_exit(void)
{
	genl_unregister_family(&icnss_genl_family);
}
+17 −0
Original line number Original line Diff line number Diff line
/* SPDX-License-Identifier: GPL-2.0-only */
/* Copyright (c) 2019-2020, The Linux Foundation. All rights reserved. */

#ifndef __ICNSS_GENL_H__
#define __ICNSS_GENL_H__

enum icnss_genl_msg_type {
	ICNSS_GENL_MSG_TYPE_UNSPEC,
	ICNSS_GENL_MSG_TYPE_QDSS,
};

int icnss_genl_init(void);
void icnss_genl_exit(void);
int icnss_genl_send_msg(void *buff, u8 type,
			char *file_name, u32 total_size);

#endif
+183 −0
Original line number Original line Diff line number Diff line
@@ -43,6 +43,7 @@
#include "qmi.h"
#include "qmi.h"
#include "debug.h"
#include "debug.h"
#include "power.h"
#include "power.h"
#include "genl.h"


#define MAX_PROP_SIZE			32
#define MAX_PROP_SIZE			32
#define NUM_LOG_PAGES			10
#define NUM_LOG_PAGES			10
@@ -163,6 +164,12 @@ char *icnss_driver_event_to_str(enum icnss_driver_event_type type)
		return "IDLE_RESTART";
		return "IDLE_RESTART";
	case ICNSS_DRIVER_EVENT_FW_INIT_DONE_IND:
	case ICNSS_DRIVER_EVENT_FW_INIT_DONE_IND:
		return "FW_INIT_DONE";
		return "FW_INIT_DONE";
	case ICNSS_DRIVER_EVENT_QDSS_TRACE_REQ_MEM:
		return "QDSS_TRACE_REQ_MEM";
	case ICNSS_DRIVER_EVENT_QDSS_TRACE_SAVE:
		return "QDSS_TRACE_SAVE";
	case ICNSS_DRIVER_EVENT_QDSS_TRACE_FREE:
		return "QDSS_TRACE_FREE";
	case ICNSS_DRIVER_EVENT_MAX:
	case ICNSS_DRIVER_EVENT_MAX:
		return "EVENT_MAX";
		return "EVENT_MAX";
	}
	}
@@ -728,6 +735,159 @@ static int icnss_driver_event_fw_init_done(struct icnss_priv *priv, void *data)
	return ret;
	return ret;
}
}


int icnss_alloc_qdss_mem(struct icnss_priv *priv)
{
	struct platform_device *pdev = priv->pdev;
	struct icnss_fw_mem *qdss_mem = priv->qdss_mem;
	int i, j;

	for (i = 0; i < priv->qdss_mem_seg_len; i++) {
		if (!qdss_mem[i].va && qdss_mem[i].size) {
			qdss_mem[i].va =
				dma_alloc_coherent(&pdev->dev,
						   qdss_mem[i].size,
						   &qdss_mem[i].pa,
						   GFP_KERNEL);
			if (!qdss_mem[i].va) {
				icnss_pr_err("Failed to allocate QDSS memory for FW, size: 0x%zx, type: %u, chuck-ID: %d\n",
					     qdss_mem[i].size,
					     qdss_mem[i].type, i);
				break;
			}
		}
	}

	/* Best-effort allocation for QDSS trace */
	if (i < priv->qdss_mem_seg_len) {
		for (j = i; j < priv->qdss_mem_seg_len; j++) {
			qdss_mem[j].type = 0;
			qdss_mem[j].size = 0;
		}
		priv->qdss_mem_seg_len = i;
	}

	return 0;
}

void icnss_free_qdss_mem(struct icnss_priv *priv)
{
	struct platform_device *pdev = priv->pdev;
	struct icnss_fw_mem *qdss_mem = priv->qdss_mem;
	int i;

	for (i = 0; i < priv->qdss_mem_seg_len; i++) {
		if (qdss_mem[i].va && qdss_mem[i].size) {
			icnss_pr_dbg("Freeing memory for QDSS: pa: %pa, size: 0x%zx, type: %u\n",
				     &qdss_mem[i].pa, qdss_mem[i].size,
				     qdss_mem[i].type);
			dma_free_coherent(&pdev->dev,
					  qdss_mem[i].size, qdss_mem[i].va,
					  qdss_mem[i].pa);
			qdss_mem[i].va = NULL;
			qdss_mem[i].pa = 0;
			qdss_mem[i].size = 0;
			qdss_mem[i].type = 0;
		}
	}
	priv->qdss_mem_seg_len = 0;
}

static int icnss_qdss_trace_req_mem_hdlr(struct icnss_priv *priv)
{
	int ret = 0;

	ret = icnss_alloc_qdss_mem(priv);
	if (ret < 0)
		return ret;

	return wlfw_qdss_trace_mem_info_send_sync(priv);
}

static void *icnss_qdss_trace_pa_to_va(struct icnss_priv *priv,
				       u64 pa, u32 size, int *seg_id)
{
	int i = 0;
	struct icnss_fw_mem *qdss_mem = priv->qdss_mem;
	u64 offset = 0;
	void *va = NULL;
	u64 local_pa;
	u32 local_size;

	for (i = 0; i < priv->qdss_mem_seg_len; i++) {
		local_pa = (u64)qdss_mem[i].pa;
		local_size = (u32)qdss_mem[i].size;
		if (pa == local_pa && size <= local_size) {
			va = qdss_mem[i].va;
			break;
		}
		if (pa > local_pa &&
		    pa < local_pa + local_size &&
		    pa + size <= local_pa + local_size) {
			offset = pa - local_pa;
			va = qdss_mem[i].va + offset;
			break;
		}
	}

	*seg_id = i;
	return va;
}

static int icnss_qdss_trace_save_hdlr(struct icnss_priv *priv,
				      void *data)
{
	struct icnss_qmi_event_qdss_trace_save_data *event_data = data;
	struct icnss_fw_mem *qdss_mem = priv->qdss_mem;
	int ret = 0;
	int i;
	void *va = NULL;
	u64 pa;
	u32 size;
	int seg_id = 0;

	if (!priv->qdss_mem_seg_len) {
		icnss_pr_err("Memory for QDSS trace is not available\n");
		return -ENOMEM;
	}

	if (event_data->mem_seg_len == 0) {
		for (i = 0; i < priv->qdss_mem_seg_len; i++) {
			ret = icnss_genl_send_msg(qdss_mem[i].va,
						  ICNSS_GENL_MSG_TYPE_QDSS,
						  event_data->file_name,
						  qdss_mem[i].size);
			if (ret < 0) {
				icnss_pr_err("Fail to save QDSS data: %d\n",
					     ret);
				break;
			}
		}
	} else {
		for (i = 0; i < event_data->mem_seg_len; i++) {
			pa = event_data->mem_seg[i].addr;
			size = event_data->mem_seg[i].size;
			va = icnss_qdss_trace_pa_to_va(priv, pa,
						       size, &seg_id);
			if (!va) {
				icnss_pr_err("Fail to find matching va for pa %pa\n",
					     &pa);
				ret = -EINVAL;
				break;
			}
			ret = icnss_genl_send_msg(va, ICNSS_GENL_MSG_TYPE_QDSS,
						  event_data->file_name, size);
			if (ret < 0) {
				icnss_pr_err("Fail to save QDSS data: %d\n",
					     ret);
				break;
			}
		}
	}

	kfree(data);
	return ret;
}

static int icnss_driver_event_register_driver(struct icnss_priv *priv,
static int icnss_driver_event_register_driver(struct icnss_priv *priv,
							 void *data)
							 void *data)
{
{
@@ -955,6 +1115,13 @@ static int icnss_driver_event_idle_restart(struct icnss_priv *priv,
	return ret;
	return ret;
}
}


static int icnss_qdss_trace_free_hdlr(struct icnss_priv *priv)
{
	icnss_free_qdss_mem(priv);

	return 0;
}

static void icnss_driver_event_work(struct work_struct *work)
static void icnss_driver_event_work(struct work_struct *work)
{
{
	struct icnss_priv *priv =
	struct icnss_priv *priv =
@@ -1018,6 +1185,16 @@ static void icnss_driver_event_work(struct work_struct *work)
			ret = icnss_driver_event_fw_init_done(priv,
			ret = icnss_driver_event_fw_init_done(priv,
							      event->data);
							      event->data);
			break;
			break;
		case ICNSS_DRIVER_EVENT_QDSS_TRACE_REQ_MEM:
			ret = icnss_qdss_trace_req_mem_hdlr(priv);
			break;
		case ICNSS_DRIVER_EVENT_QDSS_TRACE_SAVE:
			ret = icnss_qdss_trace_save_hdlr(priv,
							 event->data);
			break;
		case ICNSS_DRIVER_EVENT_QDSS_TRACE_FREE:
			ret = icnss_qdss_trace_free_hdlr(priv);
			break;
		default:
		default:
			icnss_pr_err("Invalid Event type: %d", event->type);
			icnss_pr_err("Invalid Event type: %d", event->type);
			kfree(event);
			kfree(event);
@@ -2486,6 +2663,10 @@ static int icnss_probe(struct platform_device *pdev)


	init_completion(&priv->unblock_shutdown);
	init_completion(&priv->unblock_shutdown);


	ret = icnss_genl_init();
	if (ret < 0)
		icnss_pr_err("ICNSS genl init failed %d\n", ret);

	icnss_pr_info("Platform driver probed successfully\n");
	icnss_pr_info("Platform driver probed successfully\n");


	return 0;
	return 0;
@@ -2506,6 +2687,8 @@ static int icnss_remove(struct platform_device *pdev)


	icnss_pr_info("Removing driver: state: 0x%lx\n", priv->state);
	icnss_pr_info("Removing driver: state: 0x%lx\n", priv->state);


	icnss_genl_exit();

	device_init_wakeup(&priv->pdev->dev, false);
	device_init_wakeup(&priv->pdev->dev, false);


	icnss_debugfs_destroy(priv);
	icnss_debugfs_destroy(priv);
+15 −0
Original line number Original line Diff line number Diff line
@@ -21,6 +21,7 @@


#define WCN6750_DEVICE_ID 0x6750
#define WCN6750_DEVICE_ID 0x6750
#define ADRASTEA_DEVICE_ID 0xabcd
#define ADRASTEA_DEVICE_ID 0xabcd
#define QMI_WLFW_MAX_NUM_MEM_SEG 32


extern uint64_t dynamic_feature_mask;
extern uint64_t dynamic_feature_mask;


@@ -48,6 +49,9 @@ enum icnss_driver_event_type {
	ICNSS_DRIVER_EVENT_IDLE_SHUTDOWN,
	ICNSS_DRIVER_EVENT_IDLE_SHUTDOWN,
	ICNSS_DRIVER_EVENT_IDLE_RESTART,
	ICNSS_DRIVER_EVENT_IDLE_RESTART,
	ICNSS_DRIVER_EVENT_FW_INIT_DONE_IND,
	ICNSS_DRIVER_EVENT_FW_INIT_DONE_IND,
	ICNSS_DRIVER_EVENT_QDSS_TRACE_REQ_MEM,
	ICNSS_DRIVER_EVENT_QDSS_TRACE_SAVE,
	ICNSS_DRIVER_EVENT_QDSS_TRACE_FREE,
	ICNSS_DRIVER_EVENT_MAX,
	ICNSS_DRIVER_EVENT_MAX,
};
};


@@ -130,6 +134,15 @@ struct icnss_clk_info {
	u32 enabled;
	u32 enabled;
};
};


struct icnss_fw_mem {
	size_t size;
	void *va;
	phys_addr_t pa;
	u8 valid;
	u32 type;
	unsigned long attrs;
};

struct icnss_stats {
struct icnss_stats {
	struct {
	struct {
		uint32_t posted;
		uint32_t posted;
@@ -322,6 +335,8 @@ struct icnss_priv {
	bool is_ssr;
	bool is_ssr;
	struct kobject *icnss_kobject;
	struct kobject *icnss_kobject;
	atomic_t is_shutdown;
	atomic_t is_shutdown;
	u32 qdss_mem_seg_len;
	struct icnss_fw_mem qdss_mem[QMI_WLFW_MAX_NUM_MEM_SEG];
};
};


struct icnss_reg_info {
struct icnss_reg_info {
Loading