Commit 86954c98 authored by Rafael J. Wysocki's avatar Rafael J. Wysocki
Browse files
Pull devfreq updates for v5.8 from Chanwoo Choi:

"1. Update devfreq core
 - Use lockdep function for checking mutex state instead of manual checking.
 - Replace strncpy with strscpy to fix the compile warning.

 2. Update devfreq driver
 - Add new imx-bus.c devfreq driver for controlling the bus frequenncy
   and it registers the imx interconnect device which indicates the imx-bus.c
   as the parent device. This relation make the connection between imx-bus.c
   and imx interconnect nodes. In result, the imx-bus.c devfreq driver handles
   the DEV_PM_QOS_MIN_FREQUENCY requirements from imx interconnect nodes
   in order to support the minimum bus bandwidth of interconnect nodes.

 - Delete unneed error message and update the boosting on tegra30-devfreq.c."

* tag 'devfreq-next-for-5.8' of git://git.kernel.org/pub/scm/linux/kernel/git/chanwoo/linux:
  PM / devfreq: Use lockdep asserts instead of manual checks for locked mutex
  PM / devfreq: imx-bus: Fix inconsistent IS_ERR and PTR_ERR
  PM / devfreq: Replace strncpy with strscpy
  PM / devfreq: imx: Register interconnect device
  PM / devfreq: Add generic imx bus scaling driver
  PM / devfreq: tegra30: Delete an error message in tegra_devfreq_probe()
  PM / devfreq: tegra30: Make CPUFreq notifier to take into account boosting
parents 9cb1fd0e 8fc0e48e
Loading
Loading
Loading
Loading
+8 −0
Original line number Original line Diff line number Diff line
@@ -91,6 +91,14 @@ config ARM_EXYNOS_BUS_DEVFREQ
	  and adjusts the operating frequencies and voltages with OPP support.
	  and adjusts the operating frequencies and voltages with OPP support.
	  This does not yet operate with optimal voltages.
	  This does not yet operate with optimal voltages.


config ARM_IMX_BUS_DEVFREQ
	tristate "i.MX Generic Bus DEVFREQ Driver"
	depends on ARCH_MXC || COMPILE_TEST
	select DEVFREQ_GOV_USERSPACE
	help
	  This adds the generic DEVFREQ driver for i.MX interconnects. It
	  allows adjusting NIC/NOC frequency.

config ARM_IMX8M_DDRC_DEVFREQ
config ARM_IMX8M_DDRC_DEVFREQ
	tristate "i.MX8M DDRC DEVFREQ Driver"
	tristate "i.MX8M DDRC DEVFREQ Driver"
	depends on (ARCH_MXC && HAVE_ARM_SMCCC) || \
	depends on (ARCH_MXC && HAVE_ARM_SMCCC) || \
+1 −0
Original line number Original line Diff line number Diff line
@@ -9,6 +9,7 @@ obj-$(CONFIG_DEVFREQ_GOV_PASSIVE) += governor_passive.o


# DEVFREQ Drivers
# DEVFREQ Drivers
obj-$(CONFIG_ARM_EXYNOS_BUS_DEVFREQ)	+= exynos-bus.o
obj-$(CONFIG_ARM_EXYNOS_BUS_DEVFREQ)	+= exynos-bus.o
obj-$(CONFIG_ARM_IMX_BUS_DEVFREQ)	+= imx-bus.o
obj-$(CONFIG_ARM_IMX8M_DDRC_DEVFREQ)	+= imx8m-ddrc.o
obj-$(CONFIG_ARM_IMX8M_DDRC_DEVFREQ)	+= imx8m-ddrc.o
obj-$(CONFIG_ARM_RK3399_DMC_DEVFREQ)	+= rk3399_dmc.o
obj-$(CONFIG_ARM_RK3399_DMC_DEVFREQ)	+= rk3399_dmc.o
obj-$(CONFIG_ARM_TEGRA_DEVFREQ)		+= tegra30-devfreq.o
obj-$(CONFIG_ARM_TEGRA_DEVFREQ)		+= tegra30-devfreq.o
+8 −11
Original line number Original line Diff line number Diff line
@@ -60,12 +60,12 @@ static struct devfreq *find_device_devfreq(struct device *dev)
{
{
	struct devfreq *tmp_devfreq;
	struct devfreq *tmp_devfreq;


	lockdep_assert_held(&devfreq_list_lock);

	if (IS_ERR_OR_NULL(dev)) {
	if (IS_ERR_OR_NULL(dev)) {
		pr_err("DEVFREQ: %s: Invalid parameters\n", __func__);
		pr_err("DEVFREQ: %s: Invalid parameters\n", __func__);
		return ERR_PTR(-EINVAL);
		return ERR_PTR(-EINVAL);
	}
	}
	WARN(!mutex_is_locked(&devfreq_list_lock),
	     "devfreq_list_lock must be locked.");


	list_for_each_entry(tmp_devfreq, &devfreq_list, node) {
	list_for_each_entry(tmp_devfreq, &devfreq_list, node) {
		if (tmp_devfreq->dev.parent == dev)
		if (tmp_devfreq->dev.parent == dev)
@@ -258,12 +258,12 @@ static struct devfreq_governor *find_devfreq_governor(const char *name)
{
{
	struct devfreq_governor *tmp_governor;
	struct devfreq_governor *tmp_governor;


	lockdep_assert_held(&devfreq_list_lock);

	if (IS_ERR_OR_NULL(name)) {
	if (IS_ERR_OR_NULL(name)) {
		pr_err("DEVFREQ: %s: Invalid parameters\n", __func__);
		pr_err("DEVFREQ: %s: Invalid parameters\n", __func__);
		return ERR_PTR(-EINVAL);
		return ERR_PTR(-EINVAL);
	}
	}
	WARN(!mutex_is_locked(&devfreq_list_lock),
	     "devfreq_list_lock must be locked.");


	list_for_each_entry(tmp_governor, &devfreq_governor_list, node) {
	list_for_each_entry(tmp_governor, &devfreq_governor_list, node) {
		if (!strncmp(tmp_governor->name, name, DEVFREQ_NAME_LEN))
		if (!strncmp(tmp_governor->name, name, DEVFREQ_NAME_LEN))
@@ -289,12 +289,12 @@ static struct devfreq_governor *try_then_request_governor(const char *name)
	struct devfreq_governor *governor;
	struct devfreq_governor *governor;
	int err = 0;
	int err = 0;


	lockdep_assert_held(&devfreq_list_lock);

	if (IS_ERR_OR_NULL(name)) {
	if (IS_ERR_OR_NULL(name)) {
		pr_err("DEVFREQ: %s: Invalid parameters\n", __func__);
		pr_err("DEVFREQ: %s: Invalid parameters\n", __func__);
		return ERR_PTR(-EINVAL);
		return ERR_PTR(-EINVAL);
	}
	}
	WARN(!mutex_is_locked(&devfreq_list_lock),
	     "devfreq_list_lock must be locked.");


	governor = find_devfreq_governor(name);
	governor = find_devfreq_governor(name);
	if (IS_ERR(governor)) {
	if (IS_ERR(governor)) {
@@ -392,10 +392,7 @@ int update_devfreq(struct devfreq *devfreq)
	int err = 0;
	int err = 0;
	u32 flags = 0;
	u32 flags = 0;


	if (!mutex_is_locked(&devfreq->lock)) {
	lockdep_assert_held(&devfreq->lock);
		WARN(true, "devfreq->lock must be locked by the caller.\n");
		return -EINVAL;
	}


	if (!devfreq->governor)
	if (!devfreq->governor)
		return -EINVAL;
		return -EINVAL;
@@ -768,7 +765,7 @@ struct devfreq *devfreq_add_device(struct device *dev,
	devfreq->dev.release = devfreq_dev_release;
	devfreq->dev.release = devfreq_dev_release;
	INIT_LIST_HEAD(&devfreq->node);
	INIT_LIST_HEAD(&devfreq->node);
	devfreq->profile = profile;
	devfreq->profile = profile;
	strncpy(devfreq->governor_name, governor_name, DEVFREQ_NAME_LEN);
	strscpy(devfreq->governor_name, governor_name, DEVFREQ_NAME_LEN);
	devfreq->previous_freq = profile->initial_freq;
	devfreq->previous_freq = profile->initial_freq;
	devfreq->last_status.current_frequency = profile->initial_freq;
	devfreq->last_status.current_frequency = profile->initial_freq;
	devfreq->data = data;
	devfreq->data = data;
+179 −0
Original line number Original line Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0
/*
 * Copyright 2019 NXP
 */

#include <linux/clk.h>
#include <linux/devfreq.h>
#include <linux/device.h>
#include <linux/module.h>
#include <linux/of_device.h>
#include <linux/pm_opp.h>
#include <linux/platform_device.h>
#include <linux/slab.h>

struct imx_bus {
	struct devfreq_dev_profile profile;
	struct devfreq *devfreq;
	struct clk *clk;
	struct platform_device *icc_pdev;
};

static int imx_bus_target(struct device *dev,
		unsigned long *freq, u32 flags)
{
	struct dev_pm_opp *new_opp;
	int ret;

	new_opp = devfreq_recommended_opp(dev, freq, flags);
	if (IS_ERR(new_opp)) {
		ret = PTR_ERR(new_opp);
		dev_err(dev, "failed to get recommended opp: %d\n", ret);
		return ret;
	}
	dev_pm_opp_put(new_opp);

	return dev_pm_opp_set_rate(dev, *freq);
}

static int imx_bus_get_cur_freq(struct device *dev, unsigned long *freq)
{
	struct imx_bus *priv = dev_get_drvdata(dev);

	*freq = clk_get_rate(priv->clk);

	return 0;
}

static int imx_bus_get_dev_status(struct device *dev,
		struct devfreq_dev_status *stat)
{
	struct imx_bus *priv = dev_get_drvdata(dev);

	stat->busy_time = 0;
	stat->total_time = 0;
	stat->current_frequency = clk_get_rate(priv->clk);

	return 0;
}

static void imx_bus_exit(struct device *dev)
{
	struct imx_bus *priv = dev_get_drvdata(dev);

	dev_pm_opp_of_remove_table(dev);
	platform_device_unregister(priv->icc_pdev);
}

/* imx_bus_init_icc() - register matching icc provider if required */
static int imx_bus_init_icc(struct device *dev)
{
	struct imx_bus *priv = dev_get_drvdata(dev);
	const char *icc_driver_name;

	if (!of_get_property(dev->of_node, "#interconnect-cells", 0))
		return 0;
	if (!IS_ENABLED(CONFIG_INTERCONNECT_IMX)) {
		dev_warn(dev, "imx interconnect drivers disabled\n");
		return 0;
	}

	icc_driver_name = of_device_get_match_data(dev);
	if (!icc_driver_name) {
		dev_err(dev, "unknown interconnect driver\n");
		return 0;
	}

	priv->icc_pdev = platform_device_register_data(
			dev, icc_driver_name, -1, NULL, 0);
	if (IS_ERR(priv->icc_pdev)) {
		dev_err(dev, "failed to register icc provider %s: %ld\n",
				icc_driver_name, PTR_ERR(priv->icc_pdev));
		return PTR_ERR(priv->icc_pdev);
	}

	return 0;
}

static int imx_bus_probe(struct platform_device *pdev)
{
	struct device *dev = &pdev->dev;
	struct imx_bus *priv;
	const char *gov = DEVFREQ_GOV_USERSPACE;
	int ret;

	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
	if (!priv)
		return -ENOMEM;

	/*
	 * Fetch the clock to adjust but don't explicitly enable.
	 *
	 * For imx bus clock clk_set_rate is safe no matter if the clock is on
	 * or off and some peripheral side-buses might be off unless enabled by
	 * drivers for devices on those specific buses.
	 *
	 * Rate adjustment on a disabled bus clock just takes effect later.
	 */
	priv->clk = devm_clk_get(dev, NULL);
	if (IS_ERR(priv->clk)) {
		ret = PTR_ERR(priv->clk);
		dev_err(dev, "failed to fetch clk: %d\n", ret);
		return ret;
	}
	platform_set_drvdata(pdev, priv);

	ret = dev_pm_opp_of_add_table(dev);
	if (ret < 0) {
		dev_err(dev, "failed to get OPP table\n");
		return ret;
	}

	priv->profile.polling_ms = 1000;
	priv->profile.target = imx_bus_target;
	priv->profile.get_dev_status = imx_bus_get_dev_status;
	priv->profile.exit = imx_bus_exit;
	priv->profile.get_cur_freq = imx_bus_get_cur_freq;
	priv->profile.initial_freq = clk_get_rate(priv->clk);

	priv->devfreq = devm_devfreq_add_device(dev, &priv->profile,
						gov, NULL);
	if (IS_ERR(priv->devfreq)) {
		ret = PTR_ERR(priv->devfreq);
		dev_err(dev, "failed to add devfreq device: %d\n", ret);
		goto err;
	}

	ret = imx_bus_init_icc(dev);
	if (ret)
		goto err;

	return 0;

err:
	dev_pm_opp_of_remove_table(dev);
	return ret;
}

static const struct of_device_id imx_bus_of_match[] = {
	{ .compatible = "fsl,imx8mq-noc", .data = "imx8mq-interconnect", },
	{ .compatible = "fsl,imx8mm-noc", .data = "imx8mm-interconnect", },
	{ .compatible = "fsl,imx8mn-noc", .data = "imx8mn-interconnect", },
	{ .compatible = "fsl,imx8m-noc", },
	{ .compatible = "fsl,imx8m-nic", },
	{ /* sentinel */ },
};
MODULE_DEVICE_TABLE(of, imx_bus_of_match);

static struct platform_driver imx_bus_platdrv = {
	.probe		= imx_bus_probe,
	.driver = {
		.name	= "imx-bus-devfreq",
		.of_match_table = of_match_ptr(imx_bus_of_match),
	},
};
module_platform_driver(imx_bus_platdrv);

MODULE_DESCRIPTION("Generic i.MX bus frequency scaling driver");
MODULE_AUTHOR("Leonard Crestez <leonard.crestez@nxp.com>");
MODULE_LICENSE("GPL v2");
+3 −4
Original line number Original line Diff line number Diff line
@@ -420,7 +420,7 @@ tegra_actmon_cpufreq_contribution(struct tegra_devfreq *tegra,


	static_cpu_emc_freq = actmon_cpu_to_emc_rate(tegra, cpu_freq);
	static_cpu_emc_freq = actmon_cpu_to_emc_rate(tegra, cpu_freq);


	if (dev_freq >= static_cpu_emc_freq)
	if (dev_freq + actmon_dev->boost_freq >= static_cpu_emc_freq)
		return 0;
		return 0;


	return static_cpu_emc_freq;
	return static_cpu_emc_freq;
@@ -807,10 +807,9 @@ static int tegra_devfreq_probe(struct platform_device *pdev)
	}
	}


	err = platform_get_irq(pdev, 0);
	err = platform_get_irq(pdev, 0);
	if (err < 0) {
	if (err < 0)
		dev_err(&pdev->dev, "Failed to get IRQ: %d\n", err);
		return err;
		return err;
	}

	tegra->irq = err;
	tegra->irq = err;


	irq_set_status_flags(tegra->irq, IRQ_NOAUTOEN);
	irq_set_status_flags(tegra->irq, IRQ_NOAUTOEN);