Răsfoiți Sursa

msm: camera: cpas: Add more debug information in cpas dump

Enhance cpas state dump to get more information about
bandwidth and clock status. This additional state dump
can be enabled using debugfs.

adb shell "echo 1 > /sys/kernel/debug/camera_cpas/full_state_dump"

Traverse through all bw tree nodes and print info in each node.
Print current clk frequencies of all clocks that cpas enables.
Read rpmh bcm status registers to understand mmnoc clk freq.
Add cpas monitor to save important info whenever clients
notify with an event. This monitor info is printed in cpas
state dump.

CRs-Fixed: 2754299
Change-Id: Ib9007091f7e34127f1ca92498e2537b2a06887cb
Signed-off-by: Pavan Kumar Chilamkurthi <[email protected]>
Pavan Kumar Chilamkurthi 4 ani în urmă
părinte
comite
f4ec43b748

+ 257 - 2
drivers/cam_cpas/cam_cpas_hw.c

@@ -18,6 +18,11 @@
 static uint cam_min_camnoc_ib_bw;
 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(
 	struct cam_cpas_bus_client *bus_client, uint64_t *ab, uint64_t *ib,
 	const struct cam_cpas_debug_settings *cpas_settings)
@@ -1067,9 +1072,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],
 		"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,
 		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:
 	mutex_unlock(&cpas_core->client_mutex[client_indx]);
 	mutex_unlock(&cpas_hw->hw_mutex);
@@ -1782,8 +1794,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_private_soc *soc_private =
 		(struct cam_cpas_private_soc *) cpas_hw->soc_info.soc_private;
-	int rc = 0;
 	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++) {
 		CAM_INFO(CAM_CPAS,
@@ -1815,7 +1871,182 @@ static int cam_cpas_log_vote(struct cam_hw_info *cpas_hw)
 	CAM_INFO(CAM_CPAS, "ahb client curr vote level[%d]",
 		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,
@@ -1954,6 +2185,22 @@ static int cam_cpas_hw_process_cmd(void *hw_priv,
 		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: {
 		uint32_t *selection_mask;
 
@@ -2049,6 +2296,10 @@ static int cam_cpas_util_create_debugfs(struct cam_cpas *cpas_core)
 
 	dbgfileptr = debugfs_create_bool("ahb_bus_scaling_disable", 0644,
 		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 (PTR_ERR(dbgfileptr) == -ENODEV)
 			CAM_WARN(CAM_CPAS, "DebugFS not enabled in kernel!");
@@ -2099,6 +2350,10 @@ int cam_cpas_hw_probe(struct platform_device *pdev,
 	cpas_hw->soc_info.dev_name = pdev->name;
 	cpas_hw->open_count = 0;
 	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);
 	spin_lock_init(&cpas_hw->hw_lock);
 	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)) && \
 	(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
  */
@@ -163,6 +176,56 @@ struct cam_cpas_axi_port {
 	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
  *
@@ -186,6 +249,9 @@ struct cam_cpas_axi_port {
  * @dentry: debugfs file entry
  * @ahb_bus_scaling_disable: ahb scaling based on src clk corner for bus
  * @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_hw_caps hw_caps;
@@ -208,6 +274,9 @@ struct cam_cpas {
 	struct dentry *dentry;
 	bool ahb_bus_scaling_disable;
 	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);

+ 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_LOG_VOTE,
 	CAM_CPAS_HW_CMD_SELECT_QOS,
+	CAM_CPAS_HW_CMD_LOG_EVENT,
 	CAM_CPAS_HW_CMD_INVALID,
 };
 
@@ -107,6 +108,18 @@ struct cam_cpas_hw_cmd_stop {
 	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
  *

+ 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);
 
+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 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;
 	}
 
+	/* 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;
 
 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
  * @feature_mask: feature mask value for hw supported features
  * @fuse_info: fuse information
- *
+ * @rpmh_info: RPMH BCM info
  */
 struct cam_cpas_private_soc {
 	const char *arch_compat;
@@ -111,6 +111,7 @@ struct cam_cpas_private_soc {
 	uint64_t camnoc_axi_min_ib_bw;
 	uint32_t feature_mask;
 	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);

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

+ 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_CAMNOC,
 	CAM_CPAS_REG_CAMSS,
+	CAM_CPAS_REG_RPMH,
 	CAM_CPAS_REG_MAX
 };
 
@@ -660,4 +661,23 @@ void cam_cpas_log_votes(void);
  */
 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_ */

+ 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 bus_overflow_status = 0, status_0 = 0, status_2 = 0;
 	struct cam_vfe_soc_private *soc_private = NULL;
-	uint32_t val0, val1, val2;
+	uint32_t val0, val1, val2, val3, val4;
 
 	if (!status) {
 		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)
 			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) {
 		CAM_INFO(CAM_ISP, "PDLIB / LCR Module hang");
 		/* print debug registers */
 		cam_vfe_camif_lite_overflow_debug_info(camif_lite_priv);
-		return;
 	}
 
 	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:
 	if (err_type == CAM_VFE_IRQ_STATUS_OVERFLOW) {
@@ -993,23 +981,12 @@ ife_lite:
 
 		if (bus_overflow_status & 0x08)
 			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) {
 		CAM_INFO(CAM_ISP, "RDI hang");
 		/* print debug registers */
 		cam_vfe_camif_lite_overflow_debug_info(camif_lite_priv);
-		return;
 	}
 
 	if (err_type == CAM_VFE_IRQ_STATUS_VIOLATION) {
@@ -1025,6 +1002,33 @@ ife_lite:
 		if (status_2 & 0x800)
 			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,

+ 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 bus_overflow_status = 0, status_0 = 0, status_2 = 0;
 	struct cam_vfe_soc_private *soc_private;
-	uint32_t val0, val1, val2;
+	uint32_t val0, val1, val2, val3, val4;
 
 	if (!status) {
 		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)
 			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) {
 		CAM_INFO(CAM_ISP, "PIXEL PIPE Module hang");
 		/* print debug registers */
 		cam_vfe_camif_ver3_overflow_debug_info(camif_priv);
-		return;
+		goto print_state;
 	}
 
 	if (err_type == CAM_VFE_IRQ_STATUS_VIOLATION) {
@@ -1243,6 +1231,35 @@ static void cam_vfe_camif_ver3_print_status(uint32_t *status,
 			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,
@@ -1368,7 +1385,9 @@ static int cam_vfe_camif_ver3_handle_irq_bottom_half(void *handler_priv,
 				payload->ts.mono_time.tv_sec;
 			camif_priv->sof_ts.tv_usec =
 				payload->ts.mono_time.tv_usec;
-			}
+		}
+
+		cam_cpas_notify_event("IFE SOF", evt_info.hw_idx);
 
 		if (camif_priv->event_cb)
 			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);
 
 	if (!rc) {
+		soc_private->ife_clk_src = max_clk_rate;
+
 		top_priv->hw_clk_rate = max_clk_rate;
 		rc = cam_soc_util_get_clk_level(soc_info, max_clk_rate,
 			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_isp_resource_node            *mux_res;
 	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;
 
 	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;
 	mux_res = (struct cam_isp_resource_node *)stop_args;
 	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) {
 		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;
 }
 

+ 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:
 	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,
 	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_ */