1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Copyright (c) 2020, The Linux Foundation. All rights reserved.
4  */
5 
6 #include <asm/div64.h>
7 #include <linux/interconnect-provider.h>
8 #include <linux/list_sort.h>
9 #include <linux/module.h>
10 #include <linux/of.h>
11 #include <linux/platform_device.h>
12 
13 #include <soc/qcom/rpmh.h>
14 #include <soc/qcom/tcs.h>
15 
16 #include "bcm-voter.h"
17 #include "icc-rpmh.h"
18 
19 static LIST_HEAD(bcm_voters);
20 static DEFINE_MUTEX(bcm_voter_lock);
21 
22 /**
23  * struct bcm_voter - Bus Clock Manager voter
24  * @dev: reference to the device that communicates with the BCM
25  * @np: reference to the device node to match bcm voters
26  * @lock: mutex to protect commit and wake/sleep lists in the voter
27  * @commit_list: list containing bcms to be committed to hardware
28  * @ws_list: list containing bcms that have different wake/sleep votes
29  * @voter_node: list of bcm voters
30  */
31 struct bcm_voter {
32 	struct device *dev;
33 	struct device_node *np;
34 	struct mutex lock;
35 	struct list_head commit_list;
36 	struct list_head ws_list;
37 	struct list_head voter_node;
38 };
39 
40 static int cmp_vcd(void *priv, struct list_head *a, struct list_head *b)
41 {
42 	const struct qcom_icc_bcm *bcm_a =
43 			list_entry(a, struct qcom_icc_bcm, list);
44 	const struct qcom_icc_bcm *bcm_b =
45 			list_entry(b, struct qcom_icc_bcm, list);
46 
47 	if (bcm_a->aux_data.vcd < bcm_b->aux_data.vcd)
48 		return -1;
49 	else if (bcm_a->aux_data.vcd == bcm_b->aux_data.vcd)
50 		return 0;
51 	else
52 		return 1;
53 }
54 
55 static void bcm_aggregate(struct qcom_icc_bcm *bcm)
56 {
57 	size_t i, bucket;
58 	u64 agg_avg[QCOM_ICC_NUM_BUCKETS] = {0};
59 	u64 agg_peak[QCOM_ICC_NUM_BUCKETS] = {0};
60 	u64 temp;
61 
62 	for (bucket = 0; bucket < QCOM_ICC_NUM_BUCKETS; bucket++) {
63 		for (i = 0; i < bcm->num_nodes; i++) {
64 			temp = bcm->nodes[i]->sum_avg[bucket] * bcm->aux_data.width;
65 			do_div(temp, bcm->nodes[i]->buswidth * bcm->nodes[i]->channels);
66 			agg_avg[bucket] = max(agg_avg[bucket], temp);
67 
68 			temp = bcm->nodes[i]->max_peak[bucket] * bcm->aux_data.width;
69 			do_div(temp, bcm->nodes[i]->buswidth);
70 			agg_peak[bucket] = max(agg_peak[bucket], temp);
71 		}
72 
73 		temp = agg_avg[bucket] * 1000ULL;
74 		do_div(temp, bcm->aux_data.unit);
75 		bcm->vote_x[bucket] = temp;
76 
77 		temp = agg_peak[bucket] * 1000ULL;
78 		do_div(temp, bcm->aux_data.unit);
79 		bcm->vote_y[bucket] = temp;
80 	}
81 
82 	if (bcm->keepalive && bcm->vote_x[QCOM_ICC_BUCKET_AMC] == 0 &&
83 	    bcm->vote_y[QCOM_ICC_BUCKET_AMC] == 0) {
84 		bcm->vote_x[QCOM_ICC_BUCKET_AMC] = 1;
85 		bcm->vote_x[QCOM_ICC_BUCKET_WAKE] = 1;
86 		bcm->vote_y[QCOM_ICC_BUCKET_AMC] = 1;
87 		bcm->vote_y[QCOM_ICC_BUCKET_WAKE] = 1;
88 	}
89 }
90 
91 static inline void tcs_cmd_gen(struct tcs_cmd *cmd, u64 vote_x, u64 vote_y,
92 			       u32 addr, bool commit)
93 {
94 	bool valid = true;
95 
96 	if (!cmd)
97 		return;
98 
99 	if (vote_x == 0 && vote_y == 0)
100 		valid = false;
101 
102 	if (vote_x > BCM_TCS_CMD_VOTE_MASK)
103 		vote_x = BCM_TCS_CMD_VOTE_MASK;
104 
105 	if (vote_y > BCM_TCS_CMD_VOTE_MASK)
106 		vote_y = BCM_TCS_CMD_VOTE_MASK;
107 
108 	cmd->addr = addr;
109 	cmd->data = BCM_TCS_CMD(commit, valid, vote_x, vote_y);
110 
111 	/*
112 	 * Set the wait for completion flag on command that need to be completed
113 	 * before the next command.
114 	 */
115 	if (commit)
116 		cmd->wait = true;
117 }
118 
119 static void tcs_list_gen(struct list_head *bcm_list, int bucket,
120 			 struct tcs_cmd tcs_list[MAX_BCMS],
121 			 int n[MAX_VCD + 1])
122 {
123 	struct qcom_icc_bcm *bcm;
124 	bool commit;
125 	size_t idx = 0, batch = 0, cur_vcd_size = 0;
126 
127 	memset(n, 0, sizeof(int) * (MAX_VCD + 1));
128 
129 	list_for_each_entry(bcm, bcm_list, list) {
130 		commit = false;
131 		cur_vcd_size++;
132 		if ((list_is_last(&bcm->list, bcm_list)) ||
133 		    bcm->aux_data.vcd != list_next_entry(bcm, list)->aux_data.vcd) {
134 			commit = true;
135 			cur_vcd_size = 0;
136 		}
137 		tcs_cmd_gen(&tcs_list[idx], bcm->vote_x[bucket],
138 			    bcm->vote_y[bucket], bcm->addr, commit);
139 		idx++;
140 		n[batch]++;
141 		/*
142 		 * Batch the BCMs in such a way that we do not split them in
143 		 * multiple payloads when they are under the same VCD. This is
144 		 * to ensure that every BCM is committed since we only set the
145 		 * commit bit on the last BCM request of every VCD.
146 		 */
147 		if (n[batch] >= MAX_RPMH_PAYLOAD) {
148 			if (!commit) {
149 				n[batch] -= cur_vcd_size;
150 				n[batch + 1] = cur_vcd_size;
151 			}
152 			batch++;
153 		}
154 	}
155 }
156 
157 /**
158  * of_bcm_voter_get - gets a bcm voter handle from DT node
159  * @dev: device pointer for the consumer device
160  * @name: name for the bcm voter device
161  *
162  * This function will match a device_node pointer for the phandle
163  * specified in the device DT and return a bcm_voter handle on success.
164  *
165  * Returns bcm_voter pointer or ERR_PTR() on error. EPROBE_DEFER is returned
166  * when matching bcm voter is yet to be found.
167  */
168 struct bcm_voter *of_bcm_voter_get(struct device *dev, const char *name)
169 {
170 	struct bcm_voter *voter = ERR_PTR(-EPROBE_DEFER);
171 	struct bcm_voter *temp;
172 	struct device_node *np, *node;
173 	int idx = 0;
174 
175 	if (!dev || !dev->of_node)
176 		return ERR_PTR(-ENODEV);
177 
178 	np = dev->of_node;
179 
180 	if (name) {
181 		idx = of_property_match_string(np, "qcom,bcm-voter-names", name);
182 		if (idx < 0)
183 			return ERR_PTR(idx);
184 	}
185 
186 	node = of_parse_phandle(np, "qcom,bcm-voters", idx);
187 
188 	mutex_lock(&bcm_voter_lock);
189 	list_for_each_entry(temp, &bcm_voters, voter_node) {
190 		if (temp->np == node) {
191 			voter = temp;
192 			break;
193 		}
194 	}
195 	mutex_unlock(&bcm_voter_lock);
196 
197 	return voter;
198 }
199 EXPORT_SYMBOL_GPL(of_bcm_voter_get);
200 
201 /**
202  * qcom_icc_bcm_voter_add - queues up the bcm nodes that require updates
203  * @voter: voter that the bcms are being added to
204  * @bcm: bcm to add to the commit and wake sleep list
205  */
206 void qcom_icc_bcm_voter_add(struct bcm_voter *voter, struct qcom_icc_bcm *bcm)
207 {
208 	if (!voter)
209 		return;
210 
211 	mutex_lock(&voter->lock);
212 	if (list_empty(&bcm->list))
213 		list_add_tail(&bcm->list, &voter->commit_list);
214 
215 	if (list_empty(&bcm->ws_list))
216 		list_add_tail(&bcm->ws_list, &voter->ws_list);
217 
218 	mutex_unlock(&voter->lock);
219 }
220 EXPORT_SYMBOL_GPL(qcom_icc_bcm_voter_add);
221 
222 /**
223  * qcom_icc_bcm_voter_commit - generates and commits tcs cmds based on bcms
224  * @voter: voter that needs flushing
225  *
226  * This function generates a set of AMC commands and flushes to the BCM device
227  * associated with the voter. It conditionally generate WAKE and SLEEP commands
228  * based on deltas between WAKE/SLEEP requirements. The ws_list persists
229  * through multiple commit requests and bcm nodes are removed only when the
230  * requirements for WAKE matches SLEEP.
231  *
232  * Returns 0 on success, or an appropriate error code otherwise.
233  */
234 int qcom_icc_bcm_voter_commit(struct bcm_voter *voter)
235 {
236 	struct qcom_icc_bcm *bcm;
237 	struct qcom_icc_bcm *bcm_tmp;
238 	int commit_idx[MAX_VCD + 1];
239 	struct tcs_cmd cmds[MAX_BCMS];
240 	int ret = 0;
241 
242 	if (!voter)
243 		return 0;
244 
245 	mutex_lock(&voter->lock);
246 	list_for_each_entry(bcm, &voter->commit_list, list)
247 		bcm_aggregate(bcm);
248 
249 	/*
250 	 * Pre sort the BCMs based on VCD for ease of generating a command list
251 	 * that groups the BCMs with the same VCD together. VCDs are numbered
252 	 * with lowest being the most expensive time wise, ensuring that
253 	 * those commands are being sent the earliest in the queue. This needs
254 	 * to be sorted every commit since we can't guarantee the order in which
255 	 * the BCMs are added to the list.
256 	 */
257 	list_sort(NULL, &voter->commit_list, cmp_vcd);
258 
259 	/*
260 	 * Construct the command list based on a pre ordered list of BCMs
261 	 * based on VCD.
262 	 */
263 	tcs_list_gen(&voter->commit_list, QCOM_ICC_BUCKET_AMC, cmds, commit_idx);
264 
265 	if (!commit_idx[0])
266 		goto out;
267 
268 	ret = rpmh_invalidate(voter->dev);
269 	if (ret) {
270 		pr_err("Error invalidating RPMH client (%d)\n", ret);
271 		goto out;
272 	}
273 
274 	ret = rpmh_write_batch(voter->dev, RPMH_ACTIVE_ONLY_STATE,
275 			       cmds, commit_idx);
276 	if (ret) {
277 		pr_err("Error sending AMC RPMH requests (%d)\n", ret);
278 		goto out;
279 	}
280 
281 	list_for_each_entry_safe(bcm, bcm_tmp, &voter->commit_list, list)
282 		list_del_init(&bcm->list);
283 
284 	list_for_each_entry_safe(bcm, bcm_tmp, &voter->ws_list, ws_list) {
285 		/*
286 		 * Only generate WAKE and SLEEP commands if a resource's
287 		 * requirements change as the execution environment transitions
288 		 * between different power states.
289 		 */
290 		if (bcm->vote_x[QCOM_ICC_BUCKET_WAKE] !=
291 		    bcm->vote_x[QCOM_ICC_BUCKET_SLEEP] ||
292 		    bcm->vote_y[QCOM_ICC_BUCKET_WAKE] !=
293 		    bcm->vote_y[QCOM_ICC_BUCKET_SLEEP])
294 			list_add_tail(&bcm->list, &voter->commit_list);
295 		else
296 			list_del_init(&bcm->ws_list);
297 	}
298 
299 	if (list_empty(&voter->commit_list))
300 		goto out;
301 
302 	list_sort(NULL, &voter->commit_list, cmp_vcd);
303 
304 	tcs_list_gen(&voter->commit_list, QCOM_ICC_BUCKET_WAKE, cmds, commit_idx);
305 
306 	ret = rpmh_write_batch(voter->dev, RPMH_WAKE_ONLY_STATE, cmds, commit_idx);
307 	if (ret) {
308 		pr_err("Error sending WAKE RPMH requests (%d)\n", ret);
309 		goto out;
310 	}
311 
312 	tcs_list_gen(&voter->commit_list, QCOM_ICC_BUCKET_SLEEP, cmds, commit_idx);
313 
314 	ret = rpmh_write_batch(voter->dev, RPMH_SLEEP_STATE, cmds, commit_idx);
315 	if (ret) {
316 		pr_err("Error sending SLEEP RPMH requests (%d)\n", ret);
317 		goto out;
318 	}
319 
320 out:
321 	list_for_each_entry_safe(bcm, bcm_tmp, &voter->commit_list, list)
322 		list_del_init(&bcm->list);
323 
324 	mutex_unlock(&voter->lock);
325 	return ret;
326 }
327 EXPORT_SYMBOL_GPL(qcom_icc_bcm_voter_commit);
328 
329 static int qcom_icc_bcm_voter_probe(struct platform_device *pdev)
330 {
331 	struct bcm_voter *voter;
332 
333 	voter = devm_kzalloc(&pdev->dev, sizeof(*voter), GFP_KERNEL);
334 	if (!voter)
335 		return -ENOMEM;
336 
337 	voter->dev = &pdev->dev;
338 	voter->np = pdev->dev.of_node;
339 	mutex_init(&voter->lock);
340 	INIT_LIST_HEAD(&voter->commit_list);
341 	INIT_LIST_HEAD(&voter->ws_list);
342 
343 	mutex_lock(&bcm_voter_lock);
344 	list_add_tail(&voter->voter_node, &bcm_voters);
345 	mutex_unlock(&bcm_voter_lock);
346 
347 	return 0;
348 }
349 
350 static const struct of_device_id bcm_voter_of_match[] = {
351 	{ .compatible = "qcom,bcm-voter" },
352 	{ }
353 };
354 
355 static struct platform_driver qcom_icc_bcm_voter_driver = {
356 	.probe = qcom_icc_bcm_voter_probe,
357 	.driver = {
358 		.name		= "bcm_voter",
359 		.of_match_table = bcm_voter_of_match,
360 	},
361 };
362 module_platform_driver(qcom_icc_bcm_voter_driver);
363 
364 MODULE_AUTHOR("David Dai <daidavid1@codeaurora.org>");
365 MODULE_DESCRIPTION("Qualcomm BCM Voter interconnect driver");
366 MODULE_LICENSE("GPL v2");
367