Browse Source

Merge "msm: camera: cpas: Add more debug information in cpas dump" into camera-kernel.lnx.4.0

Camera Software Integration 4 years ago
parent
commit
8fe045c35a

+ 257 - 2
drivers/cam_cpas/cam_cpas_hw.c

@@ -19,6 +19,11 @@
 static uint cam_min_camnoc_ib_bw;
 static uint cam_min_camnoc_ib_bw;
 module_param(cam_min_camnoc_ib_bw, uint, 0644);
 module_param(cam_min_camnoc_ib_bw, uint, 0644);
 
 
+static void cam_cpas_update_monitor_array(struct cam_hw_info *cpas_hw,
+	const char *identifier_string, int32_t identifier_value);
+static void cam_cpas_dump_monitor_array(
+	struct cam_cpas *cpas_core);
+
 static void cam_cpas_process_bw_overrides(
 static void cam_cpas_process_bw_overrides(
 	struct cam_cpas_bus_client *bus_client, uint64_t *ab, uint64_t *ib,
 	struct cam_cpas_bus_client *bus_client, uint64_t *ab, uint64_t *ib,
 	const struct cam_cpas_debug_settings *cpas_settings)
 	const struct cam_cpas_debug_settings *cpas_settings)
@@ -1068,9 +1073,16 @@ static int cam_cpas_hw_update_axi_vote(struct cam_hw_info *cpas_hw,
 	cam_cpas_dump_axi_vote_info(cpas_core->cpas_client[client_indx],
 	cam_cpas_dump_axi_vote_info(cpas_core->cpas_client[client_indx],
 		"Translated Vote", &axi_vote);
 		"Translated Vote", &axi_vote);
 
 
+	/* Log an entry whenever there is an AXI update - before updating */
+	cam_cpas_update_monitor_array(cpas_hw, "CPAS AXI pre-update",
+		client_indx);
+
 	rc = cam_cpas_util_apply_client_axi_vote(cpas_hw,
 	rc = cam_cpas_util_apply_client_axi_vote(cpas_hw,
 		cpas_core->cpas_client[client_indx], &axi_vote);
 		cpas_core->cpas_client[client_indx], &axi_vote);
 
 
+	/* Log an entry whenever there is an AXI update - after updating */
+	cam_cpas_update_monitor_array(cpas_hw, "CPAS AXI post-update",
+		client_indx);
 unlock_client:
 unlock_client:
 	mutex_unlock(&cpas_core->client_mutex[client_indx]);
 	mutex_unlock(&cpas_core->client_mutex[client_indx]);
 	mutex_unlock(&cpas_hw->hw_mutex);
 	mutex_unlock(&cpas_hw->hw_mutex);
@@ -1785,8 +1797,52 @@ static int cam_cpas_log_vote(struct cam_hw_info *cpas_hw)
 	struct cam_cpas *cpas_core = (struct cam_cpas *) cpas_hw->core_info;
 	struct cam_cpas *cpas_core = (struct cam_cpas *) cpas_hw->core_info;
 	struct cam_cpas_private_soc *soc_private =
 	struct cam_cpas_private_soc *soc_private =
 		(struct cam_cpas_private_soc *) cpas_hw->soc_info.soc_private;
 		(struct cam_cpas_private_soc *) cpas_hw->soc_info.soc_private;
-	int rc = 0;
 	uint32_t i;
 	uint32_t i;
+	struct cam_cpas_tree_node *curr_node;
+	struct cam_hw_soc_info *soc_info = &cpas_hw->soc_info;
+
+	/*
+	 * First print rpmh registers as early as possible to catch nearest
+	 * state of rpmh after an issue (overflow) occurs.
+	 */
+	if ((cpas_core->streamon_clients > 0) &&
+		(cpas_core->regbase_index[CAM_CPAS_REG_RPMH] != -1)) {
+		int reg_base_index =
+			cpas_core->regbase_index[CAM_CPAS_REG_RPMH];
+		void __iomem *rpmh_base =
+			soc_info->reg_map[reg_base_index].mem_base;
+		uint32_t offset_fe, offset_be;
+		uint32_t fe_val, be_val;
+		uint32_t *rpmh_info = &soc_private->rpmh_info[0];
+		uint32_t ddr_bcm_index =
+			soc_private->rpmh_info[CAM_RPMH_BCM_DDR_INDEX];
+		uint32_t mnoc_bcm_index =
+			soc_private->rpmh_info[CAM_RPMH_BCM_MNOC_INDEX];
+
+		/*
+		 * print 12 registers from 0x4, 0x800 offsets -
+		 * this will give ddr, mmnoc and other BCM info.
+		 * i=0 for DDR, i=4 for mnoc, but double check for each chipset.
+		 */
+		for (i = 0; i < rpmh_info[CAM_RPMH_NUMBER_OF_BCMS]; i++) {
+			if ((!cpas_core->full_state_dump) &&
+				(i != ddr_bcm_index) &&
+				(i != mnoc_bcm_index))
+				continue;
+
+			offset_fe = rpmh_info[CAM_RPMH_BCM_FE_OFFSET] +
+				(i * 0x4);
+			offset_be = rpmh_info[CAM_RPMH_BCM_BE_OFFSET] +
+				(i * 0x4);
+
+			fe_val = cam_io_r_mb(rpmh_base + offset_fe);
+			be_val = cam_io_r_mb(rpmh_base + offset_be);
+
+			CAM_INFO(CAM_CPAS,
+				"i=%d, FE[offset=0x%x, value=0x%x] BE[offset=0x%x, value=0x%x]",
+				i, offset_fe, fe_val, offset_be, be_val);
+		}
+	}
 
 
 	for (i = 0; i < cpas_core->num_axi_ports; i++) {
 	for (i = 0; i < cpas_core->num_axi_ports; i++) {
 		CAM_INFO(CAM_CPAS,
 		CAM_INFO(CAM_CPAS,
@@ -1818,7 +1874,182 @@ static int cam_cpas_log_vote(struct cam_hw_info *cpas_hw)
 	CAM_INFO(CAM_CPAS, "ahb client curr vote level[%d]",
 	CAM_INFO(CAM_CPAS, "ahb client curr vote level[%d]",
 		cpas_core->ahb_bus_client.curr_vote_level);
 		cpas_core->ahb_bus_client.curr_vote_level);
 
 
-	return rc;
+	if (!cpas_core->full_state_dump) {
+		CAM_DBG(CAM_CPAS, "CPAS full state dump not enabled");
+		return 0;
+	}
+
+	/* This will traverse through all nodes in the tree and print stats*/
+	for (i = 0; i < CAM_CPAS_MAX_TREE_NODES; i++) {
+		if (!soc_private->tree_node[i])
+			continue;
+		curr_node = soc_private->tree_node[i];
+
+		CAM_INFO(CAM_CPAS,
+			"[%s] Cell[%d] level[%d] PortIdx[%d][%d] camnoc_bw[%d %d %lld %lld] mnoc_bw[%lld %lld]",
+			curr_node->node_name, curr_node->cell_idx,
+			curr_node->level_idx, curr_node->axi_port_idx,
+			curr_node->camnoc_axi_port_idx,
+			curr_node->camnoc_max_needed,
+			curr_node->bus_width_factor,
+			curr_node->camnoc_bw,
+			curr_node->camnoc_bw * curr_node->bus_width_factor,
+			curr_node->mnoc_ab_bw, curr_node->mnoc_ib_bw);
+	}
+
+	if (cpas_core->streamon_clients > 0) {
+		/*
+		 * Means, cpas has clocks turned on, so we can query clk freq.
+		 * Print clk frequencies that cpas enables - this will print
+		 * camcc_ahb, camcc_axi, gcc_hf, gcc_sf as well.
+		 */
+		cam_soc_util_print_clk_freq(&cpas_hw->soc_info);
+	}
+
+	cam_cpas_dump_monitor_array(cpas_core);
+
+	return 0;
+}
+
+static void cam_cpas_update_monitor_array(struct cam_hw_info *cpas_hw,
+	const char *identifier_string, int32_t identifier_value)
+{
+	struct cam_cpas *cpas_core = (struct cam_cpas *) cpas_hw->core_info;
+	struct cam_hw_soc_info *soc_info = &cpas_hw->soc_info;
+	struct cam_cpas_private_soc *soc_private =
+		(struct cam_cpas_private_soc *) cpas_hw->soc_info.soc_private;
+	struct cam_cpas_monitor *entry;
+	int iterator;
+	int i;
+
+	CAM_CPAS_INC_MONITOR_HEAD(&cpas_core->monitor_head, &iterator);
+
+	entry = &cpas_core->monitor_entries[iterator];
+
+	ktime_get_real_ts64(&entry->timestamp);
+	strlcpy(entry->identifier_string, identifier_string,
+		sizeof(entry->identifier_string));
+
+	entry->identifier_value = identifier_value;
+
+	for (i = 0; i < cpas_core->num_axi_ports; i++) {
+		entry->axi_info[i].axi_port_name =
+			cpas_core->axi_port[i].axi_port_name;
+		entry->axi_info[i].ab_bw = cpas_core->axi_port[i].ab_bw;
+		entry->axi_info[i].ib_bw = cpas_core->axi_port[i].ib_bw;
+		entry->axi_info[i].camnoc_bw = cpas_core->axi_port[i].camnoc_bw;
+		entry->axi_info[i].applied_ab_bw =
+			cpas_core->axi_port[i].applied_ab_bw;
+		entry->axi_info[i].applied_ib_bw =
+			cpas_core->axi_port[i].applied_ib_bw;
+	}
+
+	entry->applied_camnoc_clk = cpas_core->applied_camnoc_axi_rate;
+	entry->applied_ahb_level = cpas_core->ahb_bus_client.curr_vote_level;
+
+	if ((cpas_core->streamon_clients > 0) &&
+		(cpas_core->regbase_index[CAM_CPAS_REG_RPMH] != -1) &&
+		soc_private->rpmh_info[CAM_RPMH_NUMBER_OF_BCMS]) {
+		int reg_base_index =
+			cpas_core->regbase_index[CAM_CPAS_REG_RPMH];
+		void __iomem *rpmh_base =
+			soc_info->reg_map[reg_base_index].mem_base;
+		uint32_t fe_ddr_offset =
+			soc_private->rpmh_info[CAM_RPMH_BCM_FE_OFFSET] +
+			(0x4 * soc_private->rpmh_info[CAM_RPMH_BCM_DDR_INDEX]);
+		uint32_t fe_mnoc_offset =
+			soc_private->rpmh_info[CAM_RPMH_BCM_FE_OFFSET] +
+			(0x4 * soc_private->rpmh_info[CAM_RPMH_BCM_MNOC_INDEX]);
+		uint32_t be_ddr_offset =
+			soc_private->rpmh_info[CAM_RPMH_BCM_BE_OFFSET] +
+			(0x4 * soc_private->rpmh_info[CAM_RPMH_BCM_DDR_INDEX]);
+		uint32_t be_mnoc_offset =
+			soc_private->rpmh_info[CAM_RPMH_BCM_BE_OFFSET] +
+			(0x4 * soc_private->rpmh_info[CAM_RPMH_BCM_MNOC_INDEX]);
+
+		/*
+		 * 0x4, 0x800 - DDR
+		 * 0x800, 0x810 - mmnoc
+		 */
+		entry->fe_ddr = cam_io_r_mb(rpmh_base + fe_ddr_offset);
+		entry->fe_mnoc = cam_io_r_mb(rpmh_base + fe_mnoc_offset);
+		entry->be_ddr = cam_io_r_mb(rpmh_base + be_ddr_offset);
+		entry->be_mnoc = cam_io_r_mb(rpmh_base + be_mnoc_offset);
+	}
+}
+
+static void cam_cpas_dump_monitor_array(
+	struct cam_cpas *cpas_core)
+{
+	int i = 0, j = 0;
+	int64_t state_head = 0;
+	uint32_t index, num_entries, oldest_entry;
+	uint64_t ms, tmp, hrs, min, sec;
+	struct cam_cpas_monitor *entry;
+
+	if (!cpas_core->full_state_dump)
+		return;
+
+	state_head = atomic64_read(&cpas_core->monitor_head);
+
+	if (state_head == -1) {
+		CAM_WARN(CAM_CPAS, "No valid entries in cpas monitor array");
+		return;
+	} else if (state_head < CAM_CPAS_MONITOR_MAX_ENTRIES) {
+		num_entries = state_head;
+		oldest_entry = 0;
+	} else {
+		num_entries = CAM_CPAS_MONITOR_MAX_ENTRIES;
+		div_u64_rem(state_head + 1,
+			CAM_CPAS_MONITOR_MAX_ENTRIES, &oldest_entry);
+	}
+
+	CAM_INFO(CAM_CPAS, "======== Dumping monitor information ===========");
+
+	index = oldest_entry;
+
+	for (i = 0; i < num_entries; i++) {
+		entry = &cpas_core->monitor_entries[index];
+		tmp = entry->timestamp.tv_sec;
+		ms = (entry->timestamp.tv_nsec) / 1000000;
+		sec = do_div(tmp, 60);
+		min = do_div(tmp, 60);
+		hrs = do_div(tmp, 24);
+
+		CAM_INFO(CAM_CPAS,
+			"**** %llu:%llu:%llu.%llu : Index[%d] Identifier[%s][%d] camnoc=%lld, ahb=%d",
+			hrs, min, sec, ms,
+			index,
+			entry->identifier_string, entry->identifier_value,
+			entry->applied_camnoc_clk, entry->applied_ahb_level);
+
+		for (j = 0; j < cpas_core->num_axi_ports; j++) {
+			CAM_INFO(CAM_CPAS,
+				"MNOC BW [%s] : ab=%lld, ib=%lld, camnoc=%lld",
+				entry->axi_info[j].axi_port_name,
+				entry->axi_info[j].applied_ab_bw,
+				entry->axi_info[j].applied_ib_bw,
+				entry->axi_info[j].camnoc_bw);
+		}
+
+		if (cpas_core->regbase_index[CAM_CPAS_REG_RPMH] != -1) {
+			CAM_INFO(CAM_CPAS,
+				"fe_ddr=0x%x, fe_mnoc=0x%x, be_ddr=0x%x, be_mnoc=0x%x",
+				entry->fe_ddr, entry->fe_mnoc,
+				entry->be_ddr, entry->be_mnoc);
+		}
+
+		index = (index + 1) % CAM_CPAS_MONITOR_MAX_ENTRIES;
+	}
+}
+
+static int cam_cpas_log_event(struct cam_hw_info *cpas_hw,
+	const char *identifier_string, int32_t identifier_value)
+{
+	cam_cpas_update_monitor_array(cpas_hw, identifier_string,
+		identifier_value);
+
+	return 0;
 }
 }
 
 
 static int cam_cpas_select_qos(struct cam_hw_info *cpas_hw,
 static int cam_cpas_select_qos(struct cam_hw_info *cpas_hw,
@@ -1957,6 +2188,22 @@ static int cam_cpas_hw_process_cmd(void *hw_priv,
 		break;
 		break;
 	}
 	}
 
 
+	case CAM_CPAS_HW_CMD_LOG_EVENT: {
+		struct cam_cpas_hw_cmd_notify_event *event;
+
+		if (sizeof(struct cam_cpas_hw_cmd_notify_event) != arg_size) {
+			CAM_ERR(CAM_CPAS, "cmd_type %d, size mismatch %d",
+				cmd_type, arg_size);
+			break;
+		}
+
+		event = (struct cam_cpas_hw_cmd_notify_event *)cmd_args;
+
+		rc = cam_cpas_log_event(hw_priv, event->identifier_string,
+			event->identifier_value);
+		break;
+	}
+
 	case CAM_CPAS_HW_CMD_SELECT_QOS: {
 	case CAM_CPAS_HW_CMD_SELECT_QOS: {
 		uint32_t *selection_mask;
 		uint32_t *selection_mask;
 
 
@@ -2052,6 +2299,10 @@ static int cam_cpas_util_create_debugfs(struct cam_cpas *cpas_core)
 
 
 	dbgfileptr = debugfs_create_bool("ahb_bus_scaling_disable", 0644,
 	dbgfileptr = debugfs_create_bool("ahb_bus_scaling_disable", 0644,
 		cpas_core->dentry, &cpas_core->ahb_bus_scaling_disable);
 		cpas_core->dentry, &cpas_core->ahb_bus_scaling_disable);
+
+	dbgfileptr = debugfs_create_bool("full_state_dump", 0644,
+		cpas_core->dentry, &cpas_core->full_state_dump);
+
 	if (IS_ERR(dbgfileptr)) {
 	if (IS_ERR(dbgfileptr)) {
 		if (PTR_ERR(dbgfileptr) == -ENODEV)
 		if (PTR_ERR(dbgfileptr) == -ENODEV)
 			CAM_WARN(CAM_CPAS, "DebugFS not enabled in kernel!");
 			CAM_WARN(CAM_CPAS, "DebugFS not enabled in kernel!");
@@ -2102,6 +2353,10 @@ int cam_cpas_hw_probe(struct platform_device *pdev,
 	cpas_hw->soc_info.dev_name = pdev->name;
 	cpas_hw->soc_info.dev_name = pdev->name;
 	cpas_hw->open_count = 0;
 	cpas_hw->open_count = 0;
 	cpas_core->ahb_bus_scaling_disable = false;
 	cpas_core->ahb_bus_scaling_disable = false;
+	cpas_core->full_state_dump = false;
+
+	atomic64_set(&cpas_core->monitor_head, -1);
+
 	mutex_init(&cpas_hw->hw_mutex);
 	mutex_init(&cpas_hw->hw_mutex);
 	spin_lock_init(&cpas_hw->hw_lock);
 	spin_lock_init(&cpas_hw->hw_lock);
 	init_completion(&cpas_hw->hw_complete);
 	init_completion(&cpas_hw->hw_complete);

+ 69 - 0
drivers/cam_cpas/cam_cpas_hw.h

@@ -37,6 +37,19 @@
 	((CAM_CPAS_CLIENT_REGISTERED(cpas_core, indx)) && \
 	((CAM_CPAS_CLIENT_REGISTERED(cpas_core, indx)) && \
 	(cpas_core->cpas_client[indx]->started))
 	(cpas_core->cpas_client[indx]->started))
 
 
+/* Array indices to represent corresponding RPMH BCM info */
+#define CAM_RPMH_NUMBER_OF_BCMS 0
+#define CAM_RPMH_BCM_FE_OFFSET  1
+#define CAM_RPMH_BCM_BE_OFFSET  2
+#define CAM_RPMH_BCM_DDR_INDEX  3
+#define CAM_RPMH_BCM_MNOC_INDEX 4
+#define CAM_RPMH_BCM_INFO_MAX   5
+
+#define CAM_CPAS_MONITOR_MAX_ENTRIES   20
+#define CAM_CPAS_INC_MONITOR_HEAD(head, ret) \
+	div_u64_rem(atomic64_add_return(1, head),\
+	CAM_CPAS_MONITOR_MAX_ENTRIES, (ret))
+
 /**
 /**
  * enum cam_cpas_access_type - Enum for Register access type
  * enum cam_cpas_access_type - Enum for Register access type
  */
  */
@@ -163,6 +176,56 @@ struct cam_cpas_axi_port {
 	uint64_t applied_ib_bw;
 	uint64_t applied_ib_bw;
 };
 };
 
 
+/**
+ * struct cam_cpas_axi_port_debug_info : AXI port information
+ *
+ * @axi_port_name: Name of this AXI port
+ * @ab_bw: AB bw value for this port
+ * @ib_bw: IB bw value for this port
+ * @camnoc_bw: CAMNOC bw value for this port
+ * @applied_ab_bw: applied ab bw for this port
+ * @applied_ib_bw: applied ib bw for this port
+ */
+struct cam_cpas_axi_port_debug_info {
+	const char *axi_port_name;
+	uint64_t ab_bw;
+	uint64_t ib_bw;
+	uint64_t camnoc_bw;
+	uint64_t applied_ab_bw;
+	uint64_t applied_ib_bw;
+};
+
+/**
+ * struct cam_cpas_monitor : CPAS monitor array
+ *
+ * @timestamp: Timestamp at which this monitor entry is saved
+ * @axi_info: AXI port information
+ * @identifier_string: String passed by caller
+ * @identifier_value: Identifier value passed by caller
+ * @applied_camnoc_clk: Applied camnoc axi clock rate
+ * @applied_ahb_level: Applied camcc ahb level
+ * @fe_ddr: RPMH DDR BCM FE (front-end) status register value.
+ *          This indicates requested clock plan
+ * @be_ddr: RPMH DDR BCM BE (back-end) status register value.
+ *          This indicates actual current clock plan
+ * @fe_mnoc: RPMH MNOC BCM FE (front-end) status register value.
+ *           This indicates requested clock plan
+ * @be_mnoc: RPMH MNOC BCM BE (back-end) status register value.
+ *           This indicates actual current clock plan
+ */
+struct cam_cpas_monitor {
+	struct timespec64                   timestamp;
+	char                                identifier_string[128];
+	int32_t                             identifier_value;
+	struct cam_cpas_axi_port_debug_info axi_info[CAM_CPAS_MAX_AXI_PORTS];
+	uint64_t                            applied_camnoc_clk;
+	unsigned int                        applied_ahb_level;
+	uint32_t                            fe_ddr;
+	uint32_t                            be_ddr;
+	uint32_t                            fe_mnoc;
+	uint32_t                            be_mnoc;
+};
+
 /**
 /**
  * struct cam_cpas : CPAS core data structure info
  * struct cam_cpas : CPAS core data structure info
  *
  *
@@ -186,6 +249,9 @@ struct cam_cpas_axi_port {
  * @dentry: debugfs file entry
  * @dentry: debugfs file entry
  * @ahb_bus_scaling_disable: ahb scaling based on src clk corner for bus
  * @ahb_bus_scaling_disable: ahb scaling based on src clk corner for bus
  * @applied_camnoc_axi_rate: applied camnoc axi clock rate
  * @applied_camnoc_axi_rate: applied camnoc axi clock rate
+ * @monitor_head: Monitor array head
+ * @monitor_entries: cpas monitor array
+ * @full_state_dump: Whether to enable full cpas state dump or not
  */
  */
 struct cam_cpas {
 struct cam_cpas {
 	struct cam_cpas_hw_caps hw_caps;
 	struct cam_cpas_hw_caps hw_caps;
@@ -208,6 +274,9 @@ struct cam_cpas {
 	struct dentry *dentry;
 	struct dentry *dentry;
 	bool ahb_bus_scaling_disable;
 	bool ahb_bus_scaling_disable;
 	uint64_t applied_camnoc_axi_rate;
 	uint64_t applied_camnoc_axi_rate;
+	atomic64_t  monitor_head;
+	struct cam_cpas_monitor monitor_entries[CAM_CPAS_MONITOR_MAX_ENTRIES];
+	bool full_state_dump;
 };
 };
 
 
 int cam_camsstop_get_internal_ops(struct cam_cpas_internal_ops *internal_ops);
 int cam_camsstop_get_internal_ops(struct cam_cpas_internal_ops *internal_ops);

+ 13 - 0
drivers/cam_cpas/cam_cpas_hw_intf.h

@@ -40,6 +40,7 @@ enum cam_cpas_hw_cmd_process {
 	CAM_CPAS_HW_CMD_AXI_VOTE,
 	CAM_CPAS_HW_CMD_AXI_VOTE,
 	CAM_CPAS_HW_CMD_LOG_VOTE,
 	CAM_CPAS_HW_CMD_LOG_VOTE,
 	CAM_CPAS_HW_CMD_SELECT_QOS,
 	CAM_CPAS_HW_CMD_SELECT_QOS,
+	CAM_CPAS_HW_CMD_LOG_EVENT,
 	CAM_CPAS_HW_CMD_INVALID,
 	CAM_CPAS_HW_CMD_INVALID,
 };
 };
 
 
@@ -107,6 +108,18 @@ struct cam_cpas_hw_cmd_stop {
 	uint32_t client_handle;
 	uint32_t client_handle;
 };
 };
 
 
+/**
+ * struct cam_cpas_hw_cmd_notify_event : CPAS cmd struct for notify event
+ *
+ * @identifier_string: Identifier string passed by caller
+ * @identifier_value: Identifier value passed by caller
+ *
+ */
+struct cam_cpas_hw_cmd_notify_event {
+	const char *identifier_string;
+	int32_t identifier_value;
+};
+
 /**
 /**
  * struct cam_cpas_hw_caps : CPAS HW capabilities
  * struct cam_cpas_hw_caps : CPAS HW capabilities
  *
  *

+ 31 - 0
drivers/cam_cpas/cam_cpas_intf.c

@@ -461,6 +461,37 @@ int cam_cpas_select_qos_settings(uint32_t selection_mask)
 }
 }
 EXPORT_SYMBOL(cam_cpas_select_qos_settings);
 EXPORT_SYMBOL(cam_cpas_select_qos_settings);
 
 
+int cam_cpas_notify_event(const char *identifier_string,
+	int32_t identifier_value)
+{
+	int rc = 0;
+
+	if (!CAM_CPAS_INTF_INITIALIZED()) {
+		CAM_ERR(CAM_CPAS, "cpas intf not initialized");
+		return -EBADR;
+	}
+
+	if (g_cpas_intf->hw_intf->hw_ops.process_cmd) {
+		struct cam_cpas_hw_cmd_notify_event event = { 0 };
+
+		event.identifier_string = identifier_string;
+		event.identifier_value = identifier_value;
+
+		rc = g_cpas_intf->hw_intf->hw_ops.process_cmd(
+			g_cpas_intf->hw_intf->hw_priv,
+			CAM_CPAS_HW_CMD_LOG_EVENT, &event,
+			sizeof(event));
+		if (rc)
+			CAM_ERR(CAM_CPAS, "Failed in process_cmd, rc=%d", rc);
+	} else {
+		CAM_ERR(CAM_CPAS, "Invalid process_cmd ops");
+		rc = -EBADR;
+	}
+
+	return rc;
+}
+EXPORT_SYMBOL(cam_cpas_notify_event);
+
 int cam_cpas_unregister_client(uint32_t client_handle)
 int cam_cpas_unregister_client(uint32_t client_handle)
 {
 {
 	int rc;
 	int rc;

+ 34 - 0
drivers/cam_cpas/cam_cpas_soc.c

@@ -834,6 +834,40 @@ int cam_cpas_get_custom_dt_info(struct cam_hw_info *cpas_hw,
 		goto cleanup_tree;
 		goto cleanup_tree;
 	}
 	}
 
 
+	/* Optional rpmh bcm info */
+	count = of_property_count_u32_elems(of_node, "rpmh-bcm-info");
+	/*
+	 * We expect count=5(CAM_RPMH_BCM_INFO_MAX) if valid rpmh bcm info
+	 * is available.
+	 * 0 - Total number of BCMs
+	 * 1 - First BCM FE (front-end) register offset.
+	 *     These represent requested clk plan by sw
+	 * 2 - First BCM BE (back-end) register offset.
+	 *     These represent actual clk plan at hw
+	 * 3 - DDR BCM index
+	 * 4 - MMNOC BCM index
+	 */
+	if (count == CAM_RPMH_BCM_INFO_MAX) {
+		for (i = 0; i < count; i++) {
+			rc = of_property_read_u32_index(of_node,
+				"rpmh-bcm-info", i, &soc_private->rpmh_info[i]);
+			if (rc) {
+				CAM_ERR(CAM_CPAS,
+					"Incorrect rpmh info at %d, count=%d",
+					i, count);
+				break;
+			}
+			CAM_DBG(CAM_CPAS, "RPMH BCM Info [%d]=0x%x",
+				i, soc_private->rpmh_info[i]);
+		}
+
+		if (rc)
+			soc_private->rpmh_info[CAM_RPMH_NUMBER_OF_BCMS] = 0;
+	} else {
+		CAM_DBG(CAM_CPAS, "RPMH BCM info not available in DT, count=%d",
+			count);
+	}
+
 	return 0;
 	return 0;
 
 
 cleanup_tree:
 cleanup_tree:

+ 2 - 1
drivers/cam_cpas/cam_cpas_soc.h

@@ -92,7 +92,7 @@ struct cam_cpas_tree_node {
  * @camnoc_axi_min_ib_bw: Min camnoc BW which varies based on target
  * @camnoc_axi_min_ib_bw: Min camnoc BW which varies based on target
  * @feature_mask: feature mask value for hw supported features
  * @feature_mask: feature mask value for hw supported features
  * @fuse_info: fuse information
  * @fuse_info: fuse information
- *
+ * @rpmh_info: RPMH BCM info
  */
  */
 struct cam_cpas_private_soc {
 struct cam_cpas_private_soc {
 	const char *arch_compat;
 	const char *arch_compat;
@@ -111,6 +111,7 @@ struct cam_cpas_private_soc {
 	uint64_t camnoc_axi_min_ib_bw;
 	uint64_t camnoc_axi_min_ib_bw;
 	uint32_t feature_mask;
 	uint32_t feature_mask;
 	struct cam_cpas_fuse_info fuse_info;
 	struct cam_cpas_fuse_info fuse_info;
+	uint32_t rpmh_info[CAM_RPMH_BCM_INFO_MAX];
 };
 };
 
 
 void cam_cpas_util_debug_parse_data(struct cam_cpas_private_soc *soc_private);
 void cam_cpas_util_debug_parse_data(struct cam_cpas_private_soc *soc_private);

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

@@ -314,6 +314,19 @@ static int cam_cpastop_setup_regbase_indices(struct cam_hw_soc_info *soc_info,
 		return -EINVAL;
 		return -EINVAL;
 	}
 	}
 
 
+	/* optional - rpmh register map */
+	rc = cam_common_util_get_string_index(soc_info->mem_block_name,
+		soc_info->num_mem_block, "cam_rpmh", &index);
+	if ((rc == 0) && (index < num_reg_map)) {
+		regbase_index[CAM_CPAS_REG_RPMH] = index;
+		CAM_DBG(CAM_CPAS, "regbase found for RPMH, rc=%d, %d %d",
+			rc, index, num_reg_map);
+	} else {
+		CAM_DBG(CAM_CPAS, "regbase not found for RPMH, rc=%d, %d %d",
+			rc, index, num_reg_map);
+		regbase_index[CAM_CPAS_REG_RPMH] = -1;
+	}
+
 	return 0;
 	return 0;
 }
 }
 
 

+ 20 - 0
drivers/cam_cpas/include/cam_cpas_api.h

@@ -33,6 +33,7 @@ enum cam_cpas_reg_base {
 	CAM_CPAS_REG_CPASTOP,
 	CAM_CPAS_REG_CPASTOP,
 	CAM_CPAS_REG_CAMNOC,
 	CAM_CPAS_REG_CAMNOC,
 	CAM_CPAS_REG_CAMSS,
 	CAM_CPAS_REG_CAMSS,
+	CAM_CPAS_REG_RPMH,
 	CAM_CPAS_REG_MAX
 	CAM_CPAS_REG_MAX
 };
 };
 
 
@@ -660,4 +661,23 @@ void cam_cpas_log_votes(void);
  */
  */
 int cam_cpas_select_qos_settings(uint32_t selection_mask);
 int cam_cpas_select_qos_settings(uint32_t selection_mask);
 
 
+/**
+ * cam_cpas_notify_event()
+ *
+ * @brief: API that clients can notify about their events. CPAS save the event
+ *         and any other useful information related to this event. This will
+ *         be printed while cpas state dump - cam_cpas_log_votes.
+ *         One such example is IFE notifiying SOF or EPOCH to cpas and cpas
+ *         saving axi clock information (camnoc_axi, mnoc_hf) at that point
+ *         and printing latest history on IFE overflow.
+ *
+ * @identifier_string: Identifier string passed by caller
+ * @identifier_value: Identifier value passed by caller
+ *
+ * @return 0 on success.
+ *
+ */
+int cam_cpas_notify_event(const char *identifier_string,
+	int32_t identifier_value);
+
 #endif /* _CAM_CPAS_API_H_ */
 #endif /* _CAM_CPAS_API_H_ */

+ 29 - 25
drivers/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_top/cam_vfe_camif_lite_ver3.c

@@ -840,7 +840,7 @@ static void cam_vfe_camif_lite_print_status(uint32_t *status,
 	uint32_t violation_mask = 0x3F00, violation_status = 0;
 	uint32_t violation_mask = 0x3F00, violation_status = 0;
 	uint32_t bus_overflow_status = 0, status_0 = 0, status_2 = 0;
 	uint32_t bus_overflow_status = 0, status_0 = 0, status_2 = 0;
 	struct cam_vfe_soc_private *soc_private = NULL;
 	struct cam_vfe_soc_private *soc_private = NULL;
-	uint32_t val0, val1, val2;
+	uint32_t val0, val1, val2, val3, val4;
 
 
 	if (!status) {
 	if (!status) {
 		CAM_ERR(CAM_ISP, "Invalid params");
 		CAM_ERR(CAM_ISP, "Invalid params");
@@ -897,24 +897,12 @@ static void cam_vfe_camif_lite_print_status(uint32_t *status,
 
 
 		if (bus_overflow_status & 0x02000000)
 		if (bus_overflow_status & 0x02000000)
 			CAM_INFO(CAM_ISP, "RDI2 BUS OVERFLOW");
 			CAM_INFO(CAM_ISP, "RDI2 BUS OVERFLOW");
-
-		cam_cpas_reg_read(soc_private->cpas_handle,
-			CAM_CPAS_REG_CAMNOC, 0xA20, true, &val0);
-		cam_cpas_reg_read(soc_private->cpas_handle,
-			CAM_CPAS_REG_CAMNOC, 0x1420, true, &val1);
-		cam_cpas_reg_read(soc_private->cpas_handle,
-			CAM_CPAS_REG_CAMNOC, 0x1A20, true, &val2);
-		CAM_INFO(CAM_ISP,
-			"CAMNOC REG ife_linear: 0x%X ife_rdi_wr: 0x%X ife_ubwc_stats: 0x%X",
-			val0, val1, val2);
-		cam_cpas_log_votes();
 	}
 	}
 
 
 	if (err_type == CAM_VFE_IRQ_STATUS_OVERFLOW && !bus_overflow_status) {
 	if (err_type == CAM_VFE_IRQ_STATUS_OVERFLOW && !bus_overflow_status) {
 		CAM_INFO(CAM_ISP, "PDLIB / LCR Module hang");
 		CAM_INFO(CAM_ISP, "PDLIB / LCR Module hang");
 		/* print debug registers */
 		/* print debug registers */
 		cam_vfe_camif_lite_overflow_debug_info(camif_lite_priv);
 		cam_vfe_camif_lite_overflow_debug_info(camif_lite_priv);
-		return;
 	}
 	}
 
 
 	if (err_type == CAM_VFE_IRQ_STATUS_VIOLATION) {
 	if (err_type == CAM_VFE_IRQ_STATUS_VIOLATION) {
@@ -952,7 +940,7 @@ static void cam_vfe_camif_lite_print_status(uint32_t *status,
 
 
 	}
 	}
 
 
-	return;
+	goto print_state;
 
 
 ife_lite:
 ife_lite:
 	if (err_type == CAM_VFE_IRQ_STATUS_OVERFLOW) {
 	if (err_type == CAM_VFE_IRQ_STATUS_OVERFLOW) {
@@ -993,23 +981,12 @@ ife_lite:
 
 
 		if (bus_overflow_status & 0x08)
 		if (bus_overflow_status & 0x08)
 			CAM_INFO(CAM_ISP, "RDI3 BUS OVERFLOW");
 			CAM_INFO(CAM_ISP, "RDI3 BUS OVERFLOW");
-
-		cam_cpas_reg_read(soc_private->cpas_handle,
-			CAM_CPAS_REG_CAMNOC, 0xA20, true, &val0);
-		cam_cpas_reg_read(soc_private->cpas_handle,
-			CAM_CPAS_REG_CAMNOC, 0x1420, true, &val1);
-		cam_cpas_reg_read(soc_private->cpas_handle,
-			CAM_CPAS_REG_CAMNOC, 0x1A20, true, &val2);
-		CAM_INFO(CAM_ISP,
-			"CAMNOC REG ife_linear: 0x%X ife_rdi_wr: 0x%X ife_ubwc_stats: 0x%X",
-			val0, val1, val2);
 	}
 	}
 
 
 	if (err_type == CAM_VFE_IRQ_STATUS_OVERFLOW && !bus_overflow_status) {
 	if (err_type == CAM_VFE_IRQ_STATUS_OVERFLOW && !bus_overflow_status) {
 		CAM_INFO(CAM_ISP, "RDI hang");
 		CAM_INFO(CAM_ISP, "RDI hang");
 		/* print debug registers */
 		/* print debug registers */
 		cam_vfe_camif_lite_overflow_debug_info(camif_lite_priv);
 		cam_vfe_camif_lite_overflow_debug_info(camif_lite_priv);
-		return;
 	}
 	}
 
 
 	if (err_type == CAM_VFE_IRQ_STATUS_VIOLATION) {
 	if (err_type == CAM_VFE_IRQ_STATUS_VIOLATION) {
@@ -1025,6 +1002,33 @@ ife_lite:
 		if (status_2 & 0x800)
 		if (status_2 & 0x800)
 			CAM_INFO(CAM_ISP, "RDI3 CAMIF VIOLATION");
 			CAM_INFO(CAM_ISP, "RDI3 CAMIF VIOLATION");
 	}
 	}
+
+print_state:
+	cam_cpas_reg_read(soc_private->cpas_handle,
+		CAM_CPAS_REG_CAMNOC, 0xA20, true, &val0);
+	cam_cpas_reg_read(soc_private->cpas_handle,
+		CAM_CPAS_REG_CAMNOC, 0x1420, true, &val1);
+	cam_cpas_reg_read(soc_private->cpas_handle,
+		CAM_CPAS_REG_CAMNOC, 0x1A20, true, &val2);
+	cam_cpas_reg_read(soc_private->cpas_handle,
+		CAM_CPAS_REG_CAMNOC, 0x7620, true, &val3);
+	cam_cpas_reg_read(soc_private->cpas_handle,
+		CAM_CPAS_REG_CAMNOC, 0x7420, true, &val4);
+
+	CAM_INFO(CAM_ISP,
+		"CAMNOC REG[Queued Pending] linear[%d %d] rdi0_wr[%d %d] ubwc_stats0[%d %d] ubwc_stats1[%d %d] rdi1_wr[%d %d]",
+		(val0 & 0x7FF), (val0 & 0x7F0000) >> 16,
+		(val1 & 0x7FF), (val1 & 0x7F0000) >> 16,
+		(val2 & 0x7FF), (val2 & 0x7F0000) >> 16,
+		(val3 & 0x7FF), (val3 & 0x7F0000) >> 16,
+		(val4 & 0x7FF), (val4 & 0x7F0000) >> 16);
+
+	CAM_INFO(CAM_ISP, "ife_clk_src:%lld", soc_private->ife_clk_src);
+
+	if ((err_type == CAM_VFE_IRQ_STATUS_OVERFLOW) &&
+		bus_overflow_status)
+		cam_cpas_log_votes();
+
 }
 }
 
 
 static int cam_vfe_camif_lite_handle_irq_top_half(uint32_t evt_id,
 static int cam_vfe_camif_lite_handle_irq_top_half(uint32_t evt_id,

+ 35 - 16
drivers/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_top/cam_vfe_camif_ver3.c

@@ -915,7 +915,7 @@ static void cam_vfe_camif_ver3_print_status(uint32_t *status,
 	uint32_t violation_mask = 0x3F, module_id = 0;
 	uint32_t violation_mask = 0x3F, module_id = 0;
 	uint32_t bus_overflow_status = 0, status_0 = 0, status_2 = 0;
 	uint32_t bus_overflow_status = 0, status_0 = 0, status_2 = 0;
 	struct cam_vfe_soc_private *soc_private;
 	struct cam_vfe_soc_private *soc_private;
-	uint32_t val0, val1, val2;
+	uint32_t val0, val1, val2, val3, val4;
 
 
 	if (!status) {
 	if (!status) {
 		CAM_ERR(CAM_ISP, "Invalid params");
 		CAM_ERR(CAM_ISP, "Invalid params");
@@ -1000,26 +1000,14 @@ static void cam_vfe_camif_ver3_print_status(uint32_t *status,
 
 
 		if (bus_overflow_status & 0x0200000)
 		if (bus_overflow_status & 0x0200000)
 			CAM_INFO(CAM_ISP, "PDAF BUS OVERFLOW");
 			CAM_INFO(CAM_ISP, "PDAF BUS OVERFLOW");
-
-		soc_private = camif_priv->soc_info->soc_private;
-		cam_cpas_reg_read(soc_private->cpas_handle,
-			CAM_CPAS_REG_CAMNOC, 0xA20, true, &val0);
-		cam_cpas_reg_read(soc_private->cpas_handle,
-			CAM_CPAS_REG_CAMNOC, 0x1420, true, &val1);
-		cam_cpas_reg_read(soc_private->cpas_handle,
-			CAM_CPAS_REG_CAMNOC, 0x1A20, true, &val2);
-		CAM_INFO(CAM_ISP,
-			"CAMNOC REG ife_linear: 0x%X ife_rdi_wr: 0x%X ife_ubwc_stats: 0x%X",
-			val0, val1, val2);
-		cam_cpas_log_votes();
-		return;
+		goto print_state;
 	}
 	}
 
 
 	if (err_type == CAM_VFE_IRQ_STATUS_OVERFLOW && !bus_overflow_status) {
 	if (err_type == CAM_VFE_IRQ_STATUS_OVERFLOW && !bus_overflow_status) {
 		CAM_INFO(CAM_ISP, "PIXEL PIPE Module hang");
 		CAM_INFO(CAM_ISP, "PIXEL PIPE Module hang");
 		/* print debug registers */
 		/* print debug registers */
 		cam_vfe_camif_ver3_overflow_debug_info(camif_priv);
 		cam_vfe_camif_ver3_overflow_debug_info(camif_priv);
-		return;
+		goto print_state;
 	}
 	}
 
 
 	if (err_type == CAM_VFE_IRQ_STATUS_VIOLATION) {
 	if (err_type == CAM_VFE_IRQ_STATUS_VIOLATION) {
@@ -1243,6 +1231,35 @@ static void cam_vfe_camif_ver3_print_status(uint32_t *status,
 			break;
 			break;
 		}
 		}
 	}
 	}
+
+print_state:
+	soc_private = camif_priv->soc_info->soc_private;
+
+	cam_cpas_reg_read(soc_private->cpas_handle,
+		CAM_CPAS_REG_CAMNOC, 0xA20, true, &val0);
+	cam_cpas_reg_read(soc_private->cpas_handle,
+		CAM_CPAS_REG_CAMNOC, 0x1420, true, &val1);
+	cam_cpas_reg_read(soc_private->cpas_handle,
+		CAM_CPAS_REG_CAMNOC, 0x1A20, true, &val2);
+	cam_cpas_reg_read(soc_private->cpas_handle,
+		CAM_CPAS_REG_CAMNOC, 0x7620, true, &val3);
+	cam_cpas_reg_read(soc_private->cpas_handle,
+		CAM_CPAS_REG_CAMNOC, 0x7420, true, &val4);
+
+	CAM_INFO(CAM_ISP,
+		"CAMNOC REG[Queued Pending] linear[%d %d] rdi0_wr[%d %d] ubwc_stats0[%d %d] ubwc_stats1[%d %d] rdi1_wr[%d %d]",
+		(val0 & 0x7FF), (val0 & 0x7F0000) >> 16,
+		(val1 & 0x7FF), (val1 & 0x7F0000) >> 16,
+		(val2 & 0x7FF), (val2 & 0x7F0000) >> 16,
+		(val3 & 0x7FF), (val3 & 0x7F0000) >> 16,
+		(val4 & 0x7FF), (val4 & 0x7F0000) >> 16);
+
+	CAM_INFO(CAM_ISP, "ife_clk_src:%lld", soc_private->ife_clk_src);
+
+	if ((err_type == CAM_VFE_IRQ_STATUS_OVERFLOW) &&
+		((camif_priv->cam_common_cfg.input_mux_sel_pp & 0x3) ||
+		(bus_overflow_status)))
+		cam_cpas_log_votes();
 }
 }
 
 
 static int cam_vfe_camif_ver3_handle_irq_top_half(uint32_t evt_id,
 static int cam_vfe_camif_ver3_handle_irq_top_half(uint32_t evt_id,
@@ -1368,7 +1385,9 @@ static int cam_vfe_camif_ver3_handle_irq_bottom_half(void *handler_priv,
 				payload->ts.mono_time.tv_sec;
 				payload->ts.mono_time.tv_sec;
 			camif_priv->sof_ts.tv_usec =
 			camif_priv->sof_ts.tv_usec =
 				payload->ts.mono_time.tv_usec;
 				payload->ts.mono_time.tv_usec;
-			}
+		}
+
+		cam_cpas_notify_event("IFE SOF", evt_info.hw_idx);
 
 
 		if (camif_priv->event_cb)
 		if (camif_priv->event_cb)
 			camif_priv->event_cb(camif_priv->priv,
 			camif_priv->event_cb(camif_priv->priv,

+ 8 - 0
drivers/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_top/cam_vfe_top_ver3.c

@@ -106,6 +106,8 @@ static int cam_vfe_top_ver3_set_hw_clk_rate(
 	rc = cam_soc_util_set_src_clk_rate(soc_info, max_clk_rate);
 	rc = cam_soc_util_set_src_clk_rate(soc_info, max_clk_rate);
 
 
 	if (!rc) {
 	if (!rc) {
+		soc_private->ife_clk_src = max_clk_rate;
+
 		top_priv->hw_clk_rate = max_clk_rate;
 		top_priv->hw_clk_rate = max_clk_rate;
 		rc = cam_soc_util_get_clk_level(soc_info, max_clk_rate,
 		rc = cam_soc_util_get_clk_level(soc_info, max_clk_rate,
 			soc_info->src_clk_idx, &clk_lvl);
 			soc_info->src_clk_idx, &clk_lvl);
@@ -609,6 +611,8 @@ int cam_vfe_top_ver3_stop(void *device_priv,
 	struct cam_vfe_top_ver3_priv            *top_priv;
 	struct cam_vfe_top_ver3_priv            *top_priv;
 	struct cam_isp_resource_node            *mux_res;
 	struct cam_isp_resource_node            *mux_res;
 	struct cam_hw_info                      *hw_info = NULL;
 	struct cam_hw_info                      *hw_info = NULL;
+	struct cam_hw_soc_info                  *soc_info = NULL;
+	struct cam_vfe_soc_private              *soc_private = NULL;
 	int i, rc = 0;
 	int i, rc = 0;
 
 
 	if (!device_priv || !stop_args) {
 	if (!device_priv || !stop_args) {
@@ -619,6 +623,8 @@ int cam_vfe_top_ver3_stop(void *device_priv,
 	top_priv = (struct cam_vfe_top_ver3_priv   *)device_priv;
 	top_priv = (struct cam_vfe_top_ver3_priv   *)device_priv;
 	mux_res = (struct cam_isp_resource_node *)stop_args;
 	mux_res = (struct cam_isp_resource_node *)stop_args;
 	hw_info = (struct cam_hw_info  *)mux_res->hw_intf->hw_priv;
 	hw_info = (struct cam_hw_info  *)mux_res->hw_intf->hw_priv;
+	soc_info = top_priv->common_data.soc_info;
+	soc_private = soc_info->soc_private;
 
 
 	if (mux_res->res_id < CAM_ISP_HW_VFE_IN_MAX) {
 	if (mux_res->res_id < CAM_ISP_HW_VFE_IN_MAX) {
 		rc = mux_res->stop(mux_res);
 		rc = mux_res->stop(mux_res);
@@ -641,6 +647,8 @@ int cam_vfe_top_ver3_stop(void *device_priv,
 		}
 		}
 	}
 	}
 
 
+	soc_private->ife_clk_src = 0;
+
 	return rc;
 	return rc;
 }
 }
 
 

+ 39 - 0
drivers/cam_utils/cam_soc_util.c

@@ -2488,3 +2488,42 @@ int cam_soc_util_reg_dump_to_cmd_buf(void *ctx,
 end:
 end:
 	return rc;
 	return rc;
 }
 }
+
+/**
+ * cam_soc_util_print_clk_freq()
+ *
+ * @brief:              This function gets the clk rates for each clk from clk
+ *                      driver and prints in log
+ *
+ * @soc_info:           Device soc struct to be populated
+ *
+ * @return:             success or failure
+ */
+int cam_soc_util_print_clk_freq(struct cam_hw_soc_info *soc_info)
+{
+	int i;
+	unsigned long clk_rate = 0;
+
+	if (!soc_info) {
+		CAM_ERR(CAM_UTIL, "Invalid soc info");
+		return -EINVAL;
+	}
+
+	if ((soc_info->num_clk == 0) ||
+		(soc_info->num_clk >= CAM_SOC_MAX_CLK)) {
+		CAM_ERR(CAM_UTIL, "[%s] Invalid number of clock %d",
+			soc_info->dev_name, soc_info->num_clk);
+		return -EINVAL;
+	}
+
+	for (i = 0; i < soc_info->num_clk; i++) {
+		clk_rate = clk_get_rate(soc_info->clk[i]);
+
+		CAM_INFO(CAM_UTIL,
+			"[%s] idx = %d clk name = %s clk_rate=%lld",
+			soc_info->dev_name, i, soc_info->clk_name[i],
+			clk_rate);
+	}
+
+	return 0;
+}

+ 12 - 0
drivers/cam_utils/cam_soc_util.h

@@ -687,4 +687,16 @@ int cam_soc_util_reg_dump_to_cmd_buf(void *ctx,
 	struct cam_hw_soc_dump_args *soc_dump_args,
 	struct cam_hw_soc_dump_args *soc_dump_args,
 	bool user_triggered_dump);
 	bool user_triggered_dump);
 
 
+/**
+ * cam_soc_util_print_clk_freq()
+ *
+ * @brief:              This function gets the clk rates for each clk from clk
+ *                      driver and prints in log
+ *
+ * @soc_info:           Device soc struct to be populated
+ *
+ * @return:             success or failure
+ */
+int cam_soc_util_print_clk_freq(struct cam_hw_soc_info *soc_info);
+
 #endif /* _CAM_SOC_UTIL_H_ */
 #endif /* _CAM_SOC_UTIL_H_ */