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

Commit 22cbaeeb authored by Rajasekaran Kalidoss's avatar Rajasekaran Kalidoss Committed by Gerrit - the friendly Code Review server
Browse files

cnss2: Implement cold boot cal upload/download via QMI



In QCN7605, HL(USB and SDIO) transports are supported.
Cold boot calibration data upload and download is handled
in kernel space itself. New logic was decided for upload
and download. This logic is implemented in cnss2.

Change-Id: I19fcd643a2fc656e98b5375de16229cc3217dd45
Signed-off-by: default avatarRajasekaran Kalidoss <rkalidos@codeaurora.org>
parent 5b964b49
Loading
Loading
Loading
Loading
+1 −11
Original line number Diff line number Diff line
/* Copyright (c) 2018, The Linux Foundation. All rights reserved.
/* Copyright (c) 2018-2019, 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
@@ -56,16 +56,6 @@ bool cnss_bus_req_mem_ind_valid(struct cnss_plat_data *plat_priv)
		return true;
}

bool cnss_bus_dev_cal_rep_valid(struct cnss_plat_data *plat_priv)
{
	bool ret = false;

	if (cnss_get_bus_type(plat_priv->device_id) == CNSS_BUS_USB)
		ret = true;

	return ret;
}

void *cnss_bus_dev_to_bus_priv(struct device *dev)
{
	if (!dev)
+1 −2
Original line number Diff line number Diff line
/* Copyright (c) 2018, The Linux Foundation. All rights reserved.
/* Copyright (c) 2018-2019, 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
@@ -58,5 +58,4 @@ int cnss_bus_call_driver_modem_status(struct cnss_plat_data *plat_priv,
				      int modem_current_status);
int cnss_bus_recovery_update_status(struct cnss_plat_data *plat_priv);
bool cnss_bus_req_mem_ind_valid(struct cnss_plat_data *plat_priv);
bool cnss_bus_dev_cal_rep_valid(struct cnss_plat_data *plat_priv);
#endif /* _CNSS_BUS_H */
+74 −12
Original line number Diff line number Diff line
@@ -562,13 +562,49 @@ out:
	return ret;
}

static int cnss_cal_update_hdlr(struct cnss_plat_data *plat_priv)
static int caldb_mem_bounds_check(struct cnss_plat_data *plat_priv, void *data)
{
	/* QCN7605 store the cal data sent by FW to calDB memory area
	 * get out of this after complete data is uploaded. FW is expected
	 * to send cal done
	*/
	return 0;
	int ret = 0;
	struct cnss_cal_data *cal_data = data;
	u8 *end_ptr, *cal_data_ptr;
	u32 total_size;

	end_ptr = (u8 *)plat_priv->caldb_mem + QCN7605_CALDB_SIZE;
	cal_data_ptr = (u8 *)plat_priv->caldb_mem + cal_data->index;
	total_size = cal_data->total_size;

	if (cal_data_ptr >= end_ptr || (cal_data_ptr + total_size) >= end_ptr) {
		cnss_pr_err("caldb data offset or size error\n");
		ret = -EINVAL;
	}

	return ret;
}

static int cnss_cal_update_hdlr(struct cnss_plat_data *plat_priv, void *data)
{
	int ret = 0;

	ret  = caldb_mem_bounds_check(plat_priv, data);
	if (ret)
		CNSS_ASSERT(0);
	else
		cnss_wlfw_cal_update_req_send_sync(plat_priv, data);

	return ret;
}

static int cnss_cal_download_hdlr(struct cnss_plat_data *plat_priv, void *data)
{
	int ret = 0;

	ret = caldb_mem_bounds_check(plat_priv, data);
	if (ret)
		CNSS_ASSERT(0);
	else
		cnss_wlfw_cal_download_req_send_sync(plat_priv, data);

	return ret;
}

static char *cnss_driver_event_to_str(enum cnss_driver_event_type type)
@@ -1157,11 +1193,6 @@ static int cnss_wlfw_server_arrive_hdlr(struct cnss_plat_data *plat_priv)
			goto out;

		ret = cnss_wlfw_bdf_dnld_send_sync(plat_priv);
		if (ret)
			goto out;
		/*cnss driver sends  meta data report and waits for FW_READY*/
		if (cnss_bus_dev_cal_rep_valid(plat_priv))
			ret = cnss_wlfw_cal_report_send_sync(plat_priv);
	}
out:
	return ret;
@@ -1255,7 +1286,10 @@ static void cnss_driver_event_work(struct work_struct *work)
			ret = cnss_cold_boot_cal_start_hdlr(plat_priv);
			break;
		case CNSS_DRIVER_EVENT_CAL_UPDATE:
			ret = cnss_cal_update_hdlr(plat_priv);
			ret = cnss_cal_update_hdlr(plat_priv, event->data);
			break;
		case CNSS_DRIVER_EVENT_CAL_DOWNLOAD:
			ret = cnss_cal_download_hdlr(plat_priv, event->data);
			break;
		case CNSS_DRIVER_EVENT_COLD_BOOT_CAL_DONE:
			ret = cnss_cold_boot_cal_done_hdlr(plat_priv);
@@ -1696,6 +1730,25 @@ static void cnss_event_work_deinit(struct cnss_plat_data *plat_priv)
	destroy_workqueue(plat_priv->event_wq);
}

static int cnss_alloc_caldb_mem(struct cnss_plat_data *plat_priv)
{
	int ret = 0;

	plat_priv->caldb_mem = vzalloc(QCN7605_CALDB_SIZE);
	if (plat_priv->caldb_mem)
		cnss_pr_dbg("600KB cal db alloc done caldb_mem %pK\n",
			    plat_priv->caldb_mem);
	else
		ret = -ENOMEM;

	return ret;
}

static void cnss_free_caldb_mem(struct cnss_plat_data *plat_priv)
{
	vfree(plat_priv->caldb_mem);
}

static const struct platform_device_id cnss_platform_id_table[] = {
	{ .name = "qca6174", .driver_data = QCA6174_DEVICE_ID, },
	{ .name = "qca6290", .driver_data = QCA6290_DEVICE_ID, },
@@ -1786,6 +1839,12 @@ static int cnss_probe(struct platform_device *plat_dev)
	if (ret)
		goto deinit_qmi;

	if (plat_priv->bus_type == CNSS_BUS_USB)	{
		ret = cnss_alloc_caldb_mem(plat_priv);
		if (ret)
			goto remove_debugfs;
	}

	setup_timer(&plat_priv->fw_boot_timer,
		    cnss_bus_fw_boot_timeout_hdlr, (unsigned long)plat_priv);

@@ -1803,6 +1862,8 @@ static int cnss_probe(struct platform_device *plat_dev)

	return 0;

remove_debugfs:
	cnss_debugfs_destroy(plat_priv);
deinit_qmi:
	cnss_qmi_deinit(plat_priv);
deinit_event_work:
@@ -1836,6 +1897,7 @@ static int cnss_remove(struct platform_device *plat_dev)
	device_init_wakeup(&plat_dev->dev, false);
	unregister_pm_notifier(&cnss_pm_notifier);
	del_timer(&plat_priv->fw_boot_timer);
	cnss_free_caldb_mem(plat_priv);
	cnss_debugfs_destroy(plat_priv);
	cnss_qmi_deinit(plat_priv);
	cnss_event_work_deinit(plat_priv);
+8 −1
Original line number Diff line number Diff line
/* Copyright (c) 2016-2018, The Linux Foundation. All rights reserved.
/* Copyright (c) 2016-2019, 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
@@ -29,6 +29,7 @@
#define CNSS_EVENT_UNINTERRUPTIBLE BIT(1)
#define CNSS_EVENT_SYNC_UNINTERRUPTIBLE (CNSS_EVENT_SYNC | \
				CNSS_EVENT_UNINTERRUPTIBLE)
#define QCN7605_CALDB_SIZE 614400

enum cnss_dev_bus_type {
	CNSS_BUS_NONE = -1,
@@ -174,6 +175,11 @@ enum cnss_debug_quirks {
	SKIP_RECOVERY,
};

struct cnss_cal_data {
	u32 index;
	u32 total_size;
};

struct cnss_plat_data {
	struct platform_device *plat_dev;
	void *bus_priv;
@@ -216,6 +222,7 @@ struct cnss_plat_data {
	u32 diag_reg_read_mem_type;
	u32 diag_reg_read_len;
	u8 *diag_reg_read_buf;
	void *caldb_mem;
	bool cal_done;
};

+279 −11
Original line number Diff line number Diff line
/* Copyright (c) 2015-2018, The Linux Foundation. All rights reserved.
/* Copyright (c) 2015-2019, 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
@@ -175,11 +175,13 @@ static int cnss_wlfw_host_cap_send_sync(struct cnss_plat_data *plat_priv)
	req.bdf_support_valid = 1;
	req.bdf_support = 1;

	if (cnss_get_bus_type(plat_priv->device_id) == CNSS_BUS_PCI) {
		req.m3_support_valid = 1;
		req.m3_support = 1;

		req.m3_cache_support_valid = 1;
		req.m3_cache_support = 1;
	}

	req.cal_done_valid = 1;
	req.cal_done = plat_priv->cal_done;
@@ -279,7 +281,83 @@ static int cnss_qmi_initiate_cal_update_ind_hdlr(
					 struct cnss_plat_data *plat_priv,
					 void *msg, unsigned int msg_len)
{
	return 0;
	struct msg_desc ind_desc;
	struct wlfw_initiate_cal_update_ind_msg_v01 ind_msg;
	struct cnss_cal_data *data;
	int ret = 0;

	data = kzalloc(sizeof(*data), GFP_KERNEL);
	if (!data) {
		ret = -ENOMEM;
		goto out;
	}

	ind_desc.msg_id = QMI_WLFW_INITIATE_CAL_UPDATE_IND_V01;
	ind_desc.max_msg_len = WLFW_INITIATE_CAL_UPDATE_IND_MSG_V01_MAX_MSG_LEN;
	ind_desc.ei_array = wlfw_initiate_cal_update_ind_msg_v01_ei;

	ret = qmi_kernel_decode(&ind_desc, &ind_msg, msg, msg_len);
	if (ret < 0) {
		cnss_pr_err("Failed to decode initiate cal update ind, msg_len: %u, err = %d\n",
			    ret, msg_len);
		goto qmi_fail;
	}

	if ((ind_msg.total_size > 0) && (ind_msg.cal_data_location_valid)) {
		data->index = ind_msg.cal_data_location;
		data->total_size = ind_msg.total_size;
		cnss_driver_event_post(plat_priv, CNSS_DRIVER_EVENT_CAL_UPDATE,
				       0, data);
		goto out;
	}

qmi_fail:
	kfree(data);
out:
	return ret;
}

static int cnss_qmi_initiate_cal_download_ind_hdlr(
					 struct cnss_plat_data *plat_priv,
					 void *msg, unsigned int msg_len)
{
	struct msg_desc ind_desc;
	struct wlfw_initiate_cal_download_ind_msg_v01 ind_msg;
	struct cnss_cal_data *data;
	int ret = 0;

	data = kzalloc(sizeof(*data), GFP_KERNEL);
	if (!data) {
		ret = -ENOMEM;
		goto out;
	}

	ind_desc.msg_id = QMI_WLFW_INITIATE_CAL_DOWNLOAD_IND_V01;
	ind_desc.max_msg_len =
			WLFW_INITIATE_CAL_DOWNLOAD_IND_MSG_V01_MAX_MSG_LEN;
	ind_desc.ei_array = wlfw_initiate_cal_download_ind_msg_v01_ei;

	ret = qmi_kernel_decode(&ind_desc, &ind_msg, msg, msg_len);
	if (ret < 0) {
		cnss_pr_err("Failed to decode initiate cal download ind, msg_len: %u, err = %d\n",
			    ret, msg_len);
		goto qmi_fail;
	}

	if ((ind_msg.total_size > 0) && (ind_msg.cal_data_location_valid)) {
		data->index = ind_msg.cal_data_location;
		data->total_size = ind_msg.total_size;
		cnss_driver_event_post(plat_priv,
				       CNSS_DRIVER_EVENT_CAL_DOWNLOAD,
				       0, data);

		goto out;
	}

qmi_fail:
	kfree(data);
out:
	return ret;
}

static int cnss_wlfw_request_mem_ind_hdlr(struct cnss_plat_data *plat_priv,
@@ -501,6 +579,197 @@ out:
	return ret;
}

int cnss_wlfw_cal_download_req_send_sync(struct cnss_plat_data *plat_priv,
					 void *data)
{
	struct wlfw_cal_download_req_msg_v01 *req;
	struct wlfw_cal_download_resp_msg_v01 resp;
	struct cnss_cal_data *cal_data = data;
	struct msg_desc req_desc, resp_desc;
	unsigned int remaining;
	u8 *cal_data_read_ptr;
	int ret = 0;

	cnss_pr_dbg("Sending cal download request message, state: 0x%lx\n",
		    plat_priv->driver_state);

	req = kzalloc(sizeof(*req), GFP_KERNEL);
	if (!req) {
		ret = -ENOMEM;
		goto out;
	}

	memset(&resp, 0, sizeof(resp));

	req_desc.max_msg_len = WLFW_CAL_DOWNLOAD_REQ_MSG_V01_MAX_MSG_LEN;
	req_desc.msg_id = QMI_WLFW_CAL_DOWNLOAD_REQ_V01;
	req_desc.ei_array = wlfw_cal_download_req_msg_v01_ei;

	resp_desc.max_msg_len = WLFW_CAL_DOWNLOAD_RESP_MSG_V01_MAX_MSG_LEN;
	resp_desc.msg_id = QMI_WLFW_CAL_DOWNLOAD_RESP_V01;
	resp_desc.ei_array = wlfw_cal_download_resp_msg_v01_ei;

	req->valid = true;
	req->file_id_valid = false;
	req->seg_id_valid = true;
	req->seg_id = 0;
	req->data_valid = true;
	req->end_valid = true;
	req->total_size_valid = true;
	req->total_size = cal_data->total_size;

	req->cal_data_location_valid = true;
	req->cal_data_location = cal_data->index;
	cal_data_read_ptr = (u8 *)plat_priv->caldb_mem +
				  cal_data->index;
	cnss_pr_dbg("cal_data_read_ptr %pK\n", cal_data_read_ptr);
	remaining = cal_data->total_size;

	while (remaining) {
		if (remaining > QMI_WLFW_MAX_DATA_SIZE_V01) {
			req->data_len = QMI_WLFW_MAX_DATA_SIZE_V01;
		} else {
			req->data_len = remaining;
			req->end = true;
		}

		memcpy(req->data, cal_data_read_ptr, req->data_len);
		cnss_pr_dbg("remaining %u data_len %u, seg_id %u, read_ptr %pK\n",
			    remaining, req->data_len, req->seg_id,
			    cal_data_read_ptr);

		ret = qmi_send_req_wait(plat_priv->qmi_wlfw_clnt, &req_desc,
					req, sizeof(*req), &resp_desc, &resp,
					sizeof(resp), QMI_WLFW_TIMEOUT_MS);
		if (ret < 0) {
			cnss_pr_err("Failed to send cal download request, err = %d\n",
				    ret);
			goto out;
		}

		if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
			cnss_pr_err("cal download request failed, result: %d, err: %d\n",
				    resp.resp.result, resp.resp.error);
			ret = resp.resp.result;
			goto out;
		}

		remaining -= req->data_len;
		cal_data_read_ptr += req->data_len;
		req->seg_id++;
	}

out:
	kfree(req);
	kfree(data);
	if (ret)
		CNSS_ASSERT(0);
	return ret;
}

int cnss_wlfw_cal_update_req_send_sync(struct cnss_plat_data *plat_priv,
				       void *data)
{
	struct wlfw_cal_update_req_msg_v01 req;
	struct wlfw_cal_update_resp_msg_v01 *resp = NULL;
	struct cnss_cal_data *cal_data = data;
	struct msg_desc req_desc, resp_desc;
	unsigned int remaining, data_len;
	u8 *cal_data_write_ptr;
	bool end = false;
	int ret = 0;

	cnss_pr_dbg("Sending cal update request message, state: 0x%lx\n",
		    plat_priv->driver_state);

	resp = kzalloc(sizeof(*resp), GFP_KERNEL);
	if (!resp) {
		ret = -ENOMEM;
		goto out;
	}

	memset(resp, 0, sizeof(*resp));

	req_desc.max_msg_len = WLFW_CAL_UPDATE_REQ_MSG_V01_MAX_MSG_LEN;
	req_desc.msg_id = QMI_WLFW_CAL_UPDATE_REQ_V01;
	req_desc.ei_array = wlfw_cal_update_req_msg_v01_ei;

	resp_desc.max_msg_len = WLFW_CAL_UPDATE_RESP_MSG_V01_MAX_MSG_LEN;
	resp_desc.msg_id = QMI_WLFW_CAL_UPDATE_RESP_V01;
	resp_desc.ei_array = wlfw_cal_update_resp_msg_v01_ei;

	req.cal_id = 0;
	req.seg_id = 0;
	cal_data_write_ptr = (u8 *)plat_priv->caldb_mem +
			     cal_data->index;
	remaining = cal_data->total_size;

	while (remaining) {
		if (remaining > QMI_WLFW_MAX_DATA_SIZE_V01) {
			data_len = QMI_WLFW_MAX_DATA_SIZE_V01;
		} else {
			data_len = remaining;
			end = true;
		}

		cnss_pr_dbg("remaining %u data_len %u, seg_id %u\n",
			    remaining, data_len, req.seg_id);

		ret = qmi_send_req_wait(plat_priv->qmi_wlfw_clnt, &req_desc,
					&req, sizeof(req), &resp_desc, resp,
					sizeof(*resp), QMI_WLFW_TIMEOUT_MS);
		if (ret < 0) {
			cnss_pr_err("Failed to send cal update request, err = %d\n",
				    ret);
			goto out;
		}

		if (resp->resp.result != QMI_RESULT_SUCCESS_V01) {
			cnss_pr_err("cal update request failed, result: %d, err: %d\n",
				    resp->resp.result, resp->resp.error);
			ret = resp->resp.result;
			goto out;
		}

		if (!resp->data_valid || resp->data_len != data_len) {
			cnss_pr_err("cal update read data is invalid, data_valid = %u, data_len = %u\n",
				    resp->data_valid, resp->data_len);
			ret = -EINVAL;
			goto out;
		}

		if (!resp->seg_id_valid || resp->seg_id != req.seg_id) {
			cnss_pr_err("seg_id invalid, valid = %u, seg_id = %u, req seg id =%u\n",
				    resp->seg_id_valid, resp->seg_id,
				    req.seg_id);
			ret = -EINVAL;
			goto out;
		}

		if (resp->end_valid && resp->end) {
			cnss_pr_dbg("cal update end valid =%u end = %u\n",
				    resp->end_valid, resp->end);
			/*one valid cal data is available with host*/
			plat_priv->cal_done = true;
		}

		memcpy(cal_data_write_ptr, resp->data, resp->data_len);
		cnss_pr_dbg("cal updated resp->data_len %u\n", resp->data_len);
		remaining -= resp->data_len;
		cal_data_write_ptr += resp->data_len;
		req.seg_id++;

		memset(resp, 0, sizeof(*resp));
	}

out:
	kfree(resp);
	kfree(data);
	if (ret)
		CNSS_ASSERT(0);
	return ret;
}

int cnss_wlfw_bdf_dnld_send_sync(struct cnss_plat_data *plat_priv)
{
	struct wlfw_bdf_download_req_msg_v01 *req;
@@ -965,11 +1234,6 @@ out:
	return ret;
}

int cnss_wlfw_cal_report_send_sync(struct cnss_plat_data *plat_priv)
{
	return 0;
}

static void cnss_wlfw_clnt_ind(struct qmi_handle *handle,
			       unsigned int msg_id, void *msg,
			       unsigned int msg_len, void *ind_cb_priv)
@@ -1009,6 +1273,10 @@ static void cnss_wlfw_clnt_ind(struct qmi_handle *handle,
	case QMI_WLFW_INITIATE_CAL_UPDATE_IND_V01:
		cnss_qmi_initiate_cal_update_ind_hdlr(plat_priv, msg, msg_len);
		break;
	case QMI_WLFW_INITIATE_CAL_DOWNLOAD_IND_V01:
		cnss_qmi_initiate_cal_download_ind_hdlr(plat_priv,
							msg, msg_len);
		break;
	default:
		cnss_pr_err("Invalid QMI WLFW indication, msg_id: 0x%x\n",
			    msg_id);
Loading