Pārlūkot izejas kodu

msm: camera: cpas: Enable icp clk for qchannel handshake

For some chipsets, qchannel handshake needs icp clk to be
enabled. Add support to enable icp clk while qchannel
handshake by adding as optional clk in cpas node. Whether
to enable icp clk or not is controlled through workaround
list populated for each chipset.
Add mechanism to retry qchannel acceptance if the first
auto try has failed, by explicity writing 0x1 to qchannel
ctrl register. This will bring back qchannel to good state.

CRs-Fixed: 3131613
Change-Id: Ie39a9789b2eb1bf9c0f6adb26fe6d6e1823eff70
Signed-off-by: Pavan Kumar Chilamkurthi <[email protected]>
Pavan Kumar Chilamkurthi 3 gadi atpakaļ
vecāks
revīzija
a32a315e54

+ 38 - 11
drivers/cam_cpas/cam_cpas_hw.c

@@ -1,6 +1,7 @@
 // SPDX-License-Identifier: GPL-2.0-only
 /*
  * Copyright (c) 2017-2021, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved.
  */
 
 #include <linux/device.h>
@@ -1633,6 +1634,7 @@ static int cam_cpas_hw_start(void *hw_priv, void *start_args,
 	int rc, i = 0;
 	struct cam_cpas_private_soc *soc_private = NULL;
 	bool invalid_start = true;
+	int count;
 
 	if (!hw_priv || !start_args) {
 		CAM_ERR(CAM_CPAS, "Invalid arguments %pK %pK",
@@ -1746,6 +1748,11 @@ static int cam_cpas_hw_start(void *hw_priv, void *start_args,
 			goto remove_ahb_vote;
 
 		atomic_set(&cpas_core->irq_count, 1);
+
+		count = cam_soc_util_regulators_enabled(&cpas_hw->soc_info);
+		if (count > 0)
+			CAM_DBG(CAM_CPAS, "Regulators already enabled %d", count);
+
 		rc = cam_cpas_soc_enable_resources(&cpas_hw->soc_info,
 			applied_level);
 		if (rc) {
@@ -1754,6 +1761,24 @@ static int cam_cpas_hw_start(void *hw_priv, void *start_args,
 			goto remove_axi_vote;
 		}
 
+		if (cpas_core->internal_ops.qchannel_handshake) {
+			rc = cpas_core->internal_ops.qchannel_handshake(cpas_hw, true, false);
+			if (rc) {
+				CAM_WARN(CAM_CPAS, "failed in qchannel_handshake rc=%d", rc);
+				/* Do not return error, passthrough */
+
+				rc = cpas_core->internal_ops.qchannel_handshake(cpas_hw,
+					true, true);
+				if (rc) {
+					CAM_ERR(CAM_CPAS,
+						"failed in qchannel_handshake, hw blocks may not work rc=%d",
+						rc);
+					/* Do not return error, passthrough */
+				}
+				rc = 0;
+			}
+		}
+
 		if (cpas_core->internal_ops.power_on) {
 			rc = cpas_core->internal_ops.power_on(cpas_hw);
 			if (rc) {
@@ -1831,7 +1856,7 @@ static int cam_cpas_hw_stop(void *hw_priv, void *stop_args,
 	struct cam_ahb_vote ahb_vote;
 	struct cam_axi_vote axi_vote = {0};
 	struct cam_cpas_private_soc *soc_private = NULL;
-	int rc = 0;
+	int rc = 0, count;
 	long result;
 	int retry_camnoc_idle = 0;
 
@@ -1888,12 +1913,9 @@ static int cam_cpas_hw_stop(void *hw_priv, void *stop_args,
 		}
 
 		if (cpas_core->internal_ops.qchannel_handshake) {
-			rc = cpas_core->internal_ops.qchannel_handshake(
-				cpas_hw, false);
+			rc = cpas_core->internal_ops.qchannel_handshake(cpas_hw, false, false);
 			if (rc) {
-				CAM_ERR(CAM_CPAS,
-					"failed in qchannel_handshake rc=%d",
-					rc);
+				CAM_ERR(CAM_CPAS, "failed in qchannel_handshake rc=%d", rc);
 				retry_camnoc_idle = 1;
 				/* Do not return error, passthrough */
 			}
@@ -1917,12 +1939,9 @@ static int cam_cpas_hw_stop(void *hw_priv, void *stop_args,
 		/* try again incase camnoc is still not idle */
 		if (cpas_core->internal_ops.qchannel_handshake &&
 			retry_camnoc_idle) {
-			rc = cpas_core->internal_ops.qchannel_handshake(
-				cpas_hw, false);
+			rc = cpas_core->internal_ops.qchannel_handshake(cpas_hw, false, false);
 			if (rc) {
-				CAM_ERR(CAM_CPAS,
-					"failed in qchannel_handshake rc=%d",
-					rc);
+				CAM_ERR(CAM_CPAS, "failed in qchannel_handshake rc=%d", rc);
 				/* Do not return error, passthrough */
 			}
 		}
@@ -1935,6 +1954,14 @@ static int cam_cpas_hw_stop(void *hw_priv, void *stop_args,
 		}
 		CAM_DBG(CAM_CPAS, "Disabled all the resources: irq_count=%d",
 			atomic_read(&cpas_core->irq_count));
+
+		count = cam_soc_util_regulators_enabled(&cpas_hw->soc_info);
+		if (count > 0)
+			CAM_WARN(CAM_CPAS,
+				"Client=[%d][%s][%d] qchannel shut down while top gdsc is still on %d",
+				client_indx, cpas_client->data.identifier,
+				cpas_client->data.cell_index, count);
+
 		cpas_hw->hw_state = CAM_HW_STATE_POWER_DOWN;
 	}
 

+ 2 - 1
drivers/cam_cpas/cam_cpas_hw.h

@@ -1,6 +1,7 @@
 /* SPDX-License-Identifier: GPL-2.0-only */
 /*
  * Copyright (c) 2017-2021, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved.
  */
 
 #ifndef _CAM_CPAS_HW_H_
@@ -92,7 +93,7 @@ struct cam_cpas_internal_ops {
 	int (*setup_qos_settings)(struct cam_hw_info *cpas_hw,
 		uint32_t selection_mask);
 	int (*print_poweron_settings)(struct cam_hw_info *cpas_hw);
-	int (*qchannel_handshake)(struct cam_hw_info *cpas_hw, bool power_on);
+	int (*qchannel_handshake)(struct cam_hw_info *cpas_hw, bool power_on, bool force_on);
 };
 
 /**

+ 22 - 0
drivers/cam_cpas/cam_cpas_soc.c

@@ -1,6 +1,7 @@
 // SPDX-License-Identifier: GPL-2.0-only
 /*
  * Copyright (c) 2017-2021, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved.
  */
 
 #include <linux/device.h>
@@ -20,6 +21,8 @@
 static uint cpas_dump;
 module_param(cpas_dump, uint, 0644);
 
+#define CAM_ICP_CLK_NAME "cam_icp_clk"
+
 void cam_cpas_dump_axi_vote_info(
 	const struct cam_cpas_client *cpas_client,
 	const char *identifier,
@@ -1203,6 +1206,7 @@ int cam_cpas_soc_init_resources(struct cam_hw_soc_info *soc_info,
 	irq_handler_t irq_handler, struct cam_hw_info *cpas_hw)
 {
 	int rc = 0;
+	struct cam_cpas_private_soc *soc_private;
 
 	rc = cam_soc_util_get_dt_properties(soc_info);
 	if (rc) {
@@ -1237,6 +1241,18 @@ int cam_cpas_soc_init_resources(struct cam_hw_soc_info *soc_info,
 		goto free_soc_private;
 	}
 
+	soc_private = (struct cam_cpas_private_soc *)soc_info->soc_private;
+
+	rc = cam_soc_util_get_option_clk_by_name(soc_info, CAM_ICP_CLK_NAME,
+		&soc_private->icp_clk_index);
+	if (rc) {
+		CAM_DBG(CAM_CPAS, "Option clk get failed with rc %d", rc);
+		soc_private->icp_clk_index = -1;
+		rc = 0;
+	} else {
+		CAM_DBG(CAM_CPAS, "Option clk get success index %d", soc_private->icp_clk_index);
+	}
+
 	return rc;
 
 free_soc_private:
@@ -1254,6 +1270,12 @@ int cam_cpas_soc_deinit_resources(struct cam_hw_soc_info *soc_info)
 	for (i = 0; i < soc_private->num_caches; i++)
 		llcc_slice_putd(soc_private->llcc_info[i].slic_desc);
 
+	if (soc_private->icp_clk_index != -1) {
+		rc = cam_soc_util_put_optional_clk(soc_info, soc_private->icp_clk_index);
+		if (rc)
+			CAM_ERR(CAM_CPAS, "Error Put optional clk failed rc=%d", rc);
+	}
+
 	rc = cam_soc_util_release_platform_resource(soc_info);
 	if (rc)
 		CAM_ERR(CAM_CPAS, "release platform failed, rc=%d", rc);

+ 3 - 0
drivers/cam_cpas/cam_cpas_soc.h

@@ -1,6 +1,7 @@
 /* SPDX-License-Identifier: GPL-2.0-only */
 /*
  * Copyright (c) 2017-2021, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved.
  */
 
 #ifndef _CAM_CPAS_SOC_H_
@@ -163,6 +164,7 @@ struct cam_cpas_smart_qos_info {
  * @llcc_info: Cache info
  * @enable_smart_qos: Whether to enable Smart QoS mechanism on current chipset
  * @smart_qos_info: Pointer to smart qos info
+ * @icp_clk_index: Index of optional icp clk
  */
 struct cam_cpas_private_soc {
 	const char *arch_compat;
@@ -187,6 +189,7 @@ struct cam_cpas_private_soc {
 	struct cam_sys_cache_info *llcc_info;
 	bool enable_smart_qos;
 	struct cam_cpas_smart_qos_info *smart_qos_info;
+	int32_t icp_clk_index;
 };
 
 void cam_cpas_util_debug_parse_data(struct cam_cpas_private_soc *soc_private);

+ 41 - 4
drivers/cam_cpas/cpas_top/cam_cpastop_hw.c

@@ -1,6 +1,7 @@
 // SPDX-License-Identifier: GPL-2.0-only
 /*
  * Copyright (c) 2017-2021, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved.
  */
 
 #include <linux/delay.h>
@@ -922,14 +923,19 @@ static int cam_cpastop_poweroff(struct cam_hw_info *cpas_hw)
 }
 
 static int cam_cpastop_qchannel_handshake(struct cam_hw_info *cpas_hw,
-	bool power_on)
+	bool power_on, bool force_on)
 {
 	struct cam_cpas *cpas_core = (struct cam_cpas *) cpas_hw->core_info;
 	struct cam_hw_soc_info *soc_info = &cpas_hw->soc_info;
 	int32_t reg_indx = cpas_core->regbase_index[CAM_CPAS_REG_CPASTOP];
 	uint32_t mask = 0;
 	uint32_t wait_data, qchannel_status, qdeny;
-	int rc = 0;
+	int rc = 0, ret = 0;
+	struct cam_cpas_private_soc *soc_private =
+		(struct cam_cpas_private_soc *) cpas_hw->soc_info.soc_private;
+	struct cam_cpas_hw_errata_wa_list *errata_wa_list =
+		camnoc_info->errata_wa_list;
+	bool icp_clk_enabled = false;
 
 	if (reg_indx == -1)
 		return -EINVAL;
@@ -937,7 +943,28 @@ static int cam_cpastop_qchannel_handshake(struct cam_hw_info *cpas_hw,
 	if (!qchannel_info)
 		return 0;
 
+	if (errata_wa_list && errata_wa_list->enable_icp_clk_for_qchannel.enable) {
+		CAM_DBG(CAM_CPAS, "Enabling ICP clk for qchannel handshake");
+
+		if (soc_private->icp_clk_index == -1) {
+			CAM_ERR(CAM_CPAS,
+				"ICP clock not added as optional clk, qchannel handshake will fail");
+		} else {
+			ret = cam_soc_util_clk_enable(soc_info, true, soc_private->icp_clk_index,
+				-1, NULL);
+			if (ret)
+				CAM_ERR(CAM_CPAS, "Error enable icp clk failed rc=%d", ret);
+			else
+				icp_clk_enabled = true;
+		}
+	}
+
 	if (power_on) {
+		if (force_on) {
+			cam_io_w_mb(0x1,
+			soc_info->reg_map[reg_indx].mem_base + qchannel_info->qchannel_ctrl);
+			CAM_DBG(CAM_CPAS, "Force qchannel on");
+		}
 		/* wait for QACCEPTN in QCHANNEL status*/
 		mask = BIT(0);
 		wait_data = 1;
@@ -957,20 +984,30 @@ static int cam_cpastop_qchannel_handshake(struct cam_hw_info *cpas_hw,
 		CAM_CPAS_POLL_MIN_USECS, CAM_CPAS_POLL_MAX_USECS);
 	if (rc) {
 		CAM_ERR(CAM_CPAS,
-			"camnoc idle sequence failed, qstat 0x%x",
+			"CPAS_%s camnoc idle sequence failed, qstat 0x%x",
+			power_on ? "START" : "STOP",
 			cam_io_r(soc_info->reg_map[reg_indx].mem_base +
 			qchannel_info->qchannel_status));
 		/* Do not return error, passthrough */
-		rc = 0;
 	}
 
 	/* check if deny bit is set */
 	qchannel_status = cam_io_r_mb(soc_info->reg_map[reg_indx].mem_base +
 				qchannel_info->qchannel_status);
+
+	CAM_DBG(CAM_CPAS, "CPAS_%s : qchannel status 0x%x", power_on ? "START" : "STOP",
+		qchannel_status);
+
 	qdeny = (qchannel_status & BIT(1));
 	if (!power_on && qdeny)
 		rc = -EBUSY;
 
+	if (icp_clk_enabled) {
+		ret = cam_soc_util_clk_disable(soc_info, true, soc_private->icp_clk_index);
+		if (ret)
+			CAM_ERR(CAM_CPAS, "Error disable icp clk failed rc=%d", rc);
+	}
+
 	return rc;
 }
 

+ 3 - 0
drivers/cam_cpas/cpas_top/cam_cpastop_hw.h

@@ -1,6 +1,7 @@
 /* SPDX-License-Identifier: GPL-2.0-only */
 /*
  * Copyright (c) 2017-2021, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved.
  */
 
 #ifndef _CAM_CPASTOP_HW_H_
@@ -298,10 +299,12 @@ struct cam_cpas_hw_errata_wa {
  *         camnoc slave pending transactions before turning off CPAS_TOP gdsc
  * @tcsr_camera_hf_sf_ares_glitch: Errata workaround info from ignoring
  *         erroneous signals at camera start
+ * @enable_icp_clk_for_qchannel: Need to enable ICP clk while qchannel handshake
  */
 struct cam_cpas_hw_errata_wa_list {
 	struct cam_cpas_hw_errata_wa camnoc_flush_slave_pending_trans;
 	struct cam_cpas_hw_errata_wa tcsr_camera_hf_sf_ares_glitch;
+	struct cam_cpas_hw_errata_wa enable_icp_clk_for_qchannel;
 };
 
 /**

+ 4 - 0
drivers/cam_cpas/cpas_top/cpastop_v780_100.h

@@ -1,6 +1,7 @@
 /* SPDX-License-Identifier: GPL-2.0-only */
 /*
  * Copyright (c) 2019-2021, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved.
  */
 
 #ifndef _CPASTOP_V780_100_H_
@@ -1191,6 +1192,9 @@ static struct cam_cpas_hw_errata_wa_list cam780_cpas100_errata_wa_list = {
 			.value = 0, /* expected to be 0 */
 		},
 	},
+	.enable_icp_clk_for_qchannel = {
+		.enable = true,
+	},
 };
 
 static struct cam_camnoc_info cam780_cpas100_camnoc_info = {

+ 26 - 0
drivers/cam_utils/cam_soc_util.c

@@ -1,6 +1,7 @@
 // SPDX-License-Identifier: GPL-2.0-only
 /*
  * Copyright (c) 2015-2021, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved.
  */
 
 #include <linux/of.h>
@@ -3629,3 +3630,28 @@ int cam_soc_util_print_clk_freq(struct cam_hw_soc_info *soc_info)
 
 	return 0;
 }
+
+int cam_soc_util_regulators_enabled(struct cam_hw_soc_info *soc_info)
+{
+	int j = 0, rc = 0;
+	int enabled_cnt = 0;
+
+	for (j = 0; j < soc_info->num_rgltr; j++) {
+		if (soc_info->rgltr[j]) {
+			rc = regulator_is_enabled(soc_info->rgltr[j]);
+			if (rc < 0) {
+				CAM_ERR(CAM_UTIL, "%s regulator_is_enabled failed",
+					soc_info->rgltr_name[j]);
+			} else if (rc > 0) {
+				CAM_DBG(CAM_UTIL, "%s regulator enabled",
+					soc_info->rgltr_name[j]);
+				enabled_cnt++;
+			} else {
+				CAM_DBG(CAM_UTIL, "%s regulator is disabled",
+					soc_info->rgltr_name[j]);
+			}
+		}
+	}
+
+	return enabled_cnt;
+}

+ 13 - 0
drivers/cam_utils/cam_soc_util.h

@@ -1,6 +1,7 @@
 /* SPDX-License-Identifier: GPL-2.0-only */
 /*
  * Copyright (c) 2015-2021, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved.
  */
 
 #ifndef _CAM_SOC_UTIL_H_
@@ -773,4 +774,16 @@ int cam_soc_util_print_clk_freq(struct cam_hw_soc_info *soc_info);
 int cam_soc_util_select_pinctrl_state(
 	struct cam_hw_soc_info *soc_info, int idx, bool active);
 
+/**
+ * cam_soc_util_print_clk_freq()
+ *
+ * @brief:              This function checks whether regulators of this device are enabled at this
+ *                      time.
+ *
+ * @soc_info:           Device soc struct to be populated
+ *
+ * @return:             Number of regulators enabled
+ */
+int cam_soc_util_regulators_enabled(struct cam_hw_soc_info *soc_info);
+
 #endif /* _CAM_SOC_UTIL_H_ */