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

Commit c8abb129 authored by Naman Padhiar's avatar Naman Padhiar
Browse files

icnss2: 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: I631712d0f569a22a7ed7b3ede892f701152ce5f8
Signed-off-by: default avatarNaman Padhiar <npadhiar@codeaurora.org>
parent 3b62fcac
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -3278,6 +3278,7 @@ static int icnss_probe(struct platform_device *pdev)
			icnss_pr_err("ICNSS genl init failed %d\n", ret);

		icnss_runtime_pm_init(priv);
		icnss_get_cpr_info(priv);
	}

	INIT_LIST_HEAD(&priv->icnss_tcdev_list);
+12 −0
Original line number Diff line number Diff line
@@ -133,6 +133,15 @@ struct icnss_vreg_info {
	u32 enabled;
};

struct icnss_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 icnss_vreg_type {
	ICNSS_VREG_PRIM,
};
@@ -300,6 +309,7 @@ struct icnss_priv {
	struct ce_irq_list ce_irq_list[ICNSS_MAX_IRQ_REGISTRATIONS];
	struct list_head vreg_list;
	struct list_head clk_list;
	struct icnss_cpr_info cpr_info;
	unsigned long device_id;
	struct icnss_msi_config *msi_config;
	u32 msi_base_data;
@@ -412,5 +422,7 @@ int icnss_soc_wake_event_post(struct icnss_priv *priv,
			      u32 flags, void *data);
int icnss_get_iova(struct icnss_priv *priv, u64 *addr, u64 *size);
int icnss_get_iova_ipa(struct icnss_priv *priv, u64 *addr, u64 *size);
int icnss_get_cpr_info(struct icnss_priv *priv);
int icnss_update_cpr_info(struct icnss_priv *priv);
#endif
+126 −0
Original line number Diff line number Diff line
@@ -8,6 +8,7 @@
#include <linux/pinctrl/consumer.h>
#include <linux/regulator/consumer.h>
#include <soc/qcom/cmd-db.h>
#include <linux/io.h>
#include "main.h"
#include "qmi.h"
#include "debug.h"
@@ -45,6 +46,13 @@ static struct icnss_clk_cfg icnss_adrestea_clk_list[] = {
#define ICNSS_THRESHOLD_LOW				3450000
#define ICNSS_THRESHOLD_GUARD				20000

#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
#define BT_CXMX_VOLTAGE_MV		950

static int icnss_get_vreg_single(struct icnss_priv *priv,
				 struct icnss_vreg_info *vreg)
{
@@ -801,3 +809,121 @@ int icnss_init_vph_monitor(struct icnss_priv *priv)
out:
	return ret;
}

int icnss_get_cpr_info(struct icnss_priv *priv)
{
	struct platform_device *plat_dev = priv->pdev;
	struct icnss_cpr_info *cpr_info = &priv->cpr_info;
	struct resource *res;
	resource_size_t addr_len;
	void __iomem *tcs_cmd_base_addr;
	const char *cmd_db_name;
	u32 cpr_pmic_addr = 0;
	int ret = 0;

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

	ret = of_property_read_string(plat_dev->dev.of_node,
				      "qcom,cmd_db_name", &cmd_db_name);
	if (ret) {
		icnss_pr_dbg("CommandDB name is not present for CPR\n");
		goto out;
	}

	cpr_pmic_addr = cmd_db_read_addr(cmd_db_name);
	if (cpr_pmic_addr > 0) {
		cpr_info->cpr_pmic_addr = cpr_pmic_addr;
		icnss_pr_dbg("Get CPR PMIC address 0x%x from %s\n",
			     cpr_info->cpr_pmic_addr, cmd_db_name);
	} else {
		icnss_pr_err("CPR PMIC address is not available for %s\n",
			     cmd_db_name);
		ret = -EINVAL;
		goto out;
	}

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

	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);
		icnss_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 icnss_update_cpr_info(struct icnss_priv *priv)
{
	struct icnss_cpr_info *cpr_info = &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) {
		icnss_pr_dbg("CPR is not enabled\n");
		return 0;
	}

	if (cpr_info->voltage == 0 || cpr_info->cpr_pmic_addr == 0) {
		icnss_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);
				icnss_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) {
		icnss_pr_err("Failed to find proper TCS CMD data address\n");
		return -EINVAL;
	}

update_cpr:
	cpr_info->voltage = cpr_info->voltage > BT_CXMX_VOLTAGE_MV ?
		cpr_info->voltage : BT_CXMX_VOLTAGE_MV;
	icnss_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;
}
+8 −0
Original line number Diff line number Diff line
@@ -676,6 +676,14 @@ int wlfw_cap_send_sync_msg(struct icnss_priv *priv)
				resp->fw_version_info.fw_build_timestamp,
				WLFW_MAX_TIMESTAMP_LEN + 1);
	}

	if (resp->voltage_mv_valid) {
		priv->cpr_info.voltage = resp->voltage_mv;
		icnss_pr_dbg("Voltage for CPR: %dmV\n",
			    priv->cpr_info.voltage);
		icnss_update_cpr_info(priv);
	}

	if (resp->fw_build_id_valid)
		strlcpy(priv->fw_build_id, resp->fw_build_id,
			QMI_WLFW_MAX_BUILD_ID_LEN_V01 + 1);