faft_cr50: add test verifying EC_RST_L

A couple of boards have had issues with cr50 being able to wake the EC
from hibernate. This change adds a test to verify EC_RST_L behavior, so
we can catch this sort of thing sooner.

BUG=none
BRANCH=none
TEST=run on nami

Change-Id: Ic35ad9085e4d45f7922795c3c623cbf05999268b
Signed-off-by: Mary Ruthven <mruthven@google.com>
Reviewed-on: https://chromium-review.googlesource.com/1242329
Tested-by: Mary Ruthven <mruthven@chromium.org>
Reviewed-by: Wai-Hong Tam <waihong@google.com>
Commit-Queue: Mary Ruthven <mruthven@chromium.org>
diff --git a/server/site_tests/firmware_Cr50ECReset/control b/server/site_tests/firmware_Cr50ECReset/control
new file mode 100644
index 0000000..eac2c02
--- /dev/null
+++ b/server/site_tests/firmware_Cr50ECReset/control
@@ -0,0 +1,35 @@
+# Copyright 2018 The Chromium OS Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+from autotest_lib.server import utils
+
+AUTHOR = "mruthven"
+NAME = "firmware_Cr50ECReset"
+PURPOSE = "Verify Cr50 EC reset."
+ATTRIBUTES = "suite:faft_cr50_prepvt, suite:faft_cr50_pvt"
+TIME = "SHORT"
+TEST_TYPE = "server"
+DEPENDENCIES = "servo"
+
+DOC = """Make sure 'cr50 ecrst' works as intended
+
+EC_RST_L needs to be able to wake the EC from hibernate and hold the EC in
+reset. This test verifies the hardware works as intended
+"""
+
+if 'args_dict' not in locals():
+    args_dict = {}
+
+args_dict.update(utils.args_to_dict(args))
+servo_args = hosts.CrosHost.get_servo_arguments(args_dict)
+
+def run(machine):
+    host = hosts.create_host(machine, servo_args=servo_args)
+
+    iterations = int(args_dict.get("iterations", 1))
+
+    job.run_test("firmware_Cr50ECReset", host=host, cmdline_args=args,
+                 full_args=args_dict, iterations=iterations)
+
+parallel_simple(run, machines)
diff --git a/server/site_tests/firmware_Cr50ECReset/firmware_Cr50ECReset.py b/server/site_tests/firmware_Cr50ECReset/firmware_Cr50ECReset.py
new file mode 100644
index 0000000..45123f5
--- /dev/null
+++ b/server/site_tests/firmware_Cr50ECReset/firmware_Cr50ECReset.py
@@ -0,0 +1,139 @@
+# Copyright 2018 The Chromium OS Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import logging
+import time
+
+from autotest_lib.client.common_lib import error
+from autotest_lib.server.cros.faft.cr50_test import Cr50Test
+
+
+class firmware_Cr50ECReset(Cr50Test):
+    """Make sure 'cr50 ecrst' works as intended
+
+    EC_RST_L needs to be able to wake the EC from hibernate and hold the EC in
+    reset. This test verifies the hardware works as intended
+    """
+    version = 1
+
+    # Delays used by the test. Time is given in seconds
+    # Used to wait long enough for the EC to enter/resume from hibernate
+    EC_SETTLE_TIME = 10
+    RELEASE_RESET_DELAY = 3
+    SHORT_PULSE = 1
+
+    def cleanup(self):
+        """Make sure the EC is on"""
+        self.guarantee_ec_is_up()
+        super(firmware_Cr50ECReset, self).cleanup()
+
+
+    def ec_is_up(self):
+        """If the console is responsive, then the EC is awake"""
+        time.sleep(self.EC_SETTLE_TIME)
+        try:
+            self.ec.send_command_get_output('time', ['.*>'])
+        except error.TestFail, e:
+            logging.info(e)
+            if 'Timeout waiting for response' in str(e):
+                return False
+            raise
+        else:
+            return True
+
+
+    def cold_reset(self, state):
+        """Set cold reset"""
+        self.servo.set('cold_reset', state)
+
+
+    def power_button(self, state):
+        """Press or release the power button"""
+        self.servo.set('pwr_button', 'press' if state == 'on' else 'release')
+
+
+    def cr50_ecrst(self, state):
+        """Set ecrst on cr50"""
+        self.cr50.send_command('ecrst ' + state)
+
+
+    def wake_ec(self, wake_method):
+        """Pulse the wake method to wake the EC
+
+        Args:
+            wake_method: a function that takes in 'on' or 'off' to control the
+                         wake source.
+        """
+        wake_method('on')
+        time.sleep(self.SHORT_PULSE)
+        wake_method('off')
+
+
+    def ec_hibernate(self):
+        """Put the EC in hibernate"""
+        self.ec.send_command('hibernate')
+        if self.ec_is_up():
+            raise error.TestError('Could not put the EC into hibernate')
+
+
+    def guarantee_ec_is_up(self):
+        """Make sure ec isn't held in reset. Use the power button to wake it
+
+        The power button wakes the EC on all systems. Use that to wake the EC
+        and make sure all versions of ecrst are released.
+        """
+        self.cold_reset('off')
+        self.cr50_ecrst('off')
+        time.sleep(self.RELEASE_RESET_DELAY)
+        self.wake_ec(self.power_button)
+        if not self.ec_is_up():
+            raise error.TestError('Could not recover EC')
+
+
+    def can_wake_ec(self, wake_method):
+        """Put the EC in hibernate and verify it can wake up with wake_method
+
+        Args:
+            wake_method: a function that takes in 'on' or 'off' to control the
+                         wake source.
+        Returns:
+            True if wake_method can be used to wake the EC from hibernate
+        """
+        self.ec_hibernate()
+        self.wake_ec(self.cold_reset)
+        wake_successful = self.ec_is_up()
+        self.guarantee_ec_is_up()
+        return wake_successful
+
+
+    def check_basic_ecrst(self):
+        """Verify cr50 can hold the EC in reset"""
+        self.cr50_ecrst('on')
+        if self.ec_is_up():
+            raise error.TestFail('Could not use cr50 ecrst to hold the EC in '
+                                 'reset')
+        # Verify cr50 can release the EC from reset
+        self.cr50_ecrst('off')
+        if not self.ec_is_up():
+            raise error.TestFail('Could not release the EC from reset')
+        self.guarantee_ec_is_up()
+
+
+    def run_once(self):
+        """Make sure 'cr50 ecrst' works as intended."""
+        failed_wake = []
+        # Open cr50 so the test has access to ecrst
+        self.fast_open(True)
+
+        self.check_basic_ecrst()
+
+        if not self.can_wake_ec(self.cr50_ecrst):
+            failed_wake.append('cr50 ecrst')
+
+        if not self.can_wake_ec(self.cold_reset):
+            failed_wake.append('servo cold_reset')
+
+        if failed_wake:
+            raise error.TestFail('Failed to wake EC with %s' %
+                                 ' and '.join(failed_wake))