1215976Sjmallett/***********************license start***************
2232812Sjmallett * Copyright (c) 2003-2010  Cavium Inc. (support@cavium.com). All rights
3215976Sjmallett * reserved.
4215976Sjmallett *
5215976Sjmallett *
6215976Sjmallett * Redistribution and use in source and binary forms, with or without
7215976Sjmallett * modification, are permitted provided that the following conditions are
8215976Sjmallett * met:
9215976Sjmallett *
10215976Sjmallett *   * Redistributions of source code must retain the above copyright
11215976Sjmallett *     notice, this list of conditions and the following disclaimer.
12215976Sjmallett *
13215976Sjmallett *   * Redistributions in binary form must reproduce the above
14215976Sjmallett *     copyright notice, this list of conditions and the following
15215976Sjmallett *     disclaimer in the documentation and/or other materials provided
16215976Sjmallett *     with the distribution.
17215976Sjmallett
18232812Sjmallett *   * Neither the name of Cavium Inc. nor the names of
19215976Sjmallett *     its contributors may be used to endorse or promote products
20215976Sjmallett *     derived from this software without specific prior written
21215976Sjmallett *     permission.
22215976Sjmallett
23215976Sjmallett * This Software, including technical data, may be subject to U.S. export  control
24215976Sjmallett * laws, including the U.S. Export Administration Act and its  associated
25215976Sjmallett * regulations, and may be subject to export or import  regulations in other
26215976Sjmallett * countries.
27215976Sjmallett
28215976Sjmallett * TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SOFTWARE IS PROVIDED "AS IS"
29232812Sjmallett * AND WITH ALL FAULTS AND CAVIUM INC. MAKES NO PROMISES, REPRESENTATIONS OR
30215976Sjmallett * WARRANTIES, EITHER EXPRESS, IMPLIED, STATUTORY, OR OTHERWISE, WITH RESPECT TO
31215976Sjmallett * THE SOFTWARE, INCLUDING ITS CONDITION, ITS CONFORMITY TO ANY REPRESENTATION OR
32215976Sjmallett * DESCRIPTION, OR THE EXISTENCE OF ANY LATENT OR PATENT DEFECTS, AND CAVIUM
33215976Sjmallett * SPECIFICALLY DISCLAIMS ALL IMPLIED (IF ANY) WARRANTIES OF TITLE,
34215976Sjmallett * MERCHANTABILITY, NONINFRINGEMENT, FITNESS FOR A PARTICULAR PURPOSE, LACK OF
35215976Sjmallett * VIRUSES, ACCURACY OR COMPLETENESS, QUIET ENJOYMENT, QUIET POSSESSION OR
36215976Sjmallett * CORRESPONDENCE TO DESCRIPTION. THE ENTIRE  RISK ARISING OUT OF USE OR
37215976Sjmallett * PERFORMANCE OF THE SOFTWARE LIES WITH YOU.
38215976Sjmallett ***********************license end**************************************/
39215976Sjmallett
40215976Sjmallett
41215976Sjmallett/**
42215976Sjmallett * @file
43215976Sjmallett *
44215976Sjmallett * "cvmx-usbd.c" defines a set of low level USB functions to help
45215976Sjmallett * developers create Octeon USB devices for various operating
46215976Sjmallett * systems. These functions provide a generic API to the Octeon
47215976Sjmallett * USB blocks, hiding the internal hardware specific
48215976Sjmallett * operations.
49215976Sjmallett *
50215976Sjmallett * <hr>$Revision: 32636 $<hr>
51215976Sjmallett */
52215976Sjmallett
53215976Sjmallett#ifdef CVMX_BUILD_FOR_LINUX_KERNEL
54215976Sjmallett#include <asm/octeon/cvmx.h>
55215976Sjmallett#include <asm/octeon/cvmx-clock.h>
56215976Sjmallett#include <asm/octeon/cvmx-sysinfo.h>
57215976Sjmallett#include <asm/octeon/cvmx-usbnx-defs.h>
58215976Sjmallett#include <asm/octeon/cvmx-usbcx-defs.h>
59215976Sjmallett#include <asm/octeon/cvmx-usbd.h>
60215976Sjmallett#include <asm/octeon/cvmx-swap.h>
61215976Sjmallett#include <asm/octeon/cvmx-helper.h>
62215976Sjmallett#include <asm/octeon/cvmx-helper-board.h>
63215976Sjmallett#else
64215976Sjmallett#include "cvmx.h"
65215976Sjmallett#include "cvmx-clock.h"
66215976Sjmallett#include "cvmx-sysinfo.h"
67215976Sjmallett#include "cvmx-usbd.h"
68215976Sjmallett#include "cvmx-swap.h"
69215976Sjmallett#include "cvmx-helper.h"
70215976Sjmallett#include "cvmx-helper-board.h"
71215976Sjmallett#endif
72215976Sjmallett
73215976Sjmallett#define ULL unsigned long long
74215976Sjmallett
75215976Sjmallett/**
76215976Sjmallett * @INTERNAL
77215976Sjmallett * Read a USB 32bit CSR. It performs the necessary address swizzle for 32bit
78215976Sjmallett * CSRs.
79215976Sjmallett *
80215976Sjmallett * @param usb     USB device state populated by
81215976Sjmallett *                cvmx_usbd_initialize().
82215976Sjmallett * @param address 64bit address to read
83215976Sjmallett *
84215976Sjmallett * @return Result of the read
85215976Sjmallett */
86215976Sjmallettstatic inline uint32_t __cvmx_usbd_read_csr32(cvmx_usbd_state_t *usb, uint64_t address)
87215976Sjmallett{
88215976Sjmallett    uint32_t result = cvmx_read64_uint32(address ^ 4);
89215976Sjmallett    return result;
90215976Sjmallett}
91215976Sjmallett
92215976Sjmallett
93215976Sjmallett/**
94215976Sjmallett * @INTERNAL
95215976Sjmallett * Write a USB 32bit CSR. It performs the necessary address swizzle for 32bit
96215976Sjmallett * CSRs.
97215976Sjmallett *
98215976Sjmallett * @param usb     USB device state populated by
99215976Sjmallett *                cvmx_usbd_initialize().
100215976Sjmallett * @param address 64bit address to write
101215976Sjmallett * @param value   Value to write
102215976Sjmallett */
103215976Sjmallettstatic inline void __cvmx_usbd_write_csr32(cvmx_usbd_state_t *usb, uint64_t address, uint32_t value)
104215976Sjmallett{
105215976Sjmallett    cvmx_write64_uint32(address ^ 4, value);
106215976Sjmallett    cvmx_read64_uint64(CVMX_USBNX_DMA0_INB_CHN0(usb->index));
107215976Sjmallett}
108215976Sjmallett
109215976Sjmallett/**
110215976Sjmallett * @INTERNAL
111215976Sjmallett * Calls the user supplied callback when an event happens.
112215976Sjmallett *
113215976Sjmallett * @param usb    USB device state populated by
114215976Sjmallett *               cvmx_usbd_initialize().
115215976Sjmallett * @param reason Reason for the callback
116215976Sjmallett * @param endpoint_num
117215976Sjmallett *               Endpoint number
118215976Sjmallett * @param bytes_transferred
119215976Sjmallett *               Bytes transferred
120215976Sjmallett */
121215976Sjmallettstatic void __cvmx_usbd_callback(cvmx_usbd_state_t *usb, cvmx_usbd_callback_t reason, int endpoint_num, int bytes_transferred)
122215976Sjmallett{
123215976Sjmallett    if (usb->callback[reason])
124215976Sjmallett    {
125215976Sjmallett        if (cvmx_unlikely(usb->init_flags & CVMX_USBD_INITIALIZE_FLAGS_DEBUG))
126215976Sjmallett            cvmx_dprintf("%s: Calling callback reason=%d endpoint=%d bytes=%d func=%p data=%p\n",
127215976Sjmallett                __FUNCTION__, reason, endpoint_num, bytes_transferred, usb->callback[reason], usb->callback_data[reason]);
128215976Sjmallett        usb->callback[reason](reason, endpoint_num, bytes_transferred, usb->callback_data[reason]);
129215976Sjmallett    }
130215976Sjmallett    else
131215976Sjmallett    {
132215976Sjmallett        if (cvmx_unlikely(usb->init_flags & CVMX_USBD_INITIALIZE_FLAGS_DEBUG))
133215976Sjmallett            cvmx_dprintf("%s: No callback for reason=%d endpoint=%d bytes=%d\n",
134215976Sjmallett                __FUNCTION__, reason, endpoint_num, bytes_transferred);
135215976Sjmallett    }
136215976Sjmallett}
137215976Sjmallett
138215976Sjmallett/**
139215976Sjmallett * @INTERNAL
140215976Sjmallett * Perform USB device mode initialization after a reset completes.
141215976Sjmallett * This should be called after USBC0/1_GINTSTS[USBRESET] and
142215976Sjmallett * corresponds to section 22.6.1.1, "Initialization on USB Reset",
143215976Sjmallett * in the manual.
144215976Sjmallett *
145215976Sjmallett * @param usb    USB device state populated by
146215976Sjmallett *               cvmx_usbd_initialize().
147215976Sjmallett *
148215976Sjmallett * @return Zero or negative on error.
149215976Sjmallett */
150215976Sjmallettstatic int __cvmx_usbd_device_reset_complete(cvmx_usbd_state_t *usb)
151215976Sjmallett{
152215976Sjmallett    cvmx_usbcx_ghwcfg2_t usbcx_ghwcfg2;
153215976Sjmallett    cvmx_usbcx_ghwcfg3_t usbcx_ghwcfg3;
154215976Sjmallett    cvmx_usbcx_doepmsk_t usbcx_doepmsk;
155215976Sjmallett    cvmx_usbcx_diepmsk_t usbcx_diepmsk;
156215976Sjmallett    cvmx_usbcx_daintmsk_t usbc_daintmsk;
157215976Sjmallett    cvmx_usbcx_gnptxfsiz_t gnptxfsiz;
158215976Sjmallett    int fifo_space;
159215976Sjmallett    int i;
160215976Sjmallett
161215976Sjmallett    if (cvmx_unlikely(usb->init_flags & CVMX_USBD_INITIALIZE_FLAGS_DEBUG))
162215976Sjmallett        cvmx_dprintf("%s: Processing reset\n", __FUNCTION__);
163215976Sjmallett
164215976Sjmallett    usbcx_ghwcfg2.u32 = __cvmx_usbd_read_csr32(usb, CVMX_USBCX_GHWCFG2(usb->index));
165215976Sjmallett    usbcx_ghwcfg3.u32 = __cvmx_usbd_read_csr32(usb, CVMX_USBCX_GHWCFG3(usb->index));
166215976Sjmallett
167215976Sjmallett    /* Set up the data FIFO RAM for each of the FIFOs */
168215976Sjmallett    fifo_space = usbcx_ghwcfg3.s.dfifodepth;
169215976Sjmallett
170215976Sjmallett    /* Start at the top of the FIFO and assign space for each periodic fifo */
171215976Sjmallett    for (i=usbcx_ghwcfg2.s.numdeveps; i>0; i--)
172215976Sjmallett    {
173215976Sjmallett        cvmx_usbcx_dptxfsizx_t siz;
174215976Sjmallett        siz.u32 = __cvmx_usbd_read_csr32(usb, CVMX_USBCX_DPTXFSIZX(i, usb->index));
175215976Sjmallett        fifo_space -= siz.s.dptxfsize;
176215976Sjmallett        siz.s.dptxfstaddr = fifo_space;
177215976Sjmallett        __cvmx_usbd_write_csr32(usb, CVMX_USBCX_DPTXFSIZX(i, usb->index), siz.u32);
178215976Sjmallett    }
179215976Sjmallett
180215976Sjmallett    /* Assign half the leftover space to the non periodic tx fifo */
181215976Sjmallett    gnptxfsiz.u32 = __cvmx_usbd_read_csr32(usb, CVMX_USBCX_GNPTXFSIZ(usb->index));
182215976Sjmallett    gnptxfsiz.s.nptxfdep = fifo_space / 2;
183215976Sjmallett    fifo_space -= gnptxfsiz.s.nptxfdep;
184215976Sjmallett    gnptxfsiz.s.nptxfstaddr = fifo_space;
185215976Sjmallett    __cvmx_usbd_write_csr32(usb, CVMX_USBCX_GNPTXFSIZ(usb->index), gnptxfsiz.u32);
186215976Sjmallett
187215976Sjmallett    /* Assign the remain space to the RX fifo */
188215976Sjmallett    __cvmx_usbd_write_csr32(usb, CVMX_USBCX_GRXFSIZ(usb->index), fifo_space);
189215976Sjmallett
190215976Sjmallett    /* Unmask the common endpoint interrupts */
191215976Sjmallett    usbcx_doepmsk.u32 = 0;
192215976Sjmallett    usbcx_doepmsk.s.setupmsk = 1;
193215976Sjmallett    usbcx_doepmsk.s.epdisbldmsk = 1;
194215976Sjmallett    usbcx_doepmsk.s.xfercomplmsk = 1;
195215976Sjmallett    __cvmx_usbd_write_csr32(usb, CVMX_USBCX_DOEPMSK(usb->index), usbcx_doepmsk.u32);
196215976Sjmallett    usbcx_diepmsk.u32 = 0;
197215976Sjmallett    usbcx_diepmsk.s.epdisbldmsk = 1;
198215976Sjmallett    usbcx_diepmsk.s.xfercomplmsk = 1;
199215976Sjmallett    __cvmx_usbd_write_csr32(usb, CVMX_USBCX_DIEPMSK(usb->index), usbcx_diepmsk.u32);
200215976Sjmallett
201215976Sjmallett    usbc_daintmsk.u32 = 0;
202215976Sjmallett    usbc_daintmsk.s.inepmsk = -1;
203215976Sjmallett    usbc_daintmsk.s.outepmsk = -1;
204215976Sjmallett    __cvmx_usbd_write_csr32(usb, CVMX_USBCX_DAINTMSK(usb->index), usbc_daintmsk.u32);
205215976Sjmallett
206215976Sjmallett    /* Set all endpoints to NAK */
207215976Sjmallett    for (i=0; i<usbcx_ghwcfg2.s.numdeveps+1; i++)
208215976Sjmallett    {
209215976Sjmallett        cvmx_usbcx_doepctlx_t usbc_doepctl;
210215976Sjmallett        usbc_doepctl.u32 = 0;
211215976Sjmallett        usbc_doepctl.s.snak = 1;
212215976Sjmallett        usbc_doepctl.s.usbactep = 1;
213215976Sjmallett        usbc_doepctl.s.mps = (i==0) ? 0 : 64;
214215976Sjmallett        __cvmx_usbd_write_csr32(usb, CVMX_USBCX_DOEPCTLX(i, usb->index), usbc_doepctl.u32);
215215976Sjmallett    }
216215976Sjmallett
217215976Sjmallett    return 0;
218215976Sjmallett}
219215976Sjmallett
220215976Sjmallett
221215976Sjmallett/**
222215976Sjmallett * Initialize a USB port for use. This must be called before any
223215976Sjmallett * other access to the Octeon USB port is made. The port starts
224215976Sjmallett * off in the disabled state.
225215976Sjmallett *
226215976Sjmallett * @param usb    Pointer to an empty cvmx_usbd_state_t structure
227215976Sjmallett *               that will be populated by the initialize call.
228215976Sjmallett *               This structure is then passed to all other USB
229215976Sjmallett *               functions.
230215976Sjmallett * @param usb_port_number
231215976Sjmallett *               Which Octeon USB port to initialize.
232215976Sjmallett * @param flags  Flags to control hardware initialization. See
233215976Sjmallett *               cvmx_usbd_initialize_flags_t for the flag
234215976Sjmallett *               definitions. Some flags are mandatory.
235215976Sjmallett *
236215976Sjmallett * @return Zero or a negative on error.
237215976Sjmallett */
238215976Sjmallettint cvmx_usbd_initialize(cvmx_usbd_state_t *usb,
239215976Sjmallett                                      int usb_port_number,
240215976Sjmallett                                      cvmx_usbd_initialize_flags_t flags)
241215976Sjmallett{
242215976Sjmallett    cvmx_usbnx_clk_ctl_t usbn_clk_ctl;
243215976Sjmallett    cvmx_usbnx_usbp_ctl_status_t usbn_usbp_ctl_status;
244215976Sjmallett
245215976Sjmallett    if (cvmx_unlikely(flags & CVMX_USBD_INITIALIZE_FLAGS_DEBUG))
246215976Sjmallett        cvmx_dprintf("%s: Called\n", __FUNCTION__);
247215976Sjmallett
248250132Seadler    memset(usb, 0, sizeof(*usb));
249215976Sjmallett    usb->init_flags = flags;
250215976Sjmallett    usb->index = usb_port_number;
251215976Sjmallett
252215976Sjmallett    /* Try to determine clock type automatically */
253215976Sjmallett    if ((usb->init_flags & (CVMX_USBD_INITIALIZE_FLAGS_CLOCK_XO_XI |
254215976Sjmallett                  CVMX_USBD_INITIALIZE_FLAGS_CLOCK_XO_GND)) == 0)
255215976Sjmallett    {
256215976Sjmallett        if (__cvmx_helper_board_usb_get_clock_type() == USB_CLOCK_TYPE_CRYSTAL_12)
257215976Sjmallett            usb->init_flags |= CVMX_USBD_INITIALIZE_FLAGS_CLOCK_XO_XI;  /* Only 12 MHZ crystals are supported */
258215976Sjmallett        else
259215976Sjmallett            usb->init_flags |= CVMX_USBD_INITIALIZE_FLAGS_CLOCK_XO_GND;
260215976Sjmallett    }
261215976Sjmallett
262215976Sjmallett    if (usb->init_flags & CVMX_USBD_INITIALIZE_FLAGS_CLOCK_XO_GND)
263215976Sjmallett    {
264215976Sjmallett        /* Check for auto ref clock frequency */
265215976Sjmallett        if (!(usb->init_flags & CVMX_USBD_INITIALIZE_FLAGS_CLOCK_MHZ_MASK))
266215976Sjmallett            switch (__cvmx_helper_board_usb_get_clock_type())
267215976Sjmallett            {
268215976Sjmallett                case USB_CLOCK_TYPE_REF_12:
269215976Sjmallett                    usb->init_flags |= CVMX_USBD_INITIALIZE_FLAGS_CLOCK_12MHZ;
270215976Sjmallett                    break;
271215976Sjmallett                case USB_CLOCK_TYPE_REF_24:
272215976Sjmallett                    usb->init_flags |= CVMX_USBD_INITIALIZE_FLAGS_CLOCK_24MHZ;
273215976Sjmallett                    break;
274215976Sjmallett                case USB_CLOCK_TYPE_REF_48:
275215976Sjmallett                default:
276215976Sjmallett                    usb->init_flags |= CVMX_USBD_INITIALIZE_FLAGS_CLOCK_48MHZ;
277215976Sjmallett                    break;
278215976Sjmallett            }
279215976Sjmallett    }
280215976Sjmallett
281215976Sjmallett    /* Power On Reset and PHY Initialization */
282215976Sjmallett
283215976Sjmallett    /* 1. Wait for DCOK to assert (nothing to do) */
284215976Sjmallett    /* 2a. Write USBN0/1_CLK_CTL[POR] = 1 and
285215976Sjmallett        USBN0/1_CLK_CTL[HRST,PRST,HCLK_RST] = 0 */
286215976Sjmallett    usbn_clk_ctl.u64 = cvmx_read_csr(CVMX_USBNX_CLK_CTL(usb->index));
287215976Sjmallett    usbn_clk_ctl.s.por = 1;
288215976Sjmallett    usbn_clk_ctl.s.hrst = 0;
289215976Sjmallett    usbn_clk_ctl.s.prst = 0;
290215976Sjmallett    usbn_clk_ctl.s.hclk_rst = 0;
291215976Sjmallett    usbn_clk_ctl.s.enable = 0;
292215976Sjmallett    /* 2b. Select the USB reference clock/crystal parameters by writing
293215976Sjmallett        appropriate values to USBN0/1_CLK_CTL[P_C_SEL, P_RTYPE, P_COM_ON] */
294215976Sjmallett    if (usb->init_flags & CVMX_USBD_INITIALIZE_FLAGS_CLOCK_XO_GND)
295215976Sjmallett    {
296215976Sjmallett        /* The USB port uses 12/24/48MHz 2.5V board clock
297215976Sjmallett            source at USB_XO. USB_XI should be tied to GND.
298215976Sjmallett            Most Octeon evaluation boards require this setting */
299215976Sjmallett        if (OCTEON_IS_MODEL(OCTEON_CN3XXX))
300215976Sjmallett        {
301215976Sjmallett            usbn_clk_ctl.cn31xx.p_rclk  = 1; /* From CN31XX,CN30XX manual */
302215976Sjmallett            usbn_clk_ctl.cn31xx.p_xenbn = 0;
303215976Sjmallett        }
304215976Sjmallett        else if (OCTEON_IS_MODEL(OCTEON_CN56XX) || OCTEON_IS_MODEL(OCTEON_CN50XX))
305215976Sjmallett            usbn_clk_ctl.cn56xx.p_rtype = 2; /* From CN56XX,CN50XX manual */
306215976Sjmallett        else
307215976Sjmallett            usbn_clk_ctl.cn52xx.p_rtype = 1; /* From CN52XX manual */
308215976Sjmallett
309215976Sjmallett        switch (usb->init_flags & CVMX_USBD_INITIALIZE_FLAGS_CLOCK_MHZ_MASK)
310215976Sjmallett        {
311215976Sjmallett            case CVMX_USBD_INITIALIZE_FLAGS_CLOCK_12MHZ:
312215976Sjmallett                usbn_clk_ctl.s.p_c_sel = 0;
313215976Sjmallett                break;
314215976Sjmallett            case CVMX_USBD_INITIALIZE_FLAGS_CLOCK_24MHZ:
315215976Sjmallett                usbn_clk_ctl.s.p_c_sel = 1;
316215976Sjmallett                break;
317215976Sjmallett            case CVMX_USBD_INITIALIZE_FLAGS_CLOCK_48MHZ:
318215976Sjmallett                usbn_clk_ctl.s.p_c_sel = 2;
319215976Sjmallett                break;
320215976Sjmallett        }
321215976Sjmallett    }
322215976Sjmallett    else
323215976Sjmallett    {
324215976Sjmallett        /* The USB port uses a 12MHz crystal as clock source
325215976Sjmallett            at USB_XO and USB_XI */
326215976Sjmallett        if (OCTEON_IS_MODEL(OCTEON_CN3XXX))
327215976Sjmallett        {
328215976Sjmallett            usbn_clk_ctl.cn31xx.p_rclk  = 1; /* From CN31XX,CN30XX manual */
329215976Sjmallett            usbn_clk_ctl.cn31xx.p_xenbn = 1;
330215976Sjmallett        }
331215976Sjmallett        else if (OCTEON_IS_MODEL(OCTEON_CN56XX) || OCTEON_IS_MODEL(OCTEON_CN50XX))
332215976Sjmallett            usbn_clk_ctl.cn56xx.p_rtype = 0; /* From CN56XX,CN50XX manual */
333215976Sjmallett        else
334215976Sjmallett            usbn_clk_ctl.cn52xx.p_rtype = 0; /* From CN52XX manual */
335215976Sjmallett
336215976Sjmallett        usbn_clk_ctl.s.p_c_sel = 0;
337215976Sjmallett    }
338215976Sjmallett    /* 2c. Select the HCLK via writing USBN0/1_CLK_CTL[DIVIDE, DIVIDE2] and
339215976Sjmallett        setting USBN0/1_CLK_CTL[ENABLE] = 1.  Divide the core clock down such
340215976Sjmallett        that USB is as close as possible to 125Mhz */
341215976Sjmallett    {
342215976Sjmallett        int divisor = (cvmx_clock_get_rate(CVMX_CLOCK_CORE)+125000000-1)/125000000;
343215976Sjmallett        if (divisor < 4)  /* Lower than 4 doesn't seem to work properly */
344215976Sjmallett            divisor = 4;
345215976Sjmallett        usbn_clk_ctl.s.divide = divisor;
346215976Sjmallett        usbn_clk_ctl.s.divide2 = 0;
347215976Sjmallett    }
348215976Sjmallett    cvmx_write_csr(CVMX_USBNX_CLK_CTL(usb->index), usbn_clk_ctl.u64);
349215976Sjmallett    /* 2d. Write USBN0/1_CLK_CTL[HCLK_RST] = 1 */
350215976Sjmallett    usbn_clk_ctl.s.hclk_rst = 1;
351215976Sjmallett    cvmx_write_csr(CVMX_USBNX_CLK_CTL(usb->index), usbn_clk_ctl.u64);
352215976Sjmallett    /* 2e.  Wait 64 core-clock cycles for HCLK to stabilize */
353215976Sjmallett    cvmx_wait(64);
354215976Sjmallett    /* 3. Program the power-on reset field in the USBN clock-control register:
355215976Sjmallett        USBN_CLK_CTL[POR] = 0 */
356215976Sjmallett    usbn_clk_ctl.s.por = 0;
357215976Sjmallett    cvmx_write_csr(CVMX_USBNX_CLK_CTL(usb->index), usbn_clk_ctl.u64);
358215976Sjmallett    /* 4. Wait 1 ms for PHY clock to start */
359215976Sjmallett    cvmx_wait_usec(1000);
360215976Sjmallett    /* 5. Program the Reset input from automatic test equipment field in the
361215976Sjmallett        USBP control and status register: USBN_USBP_CTL_STATUS[ATE_RESET] = 1 */
362215976Sjmallett    usbn_usbp_ctl_status.u64 = cvmx_read_csr(CVMX_USBNX_USBP_CTL_STATUS(usb->index));
363215976Sjmallett    usbn_usbp_ctl_status.s.ate_reset = 1;
364215976Sjmallett    cvmx_write_csr(CVMX_USBNX_USBP_CTL_STATUS(usb->index), usbn_usbp_ctl_status.u64);
365215976Sjmallett    /* 6. Wait 10 cycles */
366215976Sjmallett    cvmx_wait(10);
367215976Sjmallett    /* 7. Clear ATE_RESET field in the USBN clock-control register:
368215976Sjmallett        USBN_USBP_CTL_STATUS[ATE_RESET] = 0 */
369215976Sjmallett    usbn_usbp_ctl_status.s.ate_reset = 0;
370215976Sjmallett    cvmx_write_csr(CVMX_USBNX_USBP_CTL_STATUS(usb->index), usbn_usbp_ctl_status.u64);
371215976Sjmallett    /* 8. Program the PHY reset field in the USBN clock-control register:
372215976Sjmallett        USBN_CLK_CTL[PRST] = 1 */
373215976Sjmallett    usbn_clk_ctl.s.prst = 1;
374215976Sjmallett    cvmx_write_csr(CVMX_USBNX_CLK_CTL(usb->index), usbn_clk_ctl.u64);
375215976Sjmallett    /* 9. Program the USBP control and status register to select host or
376215976Sjmallett        device mode. USBN_USBP_CTL_STATUS[HST_MODE] = 0 for host, = 1 for
377215976Sjmallett        device */
378215976Sjmallett    usbn_usbp_ctl_status.s.hst_mode = 1;
379215976Sjmallett    usbn_usbp_ctl_status.s.dm_pulld = 0;
380215976Sjmallett    usbn_usbp_ctl_status.s.dp_pulld = 0;
381215976Sjmallett    cvmx_write_csr(CVMX_USBNX_USBP_CTL_STATUS(usb->index), usbn_usbp_ctl_status.u64);
382215976Sjmallett    /* 10. Wait 1 �s */
383215976Sjmallett    cvmx_wait_usec(1);
384215976Sjmallett    /* 11. Program the hreset_n field in the USBN clock-control register:
385215976Sjmallett        USBN_CLK_CTL[HRST] = 1 */
386215976Sjmallett    usbn_clk_ctl.s.hrst = 1;
387215976Sjmallett    cvmx_write_csr(CVMX_USBNX_CLK_CTL(usb->index), usbn_clk_ctl.u64);
388215976Sjmallett    /* 12. Proceed to USB core initialization */
389215976Sjmallett    usbn_clk_ctl.s.enable = 1;
390215976Sjmallett    cvmx_write_csr(CVMX_USBNX_CLK_CTL(usb->index), usbn_clk_ctl.u64);
391215976Sjmallett    cvmx_wait_usec(1);
392215976Sjmallett
393215976Sjmallett    /* Program the following fields in the global AHB configuration
394215976Sjmallett        register (USBC_GAHBCFG)
395215976Sjmallett        DMA mode, USBC_GAHBCFG[DMAEn]: 1 = DMA mode, 0 = slave mode
396215976Sjmallett        Burst length, USBC_GAHBCFG[HBSTLEN] = 0
397215976Sjmallett        Nonperiodic TxFIFO empty level (slave mode only),
398215976Sjmallett        USBC_GAHBCFG[NPTXFEMPLVL]
399215976Sjmallett        Periodic TxFIFO empty level (slave mode only),
400215976Sjmallett        USBC_GAHBCFG[PTXFEMPLVL]
401215976Sjmallett        Global interrupt mask, USBC_GAHBCFG[GLBLINTRMSK] = 1 */
402215976Sjmallett    {
403215976Sjmallett        cvmx_usbcx_gahbcfg_t usbcx_gahbcfg;
404215976Sjmallett        usbcx_gahbcfg.u32 = 0;
405215976Sjmallett        usbcx_gahbcfg.s.dmaen = 1;
406215976Sjmallett        usbcx_gahbcfg.s.hbstlen = 0;
407215976Sjmallett        usbcx_gahbcfg.s.nptxfemplvl = 1;
408215976Sjmallett        usbcx_gahbcfg.s.ptxfemplvl = 1;
409215976Sjmallett        usbcx_gahbcfg.s.glblintrmsk = 1;
410215976Sjmallett        __cvmx_usbd_write_csr32(usb, CVMX_USBCX_GAHBCFG(usb->index), usbcx_gahbcfg.u32);
411215976Sjmallett    }
412215976Sjmallett
413215976Sjmallett    /* Program the following fields in USBC_GUSBCFG register.
414215976Sjmallett        HS/FS timeout calibration, USBC_GUSBCFG[TOUTCAL] = 0
415215976Sjmallett        ULPI DDR select, USBC_GUSBCFG[DDRSEL] = 0
416215976Sjmallett        USB turnaround time, USBC_GUSBCFG[USBTRDTIM] = 0x5
417215976Sjmallett        PHY low-power clock select, USBC_GUSBCFG[PHYLPWRCLKSEL] = 0 */
418215976Sjmallett    {
419215976Sjmallett        cvmx_usbcx_gusbcfg_t usbcx_gusbcfg;
420215976Sjmallett        usbcx_gusbcfg.u32 = __cvmx_usbd_read_csr32(usb, CVMX_USBCX_GUSBCFG(usb->index));
421215976Sjmallett        usbcx_gusbcfg.s.toutcal = 0;
422215976Sjmallett        usbcx_gusbcfg.s.ddrsel = 0;
423215976Sjmallett        usbcx_gusbcfg.s.usbtrdtim = 0x5;
424215976Sjmallett        usbcx_gusbcfg.s.phylpwrclksel = 0;
425215976Sjmallett        __cvmx_usbd_write_csr32(usb, CVMX_USBCX_GUSBCFG(usb->index), usbcx_gusbcfg.u32);
426215976Sjmallett    }
427215976Sjmallett
428215976Sjmallett    /* Program the following fields in the USBC0/1_DCFG register:
429215976Sjmallett        Device speed, USBC0/1_DCFG[DEVSPD] = 0 (high speed)
430215976Sjmallett        Non-zero-length status OUT handshake, USBC0/1_DCFG[NZSTSOUTHSHK]=0
431215976Sjmallett        Periodic frame interval (if periodic endpoints are supported),
432215976Sjmallett        USBC0/1_DCFG[PERFRINT] = 1 */
433215976Sjmallett    {
434215976Sjmallett        cvmx_usbcx_dcfg_t usbcx_dcfg;
435215976Sjmallett        usbcx_dcfg.u32 = __cvmx_usbd_read_csr32(usb, CVMX_USBCX_DCFG(usb->index));
436215976Sjmallett        usbcx_dcfg.s.devspd = 0;
437215976Sjmallett        usbcx_dcfg.s.nzstsouthshk = 0;
438215976Sjmallett        usbcx_dcfg.s.perfrint = 1;
439215976Sjmallett        __cvmx_usbd_write_csr32(usb, CVMX_USBCX_DCFG(usb->index), usbcx_dcfg.u32);
440215976Sjmallett    }
441215976Sjmallett
442215976Sjmallett    /* Program the USBC0/1_GINTMSK register */
443215976Sjmallett    {
444215976Sjmallett        cvmx_usbcx_gintmsk_t usbcx_gintmsk;
445215976Sjmallett        usbcx_gintmsk.u32 = __cvmx_usbd_read_csr32(usb, CVMX_USBCX_GINTMSK(usb->index));
446215976Sjmallett        usbcx_gintmsk.s.oepintmsk = 1;
447215976Sjmallett        usbcx_gintmsk.s.inepintmsk = 1;
448215976Sjmallett        usbcx_gintmsk.s.enumdonemsk = 1;
449215976Sjmallett        usbcx_gintmsk.s.usbrstmsk = 1;
450215976Sjmallett        usbcx_gintmsk.s.usbsuspmsk = 1;
451215976Sjmallett        __cvmx_usbd_write_csr32(usb, CVMX_USBCX_GINTMSK(usb->index), usbcx_gintmsk.u32);
452215976Sjmallett    }
453215976Sjmallett
454215976Sjmallett    cvmx_usbd_disable(usb);
455215976Sjmallett    return 0;
456215976Sjmallett}
457215976Sjmallett#ifdef CVMX_BUILD_FOR_LINUX_KERNEL
458215976SjmallettEXPORT_SYMBOL(cvmx_usbd_initialize);
459215976Sjmallett#endif
460215976Sjmallett
461215976Sjmallett
462215976Sjmallett/**
463215976Sjmallett * Shutdown a USB port after a call to cvmx_usbd_initialize().
464215976Sjmallett *
465215976Sjmallett * @param usb    USB device state populated by
466215976Sjmallett *               cvmx_usbd_initialize().
467215976Sjmallett *
468215976Sjmallett * @return Zero or a negative on error.
469215976Sjmallett */
470215976Sjmallettint cvmx_usbd_shutdown(cvmx_usbd_state_t *usb)
471215976Sjmallett{
472215976Sjmallett    cvmx_usbnx_clk_ctl_t usbn_clk_ctl;
473215976Sjmallett
474215976Sjmallett    if (cvmx_unlikely(usb->init_flags & CVMX_USBD_INITIALIZE_FLAGS_DEBUG))
475215976Sjmallett        cvmx_dprintf("%s: Called\n", __FUNCTION__);
476215976Sjmallett
477215976Sjmallett    /* Disable the clocks and put them in power on reset */
478215976Sjmallett    usbn_clk_ctl.u64 = cvmx_read_csr(CVMX_USBNX_CLK_CTL(usb->index));
479215976Sjmallett    usbn_clk_ctl.s.enable = 1;
480215976Sjmallett    usbn_clk_ctl.s.por = 1;
481215976Sjmallett    usbn_clk_ctl.s.hclk_rst = 1;
482215976Sjmallett    usbn_clk_ctl.s.prst = 0;
483215976Sjmallett    usbn_clk_ctl.s.hrst = 0;
484215976Sjmallett    cvmx_write_csr(CVMX_USBNX_CLK_CTL(usb->index), usbn_clk_ctl.u64);
485215976Sjmallett    return 0;
486215976Sjmallett}
487215976Sjmallett#ifdef CVMX_BUILD_FOR_LINUX_KERNEL
488215976SjmallettEXPORT_SYMBOL(cvmx_usbd_shutdown);
489215976Sjmallett#endif
490215976Sjmallett
491215976Sjmallett
492215976Sjmallett/**
493215976Sjmallett * Enable a USB port. After this call succeeds, the USB port is
494215976Sjmallett * online and servicing requests.
495215976Sjmallett *
496215976Sjmallett * @param usb  USB device state populated by
497215976Sjmallett *               cvmx_usb_initialize().
498215976Sjmallett *
499215976Sjmallett * @return Zero or negative on error.
500215976Sjmallett */
501215976Sjmallettint cvmx_usbd_enable(cvmx_usbd_state_t *usb)
502215976Sjmallett{
503215976Sjmallett    cvmx_usbcx_dctl_t usbcx_dctl;
504215976Sjmallett    usbcx_dctl.u32 = __cvmx_usbd_read_csr32(usb, CVMX_USBCX_DCTL(usb->index));
505215976Sjmallett    usbcx_dctl.s.cgoutnak = 1;
506215976Sjmallett    usbcx_dctl.s.sftdiscon = 0;
507215976Sjmallett    __cvmx_usbd_write_csr32(usb, CVMX_USBCX_DCTL(usb->index), usbcx_dctl.u32);
508215976Sjmallett    return 0;
509215976Sjmallett}
510215976Sjmallett#ifdef CVMX_BUILD_FOR_LINUX_KERNEL
511215976SjmallettEXPORT_SYMBOL(cvmx_usbd_enable);
512215976Sjmallett#endif
513215976Sjmallett
514215976Sjmallett
515215976Sjmallett/**
516215976Sjmallett * Disable a USB port. After this call the USB port will not
517215976Sjmallett * generate data transfers and will not generate events.
518215976Sjmallett *
519215976Sjmallett * @param usb    USB device state populated by
520215976Sjmallett *               cvmx_usb_initialize().
521215976Sjmallett *
522215976Sjmallett * @return Zero or negative on error.
523215976Sjmallett */
524215976Sjmallettint cvmx_usbd_disable(cvmx_usbd_state_t *usb)
525215976Sjmallett{
526215976Sjmallett    cvmx_usbcx_dctl_t usbcx_dctl;
527215976Sjmallett    usbcx_dctl.u32 = __cvmx_usbd_read_csr32(usb, CVMX_USBCX_DCTL(usb->index));
528215976Sjmallett    usbcx_dctl.s.sgoutnak = 1;
529215976Sjmallett    usbcx_dctl.s.sftdiscon = 1;
530215976Sjmallett    __cvmx_usbd_write_csr32(usb, CVMX_USBCX_DCTL(usb->index), usbcx_dctl.u32);
531215976Sjmallett    return 0;
532215976Sjmallett}
533215976Sjmallett#ifdef CVMX_BUILD_FOR_LINUX_KERNEL
534215976SjmallettEXPORT_SYMBOL(cvmx_usbd_disable);
535215976Sjmallett#endif
536215976Sjmallett
537215976Sjmallett
538215976Sjmallett/**
539215976Sjmallett * Register a callback function to process USB events
540215976Sjmallett *
541215976Sjmallett * @param usb       USB device state populated by
542215976Sjmallett *                  cvmx_usbd_initialize().
543215976Sjmallett * @param reason    The reason this callback should be called
544215976Sjmallett * @param func      Function to call
545215976Sjmallett * @param user_data User supplied data for the callback
546215976Sjmallett *
547215976Sjmallett * @return Zero on succes, negative on failure
548215976Sjmallett */
549215976Sjmallettint cvmx_usbd_register(cvmx_usbd_state_t *usb, cvmx_usbd_callback_t reason, cvmx_usbd_callback_func_t func, void *user_data)
550215976Sjmallett{
551215976Sjmallett    if (cvmx_unlikely(usb->init_flags & CVMX_USBD_INITIALIZE_FLAGS_DEBUG))
552215976Sjmallett        cvmx_dprintf("%s: Register reason=%d func=%p data=%p\n",
553215976Sjmallett            __FUNCTION__, reason, func, user_data);
554215976Sjmallett    usb->callback[reason] = func;
555215976Sjmallett    usb->callback_data[reason] = user_data;
556215976Sjmallett    return 0;
557215976Sjmallett}
558215976Sjmallett#ifdef CVMX_BUILD_FOR_LINUX_KERNEL
559215976SjmallettEXPORT_SYMBOL(cvmx_usbd_register);
560215976Sjmallett#endif
561215976Sjmallett
562215976Sjmallett/**
563215976Sjmallett * @INTERNAL
564215976Sjmallett * Poll a device mode endpoint for status
565215976Sjmallett *
566215976Sjmallett * @param usb    USB device state populated by
567215976Sjmallett *               cvmx_usbd_initialize().
568215976Sjmallett * @param endpoint_num
569215976Sjmallett *               Endpoint to poll
570215976Sjmallett *
571215976Sjmallett * @return Zero on success
572215976Sjmallett */
573215976Sjmallettstatic int __cvmx_usbd_poll_in_endpoint(cvmx_usbd_state_t *usb, int endpoint_num)
574215976Sjmallett{
575215976Sjmallett    cvmx_usbcx_diepintx_t usbc_diepint;
576215976Sjmallett
577215976Sjmallett    if (cvmx_unlikely(usb->init_flags & CVMX_USBD_INITIALIZE_FLAGS_DEBUG))
578215976Sjmallett        cvmx_dprintf("%s: endpoint=%d\n", __FUNCTION__, endpoint_num);
579215976Sjmallett
580215976Sjmallett    usbc_diepint.u32 = __cvmx_usbd_read_csr32(usb, CVMX_USBCX_DIEPINTX(endpoint_num, usb->index));
581215976Sjmallett    __cvmx_usbd_write_csr32(usb, CVMX_USBCX_DIEPINTX(endpoint_num, usb->index), usbc_diepint.u32);
582215976Sjmallett
583215976Sjmallett    if (usbc_diepint.s.epdisbld)
584215976Sjmallett    {
585215976Sjmallett        /* Endpoint Disabled Interrupt (EPDisbld)
586215976Sjmallett            This bit indicates that the endpoint is disabled per the
587215976Sjmallett            application's request. */
588215976Sjmallett        /* Nothing to do */
589215976Sjmallett    }
590215976Sjmallett    if (usbc_diepint.s.xfercompl)
591215976Sjmallett    {
592215976Sjmallett        cvmx_usbcx_dieptsizx_t usbc_dieptsiz;
593215976Sjmallett        int bytes_transferred;
594215976Sjmallett        /* Transfer Completed Interrupt (XferCompl)
595215976Sjmallett            Indicates that the programmed transfer is complete on the AHB
596215976Sjmallett            as well as on the USB, for this endpoint. */
597215976Sjmallett        usbc_dieptsiz.u32 = __cvmx_usbd_read_csr32(usb, CVMX_USBCX_DIEPTSIZX(endpoint_num, usb->index));
598215976Sjmallett        bytes_transferred = usb->endpoint[endpoint_num].buffer_length - usbc_dieptsiz.s.xfersize;
599215976Sjmallett        __cvmx_usbd_callback(usb, CVMX_USBD_CALLBACK_IN_COMPLETE, endpoint_num, bytes_transferred);
600215976Sjmallett    }
601215976Sjmallett    return 0;
602215976Sjmallett}
603215976Sjmallett
604215976Sjmallett
605215976Sjmallett/**
606215976Sjmallett * @INTERNAL
607215976Sjmallett * Poll a device mode endpoint for status
608215976Sjmallett *
609215976Sjmallett * @param usb    USB device state populated by
610215976Sjmallett *               cvmx_usbd_initialize().
611215976Sjmallett * @param endpoint_num
612215976Sjmallett *               Endpoint to poll
613215976Sjmallett *
614215976Sjmallett * @return Zero on success
615215976Sjmallett */
616215976Sjmallettstatic int __cvmx_usbd_poll_out_endpoint(cvmx_usbd_state_t *usb, int endpoint_num)
617215976Sjmallett{
618215976Sjmallett    cvmx_usbcx_doepintx_t usbc_doepint;
619215976Sjmallett
620215976Sjmallett    if (cvmx_unlikely(usb->init_flags & CVMX_USBD_INITIALIZE_FLAGS_DEBUG))
621215976Sjmallett        cvmx_dprintf("%s: endpoint=%d\n", __FUNCTION__, endpoint_num);
622215976Sjmallett
623215976Sjmallett    usbc_doepint.u32 = __cvmx_usbd_read_csr32(usb, CVMX_USBCX_DOEPINTX(endpoint_num, usb->index));
624215976Sjmallett    __cvmx_usbd_write_csr32(usb, CVMX_USBCX_DOEPINTX(endpoint_num, usb->index), usbc_doepint.u32);
625215976Sjmallett
626215976Sjmallett    if (usbc_doepint.s.setup)
627215976Sjmallett    {
628215976Sjmallett        /* SETUP Phase Done (SetUp)
629215976Sjmallett            Applies to control OUT endpoints only.
630215976Sjmallett            Indicates that the SETUP phase for the control endpoint is
631215976Sjmallett            complete and no more back-to-back SETUP packets were
632215976Sjmallett            received for the current control transfer. On this interrupt, the
633215976Sjmallett            application can decode the received SETUP data packet. */
634215976Sjmallett        __cvmx_usbd_callback(usb, CVMX_USBD_CALLBACK_DEVICE_SETUP, endpoint_num, 0);
635215976Sjmallett    }
636215976Sjmallett    if (usbc_doepint.s.epdisbld)
637215976Sjmallett    {
638215976Sjmallett        /* Endpoint Disabled Interrupt (EPDisbld)
639215976Sjmallett            This bit indicates that the endpoint is disabled per the
640215976Sjmallett            application's request. */
641215976Sjmallett        /* Nothing to do */
642215976Sjmallett    }
643215976Sjmallett    if (usbc_doepint.s.xfercompl)
644215976Sjmallett    {
645215976Sjmallett        cvmx_usbcx_doeptsizx_t usbc_doeptsiz;
646215976Sjmallett        int bytes_transferred;
647215976Sjmallett        /* Transfer Completed Interrupt (XferCompl)
648215976Sjmallett            Indicates that the programmed transfer is complete on the AHB
649215976Sjmallett            as well as on the USB, for this endpoint. */
650215976Sjmallett        usbc_doeptsiz.u32 = __cvmx_usbd_read_csr32(usb, CVMX_USBCX_DOEPTSIZX(endpoint_num, usb->index));
651215976Sjmallett        bytes_transferred = usb->endpoint[endpoint_num].buffer_length - usbc_doeptsiz.s.xfersize;
652215976Sjmallett        __cvmx_usbd_callback(usb, CVMX_USBD_CALLBACK_OUT_COMPLETE, endpoint_num, bytes_transferred);
653215976Sjmallett    }
654215976Sjmallett
655215976Sjmallett    return 0;
656215976Sjmallett}
657215976Sjmallett
658215976Sjmallett
659215976Sjmallett/**
660215976Sjmallett * Poll the USB block for status and call all needed callback
661215976Sjmallett * handlers. This function is meant to be called in the interrupt
662215976Sjmallett * handler for the USB controller. It can also be called
663215976Sjmallett * periodically in a loop for non-interrupt based operation.
664215976Sjmallett *
665215976Sjmallett * @param usb    USB device state populated by
666215976Sjmallett *               cvmx_usbd_initialize().
667215976Sjmallett *
668215976Sjmallett * @return Zero or negative on error.
669215976Sjmallett */
670215976Sjmallettint cvmx_usbd_poll(cvmx_usbd_state_t *usb)
671215976Sjmallett{
672215976Sjmallett    cvmx_usbcx_gintsts_t usbc_gintsts;
673215976Sjmallett
674215976Sjmallett    if (cvmx_unlikely(usb->init_flags & CVMX_USBD_INITIALIZE_FLAGS_DEBUG))
675215976Sjmallett        cvmx_dprintf("%s: Called\n", __FUNCTION__);
676215976Sjmallett
677215976Sjmallett    /* Read the pending interrupts */
678215976Sjmallett    usbc_gintsts.u32 = __cvmx_usbd_read_csr32(usb, CVMX_USBCX_GINTSTS(usb->index));
679215976Sjmallett    usbc_gintsts.u32 &= __cvmx_usbd_read_csr32(usb, CVMX_USBCX_GINTMSK(usb->index));
680215976Sjmallett
681215976Sjmallett    /* Clear the interrupts now that we know about them */
682215976Sjmallett    __cvmx_usbd_write_csr32(usb, CVMX_USBCX_GINTSTS(usb->index), usbc_gintsts.u32);
683215976Sjmallett
684215976Sjmallett    if (usbc_gintsts.s.usbsusp)
685215976Sjmallett        __cvmx_usbd_callback(usb, CVMX_USBD_CALLBACK_SUSPEND, 0, 0);
686215976Sjmallett
687215976Sjmallett    if (usbc_gintsts.s.enumdone)
688215976Sjmallett        __cvmx_usbd_callback(usb, CVMX_USBD_CALLBACK_ENUM_COMPLETE, 0, 0);
689215976Sjmallett
690215976Sjmallett    if (usbc_gintsts.s.usbrst)
691215976Sjmallett    {
692215976Sjmallett        /* USB Reset (USBRst)
693215976Sjmallett            The core sets this bit to indicate that a reset is
694215976Sjmallett            detected on the USB. */
695215976Sjmallett        __cvmx_usbd_device_reset_complete(usb);
696215976Sjmallett        __cvmx_usbd_callback(usb, CVMX_USBD_CALLBACK_RESET, 0, 0);
697215976Sjmallett    }
698215976Sjmallett
699215976Sjmallett    if (usbc_gintsts.s.oepint || usbc_gintsts.s.iepint)
700215976Sjmallett    {
701215976Sjmallett        cvmx_usbcx_daint_t usbc_daint;
702215976Sjmallett        usbc_daint.u32 = __cvmx_usbd_read_csr32(usb, CVMX_USBCX_DAINT(usb->index));
703215976Sjmallett        if (usbc_daint.s.inepint)
704215976Sjmallett        {
705215976Sjmallett            int active_endpoints = usbc_daint.s.inepint;
706215976Sjmallett
707215976Sjmallett            while (active_endpoints)
708215976Sjmallett            {
709215976Sjmallett                int endpoint;
710215976Sjmallett                CVMX_CLZ(endpoint, active_endpoints);
711215976Sjmallett                endpoint = 31 - endpoint;
712215976Sjmallett                __cvmx_usbd_poll_in_endpoint(usb, endpoint);
713215976Sjmallett                active_endpoints ^= 1<<endpoint;
714215976Sjmallett            }
715215976Sjmallett        }
716215976Sjmallett        if (usbc_daint.s.outepint)
717215976Sjmallett        {
718215976Sjmallett            int active_endpoints = usbc_daint.s.outepint;
719215976Sjmallett
720215976Sjmallett            while (active_endpoints)
721215976Sjmallett            {
722215976Sjmallett                int endpoint;
723215976Sjmallett                CVMX_CLZ(endpoint, active_endpoints);
724215976Sjmallett                endpoint = 31 - endpoint;
725215976Sjmallett                __cvmx_usbd_poll_out_endpoint(usb, endpoint);
726215976Sjmallett                active_endpoints ^= 1<<endpoint;
727215976Sjmallett            }
728215976Sjmallett        }
729215976Sjmallett    }
730215976Sjmallett
731215976Sjmallett    return 0;
732215976Sjmallett}
733215976Sjmallett#ifdef CVMX_BUILD_FOR_LINUX_KERNEL
734215976SjmallettEXPORT_SYMBOL(cvmx_usbd_poll);
735215976Sjmallett#endif
736215976Sjmallett
737215976Sjmallett/**
738215976Sjmallett * Get the current USB address
739215976Sjmallett *
740215976Sjmallett * @param usb    USB device state populated by
741215976Sjmallett *               cvmx_usbd_initialize().
742215976Sjmallett *
743215976Sjmallett * @return The USB address
744215976Sjmallett */
745215976Sjmallettint cvmx_usbd_get_address(cvmx_usbd_state_t *usb)
746215976Sjmallett{
747215976Sjmallett    cvmx_usbcx_dcfg_t usbc_dcfg;
748215976Sjmallett    usbc_dcfg.u32 = __cvmx_usbd_read_csr32(usb, CVMX_USBCX_DCFG(usb->index));
749215976Sjmallett    return usbc_dcfg.s.devaddr;
750215976Sjmallett}
751215976Sjmallett#ifdef CVMX_BUILD_FOR_LINUX_KERNEL
752215976SjmallettEXPORT_SYMBOL(cvmx_usbd_get_address);
753215976Sjmallett#endif
754215976Sjmallett
755215976Sjmallett/**
756215976Sjmallett * Set the current USB address
757215976Sjmallett *
758215976Sjmallett * @param usb     USB device state populated by
759215976Sjmallett *                cvmx_usbd_initialize().
760215976Sjmallett * @param address Address to set
761215976Sjmallett */
762215976Sjmallettvoid cvmx_usbd_set_address(cvmx_usbd_state_t *usb, int address)
763215976Sjmallett{
764215976Sjmallett    cvmx_usbcx_dcfg_t usbc_dcfg;
765215976Sjmallett    usbc_dcfg.u32 = __cvmx_usbd_read_csr32(usb, CVMX_USBCX_DCFG(usb->index));
766215976Sjmallett    usbc_dcfg.s.devaddr = address;
767215976Sjmallett    __cvmx_usbd_write_csr32(usb, CVMX_USBCX_DCFG(usb->index), usbc_dcfg.u32);
768215976Sjmallett}
769215976Sjmallett#ifdef CVMX_BUILD_FOR_LINUX_KERNEL
770215976SjmallettEXPORT_SYMBOL(cvmx_usbd_set_address);
771215976Sjmallett#endif
772215976Sjmallett
773215976Sjmallett/**
774215976Sjmallett * Get the current USB speed
775215976Sjmallett *
776215976Sjmallett * @param usb    USB device state populated by
777215976Sjmallett *               cvmx_usbd_initialize().
778215976Sjmallett *
779215976Sjmallett * @return The USB speed
780215976Sjmallett */
781215976Sjmallettcvmx_usbd_speed_t cvmx_usbd_get_speed(cvmx_usbd_state_t *usb)
782215976Sjmallett{
783215976Sjmallett    cvmx_usbcx_dsts_t usbcx_dsts;
784215976Sjmallett    usbcx_dsts.u32 = __cvmx_usbd_read_csr32(usb, CVMX_USBCX_DSTS(usb->index));
785215976Sjmallett    return usbcx_dsts.s.enumspd;
786215976Sjmallett}
787215976Sjmallett#ifdef CVMX_BUILD_FOR_LINUX_KERNEL
788215976SjmallettEXPORT_SYMBOL(cvmx_usbd_get_speed);
789215976Sjmallett#endif
790215976Sjmallett
791215976Sjmallett/**
792215976Sjmallett * Set the current USB speed
793215976Sjmallett *
794215976Sjmallett * @param usb    USB device state populated by
795215976Sjmallett *               cvmx_usbd_initialize().
796215976Sjmallett * @param speed  The requested speed
797215976Sjmallett */
798215976Sjmallettvoid cvmx_usbd_set_speed(cvmx_usbd_state_t *usb, cvmx_usbd_speed_t speed)
799215976Sjmallett{
800215976Sjmallett    cvmx_usbcx_dcfg_t usbcx_dcfg;
801215976Sjmallett    usbcx_dcfg.u32 = __cvmx_usbd_read_csr32(usb, CVMX_USBCX_DCFG(usb->index));
802215976Sjmallett    usbcx_dcfg.s.devspd = speed;
803215976Sjmallett    __cvmx_usbd_write_csr32(usb, CVMX_USBCX_DCFG(usb->index), usbcx_dcfg.u32);
804215976Sjmallett}
805215976Sjmallett#ifdef CVMX_BUILD_FOR_LINUX_KERNEL
806215976SjmallettEXPORT_SYMBOL(cvmx_usbd_set_speed);
807215976Sjmallett#endif
808215976Sjmallett
809215976Sjmallett/**
810215976Sjmallett * Enable an endpoint to respond to an OUT transaction
811215976Sjmallett *
812215976Sjmallett * @param usb    USB device state populated by
813215976Sjmallett *               cvmx_usbd_initialize().
814215976Sjmallett * @param endpoint_num
815215976Sjmallett *               Endpoint number to enable
816215976Sjmallett * @param transfer_type
817215976Sjmallett *               Transfer type for the endpoint
818215976Sjmallett * @param max_packet_size
819215976Sjmallett *               Maximum packet size for the endpoint
820215976Sjmallett * @param buffer Buffer to receive the data
821215976Sjmallett * @param buffer_length
822215976Sjmallett *               Length of the buffer in bytes
823215976Sjmallett *
824215976Sjmallett * @return Zero on success, negative on failure
825215976Sjmallett */
826215976Sjmallettint cvmx_usbd_out_endpoint_enable(cvmx_usbd_state_t *usb,
827215976Sjmallett    int endpoint_num, cvmx_usbd_transfer_t transfer_type,
828215976Sjmallett    int max_packet_size, uint64_t buffer, int buffer_length)
829215976Sjmallett{
830215976Sjmallett    cvmx_usbcx_doepctlx_t usbc_doepctl;
831215976Sjmallett    cvmx_usbcx_doeptsizx_t usbc_doeptsiz;
832215976Sjmallett
833215976Sjmallett    if (cvmx_unlikely(usb->init_flags & CVMX_USBD_INITIALIZE_FLAGS_DEBUG))
834215976Sjmallett        cvmx_dprintf("%s: endpoint=%d buffer=0x%llx length=%d\n",
835215976Sjmallett            __FUNCTION__, endpoint_num, (ULL)buffer, buffer_length);
836215976Sjmallett
837215976Sjmallett    usb->endpoint[endpoint_num].buffer_length = buffer_length;
838215976Sjmallett
839215976Sjmallett    CVMX_SYNCW; /* Flush out pending writes before enable */
840215976Sjmallett
841215976Sjmallett    /* Clear any pending interrupts */
842215976Sjmallett    __cvmx_usbd_write_csr32(usb, CVMX_USBCX_DOEPINTX(endpoint_num, usb->index),
843215976Sjmallett        __cvmx_usbd_read_csr32(usb, CVMX_USBCX_DOEPINTX(endpoint_num, usb->index)));
844215976Sjmallett
845215976Sjmallett    /* Setup the locations the DMA engines use  */
846215976Sjmallett    cvmx_write_csr(CVMX_USBNX_DMA0_INB_CHN0(usb->index) + endpoint_num*8, buffer);
847215976Sjmallett
848215976Sjmallett    usbc_doeptsiz.u32 = 0;
849215976Sjmallett    usbc_doeptsiz.s.mc = 1;
850215976Sjmallett    usbc_doeptsiz.s.pktcnt = (buffer_length + max_packet_size - 1) / max_packet_size;
851215976Sjmallett    if (usbc_doeptsiz.s.pktcnt == 0)
852215976Sjmallett        usbc_doeptsiz.s.pktcnt = 1;
853215976Sjmallett    usbc_doeptsiz.s.xfersize = buffer_length;
854215976Sjmallett    __cvmx_usbd_write_csr32(usb, CVMX_USBCX_DOEPTSIZX(endpoint_num, usb->index), usbc_doeptsiz.u32);
855215976Sjmallett
856215976Sjmallett    usbc_doepctl.u32 = 0;
857215976Sjmallett    usbc_doepctl.s.epena = 1;
858215976Sjmallett    usbc_doepctl.s.setd1pid = 0;
859215976Sjmallett    usbc_doepctl.s.setd0pid = 0;
860215976Sjmallett    usbc_doepctl.s.cnak = 1;
861215976Sjmallett    usbc_doepctl.s.eptype = transfer_type;
862215976Sjmallett    usbc_doepctl.s.usbactep = 1;
863215976Sjmallett    if (endpoint_num == 0)
864215976Sjmallett    {
865215976Sjmallett        switch (max_packet_size)
866215976Sjmallett        {
867215976Sjmallett            case 8:
868215976Sjmallett                usbc_doepctl.s.mps = 3;
869215976Sjmallett                break;
870215976Sjmallett            case 16:
871215976Sjmallett                usbc_doepctl.s.mps = 2;
872215976Sjmallett                break;
873215976Sjmallett            case 32:
874215976Sjmallett                usbc_doepctl.s.mps = 1;
875215976Sjmallett                break;
876215976Sjmallett            default:
877215976Sjmallett                usbc_doepctl.s.mps = 0;
878215976Sjmallett                break;
879215976Sjmallett        }
880215976Sjmallett    }
881215976Sjmallett    else
882215976Sjmallett        usbc_doepctl.s.mps = max_packet_size;
883215976Sjmallett    __cvmx_usbd_write_csr32(usb, CVMX_USBCX_DOEPCTLX(endpoint_num, usb->index), usbc_doepctl.u32);
884215976Sjmallett
885215976Sjmallett    return 0;
886215976Sjmallett}
887215976Sjmallett#ifdef CVMX_BUILD_FOR_LINUX_KERNEL
888215976SjmallettEXPORT_SYMBOL(cvmx_usbd_out_endpoint_enable);
889215976Sjmallett#endif
890215976Sjmallett
891215976Sjmallett
892215976Sjmallett/**
893215976Sjmallett * Disable an OUT endpoint
894215976Sjmallett *
895215976Sjmallett * @param usb    USB device state populated by
896215976Sjmallett *               cvmx_usbd_initialize().
897215976Sjmallett * @param endpoint_num
898215976Sjmallett *               Endpoint number to disable
899215976Sjmallett *
900215976Sjmallett * @return Zero on success, negative on failure
901215976Sjmallett */
902215976Sjmallettint cvmx_usbd_out_endpoint_disable(cvmx_usbd_state_t *usb, int endpoint_num)
903215976Sjmallett{
904215976Sjmallett    cvmx_usbcx_doepctlx_t usbc_doepctl;
905215976Sjmallett
906215976Sjmallett    if (cvmx_unlikely(usb->init_flags & CVMX_USBD_INITIALIZE_FLAGS_DEBUG))
907215976Sjmallett        cvmx_dprintf("%s: endpoint=%d\n", __FUNCTION__, endpoint_num);
908215976Sjmallett
909215976Sjmallett    usbc_doepctl.u32 = __cvmx_usbd_read_csr32(usb, CVMX_USBCX_DOEPCTLX(endpoint_num, usb->index));
910215976Sjmallett    if (usbc_doepctl.s.epena && !usbc_doepctl.s.epdis)
911215976Sjmallett    {
912215976Sjmallett        usbc_doepctl.s.epdis = 1;
913215976Sjmallett        __cvmx_usbd_write_csr32(usb, CVMX_USBCX_DOEPCTLX(endpoint_num, usb->index), usbc_doepctl.u32);
914215976Sjmallett    }
915215976Sjmallett    return 0;
916215976Sjmallett}
917215976Sjmallett#ifdef CVMX_BUILD_FOR_LINUX_KERNEL
918215976SjmallettEXPORT_SYMBOL(cvmx_usbd_out_endpoint_disable);
919215976Sjmallett#endif
920215976Sjmallett
921215976Sjmallett
922215976Sjmallett/**
923215976Sjmallett * Enable an endpoint to respond to an IN transaction
924215976Sjmallett *
925215976Sjmallett * @param usb    USB device state populated by
926215976Sjmallett *               cvmx_usbd_initialize().
927215976Sjmallett * @param endpoint_num
928215976Sjmallett *               Endpoint number to enable
929215976Sjmallett * @param transfer_type
930215976Sjmallett *               Transfer type for the endpoint
931215976Sjmallett * @param max_packet_size
932215976Sjmallett *               Maximum packet size for the endpoint
933215976Sjmallett * @param buffer Buffer to send
934215976Sjmallett * @param buffer_length
935215976Sjmallett *               Length of the buffer in bytes
936215976Sjmallett *
937215976Sjmallett * @return Zero on success, negative on failure
938215976Sjmallett */
939215976Sjmallettint cvmx_usbd_in_endpoint_enable(cvmx_usbd_state_t *usb,
940215976Sjmallett    int endpoint_num, cvmx_usbd_transfer_t transfer_type,
941215976Sjmallett    int max_packet_size, uint64_t buffer, int buffer_length)
942215976Sjmallett{
943215976Sjmallett    cvmx_usbcx_diepctlx_t usbc_diepctl;
944215976Sjmallett    cvmx_usbcx_dieptsizx_t usbc_dieptsiz;
945215976Sjmallett
946215976Sjmallett    if (cvmx_unlikely(usb->init_flags & CVMX_USBD_INITIALIZE_FLAGS_DEBUG))
947215976Sjmallett        cvmx_dprintf("%s: endpoint=%d buffer=0x%llx length=%d\n",
948215976Sjmallett            __FUNCTION__, endpoint_num, (ULL)buffer, buffer_length);
949215976Sjmallett
950215976Sjmallett    usb->endpoint[endpoint_num].buffer_length = buffer_length;
951215976Sjmallett
952215976Sjmallett    CVMX_SYNCW; /* Flush out pending writes before enable */
953215976Sjmallett
954215976Sjmallett    /* Clear any pending interrupts */
955215976Sjmallett    __cvmx_usbd_write_csr32(usb, CVMX_USBCX_DIEPINTX(endpoint_num, usb->index),
956215976Sjmallett        __cvmx_usbd_read_csr32(usb, CVMX_USBCX_DIEPINTX(endpoint_num, usb->index)));
957215976Sjmallett
958215976Sjmallett    usbc_dieptsiz.u32 = 0;
959215976Sjmallett    usbc_dieptsiz.s.mc = 1;
960215976Sjmallett    if (buffer)
961215976Sjmallett    {
962215976Sjmallett        cvmx_write_csr(CVMX_USBNX_DMA0_OUTB_CHN0(usb->index) + endpoint_num*8, buffer);
963215976Sjmallett        usbc_dieptsiz.s.pktcnt = (buffer_length + max_packet_size - 1) / max_packet_size;
964215976Sjmallett        if (usbc_dieptsiz.s.pktcnt == 0)
965215976Sjmallett            usbc_dieptsiz.s.pktcnt = 1;
966215976Sjmallett        usbc_dieptsiz.s.xfersize = buffer_length;
967215976Sjmallett    }
968215976Sjmallett    else
969215976Sjmallett    {
970215976Sjmallett        usbc_dieptsiz.s.pktcnt = 0;
971215976Sjmallett        usbc_dieptsiz.s.xfersize = 0;
972215976Sjmallett    }
973215976Sjmallett    __cvmx_usbd_write_csr32(usb, CVMX_USBCX_DIEPTSIZX(endpoint_num, usb->index), usbc_dieptsiz.u32);
974215976Sjmallett
975215976Sjmallett    usbc_diepctl.u32 = 0;
976215976Sjmallett    usbc_diepctl.s.epena = (buffer != 0);
977215976Sjmallett    usbc_diepctl.s.setd1pid = 0;
978215976Sjmallett    usbc_diepctl.s.setd0pid = (buffer == 0);
979215976Sjmallett    usbc_diepctl.s.cnak = 1;
980215976Sjmallett    usbc_diepctl.s.txfnum = endpoint_num;
981215976Sjmallett    usbc_diepctl.s.eptype = transfer_type;
982215976Sjmallett    usbc_diepctl.s.usbactep = 1;
983215976Sjmallett    usbc_diepctl.s.nextep = endpoint_num;
984215976Sjmallett    if (endpoint_num == 0)
985215976Sjmallett    {
986215976Sjmallett        switch (max_packet_size)
987215976Sjmallett        {
988215976Sjmallett            case 8:
989215976Sjmallett                usbc_diepctl.s.mps = 3;
990215976Sjmallett                break;
991215976Sjmallett            case 16:
992215976Sjmallett                usbc_diepctl.s.mps = 2;
993215976Sjmallett                break;
994215976Sjmallett            case 32:
995215976Sjmallett                usbc_diepctl.s.mps = 1;
996215976Sjmallett                break;
997215976Sjmallett            default:
998215976Sjmallett                usbc_diepctl.s.mps = 0;
999215976Sjmallett                break;
1000215976Sjmallett        }
1001215976Sjmallett    }
1002215976Sjmallett    else
1003215976Sjmallett        usbc_diepctl.s.mps = max_packet_size;
1004215976Sjmallett    __cvmx_usbd_write_csr32(usb, CVMX_USBCX_DIEPCTLX(endpoint_num, usb->index), usbc_diepctl.u32);
1005215976Sjmallett
1006215976Sjmallett    return 0;
1007215976Sjmallett}
1008215976Sjmallett#ifdef CVMX_BUILD_FOR_LINUX_KERNEL
1009215976SjmallettEXPORT_SYMBOL(cvmx_usbd_in_endpoint_enable);
1010215976Sjmallett#endif
1011215976Sjmallett
1012215976Sjmallett
1013215976Sjmallett/**
1014215976Sjmallett * Disable an IN endpoint
1015215976Sjmallett *
1016215976Sjmallett * @param usb    USB device state populated by
1017215976Sjmallett *               cvmx_usbd_initialize().
1018215976Sjmallett * @param endpoint_num
1019215976Sjmallett *               Endpoint number to disable
1020215976Sjmallett *
1021215976Sjmallett * @return Zero on success, negative on failure
1022215976Sjmallett */
1023215976Sjmallettint cvmx_usbd_in_endpoint_disable(cvmx_usbd_state_t *usb, int endpoint_num)
1024215976Sjmallett{
1025215976Sjmallett    cvmx_usbcx_diepctlx_t usbc_diepctl;
1026215976Sjmallett
1027215976Sjmallett    if (cvmx_unlikely(usb->init_flags & CVMX_USBD_INITIALIZE_FLAGS_DEBUG))
1028215976Sjmallett        cvmx_dprintf("%s: endpoint=%d\n", __FUNCTION__, endpoint_num);
1029215976Sjmallett
1030215976Sjmallett    usbc_diepctl.u32 = __cvmx_usbd_read_csr32(usb, CVMX_USBCX_DIEPCTLX(endpoint_num, usb->index));
1031215976Sjmallett    if (usbc_diepctl.s.epena && !usbc_diepctl.s.epdis)
1032215976Sjmallett    {
1033215976Sjmallett        usbc_diepctl.s.epdis = 1;
1034215976Sjmallett        __cvmx_usbd_write_csr32(usb, CVMX_USBCX_DIEPCTLX(endpoint_num, usb->index), usbc_diepctl.u32);
1035215976Sjmallett    }
1036215976Sjmallett    return 0;
1037215976Sjmallett}
1038215976Sjmallett#ifdef CVMX_BUILD_FOR_LINUX_KERNEL
1039215976SjmallettEXPORT_SYMBOL(cvmx_usbd_in_endpoint_disable);
1040215976Sjmallett#endif
1041215976Sjmallett
1042