Explorar el Código

msm: camera: cpas: Add mechanism to check camnoc idle status

Qchannel interface is used to ensure camnoc is idle. This needs
to be check before camera core power collapse and after camera core
power on sequence to make sure camera hw blocks are in proper state.

CRs-Fixed: 2847646
Change-Id: If9dbd980c2e8e983ac973f91e3d1ed132719c395
Signed-off-by: Alok Chauhan <[email protected]>
Alok Chauhan hace 4 años
padre
commit
4461467cc3

+ 26 - 0
drivers/cam_cpas/cam_cpas_hw.c

@@ -1545,6 +1545,7 @@ static int cam_cpas_hw_stop(void *hw_priv, void *stop_args,
 	struct cam_cpas_private_soc *soc_private = NULL;
 	int rc = 0;
 	long result;
+	int retry_camnoc_idle = 0;
 
 	if (!hw_priv || !stop_args) {
 		CAM_ERR(CAM_CPAS, "Invalid arguments %pK %pK",
@@ -1598,6 +1599,18 @@ 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);
+			if (rc) {
+				CAM_ERR(CAM_CPAS,
+					"failed in qchannel_handshake rc=%d",
+					rc);
+				retry_camnoc_idle = 1;
+				/* Do not return error, passthrough */
+			}
+		}
+
 		rc = cam_cpas_soc_disable_irq(&cpas_hw->soc_info);
 		if (rc) {
 			CAM_ERR(CAM_CPAS, "disable_irq failed, rc=%d", rc);
@@ -1613,6 +1626,19 @@ static int cam_cpas_hw_stop(void *hw_priv, void *stop_args,
 				atomic_read(&cpas_core->irq_count));
 		}
 
+		/* 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);
+			if (rc) {
+				CAM_ERR(CAM_CPAS,
+					"failed in qchannel_handshake rc=%d",
+					rc);
+				/* Do not return error, passthrough */
+			}
+		}
+
 		rc = cam_cpas_soc_disable_resources(&cpas_hw->soc_info,
 			true, false);
 		if (rc) {

+ 3 - 0
drivers/cam_cpas/cam_cpas_hw.h

@@ -71,6 +71,8 @@ enum cam_cpas_access_type {
  * @power_off: Function pointer for hw core specific power off settings
  * @setup_qos_settings: Function pointer for hw to select a specific qos header
  * @print_poweron_settings: Function pointer for hw to print poweron settings
+ * @qchannel_handshake: Function pointer for hw core specific qchannel
+ *                      handshake settings
  *
  */
 struct cam_cpas_internal_ops {
@@ -86,6 +88,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);
 };
 
 /**

+ 2 - 0
drivers/cam_cpas/cam_cpas_hw_intf.h

@@ -19,6 +19,8 @@
 #define CAM_CPAS_POLL_MIN_USECS 200
 /* Maximum usecs to sleep while polling */
 #define CAM_CPAS_POLL_MAX_USECS 250
+/* Number of times to retry while polling */
+#define CAM_CPAS_POLL_QH_RETRY_CNT 50
 
 /**
  * enum cam_cpas_hw_type - Enum for CPAS HW type

+ 1 - 0
drivers/cam_cpas/camss_top/cam_camsstop_hw.c

@@ -79,6 +79,7 @@ int cam_camsstop_get_internal_ops(struct cam_cpas_internal_ops *internal_ops)
 	internal_ops->power_off = NULL;
 	internal_ops->setup_qos_settings = NULL;
 	internal_ops->print_poweron_settings = NULL;
+	internal_ops->qchannel_handshake = NULL;
 
 	return 0;
 }

+ 62 - 0
drivers/cam_cpas/cpas_top/cam_cpastop_hw.c

@@ -34,6 +34,8 @@
 #include "cam_req_mgr_workq.h"
 
 struct cam_camnoc_info *camnoc_info;
+struct cam_cpas_camnoc_qchannel *qchannel_info;
+
 
 #define CAMNOC_SLAVE_MAX_ERR_CODE 7
 static const char * const camnoc_salve_err_code[] = {
@@ -818,11 +820,65 @@ static int cam_cpastop_poweroff(struct cam_hw_info *cpas_hw)
 	return rc;
 }
 
+static int cam_cpastop_qchannel_handshake(struct cam_hw_info *cpas_hw,
+	bool power_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;
+
+	if (reg_indx == -1)
+		return -EINVAL;
+
+	if (!qchannel_info)
+		return 0;
+
+	if (power_on) {
+		/* wait for QACCEPTN in QCHANNEL status*/
+		mask = BIT(0);
+		wait_data = 1;
+	} else {
+		/* Clear the quiecience request in QCHANNEL ctrl*/
+		cam_io_w_mb(0, soc_info->reg_map[reg_indx].mem_base +
+			qchannel_info->qchannel_ctrl);
+		/* wait for QACCEPTN and QDENY in QCHANNEL status*/
+		mask = BIT(1) | BIT(0);
+		wait_data = 0;
+	}
+
+	rc = cam_io_poll_value_wmask(
+		soc_info->reg_map[reg_indx].mem_base +
+		qchannel_info->qchannel_status,
+		wait_data, mask, CAM_CPAS_POLL_QH_RETRY_CNT,
+		CAM_CPAS_POLL_MIN_USECS, CAM_CPAS_POLL_MAX_USECS);
+	if (rc) {
+		CAM_ERR(CAM_CPAS,
+			"camnoc idle sequence failed, qstat 0x%x",
+			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);
+	qdeny = (qchannel_status & BIT(1));
+	if (!power_on && qdeny)
+		rc = -EBUSY;
+
+	return rc;
+}
+
 static int cam_cpastop_init_hw_version(struct cam_hw_info *cpas_hw,
 	struct cam_cpas_hw_caps *hw_caps)
 {
 	int rc = 0;
 	struct cam_hw_soc_info *soc_info = &cpas_hw->soc_info;
+	qchannel_info = NULL;
 
 	CAM_DBG(CAM_CPAS,
 		"hw_version=0x%x Camera Version %d.%d.%d, cpas version %d.%d.%d",
@@ -864,18 +920,23 @@ static int cam_cpastop_init_hw_version(struct cam_hw_info *cpas_hw,
 		break;
 	case CAM_CPAS_TITAN_580_V100:
 		camnoc_info = &cam580_cpas100_camnoc_info;
+		qchannel_info = &cam580_cpas100_qchannel_info;
 		break;
 	case CAM_CPAS_TITAN_540_V100:
 		camnoc_info = &cam540_cpas100_camnoc_info;
+		qchannel_info = &cam540_cpas100_qchannel_info;
 		break;
 	case CAM_CPAS_TITAN_520_V100:
 		camnoc_info = &cam520_cpas100_camnoc_info;
+		qchannel_info = &cam520_cpas100_qchannel_info;
 		break;
 	case CAM_CPAS_TITAN_545_V100:
 		camnoc_info = &cam545_cpas100_camnoc_info;
+		qchannel_info = &cam545_cpas100_qchannel_info;
 		break;
 	case CAM_CPAS_TITAN_570_V200:
 		camnoc_info = &cam570_cpas200_camnoc_info;
+		qchannel_info = &cam570_cpas200_qchannel_info;
 		break;
 	case CAM_CPAS_TITAN_680_V100:
 		camnoc_info = &cam680_cpas100_camnoc_info;
@@ -961,6 +1022,7 @@ int cam_cpastop_get_internal_ops(struct cam_cpas_internal_ops *internal_ops)
 	internal_ops->setup_qos_settings = cam_cpastop_setup_qos_settings;
 	internal_ops->print_poweron_settings =
 		cam_cpastop_print_poweron_settings;
+	internal_ops->qchannel_handshake = cam_cpastop_qchannel_handshake;
 
 	return 0;
 }

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

@@ -356,4 +356,16 @@ struct cam_cpas_work_payload {
 	struct work_struct work;
 };
 
+/**
+ * struct cam_cpas_camnoc_qchannel : Cpas camnoc qchannel info
+ *
+ * @qchannel_ctrl: offset to configure to control camnoc qchannel interface
+ * @qchannel_status: offset to read camnoc qchannel interface status
+ *
+ */
+struct cam_cpas_camnoc_qchannel {
+	uint32_t qchannel_ctrl;
+	uint32_t qchannel_status;
+};
+
 #endif /* _CAM_CPASTOP_HW_H_ */

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

@@ -237,4 +237,8 @@ static struct cam_camnoc_info cam520_cpas100_camnoc_info = {
 	.errata_wa_list = NULL,
 };
 
+static struct cam_cpas_camnoc_qchannel cam520_cpas100_qchannel_info = {
+	.qchannel_ctrl   = 0x14,
+	.qchannel_status = 0x18,
+};
 #endif /* _CPASTOP_V520_100_H_ */

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

@@ -238,4 +238,8 @@ static struct cam_camnoc_info cam540_cpas100_camnoc_info = {
 	.errata_wa_list = NULL,
 };
 
+static struct cam_cpas_camnoc_qchannel cam540_cpas100_qchannel_info = {
+	.qchannel_ctrl   = 0x14,
+	.qchannel_status = 0x18,
+};
 #endif /* _CPASTOP_V540_100_H_ */

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

@@ -315,4 +315,8 @@ static struct cam_camnoc_info cam545_cpas100_camnoc_info = {
 	.errata_wa_list = NULL,
 };
 
+static struct cam_cpas_camnoc_qchannel cam545_cpas100_qchannel_info = {
+	.qchannel_ctrl   = 0x14,
+	.qchannel_status = 0x18,
+};
 #endif /* _CPASTOP_V545_100_H_ */

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

@@ -881,4 +881,8 @@ static struct cam_camnoc_info cam570_cpas200_camnoc_info = {
 	.errata_wa_list = &cam570_cpas200_errata_wa_list,
 };
 
+static struct cam_cpas_camnoc_qchannel cam570_cpas200_qchannel_info = {
+	.qchannel_ctrl   = 0x5C,
+	.qchannel_status = 0x60,
+};
 #endif /* _CPASTOP_V570_200_H_ */

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

@@ -1043,5 +1043,9 @@ static struct cam_camnoc_info cam580_cpas100_camnoc_info = {
 	.errata_wa_list = &cam580_cpas100_errata_wa_list,
 };
 
+static struct cam_cpas_camnoc_qchannel cam580_cpas100_qchannel_info = {
+	.qchannel_ctrl   = 0x5C,
+	.qchannel_status = 0x60,
+};
 #endif /* _CPASTOP_V580_100_H_ */