rockchip: rk3399-puma: implement usb_hub_reset_devices for puma-rk3399
authorPhilipp Tomsich <philipp.tomsich@theobroma-systems.com>
Wed, 22 Nov 2017 16:15:18 +0000 (17:15 +0100)
committerPhilipp Tomsich <philipp.tomsich@theobroma-systems.com>
Thu, 30 Nov 2017 21:55:27 +0000 (22:55 +0100)
For some versions of the RK3399-Q7 (at least revisions v1.1 and v1.2
are affected), we need to turn on the power for the port connected to
the on-module USB hub only when the device is probed for the first
time to ensure that the hub does not enter a low-power mode (that
U-Boot's USB stack can't deal with).

Note that this is needed for U-Boot only, as Linux eventually manages
to attach the hub even when it has entered into its low-power state
(when the hub wakes up the next time) after a few seconds.

Signed-off-by: Philipp Tomsich <philipp.tomsich@theobroma-systems.com>
Tested-by: Jakob Unterwurzacher <jakob.unterwurzacher@theobroma-systems.com>
board/theobroma-systems/puma_rk3399/puma-rk3399.c

index 2b4988e2d2255a306f4aa213c5cf450a65fca0f7..7f2dd65d446ac75ce39a5105733106f285a66651 100644 (file)
@@ -7,12 +7,13 @@
 #include <common.h>
 #include <dm.h>
 #include <misc.h>
+#include <spl.h>
+#include <usb.h>
 #include <dm/pinctrl.h>
 #include <dm/uclass-internal.h>
 #include <asm/setup.h>
 #include <asm/arch/periph.h>
 #include <power/regulator.h>
-#include <spl.h>
 #include <u-boot/sha256.h>
 
 DECLARE_GLOBAL_DATA_PTR;
@@ -158,3 +159,70 @@ void get_board_serial(struct tag_serialnr *serialnr)
        serialnr->low = (u32)(serial & 0xffffffff);
 }
 #endif
+
+/**
+ * Switch power at an external regulator (for our root hub).
+ *
+ * @param ctrl pointer to the xHCI controller
+ * @param port port number as in the control message (one-based)
+ * @param enable boolean indicating whether to enable or disable power
+ * @return returns 0 on success, an error-code on failure
+ */
+static int board_usb_port_power_set(struct udevice *dev, int port,
+                                   bool enable)
+{
+#if CONFIG_IS_ENABLED(OF_CONTROL) && CONFIG_IS_ENABLED(DM_REGULATOR)
+       /* We start counting ports at 0, while USB counts from 1. */
+       int index = port - 1;
+       const char *regname = NULL;
+       struct udevice *regulator;
+       const char *prop = "tsd,usb-port-power";
+       int ret;
+
+       debug("%s: ctrl '%s' port %d enable %s\n", __func__,
+             dev_read_name(dev), port, enable ? "true" : "false");
+
+       ret = dev_read_string_index(dev, prop, index, &regname);
+       if (ret < 0) {
+               debug("%s: ctrl '%s' port %d: no entry in '%s'\n",
+                     __func__, dev_read_name(dev), port, prop);
+               return ret;
+       }
+
+       ret = regulator_get_by_platname(regname, &regulator);
+       if (ret) {
+               debug("%s: ctrl '%s' port %d: could not get regulator '%s'\n",
+                     __func__, dev_read_name(dev), port, regname);
+               return ret;
+       }
+
+       regulator_set_enable(regulator, enable);
+       return 0;
+#else
+       return -ENOTSUPP;
+#endif
+}
+
+void usb_hub_reset_devices(struct usb_hub_device *hub, int port)
+{
+       struct udevice *dev = hub->pusb_dev->dev;
+       struct udevice *ctrl;
+
+       /* We are only interested in our root-hubs */
+       if (usb_hub_is_root_hub(dev) == false)
+               return;
+
+       ctrl = usb_get_bus(dev);
+       if (!ctrl) {
+               debug("%s: could not retrieve ctrl for hub\n", __func__);
+               return;
+       }
+
+       /*
+        * To work around an incompatibility between the single-threaded
+        * USB stack in U-Boot and (a strange low-power mode of) the USB
+        * hub we have on-module, we need to delay powering on the hub
+        * until the first time the port is probed.
+        */
+       board_usb_port_power_set(ctrl, port, true);
+}