• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Copyright 2018 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.cros.faft.cr50_test import Cr50Test
10from autotest_lib.server.cros.servo import servo
11
12
13class firmware_Cr50OpenWhileAPOff(Cr50Test):
14    """Verify the console can be opened while the AP is off.
15
16    Make sure it runs ok when cr50 saw the AP turn off and when it resets while
17    the AP is off.
18
19    This test would work the same with any cr50 ccd command that uses vendor
20    commands. 'ccd open' is just one.
21    """
22    version = 1
23
24    SLEEP_DELAY = 20
25    SHORT_DELAY = 2
26    CCD_PASSWORD_RATE_LIMIT = 3
27
28    def initialize(self, host, cmdline_args, full_args):
29        """Initialize the test"""
30        self.changed_dut_state = False
31        super(firmware_Cr50OpenWhileAPOff, self).initialize(host, cmdline_args,
32                full_args)
33
34        if not hasattr(self, 'cr50'):
35            raise error.TestNAError('Test can only be run on devices with '
36                                    'access to the Cr50 console')
37
38        # TODO(mruthven): replace with dependency on servo v4 with servo micro
39        # and type c cable.
40        if (self.servo.get_servo_version(active=True) !=
41            'servo_v4_with_servo_micro'):
42            raise error.TestNAError('Run using servo v4 with servo micro')
43
44        if not self.cr50.servo_dts_mode_is_valid():
45            raise error.TestNAError('Plug in servo v4 type c cable into ccd '
46                    'port')
47
48        self.fast_ccd_open(enable_testlab=True)
49        # make sure password is cleared.
50        self.cr50.send_command('ccd reset')
51        # Set GscFullConsole to Always, so we can always use gpioset.
52        self.cr50.set_cap('GscFullConsole', 'Always')
53        # You can only open cr50 from the console if a password is set. Set
54        # a password, so we can use it to open cr50 while the AP is off.
55        self.set_ccd_password(self.CCD_PASSWORD)
56
57        # Asserting warm_reset will hold the AP in reset if the system uses
58        # SYS_RST instead of PLT_RST. If the system uses PLT_RST, we have to
59        # hold the EC in reset to guarantee the device won't turn on during
60        # open.
61        # warm_reset doesn't interfere with rdd, so it's best to use that when
62        # possible.
63        self.reset_ec = self.cr50.uses_board_property('BOARD_USE_PLT_RESET')
64        self.changed_dut_state = True
65        if self.reset_ec and not self.reset_device_get_deep_sleep_count(True):
66            # Some devices can't tell the AP is off when the EC is off. Try
67            # deep sleep with just the AP off.
68            self.reset_ec = False
69            # If deep sleep doesn't work at all, we can't run the test.
70            if not self.reset_device_get_deep_sleep_count(True):
71                raise error.TestNAError('Skipping test on device without deep '
72                        'sleep support')
73            # We can't hold the ec in reset and enter deep sleep. Set the
74            # capability so physical presence isn't required for open.
75            logging.info("deep sleep doesn't work with EC in reset. skipping "
76                         "physical presence checks.")
77            # set OpenNoLongPP so open won't require pressing the power button.
78            self.cr50.set_cap('OpenNoLongPP', 'Always')
79        else:
80            logging.info('Physical presence can be used during open')
81
82
83    def cleanup(self):
84        """Make sure the device is on at the end of the test"""
85        # If we got far enough to start changing the DUT power state, attempt to
86        # turn the DUT back on and reenable the cr50 console.
87        try:
88            if self.changed_dut_state:
89                self.restore_dut()
90        finally:
91            super(firmware_Cr50OpenWhileAPOff, self).cleanup()
92
93
94    def restore_dut(self):
95        """Turn on the device and reset cr50
96
97        Do a deep sleep reset to fix the cr50 console. Then turn the device on.
98
99        Raises:
100            TestFail if the cr50 console doesn't work
101        """
102        logging.info('attempt cr50 console recovery')
103
104        # The console may be hung. Run through reset manually, so we dont need
105        # the console.
106        self.turn_device('off')
107        # Toggle dts mode to enter and exit deep sleep
108        self.toggle_dts_mode()
109        # Turn the device back on
110        self.turn_device('on')
111
112        # Verify the cr50 console responds to commands.
113        try:
114            logging.info(self.cr50.get_ccdstate())
115        except servo.ResponsiveConsoleError as e:
116            logging.info('Console is responsive. Unable to match output: %s',
117                         str(e))
118        except servo.UnresponsiveConsoleError as e:
119            raise error.TestFail('Could not restore Cr50 console')
120        logging.info('Cr50 console ok.')
121
122
123    def turn_device(self, state):
124        """Turn the device off or on.
125
126        If we are testing ccd open fully, it will also assert device reset so
127        power button presses wont turn on the AP
128        """
129        # Assert or deassert the device reset signal. The reset signal state
130        # should be the inverse of the device state.
131        reset_signal_state = 'on' if state == 'off' else 'off'
132        if self.reset_ec:
133            self.servo.set('cold_reset', reset_signal_state)
134        else:
135            self.servo.set('warm_reset', reset_signal_state)
136
137        time.sleep(self.SHORT_DELAY)
138
139        # Press the power button to turn on the AP, if it doesn't automatically
140        # turn on after deasserting the reset signal. ap_is_on will print the
141        # ccdstate which is useful for debugging. Do that first, so it always
142        # happens.
143        if not self.cr50.ap_is_on() and state == 'on':
144            self.servo.power_short_press()
145            time.sleep(self.SHORT_DELAY)
146
147
148    def reset_device_get_deep_sleep_count(self, deep_sleep):
149        """Reset the device. Use dts mode to enable deep sleep if requested.
150
151        Args:
152            deep_sleep: True if Cr50 should enter deep sleep
153
154        Returns:
155            The number of times Cr50 entered deep sleep during reset
156        """
157        self.turn_device('off')
158        # Do a deep sleep reset to restore the cr50 console.
159        ds_count = self.deep_sleep_reset_get_count() if deep_sleep else 0
160        self.turn_device('on')
161        return ds_count
162
163
164    def set_dts(self, state):
165        """Set servo v4 dts mode"""
166        self.servo.set_dts_mode(state)
167        # Some boards can't detect DTS mode when the EC is off. After 0.X.18,
168        # we can set CCD_MODE_L manually using gpioset. If detection is working,
169        # this won't do anything. If it isn't working, it'll force cr50 to
170        # disconnect ccd.
171        if state == 'off':
172            time.sleep(self.SHORT_DELAY)
173            self.cr50.send_command('gpioset CCD_MODE_L 1')
174
175
176    def toggle_dts_mode(self):
177        """Toggle DTS mode to enable and disable deep sleep"""
178        # We cant use cr50 ccd_disable/enable, because those uses the cr50
179        # console. Call servo_v4_dts_mode directly.
180        self.set_dts('off')
181
182        time.sleep(self.SLEEP_DELAY)
183        self.set_dts('on')
184
185
186    def deep_sleep_reset_get_count(self):
187        """Toggle ccd to get to do a deep sleep reset
188
189        Returns:
190            The number of times cr50 entered deep sleep
191        """
192        start_count = self.cr50.get_deep_sleep_count()
193        # CCD is what's keeping Cr50 awake. Toggle DTS mode to turn off ccd
194        # so cr50 will enter deep sleep
195        self.toggle_dts_mode()
196        # Return the number of times cr50 entered deep sleep.
197        return self.cr50.get_deep_sleep_count() - start_count
198
199
200    def try_ccd_open(self, cr50_reset):
201        """Try 'ccd open' and make sure the console doesn't hang"""
202        self.cr50.set_ccd_level('lock', self.CCD_PASSWORD)
203        try:
204            self.turn_device('off')
205            if cr50_reset:
206                if not self.deep_sleep_reset_get_count():
207                    raise error.TestFail('Did not detect a cr50 reset')
208            # Verify ccd open
209            self.cr50.set_ccd_level('open', self.CCD_PASSWORD)
210        finally:
211            self.restore_dut()
212
213
214    def run_once(self):
215        """Turn off the AP and try ccd open."""
216        self.try_ccd_open(False)
217        self.try_ccd_open(True)
218