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 if (vote_x
== 0 && vote_y
== 0)
102 if (vote_x
> BCM_TCS_CMD_VOTE_MASK
)
103 vote_x
= BCM_TCS_CMD_VOTE_MASK
;
105 if (vote_y
> BCM_TCS_CMD_VOTE_MASK
)
106 vote_y
= BCM_TCS_CMD_VOTE_MASK
;
109 cmd
->data
= BCM_TCS_CMD(commit
, valid
, vote_x
, vote_y
);
112 * Set the wait for completion flag on command that need to be completed
113 * before the next command.
119 static void tcs_list_gen(struct list_head
*bcm_list
, int bucket
,
120 struct tcs_cmd tcs_list
[MAX_BCMS
],
123 struct qcom_icc_bcm
*bcm
;
125 size_t idx
= 0, batch
= 0, cur_vcd_size
= 0;
127 memset(n
, 0, sizeof(int) * (MAX_VCD
+ 1));
129 list_for_each_entry(bcm
, bcm_list
, list
) {
132 if ((list_is_last(&bcm
->list
, bcm_list
)) ||
133 bcm
->aux_data
.vcd
!= list_next_entry(bcm
, list
)->aux_data
.vcd
) {
137 tcs_cmd_gen(&tcs_list
[idx
], bcm
->vote_x
[bucket
],
138 bcm
->vote_y
[bucket
], bcm
->addr
, commit
);
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.
147 if (n
[batch
] >= MAX_RPMH_PAYLOAD
) {
149 n
[batch
] -= cur_vcd_size
;
150 n
[batch
+ 1] = cur_vcd_size
;
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
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.
165 * Returns bcm_voter pointer or ERR_PTR() on error. EPROBE_DEFER is returned
166 * when matching bcm voter is yet to be found.
168 struct bcm_voter
*of_bcm_voter_get(struct device
*dev
, const char *name
)
170 struct bcm_voter
*voter
= ERR_PTR(-EPROBE_DEFER
);
171 struct bcm_voter
*temp
;
172 struct device_node
*np
, *node
;
175 if (!dev
|| !dev
->of_node
)
176 return ERR_PTR(-ENODEV
);
181 idx
= of_property_match_string(np
, "qcom,bcm-voter-names", name
);
186 node
= of_parse_phandle(np
, "qcom,bcm-voters", idx
);
188 mutex_lock(&bcm_voter_lock
);
189 list_for_each_entry(temp
, &bcm_voters
, voter_node
) {
190 if (temp
->np
== node
) {
195 mutex_unlock(&bcm_voter_lock
);
199 EXPORT_SYMBOL_GPL(of_bcm_voter_get
);
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
206 void qcom_icc_bcm_voter_add(struct bcm_voter
*voter
, struct qcom_icc_bcm
*bcm
)
211 mutex_lock(&voter
->lock
);
212 if (list_empty(&bcm
->list
))
213 list_add_tail(&bcm
->list
, &voter
->commit_list
);
215 if (list_empty(&bcm
->ws_list
))
216 list_add_tail(&bcm
->ws_list
, &voter
->ws_list
);
218 mutex_unlock(&voter
->lock
);
220 EXPORT_SYMBOL_GPL(qcom_icc_bcm_voter_add
);
223 * qcom_icc_bcm_voter_commit - generates and commits tcs cmds based on bcms
224 * @voter: voter that needs flushing
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.
232 * Returns 0 on success, or an appropriate error code otherwise.
234 int qcom_icc_bcm_voter_commit(struct bcm_voter
*voter
)
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
];
245 mutex_lock(&voter
->lock
);
246 list_for_each_entry(bcm
, &voter
->commit_list
, list
)
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.
257 list_sort(NULL
, &voter
->commit_list
, cmp_vcd
);
260 * Construct the command list based on a pre ordered list of BCMs
263 tcs_list_gen(&voter
->commit_list
, QCOM_ICC_BUCKET_AMC
, cmds
, commit_idx
);
268 ret
= rpmh_invalidate(voter
->dev
);
270 pr_err("Error invalidating RPMH client (%d)\n", ret
);
274 ret
= rpmh_write_batch(voter
->dev
, RPMH_ACTIVE_ONLY_STATE
,
277 pr_err("Error sending AMC RPMH requests (%d)\n", ret
);
281 list_for_each_entry_safe(bcm
, bcm_tmp
, &voter
->commit_list
, list
)
282 list_del_init(&bcm
->list
);
284 list_for_each_entry_safe(bcm
, bcm_tmp
, &voter
->ws_list
, ws_list
) {
286 * Only generate WAKE and SLEEP commands if a resource's
287 * requirements change as the execution environment transitions
288 * between different power states.
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
);
296 list_del_init(&bcm
->ws_list
);
299 if (list_empty(&voter
->commit_list
))
302 list_sort(NULL
, &voter
->commit_list
, cmp_vcd
);
304 tcs_list_gen(&voter
->commit_list
, QCOM_ICC_BUCKET_WAKE
, cmds
, commit_idx
);
306 ret
= rpmh_write_batch(voter
->dev
, RPMH_WAKE_ONLY_STATE
, cmds
, commit_idx
);
308 pr_err("Error sending WAKE RPMH requests (%d)\n", ret
);
312 tcs_list_gen(&voter
->commit_list
, QCOM_ICC_BUCKET_SLEEP
, cmds
, commit_idx
);
314 ret
= rpmh_write_batch(voter
->dev
, RPMH_SLEEP_STATE
, cmds
, commit_idx
);
316 pr_err("Error sending SLEEP RPMH requests (%d)\n", ret
);
321 list_for_each_entry_safe(bcm
, bcm_tmp
, &voter
->commit_list
, list
)
322 list_del_init(&bcm
->list
);
324 mutex_unlock(&voter
->lock
);
327 EXPORT_SYMBOL_GPL(qcom_icc_bcm_voter_commit
);
329 static int qcom_icc_bcm_voter_probe(struct platform_device
*pdev
)
331 struct bcm_voter
*voter
;
333 voter
= devm_kzalloc(&pdev
->dev
, sizeof(*voter
), GFP_KERNEL
);
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
);
343 mutex_lock(&bcm_voter_lock
);
344 list_add_tail(&voter
->voter_node
, &bcm_voters
);
345 mutex_unlock(&bcm_voter_lock
);
350 static const struct of_device_id bcm_voter_of_match
[] = {
351 { .compatible
= "qcom,bcm-voter" },
355 static struct platform_driver qcom_icc_bcm_voter_driver
= {
356 .probe
= qcom_icc_bcm_voter_probe
,
359 .of_match_table
= bcm_voter_of_match
,
362 module_platform_driver(qcom_icc_bcm_voter_driver
);
364 MODULE_AUTHOR("David Dai <daidavid1@codeaurora.org>");
365 MODULE_DESCRIPTION("Qualcomm BCM Voter interconnect driver");
366 MODULE_LICENSE("GPL v2");