| /* Copyright (c) 2011-2013, 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 <mach/msm_bus.h> |
| #include "msm_bus_core.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 |
| |
| #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); |
| } |
| |
| /** |
| * 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; |
| 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; |
| } |
| |
| /** |
| * If master supports dual configuration, check if |
| * the configuration needs to be changed based on |
| * incoming requests |
| */ |
| if (info->node_info->dual_conf) |
| fabdev->algo->config_master(fabdev, info, |
| req_clk, req_bw); |
| |
| 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->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"); |
| 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; |
| } |
| |
| /** |
| * msm_bus_scale_register_client() - Register the clients with the msm bus |
| * driver |
| * @pdata: Platform data of the client, containing src, dest, ab, ib |
| * |
| * Client data contains the vectors specifying arbitrated bandwidth (ab) |
| * and instantaneous bandwidth (ib) requested between a particular |
| * src and dest. |
| */ |
| uint32_t msm_bus_scale_register_client(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 -ENXIO; |
| } |
| |
| 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; |
| } |
| EXPORT_SYMBOL(msm_bus_scale_register_client); |
| |
| /** |
| * msm_bus_scale_client_update_request() - Update the request for bandwidth |
| * from a particular client |
| * |
| * cl: Handle to the client |
| * index: Index into the vector, to which the bw and clock values need to be |
| * updated |
| */ |
| int msm_bus_scale_client_update_request(uint32_t cl, unsigned index) |
| { |
| int i, ret = 0; |
| struct msm_bus_scale_pdata *pdata; |
| int pnode, src, curr, ctx; |
| uint64_t req_clk, req_bw, curr_clk, curr_bw; |
| 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"); |
| return -ENXIO; |
| } |
| |
| 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 (index == 0) { |
| /* This check protects the bus driver from clients |
| * that can leave non-zero requests after |
| * unregistering. |
| * */ |
| req_clk = 0; |
| req_bw = 0; |
| } |
| |
| 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); |
| |
| err: |
| mutex_unlock(&msm_bus_lock); |
| return ret; |
| } |
| EXPORT_SYMBOL(msm_bus_scale_client_update_request); |
| |
| 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); |
| } |
| } |
| |
| /** |
| * msm_bus_scale_unregister_client() - Unregister the client from the bus driver |
| * @cl: Handle to the client |
| */ |
| void msm_bus_scale_unregister_client(uint32_t cl) |
| { |
| struct msm_bus_client *client = (struct msm_bus_client *)(cl); |
| if (IS_ERR_OR_NULL(client)) |
| return; |
| 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); |
| } |
| EXPORT_SYMBOL(msm_bus_scale_unregister_client); |
| |