MLK-16013-18 staging: typec: handle unexpected vbus from sink
authorLi Jun <jun.li@nxp.com>
Thu, 27 Jul 2017 12:58:54 +0000 (20:58 +0800)
committerNitin Garg <nitin.garg@nxp.com>
Mon, 19 Mar 2018 20:36:19 +0000 (15:36 -0500)
Power source detected the attachment of sink, will firstly check
the vbus level to make sure power sink isn't sourcing vbus(< 600mv)
before going forward, otherwise it will terminate the session and
go to SRC_UNATTACHED state, this is required by PD compliance.

Reviewed-by: Peter Chen <peter.chen@nxp.com>
Signed-off-by: Li Jun <jun.li@nxp.com>
drivers/staging/typec/tcpci.c
drivers/staging/typec/tcpci.h
drivers/staging/typec/tcpm.c
drivers/staging/typec/tcpm.h

index 5e1778a..dbab97b 100644 (file)
@@ -318,6 +318,21 @@ static int tcpci_get_vbus(struct tcpc_dev *tcpc)
        return !!(reg & TCPC_POWER_STATUS_VBUS_PRES);
 }
 
+static unsigned int tcpci_get_vbus_vol(struct tcpc_dev *tcpc)
+{
+       struct tcpci *tcpci = tcpc_to_tcpci(tcpc);
+       unsigned int reg, ret = 0;
+
+       ret = regmap_read(tcpci->regmap, TCPC_VBUS_VOLTAGE, &reg);
+
+       /* Convert it to be the vol number(mv) */
+       ret = ((reg & TCPC_VBUS_VOL_MASK) <<
+               ((reg & TCPC_VBUS_VOL_SCALE_FACTOR_MASK) >>
+               TCPC_VBUS_VOL_SCALE_FACTOR_SHIFT)) * TCPC_VBUS_VOL_MV_UNIT;
+
+       return ret;
+}
+
 static int tcpci_set_vbus(struct tcpc_dev *tcpc, bool source, bool sink)
 {
        struct tcpci *tcpci = tcpc_to_tcpci(tcpc);
@@ -588,6 +603,7 @@ static int tcpci_probe(struct i2c_client *client,
        tcpci->tcpc.start_drp_toggling = tcpci_start_drp_toggling;
        tcpci->tcpc.vbus_detect = tcpci_vbus_detect;
        tcpci->tcpc.vbus_discharge = tcpci_vbus_force_discharge;
+       tcpci->tcpc.get_vbus_vol = tcpci_get_vbus_vol;
 
        tcpci->tcpc.set_pd_rx = tcpci_set_pd_rx;
        tcpci->tcpc.set_roles = tcpci_set_roles;
index 29a7c4b..d74423f 100644 (file)
 #define TCPC_TX_DATA                   0x54 /* through 0x6f */
 
 #define TCPC_VBUS_VOLTAGE                      0x70
+#define TCPC_VBUS_VOL_MASK                     0x3ff
+#define TCPC_VBUS_VOL_SCALE_FACTOR_MASK                0xc00
+#define TCPC_VBUS_VOL_SCALE_FACTOR_SHIFT       10
+#define TCPC_VBUS_VOL_MV_UNIT                  25
+
 #define TCPC_VBUS_SINK_DISCONNECT_THRESH       0x72
 #define TCPC_VBUS_STOP_DISCHARGE_THRESH                0x74
 #define TCPC_VBUS_VOLTAGE_ALARM_HI_CFG         0x76
index 82993f1..b4e77b2 100644 (file)
@@ -1840,6 +1840,15 @@ static bool tcpm_start_drp_toggling(struct tcpm_port *port)
        return false;
 }
 
+static bool tcpm_vbus_is_low(struct tcpm_port *port)
+{
+       if (port->tcpc->get_vbus_vol &&
+               port->tcpc->get_vbus_vol(port->tcpc) > TCPM_VBUS_PRESENT_LEVEL)
+               return false;
+       else
+               return true;
+}
+
 static void tcpm_set_cc(struct tcpm_port *port, enum typec_cc_status cc)
 {
        tcpm_log(port, "cc:=%d", cc);
@@ -2183,9 +2192,14 @@ static void run_state_machine(struct tcpm_port *port)
                break;
 
        case SRC_ATTACHED:
-               ret = tcpm_src_attach(port);
-               tcpm_set_state(port, SRC_UNATTACHED,
+               if (tcpm_vbus_is_low(port)) {
+                       ret = tcpm_src_attach(port);
+                       tcpm_set_state(port, SRC_UNATTACHED,
                               ret < 0 ? 0 : PD_T_PS_SOURCE_ON);
+               } else {
+                       tcpm_log(port, "Sink shouldn't provide vbus!");
+                       tcpm_set_state(port, SRC_UNATTACHED, 10);
+               }
                break;
        case SRC_STARTUP:
                typec_set_pwr_opmode(port->typec_port, TYPEC_PWR_MODE_USB);
index 358631f..08f103d 100644 (file)
@@ -19,6 +19,9 @@
 #include <linux/usb/typec.h>
 #include "pd.h"
 
+/* VBUS off level should be lower than it */
+#define TCPM_VBUS_PRESENT_LEVEL                600
+
 enum typec_cc_status {
        TYPEC_CC_OPEN,
        TYPEC_CC_RA,
@@ -105,6 +108,8 @@ struct tcpc_dev {
 
        int (*init)(struct tcpc_dev *dev);
        int (*get_vbus)(struct tcpc_dev *dev);
+       /* Optional, get the vbus voltage(mv) */
+       unsigned int (*get_vbus_vol)(struct tcpc_dev *dev);
        int (*set_cc)(struct tcpc_dev *dev, enum typec_cc_status cc);
        int (*get_cc)(struct tcpc_dev *dev, enum typec_cc_status *cc1,
                      enum typec_cc_status *cc2);