1 // SPDX-License-Identifier: GPL-2.0
3 * Copyright (c) 2020, The Linux Foundation. All rights reserved.
7 #include <linux/interconnect-provider.h>
8 #include <linux/list_sort.h>
9 #include <linux/module.h>
11 #include <linux/platform_device.h>
13 #include <soc/qcom/rpmh.h>
14 #include <soc/qcom/tcs.h>
16 #include "bcm-voter.h"
19 static LIST_HEAD(bcm_voters
);
20 static DEFINE_MUTEX(bcm_voter_lock
);
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
33 struct device_node
*np
;
35 struct list_head commit_list
;
36 struct list_head ws_list
;
37 struct list_head voter_node
;
40 static int cmp_vcd(void *priv
, struct list_head
*a
, struct list_head
*b
)
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
);
47 if (bcm_a
->aux_data
.vcd
< bcm_b
->aux_data
.vcd
)
49 else if (bcm_a
->aux_data
.vcd
== bcm_b
->aux_data
.vcd
)
55 static void bcm_aggregate(struct qcom_icc_bcm
*bcm
)
58 u64 agg_avg
[QCOM_ICC_NUM_BUCKETS
] = {0};
59 u64 agg_peak
[QCOM_ICC_NUM_BUCKETS
] = {0};
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
);
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
);
73 temp
= agg_avg
[bucket
] * 1000ULL;
74 do_div(temp
, bcm
->aux_data
.unit
);
75 bcm
->vote_x
[bucket
] = temp
;
77 temp
= agg_peak
[bucket
] * 1000ULL;
78 do_div(temp
, bcm
->aux_data
.unit
);
79 bcm
->vote_y
[bucket
] = temp
;
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;
91 static inline void tcs_cmd_gen(struct tcs_cmd
*cmd
, u64 vote_x
, u64 vote_y
,
92 u32 addr
, bool commit
)
99 memset(cmd
, 0, sizeof(*cmd
));
101 if (vote_x
== 0 && vote_y
== 0)
104 if (vote_x
> BCM_TCS_CMD_VOTE_MASK
)
105 vote_x
= BCM_TCS_CMD_VOTE_MASK
;
107 if (vote_y
> BCM_TCS_CMD_VOTE_MASK
)
108 vote_y
= BCM_TCS_CMD_VOTE_MASK
;
111 cmd
->data
= BCM_TCS_CMD(commit
, valid
, vote_x
, vote_y
);
114 * Set the wait for completion flag on command that need to be completed
115 * before the next command.
120 static void tcs_list_gen(struct list_head
*bcm_list
, int bucket
,
121 struct tcs_cmd tcs_list
[MAX_BCMS
],
124 struct qcom_icc_bcm
*bcm
;
126 size_t idx
= 0, batch
= 0, cur_vcd_size
= 0;
128 memset(n
, 0, sizeof(int) * (MAX_VCD
+ 1));
130 list_for_each_entry(bcm
, bcm_list
, list
) {
133 if ((list_is_last(&bcm
->list
, bcm_list
)) ||
134 bcm
->aux_data
.vcd
!= list_next_entry(bcm
, list
)->aux_data
.vcd
) {
138 tcs_cmd_gen(&tcs_list
[idx
], bcm
->vote_x
[bucket
],
139 bcm
->vote_y
[bucket
], bcm
->addr
, commit
);
143 * Batch the BCMs in such a way that we do not split them in
144 * multiple payloads when they are under the same VCD. This is
145 * to ensure that every BCM is committed since we only set the
146 * commit bit on the last BCM request of every VCD.
148 if (n
[batch
] >= MAX_RPMH_PAYLOAD
) {
150 n
[batch
] -= cur_vcd_size
;
151 n
[batch
+ 1] = cur_vcd_size
;
159 * of_bcm_voter_get - gets a bcm voter handle from DT node
160 * @dev: device pointer for the consumer device
161 * @name: name for the bcm voter device
163 * This function will match a device_node pointer for the phandle
164 * specified in the device DT and return a bcm_voter handle on success.
166 * Returns bcm_voter pointer or ERR_PTR() on error. EPROBE_DEFER is returned
167 * when matching bcm voter is yet to be found.
169 struct bcm_voter
*of_bcm_voter_get(struct device
*dev
, const char *name
)
171 struct bcm_voter
*voter
= ERR_PTR(-EPROBE_DEFER
);
172 struct bcm_voter
*temp
;
173 struct device_node
*np
, *node
;
176 if (!dev
|| !dev
->of_node
)
177 return ERR_PTR(-ENODEV
);
182 idx
= of_property_match_string(np
, "qcom,bcm-voter-names", name
);
187 node
= of_parse_phandle(np
, "qcom,bcm-voters", idx
);
189 mutex_lock(&bcm_voter_lock
);
190 list_for_each_entry(temp
, &bcm_voters
, voter_node
) {
191 if (temp
->np
== node
) {
196 mutex_unlock(&bcm_voter_lock
);
200 EXPORT_SYMBOL_GPL(of_bcm_voter_get
);
203 * qcom_icc_bcm_voter_add - queues up the bcm nodes that require updates
204 * @voter: voter that the bcms are being added to
205 * @bcm: bcm to add to the commit and wake sleep list
207 void qcom_icc_bcm_voter_add(struct bcm_voter
*voter
, struct qcom_icc_bcm
*bcm
)
212 mutex_lock(&voter
->lock
);
213 if (list_empty(&bcm
->list
))
214 list_add_tail(&bcm
->list
, &voter
->commit_list
);
216 if (list_empty(&bcm
->ws_list
))
217 list_add_tail(&bcm
->ws_list
, &voter
->ws_list
);
219 mutex_unlock(&voter
->lock
);
221 EXPORT_SYMBOL_GPL(qcom_icc_bcm_voter_add
);
224 * qcom_icc_bcm_voter_commit - generates and commits tcs cmds based on bcms
225 * @voter: voter that needs flushing
227 * This function generates a set of AMC commands and flushes to the BCM device
228 * associated with the voter. It conditionally generate WAKE and SLEEP commands
229 * based on deltas between WAKE/SLEEP requirements. The ws_list persists
230 * through multiple commit requests and bcm nodes are removed only when the
231 * requirements for WAKE matches SLEEP.
233 * Returns 0 on success, or an appropriate error code otherwise.
235 int qcom_icc_bcm_voter_commit(struct bcm_voter
*voter
)
237 struct qcom_icc_bcm
*bcm
;
238 struct qcom_icc_bcm
*bcm_tmp
;
239 int commit_idx
[MAX_VCD
+ 1];
240 struct tcs_cmd cmds
[MAX_BCMS
];
246 mutex_lock(&voter
->lock
);
247 list_for_each_entry(bcm
, &voter
->commit_list
, list
)
251 * Pre sort the BCMs based on VCD for ease of generating a command list
252 * that groups the BCMs with the same VCD together. VCDs are numbered
253 * with lowest being the most expensive time wise, ensuring that
254 * those commands are being sent the earliest in the queue. This needs
255 * to be sorted every commit since we can't guarantee the order in which
256 * the BCMs are added to the list.
258 list_sort(NULL
, &voter
->commit_list
, cmp_vcd
);
261 * Construct the command list based on a pre ordered list of BCMs
264 tcs_list_gen(&voter
->commit_list
, QCOM_ICC_BUCKET_AMC
, cmds
, commit_idx
);
269 ret
= rpmh_invalidate(voter
->dev
);
271 pr_err("Error invalidating RPMH client (%d)\n", ret
);
275 ret
= rpmh_write_batch(voter
->dev
, RPMH_ACTIVE_ONLY_STATE
,
278 pr_err("Error sending AMC RPMH requests (%d)\n", ret
);
282 list_for_each_entry_safe(bcm
, bcm_tmp
, &voter
->commit_list
, list
)
283 list_del_init(&bcm
->list
);
285 list_for_each_entry_safe(bcm
, bcm_tmp
, &voter
->ws_list
, ws_list
) {
287 * Only generate WAKE and SLEEP commands if a resource's
288 * requirements change as the execution environment transitions
289 * between different power states.
291 if (bcm
->vote_x
[QCOM_ICC_BUCKET_WAKE
] !=
292 bcm
->vote_x
[QCOM_ICC_BUCKET_SLEEP
] ||
293 bcm
->vote_y
[QCOM_ICC_BUCKET_WAKE
] !=
294 bcm
->vote_y
[QCOM_ICC_BUCKET_SLEEP
])
295 list_add_tail(&bcm
->list
, &voter
->commit_list
);
297 list_del_init(&bcm
->ws_list
);
300 if (list_empty(&voter
->commit_list
))
303 list_sort(NULL
, &voter
->commit_list
, cmp_vcd
);
305 tcs_list_gen(&voter
->commit_list
, QCOM_ICC_BUCKET_WAKE
, cmds
, commit_idx
);
307 ret
= rpmh_write_batch(voter
->dev
, RPMH_WAKE_ONLY_STATE
, cmds
, commit_idx
);
309 pr_err("Error sending WAKE RPMH requests (%d)\n", ret
);
313 tcs_list_gen(&voter
->commit_list
, QCOM_ICC_BUCKET_SLEEP
, cmds
, commit_idx
);
315 ret
= rpmh_write_batch(voter
->dev
, RPMH_SLEEP_STATE
, cmds
, commit_idx
);
317 pr_err("Error sending SLEEP RPMH requests (%d)\n", ret
);
322 list_for_each_entry_safe(bcm
, bcm_tmp
, &voter
->commit_list
, list
)
323 list_del_init(&bcm
->list
);
325 mutex_unlock(&voter
->lock
);
328 EXPORT_SYMBOL_GPL(qcom_icc_bcm_voter_commit
);
330 static int qcom_icc_bcm_voter_probe(struct platform_device
*pdev
)
332 struct bcm_voter
*voter
;
334 voter
= devm_kzalloc(&pdev
->dev
, sizeof(*voter
), GFP_KERNEL
);
338 voter
->dev
= &pdev
->dev
;
339 voter
->np
= pdev
->dev
.of_node
;
340 mutex_init(&voter
->lock
);
341 INIT_LIST_HEAD(&voter
->commit_list
);
342 INIT_LIST_HEAD(&voter
->ws_list
);
344 mutex_lock(&bcm_voter_lock
);
345 list_add_tail(&voter
->voter_node
, &bcm_voters
);
346 mutex_unlock(&bcm_voter_lock
);
351 static const struct of_device_id bcm_voter_of_match
[] = {
352 { .compatible
= "qcom,bcm-voter" },
356 static struct platform_driver qcom_icc_bcm_voter_driver
= {
357 .probe
= qcom_icc_bcm_voter_probe
,
360 .of_match_table
= bcm_voter_of_match
,
363 module_platform_driver(qcom_icc_bcm_voter_driver
);
365 MODULE_AUTHOR("David Dai <daidavid1@codeaurora.org>");
366 MODULE_DESCRIPTION("Qualcomm BCM Voter interconnect driver");
367 MODULE_LICENSE("GPL v2");