/* Copyright (c) 2012-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 #include #include #include #include #include #include #include #include "msm_bus_core.h" #define KBTOB(a) (a * 1000ULL) static const char * const hw_sel_name[] = {"RPM", "NoC", "BIMC", NULL}; static const char * const mode_sel_name[] = {"Fixed", "Limiter", "Bypass", "Regulator", NULL}; static int get_num(const char *const str[], const char *name) { int i = 0; do { if (!strcmp(name, str[i])) return i; i++; } while (str[i] != NULL); pr_err("Error: string %s not found\n", name); return -EINVAL; } static struct msm_bus_scale_pdata *get_pdata(struct platform_device *pdev, struct device_node *of_node) { struct msm_bus_scale_pdata *pdata = NULL; struct msm_bus_paths *usecase = NULL; int i = 0, j, ret, num_usecases = 0, num_paths, len; const uint32_t *vec_arr = NULL; bool mem_err = false; if (!pdev) { pr_err("Error: Null Platform device\n"); return NULL; } pdata = devm_kzalloc(&pdev->dev, sizeof(struct msm_bus_scale_pdata), GFP_KERNEL); if (!pdata) { pr_err("Error: Memory allocation for pdata failed\n"); mem_err = true; goto err; } ret = of_property_read_string(of_node, "qcom,msm-bus,name", &pdata->name); if (ret) { pr_err("Error: Client name not found\n"); goto err; } ret = of_property_read_u32(of_node, "qcom,msm-bus,num-cases", &num_usecases); if (ret) { pr_err("Error: num-usecases not found\n"); goto err; } pdata->num_usecases = num_usecases; if (of_property_read_bool(of_node, "qcom,msm-bus,active-only")) pdata->active_only = 1; else { pr_debug("active_only flag absent.\n"); pr_debug("Using dual context by default\n"); } usecase = devm_kzalloc(&pdev->dev, (sizeof(struct msm_bus_paths) * pdata->num_usecases), GFP_KERNEL); if (!usecase) { pr_err("Error: Memory allocation for paths failed\n"); mem_err = true; goto err; } ret = of_property_read_u32(of_node, "qcom,msm-bus,num-paths", &num_paths); if (ret) { pr_err("Error: num_paths not found\n"); goto err; } vec_arr = of_get_property(of_node, "qcom,msm-bus,vectors-KBps", &len); if (vec_arr == NULL) { pr_err("Error: Vector array not found\n"); goto err; } if (len != num_usecases * num_paths * sizeof(uint32_t) * 4) { pr_err("Error: Length-error on getting vectors\n"); goto err; } for (i = 0; i < num_usecases; i++) { usecase[i].num_paths = num_paths; usecase[i].vectors = devm_kzalloc(&pdev->dev, num_paths * sizeof(struct msm_bus_vectors), GFP_KERNEL); if (!usecase[i].vectors) { mem_err = true; pr_err("Error: Mem alloc failure in vectors\n"); goto err; } for (j = 0; j < num_paths; j++) { int index = ((i * num_paths) + j) * 4; usecase[i].vectors[j].src = be32_to_cpu(vec_arr[index]); usecase[i].vectors[j].dst = be32_to_cpu(vec_arr[index + 1]); usecase[i].vectors[j].ab = (uint64_t) KBTOB(be32_to_cpu(vec_arr[index + 2])); usecase[i].vectors[j].ib = (uint64_t) KBTOB(be32_to_cpu(vec_arr[index + 3])); } } pdata->usecase = usecase; return pdata; err: if (mem_err) { for (; i > 0; i--) kfree(usecase[i-1].vectors); kfree(usecase); kfree(pdata); } return NULL; } /** * msm_bus_cl_get_pdata() - Generate bus client data from device tree * provided by clients. * * of_node: Device tree node to extract information from * * The function returns a valid pointer to the allocated bus-scale-pdata * if the vectors were correctly read from the client's device node. * Any error in reading or parsing the device node will return NULL * to the caller. */ struct msm_bus_scale_pdata *msm_bus_cl_get_pdata(struct platform_device *pdev) { struct device_node *of_node; struct msm_bus_scale_pdata *pdata = NULL; if (!pdev) { pr_err("Error: Null Platform device\n"); return NULL; } of_node = pdev->dev.of_node; pdata = get_pdata(pdev, of_node); if (!pdata) { pr_err("Error getting bus pdata!\n"); return NULL; } return pdata; } EXPORT_SYMBOL(msm_bus_cl_get_pdata); /** * msm_bus_cl_pdata_from_node() - Generate bus client data from device tree * node provided by clients. This function should be used when a client * driver needs to register multiple bus-clients from a single device-tree * node associated with the platform-device. * * of_node: The subnode containing information about the bus scaling * data * * pdev: Platform device associated with the device-tree node * * The function returns a valid pointer to the allocated bus-scale-pdata * if the vectors were correctly read from the client's device node. * Any error in reading or parsing the device node will return NULL * to the caller. */ struct msm_bus_scale_pdata *msm_bus_pdata_from_node( struct platform_device *pdev, struct device_node *of_node) { struct msm_bus_scale_pdata *pdata = NULL; if (!pdev) { pr_err("Error: Null Platform device\n"); return NULL; } if (!of_node) { pr_err("Error: Null of_node passed to bus driver\n"); return NULL; } pdata = get_pdata(pdev, of_node); if (!pdata) { pr_err("Error getting bus pdata!\n"); return NULL; } return pdata; } EXPORT_SYMBOL(msm_bus_pdata_from_node); /** * msm_bus_cl_clear_pdata() - Clear pdata allocated from device-tree * of_node: Device tree node to extract information from */ void msm_bus_cl_clear_pdata(struct msm_bus_scale_pdata *pdata) { int i; for (i = 0; i < pdata->num_usecases; i++) kfree(pdata->usecase[i].vectors); kfree(pdata->usecase); kfree(pdata); } EXPORT_SYMBOL(msm_bus_cl_clear_pdata); static int *get_arr(struct platform_device *pdev, const struct device_node *node, const char *prop, int *nports) { int size = 0, ret; int *arr = NULL; if (of_get_property(node, prop, &size)) { *nports = size / sizeof(int); } else { pr_debug("Property %s not available\n", prop); *nports = 0; return NULL; } arr = devm_kzalloc(&pdev->dev, size, GFP_KERNEL); if ((size > 0) && ZERO_OR_NULL_PTR(arr)) { pr_err("Error: Failed to alloc mem for %s\n", prop); return NULL; } ret = of_property_read_u32_array(node, prop, (u32 *)arr, *nports); if (ret) { pr_err("Error in reading property: %s\n", prop); goto err; } return arr; err: devm_kfree(&pdev->dev, arr); return NULL; } static struct msm_bus_node_info *get_nodes(struct device_node *of_node, struct platform_device *pdev, struct msm_bus_fabric_registration *pdata) { struct msm_bus_node_info *info; struct device_node *child_node = NULL; int i = 0, ret; u32 temp; for_each_child_of_node(of_node, child_node) { i++; } pdata->len = i; info = (struct msm_bus_node_info *) devm_kzalloc(&pdev->dev, sizeof(struct msm_bus_node_info) * pdata->len, GFP_KERNEL); if (ZERO_OR_NULL_PTR(info)) { pr_err("Failed to alloc memory for nodes: %d\n", pdata->len); goto err; } i = 0; child_node = NULL; for_each_child_of_node(of_node, child_node) { const char *sel_str; ret = of_property_read_string(child_node, "label", &info[i].name); if (ret) pr_err("Error reading node label\n"); ret = of_property_read_u32(child_node, "cell-id", &info[i].id); if (ret) { pr_err("Error reading node id\n"); goto err; } if (of_property_read_bool(child_node, "qcom,gateway")) info[i].gateway = 1; of_property_read_u32(child_node, "qcom,mas-hw-id", &info[i].mas_hw_id); of_property_read_u32(child_node, "qcom,slv-hw-id", &info[i].slv_hw_id); info[i].masterp = get_arr(pdev, child_node, "qcom,masterp", &info[i].num_mports); /* No need to store number of qports */ info[i].qport = get_arr(pdev, child_node, "qcom,qport", &ret); pdata->nmasters += info[i].num_mports; info[i].slavep = get_arr(pdev, child_node, "qcom,slavep", &info[i].num_sports); pdata->nslaves += info[i].num_sports; info[i].tier = get_arr(pdev, child_node, "qcom,tier", &info[i].num_tiers); if (of_property_read_bool(child_node, "qcom,ahb")) info[i].ahb = 1; ret = of_property_read_string(child_node, "qcom,hw-sel", &sel_str); if (ret) info[i].hw_sel = 0; else { ret = get_num(hw_sel_name, sel_str); if (ret < 0) { pr_err("Invalid hw-sel\n"); goto err; } info[i].hw_sel = ret; } of_property_read_u32(child_node, "qcom,buswidth", &info[i].buswidth); of_property_read_u32(child_node, "qcom,ws", &info[i].ws); ret = of_property_read_u32(child_node, "qcom,thresh", &temp); if (!ret) info[i].th = (uint64_t)KBTOB(temp); ret = of_property_read_u32(child_node, "qcom,bimc,bw", &temp); if (!ret) info[i].bimc_bw = (uint64_t)KBTOB(temp); of_property_read_u32(child_node, "qcom,bimc,gp", &info[i].bimc_gp); of_property_read_u32(child_node, "qcom,bimc,thmp", &info[i].bimc_thmp); ret = of_property_read_string(child_node, "qcom,mode", &sel_str); if (ret) info[i].mode = 0; else { ret = get_num(mode_sel_name, sel_str); if (ret < 0) { pr_err("Unknown mode :%s\n", sel_str); goto err; } info[i].mode = ret; } info[i].dual_conf = of_property_read_bool(child_node, "qcom,dual-conf"); ret = of_property_read_string(child_node, "qcom,mode-thresh", &sel_str); if (ret) info[i].mode_thresh = 0; else { ret = get_num(mode_sel_name, sel_str); if (ret < 0) { pr_err("Unknown mode :%s\n", sel_str); goto err; } info[i].mode_thresh = ret; MSM_BUS_DBG("AXI: THreshold mode set: %d\n", info[i].mode_thresh); } ret = of_property_read_string(child_node, "qcom,perm-mode", &sel_str); if (ret) info[i].perm_mode = 0; else { ret = get_num(mode_sel_name, sel_str); if (ret < 0) goto err; info[i].perm_mode = 1 << ret; } of_property_read_u32(child_node, "qcom,prio-lvl", &info[i].prio_lvl); of_property_read_u32(child_node, "qcom,prio-rd", &info[i].prio_rd); of_property_read_u32(child_node, "qcom,prio-wr", &info[i].prio_wr); of_property_read_u32(child_node, "qcom,prio0", &info[i].prio0); of_property_read_u32(child_node, "qcom,prio1", &info[i].prio1); ret = of_property_read_string(child_node, "qcom,slaveclk-dual", &info[i].slaveclk[DUAL_CTX]); if (!ret) pr_debug("Got slaveclk_dual: %s\n", info[i].slaveclk[DUAL_CTX]); else info[i].slaveclk[DUAL_CTX] = NULL; ret = of_property_read_string(child_node, "qcom,slaveclk-active", &info[i].slaveclk[ACTIVE_CTX]); if (!ret) pr_debug("Got slaveclk_active\n"); else info[i].slaveclk[ACTIVE_CTX] = NULL; ret = of_property_read_string(child_node, "qcom,memclk-dual", &info[i].memclk[DUAL_CTX]); if (!ret) pr_debug("Got memclk_dual\n"); else info[i].memclk[DUAL_CTX] = NULL; ret = of_property_read_string(child_node, "qcom,memclk-active", &info[i].memclk[ACTIVE_CTX]); if (!ret) pr_debug("Got memclk_active\n"); else info[i].memclk[ACTIVE_CTX] = NULL; ret = of_property_read_string(child_node, "qcom,iface-clk-node", &info[i].iface_clk_node); if (!ret) pr_debug("Got iface_clk_node\n"); else info[i].iface_clk_node = NULL; pr_debug("Node name: %s\n", info[i].name); of_node_put(child_node); i++; } pr_debug("Bus %d added: %d masters\n", pdata->id, pdata->nmasters); pr_debug("Bus %d added: %d slaves\n", pdata->id, pdata->nslaves); return info; err: return NULL; } struct msm_bus_fabric_registration *msm_bus_of_get_fab_data(struct platform_device *pdev) { struct device_node *of_node; struct msm_bus_fabric_registration *pdata; bool mem_err = false; int ret = 0; const char *sel_str; if (!pdev) { pr_err("Error: Null platform device\n"); return NULL; } of_node = pdev->dev.of_node; pdata = devm_kzalloc(&pdev->dev, sizeof(struct msm_bus_fabric_registration), GFP_KERNEL); if (!pdata) { pr_err("Error: Memory allocation for pdata failed\n"); mem_err = true; goto err; } ret = of_property_read_string(of_node, "label", &pdata->name); if (ret) { pr_err("Error: label not found\n"); goto err; } pr_debug("Fab_of: Read name: %s\n", pdata->name); ret = of_property_read_u32(of_node, "cell-id", &pdata->id); if (ret) { pr_err("Error: num-usecases not found\n"); goto err; } pr_debug("Fab_of: Read id: %u\n", pdata->id); if (of_property_read_bool(of_node, "qcom,ahb")) pdata->ahb = 1; ret = of_property_read_string(of_node, "qcom,fabclk-dual", &pdata->fabclk[DUAL_CTX]); if (ret) { pr_debug("fabclk_dual not available\n"); pdata->fabclk[DUAL_CTX] = NULL; } else pr_debug("Fab_of: Read clk dual ctx: %s\n", pdata->fabclk[DUAL_CTX]); ret = of_property_read_string(of_node, "qcom,fabclk-active", &pdata->fabclk[ACTIVE_CTX]); if (ret) { pr_debug("Error: fabclk_active not available\n"); pdata->fabclk[ACTIVE_CTX] = NULL; } else pr_debug("Fab_of: Read clk act ctx: %s\n", pdata->fabclk[ACTIVE_CTX]); ret = of_property_read_u32(of_node, "qcom,ntieredslaves", &pdata->ntieredslaves); if (ret) { pr_err("Error: ntieredslaves not found\n"); goto err; } ret = of_property_read_u32(of_node, "qcom,qos-freq", &pdata->qos_freq); if (ret) pr_debug("qos_freq not available\n"); ret = of_property_read_string(of_node, "qcom,hw-sel", &sel_str); if (ret) { pr_err("Error: hw_sel not found\n"); goto err; } else { ret = get_num(hw_sel_name, sel_str); if (ret < 0) goto err; pdata->hw_sel = ret; } if (of_property_read_bool(of_node, "qcom,virt")) pdata->virt = true; if (of_property_read_bool(of_node, "qcom,rpm-en")) pdata->rpm_enabled = 1; pdata->info = get_nodes(of_node, pdev, pdata); return pdata; err: return NULL; } EXPORT_SYMBOL(msm_bus_of_get_fab_data);