google/gru: Power-cycle USB ports in developer/recovery modes

Gru only uses USB 2.0 in firmware to avoid all the madness associated
with Type-C port orientation and USB 3.0 tuning. We do this by isolating
the SuperSpeed lines in the Type-C PHY so it looks like they aren't
connected to the device.

Unfortunately, some devices seem to already get "locked" into SuperSpeed
mode as soon as they detect Rx terminations once, and can never snap out
again on their own. Since the terminations are already connected during
power-on reset we cannot disable them fast enough to prevent this, and
the only solution we found to date is to power-cycle the whole USB port.

Now, Gru's USB port power is controlled by the EC, and unfortunately we
have no direct host command to control it. We do however have a command
to force a certain USB PD "role", and forcing our host into "sink" mode
makes it stop sourcing power to the port. So for lack of a saner
solution we'll use this to work around our problem.

BRANCH=gru
BUG=chrome-os-partner:59346
TEST=Booted Kevin in recovery mode, confirmed that my "problem stick"
gets detected immediately (whereas previously I had to unplug/replug
it). Booted Kevin to OS in both developer and normal mode and confirmed
that USB still seems to work.

Change-Id: Ib3cceba9baa170b13f01bd5c01bd413be5b441ba
Signed-off-by: Patrick Georgi <pgeorgi@chromium.org>
Original-Commit-Id: cd695eda33299e50362f1096c46f2f5260c49036
Original-Change-Id: I2db3d6d3710d18a8b8030e94eb1ac2e931f22638
Original-Signed-off-by: Julius Werner <jwerner@chromium.org>
Original-Reviewed-on: https://chromium-review.googlesource.com/413031
Reviewed-on: https://review.coreboot.org/17628
Tested-by: build bot (Jenkins)
Reviewed-by: Martin Roth <martinroth@google.com>
diff --git a/src/mainboard/google/gru/mainboard.c b/src/mainboard/google/gru/mainboard.c
index 7e360e7..dd986c4 100644
--- a/src/mainboard/google/gru/mainboard.c
+++ b/src/mainboard/google/gru/mainboard.c
@@ -15,9 +15,11 @@
  */
 
 #include <boardid.h>
+#include <console/console.h>
 #include <delay.h>
 #include <device/device.h>
 #include <device/i2c.h>
+#include <ec/google/chromeec/ec.h>
 #include <gpio.h>
 #include <soc/bl31_plat_params.h>
 #include <soc/clock.h>
@@ -230,6 +232,17 @@
 	gpio_output(GPIO(4, D, 3), 1); /* CPU3_EDP_VDDEN for P3.3V_DISP */
 }
 
+static void usb_power_cycle(int port)
+{
+	if (google_chromeec_set_usb_pd_role(port, USB_PD_CTRL_ROLE_FORCE_SINK))
+		printk(BIOS_ERR, "ERROR: Cannot force USB%d PD sink\n", port);
+
+	mdelay(10);	/* Make sure USB stick is fully depowered. */
+
+	if (google_chromeec_set_usb_pd_role(port, USB_PD_CTRL_ROLE_TOGGLE_ON))
+		printk(BIOS_ERR, "ERROR: Cannot restore USB%d PD mode\n", port);
+}
+
 static void setup_usb(void)
 {
 	/* A few magic PHY tuning values that improve eye diagram amplitude
@@ -269,6 +282,17 @@
 
 	setup_usb_otg0();
 	setup_usb_otg1();
+
+	/*
+	 * Need to power-cycle USB ports for use in firmware, since some devices
+	 * can't fall back to USB 2.0 after they saw SuperSpeed terminations.
+	 * This takes about a dozen milliseconds, so only do it in boot modes
+	 * that have firmware UI (which one could select USB boot from).
+	 */
+	if (display_init_required()) {
+		usb_power_cycle(0);
+		usb_power_cycle(1);
+	}
 }
 
 static void mainboard_init(device_t dev)