276 lines
6.2 KiB
C
276 lines
6.2 KiB
C
/* 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/D’1,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 */
|