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

Commit a46c09b7 authored by Yue Ma's avatar Yue Ma
Browse files

cnss2: Add open loop CPR support



Open loop CPR (core power reduction) is a power save feature that
configs certain voltage for WLAN power rails at runtime. Firmware
will read fuse and calculate the proper voltage and then send it to
CNSS driver. CNSS driver then configs the voltage to WLAN PDC using
TCS command through RPMh. This is to make sure the proper voltage
can be updated during runtime instead just hardcode and vote fixed
value for WLAN power rails.

Change-Id: Ia64e5d88d0cd0704573ac7380b1734455c1b2dc0
Signed-off-by: default avatarYue Ma <yuem@codeaurora.org>
parent e45cbd6f
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -1829,6 +1829,7 @@ static int cnss_probe(struct platform_device *plat_dev)
	platform_set_drvdata(plat_dev, plat_priv);
	INIT_LIST_HEAD(&plat_priv->vreg_list);

	cnss_get_cpr_info(plat_priv);
	cnss_init_control_params(plat_priv);

	ret = cnss_get_resources(plat_priv);
+12 −0
Original line number Diff line number Diff line
@@ -231,6 +231,15 @@ struct cnss_control_params {
	unsigned int bdf_type;
};

struct cnss_cpr_info {
	resource_size_t tcs_cmd_base_addr;
	resource_size_t tcs_cmd_data_addr;
	void __iomem *tcs_cmd_base_addr_io;
	void __iomem *tcs_cmd_data_addr_io;
	u32 cpr_pmic_addr;
	u32 voltage;
};

enum cnss_ce_index {
	CNSS_CE_00,
	CNSS_CE_01,
@@ -298,6 +307,7 @@ struct cnss_plat_data {
	struct completion rddm_complete;
	struct completion recovery_complete;
	struct cnss_control_params ctrl_params;
	struct cnss_cpr_info cpr_info;
	u64 antenna;
	u64 grant;
	struct qmi_handle coex_qmi;
@@ -323,5 +333,7 @@ void cnss_unregister_subsys(struct cnss_plat_data *plat_priv);
int cnss_register_ramdump(struct cnss_plat_data *plat_priv);
void cnss_unregister_ramdump(struct cnss_plat_data *plat_priv);
void cnss_set_pin_connect_status(struct cnss_plat_data *plat_priv);
int cnss_get_cpr_info(struct cnss_plat_data *plat_priv);
int cnss_update_cpr_info(struct cnss_plat_data *plat_priv);

#endif /* _CNSS_MAIN_H */
+124 −0
Original line number Diff line number Diff line
@@ -5,6 +5,7 @@
#include <linux/of.h>
#include <linux/pinctrl/consumer.h>
#include <linux/regulator/consumer.h>
#include <soc/qcom/cmd-db.h>

#include "main.h"
#include "debug.h"
@@ -39,6 +40,12 @@ static struct cnss_vreg_cfg cnss_vreg_list[] = {
#define BOOTSTRAP_DELAY			1000
#define WLAN_ENABLE_DELAY		1000

#define TCS_CMD_DATA_ADDR_OFFSET	0x4
#define TCS_OFFSET			0xC8
#define TCS_CMD_OFFSET			0x10
#define MAX_TCS_NUM			8
#define MAX_TCS_CMD_NUM			5

static int cnss_get_vreg_single(struct cnss_plat_data *plat_priv,
				struct cnss_vreg_info *vreg)
{
@@ -532,3 +539,120 @@ void cnss_set_pin_connect_status(struct cnss_plat_data *plat_priv)

	plat_priv->pin_result.host_pin_result = pin_status;
}

int cnss_get_cpr_info(struct cnss_plat_data *plat_priv)
{
	struct platform_device *plat_dev = plat_priv->plat_dev;
	struct cnss_cpr_info *cpr_info = &plat_priv->cpr_info;
	struct resource *res;
	void __iomem *tcs_cmd_base_addr;
	u32 s2f_addr = 0, s6a_addr = 0;
	int ret = 0;

	res = platform_get_resource_byname(plat_dev, IORESOURCE_MEM, "tcs_cmd");
	if (!res) {
		cnss_pr_dbg("TCS CMD address is not present for CPR\n");
		goto out;
	}

	ret = cmd_db_ready();
	if (ret) {
		cnss_pr_err("CommandDB is not ready\n");
		goto out;
	}

	s2f_addr = cmd_db_read_addr("smpf2");
	s6a_addr = cmd_db_read_addr("smpa6");

	if (s2f_addr > 0) {
		cpr_info->cpr_pmic_addr = s2f_addr;
		cnss_pr_dbg("Get CPR PMIC address 0x%x from s2f\n",
			    cpr_info->cpr_pmic_addr);
	} else if (s6a_addr > 0) {
		cpr_info->cpr_pmic_addr = s6a_addr;
		cnss_pr_dbg("Get CPR PMIC address 0x%x from s6a\n",
			    cpr_info->cpr_pmic_addr);
	} else {
		cnss_pr_err("CPR PMIC addresses are not available\n");
		ret = -EINVAL;
		goto out;
	}

	cpr_info->tcs_cmd_base_addr = res->start;
	cnss_pr_dbg("TCS CMD base address is %pa with length %pa\n",
		    cpr_info->tcs_cmd_base_addr, resource_size(res));

	tcs_cmd_base_addr = devm_ioremap_resource(&plat_dev->dev, res);
	if (IS_ERR(tcs_cmd_base_addr)) {
		ret = PTR_ERR(tcs_cmd_base_addr);
		cnss_pr_err("Failed to map TCS CMD address, err = %d\n",
			    ret);
		goto out;
	}

	cpr_info->tcs_cmd_base_addr_io = tcs_cmd_base_addr;

	return 0;

out:
	return ret;
}

int cnss_update_cpr_info(struct cnss_plat_data *plat_priv)
{
	struct cnss_cpr_info *cpr_info = &plat_priv->cpr_info;
	u32 pmic_addr, voltage = 0, voltage_tmp, offset;
	void __iomem *tcs_cmd_addr, *tcs_cmd_data_addr;
	int i, j;

	if (cpr_info->tcs_cmd_base_addr == 0) {
		cnss_pr_dbg("CPR is not enabled\n");
		return 0;
	}

	if (cpr_info->voltage == 0 || cpr_info->cpr_pmic_addr == 0) {
		cnss_pr_err("Voltage %dmV or PMIC address 0x%x is not valid\n",
			    cpr_info->voltage, cpr_info->cpr_pmic_addr);
		return -EINVAL;
	}

	if (cpr_info->tcs_cmd_data_addr_io)
		goto update_cpr;

	for (i = 0; i < MAX_TCS_NUM; i++) {
		for (j = 0; j < MAX_TCS_CMD_NUM; j++) {
			offset = i * TCS_OFFSET + j * TCS_CMD_OFFSET;
			tcs_cmd_addr = cpr_info->tcs_cmd_base_addr_io + offset;
			pmic_addr = readl_relaxed(tcs_cmd_addr);
			if (pmic_addr == cpr_info->cpr_pmic_addr) {
				tcs_cmd_data_addr = tcs_cmd_addr +
					TCS_CMD_DATA_ADDR_OFFSET;
				voltage_tmp = readl_relaxed(tcs_cmd_data_addr);
				cnss_pr_dbg("Got voltage %dmV from i: %d, j: %d\n",
					    voltage_tmp, i, j);

				if (voltage_tmp > voltage) {
					voltage = voltage_tmp;
					cpr_info->tcs_cmd_data_addr =
						cpr_info->tcs_cmd_base_addr +
						offset +
						TCS_CMD_DATA_ADDR_OFFSET;
					cpr_info->tcs_cmd_data_addr_io =
						tcs_cmd_data_addr;
				}
			}
		}
	}

	if (!cpr_info->tcs_cmd_data_addr_io) {
		cnss_pr_err("Failed to find proper TCS CMD data address\n");
		return -EINVAL;
	}

update_cpr:
	cnss_pr_dbg("Update TCS CMD data address %pa with voltage %dmV\n",
		    cpr_info->tcs_cmd_data_addr, cpr_info->voltage);
	writel_relaxed(cpr_info->voltage, cpr_info->tcs_cmd_data_addr_io);

	return 0;
}
+6 −0
Original line number Diff line number Diff line
@@ -389,6 +389,12 @@ int cnss_wlfw_tgt_cap_send_sync(struct cnss_plat_data *plat_priv)
			resp->fw_version_info.fw_build_timestamp,
			QMI_WLFW_MAX_TIMESTAMP_LEN + 1);
	}
	if (resp->voltage_mv_valid) {
		plat_priv->cpr_info.voltage = resp->voltage_mv;
		cnss_pr_dbg("Voltage for CPR: %dmV\n",
			    plat_priv->cpr_info.voltage);
		cnss_update_cpr_info(plat_priv);
	}
	if (resp->otp_version_valid)
		plat_priv->otp_version = resp->otp_version;