1// SPDX-License-Identifier: GPL-2.0
2/*
3 * Copyright (c) 2020, The Linux Foundation. All rights reserved.
4 */
5
6#include <linux/interconnect.h>
7#include <linux/interconnect-provider.h>
8#include <linux/module.h>
9#include <linux/of.h>
10#include <linux/slab.h>
11
12#include "bcm-voter.h"
13#include "icc-rpmh.h"
14
15/**
16 * qcom_icc_pre_aggregate - cleans up stale values from prior icc_set
17 * @node: icc node to operate on
18 */
19void qcom_icc_pre_aggregate(struct icc_node *node)
20{
21	size_t i;
22	struct qcom_icc_node *qn;
23	struct qcom_icc_provider *qp;
24
25	qn = node->data;
26	qp = to_qcom_provider(node->provider);
27
28	for (i = 0; i < QCOM_ICC_NUM_BUCKETS; i++) {
29		qn->sum_avg[i] = 0;
30		qn->max_peak[i] = 0;
31	}
32
33	for (i = 0; i < qn->num_bcms; i++)
34		qcom_icc_bcm_voter_add(qp->voter, qn->bcms[i]);
35}
36EXPORT_SYMBOL_GPL(qcom_icc_pre_aggregate);
37
38/**
39 * qcom_icc_aggregate - aggregate bw for buckets indicated by tag
40 * @node: node to aggregate
41 * @tag: tag to indicate which buckets to aggregate
42 * @avg_bw: new bw to sum aggregate
43 * @peak_bw: new bw to max aggregate
44 * @agg_avg: existing aggregate avg bw val
45 * @agg_peak: existing aggregate peak bw val
46 */
47int qcom_icc_aggregate(struct icc_node *node, u32 tag, u32 avg_bw,
48		       u32 peak_bw, u32 *agg_avg, u32 *agg_peak)
49{
50	size_t i;
51	struct qcom_icc_node *qn;
52
53	qn = node->data;
54
55	if (!tag)
56		tag = QCOM_ICC_TAG_ALWAYS;
57
58	for (i = 0; i < QCOM_ICC_NUM_BUCKETS; i++) {
59		if (tag & BIT(i)) {
60			qn->sum_avg[i] += avg_bw;
61			qn->max_peak[i] = max_t(u32, qn->max_peak[i], peak_bw);
62		}
63
64		if (node->init_avg || node->init_peak) {
65			qn->sum_avg[i] = max_t(u64, qn->sum_avg[i], node->init_avg);
66			qn->max_peak[i] = max_t(u64, qn->max_peak[i], node->init_peak);
67		}
68	}
69
70	*agg_avg += avg_bw;
71	*agg_peak = max_t(u32, *agg_peak, peak_bw);
72
73	return 0;
74}
75EXPORT_SYMBOL_GPL(qcom_icc_aggregate);
76
77/**
78 * qcom_icc_set - set the constraints based on path
79 * @src: source node for the path to set constraints on
80 * @dst: destination node for the path to set constraints on
81 *
82 * Return: 0 on success, or an error code otherwise
83 */
84int qcom_icc_set(struct icc_node *src, struct icc_node *dst)
85{
86	struct qcom_icc_provider *qp;
87	struct icc_node *node;
88
89	if (!src)
90		node = dst;
91	else
92		node = src;
93
94	qp = to_qcom_provider(node->provider);
95
96	qcom_icc_bcm_voter_commit(qp->voter);
97
98	return 0;
99}
100EXPORT_SYMBOL_GPL(qcom_icc_set);
101
102struct icc_node_data *qcom_icc_xlate_extended(struct of_phandle_args *spec, void *data)
103{
104	struct icc_node_data *ndata;
105	struct icc_node *node;
106
107	node = of_icc_xlate_onecell(spec, data);
108	if (IS_ERR(node))
109		return ERR_CAST(node);
110
111	ndata = kzalloc(sizeof(*ndata), GFP_KERNEL);
112	if (!ndata)
113		return ERR_PTR(-ENOMEM);
114
115	ndata->node = node;
116
117	if (spec->args_count == 2)
118		ndata->tag = spec->args[1];
119
120	if (spec->args_count > 2)
121		pr_warn("%pOF: Too many arguments, path tag is not parsed\n", spec->np);
122
123	return ndata;
124}
125EXPORT_SYMBOL_GPL(qcom_icc_xlate_extended);
126
127/**
128 * qcom_icc_bcm_init - populates bcm aux data and connect qnodes
129 * @bcm: bcm to be initialized
130 * @dev: associated provider device
131 *
132 * Return: 0 on success, or an error code otherwise
133 */
134int qcom_icc_bcm_init(struct qcom_icc_bcm *bcm, struct device *dev)
135{
136	struct qcom_icc_node *qn;
137	const struct bcm_db *data;
138	size_t data_count;
139	int i;
140
141	/* BCM is already initialised*/
142	if (bcm->addr)
143		return 0;
144
145	bcm->addr = cmd_db_read_addr(bcm->name);
146	if (!bcm->addr) {
147		dev_err(dev, "%s could not find RPMh address\n",
148			bcm->name);
149		return -EINVAL;
150	}
151
152	data = cmd_db_read_aux_data(bcm->name, &data_count);
153	if (IS_ERR(data)) {
154		dev_err(dev, "%s command db read error (%ld)\n",
155			bcm->name, PTR_ERR(data));
156		return PTR_ERR(data);
157	}
158	if (!data_count) {
159		dev_err(dev, "%s command db missing or partial aux data\n",
160			bcm->name);
161		return -EINVAL;
162	}
163
164	bcm->aux_data.unit = le32_to_cpu(data->unit);
165	bcm->aux_data.width = le16_to_cpu(data->width);
166	bcm->aux_data.vcd = data->vcd;
167	bcm->aux_data.reserved = data->reserved;
168	INIT_LIST_HEAD(&bcm->list);
169	INIT_LIST_HEAD(&bcm->ws_list);
170
171	if (!bcm->vote_scale)
172		bcm->vote_scale = 1000;
173
174	/* Link Qnodes to their respective BCMs */
175	for (i = 0; i < bcm->num_nodes; i++) {
176		qn = bcm->nodes[i];
177		qn->bcms[qn->num_bcms] = bcm;
178		qn->num_bcms++;
179	}
180
181	return 0;
182}
183EXPORT_SYMBOL_GPL(qcom_icc_bcm_init);
184
185MODULE_LICENSE("GPL v2");
186