[astro][usb] USB PHY tuning hack

Change-Id: Ib0a02b6b136e260a034b93935779031ae9f997f2
diff --git a/system/dev/board/astro/astro-usb.c b/system/dev/board/astro/astro-usb.c
index 1d4ca42..4af8bb4 100644
--- a/system/dev/board/astro/astro-usb.c
+++ b/system/dev/board/astro/astro-usb.c
@@ -3,6 +3,7 @@
 // found in the LICENSE file.
 
 #include <ddk/debug.h>
+#include <ddk/protocol/astro-usb.h>
 #include <ddk/protocol/platform-defs.h>
 #include <hw/reg.h>
 #include <soc/aml-common/aml-usb-phy-v2.h>
@@ -51,19 +52,18 @@
 #define PLL_SETTING_6   0xe0004
 #define PLL_SETTING_7   0xe000c
 
-static zx_status_t astro_usb_tuning(zx_handle_t bti, bool host, bool default_val) {
-    io_buffer_t buf;
-    zx_status_t status;
+static zx_status_t astro_do_usb_tuning(void* ctx, bool set_default) {
+    aml_bus_t* bus = ctx;
+    volatile void* base = io_buffer_virt(&bus->usb_tuning_buf);
+    const bool host = true;
 
-    status = io_buffer_init_physical(&buf, bti, S905D2_USBPHY21_BASE, S905D2_USBPHY21_LENGTH,
-                                     get_root_resource(), ZX_CACHE_POLICY_UNCACHED_DEVICE);
-    if (status != ZX_OK) {
-        return status;
+    zxlogf(INFO, "astro_do_usb_tuning set_default: %s\n", (set_default ? "true" : "false"));
+
+    if (bus->cur_usb_tuning == !!set_default) {
+        return ZX_OK;
     }
 
-    volatile void* base = io_buffer_virt(&buf);
-
-    if (default_val) {
+    if (set_default) {
         writel(0, base + 0x38);
         writel(PLL_SETTING_5, base + 0x34);
     } else {
@@ -77,10 +77,14 @@
         writel(PLL_SETTING_5, base + 0x34);
     }
 
-    io_buffer_release(&buf);
+    bus->cur_usb_tuning = !!set_default;
     return ZX_OK;
 }
 
+astro_usb_protocol_ops_t astro_usb_ops = {
+    .do_usb_tuning = astro_do_usb_tuning,
+};
+
 zx_status_t aml_usb_init(aml_bus_t* bus) {
     zx_handle_t bti;
 
@@ -95,11 +99,20 @@
         return status;
     }
 
-    status = astro_usb_tuning(bti, true, false);
+    status = io_buffer_init_physical(&bus->usb_tuning_buf, bti, S905D2_USBPHY21_BASE,
+                                     S905D2_USBPHY21_LENGTH, get_root_resource(),
+                                     ZX_CACHE_POLICY_UNCACHED_DEVICE);
     zx_handle_close(bti);
     if (status != ZX_OK) {
         return status;
     }
+    bus->cur_usb_tuning = -1;
+
+    astro_usb_protocol_t astro_usb = {
+        .ops = &astro_usb_ops,
+        .ctx = bus,
+    };
+    pbus_set_protocol(&bus->pbus, ZX_PROTOCOL_ASTRO_USB, &astro_usb);
 
     return pbus_device_add(&bus->pbus, &xhci_dev, 0);
 }
diff --git a/system/dev/board/astro/astro.h b/system/dev/board/astro/astro.h
index 1cadfa8..82244d7 100644
--- a/system/dev/board/astro/astro.h
+++ b/system/dev/board/astro/astro.h
@@ -30,6 +30,8 @@
     serial_impl_protocol_t serial;
     zx_device_t* parent;
     iommu_protocol_t iommu;
+    io_buffer_t usb_tuning_buf;
+    int cur_usb_tuning;
 } aml_bus_t;
 
 // astro-gpio.c
diff --git a/system/dev/bus/platform/platform-bus.c b/system/dev/bus/platform/platform-bus.c
index 2a03115..794df85 100644
--- a/system/dev/bus/platform/platform-bus.c
+++ b/system/dev/bus/platform/platform-bus.c
@@ -65,6 +65,9 @@
     case ZX_PROTOCOL_SCPI:
         memcpy(&bus->scpi, protocol, sizeof(bus->scpi));
         break;
+    case ZX_PROTOCOL_ASTRO_USB:
+        memcpy(&bus->astro_usb, protocol, sizeof(bus->astro_usb));
+        break;
     default:
         // TODO(voydanoff) consider having a registry of arbitrary protocols
         return ZX_ERR_NOT_SUPPORTED;
diff --git a/system/dev/bus/platform/platform-bus.h b/system/dev/bus/platform/platform-bus.h
index a99fd74..4fa294b 100644
--- a/system/dev/bus/platform/platform-bus.h
+++ b/system/dev/bus/platform/platform-bus.h
@@ -7,6 +7,7 @@
 #include <stdint.h>
 #include <threads.h>
 #include <ddk/device.h>
+#include <ddk/protocol/astro-usb.h>
 #include <ddk/protocol/clk.h>
 #include <ddk/protocol/gpio.h>
 #include <ddk/protocol/i2c.h>
@@ -35,6 +36,7 @@
     i2c_impl_protocol_t i2c;
     clk_protocol_t clk;
     iommu_protocol_t iommu;
+    astro_usb_protocol_t astro_usb;
     zx_handle_t resource;   // root resource for platform bus
     zbi_platform_id_t platform_id;
     uint8_t* metadata;   // metadata extracted from ZBI
diff --git a/system/dev/bus/platform/platform-device.c b/system/dev/bus/platform/platform-device.c
index c67c2dc..5b4c31a 100644
--- a/system/dev/bus/platform/platform-device.c
+++ b/system/dev/bus/platform/platform-device.c
@@ -343,6 +343,14 @@
     return scpi_set_dvfs_idx(&bus->scpi, power_domain, idx);
 }
 
+static zx_status_t pdev_rpc_astro_do_usb_tuning(platform_dev_t* dev, bool set_default) {
+    platform_bus_t* bus = dev->bus;
+    if (!bus->astro_usb.ops) {
+        return ZX_ERR_NOT_SUPPORTED;
+    }
+    return astro_usb_do_usb_tuning(&bus->astro_usb, set_default);
+}
+
 static zx_status_t pdev_rpc_i2c_transact(platform_dev_t* dev, pdev_req_t* req, uint8_t* data,
                                         zx_handle_t channel) {
     platform_bus_t* bus = dev->bus;
@@ -478,6 +486,10 @@
         resp.status = pdev_rpc_scpi_set_dvfs_idx(dev, req->scpi.power_domain,
                                                  req->scpi.idx);
         break;
+
+    case PDEV_ASTRO_USB_TUNING:
+        resp.status = pdev_rpc_astro_do_usb_tuning(dev, req->index);
+        break;
     case PDEV_I2C_GET_MAX_TRANSFER:
         resp.status = i2c_impl_get_max_transfer_size(&dev->bus->i2c, req->index,
                                                      &resp.i2c_max_transfer);
diff --git a/system/dev/bus/platform/platform-proxy.c b/system/dev/bus/platform/platform-proxy.c
index 9e10162..57819bf 100644
--- a/system/dev/bus/platform/platform-proxy.c
+++ b/system/dev/bus/platform/platform-proxy.c
@@ -16,6 +16,7 @@
 #include <ddk/protocol/platform-device.h>
 #include <ddk/protocol/clk.h>
 #include <ddk/protocol/usb-mode-switch.h>
+#include <ddk/protocol/astro-usb.h>
 
 #include "platform-proxy.h"
 
@@ -304,6 +305,22 @@
     .set_dvfs_idx       = pdev_scpi_set_dvfs_idx,
 };
 
+static zx_status_t pdev_astro_do_usb_tuning(void* ctx, bool set_default) {
+    platform_proxy_t* proxy = ctx;
+    pdev_req_t req = {
+        .op = PDEV_ASTRO_USB_TUNING,
+        .index = set_default,
+    };
+
+    pdev_resp_t resp;
+    return platform_dev_rpc(proxy, &req, sizeof(req), &resp, sizeof(resp),
+                                           NULL, 0, NULL, 0, NULL);
+}
+
+static astro_usb_protocol_ops_t astro_usb_ops = {
+    .do_usb_tuning = pdev_astro_do_usb_tuning,
+};
+
 static zx_status_t pdev_mailbox_send_cmd(void* ctx, mailbox_channel_t* channel,
                                          mailbox_data_buf_t* mdata) {
     platform_proxy_t* proxy = ctx;
@@ -618,6 +635,12 @@
         proto->ops = &scpi_ops;
         return ZX_OK;
     }
+    case ZX_PROTOCOL_ASTRO_USB: {
+        astro_usb_protocol_t* proto = out;
+        proto->ctx = ctx;
+        proto->ops = &astro_usb_ops;
+        return ZX_OK;
+    }
     default:
         return ZX_ERR_NOT_SUPPORTED;
     }
diff --git a/system/dev/bus/platform/platform-proxy.h b/system/dev/bus/platform/platform-proxy.h
index fbe39f9..d46bf89 100644
--- a/system/dev/bus/platform/platform-proxy.h
+++ b/system/dev/bus/platform/platform-proxy.h
@@ -53,6 +53,9 @@
     PDEV_SCPI_GET_DVFS_INFO,
     PDEV_SCPI_GET_DVFS_IDX,
     PDEV_SCPI_SET_DVFS_IDX,
+
+    // ZX_PROTOCOL_ASTRO_USB
+    PDEV_ASTRO_USB_TUNING,
 };
 
 // context for mailbox
diff --git a/system/dev/usb/xhci/usb-xhci.c b/system/dev/usb/xhci/usb-xhci.c
index 7a9b4eb..9c1c532 100644
--- a/system/dev/usb/xhci/usb-xhci.c
+++ b/system/dev/usb/xhci/usb-xhci.c
@@ -437,6 +437,9 @@
         goto error_return;
     }
 
+    // hack for astro USB tuning
+    device_get_protocol(parent, ZX_PROTOCOL_ASTRO_USB, &xhci->astro_usb);
+
     status = pdev_get_bti(pdev, 0, &xhci->bti_handle);
     if (status != ZX_OK) {
         goto error_return;
diff --git a/system/dev/usb/xhci/xhci-root-hub.c b/system/dev/usb/xhci/xhci-root-hub.c
index a9a4d44..8eaaff8 100644
--- a/system/dev/usb/xhci/xhci-root-hub.c
+++ b/system/dev/usb/xhci/xhci-root-hub.c
@@ -401,6 +401,11 @@
                 usb_request_complete(req, ZX_OK, 0);
                 return ZX_OK;
             } else if (value == USB_FEATURE_PORT_RESET) {
+                // hack for astro USB tuning
+                if (xhci->astro_usb.ops) {
+                    astro_usb_do_usb_tuning(&xhci->astro_usb, true);
+                }
+
                 xhci_reset_port(xhci, rh, rh_port_index);
                 usb_request_complete(req, ZX_OK, 0);
                 return ZX_OK;
@@ -422,6 +427,10 @@
                     *change_bits &= ~USB_C_PORT_OVER_CURRENT;
                     break;
                 case USB_FEATURE_C_PORT_RESET:
+                    // hack for astro USB tuning
+                    if (xhci->astro_usb.ops) {
+                        astro_usb_do_usb_tuning(&xhci->astro_usb, false);
+                    }
                     *change_bits &= ~USB_C_PORT_RESET;
                     break;
             }
diff --git a/system/dev/usb/xhci/xhci.h b/system/dev/usb/xhci/xhci.h
index f12ffd0..bebba7e 100644
--- a/system/dev/usb/xhci/xhci.h
+++ b/system/dev/usb/xhci/xhci.h
@@ -14,6 +14,7 @@
 #include <threads.h>
 
 #include <ddk/device.h>
+#include <ddk/protocol/astro-usb.h>
 #include <ddk/protocol/pci.h>
 #include <ddk/protocol/platform-device.h>
 #include <ddk/protocol/usb-bus.h>
@@ -124,6 +125,9 @@
     // platform device support
     platform_device_protocol_t* pdev;
 
+    // hack for astro USB tuning
+    astro_usb_protocol_t astro_usb;
+
     // MMIO data structures
     xhci_cap_regs_t* cap_regs;
     xhci_op_regs_t* op_regs;
diff --git a/system/ulib/ddk/include/ddk/protocol/astro-usb.h b/system/ulib/ddk/include/ddk/protocol/astro-usb.h
new file mode 100644
index 0000000..31e8b37
--- /dev/null
+++ b/system/ulib/ddk/include/ddk/protocol/astro-usb.h
@@ -0,0 +1,25 @@
+// Copyright 2018 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#pragma once
+
+#include <zircon/compiler.h>
+#include <zircon/types.h>
+
+__BEGIN_CDECLS;
+
+typedef struct {
+    zx_status_t (*do_usb_tuning)(void* ctx, bool set_default);
+} astro_usb_protocol_ops_t;
+
+typedef struct {
+    astro_usb_protocol_ops_t* ops;
+    void* ctx;
+} astro_usb_protocol_t;
+
+static inline zx_status_t astro_usb_do_usb_tuning(astro_usb_protocol_t* usb, bool set_default) {
+    return usb->ops->do_usb_tuning(usb->ctx, set_default);
+}
+
+__END_CDECLS;
diff --git a/system/ulib/ddk/include/ddk/protodefs.h b/system/ulib/ddk/include/ddk/protodefs.h
index 2fd3253..6318b35 100644
--- a/system/ulib/ddk/include/ddk/protodefs.h
+++ b/system/ulib/ddk/include/ddk/protodefs.h
@@ -82,6 +82,8 @@
 DDK_PROTOCOL_DEF(MAILBOX,        'pMHU', "mailbox", 0)
 DDK_PROTOCOL_DEF(SCPI,           'pSCP', "scpi", 0)
 DDK_PROTOCOL_DEF(BACKLIGHT,      'pBKL', "backlight", 0)
+DDK_PROTOCOL_DEF(ASTRO_USB,      'pAUX', "astro-usb", PF_NOPUB)
+
 // Protocol definition at garnet/magma/src/magma_util/platform/zircon/zircon_platform_ioctl.h
 DDK_PROTOCOL_DEF(GPU,            'pGPU', "gpu", 0)
 DDK_PROTOCOL_DEF(RTC,            'pRTC', "rtc", 0)