Merge "msm: camera: cpas: Add mechanism to check camnoc idle status" into camera-kernel.lnx.4.0

This commit is contained in:
Camera Software Integration 2021-01-11 23:37:44 -08:00 • committed by Gerrit - the friendly Code Review server
commit 03e07e03dc
11 changed files with 126 additions and 0 deletions

View file

@ -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) {

View file

@ -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);
};
/**

View file

@ -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

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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_ */

View file

@ -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_ */

View file

@ -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_ */

View file

@ -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_ */

View file

@ -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_ */

View file

@ -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_ */