884 lines
23 KiB
C
884 lines
23 KiB
C
/* Copyright (c) 2014-2015, 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/clk.h>
|
|
#include <linux/device.h>
|
|
#include <linux/module.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/string.h>
|
|
#include <linux/of.h>
|
|
#include <linux/of_device.h>
|
|
#include <linux/platform_device.h>
|
|
#include <linux/msm-bus.h>
|
|
#include <linux/msm-bus-board.h>
|
|
#include <linux/msm_bus_rules.h>
|
|
#include "msm_bus_core.h"
|
|
#include "msm_bus_adhoc.h"
|
|
|
|
#define DEFAULT_QOS_FREQ 19200
|
|
#define DEFAULT_UTIL_FACT 100
|
|
#define DEFAULT_VRAIL_COMP 100
|
|
#define DEFAULT_AGG_SCHEME AGG_SCHEME_LEG
|
|
|
|
static int get_qos_mode(struct platform_device *pdev,
|
|
struct device_node *node, const char *qos_mode)
|
|
{
|
|
const char *qos_names[] = {"fixed", "limiter", "bypass", "regulator"};
|
|
int i = 0;
|
|
int ret = -1;
|
|
|
|
if (!qos_mode)
|
|
goto exit_get_qos_mode;
|
|
|
|
for (i = 0; i < ARRAY_SIZE(qos_names); i++) {
|
|
if (!strcmp(qos_mode, qos_names[i]))
|
|
break;
|
|
}
|
|
if (i == ARRAY_SIZE(qos_names))
|
|
dev_err(&pdev->dev, "Cannot match mode qos %s using Bypass",
|
|
qos_mode);
|
|
else
|
|
ret = i;
|
|
|
|
exit_get_qos_mode:
|
|
return ret;
|
|
}
|
|
|
|
static int *get_arr(struct platform_device *pdev,
|
|
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 {
|
|
dev_dbg(&pdev->dev, "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)) {
|
|
dev_err(&pdev->dev, "Error: Failed to alloc mem for %s\n",
|
|
prop);
|
|
return NULL;
|
|
}
|
|
|
|
ret = of_property_read_u32_array(node, prop, (u32 *)arr, *nports);
|
|
if (ret) {
|
|
dev_err(&pdev->dev, "Error in reading property: %s\n", prop);
|
|
goto arr_err;
|
|
}
|
|
|
|
return arr;
|
|
arr_err:
|
|
devm_kfree(&pdev->dev, arr);
|
|
return NULL;
|
|
}
|
|
|
|
static struct msm_bus_fab_device_type *get_fab_device_info(
|
|
struct device_node *dev_node,
|
|
struct platform_device *pdev)
|
|
{
|
|
struct msm_bus_fab_device_type *fab_dev;
|
|
unsigned int ret;
|
|
struct resource *res;
|
|
const char *base_name;
|
|
|
|
fab_dev = devm_kzalloc(&pdev->dev,
|
|
sizeof(struct msm_bus_fab_device_type),
|
|
GFP_KERNEL);
|
|
if (!fab_dev) {
|
|
dev_err(&pdev->dev,
|
|
"Error: Unable to allocate memory for fab_dev\n");
|
|
return NULL;
|
|
}
|
|
|
|
ret = of_property_read_string(dev_node, "qcom,base-name", &base_name);
|
|
if (ret) {
|
|
dev_err(&pdev->dev, "Error: Unable to get base address name\n");
|
|
goto fab_dev_err;
|
|
}
|
|
|
|
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, base_name);
|
|
if (!res) {
|
|
dev_err(&pdev->dev, "Error getting qos base addr %s\n",
|
|
base_name);
|
|
goto fab_dev_err;
|
|
}
|
|
fab_dev->pqos_base = res->start;
|
|
fab_dev->qos_range = resource_size(res);
|
|
fab_dev->bypass_qos_prg = of_property_read_bool(dev_node,
|
|
"qcom,bypass-qos-prg");
|
|
|
|
ret = of_property_read_u32(dev_node, "qcom,base-offset",
|
|
&fab_dev->base_offset);
|
|
if (ret)
|
|
dev_dbg(&pdev->dev, "Bus base offset is missing\n");
|
|
|
|
ret = of_property_read_u32(dev_node, "qcom,qos-off",
|
|
&fab_dev->qos_off);
|
|
if (ret)
|
|
dev_dbg(&pdev->dev, "Bus qos off is missing\n");
|
|
|
|
|
|
ret = of_property_read_u32(dev_node, "qcom,bus-type",
|
|
&fab_dev->bus_type);
|
|
if (ret) {
|
|
dev_warn(&pdev->dev, "Bus type is missing\n");
|
|
goto fab_dev_err;
|
|
}
|
|
|
|
ret = of_property_read_u32(dev_node, "qcom,qos-freq",
|
|
&fab_dev->qos_freq);
|
|
if (ret) {
|
|
dev_dbg(&pdev->dev, "Bus qos freq is missing\n");
|
|
fab_dev->qos_freq = DEFAULT_QOS_FREQ;
|
|
}
|
|
|
|
|
|
return fab_dev;
|
|
|
|
fab_dev_err:
|
|
devm_kfree(&pdev->dev, fab_dev);
|
|
fab_dev = 0;
|
|
return NULL;
|
|
}
|
|
|
|
static void get_qos_params(
|
|
struct device_node * const dev_node,
|
|
struct platform_device * const pdev,
|
|
struct msm_bus_node_info_type *node_info)
|
|
{
|
|
const char *qos_mode = NULL;
|
|
unsigned int ret;
|
|
unsigned int temp;
|
|
|
|
ret = of_property_read_string(dev_node, "qcom,qos-mode", &qos_mode);
|
|
|
|
if (ret)
|
|
node_info->qos_params.mode = -1;
|
|
else
|
|
node_info->qos_params.mode = get_qos_mode(pdev, dev_node,
|
|
qos_mode);
|
|
|
|
of_property_read_u32(dev_node, "qcom,prio-lvl",
|
|
&node_info->qos_params.prio_lvl);
|
|
|
|
of_property_read_u32(dev_node, "qcom,prio1",
|
|
&node_info->qos_params.prio1);
|
|
|
|
of_property_read_u32(dev_node, "qcom,prio0",
|
|
&node_info->qos_params.prio0);
|
|
|
|
of_property_read_u32(dev_node, "qcom,reg-prio1",
|
|
&node_info->qos_params.reg_prio1);
|
|
|
|
of_property_read_u32(dev_node, "qcom,reg-prio0",
|
|
&node_info->qos_params.reg_prio0);
|
|
|
|
of_property_read_u32(dev_node, "qcom,prio-rd",
|
|
&node_info->qos_params.prio_rd);
|
|
|
|
of_property_read_u32(dev_node, "qcom,prio-wr",
|
|
&node_info->qos_params.prio_wr);
|
|
|
|
of_property_read_u32(dev_node, "qcom,gp",
|
|
&node_info->qos_params.gp);
|
|
|
|
of_property_read_u32(dev_node, "qcom,thmp",
|
|
&node_info->qos_params.thmp);
|
|
|
|
of_property_read_u32(dev_node, "qcom,ws",
|
|
&node_info->qos_params.ws);
|
|
|
|
ret = of_property_read_u32(dev_node, "qcom,bw_buffer", &temp);
|
|
|
|
if (ret)
|
|
node_info->qos_params.bw_buffer = 0;
|
|
else
|
|
node_info->qos_params.bw_buffer = KBTOB(temp);
|
|
|
|
}
|
|
|
|
static int msm_bus_of_parse_clk_array(struct device_node *dev_node,
|
|
struct device_node *gdsc_node,
|
|
struct platform_device *pdev, struct nodeclk **clk_arr,
|
|
int *num_clks, int id)
|
|
{
|
|
int ret = 0;
|
|
int idx = 0;
|
|
struct property *prop;
|
|
const char *clk_name;
|
|
int clks = 0;
|
|
|
|
clks = of_property_count_strings(dev_node, "clock-names");
|
|
if (clks < 0) {
|
|
pr_info("%s: No qos clks node %d\n", __func__, id);
|
|
ret = clks;
|
|
goto exit_of_parse_clk_array;
|
|
}
|
|
|
|
*num_clks = clks;
|
|
*clk_arr = devm_kzalloc(&pdev->dev,
|
|
(clks * sizeof(struct nodeclk)), GFP_KERNEL);
|
|
|
|
if (!(*clk_arr)) {
|
|
pr_err("%s: Error allocating clk nodes for %d\n", __func__, id);
|
|
ret = -ENOMEM;
|
|
*num_clks = 0;
|
|
goto exit_of_parse_clk_array;
|
|
}
|
|
|
|
of_property_for_each_string(dev_node, "clock-names", prop, clk_name) {
|
|
char gdsc_string[MAX_REG_NAME];
|
|
|
|
(*clk_arr)[idx].clk = of_clk_get_by_name(dev_node, clk_name);
|
|
|
|
if (IS_ERR_OR_NULL((*clk_arr)[idx].clk)) {
|
|
dev_err(&pdev->dev,
|
|
"Failed to get clk %s for bus%d ", clk_name,
|
|
id);
|
|
continue;
|
|
}
|
|
if (strnstr(clk_name, "no-rate", strlen(clk_name)))
|
|
(*clk_arr)[idx].enable_only_clk = true;
|
|
|
|
scnprintf(gdsc_string, MAX_REG_NAME, "%s-supply", clk_name);
|
|
|
|
if (of_find_property(gdsc_node, gdsc_string, NULL))
|
|
scnprintf((*clk_arr)[idx].reg_name,
|
|
MAX_REG_NAME, "%s", clk_name);
|
|
else
|
|
scnprintf((*clk_arr)[idx].reg_name,
|
|
MAX_REG_NAME, "%c", '\0');
|
|
|
|
idx++;
|
|
}
|
|
exit_of_parse_clk_array:
|
|
return ret;
|
|
}
|
|
|
|
static void get_agg_params(
|
|
struct device_node * const dev_node,
|
|
struct platform_device * const pdev,
|
|
struct msm_bus_node_info_type *node_info)
|
|
{
|
|
int ret;
|
|
|
|
|
|
ret = of_property_read_u32(dev_node, "qcom,buswidth",
|
|
&node_info->agg_params.buswidth);
|
|
if (ret) {
|
|
dev_dbg(&pdev->dev, "Using default 8 bytes %d", node_info->id);
|
|
node_info->agg_params.buswidth = 8;
|
|
}
|
|
|
|
ret = of_property_read_u32(dev_node, "qcom,agg-ports",
|
|
&node_info->agg_params.num_aggports);
|
|
if (ret)
|
|
node_info->agg_params.num_aggports = node_info->num_qports;
|
|
|
|
ret = of_property_read_u32(dev_node, "qcom,agg-scheme",
|
|
&node_info->agg_params.agg_scheme);
|
|
if (ret) {
|
|
if (node_info->is_fab_dev)
|
|
node_info->agg_params.agg_scheme = DEFAULT_AGG_SCHEME;
|
|
else {
|
|
node_info->agg_params.agg_scheme = AGG_SCHEME_NONE;
|
|
goto exit_get_agg_params;
|
|
}
|
|
}
|
|
|
|
ret = of_property_read_u32(dev_node, "qcom,vrail-comp",
|
|
&node_info->agg_params.vrail_comp);
|
|
if (ret) {
|
|
if (node_info->is_fab_dev)
|
|
node_info->agg_params.vrail_comp = DEFAULT_VRAIL_COMP;
|
|
else
|
|
node_info->agg_params.vrail_comp = 0;
|
|
}
|
|
|
|
if (node_info->agg_params.agg_scheme == AGG_SCHEME_LEG) {
|
|
node_info->agg_params.num_util_levels = 1;
|
|
node_info->agg_params.util_levels = devm_kzalloc(&pdev->dev,
|
|
(node_info->agg_params.num_util_levels *
|
|
sizeof(struct node_util_levels_type)), GFP_KERNEL);
|
|
if (IS_ERR_OR_NULL(node_info->agg_params.util_levels))
|
|
goto err_get_agg_params;
|
|
|
|
ret = of_property_read_u32(dev_node, "qcom,util-fact",
|
|
&node_info->agg_params.util_levels[0].util_fact);
|
|
if (ret) {
|
|
if (node_info->is_fab_dev)
|
|
node_info->agg_params.util_levels[0].util_fact
|
|
= DEFAULT_UTIL_FACT;
|
|
}
|
|
} else if (node_info->agg_params.agg_scheme == AGG_SCHEME_1) {
|
|
uint32_t len = 0;
|
|
const uint32_t *util_levels;
|
|
int i, index = 0;
|
|
|
|
util_levels =
|
|
of_get_property(dev_node, "qcom,util-levels", &len);
|
|
if (!util_levels)
|
|
goto err_get_agg_params;
|
|
|
|
node_info->agg_params.num_util_levels =
|
|
len / (sizeof(uint32_t) * 2);
|
|
node_info->agg_params.util_levels = devm_kzalloc(&pdev->dev,
|
|
(node_info->agg_params.num_util_levels *
|
|
sizeof(struct node_util_levels_type)), GFP_KERNEL);
|
|
|
|
if (IS_ERR_OR_NULL(node_info->agg_params.util_levels))
|
|
goto err_get_agg_params;
|
|
|
|
for (i = 0; i < node_info->agg_params.num_util_levels; i++) {
|
|
node_info->agg_params.util_levels[i].threshold =
|
|
KBTOB(be32_to_cpu(util_levels[index++]));
|
|
node_info->agg_params.util_levels[i].util_fact =
|
|
be32_to_cpu(util_levels[index++]);
|
|
dev_dbg(&pdev->dev, "[%d]:Thresh:%llu util_fact:%d\n",
|
|
i,
|
|
node_info->agg_params.util_levels[i].threshold,
|
|
node_info->agg_params.util_levels[i].util_fact);
|
|
}
|
|
} else {
|
|
dev_err(&pdev->dev, "Agg scheme%d unknown, using default\n",
|
|
node_info->agg_params.agg_scheme);
|
|
goto err_get_agg_params;
|
|
}
|
|
|
|
exit_get_agg_params:
|
|
return;
|
|
err_get_agg_params:
|
|
node_info->agg_params.agg_scheme = DEFAULT_AGG_SCHEME;
|
|
}
|
|
|
|
static struct msm_bus_node_info_type *get_node_info_data(
|
|
struct device_node * const dev_node,
|
|
struct platform_device * const pdev)
|
|
{
|
|
struct msm_bus_node_info_type *node_info;
|
|
unsigned int ret;
|
|
int size;
|
|
int i;
|
|
struct device_node *con_node;
|
|
struct device_node *bus_dev;
|
|
|
|
node_info = devm_kzalloc(&pdev->dev,
|
|
sizeof(struct msm_bus_node_info_type),
|
|
GFP_KERNEL);
|
|
if (!node_info) {
|
|
dev_err(&pdev->dev,
|
|
"Error: Unable to allocate memory for node_info\n");
|
|
return NULL;
|
|
}
|
|
|
|
ret = of_property_read_u32(dev_node, "cell-id", &node_info->id);
|
|
if (ret) {
|
|
dev_warn(&pdev->dev, "Bus node is missing cell-id\n");
|
|
goto node_info_err;
|
|
}
|
|
ret = of_property_read_string(dev_node, "label", &node_info->name);
|
|
if (ret) {
|
|
dev_warn(&pdev->dev, "Bus node is missing name\n");
|
|
goto node_info_err;
|
|
}
|
|
node_info->qport = get_arr(pdev, dev_node, "qcom,qport",
|
|
&node_info->num_qports);
|
|
|
|
if (of_get_property(dev_node, "qcom,connections", &size)) {
|
|
node_info->num_connections = size / sizeof(int);
|
|
node_info->connections = devm_kzalloc(&pdev->dev, size,
|
|
GFP_KERNEL);
|
|
} else {
|
|
node_info->num_connections = 0;
|
|
node_info->connections = 0;
|
|
}
|
|
|
|
for (i = 0; i < node_info->num_connections; i++) {
|
|
con_node = of_parse_phandle(dev_node, "qcom,connections", i);
|
|
if (IS_ERR_OR_NULL(con_node))
|
|
goto node_info_err;
|
|
|
|
if (of_property_read_u32(con_node, "cell-id",
|
|
&node_info->connections[i]))
|
|
goto node_info_err;
|
|
of_node_put(con_node);
|
|
}
|
|
|
|
if (of_get_property(dev_node, "qcom,blacklist", &size)) {
|
|
node_info->num_blist = size/sizeof(u32);
|
|
node_info->black_listed_connections = devm_kzalloc(&pdev->dev,
|
|
size, GFP_KERNEL);
|
|
} else {
|
|
node_info->num_blist = 0;
|
|
node_info->black_listed_connections = 0;
|
|
}
|
|
|
|
for (i = 0; i < node_info->num_blist; i++) {
|
|
con_node = of_parse_phandle(dev_node, "qcom,blacklist", i);
|
|
if (IS_ERR_OR_NULL(con_node))
|
|
goto node_info_err;
|
|
|
|
if (of_property_read_u32(con_node, "cell-id",
|
|
&node_info->black_listed_connections[i]))
|
|
goto node_info_err;
|
|
of_node_put(con_node);
|
|
}
|
|
|
|
bus_dev = of_parse_phandle(dev_node, "qcom,bus-dev", 0);
|
|
if (!IS_ERR_OR_NULL(bus_dev)) {
|
|
if (of_property_read_u32(bus_dev, "cell-id",
|
|
&node_info->bus_device_id)) {
|
|
dev_err(&pdev->dev, "Can't find bus device. Node %d",
|
|
node_info->id);
|
|
goto node_info_err;
|
|
}
|
|
|
|
of_node_put(bus_dev);
|
|
} else
|
|
dev_dbg(&pdev->dev, "Can't find bdev phandle for %d",
|
|
node_info->id);
|
|
|
|
node_info->is_fab_dev = of_property_read_bool(dev_node, "qcom,fab-dev");
|
|
node_info->virt_dev = of_property_read_bool(dev_node, "qcom,virt-dev");
|
|
|
|
|
|
ret = of_property_read_u32(dev_node, "qcom,mas-rpm-id",
|
|
&node_info->mas_rpm_id);
|
|
if (ret) {
|
|
dev_dbg(&pdev->dev, "mas rpm id is missing\n");
|
|
node_info->mas_rpm_id = -1;
|
|
}
|
|
|
|
ret = of_property_read_u32(dev_node, "qcom,slv-rpm-id",
|
|
&node_info->slv_rpm_id);
|
|
if (ret) {
|
|
dev_dbg(&pdev->dev, "slv rpm id is missing\n");
|
|
node_info->slv_rpm_id = -1;
|
|
}
|
|
|
|
get_agg_params(dev_node, pdev, node_info);
|
|
get_qos_params(dev_node, pdev, node_info);
|
|
|
|
return node_info;
|
|
|
|
node_info_err:
|
|
devm_kfree(&pdev->dev, node_info);
|
|
node_info = 0;
|
|
return NULL;
|
|
}
|
|
|
|
static int get_bus_node_device_data(
|
|
struct device_node * const dev_node,
|
|
struct platform_device * const pdev,
|
|
struct msm_bus_node_device_type * const node_device)
|
|
{
|
|
bool enable_only;
|
|
bool setrate_only;
|
|
|
|
node_device->node_info = get_node_info_data(dev_node, pdev);
|
|
if (IS_ERR_OR_NULL(node_device->node_info)) {
|
|
dev_err(&pdev->dev, "Error: Node info missing\n");
|
|
return -ENODATA;
|
|
}
|
|
node_device->ap_owned = of_property_read_bool(dev_node,
|
|
"qcom,ap-owned");
|
|
|
|
if (node_device->node_info->is_fab_dev) {
|
|
struct device_node *qos_clk_node;
|
|
dev_err(&pdev->dev, "Dev %d\n", node_device->node_info->id);
|
|
|
|
if (!node_device->node_info->virt_dev) {
|
|
node_device->fabdev =
|
|
get_fab_device_info(dev_node, pdev);
|
|
if (IS_ERR_OR_NULL(node_device->fabdev)) {
|
|
dev_err(&pdev->dev,
|
|
"Error: Fabric device info missing\n");
|
|
devm_kfree(&pdev->dev, node_device->node_info);
|
|
return -ENODATA;
|
|
}
|
|
}
|
|
|
|
enable_only = of_property_read_bool(dev_node,
|
|
"qcom,enable-only-clk");
|
|
node_device->clk[DUAL_CTX].enable_only_clk = enable_only;
|
|
node_device->clk[ACTIVE_CTX].enable_only_clk = enable_only;
|
|
|
|
/*
|
|
* Doesn't make sense to have a clk handle you can't enable or
|
|
* set rate on.
|
|
*/
|
|
if (!enable_only) {
|
|
setrate_only = of_property_read_bool(dev_node,
|
|
"qcom,setrate-only-clk");
|
|
node_device->clk[DUAL_CTX].setrate_only_clk =
|
|
setrate_only;
|
|
node_device->clk[ACTIVE_CTX].setrate_only_clk =
|
|
setrate_only;
|
|
}
|
|
|
|
node_device->clk[DUAL_CTX].clk = of_clk_get_by_name(dev_node,
|
|
"bus_clk");
|
|
|
|
if (IS_ERR_OR_NULL(node_device->clk[DUAL_CTX].clk)) {
|
|
int ret;
|
|
dev_err(&pdev->dev,
|
|
"%s:Failed to get bus clk for bus%d ctx%d",
|
|
__func__, node_device->node_info->id,
|
|
DUAL_CTX);
|
|
ret = (IS_ERR(node_device->clk[DUAL_CTX].clk) ?
|
|
PTR_ERR(node_device->clk[DUAL_CTX].clk) : -ENXIO);
|
|
return ret;
|
|
}
|
|
|
|
if (of_find_property(dev_node, "bus-gdsc-supply", NULL))
|
|
scnprintf(node_device->clk[DUAL_CTX].reg_name,
|
|
MAX_REG_NAME, "%s", "bus-gdsc");
|
|
else
|
|
scnprintf(node_device->clk[DUAL_CTX].reg_name,
|
|
MAX_REG_NAME, "%c", '\0');
|
|
|
|
node_device->clk[ACTIVE_CTX].clk = of_clk_get_by_name(dev_node,
|
|
"bus_a_clk");
|
|
if (IS_ERR_OR_NULL(node_device->clk[ACTIVE_CTX].clk)) {
|
|
int ret;
|
|
dev_err(&pdev->dev,
|
|
"Failed to get bus clk for bus%d ctx%d",
|
|
node_device->node_info->id, ACTIVE_CTX);
|
|
ret = (IS_ERR(node_device->clk[DUAL_CTX].clk) ?
|
|
PTR_ERR(node_device->clk[DUAL_CTX].clk) : -ENXIO);
|
|
return ret;
|
|
}
|
|
|
|
if (of_find_property(dev_node, "bus-a-gdsc-supply", NULL))
|
|
scnprintf(node_device->clk[ACTIVE_CTX].reg_name,
|
|
MAX_REG_NAME, "%s", "bus-a-gdsc");
|
|
else
|
|
scnprintf(node_device->clk[ACTIVE_CTX].reg_name,
|
|
MAX_REG_NAME, "%c", '\0');
|
|
|
|
node_device->bus_qos_clk.clk = of_clk_get_by_name(dev_node,
|
|
"bus_qos_clk");
|
|
|
|
if (IS_ERR_OR_NULL(node_device->bus_qos_clk.clk)) {
|
|
dev_dbg(&pdev->dev,
|
|
"%s:Failed to get bus qos clk for %d",
|
|
__func__, node_device->node_info->id);
|
|
scnprintf(node_device->bus_qos_clk.reg_name,
|
|
MAX_REG_NAME, "%c", '\0');
|
|
} else {
|
|
if (of_find_property(dev_node, "bus-qos-gdsc-supply",
|
|
NULL))
|
|
scnprintf(node_device->bus_qos_clk.reg_name,
|
|
MAX_REG_NAME, "%s", "bus-qos-gdsc");
|
|
else
|
|
scnprintf(node_device->bus_qos_clk.reg_name,
|
|
MAX_REG_NAME, "%c", '\0');
|
|
}
|
|
|
|
qos_clk_node = of_find_node_by_name(dev_node,
|
|
"qcom,node-qos-clks");
|
|
|
|
if (qos_clk_node)
|
|
msm_bus_of_parse_clk_array(qos_clk_node, dev_node, pdev,
|
|
&node_device->node_qos_clks,
|
|
&node_device->num_node_qos_clks,
|
|
node_device->node_info->id);
|
|
|
|
if (msmbus_coresight_init_adhoc(pdev, dev_node))
|
|
dev_warn(&pdev->dev,
|
|
"Coresight support absent for bus: %d\n",
|
|
node_device->node_info->id);
|
|
} else {
|
|
node_device->bus_qos_clk.clk = of_clk_get_by_name(dev_node,
|
|
"bus_qos_clk");
|
|
|
|
if (IS_ERR_OR_NULL(node_device->bus_qos_clk.clk))
|
|
dev_dbg(&pdev->dev,
|
|
"%s:Failed to get bus qos clk for mas%d",
|
|
__func__, node_device->node_info->id);
|
|
|
|
if (of_find_property(dev_node, "bus-qos-gdsc-supply",
|
|
NULL))
|
|
scnprintf(node_device->bus_qos_clk.reg_name,
|
|
MAX_REG_NAME, "%s", "bus-qos-gdsc");
|
|
else
|
|
scnprintf(node_device->bus_qos_clk.reg_name,
|
|
MAX_REG_NAME, "%c", '\0');
|
|
|
|
enable_only = of_property_read_bool(dev_node,
|
|
"qcom,enable-only-clk");
|
|
node_device->clk[DUAL_CTX].enable_only_clk = enable_only;
|
|
node_device->bus_qos_clk.enable_only_clk = enable_only;
|
|
|
|
/*
|
|
* Doesn't make sense to have a clk handle you can't enable or
|
|
* set rate on.
|
|
*/
|
|
if (!enable_only) {
|
|
setrate_only = of_property_read_bool(dev_node,
|
|
"qcom,setrate-only-clk");
|
|
node_device->clk[DUAL_CTX].setrate_only_clk =
|
|
setrate_only;
|
|
node_device->clk[ACTIVE_CTX].setrate_only_clk =
|
|
setrate_only;
|
|
}
|
|
|
|
node_device->clk[DUAL_CTX].clk = of_clk_get_by_name(dev_node,
|
|
"node_clk");
|
|
|
|
if (IS_ERR_OR_NULL(node_device->clk[DUAL_CTX].clk))
|
|
dev_dbg(&pdev->dev,
|
|
"%s:Failed to get bus clk for bus%d ctx%d",
|
|
__func__, node_device->node_info->id,
|
|
DUAL_CTX);
|
|
|
|
if (of_find_property(dev_node, "node-gdsc-supply", NULL))
|
|
scnprintf(node_device->clk[DUAL_CTX].reg_name,
|
|
MAX_REG_NAME, "%s", "node-gdsc");
|
|
else
|
|
scnprintf(node_device->clk[DUAL_CTX].reg_name,
|
|
MAX_REG_NAME, "%c", '\0');
|
|
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
struct msm_bus_device_node_registration
|
|
*msm_bus_of_to_pdata(struct platform_device *pdev)
|
|
{
|
|
struct device_node *of_node, *child_node;
|
|
struct msm_bus_device_node_registration *pdata;
|
|
unsigned int i = 0, j;
|
|
unsigned int ret;
|
|
|
|
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_device_node_registration),
|
|
GFP_KERNEL);
|
|
if (!pdata) {
|
|
dev_err(&pdev->dev,
|
|
"Error: Memory allocation for pdata failed\n");
|
|
return NULL;
|
|
}
|
|
|
|
pdata->num_devices = of_get_child_count(of_node);
|
|
|
|
pdata->info = devm_kzalloc(&pdev->dev,
|
|
sizeof(struct msm_bus_node_device_type) *
|
|
pdata->num_devices, GFP_KERNEL);
|
|
|
|
if (!pdata->info) {
|
|
dev_err(&pdev->dev,
|
|
"Error: Memory allocation for pdata->info failed\n");
|
|
goto node_reg_err;
|
|
}
|
|
|
|
ret = 0;
|
|
for_each_child_of_node(of_node, child_node) {
|
|
ret = get_bus_node_device_data(child_node, pdev,
|
|
&pdata->info[i]);
|
|
if (ret) {
|
|
dev_err(&pdev->dev, "Error: unable to initialize bus nodes\n");
|
|
goto node_reg_err_1;
|
|
}
|
|
pdata->info[i].of_node = child_node;
|
|
i++;
|
|
}
|
|
|
|
dev_dbg(&pdev->dev, "bus topology:\n");
|
|
for (i = 0; i < pdata->num_devices; i++) {
|
|
dev_dbg(&pdev->dev, "id %d\nnum_qports %d\nnum_connections %d",
|
|
pdata->info[i].node_info->id,
|
|
pdata->info[i].node_info->num_qports,
|
|
pdata->info[i].node_info->num_connections);
|
|
dev_dbg(&pdev->dev, "\nbus_device_id %d\n buswidth %d\n",
|
|
pdata->info[i].node_info->bus_device_id,
|
|
pdata->info[i].node_info->agg_params.buswidth);
|
|
for (j = 0; j < pdata->info[i].node_info->num_connections;
|
|
j++) {
|
|
dev_dbg(&pdev->dev, "connection[%d]: %d\n", j,
|
|
pdata->info[i].node_info->connections[j]);
|
|
}
|
|
for (j = 0; j < pdata->info[i].node_info->num_blist;
|
|
j++) {
|
|
dev_dbg(&pdev->dev, "black_listed_node[%d]: %d\n", j,
|
|
pdata->info[i].node_info->
|
|
black_listed_connections[j]);
|
|
}
|
|
if (pdata->info[i].fabdev)
|
|
dev_dbg(&pdev->dev, "base_addr %zu\nbus_type %d\n",
|
|
(size_t)pdata->info[i].
|
|
fabdev->pqos_base,
|
|
pdata->info[i].fabdev->bus_type);
|
|
}
|
|
return pdata;
|
|
|
|
node_reg_err_1:
|
|
devm_kfree(&pdev->dev, pdata->info);
|
|
node_reg_err:
|
|
devm_kfree(&pdev->dev, pdata);
|
|
pdata = NULL;
|
|
return NULL;
|
|
}
|
|
|
|
static int msm_bus_of_get_ids(struct platform_device *pdev,
|
|
struct device_node *dev_node, int **dev_ids,
|
|
int *num_ids, char *prop_name)
|
|
{
|
|
int ret = 0;
|
|
int size, i;
|
|
struct device_node *rule_node;
|
|
int *ids = NULL;
|
|
|
|
if (of_get_property(dev_node, prop_name, &size)) {
|
|
*num_ids = size / sizeof(int);
|
|
ids = devm_kzalloc(&pdev->dev, size, GFP_KERNEL);
|
|
} else {
|
|
dev_err(&pdev->dev, "No rule nodes, skipping node");
|
|
ret = -ENXIO;
|
|
goto exit_get_ids;
|
|
}
|
|
|
|
*dev_ids = ids;
|
|
for (i = 0; i < *num_ids; i++) {
|
|
rule_node = of_parse_phandle(dev_node, prop_name, i);
|
|
if (IS_ERR_OR_NULL(rule_node)) {
|
|
dev_err(&pdev->dev, "Can't get rule node id");
|
|
ret = -ENXIO;
|
|
goto err_get_ids;
|
|
}
|
|
|
|
if (of_property_read_u32(rule_node, "cell-id",
|
|
&ids[i])) {
|
|
dev_err(&pdev->dev, "Can't get rule node id");
|
|
ret = -ENXIO;
|
|
goto err_get_ids;
|
|
}
|
|
of_node_put(rule_node);
|
|
}
|
|
exit_get_ids:
|
|
return ret;
|
|
err_get_ids:
|
|
devm_kfree(&pdev->dev, ids);
|
|
of_node_put(rule_node);
|
|
ids = NULL;
|
|
return ret;
|
|
}
|
|
|
|
int msm_bus_of_get_static_rules(struct platform_device *pdev,
|
|
struct bus_rule_type **static_rules)
|
|
{
|
|
int ret = 0;
|
|
struct device_node *of_node, *child_node;
|
|
int num_rules = 0;
|
|
int rule_idx = 0;
|
|
int bw_fld = 0;
|
|
int i;
|
|
struct bus_rule_type *local_rule = NULL;
|
|
|
|
of_node = pdev->dev.of_node;
|
|
num_rules = of_get_child_count(of_node);
|
|
local_rule = devm_kzalloc(&pdev->dev,
|
|
sizeof(struct bus_rule_type) * num_rules,
|
|
GFP_KERNEL);
|
|
|
|
if (IS_ERR_OR_NULL(local_rule)) {
|
|
ret = -ENOMEM;
|
|
goto exit_static_rules;
|
|
}
|
|
|
|
*static_rules = local_rule;
|
|
for_each_child_of_node(of_node, child_node) {
|
|
ret = msm_bus_of_get_ids(pdev, child_node,
|
|
&local_rule[rule_idx].src_id,
|
|
&local_rule[rule_idx].num_src,
|
|
"qcom,src-nodes");
|
|
|
|
ret = msm_bus_of_get_ids(pdev, child_node,
|
|
&local_rule[rule_idx].dst_node,
|
|
&local_rule[rule_idx].num_dst,
|
|
"qcom,dest-node");
|
|
|
|
ret = of_property_read_u32(child_node, "qcom,src-field",
|
|
&local_rule[rule_idx].src_field);
|
|
if (ret) {
|
|
dev_err(&pdev->dev, "src-field missing");
|
|
ret = -ENXIO;
|
|
goto err_static_rules;
|
|
}
|
|
|
|
ret = of_property_read_u32(child_node, "qcom,src-op",
|
|
&local_rule[rule_idx].op);
|
|
if (ret) {
|
|
dev_err(&pdev->dev, "src-op missing");
|
|
ret = -ENXIO;
|
|
goto err_static_rules;
|
|
}
|
|
|
|
ret = of_property_read_u32(child_node, "qcom,mode",
|
|
&local_rule[rule_idx].mode);
|
|
if (ret) {
|
|
dev_err(&pdev->dev, "mode missing");
|
|
ret = -ENXIO;
|
|
goto err_static_rules;
|
|
}
|
|
|
|
ret = of_property_read_u32(child_node, "qcom,thresh", &bw_fld);
|
|
if (ret) {
|
|
dev_err(&pdev->dev, "thresh missing");
|
|
ret = -ENXIO;
|
|
goto err_static_rules;
|
|
} else
|
|
local_rule[rule_idx].thresh = KBTOB(bw_fld);
|
|
|
|
ret = of_property_read_u32(child_node, "qcom,dest-bw",
|
|
&bw_fld);
|
|
if (ret)
|
|
local_rule[rule_idx].dst_bw = 0;
|
|
else
|
|
local_rule[rule_idx].dst_bw = KBTOB(bw_fld);
|
|
|
|
rule_idx++;
|
|
}
|
|
ret = rule_idx;
|
|
exit_static_rules:
|
|
return ret;
|
|
err_static_rules:
|
|
for (i = 0; i < num_rules; i++) {
|
|
if (!IS_ERR_OR_NULL(local_rule)) {
|
|
if (!IS_ERR_OR_NULL(local_rule[i].src_id))
|
|
devm_kfree(&pdev->dev,
|
|
local_rule[i].src_id);
|
|
if (!IS_ERR_OR_NULL(local_rule[i].dst_node))
|
|
devm_kfree(&pdev->dev,
|
|
local_rule[i].dst_node);
|
|
devm_kfree(&pdev->dev, local_rule);
|
|
}
|
|
}
|
|
*static_rules = NULL;
|
|
return ret;
|
|
}
|