1# 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 time 7 8from autotest_lib.client.common_lib import error 9from autotest_lib.server import autotest 10from autotest_lib.server.cros.faft.firmware_test import FirmwareTest 11 12 13class firmware_Cr50DeepSleepStress(FirmwareTest): 14 """Verify cr50 deep sleep after running power_SuspendStress. 15 16 Cr50 should enter deep sleep every suspend. Verify that by checking the 17 idle deep sleep count. 18 19 @param suspend_count: The number of times to reboot or suspend the device. 20 @param reset_type: a str with the cycle type: 'mem' or 'reboot' 21 """ 22 version = 1 23 24 SLEEP_DELAY = 20 25 MIN_RESUME = 15 26 MIN_SUSPEND = 15 27 MEM = 'mem' 28 29 def initialize(self, host, cmdline_args, suspend_count, reset_type): 30 """Make sure the test is running with access to the cr50 console""" 31 super(firmware_Cr50DeepSleepStress, self).initialize(host, cmdline_args) 32 if not hasattr(self, 'cr50'): 33 raise error.TestNAError('Test can only be run on devices with ' 34 'access to the Cr50 console') 35 # Reset the device 36 self.servo.get_power_state_controller().reset() 37 38 # Save the original version, so we can make sure cr50 doesn't rollback. 39 self.original_cr50_version = self.cr50.get_active_version_info() 40 41 42 def check_cr50_state(self, expected_count, expected_version): 43 """Check the reboot count and version match the expected values""" 44 count = self.cr50.get_deep_sleep_count() 45 version = self.cr50.get_active_version_info() 46 47 logging.info('running %s', version) 48 logging.info('After %s reboots, Cr50 resumed from deep sleep %s times', 49 expected_count, count) 50 51 mismatch = [] 52 if count != expected_count: 53 mismatch.append('count mismatch: expected %s got %s' % 54 (expected_count, count)) 55 if version != expected_version: 56 mismatch.append('version mismatch: expected %s got %s' % 57 (expected_version, version)) 58 59 if mismatch: 60 raise error.TestFail('Deep Sleep Failure: "%s"' % 61 '" and "'.join(mismatch)) 62 63 64 def run_reboots(self, suspend_count): 65 """Reboot the device the requested number of times 66 67 @param suspend_count: the number of times to reboot the device. 68 """ 69 if self.cr50.using_ccd(): 70 raise error.TestNAError('Reboot deep sleep tests can only be run ' 71 'with a servo flex') 72 # This test may be running on servo v4 with servo micro. That servo v4 73 # may have a type A cable or type C cable for data communication with 74 # the DUT. If it is using a type C cable, that cable will look like a 75 # debug accessory. Run ccd_disable, to stop debug accessory mode and 76 # prevent CCD from interfering with deep sleep. Running ccd_disable on 77 # a type A servo v4 or any other servo version isn't harmful. 78 self.cr50.ccd_disable() 79 80 for i in range(suspend_count): 81 # Power off the device 82 self.servo.get_power_state_controller().power_off() 83 time.sleep(self.MIN_SUSPEND) 84 85 # Power on the device 86 self.servo.power_short_press() 87 time.sleep(self.MIN_RESUME) 88 89 # Make sure it booted into normal mode 90 self.check_state((self.checkers.crossystem_checker, 91 {'mainfw_type': 'normal'})) 92 93 94 def run_suspend_resume(self, host, suspend_count): 95 """Suspend the device the requested number of times 96 97 @param host: the host object representing the DUT. 98 @param suspend_count: the number of times to suspend the device. 99 """ 100 client_at = autotest.Autotest(host) 101 102 # Disable CCD so Cr50 can enter deep sleep 103 self.cr50.ccd_disable() 104 # Duration is set to 0, because it is required but unused when 105 # iterations is given. 106 client_at.run_test('power_SuspendStress', tag='idle', 107 duration=0, 108 min_suspend=self.MIN_SUSPEND, 109 min_resume=self.MIN_RESUME, 110 check_connection=False, 111 iterations=suspend_count, 112 suspend_state=self.MEM) 113 # Reenable CCD 114 self.cr50.ccd_enable() 115 116 117 def get_expected_ds_count(self, host, reset_type, suspend_count): 118 """Returns the expected deep sleep count""" 119 is_arm = host.run('arch').stdout.strip() in ['aarch64', 'armv7l'] 120 # x86 devices should suspend once per reset. ARM will only suspend 121 # if the device enters s5. 122 return 0 if (reset_type != 'reboot' and is_arm) else suspend_count 123 124 125 def run_once(self, host, suspend_count, reset_type): 126 """Verify deep sleep after suspending for the given number of cycles 127 128 The test either suspends to s3 or reboots the device depending on 129 reset_type. There are two valid reset types: mem and reboot. The test 130 will make sure that the device is off or in s3 long enough to ensure 131 Cr50 should be able to enter deep sleep. At the end of the test, it 132 checks that Cr50 entered deep sleep the same number of times it 133 suspended. 134 135 @param host: the host object representing the DUT. 136 @param suspend_count: The number of cycles to suspend or reboot the 137 device. 138 @param reset_type: a str with the cycle type: 'mem' or 'reboot' 139 """ 140 if self.MIN_SUSPEND + self.MIN_RESUME < self.SLEEP_DELAY: 141 logging.info('Minimum suspend-resume cycle is %ds. This is ' 142 'shorter than the Cr50 idle timeout. Cr50 may not ' 143 'enter deep sleep every cycle', 144 self.MIN_SUSPEND + self.MIN_RESUME) 145 if not suspend_count: 146 raise error.TestFail('Need to provide non-zero suspend_count') 147 148 # Clear the deep sleep count 149 logging.info('Clear Cr50 deep sleep count') 150 self.cr50.clear_deep_sleep_count() 151 152 if reset_type == 'reboot': 153 self.run_reboots(suspend_count) 154 elif reset_type == 'mem': 155 self.run_suspend_resume(host, suspend_count) 156 else: 157 raise error.TestNAError('Invalid reset_type. Use "mem" or "reboot"') 158 159 # Cr50 should enter deep sleep once per suspend cycle if deep sleep is 160 # supported 161 expected_ds_count = self.get_expected_ds_count(host, reset_type, 162 suspend_count) 163 self.check_cr50_state(expected_ds_count, self.original_cr50_version) 164