blob: f81538acbb2f86b236279c62dd48a84b5985cd20 [file] [log] [blame]
Mary Ruthven9bf599e2017-10-13 16:47:06 -07001# Copyright 2017 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 logging
6import pprint
7import time
Mary Ruthven992c8db2018-05-07 11:28:57 -07008import re
Mary Ruthven9bf599e2017-10-13 16:47:06 -07009
10from autotest_lib.client.common_lib import error
11from autotest_lib.server import autotest
Mary Ruthven9b094062018-04-25 17:39:20 -070012from autotest_lib.server.cros.faft.cr50_test import Cr50Test
Mary Ruthven9bf599e2017-10-13 16:47:06 -070013
14
Mary Ruthven9b094062018-04-25 17:39:20 -070015class firmware_Cr50DeviceState(Cr50Test):
Mary Ruthven9bf599e2017-10-13 16:47:06 -070016 """Verify Cr50 tracks the EC and AP state correctly.
17
Mary Ruthven7bd39a82018-01-12 10:09:31 -080018 Put the device through S0, S0ix, S3, and G3. Cr50 responds to these state
Mary Ruthven9bf599e2017-10-13 16:47:06 -070019 changes by enabling/disabling uart and changing its suspend type. Verify
20 that none of these cause any interrupt storms on Cr50. Make sure that there
21 aren't any interrupt storms and that Cr50 enters regular or deep sleep a
22 reasonable amount of times.
23 """
24 version = 1
25
26 DEEP_SLEEP_STEP_SUFFIX = ' Num Deep Sleep Steps'
27
28 # Use negative numbers to keep track of counts not in the IRQ list.
29 KEY_DEEP_SLEEP = -3
30 KEY_TIME = -2
31 KEY_RESET = -1
32 KEY_REGULAR_SLEEP = 112
33 INT_NAME = {
34 KEY_RESET : 'Reset Count',
35 KEY_DEEP_SLEEP : 'Deep Sleep Count',
36 KEY_TIME : 'Cr50 Time',
37 4 : 'HOST_CMD_DONE',
38 81 : 'GPIO0',
39 98 : 'GPIO1',
40 103 : 'I2CS WRITE',
41 KEY_REGULAR_SLEEP : 'PMU WAKEUP',
Mary Ruthven9c5349d2018-01-12 12:39:24 -080042 113 : 'AC present FED',
43 114 : 'AC present RED',
Mary Ruthven9bf599e2017-10-13 16:47:06 -070044 124 : 'RBOX_INTR_PWRB',
Mary Ruthven9c5349d2018-01-12 12:39:24 -080045 130 : 'SPS CS deassert',
46 138 : 'SPS RXFIFO LVL',
47 159 : 'SPS RXFIFO overflow',
Mary Ruthven9bf599e2017-10-13 16:47:06 -070048 160 : 'EVENT TIMER',
49 174 : 'CR50_RX_SERVO_TX',
50 177 : 'CR50_TX_SERVO_RX',
51 181 : 'AP_TX_CR50_RX',
52 184 : 'AP_RX_CR50_TX',
53 188 : 'EC_TX_CR50_RX',
54 191 : 'EC_RX_CR50_TX',
55 193 : 'USB',
56 }
Mary Ruthvenf6c45092018-01-29 15:35:10 -080057 SLEEP_KEYS = [ KEY_REGULAR_SLEEP, KEY_DEEP_SLEEP ]
Mary Ruthvenaef90672018-01-12 15:16:55 -080058 # Cr50 won't enable any form of sleep until it has been up for 20 seconds.
59 SLEEP_DELAY = 20
Mary Ruthven9bf599e2017-10-13 16:47:06 -070060 # The time in seconds to wait in each state
61 SLEEP_TIME = 30
62 SHORT_WAIT = 5
63 CONSERVATIVE_WAIT_TIME = SLEEP_TIME + SHORT_WAIT + 10
64 # Cr50 should wake up twice per second while in regular sleep
65 SLEEP_RATE = 2
66
Mary Ruthven8e2d6f52018-02-21 14:44:36 -080067 DEEP_SLEEP_MAX = 2
Mary Ruthven9c5349d2018-01-12 12:39:24 -080068 ARM = 'ARM '
Mary Ruthven9bf599e2017-10-13 16:47:06 -070069
70 # If there are over 100,000 interrupts, it is an interrupt storm.
71 DEFAULT_COUNTS = [0, 100000]
72 # A dictionary of ok count values for each irq that shouldn't follow the
73 # DEFAULT_COUNTS range.
74 EXPECTED_IRQ_COUNT_RANGE = {
75 KEY_RESET : [0, 0],
76 KEY_DEEP_SLEEP : [0, DEEP_SLEEP_MAX],
77 KEY_TIME : [0, CONSERVATIVE_WAIT_TIME],
Mary Ruthvene989f3d2018-05-10 14:16:44 -070078 'S0ix' + DEEP_SLEEP_STEP_SUFFIX : [0, 0],
Mary Ruthven8e2d6f52018-02-21 14:44:36 -080079 # Cr50 may enter deep sleep an extra time, because of how the test
80 # collects taskinfo counts. Just verify that it does enter deep sleep
Mary Ruthvene989f3d2018-05-10 14:16:44 -070081 'S3' + DEEP_SLEEP_STEP_SUFFIX : [1, 2],
82 'G3' + DEEP_SLEEP_STEP_SUFFIX : [1, 2],
Mary Ruthven8e2d6f52018-02-21 14:44:36 -080083 # ARM devices don't enter deep sleep in S3
84 ARM + 'S3' + DEEP_SLEEP_STEP_SUFFIX : [0, 0],
85 ARM + 'G3' + DEEP_SLEEP_STEP_SUFFIX : [1, 2],
Mary Ruthven9bf599e2017-10-13 16:47:06 -070086 # Regular sleep is calculated based on the cr50 time
87 }
88
89 GET_TASKINFO = ['IRQ counts by type:(.*)Service calls']
90
91 START = ''
92 INCREASE = '+'
93 DS_RESUME = 'DS'
94
Mary Ruthveneb417f22018-04-05 17:29:51 -070095 def cleanup(self):
96 """Make sure the device is on at the end of the test."""
97 self.trigger_s0()
98 super(firmware_Cr50DeviceState, self).cleanup()
99
100
Mary Ruthven9bf599e2017-10-13 16:47:06 -0700101 def get_taskinfo_output(self):
102 """Return a dict with the irq numbers as keys and counts as values"""
103 output = self.cr50.send_command_get_output('taskinfo',
104 self.GET_TASKINFO)[0][1].strip()
Mary Ruthven992c8db2018-05-07 11:28:57 -0700105 logging.info(output)
Mary Ruthven9bf599e2017-10-13 16:47:06 -0700106 return output
107
108
109 def get_irq_counts(self):
110 """Return a dict with the irq numbers as keys and counts as values"""
111 output = self.get_taskinfo_output()
Mary Ruthven992c8db2018-05-07 11:28:57 -0700112 irq_list = re.findall('\d+\s+\d+\r', output)
Mary Ruthvenf6c45092018-01-29 15:35:10 -0800113 # Make sure the regular sleep irq is in the dictionary, even if cr50
114 # hasn't seen any interrupts. It's important the test sees that there's
115 # never an interrupt.
116 irq_counts = { self.KEY_REGULAR_SLEEP : 0 }
Mary Ruthven9bf599e2017-10-13 16:47:06 -0700117 for irq_info in irq_list:
Mary Ruthven992c8db2018-05-07 11:28:57 -0700118 logging.debug(irq_info)
Mary Ruthven9bf599e2017-10-13 16:47:06 -0700119 num, count = irq_info.split()
120 irq_counts[int(num)] = int(count)
121 irq_counts[self.KEY_RESET] = int(self.servo.get('cr50_reset_count'))
122 irq_counts[self.KEY_DEEP_SLEEP] = int(self.cr50.get_deep_sleep_count())
123 irq_counts[self.KEY_TIME] = int(self.cr50.gettime())
124 return irq_counts
125
126
127 def get_expected_count(self, irq_key, cr50_time):
128 """Get the expected irq increase for the given irq and state
129
130 Args:
131 irq_key: the irq int
132 cr50_time: the cr50 time in seconds
133
134 Returns:
135 A list with the expected irq count range [min, max]
136 """
Mary Ruthvenf6c45092018-01-29 15:35:10 -0800137 # CCD will prevent sleep
Mary Ruthveneb9afff2018-02-21 17:56:42 -0800138 if self.ccd_enabled and (irq_key in self.SLEEP_KEYS or
139 self.DEEP_SLEEP_STEP_SUFFIX in str(irq_key)):
Mary Ruthvenf6c45092018-01-29 15:35:10 -0800140 return [0, 0]
Mary Ruthven9bf599e2017-10-13 16:47:06 -0700141 if irq_key == self.KEY_REGULAR_SLEEP:
Mary Ruthvenf1490552018-04-18 16:13:29 -0700142 # If cr50_time is really low, we probably woke cr50 up using
143 # taskinfo, which would be a pmu wakeup.
144 if cr50_time == 0:
145 return [0, 1]
146
Mary Ruthvenaef90672018-01-12 15:16:55 -0800147 min_count = max(cr50_time - self.SLEEP_DELAY, 0)
148 # Just checking there is not a lot of extra sleep wakeups. Add 1 to
149 # the sleep rate so cr50 can have some extra wakeups, but not too
150 # many.
151 max_count = cr50_time * (self.SLEEP_RATE + 1)
Mary Ruthven9bf599e2017-10-13 16:47:06 -0700152 return [min_count, max_count]
153 return self.EXPECTED_IRQ_COUNT_RANGE.get(irq_key, self.DEFAULT_COUNTS)
154
155
156 def check_increase(self, irq_key, name, increase, expected_range):
157 """Verify the irq count is within the expected range
158
159 Args:
160 irq_key: the irq int
161 name: the irq name string
162 increase: the irq count
163 expected_range: A list with the valid irq count range [min, max]
164
165 Returns:
166 '' if increase is in the given range. If the increase isn't in the
167 range, it returns an error message.
168 """
169 min_count, max_count = expected_range
170 if min_count > increase or max_count < increase:
171 err_msg = '%s %s: %s not in range %s' % (name, irq_key, increase,
172 expected_range)
173 return err_msg
174 return ''
175
176
177 def get_step_events(self):
178 """Use the deep sleep counts to determine the step events"""
179 ds_counts = self.get_irq_step_counts(self.KEY_DEEP_SLEEP)
180 events = []
181 for i, count in enumerate(ds_counts):
182 if not i:
183 events.append(self.START)
184 elif count != ds_counts[i - 1]:
185 # If the deep sleep count changed, Cr50 recovered deep sleep
186 # and the irq counts are reset.
187 events.append(self.DS_RESUME)
188 else:
189 events.append(self.INCREASE)
190 return events
191
192
193 def get_irq_step_counts(self, irq_key):
194 """Get a list of the all of the step counts for the given irq"""
195 return [ irq_dict.get(irq_key, 0) for irq_dict in self.steps ]
196
197
Mary Ruthven6a4a9bc2018-01-12 10:29:06 -0800198 def check_for_errors(self, state):
199 """Check for unexpected IRQ counts at each step.
200
Mary Ruthvenf6c45092018-01-29 15:35:10 -0800201 Find the irq count errors and add them to run_errors.
Mary Ruthven9bf599e2017-10-13 16:47:06 -0700202
203 Args:
Mary Ruthven7bd39a82018-01-12 10:09:31 -0800204 state: The power state: S0, S0ix, S3, or G3.
Mary Ruthven9bf599e2017-10-13 16:47:06 -0700205 """
Mary Ruthven9bf599e2017-10-13 16:47:06 -0700206 num_steps = len(self.steps)
207 # Get all of the deep sleep counts
208 events = self.get_step_events()
209
210 irq_list = list(self.irqs)
211 irq_list.sort()
212
Mary Ruthven9c5349d2018-01-12 12:39:24 -0800213 irq_diff = ['%23s' % 'step' + ''.join(self.step_names)]
Mary Ruthven9bf599e2017-10-13 16:47:06 -0700214 step_errors = [ [] for i in range(num_steps) ]
215
216 cr50_times = self.get_irq_step_counts(self.KEY_TIME)
217 cr50_diffs = []
218 for i, cr50_time in enumerate(cr50_times):
219 if events[i] == self.INCREASE:
220 cr50_time -= cr50_times[i - 1]
221 cr50_diffs.append(cr50_time)
222
223 # Go through each irq and update its info in the progress dict
224 for irq_key in irq_list:
225 name = self.INT_NAME.get(irq_key, 'Unknown')
Mary Ruthven9c5349d2018-01-12 12:39:24 -0800226 irq_progress_str = ['%19s %3s:' % (name, irq_key)]
Mary Ruthven9bf599e2017-10-13 16:47:06 -0700227
228 irq_counts = self.get_irq_step_counts(irq_key)
229 for step, count in enumerate(irq_counts):
230 event = events[step]
231
232 # The deep sleep counts are not reset after deep sleep. Change
233 # the event to INCREASE.
234 if irq_key == self.KEY_DEEP_SLEEP and event == self.DS_RESUME:
235 event = self.INCREASE
236
237 if event == self.INCREASE:
238 count -= irq_counts[step - 1]
239
240 # Check that the count increase is within the expected value.
241 if event != self.START:
242 expected_range = self.get_expected_count(irq_key,
243 cr50_diffs[step])
244
245 rv = self.check_increase(irq_key, name, count,
246 expected_range)
247 if rv:
248 logging.info('Unexpected count for %s %s', state, rv)
249 step_errors[step].append(rv)
250
251 irq_progress_str.append(' %2s %7d' % (event, count))
252
253 irq_diff.append(''.join(irq_progress_str))
254
255 errors = {}
256
Mary Ruthven9c5349d2018-01-12 12:39:24 -0800257 ds_key = self.ARM if self.is_arm else ''
258 ds_key += state + self.DEEP_SLEEP_STEP_SUFFIX
Mary Ruthven9bf599e2017-10-13 16:47:06 -0700259 expected_range = self.get_expected_count(ds_key, 0)
260 rv = self.check_increase(None, ds_key, events.count(self.DS_RESUME),
261 expected_range)
262 if rv:
263 logging.info('Unexpected count for %s %s', state, rv)
264 errors[ds_key] = rv
265 for i, step_error in enumerate(step_errors):
266 if step_error:
267 logging.error('Step %d errors:\n%s', i,
268 pprint.pformat(step_error))
269 step = '%s step %d %s' % (state, i, self.step_names[i].strip())
270 errors[step] = step_error
271
272 logging.info('DIFF %s IRQ Counts:\n%s', state, pprint.pformat(irq_diff))
273 if errors:
274 logging.info('ERRORS %s IRQ Counts:\n%s', state,
275 pprint.pformat(errors))
Mary Ruthvenf6c45092018-01-29 15:35:10 -0800276 self.run_errors.update(errors)
Mary Ruthven9bf599e2017-10-13 16:47:06 -0700277
278
Mary Ruthveneb417f22018-04-05 17:29:51 -0700279 def trigger_s0(self):
280 """Press the power button so the DUT will wake up."""
281 self.servo.power_short_press()
282
283
Mary Ruthven9bf599e2017-10-13 16:47:06 -0700284 def enter_state(self, state):
285 """Get the command to enter the power state"""
286 self.stage_irq_add(self.get_irq_counts(), 'start %s' % state)
287 if state == 'S0':
Mary Ruthveneb417f22018-04-05 17:29:51 -0700288 self.trigger_s0()
Mary Ruthven9bf599e2017-10-13 16:47:06 -0700289 else:
290 if state == 'S0ix':
291 full_command = 'echo freeze > /sys/power/state &'
292 elif state == 'S3':
293 full_command = 'echo mem > /sys/power/state &'
Mary Ruthven7bd39a82018-01-12 10:09:31 -0800294 elif state == 'G3':
Mary Ruthven9bf599e2017-10-13 16:47:06 -0700295 full_command = 'poweroff'
296 self.faft_client.system.run_shell_command(full_command)
297
298 time.sleep(self.SHORT_WAIT);
299 # check S3 state transition
Mary Ruthven6a4a9bc2018-01-12 10:29:06 -0800300 if not self.wait_power_state(state, self.SHORT_WAIT):
Mary Ruthven9bf599e2017-10-13 16:47:06 -0700301 raise error.TestFail('Platform failed to reach %s state.' % state)
302 self.stage_irq_add(self.get_irq_counts(), 'in %s' % state)
303
304
305 def stage_irq_add(self, irq_dict, name=''):
306 """Add the current irq counts to the stored dictionary of irq info"""
307 self.steps.append(irq_dict)
308 self.step_names.append('%11s' % name)
309 self.irqs.update(irq_dict.keys())
310
311
312 def reset_irq_counts(self):
313 """Reset the test IRQ counts"""
314 self.steps = []
315 self.step_names = []
316 self.irqs = set()
317
318
319 def run_transition(self, state):
Mary Ruthven6e0a32c2018-05-10 14:39:26 -0700320 """Enter the given power state and reenter s0
Mary Ruthven9bf599e2017-10-13 16:47:06 -0700321
Mary Ruthven6e0a32c2018-05-10 14:39:26 -0700322 Enter the power state and return to S0. Wait long enough to ensure cr50
323 will enter sleep mode, so we can verify that as well.
Mary Ruthven9bf599e2017-10-13 16:47:06 -0700324
325 Args:
Mary Ruthven7bd39a82018-01-12 10:09:31 -0800326 state: the power state: S0ix, S3, or G3
Mary Ruthven9bf599e2017-10-13 16:47:06 -0700327 """
328 self.reset_irq_counts()
329
330 # Enter the given state
331 self.enter_state(state)
332
333 logging.info('waiting %d seconds', self.SLEEP_TIME)
334 time.sleep(self.SLEEP_TIME)
335
336 # Return to S0
337 self.enter_state('S0')
338
Mary Ruthven6e0a32c2018-05-10 14:39:26 -0700339
340 def verify_state(self, state):
341 """Verify cr50 behavior while running through the power state"""
342
343 try:
344 self.run_transition(state)
345 finally:
346 # reset the system to S0 no matter what happens
347 self.trigger_s0()
348
Mary Ruthven9bf599e2017-10-13 16:47:06 -0700349 # Check that the progress of the irq counts seems reasonable
Mary Ruthven6a4a9bc2018-01-12 10:29:06 -0800350 self.check_for_errors(state)
Mary Ruthven9bf599e2017-10-13 16:47:06 -0700351
Mary Ruthvenf6c45092018-01-29 15:35:10 -0800352
353 def is_arm_family(self):
354 """Returns True if the DUT is an ARM device."""
355 arch = self.host.run('arch').stdout.strip()
Mary Ruthven9c5349d2018-01-12 12:39:24 -0800356 return arch in ['aarch64', 'armv7l']
Mary Ruthven9bf599e2017-10-13 16:47:06 -0700357
Mary Ruthvenf6c45092018-01-29 15:35:10 -0800358
359 def run_through_power_states(self):
Mary Ruthven7bd39a82018-01-12 10:09:31 -0800360 """Go through S0ix, S3, and G3. Verify there are no interrupt storms"""
Mary Ruthvenf6c45092018-01-29 15:35:10 -0800361 self.run_errors = {}
362 self.ccd_str = 'ccd ' + ('enabled' if self.ccd_enabled else 'disabled')
363 logging.info('Running through states with %s', self.ccd_str)
Mary Ruthven9bf599e2017-10-13 16:47:06 -0700364
365 # Initialize the Test IRQ counts
366 self.reset_irq_counts()
367
368 # Make sure the DUT is in s0
369 self.enter_state('S0')
370
Mary Ruthvenca1df1a2018-05-04 18:23:11 -0700371 # Check if the device supports S0ix. The exit status will be 0 if it
372 # does 1 if it doesn't.
373 result = self.host.run('check_powerd_config --suspend_to_idle',
374 ignore_status=True)
375 if not result.exit_status:
376 # Login before entering S0ix so cr50 will be able to enter regular
377 # sleep
Mary Ruthvenf6c45092018-01-29 15:35:10 -0800378 client_at = autotest.Autotest(self.host)
Mary Ruthven9c5349d2018-01-12 12:39:24 -0800379 client_at.run_test('login_LoginSuccess')
Mary Ruthven6e0a32c2018-05-10 14:39:26 -0700380 self.verify_state('S0ix')
Mary Ruthven9bf599e2017-10-13 16:47:06 -0700381
382 # Enter S3
Mary Ruthven6e0a32c2018-05-10 14:39:26 -0700383 self.verify_state('S3')
Mary Ruthven9bf599e2017-10-13 16:47:06 -0700384
Mary Ruthven7bd39a82018-01-12 10:09:31 -0800385 # Enter G3
Mary Ruthven6e0a32c2018-05-10 14:39:26 -0700386 self.verify_state('G3')
Mary Ruthvenf6c45092018-01-29 15:35:10 -0800387 if self.run_errors:
388 self.all_errors[self.ccd_str] = self.run_errors
389
390
391 def run_once(self, host):
392 """Go through S0ix, S3, and G3. Verify there are no interrupt storms"""
393 self.all_errors = {}
394 self.host = host
395 self.is_arm = self.is_arm_family()
Mary Ruthvenf1490552018-04-18 16:13:29 -0700396 supports_dts_control = self.cr50.servo_v4_supports_dts_mode()
397
398 if supports_dts_control:
399 self.cr50.ccd_disable(raise_error=True)
400
Mary Ruthvenf6c45092018-01-29 15:35:10 -0800401 self.ccd_enabled = self.cr50.ccd_is_enabled()
402 self.run_through_power_states()
403
Mary Ruthvenf1490552018-04-18 16:13:29 -0700404 if supports_dts_control:
405 ccd_was_enabled = self.ccd_enabled
406 self.cr50.ccd_enable(raise_error=supports_dts_control)
407 self.ccd_enabled = self.cr50.ccd_is_enabled()
408 # If the first run had ccd disabled, and the test was able to enable
409 # ccd, run through the states again to make sure there are no issues
410 # come up when ccd is enabled.
411 if not ccd_was_enabled and self.ccd_enabled:
412 # Reboot the EC to reset the device
413 self.ec.reboot()
414 self.run_through_power_states()
415 else:
416 logging.info('Current setup only supports test with ccd %sabled.',
417 'en' if self.ccd_enabled else 'dis')
Mary Ruthven9bf599e2017-10-13 16:47:06 -0700418
Mary Ruthvenea1222e2018-10-30 10:05:54 -0700419 self.trigger_s0()
Mary Ruthven6a4a9bc2018-01-12 10:29:06 -0800420 if self.all_errors:
Mary Ruthven9d4ba2a2018-05-04 17:52:43 -0700421 raise error.TestFail('Unexpected Device State: %s' %
422 self.all_errors)
Mary Ruthvenf1490552018-04-18 16:13:29 -0700423 if not supports_dts_control:
424 raise error.TestNAError('Verified device state with %s. Please '
425 'run with type c servo v4 to test full device state.' %
426 self.ccd_str)