Add slave RPM ID to assist with identifying incorrect RPM config. Signed-off-by: Benjamin Li <benl@squareup.com> Reviewed-by: Jun Nie <jun.nie@linaro.org> Link: https://lore.kernel.org/r/20210205015205.22947-2-benl@squareup.com Signed-off-by: Georgi Djakov <georgi.djakov@linaro.org>
		
			
				
	
	
		
			192 lines
		
	
	
		
			4.2 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			192 lines
		
	
	
		
			4.2 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| // SPDX-License-Identifier: GPL-2.0
 | |
| /*
 | |
|  * Copyright (C) 2020 Linaro Ltd
 | |
|  */
 | |
| 
 | |
| #include <linux/clk.h>
 | |
| #include <linux/device.h>
 | |
| #include <linux/interconnect-provider.h>
 | |
| #include <linux/io.h>
 | |
| #include <linux/module.h>
 | |
| #include <linux/of_device.h>
 | |
| #include <linux/of_platform.h>
 | |
| #include <linux/platform_device.h>
 | |
| #include <linux/slab.h>
 | |
| 
 | |
| #include "smd-rpm.h"
 | |
| #include "icc-rpm.h"
 | |
| 
 | |
| static int qcom_icc_set(struct icc_node *src, struct icc_node *dst)
 | |
| {
 | |
| 	struct qcom_icc_provider *qp;
 | |
| 	struct qcom_icc_node *qn;
 | |
| 	struct icc_provider *provider;
 | |
| 	struct icc_node *n;
 | |
| 	u64 sum_bw;
 | |
| 	u64 max_peak_bw;
 | |
| 	u64 rate;
 | |
| 	u32 agg_avg = 0;
 | |
| 	u32 agg_peak = 0;
 | |
| 	int ret, i;
 | |
| 
 | |
| 	qn = src->data;
 | |
| 	provider = src->provider;
 | |
| 	qp = to_qcom_provider(provider);
 | |
| 
 | |
| 	list_for_each_entry(n, &provider->nodes, node_list)
 | |
| 		provider->aggregate(n, 0, n->avg_bw, n->peak_bw,
 | |
| 				    &agg_avg, &agg_peak);
 | |
| 
 | |
| 	sum_bw = icc_units_to_bps(agg_avg);
 | |
| 	max_peak_bw = icc_units_to_bps(agg_peak);
 | |
| 
 | |
| 	/* send bandwidth request message to the RPM processor */
 | |
| 	if (qn->mas_rpm_id != -1) {
 | |
| 		ret = qcom_icc_rpm_smd_send(QCOM_SMD_RPM_ACTIVE_STATE,
 | |
| 					    RPM_BUS_MASTER_REQ,
 | |
| 					    qn->mas_rpm_id,
 | |
| 					    sum_bw);
 | |
| 		if (ret) {
 | |
| 			pr_err("qcom_icc_rpm_smd_send mas %d error %d\n",
 | |
| 			       qn->mas_rpm_id, ret);
 | |
| 			return ret;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	if (qn->slv_rpm_id != -1) {
 | |
| 		ret = qcom_icc_rpm_smd_send(QCOM_SMD_RPM_ACTIVE_STATE,
 | |
| 					    RPM_BUS_SLAVE_REQ,
 | |
| 					    qn->slv_rpm_id,
 | |
| 					    sum_bw);
 | |
| 		if (ret) {
 | |
| 			pr_err("qcom_icc_rpm_smd_send slv %d error %d\n",
 | |
| 			       qn->slv_rpm_id, ret);
 | |
| 			return ret;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	rate = max(sum_bw, max_peak_bw);
 | |
| 
 | |
| 	do_div(rate, qn->buswidth);
 | |
| 
 | |
| 	if (qn->rate == rate)
 | |
| 		return 0;
 | |
| 
 | |
| 	for (i = 0; i < qp->num_clks; i++) {
 | |
| 		ret = clk_set_rate(qp->bus_clks[i].clk, rate);
 | |
| 		if (ret) {
 | |
| 			pr_err("%s clk_set_rate error: %d\n",
 | |
| 			       qp->bus_clks[i].id, ret);
 | |
| 			return ret;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	qn->rate = rate;
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| int qnoc_probe(struct platform_device *pdev, size_t cd_size, int cd_num,
 | |
| 	       const struct clk_bulk_data *cd)
 | |
| {
 | |
| 	struct device *dev = &pdev->dev;
 | |
| 	const struct qcom_icc_desc *desc;
 | |
| 	struct icc_onecell_data *data;
 | |
| 	struct icc_provider *provider;
 | |
| 	struct qcom_icc_node **qnodes;
 | |
| 	struct qcom_icc_provider *qp;
 | |
| 	struct icc_node *node;
 | |
| 	size_t num_nodes, i;
 | |
| 	int ret;
 | |
| 
 | |
| 	/* wait for the RPM proxy */
 | |
| 	if (!qcom_icc_rpm_smd_available())
 | |
| 		return -EPROBE_DEFER;
 | |
| 
 | |
| 	desc = of_device_get_match_data(dev);
 | |
| 	if (!desc)
 | |
| 		return -EINVAL;
 | |
| 
 | |
| 	qnodes = desc->nodes;
 | |
| 	num_nodes = desc->num_nodes;
 | |
| 
 | |
| 	qp = devm_kzalloc(dev, sizeof(*qp), GFP_KERNEL);
 | |
| 	if (!qp)
 | |
| 		return -ENOMEM;
 | |
| 
 | |
| 	data = devm_kzalloc(dev, struct_size(data, nodes, num_nodes),
 | |
| 			    GFP_KERNEL);
 | |
| 	if (!data)
 | |
| 		return -ENOMEM;
 | |
| 
 | |
| 	qp->bus_clks = devm_kmemdup(dev, cd, cd_size,
 | |
| 				    GFP_KERNEL);
 | |
| 	if (!qp->bus_clks)
 | |
| 		return -ENOMEM;
 | |
| 
 | |
| 	qp->num_clks = cd_num;
 | |
| 	ret = devm_clk_bulk_get(dev, qp->num_clks, qp->bus_clks);
 | |
| 	if (ret)
 | |
| 		return ret;
 | |
| 
 | |
| 	ret = clk_bulk_prepare_enable(qp->num_clks, qp->bus_clks);
 | |
| 	if (ret)
 | |
| 		return ret;
 | |
| 
 | |
| 	provider = &qp->provider;
 | |
| 	INIT_LIST_HEAD(&provider->nodes);
 | |
| 	provider->dev = dev;
 | |
| 	provider->set = qcom_icc_set;
 | |
| 	provider->aggregate = icc_std_aggregate;
 | |
| 	provider->xlate = of_icc_xlate_onecell;
 | |
| 	provider->data = data;
 | |
| 
 | |
| 	ret = icc_provider_add(provider);
 | |
| 	if (ret) {
 | |
| 		dev_err(dev, "error adding interconnect provider: %d\n", ret);
 | |
| 		clk_bulk_disable_unprepare(qp->num_clks, qp->bus_clks);
 | |
| 		return ret;
 | |
| 	}
 | |
| 
 | |
| 	for (i = 0; i < num_nodes; i++) {
 | |
| 		size_t j;
 | |
| 
 | |
| 		node = icc_node_create(qnodes[i]->id);
 | |
| 		if (IS_ERR(node)) {
 | |
| 			ret = PTR_ERR(node);
 | |
| 			goto err;
 | |
| 		}
 | |
| 
 | |
| 		node->name = qnodes[i]->name;
 | |
| 		node->data = qnodes[i];
 | |
| 		icc_node_add(node, provider);
 | |
| 
 | |
| 		for (j = 0; j < qnodes[i]->num_links; j++)
 | |
| 			icc_link_create(node, qnodes[i]->links[j]);
 | |
| 
 | |
| 		data->nodes[i] = node;
 | |
| 	}
 | |
| 	data->num_nodes = num_nodes;
 | |
| 
 | |
| 	platform_set_drvdata(pdev, qp);
 | |
| 
 | |
| 	return 0;
 | |
| err:
 | |
| 	icc_nodes_remove(provider);
 | |
| 	clk_bulk_disable_unprepare(qp->num_clks, qp->bus_clks);
 | |
| 	icc_provider_del(provider);
 | |
| 
 | |
| 	return ret;
 | |
| }
 | |
| EXPORT_SYMBOL(qnoc_probe);
 | |
| 
 | |
| int qnoc_remove(struct platform_device *pdev)
 | |
| {
 | |
| 	struct qcom_icc_provider *qp = platform_get_drvdata(pdev);
 | |
| 
 | |
| 	icc_nodes_remove(&qp->provider);
 | |
| 	clk_bulk_disable_unprepare(qp->num_clks, qp->bus_clks);
 | |
| 	return icc_provider_del(&qp->provider);
 | |
| }
 | |
| EXPORT_SYMBOL(qnoc_remove);
 |