1# Copyright (c) 2010 The Chromium OS Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5import glob, logging, os
6from autotest_lib.client.bin import test
7from autotest_lib.client.common_lib import error, utils
8
9SYSFS_CPUQUIET_ENABLE = '/sys/devices/system/cpu/cpuquiet/tegra_cpuquiet/enable'
10SYSFS_INTEL_PSTATE_PATH = '/sys/devices/system/cpu/intel_pstate'
11
12class power_CPUFreq(test.test):
13    version = 1
14
15    def initialize(self):
16        # Store the setting if the system has CPUQuiet feature
17        if os.path.exists(SYSFS_CPUQUIET_ENABLE):
18            self.is_cpuquiet_enabled = utils.read_file(SYSFS_CPUQUIET_ENABLE)
19            utils.write_one_line(SYSFS_CPUQUIET_ENABLE, '0')
20
21    def run_once(self):
22        # TODO(crbug.com/485276) Revisit this exception once we've refactored
23        # test to account for intel_pstate cpufreq driver
24        if os.path.exists(SYSFS_INTEL_PSTATE_PATH):
25            raise error.TestNAError('Test does NOT support intel_pstate driver')
26
27        cpufreq_path = '/sys/devices/system/cpu/cpu*/cpufreq'
28
29        dirs  = glob.glob(cpufreq_path)
30        if not dirs:
31            raise error.TestFail('cpufreq not supported')
32
33        keyvals = {}
34        try:
35            # First attempt to set all frequencies on each core before going
36            # on to the next core.
37            self.test_cores_in_series(dirs)
38            # Record that it was the first test that passed.
39            keyvals['test_cores_in_series'] = 1
40        except error.TestFail as exception:
41            if str(exception) == 'Unable to set frequency':
42                # If test_cores_in_series fails, try to set each frequency for
43                # all cores before moving on to the next frequency.
44
45                self.test_cores_in_parallel(dirs)
46                # Record that it was the second test that passed.
47                keyvals['test_cores_in_parallel'] = 1
48            else:
49                raise exception
50
51        self.write_perf_keyval(keyvals);
52
53    def test_cores_in_series(self, dirs):
54        for dir in dirs:
55            cpu = cpufreq(dir)
56
57            if 'userspace' not in cpu.get_available_governors():
58                raise error.TestError('userspace governor not supported')
59
60            available_frequencies = cpu.get_available_frequencies()
61            if len(available_frequencies) == 1:
62                raise error.TestFail('Not enough frequencies supported!')
63
64            # save cpufreq state so that it can be restored at the end
65            # of the test
66            cpu.save_state()
67
68            # set cpufreq governor to userspace
69            cpu.set_governor('userspace')
70
71            # cycle through all available frequencies
72            for freq in available_frequencies:
73                cpu.set_frequency(freq)
74                if freq != cpu.get_current_frequency():
75                    cpu.restore_state()
76                    raise error.TestFail('Unable to set frequency')
77
78            # restore cpufreq state
79            cpu.restore_state()
80
81    def test_cores_in_parallel(self, dirs):
82        cpus = [cpufreq(dir) for dir in dirs]
83        cpu0 = cpus[0]
84
85        # Use the first CPU's frequencies for all CPUs.  Assume that they are
86        # the same.
87        available_frequencies = cpu0.get_available_frequencies()
88        if len(available_frequencies) == 1:
89            raise error.TestFail('Not enough frequencies supported!')
90
91        for cpu in cpus:
92            if 'userspace' not in cpu.get_available_governors():
93                raise error.TestError('userspace governor not supported')
94
95            # save cpufreq state so that it can be restored at the end
96            # of the test
97            cpu.save_state()
98
99            # set cpufreq governor to userspace
100            cpu.set_governor('userspace')
101
102        # cycle through all available frequencies
103        for freq in available_frequencies:
104            for cpu in cpus:
105                cpu.set_frequency(freq)
106            for cpu in cpus:
107                if freq != cpu.get_current_frequency():
108                    cpu.restore_state()
109                    raise error.TestFail('Unable to set frequency')
110
111        for cpu in cpus:
112            # restore cpufreq state
113            cpu.restore_state()
114
115    def cleanup(self):
116        # Restore the original setting if system has CPUQuiet feature
117        if os.path.exists(SYSFS_CPUQUIET_ENABLE):
118            utils.open_write_close(
119                SYSFS_CPUQUIET_ENABLE, self.is_cpuquiet_enabled)
120
121class cpufreq(object):
122    def __init__(self, path):
123        self.__base_path = path
124        self.__save_files_list = ['scaling_max_freq', 'scaling_min_freq',
125                                  'scaling_governor']
126
127
128    def __write_file(self, file_name, data):
129        path = os.path.join(self.__base_path, file_name)
130        utils.open_write_close(path, data)
131
132
133    def __read_file(self, file_name):
134        path = os.path.join(self.__base_path, file_name)
135        f = open(path, 'r')
136        data = f.read()
137        f.close()
138        return data
139
140
141    def save_state(self):
142        logging.info('saving state:')
143        for file in self.__save_files_list:
144            data = self.__read_file(file)
145            setattr(self, file, data)
146            logging.info(file + ': '  + data)
147
148
149    def restore_state(self):
150        logging.info('restoring state:')
151        for file in self.__save_files_list:
152            # Sometimes a newline gets appended to a data string and it throws
153            # an error when being written to a sysfs file.  Call strip() to
154            # eliminateextra whitespace characters so it can be written cleanly
155            # to the file.
156            data = getattr(self, file).strip()
157            logging.info(file + ': '  + data)
158            self.__write_file(file, data)
159
160
161    def get_available_governors(self):
162        governors = self.__read_file('scaling_available_governors')
163        logging.info('available governors: %s' % governors)
164        return governors.split()
165
166
167    def get_current_governor(self):
168        governor = self.__read_file('scaling_governor')
169        logging.info('current governor: %s' % governor)
170        return governor.split()[0]
171
172
173    def set_governor(self, governor):
174        logging.info('setting governor to %s' % governor)
175        self.__write_file('scaling_governor', governor)
176
177
178    def get_available_frequencies(self):
179        frequencies = self.__read_file('scaling_available_frequencies')
180        logging.info('available frequencies: %s' % frequencies)
181        return [int(i) for i in frequencies.split()]
182
183
184    def get_current_frequency(self):
185        freq = int(self.__read_file('scaling_cur_freq'))
186        logging.info('current frequency: %s' % freq)
187        return freq
188
189
190    def set_frequency(self, frequency):
191        logging.info('setting frequency to %d' % frequency)
192        if frequency >= self.get_current_frequency():
193            file_list = ['scaling_max_freq', 'scaling_min_freq',
194                         'scaling_setspeed']
195        else:
196            file_list = ['scaling_min_freq', 'scaling_max_freq',
197                         'scaling_setspeed']
198
199        for file in file_list:
200            self.__write_file(file, str(frequency))
201