summaryrefslogtreecommitdiff
path: root/zephyr/projects/corsola/src/usb_pd_policy.c
diff options
context:
space:
mode:
Diffstat (limited to 'zephyr/projects/corsola/src/usb_pd_policy.c')
-rw-r--r--zephyr/projects/corsola/src/usb_pd_policy.c152
1 files changed, 152 insertions, 0 deletions
diff --git a/zephyr/projects/corsola/src/usb_pd_policy.c b/zephyr/projects/corsola/src/usb_pd_policy.c
new file mode 100644
index 0000000000..54322811a2
--- /dev/null
+++ b/zephyr/projects/corsola/src/usb_pd_policy.c
@@ -0,0 +1,152 @@
+/* Copyright 2021 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "atomic.h"
+#include "chipset.h"
+#include "timer.h"
+#include "usb_dp_alt_mode.h"
+#include "usb_mux.h"
+#include "usb_pd.h"
+#include "usbc_ppc.h"
+
+#if CONFIG_USB_PD_3A_PORTS != 1
+#error Corsola reference must have at least one 3.0 A port
+#endif
+
+#define CPRINTS(format, args...) cprints(CC_USBPD, format, ## args)
+#define CPRINTF(format, args...) cprintf(CC_USBPD, format, ## args)
+
+int svdm_get_hpd_gpio(int port)
+{
+ /* HPD is low active, inverse the result */
+ return !gpio_get_level(GPIO_EC_AP_DP_HPD_ODL);
+}
+
+void svdm_set_hpd_gpio(int port, int en)
+{
+ /*
+ * HPD is low active, inverse the en
+ * TODO: C0&C1 shares the same HPD, implement FCFS policy.
+ */
+ gpio_set_level(GPIO_EC_AP_DP_HPD_ODL, !en);
+}
+
+/**
+ * Is the port fine to be muxed its DisplayPort lines?
+ *
+ * Only one port can be muxed to DisplayPort at a time.
+ *
+ * @param port Port number of TCPC.
+ * @return 1 is fine; 0 is bad as other port is already muxed;
+ */
+static int is_dp_muxable(int port)
+{
+ int i;
+
+ for (i = 0; i < board_get_usb_pd_port_count(); i++) {
+ if (i != port) {
+ if (usb_mux_get(i) & USB_PD_MUX_DP_ENABLED)
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+__override int svdm_dp_attention(int port, uint32_t *payload)
+{
+ int lvl = PD_VDO_DPSTS_HPD_LVL(payload[1]);
+ int irq = PD_VDO_DPSTS_HPD_IRQ(payload[1]);
+#ifdef CONFIG_USB_PD_DP_HPD_GPIO
+ int cur_lvl = svdm_get_hpd_gpio(port);
+#endif /* CONFIG_USB_PD_DP_HPD_GPIO */
+ mux_state_t mux_state;
+
+ dp_status[port] = payload[1];
+
+ if (!is_dp_muxable(port)) {
+ /* TODO(waihong): Info user? */
+ CPRINTS("p%d: The other port is already muxed.", port);
+ return 0; /* nak */
+ }
+
+ if (lvl)
+ gpio_set_level_verbose(CC_USBPD, GPIO_DP_AUX_PATH_SEL, port);
+
+ if (chipset_in_state(CHIPSET_STATE_ANY_SUSPEND) &&
+ (irq || lvl))
+ /*
+ * Wake up the AP. IRQ or level high indicates a DP sink is now
+ * present.
+ */
+ if (IS_ENABLED(CONFIG_MKBP_EVENT))
+ pd_notify_dp_alt_mode_entry(port);
+
+ /* Its initial DP status message prior to config */
+ if (!(dp_flags[port] & DP_FLAGS_DP_ON)) {
+ if (lvl)
+ dp_flags[port] |= DP_FLAGS_HPD_HI_PENDING;
+ return 1;
+ }
+
+#ifdef CONFIG_USB_PD_DP_HPD_GPIO
+ if (irq && !lvl) {
+ /*
+ * IRQ can only be generated when the level is high, because
+ * the IRQ is signaled by a short low pulse from the high level.
+ */
+ CPRINTF("ERR:HPD:IRQ&LOW\n");
+ return 0; /* nak */
+ }
+
+ if (irq && cur_lvl) {
+ uint64_t now = get_time().val;
+ /* wait for the minimum spacing between IRQ_HPD if needed */
+ if (now < svdm_hpd_deadline[port])
+ usleep(svdm_hpd_deadline[port] - now);
+
+ /* generate IRQ_HPD pulse */
+ svdm_set_hpd_gpio(port, 0);
+ /*
+ * b/171172053#comment14: since the HPD_DSTREAM_DEBOUNCE_IRQ is
+ * very short (500us), we can use udelay instead of usleep for
+ * more stable pulse period.
+ */
+ udelay(HPD_DSTREAM_DEBOUNCE_IRQ);
+ svdm_set_hpd_gpio(port, 1);
+ } else {
+ svdm_set_hpd_gpio(port, lvl);
+ }
+
+ /* set the minimum time delay (2ms) for the next HPD IRQ */
+ svdm_hpd_deadline[port] = get_time().val + HPD_USTREAM_DEBOUNCE_LVL;
+#endif /* CONFIG_USB_PD_DP_HPD_GPIO */
+
+ mux_state = (lvl ? USB_PD_MUX_HPD_LVL : USB_PD_MUX_HPD_LVL_DEASSERTED) |
+ (irq ? USB_PD_MUX_HPD_IRQ : USB_PD_MUX_HPD_IRQ_DEASSERTED);
+ usb_mux_hpd_update(port, mux_state);
+
+#ifdef USB_PD_PORT_TCPC_MST
+ if (port == USB_PD_PORT_TCPC_MST)
+ baseboard_mst_enable_control(port, lvl);
+#endif
+
+ /* ack */
+ return 1;
+}
+
+__override void svdm_exit_dp_mode(int port)
+{
+#ifdef CONFIG_USB_PD_DP_HPD_GPIO
+ svdm_set_hpd_gpio(port, 0);
+#endif /* CONFIG_USB_PD_DP_HPD_GPIO */
+ usb_mux_hpd_update(port, USB_PD_MUX_HPD_LVL_DEASSERTED |
+ USB_PD_MUX_HPD_IRQ_DEASSERTED);
+
+#ifdef USB_PD_PORT_TCPC_MST
+ if (port == USB_PD_PORT_TCPC_MST)
+ baseboard_mst_enable_control(port, 0);
+#endif
+}