162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (C) 2020 Linaro Ltd
462306a36Sopenharmony_ci */
562306a36Sopenharmony_ci
662306a36Sopenharmony_ci#include <linux/device.h>
762306a36Sopenharmony_ci#include <linux/interconnect-provider.h>
862306a36Sopenharmony_ci#include <linux/io.h>
962306a36Sopenharmony_ci#include <linux/module.h>
1062306a36Sopenharmony_ci#include <linux/of.h>
1162306a36Sopenharmony_ci#include <linux/of_platform.h>
1262306a36Sopenharmony_ci#include <linux/platform_device.h>
1362306a36Sopenharmony_ci#include <linux/regmap.h>
1462306a36Sopenharmony_ci#include <linux/slab.h>
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci#include "icc-common.h"
1762306a36Sopenharmony_ci#include "icc-rpm.h"
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci/* QNOC QoS */
2062306a36Sopenharmony_ci#define QNOC_QOS_MCTL_LOWn_ADDR(n)	(0x8 + (n * 0x1000))
2162306a36Sopenharmony_ci#define QNOC_QOS_MCTL_DFLT_PRIO_MASK	0x70
2262306a36Sopenharmony_ci#define QNOC_QOS_MCTL_DFLT_PRIO_SHIFT	4
2362306a36Sopenharmony_ci#define QNOC_QOS_MCTL_URGFWD_EN_MASK	0x8
2462306a36Sopenharmony_ci#define QNOC_QOS_MCTL_URGFWD_EN_SHIFT	3
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci/* BIMC QoS */
2762306a36Sopenharmony_ci#define M_BKE_REG_BASE(n)		(0x300 + (0x4000 * n))
2862306a36Sopenharmony_ci#define M_BKE_EN_ADDR(n)		(M_BKE_REG_BASE(n))
2962306a36Sopenharmony_ci#define M_BKE_HEALTH_CFG_ADDR(i, n)	(M_BKE_REG_BASE(n) + 0x40 + (0x4 * i))
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci#define M_BKE_HEALTH_CFG_LIMITCMDS_MASK	0x80000000
3262306a36Sopenharmony_ci#define M_BKE_HEALTH_CFG_AREQPRIO_MASK	0x300
3362306a36Sopenharmony_ci#define M_BKE_HEALTH_CFG_PRIOLVL_MASK	0x3
3462306a36Sopenharmony_ci#define M_BKE_HEALTH_CFG_AREQPRIO_SHIFT	0x8
3562306a36Sopenharmony_ci#define M_BKE_HEALTH_CFG_LIMITCMDS_SHIFT 0x1f
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci#define M_BKE_EN_EN_BMASK		0x1
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci/* NoC QoS */
4062306a36Sopenharmony_ci#define NOC_QOS_PRIORITYn_ADDR(n)	(0x8 + (n * 0x1000))
4162306a36Sopenharmony_ci#define NOC_QOS_PRIORITY_P1_MASK	0xc
4262306a36Sopenharmony_ci#define NOC_QOS_PRIORITY_P0_MASK	0x3
4362306a36Sopenharmony_ci#define NOC_QOS_PRIORITY_P1_SHIFT	0x2
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci#define NOC_QOS_MODEn_ADDR(n)		(0xc + (n * 0x1000))
4662306a36Sopenharmony_ci#define NOC_QOS_MODEn_MASK		0x3
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci#define NOC_QOS_MODE_FIXED_VAL		0x0
4962306a36Sopenharmony_ci#define NOC_QOS_MODE_BYPASS_VAL		0x2
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci#define ICC_BUS_CLK_MIN_RATE		19200ULL /* kHz */
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_cistatic int qcom_icc_set_qnoc_qos(struct icc_node *src)
5462306a36Sopenharmony_ci{
5562306a36Sopenharmony_ci	struct icc_provider *provider = src->provider;
5662306a36Sopenharmony_ci	struct qcom_icc_provider *qp = to_qcom_provider(provider);
5762306a36Sopenharmony_ci	struct qcom_icc_node *qn = src->data;
5862306a36Sopenharmony_ci	struct qcom_icc_qos *qos = &qn->qos;
5962306a36Sopenharmony_ci	int rc;
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci	rc = regmap_update_bits(qp->regmap,
6262306a36Sopenharmony_ci			qp->qos_offset + QNOC_QOS_MCTL_LOWn_ADDR(qos->qos_port),
6362306a36Sopenharmony_ci			QNOC_QOS_MCTL_DFLT_PRIO_MASK,
6462306a36Sopenharmony_ci			qos->areq_prio << QNOC_QOS_MCTL_DFLT_PRIO_SHIFT);
6562306a36Sopenharmony_ci	if (rc)
6662306a36Sopenharmony_ci		return rc;
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci	return regmap_update_bits(qp->regmap,
6962306a36Sopenharmony_ci			qp->qos_offset + QNOC_QOS_MCTL_LOWn_ADDR(qos->qos_port),
7062306a36Sopenharmony_ci			QNOC_QOS_MCTL_URGFWD_EN_MASK,
7162306a36Sopenharmony_ci			!!qos->urg_fwd_en << QNOC_QOS_MCTL_URGFWD_EN_SHIFT);
7262306a36Sopenharmony_ci}
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_cistatic int qcom_icc_bimc_set_qos_health(struct qcom_icc_provider *qp,
7562306a36Sopenharmony_ci					struct qcom_icc_qos *qos,
7662306a36Sopenharmony_ci					int regnum)
7762306a36Sopenharmony_ci{
7862306a36Sopenharmony_ci	u32 val;
7962306a36Sopenharmony_ci	u32 mask;
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci	val = qos->prio_level;
8262306a36Sopenharmony_ci	mask = M_BKE_HEALTH_CFG_PRIOLVL_MASK;
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci	val |= qos->areq_prio << M_BKE_HEALTH_CFG_AREQPRIO_SHIFT;
8562306a36Sopenharmony_ci	mask |= M_BKE_HEALTH_CFG_AREQPRIO_MASK;
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci	/* LIMITCMDS is not present on M_BKE_HEALTH_3 */
8862306a36Sopenharmony_ci	if (regnum != 3) {
8962306a36Sopenharmony_ci		val |= qos->limit_commands << M_BKE_HEALTH_CFG_LIMITCMDS_SHIFT;
9062306a36Sopenharmony_ci		mask |= M_BKE_HEALTH_CFG_LIMITCMDS_MASK;
9162306a36Sopenharmony_ci	}
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci	return regmap_update_bits(qp->regmap,
9462306a36Sopenharmony_ci				  qp->qos_offset + M_BKE_HEALTH_CFG_ADDR(regnum, qos->qos_port),
9562306a36Sopenharmony_ci				  mask, val);
9662306a36Sopenharmony_ci}
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_cistatic int qcom_icc_set_bimc_qos(struct icc_node *src)
9962306a36Sopenharmony_ci{
10062306a36Sopenharmony_ci	struct qcom_icc_provider *qp;
10162306a36Sopenharmony_ci	struct qcom_icc_node *qn;
10262306a36Sopenharmony_ci	struct icc_provider *provider;
10362306a36Sopenharmony_ci	u32 mode = NOC_QOS_MODE_BYPASS;
10462306a36Sopenharmony_ci	u32 val = 0;
10562306a36Sopenharmony_ci	int i, rc = 0;
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci	qn = src->data;
10862306a36Sopenharmony_ci	provider = src->provider;
10962306a36Sopenharmony_ci	qp = to_qcom_provider(provider);
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci	if (qn->qos.qos_mode != NOC_QOS_MODE_INVALID)
11262306a36Sopenharmony_ci		mode = qn->qos.qos_mode;
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci	/* QoS Priority: The QoS Health parameters are getting considered
11562306a36Sopenharmony_ci	 * only if we are NOT in Bypass Mode.
11662306a36Sopenharmony_ci	 */
11762306a36Sopenharmony_ci	if (mode != NOC_QOS_MODE_BYPASS) {
11862306a36Sopenharmony_ci		for (i = 3; i >= 0; i--) {
11962306a36Sopenharmony_ci			rc = qcom_icc_bimc_set_qos_health(qp,
12062306a36Sopenharmony_ci							  &qn->qos, i);
12162306a36Sopenharmony_ci			if (rc)
12262306a36Sopenharmony_ci				return rc;
12362306a36Sopenharmony_ci		}
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci		/* Set BKE_EN to 1 when Fixed, Regulator or Limiter Mode */
12662306a36Sopenharmony_ci		val = 1;
12762306a36Sopenharmony_ci	}
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci	return regmap_update_bits(qp->regmap,
13062306a36Sopenharmony_ci				  qp->qos_offset + M_BKE_EN_ADDR(qn->qos.qos_port),
13162306a36Sopenharmony_ci				  M_BKE_EN_EN_BMASK, val);
13262306a36Sopenharmony_ci}
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_cistatic int qcom_icc_noc_set_qos_priority(struct qcom_icc_provider *qp,
13562306a36Sopenharmony_ci					 struct qcom_icc_qos *qos)
13662306a36Sopenharmony_ci{
13762306a36Sopenharmony_ci	u32 val;
13862306a36Sopenharmony_ci	int rc;
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci	/* Must be updated one at a time, P1 first, P0 last */
14162306a36Sopenharmony_ci	val = qos->areq_prio << NOC_QOS_PRIORITY_P1_SHIFT;
14262306a36Sopenharmony_ci	rc = regmap_update_bits(qp->regmap,
14362306a36Sopenharmony_ci				qp->qos_offset + NOC_QOS_PRIORITYn_ADDR(qos->qos_port),
14462306a36Sopenharmony_ci				NOC_QOS_PRIORITY_P1_MASK, val);
14562306a36Sopenharmony_ci	if (rc)
14662306a36Sopenharmony_ci		return rc;
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci	return regmap_update_bits(qp->regmap,
14962306a36Sopenharmony_ci				  qp->qos_offset + NOC_QOS_PRIORITYn_ADDR(qos->qos_port),
15062306a36Sopenharmony_ci				  NOC_QOS_PRIORITY_P0_MASK, qos->prio_level);
15162306a36Sopenharmony_ci}
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_cistatic int qcom_icc_set_noc_qos(struct icc_node *src)
15462306a36Sopenharmony_ci{
15562306a36Sopenharmony_ci	struct qcom_icc_provider *qp;
15662306a36Sopenharmony_ci	struct qcom_icc_node *qn;
15762306a36Sopenharmony_ci	struct icc_provider *provider;
15862306a36Sopenharmony_ci	u32 mode = NOC_QOS_MODE_BYPASS_VAL;
15962306a36Sopenharmony_ci	int rc = 0;
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci	qn = src->data;
16262306a36Sopenharmony_ci	provider = src->provider;
16362306a36Sopenharmony_ci	qp = to_qcom_provider(provider);
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci	if (qn->qos.qos_port < 0) {
16662306a36Sopenharmony_ci		dev_dbg(src->provider->dev,
16762306a36Sopenharmony_ci			"NoC QoS: Skipping %s: vote aggregated on parent.\n",
16862306a36Sopenharmony_ci			qn->name);
16962306a36Sopenharmony_ci		return 0;
17062306a36Sopenharmony_ci	}
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ci	if (qn->qos.qos_mode == NOC_QOS_MODE_FIXED) {
17362306a36Sopenharmony_ci		dev_dbg(src->provider->dev, "NoC QoS: %s: Set Fixed mode\n", qn->name);
17462306a36Sopenharmony_ci		mode = NOC_QOS_MODE_FIXED_VAL;
17562306a36Sopenharmony_ci		rc = qcom_icc_noc_set_qos_priority(qp, &qn->qos);
17662306a36Sopenharmony_ci		if (rc)
17762306a36Sopenharmony_ci			return rc;
17862306a36Sopenharmony_ci	} else if (qn->qos.qos_mode == NOC_QOS_MODE_BYPASS) {
17962306a36Sopenharmony_ci		dev_dbg(src->provider->dev, "NoC QoS: %s: Set Bypass mode\n", qn->name);
18062306a36Sopenharmony_ci		mode = NOC_QOS_MODE_BYPASS_VAL;
18162306a36Sopenharmony_ci	} else {
18262306a36Sopenharmony_ci		/* How did we get here? */
18362306a36Sopenharmony_ci	}
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_ci	return regmap_update_bits(qp->regmap,
18662306a36Sopenharmony_ci				  qp->qos_offset + NOC_QOS_MODEn_ADDR(qn->qos.qos_port),
18762306a36Sopenharmony_ci				  NOC_QOS_MODEn_MASK, mode);
18862306a36Sopenharmony_ci}
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_cistatic int qcom_icc_qos_set(struct icc_node *node)
19162306a36Sopenharmony_ci{
19262306a36Sopenharmony_ci	struct qcom_icc_provider *qp = to_qcom_provider(node->provider);
19362306a36Sopenharmony_ci	struct qcom_icc_node *qn = node->data;
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ci	dev_dbg(node->provider->dev, "Setting QoS for %s\n", qn->name);
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci	switch (qp->type) {
19862306a36Sopenharmony_ci	case QCOM_ICC_BIMC:
19962306a36Sopenharmony_ci		return qcom_icc_set_bimc_qos(node);
20062306a36Sopenharmony_ci	case QCOM_ICC_QNOC:
20162306a36Sopenharmony_ci		return qcom_icc_set_qnoc_qos(node);
20262306a36Sopenharmony_ci	default:
20362306a36Sopenharmony_ci		return qcom_icc_set_noc_qos(node);
20462306a36Sopenharmony_ci	}
20562306a36Sopenharmony_ci}
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_cistatic int qcom_icc_rpm_set(struct qcom_icc_node *qn, u64 *bw)
20862306a36Sopenharmony_ci{
20962306a36Sopenharmony_ci	int ret, rpm_ctx = 0;
21062306a36Sopenharmony_ci	u64 bw_bps;
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_ci	if (qn->qos.ap_owned)
21362306a36Sopenharmony_ci		return 0;
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci	for (rpm_ctx = 0; rpm_ctx < QCOM_SMD_RPM_STATE_NUM; rpm_ctx++) {
21662306a36Sopenharmony_ci		bw_bps = icc_units_to_bps(bw[rpm_ctx]);
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_ci		if (qn->mas_rpm_id != -1) {
21962306a36Sopenharmony_ci			ret = qcom_icc_rpm_smd_send(rpm_ctx,
22062306a36Sopenharmony_ci						    RPM_BUS_MASTER_REQ,
22162306a36Sopenharmony_ci						    qn->mas_rpm_id,
22262306a36Sopenharmony_ci						    bw_bps);
22362306a36Sopenharmony_ci			if (ret) {
22462306a36Sopenharmony_ci				pr_err("qcom_icc_rpm_smd_send mas %d error %d\n",
22562306a36Sopenharmony_ci				qn->mas_rpm_id, ret);
22662306a36Sopenharmony_ci				return ret;
22762306a36Sopenharmony_ci			}
22862306a36Sopenharmony_ci		}
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_ci		if (qn->slv_rpm_id != -1) {
23162306a36Sopenharmony_ci			ret = qcom_icc_rpm_smd_send(rpm_ctx,
23262306a36Sopenharmony_ci						    RPM_BUS_SLAVE_REQ,
23362306a36Sopenharmony_ci						    qn->slv_rpm_id,
23462306a36Sopenharmony_ci						    bw_bps);
23562306a36Sopenharmony_ci			if (ret) {
23662306a36Sopenharmony_ci				pr_err("qcom_icc_rpm_smd_send slv %d error %d\n",
23762306a36Sopenharmony_ci				qn->slv_rpm_id, ret);
23862306a36Sopenharmony_ci				return ret;
23962306a36Sopenharmony_ci			}
24062306a36Sopenharmony_ci		}
24162306a36Sopenharmony_ci	}
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_ci	return 0;
24462306a36Sopenharmony_ci}
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ci/**
24762306a36Sopenharmony_ci * qcom_icc_pre_bw_aggregate - cleans up values before re-aggregate requests
24862306a36Sopenharmony_ci * @node: icc node to operate on
24962306a36Sopenharmony_ci */
25062306a36Sopenharmony_cistatic void qcom_icc_pre_bw_aggregate(struct icc_node *node)
25162306a36Sopenharmony_ci{
25262306a36Sopenharmony_ci	struct qcom_icc_node *qn;
25362306a36Sopenharmony_ci	size_t i;
25462306a36Sopenharmony_ci
25562306a36Sopenharmony_ci	qn = node->data;
25662306a36Sopenharmony_ci	for (i = 0; i < QCOM_SMD_RPM_STATE_NUM; i++) {
25762306a36Sopenharmony_ci		qn->sum_avg[i] = 0;
25862306a36Sopenharmony_ci		qn->max_peak[i] = 0;
25962306a36Sopenharmony_ci	}
26062306a36Sopenharmony_ci}
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_ci/**
26362306a36Sopenharmony_ci * qcom_icc_bw_aggregate - aggregate bw for buckets indicated by tag
26462306a36Sopenharmony_ci * @node: node to aggregate
26562306a36Sopenharmony_ci * @tag: tag to indicate which buckets to aggregate
26662306a36Sopenharmony_ci * @avg_bw: new bw to sum aggregate
26762306a36Sopenharmony_ci * @peak_bw: new bw to max aggregate
26862306a36Sopenharmony_ci * @agg_avg: existing aggregate avg bw val
26962306a36Sopenharmony_ci * @agg_peak: existing aggregate peak bw val
27062306a36Sopenharmony_ci */
27162306a36Sopenharmony_cistatic int qcom_icc_bw_aggregate(struct icc_node *node, u32 tag, u32 avg_bw,
27262306a36Sopenharmony_ci				 u32 peak_bw, u32 *agg_avg, u32 *agg_peak)
27362306a36Sopenharmony_ci{
27462306a36Sopenharmony_ci	size_t i;
27562306a36Sopenharmony_ci	struct qcom_icc_node *qn;
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_ci	qn = node->data;
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_ci	if (!tag)
28062306a36Sopenharmony_ci		tag = RPM_ALWAYS_TAG;
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_ci	for (i = 0; i < QCOM_SMD_RPM_STATE_NUM; i++) {
28362306a36Sopenharmony_ci		if (tag & BIT(i)) {
28462306a36Sopenharmony_ci			qn->sum_avg[i] += avg_bw;
28562306a36Sopenharmony_ci			qn->max_peak[i] = max_t(u32, qn->max_peak[i], peak_bw);
28662306a36Sopenharmony_ci		}
28762306a36Sopenharmony_ci	}
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_ci	*agg_avg += avg_bw;
29062306a36Sopenharmony_ci	*agg_peak = max_t(u32, *agg_peak, peak_bw);
29162306a36Sopenharmony_ci	return 0;
29262306a36Sopenharmony_ci}
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_ci/**
29562306a36Sopenharmony_ci * qcom_icc_bus_aggregate - calculate bus clock rates by traversing all nodes
29662306a36Sopenharmony_ci * @provider: generic interconnect provider
29762306a36Sopenharmony_ci * @agg_clk_rate: array containing the aggregated clock rates in kHz
29862306a36Sopenharmony_ci */
29962306a36Sopenharmony_cistatic void qcom_icc_bus_aggregate(struct icc_provider *provider, u64 *agg_clk_rate)
30062306a36Sopenharmony_ci{
30162306a36Sopenharmony_ci	u64 agg_avg_rate, agg_rate;
30262306a36Sopenharmony_ci	struct qcom_icc_node *qn;
30362306a36Sopenharmony_ci	struct icc_node *node;
30462306a36Sopenharmony_ci	int i;
30562306a36Sopenharmony_ci
30662306a36Sopenharmony_ci	/*
30762306a36Sopenharmony_ci	 * Iterate nodes on the provider, aggregate bandwidth requests for
30862306a36Sopenharmony_ci	 * every bucket and convert them into bus clock rates.
30962306a36Sopenharmony_ci	 */
31062306a36Sopenharmony_ci	list_for_each_entry(node, &provider->nodes, node_list) {
31162306a36Sopenharmony_ci		qn = node->data;
31262306a36Sopenharmony_ci		for (i = 0; i < QCOM_SMD_RPM_STATE_NUM; i++) {
31362306a36Sopenharmony_ci			if (qn->channels)
31462306a36Sopenharmony_ci				agg_avg_rate = div_u64(qn->sum_avg[i], qn->channels);
31562306a36Sopenharmony_ci			else
31662306a36Sopenharmony_ci				agg_avg_rate = qn->sum_avg[i];
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_ci			agg_rate = max_t(u64, agg_avg_rate, qn->max_peak[i]);
31962306a36Sopenharmony_ci			do_div(agg_rate, qn->buswidth);
32062306a36Sopenharmony_ci
32162306a36Sopenharmony_ci			agg_clk_rate[i] = max_t(u64, agg_clk_rate[i], agg_rate);
32262306a36Sopenharmony_ci		}
32362306a36Sopenharmony_ci	}
32462306a36Sopenharmony_ci}
32562306a36Sopenharmony_ci
32662306a36Sopenharmony_cistatic int qcom_icc_set(struct icc_node *src, struct icc_node *dst)
32762306a36Sopenharmony_ci{
32862306a36Sopenharmony_ci	struct qcom_icc_node *src_qn = NULL, *dst_qn = NULL;
32962306a36Sopenharmony_ci	u64 agg_clk_rate[QCOM_SMD_RPM_STATE_NUM] = { 0 };
33062306a36Sopenharmony_ci	struct icc_provider *provider;
33162306a36Sopenharmony_ci	struct qcom_icc_provider *qp;
33262306a36Sopenharmony_ci	u64 active_rate, sleep_rate;
33362306a36Sopenharmony_ci	int ret;
33462306a36Sopenharmony_ci
33562306a36Sopenharmony_ci	src_qn = src->data;
33662306a36Sopenharmony_ci	if (dst)
33762306a36Sopenharmony_ci		dst_qn = dst->data;
33862306a36Sopenharmony_ci	provider = src->provider;
33962306a36Sopenharmony_ci	qp = to_qcom_provider(provider);
34062306a36Sopenharmony_ci
34162306a36Sopenharmony_ci	qcom_icc_bus_aggregate(provider, agg_clk_rate);
34262306a36Sopenharmony_ci	active_rate = agg_clk_rate[QCOM_SMD_RPM_ACTIVE_STATE];
34362306a36Sopenharmony_ci	sleep_rate = agg_clk_rate[QCOM_SMD_RPM_SLEEP_STATE];
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_ci	ret = qcom_icc_rpm_set(src_qn, src_qn->sum_avg);
34662306a36Sopenharmony_ci	if (ret)
34762306a36Sopenharmony_ci		return ret;
34862306a36Sopenharmony_ci
34962306a36Sopenharmony_ci	if (dst_qn) {
35062306a36Sopenharmony_ci		ret = qcom_icc_rpm_set(dst_qn, dst_qn->sum_avg);
35162306a36Sopenharmony_ci		if (ret)
35262306a36Sopenharmony_ci			return ret;
35362306a36Sopenharmony_ci	}
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_ci	/* Some providers don't have a bus clock to scale */
35662306a36Sopenharmony_ci	if (!qp->bus_clk_desc && !qp->bus_clk)
35762306a36Sopenharmony_ci		return 0;
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_ci	/*
36062306a36Sopenharmony_ci	 * Downstream checks whether the requested rate is zero, but it makes little sense
36162306a36Sopenharmony_ci	 * to vote for a value that's below the lower threshold, so let's not do so.
36262306a36Sopenharmony_ci	 */
36362306a36Sopenharmony_ci	if (qp->keep_alive)
36462306a36Sopenharmony_ci		active_rate = max(ICC_BUS_CLK_MIN_RATE, active_rate);
36562306a36Sopenharmony_ci
36662306a36Sopenharmony_ci	/* Some providers have a non-RPM-owned bus clock - convert kHz->Hz for the CCF */
36762306a36Sopenharmony_ci	if (qp->bus_clk) {
36862306a36Sopenharmony_ci		active_rate = max_t(u64, active_rate, sleep_rate);
36962306a36Sopenharmony_ci		/* ARM32 caps clk_set_rate arg to u32.. Nothing we can do about that! */
37062306a36Sopenharmony_ci		active_rate = min_t(u64, 1000ULL * active_rate, ULONG_MAX);
37162306a36Sopenharmony_ci		return clk_set_rate(qp->bus_clk, active_rate);
37262306a36Sopenharmony_ci	}
37362306a36Sopenharmony_ci
37462306a36Sopenharmony_ci	/* RPM only accepts <=INT_MAX rates */
37562306a36Sopenharmony_ci	active_rate = min_t(u64, active_rate, INT_MAX);
37662306a36Sopenharmony_ci	sleep_rate = min_t(u64, sleep_rate, INT_MAX);
37762306a36Sopenharmony_ci
37862306a36Sopenharmony_ci	if (active_rate != qp->bus_clk_rate[QCOM_SMD_RPM_ACTIVE_STATE]) {
37962306a36Sopenharmony_ci		ret = qcom_icc_rpm_set_bus_rate(qp->bus_clk_desc, QCOM_SMD_RPM_ACTIVE_STATE,
38062306a36Sopenharmony_ci						active_rate);
38162306a36Sopenharmony_ci		if (ret)
38262306a36Sopenharmony_ci			return ret;
38362306a36Sopenharmony_ci
38462306a36Sopenharmony_ci		/* Cache the rate after we've successfully commited it to RPM */
38562306a36Sopenharmony_ci		qp->bus_clk_rate[QCOM_SMD_RPM_ACTIVE_STATE] = active_rate;
38662306a36Sopenharmony_ci	}
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_ci	if (sleep_rate != qp->bus_clk_rate[QCOM_SMD_RPM_SLEEP_STATE]) {
38962306a36Sopenharmony_ci		ret = qcom_icc_rpm_set_bus_rate(qp->bus_clk_desc, QCOM_SMD_RPM_SLEEP_STATE,
39062306a36Sopenharmony_ci						sleep_rate);
39162306a36Sopenharmony_ci		if (ret)
39262306a36Sopenharmony_ci			return ret;
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_ci		/* Cache the rate after we've successfully commited it to RPM */
39562306a36Sopenharmony_ci		qp->bus_clk_rate[QCOM_SMD_RPM_SLEEP_STATE] = sleep_rate;
39662306a36Sopenharmony_ci	}
39762306a36Sopenharmony_ci
39862306a36Sopenharmony_ci	return 0;
39962306a36Sopenharmony_ci}
40062306a36Sopenharmony_ci
40162306a36Sopenharmony_ciint qnoc_probe(struct platform_device *pdev)
40262306a36Sopenharmony_ci{
40362306a36Sopenharmony_ci	struct device *dev = &pdev->dev;
40462306a36Sopenharmony_ci	const struct qcom_icc_desc *desc;
40562306a36Sopenharmony_ci	struct icc_onecell_data *data;
40662306a36Sopenharmony_ci	struct icc_provider *provider;
40762306a36Sopenharmony_ci	struct qcom_icc_node * const *qnodes;
40862306a36Sopenharmony_ci	struct qcom_icc_provider *qp;
40962306a36Sopenharmony_ci	struct icc_node *node;
41062306a36Sopenharmony_ci	size_t num_nodes, i;
41162306a36Sopenharmony_ci	const char * const *cds = NULL;
41262306a36Sopenharmony_ci	int cd_num;
41362306a36Sopenharmony_ci	int ret;
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_ci	/* wait for the RPM proxy */
41662306a36Sopenharmony_ci	if (!qcom_icc_rpm_smd_available())
41762306a36Sopenharmony_ci		return -EPROBE_DEFER;
41862306a36Sopenharmony_ci
41962306a36Sopenharmony_ci	desc = of_device_get_match_data(dev);
42062306a36Sopenharmony_ci	if (!desc)
42162306a36Sopenharmony_ci		return -EINVAL;
42262306a36Sopenharmony_ci
42362306a36Sopenharmony_ci	qnodes = desc->nodes;
42462306a36Sopenharmony_ci	num_nodes = desc->num_nodes;
42562306a36Sopenharmony_ci
42662306a36Sopenharmony_ci	if (desc->num_intf_clocks) {
42762306a36Sopenharmony_ci		cds = desc->intf_clocks;
42862306a36Sopenharmony_ci		cd_num = desc->num_intf_clocks;
42962306a36Sopenharmony_ci	} else {
43062306a36Sopenharmony_ci		/* 0 intf clocks is perfectly fine */
43162306a36Sopenharmony_ci		cd_num = 0;
43262306a36Sopenharmony_ci	}
43362306a36Sopenharmony_ci
43462306a36Sopenharmony_ci	qp = devm_kzalloc(dev, sizeof(*qp), GFP_KERNEL);
43562306a36Sopenharmony_ci	if (!qp)
43662306a36Sopenharmony_ci		return -ENOMEM;
43762306a36Sopenharmony_ci
43862306a36Sopenharmony_ci	qp->intf_clks = devm_kcalloc(dev, cd_num, sizeof(*qp->intf_clks), GFP_KERNEL);
43962306a36Sopenharmony_ci	if (!qp->intf_clks)
44062306a36Sopenharmony_ci		return -ENOMEM;
44162306a36Sopenharmony_ci
44262306a36Sopenharmony_ci	if (desc->bus_clk_desc) {
44362306a36Sopenharmony_ci		qp->bus_clk_desc = devm_kzalloc(dev, sizeof(*qp->bus_clk_desc),
44462306a36Sopenharmony_ci						GFP_KERNEL);
44562306a36Sopenharmony_ci		if (!qp->bus_clk_desc)
44662306a36Sopenharmony_ci			return -ENOMEM;
44762306a36Sopenharmony_ci
44862306a36Sopenharmony_ci		qp->bus_clk_desc = desc->bus_clk_desc;
44962306a36Sopenharmony_ci	} else {
45062306a36Sopenharmony_ci		/* Some older SoCs may have a single non-RPM-owned bus clock. */
45162306a36Sopenharmony_ci		qp->bus_clk = devm_clk_get_optional(dev, "bus");
45262306a36Sopenharmony_ci		if (IS_ERR(qp->bus_clk))
45362306a36Sopenharmony_ci			return PTR_ERR(qp->bus_clk);
45462306a36Sopenharmony_ci	}
45562306a36Sopenharmony_ci
45662306a36Sopenharmony_ci	data = devm_kzalloc(dev, struct_size(data, nodes, num_nodes),
45762306a36Sopenharmony_ci			    GFP_KERNEL);
45862306a36Sopenharmony_ci	if (!data)
45962306a36Sopenharmony_ci		return -ENOMEM;
46062306a36Sopenharmony_ci
46162306a36Sopenharmony_ci	qp->num_intf_clks = cd_num;
46262306a36Sopenharmony_ci	for (i = 0; i < cd_num; i++)
46362306a36Sopenharmony_ci		qp->intf_clks[i].id = cds[i];
46462306a36Sopenharmony_ci
46562306a36Sopenharmony_ci	qp->keep_alive = desc->keep_alive;
46662306a36Sopenharmony_ci	qp->type = desc->type;
46762306a36Sopenharmony_ci	qp->qos_offset = desc->qos_offset;
46862306a36Sopenharmony_ci
46962306a36Sopenharmony_ci	if (desc->regmap_cfg) {
47062306a36Sopenharmony_ci		struct resource *res;
47162306a36Sopenharmony_ci		void __iomem *mmio;
47262306a36Sopenharmony_ci
47362306a36Sopenharmony_ci		res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
47462306a36Sopenharmony_ci		if (!res) {
47562306a36Sopenharmony_ci			/* Try parent's regmap */
47662306a36Sopenharmony_ci			qp->regmap = dev_get_regmap(dev->parent, NULL);
47762306a36Sopenharmony_ci			if (qp->regmap)
47862306a36Sopenharmony_ci				goto regmap_done;
47962306a36Sopenharmony_ci			return -ENODEV;
48062306a36Sopenharmony_ci		}
48162306a36Sopenharmony_ci
48262306a36Sopenharmony_ci		mmio = devm_ioremap_resource(dev, res);
48362306a36Sopenharmony_ci		if (IS_ERR(mmio))
48462306a36Sopenharmony_ci			return PTR_ERR(mmio);
48562306a36Sopenharmony_ci
48662306a36Sopenharmony_ci		qp->regmap = devm_regmap_init_mmio(dev, mmio, desc->regmap_cfg);
48762306a36Sopenharmony_ci		if (IS_ERR(qp->regmap)) {
48862306a36Sopenharmony_ci			dev_err(dev, "Cannot regmap interconnect bus resource\n");
48962306a36Sopenharmony_ci			return PTR_ERR(qp->regmap);
49062306a36Sopenharmony_ci		}
49162306a36Sopenharmony_ci	}
49262306a36Sopenharmony_ci
49362306a36Sopenharmony_ciregmap_done:
49462306a36Sopenharmony_ci	ret = clk_prepare_enable(qp->bus_clk);
49562306a36Sopenharmony_ci	if (ret)
49662306a36Sopenharmony_ci		return ret;
49762306a36Sopenharmony_ci
49862306a36Sopenharmony_ci	ret = devm_clk_bulk_get(dev, qp->num_intf_clks, qp->intf_clks);
49962306a36Sopenharmony_ci	if (ret)
50062306a36Sopenharmony_ci		goto err_disable_unprepare_clk;
50162306a36Sopenharmony_ci
50262306a36Sopenharmony_ci	provider = &qp->provider;
50362306a36Sopenharmony_ci	provider->dev = dev;
50462306a36Sopenharmony_ci	provider->set = qcom_icc_set;
50562306a36Sopenharmony_ci	provider->pre_aggregate = qcom_icc_pre_bw_aggregate;
50662306a36Sopenharmony_ci	provider->aggregate = qcom_icc_bw_aggregate;
50762306a36Sopenharmony_ci	provider->xlate_extended = qcom_icc_xlate_extended;
50862306a36Sopenharmony_ci	provider->data = data;
50962306a36Sopenharmony_ci
51062306a36Sopenharmony_ci	icc_provider_init(provider);
51162306a36Sopenharmony_ci
51262306a36Sopenharmony_ci	/* If this fails, bus accesses will crash the platform! */
51362306a36Sopenharmony_ci	ret = clk_bulk_prepare_enable(qp->num_intf_clks, qp->intf_clks);
51462306a36Sopenharmony_ci	if (ret)
51562306a36Sopenharmony_ci		goto err_disable_unprepare_clk;
51662306a36Sopenharmony_ci
51762306a36Sopenharmony_ci	for (i = 0; i < num_nodes; i++) {
51862306a36Sopenharmony_ci		size_t j;
51962306a36Sopenharmony_ci
52062306a36Sopenharmony_ci		node = icc_node_create(qnodes[i]->id);
52162306a36Sopenharmony_ci		if (IS_ERR(node)) {
52262306a36Sopenharmony_ci			clk_bulk_disable_unprepare(qp->num_intf_clks,
52362306a36Sopenharmony_ci						   qp->intf_clks);
52462306a36Sopenharmony_ci			ret = PTR_ERR(node);
52562306a36Sopenharmony_ci			goto err_remove_nodes;
52662306a36Sopenharmony_ci		}
52762306a36Sopenharmony_ci
52862306a36Sopenharmony_ci		node->name = qnodes[i]->name;
52962306a36Sopenharmony_ci		node->data = qnodes[i];
53062306a36Sopenharmony_ci		icc_node_add(node, provider);
53162306a36Sopenharmony_ci
53262306a36Sopenharmony_ci		for (j = 0; j < qnodes[i]->num_links; j++)
53362306a36Sopenharmony_ci			icc_link_create(node, qnodes[i]->links[j]);
53462306a36Sopenharmony_ci
53562306a36Sopenharmony_ci		/* Set QoS registers (we only need to do it once, generally) */
53662306a36Sopenharmony_ci		if (qnodes[i]->qos.ap_owned &&
53762306a36Sopenharmony_ci		    qnodes[i]->qos.qos_mode != NOC_QOS_MODE_INVALID) {
53862306a36Sopenharmony_ci			ret = qcom_icc_qos_set(node);
53962306a36Sopenharmony_ci			if (ret) {
54062306a36Sopenharmony_ci				clk_bulk_disable_unprepare(qp->num_intf_clks,
54162306a36Sopenharmony_ci							   qp->intf_clks);
54262306a36Sopenharmony_ci				goto err_remove_nodes;
54362306a36Sopenharmony_ci			}
54462306a36Sopenharmony_ci		}
54562306a36Sopenharmony_ci
54662306a36Sopenharmony_ci		data->nodes[i] = node;
54762306a36Sopenharmony_ci	}
54862306a36Sopenharmony_ci	data->num_nodes = num_nodes;
54962306a36Sopenharmony_ci
55062306a36Sopenharmony_ci	clk_bulk_disable_unprepare(qp->num_intf_clks, qp->intf_clks);
55162306a36Sopenharmony_ci
55262306a36Sopenharmony_ci	ret = icc_provider_register(provider);
55362306a36Sopenharmony_ci	if (ret)
55462306a36Sopenharmony_ci		goto err_remove_nodes;
55562306a36Sopenharmony_ci
55662306a36Sopenharmony_ci	platform_set_drvdata(pdev, qp);
55762306a36Sopenharmony_ci
55862306a36Sopenharmony_ci	/* Populate child NoC devices if any */
55962306a36Sopenharmony_ci	if (of_get_child_count(dev->of_node) > 0) {
56062306a36Sopenharmony_ci		ret = of_platform_populate(dev->of_node, NULL, NULL, dev);
56162306a36Sopenharmony_ci		if (ret)
56262306a36Sopenharmony_ci			goto err_deregister_provider;
56362306a36Sopenharmony_ci	}
56462306a36Sopenharmony_ci
56562306a36Sopenharmony_ci	return 0;
56662306a36Sopenharmony_ci
56762306a36Sopenharmony_cierr_deregister_provider:
56862306a36Sopenharmony_ci	icc_provider_deregister(provider);
56962306a36Sopenharmony_cierr_remove_nodes:
57062306a36Sopenharmony_ci	icc_nodes_remove(provider);
57162306a36Sopenharmony_cierr_disable_unprepare_clk:
57262306a36Sopenharmony_ci	clk_disable_unprepare(qp->bus_clk);
57362306a36Sopenharmony_ci
57462306a36Sopenharmony_ci	return ret;
57562306a36Sopenharmony_ci}
57662306a36Sopenharmony_ciEXPORT_SYMBOL(qnoc_probe);
57762306a36Sopenharmony_ci
57862306a36Sopenharmony_ciint qnoc_remove(struct platform_device *pdev)
57962306a36Sopenharmony_ci{
58062306a36Sopenharmony_ci	struct qcom_icc_provider *qp = platform_get_drvdata(pdev);
58162306a36Sopenharmony_ci
58262306a36Sopenharmony_ci	icc_provider_deregister(&qp->provider);
58362306a36Sopenharmony_ci	icc_nodes_remove(&qp->provider);
58462306a36Sopenharmony_ci	clk_disable_unprepare(qp->bus_clk);
58562306a36Sopenharmony_ci
58662306a36Sopenharmony_ci	return 0;
58762306a36Sopenharmony_ci}
58862306a36Sopenharmony_ciEXPORT_SYMBOL(qnoc_remove);
589