diff --git a/drivers/soc/qcom/icnss2/main.c b/drivers/soc/qcom/icnss2/main.c index d7a3ff4969af..5431bd0c516d 100644 --- a/drivers/soc/qcom/icnss2/main.c +++ b/drivers/soc/qcom/icnss2/main.c @@ -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); diff --git a/drivers/soc/qcom/icnss2/main.h b/drivers/soc/qcom/icnss2/main.h index a273a25f83a4..4cee7c21ac03 100644 --- a/drivers/soc/qcom/icnss2/main.h +++ b/drivers/soc/qcom/icnss2/main.h @@ -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 diff --git a/drivers/soc/qcom/icnss2/power.c b/drivers/soc/qcom/icnss2/power.c index a1e238d94176..7fe57aab6d4a 100644 --- a/drivers/soc/qcom/icnss2/power.c +++ b/drivers/soc/qcom/icnss2/power.c @@ -8,6 +8,7 @@ #include #include #include +#include #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; +} diff --git a/drivers/soc/qcom/icnss2/qmi.c b/drivers/soc/qcom/icnss2/qmi.c index 74fdde017893..830eb9a872a9 100644 --- a/drivers/soc/qcom/icnss2/qmi.c +++ b/drivers/soc/qcom/icnss2/qmi.c @@ -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);