servo_v4: Add servo_v4 device interfaces

Add i2c port selection, sx1505 interfaces to servod.
Add servo_v4 xml description. This covers partial support
for Servo V4, as it's USBPD implementation is incomplete.

BUG=chromium:571476
TEST=GPIO, I2C, console work with reworked servo_v4

Change-Id: I04201dd5c613f842660cd72b0b88c189711388a5
Signed-off-by: Nick Sanders <nsanders@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/329954
Reviewed-by: Todd Broch <tbroch@chromium.org>
diff --git a/servo/data/Makefile b/servo/data/Makefile
index c906ded..f0f9de0 100644
--- a/servo/data/Makefile
+++ b/servo/data/Makefile
@@ -16,7 +16,7 @@
 		  strago_proto.xml veyron_danger_p1.xml ryu_p0_loc.xml oak.xml \
 		  veyron_brain_p1.xml ryu_p5_loc.xml ryu_evt2_loc.xml \
                   ryu_dvt_loc.xml kunimitsu.xml
-INA231		= samus.xml whale_samus_dut.xml gru_r0.xml
+INA231		= samus.xml whale_samus_dut.xml gru_r0.xml servo_v4_inas.xml
 INA3221		= glados_p1_inas.xml glados_r2_inas.xml chell_r1_inas.xml
 
 # TODO(tbroch) should probably be added to setup.py if possible
diff --git a/servo/data/servo_v4.xml b/servo/data/servo_v4.xml
new file mode 100644
index 0000000..348f655
--- /dev/null
+++ b/servo/data/servo_v4.xml
@@ -0,0 +1,299 @@
+<?xml version="1.0"?>
+<root>
+  <include>
+    <name>common.xml</name>
+  </include>
+  <include>
+    <name>servo_v4_inas.xml</name>
+  </include>
+  <map>
+    <name>onoff_pwrsrc_sel</name>
+    <doc>Source DUT Type-C power from input Type-C or host USB.
+    Default is typec</doc>
+    <params host="0" typec="1"></params>
+  </map>
+  <map>
+    <name>onoff_sbu_uart_sel</name>
+    <doc>Select between CCD or UART on SBU. Default is CCD</doc>
+    <params ccd="0" uart="1"></params>
+  </map>
+  <map>
+    <name>onoff_fastboot_duthub_sel</name>
+    <doc>Select between DUT as host or device. Default is
+    host</doc>
+    <params device="0" host="1"></params>
+  </map>
+  <map>
+    <name>onoff_dut_host_sel</name>
+    <doc>Select between DUT and host. Default is host</doc>
+    <params host="0" dut="1"></params>
+  </map>
+  <map>
+    <name>onoff_sd_usb_sel</name>
+    <doc>Select between sd and usb. Default is sd</doc>
+    <params usb="0" sd="1"></params>
+  </map>
+  <map>
+    <name>onoff_uservo_fastboot_sel</name>
+    <doc>Select between uservo and fastboot passthrough. Default is
+    uservo</doc>
+    <params uservo="0" fastboot="1"></params>
+  </map>
+  <!-- GPIO on chip settings -->
+  <control>
+    <name>dut_charge_enable</name>
+    <doc>GPIO_DUT_CHG_EN</doc>
+    <params interface="1" drv="gpio" offset="0" map="onoff"
+    init="off"></params>
+  </control>
+  <control>
+    <name>dut_charge_source</name>
+    <doc>GPIO_HOST_OR_CHG_CTL</doc>
+    <params interface="1" drv="gpio" offset="1"
+    map="onoff_pwrsrc_sel" init="typec"></params>
+  </control>
+  <control>
+    <name>dp_hpd</name>
+    <doc>DP_HPD: Displayport hotplug detect</doc>
+    <params interface="1" drv="gpio" offset="2" map="onoff"
+    init="off"></params>
+  </control>
+  <control>
+    <name>sbu_uart_sel</name>
+    <doc>SBU_UART_SEL: Select between CCD or UART on SBU</doc>
+    <params interface="1" drv="gpio" offset="3"
+    map="onoff_sbu_uart_sel" init="ccd"></params>
+  </control>
+  <control>
+    <name>host_hub_reset</name>
+    <doc>HOST_USB_HUB_RESET_L</doc>
+    <params interface="1" drv="gpio" offset="4" map="onoff_i"
+    init="off"></params>
+  </control>
+  <control>
+    <name>fastboot_duthub_mux_sel</name>
+    <alias>dut_is_host_or_device</alias>
+    <doc>FASTBOOT_DUTHUB_MUX_SEL: Select DUT USB routed as device
+    to host, or as host to duthub</doc>
+    <params interface="1" drv="gpio" offset="5"
+    map="onoff_fastboot_duthub_sel" init="host"></params>
+  </control>
+  <control>
+    <name>sbu_mux_enable</name>
+    <doc>SBU_MUX_EN: enable DUT's SBU routing</doc>
+    <params interface="1" drv="gpio" offset="6" map="onoff"
+    init="off"></params>
+  </control>
+  <control>
+    <name>dut_usb_mux_enable</name>
+    <doc>FASTBOOT_DUTHUB_MUX_EN_L: enable DUT's USB interface</doc>
+    <params interface="1" drv="gpio" offset="7" map="onoff_i"
+    init="on"></params>
+  </control>
+  <control>
+    <name>dut_hub_usb_reset</name>
+    <doc>DUT_HUB_USB_RESET_L: reset dut's usb hub</doc>
+    <params interface="1" drv="gpio" offset="8" map="onoff_i"
+    init="off"></params>
+  </control>
+  <control>
+    <name>at_hwb</name>
+    <doc>ATMEL_HWB_L: enable DFU programming of atmega</doc>
+    <params interface="1" drv="gpio" offset="9" map="onoff_i"
+    init="off"></params>
+  </control>
+  <control>
+    <name>at_rst</name>
+    <doc>ATMEL_RESET_L: reset atmega</doc>
+    <params interface="1" drv="gpio" offset="10" map="onoff_i"
+    init="on"></params>
+  </control>
+  <control>
+    <name>sd_mux_sel</name>
+    <doc>EMMC_MUX_SEL</doc>
+    <params interface="1" drv="gpio" offset="11"
+    map="onoff_dut_host_sel" init="host"></params>
+  </control>
+  <control>
+    <name>cmux_en</name>
+    <doc>CMUX_EN: enable DUT USB3/Displayport mux.</doc>
+    <params interface="1" drv="gpio" offset="12" map="onoff"
+    init="on"></params>
+  </control>
+  <control>
+    <name>sd_en</name>
+    <doc>EMMC_MUX_EN_L: enable sd mux connection</doc>
+    <params interface="1" drv="gpio" offset="13" map="onoff_i"
+    init="off"></params>
+  </control>
+  <control>
+    <name>sd_pwr_en</name>
+    <doc>EMMC_PWR_EN</doc>
+    <params interface="1" drv="gpio" offset="14" map="onoff"
+    init="off"></params>
+  </control>
+  <control>
+    <name>uservo_pwr_fault</name>
+    <doc>USERVO_FAULT_L</doc>
+    <params interface="1" drv="gpio" offset="15" map="onoff_i">
+    </params>
+  </control>
+  <control>
+    <name>usb3_pwr_fault</name>
+    <doc>USB_FAULT_L</doc>
+    <params interface="1" drv="gpio" offset="16" map="onoff_i">
+    </params>
+  </control>
+  <control>
+    <name>DUT_CC2_RPUSB</name>
+    <doc>DUT_CC2_RPUSB</doc>
+    <params interface="1" drv="gpio" offset="17" map="onoff">
+    </params>
+  </control>
+  <control>
+    <name>DUT_CC2_RD</name>
+    <doc>DUT_CC2_RD</doc>
+    <params interface="1" drv="gpio" offset="18" map="onoff">
+    </params>
+  </control>
+  <control>
+    <name>DUT_CC2_RA</name>
+    <doc>DUT_CC2_RA</doc>
+    <params interface="1" drv="gpio" offset="19" map="onoff">
+    </params>
+  </control>
+  <control>
+    <name>tp17</name>
+    <doc>testpoint 17</doc>
+    <params interface="1" drv="gpio" offset="20" map="onoff">
+    </params>
+  </control>
+  <control>
+    <name>DUT_CC1_PR3A0</name>
+    <doc>DUT_CC1_PR3A0</doc>
+    <params interface="1" drv="gpio" offset="21" map="onoff">
+    </params>
+  </control>
+  <control>
+    <name>DUT_CC1_RP1A5</name>
+    <doc>DUT_CC1_RP1A5</doc>
+    <params interface="1" drv="gpio" offset="22" map="onoff">
+    </params>
+  </control>
+  <control>
+    <name>DUT_CC1_RPUSB</name>
+    <doc>DUT_CC1_RPUSB</doc>
+    <params interface="1" drv="gpio" offset="23" map="onoff">
+    </params>
+  </control>
+  <control>
+    <name>DUT_CC1_RD</name>
+    <doc>DUT_CC1_RD</doc>
+    <params interface="1" drv="gpio" offset="24" map="onoff">
+    </params>
+  </control>
+  <control>
+    <name>DUT_CC1_RA</name>
+    <doc>DUT_CC1_RA</doc>
+    <params interface="1" drv="gpio" offset="25" map="onoff">
+    </params>
+  </control>
+  <control>
+    <name>DUT_CC2_PR3A0</name>
+    <doc>DUT_CC2_PR3A0</doc>
+    <params interface="1" drv="gpio" offset="26" map="onoff">
+    </params>
+  </control>
+  <control>
+    <name>DUT_CC2_RP1A5</name>
+    <doc>DUT_CC2_RP1A5</doc>
+    <params interface="1" drv="gpio" offset="27" map="onoff">
+    </params>
+  </control>
+  <!-- GPIO on expander settings -->
+  <control>
+    <name>sbu_flip_sel</name>
+    <doc>SBU_FLIP_SEL: Flip SBU lines based on cable
+    rotation.</doc>
+    <params interface="3" drv="sx1505" slv="0x20" port="0"
+    offset="0" map="onoff" init="off"></params>
+  </control>
+  <control>
+    <name>usb3_mux_sel</name>
+    <doc>USB3.0_TYPEA_MUX_SEL</doc>
+    <params interface="3" drv="sx1505" slv="0x20" port="0"
+    offset="1" map="onoff_dut_host_sel" init="host"></params>
+  </control>
+  <control>
+    <name>usb3_mux_en</name>
+    <doc>USB3.0_TYPEA_MUX_EN_L</doc>
+    <params interface="3" drv="sx1505" slv="0x20" port="0"
+    offset="2" map="onoff_i" init="on"></params>
+  </control>
+  <control>
+    <name>usb3_pwr_en</name>
+    <doc>USB3.0_TYPE_A_PWR_EN</doc>
+    <params interface="3" drv="sx1505" slv="0x20" port="0"
+    offset="3" map="onoff" init="on"></params>
+  </control>
+  <control>
+    <name>host_sd_usb_mux_sel</name>
+    <doc>TYPEA_EMMC_MUX_SEL</doc>
+    <params interface="3" drv="sx1505" slv="0x20" port="0"
+    offset="4" map="onoff_sd_usb_sel" init="usb"></params>
+  </control>
+  <control>
+    <name>uservo_pwr_en</name>
+    <doc>USERVO_POWER_EN</doc>
+    <params interface="3" drv="sx1505" slv="0x20" port="0"
+    offset="5" map="onoff" init="on"></params>
+  </control>
+  <control>
+    <name>uservo_fastboot_mux_sel</name>
+    <doc>USERVO_FASTBOOT_MUX_SEL</doc>
+    <params interface="3" drv="sx1505" slv="0x20" port="0"
+    offset="6" map="onoff_uservo_fastboot_sel" init="uservo">
+    </params>
+  </control>
+  <control>
+    <name>host_sd_usb_mux_en</name>
+    <doc>TYPEA_EMMC_MUX_EN_L</doc>
+    <params interface="3" drv="sx1505" slv="0x20" port="0"
+    offset="7" map="onoff_i" init="on"></params>
+  </control>
+  <!-- UART and CONSOLE settings -->
+  <control>
+    <name>raw_servo_v4_console_pty</name>
+    <doc>Servo V4's stm32 console.</doc>
+    <params cmd="get" subtype="pty" interface="2" drv="uart">
+    </params>
+  </control>
+  <control>
+    <name>dut_pty</name>
+    <doc>UART routed to DUT's SBU lines.</doc>
+    <params cmd="get" subtype="pty" interface="4" drv="uart">
+    </params>
+  </control>
+  <control>
+    <name>atmega_pty</name>
+    <doc>UART routed to atmega mcu for keyboard emulator.</doc>
+    <params cmd="get" subtype="pty" interface="5" drv="uart">
+    </params>
+  </control>
+  <!-- EC-3PO console interpreter linkup -->
+  <control>
+    <name>ec3po_servo_v4_console</name>
+    <alias>servo_v4_console_pty</alias>
+    <doc>Servo v4 console provided via EC-3PO console
+    interpreter.</doc>
+    <params cmd="get" subtype="pty" interface="6" drv="uart">
+    </params>
+  </control>
+  <control>
+    <name>servo_v4_ec3po_interp_connect</name>
+    <doc>State indicating if interpreter is listening to the servo
+    console.</doc>
+    <params interface="6" drv="ec3po_driver" map="onoff" init="on"
+    subtype="interp_connect"></params>
+  </control>
+</root>
diff --git a/servo/data/servo_v4_inas.py b/servo/data/servo_v4_inas.py
new file mode 100644
index 0000000..98f123f
--- /dev/null
+++ b/servo/data/servo_v4_inas.py
@@ -0,0 +1,8 @@
+# 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.
+inas = [
+        # TODO(nsanders): Come up with a way to support multiple voltages.
+        (0x40, 'ppdut5', 5.0, 0.01, 'rem', True), # A0: GND, A1: GND
+       ]
+interface = 3
diff --git a/servo/drv/__init__.py b/servo/drv/__init__.py
index 72222b1..c79a032 100644
--- a/servo/drv/__init__.py
+++ b/servo/drv/__init__.py
@@ -47,6 +47,7 @@
 import sleep
 import storm_power
 import stumpy_power
+import sx1505
 import tca6416
 import tcs3414
 import uart
diff --git a/servo/drv/sx1505.py b/servo/drv/sx1505.py
new file mode 100644
index 0000000..1ca485e
--- /dev/null
+++ b/servo/drv/sx1505.py
@@ -0,0 +1,144 @@
+# 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 sx1505 8bit ioexpander.
+"""
+import hw_driver
+import i2c_reg
+
+
+class Sx1505Error(Exception):
+  """Error occurred accessing Sx1505."""
+
+# We need a shared register cache between all the bits,
+# as there is no readable state register.
+# Indexing is: reg_cache[((interface, i2c addr), reg addr)] = value
+reg_cache = {}
+
+
+class sx1505(hw_driver.HwDriver):
+  """Object to access drv=sx1505 controls."""
+
+
+  # base indexes of the data, direction, pullup and pulldown registers
+  # respectively.
+  REG_DATA = 0
+  REG_DIR = 1
+  REG_PU = 2
+  REG_PD = 3
+
+
+  def __init__(self, interface, params):
+    """Constructor.
+
+    Args:
+      interface: i2c interface object to handle low-level communication to
+          control
+      params: dictionary of params needed to perform operations on ina219
+          devices.  All items are strings initially but should be cast to types
+          detailed below.
+
+
+    Mandatory Params:
+      slv: integer, 7-bit i2c slave address
+      offset: integer, gpio's bit position from lsb
+    Optional Params:
+    """
+    super(sx1505, self).__init__(interface, params)
+    slave = self._get_slave()
+    self._i2c_obj = i2c_reg.I2cReg.get_device(self._interface, slave,
+                                              addr_len=1, reg_len=1,
+                                              msb_first=True, no_read=False,
+                                              use_reg_cache=False)
+    # Remember what GPIOs we have set.
+    global reg_cache
+    self._reg_cache = reg_cache
+    self._cacheindex = (self._interface, slave)
+    # Cache REG_DATA
+    outputs = self._reg_cache.get((self._cacheindex, self.REG_DATA), 0xff)
+    self._reg_cache[(self._cacheindex, self.REG_DATA)] = outputs
+    # Cache REG_DIR
+    dir = self._reg_cache.get((self._cacheindex, self.REG_DIR), 0xff)
+    self._reg_cache[(self._cacheindex, self.REG_DIR)] = dir
+
+    # Initlialize pullup
+    if self._io_type == 'PU':
+      (_, mask) = self._get_offset_mask()
+      pu_reg = self._i2c_obj._read_reg(self.REG_PU)
+      pu_reg = pu_reg | mask
+      self._i2c_obj._write_reg(self.REG_PU, pu_reg)
+
+
+  def get(self):
+    """Get gpio value.
+
+    sx1505 has a data register REG_DATA. GPIO's current value
+    can be read there.
+
+    Returns:
+      integer in formatted representation
+    """
+    self._logger.debug("")
+    value = self._i2c_obj._read_reg(self.REG_DATA)
+    return self._create_logical_value(value)
+
+  def set(self, fmt_value):
+    """Set value on ioexpander.
+
+    1. Read cached value
+    2. Mask accordingly
+    3. Write value to Output register
+    4. Read Direction reg cache (Note 0 == output, 1 == input)
+       a. if input, Write Direction register
+    5. Read Input register for returning
+
+    Args:
+      fmt_value: Integer value to write to hardware.  If None or empty string
+        defaults to 0.
+
+    Raises:
+      Sx1505Error: If width of open drain driver is != 1 or open drain type is
+        not recognized.
+    """
+    self._logger.debug("sx1505 set %s" % fmt_value)
+    (_, mask) = self._get_offset_mask()
+    if mask is None:
+        raise Sx1505Error("Unable to determine mask.  Is offset declared?")
+
+    change_to_input = False
+    if self._io_type == "PU" and fmt_value == 1:
+      self._logger.debug("Set to input because its io type is PU")
+      change_to_input = True
+
+    # output register handling
+    if not change_to_input:
+      hw_value = 0
+      if fmt_value:
+        hw_value = self._create_hw_value(fmt_value)
+
+      current_out_reg = self._reg_cache[(self._cacheindex, self.REG_DATA)]
+      new_out_reg = hw_value | (current_out_reg & ~mask)
+      if new_out_reg != current_out_reg:
+        self._reg_cache[(self._cacheindex, self.REG_DATA)] = new_out_reg
+        self._i2c_obj._write_reg(self.REG_DATA, new_out_reg)
+
+    current_dir_reg = self._reg_cache[(self._cacheindex, self.REG_DIR)]
+    if change_to_input:
+      new_dir_reg = current_dir_reg | mask
+    else:
+      new_dir_reg = current_dir_reg & ~mask
+
+    if new_dir_reg != current_dir_reg:
+      self._reg_cache[(self._cacheindex, self.REG_DIR)] = new_dir_reg
+      self._i2c_obj._write_reg(self.REG_DIR, new_dir_reg)
+
+  def _get_slave(self):
+    """Check and return needed params to call driver.
+
+    Returns:
+      slave: 7-bit i2c address
+    """
+    if 'slv' not in self._params:
+      raise Sx1505Error("getting slave address")
+    slave = int(self._params['slv'], 0)
+    return slave
diff --git a/servo/servo_interfaces.py b/servo/servo_interfaces.py
index beec2a6..ad9c37c 100644
--- a/servo/servo_interfaces.py
+++ b/servo/servo_interfaces.py
@@ -91,12 +91,13 @@
 SERVO_V4_DEFAULTS = [(0x18d1, 0x501b)]
 for vid, pid in SERVO_V4_DEFAULTS:
   INTERFACE_DEFAULTS[vid][pid] = \
-    ['stm32_gpio',                           # 1: 32x GPIO block
-     {'name': 'stm32_uart', 'interface': 0}, # 2: UART4
-     {'name': 'stm32_uart', 'interface': 3}, # 3: servo console
-     {'name': 'stm32_i2c', 'interface': 4},  # 4: i2c
-     {'name': 'ec3po_uart',                  # 5: servo console
-      'raw_pty': 'raw_servo_console_pty'},
+    [{'name': 'stm32_gpio', 'interface': 1}, # 1: 32x GPIO block.
+     {'name': 'stm32_uart', 'interface': 0}, # 2: servo console.
+     {'name': 'stm32_i2c', 'interface': 2},  # 3: i2c
+     {'name': 'stm32_uart', 'interface': 3}, # 4: dut sbu uart
+     {'name': 'stm32_uart', 'interface': 4}, # 4: atmega uart
+     {'name': 'ec3po_uart',                  # 6: servo v4 console
+      'raw_pty': 'raw_servo_v4_console_pty'},
     ]
 
 # miniservo
diff --git a/servo/servo_server.py b/servo/servo_server.py
index 53de578..99d37e5 100755
--- a/servo/servo_server.py
+++ b/servo/servo_server.py
@@ -263,7 +263,9 @@
       Si2cError: Raised on init failure.
     """
     self._logger.info("Si2cBus: interface: %s" % interface)
-    return stm32i2c.Si2cBus(self._vendor, self._product, interface['interface'])
+    port = interface.get('port', 0)
+    return stm32i2c.Si2cBus(self._vendor, self._product,
+        interface['interface'], port=port)
 
   def _init_bb_adc(self, interface):
     """Initalize beaglebone ADC interface."""
diff --git a/servo/stm32i2c.py b/servo/stm32i2c.py
index 7c003b9..ae1d69c 100644
--- a/servo/stm32i2c.py
+++ b/servo/stm32i2c.py
@@ -46,6 +46,7 @@
     self._logger.debug("")
 
     self._port = port
+    self._logger.debug("Set port %d" % port)
 
     self._susb = stm32usb.Susb(vendor=vendor, product=product,
         interface=interface, serialname=serialname, logger=self._logger)
@@ -78,8 +79,8 @@
       Si2cError on transaction failure.
     """
     self._logger.debug("Si2c.wr_rd("
-        "slave_address=0x%x, write_list=%s, read_count=%s)" % (
-          slave_address, write_list, read_count))
+        "port=%d, slave_address=0x%x, write_list=%s, read_count=%s)" % (
+          self._port, slave_address, write_list, read_count))
 
     # Clean up args from python style to correct types.
     if not write_list: