faft: firmware_Cr50DeferredECReset releases power_button on test fail

This patch cleans up the dut control status on test failure, like
releasing power button, turning on dts mode, changing servo_v4_role
as source.

At test initialization, it checks whether the board is suitable for
this test by checking RDD recognition issue.

BUG=None
BRANCH=None
TEST=manually ran test_that on Coral, Fleex(Octopus) and Atlas.
[Coral]
----------------------------------------------------------------------
firmware_Cr50DeferredECReset                              [  PASSED  ]
firmware_Cr50DeferredECReset/firmware_Cr50DeferredECReset [  PASSED  ]
----------------------------------------------------------------------

[Fleex]
----------------------------------------------------------------------
firmware_Cr50DeferredECReset                              [  PASSED  ]
firmware_Cr50DeferredECReset/firmware_Cr50DeferredECReset [  PASSED  ]
----------------------------------------------------------------------

[Atlas]
----------------------------------------------------------------------
firmware_Cr50DeferredECReset                              [  PASSED  ]
firmware_Cr50DeferredECReset                                TEST_NA:
				This board has a RDD recognition issue
firmware_Cr50DeferredECReset/firmware_Cr50DeferredECReset [  PASSED  ]
firmware_Cr50DeferredECReset/firmware_Cr50DeferredECReset   TEST_NA:
				This board has a RDD recognition issue
----------------------------------------------------------------------

Change-Id: I54514698a0c36a35a2f8b3aef1ff4515b4d3ee7e
Signed-off-by: Namyoon Woo <namyoon@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/1551959
Legacy-Commit-Queue: Commit Bot <commit-bot@chromium.org>
Reviewed-by: Mary Ruthven <mruthven@chromium.org>
diff --git a/server/site_tests/firmware_Cr50DeferredECReset/control b/server/site_tests/firmware_Cr50DeferredECReset/control
index 7d8dde8..7433722 100644
--- a/server/site_tests/firmware_Cr50DeferredECReset/control
+++ b/server/site_tests/firmware_Cr50DeferredECReset/control
@@ -7,7 +7,7 @@
 AUTHOR = "Chrome OS Team"
 NAME = "firmware_Cr50DeferredECReset"
 PURPOSE = "Verify Deferred EC Reset."
-ATTRIBUTES = ""
+ATTRIBUTES = "suite:faft_cr50_pvt, suite:faft_cr50_prepvt, suite:faft_cr50_tot"
 TIME = "SHORT"
 TEST_TYPE = "server"
 DEPENDENCIES = "servo"
diff --git a/server/site_tests/firmware_Cr50DeferredECReset/firmware_Cr50DeferredECReset.py b/server/site_tests/firmware_Cr50DeferredECReset/firmware_Cr50DeferredECReset.py
index 244bccd..3f52c40 100644
--- a/server/site_tests/firmware_Cr50DeferredECReset/firmware_Cr50DeferredECReset.py
+++ b/server/site_tests/firmware_Cr50DeferredECReset/firmware_Cr50DeferredECReset.py
@@ -17,14 +17,54 @@
 
     After this, EC_RST_L should be deasserted as soon as the power button
     gets released.
+
+    Attributes
+        version: test version number
+        CUTOFF_DELAY: duration in second to keep cr50 powered off,
+        PD_SETTLE_TIME: duration in second to wait PD to be stable
+        HAS_CR50_RESET_ODL: boolean if 'cr50_reset_odl' control is available
     """
     version = 1
+    CUTOFF_DELAY = 10
+    PD_SETTLE_TIME = 3
+    HAS_CR50_RESET_ODL = False
+
+    def cr50_power_on_reset(self):
+        """Perform a power-on-reset on cr50.
+           If cr50_reset_odl control is available, then use it.
+           Otherwise, disconnect and reconnect battery and power source.
+        """
+        if self.HAS_CR50_RESET_ODL:
+            self.servo.set('cr50_reset_odl', 'on')
+
+            time.sleep(self.CUTOFF_DELAY)
+
+            self.servo.set('cr50_reset_odl', 'off')
+        else:
+            # Stop power delivery to dut
+            logging.info('Stop charging')
+            self.servo.set('servo_v4_role', 'snk')
+
+            # Battery Cutoff
+            logging.info('Cut battery off')
+            self.ec.send_command('cutoff')
+
+            time.sleep(self.CUTOFF_DELAY)
+
+            # Enable power delivery to dut
+            logging.info('Start charging')
+            self.servo.set('servo_v4_role', 'src')
+
+        time.sleep(self.PD_SETTLE_TIME)
 
     def initialize(self, host, cmdline_args, full_args):
-        """Initialize the test and check if
-           cr50 is exists,
-           DTS is controllable, and
-           power delivery mode and power button is adjustable.
+        """Initialize the test and check if cr50 exists, DTS is controllable,
+           and power delivery mode and power button is adjustable.
+
+        Raises:
+            TestFail: If test initialization setup fails.
+            TestNAError: If the dut is not proper for this test for its RDD
+                         recognition problem.
         """
         super(firmware_Cr50DeferredECReset, self).initialize(host, cmdline_args,
                 full_args)
@@ -33,48 +73,88 @@
                                     'access to the Cr50 console')
         if not self.cr50.servo_v4_supports_dts_mode():
             raise error.TestNAError('Need working servo v4 DTS control')
+        self.dts_restore = self.servo.get('servo_v4_dts_mode')
 
+        # Fast open cr50 and check if testlab is enabled.
         self.fast_open(enable_testlab=True)
         if not self.cr50.testlab_is_on():
-            raise error.TestNAError('Cr50 testlab mode needs to be enabled')
-
-        # Test the external power delivery
-        self.servo.set('servo_v4_role', 'snk')
-        time.sleep(3)
-
-        rv = self.ec.send_command_get_output('chgstate',['.*>'])[0].strip()
-        logging.info(rv)
-        if not 'ac = 0' in rv:
-            raise error.TestFail('Failed in setting servo_v4_role sink')
-
-        # Test stopping the external power delivery
-        self.servo.set('servo_v4_role', 'src')
-        time.sleep(3)
-
-        rv = self.ec.send_command_get_output('chgstate',['.*>'])[0].strip()
-        logging.info(rv)
-        if not 'ac = 1' in rv:
-            raise error.TestFail('Failed in setting  servo_v4_role source')
+            raise error.TestNAError('Cr50 testlab mode is not enabled')
 
         # Test if the power button is adjustable.
         self.servo.set('pwr_button', 'press')
         self.servo.set('pwr_button', 'release')
 
+        # Check if 'cr50_reset_odl' controlis available.
+        try:
+            self.servo.get('cr50_reset_odl')
+            self.HAS_CR50_RESET_ODL = True
+        except error.TestFail:
+            self.HAS_CR50_RESET_ODL = False
+
+            # Test the external power delivery
+            self.servo.set('servo_v4_role', 'snk')
+            time.sleep(self.PD_SETTLE_TIME)
+
+            rv = self.ec.send_command_get_output('chgstate',['.*>'])[0].strip()
+            logging.info(rv)
+            if 'ac = 0' not in rv:
+                raise error.TestFail('Failed to set servo_v4_role sink')
+
+            # Test stopping the external power delivery
+            self.servo.set('servo_v4_role', 'src')
+            time.sleep(self.PD_SETTLE_TIME)
+
+            rv = self.ec.send_command_get_output('chgstate',['.*>'])[0].strip()
+            logging.info(rv)
+            if 'ac = 1' not in rv:
+                raise error.TestFail('Failed to set servo_v4_role source')
+
+        # Check if the dut has any RDD recognition issue.
+        # First, cut off power source and hold the power button.
+        #        disable RDD connection.
+        self.servo.set('servo_v4_dts_mode', 'off')
+        self.servo.set('pwr_button', 'press')
+        self.cr50_power_on_reset()
+        try:
+            #  Third, check if the rdd status is disconnected.
+            #         If not, terminate the test.
+            ccdstate = self.cr50.get_ccdstate()
+
+            if ccdstate['Rdd'].lower() != 'disconnected':
+                raise error.TestNAError('This board has a RDD recognition'
+                                        ' issue')
+        finally:
+            self.servo.set_nocheck('servo_v4_dts_mode', self.dts_restore)
+            self.servo.set_nocheck('pwr_button', 'release')
+            time.sleep(self.PD_SETTLE_TIME)
+
+            self.servo.power_short_press()            # Wake up AP
+
+        logging.info('Initialization is done')
+
     def check_ecrst_asserted(self, expect_assert):
         """Ask CR50 whether EC_RST_L is asserted or deasserted.
 
         Args:
             expect_assert: True if it is expected asserted.
                            False otherwise.
+
+        Raises:
+            TestFail: if ecrst value is not as expected.
         """
 
         # If the console is responsive, then the EC is awake.
-        rv = self.cr50.send_command_get_output('ecrst',
-                ['EC_RST_L is \w{0,2}asserted.*>'])[0].strip()
-        logging.info(rv)
         expecting_txt = ' asserted' if expect_assert else ' deasserted'
+        logging.info('Checking if ecrst is %s', expecting_txt)
 
-        if not expecting_txt in rv:
+        try:
+            rv = self.cr50.send_command_get_output('ecrst',
+                    ['(?i)EC_RST_L is \w{0,2}asserted.*>'])[0].strip()
+            logging.info(rv)
+        except error.TestError as e:
+            raise error.TestFail(str(e))
+
+        if expecting_txt.lower() not in rv.lower():
             raise error.TestFail(rv)
 
     def ping_ec(self, expect_response):
@@ -83,18 +163,22 @@
         Args:
             expect_response: True if EC should respond
                              False otherwise.
+        Raises:
+            TestFail: if ec responsiveness is not as same as expected.
         """
         try:
-            rv = self.ec.send_command_get_output('time',
-                    ['time.*>'])[0].strip()
+            logging.info('Checking if ec is %sresponsive',
+                         '' if expect_response else 'not ')
+            rv = self.ec.send_command_get_output('help', ['.*>'])[0].strip()
         except error.TestFail as e:
-            logging.info(e)
+            logging.info(str(e))
             if 'Timeout waiting for response' in str(e):
                 if not expect_response:
                     return
             raise e
         else:
             if not expect_response:
+                logging.error('EC should not respond')
                 raise error.TestFail(rv)
 
     def test_deferred_ec_reset(self, power_button_hold, rdd_enable,
@@ -110,54 +194,53 @@
                                 reset.
                                 False otherwise.
         """
-        logging.info('Test deferred_ec_reset starts.')
+        logging.info('Test deferred_ec_reset starts')
         logging.info('Power button held: %s', power_button_hold)
         logging.info('RDD connection   : %s', rdd_enable)
 
-        # Stop power delivery to DUT
-        self.servo.set('servo_v4_role', 'snk')
-        time.sleep(3)
+        try:
+            # enable RDD Connection (or disable) before power-on-reset
+            self.servo.set('servo_v4_dts_mode', 'on' if rdd_enable else 'off')
 
-        # Battery Cutoff
-        self.ec.send_command('cutoff')
-        time.sleep(3)
+            # Set power button before the dut loses power,
+            # because the power button value cannot be changed during power-off.
+            self.servo.set('pwr_button',
+                           'press' if power_button_hold else 'release')
 
-        # EC should not respond
-        self.ping_ec(False)
+            # Do a power-on-reset on CR50.
+            self.cr50_power_on_reset()
 
-        # press (or release) the power button
-        power_button_setval = 'press' if power_button_hold else 'release'
-        # call set_nocheck, since power button shall be recognized as pressed
-        # at this point.
-        self.servo.set_nocheck('pwr_button', power_button_setval)
+            # Wait for a while
+            wait_sec = 30
+            logging.info('waiting for %d seconds', wait_sec)
+            time.sleep(wait_sec)
 
-        # enable RDD Connection (or disable)
-        self.servo.set_nocheck('servo_v4_dts_mode',
-                'on' if rdd_enable else 'off')
-        time.sleep(self.cr50.SHORT_WAIT)
+            # Check if EC_RST_L is asserted and EC is on.
+            # (or EC_RST_L deasserted and EC off)
+            self.check_ecrst_asserted(not expect_ec_response)
+            self.ping_ec(expect_ec_response)
 
-        # Enable power delivery to DUT
-        self.servo.set('servo_v4_role', 'src')
+            # Release power button
+            logging.info('Power button released')
+            self.servo.set('pwr_button', 'release')
+            time.sleep(self.PD_SETTLE_TIME)
 
-        # Wait for a while
-        wait_sec = 30
-        logging.info('waiting for %d seconds', wait_sec)
-        time.sleep(wait_sec)
+            # Check if EC_RST_L is deasserted and EC is on.
+            self.check_ecrst_asserted(False)
+            self.ping_ec(True)
 
-        # Check if EC_RST_L is asserted (or deasserted) and EC is on (or off).
-        self.check_ecrst_asserted(not expect_ec_response)
-        self.ping_ec(expect_ec_response)
+        finally:
+            if self.HAS_CR50_RESET_ODL:
+                self.servo.set_nocheck('cr50_reset_odl', 'off')
+            else:
+                self.servo.set_nocheck('servo_v4_role', 'src')
 
-        # Release power button
-        self.servo.set('pwr_button', 'release')
+            self.servo.set_nocheck('servo_v4_dts_mode', self.dts_restore)
+            time.sleep(1)
 
-        # Check if EC_RST_L is deasserted and EC is on.
-        self.check_ecrst_asserted(False)
-        self.ping_ec(True)
-
-        # Recover CCD
-        if self.servo.get('servo_v4_dts_mode') == 'off':
-            self.cr50.ccd_enable()
+            # Press power button to wake up AP, and releases it soon
+            # in any cases.
+            self.servo.power_short_press()
 
     def run_once(self):
         """Test deferred EC reset feature. """
@@ -165,19 +248,21 @@
         # Release power button and disable RDD on power-on reset.
         # EC should be running.
         self.test_deferred_ec_reset(power_button_hold=False, rdd_enable=False,
-            expect_ec_response=True)
+                                    expect_ec_response=True)
 
         # Release power button but enable RDD on power-on reset.
         # EC should be running.
         self.test_deferred_ec_reset(power_button_hold=False, rdd_enable=True,
-            expect_ec_response=True)
+                                    expect_ec_response=True)
 
         # Hold power button but disable RDD on power-on reset.
         # EC should be running.
         self.test_deferred_ec_reset(power_button_hold=True, rdd_enable=False,
-            expect_ec_response=True)
+                                    expect_ec_response=True)
 
         # Hold power button and enable RDD on power-on reset.
         # EC should not be running.
         self.test_deferred_ec_reset(power_button_hold=True, rdd_enable=True,
-            expect_ec_response=False)
+                                    expect_ec_response=False)
+
+        logging.info('Test is done')