cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

icc-rpm.c (11016B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*
      3 * Copyright (C) 2020 Linaro Ltd
      4 */
      5
      6#include <linux/clk.h>
      7#include <linux/device.h>
      8#include <linux/interconnect-provider.h>
      9#include <linux/io.h>
     10#include <linux/module.h>
     11#include <linux/of_device.h>
     12#include <linux/of_platform.h>
     13#include <linux/platform_device.h>
     14#include <linux/pm_domain.h>
     15#include <linux/regmap.h>
     16#include <linux/slab.h>
     17
     18#include "smd-rpm.h"
     19#include "icc-rpm.h"
     20
     21/* QNOC QoS */
     22#define QNOC_QOS_MCTL_LOWn_ADDR(n)	(0x8 + (n * 0x1000))
     23#define QNOC_QOS_MCTL_DFLT_PRIO_MASK	0x70
     24#define QNOC_QOS_MCTL_DFLT_PRIO_SHIFT	4
     25#define QNOC_QOS_MCTL_URGFWD_EN_MASK	0x8
     26#define QNOC_QOS_MCTL_URGFWD_EN_SHIFT	3
     27
     28/* BIMC QoS */
     29#define M_BKE_REG_BASE(n)		(0x300 + (0x4000 * n))
     30#define M_BKE_EN_ADDR(n)		(M_BKE_REG_BASE(n))
     31#define M_BKE_HEALTH_CFG_ADDR(i, n)	(M_BKE_REG_BASE(n) + 0x40 + (0x4 * i))
     32
     33#define M_BKE_HEALTH_CFG_LIMITCMDS_MASK	0x80000000
     34#define M_BKE_HEALTH_CFG_AREQPRIO_MASK	0x300
     35#define M_BKE_HEALTH_CFG_PRIOLVL_MASK	0x3
     36#define M_BKE_HEALTH_CFG_AREQPRIO_SHIFT	0x8
     37#define M_BKE_HEALTH_CFG_LIMITCMDS_SHIFT 0x1f
     38
     39#define M_BKE_EN_EN_BMASK		0x1
     40
     41/* NoC QoS */
     42#define NOC_QOS_PRIORITYn_ADDR(n)	(0x8 + (n * 0x1000))
     43#define NOC_QOS_PRIORITY_P1_MASK	0xc
     44#define NOC_QOS_PRIORITY_P0_MASK	0x3
     45#define NOC_QOS_PRIORITY_P1_SHIFT	0x2
     46
     47#define NOC_QOS_MODEn_ADDR(n)		(0xc + (n * 0x1000))
     48#define NOC_QOS_MODEn_MASK		0x3
     49
     50static int qcom_icc_set_qnoc_qos(struct icc_node *src, u64 max_bw)
     51{
     52	struct icc_provider *provider = src->provider;
     53	struct qcom_icc_provider *qp = to_qcom_provider(provider);
     54	struct qcom_icc_node *qn = src->data;
     55	struct qcom_icc_qos *qos = &qn->qos;
     56	int rc;
     57
     58	rc = regmap_update_bits(qp->regmap,
     59			qp->qos_offset + QNOC_QOS_MCTL_LOWn_ADDR(qos->qos_port),
     60			QNOC_QOS_MCTL_DFLT_PRIO_MASK,
     61			qos->areq_prio << QNOC_QOS_MCTL_DFLT_PRIO_SHIFT);
     62	if (rc)
     63		return rc;
     64
     65	return regmap_update_bits(qp->regmap,
     66			qp->qos_offset + QNOC_QOS_MCTL_LOWn_ADDR(qos->qos_port),
     67			QNOC_QOS_MCTL_URGFWD_EN_MASK,
     68			!!qos->urg_fwd_en << QNOC_QOS_MCTL_URGFWD_EN_SHIFT);
     69}
     70
     71static int qcom_icc_bimc_set_qos_health(struct qcom_icc_provider *qp,
     72					struct qcom_icc_qos *qos,
     73					int regnum)
     74{
     75	u32 val;
     76	u32 mask;
     77
     78	val = qos->prio_level;
     79	mask = M_BKE_HEALTH_CFG_PRIOLVL_MASK;
     80
     81	val |= qos->areq_prio << M_BKE_HEALTH_CFG_AREQPRIO_SHIFT;
     82	mask |= M_BKE_HEALTH_CFG_AREQPRIO_MASK;
     83
     84	/* LIMITCMDS is not present on M_BKE_HEALTH_3 */
     85	if (regnum != 3) {
     86		val |= qos->limit_commands << M_BKE_HEALTH_CFG_LIMITCMDS_SHIFT;
     87		mask |= M_BKE_HEALTH_CFG_LIMITCMDS_MASK;
     88	}
     89
     90	return regmap_update_bits(qp->regmap,
     91				  qp->qos_offset + M_BKE_HEALTH_CFG_ADDR(regnum, qos->qos_port),
     92				  mask, val);
     93}
     94
     95static int qcom_icc_set_bimc_qos(struct icc_node *src, u64 max_bw)
     96{
     97	struct qcom_icc_provider *qp;
     98	struct qcom_icc_node *qn;
     99	struct icc_provider *provider;
    100	u32 mode = NOC_QOS_MODE_BYPASS;
    101	u32 val = 0;
    102	int i, rc = 0;
    103
    104	qn = src->data;
    105	provider = src->provider;
    106	qp = to_qcom_provider(provider);
    107
    108	if (qn->qos.qos_mode != NOC_QOS_MODE_INVALID)
    109		mode = qn->qos.qos_mode;
    110
    111	/* QoS Priority: The QoS Health parameters are getting considered
    112	 * only if we are NOT in Bypass Mode.
    113	 */
    114	if (mode != NOC_QOS_MODE_BYPASS) {
    115		for (i = 3; i >= 0; i--) {
    116			rc = qcom_icc_bimc_set_qos_health(qp,
    117							  &qn->qos, i);
    118			if (rc)
    119				return rc;
    120		}
    121
    122		/* Set BKE_EN to 1 when Fixed, Regulator or Limiter Mode */
    123		val = 1;
    124	}
    125
    126	return regmap_update_bits(qp->regmap,
    127				  qp->qos_offset + M_BKE_EN_ADDR(qn->qos.qos_port),
    128				  M_BKE_EN_EN_BMASK, val);
    129}
    130
    131static int qcom_icc_noc_set_qos_priority(struct qcom_icc_provider *qp,
    132					 struct qcom_icc_qos *qos)
    133{
    134	u32 val;
    135	int rc;
    136
    137	/* Must be updated one at a time, P1 first, P0 last */
    138	val = qos->areq_prio << NOC_QOS_PRIORITY_P1_SHIFT;
    139	rc = regmap_update_bits(qp->regmap,
    140				qp->qos_offset + NOC_QOS_PRIORITYn_ADDR(qos->qos_port),
    141				NOC_QOS_PRIORITY_P1_MASK, val);
    142	if (rc)
    143		return rc;
    144
    145	return regmap_update_bits(qp->regmap,
    146				  qp->qos_offset + NOC_QOS_PRIORITYn_ADDR(qos->qos_port),
    147				  NOC_QOS_PRIORITY_P0_MASK, qos->prio_level);
    148}
    149
    150static int qcom_icc_set_noc_qos(struct icc_node *src, u64 max_bw)
    151{
    152	struct qcom_icc_provider *qp;
    153	struct qcom_icc_node *qn;
    154	struct icc_provider *provider;
    155	u32 mode = NOC_QOS_MODE_BYPASS;
    156	int rc = 0;
    157
    158	qn = src->data;
    159	provider = src->provider;
    160	qp = to_qcom_provider(provider);
    161
    162	if (qn->qos.qos_port < 0) {
    163		dev_dbg(src->provider->dev,
    164			"NoC QoS: Skipping %s: vote aggregated on parent.\n",
    165			qn->name);
    166		return 0;
    167	}
    168
    169	if (qn->qos.qos_mode != NOC_QOS_MODE_INVALID)
    170		mode = qn->qos.qos_mode;
    171
    172	if (mode == NOC_QOS_MODE_FIXED) {
    173		dev_dbg(src->provider->dev, "NoC QoS: %s: Set Fixed mode\n",
    174			qn->name);
    175		rc = qcom_icc_noc_set_qos_priority(qp, &qn->qos);
    176		if (rc)
    177			return rc;
    178	} else if (mode == NOC_QOS_MODE_BYPASS) {
    179		dev_dbg(src->provider->dev, "NoC QoS: %s: Set Bypass mode\n",
    180			qn->name);
    181	}
    182
    183	return regmap_update_bits(qp->regmap,
    184				  qp->qos_offset + NOC_QOS_MODEn_ADDR(qn->qos.qos_port),
    185				  NOC_QOS_MODEn_MASK, mode);
    186}
    187
    188static int qcom_icc_qos_set(struct icc_node *node, u64 sum_bw)
    189{
    190	struct qcom_icc_provider *qp = to_qcom_provider(node->provider);
    191	struct qcom_icc_node *qn = node->data;
    192
    193	dev_dbg(node->provider->dev, "Setting QoS for %s\n", qn->name);
    194
    195	switch (qp->type) {
    196	case QCOM_ICC_BIMC:
    197		return qcom_icc_set_bimc_qos(node, sum_bw);
    198	case QCOM_ICC_QNOC:
    199		return qcom_icc_set_qnoc_qos(node, sum_bw);
    200	default:
    201		return qcom_icc_set_noc_qos(node, sum_bw);
    202	}
    203}
    204
    205static int qcom_icc_rpm_set(int mas_rpm_id, int slv_rpm_id, u64 sum_bw)
    206{
    207	int ret = 0;
    208
    209	if (mas_rpm_id != -1) {
    210		ret = qcom_icc_rpm_smd_send(QCOM_SMD_RPM_ACTIVE_STATE,
    211					    RPM_BUS_MASTER_REQ,
    212					    mas_rpm_id,
    213					    sum_bw);
    214		if (ret) {
    215			pr_err("qcom_icc_rpm_smd_send mas %d error %d\n",
    216			       mas_rpm_id, ret);
    217			return ret;
    218		}
    219	}
    220
    221	if (slv_rpm_id != -1) {
    222		ret = qcom_icc_rpm_smd_send(QCOM_SMD_RPM_ACTIVE_STATE,
    223					    RPM_BUS_SLAVE_REQ,
    224					    slv_rpm_id,
    225					    sum_bw);
    226		if (ret) {
    227			pr_err("qcom_icc_rpm_smd_send slv %d error %d\n",
    228			       slv_rpm_id, ret);
    229			return ret;
    230		}
    231	}
    232
    233	return ret;
    234}
    235
    236static int qcom_icc_set(struct icc_node *src, struct icc_node *dst)
    237{
    238	struct qcom_icc_provider *qp;
    239	struct qcom_icc_node *qn;
    240	struct icc_provider *provider;
    241	struct icc_node *n;
    242	u64 sum_bw;
    243	u64 max_peak_bw;
    244	u64 rate;
    245	u32 agg_avg = 0;
    246	u32 agg_peak = 0;
    247	int ret, i;
    248
    249	qn = src->data;
    250	provider = src->provider;
    251	qp = to_qcom_provider(provider);
    252
    253	list_for_each_entry(n, &provider->nodes, node_list)
    254		provider->aggregate(n, 0, n->avg_bw, n->peak_bw,
    255				    &agg_avg, &agg_peak);
    256
    257	sum_bw = icc_units_to_bps(agg_avg);
    258	max_peak_bw = icc_units_to_bps(agg_peak);
    259
    260	if (!qn->qos.ap_owned) {
    261		/* send bandwidth request message to the RPM processor */
    262		ret = qcom_icc_rpm_set(qn->mas_rpm_id, qn->slv_rpm_id, sum_bw);
    263		if (ret)
    264			return ret;
    265	} else if (qn->qos.qos_mode != -1) {
    266		/* set bandwidth directly from the AP */
    267		ret = qcom_icc_qos_set(src, sum_bw);
    268		if (ret)
    269			return ret;
    270	}
    271
    272	rate = max(sum_bw, max_peak_bw);
    273
    274	do_div(rate, qn->buswidth);
    275	rate = min_t(u64, rate, LONG_MAX);
    276
    277	for (i = 0; i < qp->num_clks; i++) {
    278		if (qp->bus_clk_rate[i] == rate)
    279			continue;
    280
    281		ret = clk_set_rate(qp->bus_clks[i].clk, rate);
    282		if (ret) {
    283			pr_err("%s clk_set_rate error: %d\n",
    284			       qp->bus_clks[i].id, ret);
    285			return ret;
    286		}
    287		qp->bus_clk_rate[i] = rate;
    288	}
    289
    290	return 0;
    291}
    292
    293static const char * const bus_clocks[] = {
    294	"bus", "bus_a",
    295};
    296
    297int qnoc_probe(struct platform_device *pdev)
    298{
    299	struct device *dev = &pdev->dev;
    300	const struct qcom_icc_desc *desc;
    301	struct icc_onecell_data *data;
    302	struct icc_provider *provider;
    303	struct qcom_icc_node * const *qnodes;
    304	struct qcom_icc_provider *qp;
    305	struct icc_node *node;
    306	size_t num_nodes, i;
    307	const char * const *cds;
    308	int cd_num;
    309	int ret;
    310
    311	/* wait for the RPM proxy */
    312	if (!qcom_icc_rpm_smd_available())
    313		return -EPROBE_DEFER;
    314
    315	desc = of_device_get_match_data(dev);
    316	if (!desc)
    317		return -EINVAL;
    318
    319	qnodes = desc->nodes;
    320	num_nodes = desc->num_nodes;
    321
    322	if (desc->num_clocks) {
    323		cds = desc->clocks;
    324		cd_num = desc->num_clocks;
    325	} else {
    326		cds = bus_clocks;
    327		cd_num = ARRAY_SIZE(bus_clocks);
    328	}
    329
    330	qp = devm_kzalloc(dev, struct_size(qp, bus_clks, cd_num), GFP_KERNEL);
    331	if (!qp)
    332		return -ENOMEM;
    333
    334	qp->bus_clk_rate = devm_kcalloc(dev, cd_num, sizeof(*qp->bus_clk_rate),
    335					GFP_KERNEL);
    336	if (!qp->bus_clk_rate)
    337		return -ENOMEM;
    338
    339	data = devm_kzalloc(dev, struct_size(data, nodes, num_nodes),
    340			    GFP_KERNEL);
    341	if (!data)
    342		return -ENOMEM;
    343
    344	for (i = 0; i < cd_num; i++)
    345		qp->bus_clks[i].id = cds[i];
    346	qp->num_clks = cd_num;
    347
    348	qp->type = desc->type;
    349	qp->qos_offset = desc->qos_offset;
    350
    351	if (desc->regmap_cfg) {
    352		struct resource *res;
    353		void __iomem *mmio;
    354
    355		res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
    356		if (!res) {
    357			/* Try parent's regmap */
    358			qp->regmap = dev_get_regmap(dev->parent, NULL);
    359			if (qp->regmap)
    360				goto regmap_done;
    361			return -ENODEV;
    362		}
    363
    364		mmio = devm_ioremap_resource(dev, res);
    365
    366		if (IS_ERR(mmio)) {
    367			dev_err(dev, "Cannot ioremap interconnect bus resource\n");
    368			return PTR_ERR(mmio);
    369		}
    370
    371		qp->regmap = devm_regmap_init_mmio(dev, mmio, desc->regmap_cfg);
    372		if (IS_ERR(qp->regmap)) {
    373			dev_err(dev, "Cannot regmap interconnect bus resource\n");
    374			return PTR_ERR(qp->regmap);
    375		}
    376	}
    377
    378regmap_done:
    379	ret = devm_clk_bulk_get(dev, qp->num_clks, qp->bus_clks);
    380	if (ret)
    381		return ret;
    382
    383	ret = clk_bulk_prepare_enable(qp->num_clks, qp->bus_clks);
    384	if (ret)
    385		return ret;
    386
    387	if (desc->has_bus_pd) {
    388		ret = dev_pm_domain_attach(dev, true);
    389		if (ret)
    390			return ret;
    391	}
    392
    393	provider = &qp->provider;
    394	INIT_LIST_HEAD(&provider->nodes);
    395	provider->dev = dev;
    396	provider->set = qcom_icc_set;
    397	provider->aggregate = icc_std_aggregate;
    398	provider->xlate = of_icc_xlate_onecell;
    399	provider->data = data;
    400
    401	ret = icc_provider_add(provider);
    402	if (ret) {
    403		dev_err(dev, "error adding interconnect provider: %d\n", ret);
    404		clk_bulk_disable_unprepare(qp->num_clks, qp->bus_clks);
    405		return ret;
    406	}
    407
    408	for (i = 0; i < num_nodes; i++) {
    409		size_t j;
    410
    411		node = icc_node_create(qnodes[i]->id);
    412		if (IS_ERR(node)) {
    413			ret = PTR_ERR(node);
    414			goto err;
    415		}
    416
    417		node->name = qnodes[i]->name;
    418		node->data = qnodes[i];
    419		icc_node_add(node, provider);
    420
    421		for (j = 0; j < qnodes[i]->num_links; j++)
    422			icc_link_create(node, qnodes[i]->links[j]);
    423
    424		data->nodes[i] = node;
    425	}
    426	data->num_nodes = num_nodes;
    427
    428	platform_set_drvdata(pdev, qp);
    429
    430	/* Populate child NoC devices if any */
    431	if (of_get_child_count(dev->of_node) > 0)
    432		return of_platform_populate(dev->of_node, NULL, NULL, dev);
    433
    434	return 0;
    435err:
    436	icc_nodes_remove(provider);
    437	clk_bulk_disable_unprepare(qp->num_clks, qp->bus_clks);
    438	icc_provider_del(provider);
    439
    440	return ret;
    441}
    442EXPORT_SYMBOL(qnoc_probe);
    443
    444int qnoc_remove(struct platform_device *pdev)
    445{
    446	struct qcom_icc_provider *qp = platform_get_drvdata(pdev);
    447
    448	icc_nodes_remove(&qp->provider);
    449	clk_bulk_disable_unprepare(qp->num_clks, qp->bus_clks);
    450	return icc_provider_del(&qp->provider);
    451}
    452EXPORT_SYMBOL(qnoc_remove);