From 8a40a542fd3aebc307b9cdf959626f45a107ac38 Mon Sep 17 00:00:00 2001 From: Jigarkumar Zala Date: Fri, 25 Oct 2019 11:23:12 -0700 Subject: [PATCH] msm: camera: common: Add support for icc and switching bus scaling Add support for disabling bus scaling by moving bus driver references from cpas to cam utils. The references can be switched based on compile time flags in defconfig. Also, add interconnect support which has bw voting based on source and destination bus IDs. CRs-Fixed: 2525881 Change-Id: I2a78523c0cad41503fe32aeb7bf4c794a26b16b6 Signed-off-by: Mukund Madhusudan Atre Signed-off-by: Jigarkumar Zala --- drivers/Makefile | 8 ++ drivers/cam_cpas/cam_cpas_hw.c | 175 ++++++++---------------- drivers/cam_cpas/cam_cpas_hw.h | 31 ++--- drivers/cam_cpas/cam_cpas_soc.c | 198 +++++++++++++++++++++++++-- drivers/cam_cpas/cam_cpas_soc.h | 2 + drivers/cam_utils/cam_soc_bus.c | 232 ++++++++++++++++++++++++++++++++ drivers/cam_utils/cam_soc_bus.h | 96 +++++++++++++ drivers/cam_utils/cam_soc_icc.c | 149 ++++++++++++++++++++ 8 files changed, 737 insertions(+), 154 deletions(-) create mode 100644 drivers/cam_utils/cam_soc_bus.c create mode 100644 drivers/cam_utils/cam_soc_bus.h create mode 100644 drivers/cam_utils/cam_soc_icc.c diff --git a/drivers/Makefile b/drivers/Makefile index a59a49ce1be5..01e4e572c8bf 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -49,6 +49,14 @@ ifdef CONFIG_QCOM_CX_IPEAK camera-y += cam_utils/cam_cx_ipeak.o endif +ifneq (,$(filter $(CONFIG_QCOM_BUS_SCALING),y m)) +camera-y += cam_utils/cam_soc_bus.o +endif + +ifneq (,$(filter $(CONFIG_INTERCONNECT_QCOM),y m)) +camera-y += cam_utils/cam_soc_icc.o +endif + camera-$(CONFIG_SPECTRA_ISP) += \ cam_isp/isp_hw_mgr/hw_utils/cam_tasklet_util.o \ cam_isp/isp_hw_mgr/hw_utils/cam_isp_packet_parser.o \ diff --git a/drivers/cam_cpas/cam_cpas_hw.c b/drivers/cam_cpas/cam_cpas_hw.c index ae0368acdb05..061d2594421a 100644 --- a/drivers/cam_cpas/cam_cpas_hw.c +++ b/drivers/cam_cpas/cam_cpas_hw.c @@ -6,7 +6,6 @@ #include #include #include -#include #include #include #include @@ -56,73 +55,54 @@ int cam_cpas_util_reg_update(struct cam_hw_info *cpas_hw, static int cam_cpas_util_vote_bus_client_level( struct cam_cpas_bus_client *bus_client, unsigned int level) { - if (!bus_client->valid || (bus_client->dyn_vote == true)) { - CAM_ERR(CAM_CPAS, "Invalid params %d %d", bus_client->valid, - bus_client->dyn_vote); - return -EINVAL; - } + int rc = 0; - if (level >= bus_client->num_usecases) { - CAM_ERR(CAM_CPAS, "Invalid vote level=%d, usecases=%d", level, - bus_client->num_usecases); - return -EINVAL; + if (!bus_client->valid) { + CAM_ERR(CAM_CPAS, "bus client not valid"); + rc = -EINVAL; + goto end; } if (level == bus_client->curr_vote_level) - return 0; + goto end; - CAM_DBG(CAM_CPAS, "Bus client=[%d][%s] index[%d]", - bus_client->client_id, bus_client->name, level); - msm_bus_scale_client_update_request(bus_client->client_id, level); + rc = cam_soc_bus_client_update_request(bus_client->soc_bus_client, + level); + if (rc) { + CAM_ERR(CAM_CPAS, "Client: %s update request failed rc: %d", + bus_client->common_data.name, rc); + goto end; + } bus_client->curr_vote_level = level; - return 0; +end: + return rc; } static int cam_cpas_util_vote_bus_client_bw( struct cam_cpas_bus_client *bus_client, uint64_t ab, uint64_t ib, bool camnoc_bw) { - struct msm_bus_paths *path; - struct msm_bus_scale_pdata *pdata; - int idx = 0; + int rc = 0; uint64_t min_camnoc_ib_bw = CAM_CPAS_AXI_MIN_CAMNOC_IB_BW; + if (!bus_client->valid) { + CAM_ERR(CAM_CPAS, "bus client: %s not valid", + bus_client->common_data.name); + rc = -EINVAL; + goto end; + } + if (cam_min_camnoc_ib_bw > 0) min_camnoc_ib_bw = (uint64_t)cam_min_camnoc_ib_bw * 1000000L; - CAM_DBG(CAM_CPAS, "cam_min_camnoc_ib_bw = %d, min_camnoc_ib_bw=%llu", - cam_min_camnoc_ib_bw, min_camnoc_ib_bw); - - if (!bus_client->valid) { - CAM_ERR(CAM_CPAS, "bus client not valid"); - return -EINVAL; - } - - if ((bus_client->num_usecases != 2) || - (bus_client->num_paths != 1) || - (bus_client->dyn_vote != true)) { - CAM_ERR(CAM_CPAS, "dynamic update not allowed %d %d %d", - bus_client->num_usecases, bus_client->num_paths, - bus_client->dyn_vote); - return -EINVAL; - } + CAM_DBG(CAM_CPAS, + "Bus_client: %s, cam_min_camnoc_ib_bw = %d, min_camnoc_ib_bw=%llu", + bus_client->common_data.name, cam_min_camnoc_ib_bw, + min_camnoc_ib_bw); mutex_lock(&bus_client->lock); - - if (bus_client->curr_vote_level > 1) { - CAM_ERR(CAM_CPAS, "curr_vote_level %d cannot be greater than 1", - bus_client->curr_vote_level); - mutex_unlock(&bus_client->lock); - return -EINVAL; - } - - idx = bus_client->curr_vote_level; - idx = 1 - idx; - bus_client->curr_vote_level = idx; - mutex_unlock(&bus_client->lock); - - if (camnoc_bw == true) { + if (camnoc_bw) { if ((ab > 0) && (ab < CAM_CPAS_AXI_MIN_CAMNOC_AB_BW)) ab = CAM_CPAS_AXI_MIN_CAMNOC_AB_BW; @@ -136,96 +116,51 @@ static int cam_cpas_util_vote_bus_client_bw( ib = CAM_CPAS_AXI_MIN_MNOC_IB_BW; } - pdata = bus_client->pdata; - path = &(pdata->usecase[idx]); - path->vectors[0].ab = ab; - path->vectors[0].ib = ib; + rc = cam_soc_bus_client_update_bw(bus_client->soc_bus_client, ab, ib); + if (rc) { + CAM_ERR(CAM_CPAS, + "Update bw failed, ab[%llu] ib[%llu]", + ab, ib); + goto unlock_client; + } - CAM_DBG(CAM_CPAS, "Bus client=[%d][%s] :ab[%llu] ib[%llu], index[%d]", - bus_client->client_id, bus_client->name, ab, ib, idx); - msm_bus_scale_client_update_request(bus_client->client_id, idx); - - return 0; +unlock_client: + mutex_unlock(&bus_client->lock); +end: + return rc; } static int cam_cpas_util_register_bus_client( struct cam_hw_soc_info *soc_info, struct device_node *dev_node, struct cam_cpas_bus_client *bus_client) { - struct msm_bus_scale_pdata *pdata = NULL; - uint32_t client_id; - int rc; + int rc = 0; - pdata = msm_bus_pdata_from_node(soc_info->pdev, - dev_node); - if (!pdata) { - CAM_ERR(CAM_CPAS, "failed get_pdata"); - return -EINVAL; + rc = cam_soc_bus_client_register(soc_info->pdev, dev_node, + &bus_client->soc_bus_client, &bus_client->common_data); + if (rc) { + CAM_ERR(CAM_CPAS, "Bus client: %s registertion failed ,rc: %d", + bus_client->common_data.name, rc); + return rc; } - - if ((pdata->num_usecases == 0) || - (pdata->usecase[0].num_paths == 0)) { - CAM_ERR(CAM_CPAS, "usecase=%d", pdata->num_usecases); - rc = -EINVAL; - goto error; - } - - client_id = msm_bus_scale_register_client(pdata); - if (!client_id) { - CAM_ERR(CAM_CPAS, "failed in register ahb bus client"); - rc = -EINVAL; - goto error; - } - - bus_client->dyn_vote = of_property_read_bool(dev_node, - "qcom,msm-bus-vector-dyn-vote"); - - if (bus_client->dyn_vote && (pdata->num_usecases != 2)) { - CAM_ERR(CAM_CPAS, "Excess or less vectors %d", - pdata->num_usecases); - rc = -EINVAL; - goto fail_unregister_client; - } - - msm_bus_scale_client_update_request(client_id, 0); - - bus_client->src = pdata->usecase[0].vectors[0].src; - bus_client->dst = pdata->usecase[0].vectors[0].dst; - bus_client->pdata = pdata; - bus_client->client_id = client_id; - bus_client->num_usecases = pdata->num_usecases; - bus_client->num_paths = pdata->usecase[0].num_paths; bus_client->curr_vote_level = 0; bus_client->valid = true; - bus_client->name = pdata->name; mutex_init(&bus_client->lock); - CAM_DBG(CAM_CPAS, "Bus Client=[%d][%s] : src=%d, dst=%d", - bus_client->client_id, bus_client->name, - bus_client->src, bus_client->dst); - return 0; -fail_unregister_client: - msm_bus_scale_unregister_client(bus_client->client_id); -error: - return rc; - } static int cam_cpas_util_unregister_bus_client( struct cam_cpas_bus_client *bus_client) { - if (!bus_client->valid) + if (!bus_client->valid) { + CAM_ERR(CAM_CPAS, "bus client not valid"); return -EINVAL; + } - if (bus_client->dyn_vote) - cam_cpas_util_vote_bus_client_bw(bus_client, 0, 0, false); - else - cam_cpas_util_vote_bus_client_level(bus_client, 0); - - msm_bus_scale_unregister_client(bus_client->client_id); + cam_soc_bus_client_unregister(&bus_client->soc_bus_client); + bus_client->curr_vote_level = 0; bus_client->valid = false; - mutex_destroy(&bus_client->lock); return 0; @@ -765,7 +700,7 @@ vote_start_clients: continue; CAM_DBG(CAM_PERF, "Port[%s] : ab=%lld ib=%lld additional=%lld", - axi_port->axi_port_name, axi_port->ab_bw, + axi_port->bus_client.common_data.name, axi_port->ab_bw, axi_port->ib_bw, axi_port->additional_bw); if (axi_port->ab_bw) @@ -920,9 +855,9 @@ static int cam_cpas_util_apply_client_ahb_vote(struct cam_hw_info *cpas_hw, mutex_lock(&ahb_bus_client->lock); cpas_client->ahb_level = required_level; - CAM_DBG(CAM_CPAS, "Client=[%d][%s] required level[%d], curr_level[%d]", - ahb_bus_client->client_id, ahb_bus_client->name, - required_level, ahb_bus_client->curr_vote_level); + CAM_DBG(CAM_CPAS, "Client[%s] required level[%d], curr_level[%d]", + ahb_bus_client->common_data.name, required_level, + ahb_bus_client->curr_vote_level); if (required_level == ahb_bus_client->curr_vote_level) goto unlock_bus_client; diff --git a/drivers/cam_cpas/cam_cpas_hw.h b/drivers/cam_cpas/cam_cpas_hw.h index b3c01b3ee737..2b207a090a02 100644 --- a/drivers/cam_cpas/cam_cpas_hw.h +++ b/drivers/cam_cpas/cam_cpas_hw.h @@ -10,6 +10,7 @@ #include "cam_cpas_api.h" #include "cam_cpas_hw_intf.h" #include "cam_common_util.h" +#include "cam_soc_bus.h" #define CAM_CPAS_INFLIGHT_WORKS 5 #define CAM_CPAS_MAX_CLIENTS 40 @@ -117,37 +118,24 @@ struct cam_cpas_client { /** * struct cam_cpas_bus_client : Bus client information * - * @src: Bus master/src id - * @dst: Bus slave/dst id - * @pdata: Bus pdata information - * @client_id: Bus client id - * @num_usecases: Number of use cases for this client - * @num_paths: Number of paths for this client - * @curr_vote_level: current voted index - * @dyn_vote: Whether dynamic voting enabled - * @lock: Mutex lock used while voting on this client * @valid: Whether bus client is valid * @name: Name of the bus client - * + * @lock: Mutex lock used while voting on this client + * @curr_vote_level: current voted index + * @common_data: Common data fields for bus client + * @soc_bus_client: Bus client private information */ struct cam_cpas_bus_client { - int src; - int dst; - struct msm_bus_scale_pdata *pdata; - uint32_t client_id; - int num_usecases; - int num_paths; - unsigned int curr_vote_level; - bool dyn_vote; - struct mutex lock; bool valid; - const char *name; + struct mutex lock; + unsigned int curr_vote_level; + struct cam_soc_bus_client_common_data common_data; + void *soc_bus_client; }; /** * struct cam_cpas_axi_port : AXI port information * - * @axi_port_name: Name of this AXI port * @bus_client: bus client info for this port * @ib_bw_voting_needed: if this port can update ib bw dynamically * @axi_port_node: Node representing AXI Port info in device tree @@ -156,7 +144,6 @@ struct cam_cpas_bus_client { * @additional_bw: Additional bandwidth to cover non-hw cpas clients */ struct cam_cpas_axi_port { - const char *axi_port_name; struct cam_cpas_bus_client bus_client; bool ib_bw_voting_needed; struct device_node *axi_port_node; diff --git a/drivers/cam_cpas/cam_cpas_soc.c b/drivers/cam_cpas/cam_cpas_soc.c index c86753dd24d9..b8847626f0ff 100644 --- a/drivers/cam_cpas/cam_cpas_soc.c +++ b/drivers/cam_cpas/cam_cpas_soc.c @@ -163,7 +163,7 @@ static int cam_cpas_parse_node_tree(struct cam_cpas *cpas_core, rc = of_property_read_u32(level_node, "level-index", &level_idx); if (rc) { - CAM_ERR(CAM_CPAS, "Error raeding level idx rc: %d", rc); + CAM_ERR(CAM_CPAS, "Error reading level idx rc: %d", rc); return rc; } if (level_idx >= CAM_CPAS_MAX_TREE_LEVELS) { @@ -229,19 +229,82 @@ static int cam_cpas_parse_node_tree(struct cam_cpas *cpas_core, cpas_core->axi_port[mnoc_idx].axi_port_node = mnoc_node; - rc = of_property_read_string( - curr_node, "qcom,axi-port-name", - &cpas_core->axi_port[mnoc_idx] - .axi_port_name); - if (rc) { - CAM_ERR(CAM_CPAS, - "failed to read mnoc-port-name rc=%d", - rc); - return rc; + if (soc_private->bus_icc_based) { + struct of_phandle_args src_args = {0}, + dst_args = {0}; + + rc = of_property_read_string(mnoc_node, + "interconnect-names", + &cpas_core->axi_port[mnoc_idx] + .bus_client.common_data.name); + if (rc) { + CAM_ERR(CAM_CPAS, + "failed to read interconnect-names rc=%d", + rc); + return rc; + } + + rc = of_parse_phandle_with_args( + mnoc_node, "interconnects", + "#interconnect-cells", 0, + &src_args); + if (rc) { + CAM_ERR(CAM_CPAS, + "failed to read axi bus src info rc=%d", + rc); + return -EINVAL; + } + + of_node_put(src_args.np); + if (src_args.args_count != 1) { + CAM_ERR(CAM_CPAS, + "Invalid number of axi src args: %d", + src_args.args_count); + return -EINVAL; + } + + cpas_core->axi_port[mnoc_idx].bus_client + .common_data.src_id = src_args.args[0]; + + rc = of_parse_phandle_with_args( + mnoc_node, "interconnects", + "#interconnect-cells", 1, + &dst_args); + if (rc) { + CAM_ERR(CAM_CPAS, + "failed to read axi bus dst info rc=%d", + rc); + return -EINVAL; + } + + of_node_put(dst_args.np); + if (dst_args.args_count != 1) { + CAM_ERR(CAM_CPAS, + "Invalid number of axi dst args: %d", + dst_args.args_count); + return -EINVAL; + } + + cpas_core->axi_port[mnoc_idx].bus_client + .common_data.dst_id = dst_args.args[0]; + cpas_core->axi_port[mnoc_idx].bus_client + .common_data.num_usecases = 2; + } else { + rc = of_property_read_string( + curr_node, "qcom,axi-port-name", + &cpas_core->axi_port[mnoc_idx] + .bus_client.common_data.name); + if (rc) { + CAM_ERR(CAM_CPAS, + "failed to read mnoc-port-name rc=%d", + rc); + return rc; + } } + cpas_core->axi_port [mnoc_idx].ib_bw_voting_needed - = of_property_read_bool(curr_node, + = of_property_read_bool(curr_node, "ib-bw-voting-needed"); curr_node_ptr->axi_port_idx = mnoc_idx; mnoc_idx++; @@ -392,7 +455,8 @@ int cam_cpas_get_custom_dt_info(struct cam_hw_info *cpas_hw, struct platform_device *pdev, struct cam_cpas_private_soc *soc_private) { struct device_node *of_node; - int count = 0, i = 0, rc = 0; + struct of_phandle_args src_args = {0}, dst_args = {0}; + int count = 0, i = 0, rc = 0, num_bw_values = 0, num_levels = 0; struct cam_cpas *cpas_core = (struct cam_cpas *) cpas_hw->core_info; if (!soc_private || !pdev) { @@ -437,6 +501,116 @@ int cam_cpas_get_custom_dt_info(struct cam_hw_info *cpas_hw, soc_private->client_id_based = of_property_read_bool(of_node, "client-id-based"); + soc_private->bus_icc_based = of_property_read_bool(of_node, + "interconnects"); + + if (soc_private->bus_icc_based) { + rc = of_property_read_string(of_node, "interconnect-names", + &cpas_core->ahb_bus_client.common_data.name); + if (rc) { + CAM_ERR(CAM_CPAS, + "device %s failed to read interconnect-names", + pdev->name); + return rc; + } + + rc = of_parse_phandle_with_args(of_node, "interconnects", + "#interconnect-cells", 0, &src_args); + if (rc) { + CAM_ERR(CAM_CPAS, + "device %s failed to read ahb bus src info", + pdev->name); + return rc; + } + + of_node_put(src_args.np); + if (src_args.args_count != 1) { + CAM_ERR(CAM_CPAS, + "Invalid number of ahb src args: %d", + src_args.args_count); + return -EINVAL; + } + + cpas_core->ahb_bus_client.common_data.src_id = src_args.args[0]; + + rc = of_parse_phandle_with_args(of_node, "interconnects", + "#interconnect-cells", 1, &dst_args); + if (rc) { + CAM_ERR(CAM_CPAS, + "device %s failed to read ahb bus dst info", + pdev->name); + return rc; + } + + of_node_put(dst_args.np); + if (dst_args.args_count != 1) { + CAM_ERR(CAM_CPAS, + "Invalid number of ahb dst args: %d", + dst_args.args_count); + return -EINVAL; + } + + cpas_core->ahb_bus_client.common_data.dst_id = dst_args.args[0]; + + rc = of_property_read_u32(of_node, "cam-ahb-num-cases", + &cpas_core->ahb_bus_client.common_data.num_usecases); + if (rc) { + CAM_ERR(CAM_CPAS, + "device %s failed to read ahb num usecases", + pdev->name); + return rc; + } + + if (cpas_core->ahb_bus_client.common_data.num_usecases > + CAM_SOC_BUS_MAX_NUM_USECASES) { + CAM_ERR(CAM_UTIL, "Invalid number of usecases: %d", + cpas_core->ahb_bus_client.common_data + .num_usecases); + return -EINVAL; + } + + num_bw_values = of_property_count_u64_elems(of_node, + "cam-ahb-bw-KBps"); + if (num_bw_values <= 0) { + CAM_ERR(CAM_UTIL, "Error counting ahb bw values"); + return -EINVAL; + } + + num_levels = (num_bw_values / 2); + + if (num_levels != + cpas_core->ahb_bus_client.common_data.num_usecases) { + CAM_ERR(CAM_UTIL, "Invalid number of levels: %d", + num_bw_values/2); + return -EINVAL; + } + + for (i = 0; i < num_levels; i++) { + rc = of_property_read_u64_index(of_node, + "cam-ahb-bw-KBps", + (i * 2), + &cpas_core->ahb_bus_client.common_data + .bw_pair[i].ab); + if (rc) { + CAM_ERR(CAM_UTIL, + "Error reading ab bw value, rc=%d", + rc); + return rc; + } + + rc = of_property_read_u64_index(of_node, + "cam-ahb-bw-KBps", + ((i * 2) + 1), + &cpas_core->ahb_bus_client.common_data + .bw_pair[i].ib); + if (rc) { + CAM_ERR(CAM_UTIL, + "Error reading ib bw value, rc=%d", + rc); + return rc; + } + } + } count = of_property_count_strings(of_node, "client-names"); if (count <= 0) { diff --git a/drivers/cam_cpas/cam_cpas_soc.h b/drivers/cam_cpas/cam_cpas_soc.h index fea9c3c97593..ef5eb32337e6 100644 --- a/drivers/cam_cpas/cam_cpas_soc.h +++ b/drivers/cam_cpas/cam_cpas_soc.h @@ -74,6 +74,7 @@ struct cam_cpas_tree_node { * * @arch_compat: ARCH compatible string * @client_id_based: Whether clients are id based + * @bus_icc_based: Interconnect based bus interaction * @num_clients: Number of clients supported * @client_name: Client names * @tree_node: Array of pointers to all tree nodes required to calculate @@ -93,6 +94,7 @@ struct cam_cpas_tree_node { struct cam_cpas_private_soc { const char *arch_compat; bool client_id_based; + bool bus_icc_based; uint32_t num_clients; const char *client_name[CAM_CPAS_MAX_CLIENTS]; struct cam_cpas_tree_node *tree_node[CAM_CPAS_MAX_TREE_NODES]; diff --git a/drivers/cam_utils/cam_soc_bus.c b/drivers/cam_utils/cam_soc_bus.c new file mode 100644 index 000000000000..1cef2610b805 --- /dev/null +++ b/drivers/cam_utils/cam_soc_bus.c @@ -0,0 +1,232 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2019, The Linux Foundation. All rights reserved. + */ + +#include +#include "cam_soc_bus.h" + +/** + * struct cam_soc_bus_client_data : Bus client data + * + * @pdata: Bus pdata information + * @client_id: Bus client id + * @num_paths: Number of paths for this client + * @curr_vote_level: current voted index + * @dyn_vote: whether dynamic voting enabled + */ +struct cam_soc_bus_client_data { + struct msm_bus_scale_pdata *pdata; + uint32_t client_id; + int num_paths; + unsigned int curr_vote_level; + bool dyn_vote; +}; + +int cam_soc_bus_client_update_request(void *client, unsigned int idx) +{ + int rc = 0; + struct cam_soc_bus_client *bus_client = + (struct cam_soc_bus_client *) client; + struct cam_soc_bus_client_data *bus_client_data = + (struct cam_soc_bus_client_data *) bus_client->client_data; + + if (bus_client_data->dyn_vote) { + CAM_ERR(CAM_UTIL, + "Dyn update not allowed client[%d][%s], dyn_vote: %d", + bus_client_data->client_id, + bus_client->common_data->name, + bus_client_data->dyn_vote); + rc = -EINVAL; + goto end; + } + + if (idx >= bus_client->common_data->num_usecases) { + CAM_ERR(CAM_UTIL, "Invalid vote level=%d, usecases=%d", idx, + bus_client->common_data->num_usecases); + rc = -EINVAL; + goto end; + } + + CAM_DBG(CAM_UTIL, "Bus client=[%d][%s] index[%d]", + bus_client_data->client_id, bus_client->common_data->name, idx); + + rc = msm_bus_scale_client_update_request(bus_client_data->client_id, + idx); + if (rc) { + CAM_ERR(CAM_UTIL, + "Update request failed, client[%d][%s], idx: %d", + bus_client_data->client_id, + bus_client->common_data->name, idx); + goto end; + } + +end: + return rc; +} + +int cam_soc_bus_client_update_bw(void *client, uint64_t ab, uint64_t ib) +{ + int idx = 0; + struct msm_bus_paths *path; + struct msm_bus_scale_pdata *pdata; + struct cam_soc_bus_client *bus_client = + (struct cam_soc_bus_client *) client; + struct cam_soc_bus_client_data *bus_client_data = + (struct cam_soc_bus_client_data *) bus_client->client_data; + int rc = 0; + + if ((bus_client->common_data->num_usecases != 2) || + (bus_client_data->num_paths != 1) || + (!bus_client_data->dyn_vote)) { + CAM_ERR(CAM_UTIL, + "dynamic update not allowed Bus client=[%d][%s], %d %d %d", + bus_client_data->client_id, + bus_client->common_data->name, + bus_client->common_data->num_usecases, + bus_client_data->num_paths, + bus_client_data->dyn_vote); + rc = -EINVAL; + goto end; + } + + idx = bus_client_data->curr_vote_level; + idx = 1 - idx; + bus_client_data->curr_vote_level = idx; + + pdata = bus_client_data->pdata; + path = &(pdata->usecase[idx]); + path->vectors[0].ab = ab; + path->vectors[0].ib = ib; + + CAM_DBG(CAM_UTIL, "Bus client=[%d][%s] :ab[%llu] ib[%llu], index[%d]", + bus_client_data->client_id, bus_client->common_data->name, ab, + ib, idx); + rc = msm_bus_scale_client_update_request(bus_client_data->client_id, + idx); + if (rc) { + CAM_ERR(CAM_UTIL, + "Update request failed, client[%d][%s], idx: %d", + bus_client_data->client_id, + bus_client->common_data->name, idx); + return rc; + } + +end: + return rc; +} + +int cam_soc_bus_client_register(struct platform_device *pdev, + struct device_node *dev_node, void **client, + struct cam_soc_bus_client_common_data *common_data) +{ + struct msm_bus_scale_pdata *pdata = NULL; + struct cam_soc_bus_client *bus_client = NULL; + struct cam_soc_bus_client_data *bus_client_data = NULL; + uint32_t client_id; + int rc; + + bus_client = kzalloc(sizeof(struct cam_soc_bus_client), GFP_KERNEL); + if (!bus_client) { + CAM_ERR(CAM_UTIL, "Non Enought Memroy"); + rc = -ENOMEM; + goto end; + } + + *client = bus_client; + + bus_client_data = kzalloc(sizeof(struct cam_soc_bus_client_data), + GFP_KERNEL); + if (!bus_client_data) { + kfree(bus_client); + *client = NULL; + rc = -ENOMEM; + goto end; + } + + bus_client->client_data = bus_client_data; + pdata = msm_bus_pdata_from_node(pdev, + dev_node); + if (!pdata) { + CAM_ERR(CAM_UTIL, "failed get_pdata"); + rc = -EINVAL; + goto error; + } + + if ((pdata->num_usecases == 0) || + (pdata->usecase[0].num_paths == 0)) { + CAM_ERR(CAM_UTIL, "usecase=%d", pdata->num_usecases); + rc = -EINVAL; + goto error; + } + + client_id = msm_bus_scale_register_client(pdata); + if (!client_id) { + CAM_ERR(CAM_UTIL, "failed in register bus client_data"); + rc = -EINVAL; + goto error; + } + + bus_client->common_data = common_data; + + bus_client_data->dyn_vote = of_property_read_bool(dev_node, + "qcom,msm-bus-vector-dyn-vote"); + + if (bus_client_data->dyn_vote && (pdata->num_usecases != 2)) { + CAM_ERR(CAM_UTIL, "Excess or less vectors %d", + pdata->num_usecases); + rc = -EINVAL; + goto fail_unregister_client; + } + + rc = msm_bus_scale_client_update_request(client_id, 0); + if (rc) { + CAM_ERR(CAM_UTIL, "Bus client update request failed, rc = %d", + rc); + goto fail_unregister_client; + } + + bus_client->common_data->src_id = pdata->usecase[0].vectors[0].src; + bus_client->common_data->dst_id = pdata->usecase[0].vectors[0].dst; + bus_client_data->pdata = pdata; + bus_client_data->client_id = client_id; + bus_client->common_data->num_usecases = pdata->num_usecases; + bus_client_data->num_paths = pdata->usecase[0].num_paths; + bus_client->common_data->name = pdata->name; + + CAM_DBG(CAM_UTIL, "Bus Client=[%d][%s] : src=%d, dst=%d", + bus_client_data->client_id, bus_client->common_data->name, + bus_client->common_data->src_id, + bus_client->common_data->dst_id); + + return 0; +fail_unregister_client: + msm_bus_scale_unregister_client(bus_client_data->client_id); +error: + kfree(bus_client_data); + bus_client->client_data = NULL; + kfree(bus_client); + *client = NULL; +end: + return rc; + +} + +void cam_soc_bus_client_unregister(void **client) +{ + struct cam_soc_bus_client *bus_client = + (struct cam_soc_bus_client *) (*client); + struct cam_soc_bus_client_data *bus_client_data = + (struct cam_soc_bus_client_data *) bus_client->client_data; + + if (bus_client_data->dyn_vote) + cam_soc_bus_client_update_bw(bus_client, 0, 0); + else + cam_soc_bus_client_update_request(bus_client, 0); + + msm_bus_scale_unregister_client(bus_client_data->client_id); + kfree(bus_client_data); + bus_client->client_data = NULL; + kfree(bus_client); + *client = NULL; +} diff --git a/drivers/cam_utils/cam_soc_bus.h b/drivers/cam_utils/cam_soc_bus.h new file mode 100644 index 000000000000..518b6524d038 --- /dev/null +++ b/drivers/cam_utils/cam_soc_bus.h @@ -0,0 +1,96 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2019, The Linux Foundation. All rights reserved. + */ + +#ifndef _CAM_SOC_BUS_H_ +#define _CAM_SOC_BUS_H_ + +#include +#include +#include +#include "cam_debug_util.h" + +#define CAM_SOC_BUS_MAX_NUM_USECASES 8 + +/** + * struct cam_soc_bus_client_ab_ib : Bandwidth values for selected usecase + * + * @ab: Arbitrated Bandwidth + * @ib: Instantaneous Bandwidth + */ +struct cam_soc_bus_client_ab_ib { + uint64_t ab; + uint64_t ib; +}; + +/** + * struct cam_soc_bus_client_common_data : Common data fields for bus client + * + * @name: Name of bus client + * @src_id: Bus master/src id + * @dst_id: Bus slave/dst id + * @num_usecases: Number of use cases for this client + * @bw_pair: Bandwidth values for applicable usecases + */ +struct cam_soc_bus_client_common_data { + const char *name; + uint32_t src_id; + uint32_t dst_id; + int num_usecases; + struct cam_soc_bus_client_ab_ib bw_pair[CAM_SOC_BUS_MAX_NUM_USECASES]; +}; + +/** + * struct cam_soc_bus_client : Bus client information + * + * @client_data: Bus client data + * @common_data: Common data fields for bus client + */ +struct cam_soc_bus_client { + void *client_data; + struct cam_soc_bus_client_common_data *common_data; +}; + + +#if IS_REACHABLE(CONFIG_QCOM_BUS_SCALING) || \ + IS_REACHABLE(CONFIG_INTERCONNECT_QCOM) + +int cam_soc_bus_client_update_request(void *client, unsigned int idx); + +int cam_soc_bus_client_update_bw(void *client, uint64_t ab, + uint64_t ib); + +int cam_soc_bus_client_register(struct platform_device *pdev, + struct device_node *dev_node, void **client, + struct cam_soc_bus_client_common_data *common_data); + +void cam_soc_bus_client_unregister(void **client); + +#else +static inline int cam_soc_bus_client_update_request(void *client, + unsigned int idx) +{ + return 0; +} + +static inline int cam_soc_bus_client_update_bw(void *client, + uint64_t ab, uint64_t ib) +{ + return 0; +} + +static inline int cam_soc_bus_client_register( + struct platform_device *pdev, struct device_node *dev_node, + void **client, struct cam_soc_bus_client_common_data *common_data) +{ + return 0; +} + +static inline void cam_soc_bus_client_unregister(void **client) +{ +} + +#endif + +#endif /* _CAM_SOC_BUS_H_ */ diff --git a/drivers/cam_utils/cam_soc_icc.c b/drivers/cam_utils/cam_soc_icc.c new file mode 100644 index 000000000000..0a22cecc3c08 --- /dev/null +++ b/drivers/cam_utils/cam_soc_icc.c @@ -0,0 +1,149 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2019, The Linux Foundation. All rights reserved. + */ + +#include +#include "cam_soc_bus.h" + +/** + * struct cam_soc_bus_client_data : Bus client data + * + * @icc_data: Bus icc path information + */ +struct cam_soc_bus_client_data { + struct icc_path *icc_data; +}; + +int cam_soc_bus_client_update_request(void *client, unsigned int idx) +{ + int rc = 0; + uint64_t ab = 0, ib = 0; + struct cam_soc_bus_client *bus_client = + (struct cam_soc_bus_client *) client; + struct cam_soc_bus_client_data *bus_client_data = + (struct cam_soc_bus_client_data *) bus_client->client_data; + + if (idx >= bus_client->common_data->num_usecases) { + CAM_ERR(CAM_UTIL, "Invalid vote level=%d, usecases=%d", idx, + bus_client->common_data->num_usecases); + rc = -EINVAL; + goto end; + } + + ab = bus_client->common_data->bw_pair[idx].ab; + ib = bus_client->common_data->bw_pair[idx].ib; + + CAM_DBG(CAM_UTIL, "Bus client=[%s] index[%d]", + bus_client->common_data->name, idx); + + rc = icc_set_bw(bus_client_data->icc_data, ab, ib); + if (rc) { + CAM_ERR(CAM_UTIL, + "Update request failed, client[%s], idx: %d", + bus_client->common_data->name, idx); + goto end; + } + +end: + return rc; +} + +int cam_soc_bus_client_update_bw(void *client, uint64_t ab, uint64_t ib) +{ + struct cam_soc_bus_client *bus_client = + (struct cam_soc_bus_client *) client; + struct cam_soc_bus_client_data *bus_client_data = + (struct cam_soc_bus_client_data *) bus_client->client_data; + int rc = 0; + + CAM_DBG(CAM_UTIL, "Bus client=[%s] :ab[%llu] ib[%llu]", + bus_client->common_data->name, ab, ib); + rc = icc_set_bw(bus_client_data->icc_data, ab, ib); + if (rc) { + CAM_ERR(CAM_UTIL, "Update request failed, client[%s]", + bus_client->common_data->name); + goto end; + } + +end: + return rc; +} + +int cam_soc_bus_client_register(struct platform_device *pdev, + struct device_node *dev_node, void **client, + struct cam_soc_bus_client_common_data *common_data) +{ + struct cam_soc_bus_client *bus_client = NULL; + struct cam_soc_bus_client_data *bus_client_data = NULL; + int rc = 0; + + bus_client = kzalloc(sizeof(struct cam_soc_bus_client), GFP_KERNEL); + if (!bus_client) { + CAM_ERR(CAM_UTIL, "soc bus client is NULL"); + rc = -ENOMEM; + goto end; + } + + *client = bus_client; + + bus_client_data = kzalloc(sizeof(struct cam_soc_bus_client_data), + GFP_KERNEL); + if (!bus_client_data) { + kfree(bus_client); + *client = NULL; + rc = -ENOMEM; + goto end; + } + + bus_client->client_data = bus_client_data; + bus_client_data->icc_data = icc_get(&pdev->dev, + bus_client->common_data->src_id, + bus_client->common_data->dst_id); + if (!bus_client_data->icc_data) { + CAM_ERR(CAM_UTIL, "failed in register bus client"); + rc = -EINVAL; + goto error; + } + + bus_client->common_data = common_data; + + rc = icc_set_bw(bus_client_data->icc_data, 0, 0); + if (rc) { + CAM_ERR(CAM_UTIL, "Bus client update request failed, rc = %d", + rc); + goto fail_unregister_client; + } + + CAM_DBG(CAM_UTIL, "Bus Client=[%s] : src=%d, dst=%d", + bus_client->common_data->name, bus_client->common_data->src_id, + bus_client->common_data->dst_id); + + return 0; + +fail_unregister_client: + icc_put(bus_client_data->icc_data); +error: + kfree(bus_client_data); + bus_client->client_data = NULL; + kfree(bus_client); + *client = NULL; +end: + return rc; + +} + +void cam_soc_bus_client_unregister(void **client) +{ + struct cam_soc_bus_client *bus_client = + (struct cam_soc_bus_client *) (*client); + struct cam_soc_bus_client_data *bus_client_data = + (struct cam_soc_bus_client_data *) bus_client->client_data; + + icc_put(bus_client_data->icc_data); + kfree(bus_client_data); + bus_client->client_data = NULL; + kfree(bus_client); + *client = NULL; + +}