xref: /aosp_15_r20/external/coreboot/src/vendorcode/cavium/bdk/libbdk-hal/bdk-usb.c (revision b9411a12aaaa7e1e6a6fb7c5e057f44ee179a49c)
1 /***********************license start***********************************
2 * Copyright (c) 2003-2017  Cavium Inc. ([email protected]). All rights
3 * reserved.
4 *
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are
8 * met:
9 *
10 *   * Redistributions of source code must retain the above copyright
11 *     notice, this list of conditions and the following disclaimer.
12 *
13 *   * Redistributions in binary form must reproduce the above
14 *     copyright notice, this list of conditions and the following
15 *     disclaimer in the documentation and/or other materials provided
16 *     with the distribution.
17 *
18 *   * Neither the name of Cavium Inc. nor the names of
19 *     its contributors may be used to endorse or promote products
20 *     derived from this software without specific prior written
21 *     permission.
22 *
23 * This Software, including technical data, may be subject to U.S. export
24 * control laws, including the U.S. Export Administration Act and its
25 * associated regulations, and may be subject to export or import
26 * regulations in other countries.
27 *
28 * TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SOFTWARE IS PROVIDED "AS IS"
29 * AND WITH ALL FAULTS AND CAVIUM INC. MAKES NO PROMISES, REPRESENTATIONS OR
30 * WARRANTIES, EITHER EXPRESS, IMPLIED, STATUTORY, OR OTHERWISE, WITH RESPECT
31 * TO THE SOFTWARE, INCLUDING ITS CONDITION, ITS CONFORMITY TO ANY
32 * REPRESENTATION OR DESCRIPTION, OR THE EXISTENCE OF ANY LATENT OR PATENT
33 * DEFECTS, AND CAVIUM SPECIFICALLY DISCLAIMS ALL IMPLIED (IF ANY) WARRANTIES
34 * OF TITLE, MERCHANTABILITY, NONINFRINGEMENT, FITNESS FOR A PARTICULAR
35 * PURPOSE, LACK OF VIRUSES, ACCURACY OR COMPLETENESS, QUIET ENJOYMENT,
36 * QUIET POSSESSION OR CORRESPONDENCE TO DESCRIPTION. THE ENTIRE  RISK
37 * ARISING OUT OF USE OR PERFORMANCE OF THE SOFTWARE LIES WITH YOU.
38 ***********************license end**************************************/
39 #include <bdk.h>
40 #include <libbdk-arch/bdk-csrs-gpio.h>
41 #include <libbdk-arch/bdk-csrs-usbdrd.h>
42 #include <libbdk-arch/bdk-csrs-usbh.h>
43 #include <libbdk-hal/bdk-usb.h>
44 #include <libbdk-hal/bdk-config.h>
45 
46 /* This code is an optional part of the BDK. It is only linked in
47     if BDK_REQUIRE() needs it */
48 BDK_REQUIRE_DEFINE(USB);
49 
50 /**
51  * Write to DWC3 indirect debug control register
52  *
53  * @param node     Node to write to
54  * @param usb_port USB port to write to
55  * @param val      32bit value to write
56  */
write_cr_dbg_cfg(bdk_node_t node,int usb_port,uint64_t val)57 static void write_cr_dbg_cfg(bdk_node_t node, int usb_port, uint64_t val)
58 {
59     if (!CAVIUM_IS_MODEL(CAVIUM_CN88XX))
60         BDK_CSR_WRITE(node, BDK_USBDRDX_UCTL_PORTX_CR_DBG_CFG(usb_port, 0), val);
61     else
62         BDK_CSR_WRITE(node, BDK_USBHX_UCTL_PORTX_CR_DBG_CFG(usb_port, 0), val);
63 }
64 
65 /**
66  * Poll the DWC3 internal status until the ACK bit matches a desired value. Return
67  * the final status.
68  *
69  * @param node     Node to query
70  * @param usb_port USB port to query
71  * @param desired_ack
72  *                 Desired ACK bit state
73  *
74  * @return Final status with ACK at correct state
75  */
get_cr_dbg_status(bdk_node_t node,int usb_port,int desired_ack)76 static bdk_usbdrdx_uctl_portx_cr_dbg_status_t get_cr_dbg_status(bdk_node_t node, int usb_port, int desired_ack)
77 {
78     const int TIMEOUT = 1000000; /* 1 sec */
79     bdk_usbdrdx_uctl_portx_cr_dbg_status_t status;
80     if (!CAVIUM_IS_MODEL(CAVIUM_CN88XX))
81     {
82         if (BDK_CSR_WAIT_FOR_FIELD(node, BDK_USBDRDX_UCTL_PORTX_CR_DBG_STATUS(usb_port, 0), ack, ==, desired_ack, TIMEOUT))
83         {
84             BDK_TRACE(USB_XHCI, "N%d.USB%d: Timeout waiting for indirect ACK\n", node, usb_port);
85             status.u = -1;
86         }
87         else
88             status.u = BDK_CSR_READ(node, BDK_USBDRDX_UCTL_PORTX_CR_DBG_STATUS(usb_port, 0));
89     }
90     else
91     {
92         if (BDK_CSR_WAIT_FOR_FIELD(node, BDK_USBHX_UCTL_PORTX_CR_DBG_STATUS(usb_port, 0), ack, ==, desired_ack, TIMEOUT))
93         {
94             BDK_TRACE(USB_XHCI, "N%d.USB%d: Timeout waiting for indirect ACK\n", node, usb_port);
95             status.u = -1;
96         }
97         else
98             status.u = BDK_CSR_READ(node, BDK_USBHX_UCTL_PORTX_CR_DBG_STATUS(usb_port, 0));
99     }
100     return status;
101 }
102 
103 /**
104  * Perform an indirect read of an internal register inside the DWC3 usb block
105  *
106  * @param node     Node to read
107  * @param usb_port USB port to read
108  * @param addr     Indirect register address
109  *
110  * @return Value of the indirect register
111  */
dwc3_uphy_indirect_read(bdk_node_t node,int usb_port,uint32_t addr)112 static uint32_t dwc3_uphy_indirect_read(bdk_node_t node, int usb_port, uint32_t addr)
113 {
114     bdk_usbdrdx_uctl_portx_cr_dbg_cfg_t dbg_cfg;
115     bdk_usbdrdx_uctl_portx_cr_dbg_status_t status;
116 
117     /* See the CSR description for USBHX_UCTL_PORTX_CR_DBG_CFG, which describes
118        the steps implemented by this function */
119 
120     dbg_cfg.u = 0;
121     dbg_cfg.s.data_in = addr;
122     write_cr_dbg_cfg(node, usb_port, dbg_cfg.u);
123 
124     dbg_cfg.s.cap_addr = 1;
125     write_cr_dbg_cfg(node, usb_port, dbg_cfg.u);
126 
127     status = get_cr_dbg_status(node, usb_port, 1);
128     if (status.u == (uint64_t)-1)
129         return 0xffffffff;
130 
131     write_cr_dbg_cfg(node, usb_port, 0);
132     get_cr_dbg_status(node, usb_port, 0);
133 
134     dbg_cfg.u = 0;
135     dbg_cfg.s.read = 1;
136     write_cr_dbg_cfg(node, usb_port, dbg_cfg.u);
137 
138     status = get_cr_dbg_status(node, usb_port, 1);
139 
140     write_cr_dbg_cfg(node, usb_port, 0);
141     get_cr_dbg_status(node, usb_port, 0);
142 
143     return status.s.data_out;
144 }
145 
146 /**
147  * Perform an indirect write of an internal register inside the DWC3 usb block
148  *
149  * @param node     Node to write
150  * @param usb_port USB port to write
151  * @param addr     Indirect register address
152  * @param value    Value for write
153  */
dwc3_uphy_indirect_write(bdk_node_t node,int usb_port,uint32_t addr,uint16_t value)154 static void dwc3_uphy_indirect_write(bdk_node_t node, int usb_port, uint32_t addr, uint16_t value)
155 {
156     bdk_usbdrdx_uctl_portx_cr_dbg_cfg_t dbg_cfg;
157 
158     /* See the CSR description for USBHX_UCTL_PORTX_CR_DBG_CFG, which describes
159        the steps implemented by this function */
160 
161     dbg_cfg.u = 0;
162     dbg_cfg.s.data_in = addr;
163     write_cr_dbg_cfg(node, usb_port, dbg_cfg.u);
164 
165     dbg_cfg.s.cap_addr = 1;
166     write_cr_dbg_cfg(node, usb_port, dbg_cfg.u);
167 
168     get_cr_dbg_status(node, usb_port, 1);
169 
170     write_cr_dbg_cfg(node, usb_port, 0);
171     get_cr_dbg_status(node, usb_port, 0);
172 
173     dbg_cfg.u = 0;
174     dbg_cfg.s.data_in = value;
175     write_cr_dbg_cfg(node, usb_port, dbg_cfg.u);
176 
177     dbg_cfg.s.cap_data = 1;
178     write_cr_dbg_cfg(node, usb_port, dbg_cfg.u);
179 
180     get_cr_dbg_status(node, usb_port, 1);
181 
182     write_cr_dbg_cfg(node, usb_port, 0);
183     get_cr_dbg_status(node, usb_port, 0);
184 
185     dbg_cfg.u = 0;
186     dbg_cfg.s.write = 1;
187     write_cr_dbg_cfg(node, usb_port, dbg_cfg.u);
188 
189     get_cr_dbg_status(node, usb_port, 1);
190 
191     write_cr_dbg_cfg(node, usb_port, 0);
192     get_cr_dbg_status(node, usb_port, 0);
193 }
194 
195 /**
196  * Errata USB-29206 - The USB HS PLL in all 28nm devices has a
197  * design issue that may cause the VCO to lock up on
198  * initialization.  The Synopsys VCO is designed with an even
199  * number of stages and no kick-start circuit, which makes us
200  * believe that there is no question a latched up
201  * (non-oscillating) state is possible. The workaround is to
202  * check the PLL lock bit, which is just based on a counter and
203  * will not set if the VCO is not oscillating, and if it's not
204  * set do a power down/power up cycle on the PLL, which tests
205  * have proven is much more likely to guarantee the VCO will
206  * start oscillating.  Part of the problem appears to be that
207  * the normal init sequence holds the VCO in reset during the
208  * power up sequence, whereas the plain power up/down sequence
209  * does not, so the voltage changing may be helping the circuit
210  * to oscillate.
211  *
212  * @param node     Node to check
213  * @param usb_port USB port to check
214  *
215  * @return Zero on success, negative on failure
216  */
dwc3_uphy_check_pll(bdk_node_t node,int usb_port)217 static int dwc3_uphy_check_pll(bdk_node_t node, int usb_port)
218 {
219     /* Internal indirect register that reports if the phy PLL has lock.  This will
220        be 1 if lock, 0 if no lock */
221     const int DWC3_INT_IND_PLL_LOCK_REG = 0x200b;
222     /* Internal indirect UPHY register that controls the power to the UPHY PLL */
223     const int DWC3_INT_IND_UPHY_PLL_PU = 0x2012;
224     /* Write enable bit for DWC3_INT_IND_PLL_POWER_CTL */
225     const int DWC3_INT_IND_UPHY_PLL_PU_WE = 0x20;
226     /* Power enable bit for DWC3_INT_IND_PLL_POWER_CTL */
227     const int DWC3_INT_IND_UPHY_PLL_PU_POWER_EN = 0x02;
228 
229     uint32_t pll_locked = dwc3_uphy_indirect_read(node, usb_port, DWC3_INT_IND_PLL_LOCK_REG);
230     int retry_count = 0;
231     while (!pll_locked)
232     {
233         if (retry_count >= 3)
234         {
235             bdk_error("N%d.USB%d: USB2 PLL failed to lock\n", node, usb_port);
236             return -1;
237         }
238 
239         retry_count++;
240         BDK_TRACE(USB_XHCI, "N%d.USB%d: USB2 PLL didn't lock, retry %d\n", node, usb_port, retry_count);
241 
242         /* Turn on write enable for PLL power control */
243         uint32_t pwr_val = dwc3_uphy_indirect_read(node, usb_port, DWC3_INT_IND_UPHY_PLL_PU);
244         pwr_val |= DWC3_INT_IND_UPHY_PLL_PU_WE;
245         dwc3_uphy_indirect_write(node, usb_port, DWC3_INT_IND_UPHY_PLL_PU, pwr_val);
246 
247         /* Power down the PLL */
248         pwr_val &= ~DWC3_INT_IND_UPHY_PLL_PU_POWER_EN;
249         dwc3_uphy_indirect_write(node, usb_port, DWC3_INT_IND_UPHY_PLL_PU, pwr_val);
250         bdk_wait_usec(1000);
251 
252         /* Power on the PLL */
253         pwr_val |= DWC3_INT_IND_UPHY_PLL_PU_POWER_EN;
254         dwc3_uphy_indirect_write(node, usb_port, DWC3_INT_IND_UPHY_PLL_PU, pwr_val);
255         bdk_wait_usec(1000);
256 
257         /* Check for PLL Lock again */
258         pll_locked = dwc3_uphy_indirect_read(node, usb_port, DWC3_INT_IND_PLL_LOCK_REG);
259     }
260     return 0;
261 }
262 
263 /**
264  * Initialize the clocks for USB such that it is ready for a generic XHCI driver
265  *
266  * @param node       Node to init
267  * @param usb_port   Port to intialize
268  * @param clock_type Type of clock connected to the usb port
269  *
270  * @return Zero on success, negative on failure
271  */
272 
bdk_usb_initialize(bdk_node_t node,int usb_port,bdk_usb_clock_t clock_type)273 int bdk_usb_initialize(bdk_node_t node, int usb_port, bdk_usb_clock_t clock_type)
274 {
275     int is_usbdrd = !CAVIUM_IS_MODEL(CAVIUM_CN88XX);
276 
277     /* Perform the following steps to initiate a cold reset. */
278 
279     /* 1.  Wait for all voltages to reach a stable state.  Ensure the
280         reference clock is up and stable.
281         a.  If 3.3V is up first, 0.85V must be soon after (within tens of ms). */
282 
283     /* 2.  Wait for IOI reset to deassert. */
284 
285     /* 3.  If Over Current indication and/or Port Power Control features
286         are desired, program the GPIO CSRs appropriately.
287         a.  For Over Current Indication, select a GPIO for the input and
288             program GPIO_USBH_CTL[SEL].
289         b. For Port Power Control, set one of
290             GPIO_BIT_CFG(0..19)[OUTPUT_SEL] = USBH_VBUS_CTRL. */
291 
292     /* 4.  Assert all resets:
293         a.  UPHY reset: USBDRD(0..1)_UCTL_CTL[UPHY_RST] = 1
294         b. UAHC reset: USBDRD(0..1)_UCTL_CTL[UAHC_RST] = 1
295         c. UCTL reset: USBDRD(0..1)_UCTL_CTL[UCTL_RST] = 1 */
296     if (is_usbdrd)
297     {
298         BDK_CSR_MODIFY(c, node, BDK_USBDRDX_UCTL_CTL(usb_port),
299             c.s.uphy_rst = 1;
300             c.s.uahc_rst = 1;
301             c.s.uctl_rst = 1);
302     }
303     else
304     {
305         BDK_CSR_MODIFY(c, node, BDK_USBHX_UCTL_CTL(usb_port),
306             c.s.uphy_rst = 1;
307             c.s.uahc_rst = 1;
308             c.s.uctl_rst = 1);
309     }
310 
311     /* 5.  Configure the controller clock:
312         a.  Reset the clock dividers: USBDRD(0..1)_UCTL_CTL[H_CLKDIV_RST] = 1.
313         b. Select the controller clock frequency
314             USBDRD(0..1)_UCTL_CTL[H_CLKDIV] = desired value.
315             USBDRD(0..1)_UCTL_CTL[H_CLKDIV_EN] = 1 to enable the controller
316                 clock.
317             Read USBDRD(0..1)_UCTL_CTL to ensure the values take effect.
318         c.  Deassert the controller clock divider reset: USB-
319             DRD(0..1)_UCTL_CTL[H_CLKDIV_RST] = 0. */
320     uint64_t sclk_rate = bdk_clock_get_rate(node, BDK_CLOCK_SCLK);
321     uint64_t divider = (sclk_rate + 300000000-1) / 300000000;
322     /*
323     ** According to HRM Rules are:
324     ** - clock must be below 300MHz
325     ** USB3 full-rate requires 150 MHz or better
326     ** USB3 requires 125 MHz
327     ** USB2 full rate requires 90 MHz
328     ** USB2 requires 62.5 MHz
329     */
330     if (divider <= 1)
331         divider = 0;
332     else if (divider <= 2)
333         divider = 1;
334     else if (divider <= 4)
335         divider = 2;
336     else if (divider <= 6)
337         divider = 3;
338     else if (divider <= 8)
339         divider = 4;
340     else if (divider <= 16)
341         divider = 5;
342     else if (divider <= 24)
343         divider = 6;
344     else
345         divider = 7;
346     if (is_usbdrd)
347     {
348         BDK_CSR_MODIFY(c, node, BDK_USBDRDX_UCTL_CTL(usb_port),
349             c.s.h_clkdiv_rst = 1);
350         BDK_CSR_MODIFY(c, node, BDK_USBDRDX_UCTL_CTL(usb_port),
351             c.s.h_clkdiv_sel = divider;
352             c.s.h_clk_en = 1);
353         BDK_CSR_MODIFY(c, node, BDK_USBDRDX_UCTL_CTL(usb_port),
354             c.s.h_clkdiv_rst = 0);
355     }
356     else
357     {
358         BDK_CSR_MODIFY(c, node, BDK_USBHX_UCTL_CTL(usb_port),
359             c.s.h_clkdiv_rst = 1);
360         BDK_CSR_MODIFY(c, node, BDK_USBHX_UCTL_CTL(usb_port),
361             c.s.h_clkdiv_sel = divider;
362             c.s.h_clk_en = 1);
363         BDK_CSR_MODIFY(c, node, BDK_USBHX_UCTL_CTL(usb_port),
364             c.s.h_clkdiv_rst = 0);
365     }
366     {
367         static bool printit[2] = {true,true};
368         if (printit[usb_port]) {
369             uint64_t fr_div;
370             if (divider < 5) fr_div = divider * 2;
371             else fr_div = 8 * (divider - 3);
372             uint64_t freq = 0;
373             if (fr_div > 0)
374                 freq = (typeof(freq)) (sclk_rate / fr_div);
375             const char *token;
376             if (freq < 62500000ULL) token = "???Low";
377             else if (freq < 90000000ULL) token = "USB2";
378             else if (freq < 125000000ULL) token = "USB2 Full";
379             else if (freq < 150000000ULL) token = "USB3";
380             else token = "USB3 Full";
381             BDK_TRACE(USB_XHCI, "Freq %lld - %s\n",
382                    (unsigned long long)freq, token);
383             printit[usb_port] = false;
384         }
385     }
386 
387     /* 6.  Configure the strap signals in USBDRD(0..1)_UCTL_CTL.
388         a.  Reference clock configuration (see Table 31.2): USB-
389             DRD(0..1)_UCTL_CTL[REF_CLK_FSEL, MPLL_MULTIPLIER,
390             REF_CLK_SEL, REF_CLK_DIV2].
391         b. Configure and enable spread-spectrum for SuperSpeed:
392             USBDRD(0..1)_UCTL_CTL[SSC_RANGE, SSC_EN, SSC_REF_CLK_SEL].
393         c. Enable USBDRD(0..1)_UCTL_CTL[REF_SSP_EN].
394         d. Configure PHY ports:
395             USBDRD(0..1)_UCTL_CTL[USB*_PORT_PERM_ATTACH, USB*_PORT_DISABLE]. */
396     if (is_usbdrd)
397     {
398         int ref_clk_src = 0;
399         int ref_clk_fsel = 0x27;
400         if (CAVIUM_IS_MODEL(CAVIUM_CN83XX)) {
401             if (BDK_USB_CLOCK_SS_PAD_HS_PAD != clock_type) {
402                 bdk_error("Node %d usb_port %d: usb clock type %d is invalid\n", node, usb_port, clock_type);
403                 return -1;
404             }
405         }
406         else if (CAVIUM_IS_MODEL(CAVIUM_CN81XX)) {
407             switch (clock_type)
408             {
409             default:
410                 bdk_error("Node %d usb_port %d: usb clock type %d is invalid\n", node, usb_port, clock_type);
411                 return -1;
412             case BDK_USB_CLOCK_SS_PAD_HS_PAD : ref_clk_src = 2; break;
413             case BDK_USB_CLOCK_SS_REF0_HS_REF0 : ref_clk_src = 0; break;  /* Superspeed and high speed use DLM/QLM ref clock 0 */
414             case BDK_USB_CLOCK_SS_REF1_HS_REF1 : ref_clk_src = 1; break;  /* Superspeed and high speed use DLM/QLM ref clock 1 */
415             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 */
416             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 */
417             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 */
418             }
419         }
420         BDK_CSR_MODIFY(c, node, BDK_USBDRDX_UCTL_CTL(usb_port),
421             c.s.ref_clk_fsel = ref_clk_fsel;
422             c.s.mpll_multiplier = 0x19;
423             c.s.ref_clk_sel = ref_clk_src;
424             c.s.ref_clk_div2 = 0);
425         BDK_CSR_MODIFY(c, node, BDK_USBDRDX_UCTL_CTL(usb_port),
426             c.s.ssc_en = 1;
427             c.s.ssc_ref_clk_sel = 0);
428         BDK_CSR_MODIFY(c, node, BDK_USBDRDX_UCTL_CTL(usb_port),
429             c.s.ref_ssp_en = 1);
430     }
431     else
432     {
433         if (BDK_USB_CLOCK_SS_PAD_HS_PAD != clock_type) {
434             bdk_error("Node %d usb_port %d: usb clock type %d is invalid\n", node, usb_port, clock_type);
435             return -1;
436         }
437         BDK_CSR_MODIFY(c, node, BDK_USBHX_UCTL_CTL(usb_port),
438             c.s.ref_clk_fsel = 0x27;
439             c.s.mpll_multiplier = 0;
440             c.s.ref_clk_sel = 0;
441             c.s.ref_clk_div2 = 0);
442         BDK_CSR_MODIFY(c, node, BDK_USBHX_UCTL_CTL(usb_port),
443             c.s.ssc_en = 1;
444             c.s.ssc_ref_clk_sel = 0);
445         BDK_CSR_MODIFY(c, node, BDK_USBHX_UCTL_CTL(usb_port),
446             c.s.ref_ssp_en = 1);
447     }
448     /* Hardware default is for ports to be enabled and not perm attach. Don't
449         change it */
450 
451     /* 7.  The PHY resets in lowest-power mode. Power up the per-port PHY
452        logic by enabling the following:
453         a.  USBDRD(0..1)_UCTL_CTL [HS_POWER_EN] if high-speed/full-speed/low-
454             speed functionality needed.
455         b. USBDRD(0..1)_UCTL_CTL [SS_POWER_EN] if SuperSpeed functionality
456             needed. */
457     if (is_usbdrd)
458     {
459         BDK_CSR_MODIFY(c, node, BDK_USBDRDX_UCTL_CTL(usb_port),
460             c.s.hs_power_en = 1;
461             c.s.ss_power_en = 1);
462     }
463     else
464     {
465         BDK_CSR_MODIFY(c, node, BDK_USBHX_UCTL_CTL(usb_port),
466             c.s.hs_power_en = 1;
467             c.s.ss_power_en = 1);
468     }
469 
470     /* 8.  Wait 10 controller-clock cycles from step 5. for controller clock
471         to start and async FIFO to properly reset. */
472     bdk_wait_usec(1);
473 
474     /* 9.  Deassert UCTL and UAHC resets:
475         a.  USBDRD(0..1)_UCTL_CTL[UCTL_RST] = 0
476         b. USBDRD(0..1)_UCTL_CTL[UAHC_RST] = 0
477         c. [optional] For port-power control:
478         - Set one of GPIO_BIT_CFG(0..47)[PIN_SEL] =  USB0_VBUS_CTRLor USB1_VBUS_CTRL.
479         - Set USBDRD(0..1)_UCTL_HOST_CFG[PPC_EN] = 1 and USBDRD(0..1)_UCTL_HOST_CFG[PPC_ACTIVE_HIGH_EN] = 1.
480         - Wait for the external power management chip to power the VBUS.ional port-power control.
481         ]
482         d. You will have to wait 10 controller-clock cycles before accessing
483             any controller-clock-only registers. */
484     if (is_usbdrd)
485     {
486         BDK_CSR_MODIFY(c, node, BDK_USBDRDX_UCTL_CTL(usb_port),
487             c.s.uctl_rst = 0);
488     }
489     else
490     {
491         BDK_CSR_MODIFY(c, node, BDK_USBHX_UCTL_CTL(usb_port),
492             c.s.uctl_rst = 0);
493     }
494     bdk_wait_usec(1);
495 
496     int usb_gpio = bdk_config_get_int(BDK_CONFIG_USB_PWR_GPIO, node, usb_port);
497     int usb_polarity = bdk_config_get_int(BDK_CONFIG_USB_PWR_GPIO_POLARITY, node, usb_port);
498     if (-1 != usb_gpio) {
499         int gsrc = BDK_GPIO_PIN_SEL_E_USBX_VBUS_CTRL_CN88XX(usb_port);
500         if (CAVIUM_IS_MODEL(CAVIUM_CN88XX)) {
501             gsrc = BDK_GPIO_PIN_SEL_E_USBX_VBUS_CTRL_CN88XX(usb_port);
502         }
503         else if (CAVIUM_IS_MODEL(CAVIUM_CN81XX)) {
504             gsrc = BDK_GPIO_PIN_SEL_E_USBX_VBUS_CTRL_CN81XX(usb_port);
505         }
506         else if (CAVIUM_IS_MODEL(CAVIUM_CN83XX)) {
507             gsrc = BDK_GPIO_PIN_SEL_E_USBX_VBUS_CTRL_CN83XX(usb_port);}
508         else {
509             bdk_error("USB_VBUS_CTRL GPIO: unknown chip model\n");
510         }
511 
512         BDK_CSR_MODIFY(c,node,BDK_GPIO_BIT_CFGX(usb_gpio),
513                        c.s.pin_sel = gsrc;
514                        c.s.pin_xor = (usb_polarity) ? 0 : 1;
515             );
516 
517         if (is_usbdrd)
518         {
519             BDK_CSR_MODIFY(c, node, BDK_USBDRDX_UCTL_HOST_CFG(usb_port),
520                            c.s.ppc_en = 1;
521                            c.s.ppc_active_high_en = 1);
522         }
523         else
524         {
525             BDK_CSR_MODIFY(c, node, BDK_USBHX_UCTL_HOST_CFG(usb_port),
526                            c.s.ppc_en = 1;
527                            c.s.ppc_active_high_en = 1);
528         }
529         bdk_wait_usec(100000);
530     }
531 
532     if (is_usbdrd)
533     {
534         BDK_CSR_MODIFY(c, node, BDK_USBDRDX_UCTL_CTL(usb_port),
535             c.s.uahc_rst = 0);
536     }
537     else
538     {
539         BDK_CSR_MODIFY(c, node, BDK_USBHX_UCTL_CTL(usb_port),
540             c.s.uahc_rst = 0);
541     }
542 
543     bdk_wait_usec(100000);
544     bdk_wait_usec(1);
545 
546     /* 10. Enable conditional coprocessor clock of UCTL by writing USB-
547         DRD(0..1)_UCTL_CTL[CSCLK_EN] = 1. */
548     if (is_usbdrd)
549     {
550         if (CAVIUM_IS_MODEL(CAVIUM_CN8XXX))
551         {
552             /* CN9XXX make coprocessor clock automatic */
553             BDK_CSR_MODIFY(c, node, BDK_USBDRDX_UCTL_CTL(usb_port),
554                 c.cn83xx.csclk_en = 1);
555         }
556     }
557     else
558     {
559         BDK_CSR_MODIFY(c, node, BDK_USBHX_UCTL_CTL(usb_port),
560             c.s.csclk_en = 1);
561     }
562 
563     /* 11. Set USBDRD(0..1)_UCTL_CTL[DRD_MODE] to 1 for device mode, 0 for
564         host mode. */
565     if (is_usbdrd)
566     {
567         BDK_CSR_MODIFY(c, node, BDK_USBDRDX_UCTL_CTL(usb_port),
568             c.s.drd_mode = 0);
569     }
570 
571     /* 12. Soft reset the UPHY and UAHC logic via the UAHC controls:
572         a.  USBDRD(0..1)_UAHC_GUSB2PHYCFG(0)[PHYSOFTRST] = 1
573         b. USBDRD(0..1)_UAHC_GUSB3PIPECTL(0)[PHYSOFTRST] = 1
574         c. USBDRD(0..1)_UAHC_GCTL[CORESOFTRESET] = 1 */
575     if (is_usbdrd)
576     {
577         BDK_CSR_MODIFY(c, node, BDK_USBDRDX_UAHC_GUSB2PHYCFGX(usb_port, 0),
578             c.s.physoftrst = 1);
579         BDK_CSR_MODIFY(c, node, BDK_USBDRDX_UAHC_GUSB3PIPECTLX(usb_port, 0),
580             c.s.physoftrst = 1);
581         BDK_CSR_MODIFY(c, node, BDK_USBDRDX_UAHC_GCTL(usb_port),
582             c.s.coresoftreset = 1);
583     }
584     else
585     {
586         BDK_CSR_MODIFY(c, node, BDK_USBHX_UAHC_GUSB2PHYCFGX(usb_port, 0),
587             c.s.physoftrst = 1);
588         BDK_CSR_MODIFY(c, node, BDK_USBHX_UAHC_GUSB3PIPECTLX(usb_port, 0),
589             c.s.physoftrst = 1);
590         BDK_CSR_MODIFY(c, node, BDK_USBHX_UAHC_GCTL(usb_port),
591             c.s.coresoftreset = 1);
592     }
593 
594     /* 13. Program USBDRD(0..1)_UAHC_GCTL[PRTCAPDIR] to 0x2 for device mode
595         or 0x1 for host mode. */
596     if (is_usbdrd)
597     {
598         BDK_CSR_MODIFY(c, node, BDK_USBDRDX_UAHC_GCTL(usb_port),
599             c.s.prtcapdir = 1);
600     }
601     else
602     {
603         BDK_CSR_MODIFY(c, node, BDK_USBHX_UAHC_GCTL(usb_port),
604             c.s.prtcapdir = 1);
605     }
606 
607     /* 14. Wait 10us after step 13. for the PHY to complete its reset. */
608     bdk_wait_usec(10);
609 
610     /* 15. Deassert UPHY reset: USBDRD(0..1)_UCTL_CTL[UPHY_RST] = 0. */
611     if (is_usbdrd)
612     {
613         BDK_CSR_MODIFY(c, node, BDK_USBDRDX_UCTL_CTL(usb_port),
614             c.s.uphy_rst = 0);
615     }
616     else
617     {
618         BDK_CSR_MODIFY(c, node, BDK_USBHX_UCTL_CTL(usb_port),
619             c.s.uphy_rst = 0);
620     }
621 
622     /* 16. Wait for at least 45us after step 15. for UPHY to output
623         stable PHYCLOCK. */
624     bdk_wait_usec(45);
625 
626     /* Workround Errata USB-29206 */
627     if (dwc3_uphy_check_pll(node, usb_port))
628         return -1;
629 
630     /* 17. Initialize any other strap signals necessary and make sure they
631        propagate by reading back the last register written.
632         a.  UCTL
633             USBDRD(0..1)_UCTL_PORT0_CFG_*[*_TUNE]
634             USBDRD(0..1)_UCTL_PORT0_CFG_*[PCS_*]
635             USBDRD(0..1)_UCTL_PORT0_CFG_*[LANE0_TX_TERM_OFFSET]
636             USBDRD(0..1)_UCTL_PORT0_CFG_*[TX_VBOOST_LVL]
637             USBDRD(0..1)_UCTL__PORT0_CFG_*[LOS_BIAS]
638             USBDRD(0..1)_UCTL_HOST_CFG
639             USBDRD(0..1)_UCTL_SHIM_CFG
640         b.  UAHC: only the following UAHC registers are accessible during
641             CoreSoftReset.
642             USBDRD(0..1)_UAHC_GCTL
643             USBDRD(0..1)_UAHC_GUCTL
644             USBDRD(0..1)_UAHC_GSTS
645             USBDRD(0..1)_UAHC_GUID
646             USBDRD(0..1)_UAHC_GUSB2PHYCFG(0)
647             USBDRD(0..1)_UAHC_GUSB3PIPECTL(0) */
648 
649     /* 18. Release soft reset the UPHY and UAHC logic via the UAHC controls:
650         a.  USBDRD(0..1)_UAHC_GUSB2PHYCFG(0)[PHYSOFTRST] = 0
651         b. USBDRD(0..1)_UAHC_GUSB3PIPECTL(0)[PHYSOFTRST] = 0
652         c. USBDRD(0..1)_UAHC_GCTL[CORESOFTRESET] = 0 */
653     if (is_usbdrd)
654     {
655         BDK_CSR_MODIFY(c, node, BDK_USBDRDX_UAHC_GUSB2PHYCFGX(usb_port, 0),
656             c.s.physoftrst = 0);
657         BDK_CSR_MODIFY(c, node, BDK_USBDRDX_UAHC_GUSB3PIPECTLX(usb_port, 0),
658             c.s.physoftrst = 0);
659         BDK_CSR_MODIFY(c, node, BDK_USBDRDX_UAHC_GCTL(usb_port),
660             c.s.coresoftreset = 0);
661     }
662     else
663     {
664         BDK_CSR_MODIFY(c, node, BDK_USBHX_UAHC_GUSB2PHYCFGX(usb_port, 0),
665             c.s.physoftrst = 0);
666         BDK_CSR_MODIFY(c, node, BDK_USBHX_UAHC_GUSB3PIPECTLX(usb_port, 0),
667             c.s.physoftrst = 0);
668         BDK_CSR_MODIFY(c, node, BDK_USBHX_UAHC_GCTL(usb_port),
669             c.s.coresoftreset = 0);
670     }
671 
672     /* 19. Configure the remaining UAHC_G* registers as needed, including
673        any that were not configured in step 17.-b. */
674 
675     /* 20. Initialize the USB controller:
676         a. To initialize the UAHC as a USB host controller, the application
677             should perform the steps described in the xHCI specification
678             (UAHC_X* registers). The xHCI sequence starts with poll for a 0 in
679             USBDRD(0..1)_UAHC_USBSTS[CNR].
680         b. To initialize the UAHC as a device, the application should TBD. The
681             device initiation sequence starts with a device soft reset by
682             setting USBDRD(0..1)_UAHC_DCTL[CSFTRST] = 1 and wait for a read
683             operation to return 0. */
684     return 0;
685 }
686