Loading drivers/cam_cpas/cam_cpas_hw.c +257 −2 Original line number Diff line number Diff line Loading @@ -19,6 +19,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) Loading Loading @@ -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], "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); Loading Loading @@ -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_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, Loading Loading @@ -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]", 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, Loading Loading @@ -1957,6 +2188,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; Loading Loading @@ -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, 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!"); Loading Loading @@ -2102,6 +2353,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); Loading drivers/cam_cpas/cam_cpas_hw.h +69 −0 Original line number Diff line number Diff line Loading @@ -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 */ Loading Loading @@ -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 * Loading @@ -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; Loading @@ -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); Loading drivers/cam_cpas/cam_cpas_hw_intf.h +13 −0 Original line number Diff line number Diff line Loading @@ -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, }; Loading Loading @@ -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 * Loading drivers/cam_cpas/cam_cpas_intf.c +31 −0 Original line number Diff line number Diff line Loading @@ -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; Loading drivers/cam_cpas/cam_cpas_soc.c +34 −0 Original line number Diff line number Diff line Loading @@ -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 Loading
drivers/cam_cpas/cam_cpas_hw.c +257 −2 Original line number Diff line number Diff line Loading @@ -19,6 +19,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) Loading Loading @@ -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], "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); Loading Loading @@ -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_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, Loading Loading @@ -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]", 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, Loading Loading @@ -1957,6 +2188,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; Loading Loading @@ -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, 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!"); Loading Loading @@ -2102,6 +2353,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); Loading
drivers/cam_cpas/cam_cpas_hw.h +69 −0 Original line number Diff line number Diff line Loading @@ -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 */ Loading Loading @@ -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 * Loading @@ -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; Loading @@ -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); Loading
drivers/cam_cpas/cam_cpas_hw_intf.h +13 −0 Original line number Diff line number Diff line Loading @@ -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, }; Loading Loading @@ -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 * Loading
drivers/cam_cpas/cam_cpas_intf.c +31 −0 Original line number Diff line number Diff line Loading @@ -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; Loading
drivers/cam_cpas/cam_cpas_soc.c +34 −0 Original line number Diff line number Diff line Loading @@ -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