162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (c) 2020-2021, The Linux Foundation. All rights reserved.
462306a36Sopenharmony_ci */
562306a36Sopenharmony_ci
662306a36Sopenharmony_ci#include <asm/div64.h>
762306a36Sopenharmony_ci#include <linux/interconnect-provider.h>
862306a36Sopenharmony_ci#include <linux/list_sort.h>
962306a36Sopenharmony_ci#include <linux/module.h>
1062306a36Sopenharmony_ci#include <linux/of.h>
1162306a36Sopenharmony_ci#include <linux/platform_device.h>
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ci#include <soc/qcom/rpmh.h>
1462306a36Sopenharmony_ci#include <soc/qcom/tcs.h>
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci#include "bcm-voter.h"
1762306a36Sopenharmony_ci#include "icc-rpmh.h"
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_cistatic LIST_HEAD(bcm_voters);
2062306a36Sopenharmony_cistatic DEFINE_MUTEX(bcm_voter_lock);
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci/**
2362306a36Sopenharmony_ci * struct bcm_voter - Bus Clock Manager voter
2462306a36Sopenharmony_ci * @dev: reference to the device that communicates with the BCM
2562306a36Sopenharmony_ci * @np: reference to the device node to match bcm voters
2662306a36Sopenharmony_ci * @lock: mutex to protect commit and wake/sleep lists in the voter
2762306a36Sopenharmony_ci * @commit_list: list containing bcms to be committed to hardware
2862306a36Sopenharmony_ci * @ws_list: list containing bcms that have different wake/sleep votes
2962306a36Sopenharmony_ci * @voter_node: list of bcm voters
3062306a36Sopenharmony_ci * @tcs_wait: mask for which buckets require TCS completion
3162306a36Sopenharmony_ci */
3262306a36Sopenharmony_cistruct bcm_voter {
3362306a36Sopenharmony_ci	struct device *dev;
3462306a36Sopenharmony_ci	struct device_node *np;
3562306a36Sopenharmony_ci	struct mutex lock;
3662306a36Sopenharmony_ci	struct list_head commit_list;
3762306a36Sopenharmony_ci	struct list_head ws_list;
3862306a36Sopenharmony_ci	struct list_head voter_node;
3962306a36Sopenharmony_ci	u32 tcs_wait;
4062306a36Sopenharmony_ci};
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_cistatic int cmp_vcd(void *priv, const struct list_head *a, const struct list_head *b)
4362306a36Sopenharmony_ci{
4462306a36Sopenharmony_ci	const struct qcom_icc_bcm *bcm_a = list_entry(a, struct qcom_icc_bcm, list);
4562306a36Sopenharmony_ci	const struct qcom_icc_bcm *bcm_b = list_entry(b, struct qcom_icc_bcm, list);
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci	return bcm_a->aux_data.vcd - bcm_b->aux_data.vcd;
4862306a36Sopenharmony_ci}
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_cistatic u64 bcm_div(u64 num, u32 base)
5162306a36Sopenharmony_ci{
5262306a36Sopenharmony_ci	/* Ensure that small votes aren't lost. */
5362306a36Sopenharmony_ci	if (num && num < base)
5462306a36Sopenharmony_ci		return 1;
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci	do_div(num, base);
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci	return num;
5962306a36Sopenharmony_ci}
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci/* BCMs with enable_mask use one-hot-encoding for on/off signaling */
6262306a36Sopenharmony_cistatic void bcm_aggregate_mask(struct qcom_icc_bcm *bcm)
6362306a36Sopenharmony_ci{
6462306a36Sopenharmony_ci	struct qcom_icc_node *node;
6562306a36Sopenharmony_ci	int bucket, i;
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci	for (bucket = 0; bucket < QCOM_ICC_NUM_BUCKETS; bucket++) {
6862306a36Sopenharmony_ci		bcm->vote_x[bucket] = 0;
6962306a36Sopenharmony_ci		bcm->vote_y[bucket] = 0;
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci		for (i = 0; i < bcm->num_nodes; i++) {
7262306a36Sopenharmony_ci			node = bcm->nodes[i];
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci			/* If any vote in this bucket exists, keep the BCM enabled */
7562306a36Sopenharmony_ci			if (node->sum_avg[bucket] || node->max_peak[bucket]) {
7662306a36Sopenharmony_ci				bcm->vote_x[bucket] = 0;
7762306a36Sopenharmony_ci				bcm->vote_y[bucket] = bcm->enable_mask;
7862306a36Sopenharmony_ci				break;
7962306a36Sopenharmony_ci			}
8062306a36Sopenharmony_ci		}
8162306a36Sopenharmony_ci	}
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci	if (bcm->keepalive) {
8462306a36Sopenharmony_ci		bcm->vote_x[QCOM_ICC_BUCKET_AMC] = bcm->enable_mask;
8562306a36Sopenharmony_ci		bcm->vote_x[QCOM_ICC_BUCKET_WAKE] = bcm->enable_mask;
8662306a36Sopenharmony_ci		bcm->vote_y[QCOM_ICC_BUCKET_AMC] = bcm->enable_mask;
8762306a36Sopenharmony_ci		bcm->vote_y[QCOM_ICC_BUCKET_WAKE] = bcm->enable_mask;
8862306a36Sopenharmony_ci	}
8962306a36Sopenharmony_ci}
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_cistatic void bcm_aggregate(struct qcom_icc_bcm *bcm)
9262306a36Sopenharmony_ci{
9362306a36Sopenharmony_ci	struct qcom_icc_node *node;
9462306a36Sopenharmony_ci	size_t i, bucket;
9562306a36Sopenharmony_ci	u64 agg_avg[QCOM_ICC_NUM_BUCKETS] = {0};
9662306a36Sopenharmony_ci	u64 agg_peak[QCOM_ICC_NUM_BUCKETS] = {0};
9762306a36Sopenharmony_ci	u64 temp;
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci	for (bucket = 0; bucket < QCOM_ICC_NUM_BUCKETS; bucket++) {
10062306a36Sopenharmony_ci		for (i = 0; i < bcm->num_nodes; i++) {
10162306a36Sopenharmony_ci			node = bcm->nodes[i];
10262306a36Sopenharmony_ci			temp = bcm_div(node->sum_avg[bucket] * bcm->aux_data.width,
10362306a36Sopenharmony_ci				       node->buswidth * node->channels);
10462306a36Sopenharmony_ci			agg_avg[bucket] = max(agg_avg[bucket], temp);
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci			temp = bcm_div(node->max_peak[bucket] * bcm->aux_data.width,
10762306a36Sopenharmony_ci				       node->buswidth);
10862306a36Sopenharmony_ci			agg_peak[bucket] = max(agg_peak[bucket], temp);
10962306a36Sopenharmony_ci		}
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci		temp = agg_avg[bucket] * bcm->vote_scale;
11262306a36Sopenharmony_ci		bcm->vote_x[bucket] = bcm_div(temp, bcm->aux_data.unit);
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci		temp = agg_peak[bucket] * bcm->vote_scale;
11562306a36Sopenharmony_ci		bcm->vote_y[bucket] = bcm_div(temp, bcm->aux_data.unit);
11662306a36Sopenharmony_ci	}
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ci	if (bcm->keepalive && bcm->vote_x[QCOM_ICC_BUCKET_AMC] == 0 &&
11962306a36Sopenharmony_ci	    bcm->vote_y[QCOM_ICC_BUCKET_AMC] == 0) {
12062306a36Sopenharmony_ci		bcm->vote_x[QCOM_ICC_BUCKET_AMC] = 1;
12162306a36Sopenharmony_ci		bcm->vote_x[QCOM_ICC_BUCKET_WAKE] = 1;
12262306a36Sopenharmony_ci		bcm->vote_y[QCOM_ICC_BUCKET_AMC] = 1;
12362306a36Sopenharmony_ci		bcm->vote_y[QCOM_ICC_BUCKET_WAKE] = 1;
12462306a36Sopenharmony_ci	}
12562306a36Sopenharmony_ci}
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_cistatic inline void tcs_cmd_gen(struct tcs_cmd *cmd, u64 vote_x, u64 vote_y,
12862306a36Sopenharmony_ci			       u32 addr, bool commit, bool wait)
12962306a36Sopenharmony_ci{
13062306a36Sopenharmony_ci	bool valid = true;
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci	if (!cmd)
13362306a36Sopenharmony_ci		return;
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci	memset(cmd, 0, sizeof(*cmd));
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci	if (vote_x == 0 && vote_y == 0)
13862306a36Sopenharmony_ci		valid = false;
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci	if (vote_x > BCM_TCS_CMD_VOTE_MASK)
14162306a36Sopenharmony_ci		vote_x = BCM_TCS_CMD_VOTE_MASK;
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci	if (vote_y > BCM_TCS_CMD_VOTE_MASK)
14462306a36Sopenharmony_ci		vote_y = BCM_TCS_CMD_VOTE_MASK;
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci	cmd->addr = addr;
14762306a36Sopenharmony_ci	cmd->data = BCM_TCS_CMD(commit, valid, vote_x, vote_y);
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci	/*
15062306a36Sopenharmony_ci	 * Set the wait for completion flag on command that need to be completed
15162306a36Sopenharmony_ci	 * before the next command.
15262306a36Sopenharmony_ci	 */
15362306a36Sopenharmony_ci	cmd->wait = wait;
15462306a36Sopenharmony_ci}
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_cistatic void tcs_list_gen(struct bcm_voter *voter, int bucket,
15762306a36Sopenharmony_ci			 struct tcs_cmd tcs_list[MAX_VCD],
15862306a36Sopenharmony_ci			 int n[MAX_VCD + 1])
15962306a36Sopenharmony_ci{
16062306a36Sopenharmony_ci	struct list_head *bcm_list = &voter->commit_list;
16162306a36Sopenharmony_ci	struct qcom_icc_bcm *bcm;
16262306a36Sopenharmony_ci	bool commit, wait;
16362306a36Sopenharmony_ci	size_t idx = 0, batch = 0, cur_vcd_size = 0;
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci	memset(n, 0, sizeof(int) * (MAX_VCD + 1));
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci	list_for_each_entry(bcm, bcm_list, list) {
16862306a36Sopenharmony_ci		commit = false;
16962306a36Sopenharmony_ci		cur_vcd_size++;
17062306a36Sopenharmony_ci		if ((list_is_last(&bcm->list, bcm_list)) ||
17162306a36Sopenharmony_ci		    bcm->aux_data.vcd != list_next_entry(bcm, list)->aux_data.vcd) {
17262306a36Sopenharmony_ci			commit = true;
17362306a36Sopenharmony_ci			cur_vcd_size = 0;
17462306a36Sopenharmony_ci		}
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ci		wait = commit && (voter->tcs_wait & BIT(bucket));
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ci		tcs_cmd_gen(&tcs_list[idx], bcm->vote_x[bucket],
17962306a36Sopenharmony_ci			    bcm->vote_y[bucket], bcm->addr, commit, wait);
18062306a36Sopenharmony_ci		idx++;
18162306a36Sopenharmony_ci		n[batch]++;
18262306a36Sopenharmony_ci		/*
18362306a36Sopenharmony_ci		 * Batch the BCMs in such a way that we do not split them in
18462306a36Sopenharmony_ci		 * multiple payloads when they are under the same VCD. This is
18562306a36Sopenharmony_ci		 * to ensure that every BCM is committed since we only set the
18662306a36Sopenharmony_ci		 * commit bit on the last BCM request of every VCD.
18762306a36Sopenharmony_ci		 */
18862306a36Sopenharmony_ci		if (n[batch] >= MAX_RPMH_PAYLOAD) {
18962306a36Sopenharmony_ci			if (!commit) {
19062306a36Sopenharmony_ci				n[batch] -= cur_vcd_size;
19162306a36Sopenharmony_ci				n[batch + 1] = cur_vcd_size;
19262306a36Sopenharmony_ci			}
19362306a36Sopenharmony_ci			batch++;
19462306a36Sopenharmony_ci		}
19562306a36Sopenharmony_ci	}
19662306a36Sopenharmony_ci}
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_ci/**
19962306a36Sopenharmony_ci * of_bcm_voter_get - gets a bcm voter handle from DT node
20062306a36Sopenharmony_ci * @dev: device pointer for the consumer device
20162306a36Sopenharmony_ci * @name: name for the bcm voter device
20262306a36Sopenharmony_ci *
20362306a36Sopenharmony_ci * This function will match a device_node pointer for the phandle
20462306a36Sopenharmony_ci * specified in the device DT and return a bcm_voter handle on success.
20562306a36Sopenharmony_ci *
20662306a36Sopenharmony_ci * Returns bcm_voter pointer or ERR_PTR() on error. EPROBE_DEFER is returned
20762306a36Sopenharmony_ci * when matching bcm voter is yet to be found.
20862306a36Sopenharmony_ci */
20962306a36Sopenharmony_cistruct bcm_voter *of_bcm_voter_get(struct device *dev, const char *name)
21062306a36Sopenharmony_ci{
21162306a36Sopenharmony_ci	struct bcm_voter *voter = ERR_PTR(-EPROBE_DEFER);
21262306a36Sopenharmony_ci	struct bcm_voter *temp;
21362306a36Sopenharmony_ci	struct device_node *np, *node;
21462306a36Sopenharmony_ci	int idx = 0;
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_ci	if (!dev || !dev->of_node)
21762306a36Sopenharmony_ci		return ERR_PTR(-ENODEV);
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_ci	np = dev->of_node;
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ci	if (name) {
22262306a36Sopenharmony_ci		idx = of_property_match_string(np, "qcom,bcm-voter-names", name);
22362306a36Sopenharmony_ci		if (idx < 0)
22462306a36Sopenharmony_ci			return ERR_PTR(idx);
22562306a36Sopenharmony_ci	}
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_ci	node = of_parse_phandle(np, "qcom,bcm-voters", idx);
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_ci	mutex_lock(&bcm_voter_lock);
23062306a36Sopenharmony_ci	list_for_each_entry(temp, &bcm_voters, voter_node) {
23162306a36Sopenharmony_ci		if (temp->np == node) {
23262306a36Sopenharmony_ci			voter = temp;
23362306a36Sopenharmony_ci			break;
23462306a36Sopenharmony_ci		}
23562306a36Sopenharmony_ci	}
23662306a36Sopenharmony_ci	mutex_unlock(&bcm_voter_lock);
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci	of_node_put(node);
23962306a36Sopenharmony_ci	return voter;
24062306a36Sopenharmony_ci}
24162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(of_bcm_voter_get);
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_ci/**
24462306a36Sopenharmony_ci * qcom_icc_bcm_voter_add - queues up the bcm nodes that require updates
24562306a36Sopenharmony_ci * @voter: voter that the bcms are being added to
24662306a36Sopenharmony_ci * @bcm: bcm to add to the commit and wake sleep list
24762306a36Sopenharmony_ci */
24862306a36Sopenharmony_civoid qcom_icc_bcm_voter_add(struct bcm_voter *voter, struct qcom_icc_bcm *bcm)
24962306a36Sopenharmony_ci{
25062306a36Sopenharmony_ci	if (!voter)
25162306a36Sopenharmony_ci		return;
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_ci	mutex_lock(&voter->lock);
25462306a36Sopenharmony_ci	if (list_empty(&bcm->list))
25562306a36Sopenharmony_ci		list_add_tail(&bcm->list, &voter->commit_list);
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ci	if (list_empty(&bcm->ws_list))
25862306a36Sopenharmony_ci		list_add_tail(&bcm->ws_list, &voter->ws_list);
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_ci	mutex_unlock(&voter->lock);
26162306a36Sopenharmony_ci}
26262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(qcom_icc_bcm_voter_add);
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_ci/**
26562306a36Sopenharmony_ci * qcom_icc_bcm_voter_commit - generates and commits tcs cmds based on bcms
26662306a36Sopenharmony_ci * @voter: voter that needs flushing
26762306a36Sopenharmony_ci *
26862306a36Sopenharmony_ci * This function generates a set of AMC commands and flushes to the BCM device
26962306a36Sopenharmony_ci * associated with the voter. It conditionally generate WAKE and SLEEP commands
27062306a36Sopenharmony_ci * based on deltas between WAKE/SLEEP requirements. The ws_list persists
27162306a36Sopenharmony_ci * through multiple commit requests and bcm nodes are removed only when the
27262306a36Sopenharmony_ci * requirements for WAKE matches SLEEP.
27362306a36Sopenharmony_ci *
27462306a36Sopenharmony_ci * Returns 0 on success, or an appropriate error code otherwise.
27562306a36Sopenharmony_ci */
27662306a36Sopenharmony_ciint qcom_icc_bcm_voter_commit(struct bcm_voter *voter)
27762306a36Sopenharmony_ci{
27862306a36Sopenharmony_ci	struct qcom_icc_bcm *bcm;
27962306a36Sopenharmony_ci	struct qcom_icc_bcm *bcm_tmp;
28062306a36Sopenharmony_ci	int commit_idx[MAX_VCD + 1];
28162306a36Sopenharmony_ci	struct tcs_cmd cmds[MAX_BCMS];
28262306a36Sopenharmony_ci	int ret = 0;
28362306a36Sopenharmony_ci
28462306a36Sopenharmony_ci	if (!voter)
28562306a36Sopenharmony_ci		return 0;
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_ci	mutex_lock(&voter->lock);
28862306a36Sopenharmony_ci	list_for_each_entry(bcm, &voter->commit_list, list) {
28962306a36Sopenharmony_ci		if (bcm->enable_mask)
29062306a36Sopenharmony_ci			bcm_aggregate_mask(bcm);
29162306a36Sopenharmony_ci		else
29262306a36Sopenharmony_ci			bcm_aggregate(bcm);
29362306a36Sopenharmony_ci	}
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_ci	/*
29662306a36Sopenharmony_ci	 * Pre sort the BCMs based on VCD for ease of generating a command list
29762306a36Sopenharmony_ci	 * that groups the BCMs with the same VCD together. VCDs are numbered
29862306a36Sopenharmony_ci	 * with lowest being the most expensive time wise, ensuring that
29962306a36Sopenharmony_ci	 * those commands are being sent the earliest in the queue. This needs
30062306a36Sopenharmony_ci	 * to be sorted every commit since we can't guarantee the order in which
30162306a36Sopenharmony_ci	 * the BCMs are added to the list.
30262306a36Sopenharmony_ci	 */
30362306a36Sopenharmony_ci	list_sort(NULL, &voter->commit_list, cmp_vcd);
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_ci	/*
30662306a36Sopenharmony_ci	 * Construct the command list based on a pre ordered list of BCMs
30762306a36Sopenharmony_ci	 * based on VCD.
30862306a36Sopenharmony_ci	 */
30962306a36Sopenharmony_ci	tcs_list_gen(voter, QCOM_ICC_BUCKET_AMC, cmds, commit_idx);
31062306a36Sopenharmony_ci	if (!commit_idx[0])
31162306a36Sopenharmony_ci		goto out;
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_ci	rpmh_invalidate(voter->dev);
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_ci	ret = rpmh_write_batch(voter->dev, RPMH_ACTIVE_ONLY_STATE,
31662306a36Sopenharmony_ci			       cmds, commit_idx);
31762306a36Sopenharmony_ci	if (ret) {
31862306a36Sopenharmony_ci		pr_err("Error sending AMC RPMH requests (%d)\n", ret);
31962306a36Sopenharmony_ci		goto out;
32062306a36Sopenharmony_ci	}
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_ci	list_for_each_entry_safe(bcm, bcm_tmp, &voter->commit_list, list)
32362306a36Sopenharmony_ci		list_del_init(&bcm->list);
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_ci	list_for_each_entry_safe(bcm, bcm_tmp, &voter->ws_list, ws_list) {
32662306a36Sopenharmony_ci		/*
32762306a36Sopenharmony_ci		 * Only generate WAKE and SLEEP commands if a resource's
32862306a36Sopenharmony_ci		 * requirements change as the execution environment transitions
32962306a36Sopenharmony_ci		 * between different power states.
33062306a36Sopenharmony_ci		 */
33162306a36Sopenharmony_ci		if (bcm->vote_x[QCOM_ICC_BUCKET_WAKE] !=
33262306a36Sopenharmony_ci		    bcm->vote_x[QCOM_ICC_BUCKET_SLEEP] ||
33362306a36Sopenharmony_ci		    bcm->vote_y[QCOM_ICC_BUCKET_WAKE] !=
33462306a36Sopenharmony_ci		    bcm->vote_y[QCOM_ICC_BUCKET_SLEEP])
33562306a36Sopenharmony_ci			list_add_tail(&bcm->list, &voter->commit_list);
33662306a36Sopenharmony_ci		else
33762306a36Sopenharmony_ci			list_del_init(&bcm->ws_list);
33862306a36Sopenharmony_ci	}
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_ci	if (list_empty(&voter->commit_list))
34162306a36Sopenharmony_ci		goto out;
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_ci	list_sort(NULL, &voter->commit_list, cmp_vcd);
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_ci	tcs_list_gen(voter, QCOM_ICC_BUCKET_WAKE, cmds, commit_idx);
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_ci	ret = rpmh_write_batch(voter->dev, RPMH_WAKE_ONLY_STATE, cmds, commit_idx);
34862306a36Sopenharmony_ci	if (ret) {
34962306a36Sopenharmony_ci		pr_err("Error sending WAKE RPMH requests (%d)\n", ret);
35062306a36Sopenharmony_ci		goto out;
35162306a36Sopenharmony_ci	}
35262306a36Sopenharmony_ci
35362306a36Sopenharmony_ci	tcs_list_gen(voter, QCOM_ICC_BUCKET_SLEEP, cmds, commit_idx);
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_ci	ret = rpmh_write_batch(voter->dev, RPMH_SLEEP_STATE, cmds, commit_idx);
35662306a36Sopenharmony_ci	if (ret) {
35762306a36Sopenharmony_ci		pr_err("Error sending SLEEP RPMH requests (%d)\n", ret);
35862306a36Sopenharmony_ci		goto out;
35962306a36Sopenharmony_ci	}
36062306a36Sopenharmony_ci
36162306a36Sopenharmony_ciout:
36262306a36Sopenharmony_ci	list_for_each_entry_safe(bcm, bcm_tmp, &voter->commit_list, list)
36362306a36Sopenharmony_ci		list_del_init(&bcm->list);
36462306a36Sopenharmony_ci
36562306a36Sopenharmony_ci	mutex_unlock(&voter->lock);
36662306a36Sopenharmony_ci	return ret;
36762306a36Sopenharmony_ci}
36862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(qcom_icc_bcm_voter_commit);
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_cistatic int qcom_icc_bcm_voter_probe(struct platform_device *pdev)
37162306a36Sopenharmony_ci{
37262306a36Sopenharmony_ci	struct device_node *np = pdev->dev.of_node;
37362306a36Sopenharmony_ci	struct bcm_voter *voter;
37462306a36Sopenharmony_ci
37562306a36Sopenharmony_ci	voter = devm_kzalloc(&pdev->dev, sizeof(*voter), GFP_KERNEL);
37662306a36Sopenharmony_ci	if (!voter)
37762306a36Sopenharmony_ci		return -ENOMEM;
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_ci	voter->dev = &pdev->dev;
38062306a36Sopenharmony_ci	voter->np = np;
38162306a36Sopenharmony_ci
38262306a36Sopenharmony_ci	if (of_property_read_u32(np, "qcom,tcs-wait", &voter->tcs_wait))
38362306a36Sopenharmony_ci		voter->tcs_wait = QCOM_ICC_TAG_ACTIVE_ONLY;
38462306a36Sopenharmony_ci
38562306a36Sopenharmony_ci	mutex_init(&voter->lock);
38662306a36Sopenharmony_ci	INIT_LIST_HEAD(&voter->commit_list);
38762306a36Sopenharmony_ci	INIT_LIST_HEAD(&voter->ws_list);
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_ci	mutex_lock(&bcm_voter_lock);
39062306a36Sopenharmony_ci	list_add_tail(&voter->voter_node, &bcm_voters);
39162306a36Sopenharmony_ci	mutex_unlock(&bcm_voter_lock);
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_ci	return 0;
39462306a36Sopenharmony_ci}
39562306a36Sopenharmony_ci
39662306a36Sopenharmony_cistatic const struct of_device_id bcm_voter_of_match[] = {
39762306a36Sopenharmony_ci	{ .compatible = "qcom,bcm-voter" },
39862306a36Sopenharmony_ci	{ }
39962306a36Sopenharmony_ci};
40062306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, bcm_voter_of_match);
40162306a36Sopenharmony_ci
40262306a36Sopenharmony_cistatic struct platform_driver qcom_icc_bcm_voter_driver = {
40362306a36Sopenharmony_ci	.probe = qcom_icc_bcm_voter_probe,
40462306a36Sopenharmony_ci	.driver = {
40562306a36Sopenharmony_ci		.name		= "bcm_voter",
40662306a36Sopenharmony_ci		.of_match_table = bcm_voter_of_match,
40762306a36Sopenharmony_ci	},
40862306a36Sopenharmony_ci};
40962306a36Sopenharmony_cimodule_platform_driver(qcom_icc_bcm_voter_driver);
41062306a36Sopenharmony_ci
41162306a36Sopenharmony_ciMODULE_AUTHOR("David Dai <daidavid1@codeaurora.org>");
41262306a36Sopenharmony_ciMODULE_DESCRIPTION("Qualcomm BCM Voter interconnect driver");
41362306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
414