summaryrefslogtreecommitdiff
path: root/drivers/usb/pd/richtek/pd_policy_engine_snk.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb/pd/richtek/pd_policy_engine_snk.c')
-rw-r--r--drivers/usb/pd/richtek/pd_policy_engine_snk.c205
1 files changed, 205 insertions, 0 deletions
diff --git a/drivers/usb/pd/richtek/pd_policy_engine_snk.c b/drivers/usb/pd/richtek/pd_policy_engine_snk.c
new file mode 100644
index 000000000000..27f60340ed9d
--- /dev/null
+++ b/drivers/usb/pd/richtek/pd_policy_engine_snk.c
@@ -0,0 +1,205 @@
+/*
+ * Copyright (C) 2016 Richtek Technology Corp.
+ *
+ * Power Delivery Policy Engine for SNK
+ *
+ * Author: TH <tsunghan_tsai@richtek.com>
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/hisi/usb/pd/richtek/pd_core.h>
+#include <linux/hisi/usb/pd/richtek/pd_dpm_core.h>
+#include <linux/hisi/usb/pd/richtek/tcpci.h>
+#include <linux/hisi/usb/pd/richtek/pd_policy_engine.h>
+
+/*
+ * [PD2.0] Figure 8-39 Sink Port state diagram
+ */
+
+void pe_snk_startup_entry(pd_port_t *pd_port, pd_event_t *pd_event)
+{
+ u8 rx_cap = PD_RX_CAP_PE_STARTUP;
+
+ pd_port->state_machine = PE_STATE_MACHINE_SINK;
+ pd_reset_protocol_layer(pd_port);
+
+ switch (pd_event->event_type) {
+ case PD_EVT_HW_MSG: /* CC attached */
+ pd_put_pe_event(pd_port, PD_PE_RESET_PRL_COMPLETED);
+ break;
+
+ case PD_EVT_PE_MSG: /* From Hard-Reset */
+ pd_enable_vbus_valid_detection(pd_port, false);
+ break;
+
+ case PD_EVT_CTRL_MSG: /* From PR-SWAP (Received PS_RDY) */
+ /* If we reset rx_cap in here, */
+ /* maybe can't meet tSwapSink (Check it later) */
+ if (!pd_dpm_check_vbus_valid(pd_port)) {
+ PE_INFO("rx_cap_on\r\n");
+ rx_cap = PD_RX_CAP_PE_SEND_WAIT_CAP;
+ }
+
+ pd_put_pe_event(pd_port, PD_PE_RESET_PRL_COMPLETED);
+ pd_free_pd_event(pd_port, pd_event);
+ break;
+ }
+
+ pd_set_rx_enable(pd_port, rx_cap);
+}
+
+void pe_snk_discovery_entry(pd_port_t *pd_port, pd_event_t *pd_event)
+{
+#ifdef CONFIG_USB_PD_FAST_RESP_TYPEC_SRC
+ pd_disable_timer(pd_port, PD_TIMER_SRC_RECOVER);
+#endif /* CONFIG_USB_PD_FAST_RESP_TYPEC_SRC */
+ pd_enable_vbus_valid_detection(pd_port, true);
+}
+
+void pe_snk_wait_for_capabilities_entry(
+ pd_port_t *pd_port, pd_event_t *pd_event)
+{
+ pd_notify_pe_hard_reset_completed(pd_port);
+
+ pd_set_rx_enable(pd_port, PD_RX_CAP_PE_SEND_WAIT_CAP);
+ pd_enable_timer(pd_port, PD_TIMER_SINK_WAIT_CAP);
+ pd_free_pd_event(pd_port, pd_event);
+}
+
+void pe_snk_wait_for_capabilities_exit(pd_port_t *pd_port, pd_event_t *pd_event)
+{
+ pd_disable_timer(pd_port, PD_TIMER_SINK_WAIT_CAP);
+}
+
+void pe_snk_evaluate_capability_entry(pd_port_t *pd_port, pd_event_t *pd_event)
+{
+ /* Stop NoResponseTimer and reset HardResetCounter to zero */
+
+ pd_disable_timer(pd_port, PD_TIMER_NO_RESPONSE);
+
+ pd_port->hard_reset_counter = 0;
+ pd_port->pd_connected = 1;
+ pd_port->pd_prev_connected = 1;
+ pd_port->explicit_contract = false;
+
+ pd_dpm_snk_evaluate_caps(pd_port, pd_event);
+ pd_free_pd_event(pd_port, pd_event);
+}
+
+void pe_snk_select_capability_entry(pd_port_t *pd_port, pd_event_t *pd_event)
+{
+ if (pd_event->msg == PD_DPM_NOTIFIED) {
+ PE_DBG("SelectCap%d, rdo:0x%08x\r\n",
+ pd_event->msg_sec, pd_port->last_rdo);
+ } else {
+ /* new request, for debug only */
+ /* pd_dpm_sink_vbus(pd_port, false); */
+ PE_DBG("NewReq, rdo:0x%08x\r\n", pd_port->last_rdo);
+ }
+
+ pd_lock_msg_output(pd_port); /* SenderResponse */
+ pd_send_data_msg(pd_port,
+ TCPC_TX_SOP, PD_DATA_REQUEST, 1, &pd_port->last_rdo);
+ pd_free_pd_event(pd_port, pd_event);
+}
+
+void pe_snk_select_capability_exit(pd_port_t *pd_port, pd_event_t *pd_event)
+{
+ pd_disable_timer(pd_port, PD_TIMER_SENDER_RESPONSE);
+
+ if (pd_event_msg_match(pd_event,
+ PD_EVT_CTRL_MSG, PD_CTRL_ACCEPT))
+ pd_port->remote_selected_cap = RDO_POS(pd_port->last_rdo);
+
+ /* Waiting for Hard-Reset Done */
+ if (!pd_event_msg_match(pd_event,
+ PD_EVT_TIMER_MSG, PD_TIMER_SENDER_RESPONSE))
+ pd_unlock_msg_output(pd_port);
+}
+
+void pe_snk_transition_sink_entry(pd_port_t *pd_port, pd_event_t *pd_event)
+{
+ pd_enable_timer(pd_port, PD_TIMER_PS_TRANSITION);
+
+ if (pd_event->msg == PD_CTRL_GOTO_MIN) {
+ if (pd_port->dpm_caps & DPM_CAP_LOCAL_GIVE_BACK) {
+ pd_port->request_i_new = pd_port->request_i_op;
+ pd_dpm_snk_transition_power(pd_port, pd_event);
+ }
+ }
+
+ pd_free_pd_event(pd_port, pd_event);
+}
+
+void pe_snk_transition_sink_exit(pd_port_t *pd_port, pd_event_t *pd_event)
+{
+ if (pd_event_msg_match(pd_event, PD_EVT_CTRL_MSG, PD_CTRL_PS_RDY))
+ pd_dpm_snk_transition_power(pd_port, pd_event);
+
+ pd_disable_timer(pd_port, PD_TIMER_PS_TRANSITION);
+}
+
+void pe_snk_ready_entry(pd_port_t *pd_port, pd_event_t *pd_event)
+{
+ if (pd_event_msg_match(pd_event, PD_EVT_CTRL_MSG, PD_CTRL_WAIT))
+ pd_enable_timer(pd_port, PD_TIMER_SINK_REQUEST);
+
+ pd_port->state_machine = PE_STATE_MACHINE_SINK;
+ pe_power_ready_entry(pd_port, pd_event);
+}
+
+void pe_snk_hard_reset_entry(pd_port_t *pd_port, pd_event_t *pd_event)
+{
+ pd_send_hard_reset(pd_port);
+ pd_free_pd_event(pd_port, pd_event);
+}
+
+void pe_snk_transition_to_default_entry(
+ pd_port_t *pd_port, pd_event_t *pd_event)
+{
+ pd_reset_local_hw(pd_port);
+ pd_dpm_snk_hard_reset(pd_port, pd_event);
+}
+
+void pe_snk_transition_to_default_exit(
+ pd_port_t *pd_port, pd_event_t *pd_event)
+{
+ pd_enable_timer(pd_port, PD_TIMER_NO_RESPONSE);
+
+#ifdef CONFIG_USB_PD_FAST_RESP_TYPEC_SRC
+ if (!pd_port->pd_prev_connected)
+ pd_enable_timer(pd_port, PD_TIMER_SRC_RECOVER);
+#endif /* CONFIG_USB_PD_FAST_RESP_TYPEC_SRC */
+}
+
+void pe_snk_give_sink_cap_entry(
+ pd_port_t *pd_port, pd_event_t *pd_event)
+{
+ pd_dpm_send_sink_caps(pd_port);
+ pd_free_pd_event(pd_port, pd_event);
+}
+
+void pe_snk_get_source_cap_entry(
+ pd_port_t *pd_port, pd_event_t *pd_event)
+{
+ pd_send_ctrl_msg(pd_port, TCPC_TX_SOP, PD_CTRL_GET_SOURCE_CAP);
+}
+
+void pe_snk_send_soft_reset_entry(pd_port_t *pd_port, pd_event_t *pd_event)
+{
+ pd_send_soft_reset(pd_port, PE_STATE_MACHINE_SINK);
+ pd_free_pd_event(pd_port, pd_event);
+}
+
+void pe_snk_soft_reset_entry(pd_port_t *pd_port, pd_event_t *pd_event)
+{
+ pd_handle_soft_reset(pd_port, PE_STATE_MACHINE_SINK);
+ pd_free_pd_event(pd_port, pd_event);
+}