diff --git a/drivers/cam_cpas/cam_cpas_hw.c b/drivers/cam_cpas/cam_cpas_hw.c index ed3a9e45dd..8d10bb4bdd 100644 --- a/drivers/cam_cpas/cam_cpas_hw.c +++ b/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 @@ -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; } diff --git a/drivers/cam_cpas/cam_cpas_hw.h b/drivers/cam_cpas/cam_cpas_hw.h index df80561963..c6c61e06f8 100644 --- a/drivers/cam_cpas/cam_cpas_hw.h +++ b/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); }; /** diff --git a/drivers/cam_cpas/cam_cpas_soc.c b/drivers/cam_cpas/cam_cpas_soc.c index 1b602ee4c4..01600ad73b 100644 --- a/drivers/cam_cpas/cam_cpas_soc.c +++ b/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 @@ -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); diff --git a/drivers/cam_cpas/cam_cpas_soc.h b/drivers/cam_cpas/cam_cpas_soc.h index 3858d744b7..b9e3ea4efc 100644 --- a/drivers/cam_cpas/cam_cpas_soc.h +++ b/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); diff --git a/drivers/cam_cpas/cpas_top/cam_cpastop_hw.c b/drivers/cam_cpas/cpas_top/cam_cpastop_hw.c index ee297496ea..4d0e98245e 100644 --- a/drivers/cam_cpas/cpas_top/cam_cpastop_hw.c +++ b/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 @@ -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; } diff --git a/drivers/cam_cpas/cpas_top/cam_cpastop_hw.h b/drivers/cam_cpas/cpas_top/cam_cpastop_hw.h index c5485de41c..224d7062f8 100644 --- a/drivers/cam_cpas/cpas_top/cam_cpastop_hw.h +++ b/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; }; /** diff --git a/drivers/cam_cpas/cpas_top/cpastop_v780_100.h b/drivers/cam_cpas/cpas_top/cpastop_v780_100.h index bb8f9d9ba7..41c29ffe4d 100644 --- a/drivers/cam_cpas/cpas_top/cpastop_v780_100.h +++ b/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 = { diff --git a/drivers/cam_utils/cam_soc_util.c b/drivers/cam_utils/cam_soc_util.c index 16e012fc9e..3e27dcf584 100644 --- a/drivers/cam_utils/cam_soc_util.c +++ b/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 @@ -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; +} diff --git a/drivers/cam_utils/cam_soc_util.h b/drivers/cam_utils/cam_soc_util.h index 7a4277b0db..d54104515a 100644 --- a/drivers/cam_utils/cam_soc_util.h +++ b/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_ */