1976daac4SDavid Dai // SPDX-License-Identifier: GPL-2.0
2976daac4SDavid Dai /*
3976daac4SDavid Dai * Copyright (c) 2020, The Linux Foundation. All rights reserved.
4976daac4SDavid Dai */
5976daac4SDavid Dai
6976daac4SDavid Dai #include <linux/interconnect.h>
7976daac4SDavid Dai #include <linux/interconnect-provider.h>
8976daac4SDavid Dai #include <linux/module.h>
96caa3070SGeorgi Djakov #include <linux/of.h>
10cff66aceSRob Herring #include <linux/of_platform.h>
116caa3070SGeorgi Djakov #include <linux/slab.h>
12976daac4SDavid Dai
13976daac4SDavid Dai #include "bcm-voter.h"
14cb4805b5SLeo Yan #include "icc-common.h"
15976daac4SDavid Dai #include "icc-rpmh.h"
16976daac4SDavid Dai
17976daac4SDavid Dai /**
18976daac4SDavid Dai * qcom_icc_pre_aggregate - cleans up stale values from prior icc_set
19976daac4SDavid Dai * @node: icc node to operate on
20976daac4SDavid Dai */
qcom_icc_pre_aggregate(struct icc_node * node)21976daac4SDavid Dai void qcom_icc_pre_aggregate(struct icc_node *node)
22976daac4SDavid Dai {
23976daac4SDavid Dai size_t i;
24976daac4SDavid Dai struct qcom_icc_node *qn;
25b95b668eSMike Tipton struct qcom_icc_provider *qp;
26976daac4SDavid Dai
27976daac4SDavid Dai qn = node->data;
28b95b668eSMike Tipton qp = to_qcom_provider(node->provider);
29976daac4SDavid Dai
30976daac4SDavid Dai for (i = 0; i < QCOM_ICC_NUM_BUCKETS; i++) {
31976daac4SDavid Dai qn->sum_avg[i] = 0;
32976daac4SDavid Dai qn->max_peak[i] = 0;
33976daac4SDavid Dai }
34b95b668eSMike Tipton
35b95b668eSMike Tipton for (i = 0; i < qn->num_bcms; i++)
36b95b668eSMike Tipton qcom_icc_bcm_voter_add(qp->voter, qn->bcms[i]);
37976daac4SDavid Dai }
38976daac4SDavid Dai EXPORT_SYMBOL_GPL(qcom_icc_pre_aggregate);
39976daac4SDavid Dai
40976daac4SDavid Dai /**
41976daac4SDavid Dai * qcom_icc_aggregate - aggregate bw for buckets indicated by tag
42976daac4SDavid Dai * @node: node to aggregate
43976daac4SDavid Dai * @tag: tag to indicate which buckets to aggregate
44976daac4SDavid Dai * @avg_bw: new bw to sum aggregate
45976daac4SDavid Dai * @peak_bw: new bw to max aggregate
46976daac4SDavid Dai * @agg_avg: existing aggregate avg bw val
47976daac4SDavid Dai * @agg_peak: existing aggregate peak bw val
48976daac4SDavid Dai */
qcom_icc_aggregate(struct icc_node * node,u32 tag,u32 avg_bw,u32 peak_bw,u32 * agg_avg,u32 * agg_peak)49976daac4SDavid Dai int qcom_icc_aggregate(struct icc_node *node, u32 tag, u32 avg_bw,
50976daac4SDavid Dai u32 peak_bw, u32 *agg_avg, u32 *agg_peak)
51976daac4SDavid Dai {
52976daac4SDavid Dai size_t i;
53976daac4SDavid Dai struct qcom_icc_node *qn;
54976daac4SDavid Dai
55976daac4SDavid Dai qn = node->data;
56976daac4SDavid Dai
57976daac4SDavid Dai if (!tag)
58976daac4SDavid Dai tag = QCOM_ICC_TAG_ALWAYS;
59976daac4SDavid Dai
60976daac4SDavid Dai for (i = 0; i < QCOM_ICC_NUM_BUCKETS; i++) {
61976daac4SDavid Dai if (tag & BIT(i)) {
62976daac4SDavid Dai qn->sum_avg[i] += avg_bw;
63976daac4SDavid Dai qn->max_peak[i] = max_t(u32, qn->max_peak[i], peak_bw);
64976daac4SDavid Dai }
65ce5a5957SMike Tipton
66ce5a5957SMike Tipton if (node->init_avg || node->init_peak) {
67ce5a5957SMike Tipton qn->sum_avg[i] = max_t(u64, qn->sum_avg[i], node->init_avg);
68ce5a5957SMike Tipton qn->max_peak[i] = max_t(u64, qn->max_peak[i], node->init_peak);
69ce5a5957SMike Tipton }
70976daac4SDavid Dai }
71976daac4SDavid Dai
72976daac4SDavid Dai *agg_avg += avg_bw;
73976daac4SDavid Dai *agg_peak = max_t(u32, *agg_peak, peak_bw);
74976daac4SDavid Dai
75976daac4SDavid Dai return 0;
76976daac4SDavid Dai }
77976daac4SDavid Dai EXPORT_SYMBOL_GPL(qcom_icc_aggregate);
78976daac4SDavid Dai
79976daac4SDavid Dai /**
80976daac4SDavid Dai * qcom_icc_set - set the constraints based on path
81976daac4SDavid Dai * @src: source node for the path to set constraints on
82976daac4SDavid Dai * @dst: destination node for the path to set constraints on
83976daac4SDavid Dai *
84976daac4SDavid Dai * Return: 0 on success, or an error code otherwise
85976daac4SDavid Dai */
qcom_icc_set(struct icc_node * src,struct icc_node * dst)86976daac4SDavid Dai int qcom_icc_set(struct icc_node *src, struct icc_node *dst)
87976daac4SDavid Dai {
88976daac4SDavid Dai struct qcom_icc_provider *qp;
89976daac4SDavid Dai struct icc_node *node;
90976daac4SDavid Dai
91976daac4SDavid Dai if (!src)
92976daac4SDavid Dai node = dst;
93976daac4SDavid Dai else
94976daac4SDavid Dai node = src;
95976daac4SDavid Dai
96976daac4SDavid Dai qp = to_qcom_provider(node->provider);
97976daac4SDavid Dai
98976daac4SDavid Dai qcom_icc_bcm_voter_commit(qp->voter);
99976daac4SDavid Dai
100976daac4SDavid Dai return 0;
101976daac4SDavid Dai }
102976daac4SDavid Dai EXPORT_SYMBOL_GPL(qcom_icc_set);
103976daac4SDavid Dai
104976daac4SDavid Dai /**
105976daac4SDavid Dai * qcom_icc_bcm_init - populates bcm aux data and connect qnodes
106976daac4SDavid Dai * @bcm: bcm to be initialized
107976daac4SDavid Dai * @dev: associated provider device
108976daac4SDavid Dai *
109976daac4SDavid Dai * Return: 0 on success, or an error code otherwise
110976daac4SDavid Dai */
qcom_icc_bcm_init(struct qcom_icc_bcm * bcm,struct device * dev)111976daac4SDavid Dai int qcom_icc_bcm_init(struct qcom_icc_bcm *bcm, struct device *dev)
112976daac4SDavid Dai {
113976daac4SDavid Dai struct qcom_icc_node *qn;
114976daac4SDavid Dai const struct bcm_db *data;
115976daac4SDavid Dai size_t data_count;
116976daac4SDavid Dai int i;
117976daac4SDavid Dai
118976daac4SDavid Dai /* BCM is already initialised*/
119976daac4SDavid Dai if (bcm->addr)
120976daac4SDavid Dai return 0;
121976daac4SDavid Dai
122976daac4SDavid Dai bcm->addr = cmd_db_read_addr(bcm->name);
123976daac4SDavid Dai if (!bcm->addr) {
124976daac4SDavid Dai dev_err(dev, "%s could not find RPMh address\n",
125976daac4SDavid Dai bcm->name);
126976daac4SDavid Dai return -EINVAL;
127976daac4SDavid Dai }
128976daac4SDavid Dai
129976daac4SDavid Dai data = cmd_db_read_aux_data(bcm->name, &data_count);
130976daac4SDavid Dai if (IS_ERR(data)) {
131976daac4SDavid Dai dev_err(dev, "%s command db read error (%ld)\n",
132976daac4SDavid Dai bcm->name, PTR_ERR(data));
133976daac4SDavid Dai return PTR_ERR(data);
134976daac4SDavid Dai }
135976daac4SDavid Dai if (!data_count) {
136976daac4SDavid Dai dev_err(dev, "%s command db missing or partial aux data\n",
137976daac4SDavid Dai bcm->name);
138976daac4SDavid Dai return -EINVAL;
139976daac4SDavid Dai }
140976daac4SDavid Dai
141976daac4SDavid Dai bcm->aux_data.unit = le32_to_cpu(data->unit);
142976daac4SDavid Dai bcm->aux_data.width = le16_to_cpu(data->width);
143976daac4SDavid Dai bcm->aux_data.vcd = data->vcd;
144976daac4SDavid Dai bcm->aux_data.reserved = data->reserved;
145976daac4SDavid Dai INIT_LIST_HEAD(&bcm->list);
146976daac4SDavid Dai INIT_LIST_HEAD(&bcm->ws_list);
147976daac4SDavid Dai
148cb30e029SMike Tipton if (!bcm->vote_scale)
149cb30e029SMike Tipton bcm->vote_scale = 1000;
150cb30e029SMike Tipton
151976daac4SDavid Dai /* Link Qnodes to their respective BCMs */
152976daac4SDavid Dai for (i = 0; i < bcm->num_nodes; i++) {
153976daac4SDavid Dai qn = bcm->nodes[i];
154976daac4SDavid Dai qn->bcms[qn->num_bcms] = bcm;
155976daac4SDavid Dai qn->num_bcms++;
156976daac4SDavid Dai }
157976daac4SDavid Dai
158976daac4SDavid Dai return 0;
159976daac4SDavid Dai }
160976daac4SDavid Dai EXPORT_SYMBOL_GPL(qcom_icc_bcm_init);
161976daac4SDavid Dai
qcom_icc_rpmh_probe(struct platform_device * pdev)162789a39adSMike Tipton int qcom_icc_rpmh_probe(struct platform_device *pdev)
163789a39adSMike Tipton {
164789a39adSMike Tipton const struct qcom_icc_desc *desc;
165789a39adSMike Tipton struct device *dev = &pdev->dev;
166789a39adSMike Tipton struct icc_onecell_data *data;
167789a39adSMike Tipton struct icc_provider *provider;
1682ccf33c0SKrzysztof Kozlowski struct qcom_icc_node * const *qnodes, *qn;
169789a39adSMike Tipton struct qcom_icc_provider *qp;
170789a39adSMike Tipton struct icc_node *node;
171789a39adSMike Tipton size_t num_nodes, i, j;
172789a39adSMike Tipton int ret;
173789a39adSMike Tipton
174789a39adSMike Tipton desc = of_device_get_match_data(dev);
175789a39adSMike Tipton if (!desc)
176789a39adSMike Tipton return -EINVAL;
177789a39adSMike Tipton
178789a39adSMike Tipton qnodes = desc->nodes;
179789a39adSMike Tipton num_nodes = desc->num_nodes;
180789a39adSMike Tipton
181789a39adSMike Tipton qp = devm_kzalloc(dev, sizeof(*qp), GFP_KERNEL);
182789a39adSMike Tipton if (!qp)
183789a39adSMike Tipton return -ENOMEM;
184789a39adSMike Tipton
185789a39adSMike Tipton data = devm_kzalloc(dev, struct_size(data, nodes, num_nodes), GFP_KERNEL);
186789a39adSMike Tipton if (!data)
187789a39adSMike Tipton return -ENOMEM;
188*dd4904f3SKees Cook data->num_nodes = num_nodes;
189789a39adSMike Tipton
190789a39adSMike Tipton provider = &qp->provider;
191789a39adSMike Tipton provider->dev = dev;
192789a39adSMike Tipton provider->set = qcom_icc_set;
193789a39adSMike Tipton provider->pre_aggregate = qcom_icc_pre_aggregate;
194789a39adSMike Tipton provider->aggregate = qcom_icc_aggregate;
195789a39adSMike Tipton provider->xlate_extended = qcom_icc_xlate_extended;
196789a39adSMike Tipton provider->data = data;
197789a39adSMike Tipton
19874240a5bSJohan Hovold icc_provider_init(provider);
19974240a5bSJohan Hovold
200789a39adSMike Tipton qp->dev = dev;
201789a39adSMike Tipton qp->bcms = desc->bcms;
202789a39adSMike Tipton qp->num_bcms = desc->num_bcms;
203789a39adSMike Tipton
204789a39adSMike Tipton qp->voter = of_bcm_voter_get(qp->dev, NULL);
205789a39adSMike Tipton if (IS_ERR(qp->voter))
206789a39adSMike Tipton return PTR_ERR(qp->voter);
207789a39adSMike Tipton
208789a39adSMike Tipton for (i = 0; i < qp->num_bcms; i++)
209789a39adSMike Tipton qcom_icc_bcm_init(qp->bcms[i], dev);
210789a39adSMike Tipton
211789a39adSMike Tipton for (i = 0; i < num_nodes; i++) {
212789a39adSMike Tipton qn = qnodes[i];
213789a39adSMike Tipton if (!qn)
214789a39adSMike Tipton continue;
215789a39adSMike Tipton
216789a39adSMike Tipton node = icc_node_create(qn->id);
217789a39adSMike Tipton if (IS_ERR(node)) {
218789a39adSMike Tipton ret = PTR_ERR(node);
21974240a5bSJohan Hovold goto err_remove_nodes;
220789a39adSMike Tipton }
221789a39adSMike Tipton
222789a39adSMike Tipton node->name = qn->name;
223789a39adSMike Tipton node->data = qn;
224789a39adSMike Tipton icc_node_add(node, provider);
225789a39adSMike Tipton
226789a39adSMike Tipton for (j = 0; j < qn->num_links; j++)
227789a39adSMike Tipton icc_link_create(node, qn->links[j]);
228789a39adSMike Tipton
229789a39adSMike Tipton data->nodes[i] = node;
230789a39adSMike Tipton }
231789a39adSMike Tipton
23274240a5bSJohan Hovold ret = icc_provider_register(provider);
23374240a5bSJohan Hovold if (ret)
23474240a5bSJohan Hovold goto err_remove_nodes;
23574240a5bSJohan Hovold
236789a39adSMike Tipton platform_set_drvdata(pdev, qp);
237789a39adSMike Tipton
23857eb1477SLuca Weiss /* Populate child NoC devices if any */
2396570d1d4SJohan Hovold if (of_get_child_count(dev->of_node) > 0) {
2406570d1d4SJohan Hovold ret = of_platform_populate(dev->of_node, NULL, NULL, dev);
2416570d1d4SJohan Hovold if (ret)
24274240a5bSJohan Hovold goto err_deregister_provider;
2436570d1d4SJohan Hovold }
24457eb1477SLuca Weiss
245789a39adSMike Tipton return 0;
24674240a5bSJohan Hovold
24774240a5bSJohan Hovold err_deregister_provider:
24874240a5bSJohan Hovold icc_provider_deregister(provider);
24974240a5bSJohan Hovold err_remove_nodes:
250789a39adSMike Tipton icc_nodes_remove(provider);
25174240a5bSJohan Hovold
252789a39adSMike Tipton return ret;
253789a39adSMike Tipton }
254789a39adSMike Tipton EXPORT_SYMBOL_GPL(qcom_icc_rpmh_probe);
255789a39adSMike Tipton
qcom_icc_rpmh_remove(struct platform_device * pdev)256789a39adSMike Tipton int qcom_icc_rpmh_remove(struct platform_device *pdev)
257789a39adSMike Tipton {
258789a39adSMike Tipton struct qcom_icc_provider *qp = platform_get_drvdata(pdev);
259789a39adSMike Tipton
26074240a5bSJohan Hovold icc_provider_deregister(&qp->provider);
261789a39adSMike Tipton icc_nodes_remove(&qp->provider);
2624681086cSUwe Kleine-König
2634681086cSUwe Kleine-König return 0;
264789a39adSMike Tipton }
265789a39adSMike Tipton EXPORT_SYMBOL_GPL(qcom_icc_rpmh_remove);
266789a39adSMike Tipton
267976daac4SDavid Dai MODULE_LICENSE("GPL v2");
268