diff options
author | Patryk Duda <pdk@semihalf.com> | 2021-01-05 15:45:46 +0100 |
---|---|---|
committer | Commit Bot <commit-bot@chromium.org> | 2021-01-28 02:12:40 +0000 |
commit | b7308fdff1300bb4bf0362e71ed022b2dbf92c25 (patch) | |
tree | f7f6cfaa46c4866ab9c2e5157f5ecee53bd87614 /driver | |
parent | f70199129b2a48e0071ac684b2c1a1e321ff43c1 (diff) | |
download | chrome-ec-b7308fdff1300bb4bf0362e71ed022b2dbf92c25.tar.gz |
ps8xxx: Set RP during LPM enter if TCPC acts as mux only
Some boards (eg. ampton) uses TCPC/MUX chips (eg. PS8751) as muxers
only. In this case CC lines are simply not connected. Nevertheless
setting CC lines to approptiate value can decrease power consumption.
This patch implements custom PS8xxx MUX driver which is responsible for
setting RP on both CC lines on Low Power Mode enter when this TCPC is
used as muxer only (USB_MUX_FLAG_NOT_TCPC flag is set). Due to flash
size constraints, this driver is only available when appropriate config
is defined.
Unfortunately, RP can't be set once during initialization because
after switching mux appropriately there is no connection. To work
properly RD should be set on both CC lines. Changing RD -> RP after
switching mux doesn't work (breaks connection), even with some delay
before switching to RP again.
Moreover, when PS8751 is in standby mode, first I2C transaction always
fails. Documentation suggests that device could be woken up by
performing I2C read from PS8XXX_REG_I2C_DEBUGGING_ENABLE register.
For more information about purpose of this change please refer to
b:113830171#comment18 and further.
BUG=b:151155658
BRANCH=none
TEST=Flash EC ToT on Ampton. Check if power consumption is lower.
Don't connect devices to tested USB-C port.
Issue 'i2cxfer r 2 0xB 0x1A' 2 times within 2 seconds and check
if it returns 0x05 (DRP disabled, RP default, CC1, CC2 set to RP).
Repeat above with command 'i2cxfer r 4 0xB 0x1A'.
NOTE: PS8751 goes to Low Power Mode automatically after 2 seconds
when RP is set that is why we need to read register 2 times, first
to wake up device, second to read value.
TEST=Connect device to USB-C port, check in dmesg that device is
recognised as SuperSpeed device. Repeat this test 10 times. Check
also different rotation.
Change-Id: Ie1bac6caa9912c024c87792536d7a35863fa96a0
Signed-off-by: Patryk Duda <pdk@semihalf.com>
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/ec/+/2614618
Reviewed-by: Wai-Hong Tam <waihong@google.com>
Commit-Queue: Wai-Hong Tam <waihong@google.com>
Diffstat (limited to 'driver')
-rw-r--r-- | driver/tcpm/ps8xxx.c | 107 | ||||
-rw-r--r-- | driver/tcpm/tcpci.c | 2 |
2 files changed, 108 insertions, 1 deletions
diff --git a/driver/tcpm/ps8xxx.c b/driver/tcpm/ps8xxx.c index ba79cb5c3b..8390815c76 100644 --- a/driver/tcpm/ps8xxx.c +++ b/driver/tcpm/ps8xxx.c @@ -48,6 +48,13 @@ #endif /* CONFIG_USB_PD_TCPM_PS8751 */ +#ifdef CONFIG_USB_PD_TCPM_PS8751_CUSTOM_MUX_DRIVER +#if !defined(CONFIG_USB_PD_TCPM_PS8751) +#error "Custom MUX driver is available only for PS8751" +#endif + +#endif /* CONFIG_USB_PD_TCPM_PS8751_CUSTOM_MUX_DRIVER */ + #define CPRINTF(format, args...) cprintf(CC_USBPD, format, ## args) #define CPRINTS(format, args...) cprints(CC_USBPD, format, ## args) @@ -76,6 +83,8 @@ static bool ps8815_role_control_delay[CONFIG_USB_PD_PORT_MAX_COUNT]; */ static uint64_t hpd_deadline[CONFIG_USB_PD_PORT_MAX_COUNT]; +void ps8xxx_wake_from_standby(const struct usb_mux *me); + #if defined(CONFIG_USB_PD_TCPM_PS8705) || \ defined(CONFIG_USB_PD_TCPM_PS8751) || \ defined(CONFIG_USB_PD_TCPM_PS8755) || \ @@ -300,6 +309,11 @@ void ps8xxx_tcpc_update_hpd_status(const struct usb_mux *me, { int port = me->usb_port; + if (IS_ENABLED(CONFIG_USB_PD_TCPM_PS8751_CUSTOM_MUX_DRIVER) && + product_id[me->usb_port] == PS8751_PRODUCT_ID && + me->flags & USB_MUX_FLAG_NOT_TCPC) + ps8xxx_wake_from_standby(me); + dp_set_hpd(me, hpd_lvl); if (hpd_irq) { @@ -640,3 +654,96 @@ struct i2c_stress_test_dev ps8xxx_i2c_stress_test_dev = { .i2c_write = &tcpc_i2c_write, }; #endif /* CONFIG_CMD_I2C_STRESS_TEST_TCPC */ + +#ifdef CONFIG_USB_PD_TCPM_PS8751_CUSTOM_MUX_DRIVER + +static int ps8xxx_mux_init(const struct usb_mux *me) +{ + RETURN_ERROR(tcpci_tcpm_mux_init(me)); + + /* If this MUX is also the TCPC, then skip init */ + if (!(me->flags & USB_MUX_FLAG_NOT_TCPC)) + return EC_SUCCESS; + + product_id[me->usb_port] = board_get_ps8xxx_product_id(me->usb_port); + + return EC_SUCCESS; +} + +/* + * PS8751 goes to standby mode automatically when both CC lines are set to RP. + * In standby mode it doesn't respond to first I2C access, but next + * transactions are working fine (until it goes to sleep again). + * + * To wake device documentation recommends read content of 0xA0 register. + */ +void ps8xxx_wake_from_standby(const struct usb_mux *me) +{ + int reg; + + /* Since we are waking up device, this call will most likely fail */ + mux_read(me, PS8XXX_REG_I2C_DEBUGGING_ENABLE, ®); + msleep(10); +} + +static int ps8xxx_mux_set(const struct usb_mux *me, mux_state_t mux_state) +{ + + if (product_id[me->usb_port] == PS8751_PRODUCT_ID && + me->flags & USB_MUX_FLAG_NOT_TCPC) { + ps8xxx_wake_from_standby(me); + + /* + * To operate properly, when working as mux only, PS8751 CC + * lines needs to be RD all the time. Changing to RP after + * setting mux breaks SuperSpeed connection. + */ + if (mux_state != USB_PD_MUX_NONE) + RETURN_ERROR(mux_write(me, TCPC_REG_ROLE_CTRL, + TCPC_REG_ROLE_CTRL_SET(TYPEC_NO_DRP, + TYPEC_RP_USB, + TYPEC_CC_RD, + TYPEC_CC_RD))); + } + + return tcpci_tcpm_mux_set(me, mux_state); +} + +static int ps8xxx_mux_get(const struct usb_mux *me, mux_state_t *mux_state) +{ + if (product_id[me->usb_port] == PS8751_PRODUCT_ID && + me->flags & USB_MUX_FLAG_NOT_TCPC) + ps8xxx_wake_from_standby(me); + + return tcpci_tcpm_mux_get(me, mux_state); +} + +static int ps8xxx_mux_enter_low_power(const struct usb_mux *me) +{ + /* + * Set PS8751 lines to RP. This allows device to standby + * automatically after ~2 seconds + */ + if (product_id[me->usb_port] == PS8751_PRODUCT_ID && + me->flags & USB_MUX_FLAG_NOT_TCPC) { + /* + * It may happen that this write will fail, but + * RP seems to be set correctly + */ + mux_write(me, TCPC_REG_ROLE_CTRL, + TCPC_REG_ROLE_CTRL_SET(TYPEC_NO_DRP, TYPEC_RP_USB, + TYPEC_CC_RP, TYPEC_CC_RP)); + return EC_SUCCESS; + } + + return tcpci_tcpm_mux_enter_low_power(me); +} + +const struct usb_mux_driver ps8xxx_usb_mux_driver = { + .init = &ps8xxx_mux_init, + .set = &ps8xxx_mux_set, + .get = &ps8xxx_mux_get, + .enter_low_power_mode = &ps8xxx_mux_enter_low_power, +}; + +#endif /* CONFIG_USB_PD_TCPM_PS8751_CUSTOM_MUX_DRIVER */ diff --git a/driver/tcpm/tcpci.c b/driver/tcpm/tcpci.c index 0f03f06a35..36f4cf4085 100644 --- a/driver/tcpm/tcpci.c +++ b/driver/tcpm/tcpci.c @@ -1498,7 +1498,7 @@ int tcpci_tcpm_mux_init(const struct usb_mux *me) return error ? EC_ERROR_UNKNOWN : EC_SUCCESS; } -static int tcpci_tcpm_mux_enter_low_power(const struct usb_mux *me) +int tcpci_tcpm_mux_enter_low_power(const struct usb_mux *me) { /* If this MUX is also the TCPC, then skip low power */ if (!(me->flags & USB_MUX_FLAG_NOT_TCPC)) |