xref: /openbmc/linux/drivers/interconnect/qcom/icc-rpm.c (revision b97d6790d03b763eca08847a9a5869a4291b9f9a)
162feb14eSJun Nie // SPDX-License-Identifier: GPL-2.0
262feb14eSJun Nie /*
362feb14eSJun Nie  * Copyright (C) 2020 Linaro Ltd
462feb14eSJun Nie  */
562feb14eSJun Nie 
662feb14eSJun Nie #include <linux/device.h>
762feb14eSJun Nie #include <linux/interconnect-provider.h>
862feb14eSJun Nie #include <linux/io.h>
962feb14eSJun Nie #include <linux/module.h>
10cff66aceSRob Herring #include <linux/of.h>
1162feb14eSJun Nie #include <linux/of_platform.h>
1262feb14eSJun Nie #include <linux/platform_device.h>
132b6c7d64SDmitry Baryshkov #include <linux/regmap.h>
1462feb14eSJun Nie #include <linux/slab.h>
1562feb14eSJun Nie 
16ad510e4eSLeo Yan #include "icc-common.h"
1762feb14eSJun Nie #include "icc-rpm.h"
1862feb14eSJun Nie 
1908c59040SShawn Guo /* QNOC QoS */
2008c59040SShawn Guo #define QNOC_QOS_MCTL_LOWn_ADDR(n)	(0x8 + (n * 0x1000))
2108c59040SShawn Guo #define QNOC_QOS_MCTL_DFLT_PRIO_MASK	0x70
2208c59040SShawn Guo #define QNOC_QOS_MCTL_DFLT_PRIO_SHIFT	4
2308c59040SShawn Guo #define QNOC_QOS_MCTL_URGFWD_EN_MASK	0x8
2408c59040SShawn Guo #define QNOC_QOS_MCTL_URGFWD_EN_SHIFT	3
2508c59040SShawn Guo 
262b6c7d64SDmitry Baryshkov /* BIMC QoS */
272b6c7d64SDmitry Baryshkov #define M_BKE_REG_BASE(n)		(0x300 + (0x4000 * n))
282b6c7d64SDmitry Baryshkov #define M_BKE_EN_ADDR(n)		(M_BKE_REG_BASE(n))
292b6c7d64SDmitry Baryshkov #define M_BKE_HEALTH_CFG_ADDR(i, n)	(M_BKE_REG_BASE(n) + 0x40 + (0x4 * i))
302b6c7d64SDmitry Baryshkov 
312b6c7d64SDmitry Baryshkov #define M_BKE_HEALTH_CFG_LIMITCMDS_MASK	0x80000000
322b6c7d64SDmitry Baryshkov #define M_BKE_HEALTH_CFG_AREQPRIO_MASK	0x300
332b6c7d64SDmitry Baryshkov #define M_BKE_HEALTH_CFG_PRIOLVL_MASK	0x3
342b6c7d64SDmitry Baryshkov #define M_BKE_HEALTH_CFG_AREQPRIO_SHIFT	0x8
352b6c7d64SDmitry Baryshkov #define M_BKE_HEALTH_CFG_LIMITCMDS_SHIFT 0x1f
362b6c7d64SDmitry Baryshkov 
372b6c7d64SDmitry Baryshkov #define M_BKE_EN_EN_BMASK		0x1
382b6c7d64SDmitry Baryshkov 
392b6c7d64SDmitry Baryshkov /* NoC QoS */
402b6c7d64SDmitry Baryshkov #define NOC_QOS_PRIORITYn_ADDR(n)	(0x8 + (n * 0x1000))
412b6c7d64SDmitry Baryshkov #define NOC_QOS_PRIORITY_P1_MASK	0xc
422b6c7d64SDmitry Baryshkov #define NOC_QOS_PRIORITY_P0_MASK	0x3
432b6c7d64SDmitry Baryshkov #define NOC_QOS_PRIORITY_P1_SHIFT	0x2
442b6c7d64SDmitry Baryshkov 
452b6c7d64SDmitry Baryshkov #define NOC_QOS_MODEn_ADDR(n)		(0xc + (n * 0x1000))
462b6c7d64SDmitry Baryshkov #define NOC_QOS_MODEn_MASK		0x3
472b6c7d64SDmitry Baryshkov 
481d779317SKonrad Dybcio #define NOC_QOS_MODE_FIXED_VAL		0x0
491d779317SKonrad Dybcio #define NOC_QOS_MODE_BYPASS_VAL		0x2
501d779317SKonrad Dybcio 
5111f63efeSKonrad Dybcio #define ICC_BUS_CLK_MIN_RATE		19200ULL /* kHz */
52b979049cSKonrad Dybcio 
qcom_icc_set_qnoc_qos(struct icc_node * src)53ca545907SKonrad Dybcio static int qcom_icc_set_qnoc_qos(struct icc_node *src)
5408c59040SShawn Guo {
5508c59040SShawn Guo 	struct icc_provider *provider = src->provider;
5608c59040SShawn Guo 	struct qcom_icc_provider *qp = to_qcom_provider(provider);
5708c59040SShawn Guo 	struct qcom_icc_node *qn = src->data;
5808c59040SShawn Guo 	struct qcom_icc_qos *qos = &qn->qos;
5908c59040SShawn Guo 	int rc;
6008c59040SShawn Guo 
6108c59040SShawn Guo 	rc = regmap_update_bits(qp->regmap,
6208c59040SShawn Guo 			qp->qos_offset + QNOC_QOS_MCTL_LOWn_ADDR(qos->qos_port),
6308c59040SShawn Guo 			QNOC_QOS_MCTL_DFLT_PRIO_MASK,
6408c59040SShawn Guo 			qos->areq_prio << QNOC_QOS_MCTL_DFLT_PRIO_SHIFT);
6508c59040SShawn Guo 	if (rc)
6608c59040SShawn Guo 		return rc;
6708c59040SShawn Guo 
6808c59040SShawn Guo 	return regmap_update_bits(qp->regmap,
6908c59040SShawn Guo 			qp->qos_offset + QNOC_QOS_MCTL_LOWn_ADDR(qos->qos_port),
7008c59040SShawn Guo 			QNOC_QOS_MCTL_URGFWD_EN_MASK,
7108c59040SShawn Guo 			!!qos->urg_fwd_en << QNOC_QOS_MCTL_URGFWD_EN_SHIFT);
7208c59040SShawn Guo }
7308c59040SShawn Guo 
qcom_icc_bimc_set_qos_health(struct qcom_icc_provider * qp,struct qcom_icc_qos * qos,int regnum)740788f4d5SDmitry Baryshkov static int qcom_icc_bimc_set_qos_health(struct qcom_icc_provider *qp,
752b6c7d64SDmitry Baryshkov 					struct qcom_icc_qos *qos,
762b6c7d64SDmitry Baryshkov 					int regnum)
772b6c7d64SDmitry Baryshkov {
782b6c7d64SDmitry Baryshkov 	u32 val;
792b6c7d64SDmitry Baryshkov 	u32 mask;
802b6c7d64SDmitry Baryshkov 
812b6c7d64SDmitry Baryshkov 	val = qos->prio_level;
822b6c7d64SDmitry Baryshkov 	mask = M_BKE_HEALTH_CFG_PRIOLVL_MASK;
832b6c7d64SDmitry Baryshkov 
842b6c7d64SDmitry Baryshkov 	val |= qos->areq_prio << M_BKE_HEALTH_CFG_AREQPRIO_SHIFT;
852b6c7d64SDmitry Baryshkov 	mask |= M_BKE_HEALTH_CFG_AREQPRIO_MASK;
862b6c7d64SDmitry Baryshkov 
872b6c7d64SDmitry Baryshkov 	/* LIMITCMDS is not present on M_BKE_HEALTH_3 */
882b6c7d64SDmitry Baryshkov 	if (regnum != 3) {
892b6c7d64SDmitry Baryshkov 		val |= qos->limit_commands << M_BKE_HEALTH_CFG_LIMITCMDS_SHIFT;
902b6c7d64SDmitry Baryshkov 		mask |= M_BKE_HEALTH_CFG_LIMITCMDS_MASK;
912b6c7d64SDmitry Baryshkov 	}
922b6c7d64SDmitry Baryshkov 
930788f4d5SDmitry Baryshkov 	return regmap_update_bits(qp->regmap,
940788f4d5SDmitry Baryshkov 				  qp->qos_offset + M_BKE_HEALTH_CFG_ADDR(regnum, qos->qos_port),
952b6c7d64SDmitry Baryshkov 				  mask, val);
962b6c7d64SDmitry Baryshkov }
972b6c7d64SDmitry Baryshkov 
qcom_icc_set_bimc_qos(struct icc_node * src)98ca545907SKonrad Dybcio static int qcom_icc_set_bimc_qos(struct icc_node *src)
992b6c7d64SDmitry Baryshkov {
1002b6c7d64SDmitry Baryshkov 	struct qcom_icc_provider *qp;
1012b6c7d64SDmitry Baryshkov 	struct qcom_icc_node *qn;
1022b6c7d64SDmitry Baryshkov 	struct icc_provider *provider;
1032b6c7d64SDmitry Baryshkov 	u32 mode = NOC_QOS_MODE_BYPASS;
1042b6c7d64SDmitry Baryshkov 	u32 val = 0;
1052b6c7d64SDmitry Baryshkov 	int i, rc = 0;
1062b6c7d64SDmitry Baryshkov 
1072b6c7d64SDmitry Baryshkov 	qn = src->data;
1082b6c7d64SDmitry Baryshkov 	provider = src->provider;
1092b6c7d64SDmitry Baryshkov 	qp = to_qcom_provider(provider);
1102b6c7d64SDmitry Baryshkov 
11142cdeb69SShawn Guo 	if (qn->qos.qos_mode != NOC_QOS_MODE_INVALID)
1122b6c7d64SDmitry Baryshkov 		mode = qn->qos.qos_mode;
1132b6c7d64SDmitry Baryshkov 
1142b6c7d64SDmitry Baryshkov 	/* QoS Priority: The QoS Health parameters are getting considered
1152b6c7d64SDmitry Baryshkov 	 * only if we are NOT in Bypass Mode.
1162b6c7d64SDmitry Baryshkov 	 */
1172b6c7d64SDmitry Baryshkov 	if (mode != NOC_QOS_MODE_BYPASS) {
1182b6c7d64SDmitry Baryshkov 		for (i = 3; i >= 0; i--) {
1190788f4d5SDmitry Baryshkov 			rc = qcom_icc_bimc_set_qos_health(qp,
1202b6c7d64SDmitry Baryshkov 							  &qn->qos, i);
1212b6c7d64SDmitry Baryshkov 			if (rc)
1222b6c7d64SDmitry Baryshkov 				return rc;
1232b6c7d64SDmitry Baryshkov 		}
1242b6c7d64SDmitry Baryshkov 
1252b6c7d64SDmitry Baryshkov 		/* Set BKE_EN to 1 when Fixed, Regulator or Limiter Mode */
1262b6c7d64SDmitry Baryshkov 		val = 1;
1272b6c7d64SDmitry Baryshkov 	}
1282b6c7d64SDmitry Baryshkov 
1290788f4d5SDmitry Baryshkov 	return regmap_update_bits(qp->regmap,
1300788f4d5SDmitry Baryshkov 				  qp->qos_offset + M_BKE_EN_ADDR(qn->qos.qos_port),
1312b6c7d64SDmitry Baryshkov 				  M_BKE_EN_EN_BMASK, val);
1322b6c7d64SDmitry Baryshkov }
1332b6c7d64SDmitry Baryshkov 
qcom_icc_noc_set_qos_priority(struct qcom_icc_provider * qp,struct qcom_icc_qos * qos)1340788f4d5SDmitry Baryshkov static int qcom_icc_noc_set_qos_priority(struct qcom_icc_provider *qp,
1352b6c7d64SDmitry Baryshkov 					 struct qcom_icc_qos *qos)
1362b6c7d64SDmitry Baryshkov {
1372b6c7d64SDmitry Baryshkov 	u32 val;
1382b6c7d64SDmitry Baryshkov 	int rc;
1392b6c7d64SDmitry Baryshkov 
1402b6c7d64SDmitry Baryshkov 	/* Must be updated one at a time, P1 first, P0 last */
1412b6c7d64SDmitry Baryshkov 	val = qos->areq_prio << NOC_QOS_PRIORITY_P1_SHIFT;
1420788f4d5SDmitry Baryshkov 	rc = regmap_update_bits(qp->regmap,
1430788f4d5SDmitry Baryshkov 				qp->qos_offset + NOC_QOS_PRIORITYn_ADDR(qos->qos_port),
1442b6c7d64SDmitry Baryshkov 				NOC_QOS_PRIORITY_P1_MASK, val);
1452b6c7d64SDmitry Baryshkov 	if (rc)
1462b6c7d64SDmitry Baryshkov 		return rc;
1472b6c7d64SDmitry Baryshkov 
1480788f4d5SDmitry Baryshkov 	return regmap_update_bits(qp->regmap,
1490788f4d5SDmitry Baryshkov 				  qp->qos_offset + NOC_QOS_PRIORITYn_ADDR(qos->qos_port),
1502b6c7d64SDmitry Baryshkov 				  NOC_QOS_PRIORITY_P0_MASK, qos->prio_level);
1512b6c7d64SDmitry Baryshkov }
1522b6c7d64SDmitry Baryshkov 
qcom_icc_set_noc_qos(struct icc_node * src)153ca545907SKonrad Dybcio static int qcom_icc_set_noc_qos(struct icc_node *src)
1542b6c7d64SDmitry Baryshkov {
1552b6c7d64SDmitry Baryshkov 	struct qcom_icc_provider *qp;
1562b6c7d64SDmitry Baryshkov 	struct qcom_icc_node *qn;
1572b6c7d64SDmitry Baryshkov 	struct icc_provider *provider;
1581d779317SKonrad Dybcio 	u32 mode = NOC_QOS_MODE_BYPASS_VAL;
1592b6c7d64SDmitry Baryshkov 	int rc = 0;
1602b6c7d64SDmitry Baryshkov 
1612b6c7d64SDmitry Baryshkov 	qn = src->data;
1622b6c7d64SDmitry Baryshkov 	provider = src->provider;
1632b6c7d64SDmitry Baryshkov 	qp = to_qcom_provider(provider);
1642b6c7d64SDmitry Baryshkov 
1652b6c7d64SDmitry Baryshkov 	if (qn->qos.qos_port < 0) {
1662b6c7d64SDmitry Baryshkov 		dev_dbg(src->provider->dev,
1672b6c7d64SDmitry Baryshkov 			"NoC QoS: Skipping %s: vote aggregated on parent.\n",
1682b6c7d64SDmitry Baryshkov 			qn->name);
1692b6c7d64SDmitry Baryshkov 		return 0;
1702b6c7d64SDmitry Baryshkov 	}
1712b6c7d64SDmitry Baryshkov 
1721d779317SKonrad Dybcio 	if (qn->qos.qos_mode == NOC_QOS_MODE_FIXED) {
1731d779317SKonrad Dybcio 		dev_dbg(src->provider->dev, "NoC QoS: %s: Set Fixed mode\n", qn->name);
1741d779317SKonrad Dybcio 		mode = NOC_QOS_MODE_FIXED_VAL;
1750788f4d5SDmitry Baryshkov 		rc = qcom_icc_noc_set_qos_priority(qp, &qn->qos);
1762b6c7d64SDmitry Baryshkov 		if (rc)
1772b6c7d64SDmitry Baryshkov 			return rc;
1781d779317SKonrad Dybcio 	} else if (qn->qos.qos_mode == NOC_QOS_MODE_BYPASS) {
1791d779317SKonrad Dybcio 		dev_dbg(src->provider->dev, "NoC QoS: %s: Set Bypass mode\n", qn->name);
1801d779317SKonrad Dybcio 		mode = NOC_QOS_MODE_BYPASS_VAL;
1811d779317SKonrad Dybcio 	} else {
1821d779317SKonrad Dybcio 		/* How did we get here? */
1832b6c7d64SDmitry Baryshkov 	}
1842b6c7d64SDmitry Baryshkov 
1852b6c7d64SDmitry Baryshkov 	return regmap_update_bits(qp->regmap,
1860788f4d5SDmitry Baryshkov 				  qp->qos_offset + NOC_QOS_MODEn_ADDR(qn->qos.qos_port),
1872b6c7d64SDmitry Baryshkov 				  NOC_QOS_MODEn_MASK, mode);
1882b6c7d64SDmitry Baryshkov }
1892b6c7d64SDmitry Baryshkov 
qcom_icc_qos_set(struct icc_node * node)190ca545907SKonrad Dybcio static int qcom_icc_qos_set(struct icc_node *node)
1912b6c7d64SDmitry Baryshkov {
1922b6c7d64SDmitry Baryshkov 	struct qcom_icc_provider *qp = to_qcom_provider(node->provider);
1932b6c7d64SDmitry Baryshkov 	struct qcom_icc_node *qn = node->data;
1942b6c7d64SDmitry Baryshkov 
1952b6c7d64SDmitry Baryshkov 	dev_dbg(node->provider->dev, "Setting QoS for %s\n", qn->name);
1962b6c7d64SDmitry Baryshkov 
19708c59040SShawn Guo 	switch (qp->type) {
19808c59040SShawn Guo 	case QCOM_ICC_BIMC:
199ca545907SKonrad Dybcio 		return qcom_icc_set_bimc_qos(node);
20008c59040SShawn Guo 	case QCOM_ICC_QNOC:
201ca545907SKonrad Dybcio 		return qcom_icc_set_qnoc_qos(node);
20208c59040SShawn Guo 	default:
203ca545907SKonrad Dybcio 		return qcom_icc_set_noc_qos(node);
2042b6c7d64SDmitry Baryshkov 	}
20508c59040SShawn Guo }
2062b6c7d64SDmitry Baryshkov 
qcom_icc_rpm_set(struct qcom_icc_node * qn,u64 * bw)20732846c4aSKonrad Dybcio static int qcom_icc_rpm_set(struct qcom_icc_node *qn, u64 *bw)
2082b6c7d64SDmitry Baryshkov {
20932846c4aSKonrad Dybcio 	int ret, rpm_ctx = 0;
21032846c4aSKonrad Dybcio 	u64 bw_bps;
2112b6c7d64SDmitry Baryshkov 
21232882f65SKonrad Dybcio 	if (qn->qos.ap_owned)
21332882f65SKonrad Dybcio 		return 0;
21432882f65SKonrad Dybcio 
21532846c4aSKonrad Dybcio 	for (rpm_ctx = 0; rpm_ctx < QCOM_SMD_RPM_STATE_NUM; rpm_ctx++) {
21632846c4aSKonrad Dybcio 		bw_bps = icc_units_to_bps(bw[rpm_ctx]);
21732846c4aSKonrad Dybcio 
21832882f65SKonrad Dybcio 		if (qn->mas_rpm_id != -1) {
21932846c4aSKonrad Dybcio 			ret = qcom_icc_rpm_smd_send(rpm_ctx,
2202b6c7d64SDmitry Baryshkov 						    RPM_BUS_MASTER_REQ,
22132882f65SKonrad Dybcio 						    qn->mas_rpm_id,
22232846c4aSKonrad Dybcio 						    bw_bps);
2232b6c7d64SDmitry Baryshkov 			if (ret) {
2242b6c7d64SDmitry Baryshkov 				pr_err("qcom_icc_rpm_smd_send mas %d error %d\n",
22532882f65SKonrad Dybcio 				qn->mas_rpm_id, ret);
2262b6c7d64SDmitry Baryshkov 				return ret;
2272b6c7d64SDmitry Baryshkov 			}
2282b6c7d64SDmitry Baryshkov 		}
2292b6c7d64SDmitry Baryshkov 
23032882f65SKonrad Dybcio 		if (qn->slv_rpm_id != -1) {
23132846c4aSKonrad Dybcio 			ret = qcom_icc_rpm_smd_send(rpm_ctx,
2322b6c7d64SDmitry Baryshkov 						    RPM_BUS_SLAVE_REQ,
23332882f65SKonrad Dybcio 						    qn->slv_rpm_id,
23432846c4aSKonrad Dybcio 						    bw_bps);
2352b6c7d64SDmitry Baryshkov 			if (ret) {
2362b6c7d64SDmitry Baryshkov 				pr_err("qcom_icc_rpm_smd_send slv %d error %d\n",
23732882f65SKonrad Dybcio 				qn->slv_rpm_id, ret);
2382b6c7d64SDmitry Baryshkov 				return ret;
2392b6c7d64SDmitry Baryshkov 			}
2402b6c7d64SDmitry Baryshkov 		}
24132846c4aSKonrad Dybcio 	}
2422b6c7d64SDmitry Baryshkov 
243c73e60e0SGeorgi Djakov 	return 0;
2442b6c7d64SDmitry Baryshkov }
2452b6c7d64SDmitry Baryshkov 
246dcbce7b0SLeo Yan /**
247dcbce7b0SLeo Yan  * qcom_icc_pre_bw_aggregate - cleans up values before re-aggregate requests
248dcbce7b0SLeo Yan  * @node: icc node to operate on
249dcbce7b0SLeo Yan  */
qcom_icc_pre_bw_aggregate(struct icc_node * node)250dcbce7b0SLeo Yan static void qcom_icc_pre_bw_aggregate(struct icc_node *node)
251dcbce7b0SLeo Yan {
252dcbce7b0SLeo Yan 	struct qcom_icc_node *qn;
253dcbce7b0SLeo Yan 	size_t i;
254dcbce7b0SLeo Yan 
255dcbce7b0SLeo Yan 	qn = node->data;
2566ed0e5e6SKonrad Dybcio 	for (i = 0; i < QCOM_SMD_RPM_STATE_NUM; i++) {
257dcbce7b0SLeo Yan 		qn->sum_avg[i] = 0;
258dcbce7b0SLeo Yan 		qn->max_peak[i] = 0;
259dcbce7b0SLeo Yan 	}
260dcbce7b0SLeo Yan }
261dcbce7b0SLeo Yan 
262dcbce7b0SLeo Yan /**
263dcbce7b0SLeo Yan  * qcom_icc_bw_aggregate - aggregate bw for buckets indicated by tag
264dcbce7b0SLeo Yan  * @node: node to aggregate
265dcbce7b0SLeo Yan  * @tag: tag to indicate which buckets to aggregate
266dcbce7b0SLeo Yan  * @avg_bw: new bw to sum aggregate
267dcbce7b0SLeo Yan  * @peak_bw: new bw to max aggregate
268dcbce7b0SLeo Yan  * @agg_avg: existing aggregate avg bw val
269dcbce7b0SLeo Yan  * @agg_peak: existing aggregate peak bw val
270dcbce7b0SLeo Yan  */
qcom_icc_bw_aggregate(struct icc_node * node,u32 tag,u32 avg_bw,u32 peak_bw,u32 * agg_avg,u32 * agg_peak)271dcbce7b0SLeo Yan static int qcom_icc_bw_aggregate(struct icc_node *node, u32 tag, u32 avg_bw,
272dcbce7b0SLeo Yan 				 u32 peak_bw, u32 *agg_avg, u32 *agg_peak)
273dcbce7b0SLeo Yan {
274dcbce7b0SLeo Yan 	size_t i;
275dcbce7b0SLeo Yan 	struct qcom_icc_node *qn;
276dcbce7b0SLeo Yan 
277dcbce7b0SLeo Yan 	qn = node->data;
278dcbce7b0SLeo Yan 
279dcbce7b0SLeo Yan 	if (!tag)
2806ed0e5e6SKonrad Dybcio 		tag = RPM_ALWAYS_TAG;
281dcbce7b0SLeo Yan 
2826ed0e5e6SKonrad Dybcio 	for (i = 0; i < QCOM_SMD_RPM_STATE_NUM; i++) {
283dcbce7b0SLeo Yan 		if (tag & BIT(i)) {
284dcbce7b0SLeo Yan 			qn->sum_avg[i] += avg_bw;
285dcbce7b0SLeo Yan 			qn->max_peak[i] = max_t(u32, qn->max_peak[i], peak_bw);
286dcbce7b0SLeo Yan 		}
287dcbce7b0SLeo Yan 	}
288dcbce7b0SLeo Yan 
289dcbce7b0SLeo Yan 	*agg_avg += avg_bw;
290dcbce7b0SLeo Yan 	*agg_peak = max_t(u32, *agg_peak, peak_bw);
291dcbce7b0SLeo Yan 	return 0;
292dcbce7b0SLeo Yan }
293dcbce7b0SLeo Yan 
294e3305daaSLeo Yan /**
29517fc623eSKonrad Dybcio  * qcom_icc_bus_aggregate - calculate bus clock rates by traversing all nodes
296e3305daaSLeo Yan  * @provider: generic interconnect provider
29717fc623eSKonrad Dybcio  * @agg_clk_rate: array containing the aggregated clock rates in kHz
298e3305daaSLeo Yan  */
qcom_icc_bus_aggregate(struct icc_provider * provider,u64 * agg_clk_rate)29917fc623eSKonrad Dybcio static void qcom_icc_bus_aggregate(struct icc_provider *provider, u64 *agg_clk_rate)
300e3305daaSLeo Yan {
30117fc623eSKonrad Dybcio 	u64 agg_avg_rate, agg_rate;
302e3305daaSLeo Yan 	struct qcom_icc_node *qn;
30317fc623eSKonrad Dybcio 	struct icc_node *node;
304e3305daaSLeo Yan 	int i;
305e3305daaSLeo Yan 
306e3305daaSLeo Yan 	/*
30717fc623eSKonrad Dybcio 	 * Iterate nodes on the provider, aggregate bandwidth requests for
30817fc623eSKonrad Dybcio 	 * every bucket and convert them into bus clock rates.
309e3305daaSLeo Yan 	 */
310e3305daaSLeo Yan 	list_for_each_entry(node, &provider->nodes, node_list) {
311e3305daaSLeo Yan 		qn = node->data;
3126ed0e5e6SKonrad Dybcio 		for (i = 0; i < QCOM_SMD_RPM_STATE_NUM; i++) {
31302819953SKonrad Dybcio 			if (qn->channels)
31417fc623eSKonrad Dybcio 				agg_avg_rate = div_u64(qn->sum_avg[i], qn->channels);
31502819953SKonrad Dybcio 			else
31617fc623eSKonrad Dybcio 				agg_avg_rate = qn->sum_avg[i];
317e3305daaSLeo Yan 
31817fc623eSKonrad Dybcio 			agg_rate = max_t(u64, agg_avg_rate, qn->max_peak[i]);
31917fc623eSKonrad Dybcio 			do_div(agg_rate, qn->buswidth);
32017fc623eSKonrad Dybcio 
32117fc623eSKonrad Dybcio 			agg_clk_rate[i] = max_t(u64, agg_clk_rate[i], agg_rate);
32217fc623eSKonrad Dybcio 		}
32317fc623eSKonrad Dybcio 	}
324e3305daaSLeo Yan }
325e3305daaSLeo Yan 
qcom_icc_set(struct icc_node * src,struct icc_node * dst)32662feb14eSJun Nie static int qcom_icc_set(struct icc_node *src, struct icc_node *dst)
32762feb14eSJun Nie {
328751f4d14SBryan O'Donoghue 	struct qcom_icc_node *src_qn = NULL, *dst_qn = NULL;
32917fc623eSKonrad Dybcio 	u64 agg_clk_rate[QCOM_SMD_RPM_STATE_NUM] = { 0 };
33062feb14eSJun Nie 	struct icc_provider *provider;
33117fc623eSKonrad Dybcio 	struct qcom_icc_provider *qp;
33211f63efeSKonrad Dybcio 	u64 active_rate, sleep_rate;
33311f63efeSKonrad Dybcio 	int ret;
33462feb14eSJun Nie 
335751f4d14SBryan O'Donoghue 	src_qn = src->data;
336751f4d14SBryan O'Donoghue 	if (dst)
337751f4d14SBryan O'Donoghue 		dst_qn = dst->data;
33862feb14eSJun Nie 	provider = src->provider;
33962feb14eSJun Nie 	qp = to_qcom_provider(provider);
34062feb14eSJun Nie 
34117fc623eSKonrad Dybcio 	qcom_icc_bus_aggregate(provider, agg_clk_rate);
34217fc623eSKonrad Dybcio 	active_rate = agg_clk_rate[QCOM_SMD_RPM_ACTIVE_STATE];
34317fc623eSKonrad Dybcio 	sleep_rate = agg_clk_rate[QCOM_SMD_RPM_SLEEP_STATE];
34462feb14eSJun Nie 
3453b748010SKonrad Dybcio 	ret = qcom_icc_rpm_set(src_qn, src_qn->sum_avg);
3462b6c7d64SDmitry Baryshkov 	if (ret)
34762feb14eSJun Nie 		return ret;
34832882f65SKonrad Dybcio 
349751f4d14SBryan O'Donoghue 	if (dst_qn) {
3503b748010SKonrad Dybcio 		ret = qcom_icc_rpm_set(dst_qn, dst_qn->sum_avg);
3512b6c7d64SDmitry Baryshkov 		if (ret)
35262feb14eSJun Nie 			return ret;
35362feb14eSJun Nie 	}
35462feb14eSJun Nie 
35511f63efeSKonrad Dybcio 	/* Some providers don't have a bus clock to scale */
35611f63efeSKonrad Dybcio 	if (!qp->bus_clk_desc && !qp->bus_clk)
35711f63efeSKonrad Dybcio 		return 0;
35862feb14eSJun Nie 
359b979049cSKonrad Dybcio 	/*
360b979049cSKonrad Dybcio 	 * Downstream checks whether the requested rate is zero, but it makes little sense
361b979049cSKonrad Dybcio 	 * to vote for a value that's below the lower threshold, so let's not do so.
362b979049cSKonrad Dybcio 	 */
36311f63efeSKonrad Dybcio 	if (qp->keep_alive)
36411f63efeSKonrad Dybcio 		active_rate = max(ICC_BUS_CLK_MIN_RATE, active_rate);
365b979049cSKonrad Dybcio 
36611f63efeSKonrad Dybcio 	/* Some providers have a non-RPM-owned bus clock - convert kHz->Hz for the CCF */
36711f63efeSKonrad Dybcio 	if (qp->bus_clk) {
36811f63efeSKonrad Dybcio 		active_rate = max_t(u64, active_rate, sleep_rate);
36911f63efeSKonrad Dybcio 		/* ARM32 caps clk_set_rate arg to u32.. Nothing we can do about that! */
37011f63efeSKonrad Dybcio 		active_rate = min_t(u64, 1000ULL * active_rate, ULONG_MAX);
37111f63efeSKonrad Dybcio 		return clk_set_rate(qp->bus_clk, active_rate);
37262feb14eSJun Nie 	}
37311f63efeSKonrad Dybcio 
37411f63efeSKonrad Dybcio 	/* RPM only accepts <=INT_MAX rates */
37511f63efeSKonrad Dybcio 	active_rate = min_t(u64, active_rate, INT_MAX);
37611f63efeSKonrad Dybcio 	sleep_rate = min_t(u64, sleep_rate, INT_MAX);
37711f63efeSKonrad Dybcio 
37811f63efeSKonrad Dybcio 	if (active_rate != qp->bus_clk_rate[QCOM_SMD_RPM_ACTIVE_STATE]) {
37911f63efeSKonrad Dybcio 		ret = qcom_icc_rpm_set_bus_rate(qp->bus_clk_desc, QCOM_SMD_RPM_ACTIVE_STATE,
38011f63efeSKonrad Dybcio 						active_rate);
38111f63efeSKonrad Dybcio 		if (ret)
38211f63efeSKonrad Dybcio 			return ret;
38311f63efeSKonrad Dybcio 
38411f63efeSKonrad Dybcio 		/* Cache the rate after we've successfully commited it to RPM */
38511f63efeSKonrad Dybcio 		qp->bus_clk_rate[QCOM_SMD_RPM_ACTIVE_STATE] = active_rate;
38611f63efeSKonrad Dybcio 	}
38711f63efeSKonrad Dybcio 
38811f63efeSKonrad Dybcio 	if (sleep_rate != qp->bus_clk_rate[QCOM_SMD_RPM_SLEEP_STATE]) {
38911f63efeSKonrad Dybcio 		ret = qcom_icc_rpm_set_bus_rate(qp->bus_clk_desc, QCOM_SMD_RPM_SLEEP_STATE,
39011f63efeSKonrad Dybcio 						sleep_rate);
39111f63efeSKonrad Dybcio 		if (ret)
39211f63efeSKonrad Dybcio 			return ret;
39311f63efeSKonrad Dybcio 
39411f63efeSKonrad Dybcio 		/* Cache the rate after we've successfully commited it to RPM */
39511f63efeSKonrad Dybcio 		qp->bus_clk_rate[QCOM_SMD_RPM_SLEEP_STATE] = sleep_rate;
39662feb14eSJun Nie 	}
39762feb14eSJun Nie 
39862feb14eSJun Nie 	return 0;
39962feb14eSJun Nie }
40062feb14eSJun Nie 
qnoc_probe(struct platform_device * pdev)40163e8ab61SDmitry Baryshkov int qnoc_probe(struct platform_device *pdev)
40262feb14eSJun Nie {
40362feb14eSJun Nie 	struct device *dev = &pdev->dev;
40462feb14eSJun Nie 	const struct qcom_icc_desc *desc;
40562feb14eSJun Nie 	struct icc_onecell_data *data;
40662feb14eSJun Nie 	struct icc_provider *provider;
4072ccf33c0SKrzysztof Kozlowski 	struct qcom_icc_node * const *qnodes;
40862feb14eSJun Nie 	struct qcom_icc_provider *qp;
40962feb14eSJun Nie 	struct icc_node *node;
41062feb14eSJun Nie 	size_t num_nodes, i;
4112e2113c8SKonrad Dybcio 	const char * const *cds = NULL;
41263e8ab61SDmitry Baryshkov 	int cd_num;
41362feb14eSJun Nie 	int ret;
41462feb14eSJun Nie 
41562feb14eSJun Nie 	/* wait for the RPM proxy */
41662feb14eSJun Nie 	if (!qcom_icc_rpm_smd_available())
41762feb14eSJun Nie 		return -EPROBE_DEFER;
41862feb14eSJun Nie 
41962feb14eSJun Nie 	desc = of_device_get_match_data(dev);
42062feb14eSJun Nie 	if (!desc)
42162feb14eSJun Nie 		return -EINVAL;
42262feb14eSJun Nie 
42362feb14eSJun Nie 	qnodes = desc->nodes;
42462feb14eSJun Nie 	num_nodes = desc->num_nodes;
42562feb14eSJun Nie 
4262e2113c8SKonrad Dybcio 	if (desc->num_intf_clocks) {
4272e2113c8SKonrad Dybcio 		cds = desc->intf_clocks;
4282e2113c8SKonrad Dybcio 		cd_num = desc->num_intf_clocks;
4292e2113c8SKonrad Dybcio 	} else {
4302e2113c8SKonrad Dybcio 		/* 0 intf clocks is perfectly fine */
4312e2113c8SKonrad Dybcio 		cd_num = 0;
4322e2113c8SKonrad Dybcio 	}
4332e2113c8SKonrad Dybcio 
4342e2113c8SKonrad Dybcio 	qp = devm_kzalloc(dev, sizeof(*qp), GFP_KERNEL);
4352e2113c8SKonrad Dybcio 	if (!qp)
4362e2113c8SKonrad Dybcio 		return -ENOMEM;
4372e2113c8SKonrad Dybcio 
4380ebee0a6SDan Carpenter 	qp->intf_clks = devm_kcalloc(dev, cd_num, sizeof(*qp->intf_clks), GFP_KERNEL);
4392e2113c8SKonrad Dybcio 	if (!qp->intf_clks)
4402e2113c8SKonrad Dybcio 		return -ENOMEM;
4412e2113c8SKonrad Dybcio 
44211f63efeSKonrad Dybcio 	if (desc->bus_clk_desc) {
44311f63efeSKonrad Dybcio 		qp->bus_clk_desc = devm_kzalloc(dev, sizeof(*qp->bus_clk_desc),
44411f63efeSKonrad Dybcio 						GFP_KERNEL);
44511f63efeSKonrad Dybcio 		if (!qp->bus_clk_desc)
44611f63efeSKonrad Dybcio 			return -ENOMEM;
44711f63efeSKonrad Dybcio 
44811f63efeSKonrad Dybcio 		qp->bus_clk_desc = desc->bus_clk_desc;
44911f63efeSKonrad Dybcio 	} else {
45011f63efeSKonrad Dybcio 		/* Some older SoCs may have a single non-RPM-owned bus clock. */
45111f63efeSKonrad Dybcio 		qp->bus_clk = devm_clk_get_optional(dev, "bus");
45211f63efeSKonrad Dybcio 		if (IS_ERR(qp->bus_clk))
45311f63efeSKonrad Dybcio 			return PTR_ERR(qp->bus_clk);
45411f63efeSKonrad Dybcio 	}
45511f63efeSKonrad Dybcio 
4562e2113c8SKonrad Dybcio 	data = devm_kzalloc(dev, struct_size(data, nodes, num_nodes),
4572e2113c8SKonrad Dybcio 			    GFP_KERNEL);
4582e2113c8SKonrad Dybcio 	if (!data)
4592e2113c8SKonrad Dybcio 		return -ENOMEM;
4602e2113c8SKonrad Dybcio 
4612e2113c8SKonrad Dybcio 	qp->num_intf_clks = cd_num;
4622e2113c8SKonrad Dybcio 	for (i = 0; i < cd_num; i++)
4632e2113c8SKonrad Dybcio 		qp->intf_clks[i].id = cds[i];
4642e2113c8SKonrad Dybcio 
465b979049cSKonrad Dybcio 	qp->keep_alive = desc->keep_alive;
466e9d54c26SShawn Guo 	qp->type = desc->type;
4670788f4d5SDmitry Baryshkov 	qp->qos_offset = desc->qos_offset;
4682b6c7d64SDmitry Baryshkov 
4692b6c7d64SDmitry Baryshkov 	if (desc->regmap_cfg) {
4702b6c7d64SDmitry Baryshkov 		struct resource *res;
4712b6c7d64SDmitry Baryshkov 		void __iomem *mmio;
4722b6c7d64SDmitry Baryshkov 
4732b6c7d64SDmitry Baryshkov 		res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
474e39bf297SShawn Guo 		if (!res) {
475e39bf297SShawn Guo 			/* Try parent's regmap */
476e39bf297SShawn Guo 			qp->regmap = dev_get_regmap(dev->parent, NULL);
477e39bf297SShawn Guo 			if (qp->regmap)
478e39bf297SShawn Guo 				goto regmap_done;
4792b6c7d64SDmitry Baryshkov 			return -ENODEV;
480e39bf297SShawn Guo 		}
4812b6c7d64SDmitry Baryshkov 
4822b6c7d64SDmitry Baryshkov 		mmio = devm_ioremap_resource(dev, res);
483c423f016SShang XiaoJing 		if (IS_ERR(mmio))
4842b6c7d64SDmitry Baryshkov 			return PTR_ERR(mmio);
4852b6c7d64SDmitry Baryshkov 
4862b6c7d64SDmitry Baryshkov 		qp->regmap = devm_regmap_init_mmio(dev, mmio, desc->regmap_cfg);
4872b6c7d64SDmitry Baryshkov 		if (IS_ERR(qp->regmap)) {
4882b6c7d64SDmitry Baryshkov 			dev_err(dev, "Cannot regmap interconnect bus resource\n");
4892b6c7d64SDmitry Baryshkov 			return PTR_ERR(qp->regmap);
4902b6c7d64SDmitry Baryshkov 		}
4912b6c7d64SDmitry Baryshkov 	}
4922b6c7d64SDmitry Baryshkov 
493e39bf297SShawn Guo regmap_done:
49411f63efeSKonrad Dybcio 	ret = clk_prepare_enable(qp->bus_clk);
49562feb14eSJun Nie 	if (ret)
49662feb14eSJun Nie 		return ret;
49762feb14eSJun Nie 
4982e2113c8SKonrad Dybcio 	ret = devm_clk_bulk_get(dev, qp->num_intf_clks, qp->intf_clks);
4992e2113c8SKonrad Dybcio 	if (ret)
500*0967f1d9SYang Yingliang 		goto err_disable_unprepare_clk;
5012e2113c8SKonrad Dybcio 
50262feb14eSJun Nie 	provider = &qp->provider;
50362feb14eSJun Nie 	provider->dev = dev;
50462feb14eSJun Nie 	provider->set = qcom_icc_set;
505dcbce7b0SLeo Yan 	provider->pre_aggregate = qcom_icc_pre_bw_aggregate;
506dcbce7b0SLeo Yan 	provider->aggregate = qcom_icc_bw_aggregate;
507ad510e4eSLeo Yan 	provider->xlate_extended = qcom_icc_xlate_extended;
50862feb14eSJun Nie 	provider->data = data;
50962feb14eSJun Nie 
51090ae93d8SJohan Hovold 	icc_provider_init(provider);
51162feb14eSJun Nie 
5122e2113c8SKonrad Dybcio 	/* If this fails, bus accesses will crash the platform! */
5132e2113c8SKonrad Dybcio 	ret = clk_bulk_prepare_enable(qp->num_intf_clks, qp->intf_clks);
5142e2113c8SKonrad Dybcio 	if (ret)
515*0967f1d9SYang Yingliang 		goto err_disable_unprepare_clk;
5162e2113c8SKonrad Dybcio 
51762feb14eSJun Nie 	for (i = 0; i < num_nodes; i++) {
51862feb14eSJun Nie 		size_t j;
51962feb14eSJun Nie 
52062feb14eSJun Nie 		node = icc_node_create(qnodes[i]->id);
52162feb14eSJun Nie 		if (IS_ERR(node)) {
522*0967f1d9SYang Yingliang 			clk_bulk_disable_unprepare(qp->num_intf_clks,
523*0967f1d9SYang Yingliang 						   qp->intf_clks);
52462feb14eSJun Nie 			ret = PTR_ERR(node);
52590ae93d8SJohan Hovold 			goto err_remove_nodes;
52662feb14eSJun Nie 		}
52762feb14eSJun Nie 
52862feb14eSJun Nie 		node->name = qnodes[i]->name;
52962feb14eSJun Nie 		node->data = qnodes[i];
53062feb14eSJun Nie 		icc_node_add(node, provider);
53162feb14eSJun Nie 
53262feb14eSJun Nie 		for (j = 0; j < qnodes[i]->num_links; j++)
53362feb14eSJun Nie 			icc_link_create(node, qnodes[i]->links[j]);
53462feb14eSJun Nie 
53532882f65SKonrad Dybcio 		/* Set QoS registers (we only need to do it once, generally) */
53632882f65SKonrad Dybcio 		if (qnodes[i]->qos.ap_owned &&
53732882f65SKonrad Dybcio 		    qnodes[i]->qos.qos_mode != NOC_QOS_MODE_INVALID) {
53832882f65SKonrad Dybcio 			ret = qcom_icc_qos_set(node);
539*0967f1d9SYang Yingliang 			if (ret) {
540*0967f1d9SYang Yingliang 				clk_bulk_disable_unprepare(qp->num_intf_clks,
541*0967f1d9SYang Yingliang 							   qp->intf_clks);
542*0967f1d9SYang Yingliang 				goto err_remove_nodes;
543*0967f1d9SYang Yingliang 			}
54432882f65SKonrad Dybcio 		}
54532882f65SKonrad Dybcio 
54662feb14eSJun Nie 		data->nodes[i] = node;
54762feb14eSJun Nie 	}
54862feb14eSJun Nie 	data->num_nodes = num_nodes;
54962feb14eSJun Nie 
5502e2113c8SKonrad Dybcio 	clk_bulk_disable_unprepare(qp->num_intf_clks, qp->intf_clks);
5512e2113c8SKonrad Dybcio 
55290ae93d8SJohan Hovold 	ret = icc_provider_register(provider);
55390ae93d8SJohan Hovold 	if (ret)
55490ae93d8SJohan Hovold 		goto err_remove_nodes;
55590ae93d8SJohan Hovold 
55662feb14eSJun Nie 	platform_set_drvdata(pdev, qp);
55762feb14eSJun Nie 
558e39bf297SShawn Guo 	/* Populate child NoC devices if any */
559bc463201SJohan Hovold 	if (of_get_child_count(dev->of_node) > 0) {
560bc463201SJohan Hovold 		ret = of_platform_populate(dev->of_node, NULL, NULL, dev);
561bc463201SJohan Hovold 		if (ret)
56290ae93d8SJohan Hovold 			goto err_deregister_provider;
563bc463201SJohan Hovold 	}
564e39bf297SShawn Guo 
56562feb14eSJun Nie 	return 0;
56690ae93d8SJohan Hovold 
56790ae93d8SJohan Hovold err_deregister_provider:
56890ae93d8SJohan Hovold 	icc_provider_deregister(provider);
56990ae93d8SJohan Hovold err_remove_nodes:
57062feb14eSJun Nie 	icc_nodes_remove(provider);
571*0967f1d9SYang Yingliang err_disable_unprepare_clk:
57211f63efeSKonrad Dybcio 	clk_disable_unprepare(qp->bus_clk);
57362feb14eSJun Nie 
57462feb14eSJun Nie 	return ret;
57562feb14eSJun Nie }
57662feb14eSJun Nie EXPORT_SYMBOL(qnoc_probe);
57762feb14eSJun Nie 
qnoc_remove(struct platform_device * pdev)57862feb14eSJun Nie int qnoc_remove(struct platform_device *pdev)
57962feb14eSJun Nie {
58062feb14eSJun Nie 	struct qcom_icc_provider *qp = platform_get_drvdata(pdev);
58162feb14eSJun Nie 
58290ae93d8SJohan Hovold 	icc_provider_deregister(&qp->provider);
58362feb14eSJun Nie 	icc_nodes_remove(&qp->provider);
58411f63efeSKonrad Dybcio 	clk_disable_unprepare(qp->bus_clk);
5858ef2ca20SUwe Kleine-König 
5868ef2ca20SUwe Kleine-König 	return 0;
58762feb14eSJun Nie }
58862feb14eSJun Nie EXPORT_SYMBOL(qnoc_remove);
589