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, ®);
+ 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);
TCPC_CMD_DISABLE_SRC_VBUS);
if (ret < 0)
return ret;
+
+ /* Enable force discharge */
+ tcpci_vbus_force_discharge(tcpc, true);
}
if (!sink) {
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;
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;
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;
#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)
#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
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,
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;
}
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);
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;
};
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 */