MLK-16013-15: staging: typec: tcpci: Enable vbus force discharge
authorLi Jun <jun.li@nxp.com>
Wed, 26 Jul 2017 16:57:32 +0000 (00:57 +0800)
committerNitin Garg <nitin.garg@nxp.com>
Mon, 19 Mar 2018 20:36:19 +0000 (15:36 -0500)
Enable vbus low voltage alert and do force discharge, this can aid turn
off vbus quickly.

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 2b6937a..7294b30 100644 (file)
@@ -58,6 +58,31 @@ static int tcpci_write16(struct tcpci *tcpci, unsigned int reg, u16 val)
        return regmap_raw_write(tcpci->regmap, reg, &val, sizeof(u16));
 }
 
+static int tcpci_vbus_force_discharge(struct tcpc_dev *tcpc, bool enable)
+{
+       struct tcpci *tcpci = tcpc_to_tcpci(tcpc);
+       unsigned int reg;
+       int ret;
+
+       if (enable)
+               regmap_write(tcpci->regmap,
+                       TCPC_VBUS_VOLTAGE_ALARM_LO_CFG, 0x1c);
+       else
+               regmap_write(tcpci->regmap,
+                       TCPC_VBUS_VOLTAGE_ALARM_LO_CFG, 0);
+
+       regmap_read(tcpci->regmap, TCPC_POWER_CTRL, &reg);
+       if (enable)
+               reg |= TCPC_POWER_CTRL_FORCEDISCH;
+       else
+               reg &= ~TCPC_POWER_CTRL_FORCEDISCH;
+       ret = regmap_write(tcpci->regmap, TCPC_POWER_CTRL, reg);
+       if (ret < 0)
+               return ret;
+
+       return 0;
+}
+
 static int tcpci_set_cc(struct tcpc_dev *tcpc, enum typec_cc_status cc)
 {
        struct tcpci *tcpci = tcpc_to_tcpci(tcpc);
@@ -254,6 +279,9 @@ static int tcpci_set_vbus(struct tcpc_dev *tcpc, bool source, bool sink)
                                   TCPC_CMD_DISABLE_SRC_VBUS);
                if (ret < 0)
                        return ret;
+
+               /* Enable force discharge */
+               tcpci_vbus_force_discharge(tcpc, true);
        }
 
        if (!sink) {
@@ -371,7 +399,8 @@ static int tcpci_init(struct tcpc_dev *tcpc)
        reg = TCPC_ALERT_TX_SUCCESS | TCPC_ALERT_TX_FAILED |
                TCPC_ALERT_TX_DISCARDED | TCPC_ALERT_RX_STATUS |
                TCPC_ALERT_RX_HARD_RST | TCPC_ALERT_CC_STATUS |
-               TCPC_ALERT_RX_BUF_OVF | TCPC_ALERT_FAULT;
+               TCPC_ALERT_RX_BUF_OVF | TCPC_ALERT_FAULT |
+               TCPC_ALERT_V_ALARM_LO;
        if (tcpci->controls_vbus)
                reg |= TCPC_ALERT_POWER_STATUS;
        tcpci->irq_mask = reg;
@@ -410,6 +439,9 @@ static irqreturn_t tcpci_irq(int irq, void *dev_id)
                        tcpm_vbus_change(tcpci->port);
        }
 
+       if (status & TCPC_ALERT_V_ALARM_LO)
+               tcpm_vbus_low_alarm(tcpci->port);
+
        if (status & TCPC_ALERT_RX_STATUS) {
                struct pd_message msg;
                unsigned int cnt;
@@ -504,6 +536,7 @@ static int tcpci_probe(struct i2c_client *client,
        tcpci->tcpc.set_vconn = tcpci_set_vconn;
        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.set_pd_rx = tcpci_set_pd_rx;
        tcpci->tcpc.set_roles = tcpci_set_roles;
index 2f4da3d..29a7c4b 100644 (file)
@@ -67,6 +67,7 @@
 
 #define TCPC_POWER_CTRL                        0x1c
 #define TCPC_POWER_CTRL_VCONN_ENABLE   BIT(0)
+#define TCPC_POWER_CTRL_FORCEDISCH     BIT(2)
 
 #define TCPC_CC_STATUS                 0x1d
 #define TCPC_CC_STATUS_TERM            BIT(4)
index cf414de..dc2f09e 100644 (file)
@@ -151,6 +151,7 @@ enum pd_msg_request {
 #define TCPM_CC_EVENT          BIT(0)
 #define TCPM_VBUS_EVENT                BIT(1)
 #define TCPM_RESET_EVENT       BIT(2)
+#define TCPM_VBUS_LOW_ALARM    BIT(3)
 
 #define LOG_BUFFER_ENTRIES     1024
 #define LOG_BUFFER_ENTRY_SIZE  128
@@ -3023,6 +3024,14 @@ static void _tcpm_pd_hard_reset(struct tcpm_port *port)
                       0);
 }
 
+static void _tcpm_vbus_discharge(struct tcpm_port *port, bool on)
+{
+       tcpm_log_force(port, "%s force discharge", on ? "Enable":"Disable");
+
+       if (port->tcpc && port->tcpc->vbus_discharge)
+               port->tcpc->vbus_discharge(port->tcpc, false);
+}
+
 static void tcpm_pd_event_handler(struct work_struct *work)
 {
        struct tcpm_port *port = container_of(work, struct tcpm_port,
@@ -3047,6 +3056,10 @@ static void tcpm_pd_event_handler(struct work_struct *work)
                        else
                                _tcpm_pd_vbus_off(port);
                }
+
+               if (events & TCPM_VBUS_LOW_ALARM)
+                       _tcpm_vbus_discharge(port, false);
+
                if (events & TCPM_CC_EVENT) {
                        enum typec_cc_status cc1, cc2;
 
@@ -3077,6 +3090,15 @@ void tcpm_vbus_change(struct tcpm_port *port)
 }
 EXPORT_SYMBOL_GPL(tcpm_vbus_change);
 
+void tcpm_vbus_low_alarm(struct tcpm_port *port)
+{
+       spin_lock(&port->pd_event_lock);
+       port->pd_events |= TCPM_VBUS_LOW_ALARM;
+       spin_unlock(&port->pd_event_lock);
+       queue_work(port->wq, &port->event_work);
+}
+EXPORT_SYMBOL_GPL(tcpm_vbus_low_alarm);
+
 void tcpm_pd_hard_reset(struct tcpm_port *port)
 {
        spin_lock(&port->pd_event_lock);
index e504399..a231e7c 100644 (file)
@@ -122,6 +122,8 @@ struct tcpc_dev {
        int (*pd_transmit)(struct tcpc_dev *dev, enum tcpm_transmit_type type,
                           const struct pd_message *msg);
        int (*vbus_detect)(struct tcpc_dev *dev, bool enable);
+       int (*vbus_discharge)(struct tcpc_dev *tcpc, bool enable);
+
        struct tcpc_mux_dev *mux;
 };
 
@@ -147,5 +149,6 @@ void tcpm_pd_transmit_complete(struct tcpm_port *port,
                               enum tcpm_transmit_status status);
 void tcpm_pd_hard_reset(struct tcpm_port *port);
 void tcpm_tcpc_reset(struct tcpm_port *port);
+void tcpm_vbus_low_alarm(struct tcpm_port *port);
 
 #endif /* __LINUX_USB_TCPM_H */