Commit 031f469e authored by Rafael J. Wysocki's avatar Rafael J. Wysocki
Browse files

Merge branch 'pm-devfreq'

* pm-devfreq: (28 commits)
  PM / devfreq: passive: fix compiler warning
  PM / devfreq: passive: Use non-devm notifiers
  PM / devfreq: exynos-bus: Convert to use dev_pm_opp_set_rate()
  PM / devfreq: exynos-bus: Correct clock enable sequence
  PM / devfreq: Correct devm_devfreq_remove_device() documentation
  PM / devfreq: events: extend events by type of counted data
  PM / devfreq: exynos-events: change matching code during probe
  PM / devfreq: tegra20: add COMMON_CLK dependency
  PM / devfreq: events: add Exynos PPMU new events
  PM / devfreq: Fix kernel oops on governor module load
  PM / devfreq: rk3399_dmc: Fix spelling typo
  PM / devfreq: Fix spelling typo
  PM / devfreq: Introduce driver for NVIDIA Tegra20
  PM / devfreq: tegra: Rename tegra-devfreq.c to tegra30-devfreq.c
  PM / devfreq: tegra: Enable COMPILE_TEST for the driver
  PM / devfreq: tegra: Support Tegra30
  PM / devfreq: tegra: Reconfigure hardware on governor's restart
  PM / devfreq: tegra: Move governor registration to driver's probe
  PM / devfreq: tegra: Mark ACTMON's governor as immutable
  PM / devfreq: tegra: Avoid inconsistency of current frequency value
  ...
parents ca61a72a da9cd91c
Loading
Loading
Loading
Loading
+16 −3
Original line number Diff line number Diff line
@@ -93,15 +93,28 @@ config ARM_EXYNOS_BUS_DEVFREQ
	  This does not yet operate with optimal voltages.

config ARM_TEGRA_DEVFREQ
	tristate "Tegra DEVFREQ Driver"
	depends on ARCH_TEGRA_124_SOC
	select DEVFREQ_GOV_SIMPLE_ONDEMAND
	tristate "NVIDIA Tegra30/114/124/210 DEVFREQ Driver"
	depends on ARCH_TEGRA_3x_SOC || ARCH_TEGRA_114_SOC || \
		ARCH_TEGRA_132_SOC || ARCH_TEGRA_124_SOC || \
		ARCH_TEGRA_210_SOC || \
		COMPILE_TEST
	select PM_OPP
	help
	  This adds the DEVFREQ driver for the Tegra family of SoCs.
	  It reads ACTMON counters of memory controllers and adjusts the
	  operating frequencies and voltages with OPP support.

config ARM_TEGRA20_DEVFREQ
	tristate "NVIDIA Tegra20 DEVFREQ Driver"
	depends on (TEGRA_MC && TEGRA20_EMC) || COMPILE_TEST
	depends on COMMON_CLK
	select DEVFREQ_GOV_SIMPLE_ONDEMAND
	select PM_OPP
	help
	  This adds the DEVFREQ driver for the Tegra20 family of SoCs.
	  It reads Memory Controller counters and adjusts the operating
	  frequencies and voltages with OPP support.

config ARM_RK3399_DMC_DEVFREQ
	tristate "ARM RK3399 DMC DEVFREQ Driver"
	depends on ARCH_ROCKCHIP
+2 −1
Original line number Diff line number Diff line
@@ -10,7 +10,8 @@ obj-$(CONFIG_DEVFREQ_GOV_PASSIVE) += governor_passive.o
# DEVFREQ Drivers
obj-$(CONFIG_ARM_EXYNOS_BUS_DEVFREQ)	+= exynos-bus.o
obj-$(CONFIG_ARM_RK3399_DMC_DEVFREQ)	+= rk3399_dmc.o
obj-$(CONFIG_ARM_TEGRA_DEVFREQ)		+= tegra-devfreq.o
obj-$(CONFIG_ARM_TEGRA_DEVFREQ)		+= tegra30-devfreq.o
obj-$(CONFIG_ARM_TEGRA20_DEVFREQ)	+= tegra20-devfreq.o

# DEVFREQ Event Drivers
obj-$(CONFIG_PM_DEVFREQ_EVENT)		+= event/
+6 −6
Original line number Diff line number Diff line
@@ -254,7 +254,7 @@ static struct devfreq_governor *try_then_request_governor(const char *name)
		/* Restore previous state before return */
		mutex_lock(&devfreq_list_lock);
		if (err)
			return ERR_PTR(err);
			return (err < 0) ? ERR_PTR(err) : ERR_PTR(-EINVAL);

		governor = find_devfreq_governor(name);
	}
@@ -402,7 +402,7 @@ static void devfreq_monitor(struct work_struct *work)
 * devfreq_monitor_start() - Start load monitoring of devfreq instance
 * @devfreq:	the devfreq instance.
 *
 * Helper function for starting devfreq device load monitoing. By
 * Helper function for starting devfreq device load monitoring. By
 * default delayed work based monitoring is supported. Function
 * to be called from governor in response to DEVFREQ_GOV_START
 * event when device is added to devfreq framework.
@@ -420,7 +420,7 @@ EXPORT_SYMBOL(devfreq_monitor_start);
 * devfreq_monitor_stop() - Stop load monitoring of a devfreq instance
 * @devfreq:	the devfreq instance.
 *
 * Helper function to stop devfreq device load monitoing. Function
 * Helper function to stop devfreq device load monitoring. Function
 * to be called from governor in response to DEVFREQ_GOV_STOP
 * event when device is removed from devfreq framework.
 */
@@ -434,7 +434,7 @@ EXPORT_SYMBOL(devfreq_monitor_stop);
 * devfreq_monitor_suspend() - Suspend load monitoring of a devfreq instance
 * @devfreq:	the devfreq instance.
 *
 * Helper function to suspend devfreq device load monitoing. Function
 * Helper function to suspend devfreq device load monitoring. Function
 * to be called from governor in response to DEVFREQ_GOV_SUSPEND
 * event or when polling interval is set to zero.
 *
@@ -461,7 +461,7 @@ EXPORT_SYMBOL(devfreq_monitor_suspend);
 * devfreq_monitor_resume() - Resume load monitoring of a devfreq instance
 * @devfreq:    the devfreq instance.
 *
 * Helper function to resume devfreq device load monitoing. Function
 * Helper function to resume devfreq device load monitoring. Function
 * to be called from governor in response to DEVFREQ_GOV_RESUME
 * event or when polling interval is set to non-zero.
 */
@@ -867,7 +867,7 @@ EXPORT_SYMBOL_GPL(devfreq_get_devfreq_by_phandle);

/**
 * devm_devfreq_remove_device() - Resource-managed devfreq_remove_device()
 * @dev:	the device to add devfreq feature.
 * @dev:	the device from which to remove devfreq feature.
 * @devfreq:	the devfreq instance to be removed
 */
void devm_devfreq_remove_device(struct device *dev, struct devfreq *devfreq)
+72 −32
Original line number Diff line number Diff line
@@ -13,6 +13,7 @@
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of_address.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/suspend.h>
@@ -20,6 +21,11 @@

#include "exynos-ppmu.h"

enum exynos_ppmu_type {
	EXYNOS_TYPE_PPMU,
	EXYNOS_TYPE_PPMU_V2,
};

struct exynos_ppmu_data {
	struct clk *clk;
};
@@ -33,6 +39,7 @@ struct exynos_ppmu {
	struct regmap *regmap;

	struct exynos_ppmu_data ppmu;
	enum exynos_ppmu_type ppmu_type;
};

#define PPMU_EVENT(name)			\
@@ -86,6 +93,12 @@ static struct __exynos_ppmu_events {
	PPMU_EVENT(d1-cpu),
	PPMU_EVENT(d1-general),
	PPMU_EVENT(d1-rt),

	/* For Exynos5422 SoC */
	PPMU_EVENT(dmc0_0),
	PPMU_EVENT(dmc0_1),
	PPMU_EVENT(dmc1_0),
	PPMU_EVENT(dmc1_1),
};

static int exynos_ppmu_find_ppmu_id(struct devfreq_event_dev *edev)
@@ -151,9 +164,9 @@ static int exynos_ppmu_set_event(struct devfreq_event_dev *edev)
	if (ret < 0)
		return ret;

	/* Set the event of Read/Write data count  */
	/* Set the event of proper data type monitoring */
	ret = regmap_write(info->regmap, PPMU_BEVTxSEL(id),
				PPMU_RO_DATA_CNT | PPMU_WO_DATA_CNT);
			   edev->desc->event_type);
	if (ret < 0)
		return ret;

@@ -365,23 +378,11 @@ static int exynos_ppmu_v2_set_event(struct devfreq_event_dev *edev)
	if (ret < 0)
		return ret;

	/* Set the event of Read/Write data count  */
	switch (id) {
	case PPMU_PMNCNT0:
	case PPMU_PMNCNT1:
	case PPMU_PMNCNT2:
		ret = regmap_write(info->regmap, PPMU_V2_CH_EVx_TYPE(id),
				PPMU_V2_RO_DATA_CNT | PPMU_V2_WO_DATA_CNT);
		if (ret < 0)
			return ret;
		break;
	case PPMU_PMNCNT3:
	/* Set the event of proper data type monitoring */
	ret = regmap_write(info->regmap, PPMU_V2_CH_EVx_TYPE(id),
				PPMU_V2_EVT3_RW_DATA_CNT);
			   edev->desc->event_type);
	if (ret < 0)
		return ret;
		break;
	}

	/* Reset cycle counter/performance counter and enable PPMU */
	ret = regmap_read(info->regmap, PPMU_V2_PMNC, &pmnc);
@@ -480,31 +481,24 @@ static const struct devfreq_event_ops exynos_ppmu_v2_ops = {
static const struct of_device_id exynos_ppmu_id_match[] = {
	{
		.compatible = "samsung,exynos-ppmu",
		.data = (void *)&exynos_ppmu_ops,
		.data = (void *)EXYNOS_TYPE_PPMU,
	}, {
		.compatible = "samsung,exynos-ppmu-v2",
		.data = (void *)&exynos_ppmu_v2_ops,
		.data = (void *)EXYNOS_TYPE_PPMU_V2,
	},
	{ /* sentinel */ },
};
MODULE_DEVICE_TABLE(of, exynos_ppmu_id_match);

static struct devfreq_event_ops *exynos_bus_get_ops(struct device_node *np)
{
	const struct of_device_id *match;

	match = of_match_node(exynos_ppmu_id_match, np);
	return (struct devfreq_event_ops *)match->data;
}

static int of_get_devfreq_events(struct device_node *np,
				 struct exynos_ppmu *info)
{
	struct devfreq_event_desc *desc;
	struct devfreq_event_ops *event_ops;
	struct device *dev = info->dev;
	struct device_node *events_np, *node;
	int i, j, count;
	const struct of_device_id *of_id;
	int ret;

	events_np = of_get_child_by_name(np, "events");
	if (!events_np) {
@@ -512,7 +506,6 @@ static int of_get_devfreq_events(struct device_node *np,
			"failed to get child node of devfreq-event devices\n");
		return -EINVAL;
	}
	event_ops = exynos_bus_get_ops(np);

	count = of_get_child_count(events_np);
	desc = devm_kcalloc(dev, count, sizeof(*desc), GFP_KERNEL);
@@ -520,6 +513,12 @@ static int of_get_devfreq_events(struct device_node *np,
		return -ENOMEM;
	info->num_events = count;

	of_id = of_match_device(exynos_ppmu_id_match, dev);
	if (of_id)
		info->ppmu_type = (enum exynos_ppmu_type)of_id->data;
	else
		return -EINVAL;

	j = 0;
	for_each_child_of_node(events_np, node) {
		for (i = 0; i < ARRAY_SIZE(ppmu_events); i++) {
@@ -537,10 +536,51 @@ static int of_get_devfreq_events(struct device_node *np,
			continue;
		}

		desc[j].ops = event_ops;
		switch (info->ppmu_type) {
		case EXYNOS_TYPE_PPMU:
			desc[j].ops = &exynos_ppmu_ops;
			break;
		case EXYNOS_TYPE_PPMU_V2:
			desc[j].ops = &exynos_ppmu_v2_ops;
			break;
		}

		desc[j].driver_data = info;

		of_property_read_string(node, "event-name", &desc[j].name);
		ret = of_property_read_u32(node, "event-data-type",
					   &desc[j].event_type);
		if (ret) {
			/* Set the event of proper data type counting.
			 * Check if the data type has been defined in DT,
			 * use default if not.
			 */
			if (info->ppmu_type == EXYNOS_TYPE_PPMU_V2) {
				struct devfreq_event_dev edev;
				int id;
				/* Not all registers take the same value for
				 * read+write data count.
				 */
				edev.desc = &desc[j];
				id = exynos_ppmu_find_ppmu_id(&edev);

				switch (id) {
				case PPMU_PMNCNT0:
				case PPMU_PMNCNT1:
				case PPMU_PMNCNT2:
					desc[j].event_type = PPMU_V2_RO_DATA_CNT
						| PPMU_V2_WO_DATA_CNT;
					break;
				case PPMU_PMNCNT3:
					desc[j].event_type =
						PPMU_V2_EVT3_RW_DATA_CNT;
					break;
				}
			} else {
				desc[j].event_type = PPMU_RO_DATA_CNT |
					PPMU_WO_DATA_CNT;
			}
		}

		j++;
	}
+37 −116
Original line number Diff line number Diff line
@@ -22,7 +22,6 @@
#include <linux/slab.h>

#define DEFAULT_SATURATION_RATIO	40
#define DEFAULT_VOLTAGE_TOLERANCE	2

struct exynos_bus {
	struct device *dev;
@@ -34,9 +33,8 @@ struct exynos_bus {

	unsigned long curr_freq;

	struct regulator *regulator;
	struct opp_table *opp_table;
	struct clk *clk;
	unsigned int voltage_tolerance;
	unsigned int ratio;
};

@@ -90,62 +88,29 @@ static int exynos_bus_get_event(struct exynos_bus *bus,
}

/*
 * Must necessary function for devfreq simple-ondemand governor
 * devfreq function for both simple-ondemand and passive governor
 */
static int exynos_bus_target(struct device *dev, unsigned long *freq, u32 flags)
{
	struct exynos_bus *bus = dev_get_drvdata(dev);
	struct dev_pm_opp *new_opp;
	unsigned long old_freq, new_freq, new_volt, tol;
	int ret = 0;

	/* Get new opp-bus instance according to new bus clock */
	/* Get correct frequency for bus. */
	new_opp = devfreq_recommended_opp(dev, freq, flags);
	if (IS_ERR(new_opp)) {
		dev_err(dev, "failed to get recommended opp instance\n");
		return PTR_ERR(new_opp);
	}

	new_freq = dev_pm_opp_get_freq(new_opp);
	new_volt = dev_pm_opp_get_voltage(new_opp);
	dev_pm_opp_put(new_opp);

	old_freq = bus->curr_freq;

	if (old_freq == new_freq)
		return 0;
	tol = new_volt * bus->voltage_tolerance / 100;

	/* Change voltage and frequency according to new OPP level */
	mutex_lock(&bus->lock);
	ret = dev_pm_opp_set_rate(dev, *freq);
	if (!ret)
		bus->curr_freq = *freq;

	if (old_freq < new_freq) {
		ret = regulator_set_voltage_tol(bus->regulator, new_volt, tol);
		if (ret < 0) {
			dev_err(bus->dev, "failed to set voltage\n");
			goto out;
		}
	}

	ret = clk_set_rate(bus->clk, new_freq);
	if (ret < 0) {
		dev_err(dev, "failed to change clock of bus\n");
		clk_set_rate(bus->clk, old_freq);
		goto out;
	}

	if (old_freq > new_freq) {
		ret = regulator_set_voltage_tol(bus->regulator, new_volt, tol);
		if (ret < 0) {
			dev_err(bus->dev, "failed to set voltage\n");
			goto out;
		}
	}
	bus->curr_freq = new_freq;

	dev_dbg(dev, "Set the frequency of bus (%luHz -> %luHz, %luHz)\n",
			old_freq, new_freq, clk_get_rate(bus->clk));
out:
	mutex_unlock(&bus->lock);

	return ret;
@@ -191,57 +156,12 @@ static void exynos_bus_exit(struct device *dev)
	if (ret < 0)
		dev_warn(dev, "failed to disable the devfreq-event devices\n");

	if (bus->regulator)
		regulator_disable(bus->regulator);

	dev_pm_opp_of_remove_table(dev);
	clk_disable_unprepare(bus->clk);
	if (bus->opp_table) {
		dev_pm_opp_put_regulators(bus->opp_table);
		bus->opp_table = NULL;
	}

/*
 * Must necessary function for devfreq passive governor
 */
static int exynos_bus_passive_target(struct device *dev, unsigned long *freq,
					u32 flags)
{
	struct exynos_bus *bus = dev_get_drvdata(dev);
	struct dev_pm_opp *new_opp;
	unsigned long old_freq, new_freq;
	int ret = 0;

	/* Get new opp-bus instance according to new bus clock */
	new_opp = devfreq_recommended_opp(dev, freq, flags);
	if (IS_ERR(new_opp)) {
		dev_err(dev, "failed to get recommended opp instance\n");
		return PTR_ERR(new_opp);
	}

	new_freq = dev_pm_opp_get_freq(new_opp);
	dev_pm_opp_put(new_opp);

	old_freq = bus->curr_freq;

	if (old_freq == new_freq)
		return 0;

	/* Change the frequency according to new OPP level */
	mutex_lock(&bus->lock);

	ret = clk_set_rate(bus->clk, new_freq);
	if (ret < 0) {
		dev_err(dev, "failed to set the clock of bus\n");
		goto out;
	}

	*freq = new_freq;
	bus->curr_freq = new_freq;

	dev_dbg(dev, "Set the frequency of bus (%luHz -> %luHz, %luHz)\n",
			old_freq, new_freq, clk_get_rate(bus->clk));
out:
	mutex_unlock(&bus->lock);

	return ret;
}

static void exynos_bus_passive_exit(struct device *dev)
@@ -256,21 +176,19 @@ static int exynos_bus_parent_parse_of(struct device_node *np,
					struct exynos_bus *bus)
{
	struct device *dev = bus->dev;
	struct opp_table *opp_table;
	const char *vdd = "vdd";
	int i, ret, count, size;

	/* Get the regulator to provide each bus with the power */
	bus->regulator = devm_regulator_get(dev, "vdd");
	if (IS_ERR(bus->regulator)) {
		dev_err(dev, "failed to get VDD regulator\n");
		return PTR_ERR(bus->regulator);
	}

	ret = regulator_enable(bus->regulator);
	if (ret < 0) {
		dev_err(dev, "failed to enable VDD regulator\n");
	opp_table = dev_pm_opp_set_regulators(dev, &vdd, 1);
	if (IS_ERR(opp_table)) {
		ret = PTR_ERR(opp_table);
		dev_err(dev, "failed to set regulators %d\n", ret);
		return ret;
	}

	bus->opp_table = opp_table;

	/*
	 * Get the devfreq-event devices to get the current utilization of
	 * buses. This raw data will be used in devfreq ondemand governor.
@@ -311,14 +229,11 @@ static int exynos_bus_parent_parse_of(struct device_node *np,
	if (of_property_read_u32(np, "exynos,saturation-ratio", &bus->ratio))
		bus->ratio = DEFAULT_SATURATION_RATIO;

	if (of_property_read_u32(np, "exynos,voltage-tolerance",
					&bus->voltage_tolerance))
		bus->voltage_tolerance = DEFAULT_VOLTAGE_TOLERANCE;

	return 0;

err_regulator:
	regulator_disable(bus->regulator);
	dev_pm_opp_put_regulators(bus->opp_table);
	bus->opp_table = NULL;

	return ret;
}
@@ -383,6 +298,7 @@ static int exynos_bus_probe(struct platform_device *pdev)
	struct exynos_bus *bus;
	int ret, max_state;
	unsigned long min_freq, max_freq;
	bool passive = false;

	if (!np) {
		dev_err(dev, "failed to find devicetree node\n");
@@ -396,27 +312,27 @@ static int exynos_bus_probe(struct platform_device *pdev)
	bus->dev = &pdev->dev;
	platform_set_drvdata(pdev, bus);

	/* Parse the device-tree to get the resource information */
	ret = exynos_bus_parse_of(np, bus);
	if (ret < 0)
		return ret;

	profile = devm_kzalloc(dev, sizeof(*profile), GFP_KERNEL);
	if (!profile) {
		ret = -ENOMEM;
		goto err;
	}
	if (!profile)
		return -ENOMEM;

	node = of_parse_phandle(dev->of_node, "devfreq", 0);
	if (node) {
		of_node_put(node);
		goto passive;
		passive = true;
	} else {
		ret = exynos_bus_parent_parse_of(np, bus);
		if (ret < 0)
			return ret;
	}

	/* Parse the device-tree to get the resource information */
	ret = exynos_bus_parse_of(np, bus);
	if (ret < 0)
		goto err;
		goto err_reg;

	if (passive)
		goto passive;

	/* Initialize the struct profile and governor data for parent device */
	profile->polling_ms = 50;
@@ -468,7 +384,7 @@ static int exynos_bus_probe(struct platform_device *pdev)
	goto out;
passive:
	/* Initialize the struct profile and governor data for passive device */
	profile->target = exynos_bus_passive_target;
	profile->target = exynos_bus_target;
	profile->exit = exynos_bus_passive_exit;

	/* Get the instance of parent devfreq device */
@@ -507,6 +423,11 @@ out:
err:
	dev_pm_opp_of_remove_table(dev);
	clk_disable_unprepare(bus->clk);
err_reg:
	if (!passive) {
		dev_pm_opp_put_regulators(bus->opp_table);
		bus->opp_table = NULL;
	}

	return ret;
}
Loading