1# Copyright 2019 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_Cr50DeferredECReset(Cr50Test):
14    """Verify EC_RST_L stays asserted only if all conditions below are True.
15    (1) System got 'Power-On reset'.
16    (2) RDD cable is connected.
17    (3) The power button is held.
18
19    After this, EC_RST_L should be deasserted as soon as the power button
20    gets released.
21
22    Attributes
23        version: test version number
24        CUTOFF_DELAY: duration in second to keep cr50 powered off,
25        PD_SETTLE_TIME: duration in second to wait PD to be stable
26        HAS_CR50_RESET_ODL: boolean if 'cr50_reset_odl' control is available
27    """
28    version = 1
29    CUTOFF_DELAY = 10
30    PD_SETTLE_TIME = 3
31    WAIT_DUT_UP = 5
32    HAS_CR50_RESET_ODL = False
33
34    def cr50_power_on_reset(self):
35        """Perform a power-on-reset on cr50.
36           If cr50_reset_odl control is available, then use it.
37           Otherwise, disconnect and reconnect battery and power source.
38        """
39        if self.HAS_CR50_RESET_ODL:
40            self.servo.set('cr50_reset_odl', 'on')
41
42            time.sleep(self.CUTOFF_DELAY)
43
44            self.servo.set('cr50_reset_odl', 'off')
45        else:
46            # Stop power delivery to dut
47            logging.info('Stop charging')
48            self.servo.set('servo_pd_role', 'snk')
49
50            # Battery Cutoff
51            logging.info('Cut battery off')
52            self.ec.send_command('cutoff')
53
54            time.sleep(self.CUTOFF_DELAY)
55
56            # Enable power delivery to dut
57            logging.info('Start charging')
58            self.servo.set('servo_pd_role', 'src')
59
60        time.sleep(self.PD_SETTLE_TIME)
61
62    def ac_is_plugged_in(self):
63        """Check if AC is plugged.
64
65        Returns:
66            True if AC is plugged, or False otherwise.
67        """
68
69        rv = self.ec.send_command_get_output('chgstate',
70                                             [r'ac\s*=\s*(0|1)\s*'])[0][1]
71        return rv == '1'
72
73    def cleanup(self):
74        """Restore dts mode."""
75        try:
76            if hasattr(self, 'HAS_CR50_RESET_ODL'):
77                self.restore_dut(self.HAS_CR50_RESET_ODL)
78                self.servo.set_dts_mode(self.dts_restore)
79        finally:
80            super(firmware_Cr50DeferredECReset, self).cleanup()
81
82    def initialize(self, host, cmdline_args, full_args):
83        """Initialize the test and check if cr50 exists, DTS is controllable,
84           and power delivery mode and power button is adjustable.
85
86        Raises:
87            TestFail: If test initialization setup fails.
88            TestNAError: If the dut is not proper for this test for its RDD
89                         recognition problem.
90        """
91        super(firmware_Cr50DeferredECReset, self).initialize(host, cmdline_args,
92                full_args)
93        if not hasattr(self, 'cr50'):
94            raise error.TestNAError('Test can only be run on devices with '
95                                    'access to the Cr50 console')
96        if not self.cr50.servo_dts_mode_is_valid():
97            raise error.TestNAError('Need working servo v4 DTS control')
98        self.dts_restore = self.servo.get_dts_mode()
99
100        # Fast open cr50 and check if testlab is enabled.
101        self.fast_ccd_open(enable_testlab=True)
102        if not self.cr50.testlab_is_on():
103            raise error.TestNAError('Cr50 testlab mode is not enabled')
104
105        # Check 'rdd_leakage' is marked in cr50 capability.
106        if self.check_cr50_capability(['rdd_leakage']):
107            self.rdd_leakage = True
108            logging.warning('RDD leakage is marked in cr50 cap config')
109        else:
110            self.rdd_leakage = False
111
112        # Test if the power button is adjustable.
113        self.servo.set('pwr_button', 'press')
114        self.servo.set('pwr_button', 'release')
115
116        # Check if 'cr50_reset_odl' controlis available.
117        try:
118            self.servo.get('cr50_reset_odl')
119            self.HAS_CR50_RESET_ODL = True
120        except error.TestFail:
121            self.HAS_CR50_RESET_ODL = False
122
123            # Test the external power delivery
124            self.servo.set('servo_pd_role', 'snk')
125            time.sleep(self.PD_SETTLE_TIME)
126
127            if self.ac_is_plugged_in():
128                raise error.TestFail('Failed to set servo_v4_role sink')
129
130            # Test stopping the external power delivery
131            self.servo.set('servo_pd_role', 'src')
132            time.sleep(self.PD_SETTLE_TIME)
133
134            if not self.ac_is_plugged_in():
135                raise error.TestFail('Failed to set servo_v4_role source')
136
137        # Check if the dut has any RDD recognition issue.
138        # First, cut off power source and hold the power button.
139        #        disable RDD connection.
140        self.servo.set_dts_mode('off')
141        self.servo.set('pwr_button', 'press')
142        self.cr50_power_on_reset()
143        try:
144            #  Third, check if the rdd status is disconnected.
145            #         If not, terminate the test.
146            ccdstate = self.cr50.get_ccdstate()
147
148            if (ccdstate['Rdd'].lower() != 'disconnected') != self.rdd_leakage:
149                raise error.TestError('RDD leakage does not match capability'
150                                      ' configuration.')
151        finally:
152            self.restore_dut(False)
153
154        logging.info('Initialization is done')
155
156    def restore_dut(self, use_cr50_reset):
157        """Restore the dut state."""
158        logging.info('Restore the dut')
159        self.servo.set('pwr_button', 'release')
160
161        if use_cr50_reset:
162            self.servo.set_nocheck('cr50_reset_odl', 'off')
163        else:
164            time.sleep(self.PD_SETTLE_TIME)
165            self.servo.set_nocheck('servo_pd_role', 'snk')
166            time.sleep(self.PD_SETTLE_TIME)
167            self.servo.set_nocheck('servo_pd_role', 'src')
168
169        # Give the EC some time to come up before resetting cr50.
170        time.sleep(self.WAIT_DUT_UP)
171
172        # Reboot cr50 to ensure EC_RST_L is deasserted.
173        self.fast_ccd_open(enable_testlab=True)
174        self.cr50.reboot()
175
176        time.sleep(self.WAIT_DUT_UP)
177
178        # Press power button to wake up AP, and releases it soon
179        # in any cases.
180        if not self.cr50.ap_is_on():
181            self.servo.power_short_press()
182        logging.info('Restoration done')
183
184    def check_ecrst_asserted(self, expect_assert):
185        """Ask CR50 whether EC_RST_L is asserted or deasserted.
186
187        Args:
188            expect_assert: True if it is expected asserted.
189                           False otherwise.
190
191        Raises:
192            TestFail: if ecrst value is not as expected.
193        """
194
195        # If the console is responsive, then the EC is awake.
196        expected_txt = 'asserted' if expect_assert else 'deasserted'
197        logging.info('Checking if ecrst is %s', expected_txt)
198
199        try:
200            rv = self.cr50.send_command_retry_get_output(
201                    'ecrst', [r'EC_RST_L is ((de)?asserted)'], safe=True)
202            logging.info(rv)
203        except error.TestError as e:
204            raise error.TestFail(str(e))
205        actual_txt = rv[0][1]
206        logging.info('ecrst is %s', actual_txt)
207        if actual_txt != expected_txt:
208            raise error.TestFail('EC_RST_L mismatch: expected %r got %r' %
209                                 (expected_txt, actual_txt))
210
211    def ping_ec(self, expect_response):
212        """Check if EC is running and responding.
213
214        Args:
215            expect_response: True if EC should respond
216                             False otherwise.
217        Raises:
218            TestFail: if ec responsiveness is not as same as expected.
219        """
220        try:
221            logging.info('Checking if ec is %sresponsive',
222                         '' if expect_response else 'not ')
223            rv = self.ec.send_command_get_output('help', ['.*>'])[0].strip()
224        except servo.UnresponsiveConsoleError as e:
225            logging.info(str(e))
226            return
227        else:
228            if not expect_response:
229                logging.error('EC should not respond')
230                raise error.TestFail(rv)
231
232    def test_deferred_ec_reset(self, power_button_hold, rdd_enable):
233        """Do a power-on reset, and check if EC responds.
234
235        Args:
236            power_button_hold: True if it should be pressed on a system reset.
237                               False otherwise.
238            rdd_enable: True if RDD should be detected on a system reset.
239                        False otherwise.
240        """
241
242        # If the board has a rdd leakage issue, RDD shall be detected
243        # always in G3. EC_RST will be asserted if the power_button is
244        # being presed in this test.
245        expect_ec_response = not (power_button_hold and
246                                  (rdd_enable or self.rdd_leakage))
247        logging.info('Test deferred_ec_reset starts')
248        logging.info('Power button held    : %s', power_button_hold)
249        logging.info('RDD connection       : %s', rdd_enable)
250        logging.info('RDD leakage          : %s', self.rdd_leakage)
251        logging.info('Expected EC response : %s', expect_ec_response)
252
253        try:
254            # enable RDD Connection (or disable) before power-on-reset
255            self.servo.set_dts_mode('on' if rdd_enable else 'off')
256
257            # Set power button before the dut loses power,
258            # because the power button value cannot be changed during power-off.
259            self.servo.set('pwr_button',
260                           'press' if power_button_hold else 'release')
261
262            # Do a power-on-reset on CR50.
263            self.cr50_power_on_reset()
264
265            # Wait for a while
266            wait_sec = 30
267            logging.info('waiting for %d seconds', wait_sec)
268            time.sleep(wait_sec)
269
270            # Check if EC_RST_L is asserted and EC is on.
271            # (or EC_RST_L deasserted and EC off)
272            self.check_ecrst_asserted(not expect_ec_response)
273            self.ping_ec(expect_ec_response)
274
275            # Release power button
276            logging.info('Power button released')
277            self.servo.set('pwr_button', 'release')
278            time.sleep(self.PD_SETTLE_TIME)
279
280            # Check if EC_RST_L is deasserted and EC is on.
281            self.check_ecrst_asserted(False)
282            self.ping_ec(True)
283
284        finally:
285            self.restore_dut(self.HAS_CR50_RESET_ODL)
286
287    def run_once(self):
288        """Test deferred EC reset feature. """
289
290        # Release power button and disable RDD on power-on reset.
291        # EC should be running.
292        self.test_deferred_ec_reset(power_button_hold=False, rdd_enable=False)
293
294        # Release power button but enable RDD on power-on reset.
295        # EC should be running.
296        self.test_deferred_ec_reset(power_button_hold=False, rdd_enable=True)
297
298        # Hold power button but disable RDD on power-on reset.
299        # EC should be running.
300        self.test_deferred_ec_reset(power_button_hold=True, rdd_enable=False)
301
302        # Hold power button and enable RDD on power-on reset.
303        # EC should not be running.
304        self.test_deferred_ec_reset(power_button_hold=True, rdd_enable=True)
305
306        logging.info('Test is done')
307