Wilco: Test Wilco-specific EC reset Cr50 behavior

Much of firmware_Cr50ECReset doesn't make sense without a Chrome EC. The
ecrst Cr50 command should still work, so test it. Wilco's EC requires a
longer reset pulse than the Chrome EC, so make sure 'ecrst pulse' works.

BUG=b:132070766
TEST=Ran the test against Arcada and Sarien

Change-Id: Ice849a595ba9c2393bc97f82758f5f81005bf02d
Reviewed-on: https://chromium-review.googlesource.com/1601392
Commit-Ready: ChromeOS CL Exonerator Bot <chromiumos-cl-exonerator@appspot.gserviceaccount.com>
Tested-by: Abe Levkoy <alevkoy@chromium.org>
Reviewed-by: Mary Ruthven <mruthven@chromium.org>
diff --git a/server/site_tests/firmware_Cr50WilcoEcrst/control b/server/site_tests/firmware_Cr50WilcoEcrst/control
new file mode 100644
index 0000000..7d2ddaa
--- /dev/null
+++ b/server/site_tests/firmware_Cr50WilcoEcrst/control
@@ -0,0 +1,36 @@
+# Copyright 2019 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 = "alevkoy"
+NAME = "firmware_Cr50WilcoEcrst"
+PURPOSE = "Verify Cr50 EC reset."
+ATTRIBUTES = "suite:faft_wilco, suite:faft_cr50_prepvt, suite:faft_cr50_pvt"
+TIME = "SHORT"
+TEST_TYPE = "server"
+DEPENDENCIES = "servo, board:sarien"
+JOB_RETRIES = 0
+
+DOC = """Make sure Cr50's ecrst command works as intended on Wilco.
+
+EC_RST_L needs to be able to 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_Cr50WilcoEcrst", host=host, cmdline_args=args,
+                 full_args=args_dict, iterations=iterations)
+
+parallel_simple(run, machines)
diff --git a/server/site_tests/firmware_Cr50WilcoEcrst/firmware_Cr50WilcoEcrst.py b/server/site_tests/firmware_Cr50WilcoEcrst/firmware_Cr50WilcoEcrst.py
new file mode 100644
index 0000000..0bff15f
--- /dev/null
+++ b/server/site_tests/firmware_Cr50WilcoEcrst/firmware_Cr50WilcoEcrst.py
@@ -0,0 +1,94 @@
+# Copyright 2019 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_Cr50WilcoEcrst(Cr50Test):
+    """Make sure Cr50's ecrst command works as intended.
+
+    EC_RST_L needs to be able to hold the EC in reset. This test verifies the
+    hardware works as intended.
+    """
+    version = 1
+
+    # How long to hold 'ecrst on', in seconds
+    SHORT_PULSE = 1
+
+
+    def initialize(self, host, cmdline_args, full_args):
+        super(firmware_Cr50WilcoEcrst, self).initialize(host, cmdline_args,
+                full_args)
+        if not self.faft_config.gsc_can_wake_ec_with_reset:
+            raise error.TestNAError("This DUT has a hardware limitation that "
+                                    "prevents cr50 from waking the EC with "
+                                    "EC_RST_L.")
+        # This test only makes sense with a Wilco EC.
+        if self.check_ec_capability():
+            raise error.TestNAError("Nothing needs to be tested on this device")
+
+        # Open Cr50, so the test has access to ecrst.
+        self.fast_open(True)
+
+
+    def cr50_ecrst(self, state):
+        """Set ecrst on Cr50."""
+        self.cr50.send_command('ecrst ' + state)
+
+
+    def make_ec_reset_bring_up_ap(self):
+        """Force the AP to come back up after the next EC reset.
+
+        This is not the default behavior on Wilco. The AFTERG3_EN bit in the
+        GEN_PMCON_A register is set by default, which causes the AP to remain
+        down after an EC reset. Clearing it will cause the AP to come after the
+        next EC reset, at which point Coreboot will set it again, making the
+        change in behavior temporary.
+        """
+        GEN_PMCON_A_ADDRESS = 0xfe001020
+        AFTERG3_EN_BIT = 0x1
+        orig_reg_string = self.faft_client.system.run_shell_command_get_output(
+                'iotools mmio_read32 %#x' % (GEN_PMCON_A_ADDRESS))
+        logging.info('iotools output: %s', orig_reg_string)
+        orig_reg_value = int(orig_reg_string[0], 0)
+        if orig_reg_value > 0xffffffff:
+            raise error.TestError(
+                    'iotools mmio_read32 returned a value larger than 32 bits')
+        new_reg_value = orig_reg_value & ~AFTERG3_EN_BIT
+        self.faft_client.system.run_shell_command('iotools mmio_write32 %#x %#x'
+                % (GEN_PMCON_A_ADDRESS, new_reg_value))
+
+
+    def check_ecrst_on_off(self):
+        """Verify Cr50 can hold the EC in reset."""
+        self.make_ec_reset_bring_up_ap()
+
+        self.cr50_ecrst('on')
+        # There isn't a good way to directly tell if the EC is in reset, so
+        # verify that the AP has gone down.
+        self.switcher.wait_for_client_offline()
+        time.sleep(self.SHORT_PULSE)
+
+        self.cr50_ecrst('off')
+        self.switcher.wait_for_client()
+
+
+    def check_ecrst_pulse(self):
+        """Verify Cr50 can reset the EC with a pulse."""
+        self.make_ec_reset_bring_up_ap()
+
+        orig_boot_id = self.get_bootid()
+        self.cr50_ecrst('pulse')
+        self.switcher.wait_for_client_offline(orig_boot_id=orig_boot_id)
+        self.switcher.wait_for_client()
+
+
+    def run_once(self):
+        """Run the test."""
+        self.check_ecrst_on_off()
+        self.check_ecrst_pulse()