blob: a403842b5219f52bf18f1b42c08be98fa1a6e709 [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
Mary Ruthven9bf599e2017-10-13 16:47:06 -070096 def get_taskinfo_output(self):
97 """Return a dict with the irq numbers as keys and counts as values"""
98 output = self.cr50.send_command_get_output('taskinfo',
99 self.GET_TASKINFO)[0][1].strip()
Mary Ruthven992c8db2018-05-07 11:28:57 -0700100 logging.info(output)
Mary Ruthven9bf599e2017-10-13 16:47:06 -0700101 return output
102
103
104 def get_irq_counts(self):
105 """Return a dict with the irq numbers as keys and counts as values"""
106 output = self.get_taskinfo_output()
Mary Ruthven992c8db2018-05-07 11:28:57 -0700107 irq_list = re.findall('\d+\s+\d+\r', output)
Mary Ruthvenf6c45092018-01-29 15:35:10 -0800108 # Make sure the regular sleep irq is in the dictionary, even if cr50
109 # hasn't seen any interrupts. It's important the test sees that there's
110 # never an interrupt.
111 irq_counts = { self.KEY_REGULAR_SLEEP : 0 }
Mary Ruthven9bf599e2017-10-13 16:47:06 -0700112 for irq_info in irq_list:
Mary Ruthven992c8db2018-05-07 11:28:57 -0700113 logging.debug(irq_info)
Mary Ruthven9bf599e2017-10-13 16:47:06 -0700114 num, count = irq_info.split()
115 irq_counts[int(num)] = int(count)
116 irq_counts[self.KEY_RESET] = int(self.servo.get('cr50_reset_count'))
117 irq_counts[self.KEY_DEEP_SLEEP] = int(self.cr50.get_deep_sleep_count())
118 irq_counts[self.KEY_TIME] = int(self.cr50.gettime())
119 return irq_counts
120
121
122 def get_expected_count(self, irq_key, cr50_time):
123 """Get the expected irq increase for the given irq and state
124
125 Args:
126 irq_key: the irq int
127 cr50_time: the cr50 time in seconds
128
129 Returns:
130 A list with the expected irq count range [min, max]
131 """
Mary Ruthvenf6c45092018-01-29 15:35:10 -0800132 # CCD will prevent sleep
Mary Ruthveneb9afff2018-02-21 17:56:42 -0800133 if self.ccd_enabled and (irq_key in self.SLEEP_KEYS or
134 self.DEEP_SLEEP_STEP_SUFFIX in str(irq_key)):
Mary Ruthvenf6c45092018-01-29 15:35:10 -0800135 return [0, 0]
Mary Ruthven9bf599e2017-10-13 16:47:06 -0700136 if irq_key == self.KEY_REGULAR_SLEEP:
Mary Ruthvenf1490552018-04-18 16:13:29 -0700137 # If cr50_time is really low, we probably woke cr50 up using
138 # taskinfo, which would be a pmu wakeup.
139 if cr50_time == 0:
140 return [0, 1]
141
Mary Ruthvenaef90672018-01-12 15:16:55 -0800142 min_count = max(cr50_time - self.SLEEP_DELAY, 0)
143 # Just checking there is not a lot of extra sleep wakeups. Add 1 to
144 # the sleep rate so cr50 can have some extra wakeups, but not too
145 # many.
146 max_count = cr50_time * (self.SLEEP_RATE + 1)
Mary Ruthven9bf599e2017-10-13 16:47:06 -0700147 return [min_count, max_count]
148 return self.EXPECTED_IRQ_COUNT_RANGE.get(irq_key, self.DEFAULT_COUNTS)
149
150
151 def check_increase(self, irq_key, name, increase, expected_range):
152 """Verify the irq count is within the expected range
153
154 Args:
155 irq_key: the irq int
156 name: the irq name string
157 increase: the irq count
158 expected_range: A list with the valid irq count range [min, max]
159
160 Returns:
161 '' if increase is in the given range. If the increase isn't in the
162 range, it returns an error message.
163 """
164 min_count, max_count = expected_range
165 if min_count > increase or max_count < increase:
166 err_msg = '%s %s: %s not in range %s' % (name, irq_key, increase,
167 expected_range)
168 return err_msg
169 return ''
170
171
172 def get_step_events(self):
173 """Use the deep sleep counts to determine the step events"""
174 ds_counts = self.get_irq_step_counts(self.KEY_DEEP_SLEEP)
175 events = []
176 for i, count in enumerate(ds_counts):
177 if not i:
178 events.append(self.START)
179 elif count != ds_counts[i - 1]:
180 # If the deep sleep count changed, Cr50 recovered deep sleep
181 # and the irq counts are reset.
182 events.append(self.DS_RESUME)
183 else:
184 events.append(self.INCREASE)
185 return events
186
187
188 def get_irq_step_counts(self, irq_key):
189 """Get a list of the all of the step counts for the given irq"""
190 return [ irq_dict.get(irq_key, 0) for irq_dict in self.steps ]
191
192
Mary Ruthven6a4a9bc2018-01-12 10:29:06 -0800193 def check_for_errors(self, state):
194 """Check for unexpected IRQ counts at each step.
195
Mary Ruthvenf6c45092018-01-29 15:35:10 -0800196 Find the irq count errors and add them to run_errors.
Mary Ruthven9bf599e2017-10-13 16:47:06 -0700197
198 Args:
Mary Ruthven7bd39a82018-01-12 10:09:31 -0800199 state: The power state: S0, S0ix, S3, or G3.
Mary Ruthven9bf599e2017-10-13 16:47:06 -0700200 """
Mary Ruthven9bf599e2017-10-13 16:47:06 -0700201 num_steps = len(self.steps)
202 # Get all of the deep sleep counts
203 events = self.get_step_events()
204
205 irq_list = list(self.irqs)
206 irq_list.sort()
207
Mary Ruthven9c5349d2018-01-12 12:39:24 -0800208 irq_diff = ['%23s' % 'step' + ''.join(self.step_names)]
Mary Ruthven9bf599e2017-10-13 16:47:06 -0700209 step_errors = [ [] for i in range(num_steps) ]
210
211 cr50_times = self.get_irq_step_counts(self.KEY_TIME)
212 cr50_diffs = []
213 for i, cr50_time in enumerate(cr50_times):
214 if events[i] == self.INCREASE:
215 cr50_time -= cr50_times[i - 1]
216 cr50_diffs.append(cr50_time)
217
218 # Go through each irq and update its info in the progress dict
219 for irq_key in irq_list:
220 name = self.INT_NAME.get(irq_key, 'Unknown')
Mary Ruthven9c5349d2018-01-12 12:39:24 -0800221 irq_progress_str = ['%19s %3s:' % (name, irq_key)]
Mary Ruthven9bf599e2017-10-13 16:47:06 -0700222
223 irq_counts = self.get_irq_step_counts(irq_key)
224 for step, count in enumerate(irq_counts):
225 event = events[step]
226
227 # The deep sleep counts are not reset after deep sleep. Change
228 # the event to INCREASE.
229 if irq_key == self.KEY_DEEP_SLEEP and event == self.DS_RESUME:
230 event = self.INCREASE
231
232 if event == self.INCREASE:
233 count -= irq_counts[step - 1]
234
235 # Check that the count increase is within the expected value.
236 if event != self.START:
237 expected_range = self.get_expected_count(irq_key,
238 cr50_diffs[step])
239
240 rv = self.check_increase(irq_key, name, count,
241 expected_range)
242 if rv:
243 logging.info('Unexpected count for %s %s', state, rv)
244 step_errors[step].append(rv)
245
246 irq_progress_str.append(' %2s %7d' % (event, count))
247
248 irq_diff.append(''.join(irq_progress_str))
249
250 errors = {}
251
Mary Ruthven9c5349d2018-01-12 12:39:24 -0800252 ds_key = self.ARM if self.is_arm else ''
253 ds_key += state + self.DEEP_SLEEP_STEP_SUFFIX
Mary Ruthven9bf599e2017-10-13 16:47:06 -0700254 expected_range = self.get_expected_count(ds_key, 0)
255 rv = self.check_increase(None, ds_key, events.count(self.DS_RESUME),
256 expected_range)
257 if rv:
258 logging.info('Unexpected count for %s %s', state, rv)
259 errors[ds_key] = rv
260 for i, step_error in enumerate(step_errors):
261 if step_error:
262 logging.error('Step %d errors:\n%s', i,
263 pprint.pformat(step_error))
264 step = '%s step %d %s' % (state, i, self.step_names[i].strip())
265 errors[step] = step_error
266
267 logging.info('DIFF %s IRQ Counts:\n%s', state, pprint.pformat(irq_diff))
268 if errors:
269 logging.info('ERRORS %s IRQ Counts:\n%s', state,
270 pprint.pformat(errors))
Mary Ruthvenf6c45092018-01-29 15:35:10 -0800271 self.run_errors.update(errors)
Mary Ruthven9bf599e2017-10-13 16:47:06 -0700272
273
Mary Ruthveneb417f22018-04-05 17:29:51 -0700274 def trigger_s0(self):
275 """Press the power button so the DUT will wake up."""
276 self.servo.power_short_press()
277
278
Mary Ruthven9bf599e2017-10-13 16:47:06 -0700279 def enter_state(self, state):
280 """Get the command to enter the power state"""
281 self.stage_irq_add(self.get_irq_counts(), 'start %s' % state)
282 if state == 'S0':
Mary Ruthveneb417f22018-04-05 17:29:51 -0700283 self.trigger_s0()
Mary Ruthven9bf599e2017-10-13 16:47:06 -0700284 else:
285 if state == 'S0ix':
286 full_command = 'echo freeze > /sys/power/state &'
287 elif state == 'S3':
288 full_command = 'echo mem > /sys/power/state &'
Mary Ruthven7bd39a82018-01-12 10:09:31 -0800289 elif state == 'G3':
Mary Ruthven9bf599e2017-10-13 16:47:06 -0700290 full_command = 'poweroff'
291 self.faft_client.system.run_shell_command(full_command)
292
293 time.sleep(self.SHORT_WAIT);
294 # check S3 state transition
Mary Ruthven6a4a9bc2018-01-12 10:29:06 -0800295 if not self.wait_power_state(state, self.SHORT_WAIT):
Mary Ruthven9bf599e2017-10-13 16:47:06 -0700296 raise error.TestFail('Platform failed to reach %s state.' % state)
297 self.stage_irq_add(self.get_irq_counts(), 'in %s' % state)
298
299
300 def stage_irq_add(self, irq_dict, name=''):
301 """Add the current irq counts to the stored dictionary of irq info"""
302 self.steps.append(irq_dict)
303 self.step_names.append('%11s' % name)
304 self.irqs.update(irq_dict.keys())
305
306
307 def reset_irq_counts(self):
308 """Reset the test IRQ counts"""
309 self.steps = []
310 self.step_names = []
311 self.irqs = set()
312
313
314 def run_transition(self, state):
Mary Ruthven6e0a32c2018-05-10 14:39:26 -0700315 """Enter the given power state and reenter s0
Mary Ruthven9bf599e2017-10-13 16:47:06 -0700316
Mary Ruthven6e0a32c2018-05-10 14:39:26 -0700317 Enter the power state and return to S0. Wait long enough to ensure cr50
318 will enter sleep mode, so we can verify that as well.
Mary Ruthven9bf599e2017-10-13 16:47:06 -0700319
320 Args:
Mary Ruthven7bd39a82018-01-12 10:09:31 -0800321 state: the power state: S0ix, S3, or G3
Mary Ruthven9bf599e2017-10-13 16:47:06 -0700322 """
323 self.reset_irq_counts()
324
325 # Enter the given state
326 self.enter_state(state)
327
328 logging.info('waiting %d seconds', self.SLEEP_TIME)
329 time.sleep(self.SLEEP_TIME)
330
331 # Return to S0
332 self.enter_state('S0')
333
Mary Ruthven6e0a32c2018-05-10 14:39:26 -0700334
335 def verify_state(self, state):
336 """Verify cr50 behavior while running through the power state"""
337
338 try:
339 self.run_transition(state)
340 finally:
341 # reset the system to S0 no matter what happens
342 self.trigger_s0()
343
Mary Ruthven9bf599e2017-10-13 16:47:06 -0700344 # Check that the progress of the irq counts seems reasonable
Mary Ruthven6a4a9bc2018-01-12 10:29:06 -0800345 self.check_for_errors(state)
Mary Ruthven9bf599e2017-10-13 16:47:06 -0700346
Mary Ruthvenf6c45092018-01-29 15:35:10 -0800347
348 def is_arm_family(self):
349 """Returns True if the DUT is an ARM device."""
350 arch = self.host.run('arch').stdout.strip()
Mary Ruthven9c5349d2018-01-12 12:39:24 -0800351 return arch in ['aarch64', 'armv7l']
Mary Ruthven9bf599e2017-10-13 16:47:06 -0700352
Mary Ruthvenf6c45092018-01-29 15:35:10 -0800353
354 def run_through_power_states(self):
Mary Ruthven7bd39a82018-01-12 10:09:31 -0800355 """Go through S0ix, S3, and G3. Verify there are no interrupt storms"""
Mary Ruthvenf6c45092018-01-29 15:35:10 -0800356 self.run_errors = {}
357 self.ccd_str = 'ccd ' + ('enabled' if self.ccd_enabled else 'disabled')
358 logging.info('Running through states with %s', self.ccd_str)
Mary Ruthven9bf599e2017-10-13 16:47:06 -0700359
360 # Initialize the Test IRQ counts
361 self.reset_irq_counts()
362
363 # Make sure the DUT is in s0
364 self.enter_state('S0')
365
Mary Ruthvenca1df1a2018-05-04 18:23:11 -0700366 # Check if the device supports S0ix. The exit status will be 0 if it
367 # does 1 if it doesn't.
368 result = self.host.run('check_powerd_config --suspend_to_idle',
369 ignore_status=True)
370 if not result.exit_status:
371 # Login before entering S0ix so cr50 will be able to enter regular
372 # sleep
Mary Ruthvenf6c45092018-01-29 15:35:10 -0800373 client_at = autotest.Autotest(self.host)
Mary Ruthven9c5349d2018-01-12 12:39:24 -0800374 client_at.run_test('login_LoginSuccess')
Mary Ruthven6e0a32c2018-05-10 14:39:26 -0700375 self.verify_state('S0ix')
Mary Ruthven9bf599e2017-10-13 16:47:06 -0700376
377 # Enter S3
Mary Ruthven6e0a32c2018-05-10 14:39:26 -0700378 self.verify_state('S3')
Mary Ruthven9bf599e2017-10-13 16:47:06 -0700379
Mary Ruthven7bd39a82018-01-12 10:09:31 -0800380 # Enter G3
Mary Ruthven6e0a32c2018-05-10 14:39:26 -0700381 self.verify_state('G3')
Mary Ruthvenf6c45092018-01-29 15:35:10 -0800382 if self.run_errors:
383 self.all_errors[self.ccd_str] = self.run_errors
384
385
386 def run_once(self, host):
387 """Go through S0ix, S3, and G3. Verify there are no interrupt storms"""
388 self.all_errors = {}
389 self.host = host
390 self.is_arm = self.is_arm_family()
Mary Ruthvenf1490552018-04-18 16:13:29 -0700391 supports_dts_control = self.cr50.servo_v4_supports_dts_mode()
392
393 if supports_dts_control:
394 self.cr50.ccd_disable(raise_error=True)
395
Mary Ruthvenf6c45092018-01-29 15:35:10 -0800396 self.ccd_enabled = self.cr50.ccd_is_enabled()
397 self.run_through_power_states()
398
Mary Ruthvenf1490552018-04-18 16:13:29 -0700399 if supports_dts_control:
400 ccd_was_enabled = self.ccd_enabled
401 self.cr50.ccd_enable(raise_error=supports_dts_control)
402 self.ccd_enabled = self.cr50.ccd_is_enabled()
403 # If the first run had ccd disabled, and the test was able to enable
404 # ccd, run through the states again to make sure there are no issues
405 # come up when ccd is enabled.
406 if not ccd_was_enabled and self.ccd_enabled:
Mary Ruthvenf1490552018-04-18 16:13:29 -0700407 self.run_through_power_states()
408 else:
409 logging.info('Current setup only supports test with ccd %sabled.',
410 'en' if self.ccd_enabled else 'dis')
Mary Ruthven9bf599e2017-10-13 16:47:06 -0700411
Mary Ruthvenea1222e2018-10-30 10:05:54 -0700412 self.trigger_s0()
Mary Ruthven6a4a9bc2018-01-12 10:29:06 -0800413 if self.all_errors:
Mary Ruthven9d4ba2a2018-05-04 17:52:43 -0700414 raise error.TestFail('Unexpected Device State: %s' %
415 self.all_errors)
Mary Ruthvenf1490552018-04-18 16:13:29 -0700416 if not supports_dts_control:
417 raise error.TestNAError('Verified device state with %s. Please '
418 'run with type c servo v4 to test full device state.' %
419 self.ccd_str)