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-bus.c (4132B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*
      3 * Copyright 2019 NXP
      4 */
      5
      6#include <linux/clk.h>
      7#include <linux/devfreq.h>
      8#include <linux/device.h>
      9#include <linux/module.h>
     10#include <linux/of_device.h>
     11#include <linux/pm_opp.h>
     12#include <linux/platform_device.h>
     13#include <linux/slab.h>
     14
     15struct imx_bus {
     16	struct devfreq_dev_profile profile;
     17	struct devfreq *devfreq;
     18	struct clk *clk;
     19	struct platform_device *icc_pdev;
     20};
     21
     22static int imx_bus_target(struct device *dev,
     23		unsigned long *freq, u32 flags)
     24{
     25	struct dev_pm_opp *new_opp;
     26	int ret;
     27
     28	new_opp = devfreq_recommended_opp(dev, freq, flags);
     29	if (IS_ERR(new_opp)) {
     30		ret = PTR_ERR(new_opp);
     31		dev_err(dev, "failed to get recommended opp: %d\n", ret);
     32		return ret;
     33	}
     34	dev_pm_opp_put(new_opp);
     35
     36	return dev_pm_opp_set_rate(dev, *freq);
     37}
     38
     39static int imx_bus_get_cur_freq(struct device *dev, unsigned long *freq)
     40{
     41	struct imx_bus *priv = dev_get_drvdata(dev);
     42
     43	*freq = clk_get_rate(priv->clk);
     44
     45	return 0;
     46}
     47
     48static void imx_bus_exit(struct device *dev)
     49{
     50	struct imx_bus *priv = dev_get_drvdata(dev);
     51
     52	dev_pm_opp_of_remove_table(dev);
     53	platform_device_unregister(priv->icc_pdev);
     54}
     55
     56/* imx_bus_init_icc() - register matching icc provider if required */
     57static int imx_bus_init_icc(struct device *dev)
     58{
     59	struct imx_bus *priv = dev_get_drvdata(dev);
     60	const char *icc_driver_name;
     61
     62	if (!of_get_property(dev->of_node, "#interconnect-cells", 0))
     63		return 0;
     64	if (!IS_ENABLED(CONFIG_INTERCONNECT_IMX)) {
     65		dev_warn(dev, "imx interconnect drivers disabled\n");
     66		return 0;
     67	}
     68
     69	icc_driver_name = of_device_get_match_data(dev);
     70	if (!icc_driver_name) {
     71		dev_err(dev, "unknown interconnect driver\n");
     72		return 0;
     73	}
     74
     75	priv->icc_pdev = platform_device_register_data(
     76			dev, icc_driver_name, -1, NULL, 0);
     77	if (IS_ERR(priv->icc_pdev)) {
     78		dev_err(dev, "failed to register icc provider %s: %ld\n",
     79				icc_driver_name, PTR_ERR(priv->icc_pdev));
     80		return PTR_ERR(priv->icc_pdev);
     81	}
     82
     83	return 0;
     84}
     85
     86static int imx_bus_probe(struct platform_device *pdev)
     87{
     88	struct device *dev = &pdev->dev;
     89	struct imx_bus *priv;
     90	const char *gov = DEVFREQ_GOV_USERSPACE;
     91	int ret;
     92
     93	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
     94	if (!priv)
     95		return -ENOMEM;
     96
     97	/*
     98	 * Fetch the clock to adjust but don't explicitly enable.
     99	 *
    100	 * For imx bus clock clk_set_rate is safe no matter if the clock is on
    101	 * or off and some peripheral side-buses might be off unless enabled by
    102	 * drivers for devices on those specific buses.
    103	 *
    104	 * Rate adjustment on a disabled bus clock just takes effect later.
    105	 */
    106	priv->clk = devm_clk_get(dev, NULL);
    107	if (IS_ERR(priv->clk)) {
    108		ret = PTR_ERR(priv->clk);
    109		dev_err(dev, "failed to fetch clk: %d\n", ret);
    110		return ret;
    111	}
    112	platform_set_drvdata(pdev, priv);
    113
    114	ret = dev_pm_opp_of_add_table(dev);
    115	if (ret < 0) {
    116		dev_err(dev, "failed to get OPP table\n");
    117		return ret;
    118	}
    119
    120	priv->profile.target = imx_bus_target;
    121	priv->profile.exit = imx_bus_exit;
    122	priv->profile.get_cur_freq = imx_bus_get_cur_freq;
    123	priv->profile.initial_freq = clk_get_rate(priv->clk);
    124
    125	priv->devfreq = devm_devfreq_add_device(dev, &priv->profile,
    126						gov, NULL);
    127	if (IS_ERR(priv->devfreq)) {
    128		ret = PTR_ERR(priv->devfreq);
    129		dev_err(dev, "failed to add devfreq device: %d\n", ret);
    130		goto err;
    131	}
    132
    133	ret = imx_bus_init_icc(dev);
    134	if (ret)
    135		goto err;
    136
    137	return 0;
    138
    139err:
    140	dev_pm_opp_of_remove_table(dev);
    141	return ret;
    142}
    143
    144static const struct of_device_id imx_bus_of_match[] = {
    145	{ .compatible = "fsl,imx8mq-noc", .data = "imx8mq-interconnect", },
    146	{ .compatible = "fsl,imx8mm-noc", .data = "imx8mm-interconnect", },
    147	{ .compatible = "fsl,imx8mn-noc", .data = "imx8mn-interconnect", },
    148	{ .compatible = "fsl,imx8m-noc", },
    149	{ .compatible = "fsl,imx8m-nic", },
    150	{ /* sentinel */ },
    151};
    152MODULE_DEVICE_TABLE(of, imx_bus_of_match);
    153
    154static struct platform_driver imx_bus_platdrv = {
    155	.probe		= imx_bus_probe,
    156	.driver = {
    157		.name	= "imx-bus-devfreq",
    158		.of_match_table = imx_bus_of_match,
    159	},
    160};
    161module_platform_driver(imx_bus_platdrv);
    162
    163MODULE_DESCRIPTION("Generic i.MX bus frequency scaling driver");
    164MODULE_AUTHOR("Leonard Crestez <leonard.crestez@nxp.com>");
    165MODULE_LICENSE("GPL v2");