diff options
Diffstat (limited to 'drivers/soc/qcom/msm_bus/msm_bus_arb.c')
-rw-r--r-- | drivers/soc/qcom/msm_bus/msm_bus_arb.c | 1137 |
1 files changed, 1137 insertions, 0 deletions
diff --git a/drivers/soc/qcom/msm_bus/msm_bus_arb.c b/drivers/soc/qcom/msm_bus/msm_bus_arb.c new file mode 100644 index 000000000000..0a92e182210d --- /dev/null +++ b/drivers/soc/qcom/msm_bus/msm_bus_arb.c @@ -0,0 +1,1137 @@ +/* Copyright (c) 2011-2014, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#define pr_fmt(fmt) "AXI: %s(): " fmt, __func__ + +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/mutex.h> +#include <linux/radix-tree.h> +#include <linux/clk.h> +#include <linux/msm-bus.h> +#include "msm_bus_core.h" +#include <trace/events/trace_msm_bus.h> + +#define INDEX_MASK 0x0000FFFF +#define PNODE_MASK 0xFFFF0000 +#define SHIFT_VAL 16 +#define CREATE_PNODE_ID(n, i) (((n) << SHIFT_VAL) | (i)) +#define GET_INDEX(n) ((n) & INDEX_MASK) +#define GET_NODE(n) ((n) >> SHIFT_VAL) +#define IS_NODE(n) ((n) % FABRIC_ID_KEY) +#define SEL_FAB_CLK 1 +#define SEL_SLAVE_CLK 0 +/* + * To get to BIMC BW convert Hz to bytes by multiplying bus width(8), + * double-data-rate(2) * ddr-channels(2). + */ +#define GET_BIMC_BW(clk) (clk * 8 * 2 * 2) + +#define BW_TO_CLK_FREQ_HZ(width, bw) \ + msm_bus_div64(width, bw) + +#define IS_MASTER_VALID(mas) \ + (((mas >= MSM_BUS_MASTER_FIRST) && (mas <= MSM_BUS_MASTER_LAST)) \ + ? 1 : 0) + +#define IS_SLAVE_VALID(slv) \ + (((slv >= MSM_BUS_SLAVE_FIRST) && (slv <= MSM_BUS_SLAVE_LAST)) ? 1 : 0) + +static DEFINE_MUTEX(msm_bus_lock); + +/* This function uses shift operations to divide 64 bit value for higher + * efficiency. The divisor expected are number of ports or bus-width. + * These are expected to be 1, 2, 4, 8, 16 and 32 in most cases. + * + * To account for exception to the above divisor values, the standard + * do_div function is used. + * */ +uint64_t msm_bus_div64(unsigned int w, uint64_t bw) +{ + uint64_t *b = &bw; + + if ((bw > 0) && (bw < w)) + return 1; + + switch (w) { + case 0: + WARN(1, "AXI: Divide by 0 attempted\n"); + case 1: return bw; + case 2: return (bw >> 1); + case 4: return (bw >> 2); + case 8: return (bw >> 3); + case 16: return (bw >> 4); + case 32: return (bw >> 5); + } + + do_div(*b, w); + return *b; +} + +/** + * add_path_node: Adds the path information to the current node + * @info: Internal node info structure + * @next: Combination of the id and index of the next node + * Function returns: Number of pnodes (path_nodes) on success, + * error on failure. + * + * Every node maintains the list of path nodes. A path node is + * reached by finding the node-id and index stored at the current + * node. This makes updating the paths with requested bw and clock + * values efficient, as it avoids lookup for each update-path request. + */ +static int add_path_node(struct msm_bus_inode_info *info, int next) +{ + struct path_node *pnode; + int i; + if (ZERO_OR_NULL_PTR(info)) { + MSM_BUS_ERR("Cannot find node info!: id :%d\n", + info->node_info->priv_id); + return -ENXIO; + } + + for (i = 0; i <= info->num_pnodes; i++) { + if (info->pnode[i].next == -2) { + MSM_BUS_DBG("Reusing pnode for info: %d at index: %d\n", + info->node_info->priv_id, i); + info->pnode[i].clk[DUAL_CTX] = 0; + info->pnode[i].clk[ACTIVE_CTX] = 0; + info->pnode[i].bw[DUAL_CTX] = 0; + info->pnode[i].bw[ACTIVE_CTX] = 0; + info->pnode[i].next = next; + MSM_BUS_DBG("%d[%d] : (%d, %d)\n", + info->node_info->priv_id, i, GET_NODE(next), + GET_INDEX(next)); + return i; + } + } + + info->num_pnodes++; + pnode = krealloc(info->pnode, + ((info->num_pnodes + 1) * sizeof(struct path_node)) + , GFP_KERNEL); + if (ZERO_OR_NULL_PTR(pnode)) { + MSM_BUS_ERR("Error creating path node!\n"); + info->num_pnodes--; + return -ENOMEM; + } + info->pnode = pnode; + info->pnode[info->num_pnodes].clk[DUAL_CTX] = 0; + info->pnode[info->num_pnodes].clk[ACTIVE_CTX] = 0; + info->pnode[info->num_pnodes].bw[DUAL_CTX] = 0; + info->pnode[info->num_pnodes].bw[ACTIVE_CTX] = 0; + info->pnode[info->num_pnodes].next = next; + MSM_BUS_DBG("%d[%d] : (%d, %d)\n", info->node_info->priv_id, + info->num_pnodes, GET_NODE(next), GET_INDEX(next)); + return info->num_pnodes; +} + +static int clearvisitedflag(struct device *dev, void *data) +{ + struct msm_bus_fabric_device *fabdev = to_msm_bus_fabric_device(dev); + fabdev->visited = false; + return 0; +} + +/** + * getpath() - Finds the path from the topology between src and dest + * @src: Source. This is the master from which the request originates + * @dest: Destination. This is the slave to which we're trying to reach + * + * Function returns: next_pnode_id. The higher 16 bits of the next_pnode_id + * represent the src id of the next node on path. The lower 16 bits of the + * next_pnode_id represent the "index", which is the next entry in the array + * of pnodes for that node to fill in clk and bw values. This is created using + * CREATE_PNODE_ID. The return value is stored in ret_pnode, and this is added + * to the list of path nodes. + * + * This function recursively finds the path by updating the src to the + * closest possible node to dest. + */ +static int getpath(int src, int dest) +{ + int pnode_num = -1, i; + struct msm_bus_fabnodeinfo *fabnodeinfo; + struct msm_bus_fabric_device *fabdev; + int next_pnode_id = -1; + struct msm_bus_inode_info *info = NULL; + int _src = src/FABRIC_ID_KEY; + int _dst = dest/FABRIC_ID_KEY; + int ret_pnode = -1; + int fabid = GET_FABID(src); + + /* Find the location of fabric for the src */ + MSM_BUS_DBG("%d --> %d\n", src, dest); + + fabdev = msm_bus_get_fabric_device(fabid); + if (!fabdev) { + MSM_BUS_WARN("Fabric Not yet registered. Try again\n"); + return -ENXIO; + } + + /* Are we there yet? */ + if (src == dest) { + info = fabdev->algo->find_node(fabdev, src); + if (ZERO_OR_NULL_PTR(info)) { + MSM_BUS_ERR("Node %d not found\n", dest); + return -ENXIO; + } + + for (i = 0; i <= info->num_pnodes; i++) { + if (info->pnode[i].next == -2) { + MSM_BUS_DBG("src = dst Reusing pnode for" + " info: %d at index: %d\n", + info->node_info->priv_id, i); + next_pnode_id = CREATE_PNODE_ID(src, i); + info->pnode[i].clk[DUAL_CTX] = 0; + info->pnode[i].bw[DUAL_CTX] = 0; + info->pnode[i].next = next_pnode_id; + MSM_BUS_DBG("returning: %d, %d\n", GET_NODE + (next_pnode_id), GET_INDEX(next_pnode_id)); + return next_pnode_id; + } + } + next_pnode_id = CREATE_PNODE_ID(src, (info->num_pnodes + 1)); + pnode_num = add_path_node(info, next_pnode_id); + if (pnode_num < 0) { + MSM_BUS_ERR("Error adding path node\n"); + return -ENXIO; + } + MSM_BUS_DBG("returning: %d, %d\n", GET_NODE(next_pnode_id), + GET_INDEX(next_pnode_id)); + return next_pnode_id; + } else if (_src == _dst) { + /* + * src and dest belong to same fabric, find the destination + * from the radix tree + */ + info = fabdev->algo->find_node(fabdev, dest); + if (ZERO_OR_NULL_PTR(info)) { + MSM_BUS_ERR("Node %d not found\n", dest); + return -ENXIO; + } + + ret_pnode = getpath(info->node_info->priv_id, dest); + next_pnode_id = ret_pnode; + } else { + /* find the dest fabric */ + int trynextgw = true; + struct list_head *gateways = fabdev->algo->get_gw_list(fabdev); + list_for_each_entry(fabnodeinfo, gateways, list) { + /* see if the destination is at a connected fabric */ + if (_dst == (fabnodeinfo->info->node_info->priv_id / + FABRIC_ID_KEY)) { + /* Found the fab on which the device exists */ + info = fabnodeinfo->info; + trynextgw = false; + ret_pnode = getpath(info->node_info->priv_id, + dest); + pnode_num = add_path_node(info, ret_pnode); + if (pnode_num < 0) { + MSM_BUS_ERR("Error adding path node\n"); + return -ENXIO; + } + next_pnode_id = CREATE_PNODE_ID( + info->node_info->priv_id, pnode_num); + break; + } + } + + /* find the gateway */ + if (trynextgw) { + gateways = fabdev->algo->get_gw_list(fabdev); + list_for_each_entry(fabnodeinfo, gateways, list) { + struct msm_bus_fabric_device *gwfab = + msm_bus_get_fabric_device(fabnodeinfo-> + info->node_info->priv_id); + if (!gwfab) { + MSM_BUS_ERR("Err: No gateway found\n"); + return -ENXIO; + } + + if (!gwfab->visited) { + MSM_BUS_DBG("VISITED ID: %d\n", + gwfab->id); + gwfab->visited = true; + info = fabnodeinfo->info; + ret_pnode = getpath(info-> + node_info->priv_id, dest); + pnode_num = add_path_node(info, + ret_pnode); + if (pnode_num < 0) { + MSM_BUS_ERR("Malloc failure in" + " adding path node\n"); + return -ENXIO; + } + next_pnode_id = CREATE_PNODE_ID( + info->node_info->priv_id, pnode_num); + break; + } + } + if (next_pnode_id < 0) + return -ENXIO; + } + } + + if (!IS_NODE(src)) { + MSM_BUS_DBG("Returning next_pnode_id:%d[%d]\n", GET_NODE( + next_pnode_id), GET_INDEX(next_pnode_id)); + return next_pnode_id; + } + info = fabdev->algo->find_node(fabdev, src); + if (!info) { + MSM_BUS_ERR("Node info not found.\n"); + return -ENXIO; + } + + pnode_num = add_path_node(info, next_pnode_id); + MSM_BUS_DBG(" Last: %d[%d] = (%d, %d)\n", + src, info->num_pnodes, GET_NODE(next_pnode_id), + GET_INDEX(next_pnode_id)); + MSM_BUS_DBG("returning: %d, %d\n", src, pnode_num); + return CREATE_PNODE_ID(src, pnode_num); +} + +static uint64_t get_node_maxib(struct msm_bus_inode_info *info) +{ + int i, ctx; + uint64_t maxib = 0; + + for (i = 0; i <= info->num_pnodes; i++) { + for (ctx = 0; ctx < NUM_CTX; ctx++) + maxib = max(info->pnode[i].clk[ctx], maxib); + } + + MSM_BUS_DBG("%s: Node %d numpnodes %d maxib %llu", __func__, + info->num_pnodes, info->node_info->id, maxib); + return maxib; +} + + +static uint64_t get_node_sumab(struct msm_bus_inode_info *info) +{ + int i; + uint64_t maxab = 0; + + for (i = 0; i <= info->num_pnodes; i++) + maxab += info->pnode[i].bw[DUAL_CTX]; + + MSM_BUS_DBG("%s: Node %d numpnodes %d maxib %llu", __func__, + info->num_pnodes, info->node_info->id, maxab); + return maxab; +} + +static uint64_t get_vfe_bw(void) +{ + int vfe_id = MSM_BUS_MASTER_VFE; + int iid = msm_bus_board_get_iid(vfe_id); + int fabid; + struct msm_bus_fabric_device *fabdev; + struct msm_bus_inode_info *info; + uint64_t vfe_bw = 0; + + fabid = GET_FABID(iid); + fabdev = msm_bus_get_fabric_device(fabid); + info = fabdev->algo->find_node(fabdev, iid); + + if (!info) { + MSM_BUS_ERR("%s: Can't find node %d", __func__, + vfe_id); + goto exit_get_vfe_bw; + } + + vfe_bw = get_node_sumab(info); + MSM_BUS_DBG("vfe_ab %llu", vfe_bw); + +exit_get_vfe_bw: + return vfe_bw; +} + +static uint64_t get_mdp_bw(void) +{ + int ids[] = {MSM_BUS_MASTER_MDP_PORT0, MSM_BUS_MASTER_MDP_PORT1}; + int i; + uint64_t mdp_ab = 0; + uint32_t ff = 0; + + for (i = 0; i < ARRAY_SIZE(ids); i++) { + int iid = msm_bus_board_get_iid(ids[i]); + int fabid; + struct msm_bus_fabric_device *fabdev; + struct msm_bus_inode_info *info; + + fabid = GET_FABID(iid); + fabdev = msm_bus_get_fabric_device(fabid); + info = fabdev->algo->find_node(fabdev, iid); + + if (!info) { + MSM_BUS_ERR("%s: Can't find node %d", __func__, + ids[i]); + continue; + } + + mdp_ab += get_node_sumab(info); + MSM_BUS_DBG("mdp_ab %llu", mdp_ab); + ff = info->node_info->ff; + } + + if (ff) { + mdp_ab = msm_bus_div64(2 * ff, 100 * mdp_ab); + } else { + MSM_BUS_ERR("MDP FF is 0"); + mdp_ab = 0; + } + + + MSM_BUS_DBG("MDP BW %llu\n", mdp_ab); + return mdp_ab; +} + +static uint64_t get_rt_bw(void) +{ + uint64_t rt_bw = 0; + + rt_bw += get_mdp_bw(); + rt_bw += get_vfe_bw(); + + return rt_bw; +} + +static uint64_t get_avail_bw(struct msm_bus_fabric_device *fabdev) +{ + uint64_t fabclk_rate = 0; + int i; + uint64_t avail_bw = 0; + uint64_t rt_bw = get_rt_bw(); + struct msm_bus_fabric *fabric = to_msm_bus_fabric(fabdev); + + if (!rt_bw) + goto exit_get_avail_bw; + + for (i = 0; i < NUM_CTX; i++) { + uint64_t ctx_rate; + ctx_rate = + fabric->info.nodeclk[i].rate; + fabclk_rate = max(ctx_rate, fabclk_rate); + } + + if (!fabdev->eff_fact || !fabdev->nr_lim_thresh) { + MSM_BUS_ERR("Error: Eff-fact %d; nr_thresh %llu", + fabdev->eff_fact, fabdev->nr_lim_thresh); + return 0; + } + + avail_bw = msm_bus_div64(100, + (GET_BIMC_BW(fabclk_rate) * fabdev->eff_fact)); + + if (avail_bw >= fabdev->nr_lim_thresh) + return 0; + + MSM_BUS_DBG("%s: Total_avail_bw %llu, rt_bw %llu\n", + __func__, avail_bw, rt_bw); + trace_bus_avail_bw(avail_bw, rt_bw); + + if (avail_bw < rt_bw) { + MSM_BUS_ERR("\n%s: ERROR avail BW %llu < MDP %llu", + __func__, avail_bw, rt_bw); + avail_bw = 0; + goto exit_get_avail_bw; + } + avail_bw -= rt_bw; + +exit_get_avail_bw: + return avail_bw; +} + +static void program_nr_limits(struct msm_bus_fabric_device *fabdev) +{ + int num_nr_lim = 0; + int i; + struct msm_bus_inode_info *info[fabdev->num_nr_lim]; + struct msm_bus_fabric *fabric = to_msm_bus_fabric(fabdev); + + num_nr_lim = radix_tree_gang_lookup_tag(&fabric->fab_tree, + (void **)&info, fabric->fabdev.id, fabdev->num_nr_lim, + MASTER_NODE); + + for (i = 0; i < num_nr_lim; i++) + fabdev->algo->config_limiter(fabdev, info[i]); +} + +static int msm_bus_commit_limiter(struct device *dev, void *data) +{ + int ret = 0; + struct msm_bus_fabric_device *fabdev = to_msm_bus_fabric_device(dev); + + MSM_BUS_DBG("fabid: %d\n", fabdev->id); + program_nr_limits(fabdev); + return ret; +} + +static void compute_nr_limits(struct msm_bus_fabric_device *fabdev, int pnode) +{ + uint64_t total_ib = 0; + int num_nr_lim = 0; + uint64_t avail_bw = 0; + struct msm_bus_inode_info *info[fabdev->num_nr_lim]; + struct msm_bus_fabric *fabric = to_msm_bus_fabric(fabdev); + int i; + + num_nr_lim = radix_tree_gang_lookup_tag(&fabric->fab_tree, + (void **)&info, fabric->fabdev.id, fabdev->num_nr_lim, + MASTER_NODE); + + MSM_BUS_DBG("%s: Found %d NR LIM nodes", __func__, num_nr_lim); + for (i = 0; i < num_nr_lim; i++) + total_ib += get_node_maxib(info[i]); + + avail_bw = get_avail_bw(fabdev); + MSM_BUS_DBG("\n %s: Avail BW %llu", __func__, avail_bw); + + for (i = 0; i < num_nr_lim; i++) { + uint32_t node_pct = 0; + uint64_t new_lim_bw = 0; + uint64_t node_max_ib = 0; + uint32_t node_max_ib_kB = 0; + uint32_t total_ib_kB = 0; + uint64_t bw_node; + + node_max_ib = get_node_maxib(info[i]); + node_max_ib_kB = msm_bus_div64(1024, node_max_ib); + total_ib_kB = msm_bus_div64(1024, total_ib); + node_pct = (node_max_ib_kB * 100) / total_ib_kB; + bw_node = node_pct * avail_bw; + new_lim_bw = msm_bus_div64(100, bw_node); + + /* + * if limiter bw is more than the requested IB clip to + requested IB. + */ + if (new_lim_bw >= node_max_ib) + new_lim_bw = node_max_ib; + + /* + * if there is a floor bw for this nr lim node and + * if there is available bw to divy up among the nr masters + * and if the nr lim masters have a non zero vote and + * if the limited bw is below the floor for this node. + * then limit this node to the floor bw. + */ + if (info[i]->node_info->floor_bw && node_max_ib && avail_bw && + (new_lim_bw <= info[i]->node_info->floor_bw)) { + MSM_BUS_ERR("\nNode %d:Limiting BW:%llu < floor:%llu", + info[i]->node_info->id, new_lim_bw, + info[i]->node_info->floor_bw); + new_lim_bw = info[i]->node_info->floor_bw; + } + + if (new_lim_bw != info[i]->cur_lim_bw) { + info[i]->cur_lim_bw = new_lim_bw; + MSM_BUS_DBG("NodeId %d: Requested IB %llu", + info[i]->node_info->id, node_max_ib); + MSM_BUS_DBG("Limited to %llu(%d pct of Avail %llu )\n", + new_lim_bw, node_pct, avail_bw); + } else { + MSM_BUS_DBG("NodeId %d: No change Limited to %llu\n", + info[i]->node_info->id, info[i]->cur_lim_bw); + } + } +} + +static void setup_nr_limits(int curr, int pnode) +{ + struct msm_bus_fabric_device *fabdev = + msm_bus_get_fabric_device(GET_FABID(curr)); + struct msm_bus_inode_info *info; + + if (!fabdev) { + MSM_BUS_WARN("Fabric Not yet registered. Try again\n"); + goto exit_setup_nr_limits; + } + + /* This logic is currently applicable to BIMC masters only */ + if (fabdev->id != MSM_BUS_FAB_DEFAULT) { + MSM_BUS_ERR("Static limiting of NR masters only for BIMC\n"); + goto exit_setup_nr_limits; + } + + info = fabdev->algo->find_node(fabdev, curr); + if (!info) { + MSM_BUS_ERR("Cannot find node info!\n"); + goto exit_setup_nr_limits; + } + + compute_nr_limits(fabdev, pnode); +exit_setup_nr_limits: + return; +} + +static bool is_nr_lim(int id) +{ + struct msm_bus_fabric_device *fabdev = msm_bus_get_fabric_device + (GET_FABID(id)); + struct msm_bus_inode_info *info; + bool ret = false; + + if (!fabdev) { + MSM_BUS_ERR("Bus device for bus ID: %d not found!\n", + GET_FABID(id)); + goto exit_is_nr_lim; + } + + info = fabdev->algo->find_node(fabdev, id); + if (!info) + MSM_BUS_ERR("Cannot find node info %d!\n", id); + else if ((info->node_info->nr_lim || info->node_info->rt_mas)) + ret = true; +exit_is_nr_lim: + return ret; +} + +/** + * update_path() - Update the path with the bandwidth and clock values, as + * requested by the client. + * + * @curr: Current source node, as specified in the client vector (master) + * @pnode: The first-hop node on the path, stored in the internal client struct + * @req_clk: Requested clock value from the vector + * @req_bw: Requested bandwidth value from the vector + * @curr_clk: Current clock frequency + * @curr_bw: Currently allocated bandwidth + * + * This function updates the nodes on the path calculated using getpath(), with + * clock and bandwidth values. The sum of bandwidths, and the max of clock + * frequencies is calculated at each node on the path. Commit data to be sent + * to RPM for each master and slave is also calculated here. + */ +static int update_path(int curr, int pnode, uint64_t req_clk, uint64_t req_bw, + uint64_t curr_clk, uint64_t curr_bw, unsigned int ctx, unsigned int + cl_active_flag) +{ + int index, ret = 0; + struct msm_bus_inode_info *info; + struct msm_bus_inode_info *src_info; + int next_pnode; + int64_t add_bw = req_bw - curr_bw; + uint64_t bwsum = 0; + uint64_t req_clk_hz, curr_clk_hz, bwsum_hz; + int *master_tiers; + struct msm_bus_fabric_device *fabdev = msm_bus_get_fabric_device + (GET_FABID(curr)); + + if (!fabdev) { + MSM_BUS_ERR("Bus device for bus ID: %d not found!\n", + GET_FABID(curr)); + return -ENXIO; + } + + MSM_BUS_DBG("args: %d %d %d %llu %llu %llu %llu %u\n", + curr, GET_NODE(pnode), GET_INDEX(pnode), req_clk, req_bw, + curr_clk, curr_bw, ctx); + index = GET_INDEX(pnode); + MSM_BUS_DBG("Client passed index :%d\n", index); + info = fabdev->algo->find_node(fabdev, curr); + if (!info) { + MSM_BUS_ERR("Cannot find node info!\n"); + return -ENXIO; + } + src_info = info; + + info->link_info.sel_bw = &info->link_info.bw[ctx]; + info->link_info.sel_clk = &info->link_info.clk[ctx]; + *info->link_info.sel_bw += add_bw; + + info->pnode[index].sel_bw = &info->pnode[index].bw[ctx]; + + /** + * To select the right clock, AND the context with + * client active flag. + */ + info->pnode[index].sel_clk = &info->pnode[index].clk[ctx & + cl_active_flag]; + *info->pnode[index].sel_bw += add_bw; + *info->pnode[index].sel_clk = req_clk; + + /** + * If master supports dual configuration, check if + * the configuration needs to be changed based on + * incoming requests + */ + if (info->node_info->dual_conf) { + uint64_t node_maxib = 0; + node_maxib = get_node_maxib(info); + fabdev->algo->config_master(fabdev, info, + node_maxib, req_bw); + } + + info->link_info.num_tiers = info->node_info->num_tiers; + info->link_info.tier = info->node_info->tier; + master_tiers = info->node_info->tier; + + do { + struct msm_bus_inode_info *hop; + fabdev = msm_bus_get_fabric_device(GET_FABID(curr)); + if (!fabdev) { + MSM_BUS_ERR("Fabric not found\n"); + return -ENXIO; + } + MSM_BUS_DBG("id: %d\n", info->node_info->priv_id); + + /* find next node and index */ + next_pnode = info->pnode[index].next; + curr = GET_NODE(next_pnode); + index = GET_INDEX(next_pnode); + MSM_BUS_DBG("id:%d, next: %d\n", info-> + node_info->priv_id, curr); + + /* Get hop */ + /* check if we are here as gateway, or does the hop belong to + * this fabric */ + if (IS_NODE(curr)) + hop = fabdev->algo->find_node(fabdev, curr); + else + hop = fabdev->algo->find_gw_node(fabdev, curr); + if (!hop) { + MSM_BUS_ERR("Null Info found for hop\n"); + return -ENXIO; + } + + hop->link_info.sel_bw = &hop->link_info.bw[ctx]; + hop->link_info.sel_clk = &hop->link_info.clk[ctx]; + *hop->link_info.sel_bw += add_bw; + + hop->pnode[index].sel_bw = &hop->pnode[index].bw[ctx]; + hop->pnode[index].sel_clk = &hop->pnode[index].clk[ctx & + cl_active_flag]; + + if (!hop->node_info->buswidth) { + MSM_BUS_WARN("No bus width found. Using default\n"); + hop->node_info->buswidth = 8; + } + *hop->pnode[index].sel_clk = BW_TO_CLK_FREQ_HZ(hop->node_info-> + buswidth, req_clk); + *hop->pnode[index].sel_bw += add_bw; + MSM_BUS_DBG("fabric: %d slave: %d, slave-width: %d info: %d\n", + fabdev->id, hop->node_info->priv_id, hop->node_info-> + buswidth, info->node_info->priv_id); + /* Update Bandwidth */ + fabdev->algo->update_bw(fabdev, hop, info, add_bw, + master_tiers, ctx); + bwsum = *hop->link_info.sel_bw; + /* Update Fabric clocks */ + curr_clk_hz = BW_TO_CLK_FREQ_HZ(hop->node_info->buswidth, + curr_clk); + req_clk_hz = BW_TO_CLK_FREQ_HZ(hop->node_info->buswidth, + req_clk); + bwsum_hz = BW_TO_CLK_FREQ_HZ(hop->node_info->buswidth, + bwsum); + /* Account for multiple channels if any */ + if (hop->node_info->num_sports > 1) + bwsum_hz = msm_bus_div64(hop->node_info->num_sports, + bwsum_hz); + MSM_BUS_DBG("AXI: Hop: %d, ports: %d, bwsum_hz: %llu\n", + hop->node_info->id, hop->node_info->num_sports, + bwsum_hz); + MSM_BUS_DBG("up-clk: curr_hz: %llu, req_hz: %llu, bw_hz %llu\n", + curr_clk, req_clk, bwsum_hz); + ret = fabdev->algo->update_clks(fabdev, hop, index, + curr_clk_hz, req_clk_hz, bwsum_hz, SEL_FAB_CLK, + ctx, cl_active_flag); + if (ret) + MSM_BUS_WARN("Failed to update clk\n"); + info = hop; + } while (GET_NODE(info->pnode[index].next) != info->node_info->priv_id); + + /* Update BW, clk after exiting the loop for the last one */ + if (!info) { + MSM_BUS_ERR("Cannot find node info!\n"); + return -ENXIO; + } + + /* Update slave clocks */ + ret = fabdev->algo->update_clks(fabdev, info, index, curr_clk_hz, + req_clk_hz, bwsum_hz, SEL_SLAVE_CLK, ctx, cl_active_flag); + if (ret) + MSM_BUS_ERR("Failed to update clk\n"); + + if ((ctx == cl_active_flag) && + ((src_info->node_info->nr_lim || src_info->node_info->rt_mas))) + setup_nr_limits(curr, pnode); + + /* If freq is going down , apply the changes now before + * we commit clk data. + */ + if ((req_clk < curr_clk) || (req_bw < curr_bw)) + bus_for_each_dev(&msm_bus_type, NULL, NULL, + msm_bus_commit_limiter); + return ret; +} + +/** + * msm_bus_commit_fn() - Commits the data for fabric to rpm + * @dev: fabric device + * @data: NULL + */ +static int msm_bus_commit_fn(struct device *dev, void *data) +{ + int ret = 0; + struct msm_bus_fabric_device *fabdev = to_msm_bus_fabric_device(dev); + MSM_BUS_DBG("Committing: fabid: %d\n", fabdev->id); + ret = fabdev->algo->commit(fabdev); + return ret; +} + +static uint32_t register_client_legacy(struct msm_bus_scale_pdata *pdata) +{ + struct msm_bus_client *client = NULL; + int i; + int src, dest, nfab; + struct msm_bus_fabric_device *deffab; + + deffab = msm_bus_get_fabric_device(MSM_BUS_FAB_DEFAULT); + if (!deffab) { + MSM_BUS_ERR("Error finding default fabric\n"); + return 0; + } + + nfab = msm_bus_get_num_fab(); + if (nfab < deffab->board_algo->board_nfab) { + MSM_BUS_ERR("Can't register client!\n" + "Num of fabrics up: %d\n", + nfab); + return 0; + } + + if ((!pdata) || (pdata->usecase->num_paths == 0) || IS_ERR(pdata)) { + MSM_BUS_ERR("Cannot register client with null data\n"); + return 0; + } + + client = kzalloc(sizeof(struct msm_bus_client), GFP_KERNEL); + if (!client) { + MSM_BUS_ERR("Error allocating client\n"); + return 0; + } + + mutex_lock(&msm_bus_lock); + client->pdata = pdata; + client->curr = -1; + for (i = 0; i < pdata->usecase->num_paths; i++) { + int *pnode; + struct msm_bus_fabric_device *srcfab; + pnode = krealloc(client->src_pnode, ((i + 1) * sizeof(int)), + GFP_KERNEL); + if (ZERO_OR_NULL_PTR(pnode)) { + MSM_BUS_ERR("Invalid Pnode ptr!\n"); + continue; + } else + client->src_pnode = pnode; + + if (!IS_MASTER_VALID(pdata->usecase->vectors[i].src)) { + MSM_BUS_ERR("Invalid Master ID %d in request!\n", + pdata->usecase->vectors[i].src); + goto err; + } + + if (!IS_SLAVE_VALID(pdata->usecase->vectors[i].dst)) { + MSM_BUS_ERR("Invalid Slave ID %d in request!\n", + pdata->usecase->vectors[i].dst); + goto err; + } + + src = msm_bus_board_get_iid(pdata->usecase->vectors[i].src); + if (src == -ENXIO) { + MSM_BUS_ERR("Master %d not supported. Client cannot be" + " registered\n", + pdata->usecase->vectors[i].src); + goto err; + } + dest = msm_bus_board_get_iid(pdata->usecase->vectors[i].dst); + if (dest == -ENXIO) { + MSM_BUS_ERR("Slave %d not supported. Client cannot be" + " registered\n", + pdata->usecase->vectors[i].dst); + goto err; + } + srcfab = msm_bus_get_fabric_device(GET_FABID(src)); + if (!srcfab) { + MSM_BUS_ERR("Fabric not found\n"); + goto err; + } + + srcfab->visited = true; + pnode[i] = getpath(src, dest); + bus_for_each_dev(&msm_bus_type, NULL, NULL, clearvisitedflag); + if (pnode[i] == -ENXIO) { + MSM_BUS_ERR("Cannot register client now! Try again!\n"); + goto err; + } + } + msm_bus_dbg_client_data(client->pdata, MSM_BUS_DBG_REGISTER, + (uint32_t)client); + mutex_unlock(&msm_bus_lock); + MSM_BUS_DBG("ret: %u num_paths: %d\n", (uint32_t)client, + pdata->usecase->num_paths); + return (uint32_t)(client); +err: + kfree(client->src_pnode); + kfree(client); + mutex_unlock(&msm_bus_lock); + return 0; +} + +static int update_request_legacy(uint32_t cl, unsigned index) +{ + int i, ret = 0; + struct msm_bus_scale_pdata *pdata; + int pnode, src = 0, curr, ctx; + uint64_t req_clk = 0, req_bw = 0, curr_clk = 0, curr_bw = 0; + struct msm_bus_client *client = (struct msm_bus_client *)cl; + if (IS_ERR_OR_NULL(client)) { + MSM_BUS_ERR("msm_bus_scale_client update req error %d\n", + (uint32_t)client); + return -ENXIO; + } + + mutex_lock(&msm_bus_lock); + if (client->curr == index) + goto err; + + curr = client->curr; + pdata = client->pdata; + if (!pdata) { + MSM_BUS_ERR("Null pdata passed to update-request\n"); + ret = -ENXIO; + goto err; + } + + if (index >= pdata->num_usecases) { + MSM_BUS_ERR("Client %u passed invalid index: %d\n", + (uint32_t)client, index); + ret = -ENXIO; + goto err; + } + + MSM_BUS_DBG("cl: %u index: %d curr: %d num_paths: %d\n", + cl, index, client->curr, client->pdata->usecase->num_paths); + + for (i = 0; i < pdata->usecase->num_paths; i++) { + src = msm_bus_board_get_iid(client->pdata->usecase[index]. + vectors[i].src); + if (src == -ENXIO) { + MSM_BUS_ERR("Master %d not supported. Request cannot" + " be updated\n", client->pdata->usecase-> + vectors[i].src); + goto err; + } + + if (msm_bus_board_get_iid(client->pdata->usecase[index]. + vectors[i].dst) == -ENXIO) { + MSM_BUS_ERR("Slave %d not supported. Request cannot" + " be updated\n", client->pdata->usecase-> + vectors[i].dst); + } + + pnode = client->src_pnode[i]; + req_clk = client->pdata->usecase[index].vectors[i].ib; + req_bw = client->pdata->usecase[index].vectors[i].ab; + if (curr < 0) { + curr_clk = 0; + curr_bw = 0; + } else { + curr_clk = client->pdata->usecase[curr].vectors[i].ib; + curr_bw = client->pdata->usecase[curr].vectors[i].ab; + MSM_BUS_DBG("ab: %llu ib: %llu\n", curr_bw, curr_clk); + } + + if (!pdata->active_only) { + ret = update_path(src, pnode, req_clk, req_bw, + curr_clk, curr_bw, 0, pdata->active_only); + if (ret) { + MSM_BUS_ERR("Update path failed! %d\n", ret); + goto err; + } + } + + ret = update_path(src, pnode, req_clk, req_bw, curr_clk, + curr_bw, ACTIVE_CTX, pdata->active_only); + if (ret) { + MSM_BUS_ERR("Update Path failed! %d\n", ret); + goto err; + } + } + + client->curr = index; + ctx = ACTIVE_CTX; + msm_bus_dbg_client_data(client->pdata, index, cl); + bus_for_each_dev(&msm_bus_type, NULL, NULL, msm_bus_commit_fn); + + /* For NR/RT limited masters, if freq is going up , apply the changes + * after we commit clk data. + */ + if (is_nr_lim(src) && ((req_clk > curr_clk) || (req_bw > curr_bw))) + bus_for_each_dev(&msm_bus_type, NULL, NULL, + msm_bus_commit_limiter); + +err: + mutex_unlock(&msm_bus_lock); + return ret; +} + +static int reset_pnodes(int curr, int pnode) +{ + struct msm_bus_inode_info *info; + struct msm_bus_fabric_device *fabdev; + int index, next_pnode; + fabdev = msm_bus_get_fabric_device(GET_FABID(curr)); + if (!fabdev) { + MSM_BUS_ERR("Fabric not found for: %d\n", + (GET_FABID(curr))); + return -ENXIO; + } + + index = GET_INDEX(pnode); + info = fabdev->algo->find_node(fabdev, curr); + if (!info) { + MSM_BUS_ERR("Cannot find node info!\n"); + return -ENXIO; + } + + MSM_BUS_DBG("Starting the loop--remove\n"); + do { + struct msm_bus_inode_info *hop; + fabdev = msm_bus_get_fabric_device(GET_FABID(curr)); + if (!fabdev) { + MSM_BUS_ERR("Fabric not found\n"); + return -ENXIO; + } + + next_pnode = info->pnode[index].next; + info->pnode[index].next = -2; + curr = GET_NODE(next_pnode); + index = GET_INDEX(next_pnode); + if (IS_NODE(curr)) + hop = fabdev->algo->find_node(fabdev, curr); + else + hop = fabdev->algo->find_gw_node(fabdev, curr); + if (!hop) { + MSM_BUS_ERR("Null Info found for hop\n"); + return -ENXIO; + } + + MSM_BUS_DBG("%d[%d] = %d\n", info->node_info->priv_id, index, + info->pnode[index].next); + MSM_BUS_DBG("num_pnodes: %d: %d\n", info->node_info->priv_id, + info->num_pnodes); + info = hop; + } while (GET_NODE(info->pnode[index].next) != info->node_info->priv_id); + + info->pnode[index].next = -2; + MSM_BUS_DBG("%d[%d] = %d\n", info->node_info->priv_id, index, + info->pnode[index].next); + MSM_BUS_DBG("num_pnodes: %d: %d\n", info->node_info->priv_id, + info->num_pnodes); + return 0; +} + +int msm_bus_board_get_iid(int id) +{ + struct msm_bus_fabric_device *deffab; + + deffab = msm_bus_get_fabric_device(MSM_BUS_FAB_DEFAULT); + if (!deffab) { + MSM_BUS_ERR("Error finding default fabric\n"); + return -ENXIO; + } + + return deffab->board_algo->get_iid(id); +} + +void msm_bus_scale_client_reset_pnodes(uint32_t cl) +{ + int i, src, pnode, index; + struct msm_bus_client *client = (struct msm_bus_client *)(cl); + if (IS_ERR_OR_NULL(client)) { + MSM_BUS_ERR("msm_bus_scale_reset_pnodes error\n"); + return; + } + index = 0; + for (i = 0; i < client->pdata->usecase->num_paths; i++) { + src = msm_bus_board_get_iid( + client->pdata->usecase[index].vectors[i].src); + pnode = client->src_pnode[i]; + MSM_BUS_DBG("(%d, %d)\n", GET_NODE(pnode), GET_INDEX(pnode)); + reset_pnodes(src, pnode); + } +} + +static void unregister_client_legacy(uint32_t cl) +{ + int i; + struct msm_bus_client *client = (struct msm_bus_client *)(cl); + bool warn = false; + if (IS_ERR_OR_NULL(client)) + return; + + for (i = 0; i < client->pdata->usecase->num_paths; i++) { + if ((client->pdata->usecase[0].vectors[i].ab) || + (client->pdata->usecase[0].vectors[i].ib)) { + warn = true; + break; + } + } + + if (warn) { + int num_paths = client->pdata->usecase->num_paths; + int ab[num_paths], ib[num_paths]; + WARN(1, "%s called unregister with non-zero vectors\n", + client->pdata->name); + + /* + * Save client values and zero them out to + * cleanly unregister + */ + for (i = 0; i < num_paths; i++) { + ab[i] = client->pdata->usecase[0].vectors[i].ab; + ib[i] = client->pdata->usecase[0].vectors[i].ib; + client->pdata->usecase[0].vectors[i].ab = 0; + client->pdata->usecase[0].vectors[i].ib = 0; + } + + msm_bus_scale_client_update_request(cl, 0); + + /* Restore client vectors if required for re-registering. */ + for (i = 0; i < num_paths; i++) { + client->pdata->usecase[0].vectors[i].ab = ab[i]; + client->pdata->usecase[0].vectors[i].ib = ib[i]; + } + } else if (client->curr != 0) + msm_bus_scale_client_update_request(cl, 0); + + MSM_BUS_DBG("Unregistering client %d\n", cl); + mutex_lock(&msm_bus_lock); + msm_bus_scale_client_reset_pnodes(cl); + msm_bus_dbg_client_data(client->pdata, MSM_BUS_DBG_UNREGISTER, cl); + mutex_unlock(&msm_bus_lock); + kfree(client->src_pnode); + kfree(client); +} + +void msm_bus_arb_setops_legacy(struct msm_bus_arb_ops *arb_ops) +{ + arb_ops->register_client = register_client_legacy; + arb_ops->update_request = update_request_legacy; + arb_ops->unregister_client = unregister_client_legacy; +} + |