From 3f527598f0858114f45662fce1bccfaa35798c85 Mon Sep 17 00:00:00 2001 From: Alok Chauhan Date: Wed, 2 Dec 2020 17:09:07 +0530 Subject: [PATCH] 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 --- drivers/cam_cpas/cam_cpas_hw.c | 26 ++++++++ drivers/cam_cpas/cam_cpas_hw.h | 3 + drivers/cam_cpas/cam_cpas_hw_intf.h | 2 + drivers/cam_cpas/camss_top/cam_camsstop_hw.c | 1 + drivers/cam_cpas/cpas_top/cam_cpastop_hw.c | 62 ++++++++++++++++++++ drivers/cam_cpas/cpas_top/cam_cpastop_hw.h | 12 ++++ drivers/cam_cpas/cpas_top/cpastop_v520_100.h | 4 ++ drivers/cam_cpas/cpas_top/cpastop_v540_100.h | 4 ++ drivers/cam_cpas/cpas_top/cpastop_v545_100.h | 4 ++ drivers/cam_cpas/cpas_top/cpastop_v570_200.h | 4 ++ drivers/cam_cpas/cpas_top/cpastop_v580_100.h | 4 ++ 11 files changed, 126 insertions(+) diff --git a/drivers/cam_cpas/cam_cpas_hw.c b/drivers/cam_cpas/cam_cpas_hw.c index 56bea71d400b..2119e1b6885b 100644 --- a/drivers/cam_cpas/cam_cpas_hw.c +++ b/drivers/cam_cpas/cam_cpas_hw.c @@ -1544,6 +1544,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", @@ -1597,6 +1598,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); @@ -1612,6 +1625,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) { diff --git a/drivers/cam_cpas/cam_cpas_hw.h b/drivers/cam_cpas/cam_cpas_hw.h index 1c25d0f56203..a7c7f429ccc2 100644 --- a/drivers/cam_cpas/cam_cpas_hw.h +++ b/drivers/cam_cpas/cam_cpas_hw.h @@ -70,6 +70,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 { @@ -85,6 +87,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); }; /** diff --git a/drivers/cam_cpas/cam_cpas_hw_intf.h b/drivers/cam_cpas/cam_cpas_hw_intf.h index fc9297ac69c1..09d1180a4b01 100644 --- a/drivers/cam_cpas/cam_cpas_hw_intf.h +++ b/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 diff --git a/drivers/cam_cpas/camss_top/cam_camsstop_hw.c b/drivers/cam_cpas/camss_top/cam_camsstop_hw.c index 23f8542cdd87..d8552c56d1db 100644 --- a/drivers/cam_cpas/camss_top/cam_camsstop_hw.c +++ b/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; } diff --git a/drivers/cam_cpas/cpas_top/cam_cpastop_hw.c b/drivers/cam_cpas/cpas_top/cam_cpastop_hw.c index 67faa3443260..8c0f3e618082 100644 --- a/drivers/cam_cpas/cpas_top/cam_cpastop_hw.c +++ b/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[] = { @@ -809,11 +811,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", @@ -855,18 +911,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; @@ -952,6 +1013,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; } diff --git a/drivers/cam_cpas/cpas_top/cam_cpastop_hw.h b/drivers/cam_cpas/cpas_top/cam_cpastop_hw.h index 2d59122cab92..3992e079ad18 100644 --- a/drivers/cam_cpas/cpas_top/cam_cpastop_hw.h +++ b/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_ */ diff --git a/drivers/cam_cpas/cpas_top/cpastop_v520_100.h b/drivers/cam_cpas/cpas_top/cpastop_v520_100.h index ec99574ebf10..4a8c4f945d03 100644 --- a/drivers/cam_cpas/cpas_top/cpastop_v520_100.h +++ b/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_ */ diff --git a/drivers/cam_cpas/cpas_top/cpastop_v540_100.h b/drivers/cam_cpas/cpas_top/cpastop_v540_100.h index 65cb1fabc701..3d9e65ed3c1f 100644 --- a/drivers/cam_cpas/cpas_top/cpastop_v540_100.h +++ b/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_ */ diff --git a/drivers/cam_cpas/cpas_top/cpastop_v545_100.h b/drivers/cam_cpas/cpas_top/cpastop_v545_100.h index d7cbe7794f6e..d3ad5fbc1075 100644 --- a/drivers/cam_cpas/cpas_top/cpastop_v545_100.h +++ b/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_ */ diff --git a/drivers/cam_cpas/cpas_top/cpastop_v570_200.h b/drivers/cam_cpas/cpas_top/cpastop_v570_200.h index 02410cc00876..49978a0f5279 100644 --- a/drivers/cam_cpas/cpas_top/cpastop_v570_200.h +++ b/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_ */ diff --git a/drivers/cam_cpas/cpas_top/cpastop_v580_100.h b/drivers/cam_cpas/cpas_top/cpastop_v580_100.h index 9855fdf6821c..7c01ae3cdcb0 100644 --- a/drivers/cam_cpas/cpas_top/cpastop_v580_100.h +++ b/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_ */