mirror of
https://github.com/edk2-porting/linux-next.git
synced 2024-12-23 20:53:53 +08:00
Update devfreq for 5.11
Detailed description for this pull request: 1. Update devfreq core - Add new devfreq_frequency tracepoint to show the frequency change information. - Add governor feature flag. The devfreq governor is able to have the specific flag in order to contain the non-common feature. For example, if governor contains the 'immutable' feature, don't allow user to change the governor via sysfs. - Add governor sysfs attribute flag for each sysfs file. Prior to that devfreq subsystem show the all sysfs files regardless of governor type. But, some sysfs fils are not supported on the specific devfreq governor. In order to show the only supported sysfs files according to the governor, clarify the access permission of sysfs attributes according to governor. When adding the devfreq governor, can specify the available attribute information by using DEVFREQ_GOV_ATTR_* constant variable. The user can read or write the sysfs attributes in accordance to the specified attributes. - Clean-up the code to remove the duplicate code for the devfreq tracepoint and to remove redundant governor_name field from struct devfreq 2. Update exynos-bus.c devfreq driver - Add interconnect API support for the Samsung Exynos Bus Frequency driver of exynos-bus.c. Complementing the devfreq driver with an interconnect functionality allows to ensure the QoS requirements of devices accessing the system memory (e.g. video processing devices) are fulfilled and allows to avoid issues like the DMA underrun. 3. Update tegra devfreq driver - Add interconnect support and OPP interface for tegra30-devfreq.c. Also, it is to guarantee the QoS requirement of some devices like display controller. - Move tegra20-devfreq.c from drivers/devfreq/ into driver/memory/tegra/ in order to use the more proper monitoring feature such as EMC_STAT which is based in driver/memory/tegra/. - Separate the configuration information for different SoC on tegra30-devfrqe.c. The tegra30-devfreq.c had been supported both tegra30-actmon and tegra124-actmon devices. In order to use the more correct configuration data, separate them. - Use dev_err_probe() to handle the deferred probe error on tegra30-devfreq.c. 4. Pull the request of 'Tegra SoC and clock controller changes for v5.11' sent by Krzysztof Kozlowski <krzk@kernel.org> in order to prevent the build error. -----BEGIN PGP SIGNATURE----- iQJKBAABCgA0FiEEsSpuqBtbWtRe4rLGnM3fLN7rz1MFAl/TI+UWHGN3MDAuY2hv aUBzYW1zdW5nLmNvbQAKCRCczd8s3uvPU8M/EAClq1DatrIsc6eysfvUUYhZgGD4 QC3WrIQ858y9/rZT1If+uZeif6DonOSIYBetCa0Wo5kiuN3BvSCZEnAsndqRPjk1 NyvRbThXUCMrtlgah/rsVPRCgwS8UidTeCqhO1n2kvHlIINgyvqS8EVpDbrOjf8s 0MFAmsh/PK0bIQC53yHs24XhLm4ddk+K1Gw2xlyd+c2E/+6MGeKbtc1nfqSy337K 3GJe9IwC0JHQGC1tPi4wnA+/S+gK0/tP3RGPlF9fk+fnccWSDhIu6tQQh1hjaLwC 5hbjwavEcr67/E0/zkQs+Bm7mEHtHFERAOPKv3C4UzPCL5wg/Z6hVphaKb+hjFDE kD2JRCyNHwSFemu78+ooef2Af745AH9OJ6Z1ADwTyyI6wPS0qUm0/m3Eu33kJdPb f7v+DMkqEnzEGn6gMJ2/WAEXmGi7vjwp6W6bqf5Ft13te7K8OKK8jNiFcgDPXznN 7km9gG8/j/nS9J5s01VhxU9QmwD65RsKR8upBhIpkPoMktBoH7Q4P2Ab9XlZbliG F0p1S6dnwxQI3BIP+BwkGVwgSINPd6bd7PyTCQJx2d3VqF5JVW3jEfXouWw6h8zO pW+lgUBr8+M5K99UYj8wktROtt5kKQQ0yd85XBdIqSZWZJ2J/D71XxcDjvZdvF3L 4vAeDY224dRuy+PP7g== =t1gj -----END PGP SIGNATURE----- Merge tag 'devfreq-next-for-5.11' of git://git.kernel.org/pub/scm/linux/kernel/git/chanwoo/linux Pull devfreq updates for 5.11 from Chanwoo Choi: 1. Update devfreq core - Add new devfreq_frequency tracepoint to show the frequency change information. - Add governor feature flag. The devfreq governor is able to set the specific flag in order to support a non-common feature. For example, if the governor supports the 'immutable' feature, don't allow user space to change the governor via sysfs. - Add governor sysfs attribute flag for each sysfs file. Prior to that the devfreq subsystem allowed all of the sysfs files to be accessed regardless of the governor type. But some sysfs fils are not supported by specific devfreq governors. In order to only allow the sysfs files supported by the governor to be accessed, clarify the access permissions of sysfs attributes according to the governor. When adding the devfreq governor, specify the available attribute information by using DEVFREQ_GOV_ATTR_* symbols. The user can read or write the sysfs attributes in accordance to the specified access permissions. - Clean-up the code to reduce duplication for the devfreq tracepoint and to remove redundant governor_name field from struct devfreq. 2. Update exynos-bus.c devfreq driver - Add interconnect API support to the Samsung Exynos Bus Frequency driver, exynos-bus.c. Complementing the devfreq driver with interconnect functionality allows to ensure that the QoS requirements regarding devices accessing the system memory (e.g. video processing devices) will be met and allows to avoid issues like DMA underrun. 3. Update tegra devfreq driver - Add interconnect support and OPP interface to tegra30-devfreq.c. Also, it is to guarantee the QoS requirement of some devices like the display controller. - Move tegra20-devfreq.c from drivers/devfreq/ into drivers/memory/tegra/ in order to use the more proper monitoring feature such as EMC_STAT which is located in drivers/memory/tegra/. - Separate the configuration information for different SoCs in tegra30-devfrqe.c. The tegra30-devfreq.c had been supporting both tegra30-actmon and tegra124-actmon devices. In order to use the more correct configuration data, separate them. - Use dev_err_probe() to handle the deferred probe error in tegra30-devfreq.c. 4. Pull the request of 'Tegra SoC and clock controller changes for v5.11' sent by Krzysztof Kozlowski <krzk@kernel.org> in order to avoid a build error." * tag 'devfreq-next-for-5.11' of git://git.kernel.org/pub/scm/linux/kernel/git/chanwoo/linux: PM / devfreq: tegra30: Separate configurations per-SoC generation PM / devfreq: tegra30: Support interconnect and OPPs from device-tree PM / devfreq: tegra20: Deprecate in a favor of emc-stat based driver PM / devfreq: exynos-bus: Add registration of interconnect child device dt-bindings: devfreq: Add documentation for the interconnect properties soc/tegra: fuse: Add stub for tegra_sku_info soc/tegra: fuse: Export tegra_read_ram_code() clk: tegra: Export Tegra20 EMC kernel symbols PM / devfreq: tegra30: Silence deferred probe error PM / devfreq: tegra20: Relax Kconfig dependency PM / devfreq: tegra20: Silence deferred probe error PM / devfreq: Remove redundant governor_name from struct devfreq PM / devfreq: Add governor attribute flag for specifc sysfs nodes PM / devfreq: Add governor feature flag PM / devfreq: Add tracepoint for frequency changes PM / devfreq: Unify frequency change to devfreq_update_target func trace: events: devfreq: Use fixed indentation size to improve readability
This commit is contained in:
commit
d3569c149d
@ -37,20 +37,6 @@ Description:
|
||||
The /sys/class/devfreq/.../target_freq shows the next governor
|
||||
predicted target frequency of the corresponding devfreq object.
|
||||
|
||||
What: /sys/class/devfreq/.../polling_interval
|
||||
Date: September 2011
|
||||
Contact: MyungJoo Ham <myungjoo.ham@samsung.com>
|
||||
Description:
|
||||
The /sys/class/devfreq/.../polling_interval shows and sets
|
||||
the requested polling interval of the corresponding devfreq
|
||||
object. The values are represented in ms. If the value is
|
||||
less than 1 jiffy, it is considered to be 0, which means
|
||||
no polling. This value is meaningless if the governor is
|
||||
not polling; thus. If the governor is not using
|
||||
devfreq-provided central polling
|
||||
(/sys/class/devfreq/.../central_polling is 0), this value
|
||||
may be useless.
|
||||
|
||||
What: /sys/class/devfreq/.../trans_stat
|
||||
Date: October 2012
|
||||
Contact: MyungJoo Ham <myungjoo.ham@samsung.com>
|
||||
@ -66,14 +52,6 @@ Description:
|
||||
|
||||
echo 0 > /sys/class/devfreq/.../trans_stat
|
||||
|
||||
What: /sys/class/devfreq/.../userspace/set_freq
|
||||
Date: September 2011
|
||||
Contact: MyungJoo Ham <myungjoo.ham@samsung.com>
|
||||
Description:
|
||||
The /sys/class/devfreq/.../userspace/set_freq shows and
|
||||
sets the requested frequency for the devfreq object if
|
||||
userspace governor is in effect.
|
||||
|
||||
What: /sys/class/devfreq/.../available_frequencies
|
||||
Date: October 2012
|
||||
Contact: Nishanth Menon <nm@ti.com>
|
||||
@ -110,6 +88,35 @@ Description:
|
||||
The max_freq overrides min_freq because max_freq may be
|
||||
used to throttle devices to avoid overheating.
|
||||
|
||||
What: /sys/class/devfreq/.../polling_interval
|
||||
Date: September 2011
|
||||
Contact: MyungJoo Ham <myungjoo.ham@samsung.com>
|
||||
Description:
|
||||
The /sys/class/devfreq/.../polling_interval shows and sets
|
||||
the requested polling interval of the corresponding devfreq
|
||||
object. The values are represented in ms. If the value is
|
||||
less than 1 jiffy, it is considered to be 0, which means
|
||||
no polling. This value is meaningless if the governor is
|
||||
not polling; thus. If the governor is not using
|
||||
devfreq-provided central polling
|
||||
(/sys/class/devfreq/.../central_polling is 0), this value
|
||||
may be useless.
|
||||
|
||||
A list of governors that support the node:
|
||||
- simple_ondmenad
|
||||
- tegra_actmon
|
||||
|
||||
What: /sys/class/devfreq/.../userspace/set_freq
|
||||
Date: September 2011
|
||||
Contact: MyungJoo Ham <myungjoo.ham@samsung.com>
|
||||
Description:
|
||||
The /sys/class/devfreq/.../userspace/set_freq shows and
|
||||
sets the requested frequency for the devfreq object if
|
||||
userspace governor is in effect.
|
||||
|
||||
A list of governors that support the node:
|
||||
- userspace
|
||||
|
||||
What: /sys/class/devfreq/.../timer
|
||||
Date: July 2020
|
||||
Contact: Chanwoo Choi <cw00.choi@samsung.com>
|
||||
@ -122,3 +129,6 @@ Description:
|
||||
|
||||
echo deferrable > /sys/class/devfreq/.../timer
|
||||
echo delayed > /sys/class/devfreq/.../timer
|
||||
|
||||
A list of governors that support the node:
|
||||
- simple_ondemand
|
||||
|
@ -51,6 +51,19 @@ Optional properties only for parent bus device:
|
||||
- exynos,saturation-ratio: the percentage value which is used to calibrate
|
||||
the performance count against total cycle count.
|
||||
|
||||
Optional properties for the interconnect functionality (QoS frequency
|
||||
constraints):
|
||||
- #interconnect-cells: should be 0.
|
||||
- interconnects: as documented in ../interconnect.txt, describes a path at the
|
||||
higher level interconnects used by this interconnect provider.
|
||||
If this interconnect provider is directly linked to a top level interconnect
|
||||
provider the property contains only one phandle. The provider extends
|
||||
the interconnect graph by linking its node to a node registered by provider
|
||||
pointed to by first phandle in the 'interconnects' property.
|
||||
|
||||
- samsung,data-clock-ratio: ratio of the data throughput in B/s to minimum data
|
||||
clock frequency in Hz, default value is 8 when this property is missing.
|
||||
|
||||
Detailed correlation between sub-blocks and power line according to Exynos SoC:
|
||||
- In case of Exynos3250, there are two power line as following:
|
||||
VDD_MIF |--- DMC
|
||||
@ -419,3 +432,57 @@ Example2 :
|
||||
devfreq = <&bus_leftbus>;
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
Example 3:
|
||||
An interconnect path "bus_display -- bus_leftbus -- bus_dmc" on
|
||||
Exynos4412 SoC with video mixer as an interconnect consumer device.
|
||||
|
||||
soc {
|
||||
bus_dmc: bus_dmc {
|
||||
compatible = "samsung,exynos-bus";
|
||||
clocks = <&clock CLK_DIV_DMC>;
|
||||
clock-names = "bus";
|
||||
operating-points-v2 = <&bus_dmc_opp_table>;
|
||||
samsung,data-clock-ratio = <4>;
|
||||
#interconnect-cells = <0>;
|
||||
};
|
||||
|
||||
bus_leftbus: bus_leftbus {
|
||||
compatible = "samsung,exynos-bus";
|
||||
clocks = <&clock CLK_DIV_GDL>;
|
||||
clock-names = "bus";
|
||||
operating-points-v2 = <&bus_leftbus_opp_table>;
|
||||
#interconnect-cells = <0>;
|
||||
interconnects = <&bus_dmc>;
|
||||
};
|
||||
|
||||
bus_display: bus_display {
|
||||
compatible = "samsung,exynos-bus";
|
||||
clocks = <&clock CLK_ACLK160>;
|
||||
clock-names = "bus";
|
||||
operating-points-v2 = <&bus_display_opp_table>;
|
||||
#interconnect-cells = <0>;
|
||||
interconnects = <&bus_leftbus &bus_dmc>;
|
||||
};
|
||||
|
||||
bus_dmc_opp_table: opp_table1 {
|
||||
compatible = "operating-points-v2";
|
||||
/* ... */
|
||||
}
|
||||
|
||||
bus_leftbus_opp_table: opp_table3 {
|
||||
compatible = "operating-points-v2";
|
||||
/* ... */
|
||||
};
|
||||
|
||||
bus_display_opp_table: opp_table4 {
|
||||
compatible = "operating-points-v2";
|
||||
/* .. */
|
||||
};
|
||||
|
||||
&mixer {
|
||||
compatible = "samsung,exynos4212-mixer";
|
||||
interconnects = <&bus_display &bus_dmc>;
|
||||
/* ... */
|
||||
};
|
||||
};
|
||||
|
@ -11313,7 +11313,6 @@ L: linux-pm@vger.kernel.org
|
||||
L: linux-tegra@vger.kernel.org
|
||||
T: git git://git.kernel.org/pub/scm/linux/kernel/git/chanwoo/linux.git
|
||||
S: Maintained
|
||||
F: drivers/devfreq/tegra20-devfreq.c
|
||||
F: drivers/devfreq/tegra30-devfreq.c
|
||||
|
||||
MEMORY MANAGEMENT
|
||||
|
@ -13,6 +13,7 @@
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/clk/tegra.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
@ -235,6 +236,7 @@ void tegra20_clk_set_emc_round_callback(tegra20_clk_emc_round_cb *round_cb,
|
||||
emc->cb_arg = cb_arg;
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(tegra20_clk_set_emc_round_callback);
|
||||
|
||||
bool tegra20_clk_emc_driver_available(struct clk_hw *emc_hw)
|
||||
{
|
||||
@ -291,3 +293,4 @@ int tegra20_clk_prepare_emc_mc_same_freq(struct clk *emc_clk, bool same)
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(tegra20_clk_prepare_emc_mc_same_freq);
|
||||
|
@ -121,16 +121,6 @@ config ARM_TEGRA_DEVFREQ
|
||||
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
|
||||
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 && HAVE_ARM_SMCCC) || \
|
||||
|
@ -13,7 +13,6 @@ obj-$(CONFIG_ARM_IMX_BUS_DEVFREQ) += imx-bus.o
|
||||
obj-$(CONFIG_ARM_IMX8M_DDRC_DEVFREQ) += imx8m-ddrc.o
|
||||
obj-$(CONFIG_ARM_RK3399_DMC_DEVFREQ) += rk3399_dmc.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/
|
||||
|
@ -31,6 +31,8 @@
|
||||
#define CREATE_TRACE_POINTS
|
||||
#include <trace/events/devfreq.h>
|
||||
|
||||
#define IS_SUPPORTED_FLAG(f, name) ((f & DEVFREQ_GOV_FLAG_##name) ? true : false)
|
||||
#define IS_SUPPORTED_ATTR(f, name) ((f & DEVFREQ_GOV_ATTR_##name) ? true : false)
|
||||
#define HZ_PER_KHZ 1000
|
||||
|
||||
static struct class *devfreq_class;
|
||||
@ -367,6 +369,14 @@ static int devfreq_set_target(struct devfreq *devfreq, unsigned long new_freq,
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
* Print devfreq_frequency trace information between DEVFREQ_PRECHANGE
|
||||
* and DEVFREQ_POSTCHANGE because for showing the correct frequency
|
||||
* change order of between devfreq device and passive devfreq device.
|
||||
*/
|
||||
if (trace_devfreq_frequency_enabled() && new_freq != cur_freq)
|
||||
trace_devfreq_frequency(devfreq, new_freq, cur_freq);
|
||||
|
||||
freqs.new = new_freq;
|
||||
devfreq_notify_transition(devfreq, &freqs, DEVFREQ_POSTCHANGE);
|
||||
|
||||
@ -382,18 +392,19 @@ static int devfreq_set_target(struct devfreq *devfreq, unsigned long new_freq,
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Load monitoring helper functions for governors use */
|
||||
|
||||
/**
|
||||
* update_devfreq() - Reevaluate the device and configure frequency.
|
||||
* devfreq_update_target() - Reevaluate the device and configure frequency
|
||||
* on the final stage.
|
||||
* @devfreq: the devfreq instance.
|
||||
* @freq: the new frequency of parent device. This argument
|
||||
* is only used for devfreq device using passive governor.
|
||||
*
|
||||
* Note: Lock devfreq->lock before calling update_devfreq
|
||||
* This function is exported for governors.
|
||||
* Note: Lock devfreq->lock before calling devfreq_update_target. This function
|
||||
* should be only used by both update_devfreq() and devfreq governors.
|
||||
*/
|
||||
int update_devfreq(struct devfreq *devfreq)
|
||||
int devfreq_update_target(struct devfreq *devfreq, unsigned long freq)
|
||||
{
|
||||
unsigned long freq, min_freq, max_freq;
|
||||
unsigned long min_freq, max_freq;
|
||||
int err = 0;
|
||||
u32 flags = 0;
|
||||
|
||||
@ -418,7 +429,21 @@ int update_devfreq(struct devfreq *devfreq)
|
||||
}
|
||||
|
||||
return devfreq_set_target(devfreq, freq, flags);
|
||||
}
|
||||
EXPORT_SYMBOL(devfreq_update_target);
|
||||
|
||||
/* Load monitoring helper functions for governors use */
|
||||
|
||||
/**
|
||||
* update_devfreq() - Reevaluate the device and configure frequency.
|
||||
* @devfreq: the devfreq instance.
|
||||
*
|
||||
* Note: Lock devfreq->lock before calling update_devfreq
|
||||
* This function is exported for governors.
|
||||
*/
|
||||
int update_devfreq(struct devfreq *devfreq)
|
||||
{
|
||||
return devfreq_update_target(devfreq, 0L);
|
||||
}
|
||||
EXPORT_SYMBOL(update_devfreq);
|
||||
|
||||
@ -456,7 +481,7 @@ static void devfreq_monitor(struct work_struct *work)
|
||||
*/
|
||||
void devfreq_monitor_start(struct devfreq *devfreq)
|
||||
{
|
||||
if (devfreq->governor->interrupt_driven)
|
||||
if (IS_SUPPORTED_FLAG(devfreq->governor->flags, IRQ_DRIVEN))
|
||||
return;
|
||||
|
||||
switch (devfreq->profile->timer) {
|
||||
@ -486,7 +511,7 @@ EXPORT_SYMBOL(devfreq_monitor_start);
|
||||
*/
|
||||
void devfreq_monitor_stop(struct devfreq *devfreq)
|
||||
{
|
||||
if (devfreq->governor->interrupt_driven)
|
||||
if (IS_SUPPORTED_FLAG(devfreq->governor->flags, IRQ_DRIVEN))
|
||||
return;
|
||||
|
||||
cancel_delayed_work_sync(&devfreq->work);
|
||||
@ -517,7 +542,7 @@ void devfreq_monitor_suspend(struct devfreq *devfreq)
|
||||
devfreq->stop_polling = true;
|
||||
mutex_unlock(&devfreq->lock);
|
||||
|
||||
if (devfreq->governor->interrupt_driven)
|
||||
if (IS_SUPPORTED_FLAG(devfreq->governor->flags, IRQ_DRIVEN))
|
||||
return;
|
||||
|
||||
cancel_delayed_work_sync(&devfreq->work);
|
||||
@ -537,12 +562,13 @@ void devfreq_monitor_resume(struct devfreq *devfreq)
|
||||
unsigned long freq;
|
||||
|
||||
mutex_lock(&devfreq->lock);
|
||||
|
||||
if (IS_SUPPORTED_FLAG(devfreq->governor->flags, IRQ_DRIVEN))
|
||||
goto out_update;
|
||||
|
||||
if (!devfreq->stop_polling)
|
||||
goto out;
|
||||
|
||||
if (devfreq->governor->interrupt_driven)
|
||||
goto out_update;
|
||||
|
||||
if (!delayed_work_pending(&devfreq->work) &&
|
||||
devfreq->profile->polling_ms)
|
||||
queue_delayed_work(devfreq_wq, &devfreq->work,
|
||||
@ -577,10 +603,10 @@ void devfreq_update_interval(struct devfreq *devfreq, unsigned int *delay)
|
||||
mutex_lock(&devfreq->lock);
|
||||
devfreq->profile->polling_ms = new_delay;
|
||||
|
||||
if (devfreq->stop_polling)
|
||||
if (IS_SUPPORTED_FLAG(devfreq->governor->flags, IRQ_DRIVEN))
|
||||
goto out;
|
||||
|
||||
if (devfreq->governor->interrupt_driven)
|
||||
if (devfreq->stop_polling)
|
||||
goto out;
|
||||
|
||||
/* if new delay is zero, stop polling */
|
||||
@ -735,6 +761,11 @@ static void devfreq_dev_release(struct device *dev)
|
||||
kfree(devfreq);
|
||||
}
|
||||
|
||||
static void create_sysfs_files(struct devfreq *devfreq,
|
||||
const struct devfreq_governor *gov);
|
||||
static void remove_sysfs_files(struct devfreq *devfreq,
|
||||
const struct devfreq_governor *gov);
|
||||
|
||||
/**
|
||||
* devfreq_add_device() - Add devfreq feature to the device
|
||||
* @dev: the device to add devfreq feature.
|
||||
@ -780,7 +811,6 @@ struct devfreq *devfreq_add_device(struct device *dev,
|
||||
devfreq->dev.release = devfreq_dev_release;
|
||||
INIT_LIST_HEAD(&devfreq->node);
|
||||
devfreq->profile = profile;
|
||||
strscpy(devfreq->governor_name, governor_name, DEVFREQ_NAME_LEN);
|
||||
devfreq->previous_freq = profile->initial_freq;
|
||||
devfreq->last_status.current_frequency = profile->initial_freq;
|
||||
devfreq->data = data;
|
||||
@ -876,7 +906,7 @@ struct devfreq *devfreq_add_device(struct device *dev,
|
||||
|
||||
mutex_lock(&devfreq_list_lock);
|
||||
|
||||
governor = try_then_request_governor(devfreq->governor_name);
|
||||
governor = try_then_request_governor(governor_name);
|
||||
if (IS_ERR(governor)) {
|
||||
dev_err(dev, "%s: Unable to find governor for the device\n",
|
||||
__func__);
|
||||
@ -892,6 +922,7 @@ struct devfreq *devfreq_add_device(struct device *dev,
|
||||
__func__);
|
||||
goto err_init;
|
||||
}
|
||||
create_sysfs_files(devfreq, devfreq->governor);
|
||||
|
||||
list_add(&devfreq->node, &devfreq_list);
|
||||
|
||||
@ -922,9 +953,12 @@ int devfreq_remove_device(struct devfreq *devfreq)
|
||||
if (!devfreq)
|
||||
return -EINVAL;
|
||||
|
||||
if (devfreq->governor)
|
||||
if (devfreq->governor) {
|
||||
devfreq->governor->event_handler(devfreq,
|
||||
DEVFREQ_GOV_STOP, NULL);
|
||||
remove_sysfs_files(devfreq, devfreq->governor);
|
||||
}
|
||||
|
||||
device_unregister(&devfreq->dev);
|
||||
|
||||
return 0;
|
||||
@ -1214,7 +1248,7 @@ int devfreq_add_governor(struct devfreq_governor *governor)
|
||||
int ret = 0;
|
||||
struct device *dev = devfreq->dev.parent;
|
||||
|
||||
if (!strncmp(devfreq->governor_name, governor->name,
|
||||
if (!strncmp(devfreq->governor->name, governor->name,
|
||||
DEVFREQ_NAME_LEN)) {
|
||||
/* The following should never occur */
|
||||
if (devfreq->governor) {
|
||||
@ -1276,7 +1310,7 @@ int devfreq_remove_governor(struct devfreq_governor *governor)
|
||||
int ret;
|
||||
struct device *dev = devfreq->dev.parent;
|
||||
|
||||
if (!strncmp(devfreq->governor_name, governor->name,
|
||||
if (!strncmp(devfreq->governor->name, governor->name,
|
||||
DEVFREQ_NAME_LEN)) {
|
||||
/* we should have a devfreq governor! */
|
||||
if (!devfreq->governor) {
|
||||
@ -1347,36 +1381,53 @@ static ssize_t governor_store(struct device *dev, struct device_attribute *attr,
|
||||
if (df->governor == governor) {
|
||||
ret = 0;
|
||||
goto out;
|
||||
} else if (df->governor->immutable || governor->immutable) {
|
||||
} else if (IS_SUPPORTED_FLAG(df->governor->flags, IMMUTABLE)
|
||||
|| IS_SUPPORTED_FLAG(governor->flags, IMMUTABLE)) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* Stop the current governor and remove the specific sysfs files
|
||||
* which depend on current governor.
|
||||
*/
|
||||
ret = df->governor->event_handler(df, DEVFREQ_GOV_STOP, NULL);
|
||||
if (ret) {
|
||||
dev_warn(dev, "%s: Governor %s not stopped(%d)\n",
|
||||
__func__, df->governor->name, ret);
|
||||
goto out;
|
||||
}
|
||||
remove_sysfs_files(df, df->governor);
|
||||
|
||||
/*
|
||||
* Start the new governor and create the specific sysfs files
|
||||
* which depend on the new governor.
|
||||
*/
|
||||
prev_governor = df->governor;
|
||||
df->governor = governor;
|
||||
strncpy(df->governor_name, governor->name, DEVFREQ_NAME_LEN);
|
||||
ret = df->governor->event_handler(df, DEVFREQ_GOV_START, NULL);
|
||||
if (ret) {
|
||||
dev_warn(dev, "%s: Governor %s not started(%d)\n",
|
||||
__func__, df->governor->name, ret);
|
||||
|
||||
/* Restore previous governor */
|
||||
df->governor = prev_governor;
|
||||
strncpy(df->governor_name, prev_governor->name,
|
||||
DEVFREQ_NAME_LEN);
|
||||
ret = df->governor->event_handler(df, DEVFREQ_GOV_START, NULL);
|
||||
if (ret) {
|
||||
dev_err(dev,
|
||||
"%s: reverting to Governor %s failed (%d)\n",
|
||||
__func__, df->governor_name, ret);
|
||||
__func__, prev_governor->name, ret);
|
||||
df->governor = NULL;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Create the sysfs files for the new governor. But if failed to start
|
||||
* the new governor, restore the sysfs files of previous governor.
|
||||
*/
|
||||
create_sysfs_files(df, df->governor);
|
||||
|
||||
out:
|
||||
mutex_unlock(&devfreq_list_lock);
|
||||
|
||||
@ -1402,9 +1453,9 @@ static ssize_t available_governors_show(struct device *d,
|
||||
* The devfreq with immutable governor (e.g., passive) shows
|
||||
* only own governor.
|
||||
*/
|
||||
if (df->governor->immutable) {
|
||||
if (IS_SUPPORTED_FLAG(df->governor->flags, IMMUTABLE)) {
|
||||
count = scnprintf(&buf[count], DEVFREQ_NAME_LEN,
|
||||
"%s ", df->governor_name);
|
||||
"%s ", df->governor->name);
|
||||
/*
|
||||
* The devfreq device shows the registered governor except for
|
||||
* immutable governors such as passive governor .
|
||||
@ -1413,7 +1464,7 @@ static ssize_t available_governors_show(struct device *d,
|
||||
struct devfreq_governor *governor;
|
||||
|
||||
list_for_each_entry(governor, &devfreq_governor_list, node) {
|
||||
if (governor->immutable)
|
||||
if (IS_SUPPORTED_FLAG(governor->flags, IMMUTABLE))
|
||||
continue;
|
||||
count += scnprintf(&buf[count], (PAGE_SIZE - count - 2),
|
||||
"%s ", governor->name);
|
||||
@ -1458,39 +1509,6 @@ static ssize_t target_freq_show(struct device *dev,
|
||||
}
|
||||
static DEVICE_ATTR_RO(target_freq);
|
||||
|
||||
static ssize_t polling_interval_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct devfreq *df = to_devfreq(dev);
|
||||
|
||||
if (!df->profile)
|
||||
return -EINVAL;
|
||||
|
||||
return sprintf(buf, "%d\n", df->profile->polling_ms);
|
||||
}
|
||||
|
||||
static ssize_t polling_interval_store(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct devfreq *df = to_devfreq(dev);
|
||||
unsigned int value;
|
||||
int ret;
|
||||
|
||||
if (!df->governor)
|
||||
return -EINVAL;
|
||||
|
||||
ret = sscanf(buf, "%u", &value);
|
||||
if (ret != 1)
|
||||
return -EINVAL;
|
||||
|
||||
df->governor->event_handler(df, DEVFREQ_GOV_UPDATE_INTERVAL, &value);
|
||||
ret = count;
|
||||
|
||||
return ret;
|
||||
}
|
||||
static DEVICE_ATTR_RW(polling_interval);
|
||||
|
||||
static ssize_t min_freq_store(struct device *dev, struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
@ -1698,6 +1716,53 @@ static ssize_t trans_stat_store(struct device *dev,
|
||||
}
|
||||
static DEVICE_ATTR_RW(trans_stat);
|
||||
|
||||
static struct attribute *devfreq_attrs[] = {
|
||||
&dev_attr_name.attr,
|
||||
&dev_attr_governor.attr,
|
||||
&dev_attr_available_governors.attr,
|
||||
&dev_attr_cur_freq.attr,
|
||||
&dev_attr_available_frequencies.attr,
|
||||
&dev_attr_target_freq.attr,
|
||||
&dev_attr_min_freq.attr,
|
||||
&dev_attr_max_freq.attr,
|
||||
&dev_attr_trans_stat.attr,
|
||||
NULL,
|
||||
};
|
||||
ATTRIBUTE_GROUPS(devfreq);
|
||||
|
||||
static ssize_t polling_interval_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct devfreq *df = to_devfreq(dev);
|
||||
|
||||
if (!df->profile)
|
||||
return -EINVAL;
|
||||
|
||||
return sprintf(buf, "%d\n", df->profile->polling_ms);
|
||||
}
|
||||
|
||||
static ssize_t polling_interval_store(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct devfreq *df = to_devfreq(dev);
|
||||
unsigned int value;
|
||||
int ret;
|
||||
|
||||
if (!df->governor)
|
||||
return -EINVAL;
|
||||
|
||||
ret = sscanf(buf, "%u", &value);
|
||||
if (ret != 1)
|
||||
return -EINVAL;
|
||||
|
||||
df->governor->event_handler(df, DEVFREQ_GOV_UPDATE_INTERVAL, &value);
|
||||
ret = count;
|
||||
|
||||
return ret;
|
||||
}
|
||||
static DEVICE_ATTR_RW(polling_interval);
|
||||
|
||||
static ssize_t timer_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
@ -1761,21 +1826,36 @@ out:
|
||||
}
|
||||
static DEVICE_ATTR_RW(timer);
|
||||
|
||||
static struct attribute *devfreq_attrs[] = {
|
||||
&dev_attr_name.attr,
|
||||
&dev_attr_governor.attr,
|
||||
&dev_attr_available_governors.attr,
|
||||
&dev_attr_cur_freq.attr,
|
||||
&dev_attr_available_frequencies.attr,
|
||||
&dev_attr_target_freq.attr,
|
||||
&dev_attr_polling_interval.attr,
|
||||
&dev_attr_min_freq.attr,
|
||||
&dev_attr_max_freq.attr,
|
||||
&dev_attr_trans_stat.attr,
|
||||
&dev_attr_timer.attr,
|
||||
NULL,
|
||||
};
|
||||
ATTRIBUTE_GROUPS(devfreq);
|
||||
#define CREATE_SYSFS_FILE(df, name) \
|
||||
{ \
|
||||
int ret; \
|
||||
ret = sysfs_create_file(&df->dev.kobj, &dev_attr_##name.attr); \
|
||||
if (ret < 0) { \
|
||||
dev_warn(&df->dev, \
|
||||
"Unable to create attr(%s)\n", "##name"); \
|
||||
} \
|
||||
} \
|
||||
|
||||
/* Create the specific sysfs files which depend on each governor. */
|
||||
static void create_sysfs_files(struct devfreq *devfreq,
|
||||
const struct devfreq_governor *gov)
|
||||
{
|
||||
if (IS_SUPPORTED_ATTR(gov->attrs, POLLING_INTERVAL))
|
||||
CREATE_SYSFS_FILE(devfreq, polling_interval);
|
||||
if (IS_SUPPORTED_ATTR(gov->attrs, TIMER))
|
||||
CREATE_SYSFS_FILE(devfreq, timer);
|
||||
}
|
||||
|
||||
/* Remove the specific sysfs files which depend on each governor. */
|
||||
static void remove_sysfs_files(struct devfreq *devfreq,
|
||||
const struct devfreq_governor *gov)
|
||||
{
|
||||
if (IS_SUPPORTED_ATTR(gov->attrs, POLLING_INTERVAL))
|
||||
sysfs_remove_file(&devfreq->dev.kobj,
|
||||
&dev_attr_polling_interval.attr);
|
||||
if (IS_SUPPORTED_ATTR(gov->attrs, TIMER))
|
||||
sysfs_remove_file(&devfreq->dev.kobj, &dev_attr_timer.attr);
|
||||
}
|
||||
|
||||
/**
|
||||
* devfreq_summary_show() - Show the summary of the devfreq devices
|
||||
@ -1818,7 +1898,7 @@ static int devfreq_summary_show(struct seq_file *s, void *data)
|
||||
|
||||
list_for_each_entry_reverse(devfreq, &devfreq_list, node) {
|
||||
#if IS_ENABLED(CONFIG_DEVFREQ_GOV_PASSIVE)
|
||||
if (!strncmp(devfreq->governor_name, DEVFREQ_GOV_PASSIVE,
|
||||
if (!strncmp(devfreq->governor->name, DEVFREQ_GOV_PASSIVE,
|
||||
DEVFREQ_NAME_LEN)) {
|
||||
struct devfreq_passive_data *data = devfreq->data;
|
||||
|
||||
@ -1832,15 +1912,19 @@ static int devfreq_summary_show(struct seq_file *s, void *data)
|
||||
mutex_lock(&devfreq->lock);
|
||||
cur_freq = devfreq->previous_freq;
|
||||
get_freq_range(devfreq, &min_freq, &max_freq);
|
||||
polling_ms = devfreq->profile->polling_ms;
|
||||
timer = devfreq->profile->timer;
|
||||
|
||||
if (IS_SUPPORTED_ATTR(devfreq->governor->attrs, POLLING_INTERVAL))
|
||||
polling_ms = devfreq->profile->polling_ms;
|
||||
else
|
||||
polling_ms = 0;
|
||||
mutex_unlock(&devfreq->lock);
|
||||
|
||||
seq_printf(s,
|
||||
"%-30s %-30s %-15s %-10s %10d %12ld %12ld %12ld\n",
|
||||
dev_name(&devfreq->dev),
|
||||
p_devfreq ? dev_name(&p_devfreq->dev) : "null",
|
||||
devfreq->governor_name,
|
||||
devfreq->governor->name,
|
||||
polling_ms ? timer_name[timer] : "null",
|
||||
polling_ms,
|
||||
cur_freq,
|
||||
|
@ -24,6 +24,7 @@
|
||||
|
||||
struct exynos_bus {
|
||||
struct device *dev;
|
||||
struct platform_device *icc_pdev;
|
||||
|
||||
struct devfreq *devfreq;
|
||||
struct devfreq_event_dev **edev;
|
||||
@ -156,6 +157,8 @@ static void exynos_bus_exit(struct device *dev)
|
||||
if (ret < 0)
|
||||
dev_warn(dev, "failed to disable the devfreq-event devices\n");
|
||||
|
||||
platform_device_unregister(bus->icc_pdev);
|
||||
|
||||
dev_pm_opp_of_remove_table(dev);
|
||||
clk_disable_unprepare(bus->clk);
|
||||
if (bus->opp_table) {
|
||||
@ -168,6 +171,8 @@ static void exynos_bus_passive_exit(struct device *dev)
|
||||
{
|
||||
struct exynos_bus *bus = dev_get_drvdata(dev);
|
||||
|
||||
platform_device_unregister(bus->icc_pdev);
|
||||
|
||||
dev_pm_opp_of_remove_table(dev);
|
||||
clk_disable_unprepare(bus->clk);
|
||||
}
|
||||
@ -432,6 +437,18 @@ static int exynos_bus_probe(struct platform_device *pdev)
|
||||
if (ret < 0)
|
||||
goto err;
|
||||
|
||||
/* Create child platform device for the interconnect provider */
|
||||
if (of_get_property(dev->of_node, "#interconnect-cells", NULL)) {
|
||||
bus->icc_pdev = platform_device_register_data(
|
||||
dev, "exynos-generic-icc",
|
||||
PLATFORM_DEVID_AUTO, NULL, 0);
|
||||
|
||||
if (IS_ERR(bus->icc_pdev)) {
|
||||
ret = PTR_ERR(bus->icc_pdev);
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
|
||||
max_state = bus->devfreq->profile->max_state;
|
||||
min_freq = (bus->devfreq->profile->freq_table[0] / 1000);
|
||||
max_freq = (bus->devfreq->profile->freq_table[max_state - 1] / 1000);
|
||||
|
@ -13,6 +13,8 @@
|
||||
|
||||
#include <linux/devfreq.h>
|
||||
|
||||
#define DEVFREQ_NAME_LEN 16
|
||||
|
||||
#define to_devfreq(DEV) container_of((DEV), struct devfreq, dev)
|
||||
|
||||
/* Devfreq events */
|
||||
@ -25,14 +27,32 @@
|
||||
#define DEVFREQ_MIN_FREQ 0
|
||||
#define DEVFREQ_MAX_FREQ ULONG_MAX
|
||||
|
||||
/*
|
||||
* Definition of the governor feature flags
|
||||
* - DEVFREQ_GOV_FLAG_IMMUTABLE
|
||||
* : This governor is never changeable to other governors.
|
||||
* - DEVFREQ_GOV_FLAG_IRQ_DRIVEN
|
||||
* : The devfreq won't schedule the work for this governor.
|
||||
*/
|
||||
#define DEVFREQ_GOV_FLAG_IMMUTABLE BIT(0)
|
||||
#define DEVFREQ_GOV_FLAG_IRQ_DRIVEN BIT(1)
|
||||
|
||||
/*
|
||||
* Definition of governor attribute flags except for common sysfs attributes
|
||||
* - DEVFREQ_GOV_ATTR_POLLING_INTERVAL
|
||||
* : Indicate polling_interal sysfs attribute
|
||||
* - DEVFREQ_GOV_ATTR_TIMER
|
||||
* : Indicate timer sysfs attribute
|
||||
*/
|
||||
#define DEVFREQ_GOV_ATTR_POLLING_INTERVAL BIT(0)
|
||||
#define DEVFREQ_GOV_ATTR_TIMER BIT(1)
|
||||
|
||||
/**
|
||||
* struct devfreq_governor - Devfreq policy governor
|
||||
* @node: list node - contains registered devfreq governors
|
||||
* @name: Governor's name
|
||||
* @immutable: Immutable flag for governor. If the value is 1,
|
||||
* this governor is never changeable to other governor.
|
||||
* @interrupt_driven: Devfreq core won't schedule polling work for this
|
||||
* governor if value is set to 1.
|
||||
* @attrs: Governor's sysfs attribute flags
|
||||
* @flags: Governor's feature flags
|
||||
* @get_target_freq: Returns desired operating frequency for the device.
|
||||
* Basically, get_target_freq will run
|
||||
* devfreq_dev_profile.get_dev_status() to get the
|
||||
@ -50,8 +70,8 @@ struct devfreq_governor {
|
||||
struct list_head node;
|
||||
|
||||
const char name[DEVFREQ_NAME_LEN];
|
||||
const unsigned int immutable;
|
||||
const unsigned int interrupt_driven;
|
||||
const u64 attrs;
|
||||
const u64 flags;
|
||||
int (*get_target_freq)(struct devfreq *this, unsigned long *freq);
|
||||
int (*event_handler)(struct devfreq *devfreq,
|
||||
unsigned int event, void *data);
|
||||
@ -67,6 +87,7 @@ int devfreq_add_governor(struct devfreq_governor *governor);
|
||||
int devfreq_remove_governor(struct devfreq_governor *governor);
|
||||
|
||||
int devfreq_update_status(struct devfreq *devfreq, unsigned long freq);
|
||||
int devfreq_update_target(struct devfreq *devfreq, unsigned long freq);
|
||||
|
||||
static inline int devfreq_update_stats(struct devfreq *df)
|
||||
{
|
||||
|
@ -92,36 +92,6 @@ out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int update_devfreq_passive(struct devfreq *devfreq, unsigned long freq)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (!devfreq->governor)
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock_nested(&devfreq->lock, SINGLE_DEPTH_NESTING);
|
||||
|
||||
ret = devfreq->governor->get_target_freq(devfreq, &freq);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
ret = devfreq->profile->target(devfreq->dev.parent, &freq, 0);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
if (devfreq->profile->freq_table
|
||||
&& (devfreq_update_status(devfreq, freq)))
|
||||
dev_err(&devfreq->dev,
|
||||
"Couldn't update frequency transition information.\n");
|
||||
|
||||
devfreq->previous_freq = freq;
|
||||
|
||||
out:
|
||||
mutex_unlock(&devfreq->lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int devfreq_passive_notifier_call(struct notifier_block *nb,
|
||||
unsigned long event, void *ptr)
|
||||
{
|
||||
@ -131,17 +101,25 @@ static int devfreq_passive_notifier_call(struct notifier_block *nb,
|
||||
struct devfreq *parent = (struct devfreq *)data->parent;
|
||||
struct devfreq_freqs *freqs = (struct devfreq_freqs *)ptr;
|
||||
unsigned long freq = freqs->new;
|
||||
int ret = 0;
|
||||
|
||||
mutex_lock_nested(&devfreq->lock, SINGLE_DEPTH_NESTING);
|
||||
switch (event) {
|
||||
case DEVFREQ_PRECHANGE:
|
||||
if (parent->previous_freq > freq)
|
||||
update_devfreq_passive(devfreq, freq);
|
||||
ret = devfreq_update_target(devfreq, freq);
|
||||
|
||||
break;
|
||||
case DEVFREQ_POSTCHANGE:
|
||||
if (parent->previous_freq < freq)
|
||||
update_devfreq_passive(devfreq, freq);
|
||||
ret = devfreq_update_target(devfreq, freq);
|
||||
break;
|
||||
}
|
||||
mutex_unlock(&devfreq->lock);
|
||||
|
||||
if (ret < 0)
|
||||
dev_warn(&devfreq->dev,
|
||||
"failed to update devfreq using passive governor\n");
|
||||
|
||||
return NOTIFY_DONE;
|
||||
}
|
||||
@ -180,7 +158,7 @@ static int devfreq_passive_event_handler(struct devfreq *devfreq,
|
||||
|
||||
static struct devfreq_governor devfreq_passive = {
|
||||
.name = DEVFREQ_GOV_PASSIVE,
|
||||
.immutable = 1,
|
||||
.flags = DEVFREQ_GOV_FLAG_IMMUTABLE,
|
||||
.get_target_freq = devfreq_passive_get_target_freq,
|
||||
.event_handler = devfreq_passive_event_handler,
|
||||
};
|
||||
|
@ -117,6 +117,8 @@ static int devfreq_simple_ondemand_handler(struct devfreq *devfreq,
|
||||
|
||||
static struct devfreq_governor devfreq_simple_ondemand = {
|
||||
.name = DEVFREQ_GOV_SIMPLE_ONDEMAND,
|
||||
.attrs = DEVFREQ_GOV_ATTR_POLLING_INTERVAL
|
||||
| DEVFREQ_GOV_ATTR_TIMER,
|
||||
.get_target_freq = devfreq_simple_ondemand_func,
|
||||
.event_handler = devfreq_simple_ondemand_handler,
|
||||
};
|
||||
|
@ -1,212 +0,0 @@
|
||||
// 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");
|
@ -19,6 +19,8 @@
|
||||
#include <linux/reset.h>
|
||||
#include <linux/workqueue.h>
|
||||
|
||||
#include <soc/tegra/fuse.h>
|
||||
|
||||
#include "governor.h"
|
||||
|
||||
#define ACTMON_GLB_STATUS 0x0
|
||||
@ -55,13 +57,6 @@
|
||||
#define ACTMON_BELOW_WMARK_WINDOW 3
|
||||
#define ACTMON_BOOST_FREQ_STEP 16000
|
||||
|
||||
/*
|
||||
* Activity counter is incremented every 256 memory transactions, and each
|
||||
* transaction takes 4 EMC clocks for Tegra124; So the COUNT_WEIGHT is
|
||||
* 4 * 256 = 1024.
|
||||
*/
|
||||
#define ACTMON_COUNT_WEIGHT 0x400
|
||||
|
||||
/*
|
||||
* ACTMON_AVERAGE_WINDOW_LOG2: default value for @DEV_CTRL_K_VAL, which
|
||||
* translates to 2 ^ (K_VAL + 1). ex: 2 ^ (6 + 1) = 128
|
||||
@ -109,7 +104,7 @@ enum tegra_actmon_device {
|
||||
MCCPU,
|
||||
};
|
||||
|
||||
static const struct tegra_devfreq_device_config actmon_device_configs[] = {
|
||||
static const struct tegra_devfreq_device_config tegra124_device_configs[] = {
|
||||
{
|
||||
/* MCALL: All memory accesses (including from the CPUs) */
|
||||
.offset = 0x1c0,
|
||||
@ -131,6 +126,28 @@ static const struct tegra_devfreq_device_config actmon_device_configs[] = {
|
||||
},
|
||||
};
|
||||
|
||||
static const struct tegra_devfreq_device_config tegra30_device_configs[] = {
|
||||
{
|
||||
/* MCALL: All memory accesses (including from the CPUs) */
|
||||
.offset = 0x1c0,
|
||||
.irq_mask = 1 << 26,
|
||||
.boost_up_coeff = 200,
|
||||
.boost_down_coeff = 50,
|
||||
.boost_up_threshold = 20,
|
||||
.boost_down_threshold = 10,
|
||||
},
|
||||
{
|
||||
/* MCCPU: memory accesses from the CPUs */
|
||||
.offset = 0x200,
|
||||
.irq_mask = 1 << 25,
|
||||
.boost_up_coeff = 800,
|
||||
.boost_down_coeff = 40,
|
||||
.boost_up_threshold = 27,
|
||||
.boost_down_threshold = 10,
|
||||
.avg_dependency_threshold = 16000, /* 16MHz in kHz units */
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* struct tegra_devfreq_device - state specific to an ACTMON device
|
||||
*
|
||||
@ -153,8 +170,15 @@ struct tegra_devfreq_device {
|
||||
unsigned long target_freq;
|
||||
};
|
||||
|
||||
struct tegra_devfreq_soc_data {
|
||||
const struct tegra_devfreq_device_config *configs;
|
||||
/* Weight value for count measurements */
|
||||
unsigned int count_weight;
|
||||
};
|
||||
|
||||
struct tegra_devfreq {
|
||||
struct devfreq *devfreq;
|
||||
struct opp_table *opp_table;
|
||||
|
||||
struct reset_control *reset;
|
||||
struct clk *clock;
|
||||
@ -168,11 +192,13 @@ struct tegra_devfreq {
|
||||
struct delayed_work cpufreq_update_work;
|
||||
struct notifier_block cpu_rate_change_nb;
|
||||
|
||||
struct tegra_devfreq_device devices[ARRAY_SIZE(actmon_device_configs)];
|
||||
struct tegra_devfreq_device devices[2];
|
||||
|
||||
unsigned int irq;
|
||||
|
||||
bool started;
|
||||
|
||||
const struct tegra_devfreq_soc_data *soc;
|
||||
};
|
||||
|
||||
struct tegra_actmon_emc_ratio {
|
||||
@ -485,7 +511,7 @@ static void tegra_actmon_configure_device(struct tegra_devfreq *tegra,
|
||||
tegra_devfreq_update_avg_wmark(tegra, dev);
|
||||
tegra_devfreq_update_wmark(tegra, dev);
|
||||
|
||||
device_writel(dev, ACTMON_COUNT_WEIGHT, ACTMON_DEV_COUNT_WEIGHT);
|
||||
device_writel(dev, tegra->soc->count_weight, ACTMON_DEV_COUNT_WEIGHT);
|
||||
device_writel(dev, ACTMON_INTR_STATUS_CLEAR, ACTMON_DEV_INTR_STATUS);
|
||||
|
||||
val |= ACTMON_DEV_CTRL_ENB_PERIODIC;
|
||||
@ -612,34 +638,19 @@ static void tegra_actmon_stop(struct tegra_devfreq *tegra)
|
||||
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;
|
||||
int ret;
|
||||
|
||||
opp = devfreq_recommended_opp(dev, freq, flags);
|
||||
if (IS_ERR(opp)) {
|
||||
dev_err(dev, "Failed to find opp for %lu Hz\n", *freq);
|
||||
return PTR_ERR(opp);
|
||||
}
|
||||
rate = dev_pm_opp_get_freq(opp);
|
||||
|
||||
ret = dev_pm_opp_set_bw(dev, opp);
|
||||
dev_pm_opp_put(opp);
|
||||
|
||||
err = clk_set_min_rate(tegra->emc_clock, rate * KHZ);
|
||||
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;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int tegra_devfreq_get_dev_status(struct device *dev,
|
||||
@ -655,7 +666,7 @@ static int tegra_devfreq_get_dev_status(struct device *dev,
|
||||
stat->private_data = tegra;
|
||||
|
||||
/* The below are to be used by the other governors */
|
||||
stat->current_frequency = cur_freq;
|
||||
stat->current_frequency = cur_freq * KHZ;
|
||||
|
||||
actmon_dev = &tegra->devices[MCALL];
|
||||
|
||||
@ -705,7 +716,12 @@ static int tegra_governor_get_target(struct devfreq *devfreq,
|
||||
target_freq = max(target_freq, dev->target_freq);
|
||||
}
|
||||
|
||||
*freq = target_freq;
|
||||
/*
|
||||
* tegra-devfreq driver operates with KHz units, while OPP table
|
||||
* entries use Hz units. Hence we need to convert the units for the
|
||||
* devfreq core.
|
||||
*/
|
||||
*freq = target_freq * KHZ;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -765,14 +781,16 @@ static int tegra_governor_event_handler(struct devfreq *devfreq,
|
||||
|
||||
static struct devfreq_governor tegra_devfreq_governor = {
|
||||
.name = "tegra_actmon",
|
||||
.attrs = DEVFREQ_GOV_ATTR_POLLING_INTERVAL,
|
||||
.flags = DEVFREQ_GOV_FLAG_IMMUTABLE
|
||||
| DEVFREQ_GOV_FLAG_IRQ_DRIVEN,
|
||||
.get_target_freq = tegra_governor_get_target,
|
||||
.event_handler = tegra_governor_event_handler,
|
||||
.immutable = true,
|
||||
.interrupt_driven = true,
|
||||
};
|
||||
|
||||
static int tegra_devfreq_probe(struct platform_device *pdev)
|
||||
{
|
||||
u32 hw_version = BIT(tegra_sku_info.soc_speedo_id);
|
||||
struct tegra_devfreq_device *dev;
|
||||
struct tegra_devfreq *tegra;
|
||||
struct devfreq *devfreq;
|
||||
@ -784,6 +802,8 @@ static int tegra_devfreq_probe(struct platform_device *pdev)
|
||||
if (!tegra)
|
||||
return -ENOMEM;
|
||||
|
||||
tegra->soc = of_device_get_match_data(&pdev->dev);
|
||||
|
||||
tegra->regs = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(tegra->regs))
|
||||
return PTR_ERR(tegra->regs);
|
||||
@ -801,10 +821,9 @@ static int tegra_devfreq_probe(struct platform_device *pdev)
|
||||
}
|
||||
|
||||
tegra->emc_clock = devm_clk_get(&pdev->dev, "emc");
|
||||
if (IS_ERR(tegra->emc_clock)) {
|
||||
dev_err(&pdev->dev, "Failed to get emc clock\n");
|
||||
return PTR_ERR(tegra->emc_clock);
|
||||
}
|
||||
if (IS_ERR(tegra->emc_clock))
|
||||
return dev_err_probe(&pdev->dev, PTR_ERR(tegra->emc_clock),
|
||||
"Failed to get emc clock\n");
|
||||
|
||||
err = platform_get_irq(pdev, 0);
|
||||
if (err < 0)
|
||||
@ -822,11 +841,25 @@ static int tegra_devfreq_probe(struct platform_device *pdev)
|
||||
return err;
|
||||
}
|
||||
|
||||
tegra->opp_table = dev_pm_opp_set_supported_hw(&pdev->dev,
|
||||
&hw_version, 1);
|
||||
err = PTR_ERR_OR_ZERO(tegra->opp_table);
|
||||
if (err) {
|
||||
dev_err(&pdev->dev, "Failed to set supported HW: %d\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
err = dev_pm_opp_of_add_table(&pdev->dev);
|
||||
if (err) {
|
||||
dev_err(&pdev->dev, "Failed to add OPP table: %d\n", err);
|
||||
goto put_hw;
|
||||
}
|
||||
|
||||
err = clk_prepare_enable(tegra->clock);
|
||||
if (err) {
|
||||
dev_err(&pdev->dev,
|
||||
"Failed to prepare and enable ACTMON clock\n");
|
||||
return err;
|
||||
goto remove_table;
|
||||
}
|
||||
|
||||
err = reset_control_reset(tegra->reset);
|
||||
@ -844,29 +877,12 @@ static int tegra_devfreq_probe(struct platform_device *pdev)
|
||||
|
||||
tegra->max_freq = rate / KHZ;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(actmon_device_configs); i++) {
|
||||
for (i = 0; i < ARRAY_SIZE(tegra->devices); i++) {
|
||||
dev = tegra->devices + i;
|
||||
dev->config = actmon_device_configs + i;
|
||||
dev->config = tegra->soc->configs + i;
|
||||
dev->regs = tegra->regs + dev->config->offset;
|
||||
}
|
||||
|
||||
for (rate = 0; rate <= tegra->max_freq * KHZ; rate++) {
|
||||
rate = clk_round_rate(tegra->emc_clock, rate);
|
||||
|
||||
if (rate < 0) {
|
||||
dev_err(&pdev->dev,
|
||||
"Failed to round clock rate: %ld\n", rate);
|
||||
err = rate;
|
||||
goto remove_opps;
|
||||
}
|
||||
|
||||
err = dev_pm_opp_add(&pdev->dev, rate / KHZ, 0);
|
||||
if (err) {
|
||||
dev_err(&pdev->dev, "Failed to add OPP: %d\n", err);
|
||||
goto remove_opps;
|
||||
}
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, tegra);
|
||||
|
||||
tegra->clk_rate_change_nb.notifier_call = tegra_actmon_clk_notify_cb;
|
||||
@ -882,7 +898,6 @@ static int tegra_devfreq_probe(struct platform_device *pdev)
|
||||
}
|
||||
|
||||
tegra_devfreq_profile.initial_freq = clk_get_rate(tegra->emc_clock);
|
||||
tegra_devfreq_profile.initial_freq /= KHZ;
|
||||
|
||||
devfreq = devfreq_add_device(&pdev->dev, &tegra_devfreq_profile,
|
||||
"tegra_actmon", NULL);
|
||||
@ -902,6 +917,10 @@ remove_opps:
|
||||
reset_control_reset(tegra->reset);
|
||||
disable_clk:
|
||||
clk_disable_unprepare(tegra->clock);
|
||||
remove_table:
|
||||
dev_pm_opp_of_remove_table(&pdev->dev);
|
||||
put_hw:
|
||||
dev_pm_opp_put_supported_hw(tegra->opp_table);
|
||||
|
||||
return err;
|
||||
}
|
||||
@ -913,17 +932,33 @@ static int tegra_devfreq_remove(struct platform_device *pdev)
|
||||
devfreq_remove_device(tegra->devfreq);
|
||||
devfreq_remove_governor(&tegra_devfreq_governor);
|
||||
|
||||
dev_pm_opp_remove_all_dynamic(&pdev->dev);
|
||||
|
||||
reset_control_reset(tegra->reset);
|
||||
clk_disable_unprepare(tegra->clock);
|
||||
|
||||
dev_pm_opp_of_remove_table(&pdev->dev);
|
||||
dev_pm_opp_put_supported_hw(tegra->opp_table);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct tegra_devfreq_soc_data tegra124_soc = {
|
||||
.configs = tegra124_device_configs,
|
||||
|
||||
/*
|
||||
* Activity counter is incremented every 256 memory transactions,
|
||||
* and each transaction takes 4 EMC clocks.
|
||||
*/
|
||||
.count_weight = 4 * 256,
|
||||
};
|
||||
|
||||
static const struct tegra_devfreq_soc_data tegra30_soc = {
|
||||
.configs = tegra30_device_configs,
|
||||
.count_weight = 2 * 256,
|
||||
};
|
||||
|
||||
static const struct of_device_id tegra_devfreq_of_match[] = {
|
||||
{ .compatible = "nvidia,tegra30-actmon" },
|
||||
{ .compatible = "nvidia,tegra124-actmon" },
|
||||
{ .compatible = "nvidia,tegra30-actmon", .data = &tegra30_soc, },
|
||||
{ .compatible = "nvidia,tegra124-actmon", .data = &tegra124_soc, },
|
||||
{ },
|
||||
};
|
||||
|
||||
|
@ -3,6 +3,7 @@
|
||||
* Copyright (c) 2014, NVIDIA CORPORATION. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <linux/export.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_address.h>
|
||||
@ -90,6 +91,7 @@ u32 tegra_read_ram_code(void)
|
||||
|
||||
return straps >> PMC_STRAPPING_OPT_A_RAM_CODE_SHIFT;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(tegra_read_ram_code);
|
||||
|
||||
static const struct of_device_id apbmisc_match[] __initconst = {
|
||||
{ .compatible = "nvidia,tegra20-apbmisc", },
|
||||
|
@ -15,8 +15,6 @@
|
||||
#include <linux/pm_opp.h>
|
||||
#include <linux/pm_qos.h>
|
||||
|
||||
#define DEVFREQ_NAME_LEN 16
|
||||
|
||||
/* DEVFREQ governor name */
|
||||
#define DEVFREQ_GOV_SIMPLE_ONDEMAND "simple_ondemand"
|
||||
#define DEVFREQ_GOV_PERFORMANCE "performance"
|
||||
@ -139,7 +137,6 @@ struct devfreq_stats {
|
||||
* using devfreq.
|
||||
* @profile: device-specific devfreq profile
|
||||
* @governor: method how to choose frequency based on the usage.
|
||||
* @governor_name: devfreq governor name for use with this devfreq
|
||||
* @nb: notifier block used to notify devfreq object that it should
|
||||
* reevaluate operable frequencies. Devfreq users may use
|
||||
* devfreq.nb to the corresponding register notifier call chain.
|
||||
@ -176,7 +173,6 @@ struct devfreq {
|
||||
struct device dev;
|
||||
struct devfreq_dev_profile *profile;
|
||||
const struct devfreq_governor *governor;
|
||||
char governor_name[DEVFREQ_NAME_LEN];
|
||||
struct notifier_block nb;
|
||||
struct delayed_work work;
|
||||
|
||||
|
@ -56,7 +56,11 @@ u32 tegra_read_straps(void);
|
||||
u32 tegra_read_ram_code(void);
|
||||
int tegra_fuse_readl(unsigned long offset, u32 *value);
|
||||
|
||||
#ifdef CONFIG_ARCH_TEGRA
|
||||
extern struct tegra_sku_info tegra_sku_info;
|
||||
#else
|
||||
static struct tegra_sku_info tegra_sku_info __maybe_unused;
|
||||
#endif
|
||||
|
||||
struct device *tegra_soc_device_register(void);
|
||||
|
||||
|
@ -8,6 +8,34 @@
|
||||
#include <linux/devfreq.h>
|
||||
#include <linux/tracepoint.h>
|
||||
|
||||
TRACE_EVENT(devfreq_frequency,
|
||||
TP_PROTO(struct devfreq *devfreq, unsigned long freq,
|
||||
unsigned long prev_freq),
|
||||
|
||||
TP_ARGS(devfreq, freq, prev_freq),
|
||||
|
||||
TP_STRUCT__entry(
|
||||
__string(dev_name, dev_name(&devfreq->dev))
|
||||
__field(unsigned long, freq)
|
||||
__field(unsigned long, prev_freq)
|
||||
__field(unsigned long, busy_time)
|
||||
__field(unsigned long, total_time)
|
||||
),
|
||||
|
||||
TP_fast_assign(
|
||||
__assign_str(dev_name, dev_name(&devfreq->dev));
|
||||
__entry->freq = freq;
|
||||
__entry->prev_freq = prev_freq;
|
||||
__entry->busy_time = devfreq->last_status.busy_time;
|
||||
__entry->total_time = devfreq->last_status.total_time;
|
||||
),
|
||||
|
||||
TP_printk("dev_name=%-30s freq=%-12lu prev_freq=%-12lu load=%-2lu",
|
||||
__get_str(dev_name), __entry->freq, __entry->prev_freq,
|
||||
__entry->total_time == 0 ? 0 :
|
||||
(100 * __entry->busy_time) / __entry->total_time)
|
||||
);
|
||||
|
||||
TRACE_EVENT(devfreq_monitor,
|
||||
TP_PROTO(struct devfreq *devfreq),
|
||||
|
||||
@ -29,7 +57,7 @@ TRACE_EVENT(devfreq_monitor,
|
||||
__assign_str(dev_name, dev_name(&devfreq->dev));
|
||||
),
|
||||
|
||||
TP_printk("dev_name=%s freq=%lu polling_ms=%u load=%lu",
|
||||
TP_printk("dev_name=%-30s freq=%-12lu polling_ms=%-3u load=%-2lu",
|
||||
__get_str(dev_name), __entry->freq, __entry->polling_ms,
|
||||
__entry->total_time == 0 ? 0 :
|
||||
(100 * __entry->busy_time) / __entry->total_time)
|
||||
|
Loading…
Reference in New Issue
Block a user