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

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
...@@ -93,15 +93,28 @@ config ARM_EXYNOS_BUS_DEVFREQ ...@@ -93,15 +93,28 @@ config ARM_EXYNOS_BUS_DEVFREQ
This does not yet operate with optimal voltages. This does not yet operate with optimal voltages.
config ARM_TEGRA_DEVFREQ config ARM_TEGRA_DEVFREQ
tristate "Tegra DEVFREQ Driver" tristate "NVIDIA Tegra30/114/124/210 DEVFREQ Driver"
depends on ARCH_TEGRA_124_SOC depends on ARCH_TEGRA_3x_SOC || ARCH_TEGRA_114_SOC || \
select DEVFREQ_GOV_SIMPLE_ONDEMAND ARCH_TEGRA_132_SOC || ARCH_TEGRA_124_SOC || \
ARCH_TEGRA_210_SOC || \
COMPILE_TEST
select PM_OPP select PM_OPP
help help
This adds the DEVFREQ driver for the Tegra family of SoCs. This adds the DEVFREQ driver for the Tegra family of SoCs.
It reads ACTMON counters of memory controllers and adjusts the It reads ACTMON counters of memory controllers and adjusts the
operating frequencies and voltages with OPP support. 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 config ARM_RK3399_DMC_DEVFREQ
tristate "ARM RK3399 DMC DEVFREQ Driver" tristate "ARM RK3399 DMC DEVFREQ Driver"
depends on ARCH_ROCKCHIP depends on ARCH_ROCKCHIP
......
...@@ -10,7 +10,8 @@ obj-$(CONFIG_DEVFREQ_GOV_PASSIVE) += governor_passive.o ...@@ -10,7 +10,8 @@ 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_RK3399_DMC_DEVFREQ) += rk3399_dmc.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 # DEVFREQ Event Drivers
obj-$(CONFIG_PM_DEVFREQ_EVENT) += event/ obj-$(CONFIG_PM_DEVFREQ_EVENT) += event/
...@@ -254,7 +254,7 @@ static struct devfreq_governor *try_then_request_governor(const char *name) ...@@ -254,7 +254,7 @@ static struct devfreq_governor *try_then_request_governor(const char *name)
/* Restore previous state before return */ /* Restore previous state before return */
mutex_lock(&devfreq_list_lock); mutex_lock(&devfreq_list_lock);
if (err) if (err)
return ERR_PTR(err); return (err < 0) ? ERR_PTR(err) : ERR_PTR(-EINVAL);
governor = find_devfreq_governor(name); governor = find_devfreq_governor(name);
} }
...@@ -402,7 +402,7 @@ static void devfreq_monitor(struct work_struct *work) ...@@ -402,7 +402,7 @@ static void devfreq_monitor(struct work_struct *work)
* devfreq_monitor_start() - Start load monitoring of devfreq instance * devfreq_monitor_start() - Start load monitoring of devfreq instance
* @devfreq: the 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 * default delayed work based monitoring is supported. Function
* to be called from governor in response to DEVFREQ_GOV_START * to be called from governor in response to DEVFREQ_GOV_START
* event when device is added to devfreq framework. * event when device is added to devfreq framework.
...@@ -420,7 +420,7 @@ EXPORT_SYMBOL(devfreq_monitor_start); ...@@ -420,7 +420,7 @@ EXPORT_SYMBOL(devfreq_monitor_start);
* devfreq_monitor_stop() - Stop load monitoring of a devfreq instance * devfreq_monitor_stop() - Stop load monitoring of a devfreq instance
* @devfreq: the 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 * to be called from governor in response to DEVFREQ_GOV_STOP
* event when device is removed from devfreq framework. * event when device is removed from devfreq framework.
*/ */
...@@ -434,7 +434,7 @@ EXPORT_SYMBOL(devfreq_monitor_stop); ...@@ -434,7 +434,7 @@ EXPORT_SYMBOL(devfreq_monitor_stop);
* devfreq_monitor_suspend() - Suspend load monitoring of a devfreq instance * devfreq_monitor_suspend() - Suspend load monitoring of a devfreq instance
* @devfreq: the 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 * to be called from governor in response to DEVFREQ_GOV_SUSPEND
* event or when polling interval is set to zero. * event or when polling interval is set to zero.
* *
...@@ -461,7 +461,7 @@ EXPORT_SYMBOL(devfreq_monitor_suspend); ...@@ -461,7 +461,7 @@ EXPORT_SYMBOL(devfreq_monitor_suspend);
* devfreq_monitor_resume() - Resume load monitoring of a devfreq instance * devfreq_monitor_resume() - Resume load monitoring of a devfreq instance
* @devfreq: the 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 * to be called from governor in response to DEVFREQ_GOV_RESUME
* event or when polling interval is set to non-zero. * event or when polling interval is set to non-zero.
*/ */
...@@ -867,7 +867,7 @@ EXPORT_SYMBOL_GPL(devfreq_get_devfreq_by_phandle); ...@@ -867,7 +867,7 @@ EXPORT_SYMBOL_GPL(devfreq_get_devfreq_by_phandle);
/** /**
* devm_devfreq_remove_device() - Resource-managed devfreq_remove_device() * 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 * @devfreq: the devfreq instance to be removed
*/ */
void devm_devfreq_remove_device(struct device *dev, struct devfreq *devfreq) void devm_devfreq_remove_device(struct device *dev, struct devfreq *devfreq)
......
...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/of_address.h> #include <linux/of_address.h>
#include <linux/of_device.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/regmap.h> #include <linux/regmap.h>
#include <linux/suspend.h> #include <linux/suspend.h>
...@@ -20,6 +21,11 @@ ...@@ -20,6 +21,11 @@
#include "exynos-ppmu.h" #include "exynos-ppmu.h"
enum exynos_ppmu_type {
EXYNOS_TYPE_PPMU,
EXYNOS_TYPE_PPMU_V2,
};
struct exynos_ppmu_data { struct exynos_ppmu_data {
struct clk *clk; struct clk *clk;
}; };
...@@ -33,6 +39,7 @@ struct exynos_ppmu { ...@@ -33,6 +39,7 @@ struct exynos_ppmu {
struct regmap *regmap; struct regmap *regmap;
struct exynos_ppmu_data ppmu; struct exynos_ppmu_data ppmu;
enum exynos_ppmu_type ppmu_type;
}; };
#define PPMU_EVENT(name) \ #define PPMU_EVENT(name) \
...@@ -86,6 +93,12 @@ static struct __exynos_ppmu_events { ...@@ -86,6 +93,12 @@ static struct __exynos_ppmu_events {
PPMU_EVENT(d1-cpu), PPMU_EVENT(d1-cpu),
PPMU_EVENT(d1-general), PPMU_EVENT(d1-general),
PPMU_EVENT(d1-rt), 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) 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) ...@@ -151,9 +164,9 @@ static int exynos_ppmu_set_event(struct devfreq_event_dev *edev)
if (ret < 0) if (ret < 0)
return ret; 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), ret = regmap_write(info->regmap, PPMU_BEVTxSEL(id),
PPMU_RO_DATA_CNT | PPMU_WO_DATA_CNT); edev->desc->event_type);
if (ret < 0) if (ret < 0)
return ret; return ret;
...@@ -365,23 +378,11 @@ static int exynos_ppmu_v2_set_event(struct devfreq_event_dev *edev) ...@@ -365,23 +378,11 @@ static int exynos_ppmu_v2_set_event(struct devfreq_event_dev *edev)
if (ret < 0) if (ret < 0)
return ret; return ret;
/* Set the event of Read/Write data count */ /* Set the event of proper data type monitoring */
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:
ret = regmap_write(info->regmap, PPMU_V2_CH_EVx_TYPE(id), ret = regmap_write(info->regmap, PPMU_V2_CH_EVx_TYPE(id),
PPMU_V2_EVT3_RW_DATA_CNT); edev->desc->event_type);
if (ret < 0) if (ret < 0)
return ret; return ret;
break;
}
/* Reset cycle counter/performance counter and enable PPMU */ /* Reset cycle counter/performance counter and enable PPMU */
ret = regmap_read(info->regmap, PPMU_V2_PMNC, &pmnc); ret = regmap_read(info->regmap, PPMU_V2_PMNC, &pmnc);
...@@ -480,31 +481,24 @@ static const struct devfreq_event_ops exynos_ppmu_v2_ops = { ...@@ -480,31 +481,24 @@ static const struct devfreq_event_ops exynos_ppmu_v2_ops = {
static const struct of_device_id exynos_ppmu_id_match[] = { static const struct of_device_id exynos_ppmu_id_match[] = {
{ {
.compatible = "samsung,exynos-ppmu", .compatible = "samsung,exynos-ppmu",
.data = (void *)&exynos_ppmu_ops, .data = (void *)EXYNOS_TYPE_PPMU,
}, { }, {
.compatible = "samsung,exynos-ppmu-v2", .compatible = "samsung,exynos-ppmu-v2",
.data = (void *)&exynos_ppmu_v2_ops, .data = (void *)EXYNOS_TYPE_PPMU_V2,
}, },
{ /* sentinel */ }, { /* sentinel */ },
}; };
MODULE_DEVICE_TABLE(of, exynos_ppmu_id_match); 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, static int of_get_devfreq_events(struct device_node *np,
struct exynos_ppmu *info) struct exynos_ppmu *info)
{ {
struct devfreq_event_desc *desc; struct devfreq_event_desc *desc;
struct devfreq_event_ops *event_ops;
struct device *dev = info->dev; struct device *dev = info->dev;
struct device_node *events_np, *node; struct device_node *events_np, *node;
int i, j, count; int i, j, count;
const struct of_device_id *of_id;
int ret;
events_np = of_get_child_by_name(np, "events"); events_np = of_get_child_by_name(np, "events");
if (!events_np) { if (!events_np) {
...@@ -512,7 +506,6 @@ static int of_get_devfreq_events(struct device_node *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"); "failed to get child node of devfreq-event devices\n");
return -EINVAL; return -EINVAL;
} }
event_ops = exynos_bus_get_ops(np);
count = of_get_child_count(events_np); count = of_get_child_count(events_np);
desc = devm_kcalloc(dev, count, sizeof(*desc), GFP_KERNEL); desc = devm_kcalloc(dev, count, sizeof(*desc), GFP_KERNEL);
...@@ -520,6 +513,12 @@ static int of_get_devfreq_events(struct device_node *np, ...@@ -520,6 +513,12 @@ static int of_get_devfreq_events(struct device_node *np,
return -ENOMEM; return -ENOMEM;
info->num_events = count; 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; j = 0;
for_each_child_of_node(events_np, node) { for_each_child_of_node(events_np, node) {
for (i = 0; i < ARRAY_SIZE(ppmu_events); i++) { for (i = 0; i < ARRAY_SIZE(ppmu_events); i++) {
...@@ -537,10 +536,51 @@ static int of_get_devfreq_events(struct device_node *np, ...@@ -537,10 +536,51 @@ static int of_get_devfreq_events(struct device_node *np,
continue; 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; desc[j].driver_data = info;
of_property_read_string(node, "event-name", &desc[j].name); 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++; j++;
} }
......
...@@ -22,7 +22,6 @@ ...@@ -22,7 +22,6 @@
#include <linux/slab.h> #include <linux/slab.h>
#define DEFAULT_SATURATION_RATIO 40 #define DEFAULT_SATURATION_RATIO 40
#define DEFAULT_VOLTAGE_TOLERANCE 2
struct exynos_bus { struct exynos_bus {
struct device *dev; struct device *dev;
...@@ -34,9 +33,8 @@ struct exynos_bus { ...@@ -34,9 +33,8 @@ struct exynos_bus {
unsigned long curr_freq; unsigned long curr_freq;
struct regulator *regulator; struct opp_table *opp_table;
struct clk *clk; struct clk *clk;
unsigned int voltage_tolerance;
unsigned int ratio; unsigned int ratio;
}; };
...@@ -90,62 +88,29 @@ static int exynos_bus_get_event(struct exynos_bus *bus, ...@@ -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) static int exynos_bus_target(struct device *dev, unsigned long *freq, u32 flags)
{ {
struct exynos_bus *bus = dev_get_drvdata(dev); struct exynos_bus *bus = dev_get_drvdata(dev);
struct dev_pm_opp *new_opp; struct dev_pm_opp *new_opp;
unsigned long old_freq, new_freq, new_volt, tol;
int ret = 0; 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); new_opp = devfreq_recommended_opp(dev, freq, flags);
if (IS_ERR(new_opp)) { if (IS_ERR(new_opp)) {
dev_err(dev, "failed to get recommended opp instance\n"); dev_err(dev, "failed to get recommended opp instance\n");
return PTR_ERR(new_opp); 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); 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 */ /* Change voltage and frequency according to new OPP level */
mutex_lock(&bus->lock); 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); mutex_unlock(&bus->lock);
return ret; return ret;
...@@ -191,57 +156,12 @@ static void exynos_bus_exit(struct device *dev) ...@@ -191,57 +156,12 @@ static void exynos_bus_exit(struct device *dev)
if (ret < 0) if (ret < 0)
dev_warn(dev, "failed to disable the devfreq-event devices\n"); 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); dev_pm_opp_of_remove_table(dev);
clk_disable_unprepare(bus->clk); 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) static void exynos_bus_passive_exit(struct device *dev)
...@@ -256,21 +176,19 @@ static int exynos_bus_parent_parse_of(struct device_node *np, ...@@ -256,21 +176,19 @@ static int exynos_bus_parent_parse_of(struct device_node *np,
struct exynos_bus *bus) struct exynos_bus *bus)
{ {
struct device *dev = bus->dev; struct device *dev = bus->dev;
struct opp_table *opp_table;
const char *vdd = "vdd";
int i, ret, count, size; int i, ret, count, size;
/* Get the regulator to provide each bus with the power */ opp_table = dev_pm_opp_set_regulators(dev, &vdd, 1);
bus->regulator = devm_regulator_get(dev, "vdd"); if (IS_ERR(opp_table)) {
if (IS_ERR(bus->regulator)) { ret = PTR_ERR(opp_table);
dev_err(dev, "failed to get VDD regulator\n"); dev_err(dev, "failed to set regulators %d\n", ret);
return PTR_ERR(bus->regulator);
}
ret = regulator_enable(bus->regulator);
if (ret < 0) {
dev_err(dev, "failed to enable VDD regulator\n");
return ret; return ret;
} }
bus->opp_table = opp_table;
/* /*
* Get the devfreq-event devices to get the current utilization of * Get the devfreq-event devices to get the current utilization of
* buses. This raw data will be used in devfreq ondemand governor. * 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, ...@@ -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)) if (of_property_read_u32(np, "exynos,saturation-ratio", &bus->ratio))
bus->ratio = DEFAULT_SATURATION_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; return 0;
err_regulator: err_regulator:
regulator_disable(bus->regulator); dev_pm_opp_put_regulators(bus->opp_table);
bus->opp_table = NULL;
return ret; return ret;
} }
...@@ -383,6 +298,7 @@ static int exynos_bus_probe(struct platform_device *pdev) ...@@ -383,6 +298,7 @@ static int exynos_bus_probe(struct platform_device *pdev)
struct exynos_bus *bus; struct exynos_bus *bus;
int ret, max_state; int ret, max_state;
unsigned long min_freq, max_freq; unsigned long min_freq, max_freq;
bool passive = false;
if (!np) { if (!np) {
dev_err(dev, "failed to find devicetree node\n"); dev_err(dev, "failed to find devicetree node\n");
...@@ -396,27 +312,27 @@ static int exynos_bus_probe(struct platform_device *pdev) ...@@ -396,27 +312,27 @@ static int exynos_bus_probe(struct platform_device *pdev)
bus->dev = &pdev->dev; bus->dev = &pdev->dev;
platform_set_drvdata(pdev, bus); 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); profile = devm_kzalloc(dev, sizeof(*profile), GFP_KERNEL);
if (!profile) { if (!profile)
ret = -ENOMEM; return -ENOMEM;
goto err;
}
node = of_parse_phandle(dev->of_node, "devfreq", 0); node = of_parse_phandle(dev->of_node, "devfreq", 0);
if (node) { if (node) {
of_node_put(node); of_node_put(node);
goto passive; passive = true;
} else { } else {
ret = exynos_bus_parent_parse_of(np, bus); 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) if (ret < 0)
goto err; goto err_reg;
if (passive)
goto passive;
/* Initialize the struct profile and governor data for parent device */ /* Initialize the struct profile and governor data for parent device */
profile->polling_ms = 50; profile->polling_ms = 50;
...@@ -468,7 +384,7 @@ static int exynos_bus_probe(struct platform_device *pdev) ...@@ -468,7 +384,7 @@ static int exynos_bus_probe(struct platform_device *pdev)
goto out; goto out;
passive: passive:
/* Initialize the struct profile and governor data for passive device */ /* 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; profile->exit = exynos_bus_passive_exit;
/* Get the instance of parent devfreq device */ /* Get the instance of parent devfreq device */
...@@ -507,6 +423,11 @@ static int exynos_bus_probe(struct platform_device *pdev) ...@@ -507,6 +423,11 @@ static int exynos_bus_probe(struct platform_device *pdev)
err: err:
dev_pm_opp_of_remove_table(dev); dev_pm_opp_of_remove_table(dev);
clk_disable_unprepare(bus->clk); clk_disable_unprepare(bus->clk);
err_reg:
if (!passive) {
dev_pm_opp_put_regulators(bus->opp_table);
bus->opp_table = NULL;
}
return ret; return ret;
} }
......
...@@ -149,7 +149,6 @@ static int devfreq_passive_notifier_call(struct notifier_block *nb, ...@@ -149,7 +149,6 @@ static int devfreq_passive_notifier_call(struct notifier_block *nb,
static int devfreq_passive_event_handler(struct devfreq *devfreq, static int devfreq_passive_event_handler(struct devfreq *devfreq,
unsigned int event, void *data) unsigned int event, void *data)
{ {
struct device *dev = devfreq->dev.parent;
struct devfreq_passive_data *p_data struct devfreq_passive_data *p_data
= (struct devfreq_passive_data *)devfreq->data; = (struct devfreq_passive_data *)devfreq->data;
struct devfreq *parent = (struct devfreq *)p_data->parent; struct devfreq *parent = (struct devfreq *)p_data->parent;
...@@ -165,12 +164,12 @@ static int devfreq_passive_event_handler(struct devfreq *devfreq, ...@@ -165,12 +164,12 @@ static int devfreq_passive_event_handler(struct devfreq *devfreq,
p_data->this = devfreq; p_data->this = devfreq;
nb->notifier_call = devfreq_passive_notifier_call; nb->notifier_call = devfreq_passive_notifier_call;
ret = devm_devfreq_register_notifier(dev, parent, nb, ret = devfreq_register_notifier(parent, nb,
DEVFREQ_TRANSITION_NOTIFIER); DEVFREQ_TRANSITION_NOTIFIER);
break; break;
case DEVFREQ_GOV_STOP: case DEVFREQ_GOV_STOP:
devm_devfreq_unregister_notifier(dev, parent, nb, WARN_ON(devfreq_unregister_notifier(parent, nb,
DEVFREQ_TRANSITION_NOTIFIER); DEVFREQ_TRANSITION_NOTIFIER));
break; break;
default: default:
break; break;
......
...@@ -351,7 +351,7 @@ static int rk3399_dmcfreq_probe(struct platform_device *pdev) ...@@ -351,7 +351,7 @@ static int rk3399_dmcfreq_probe(struct platform_device *pdev)
/* /*
* Get dram timing and pass it to arm trust firmware, * Get dram timing and pass it to arm trust firmware,
* the dram drvier in arm trust firmware will get these * the dram driver in arm trust firmware will get these
* timing and to do dram initial. * timing and to do dram initial.
*/ */
if (!of_get_ddr_timings(&data->timing, np)) { if (!of_get_ddr_timings(&data->timing, np)) {
......
// SPDX-License-Identifier: GPL-2.0
/*
* NVIDIA Tegra20 devfreq driver
*
* Copyright (C) 2019 GRATE-DRIVER project
*/
#include <linux/clk.h>
#include <linux/devfreq.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/pm_opp.h>
#include <linux/slab.h>
#include <soc/tegra/mc.h>
#include "governor.h"
#define MC_STAT_CONTROL 0x90
#define MC_STAT_EMC_CLOCK_LIMIT 0xa0
#define MC_STAT_EMC_CLOCKS 0xa4
#define MC_STAT_EMC_CONTROL 0xa8
#define MC_STAT_EMC_COUNT 0xb8
#define EMC_GATHER_CLEAR (1 << 8)
#define EMC_GATHER_ENABLE (3 << 8)
struct tegra_devfreq {
struct devfreq *devfreq;
struct clk *emc_clock;
void __iomem *regs;
};
static int tegra_devfreq_target(struct device *dev, unsigned long *freq,
u32 flags)
{
struct tegra_devfreq *tegra = dev_get_drvdata(dev);
struct devfreq *devfreq = tegra->devfreq;
struct dev_pm_opp *opp;
unsigned long rate;
int err;
opp = devfreq_recommended_opp(dev, freq, flags);
if (IS_ERR(opp))
return PTR_ERR(opp);
rate = dev_pm_opp_get_freq(opp);
dev_pm_opp_put(opp);
err = clk_set_min_rate(tegra->emc_clock, rate);
if (err)
return err;
err = clk_set_rate(tegra->emc_clock, 0);
if (err)
goto restore_min_rate;
return 0;
restore_min_rate:
clk_set_min_rate(tegra->emc_clock, devfreq->previous_freq);
return err;
}
static int tegra_devfreq_get_dev_status(struct device *dev,
struct devfreq_dev_status *stat)
{
struct tegra_devfreq *tegra = dev_get_drvdata(dev);
/*
* EMC_COUNT returns number of memory events, that number is lower
* than the number of clocks. Conversion ratio of 1/8 results in a
* bit higher bandwidth than actually needed, it is good enough for
* the time being because drivers don't support requesting minimum
* needed memory bandwidth yet.
*
* TODO: adjust the ratio value once relevant drivers will support
* memory bandwidth management.
*/
stat->busy_time = readl_relaxed(tegra->regs + MC_STAT_EMC_COUNT);
stat->total_time = readl_relaxed(tegra->regs + MC_STAT_EMC_CLOCKS) / 8;
stat->current_frequency = clk_get_rate(tegra->emc_clock);
writel_relaxed(EMC_GATHER_CLEAR, tegra->regs + MC_STAT_CONTROL);
writel_relaxed(EMC_GATHER_ENABLE, tegra->regs + MC_STAT_CONTROL);
return 0;
}
static struct devfreq_dev_profile tegra_devfreq_profile = {
.polling_ms = 500,
.target = tegra_devfreq_target,
.get_dev_status = tegra_devfreq_get_dev_status,
};
static struct tegra_mc *tegra_get_memory_controller(void)
{
struct platform_device *pdev;
struct device_node *np;
struct tegra_mc *mc;
np = of_find_compatible_node(NULL, NULL, "nvidia,tegra20-mc-gart");
if (!np)
return ERR_PTR(-ENOENT);
pdev = of_find_device_by_node(np);
of_node_put(np);
if (!pdev)
return ERR_PTR(-ENODEV);
mc = platform_get_drvdata(pdev);
if (!mc)
return ERR_PTR(-EPROBE_DEFER);
return mc;
}
static int tegra_devfreq_probe(struct platform_device *pdev)
{
struct tegra_devfreq *tegra;
struct tegra_mc *mc;
unsigned long max_rate;
unsigned long rate;
int err;
mc = tegra_get_memory_controller();
if (IS_ERR(mc)) {
err = PTR_ERR(mc);
dev_err(&pdev->dev, "failed to get memory controller: %d\n",
err);
return err;
}
tegra = devm_kzalloc(&pdev->dev, sizeof(*tegra), GFP_KERNEL);
if (!tegra)
return -ENOMEM;
/* EMC is a system-critical clock that is always enabled */
tegra->emc_clock = devm_clk_get(&pdev->dev, "emc");
if (IS_ERR(tegra->emc_clock)) {
err = PTR_ERR(tegra->emc_clock);
dev_err(&pdev->dev, "failed to get emc clock: %d\n", err);
return err;
}
tegra->regs = mc->regs;
max_rate = clk_round_rate(tegra->emc_clock, ULONG_MAX);
for (rate = 0; rate <= max_rate; rate++) {
rate = clk_round_rate(tegra->emc_clock, rate);
err = dev_pm_opp_add(&pdev->dev, rate, 0);
if (err) {
dev_err(&pdev->dev, "failed to add opp: %d\n", err);
goto remove_opps;
}
}
/*
* Reset statistic gathers state, select global bandwidth for the
* statistics collection mode and set clocks counter saturation
* limit to maximum.
*/
writel_relaxed(0x00000000, tegra->regs + MC_STAT_CONTROL);
writel_relaxed(0x00000000, tegra->regs + MC_STAT_EMC_CONTROL);
writel_relaxed(0xffffffff, tegra->regs + MC_STAT_EMC_CLOCK_LIMIT);
platform_set_drvdata(pdev, tegra);
tegra->devfreq = devfreq_add_device(&pdev->dev, &tegra_devfreq_profile,
DEVFREQ_GOV_SIMPLE_ONDEMAND, NULL);
if (IS_ERR(tegra->devfreq)) {
err = PTR_ERR(tegra->devfreq);
goto remove_opps;
}
return 0;
remove_opps:
dev_pm_opp_remove_all_dynamic(&pdev->dev);
return err;
}
static int tegra_devfreq_remove(struct platform_device *pdev)
{
struct tegra_devfreq *tegra = platform_get_drvdata(pdev);
devfreq_remove_device(tegra->devfreq);
dev_pm_opp_remove_all_dynamic(&pdev->dev);
return 0;
}
static struct platform_driver tegra_devfreq_driver = {
.probe = tegra_devfreq_probe,
.remove = tegra_devfreq_remove,
.driver = {
.name = "tegra20-devfreq",
},
};
module_platform_driver(tegra_devfreq_driver);
MODULE_ALIAS("platform:tegra20-devfreq");
MODULE_AUTHOR("Dmitry Osipenko <digetx@gmail.com>");
MODULE_DESCRIPTION("NVIDIA Tegra20 devfreq driver");
MODULE_LICENSE("GPL v2");
...@@ -78,14 +78,20 @@ struct devfreq_event_ops { ...@@ -78,14 +78,20 @@ struct devfreq_event_ops {
* struct devfreq_event_desc - the descriptor of devfreq-event device * struct devfreq_event_desc - the descriptor of devfreq-event device
* *
* @name : the name of devfreq-event device. * @name : the name of devfreq-event device.
* @event_type : the type of the event determined and used by driver
* @driver_data : the private data for devfreq-event driver. * @driver_data : the private data for devfreq-event driver.
* @ops : the operation to control devfreq-event device. * @ops : the operation to control devfreq-event device.
* *
* Each devfreq-event device is described with a this structure. * Each devfreq-event device is described with a this structure.
* This structure contains the various data for devfreq-event device. * This structure contains the various data for devfreq-event device.
* The event_type describes what is going to be counted in the register.
* It might choose to count e.g. read requests, write data in bytes, etc.
* The full supported list of types is present in specyfic header in:
* include/dt-bindings/pmu/.
*/ */
struct devfreq_event_desc { struct devfreq_event_desc {
const char *name; const char *name;
u32 event_type;
void *driver_data; void *driver_data;
const struct devfreq_event_ops *ops; const struct devfreq_event_ops *ops;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment