mirror of
https://github.com/ARM-software/devlib.git
synced 2025-02-07 05:30:44 +00:00
module: Add devfreq module
Add support for devfreq. This is used for example to get/set the frequency and/or governor of the GPU.
This commit is contained in:
parent
d81b72a91b
commit
0915d97f71
@ -47,6 +47,37 @@ cpufreq_trace_all_frequencies() {
|
|||||||
done
|
done
|
||||||
}
|
}
|
||||||
|
|
||||||
|
################################################################################
|
||||||
|
# DevFrequency Utility Functions
|
||||||
|
################################################################################
|
||||||
|
|
||||||
|
devfreq_set_all_frequencies() {
|
||||||
|
FREQ=$1
|
||||||
|
for DEV in /sys/class/devfreq/*; do
|
||||||
|
echo $FREQ > $DEV/min_freq
|
||||||
|
echo $FREQ > $DEV/max_freq
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
devfreq_get_all_frequencies() {
|
||||||
|
for DEV in /sys/class/devfreq/*; do
|
||||||
|
echo "`basename $DEV` `cat $DEV/cur_freq`"
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
devfreq_set_all_governors() {
|
||||||
|
GOV=$1
|
||||||
|
for DEV in /sys/class/devfreq/*; do
|
||||||
|
echo $GOV > $DEV/governor
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
devfreq_get_all_governors() {
|
||||||
|
for DEV in /sys/class/devfreq/*; do
|
||||||
|
echo "`basename $DEV` `cat $DEV/governor`"
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
################################################################################
|
################################################################################
|
||||||
# CPUIdle Utility Functions
|
# CPUIdle Utility Functions
|
||||||
################################################################################
|
################################################################################
|
||||||
@ -253,6 +284,18 @@ cpufreq_get_all_governors)
|
|||||||
cpufreq_trace_all_frequencies)
|
cpufreq_trace_all_frequencies)
|
||||||
cpufreq_trace_all_frequencies $*
|
cpufreq_trace_all_frequencies $*
|
||||||
;;
|
;;
|
||||||
|
devfreq_set_all_frequencies)
|
||||||
|
devfreq_set_all_frequencies $*
|
||||||
|
;;
|
||||||
|
devfreq_get_all_frequencies)
|
||||||
|
devfreq_get_all_frequencies
|
||||||
|
;;
|
||||||
|
devfreq_set_all_governors)
|
||||||
|
devfreq_set_all_governors $*
|
||||||
|
;;
|
||||||
|
devfreq_get_all_governors)
|
||||||
|
devfreq_get_all_governors
|
||||||
|
;;
|
||||||
cpuidle_wake_all_cpus)
|
cpuidle_wake_all_cpus)
|
||||||
cpuidle_wake_all_cpus $*
|
cpuidle_wake_all_cpus $*
|
||||||
;;
|
;;
|
||||||
|
261
devlib/module/devfreq.py
Normal file
261
devlib/module/devfreq.py
Normal file
@ -0,0 +1,261 @@
|
|||||||
|
# Copyright 2018 ARM Limited
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
#
|
||||||
|
from devlib.module import Module
|
||||||
|
from devlib.exception import TargetError
|
||||||
|
from devlib.utils.misc import memoized
|
||||||
|
|
||||||
|
class DevfreqModule(Module):
|
||||||
|
|
||||||
|
name = 'devfreq'
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def probe(target):
|
||||||
|
path = '/sys/class/devfreq/'
|
||||||
|
if not target.file_exists(path):
|
||||||
|
return False
|
||||||
|
|
||||||
|
# Check that at least one policy is implemented
|
||||||
|
if not target.list_directory(path):
|
||||||
|
return False
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
@memoized
|
||||||
|
def list_devices(self):
|
||||||
|
"""Returns a list of devfreq devices supported by the target platform."""
|
||||||
|
sysfile = '/sys/class/devfreq/'
|
||||||
|
return self.target.list_directory(sysfile)
|
||||||
|
|
||||||
|
@memoized
|
||||||
|
def list_governors(self, device):
|
||||||
|
"""Returns a list of governors supported by the device."""
|
||||||
|
sysfile = '/sys/class/devfreq/{}/available_governors'.format(device)
|
||||||
|
output = self.target.read_value(sysfile)
|
||||||
|
return output.strip().split()
|
||||||
|
|
||||||
|
def get_governor(self, device):
|
||||||
|
"""Returns the governor currently set for the specified device."""
|
||||||
|
if isinstance(device, int):
|
||||||
|
device = 'device{}'.format(device)
|
||||||
|
sysfile = '/sys/class/devfreq/{}/governor'.format(device)
|
||||||
|
return self.target.read_value(sysfile)
|
||||||
|
|
||||||
|
def set_governor(self, device, governor):
|
||||||
|
"""
|
||||||
|
Set the governor for the specified device.
|
||||||
|
|
||||||
|
:param device: The device for which the governor is to be set. This must be
|
||||||
|
the full name as it appears in sysfs, e.g. "e82c0000.mali".
|
||||||
|
:param governor: The name of the governor to be used. This must be
|
||||||
|
supported by the specific device.
|
||||||
|
|
||||||
|
Additional keyword arguments can be used to specify governor tunables for
|
||||||
|
governors that support them.
|
||||||
|
|
||||||
|
:raises: TargetError if governor is not supported by the device, or if,
|
||||||
|
for some reason, the governor could not be set.
|
||||||
|
|
||||||
|
"""
|
||||||
|
supported = self.list_governors(device)
|
||||||
|
if governor not in supported:
|
||||||
|
raise TargetError('Governor {} not supported for device {}'.format(governor, device))
|
||||||
|
sysfile = '/sys/class/devfreq/{}/governor'.format(device)
|
||||||
|
self.target.write_value(sysfile, governor)
|
||||||
|
|
||||||
|
@memoized
|
||||||
|
def list_frequencies(self, device):
|
||||||
|
"""
|
||||||
|
Returns a list of frequencies supported by the device or an empty list
|
||||||
|
if could not be found.
|
||||||
|
"""
|
||||||
|
cmd = 'cat /sys/class/devfreq/{}/available_frequencies'.format(device)
|
||||||
|
output = self.target.execute(cmd)
|
||||||
|
available_frequencies = [int(freq) for freq in output.strip().split()]
|
||||||
|
|
||||||
|
return available_frequencies
|
||||||
|
|
||||||
|
def get_min_frequency(self, device):
|
||||||
|
"""
|
||||||
|
Returns the min frequency currently set for the specified device.
|
||||||
|
|
||||||
|
Warning, this method does not check if the device is present or not. It
|
||||||
|
will try to read the minimum frequency and the following exception will
|
||||||
|
be raised ::
|
||||||
|
|
||||||
|
:raises: TargetError if for some reason the frequency could not be read.
|
||||||
|
|
||||||
|
"""
|
||||||
|
sysfile = '/sys/class/devfreq/{}/min_freq'.format(device)
|
||||||
|
return self.target.read_int(sysfile)
|
||||||
|
|
||||||
|
def set_min_frequency(self, device, frequency, exact=True):
|
||||||
|
"""
|
||||||
|
Sets the minimum value for device frequency. Actual frequency will
|
||||||
|
depend on the thermal governor used and may vary during execution. The
|
||||||
|
value should be either an int or a string representing an integer. The
|
||||||
|
Value must also be supported by the device. The available frequencies
|
||||||
|
can be obtained by calling list_frequencies() or examining
|
||||||
|
|
||||||
|
/sys/class/devfreq/<device_name>/available_frequencies
|
||||||
|
|
||||||
|
on the device.
|
||||||
|
|
||||||
|
:raises: TargetError if the frequency is not supported by the device, or if, for
|
||||||
|
some reason, frequency could not be set.
|
||||||
|
:raises: ValueError if ``frequency`` is not an integer.
|
||||||
|
|
||||||
|
"""
|
||||||
|
available_frequencies = self.list_frequencies(device)
|
||||||
|
try:
|
||||||
|
value = int(frequency)
|
||||||
|
if exact and available_frequencies and value not in available_frequencies:
|
||||||
|
raise TargetError('Can\'t set {} frequency to {}\nmust be in {}'.format(device,
|
||||||
|
value,
|
||||||
|
available_frequencies))
|
||||||
|
sysfile = '/sys/class/devfreq/{}/min_freq'.format(device)
|
||||||
|
self.target.write_value(sysfile, value)
|
||||||
|
except ValueError:
|
||||||
|
raise ValueError('Frequency must be an integer; got: "{}"'.format(frequency))
|
||||||
|
|
||||||
|
def get_frequency(self, device):
|
||||||
|
"""
|
||||||
|
Returns the current frequency currently set for the specified device.
|
||||||
|
|
||||||
|
Warning, this method does not check if the device is present or not. It
|
||||||
|
will try to read the current frequency and the following exception will
|
||||||
|
be raised ::
|
||||||
|
|
||||||
|
:raises: TargetError if for some reason the frequency could not be read.
|
||||||
|
|
||||||
|
"""
|
||||||
|
sysfile = '/sys/class/devfreq/{}/cur_freq'.format(device)
|
||||||
|
return self.target.read_int(sysfile)
|
||||||
|
|
||||||
|
def get_max_frequency(self, device):
|
||||||
|
"""
|
||||||
|
Returns the max frequency currently set for the specified device.
|
||||||
|
|
||||||
|
Warning, this method does not check if the device is online or not. It will
|
||||||
|
try to read the maximum frequency and the following exception will be
|
||||||
|
raised ::
|
||||||
|
|
||||||
|
:raises: TargetError if for some reason the frequency could not be read.
|
||||||
|
"""
|
||||||
|
sysfile = '/sys/class/devfreq/{}/max_freq'.format(device)
|
||||||
|
return self.target.read_int(sysfile)
|
||||||
|
|
||||||
|
def set_max_frequency(self, device, frequency, exact=True):
|
||||||
|
"""
|
||||||
|
Sets the maximum value for device frequency. Actual frequency will
|
||||||
|
depend on the Governor used and may vary during execution. The value
|
||||||
|
should be either an int or a string representing an integer. The Value
|
||||||
|
must also be supported by the device. The available frequencies can be
|
||||||
|
obtained by calling get_frequencies() or examining
|
||||||
|
|
||||||
|
/sys/class/devfreq/<device_name>/available_frequencies
|
||||||
|
|
||||||
|
on the device.
|
||||||
|
|
||||||
|
:raises: TargetError if the frequency is not supported by the device, or
|
||||||
|
if, for some reason, frequency could not be set.
|
||||||
|
:raises: ValueError if ``frequency`` is not an integer.
|
||||||
|
|
||||||
|
"""
|
||||||
|
available_frequencies = self.list_frequencies(device)
|
||||||
|
try:
|
||||||
|
value = int(frequency)
|
||||||
|
except ValueError:
|
||||||
|
raise ValueError('Frequency must be an integer; got: "{}"'.format(frequency))
|
||||||
|
|
||||||
|
if exact and value not in available_frequencies:
|
||||||
|
raise TargetError('Can\'t set {} frequency to {}\nmust be in {}'.format(device,
|
||||||
|
value,
|
||||||
|
available_frequencies))
|
||||||
|
sysfile = '/sys/class/devfreq/{}/max_freq'.format(device)
|
||||||
|
self.target.write_value(sysfile, value)
|
||||||
|
|
||||||
|
def set_governor_for_devices(self, devices, governor):
|
||||||
|
"""
|
||||||
|
Set the governor for the specified list of devices.
|
||||||
|
|
||||||
|
:param devices: The list of device for which the governor is to be set.
|
||||||
|
"""
|
||||||
|
for device in devices:
|
||||||
|
self.set_governor(device, governor)
|
||||||
|
|
||||||
|
def set_all_governors(self, governor):
|
||||||
|
"""
|
||||||
|
Set the specified governor for all the (available) devices
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
return self.target._execute_util(
|
||||||
|
'devfreq_set_all_governors {}'.format(governor), as_root=True)
|
||||||
|
except TargetError as e:
|
||||||
|
if ("echo: I/O error" in str(e) or
|
||||||
|
"write error: Invalid argument" in str(e)):
|
||||||
|
|
||||||
|
devs_unsupported = [d for d in self.target.list_devices()
|
||||||
|
if governor not in self.list_governors(d)]
|
||||||
|
raise TargetError("Governor {} unsupported for devices {}".format(
|
||||||
|
governor, devs_unsupported))
|
||||||
|
else:
|
||||||
|
raise
|
||||||
|
|
||||||
|
def get_all_governors(self):
|
||||||
|
"""
|
||||||
|
Get the current governor for all the (online) CPUs
|
||||||
|
"""
|
||||||
|
output = self.target._execute_util(
|
||||||
|
'devfreq_get_all_governors', as_root=True)
|
||||||
|
governors = {}
|
||||||
|
for x in output.splitlines():
|
||||||
|
kv = x.split(' ')
|
||||||
|
if kv[0] == '':
|
||||||
|
break
|
||||||
|
governors[kv[0]] = kv[1]
|
||||||
|
return governors
|
||||||
|
|
||||||
|
def set_frequency_for_devices(self, devices, freq, exact=False):
|
||||||
|
"""
|
||||||
|
Set the frequency for the specified list of devices.
|
||||||
|
|
||||||
|
:param devices: The list of device for which the frequency has to be set.
|
||||||
|
"""
|
||||||
|
for device in devices:
|
||||||
|
self.set_max_frequency(device, freq, exact)
|
||||||
|
self.set_min_frequency(device, freq, exact)
|
||||||
|
|
||||||
|
def set_all_frequencies(self, freq):
|
||||||
|
"""
|
||||||
|
Set the specified (minimum) frequency for all the (available) devices
|
||||||
|
"""
|
||||||
|
return self.target._execute_util(
|
||||||
|
'devfreq_set_all_frequencies {}'.format(freq),
|
||||||
|
as_root=True)
|
||||||
|
|
||||||
|
def get_all_frequencies(self):
|
||||||
|
"""
|
||||||
|
Get the current frequency for all the (available) devices
|
||||||
|
"""
|
||||||
|
output = self.target._execute_util(
|
||||||
|
'devfreq_get_all_frequencies', as_root=True)
|
||||||
|
frequencies = {}
|
||||||
|
for x in output.splitlines():
|
||||||
|
kv = x.split(' ')
|
||||||
|
if kv[0] == '':
|
||||||
|
break
|
||||||
|
frequencies[kv[0]] = kv[1]
|
||||||
|
return frequencies
|
||||||
|
|
Loading…
x
Reference in New Issue
Block a user