fw_wp_state: Introduce abstraction of firmware write-protection

Modifying the firmware protection is different on CCD-capable devices.
It is done by the 'wp' command on CCD console instead of the servo
pins:
  fw_wp_vref:some_voltage
  fw_wp_en:on
  fw_wp:on/off

This CL unifies these 2 approaches by introducing a new servo
control, fw_wp_state (similar to power_state). Different boards have
different implementations (via servo overlay and driver).

BUG=chrome-os-partner:58041
TEST=Tested on servo v3 with micro-servo and CCD.
servo> dut-control fw_wp_state:force_on fw_wp_state
fw_wp_state:force_on
dut> crossystem wpsw_cur # Firmware WP hardware switch current position
1
servo> dut-control fw_wp_state:force_off fw_wp_state
fw_wp_state:force_off
dut> crossystem wpsw_cur
0
servo> dut-control fw_wp_state:reset fw_wp_state # micro-servo only
fw_wp_state:off
dut> crossystem wpsw_cur
0

Change-Id: If8dd7c0b07ccd820b4d956cef6cc48ba2685b320
Reviewed-on: https://chromium-review.googlesource.com/397839
Commit-Ready: Wai-Hong Tam <waihong@google.com>
Tested-by: Wai-Hong Tam <waihong@google.com>
Reviewed-by: Wai-Hong Tam <waihong@google.com>
diff --git a/servo/data/ccd_cr50.xml b/servo/data/ccd_cr50.xml
index 84a3726..2130746 100644
--- a/servo/data/ccd_cr50.xml
+++ b/servo/data/ccd_cr50.xml
@@ -75,4 +75,9 @@
     <params subtype="ccd_ec_uart_en" interface="8" drv="cr50" map="onoff"
     clobber_ok="" init="on"></params>
   </control>
+  <control>
+    <name>fw_wp_state</name>
+    <doc>Used to turn fw wp off and on</doc>
+    <params interface="8" drv="fw_wp_ccd" input_type="str"/>
+  </control>
 </root>
diff --git a/servo/data/servo_auron_overlay.xml b/servo/data/servo_auron_overlay.xml
index 901cfee..d8d261c 100644
--- a/servo/data/servo_auron_overlay.xml
+++ b/servo/data/servo_auron_overlay.xml
@@ -10,4 +10,9 @@
     <doc>Used to turn the DUT off and on</doc>
     <params clobber_ok="" reset_recovery="5.0"/>
   </control>
+  <control>
+    <name>fw_wp_state</name>
+    <doc>Used to turn fw wp off and on</doc>
+    <params clobber_ok="" fw_wp_vref="pp3300"/>
+  </control>
 </root>
diff --git a/servo/data/servo_beltino_overlay.xml b/servo/data/servo_beltino_overlay.xml
index 3815445..76a4443 100644
--- a/servo/data/servo_beltino_overlay.xml
+++ b/servo/data/servo_beltino_overlay.xml
@@ -11,4 +11,9 @@
     <params clobber_ok="" cmd="set" interface="servo" drv="beltino_power"
             input_type="str" reset_hold="0.1" reset_recovery="0.0"/>
   </control>
+  <control>
+    <name>fw_wp_state</name>
+    <doc>Used to turn fw wp off and on</doc>
+    <params clobber_ok="" fw_wp_vref="pp3300"/>
+  </control>
 </root>
diff --git a/servo/data/servo_cyan_overlay.xml b/servo/data/servo_cyan_overlay.xml
index e3e0026..dd50e13 100644
--- a/servo/data/servo_cyan_overlay.xml
+++ b/servo/data/servo_cyan_overlay.xml
@@ -12,6 +12,11 @@
     shutdown_delay="11.0" />
   </control>
   <control>
+    <name>fw_wp_state</name>
+    <doc>Used to turn fw wp off and on</doc>
+    <params clobber_ok="" fw_wp_vref="pp1800"/>
+  </control>
+  <control>
     <name>ec_chip</name>
     <doc>EC chip name (read-only)</doc>
     <params clobber_ok="" cmd="get" subtype="chip" interface="servo"
diff --git a/servo/data/servo_glados_overlay.xml b/servo/data/servo_glados_overlay.xml
index 616e590..4013789 100644
--- a/servo/data/servo_glados_overlay.xml
+++ b/servo/data/servo_glados_overlay.xml
@@ -14,6 +14,11 @@
     shutdown_delay="11.0" boot_to_rec_screen_delay="5.0" />
   </control>
   <control>
+    <name>fw_wp_state</name>
+    <doc>Used to turn fw wp off and on</doc>
+    <params clobber_ok="" fw_wp_vref="pp3300"/>
+  </control>
+  <control>
     <name>dev_mode</name>
     <alias>usbpd_boot_mode</alias>
     <params clobber_ok="" od="PU" map="onoff_i"></params>
diff --git a/servo/data/servo_gnawty_overlay.xml b/servo/data/servo_gnawty_overlay.xml
index 9073c39..7973463 100644
--- a/servo/data/servo_gnawty_overlay.xml
+++ b/servo/data/servo_gnawty_overlay.xml
@@ -11,4 +11,9 @@
     <params drv="cros_ec_softrec_power" clobber_ok="" reset_recovery="0.6"
     shutdown_delay="11.0" />
   </control>
+  <control>
+    <name>fw_wp_state</name>
+    <doc>Used to turn fw wp off and on</doc>
+    <params clobber_ok="" fw_wp_vref="pp3300"/>
+  </control>
 </root>
diff --git a/servo/data/servo_jecht_overlay.xml b/servo/data/servo_jecht_overlay.xml
index 3815445..76a4443 100644
--- a/servo/data/servo_jecht_overlay.xml
+++ b/servo/data/servo_jecht_overlay.xml
@@ -11,4 +11,9 @@
     <params clobber_ok="" cmd="set" interface="servo" drv="beltino_power"
             input_type="str" reset_hold="0.1" reset_recovery="0.0"/>
   </control>
+  <control>
+    <name>fw_wp_state</name>
+    <doc>Used to turn fw wp off and on</doc>
+    <params clobber_ok="" fw_wp_vref="pp3300"/>
+  </control>
 </root>
diff --git a/servo/data/servo_kunimitsu_overlay.xml b/servo/data/servo_kunimitsu_overlay.xml
index ae8faa9..5d1283e 100644
--- a/servo/data/servo_kunimitsu_overlay.xml
+++ b/servo/data/servo_kunimitsu_overlay.xml
@@ -7,4 +7,9 @@
     <alias>usbpd_boot_mode</alias>
     <params clobber_ok="" od="PP" map="onoff"></params>
   </control>
+  <control>
+    <name>fw_wp_state</name>
+    <doc>Used to turn fw wp off and on</doc>
+    <params clobber_ok="" fw_wp_vref="pp3300"/>
+  </control>
 </root>
diff --git a/servo/data/servo_link_overlay.xml b/servo/data/servo_link_overlay.xml
index f3ee378..5a6fbc8 100644
--- a/servo/data/servo_link_overlay.xml
+++ b/servo/data/servo_link_overlay.xml
@@ -16,4 +16,9 @@
     <params  clobber_ok="" cmd="set" drv="link_power"
       shutdown_ec_command="x86shutdown"/>
   </control>
+  <control>
+    <name>fw_wp_state</name>
+    <doc>Used to turn fw wp off and on</doc>
+    <params clobber_ok="" fw_wp_vref="pp3300"/>
+  </control>
 </root>
diff --git a/servo/data/servo_micro.xml b/servo/data/servo_micro.xml
index a44f9ee..a134204 100644
--- a/servo/data/servo_micro.xml
+++ b/servo/data/servo_micro.xml
@@ -239,8 +239,12 @@
     <params interface="4" drv="tca6416" slv="0x20" od="PU" port="1"
     offset="7" map="onoff" init="off"></params>
   </control>
-
-
+  <control>
+    <name>fw_wp_state</name>
+    <doc>Used to turn fw wp off and on</doc>
+    <params interface="servo" drv="fw_wp_servoflex" input_type="str"
+    fw_wp_vref="pp1800"/>
+  </control>
 
   <!-- UART and CONSOLE settings -->
   <control>
diff --git a/servo/data/servo_ninja_overlay.xml b/servo/data/servo_ninja_overlay.xml
index 36390b7..c8a2ad8 100644
--- a/servo/data/servo_ninja_overlay.xml
+++ b/servo/data/servo_ninja_overlay.xml
@@ -14,4 +14,9 @@
     <params drv="cros_ec_softrec_power" clobber_ok="" reset_recovery="0.6"
     shutdown_delay="11.0" />
   </control>
+  <control>
+    <name>fw_wp_state</name>
+    <doc>Used to turn fw wp off and on</doc>
+    <params clobber_ok="" fw_wp_vref="pp3300"/>
+  </control>
 </root>
diff --git a/servo/data/servo_nyan_overlay.xml b/servo/data/servo_nyan_overlay.xml
index bbc1ecc..4ab929e 100644
--- a/servo/data/servo_nyan_overlay.xml
+++ b/servo/data/servo_nyan_overlay.xml
@@ -11,4 +11,9 @@
     <params clobber_ok="" shutdown_delay="3.0" reset_recovery="5.0"
             drv="cros_ec_softrec_power" />
   </control>
+  <control>
+    <name>fw_wp_state</name>
+    <doc>Used to turn fw wp off and on</doc>
+    <params clobber_ok="" fw_wp_vref="off"/>
+  </control>
 </root>
diff --git a/servo/data/servo_quawks_overlay.xml b/servo/data/servo_quawks_overlay.xml
index 134e5ad..2a707d3 100644
--- a/servo/data/servo_quawks_overlay.xml
+++ b/servo/data/servo_quawks_overlay.xml
@@ -2,4 +2,9 @@
   <include>
     <name>servo_rambi_overlay.xml</name>
   </include>
+  <control>
+    <name>fw_wp_state</name>
+    <doc>Used to turn fw wp off and on</doc>
+    <params clobber_ok="" fw_wp_vref="off"/>
+  </control>
 </root>
diff --git a/servo/data/servo_rambi_overlay.xml b/servo/data/servo_rambi_overlay.xml
index 2c95cd0..5cce66c 100644
--- a/servo/data/servo_rambi_overlay.xml
+++ b/servo/data/servo_rambi_overlay.xml
@@ -11,4 +11,9 @@
     <params drv="cros_ec_softrec_power" clobber_ok=""
     shutdown_delay="11.0" />
   </control>
+  <control>
+    <name>fw_wp_state</name>
+    <doc>Used to turn fw wp off and on</doc>
+    <params clobber_ok="" fw_wp_vref="pp1800"/>
+  </control>
 </root>
diff --git a/servo/data/servo_reef_overlay.xml b/servo/data/servo_reef_overlay.xml
index 68f77c8..bc35a22 100644
--- a/servo/data/servo_reef_overlay.xml
+++ b/servo/data/servo_reef_overlay.xml
@@ -14,6 +14,11 @@
     shutdown_delay="11.0" />
   </control>
   <control>
+    <name>fw_wp_state</name>
+    <doc>Used to turn fw wp off and on</doc>
+    <params clobber_ok="" fw_wp_vref="pp1800"/>
+  </control>
+  <control>
     <name>ec_chip</name>
     <doc>EC chip name (read-only)</doc>
     <params clobber_ok="" cmd="get" subtype="chip" interface="servo"
diff --git a/servo/data/servo_samus_overlay.xml b/servo/data/servo_samus_overlay.xml
index 999f380..7c3862e 100644
--- a/servo/data/servo_samus_overlay.xml
+++ b/servo/data/servo_samus_overlay.xml
@@ -15,6 +15,11 @@
     shutdown_delay="11.0" boot_to_rec_screen_delay="10.0" />
   </control>
   <control>
+    <name>fw_wp_state</name>
+    <doc>Used to turn fw wp off and on</doc>
+    <params clobber_ok="" fw_wp_vref="pp3300"/>
+  </control>
+  <control>
     <name>usbpd_chip</name>
     <doc>USB PD chip name (read-only)</doc>
     <params clobber_ok="" cmd="get" subtype="chip" interface="servo"
diff --git a/servo/data/servo_slippy_overlay.xml b/servo/data/servo_slippy_overlay.xml
index 901cfee..d8d261c 100644
--- a/servo/data/servo_slippy_overlay.xml
+++ b/servo/data/servo_slippy_overlay.xml
@@ -10,4 +10,9 @@
     <doc>Used to turn the DUT off and on</doc>
     <params clobber_ok="" reset_recovery="5.0"/>
   </control>
+  <control>
+    <name>fw_wp_state</name>
+    <doc>Used to turn fw wp off and on</doc>
+    <params clobber_ok="" fw_wp_vref="pp3300"/>
+  </control>
 </root>
diff --git a/servo/data/servo_strago_overlay.xml b/servo/data/servo_strago_overlay.xml
index 1119745..1622849 100644
--- a/servo/data/servo_strago_overlay.xml
+++ b/servo/data/servo_strago_overlay.xml
@@ -12,6 +12,11 @@
     shutdown_delay="11.0" />
   </control>
   <control>
+    <name>fw_wp_state</name>
+    <doc>Used to turn fw wp off and on</doc>
+    <params clobber_ok="" fw_wp_vref="pp1800"/>
+  </control>
+  <control>
     <name>ec_chip</name>
     <doc>EC chip name (read-only)</doc>
     <params clobber_ok="" cmd="get" subtype="chip" interface="servo"
diff --git a/servo/data/servo_v2_r0.xml b/servo/data/servo_v2_r0.xml
index 700cb19..c0cddf1 100644
--- a/servo/data/servo_v2_r0.xml
+++ b/servo/data/servo_v2_r0.xml
@@ -728,4 +728,10 @@
     <params interface="10" drv="ec3po_driver" map="onoff" init="on"
     subtype="interp_connect"></params>
   </control>
+  <control>
+    <name>fw_wp_state</name>
+    <doc>Used to turn fw wp off and on</doc>
+    <params interface="servo" drv="fw_wp_servoflex" input_type="str"
+    fw_wp_vref="pp1800"/>
+  </control>
 </root>
diff --git a/servo/data/servo_v3_r0.xml b/servo/data/servo_v3_r0.xml
index 65e8c1b..0ebe4a0 100644
--- a/servo/data/servo_v3_r0.xml
+++ b/servo/data/servo_v3_r0.xml
@@ -671,4 +671,10 @@
     <params interface="10" drv="ec3po_driver" map="onoff" init="on"
     subtype="interp_connect"></params>
   </control>
+  <control>
+    <name>fw_wp_state</name>
+    <doc>Used to turn fw wp off and on</doc>
+    <params interface="servo" drv="fw_wp_servoflex" input_type="str"
+    fw_wp_vref="pp1800"/>
+  </control>
 </root>
diff --git a/servo/data/servo_veyron_overlay.xml b/servo/data/servo_veyron_overlay.xml
index c68d0ff..db26b6b 100644
--- a/servo/data/servo_veyron_overlay.xml
+++ b/servo/data/servo_veyron_overlay.xml
@@ -12,4 +12,9 @@
     <params clobber_ok="" cmd="set" shutdown_ec_command="power off"
             drv="veyron_power" reset_recovery="0.5"/>
   </control>
+  <control>
+    <name>fw_wp_state</name>
+    <doc>Used to turn fw wp off and on</doc>
+    <params clobber_ok="" fw_wp_vref="pp3300"/>
+  </control>
 </root>
diff --git a/servo/drv/__init__.py b/servo/drv/__init__.py
index b87b26a..96273fd 100644
--- a/servo/drv/__init__.py
+++ b/servo/drv/__init__.py
@@ -23,6 +23,9 @@
 import ec3po_gpio
 import ec3po_driver
 import ec3po_servo_micro
+import fw_wp_ccd
+import fw_wp_servoflex
+import fw_wp_state
 import gpio
 import hw_driver
 import i2c_reg
diff --git a/servo/drv/fw_wp_ccd.py b/servo/drv/fw_wp_ccd.py
new file mode 100644
index 0000000..ca7e7d4
--- /dev/null
+++ b/servo/drv/fw_wp_ccd.py
@@ -0,0 +1,49 @@
+# Copyright 2016 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 fw_wp_state
+import pty_driver
+
+
+class fwWpCcdError(Exception):
+  """Exception class for fwWpCcd."""
+
+
+class fwWpCcd(fw_wp_state.FwWpStateDriver, pty_driver.ptyDriver):
+  """Driver for fw_wp_state for boards with CCD."""
+
+  def __init__(self, interface, params):
+    """Constructor.
+
+    Args:
+      interface: driver interface object
+      params: dictionary of params
+    """
+    super(fwWpCcd, self).__init__(interface, params)
+
+  def _force_on(self):
+    """Force the firmware to write-protected."""
+    self._issue_cmd("wp on")
+
+  def _force_off(self):
+    """Force the firmware to not write-protected."""
+    self._issue_cmd("wp off")
+
+  def _reset(self):
+    """Reset the firmware write-protection state to the system value."""
+    # The WP is totally controlled by CCD chip. Do nothing to reset.
+    pass
+
+  def _get_state(self):
+    """Get the firmware write-protection state."""
+    # The output string is defined in ec/board/cr50/wp.c
+    result = self._issue_cmd_get_results(
+        "wp", ["Flash WP is (enabled|disabled)"])[0]
+    if result is None:
+      raise fwWpCcdError("Cannot retrieve wp result on CCD console.")
+
+    if result[1] == "enabled":
+      return self._STATE_FORCE_ON
+    else:
+      return self._STATE_FORCE_OFF
diff --git a/servo/drv/fw_wp_servoflex.py b/servo/drv/fw_wp_servoflex.py
new file mode 100644
index 0000000..91d2568
--- /dev/null
+++ b/servo/drv/fw_wp_servoflex.py
@@ -0,0 +1,44 @@
+# Copyright 2016 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 fw_wp_state
+
+
+class fwWpServoflex(fw_wp_state.FwWpStateDriver):
+  """Driver for fw_wp_state for boards connecting servoflex's."""
+
+  def __init__(self, interface, params):
+    """Constructor.
+
+    Args:
+      interface: driver interface object
+      params: dictionary of params
+    """
+    super(fwWpServoflex, self).__init__(interface, params)
+    self._fw_wp_vref = self._params.get('fw_wp_vref', 'pp1800')
+
+  def _force_on(self):
+    """Force the firmware to write-protected."""
+    self._interface.set('fw_wp_vref', self._fw_wp_vref)
+    self._interface.set('fw_wp_en', 'on')
+    self._interface.set('fw_wp', 'on')
+
+  def _force_off(self):
+    """Force the firmware to not write-protected."""
+    self._interface.set('fw_wp_vref', self._fw_wp_vref)
+    self._interface.set('fw_wp_en', 'on')
+    self._interface.set('fw_wp', 'off')
+
+  def _reset(self):
+    """Reset the firmware write-protection state to the system value."""
+    self._interface.set('fw_wp_en', 'off')
+
+  def _get_state(self):
+    """Get the firmware write-protection state."""
+    fw_wp_en = (self._interface.get('fw_wp_en') == 'on')
+    fw_wp = (self._interface.get('fw_wp') == 'on')
+    if fw_wp_en:
+      return self._STATE_FORCE_ON if fw_wp else self._STATE_FORCE_OFF
+    else:
+      return self._STATE_ON if fw_wp else self._STATE_OFF
diff --git a/servo/drv/fw_wp_state.py b/servo/drv/fw_wp_state.py
new file mode 100644
index 0000000..172a3aa
--- /dev/null
+++ b/servo/drv/fw_wp_state.py
@@ -0,0 +1,70 @@
+# Copyright 2016 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 hw_driver
+
+
+class FwWpStateDriver(hw_driver.HwDriver):
+  """Abstract superclass to provide board-specific WP operations.
+
+  This driver handles a single control with these settings:
+    * 'force_on' - Force to turn on firmware write-protection.
+    * 'force_off' - Force to turn off firmware write-protection.
+    * 'reset' - For setter, reset the value to the system one.
+    * 'on' - For getter, the system value is write-protected.
+    * 'off' - For getter, the system value is not write-protected.
+
+  Actual implementation of the required behaviors is delegated to
+  the methods `_force_on()`, `_force_off()`, `_reset()`, and
+  `_get_state()`, which must be implemented in a subclass.
+
+  """
+
+  _STATE_FORCE_ON = 'force_on'
+  _STATE_FORCE_OFF = 'force_off'
+  _STATE_RESET = 'reset'
+  _STATE_ON = 'on'
+  _STATE_OFF = 'off'
+
+  def __init__(self, interface, params):
+    """Constructor.
+
+    Args:
+      interface: driver interface object
+      params: dictionary of params
+    """
+    super(FwWpStateDriver, self).__init__(interface, params)
+
+  def _force_on(self):
+    """Force the firmware to write-protected."""
+    raise NotImplementedError()
+
+  def _force_off(self):
+    """Force the firmware to not write-protected."""
+    raise NotImplementedError()
+
+  def _reset(self):
+    """Reset the firmware write-protection state to the system value."""
+    raise NotImplementedError()
+
+  def _get_state(self):
+    """Get the firmware write-protection state."""
+    raise NotImplementedError()
+
+  def set(self, statename):
+    """Set firmware write-protection state according to `statename`."""
+    if statename == self._STATE_FORCE_ON:
+      self._force_on()
+    elif statename == self._STATE_FORCE_OFF:
+      self._force_off()
+    elif statename == self._STATE_RESET:
+      self._reset()
+    else:
+      raise ValueError("Invalid fw_wp_state setting: '%s'. Try one of "
+                       "'%s', '%s', or '%s'." % (statename,
+                           self._FORCE_ON, self._FORCE_OFF, self._RESET))
+
+  def get(self):
+    """Get firmware write-protection state."""
+    return self._get_state()