retimer: Add driver support for Intel Burnside Bridge retimer

Burnside Bridge is a Type-C multi-protocol retimer to be used in on-board
applications. Burnside Bridge offers the ability to latch protocol signals
into on-chip memory before retransmitting them onwards. It can be used to
extend the physical length of the system without increasing high frequency
jitter.

Burnside Bridge supports spec compliant retimer of following protocols:
1. Display Port: four unidirectional DP lanes
2. USB3.1 Gen1/2: one bi-directional USB lane
3. Thunderbolt: two bi-directional CIO lanes
4. Multifunction Display (MFD): two unidirectional lanes of DP and one
   bi-directional lane of USB3.1 Gen1/2

Note: Only item 1, 2 & 4 are supported in this CL. Item 3 support will
      be added in follow on CLs.

BUG=b:127623438
BRANCH=none
TEST=Manually verified on ICLRVP, able to configure the registers

Change-Id: I2d60dbcaf8fe7a1503f09a2f16007409f059f54e
Signed-off-by: Ayushee <ayushee.shah@intel.com>
Signed-off-by: Vijay Hiremath <vijay.p.hiremath@intel.com>
Reviewed-on: https://chromium-review.googlesource.com/1594170
Commit-Ready: Jett Rink <jettrink@chromium.org>
Tested-by: Vijay P Hiremath <vijay.p.hiremath@intel.com>
Reviewed-by: Jett Rink <jettrink@chromium.org>
Reviewed-by: Divya S Sasidharan <divya.s.sasidharan@intel.com>
This commit is contained in:
Vijay Hiremath 2019-03-06 13:01:45 -08:00 committed by chrome-bot
parent d823206bd2
commit a84d8055a5
8 changed files with 429 additions and 0 deletions

View File

@ -376,6 +376,18 @@ int pd_is_vbus_present(int port)
#endif
}
#ifdef CONFIG_USB_PD_RETIMER
int pd_is_ufp(int port)
{
return pd[port].cc_state == PD_CC_UFP_ATTACHED;
}
int pd_is_debug_acc(int port)
{
return pd[port].cc_state == PD_CC_DEBUG_ACC;
}
#endif
static void set_polarity(int port, int polarity)
{
tcpm_set_polarity(port, polarity);

View File

@ -126,6 +126,9 @@ driver-$(CONFIG_USB_PD_TCPM_PS8805)+=tcpm/ps8xxx.o
driver-$(CONFIG_USB_PD_TCPM_TUSB422)+=tcpm/tusb422.o
driver-$(CONFIG_USB_PD_TCPM_NCT38XX)+=tcpm/nct38xx.o
# Type-C Retimer drivers
driver-$(CONFIG_USB_PD_RETIMER_INTEL_BB)+=retimer/bb_retimer.o
# USB mux high-level driver
driver-$(CONFIG_USBC_SS_MUX)+=usb_mux.o

275
driver/retimer/bb_retimer.c Normal file
View File

@ -0,0 +1,275 @@
/* 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.
*
* Driver for Intel Burnside Bridge - Thunderbolt/USB/DisplayPort Retimer
*/
#include "bb_retimer.h"
#include "common.h"
#include "console.h"
#include "i2c.h"
#include "timer.h"
#include "usb_pd.h"
#include "usb_retimer.h"
#include "util.h"
#define BB_RETIMER_REG_SIZE 4
#define BB_RETIMER_READ_SIZE (BB_RETIMER_REG_SIZE + 1)
#define BB_RETIMER_WRITE_SIZE (BB_RETIMER_REG_SIZE + 2)
#define CPRINTS(format, args...) cprints(CC_USBCHARGE, format, ## args)
#define CPRINTF(format, args...) cprintf(CC_USBCHARGE, format, ## args)
static int bb_retimer_read(int port, const uint8_t offset, uint32_t *data)
{
int rv;
uint8_t buf[BB_RETIMER_READ_SIZE];
/*
* Read sequence
* Slave Addr(w) - Reg offset - repeated start - Slave Addr(r)
* byte[0] : Read size
* byte[1:4] : Data [LSB -> MSB]
* Stop
*/
rv = i2c_xfer(bb_retimers[port].i2c_port, bb_retimers[port].i2c_addr,
&offset, 1, buf, BB_RETIMER_READ_SIZE);
if (rv)
return rv;
if (buf[0] != BB_RETIMER_REG_SIZE)
return EC_ERROR_UNKNOWN;
*data = buf[1] | (buf[2] << 8) | (buf[3] << 16) | (buf[4] << 24);
return EC_SUCCESS;
}
static int bb_retimer_write(int port, const uint8_t offset, uint32_t data)
{
uint8_t buf[BB_RETIMER_WRITE_SIZE];
/*
* Write sequence
* Slave Addr(w)
* byte[0] : Reg offset
* byte[1] : Write Size
* byte[2:5] : Data [LSB -> MSB]
* stop
*/
buf[0] = offset;
buf[1] = BB_RETIMER_REG_SIZE;
buf[2] = data & 0xFF;
buf[3] = (data >> 8) & 0xFF;
buf[4] = (data >> 16) & 0xFF;
buf[5] = (data >> 24) & 0xFF;
return i2c_xfer(bb_retimers[port].i2c_port, bb_retimers[port].i2c_addr,
buf, BB_RETIMER_WRITE_SIZE, NULL, 0);
}
static void bb_retimer_power_handle(int port, int on_off)
{
struct bb_retimer *retimer;
/* handle retimer's power domain */
retimer = &bb_retimers[port];
if (on_off) {
gpio_set_level(retimer->usb_ls_en_gpio, 1);
msleep(1);
gpio_set_level(retimer->retimer_rst_gpio, 1);
msleep(10);
gpio_set_level(retimer->force_power_gpio, 1);
/*
* If BB retimer NVM is shared between two ports allow 40ms
* time for both retimers to be initialized. Else allow 20ms
* to initialize.
*/
if (retimer->shared_nvm)
msleep(40);
else
msleep(20);
} else {
gpio_set_level(retimer->force_power_gpio, 0);
msleep(1);
gpio_set_level(retimer->retimer_rst_gpio, 0);
msleep(1);
gpio_set_level(retimer->usb_ls_en_gpio, 0);
}
}
int retimer_set_state(int port, mux_state_t mux_state)
{
uint32_t set_retimer_con = 0;
uint8_t dp_pin_mode;
/*
* Bit 0: DATA_CONNECTION_PRESENT
* 0 - No connection present
* 1 - Connection present
*/
if (mux_state & USB_PD_MUX_USB_ENABLED ||
mux_state & USB_PD_MUX_DP_ENABLED)
set_retimer_con |= BB_RETIMER_DATA_CONNECTION_PRESENT;
/*
* Bit 1: CONNECTION_ORIENTATION
* 0 - Normal
* 1 - reversed
*/
if (mux_state & USB_PD_MUX_POLARITY_INVERTED)
set_retimer_con |= BB_RETIMER_CONNECTION_ORIENTATION;
/*
* TODO: b:129990370
* Bit 2: ACTIVE_CABLE
* 0 - Passive
* 1 -TBT Active cable
*/
/*
* Bit 5: USB_3_CONNECTION
* 0 - No USB3.1 Connection
* 1 - USB3.1 connection
*/
if (mux_state & USB_PD_MUX_USB_ENABLED) {
set_retimer_con |= BB_RETIMER_USB_3_CONNECTION;
/*
* Bit 7: USB_DATA_ROLE (ignored if BIT5=0)
* 0 - DFP
* 1 - UPF
*/
if (pd_is_ufp(port))
set_retimer_con |= BB_RETIMER_USB_DATA_ROLE;
}
/*
* Bit 8: DP_CONNECTION
* 0 No DP connection
* 1 DP connected
*/
if (mux_state & USB_PD_MUX_DP_ENABLED) {
set_retimer_con |= BB_RETIMER_DP_CONNECTION;
/*
* Bit 10-11: DP_PIN_ASSIGNMENT (ignored if BIT8 = 0)
* 00 Pin assignments E/E
* 01 Pin assignments C/C/D/D1,2
* 10, 11 - reserved
*/
dp_pin_mode = board_get_dp_pin_mode(port);
if (dp_pin_mode == MODE_DP_PIN_C ||
dp_pin_mode == MODE_DP_PIN_D)
set_retimer_con |= BB_RETIMER_DP_PIN_ASSIGNMENT;
/*
* Bit 14: IRQ_HPD (ignored if BIT8 = 0)
* 0 - No IRQ_HPD
* 1 - IRQ_HPD received
*/
if (mux_state & USB_PD_MUX_HPD_IRQ)
set_retimer_con |= BB_RETIMER_IRQ_HPD;
/*
* Bit 15: HPD_LVL (ignored if BIT8 = 0)
* 0 - HPD_State Low
* 1 - HPD_State High
*/
if (mux_state & USB_PD_MUX_HPD_LVL)
set_retimer_con |= BB_RETIMER_HPD_LVL;
}
/*
* Bit 12: DEBUG_ACCESSORY_MODE
* 0 - Not in debug mode
* 1 - In debug accessory mode
*/
if (pd_is_debug_acc(port))
set_retimer_con |= BB_RETIMER_DEBUG_ACCESSORY_MODE;
/* Writing the register4 */
return bb_retimer_write(port, BB_RETIMER_REG_CONNECTION_STATE,
set_retimer_con);
}
int retimer_low_power_mode(int port)
{
bb_retimer_power_handle(port, 0);
return EC_SUCCESS;
}
int retimer_init(int port)
{
int rv;
uint32_t data;
bb_retimer_power_handle(port, 1);
rv = bb_retimer_read(port, BB_RETIMER_REG_VENDOR_ID, &data);
if (rv)
return rv;
if (data != BB_RETIMER_VENDOR_ID)
return EC_ERROR_UNKNOWN;
rv = bb_retimer_read(port, BB_RETIMER_REG_DEVICE_ID, &data);
if (rv)
return rv;
if (data != BB_RETIMER_DEVICE_ID)
return EC_ERROR_UNKNOWN;
return EC_SUCCESS;
}
#ifdef CONFIG_CMD_RETIMER
static int console_command_bb_retimer(int argc, char **argv)
{
char rw, *e;
int rv, port, reg, data, val;
if (argc < 4)
return EC_ERROR_PARAM_COUNT;
/* Get port number */
port = strtoi(argv[1], &e, 0);
if (*e || port < 0 || port > CONFIG_USB_PD_PORT_COUNT)
return EC_ERROR_PARAM1;
/* Validate r/w selection */
rw = argv[2][0];
if (rw != 'w' && rw != 'r')
return EC_ERROR_PARAM2;
/* Get register address */
reg = strtoi(argv[3], &e, 0);
if (*e || reg < 0)
return EC_ERROR_PARAM3;
if (rw == 'r')
rv = bb_retimer_read(port, reg, &data);
else {
/* Get value to be written */
val = strtoi(argv[4], &e, 0);
if (*e || val < 0)
return EC_ERROR_PARAM4;
rv = bb_retimer_write(port, reg, val);
if (rv == EC_SUCCESS) {
rv = bb_retimer_read(port, reg, &data);
if (rv == EC_SUCCESS && data != val)
rv = EC_ERROR_UNKNOWN;
}
}
if (rv == EC_SUCCESS)
CPRINTS("register 0x%x [%d] = 0x%x [%d]", reg, reg, data, data);
return rv;
}
DECLARE_CONSOLE_COMMAND(bb, console_command_bb_retimer,
"<port> <r/w> <reg> | <val>",
"Read or write to BB retimer register");
#endif /* CONFIG_CMD_RETIMER */

View File

@ -0,0 +1,56 @@
/* 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.
*
* Driver header for Intel Burnside Bridge - Thunderbolt/USB/DisplayPort Retimer
*/
#ifndef __CROS_EC_BB_RETIMER_H
#define __CROS_EC_BB_RETIMER_H
#include "gpio.h"
/* Burnside Bridge I2C Configuration Space */
#define BB_RETIMER_REG_VENDOR_ID 0
#define BB_RETIMER_VENDOR_ID 0x8086
#define BB_RETIMER_REG_DEVICE_ID 1
#define BB_RETIMER_DEVICE_ID 0x15EE
/* Connection State Register Attributes */
#define BB_RETIMER_REG_CONNECTION_STATE 4
#define BB_RETIMER_DATA_CONNECTION_PRESENT BIT(0)
#define BB_RETIMER_CONNECTION_ORIENTATION BIT(1)
#define BB_RETIMER_ACTIVE_CABLE BIT(2)
#define BB_RETIMER_USB_3_CONNECTION BIT(5)
#define BB_RETIMER_USB_DATA_ROLE BIT(7)
#define BB_RETIMER_DP_CONNECTION BIT(8)
#define BB_RETIMER_DP_PIN_ASSIGNMENT BIT(10)
#define BB_RETIMER_DEBUG_ACCESSORY_MODE BIT(12)
#define BB_RETIMER_IRQ_HPD BIT(14)
#define BB_RETIMER_HPD_LVL BIT(15)
/* Describes a USB Retimer present in the system */
struct bb_retimer {
/* USB Retimer I2C port */
const int i2c_port;
/* USB Retimer I2C address */
const int i2c_addr;
/* NVM flag if shared with multiple BB-retimers */
uint8_t shared_nvm;
/* Retimer control GPIOs */
enum gpio_signal usb_ls_en_gpio; /* Load switch enable */
enum gpio_signal retimer_rst_gpio; /* Retimer reset */
enum gpio_signal force_power_gpio; /* Force power (active/low) */
};
/*
* USB Retimers in system, ordered by PD port #, defined at board-level
* CONFIG_USB_PD_RETIMER_INTEL_BB need to be defind at board-level.
*/
extern struct bb_retimer bb_retimers[];
#endif /* __CROS_EC_BB_RETIMER_H */

View File

@ -9,6 +9,7 @@
#include "console.h"
#include "host_command.h"
#include "usb_mux.h"
#include "usb_retimer.h"
#include "util.h"
/*
@ -26,13 +27,21 @@ static inline void virtual_mux_update_state(int port, mux_state_t mux_state)
{
if (virtual_mux_state[port] != mux_state) {
virtual_mux_state[port] = mux_state;
#ifdef CONFIG_USB_PD_RETIMER
if (retimer_set_state(port, mux_state))
return;
#endif
host_set_single_event(EC_HOST_EVENT_USB_MUX);
}
}
static int virtual_init(int port)
{
#ifdef CONFIG_USB_PD_RETIMER
return retimer_init(port);
#else
return EC_SUCCESS;
#endif
}
/*
@ -76,4 +85,7 @@ const struct usb_mux_driver virtual_usb_mux_driver = {
.init = virtual_init,
.set = virtual_set_mux,
.get = virtual_get_mux,
#ifdef CONFIG_USB_PD_RETIMER
.enter_low_power_mode = retimer_low_power_mode,
#endif
};

View File

@ -1091,6 +1091,7 @@
#undef CONFIG_CMD_BATDEBUG
#define CONFIG_CMD_BATTFAKE
#undef CONFIG_CMD_BATT_MFG_ACCESS
#define CONFIG_CMD_RETIMER
#undef CONFIG_CMD_BUTTON
#define CONFIG_CMD_CBI
#undef CONFIG_CMD_CCD_DISABLE /* 'ccd disable' subcommand */
@ -3567,6 +3568,16 @@
#undef CONFIG_USB_PD_TCPM_MT6370
#undef CONFIG_USB_PD_TCPM_TUSB422
/*
* Type-C multi-protocol retimer is present.
*/
#undef CONFIG_USB_PD_RETIMER
/*
* Type-C multi-protocol retimer to be used in on-board applications.
*/
#undef CONFIG_USB_PD_RETIMER_INTEL_BB
/*
* Adds an EC console command to erase the ANX7447 OCM flash.
* Note: this is intended to be a temporary option and
@ -4334,6 +4345,12 @@
#define CONFIG_BC12_DETECT_DATA_ROLE_TRIGGER
#endif
/*****************************************************************************/
/* Define derived config options for Retimer chips */
#ifdef CONFIG_USB_PD_RETIMER_INTEL_BB
#define CONFIG_USB_PD_RETIMER
#endif
/*****************************************************************************/
/*
* Handle task-dependent configs.

View File

@ -1962,6 +1962,22 @@ int pd_is_vbus_present(int port);
*/
uint8_t board_get_dp_pin_mode(int port);
#ifdef CONFIG_USB_PD_RETIMER
/**
* Return true if specified PD port is UFP.
*
* @param port USB-C port number
*/
int pd_is_ufp(int port);
/**
* Return true if specified PD port is debug accessory.
*
* @param port USB-C port number
*/
int pd_is_debug_acc(int port);
#endif
/* ----- Logging ----- */
#ifdef CONFIG_USB_PD_LOGGING
/**

38
include/usb_retimer.h Normal file
View File

@ -0,0 +1,38 @@
/* 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.
*/
/* USB retimer driver */
#ifndef __CROS_EC_USB_RETIMER_H
#define __CROS_EC_USB_RETIMER_H
#include "usb_mux.h"
/**
* Set USB retimer state
*
* @param port Port number.
* @param mux_state current MUX state
* @return Non-zero if fail; otherwise, zero.
*/
int retimer_set_state(int port, mux_state_t mux_state);
/**
* USB retimer enter to low power mode.
*
* @param port Port number.
* @return Non-zero if fail; otherwise, zero.
*/
int retimer_low_power_mode(int port);
/**
* Initialize USB Retimer to its default state.
*
* @param port Port number.
* @return Non-zero if fail; otherwise, zero.
*/
int retimer_init(int port);
#endif /* __CROS_EC_USB_RETIMER_H */