summaryrefslogtreecommitdiff
path: root/driver
diff options
context:
space:
mode:
authorPatryk Duda <pdk@semihalf.com>2021-01-05 15:45:46 +0100
committerCommit Bot <commit-bot@chromium.org>2021-01-28 02:12:40 +0000
commitb7308fdff1300bb4bf0362e71ed022b2dbf92c25 (patch)
treef7f6cfaa46c4866ab9c2e5157f5ecee53bd87614 /driver
parentf70199129b2a48e0071ac684b2c1a1e321ff43c1 (diff)
downloadchrome-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.c107
-rw-r--r--driver/tcpm/tcpci.c2
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, &reg);
+ 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))