• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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