Loading drivers/soc/qcom/icnss2/main.c +1 −0 Original line number Diff line number Diff line Loading @@ -3267,6 +3267,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); Loading drivers/soc/qcom/icnss2/main.h +12 −0 Original line number Diff line number Diff line Loading @@ -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, }; Loading Loading @@ -307,6 +316,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; Loading Loading @@ -419,5 +429,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 drivers/soc/qcom/icnss2/power.c +126 −0 Original line number Diff line number Diff line Loading @@ -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" Loading Loading @@ -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) { Loading Loading @@ -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; } drivers/soc/qcom/icnss2/qmi.c +8 −0 Original line number Diff line number Diff line Loading @@ -674,6 +674,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); Loading Loading
drivers/soc/qcom/icnss2/main.c +1 −0 Original line number Diff line number Diff line Loading @@ -3267,6 +3267,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); Loading
drivers/soc/qcom/icnss2/main.h +12 −0 Original line number Diff line number Diff line Loading @@ -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, }; Loading Loading @@ -307,6 +316,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; Loading Loading @@ -419,5 +429,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
drivers/soc/qcom/icnss2/power.c +126 −0 Original line number Diff line number Diff line Loading @@ -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" Loading Loading @@ -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) { Loading Loading @@ -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; }
drivers/soc/qcom/icnss2/qmi.c +8 −0 Original line number Diff line number Diff line Loading @@ -674,6 +674,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); Loading