diff --git a/drivers/cam_cpas/cam_cpas_hw.c b/drivers/cam_cpas/cam_cpas_hw.c index d8f18a8220..6d3f0e2b96 100644 --- a/drivers/cam_cpas/cam_cpas_hw.c +++ b/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); diff --git a/drivers/cam_cpas/cam_cpas_hw.h b/drivers/cam_cpas/cam_cpas_hw.h index 4d76b80fa7..3301cdbff8 100644 --- a/drivers/cam_cpas/cam_cpas_hw.h +++ b/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); diff --git a/drivers/cam_cpas/cam_cpas_hw_intf.h b/drivers/cam_cpas/cam_cpas_hw_intf.h index c1e81f2a76..fc9297ac69 100644 --- a/drivers/cam_cpas/cam_cpas_hw_intf.h +++ b/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 * diff --git a/drivers/cam_cpas/cam_cpas_intf.c b/drivers/cam_cpas/cam_cpas_intf.c index d75c51488e..a3ae586186 100644 --- a/drivers/cam_cpas/cam_cpas_intf.c +++ b/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; diff --git a/drivers/cam_cpas/cam_cpas_soc.c b/drivers/cam_cpas/cam_cpas_soc.c index f569078ac8..b466408509 100644 --- a/drivers/cam_cpas/cam_cpas_soc.c +++ b/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: diff --git a/drivers/cam_cpas/cam_cpas_soc.h b/drivers/cam_cpas/cam_cpas_soc.h index 71d11bdfc3..50d94549ba 100644 --- a/drivers/cam_cpas/cam_cpas_soc.h +++ b/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); diff --git a/drivers/cam_cpas/cpas_top/cam_cpastop_hw.c b/drivers/cam_cpas/cpas_top/cam_cpastop_hw.c index ea1a076ca7..f07e16d7f4 100644 --- a/drivers/cam_cpas/cpas_top/cam_cpastop_hw.c +++ b/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; } diff --git a/drivers/cam_cpas/include/cam_cpas_api.h b/drivers/cam_cpas/include/cam_cpas_api.h index 1c248060bb..4c895ec391 100644 --- a/drivers/cam_cpas/include/cam_cpas_api.h +++ b/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_ */ diff --git a/drivers/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_top/cam_vfe_camif_lite_ver3.c b/drivers/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_top/cam_vfe_camif_lite_ver3.c index b54c43a145..1cc89c3201 100644 --- a/drivers/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_top/cam_vfe_camif_lite_ver3.c +++ b/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, diff --git a/drivers/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_top/cam_vfe_camif_ver3.c b/drivers/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_top/cam_vfe_camif_ver3.c index 27fdde7718..67cd7f4cd5 100644 --- a/drivers/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_top/cam_vfe_camif_ver3.c +++ b/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, diff --git a/drivers/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_top/cam_vfe_top_ver3.c b/drivers/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_top/cam_vfe_top_ver3.c index 6a66955ed2..f3a8217dbe 100644 --- a/drivers/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_top/cam_vfe_top_ver3.c +++ b/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; } diff --git a/drivers/cam_utils/cam_soc_util.c b/drivers/cam_utils/cam_soc_util.c index 764002d2ac..80611f9bff 100644 --- a/drivers/cam_utils/cam_soc_util.c +++ b/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; +} diff --git a/drivers/cam_utils/cam_soc_util.h b/drivers/cam_utils/cam_soc_util.h index a71820a075..04e0e91368 100644 --- a/drivers/cam_utils/cam_soc_util.h +++ b/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_ */