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

imx.c (6961B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*
      3 * Interconnect framework driver for i.MX SoC
      4 *
      5 * Copyright (c) 2019, BayLibre
      6 * Copyright (c) 2019-2020, NXP
      7 * Author: Alexandre Bailon <abailon@baylibre.com>
      8 * Author: Leonard Crestez <leonard.crestez@nxp.com>
      9 */
     10
     11#include <linux/device.h>
     12#include <linux/interconnect-provider.h>
     13#include <linux/module.h>
     14#include <linux/of.h>
     15#include <linux/of_platform.h>
     16#include <linux/platform_device.h>
     17#include <linux/pm_qos.h>
     18
     19#include "imx.h"
     20
     21/* private icc_node data */
     22struct imx_icc_node {
     23	const struct imx_icc_node_desc *desc;
     24	struct device *qos_dev;
     25	struct dev_pm_qos_request qos_req;
     26};
     27
     28static int imx_icc_get_bw(struct icc_node *node, u32 *avg, u32 *peak)
     29{
     30	*avg = 0;
     31	*peak = 0;
     32
     33	return 0;
     34}
     35
     36static int imx_icc_node_set(struct icc_node *node)
     37{
     38	struct device *dev = node->provider->dev;
     39	struct imx_icc_node *node_data = node->data;
     40	u64 freq;
     41
     42	if (!node_data->qos_dev)
     43		return 0;
     44
     45	freq = (node->avg_bw + node->peak_bw) * node_data->desc->adj->bw_mul;
     46	do_div(freq, node_data->desc->adj->bw_div);
     47	dev_dbg(dev, "node %s device %s avg_bw %ukBps peak_bw %ukBps min_freq %llukHz\n",
     48		node->name, dev_name(node_data->qos_dev),
     49		node->avg_bw, node->peak_bw, freq);
     50
     51	if (freq > S32_MAX) {
     52		dev_err(dev, "%s can't request more than S32_MAX freq\n",
     53				node->name);
     54		return -ERANGE;
     55	}
     56
     57	dev_pm_qos_update_request(&node_data->qos_req, freq);
     58
     59	return 0;
     60}
     61
     62static int imx_icc_set(struct icc_node *src, struct icc_node *dst)
     63{
     64	return imx_icc_node_set(dst);
     65}
     66
     67/* imx_icc_node_destroy() - Destroy an imx icc_node, including private data */
     68static void imx_icc_node_destroy(struct icc_node *node)
     69{
     70	struct imx_icc_node *node_data = node->data;
     71	int ret;
     72
     73	if (dev_pm_qos_request_active(&node_data->qos_req)) {
     74		ret = dev_pm_qos_remove_request(&node_data->qos_req);
     75		if (ret)
     76			dev_warn(node->provider->dev,
     77				 "failed to remove qos request for %s\n",
     78				 dev_name(node_data->qos_dev));
     79	}
     80
     81	put_device(node_data->qos_dev);
     82	icc_node_del(node);
     83	icc_node_destroy(node->id);
     84}
     85
     86static int imx_icc_node_init_qos(struct icc_provider *provider,
     87				 struct icc_node *node)
     88{
     89	struct imx_icc_node *node_data = node->data;
     90	const struct imx_icc_node_adj_desc *adj = node_data->desc->adj;
     91	struct device *dev = provider->dev;
     92	struct device_node *dn = NULL;
     93	struct platform_device *pdev;
     94
     95	if (adj->main_noc) {
     96		node_data->qos_dev = dev;
     97		dev_dbg(dev, "icc node %s[%d] is main noc itself\n",
     98			node->name, node->id);
     99	} else {
    100		dn = of_parse_phandle(dev->of_node, adj->phandle_name, 0);
    101		if (!dn) {
    102			dev_warn(dev, "Failed to parse %s\n",
    103				 adj->phandle_name);
    104			return -ENODEV;
    105		}
    106		/* Allow scaling to be disabled on a per-node basis */
    107		if (!of_device_is_available(dn)) {
    108			dev_warn(dev, "Missing property %s, skip scaling %s\n",
    109				 adj->phandle_name, node->name);
    110			of_node_put(dn);
    111			return 0;
    112		}
    113
    114		pdev = of_find_device_by_node(dn);
    115		of_node_put(dn);
    116		if (!pdev) {
    117			dev_warn(dev, "node %s[%d] missing device for %pOF\n",
    118				 node->name, node->id, dn);
    119			return -EPROBE_DEFER;
    120		}
    121		node_data->qos_dev = &pdev->dev;
    122		dev_dbg(dev, "node %s[%d] has device node %pOF\n",
    123			node->name, node->id, dn);
    124	}
    125
    126	return dev_pm_qos_add_request(node_data->qos_dev,
    127				      &node_data->qos_req,
    128				      DEV_PM_QOS_MIN_FREQUENCY, 0);
    129}
    130
    131static struct icc_node *imx_icc_node_add(struct icc_provider *provider,
    132					 const struct imx_icc_node_desc *node_desc)
    133{
    134	struct device *dev = provider->dev;
    135	struct imx_icc_node *node_data;
    136	struct icc_node *node;
    137	int ret;
    138
    139	node = icc_node_create(node_desc->id);
    140	if (IS_ERR(node)) {
    141		dev_err(dev, "failed to create node %d\n", node_desc->id);
    142		return node;
    143	}
    144
    145	if (node->data) {
    146		dev_err(dev, "already created node %s id=%d\n",
    147			node_desc->name, node_desc->id);
    148		return ERR_PTR(-EEXIST);
    149	}
    150
    151	node_data = devm_kzalloc(dev, sizeof(*node_data), GFP_KERNEL);
    152	if (!node_data) {
    153		icc_node_destroy(node->id);
    154		return ERR_PTR(-ENOMEM);
    155	}
    156
    157	node->name = node_desc->name;
    158	node->data = node_data;
    159	node_data->desc = node_desc;
    160	icc_node_add(node, provider);
    161
    162	if (node_desc->adj) {
    163		ret = imx_icc_node_init_qos(provider, node);
    164		if (ret < 0) {
    165			imx_icc_node_destroy(node);
    166			return ERR_PTR(ret);
    167		}
    168	}
    169
    170	return node;
    171}
    172
    173static void imx_icc_unregister_nodes(struct icc_provider *provider)
    174{
    175	struct icc_node *node, *tmp;
    176
    177	list_for_each_entry_safe(node, tmp, &provider->nodes, node_list)
    178		imx_icc_node_destroy(node);
    179}
    180
    181static int imx_icc_register_nodes(struct icc_provider *provider,
    182				  const struct imx_icc_node_desc *descs,
    183				  int count)
    184{
    185	struct icc_onecell_data *provider_data = provider->data;
    186	int ret;
    187	int i;
    188
    189	for (i = 0; i < count; i++) {
    190		struct icc_node *node;
    191		const struct imx_icc_node_desc *node_desc = &descs[i];
    192		size_t j;
    193
    194		node = imx_icc_node_add(provider, node_desc);
    195		if (IS_ERR(node)) {
    196			ret = dev_err_probe(provider->dev, PTR_ERR(node),
    197					    "failed to add %s\n", node_desc->name);
    198			goto err;
    199		}
    200		provider_data->nodes[node->id] = node;
    201
    202		for (j = 0; j < node_desc->num_links; j++) {
    203			ret = icc_link_create(node, node_desc->links[j]);
    204			if (ret) {
    205				dev_err(provider->dev, "failed to link node %d to %d: %d\n",
    206					node->id, node_desc->links[j], ret);
    207				goto err;
    208			}
    209		}
    210	}
    211
    212	return 0;
    213
    214err:
    215	imx_icc_unregister_nodes(provider);
    216
    217	return ret;
    218}
    219
    220static int get_max_node_id(struct imx_icc_node_desc *nodes, int nodes_count)
    221{
    222	int i, ret = 0;
    223
    224	for (i = 0; i < nodes_count; ++i)
    225		if (nodes[i].id > ret)
    226			ret = nodes[i].id;
    227
    228	return ret;
    229}
    230
    231int imx_icc_register(struct platform_device *pdev,
    232		     struct imx_icc_node_desc *nodes, int nodes_count)
    233{
    234	struct device *dev = &pdev->dev;
    235	struct icc_onecell_data *data;
    236	struct icc_provider *provider;
    237	int max_node_id;
    238	int ret;
    239
    240	/* icc_onecell_data is indexed by node_id, unlike nodes param */
    241	max_node_id = get_max_node_id(nodes, nodes_count);
    242	data = devm_kzalloc(dev, struct_size(data, nodes, max_node_id),
    243			    GFP_KERNEL);
    244	if (!data)
    245		return -ENOMEM;
    246	data->num_nodes = max_node_id;
    247
    248	provider = devm_kzalloc(dev, sizeof(*provider), GFP_KERNEL);
    249	if (!provider)
    250		return -ENOMEM;
    251	provider->set = imx_icc_set;
    252	provider->get_bw = imx_icc_get_bw;
    253	provider->aggregate = icc_std_aggregate;
    254	provider->xlate = of_icc_xlate_onecell;
    255	provider->data = data;
    256	provider->dev = dev->parent;
    257	platform_set_drvdata(pdev, provider);
    258
    259	ret = icc_provider_add(provider);
    260	if (ret) {
    261		dev_err(dev, "error adding interconnect provider: %d\n", ret);
    262		return ret;
    263	}
    264
    265	ret = imx_icc_register_nodes(provider, nodes, nodes_count);
    266	if (ret)
    267		goto provider_del;
    268
    269	return 0;
    270
    271provider_del:
    272	icc_provider_del(provider);
    273	return ret;
    274}
    275EXPORT_SYMBOL_GPL(imx_icc_register);
    276
    277int imx_icc_unregister(struct platform_device *pdev)
    278{
    279	struct icc_provider *provider = platform_get_drvdata(pdev);
    280
    281	imx_icc_unregister_nodes(provider);
    282
    283	return icc_provider_del(provider);
    284}
    285EXPORT_SYMBOL_GPL(imx_icc_unregister);
    286
    287MODULE_LICENSE("GPL v2");