Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit f4ec43b7 authored by Pavan Kumar Chilamkurthi's avatar Pavan Kumar Chilamkurthi
Browse files

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: default avatarPavan Kumar Chilamkurthi <pchilamk@codeaurora.org>
parent 80e8ff8c
Loading
Loading
Loading
Loading
+257 −2
Original line number Diff line number Diff line
@@ -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
Original line number Diff line number Diff line
@@ -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
Original line number Diff line number Diff line
@@ -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
Original line number Diff line number Diff line
@@ -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
Original line number Diff line number Diff line
@@ -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:
Loading