mirror of
				https://github.com/ARM-software/devlib.git
				synced 2025-10-31 14:01:20 +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:
		
				
					committed by
					
						 setrofim
						setrofim
					
				
			
			
				
	
			
			
			
						parent
						
							d81b72a91b
						
					
				
				
					commit
					0915d97f71
				
			| @@ -47,6 +47,37 @@ cpufreq_trace_all_frequencies() { | ||||
| 	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 | ||||
| ################################################################################ | ||||
| @@ -253,6 +284,18 @@ cpufreq_get_all_governors) | ||||
| 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 $* | ||||
|     ;; | ||||
|   | ||||
							
								
								
									
										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 | ||||
|  | ||||
		Reference in New Issue
	
	Block a user