aboutsummaryrefslogtreecommitdiff
path: root/src/vendorcode/cavium/bdk/libbdk-hal/bdk-usb.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/vendorcode/cavium/bdk/libbdk-hal/bdk-usb.c')
-rw-r--r--src/vendorcode/cavium/bdk/libbdk-hal/bdk-usb.c683
1 files changed, 683 insertions, 0 deletions
diff --git a/src/vendorcode/cavium/bdk/libbdk-hal/bdk-usb.c b/src/vendorcode/cavium/bdk/libbdk-hal/bdk-usb.c
new file mode 100644
index 0000000000..eb2a85fa0d
--- /dev/null
+++ b/src/vendorcode/cavium/bdk/libbdk-hal/bdk-usb.c
@@ -0,0 +1,683 @@
+/***********************license start***********************************
+* Copyright (c) 2003-2017 Cavium Inc. (support@cavium.com). All rights
+* reserved.
+*
+*
+* Redistribution and use in source and binary forms, with or without
+* modification, are permitted provided that the following conditions are
+* met:
+*
+* * Redistributions of source code must retain the above copyright
+* notice, this list of conditions and the following disclaimer.
+*
+* * Redistributions in binary form must reproduce the above
+* copyright notice, this list of conditions and the following
+* disclaimer in the documentation and/or other materials provided
+* with the distribution.
+*
+* * Neither the name of Cavium Inc. nor the names of
+* its contributors may be used to endorse or promote products
+* derived from this software without specific prior written
+* permission.
+*
+* This Software, including technical data, may be subject to U.S. export
+* control laws, including the U.S. Export Administration Act and its
+* associated regulations, and may be subject to export or import
+* regulations in other countries.
+*
+* TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SOFTWARE IS PROVIDED "AS IS"
+* AND WITH ALL FAULTS AND CAVIUM INC. MAKES NO PROMISES, REPRESENTATIONS OR
+* WARRANTIES, EITHER EXPRESS, IMPLIED, STATUTORY, OR OTHERWISE, WITH RESPECT
+* TO THE SOFTWARE, INCLUDING ITS CONDITION, ITS CONFORMITY TO ANY
+* REPRESENTATION OR DESCRIPTION, OR THE EXISTENCE OF ANY LATENT OR PATENT
+* DEFECTS, AND CAVIUM SPECIFICALLY DISCLAIMS ALL IMPLIED (IF ANY) WARRANTIES
+* OF TITLE, MERCHANTABILITY, NONINFRINGEMENT, FITNESS FOR A PARTICULAR
+* PURPOSE, LACK OF VIRUSES, ACCURACY OR COMPLETENESS, QUIET ENJOYMENT,
+* QUIET POSSESSION OR CORRESPONDENCE TO DESCRIPTION. THE ENTIRE RISK
+* ARISING OUT OF USE OR PERFORMANCE OF THE SOFTWARE LIES WITH YOU.
+***********************license end**************************************/
+#include <bdk.h>
+#include <libbdk-arch/bdk-csrs-gpio.h>
+#include <libbdk-arch/bdk-csrs-usbdrd.h>
+#include <libbdk-arch/bdk-csrs-usbh.h>
+#include <libbdk-hal/bdk-usb.h>
+#include <libbdk-hal/bdk-config.h>
+
+/* This code is an optional part of the BDK. It is only linked in
+ if BDK_REQUIRE() needs it */
+BDK_REQUIRE_DEFINE(USB);
+
+/**
+ * Write to DWC3 indirect debug control register
+ *
+ * @param node Node to write to
+ * @param usb_port USB port to write to
+ * @param val 32bit value to write
+ */
+static void write_cr_dbg_cfg(bdk_node_t node, int usb_port, uint64_t val)
+{
+ if (!CAVIUM_IS_MODEL(CAVIUM_CN88XX))
+ BDK_CSR_WRITE(node, BDK_USBDRDX_UCTL_PORTX_CR_DBG_CFG(usb_port, 0), val);
+ else
+ BDK_CSR_WRITE(node, BDK_USBHX_UCTL_PORTX_CR_DBG_CFG(usb_port, 0), val);
+}
+
+/**
+ * Poll the DWC3 internal status until the ACK bit matches a desired value. Return
+ * the final status.
+ *
+ * @param node Node to query
+ * @param usb_port USB port to query
+ * @param desired_ack
+ * Desired ACK bit state
+ *
+ * @return Final status with ACK at correct state
+ */
+static bdk_usbdrdx_uctl_portx_cr_dbg_status_t get_cr_dbg_status(bdk_node_t node, int usb_port, int desired_ack)
+{
+ const int TIMEOUT = 1000000; /* 1 sec */
+ bdk_usbdrdx_uctl_portx_cr_dbg_status_t status;
+ if (!CAVIUM_IS_MODEL(CAVIUM_CN88XX))
+ {
+ if (BDK_CSR_WAIT_FOR_FIELD(node, BDK_USBDRDX_UCTL_PORTX_CR_DBG_STATUS(usb_port, 0), ack, ==, desired_ack, TIMEOUT))
+ {
+ BDK_TRACE(USB_XHCI, "N%d.USB%d: Timeout waiting for indirect ACK\n", node, usb_port);
+ status.u = -1;
+ }
+ else
+ status.u = BDK_CSR_READ(node, BDK_USBDRDX_UCTL_PORTX_CR_DBG_STATUS(usb_port, 0));
+ }
+ else
+ {
+ if (BDK_CSR_WAIT_FOR_FIELD(node, BDK_USBHX_UCTL_PORTX_CR_DBG_STATUS(usb_port, 0), ack, ==, desired_ack, TIMEOUT))
+ {
+ BDK_TRACE(USB_XHCI, "N%d.USB%d: Timeout waiting for indirect ACK\n", node, usb_port);
+ status.u = -1;
+ }
+ else
+ status.u = BDK_CSR_READ(node, BDK_USBHX_UCTL_PORTX_CR_DBG_STATUS(usb_port, 0));
+ }
+ return status;
+}
+
+/**
+ * Perform an indirect read of an internal register inside the DWC3 usb block
+ *
+ * @param node Node to read
+ * @param usb_port USB port to read
+ * @param addr Indirect register address
+ *
+ * @return Value of the indirect register
+ */
+static uint32_t dwc3_uphy_indirect_read(bdk_node_t node, int usb_port, uint32_t addr)
+{
+ bdk_usbdrdx_uctl_portx_cr_dbg_cfg_t dbg_cfg;
+ bdk_usbdrdx_uctl_portx_cr_dbg_status_t status;
+
+ /* See the CSR description for USBHX_UCTL_PORTX_CR_DBG_CFG, which describes
+ the steps implemented by this function */
+
+ dbg_cfg.u = 0;
+ dbg_cfg.s.data_in = addr;
+ write_cr_dbg_cfg(node, usb_port, dbg_cfg.u);
+
+ dbg_cfg.s.cap_addr = 1;
+ write_cr_dbg_cfg(node, usb_port, dbg_cfg.u);
+
+ status = get_cr_dbg_status(node, usb_port, 1);
+ if (status.u == (uint64_t)-1)
+ return 0xffffffff;
+
+ write_cr_dbg_cfg(node, usb_port, 0);
+ get_cr_dbg_status(node, usb_port, 0);
+
+ dbg_cfg.u = 0;
+ dbg_cfg.s.read = 1;
+ write_cr_dbg_cfg(node, usb_port, dbg_cfg.u);
+
+ status = get_cr_dbg_status(node, usb_port, 1);
+
+ write_cr_dbg_cfg(node, usb_port, 0);
+ get_cr_dbg_status(node, usb_port, 0);
+
+ return status.s.data_out;
+}
+
+/**
+ * Perform an indirect write of an internal register inside the DWC3 usb block
+ *
+ * @param node Node to write
+ * @param usb_port USB port to write
+ * @param addr Indirect register address
+ * @param value Value for write
+ */
+static void dwc3_uphy_indirect_write(bdk_node_t node, int usb_port, uint32_t addr, uint16_t value)
+{
+ bdk_usbdrdx_uctl_portx_cr_dbg_cfg_t dbg_cfg;
+
+ /* See the CSR description for USBHX_UCTL_PORTX_CR_DBG_CFG, which describes
+ the steps implemented by this function */
+
+ dbg_cfg.u = 0;
+ dbg_cfg.s.data_in = addr;
+ write_cr_dbg_cfg(node, usb_port, dbg_cfg.u);
+
+ dbg_cfg.s.cap_addr = 1;
+ write_cr_dbg_cfg(node, usb_port, dbg_cfg.u);
+
+ get_cr_dbg_status(node, usb_port, 1);
+
+ write_cr_dbg_cfg(node, usb_port, 0);
+ get_cr_dbg_status(node, usb_port, 0);
+
+ dbg_cfg.u = 0;
+ dbg_cfg.s.data_in = value;
+ write_cr_dbg_cfg(node, usb_port, dbg_cfg.u);
+
+ dbg_cfg.s.cap_data = 1;
+ write_cr_dbg_cfg(node, usb_port, dbg_cfg.u);
+
+ get_cr_dbg_status(node, usb_port, 1);
+
+ write_cr_dbg_cfg(node, usb_port, 0);
+ get_cr_dbg_status(node, usb_port, 0);
+
+ dbg_cfg.u = 0;
+ dbg_cfg.s.write = 1;
+ write_cr_dbg_cfg(node, usb_port, dbg_cfg.u);
+
+ get_cr_dbg_status(node, usb_port, 1);
+
+ write_cr_dbg_cfg(node, usb_port, 0);
+ get_cr_dbg_status(node, usb_port, 0);
+}
+
+/**
+ * Errata USB-29206 - The USB HS PLL in all 28nm devices has a
+ * design issue that may cause the VCO to lock up on
+ * initialization. The Synopsys VCO is designed with an even
+ * number of stages and no kick-start circuit, which makes us
+ * believe that there is no question a latched up
+ * (non-oscillating) state is possible. The workaround is to
+ * check the PLL lock bit, which is just based on a counter and
+ * will not set if the VCO is not oscillating, and if it's not
+ * set do a power down/power up cycle on the PLL, which tests
+ * have proven is much more likely to guarantee the VCO will
+ * start oscillating. Part of the problem appears to be that
+ * the normal init sequence holds the VCO in reset during the
+ * power up sequence, whereas the plain power up/down sequence
+ * does not, so the voltage changing may be helping the circuit
+ * to oscillate.
+ *
+ * @param node Node to check
+ * @param usb_port USB port to check
+ *
+ * @return Zero on success, negative on failure
+ */
+static int dwc3_uphy_check_pll(bdk_node_t node, int usb_port)
+{
+ /* Internal indirect register that reports if the phy PLL has lock. This will
+ be 1 if lock, 0 if no lock */
+ const int DWC3_INT_IND_PLL_LOCK_REG = 0x200b;
+ /* Internal indirect UPHY register that controls the power to the UPHY PLL */
+ const int DWC3_INT_IND_UPHY_PLL_PU = 0x2012;
+ /* Write enable bit for DWC3_INT_IND_PLL_POWER_CTL */
+ const int DWC3_INT_IND_UPHY_PLL_PU_WE = 0x20;
+ /* Power enable bit for DWC3_INT_IND_PLL_POWER_CTL */
+ const int DWC3_INT_IND_UPHY_PLL_PU_POWER_EN = 0x02;
+
+ uint32_t pll_locked = dwc3_uphy_indirect_read(node, usb_port, DWC3_INT_IND_PLL_LOCK_REG);
+ int retry_count = 0;
+ while (!pll_locked)
+ {
+ if (retry_count >= 3)
+ {
+ bdk_error("N%d.USB%d: USB2 PLL failed to lock\n", node, usb_port);
+ return -1;
+ }
+
+ retry_count++;
+ BDK_TRACE(USB_XHCI, "N%d.USB%d: USB2 PLL didn't lock, retry %d\n", node, usb_port, retry_count);
+
+ /* Turn on write enable for PLL power control */
+ uint32_t pwr_val = dwc3_uphy_indirect_read(node, usb_port, DWC3_INT_IND_UPHY_PLL_PU);
+ pwr_val |= DWC3_INT_IND_UPHY_PLL_PU_WE;
+ dwc3_uphy_indirect_write(node, usb_port, DWC3_INT_IND_UPHY_PLL_PU, pwr_val);
+
+ /* Power down the PLL */
+ pwr_val &= ~DWC3_INT_IND_UPHY_PLL_PU_POWER_EN;
+ dwc3_uphy_indirect_write(node, usb_port, DWC3_INT_IND_UPHY_PLL_PU, pwr_val);
+ bdk_wait_usec(1000);
+
+ /* Power on the PLL */
+ pwr_val |= DWC3_INT_IND_UPHY_PLL_PU_POWER_EN;
+ dwc3_uphy_indirect_write(node, usb_port, DWC3_INT_IND_UPHY_PLL_PU, pwr_val);
+ bdk_wait_usec(1000);
+
+ /* Check for PLL Lock again */
+ pll_locked = dwc3_uphy_indirect_read(node, usb_port, DWC3_INT_IND_PLL_LOCK_REG);
+ }
+ return 0;
+}
+
+/**
+ * Initialize the clocks for USB such that it is ready for a generic XHCI driver
+ *
+ * @param node Node to init
+ * @param usb_port Port to intialize
+ * @param clock_type Type of clock connected to the usb port
+ *
+ * @return Zero on success, negative on failure
+ */
+
+int bdk_usb_initialize(bdk_node_t node, int usb_port, bdk_usb_clock_t clock_type)
+{
+ int is_usbdrd = !CAVIUM_IS_MODEL(CAVIUM_CN88XX);
+
+ /* Perform the following steps to initiate a cold reset. */
+
+ /* 1. Wait for all voltages to reach a stable state. Ensure the
+ reference clock is up and stable.
+ a. If 3.3V is up first, 0.85V must be soon after (within tens of ms). */
+
+ /* 2. Wait for IOI reset to deassert. */
+
+ /* 3. If Over Current indication and/or Port Power Control features
+ are desired, program the GPIO CSRs appropriately.
+ a. For Over Current Indication, select a GPIO for the input and
+ program GPIO_USBH_CTL[SEL].
+ b. For Port Power Control, set one of
+ GPIO_BIT_CFG(0..19)[OUTPUT_SEL] = USBH_VBUS_CTRL. */
+
+ /* 4. Assert all resets:
+ a. UPHY reset: USBDRD(0..1)_UCTL_CTL[UPHY_RST] = 1
+ b. UAHC reset: USBDRD(0..1)_UCTL_CTL[UAHC_RST] = 1
+ c. UCTL reset: USBDRD(0..1)_UCTL_CTL[UCTL_RST] = 1 */
+ if (is_usbdrd)
+ {
+ BDK_CSR_MODIFY(c, node, BDK_USBDRDX_UCTL_CTL(usb_port),
+ c.s.uphy_rst = 1;
+ c.s.uahc_rst = 1;
+ c.s.uctl_rst = 1);
+ }
+ else
+ {
+ BDK_CSR_MODIFY(c, node, BDK_USBHX_UCTL_CTL(usb_port),
+ c.s.uphy_rst = 1;
+ c.s.uahc_rst = 1;
+ c.s.uctl_rst = 1);
+ }
+
+ /* 5. Configure the controller clock:
+ a. Reset the clock dividers: USBDRD(0..1)_UCTL_CTL[H_CLKDIV_RST] = 1.
+ b. Select the controller clock frequency
+ USBDRD(0..1)_UCTL_CTL[H_CLKDIV] = desired value.
+ USBDRD(0..1)_UCTL_CTL[H_CLKDIV_EN] = 1 to enable the controller
+ clock.
+ Read USBDRD(0..1)_UCTL_CTL to ensure the values take effect.
+ c. Deassert the controller clock divider reset: USB-
+ DRD(0..1)_UCTL_CTL[H_CLKDIV_RST] = 0. */
+ uint64_t sclk_rate = bdk_clock_get_rate(node, BDK_CLOCK_SCLK);
+ uint64_t divider = (sclk_rate + 300000000-1) / 300000000;
+ /*
+ ** According to HRM Rules are:
+ ** - clock must be below 300MHz
+ ** USB3 full-rate requires 150 MHz or better
+ ** USB3 requires 125 MHz
+ ** USB2 full rate requires 90 MHz
+ ** USB2 requires 62.5 MHz
+ */
+ if (divider <= 1)
+ divider = 0;
+ else if (divider <= 2)
+ divider = 1;
+ else if (divider <= 4)
+ divider = 2;
+ else if (divider <= 6)
+ divider = 3;
+ else if (divider <= 8)
+ divider = 4;
+ else if (divider <= 16)
+ divider = 5;
+ else if (divider <= 24)
+ divider = 6;
+ else
+ divider = 7;
+ if (is_usbdrd)
+ {
+ BDK_CSR_MODIFY(c, node, BDK_USBDRDX_UCTL_CTL(usb_port),
+ c.s.h_clkdiv_rst = 1);
+ BDK_CSR_MODIFY(c, node, BDK_USBDRDX_UCTL_CTL(usb_port),
+ c.s.h_clkdiv_sel = divider;
+ c.s.h_clk_en = 1);
+ BDK_CSR_MODIFY(c, node, BDK_USBDRDX_UCTL_CTL(usb_port),
+ c.s.h_clkdiv_rst = 0);
+ }
+ else
+ {
+ BDK_CSR_MODIFY(c, node, BDK_USBHX_UCTL_CTL(usb_port),
+ c.s.h_clkdiv_rst = 1);
+ BDK_CSR_MODIFY(c, node, BDK_USBHX_UCTL_CTL(usb_port),
+ c.s.h_clkdiv_sel = divider;
+ c.s.h_clk_en = 1);
+ BDK_CSR_MODIFY(c, node, BDK_USBHX_UCTL_CTL(usb_port),
+ c.s.h_clkdiv_rst = 0);
+ }
+ {
+ static bool printit[2] = {true,true};
+ if (printit[usb_port]) {
+ uint64_t fr_div;
+ if (divider < 5) fr_div = divider * 2;
+ else fr_div = 8 * (divider - 3);
+ uint64_t freq = (typeof(freq)) (sclk_rate / fr_div);
+ const char *token;
+ if (freq < 62500000ULL) token = "???Low";
+ else if (freq < 90000000ULL) token = "USB2";
+ else if (freq < 125000000ULL) token = "USB2 Full";
+ else if (freq < 150000000ULL) token = "USB3";
+ else token = "USB3 Full";
+ BDK_TRACE(USB_XHCI, "Freq %lld - %s\n",
+ (unsigned long long)freq, token);
+ printit[usb_port] = false;
+ }
+ }
+
+ /* 6. Configure the strap signals in USBDRD(0..1)_UCTL_CTL.
+ a. Reference clock configuration (see Table 31.2): USB-
+ DRD(0..1)_UCTL_CTL[REF_CLK_FSEL, MPLL_MULTIPLIER,
+ REF_CLK_SEL, REF_CLK_DIV2].
+ b. Configure and enable spread-spectrum for SuperSpeed:
+ USBDRD(0..1)_UCTL_CTL[SSC_RANGE, SSC_EN, SSC_REF_CLK_SEL].
+ c. Enable USBDRD(0..1)_UCTL_CTL[REF_SSP_EN].
+ d. Configure PHY ports:
+ USBDRD(0..1)_UCTL_CTL[USB*_PORT_PERM_ATTACH, USB*_PORT_DISABLE]. */
+ if (is_usbdrd)
+ {
+ int ref_clk_src = 0;
+ int ref_clk_fsel = 0x27;
+ if (CAVIUM_IS_MODEL(CAVIUM_CN83XX)) {
+ if (BDK_USB_CLOCK_SS_PAD_HS_PAD != clock_type) {
+ bdk_error("Node %d usb_port %d: usb clock type %d is invalid\n", node, usb_port, clock_type);
+ return -1;
+ }
+ }
+ else if (CAVIUM_IS_MODEL(CAVIUM_CN81XX)) {
+ switch (clock_type)
+ {
+ default:
+ bdk_error("Node %d usb_port %d: usb clock type %d is invalid\n", node, usb_port, clock_type);
+ return -1;
+ case BDK_USB_CLOCK_SS_PAD_HS_PAD : ref_clk_src = 2; break;
+ case BDK_USB_CLOCK_SS_REF0_HS_REF0 : ref_clk_src = 0; break; /* Superspeed and high speed use DLM/QLM ref clock 0 */
+ case BDK_USB_CLOCK_SS_REF1_HS_REF1 : ref_clk_src = 1; break; /* Superspeed and high speed use DLM/QLM ref clock 1 */
+ case BDK_USB_CLOCK_SS_PAD_HS_PLL : ref_clk_src = 6; ref_clk_fsel = 0x7; break; /* Superspeed uses PAD clock, high speed uses PLL ref clock */
+ case BDK_USB_CLOCK_SS_REF0_HS_PLL : ref_clk_src = 4; ref_clk_fsel = 0x7; break; /* Superspeed uses DLM/QLM ref clock 0, high speed uses PLL ref clock */
+ case BDK_USB_CLOCK_SS_REF1_HS_PLL: ref_clk_src = 5; ref_clk_fsel =0x7; break; /* Superspeed uses DLM/QLM ref clock 1, high speed uses PLL ref clock */
+ }
+ }
+ BDK_CSR_MODIFY(c, node, BDK_USBDRDX_UCTL_CTL(usb_port),
+ c.s.ref_clk_fsel = ref_clk_fsel;
+ c.s.mpll_multiplier = 0x19;
+ c.s.ref_clk_sel = ref_clk_src;
+ c.s.ref_clk_div2 = 0);
+ BDK_CSR_MODIFY(c, node, BDK_USBDRDX_UCTL_CTL(usb_port),
+ c.s.ssc_en = 1;
+ c.s.ssc_ref_clk_sel = 0);
+ BDK_CSR_MODIFY(c, node, BDK_USBDRDX_UCTL_CTL(usb_port),
+ c.s.ref_ssp_en = 1);
+ }
+ else
+ {
+ if (BDK_USB_CLOCK_SS_PAD_HS_PAD != clock_type) {
+ bdk_error("Node %d usb_port %d: usb clock type %d is invalid\n", node, usb_port, clock_type);
+ return -1;
+ }
+ BDK_CSR_MODIFY(c, node, BDK_USBHX_UCTL_CTL(usb_port),
+ c.s.ref_clk_fsel = 0x27;
+ c.s.mpll_multiplier = 0;
+ c.s.ref_clk_sel = 0;
+ c.s.ref_clk_div2 = 0);
+ BDK_CSR_MODIFY(c, node, BDK_USBHX_UCTL_CTL(usb_port),
+ c.s.ssc_en = 1;
+ c.s.ssc_ref_clk_sel = 0);
+ BDK_CSR_MODIFY(c, node, BDK_USBHX_UCTL_CTL(usb_port),
+ c.s.ref_ssp_en = 1);
+ }
+ /* Hardware default is for ports to be enabled and not perm attach. Don't
+ change it */
+
+ /* 7. The PHY resets in lowest-power mode. Power up the per-port PHY
+ logic by enabling the following:
+ a. USBDRD(0..1)_UCTL_CTL [HS_POWER_EN] if high-speed/full-speed/low-
+ speed functionality needed.
+ b. USBDRD(0..1)_UCTL_CTL [SS_POWER_EN] if SuperSpeed functionality
+ needed. */
+ if (is_usbdrd)
+ {
+ BDK_CSR_MODIFY(c, node, BDK_USBDRDX_UCTL_CTL(usb_port),
+ c.s.hs_power_en = 1;
+ c.s.ss_power_en = 1);
+ }
+ else
+ {
+ BDK_CSR_MODIFY(c, node, BDK_USBHX_UCTL_CTL(usb_port),
+ c.s.hs_power_en = 1;
+ c.s.ss_power_en = 1);
+ }
+
+ /* 8. Wait 10 controller-clock cycles from step 5. for controller clock
+ to start and async FIFO to properly reset. */
+ bdk_wait_usec(1);
+
+ /* 9. Deassert UCTL and UAHC resets:
+ a. USBDRD(0..1)_UCTL_CTL[UCTL_RST] = 0
+ b. USBDRD(0..1)_UCTL_CTL[UAHC_RST] = 0
+ c. [optional] For port-power control:
+ - Set one of GPIO_BIT_CFG(0..47)[PIN_SEL] = USB0_VBUS_CTRLor USB1_VBUS_CTRL.
+ - Set USBDRD(0..1)_UCTL_HOST_CFG[PPC_EN] = 1 and USBDRD(0..1)_UCTL_HOST_CFG[PPC_ACTIVE_HIGH_EN] = 1.
+ - Wait for the external power management chip to power the VBUS.ional port-power control.
+ ]
+ d. You will have to wait 10 controller-clock cycles before accessing
+ any controller-clock-only registers. */
+ if (is_usbdrd)
+ {
+ BDK_CSR_MODIFY(c, node, BDK_USBDRDX_UCTL_CTL(usb_port),
+ c.s.uctl_rst = 0);
+ }
+ else
+ {
+ BDK_CSR_MODIFY(c, node, BDK_USBHX_UCTL_CTL(usb_port),
+ c.s.uctl_rst = 0);
+ }
+ bdk_wait_usec(1);
+
+ int usb_gpio = bdk_config_get_int(BDK_CONFIG_USB_PWR_GPIO, node, usb_port);
+ int usb_polarity = bdk_config_get_int(BDK_CONFIG_USB_PWR_GPIO_POLARITY, node, usb_port);
+ if (-1 != usb_gpio) {
+ int gsrc = BDK_GPIO_PIN_SEL_E_USBX_VBUS_CTRL_CN88XX(usb_port);
+ if (CAVIUM_IS_MODEL(CAVIUM_CN88XX)) {
+ gsrc = BDK_GPIO_PIN_SEL_E_USBX_VBUS_CTRL_CN88XX(usb_port);
+ }
+ else if (CAVIUM_IS_MODEL(CAVIUM_CN81XX)) {
+ gsrc = BDK_GPIO_PIN_SEL_E_USBX_VBUS_CTRL_CN81XX(usb_port);
+ }
+ else if (CAVIUM_IS_MODEL(CAVIUM_CN83XX)) {
+ gsrc = BDK_GPIO_PIN_SEL_E_USBX_VBUS_CTRL_CN83XX(usb_port);}
+ else {
+ bdk_error("USB_VBUS_CTRL GPIO: unknown chip model\n");
+ }
+
+ BDK_CSR_MODIFY(c,node,BDK_GPIO_BIT_CFGX(usb_gpio),
+ c.s.pin_sel = gsrc;
+ c.s.pin_xor = (usb_polarity) ? 0 : 1;
+ );
+
+ if (is_usbdrd)
+ {
+ BDK_CSR_MODIFY(c, node, BDK_USBDRDX_UCTL_HOST_CFG(usb_port),
+ c.s.ppc_en = 1;
+ c.s.ppc_active_high_en = 1);
+ }
+ else
+ {
+ BDK_CSR_MODIFY(c, node, BDK_USBHX_UCTL_HOST_CFG(usb_port),
+ c.s.ppc_en = 1;
+ c.s.ppc_active_high_en = 1);
+ }
+ bdk_wait_usec(100000);
+ }
+
+ if (is_usbdrd)
+ {
+ BDK_CSR_MODIFY(c, node, BDK_USBDRDX_UCTL_CTL(usb_port),
+ c.s.uahc_rst = 0);
+ }
+ else
+ {
+ BDK_CSR_MODIFY(c, node, BDK_USBHX_UCTL_CTL(usb_port),
+ c.s.uahc_rst = 0);
+ }
+
+ bdk_wait_usec(100000);
+ bdk_wait_usec(1);
+
+ /* 10. Enable conditional coprocessor clock of UCTL by writing USB-
+ DRD(0..1)_UCTL_CTL[CSCLK_EN] = 1. */
+ if (is_usbdrd)
+ {
+ if (CAVIUM_IS_MODEL(CAVIUM_CN8XXX))
+ {
+ /* CN9XXX make coprocessor clock automatic */
+ BDK_CSR_MODIFY(c, node, BDK_USBDRDX_UCTL_CTL(usb_port),
+ c.cn83xx.csclk_en = 1);
+ }
+ }
+ else
+ {
+ BDK_CSR_MODIFY(c, node, BDK_USBHX_UCTL_CTL(usb_port),
+ c.s.csclk_en = 1);
+ }
+
+ /* 11. Set USBDRD(0..1)_UCTL_CTL[DRD_MODE] to 1 for device mode, 0 for
+ host mode. */
+ if (is_usbdrd)
+ {
+ BDK_CSR_MODIFY(c, node, BDK_USBDRDX_UCTL_CTL(usb_port),
+ c.s.drd_mode = 0);
+ }
+
+ /* 12. Soft reset the UPHY and UAHC logic via the UAHC controls:
+ a. USBDRD(0..1)_UAHC_GUSB2PHYCFG(0)[PHYSOFTRST] = 1
+ b. USBDRD(0..1)_UAHC_GUSB3PIPECTL(0)[PHYSOFTRST] = 1
+ c. USBDRD(0..1)_UAHC_GCTL[CORESOFTRESET] = 1 */
+ if (is_usbdrd)
+ {
+ BDK_CSR_MODIFY(c, node, BDK_USBDRDX_UAHC_GUSB2PHYCFGX(usb_port, 0),
+ c.s.physoftrst = 1);
+ BDK_CSR_MODIFY(c, node, BDK_USBDRDX_UAHC_GUSB3PIPECTLX(usb_port, 0),
+ c.s.physoftrst = 1);
+ BDK_CSR_MODIFY(c, node, BDK_USBDRDX_UAHC_GCTL(usb_port),
+ c.s.coresoftreset = 1);
+ }
+ else
+ {
+ BDK_CSR_MODIFY(c, node, BDK_USBHX_UAHC_GUSB2PHYCFGX(usb_port, 0),
+ c.s.physoftrst = 1);
+ BDK_CSR_MODIFY(c, node, BDK_USBHX_UAHC_GUSB3PIPECTLX(usb_port, 0),
+ c.s.physoftrst = 1);
+ BDK_CSR_MODIFY(c, node, BDK_USBHX_UAHC_GCTL(usb_port),
+ c.s.coresoftreset = 1);
+ }
+
+ /* 13. Program USBDRD(0..1)_UAHC_GCTL[PRTCAPDIR] to 0x2 for device mode
+ or 0x1 for host mode. */
+ if (is_usbdrd)
+ {
+ BDK_CSR_MODIFY(c, node, BDK_USBDRDX_UAHC_GCTL(usb_port),
+ c.s.prtcapdir = 1);
+ }
+ else
+ {
+ BDK_CSR_MODIFY(c, node, BDK_USBHX_UAHC_GCTL(usb_port),
+ c.s.prtcapdir = 1);
+ }
+
+ /* 14. Wait 10us after step 13. for the PHY to complete its reset. */
+ bdk_wait_usec(10);
+
+ /* 15. Deassert UPHY reset: USBDRD(0..1)_UCTL_CTL[UPHY_RST] = 0. */
+ if (is_usbdrd)
+ {
+ BDK_CSR_MODIFY(c, node, BDK_USBDRDX_UCTL_CTL(usb_port),
+ c.s.uphy_rst = 0);
+ }
+ else
+ {
+ BDK_CSR_MODIFY(c, node, BDK_USBHX_UCTL_CTL(usb_port),
+ c.s.uphy_rst = 0);
+ }
+
+ /* 16. Wait for at least 45us after step 15. for UPHY to output
+ stable PHYCLOCK. */
+ bdk_wait_usec(45);
+
+ /* Workround Errata USB-29206 */
+ if (dwc3_uphy_check_pll(node, usb_port))
+ return -1;
+
+ /* 17. Initialize any other strap signals necessary and make sure they
+ propagate by reading back the last register written.
+ a. UCTL
+ USBDRD(0..1)_UCTL_PORT0_CFG_*[*_TUNE]
+ USBDRD(0..1)_UCTL_PORT0_CFG_*[PCS_*]
+ USBDRD(0..1)_UCTL_PORT0_CFG_*[LANE0_TX_TERM_OFFSET]
+ USBDRD(0..1)_UCTL_PORT0_CFG_*[TX_VBOOST_LVL]
+ USBDRD(0..1)_UCTL__PORT0_CFG_*[LOS_BIAS]
+ USBDRD(0..1)_UCTL_HOST_CFG
+ USBDRD(0..1)_UCTL_SHIM_CFG
+ b. UAHC: only the following UAHC registers are accessible during
+ CoreSoftReset.
+ USBDRD(0..1)_UAHC_GCTL
+ USBDRD(0..1)_UAHC_GUCTL
+ USBDRD(0..1)_UAHC_GSTS
+ USBDRD(0..1)_UAHC_GUID
+ USBDRD(0..1)_UAHC_GUSB2PHYCFG(0)
+ USBDRD(0..1)_UAHC_GUSB3PIPECTL(0) */
+
+ /* 18. Release soft reset the UPHY and UAHC logic via the UAHC controls:
+ a. USBDRD(0..1)_UAHC_GUSB2PHYCFG(0)[PHYSOFTRST] = 0
+ b. USBDRD(0..1)_UAHC_GUSB3PIPECTL(0)[PHYSOFTRST] = 0
+ c. USBDRD(0..1)_UAHC_GCTL[CORESOFTRESET] = 0 */
+ if (is_usbdrd)
+ {
+ BDK_CSR_MODIFY(c, node, BDK_USBDRDX_UAHC_GUSB2PHYCFGX(usb_port, 0),
+ c.s.physoftrst = 0);
+ BDK_CSR_MODIFY(c, node, BDK_USBDRDX_UAHC_GUSB3PIPECTLX(usb_port, 0),
+ c.s.physoftrst = 0);
+ BDK_CSR_MODIFY(c, node, BDK_USBDRDX_UAHC_GCTL(usb_port),
+ c.s.coresoftreset = 0);
+ }
+ else
+ {
+ BDK_CSR_MODIFY(c, node, BDK_USBHX_UAHC_GUSB2PHYCFGX(usb_port, 0),
+ c.s.physoftrst = 0);
+ BDK_CSR_MODIFY(c, node, BDK_USBHX_UAHC_GUSB3PIPECTLX(usb_port, 0),
+ c.s.physoftrst = 0);
+ BDK_CSR_MODIFY(c, node, BDK_USBHX_UAHC_GCTL(usb_port),
+ c.s.coresoftreset = 0);
+ }
+
+ /* 19. Configure the remaining UAHC_G* registers as needed, including
+ any that were not configured in step 17.-b. */
+
+ /* 20. Initialize the USB controller:
+ a. To initialize the UAHC as a USB host controller, the application
+ should perform the steps described in the xHCI specification
+ (UAHC_X* registers). The xHCI sequence starts with poll for a 0 in
+ USBDRD(0..1)_UAHC_USBSTS[CNR].
+ b. To initialize the UAHC as a device, the application should TBD. The
+ device initiation sequence starts with a device soft reset by
+ setting USBDRD(0..1)_UAHC_DCTL[CSFTRST] = 1 and wait for a read
+ operation to return 0. */
+ return 0;
+}