blob: c9c2916ba5c890c4a60483c34c8422cfe53f2b40 [file] [log] [blame]
# 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.
"""Driver for board config controls of drv=cr50.
Provides the following Cr50 controlled function:
cold_reset
warm_reset
ccd_keepalive_en
"""
import functools
import logging
import pty_driver
# cr50 firmware versions verified compatible with servod
VALID_VERSIONS = ["0.1.0/cr50_v1.9308_B.293-ad55183"]
class cr50Error(Exception):
"""Exception class for Cr50."""
def restricted_command(func):
"""Decorator for methods which use restricted console command."""
@functools.wraps(func)
def wrapper(instance, *args, **kwargs):
try:
return func(instance, *args, **kwargs)
except pty_driver.ptyError, e:
if str(e) == 'Timeout waiting for response.':
if instance._Get_ccd_level() == 'Locked':
raise cr50Error("CCD console is locked. Perform the unlock process!")
# Raise the original exception
raise
return wrapper
class cr50(pty_driver.ptyDriver):
"""Object to access drv=cr50 controls.
Note, instances of this object get dispatched via base class,
HwDriver's get/set method. That method ultimately calls:
"_[GS]et_%s" % params['subtype'] below.
For example, a control to read kbd_en would be dispatched to
call _Get_kbd_en.
"""
def __init__(self, interface, params):
"""Constructor.
Args:
interface: FTDI interface object to handle low-level communication to
control
params: dictionary of params needed to perform operations on
devices. The only params used now is 'subtype', which is used
by get/set method of base class to decide how to dispatch
request.
"""
super(cr50, self).__init__(interface, params)
self._logger.debug("")
self._interface = interface
if not hasattr(self._interface, '_ec_uart_bitbang_props'):
self._interface._ec_uart_bitbang_props = {
"enabled" : False,
"parity" : None,
"baudrate" : None
}
def _issue_cmd_get_results(self, cmds, regex_list,
timeout=pty_driver.DEFAULT_UART_TIMEOUT):
"""Send \n to cr50 to make sure it is awake before sending cmds"""
super(cr50, self)._issue_cmd_get_results('\n', [])
return super(cr50, self)._issue_cmd_get_results(cmds, regex_list, timeout)
def _Get_cold_reset(self):
"""Getter of cold_reset (active low).
Returns:
0: cold_reset on.
1: cold_reset off.
"""
result = self._issue_cmd_get_results(
"ecrst", ["EC_RST_L is (asserted|deasserted)"])[0]
if result is None:
raise cr50Error("Cannot retrieve ecrst result on cr50 console.")
return 0 if result[1] == "asserted" else 1
def _Set_cold_reset(self, value):
"""Setter of cold_reset (active low).
Args:
value: 0=on, 1=off.
"""
if value == 0:
self._issue_cmd("ecrst on")
else:
self._issue_cmd("ecrst off")
def _Get_ccd_state(self):
"""Run a basic command that should take a short amount of time to check
if ccd endpoints are still working.
Returns:
0: ccd is off.
1: ccd is on.
"""
try:
# If gettime fails then the cr50 console is not working, which means
# ccd is not working
result = self._issue_cmd_get_results("gettime", ['.'], 3)
except:
return 0
return 1
def _Get_warm_reset(self):
"""Getter of warm_reset (active low).
Returns:
0: warm_reset on.
1: warm_reset off.
"""
result = self._issue_cmd_get_results(
"sysrst", ["SYS_RST_L is (asserted|deasserted)"])[0]
if result is None:
raise cr50Error("Cannot retrieve sysrst result on cr50 console.")
return 0 if result[1] == "asserted" else 1
def _Set_warm_reset(self, value):
"""Setter of warm_reset (active low).
Args:
value: 0=on, 1=off.
"""
if value == 0:
self._issue_cmd("sysrst on")
else:
self._issue_cmd("sysrst off")
@restricted_command
def _Get_pwr_button(self):
"""Getter of pwr_button.
Returns:
0: power button press.
1: power button release.
"""
result = self._issue_cmd_get_results(
"powerbtn", ["powerbtn: (forced press|pressed|released)"])[0]
if result is None:
raise cr50Error("Cannot retrieve power button result on cr50 console.")
return 1 if result[1] == "released" else 0
def _Get_reset_count(self):
"""Getter of reset count.
Returns:
The reset count
"""
result = self._issue_cmd_get_results(
"sysinfo", ["Reset count: (\d+)"])[0]
if result is None:
raise cr50Error("Cannot retrieve the reset count on cr50 console.")
return result[1]
def _Get_devid(self):
"""Getter of devid.
Returns:
The cr50 devid string
"""
result = self._issue_cmd_get_results(
"sysinfo", ["DEV_ID:\s+(0x[0-9a-z]{8} 0x[0-9a-z]{8})"])[0][1]
if result is None:
raise cr50Error("Cannot retrieve the devid result on cr50 console.")
return result
def _Get_ver(self):
"""Getter of ver.
Returns:
The cr50 version string
"""
result = self._issue_cmd_get_results(
"ver", ["RW_(A|B):\s+\*\s+(\S+)\s"])[0]
if result is None:
raise cr50Error("Cannot retrieve the version result on cr50 console.")
return result[2]
def _Get_ver_valid(self):
"""Getter of ver_valid.
Returns:
Boolean: Is the cr50 version string one of the validated versions?
"""
ver = self._Get_ver()
valid = ver in VALID_VERSIONS
if not valid:
logging.warn("Detected cr50 version: %s", ver)
logging.warn("Not in valid versions: %s", VALID_VERSIONS)
return valid
def _Set_cr50_reboot(self, value):
"""Reboot cr50 ignoring the value."""
self._issue_cmd("reboot")
def _Get_ccd_level(self):
"""Getter of ccd_level.
Returns:
lock, unlock, or open based on the current ccd privilege level.
"""
result = self._issue_cmd_get_results(
"ccd", ["State:\s+(Lock|Unlock|Open)"])[0]
if result is None:
raise cr50Error("Cannot retrieve ccd privilege level on cr50 console.")
return result[1].lower()
def _Set_ccd_noop(self, value):
"""Used to ignore servo controls"""
pass
def _Get_ccd_noop(self):
"""Used to ignore servo controls"""
return "ERR"
def _get_ccd_cap_state(self, cap):
"""Get the current state of the ccd capability"""
result = self._issue_cmd_get_results("ccdstate", ["%s:([^\n]*)\n" % cap])
return result[0][1].strip()
def _Get_ccd_testlab(self):
"""Getter of ccd_testlab.
Returns:
'on' or 'off' if ccd testlab mode is enabled or disabled. 'unsupported'
if cr50 doesn't have testlab support.
"""
result = self._issue_cmd_get_results("ccd testlab",
["(CCD test lab mode (ena|dis)|Access Denied)"])[0][1]
if result == "Access Denied":
return "unsupported"
return "on" if "ena" in result else "off"
def _Set_ccd_testlab(self, value):
"""Setter of ccd_testlab.
We dont want to accidentally disable ccd testlab mode. Only accept the value
open. This will change the ccd privilege level without any physical
presence.
Args:
value: 'open'
"""
if value == "open":
self._issue_cmd("ccd testlab open")
else:
raise ValueError("Invalid ccd testlab setting: '%s'. Try 'open'" % value)
def _Get_ccd_keepalive_en(self):
"""Getter of ccd_keepalive_en.
Returns:
0: keepalive disabled.
1: keepalive enabled.
"""
rdd = self._get_ccd_cap_state("Rdd")
return "on" if "keepalive" in rdd else "off"
def _Set_ccd_keepalive_en(self, value):
"""Setter of ccd_keepalive_en.
Args:
value: 0=off, 1=on.
"""
if value == "off" or value == "on":
self._issue_cmd("rddkeepalive %s" % value)
else:
raise ValueError("Invalid ec_keepalive_en setting: '%s'. Try one of "
"'on', or 'off'." % value)
def _Get_ec_uart_bitbang_en(self):
return self._interface._ec_uart_bitbang_props["enabled"]
def _Set_ec_uart_bitbang_en(self, value):
if value:
# We need parity and baudrate settings in order to enable bit banging.
if not self._interface._ec_uart_bitbang_props["parity"]:
raise ValueError(
"No parity set. Try setting 'ec_uart_parity' first.")
if not self._interface._ec_uart_bitbang_props["baudrate"]:
raise ValueError(
"No baud rate set. Try setting 'ec_uart_baudrate' first.")
# The EC UART index is 2.
cmd = "%s %s %s" % ("bitbang 2",
self._interface._ec_uart_bitbang_props["baudrate"],
self._interface._ec_uart_bitbang_props["parity"])
try:
result = self._issue_cmd_get_results(cmd, ["Bit bang enabled"])
if result is None:
raise cr50Error("Unable to enable bit bang mode!")
except pty_driver.ptyError:
raise cr50Error("Unable to enable bit bang mode!")
self._interface._ec_uart_bitbang_props["enabled"] = 1
else:
self._issue_cmd("bitbang 2 disable")
self._interface._ec_uart_bitbang_props["enabled"] = 0
def _Get_ccd_ec_uart_parity(self):
self._logger.debug("%r", self._interface._ec_uart_bitbang_props)
return self._interface._ec_uart_bitbang_props["parity"]
def _Set_ccd_ec_uart_parity(self, value):
if value.lower() not in ["odd", "even", "none"]:
raise ValueError("Bad parity (%s). Try 'odd', 'even', or 'none'." % value)
self._interface._ec_uart_bitbang_props["parity"] = value
self._logger.debug("%r", self._interface._ec_uart_bitbang_props)
def _Get_ccd_ec_uart_baudrate(self):
return self._interface._ec_uart_bitbang_props["baudrate"]
def _Set_ccd_ec_uart_baudrate(self, value):
if value is not None and value.lower() not in ["none","1200", "2400",
"4800", "9600", "19200",
"38400", "57600", "115200"]:
raise ValueError("Bad baud rate(%s). Try '1200', '2400', '4800', '9600',"
" '19200', '38400', '57600', or '115200'" % value)
if value.lower() == "none":
value = None
self._interface._ec_uart_bitbang_props["baudrate"] = value
def _Get_ec_boot_mode(self):
boot_mode = "off"
result = self._issue_cmd_get_results("gpioget EC_FLASH_SELECT",
["\s+([01])\s+EC_FLASH_SELECT"])[0]
if result:
if result[0] == "1":
boot_mode = "on"
return boot_mode
def _Set_ec_boot_mode(self, value):
self._issue_cmd("gpioset EC_FLASH_SELECT %s" % value)