cvmx-usb.c revision 229071
1210284Sjmallett/***********************license start***************
2215990Sjmallett * Copyright (c) 2003-2010  Cavium Networks (support@cavium.com). All rights
3215990Sjmallett * reserved.
4210284Sjmallett *
5210284Sjmallett *
6215990Sjmallett * Redistribution and use in source and binary forms, with or without
7215990Sjmallett * modification, are permitted provided that the following conditions are
8215990Sjmallett * met:
9210284Sjmallett *
10215990Sjmallett *   * Redistributions of source code must retain the above copyright
11215990Sjmallett *     notice, this list of conditions and the following disclaimer.
12210284Sjmallett *
13215990Sjmallett *   * Redistributions in binary form must reproduce the above
14215990Sjmallett *     copyright notice, this list of conditions and the following
15215990Sjmallett *     disclaimer in the documentation and/or other materials provided
16215990Sjmallett *     with the distribution.
17215990Sjmallett
18215990Sjmallett *   * Neither the name of Cavium Networks nor the names of
19215990Sjmallett *     its contributors may be used to endorse or promote products
20215990Sjmallett *     derived from this software without specific prior written
21215990Sjmallett *     permission.
22215990Sjmallett
23215990Sjmallett * This Software, including technical data, may be subject to U.S. export  control
24215990Sjmallett * laws, including the U.S. Export Administration Act and its  associated
25215990Sjmallett * regulations, and may be subject to export or import  regulations in other
26215990Sjmallett * countries.
27215990Sjmallett
28215990Sjmallett * TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SOFTWARE IS PROVIDED "AS IS"
29215990Sjmallett * AND WITH ALL FAULTS AND CAVIUM  NETWORKS MAKES NO PROMISES, REPRESENTATIONS OR
30215990Sjmallett * WARRANTIES, EITHER EXPRESS, IMPLIED, STATUTORY, OR OTHERWISE, WITH RESPECT TO
31215990Sjmallett * THE SOFTWARE, INCLUDING ITS CONDITION, ITS CONFORMITY TO ANY REPRESENTATION OR
32215990Sjmallett * DESCRIPTION, OR THE EXISTENCE OF ANY LATENT OR PATENT DEFECTS, AND CAVIUM
33215990Sjmallett * SPECIFICALLY DISCLAIMS ALL IMPLIED (IF ANY) WARRANTIES OF TITLE,
34215990Sjmallett * MERCHANTABILITY, NONINFRINGEMENT, FITNESS FOR A PARTICULAR PURPOSE, LACK OF
35215990Sjmallett * VIRUSES, ACCURACY OR COMPLETENESS, QUIET ENJOYMENT, QUIET POSSESSION OR
36215990Sjmallett * CORRESPONDENCE TO DESCRIPTION. THE ENTIRE  RISK ARISING OUT OF USE OR
37215990Sjmallett * PERFORMANCE OF THE SOFTWARE LIES WITH YOU.
38210284Sjmallett ***********************license end**************************************/
39210284Sjmallett
40215990Sjmallett
41210284Sjmallett/**
42210284Sjmallett * @file
43210284Sjmallett *
44210284Sjmallett * "cvmx-usb.c" defines a set of low level USB functions to help
45210284Sjmallett * developers create Octeon USB drivers for various operating
46210284Sjmallett * systems. These functions provide a generic API to the Octeon
47210284Sjmallett * USB blocks, hiding the internal hardware specific
48210284Sjmallett * operations.
49210284Sjmallett *
50210284Sjmallett * <hr>$Revision: 32636 $<hr>
51210284Sjmallett */
52215990Sjmallett#ifdef CVMX_BUILD_FOR_LINUX_KERNEL
53215990Sjmallett#include <asm/octeon/cvmx.h>
54215990Sjmallett#include <asm/octeon/cvmx-clock.h>
55215990Sjmallett#include <asm/octeon/cvmx-sysinfo.h>
56215990Sjmallett#include <asm/octeon/cvmx-usbnx-defs.h>
57215990Sjmallett#include <asm/octeon/cvmx-usbcx-defs.h>
58215990Sjmallett#include <asm/octeon/cvmx-usb.h>
59215990Sjmallett#include <asm/octeon/cvmx-helper.h>
60215990Sjmallett#include <asm/octeon/cvmx-helper-board.h>
61215990Sjmallett#include <asm/octeon/cvmx-swap.h>
62215990Sjmallett#if 0
63215990Sjmallett   /* Do not use cvmx-error.h for now. When the cvmx-error.h is properly
64215990Sjmallett    * ported, remove the above #if 0, and all #ifdef __CVMX_ERROR_H__ within
65215990Sjmallett    * this file */
66215990Sjmallett   #include <asm/octeon/cvmx-error.h>
67215990Sjmallett#endif
68215990Sjmallett#else
69210284Sjmallett#include "cvmx.h"
70215990Sjmallett#include "cvmx-clock.h"
71210284Sjmallett#include "cvmx-sysinfo.h"
72210284Sjmallett#include "cvmx-usb.h"
73210284Sjmallett#include "cvmx-helper.h"
74210284Sjmallett#include "cvmx-helper-board.h"
75210284Sjmallett#include "cvmx-csr-db.h"
76210284Sjmallett#include "cvmx-swap.h"
77215990Sjmallett#include "cvmx-error.h"
78215990Sjmallett#endif
79210284Sjmallett
80210284Sjmallett#define MAX_RETRIES         3   /* Maximum number of times to retry failed transactions */
81210284Sjmallett#define MAX_PIPES           32  /* Maximum number of pipes that can be open at once */
82210284Sjmallett#define MAX_TRANSACTIONS    256 /* Maximum number of outstanding transactions across all pipes */
83210284Sjmallett#define MAX_CHANNELS        8   /* Maximum number of hardware channels supported by the USB block */
84210284Sjmallett#define MAX_USB_ADDRESS     127 /* The highest valid USB device address */
85210284Sjmallett#define MAX_USB_ENDPOINT    15  /* The highest valid USB endpoint number */
86210284Sjmallett#define MAX_USB_HUB_PORT    15  /* The highest valid port number on a hub */
87215990Sjmallett#define MAX_TRANSFER_BYTES  ((1<<19)-1) /* The low level hardware can transfer a maximum of this number of bytes in each transfer. The field is 19 bits wide */
88215990Sjmallett#define MAX_TRANSFER_PACKETS ((1<<10)-1) /* The low level hardware can transfer a maximum of this number of packets in each transfer. The field is 10 bits wide */
89210284Sjmallett#define ALLOW_CSR_DECODES   0   /* CSR decoding when CVMX_USB_INITIALIZE_FLAGS_DEBUG_CSRS is set
90210284Sjmallett                                    enlarges the code a lot. This define overrides the ability to do CSR
91210284Sjmallett                                    decoding since it isn't necessary 99% of the time. Change this to a
92210284Sjmallett                                    one if you need CSR decoding */
93210284Sjmallett
94210284Sjmallett/* These defines disable the normal read and write csr. This is so I can add
95210284Sjmallett    extra debug stuff to the usb specific version and I won't use the normal
96210284Sjmallett    version by mistake */
97210284Sjmallett#define cvmx_read_csr use_cvmx_usb_read_csr64_instead_of_cvmx_read_csr
98210284Sjmallett#define cvmx_write_csr use_cvmx_usb_write_csr64_instead_of_cvmx_write_csr
99210284Sjmallett
100210284Sjmalletttypedef enum
101210284Sjmallett{
102210284Sjmallett    __CVMX_USB_TRANSACTION_FLAGS_IN_USE = 1<<16,
103210284Sjmallett} cvmx_usb_transaction_flags_t;
104210284Sjmallett
105210284Sjmallett/**
106210284Sjmallett * Logical transactions may take numerous low level
107210284Sjmallett * transactions, especially when splits are concerned. This
108210284Sjmallett * enum represents all of the possible stages a transaction can
109210284Sjmallett * be in. Note that split completes are always even. This is so
110210284Sjmallett * the NAK handler can backup to the previous low level
111210284Sjmallett * transaction with a simple clearing of bit 0.
112210284Sjmallett */
113210284Sjmalletttypedef enum
114210284Sjmallett{
115210284Sjmallett    CVMX_USB_STAGE_NON_CONTROL,
116210284Sjmallett    CVMX_USB_STAGE_NON_CONTROL_SPLIT_COMPLETE,
117210284Sjmallett    CVMX_USB_STAGE_SETUP,
118210284Sjmallett    CVMX_USB_STAGE_SETUP_SPLIT_COMPLETE,
119210284Sjmallett    CVMX_USB_STAGE_DATA,
120210284Sjmallett    CVMX_USB_STAGE_DATA_SPLIT_COMPLETE,
121210284Sjmallett    CVMX_USB_STAGE_STATUS,
122210284Sjmallett    CVMX_USB_STAGE_STATUS_SPLIT_COMPLETE,
123210284Sjmallett} cvmx_usb_stage_t;
124210284Sjmallett
125210284Sjmallett/**
126210284Sjmallett * This structure describes each pending USB transaction
127210284Sjmallett * regardless of type. These are linked together to form a list
128210284Sjmallett * of pending requests for a pipe.
129210284Sjmallett */
130210284Sjmalletttypedef struct cvmx_usb_transaction
131210284Sjmallett{
132210284Sjmallett    struct cvmx_usb_transaction *prev;  /**< Transaction before this one in the pipe */
133210284Sjmallett    struct cvmx_usb_transaction *next;  /**< Transaction after this one in the pipe */
134210284Sjmallett    cvmx_usb_transfer_t type;           /**< Type of transaction, duplicated of the pipe */
135210284Sjmallett    cvmx_usb_transaction_flags_t flags; /**< State flags for this transaction */
136210284Sjmallett    uint64_t buffer;                    /**< User's physical buffer address to read/write */
137210284Sjmallett    int buffer_length;                  /**< Size of the user's buffer in bytes */
138210284Sjmallett    uint64_t control_header;            /**< For control transactions, physical address of the 8 byte standard header */
139210284Sjmallett    int iso_start_frame;                /**< For ISO transactions, the starting frame number */
140210284Sjmallett    int iso_number_packets;             /**< For ISO transactions, the number of packets in the request */
141210284Sjmallett    cvmx_usb_iso_packet_t *iso_packets; /**< For ISO transactions, the sub packets in the request */
142210284Sjmallett    int xfersize;
143210284Sjmallett    int pktcnt;
144210284Sjmallett    int retries;
145210284Sjmallett    int actual_bytes;                   /**< Actual bytes transfer for this transaction */
146210284Sjmallett    cvmx_usb_stage_t stage;             /**< For control transactions, the current stage */
147210284Sjmallett    cvmx_usb_callback_func_t callback;  /**< User's callback function when complete */
148210284Sjmallett    void *callback_data;                /**< User's data */
149210284Sjmallett} cvmx_usb_transaction_t;
150210284Sjmallett
151210284Sjmallett/**
152210284Sjmallett * A pipe represents a virtual connection between Octeon and some
153210284Sjmallett * USB device. It contains a list of pending request to the device.
154210284Sjmallett */
155210284Sjmalletttypedef struct cvmx_usb_pipe
156210284Sjmallett{
157210284Sjmallett    struct cvmx_usb_pipe *prev;         /**< Pipe before this one in the list */
158210284Sjmallett    struct cvmx_usb_pipe *next;         /**< Pipe after this one in the list */
159210284Sjmallett    cvmx_usb_transaction_t *head;       /**< The first pending transaction */
160210284Sjmallett    cvmx_usb_transaction_t *tail;       /**< The last pending transaction */
161215990Sjmallett    uint64_t interval;                  /**< For periodic pipes, the interval between packets in frames */
162215990Sjmallett    uint64_t next_tx_frame;             /**< The next frame this pipe is allowed to transmit on */
163210284Sjmallett    cvmx_usb_pipe_flags_t flags;        /**< State flags for this pipe */
164210284Sjmallett    cvmx_usb_speed_t device_speed;      /**< Speed of device connected to this pipe */
165210284Sjmallett    cvmx_usb_transfer_t transfer_type;  /**< Type of transaction supported by this pipe */
166210284Sjmallett    cvmx_usb_direction_t transfer_dir;  /**< IN or OUT. Ignored for Control */
167210284Sjmallett    int multi_count;                    /**< Max packet in a row for the device */
168210284Sjmallett    uint16_t max_packet;                /**< The device's maximum packet size in bytes */
169210284Sjmallett    uint8_t device_addr;                /**< USB device address at other end of pipe */
170210284Sjmallett    uint8_t endpoint_num;               /**< USB endpoint number at other end of pipe */
171210284Sjmallett    uint8_t hub_device_addr;            /**< Hub address this device is connected to */
172210284Sjmallett    uint8_t hub_port;                   /**< Hub port this device is connected to */
173210284Sjmallett    uint8_t pid_toggle;                 /**< This toggles between 0/1 on every packet send to track the data pid needed */
174210284Sjmallett    uint8_t channel;                    /**< Hardware DMA channel for this pipe */
175210284Sjmallett    int8_t  split_sc_frame;             /**< The low order bits of the frame number the split complete should be sent on */
176210284Sjmallett} cvmx_usb_pipe_t;
177210284Sjmallett
178210284Sjmalletttypedef struct
179210284Sjmallett{
180210284Sjmallett    cvmx_usb_pipe_t *head;              /**< Head of the list, or NULL if empty */
181210284Sjmallett    cvmx_usb_pipe_t *tail;              /**< Tail if the list, or NULL if empty */
182210284Sjmallett} cvmx_usb_pipe_list_t;
183210284Sjmallett
184215990Sjmalletttypedef struct
185215990Sjmallett{
186215990Sjmallett    struct
187215990Sjmallett    {
188215990Sjmallett        int channel;
189215990Sjmallett        int size;
190215990Sjmallett        uint64_t address;
191215990Sjmallett    } entry[MAX_CHANNELS+1];
192215990Sjmallett    int head;
193215990Sjmallett    int tail;
194215990Sjmallett} cvmx_usb_tx_fifo_t;
195215990Sjmallett
196210284Sjmallett/**
197210284Sjmallett * The state of the USB block is stored in this structure
198210284Sjmallett */
199210284Sjmalletttypedef struct
200210284Sjmallett{
201210284Sjmallett    int init_flags;                     /**< Flags passed to initialize */
202210284Sjmallett    int index;                          /**< Which USB block this is for */
203210284Sjmallett    int idle_hardware_channels;         /**< Bit set for every idle hardware channel */
204210284Sjmallett    cvmx_usbcx_hprt_t usbcx_hprt;       /**< Stored port status so we don't need to read a CSR to determine splits */
205210284Sjmallett    cvmx_usb_pipe_t *pipe_for_channel[MAX_CHANNELS];    /**< Map channels to pipes */
206210284Sjmallett    cvmx_usb_transaction_t *free_transaction_head;      /**< List of free transactions head */
207210284Sjmallett    cvmx_usb_transaction_t *free_transaction_tail;      /**< List of free transactions tail */
208210284Sjmallett    cvmx_usb_pipe_t pipe[MAX_PIPES];                    /**< Storage for pipes */
209210284Sjmallett    cvmx_usb_transaction_t transaction[MAX_TRANSACTIONS];       /**< Storage for transactions */
210210284Sjmallett    cvmx_usb_callback_func_t callback[__CVMX_USB_CALLBACK_END]; /**< User global callbacks */
211210284Sjmallett    void *callback_data[__CVMX_USB_CALLBACK_END];               /**< User data for each callback */
212210284Sjmallett    int indent;                         /**< Used by debug output to indent functions */
213210284Sjmallett    cvmx_usb_port_status_t port_status; /**< Last port status used for change notification */
214210284Sjmallett    cvmx_usb_pipe_list_t free_pipes;    /**< List of all pipes that are currently closed */
215210284Sjmallett    cvmx_usb_pipe_list_t idle_pipes;    /**< List of open pipes that have no transactions */
216210284Sjmallett    cvmx_usb_pipe_list_t active_pipes[4]; /**< Active pipes indexed by transfer type */
217215990Sjmallett    uint64_t frame_number;              /**< Increments every SOF interrupt for time keeping */
218215990Sjmallett    cvmx_usb_transaction_t *active_split; /**< Points to the current active split, or NULL */
219215990Sjmallett    cvmx_usb_tx_fifo_t periodic;
220215990Sjmallett    cvmx_usb_tx_fifo_t nonperiodic;
221210284Sjmallett} cvmx_usb_internal_state_t;
222210284Sjmallett
223210284Sjmallett/* This macro logs out whenever a function is called if debugging is on */
224210284Sjmallett#define CVMX_USB_LOG_CALLED() \
225210284Sjmallett    if (cvmx_unlikely(usb->init_flags & CVMX_USB_INITIALIZE_FLAGS_DEBUG_CALLS)) \
226210284Sjmallett        cvmx_dprintf("%*s%s: called\n", 2*usb->indent++, "", __FUNCTION__);
227210284Sjmallett
228210284Sjmallett/* This macro logs out each function parameter if debugging is on */
229210284Sjmallett#define CVMX_USB_LOG_PARAM(format, param) \
230210284Sjmallett    if (cvmx_unlikely(usb->init_flags & CVMX_USB_INITIALIZE_FLAGS_DEBUG_CALLS)) \
231210284Sjmallett        cvmx_dprintf("%*s%s: param %s = " format "\n", 2*usb->indent, "", __FUNCTION__, #param, param);
232210284Sjmallett
233210284Sjmallett/* This macro logs out when a function returns a value */
234210284Sjmallett#define CVMX_USB_RETURN(v)                                              \
235210284Sjmallett    do {                                                                \
236210311Sjmallett        __typeof(v) r = v;                                              \
237210284Sjmallett        if (cvmx_unlikely(usb->init_flags & CVMX_USB_INITIALIZE_FLAGS_DEBUG_CALLS))    \
238210284Sjmallett            cvmx_dprintf("%*s%s: returned %s(%d)\n", 2*--usb->indent, "", __FUNCTION__, #v, r); \
239210284Sjmallett        return r;                                                       \
240210284Sjmallett    } while (0);
241210284Sjmallett
242210284Sjmallett/* This macro logs out when a function doesn't return a value */
243210284Sjmallett#define CVMX_USB_RETURN_NOTHING()                                       \
244210284Sjmallett    do {                                                                \
245210284Sjmallett        if (cvmx_unlikely(usb->init_flags & CVMX_USB_INITIALIZE_FLAGS_DEBUG_CALLS))    \
246210284Sjmallett            cvmx_dprintf("%*s%s: returned\n", 2*--usb->indent, "", __FUNCTION__); \
247210284Sjmallett        return;                                                         \
248210284Sjmallett    } while (0);
249210284Sjmallett
250210284Sjmallett/* This macro spins on a field waiting for it to reach a value */
251210284Sjmallett#define CVMX_WAIT_FOR_FIELD32(address, type, field, op, value, timeout_usec)\
252210284Sjmallett    ({int result;                                                       \
253210284Sjmallett    do {                                                                \
254210284Sjmallett        uint64_t done = cvmx_get_cycle() + (uint64_t)timeout_usec *     \
255215990Sjmallett                           cvmx_clock_get_rate(CVMX_CLOCK_CORE) / 1000000;  \
256210284Sjmallett        type c;                                                         \
257210284Sjmallett        while (1)                                                       \
258210284Sjmallett        {                                                               \
259210284Sjmallett            c.u32 = __cvmx_usb_read_csr32(usb, address);                \
260210284Sjmallett            if (c.s.field op (value)) {                                 \
261210284Sjmallett                result = 0;                                             \
262210284Sjmallett                break;                                                  \
263210284Sjmallett            } else if (cvmx_get_cycle() > done) {                       \
264210284Sjmallett                result = -1;                                            \
265210284Sjmallett                break;                                                  \
266210284Sjmallett            } else                                                      \
267210284Sjmallett                cvmx_wait(100);                                         \
268210284Sjmallett        }                                                               \
269210284Sjmallett    } while (0);                                                        \
270210284Sjmallett    result;})
271210284Sjmallett
272210284Sjmallett/* This macro logically sets a single field in a CSR. It does the sequence
273210284Sjmallett    read, modify, and write */
274210284Sjmallett#define USB_SET_FIELD32(address, type, field, value)\
275210284Sjmallett    do {                                            \
276210284Sjmallett        type c;                                     \
277210284Sjmallett        c.u32 = __cvmx_usb_read_csr32(usb, address);\
278210284Sjmallett        c.s.field = value;                          \
279210284Sjmallett        __cvmx_usb_write_csr32(usb, address, c.u32);\
280210284Sjmallett    } while (0)
281210284Sjmallett
282215990Sjmallett/* Returns the IO address to push/pop stuff data from the FIFOs */
283215990Sjmallett#define USB_FIFO_ADDRESS(channel, usb_index) (CVMX_USBCX_GOTGCTL(usb_index) + ((channel)+1)*0x1000)
284210284Sjmallett
285210284Sjmallett/**
286210284Sjmallett * @INTERNAL
287210284Sjmallett * Read a USB 32bit CSR. It performs the necessary address swizzle
288210284Sjmallett * for 32bit CSRs and logs the value in a readable format if
289210284Sjmallett * debugging is on.
290210284Sjmallett *
291210284Sjmallett * @param usb     USB block this access is for
292210284Sjmallett * @param address 64bit address to read
293210284Sjmallett *
294210284Sjmallett * @return Result of the read
295210284Sjmallett */
296210284Sjmallettstatic inline uint32_t __cvmx_usb_read_csr32(cvmx_usb_internal_state_t *usb,
297210284Sjmallett                                             uint64_t address)
298210284Sjmallett{
299210284Sjmallett    uint32_t result = cvmx_read64_uint32(address ^ 4);
300210284Sjmallett#if ALLOW_CSR_DECODES
301210284Sjmallett    if (cvmx_unlikely(usb->init_flags & CVMX_USB_INITIALIZE_FLAGS_DEBUG_CSRS))
302210284Sjmallett    {
303210284Sjmallett        cvmx_dprintf("Read: ");
304210284Sjmallett        cvmx_csr_db_decode(cvmx_get_proc_id(), address, result);
305210284Sjmallett    }
306210284Sjmallett#endif
307210284Sjmallett    return result;
308210284Sjmallett}
309210284Sjmallett
310210284Sjmallett
311210284Sjmallett/**
312210284Sjmallett * @INTERNAL
313210284Sjmallett * Write a USB 32bit CSR. It performs the necessary address
314210284Sjmallett * swizzle for 32bit CSRs and logs the value in a readable format
315210284Sjmallett * if debugging is on.
316210284Sjmallett *
317210284Sjmallett * @param usb     USB block this access is for
318210284Sjmallett * @param address 64bit address to write
319210284Sjmallett * @param value   Value to write
320210284Sjmallett */
321210284Sjmallettstatic inline void __cvmx_usb_write_csr32(cvmx_usb_internal_state_t *usb,
322210284Sjmallett                                          uint64_t address, uint32_t value)
323210284Sjmallett{
324210284Sjmallett#if ALLOW_CSR_DECODES
325210284Sjmallett    if (cvmx_unlikely(usb->init_flags & CVMX_USB_INITIALIZE_FLAGS_DEBUG_CSRS))
326210284Sjmallett    {
327210284Sjmallett        cvmx_dprintf("Write: ");
328210284Sjmallett        cvmx_csr_db_decode(cvmx_get_proc_id(), address, value);
329210284Sjmallett    }
330210284Sjmallett#endif
331210284Sjmallett    cvmx_write64_uint32(address ^ 4, value);
332215990Sjmallett    cvmx_read64_uint64(CVMX_USBNX_DMA0_INB_CHN0(usb->index));
333210284Sjmallett}
334210284Sjmallett
335210284Sjmallett
336210284Sjmallett/**
337210284Sjmallett * @INTERNAL
338210284Sjmallett * Read a USB 64bit CSR. It logs the value in a readable format if
339210284Sjmallett * debugging is on.
340210284Sjmallett *
341210284Sjmallett * @param usb     USB block this access is for
342210284Sjmallett * @param address 64bit address to read
343210284Sjmallett *
344210284Sjmallett * @return Result of the read
345210284Sjmallett */
346210284Sjmallettstatic inline uint64_t __cvmx_usb_read_csr64(cvmx_usb_internal_state_t *usb,
347210284Sjmallett                                             uint64_t address)
348210284Sjmallett{
349210284Sjmallett    uint64_t result = cvmx_read64_uint64(address);
350210284Sjmallett#if ALLOW_CSR_DECODES
351210284Sjmallett    if (cvmx_unlikely(usb->init_flags & CVMX_USB_INITIALIZE_FLAGS_DEBUG_CSRS))
352210284Sjmallett    {
353210284Sjmallett        cvmx_dprintf("Read: ");
354210284Sjmallett        cvmx_csr_db_decode(cvmx_get_proc_id(), address, result);
355210284Sjmallett    }
356210284Sjmallett#endif
357210284Sjmallett    return result;
358210284Sjmallett}
359210284Sjmallett
360210284Sjmallett
361210284Sjmallett/**
362210284Sjmallett * @INTERNAL
363210284Sjmallett * Write a USB 64bit CSR. It logs the value in a readable format
364210284Sjmallett * if debugging is on.
365210284Sjmallett *
366210284Sjmallett * @param usb     USB block this access is for
367210284Sjmallett * @param address 64bit address to write
368210284Sjmallett * @param value   Value to write
369210284Sjmallett */
370210284Sjmallettstatic inline void __cvmx_usb_write_csr64(cvmx_usb_internal_state_t *usb,
371210284Sjmallett                                          uint64_t address, uint64_t value)
372210284Sjmallett{
373210284Sjmallett#if ALLOW_CSR_DECODES
374210284Sjmallett    if (cvmx_unlikely(usb->init_flags & CVMX_USB_INITIALIZE_FLAGS_DEBUG_CSRS))
375210284Sjmallett    {
376210284Sjmallett        cvmx_dprintf("Write: ");
377210284Sjmallett        cvmx_csr_db_decode(cvmx_get_proc_id(), address, value);
378210284Sjmallett    }
379210284Sjmallett#endif
380210284Sjmallett    cvmx_write64_uint64(address, value);
381210284Sjmallett}
382210284Sjmallett
383210284Sjmallett
384210284Sjmallett/**
385210284Sjmallett * @INTERNAL
386215990Sjmallett * Utility function to convert complete codes into strings
387210284Sjmallett *
388210284Sjmallett * @param complete_code
389210284Sjmallett *               Code to convert
390210284Sjmallett *
391210284Sjmallett * @return Human readable string
392210284Sjmallett */
393210284Sjmallettstatic const char *__cvmx_usb_complete_to_string(cvmx_usb_complete_t complete_code)
394210284Sjmallett{
395210284Sjmallett    switch (complete_code)
396210284Sjmallett    {
397210284Sjmallett        case CVMX_USB_COMPLETE_SUCCESS: return "SUCCESS";
398210284Sjmallett        case CVMX_USB_COMPLETE_SHORT:   return "SHORT";
399210284Sjmallett        case CVMX_USB_COMPLETE_CANCEL:  return "CANCEL";
400210284Sjmallett        case CVMX_USB_COMPLETE_ERROR:   return "ERROR";
401210284Sjmallett        case CVMX_USB_COMPLETE_STALL:   return "STALL";
402210284Sjmallett        case CVMX_USB_COMPLETE_XACTERR: return "XACTERR";
403210284Sjmallett        case CVMX_USB_COMPLETE_DATATGLERR: return "DATATGLERR";
404210284Sjmallett        case CVMX_USB_COMPLETE_BABBLEERR: return "BABBLEERR";
405210284Sjmallett        case CVMX_USB_COMPLETE_FRAMEERR: return "FRAMEERR";
406210284Sjmallett    }
407210284Sjmallett    return "Update __cvmx_usb_complete_to_string";
408210284Sjmallett}
409210284Sjmallett
410210284Sjmallett
411210284Sjmallett/**
412210284Sjmallett * @INTERNAL
413210284Sjmallett * Return non zero if this pipe connects to a non HIGH speed
414210284Sjmallett * device through a high speed hub.
415210284Sjmallett *
416210284Sjmallett * @param usb    USB block this access is for
417210284Sjmallett * @param pipe   Pipe to check
418210284Sjmallett *
419210284Sjmallett * @return Non zero if we need to do split transactions
420210284Sjmallett */
421210284Sjmallettstatic inline int __cvmx_usb_pipe_needs_split(cvmx_usb_internal_state_t *usb, cvmx_usb_pipe_t *pipe)
422210284Sjmallett{
423210284Sjmallett    return ((pipe->device_speed != CVMX_USB_SPEED_HIGH) && (usb->usbcx_hprt.s.prtspd == CVMX_USB_SPEED_HIGH));
424210284Sjmallett}
425210284Sjmallett
426210284Sjmallett
427210284Sjmallett/**
428210284Sjmallett * @INTERNAL
429210284Sjmallett * Trivial utility function to return the correct PID for a pipe
430210284Sjmallett *
431210284Sjmallett * @param pipe   pipe to check
432210284Sjmallett *
433210284Sjmallett * @return PID for pipe
434210284Sjmallett */
435210284Sjmallettstatic inline int __cvmx_usb_get_data_pid(cvmx_usb_pipe_t *pipe)
436210284Sjmallett{
437210284Sjmallett    if (pipe->pid_toggle)
438210284Sjmallett        return 2; /* Data1 */
439210284Sjmallett    else
440210284Sjmallett        return 0; /* Data0 */
441210284Sjmallett}
442210284Sjmallett
443210284Sjmallett
444210284Sjmallett/**
445210284Sjmallett * Return the number of USB ports supported by this Octeon
446210284Sjmallett * chip. If the chip doesn't support USB, or is not supported
447210284Sjmallett * by this API, a zero will be returned. Most Octeon chips
448210284Sjmallett * support one usb port, but some support two ports.
449210284Sjmallett * cvmx_usb_initialize() must be called on independent
450210284Sjmallett * cvmx_usb_state_t structures.
451210284Sjmallett *
452210284Sjmallett * This utilizes cvmx_helper_board_usb_get_num_ports()
453215990Sjmallett * to get any board specific variations.
454210284Sjmallett *
455210284Sjmallett * @return Number of port, zero if usb isn't supported
456210284Sjmallett */
457210284Sjmallettint cvmx_usb_get_num_ports(void)
458210284Sjmallett{
459210284Sjmallett    int arch_ports = 0;
460210284Sjmallett
461215990Sjmallett    if (OCTEON_IS_MODEL(OCTEON_CN56XX))
462215990Sjmallett        arch_ports = 1;
463215990Sjmallett    else if (OCTEON_IS_MODEL(OCTEON_CN52XX))
464210284Sjmallett        arch_ports = 2;
465215990Sjmallett    else if (OCTEON_IS_MODEL(OCTEON_CN50XX))
466215990Sjmallett        arch_ports = 1;
467210284Sjmallett    else if (OCTEON_IS_MODEL(OCTEON_CN31XX))
468215990Sjmallett        arch_ports = 1;
469215990Sjmallett    else if (OCTEON_IS_MODEL(OCTEON_CN30XX))
470215990Sjmallett        arch_ports = 1;
471215990Sjmallett    else
472210284Sjmallett        arch_ports = 0;
473210284Sjmallett
474210284Sjmallett    return __cvmx_helper_board_usb_get_num_ports(arch_ports);
475210284Sjmallett}
476215990Sjmallett#ifdef CVMX_BUILD_FOR_LINUX_KERNEL
477215990SjmallettEXPORT_SYMBOL(cvmx_usb_get_num_ports);
478215990Sjmallett#endif
479210284Sjmallett
480210284Sjmallett
481210284Sjmallett/**
482210284Sjmallett * @INTERNAL
483210284Sjmallett * Allocate a usb transaction for use
484210284Sjmallett *
485210284Sjmallett * @param usb    USB device state populated by
486210284Sjmallett *               cvmx_usb_initialize().
487210284Sjmallett *
488210284Sjmallett * @return Transaction or NULL
489210284Sjmallett */
490210284Sjmallettstatic inline cvmx_usb_transaction_t *__cvmx_usb_alloc_transaction(cvmx_usb_internal_state_t *usb)
491210284Sjmallett{
492210284Sjmallett    cvmx_usb_transaction_t *t;
493210284Sjmallett    t = usb->free_transaction_head;
494210284Sjmallett    if (t)
495210284Sjmallett    {
496210284Sjmallett        usb->free_transaction_head = t->next;
497210284Sjmallett        if (!usb->free_transaction_head)
498210284Sjmallett            usb->free_transaction_tail = NULL;
499210284Sjmallett    }
500210284Sjmallett    else if (cvmx_unlikely(usb->init_flags & CVMX_USB_INITIALIZE_FLAGS_DEBUG_INFO))
501210284Sjmallett        cvmx_dprintf("%s: Failed to allocate a transaction\n", __FUNCTION__);
502210284Sjmallett    if (t)
503210284Sjmallett    {
504210284Sjmallett        memset(t, 0, sizeof(*t));
505210284Sjmallett        t->flags = __CVMX_USB_TRANSACTION_FLAGS_IN_USE;
506210284Sjmallett    }
507210284Sjmallett    return t;
508210284Sjmallett}
509210284Sjmallett
510210284Sjmallett
511210284Sjmallett/**
512210284Sjmallett * @INTERNAL
513210284Sjmallett * Free a usb transaction
514210284Sjmallett *
515210284Sjmallett * @param usb    USB device state populated by
516210284Sjmallett *               cvmx_usb_initialize().
517210284Sjmallett * @param transaction
518210284Sjmallett *               Transaction to free
519210284Sjmallett */
520210284Sjmallettstatic inline void __cvmx_usb_free_transaction(cvmx_usb_internal_state_t *usb,
521210284Sjmallett                                        cvmx_usb_transaction_t *transaction)
522210284Sjmallett{
523210284Sjmallett    transaction->flags = 0;
524210284Sjmallett    transaction->prev = NULL;
525210284Sjmallett    transaction->next = NULL;
526210284Sjmallett    if (usb->free_transaction_tail)
527210284Sjmallett        usb->free_transaction_tail->next = transaction;
528210284Sjmallett    else
529210284Sjmallett        usb->free_transaction_head = transaction;
530210284Sjmallett    usb->free_transaction_tail = transaction;
531210284Sjmallett}
532210284Sjmallett
533210284Sjmallett
534210284Sjmallett/**
535210284Sjmallett * @INTERNAL
536210284Sjmallett * Add a pipe to the tail of a list
537210284Sjmallett * @param list   List to add pipe to
538210284Sjmallett * @param pipe   Pipe to add
539210284Sjmallett */
540210284Sjmallettstatic inline void __cvmx_usb_append_pipe(cvmx_usb_pipe_list_t *list, cvmx_usb_pipe_t *pipe)
541210284Sjmallett{
542210284Sjmallett    pipe->next = NULL;
543210284Sjmallett    pipe->prev = list->tail;
544210284Sjmallett    if (list->tail)
545210284Sjmallett        list->tail->next = pipe;
546210284Sjmallett    else
547210284Sjmallett        list->head = pipe;
548210284Sjmallett    list->tail = pipe;
549210284Sjmallett}
550210284Sjmallett
551210284Sjmallett
552210284Sjmallett/**
553210284Sjmallett * @INTERNAL
554210284Sjmallett * Remove a pipe from a list
555210284Sjmallett * @param list   List to remove pipe from
556210284Sjmallett * @param pipe   Pipe to remove
557210284Sjmallett */
558210284Sjmallettstatic inline void __cvmx_usb_remove_pipe(cvmx_usb_pipe_list_t *list, cvmx_usb_pipe_t *pipe)
559210284Sjmallett{
560210284Sjmallett    if (list->head == pipe)
561210284Sjmallett    {
562210284Sjmallett        list->head = pipe->next;
563210284Sjmallett        pipe->next = NULL;
564210284Sjmallett        if (list->head)
565210284Sjmallett            list->head->prev = NULL;
566210284Sjmallett        else
567210284Sjmallett            list->tail = NULL;
568210284Sjmallett    }
569210284Sjmallett    else if (list->tail == pipe)
570210284Sjmallett    {
571210284Sjmallett        list->tail = pipe->prev;
572210284Sjmallett        list->tail->next = NULL;
573210284Sjmallett        pipe->prev = NULL;
574210284Sjmallett    }
575210284Sjmallett    else
576210284Sjmallett    {
577210284Sjmallett        pipe->prev->next = pipe->next;
578210284Sjmallett        pipe->next->prev = pipe->prev;
579210284Sjmallett        pipe->prev = NULL;
580210284Sjmallett        pipe->next = NULL;
581210284Sjmallett    }
582210284Sjmallett}
583210284Sjmallett
584210284Sjmallett
585210284Sjmallett/**
586210284Sjmallett * Initialize a USB port for use. This must be called before any
587210284Sjmallett * other access to the Octeon USB port is made. The port starts
588210284Sjmallett * off in the disabled state.
589210284Sjmallett *
590210284Sjmallett * @param state  Pointer to an empty cvmx_usb_state_t structure
591210284Sjmallett *               that will be populated by the initialize call.
592210284Sjmallett *               This structure is then passed to all other USB
593210284Sjmallett *               functions.
594210284Sjmallett * @param usb_port_number
595210284Sjmallett *               Which Octeon USB port to initialize.
596210284Sjmallett * @param flags  Flags to control hardware initialization. See
597210284Sjmallett *               cvmx_usb_initialize_flags_t for the flag
598210284Sjmallett *               definitions. Some flags are mandatory.
599210284Sjmallett *
600210284Sjmallett * @return CVMX_USB_SUCCESS or a negative error code defined in
601210284Sjmallett *         cvmx_usb_status_t.
602210284Sjmallett */
603210284Sjmallettcvmx_usb_status_t cvmx_usb_initialize(cvmx_usb_state_t *state,
604210284Sjmallett                                      int usb_port_number,
605210284Sjmallett                                      cvmx_usb_initialize_flags_t flags)
606210284Sjmallett{
607210284Sjmallett    cvmx_usbnx_clk_ctl_t usbn_clk_ctl;
608210284Sjmallett    cvmx_usbnx_usbp_ctl_status_t usbn_usbp_ctl_status;
609210284Sjmallett    cvmx_usb_internal_state_t *usb = (cvmx_usb_internal_state_t*)state;
610210284Sjmallett
611210284Sjmallett    usb->init_flags = flags;
612210284Sjmallett    CVMX_USB_LOG_CALLED();
613210284Sjmallett    CVMX_USB_LOG_PARAM("%p", state);
614210284Sjmallett    CVMX_USB_LOG_PARAM("%d", usb_port_number);
615210284Sjmallett    CVMX_USB_LOG_PARAM("0x%x", flags);
616210284Sjmallett
617210284Sjmallett    /* Make sure that state is large enough to store the internal state */
618210284Sjmallett    if (sizeof(*state) < sizeof(*usb))
619210284Sjmallett        CVMX_USB_RETURN(CVMX_USB_INVALID_PARAM);
620210284Sjmallett    /* At first allow 0-1 for the usb port number */
621210284Sjmallett    if ((usb_port_number < 0) || (usb_port_number > 1))
622210284Sjmallett        CVMX_USB_RETURN(CVMX_USB_INVALID_PARAM);
623210284Sjmallett    /* For all chips except 52XX there is only one port */
624210284Sjmallett    if (!OCTEON_IS_MODEL(OCTEON_CN52XX) && (usb_port_number > 0))
625210284Sjmallett        CVMX_USB_RETURN(CVMX_USB_INVALID_PARAM);
626210284Sjmallett    /* Try to determine clock type automatically */
627210284Sjmallett    if ((flags & (CVMX_USB_INITIALIZE_FLAGS_CLOCK_XO_XI |
628210284Sjmallett                  CVMX_USB_INITIALIZE_FLAGS_CLOCK_XO_GND)) == 0)
629210284Sjmallett    {
630210284Sjmallett        if (__cvmx_helper_board_usb_get_clock_type() == USB_CLOCK_TYPE_CRYSTAL_12)
631210284Sjmallett            flags |= CVMX_USB_INITIALIZE_FLAGS_CLOCK_XO_XI;  /* Only 12 MHZ crystals are supported */
632210284Sjmallett        else
633210284Sjmallett            flags |= CVMX_USB_INITIALIZE_FLAGS_CLOCK_XO_GND;
634210284Sjmallett    }
635210284Sjmallett
636210284Sjmallett    if (flags & CVMX_USB_INITIALIZE_FLAGS_CLOCK_XO_GND)
637210284Sjmallett    {
638210284Sjmallett        /* Check for auto ref clock frequency */
639210284Sjmallett        if (!(flags & CVMX_USB_INITIALIZE_FLAGS_CLOCK_MHZ_MASK))
640210284Sjmallett            switch (__cvmx_helper_board_usb_get_clock_type())
641210284Sjmallett            {
642210284Sjmallett                case USB_CLOCK_TYPE_REF_12:
643210284Sjmallett                    flags |= CVMX_USB_INITIALIZE_FLAGS_CLOCK_12MHZ;
644210284Sjmallett                    break;
645210284Sjmallett                case USB_CLOCK_TYPE_REF_24:
646210284Sjmallett                    flags |= CVMX_USB_INITIALIZE_FLAGS_CLOCK_24MHZ;
647210284Sjmallett                    break;
648210284Sjmallett                case USB_CLOCK_TYPE_REF_48:
649210284Sjmallett                    flags |= CVMX_USB_INITIALIZE_FLAGS_CLOCK_48MHZ;
650210284Sjmallett                    break;
651210284Sjmallett                default:
652210284Sjmallett                    CVMX_USB_RETURN(CVMX_USB_INVALID_PARAM);
653210284Sjmallett                    break;
654210284Sjmallett            }
655210284Sjmallett    }
656210284Sjmallett
657229071Sgonzo    memset(usb, 0, sizeof(*usb));
658210284Sjmallett    usb->init_flags = flags;
659210284Sjmallett
660210284Sjmallett    /* Initialize the USB state structure */
661210284Sjmallett    {
662210284Sjmallett        int i;
663210284Sjmallett        usb->index = usb_port_number;
664210284Sjmallett
665210284Sjmallett        /* Initialize the transaction double linked list */
666210284Sjmallett        usb->free_transaction_head = NULL;
667210284Sjmallett        usb->free_transaction_tail = NULL;
668210284Sjmallett        for (i=0; i<MAX_TRANSACTIONS; i++)
669210284Sjmallett            __cvmx_usb_free_transaction(usb, usb->transaction + i);
670210284Sjmallett        for (i=0; i<MAX_PIPES; i++)
671210284Sjmallett            __cvmx_usb_append_pipe(&usb->free_pipes, usb->pipe + i);
672210284Sjmallett    }
673210284Sjmallett
674210284Sjmallett    /* Power On Reset and PHY Initialization */
675210284Sjmallett
676210284Sjmallett    /* 1. Wait for DCOK to assert (nothing to do) */
677210284Sjmallett    /* 2a. Write USBN0/1_CLK_CTL[POR] = 1 and
678210284Sjmallett        USBN0/1_CLK_CTL[HRST,PRST,HCLK_RST] = 0 */
679210284Sjmallett    usbn_clk_ctl.u64 = __cvmx_usb_read_csr64(usb, CVMX_USBNX_CLK_CTL(usb->index));
680210284Sjmallett    usbn_clk_ctl.s.por = 1;
681210284Sjmallett    usbn_clk_ctl.s.hrst = 0;
682210284Sjmallett    usbn_clk_ctl.s.prst = 0;
683210284Sjmallett    usbn_clk_ctl.s.hclk_rst = 0;
684210284Sjmallett    usbn_clk_ctl.s.enable = 0;
685210284Sjmallett    /* 2b. Select the USB reference clock/crystal parameters by writing
686210284Sjmallett        appropriate values to USBN0/1_CLK_CTL[P_C_SEL, P_RTYPE, P_COM_ON] */
687210284Sjmallett    if (usb->init_flags & CVMX_USB_INITIALIZE_FLAGS_CLOCK_XO_GND)
688210284Sjmallett    {
689210284Sjmallett        /* The USB port uses 12/24/48MHz 2.5V board clock
690210284Sjmallett            source at USB_XO. USB_XI should be tied to GND.
691210284Sjmallett            Most Octeon evaluation boards require this setting */
692210284Sjmallett        if (OCTEON_IS_MODEL(OCTEON_CN3XXX))
693210284Sjmallett        {
694210284Sjmallett            usbn_clk_ctl.cn31xx.p_rclk  = 1; /* From CN31XX,CN30XX manual */
695210284Sjmallett            usbn_clk_ctl.cn31xx.p_xenbn = 0;
696210284Sjmallett        }
697210284Sjmallett        else if (OCTEON_IS_MODEL(OCTEON_CN56XX) || OCTEON_IS_MODEL(OCTEON_CN50XX))
698210284Sjmallett            usbn_clk_ctl.cn56xx.p_rtype = 2; /* From CN56XX,CN50XX manual */
699210284Sjmallett        else
700210284Sjmallett            usbn_clk_ctl.cn52xx.p_rtype = 1; /* From CN52XX manual */
701210284Sjmallett
702210284Sjmallett        switch (flags & CVMX_USB_INITIALIZE_FLAGS_CLOCK_MHZ_MASK)
703210284Sjmallett        {
704210284Sjmallett            case CVMX_USB_INITIALIZE_FLAGS_CLOCK_12MHZ:
705210284Sjmallett                usbn_clk_ctl.s.p_c_sel = 0;
706210284Sjmallett                break;
707210284Sjmallett            case CVMX_USB_INITIALIZE_FLAGS_CLOCK_24MHZ:
708210284Sjmallett                usbn_clk_ctl.s.p_c_sel = 1;
709210284Sjmallett                break;
710210284Sjmallett            case CVMX_USB_INITIALIZE_FLAGS_CLOCK_48MHZ:
711210284Sjmallett                usbn_clk_ctl.s.p_c_sel = 2;
712210284Sjmallett                break;
713210284Sjmallett        }
714210284Sjmallett    }
715210284Sjmallett    else
716210284Sjmallett    {
717210284Sjmallett        /* The USB port uses a 12MHz crystal as clock source
718210284Sjmallett            at USB_XO and USB_XI */
719210284Sjmallett        if (OCTEON_IS_MODEL(OCTEON_CN3XXX))
720210284Sjmallett        {
721210284Sjmallett            usbn_clk_ctl.cn31xx.p_rclk  = 1; /* From CN31XX,CN30XX manual */
722210284Sjmallett            usbn_clk_ctl.cn31xx.p_xenbn = 1;
723210284Sjmallett        }
724210284Sjmallett        else if (OCTEON_IS_MODEL(OCTEON_CN56XX) || OCTEON_IS_MODEL(OCTEON_CN50XX))
725210284Sjmallett            usbn_clk_ctl.cn56xx.p_rtype = 0; /* From CN56XX,CN50XX manual */
726210284Sjmallett        else
727210284Sjmallett            usbn_clk_ctl.cn52xx.p_rtype = 0; /* From CN52XX manual */
728210284Sjmallett
729210284Sjmallett        usbn_clk_ctl.s.p_c_sel = 0;
730210284Sjmallett    }
731210284Sjmallett    /* 2c. Select the HCLK via writing USBN0/1_CLK_CTL[DIVIDE, DIVIDE2] and
732210284Sjmallett        setting USBN0/1_CLK_CTL[ENABLE] = 1.  Divide the core clock down such
733210284Sjmallett        that USB is as close as possible to 125Mhz */
734210284Sjmallett    {
735215990Sjmallett        int divisor = (cvmx_clock_get_rate(CVMX_CLOCK_CORE)+125000000-1)/125000000;
736210284Sjmallett        if (divisor < 4)  /* Lower than 4 doesn't seem to work properly */
737210284Sjmallett            divisor = 4;
738210284Sjmallett        usbn_clk_ctl.s.divide = divisor;
739210284Sjmallett        usbn_clk_ctl.s.divide2 = 0;
740210284Sjmallett    }
741210284Sjmallett    __cvmx_usb_write_csr64(usb, CVMX_USBNX_CLK_CTL(usb->index),
742210284Sjmallett                           usbn_clk_ctl.u64);
743210284Sjmallett    /* 2d. Write USBN0/1_CLK_CTL[HCLK_RST] = 1 */
744210284Sjmallett    usbn_clk_ctl.s.hclk_rst = 1;
745210284Sjmallett    __cvmx_usb_write_csr64(usb, CVMX_USBNX_CLK_CTL(usb->index),
746210284Sjmallett                           usbn_clk_ctl.u64);
747210284Sjmallett    /* 2e.  Wait 64 core-clock cycles for HCLK to stabilize */
748210284Sjmallett    cvmx_wait(64);
749210284Sjmallett    /* 3. Program the power-on reset field in the USBN clock-control register:
750210284Sjmallett        USBN_CLK_CTL[POR] = 0 */
751210284Sjmallett    usbn_clk_ctl.s.por = 0;
752210284Sjmallett    __cvmx_usb_write_csr64(usb, CVMX_USBNX_CLK_CTL(usb->index),
753210284Sjmallett                           usbn_clk_ctl.u64);
754210284Sjmallett    /* 4. Wait 1 ms for PHY clock to start */
755210284Sjmallett    cvmx_wait_usec(1000);
756210284Sjmallett    /* 5. Program the Reset input from automatic test equipment field in the
757210284Sjmallett        USBP control and status register: USBN_USBP_CTL_STATUS[ATE_RESET] = 1 */
758210284Sjmallett    usbn_usbp_ctl_status.u64 = __cvmx_usb_read_csr64(usb, CVMX_USBNX_USBP_CTL_STATUS(usb->index));
759210284Sjmallett    usbn_usbp_ctl_status.s.ate_reset = 1;
760210284Sjmallett    __cvmx_usb_write_csr64(usb, CVMX_USBNX_USBP_CTL_STATUS(usb->index),
761210284Sjmallett                           usbn_usbp_ctl_status.u64);
762210284Sjmallett    /* 6. Wait 10 cycles */
763210284Sjmallett    cvmx_wait(10);
764210284Sjmallett    /* 7. Clear ATE_RESET field in the USBN clock-control register:
765210284Sjmallett        USBN_USBP_CTL_STATUS[ATE_RESET] = 0 */
766210284Sjmallett    usbn_usbp_ctl_status.s.ate_reset = 0;
767210284Sjmallett    __cvmx_usb_write_csr64(usb, CVMX_USBNX_USBP_CTL_STATUS(usb->index),
768210284Sjmallett                           usbn_usbp_ctl_status.u64);
769210284Sjmallett    /* 8. Program the PHY reset field in the USBN clock-control register:
770210284Sjmallett        USBN_CLK_CTL[PRST] = 1 */
771210284Sjmallett    usbn_clk_ctl.s.prst = 1;
772210284Sjmallett    __cvmx_usb_write_csr64(usb, CVMX_USBNX_CLK_CTL(usb->index),
773210284Sjmallett                           usbn_clk_ctl.u64);
774210284Sjmallett    /* 9. Program the USBP control and status register to select host or
775210284Sjmallett        device mode. USBN_USBP_CTL_STATUS[HST_MODE] = 0 for host, = 1 for
776210284Sjmallett        device */
777215990Sjmallett    usbn_usbp_ctl_status.s.hst_mode = 0;
778210284Sjmallett    __cvmx_usb_write_csr64(usb, CVMX_USBNX_USBP_CTL_STATUS(usb->index),
779210284Sjmallett                           usbn_usbp_ctl_status.u64);
780210284Sjmallett    /* 10. Wait 1 �s */
781210284Sjmallett    cvmx_wait_usec(1);
782210284Sjmallett    /* 11. Program the hreset_n field in the USBN clock-control register:
783210284Sjmallett        USBN_CLK_CTL[HRST] = 1 */
784210284Sjmallett    usbn_clk_ctl.s.hrst = 1;
785210284Sjmallett    __cvmx_usb_write_csr64(usb, CVMX_USBNX_CLK_CTL(usb->index),
786210284Sjmallett                           usbn_clk_ctl.u64);
787210284Sjmallett    /* 12. Proceed to USB core initialization */
788210284Sjmallett    usbn_clk_ctl.s.enable = 1;
789210284Sjmallett    __cvmx_usb_write_csr64(usb, CVMX_USBNX_CLK_CTL(usb->index),
790210284Sjmallett                           usbn_clk_ctl.u64);
791210284Sjmallett    cvmx_wait_usec(1);
792210284Sjmallett
793210284Sjmallett    /* USB Core Initialization */
794210284Sjmallett
795210284Sjmallett    /* 1. Read USBC_GHWCFG1, USBC_GHWCFG2, USBC_GHWCFG3, USBC_GHWCFG4 to
796210284Sjmallett        determine USB core configuration parameters. */
797210284Sjmallett    /* Nothing needed */
798210284Sjmallett    /* 2. Program the following fields in the global AHB configuration
799210284Sjmallett        register (USBC_GAHBCFG)
800210284Sjmallett        DMA mode, USBC_GAHBCFG[DMAEn]: 1 = DMA mode, 0 = slave mode
801210284Sjmallett        Burst length, USBC_GAHBCFG[HBSTLEN] = 0
802210284Sjmallett        Nonperiodic TxFIFO empty level (slave mode only),
803210284Sjmallett        USBC_GAHBCFG[NPTXFEMPLVL]
804210284Sjmallett        Periodic TxFIFO empty level (slave mode only),
805210284Sjmallett        USBC_GAHBCFG[PTXFEMPLVL]
806210284Sjmallett        Global interrupt mask, USBC_GAHBCFG[GLBLINTRMSK] = 1 */
807210284Sjmallett    {
808210284Sjmallett        cvmx_usbcx_gahbcfg_t usbcx_gahbcfg;
809215990Sjmallett        /* Due to an errata, CN31XX doesn't support DMA */
810215990Sjmallett        if (OCTEON_IS_MODEL(OCTEON_CN31XX))
811215990Sjmallett            usb->init_flags |= CVMX_USB_INITIALIZE_FLAGS_NO_DMA;
812210284Sjmallett        usbcx_gahbcfg.u32 = 0;
813215990Sjmallett        usbcx_gahbcfg.s.dmaen = !(usb->init_flags & CVMX_USB_INITIALIZE_FLAGS_NO_DMA);
814215990Sjmallett        if (usb->init_flags & CVMX_USB_INITIALIZE_FLAGS_NO_DMA)
815215990Sjmallett            usb->idle_hardware_channels = 0x1;  /* Only use one channel with non DMA */
816215990Sjmallett        else if (OCTEON_IS_MODEL(OCTEON_CN5XXX))
817215990Sjmallett            usb->idle_hardware_channels = 0xf7; /* CN5XXX have an errata with channel 3 */
818215990Sjmallett        else
819210284Sjmallett            usb->idle_hardware_channels = 0xff;
820210284Sjmallett        usbcx_gahbcfg.s.hbstlen = 0;
821210284Sjmallett        usbcx_gahbcfg.s.nptxfemplvl = 1;
822210284Sjmallett        usbcx_gahbcfg.s.ptxfemplvl = 1;
823210284Sjmallett        usbcx_gahbcfg.s.glblintrmsk = 1;
824210284Sjmallett        __cvmx_usb_write_csr32(usb, CVMX_USBCX_GAHBCFG(usb->index),
825210284Sjmallett                               usbcx_gahbcfg.u32);
826210284Sjmallett    }
827210284Sjmallett    /* 3. Program the following fields in USBC_GUSBCFG register.
828210284Sjmallett        HS/FS timeout calibration, USBC_GUSBCFG[TOUTCAL] = 0
829210284Sjmallett        ULPI DDR select, USBC_GUSBCFG[DDRSEL] = 0
830210284Sjmallett        USB turnaround time, USBC_GUSBCFG[USBTRDTIM] = 0x5
831210284Sjmallett        PHY low-power clock select, USBC_GUSBCFG[PHYLPWRCLKSEL] = 0 */
832210284Sjmallett    {
833210284Sjmallett        cvmx_usbcx_gusbcfg_t usbcx_gusbcfg;
834210284Sjmallett        usbcx_gusbcfg.u32 = __cvmx_usb_read_csr32(usb, CVMX_USBCX_GUSBCFG(usb->index));
835210284Sjmallett        usbcx_gusbcfg.s.toutcal = 0;
836210284Sjmallett        usbcx_gusbcfg.s.ddrsel = 0;
837210284Sjmallett        usbcx_gusbcfg.s.usbtrdtim = 0x5;
838210284Sjmallett        usbcx_gusbcfg.s.phylpwrclksel = 0;
839210284Sjmallett        __cvmx_usb_write_csr32(usb, CVMX_USBCX_GUSBCFG(usb->index),
840210284Sjmallett                               usbcx_gusbcfg.u32);
841210284Sjmallett    }
842210284Sjmallett    /* 4. The software must unmask the following bits in the USBC_GINTMSK
843210284Sjmallett        register.
844210284Sjmallett        OTG interrupt mask, USBC_GINTMSK[OTGINTMSK] = 1
845210284Sjmallett        Mode mismatch interrupt mask, USBC_GINTMSK[MODEMISMSK] = 1 */
846210284Sjmallett    {
847210284Sjmallett        cvmx_usbcx_gintmsk_t usbcx_gintmsk;
848210284Sjmallett        int channel;
849210284Sjmallett
850210284Sjmallett        usbcx_gintmsk.u32 = __cvmx_usb_read_csr32(usb, CVMX_USBCX_GINTMSK(usb->index));
851210284Sjmallett        usbcx_gintmsk.s.otgintmsk = 1;
852210284Sjmallett        usbcx_gintmsk.s.modemismsk = 1;
853210284Sjmallett        usbcx_gintmsk.s.hchintmsk = 1;
854210284Sjmallett        usbcx_gintmsk.s.sofmsk = 0;
855215990Sjmallett        /* We need RX FIFO interrupts if we don't have DMA */
856215990Sjmallett        if (usb->init_flags & CVMX_USB_INITIALIZE_FLAGS_NO_DMA)
857215990Sjmallett            usbcx_gintmsk.s.rxflvlmsk = 1;
858210284Sjmallett        __cvmx_usb_write_csr32(usb, CVMX_USBCX_GINTMSK(usb->index),
859210284Sjmallett                               usbcx_gintmsk.u32);
860210284Sjmallett
861215990Sjmallett        /* Disable all channel interrupts. We'll enable them per channel later */
862210284Sjmallett        for (channel=0; channel<8; channel++)
863215990Sjmallett            __cvmx_usb_write_csr32(usb, CVMX_USBCX_HCINTMSKX(channel, usb->index), 0);
864210284Sjmallett    }
865210284Sjmallett
866210284Sjmallett    {
867210284Sjmallett        /* Host Port Initialization */
868210284Sjmallett        if (cvmx_unlikely(usb->init_flags & CVMX_USB_INITIALIZE_FLAGS_DEBUG_INFO))
869210284Sjmallett            cvmx_dprintf("%s: USB%d is in host mode\n", __FUNCTION__, usb->index);
870210284Sjmallett
871210284Sjmallett        /* 1. Program the host-port interrupt-mask field to unmask,
872210284Sjmallett            USBC_GINTMSK[PRTINT] = 1 */
873210284Sjmallett        USB_SET_FIELD32(CVMX_USBCX_GINTMSK(usb->index), cvmx_usbcx_gintmsk_t,
874210284Sjmallett                        prtintmsk, 1);
875210284Sjmallett        USB_SET_FIELD32(CVMX_USBCX_GINTMSK(usb->index), cvmx_usbcx_gintmsk_t,
876210284Sjmallett                        disconnintmsk, 1);
877210284Sjmallett        /* 2. Program the USBC_HCFG register to select full-speed host or
878210284Sjmallett            high-speed host. */
879210284Sjmallett        {
880210284Sjmallett            cvmx_usbcx_hcfg_t usbcx_hcfg;
881210284Sjmallett            usbcx_hcfg.u32 = __cvmx_usb_read_csr32(usb, CVMX_USBCX_HCFG(usb->index));
882210284Sjmallett            usbcx_hcfg.s.fslssupp = 0;
883210284Sjmallett            usbcx_hcfg.s.fslspclksel = 0;
884210284Sjmallett            __cvmx_usb_write_csr32(usb, CVMX_USBCX_HCFG(usb->index), usbcx_hcfg.u32);
885210284Sjmallett        }
886210284Sjmallett        /* 3. Program the port power bit to drive VBUS on the USB,
887210284Sjmallett            USBC_HPRT[PRTPWR] = 1 */
888210284Sjmallett        USB_SET_FIELD32(CVMX_USBCX_HPRT(usb->index), cvmx_usbcx_hprt_t, prtpwr, 1);
889210284Sjmallett
890210284Sjmallett        /* Steps 4-15 from the manual are done later in the port enable */
891210284Sjmallett    }
892210284Sjmallett
893215990Sjmallett#ifdef __CVMX_ERROR_H__
894215990Sjmallett    cvmx_error_enable_group(CVMX_ERROR_GROUP_USB, usb->index);
895215990Sjmallett#endif
896210284Sjmallett    CVMX_USB_RETURN(CVMX_USB_SUCCESS);
897210284Sjmallett}
898215990Sjmallett#ifdef CVMX_BUILD_FOR_LINUX_KERNEL
899215990SjmallettEXPORT_SYMBOL(cvmx_usb_initialize);
900215990Sjmallett#endif
901210284Sjmallett
902210284Sjmallett
903210284Sjmallett/**
904210284Sjmallett * Shutdown a USB port after a call to cvmx_usb_initialize().
905210284Sjmallett * The port should be disabled with all pipes closed when this
906210284Sjmallett * function is called.
907210284Sjmallett *
908210284Sjmallett * @param state  USB device state populated by
909210284Sjmallett *               cvmx_usb_initialize().
910210284Sjmallett *
911210284Sjmallett * @return CVMX_USB_SUCCESS or a negative error code defined in
912210284Sjmallett *         cvmx_usb_status_t.
913210284Sjmallett */
914210284Sjmallettcvmx_usb_status_t cvmx_usb_shutdown(cvmx_usb_state_t *state)
915210284Sjmallett{
916210284Sjmallett    cvmx_usbnx_clk_ctl_t usbn_clk_ctl;
917210284Sjmallett    cvmx_usb_internal_state_t *usb = (cvmx_usb_internal_state_t*)state;
918210284Sjmallett
919210284Sjmallett    CVMX_USB_LOG_CALLED();
920210284Sjmallett    CVMX_USB_LOG_PARAM("%p", state);
921210284Sjmallett
922210284Sjmallett    /* Make sure all pipes are closed */
923210284Sjmallett    if (usb->idle_pipes.head ||
924210284Sjmallett        usb->active_pipes[CVMX_USB_TRANSFER_ISOCHRONOUS].head ||
925210284Sjmallett        usb->active_pipes[CVMX_USB_TRANSFER_INTERRUPT].head ||
926210284Sjmallett        usb->active_pipes[CVMX_USB_TRANSFER_CONTROL].head ||
927210284Sjmallett        usb->active_pipes[CVMX_USB_TRANSFER_BULK].head)
928210284Sjmallett        CVMX_USB_RETURN(CVMX_USB_BUSY);
929210284Sjmallett
930215990Sjmallett#ifdef __CVMX_ERROR_H__
931215990Sjmallett    cvmx_error_disable_group(CVMX_ERROR_GROUP_USB, usb->index);
932215990Sjmallett#endif
933215990Sjmallett
934210284Sjmallett    /* Disable the clocks and put them in power on reset */
935210284Sjmallett    usbn_clk_ctl.u64 = __cvmx_usb_read_csr64(usb, CVMX_USBNX_CLK_CTL(usb->index));
936210284Sjmallett    usbn_clk_ctl.s.enable = 1;
937210284Sjmallett    usbn_clk_ctl.s.por = 1;
938210284Sjmallett    usbn_clk_ctl.s.hclk_rst = 1;
939210284Sjmallett    usbn_clk_ctl.s.prst = 0;
940210284Sjmallett    usbn_clk_ctl.s.hrst = 0;
941210284Sjmallett    __cvmx_usb_write_csr64(usb, CVMX_USBNX_CLK_CTL(usb->index),
942210284Sjmallett                           usbn_clk_ctl.u64);
943210284Sjmallett    CVMX_USB_RETURN(CVMX_USB_SUCCESS);
944210284Sjmallett}
945215990Sjmallett#ifdef CVMX_BUILD_FOR_LINUX_KERNEL
946215990SjmallettEXPORT_SYMBOL(cvmx_usb_shutdown);
947215990Sjmallett#endif
948210284Sjmallett
949210284Sjmallett
950210284Sjmallett/**
951210284Sjmallett * Enable a USB port. After this call succeeds, the USB port is
952210284Sjmallett * online and servicing requests.
953210284Sjmallett *
954210284Sjmallett * @param state  USB device state populated by
955210284Sjmallett *               cvmx_usb_initialize().
956210284Sjmallett *
957210284Sjmallett * @return CVMX_USB_SUCCESS or a negative error code defined in
958210284Sjmallett *         cvmx_usb_status_t.
959210284Sjmallett */
960210284Sjmallettcvmx_usb_status_t cvmx_usb_enable(cvmx_usb_state_t *state)
961210284Sjmallett{
962210284Sjmallett    cvmx_usbcx_ghwcfg3_t usbcx_ghwcfg3;
963210284Sjmallett    cvmx_usb_internal_state_t *usb = (cvmx_usb_internal_state_t*)state;
964210284Sjmallett
965210284Sjmallett    CVMX_USB_LOG_CALLED();
966210284Sjmallett    CVMX_USB_LOG_PARAM("%p", state);
967210284Sjmallett
968210284Sjmallett    usb->usbcx_hprt.u32 = __cvmx_usb_read_csr32(usb, CVMX_USBCX_HPRT(usb->index));
969210284Sjmallett
970210284Sjmallett    /* If the port is already enabled the just return. We don't need to do
971210284Sjmallett        anything */
972210284Sjmallett    if (usb->usbcx_hprt.s.prtena)
973210284Sjmallett        CVMX_USB_RETURN(CVMX_USB_SUCCESS);
974210284Sjmallett
975210284Sjmallett    /* If there is nothing plugged into the port then fail immediately */
976210284Sjmallett    if (!usb->usbcx_hprt.s.prtconnsts)
977210284Sjmallett    {
978210284Sjmallett        if (cvmx_unlikely(usb->init_flags & CVMX_USB_INITIALIZE_FLAGS_DEBUG_INFO))
979210284Sjmallett            cvmx_dprintf("%s: USB%d Nothing plugged into the port\n", __FUNCTION__, usb->index);
980210284Sjmallett        CVMX_USB_RETURN(CVMX_USB_TIMEOUT);
981210284Sjmallett    }
982210284Sjmallett
983210284Sjmallett    /* Program the port reset bit to start the reset process */
984210284Sjmallett    USB_SET_FIELD32(CVMX_USBCX_HPRT(usb->index), cvmx_usbcx_hprt_t, prtrst, 1);
985210284Sjmallett
986210284Sjmallett    /* Wait at least 50ms (high speed), or 10ms (full speed) for the reset
987210284Sjmallett        process to complete. */
988210284Sjmallett    cvmx_wait_usec(50000);
989210284Sjmallett
990210284Sjmallett    /* Program the port reset bit to 0, USBC_HPRT[PRTRST] = 0 */
991210284Sjmallett    USB_SET_FIELD32(CVMX_USBCX_HPRT(usb->index), cvmx_usbcx_hprt_t, prtrst, 0);
992210284Sjmallett
993210284Sjmallett    /* Wait for the USBC_HPRT[PRTENA]. */
994210284Sjmallett    if (CVMX_WAIT_FOR_FIELD32(CVMX_USBCX_HPRT(usb->index), cvmx_usbcx_hprt_t,
995210284Sjmallett                              prtena, ==, 1, 100000))
996210284Sjmallett    {
997210284Sjmallett        if (cvmx_unlikely(usb->init_flags & CVMX_USB_INITIALIZE_FLAGS_DEBUG_INFO))
998210284Sjmallett            cvmx_dprintf("%s: Timeout waiting for the port to finish reset\n",
999210284Sjmallett                         __FUNCTION__);
1000210284Sjmallett        CVMX_USB_RETURN(CVMX_USB_TIMEOUT);
1001210284Sjmallett    }
1002210284Sjmallett
1003210284Sjmallett    /* Read the port speed field to get the enumerated speed, USBC_HPRT[PRTSPD]. */
1004210284Sjmallett    usb->usbcx_hprt.u32 = __cvmx_usb_read_csr32(usb, CVMX_USBCX_HPRT(usb->index));
1005210284Sjmallett    if (cvmx_unlikely(usb->init_flags & CVMX_USB_INITIALIZE_FLAGS_DEBUG_INFO))
1006210284Sjmallett        cvmx_dprintf("%s: USB%d is in %s speed mode\n", __FUNCTION__, usb->index,
1007210284Sjmallett                     (usb->usbcx_hprt.s.prtspd == CVMX_USB_SPEED_HIGH) ? "high" :
1008210284Sjmallett                     (usb->usbcx_hprt.s.prtspd == CVMX_USB_SPEED_FULL) ? "full" :
1009210284Sjmallett                     "low");
1010210284Sjmallett
1011210284Sjmallett    usbcx_ghwcfg3.u32 = __cvmx_usb_read_csr32(usb, CVMX_USBCX_GHWCFG3(usb->index));
1012210284Sjmallett
1013210284Sjmallett    /* 13. Program the USBC_GRXFSIZ register to select the size of the receive
1014210284Sjmallett        FIFO (25%). */
1015210284Sjmallett    USB_SET_FIELD32(CVMX_USBCX_GRXFSIZ(usb->index), cvmx_usbcx_grxfsiz_t,
1016210284Sjmallett                    rxfdep, usbcx_ghwcfg3.s.dfifodepth / 4);
1017210284Sjmallett    /* 14. Program the USBC_GNPTXFSIZ register to select the size and the
1018210284Sjmallett        start address of the non- periodic transmit FIFO for nonperiodic
1019210284Sjmallett        transactions (50%). */
1020210284Sjmallett    {
1021210284Sjmallett        cvmx_usbcx_gnptxfsiz_t siz;
1022210284Sjmallett        siz.u32 = __cvmx_usb_read_csr32(usb, CVMX_USBCX_GNPTXFSIZ(usb->index));
1023210284Sjmallett        siz.s.nptxfdep = usbcx_ghwcfg3.s.dfifodepth / 2;
1024210284Sjmallett        siz.s.nptxfstaddr = usbcx_ghwcfg3.s.dfifodepth / 4;
1025210284Sjmallett        __cvmx_usb_write_csr32(usb, CVMX_USBCX_GNPTXFSIZ(usb->index), siz.u32);
1026210284Sjmallett    }
1027210284Sjmallett    /* 15. Program the USBC_HPTXFSIZ register to select the size and start
1028210284Sjmallett        address of the periodic transmit FIFO for periodic transactions (25%). */
1029210284Sjmallett    {
1030210284Sjmallett        cvmx_usbcx_hptxfsiz_t siz;
1031210284Sjmallett        siz.u32 = __cvmx_usb_read_csr32(usb, CVMX_USBCX_HPTXFSIZ(usb->index));
1032210284Sjmallett        siz.s.ptxfsize = usbcx_ghwcfg3.s.dfifodepth / 4;
1033210284Sjmallett        siz.s.ptxfstaddr = 3 * usbcx_ghwcfg3.s.dfifodepth / 4;
1034210284Sjmallett        __cvmx_usb_write_csr32(usb, CVMX_USBCX_HPTXFSIZ(usb->index), siz.u32);
1035210284Sjmallett    }
1036210284Sjmallett    /* Flush all FIFOs */
1037210284Sjmallett    USB_SET_FIELD32(CVMX_USBCX_GRSTCTL(usb->index), cvmx_usbcx_grstctl_t, txfnum, 0x10);
1038210284Sjmallett    USB_SET_FIELD32(CVMX_USBCX_GRSTCTL(usb->index), cvmx_usbcx_grstctl_t, txfflsh, 1);
1039210284Sjmallett    CVMX_WAIT_FOR_FIELD32(CVMX_USBCX_GRSTCTL(usb->index), cvmx_usbcx_grstctl_t,
1040210284Sjmallett                          txfflsh, ==, 0, 100);
1041210284Sjmallett    USB_SET_FIELD32(CVMX_USBCX_GRSTCTL(usb->index), cvmx_usbcx_grstctl_t, rxfflsh, 1);
1042210284Sjmallett    CVMX_WAIT_FOR_FIELD32(CVMX_USBCX_GRSTCTL(usb->index), cvmx_usbcx_grstctl_t,
1043210284Sjmallett                          rxfflsh, ==, 0, 100);
1044210284Sjmallett
1045210284Sjmallett    CVMX_USB_RETURN(CVMX_USB_SUCCESS);
1046210284Sjmallett}
1047215990Sjmallett#ifdef CVMX_BUILD_FOR_LINUX_KERNEL
1048215990SjmallettEXPORT_SYMBOL(cvmx_usb_enable);
1049215990Sjmallett#endif
1050210284Sjmallett
1051210284Sjmallett
1052210284Sjmallett/**
1053210284Sjmallett * Disable a USB port. After this call the USB port will not
1054210284Sjmallett * generate data transfers and will not generate events.
1055210284Sjmallett * Transactions in process will fail and call their
1056210284Sjmallett * associated callbacks.
1057210284Sjmallett *
1058210284Sjmallett * @param state  USB device state populated by
1059210284Sjmallett *               cvmx_usb_initialize().
1060210284Sjmallett *
1061210284Sjmallett * @return CVMX_USB_SUCCESS or a negative error code defined in
1062210284Sjmallett *         cvmx_usb_status_t.
1063210284Sjmallett */
1064210284Sjmallettcvmx_usb_status_t cvmx_usb_disable(cvmx_usb_state_t *state)
1065210284Sjmallett{
1066210284Sjmallett    cvmx_usb_internal_state_t *usb = (cvmx_usb_internal_state_t*)state;
1067210284Sjmallett
1068210284Sjmallett    CVMX_USB_LOG_CALLED();
1069210284Sjmallett    CVMX_USB_LOG_PARAM("%p", state);
1070210284Sjmallett
1071210284Sjmallett    /* Disable the port */
1072210284Sjmallett    USB_SET_FIELD32(CVMX_USBCX_HPRT(usb->index), cvmx_usbcx_hprt_t, prtena, 1);
1073210284Sjmallett    CVMX_USB_RETURN(CVMX_USB_SUCCESS);
1074210284Sjmallett}
1075215990Sjmallett#ifdef CVMX_BUILD_FOR_LINUX_KERNEL
1076215990SjmallettEXPORT_SYMBOL(cvmx_usb_disable);
1077215990Sjmallett#endif
1078210284Sjmallett
1079210284Sjmallett
1080210284Sjmallett/**
1081210284Sjmallett * Get the current state of the USB port. Use this call to
1082210284Sjmallett * determine if the usb port has anything connected, is enabled,
1083210284Sjmallett * or has some sort of error condition. The return value of this
1084210284Sjmallett * call has "changed" bits to signal of the value of some fields
1085210284Sjmallett * have changed between calls. These "changed" fields are based
1086210284Sjmallett * on the last call to cvmx_usb_set_status(). In order to clear
1087210284Sjmallett * them, you must update the status through cvmx_usb_set_status().
1088210284Sjmallett *
1089210284Sjmallett * @param state  USB device state populated by
1090210284Sjmallett *               cvmx_usb_initialize().
1091210284Sjmallett *
1092210284Sjmallett * @return Port status information
1093210284Sjmallett */
1094210284Sjmallettcvmx_usb_port_status_t cvmx_usb_get_status(cvmx_usb_state_t *state)
1095210284Sjmallett{
1096210284Sjmallett    cvmx_usbcx_hprt_t usbc_hprt;
1097210284Sjmallett    cvmx_usb_port_status_t result;
1098210284Sjmallett    cvmx_usb_internal_state_t *usb = (cvmx_usb_internal_state_t*)state;
1099210284Sjmallett
1100210284Sjmallett    memset(&result, 0, sizeof(result));
1101210284Sjmallett
1102210284Sjmallett    CVMX_USB_LOG_CALLED();
1103210284Sjmallett    CVMX_USB_LOG_PARAM("%p", state);
1104210284Sjmallett
1105210284Sjmallett    usbc_hprt.u32 = __cvmx_usb_read_csr32(usb, CVMX_USBCX_HPRT(usb->index));
1106210284Sjmallett    result.port_enabled = usbc_hprt.s.prtena;
1107210284Sjmallett    result.port_over_current = usbc_hprt.s.prtovrcurract;
1108210284Sjmallett    result.port_powered = usbc_hprt.s.prtpwr;
1109210284Sjmallett    result.port_speed = usbc_hprt.s.prtspd;
1110210284Sjmallett    result.connected = usbc_hprt.s.prtconnsts;
1111210284Sjmallett    result.connect_change = (result.connected != usb->port_status.connected);
1112210284Sjmallett
1113210284Sjmallett    if (cvmx_unlikely(usb->init_flags & CVMX_USB_INITIALIZE_FLAGS_DEBUG_CALLS))
1114210284Sjmallett        cvmx_dprintf("%*s%s: returned port enabled=%d, over_current=%d, powered=%d, speed=%d, connected=%d, connect_change=%d\n",
1115210284Sjmallett                     2*(--usb->indent), "", __FUNCTION__,
1116210284Sjmallett                     result.port_enabled,
1117210284Sjmallett                     result.port_over_current,
1118210284Sjmallett                     result.port_powered,
1119210284Sjmallett                     result.port_speed,
1120210284Sjmallett                     result.connected,
1121210284Sjmallett                     result.connect_change);
1122210284Sjmallett    return result;
1123210284Sjmallett}
1124215990Sjmallett#ifdef CVMX_BUILD_FOR_LINUX_KERNEL
1125215990SjmallettEXPORT_SYMBOL(cvmx_usb_get_status);
1126215990Sjmallett#endif
1127210284Sjmallett
1128210284Sjmallett
1129210284Sjmallett/**
1130210284Sjmallett * Set the current state of the USB port. The status is used as
1131210284Sjmallett * a reference for the "changed" bits returned by
1132210284Sjmallett * cvmx_usb_get_status(). Other than serving as a reference, the
1133210284Sjmallett * status passed to this function is not used. No fields can be
1134210284Sjmallett * changed through this call.
1135210284Sjmallett *
1136210284Sjmallett * @param state  USB device state populated by
1137210284Sjmallett *               cvmx_usb_initialize().
1138210284Sjmallett * @param port_status
1139210284Sjmallett *               Port status to set, most like returned by cvmx_usb_get_status()
1140210284Sjmallett */
1141210284Sjmallettvoid cvmx_usb_set_status(cvmx_usb_state_t *state, cvmx_usb_port_status_t port_status)
1142210284Sjmallett{
1143210284Sjmallett    cvmx_usb_internal_state_t *usb = (cvmx_usb_internal_state_t*)state;
1144210284Sjmallett    CVMX_USB_LOG_CALLED();
1145210284Sjmallett    CVMX_USB_LOG_PARAM("%p", state);
1146210284Sjmallett    usb->port_status = port_status;
1147210284Sjmallett    CVMX_USB_RETURN_NOTHING();
1148210284Sjmallett}
1149215990Sjmallett#ifdef CVMX_BUILD_FOR_LINUX_KERNEL
1150215990SjmallettEXPORT_SYMBOL(cvmx_usb_set_status);
1151215990Sjmallett#endif
1152210284Sjmallett
1153210284Sjmallett
1154210284Sjmallett/**
1155210284Sjmallett * @INTERNAL
1156210284Sjmallett * Convert a USB transaction into a handle
1157210284Sjmallett *
1158210284Sjmallett * @param usb    USB device state populated by
1159210284Sjmallett *               cvmx_usb_initialize().
1160210284Sjmallett * @param transaction
1161210284Sjmallett *               Transaction to get handle for
1162210284Sjmallett *
1163210284Sjmallett * @return Handle
1164210284Sjmallett */
1165210284Sjmallettstatic inline int __cvmx_usb_get_submit_handle(cvmx_usb_internal_state_t *usb,
1166210284Sjmallett                                        cvmx_usb_transaction_t *transaction)
1167210284Sjmallett{
1168210284Sjmallett    return ((unsigned long)transaction - (unsigned long)usb->transaction) /
1169210284Sjmallett            sizeof(*transaction);
1170210284Sjmallett}
1171210284Sjmallett
1172210284Sjmallett
1173210284Sjmallett/**
1174210284Sjmallett * @INTERNAL
1175210284Sjmallett * Convert a USB pipe into a handle
1176210284Sjmallett *
1177210284Sjmallett * @param usb    USB device state populated by
1178210284Sjmallett *               cvmx_usb_initialize().
1179210284Sjmallett * @param pipe   Pipe to get handle for
1180210284Sjmallett *
1181210284Sjmallett * @return Handle
1182210284Sjmallett */
1183210284Sjmallettstatic inline int __cvmx_usb_get_pipe_handle(cvmx_usb_internal_state_t *usb,
1184210284Sjmallett                                        cvmx_usb_pipe_t *pipe)
1185210284Sjmallett{
1186210284Sjmallett    return ((unsigned long)pipe - (unsigned long)usb->pipe) / sizeof(*pipe);
1187210284Sjmallett}
1188210284Sjmallett
1189210284Sjmallett
1190210284Sjmallett/**
1191210284Sjmallett * Open a virtual pipe between the host and a USB device. A pipe
1192210284Sjmallett * must be opened before data can be transferred between a device
1193210284Sjmallett * and Octeon.
1194210284Sjmallett *
1195210284Sjmallett * @param state      USB device state populated by
1196210284Sjmallett *                   cvmx_usb_initialize().
1197210284Sjmallett * @param flags      Optional pipe flags defined in
1198210284Sjmallett *                   cvmx_usb_pipe_flags_t.
1199210284Sjmallett * @param device_addr
1200210284Sjmallett *                   USB device address to open the pipe to
1201210284Sjmallett *                   (0-127).
1202210284Sjmallett * @param endpoint_num
1203210284Sjmallett *                   USB endpoint number to open the pipe to
1204210284Sjmallett *                   (0-15).
1205210284Sjmallett * @param device_speed
1206210284Sjmallett *                   The speed of the device the pipe is going
1207210284Sjmallett *                   to. This must match the device's speed,
1208210284Sjmallett *                   which may be different than the port speed.
1209210284Sjmallett * @param max_packet The maximum packet length the device can
1210210284Sjmallett *                   transmit/receive (low speed=0-8, full
1211210284Sjmallett *                   speed=0-1023, high speed=0-1024). This value
1212215990Sjmallett *                   comes from the standard endpoint descriptor
1213210284Sjmallett *                   field wMaxPacketSize bits <10:0>.
1214210284Sjmallett * @param transfer_type
1215210284Sjmallett *                   The type of transfer this pipe is for.
1216210284Sjmallett * @param transfer_dir
1217210284Sjmallett *                   The direction the pipe is in. This is not
1218210284Sjmallett *                   used for control pipes.
1219210284Sjmallett * @param interval   For ISOCHRONOUS and INTERRUPT transfers,
1220210284Sjmallett *                   this is how often the transfer is scheduled
1221210284Sjmallett *                   for. All other transfers should specify
1222210284Sjmallett *                   zero. The units are in frames (8000/sec at
1223210284Sjmallett *                   high speed, 1000/sec for full speed).
1224210284Sjmallett * @param multi_count
1225210284Sjmallett *                   For high speed devices, this is the maximum
1226210284Sjmallett *                   allowed number of packet per microframe.
1227210284Sjmallett *                   Specify zero for non high speed devices. This
1228215990Sjmallett *                   value comes from the standard endpoint descriptor
1229210284Sjmallett *                   field wMaxPacketSize bits <12:11>.
1230210284Sjmallett * @param hub_device_addr
1231210284Sjmallett *                   Hub device address this device is connected
1232210284Sjmallett *                   to. Devices connected directly to Octeon
1233210284Sjmallett *                   use zero. This is only used when the device
1234210284Sjmallett *                   is full/low speed behind a high speed hub.
1235210284Sjmallett *                   The address will be of the high speed hub,
1236210284Sjmallett *                   not and full speed hubs after it.
1237210284Sjmallett * @param hub_port   Which port on the hub the device is
1238210284Sjmallett *                   connected. Use zero for devices connected
1239210284Sjmallett *                   directly to Octeon. Like hub_device_addr,
1240210284Sjmallett *                   this is only used for full/low speed
1241210284Sjmallett *                   devices behind a high speed hub.
1242210284Sjmallett *
1243210284Sjmallett * @return A non negative value is a pipe handle. Negative
1244210284Sjmallett *         values are failure codes from cvmx_usb_status_t.
1245210284Sjmallett */
1246210284Sjmallettint cvmx_usb_open_pipe(cvmx_usb_state_t *state, cvmx_usb_pipe_flags_t flags,
1247210284Sjmallett                       int device_addr, int endpoint_num,
1248210284Sjmallett                       cvmx_usb_speed_t device_speed, int max_packet,
1249210284Sjmallett                       cvmx_usb_transfer_t transfer_type,
1250210284Sjmallett                       cvmx_usb_direction_t transfer_dir, int interval,
1251210284Sjmallett                       int multi_count, int hub_device_addr, int hub_port)
1252210284Sjmallett{
1253210284Sjmallett    cvmx_usb_pipe_t *pipe;
1254210284Sjmallett    cvmx_usb_internal_state_t *usb = (cvmx_usb_internal_state_t*)state;
1255210284Sjmallett
1256210284Sjmallett    CVMX_USB_LOG_CALLED();
1257210284Sjmallett    CVMX_USB_LOG_PARAM("%p", state);
1258210284Sjmallett    CVMX_USB_LOG_PARAM("0x%x", flags);
1259210284Sjmallett    CVMX_USB_LOG_PARAM("%d", device_addr);
1260210284Sjmallett    CVMX_USB_LOG_PARAM("%d", endpoint_num);
1261210284Sjmallett    CVMX_USB_LOG_PARAM("%d", device_speed);
1262210284Sjmallett    CVMX_USB_LOG_PARAM("%d", max_packet);
1263210284Sjmallett    CVMX_USB_LOG_PARAM("%d", transfer_type);
1264210284Sjmallett    CVMX_USB_LOG_PARAM("%d", transfer_dir);
1265210284Sjmallett    CVMX_USB_LOG_PARAM("%d", interval);
1266210284Sjmallett    CVMX_USB_LOG_PARAM("%d", multi_count);
1267210284Sjmallett    CVMX_USB_LOG_PARAM("%d", hub_device_addr);
1268210284Sjmallett    CVMX_USB_LOG_PARAM("%d", hub_port);
1269210284Sjmallett
1270210284Sjmallett    if (cvmx_unlikely((device_addr < 0) || (device_addr > MAX_USB_ADDRESS)))
1271210284Sjmallett        CVMX_USB_RETURN(CVMX_USB_INVALID_PARAM);
1272210284Sjmallett    if (cvmx_unlikely((endpoint_num < 0) || (endpoint_num > MAX_USB_ENDPOINT)))
1273210284Sjmallett        CVMX_USB_RETURN(CVMX_USB_INVALID_PARAM);
1274210284Sjmallett    if (cvmx_unlikely(device_speed > CVMX_USB_SPEED_LOW))
1275210284Sjmallett        CVMX_USB_RETURN(CVMX_USB_INVALID_PARAM);
1276210284Sjmallett    if (cvmx_unlikely((max_packet <= 0) || (max_packet > 1024)))
1277210284Sjmallett        CVMX_USB_RETURN(CVMX_USB_INVALID_PARAM);
1278210284Sjmallett    if (cvmx_unlikely(transfer_type > CVMX_USB_TRANSFER_INTERRUPT))
1279210284Sjmallett        CVMX_USB_RETURN(CVMX_USB_INVALID_PARAM);
1280210284Sjmallett    if (cvmx_unlikely((transfer_dir != CVMX_USB_DIRECTION_OUT) &&
1281210284Sjmallett        (transfer_dir != CVMX_USB_DIRECTION_IN)))
1282210284Sjmallett        CVMX_USB_RETURN(CVMX_USB_INVALID_PARAM);
1283210284Sjmallett    if (cvmx_unlikely(interval < 0))
1284210284Sjmallett        CVMX_USB_RETURN(CVMX_USB_INVALID_PARAM);
1285210284Sjmallett    if (cvmx_unlikely((transfer_type == CVMX_USB_TRANSFER_CONTROL) && interval))
1286210284Sjmallett        CVMX_USB_RETURN(CVMX_USB_INVALID_PARAM);
1287210284Sjmallett    if (cvmx_unlikely(multi_count < 0))
1288210284Sjmallett        CVMX_USB_RETURN(CVMX_USB_INVALID_PARAM);
1289210284Sjmallett    if (cvmx_unlikely((device_speed != CVMX_USB_SPEED_HIGH) &&
1290210284Sjmallett        (multi_count != 0)))
1291210284Sjmallett        CVMX_USB_RETURN(CVMX_USB_INVALID_PARAM);
1292210284Sjmallett    if (cvmx_unlikely((hub_device_addr < 0) || (hub_device_addr > MAX_USB_ADDRESS)))
1293210284Sjmallett        CVMX_USB_RETURN(CVMX_USB_INVALID_PARAM);
1294210284Sjmallett    if (cvmx_unlikely((hub_port < 0) || (hub_port > MAX_USB_HUB_PORT)))
1295210284Sjmallett        CVMX_USB_RETURN(CVMX_USB_INVALID_PARAM);
1296210284Sjmallett
1297210284Sjmallett    /* Find a free pipe */
1298210284Sjmallett    pipe = usb->free_pipes.head;
1299210284Sjmallett    if (!pipe)
1300210284Sjmallett        CVMX_USB_RETURN(CVMX_USB_NO_MEMORY);
1301210284Sjmallett    __cvmx_usb_remove_pipe(&usb->free_pipes, pipe);
1302210284Sjmallett    pipe->flags = flags | __CVMX_USB_PIPE_FLAGS_OPEN;
1303210284Sjmallett    if ((device_speed == CVMX_USB_SPEED_HIGH) &&
1304210284Sjmallett        (transfer_dir == CVMX_USB_DIRECTION_OUT) &&
1305210284Sjmallett        (transfer_type == CVMX_USB_TRANSFER_BULK))
1306210284Sjmallett        pipe->flags |= __CVMX_USB_PIPE_FLAGS_NEED_PING;
1307210284Sjmallett    pipe->device_addr = device_addr;
1308210284Sjmallett    pipe->endpoint_num = endpoint_num;
1309210284Sjmallett    pipe->device_speed = device_speed;
1310210284Sjmallett    pipe->max_packet = max_packet;
1311210284Sjmallett    pipe->transfer_type = transfer_type;
1312210284Sjmallett    pipe->transfer_dir = transfer_dir;
1313210284Sjmallett    /* All pipes use interval to rate limit NAK processing. Force an interval
1314210284Sjmallett        if one wasn't supplied */
1315210284Sjmallett    if (!interval)
1316210284Sjmallett        interval = 1;
1317215990Sjmallett    if (__cvmx_usb_pipe_needs_split(usb, pipe))
1318215990Sjmallett    {
1319215990Sjmallett        pipe->interval = interval*8;
1320215990Sjmallett        /* Force start splits to be schedule on uFrame 0 */
1321215990Sjmallett        pipe->next_tx_frame = ((usb->frame_number+7)&~7) + pipe->interval;
1322215990Sjmallett    }
1323210284Sjmallett    else
1324215990Sjmallett    {
1325215990Sjmallett        pipe->interval = interval;
1326215990Sjmallett        pipe->next_tx_frame = usb->frame_number + pipe->interval;
1327215990Sjmallett    }
1328210284Sjmallett    pipe->multi_count = multi_count;
1329210284Sjmallett    pipe->hub_device_addr = hub_device_addr;
1330210284Sjmallett    pipe->hub_port = hub_port;
1331210284Sjmallett    pipe->pid_toggle = 0;
1332210284Sjmallett    pipe->split_sc_frame = -1;
1333210284Sjmallett    __cvmx_usb_append_pipe(&usb->idle_pipes, pipe);
1334210284Sjmallett
1335210284Sjmallett    /* We don't need to tell the hardware about this pipe yet since
1336210284Sjmallett        it doesn't have any submitted requests */
1337210284Sjmallett
1338210284Sjmallett    CVMX_USB_RETURN(__cvmx_usb_get_pipe_handle(usb, pipe));
1339210284Sjmallett}
1340215990Sjmallett#ifdef CVMX_BUILD_FOR_LINUX_KERNEL
1341215990SjmallettEXPORT_SYMBOL(cvmx_usb_open_pipe);
1342215990Sjmallett#endif
1343210284Sjmallett
1344210284Sjmallett
1345210284Sjmallett/**
1346210284Sjmallett * @INTERNAL
1347215990Sjmallett * Poll the RX FIFOs and remove data as needed. This function is only used
1348215990Sjmallett * in non DMA mode. It is very important that this function be called quickly
1349215990Sjmallett * enough to prevent FIFO overflow.
1350215990Sjmallett *
1351215990Sjmallett * @param usb     USB device state populated by
1352215990Sjmallett *                cvmx_usb_initialize().
1353215990Sjmallett */
1354215990Sjmallettstatic void __cvmx_usb_poll_rx_fifo(cvmx_usb_internal_state_t *usb)
1355215990Sjmallett{
1356215990Sjmallett    cvmx_usbcx_grxstsph_t rx_status;
1357215990Sjmallett    int channel;
1358215990Sjmallett    int bytes;
1359215990Sjmallett    uint64_t address;
1360215990Sjmallett    uint32_t *ptr;
1361215990Sjmallett
1362215990Sjmallett    CVMX_USB_LOG_CALLED();
1363215990Sjmallett    CVMX_USB_LOG_PARAM("%p", usb);
1364215990Sjmallett
1365215990Sjmallett    rx_status.u32 = __cvmx_usb_read_csr32(usb, CVMX_USBCX_GRXSTSPH(usb->index));
1366215990Sjmallett    /* Only read data if IN data is there */
1367215990Sjmallett    if (rx_status.s.pktsts != 2)
1368215990Sjmallett        CVMX_USB_RETURN_NOTHING();
1369215990Sjmallett    /* Check if no data is available */
1370215990Sjmallett    if (!rx_status.s.bcnt)
1371215990Sjmallett        CVMX_USB_RETURN_NOTHING();
1372215990Sjmallett
1373215990Sjmallett    channel = rx_status.s.chnum;
1374215990Sjmallett    bytes = rx_status.s.bcnt;
1375215990Sjmallett    if (!bytes)
1376215990Sjmallett        CVMX_USB_RETURN_NOTHING();
1377215990Sjmallett
1378215990Sjmallett    /* Get where the DMA engine would have written this data */
1379215990Sjmallett    address = __cvmx_usb_read_csr64(usb, CVMX_USBNX_DMA0_INB_CHN0(usb->index) + channel*8);
1380215990Sjmallett    ptr = cvmx_phys_to_ptr(address);
1381215990Sjmallett    __cvmx_usb_write_csr64(usb, CVMX_USBNX_DMA0_INB_CHN0(usb->index) + channel*8, address + bytes);
1382215990Sjmallett
1383215990Sjmallett    /* Loop writing the FIFO data for this packet into memory */
1384215990Sjmallett    while (bytes > 0)
1385215990Sjmallett    {
1386215990Sjmallett        *ptr++ = __cvmx_usb_read_csr32(usb, USB_FIFO_ADDRESS(channel, usb->index));
1387215990Sjmallett        bytes -= 4;
1388215990Sjmallett    }
1389215990Sjmallett    CVMX_SYNCW;
1390215990Sjmallett
1391215990Sjmallett    CVMX_USB_RETURN_NOTHING();
1392215990Sjmallett}
1393215990Sjmallett
1394215990Sjmallett
1395215990Sjmallett/**
1396215990Sjmallett * Fill the TX hardware fifo with data out of the software
1397215990Sjmallett * fifos
1398215990Sjmallett *
1399215990Sjmallett * @param usb       USB device state populated by
1400215990Sjmallett *                  cvmx_usb_initialize().
1401215990Sjmallett * @param fifo      Software fifo to use
1402215990Sjmallett * @param available Amount of space in the hardware fifo
1403215990Sjmallett *
1404215990Sjmallett * @return Non zero if the hardware fifo was too small and needs
1405215990Sjmallett *         to be serviced again.
1406215990Sjmallett */
1407215990Sjmallettstatic int __cvmx_usb_fill_tx_hw(cvmx_usb_internal_state_t *usb, cvmx_usb_tx_fifo_t *fifo, int available)
1408215990Sjmallett{
1409215990Sjmallett    CVMX_USB_LOG_CALLED();
1410215990Sjmallett    CVMX_USB_LOG_PARAM("%p", usb);
1411215990Sjmallett    CVMX_USB_LOG_PARAM("%p", fifo);
1412215990Sjmallett    CVMX_USB_LOG_PARAM("%d", available);
1413215990Sjmallett
1414215990Sjmallett    /* We're done either when there isn't anymore space or the software FIFO
1415215990Sjmallett        is empty */
1416215990Sjmallett    while (available && (fifo->head != fifo->tail))
1417215990Sjmallett    {
1418215990Sjmallett        int i = fifo->tail;
1419215990Sjmallett        const uint32_t *ptr = cvmx_phys_to_ptr(fifo->entry[i].address);
1420215990Sjmallett        uint64_t csr_address = USB_FIFO_ADDRESS(fifo->entry[i].channel, usb->index) ^ 4;
1421215990Sjmallett        int words = available;
1422215990Sjmallett
1423215990Sjmallett        /* Limit the amount of data to waht the SW fifo has */
1424215990Sjmallett        if (fifo->entry[i].size <= available)
1425215990Sjmallett        {
1426215990Sjmallett            words = fifo->entry[i].size;
1427215990Sjmallett            fifo->tail++;
1428215990Sjmallett            if (fifo->tail > MAX_CHANNELS)
1429215990Sjmallett                fifo->tail = 0;
1430215990Sjmallett        }
1431215990Sjmallett
1432215990Sjmallett        /* Update the next locations and counts */
1433215990Sjmallett        available -= words;
1434215990Sjmallett        fifo->entry[i].address += words * 4;
1435215990Sjmallett        fifo->entry[i].size -= words;
1436215990Sjmallett
1437215990Sjmallett        /* Write the HW fifo data. The read every three writes is due
1438215990Sjmallett            to an errata on CN3XXX chips */
1439215990Sjmallett        while (words > 3)
1440215990Sjmallett        {
1441215990Sjmallett            cvmx_write64_uint32(csr_address, *ptr++);
1442215990Sjmallett            cvmx_write64_uint32(csr_address, *ptr++);
1443215990Sjmallett            cvmx_write64_uint32(csr_address, *ptr++);
1444215990Sjmallett            cvmx_read64_uint64(CVMX_USBNX_DMA0_INB_CHN0(usb->index));
1445215990Sjmallett            words -= 3;
1446215990Sjmallett        }
1447215990Sjmallett        cvmx_write64_uint32(csr_address, *ptr++);
1448215990Sjmallett        if (--words)
1449215990Sjmallett        {
1450215990Sjmallett            cvmx_write64_uint32(csr_address, *ptr++);
1451215990Sjmallett            if (--words)
1452215990Sjmallett                cvmx_write64_uint32(csr_address, *ptr++);
1453215990Sjmallett        }
1454215990Sjmallett        cvmx_read64_uint64(CVMX_USBNX_DMA0_INB_CHN0(usb->index));
1455215990Sjmallett    }
1456215990Sjmallett    CVMX_USB_RETURN(fifo->head != fifo->tail);
1457215990Sjmallett}
1458215990Sjmallett
1459215990Sjmallett
1460215990Sjmallett/**
1461215990Sjmallett * Check the hardware FIFOs and fill them as needed
1462215990Sjmallett *
1463215990Sjmallett * @param usb    USB device state populated by
1464215990Sjmallett *               cvmx_usb_initialize().
1465215990Sjmallett */
1466215990Sjmallettstatic void __cvmx_usb_poll_tx_fifo(cvmx_usb_internal_state_t *usb)
1467215990Sjmallett{
1468215990Sjmallett    CVMX_USB_LOG_CALLED();
1469215990Sjmallett    CVMX_USB_LOG_PARAM("%p", usb);
1470215990Sjmallett
1471215990Sjmallett    if (usb->periodic.head != usb->periodic.tail)
1472215990Sjmallett    {
1473215990Sjmallett        cvmx_usbcx_hptxsts_t tx_status;
1474215990Sjmallett        tx_status.u32 = __cvmx_usb_read_csr32(usb, CVMX_USBCX_HPTXSTS(usb->index));
1475215990Sjmallett        if (__cvmx_usb_fill_tx_hw(usb, &usb->periodic, tx_status.s.ptxfspcavail))
1476215990Sjmallett            USB_SET_FIELD32(CVMX_USBCX_GINTMSK(usb->index), cvmx_usbcx_gintmsk_t, ptxfempmsk, 1);
1477215990Sjmallett        else
1478215990Sjmallett            USB_SET_FIELD32(CVMX_USBCX_GINTMSK(usb->index), cvmx_usbcx_gintmsk_t, ptxfempmsk, 0);
1479215990Sjmallett    }
1480215990Sjmallett
1481215990Sjmallett    if (usb->nonperiodic.head != usb->nonperiodic.tail)
1482215990Sjmallett    {
1483215990Sjmallett        cvmx_usbcx_gnptxsts_t tx_status;
1484215990Sjmallett        tx_status.u32 = __cvmx_usb_read_csr32(usb, CVMX_USBCX_GNPTXSTS(usb->index));
1485215990Sjmallett        if (__cvmx_usb_fill_tx_hw(usb, &usb->nonperiodic, tx_status.s.nptxfspcavail))
1486215990Sjmallett            USB_SET_FIELD32(CVMX_USBCX_GINTMSK(usb->index), cvmx_usbcx_gintmsk_t, nptxfempmsk, 1);
1487215990Sjmallett        else
1488215990Sjmallett            USB_SET_FIELD32(CVMX_USBCX_GINTMSK(usb->index), cvmx_usbcx_gintmsk_t, nptxfempmsk, 0);
1489215990Sjmallett    }
1490215990Sjmallett
1491215990Sjmallett    CVMX_USB_RETURN_NOTHING();
1492215990Sjmallett}
1493215990Sjmallett
1494215990Sjmallett
1495215990Sjmallett/**
1496215990Sjmallett * @INTERNAL
1497215990Sjmallett * Fill the TX FIFO with an outgoing packet
1498215990Sjmallett *
1499215990Sjmallett * @param usb     USB device state populated by
1500215990Sjmallett *                cvmx_usb_initialize().
1501215990Sjmallett * @param channel Channel number to get packet from
1502215990Sjmallett */
1503215990Sjmallettstatic void __cvmx_usb_fill_tx_fifo(cvmx_usb_internal_state_t *usb, int channel)
1504215990Sjmallett{
1505215990Sjmallett    cvmx_usbcx_hccharx_t hcchar;
1506215990Sjmallett    cvmx_usbcx_hcspltx_t usbc_hcsplt;
1507215990Sjmallett    cvmx_usbcx_hctsizx_t usbc_hctsiz;
1508215990Sjmallett    cvmx_usb_tx_fifo_t *fifo;
1509215990Sjmallett
1510215990Sjmallett    CVMX_USB_LOG_CALLED();
1511215990Sjmallett    CVMX_USB_LOG_PARAM("%p", usb);
1512215990Sjmallett    CVMX_USB_LOG_PARAM("%d", channel);
1513215990Sjmallett
1514215990Sjmallett    /* We only need to fill data on outbound channels */
1515215990Sjmallett    hcchar.u32 = __cvmx_usb_read_csr32(usb, CVMX_USBCX_HCCHARX(channel, usb->index));
1516215990Sjmallett    if (hcchar.s.epdir != CVMX_USB_DIRECTION_OUT)
1517215990Sjmallett        CVMX_USB_RETURN_NOTHING();
1518215990Sjmallett
1519215990Sjmallett    /* OUT Splits only have data on the start and not the complete */
1520215990Sjmallett    usbc_hcsplt.u32 = __cvmx_usb_read_csr32(usb, CVMX_USBCX_HCSPLTX(channel, usb->index));
1521215990Sjmallett    if (usbc_hcsplt.s.spltena && usbc_hcsplt.s.compsplt)
1522215990Sjmallett        CVMX_USB_RETURN_NOTHING();
1523215990Sjmallett
1524215990Sjmallett    /* Find out how many bytes we need to fill and convert it into 32bit words */
1525215990Sjmallett    usbc_hctsiz.u32 = __cvmx_usb_read_csr32(usb, CVMX_USBCX_HCTSIZX(channel, usb->index));
1526215990Sjmallett    if (!usbc_hctsiz.s.xfersize)
1527215990Sjmallett        CVMX_USB_RETURN_NOTHING();
1528215990Sjmallett
1529215990Sjmallett    if ((hcchar.s.eptype == CVMX_USB_TRANSFER_INTERRUPT) ||
1530215990Sjmallett        (hcchar.s.eptype == CVMX_USB_TRANSFER_ISOCHRONOUS))
1531215990Sjmallett        fifo = &usb->periodic;
1532215990Sjmallett    else
1533215990Sjmallett        fifo = &usb->nonperiodic;
1534215990Sjmallett
1535215990Sjmallett    fifo->entry[fifo->head].channel = channel;
1536215990Sjmallett    fifo->entry[fifo->head].address = __cvmx_usb_read_csr64(usb, CVMX_USBNX_DMA0_OUTB_CHN0(usb->index) + channel*8);
1537215990Sjmallett    fifo->entry[fifo->head].size = (usbc_hctsiz.s.xfersize+3)>>2;
1538215990Sjmallett    fifo->head++;
1539215990Sjmallett    if (fifo->head > MAX_CHANNELS)
1540215990Sjmallett        fifo->head = 0;
1541215990Sjmallett
1542215990Sjmallett    __cvmx_usb_poll_tx_fifo(usb);
1543215990Sjmallett
1544215990Sjmallett    CVMX_USB_RETURN_NOTHING();
1545215990Sjmallett}
1546215990Sjmallett
1547215990Sjmallett/**
1548215990Sjmallett * @INTERNAL
1549210284Sjmallett * Perform channel specific setup for Control transactions. All
1550210284Sjmallett * the generic stuff will already have been done in
1551210284Sjmallett * __cvmx_usb_start_channel()
1552210284Sjmallett *
1553210284Sjmallett * @param usb     USB device state populated by
1554210284Sjmallett *                cvmx_usb_initialize().
1555210284Sjmallett * @param channel Channel to setup
1556210284Sjmallett * @param pipe    Pipe for control transaction
1557210284Sjmallett */
1558210284Sjmallettstatic void __cvmx_usb_start_channel_control(cvmx_usb_internal_state_t *usb,
1559210284Sjmallett                                             int channel,
1560210284Sjmallett                                             cvmx_usb_pipe_t *pipe)
1561210284Sjmallett{
1562210284Sjmallett    cvmx_usb_transaction_t *transaction = pipe->head;
1563210284Sjmallett    cvmx_usb_control_header_t *header = cvmx_phys_to_ptr(transaction->control_header);
1564210284Sjmallett    int bytes_to_transfer = transaction->buffer_length - transaction->actual_bytes;
1565215990Sjmallett    int packets_to_transfer;
1566210284Sjmallett    cvmx_usbcx_hctsizx_t usbc_hctsiz;
1567210284Sjmallett
1568210284Sjmallett    CVMX_USB_LOG_CALLED();
1569210284Sjmallett    CVMX_USB_LOG_PARAM("%p", usb);
1570210284Sjmallett    CVMX_USB_LOG_PARAM("%d", channel);
1571210284Sjmallett    CVMX_USB_LOG_PARAM("%p", pipe);
1572210284Sjmallett
1573210284Sjmallett    usbc_hctsiz.u32 = __cvmx_usb_read_csr32(usb, CVMX_USBCX_HCTSIZX(channel, usb->index));
1574210284Sjmallett
1575210284Sjmallett    switch (transaction->stage)
1576210284Sjmallett    {
1577210284Sjmallett        case CVMX_USB_STAGE_NON_CONTROL:
1578210284Sjmallett        case CVMX_USB_STAGE_NON_CONTROL_SPLIT_COMPLETE:
1579210284Sjmallett            cvmx_dprintf("%s: ERROR - Non control stage\n", __FUNCTION__);
1580210284Sjmallett            break;
1581210284Sjmallett        case CVMX_USB_STAGE_SETUP:
1582210284Sjmallett            usbc_hctsiz.s.pid = 3; /* Setup */
1583215990Sjmallett            bytes_to_transfer = sizeof(*header);
1584210284Sjmallett            /* All Control operations start with a setup going OUT */
1585210284Sjmallett            USB_SET_FIELD32(CVMX_USBCX_HCCHARX(channel, usb->index), cvmx_usbcx_hccharx_t, epdir, CVMX_USB_DIRECTION_OUT);
1586210284Sjmallett            /* Setup send the control header instead of the buffer data. The
1587210284Sjmallett                buffer data will be used in the next stage */
1588210284Sjmallett            __cvmx_usb_write_csr64(usb, CVMX_USBNX_DMA0_OUTB_CHN0(usb->index) + channel*8, transaction->control_header);
1589210284Sjmallett            break;
1590210284Sjmallett        case CVMX_USB_STAGE_SETUP_SPLIT_COMPLETE:
1591210284Sjmallett            usbc_hctsiz.s.pid = 3; /* Setup */
1592215990Sjmallett            bytes_to_transfer = 0;
1593210284Sjmallett            /* All Control operations start with a setup going OUT */
1594210284Sjmallett            USB_SET_FIELD32(CVMX_USBCX_HCCHARX(channel, usb->index), cvmx_usbcx_hccharx_t, epdir, CVMX_USB_DIRECTION_OUT);
1595210284Sjmallett            USB_SET_FIELD32(CVMX_USBCX_HCSPLTX(channel, usb->index), cvmx_usbcx_hcspltx_t, compsplt, 1);
1596210284Sjmallett            break;
1597210284Sjmallett        case CVMX_USB_STAGE_DATA:
1598210284Sjmallett            usbc_hctsiz.s.pid = __cvmx_usb_get_data_pid(pipe);
1599210284Sjmallett            if (__cvmx_usb_pipe_needs_split(usb, pipe))
1600210284Sjmallett            {
1601215990Sjmallett                if (header->s.request_type & 0x80)
1602215990Sjmallett                    bytes_to_transfer = 0;
1603215990Sjmallett                else if (bytes_to_transfer > pipe->max_packet)
1604215990Sjmallett                    bytes_to_transfer = pipe->max_packet;
1605210284Sjmallett            }
1606210284Sjmallett            USB_SET_FIELD32(CVMX_USBCX_HCCHARX(channel, usb->index),
1607210284Sjmallett                            cvmx_usbcx_hccharx_t, epdir,
1608210284Sjmallett                            ((header->s.request_type & 0x80) ?
1609210284Sjmallett                             CVMX_USB_DIRECTION_IN :
1610210284Sjmallett                             CVMX_USB_DIRECTION_OUT));
1611210284Sjmallett            break;
1612210284Sjmallett        case CVMX_USB_STAGE_DATA_SPLIT_COMPLETE:
1613210284Sjmallett            usbc_hctsiz.s.pid = __cvmx_usb_get_data_pid(pipe);
1614215990Sjmallett            if (!(header->s.request_type & 0x80))
1615215990Sjmallett                bytes_to_transfer = 0;
1616210284Sjmallett            USB_SET_FIELD32(CVMX_USBCX_HCCHARX(channel, usb->index),
1617210284Sjmallett                            cvmx_usbcx_hccharx_t, epdir,
1618210284Sjmallett                            ((header->s.request_type & 0x80) ?
1619210284Sjmallett                             CVMX_USB_DIRECTION_IN :
1620210284Sjmallett                             CVMX_USB_DIRECTION_OUT));
1621210284Sjmallett            USB_SET_FIELD32(CVMX_USBCX_HCSPLTX(channel, usb->index), cvmx_usbcx_hcspltx_t, compsplt, 1);
1622210284Sjmallett            break;
1623210284Sjmallett        case CVMX_USB_STAGE_STATUS:
1624210284Sjmallett            usbc_hctsiz.s.pid = __cvmx_usb_get_data_pid(pipe);
1625215990Sjmallett            bytes_to_transfer = 0;
1626210284Sjmallett            USB_SET_FIELD32(CVMX_USBCX_HCCHARX(channel, usb->index), cvmx_usbcx_hccharx_t, epdir,
1627210284Sjmallett                            ((header->s.request_type & 0x80) ?
1628210284Sjmallett                             CVMX_USB_DIRECTION_OUT :
1629210284Sjmallett                             CVMX_USB_DIRECTION_IN));
1630210284Sjmallett            break;
1631210284Sjmallett        case CVMX_USB_STAGE_STATUS_SPLIT_COMPLETE:
1632210284Sjmallett            usbc_hctsiz.s.pid = __cvmx_usb_get_data_pid(pipe);
1633215990Sjmallett            bytes_to_transfer = 0;
1634210284Sjmallett            USB_SET_FIELD32(CVMX_USBCX_HCCHARX(channel, usb->index), cvmx_usbcx_hccharx_t, epdir,
1635210284Sjmallett                            ((header->s.request_type & 0x80) ?
1636210284Sjmallett                             CVMX_USB_DIRECTION_OUT :
1637210284Sjmallett                             CVMX_USB_DIRECTION_IN));
1638210284Sjmallett            USB_SET_FIELD32(CVMX_USBCX_HCSPLTX(channel, usb->index), cvmx_usbcx_hcspltx_t, compsplt, 1);
1639210284Sjmallett            break;
1640210284Sjmallett    }
1641210284Sjmallett
1642215990Sjmallett    /* Make sure the transfer never exceeds the byte limit of the hardware.
1643215990Sjmallett        Further bytes will be sent as continued transactions */
1644215990Sjmallett    if (bytes_to_transfer > MAX_TRANSFER_BYTES)
1645215990Sjmallett    {
1646215990Sjmallett        /* Round MAX_TRANSFER_BYTES to a multiple of out packet size */
1647215990Sjmallett        bytes_to_transfer = MAX_TRANSFER_BYTES / pipe->max_packet;
1648215990Sjmallett        bytes_to_transfer *= pipe->max_packet;
1649215990Sjmallett    }
1650210284Sjmallett
1651215990Sjmallett    /* Calculate the number of packets to transfer. If the length is zero
1652215990Sjmallett        we still need to transfer one packet */
1653215990Sjmallett    packets_to_transfer = (bytes_to_transfer + pipe->max_packet - 1) / pipe->max_packet;
1654215990Sjmallett    if (packets_to_transfer == 0)
1655215990Sjmallett        packets_to_transfer = 1;
1656215990Sjmallett    else if ((packets_to_transfer>1) && (usb->init_flags & CVMX_USB_INITIALIZE_FLAGS_NO_DMA))
1657215990Sjmallett    {
1658215990Sjmallett        /* Limit to one packet when not using DMA. Channels must be restarted
1659215990Sjmallett            between every packet for IN transactions, so there is no reason to
1660215990Sjmallett            do multiple packets in a row */
1661215990Sjmallett        packets_to_transfer = 1;
1662215990Sjmallett        bytes_to_transfer = packets_to_transfer * pipe->max_packet;
1663215990Sjmallett    }
1664215990Sjmallett    else if (packets_to_transfer > MAX_TRANSFER_PACKETS)
1665215990Sjmallett    {
1666215990Sjmallett        /* Limit the number of packet and data transferred to what the
1667215990Sjmallett            hardware can handle */
1668215990Sjmallett        packets_to_transfer = MAX_TRANSFER_PACKETS;
1669215990Sjmallett        bytes_to_transfer = packets_to_transfer * pipe->max_packet;
1670215990Sjmallett    }
1671215990Sjmallett
1672215990Sjmallett    usbc_hctsiz.s.xfersize = bytes_to_transfer;
1673215990Sjmallett    usbc_hctsiz.s.pktcnt = packets_to_transfer;
1674215990Sjmallett
1675210284Sjmallett    __cvmx_usb_write_csr32(usb, CVMX_USBCX_HCTSIZX(channel, usb->index), usbc_hctsiz.u32);
1676210284Sjmallett    CVMX_USB_RETURN_NOTHING();
1677210284Sjmallett}
1678210284Sjmallett
1679210284Sjmallett
1680210284Sjmallett/**
1681210284Sjmallett * @INTERNAL
1682210284Sjmallett * Start a channel to perform the pipe's head transaction
1683210284Sjmallett *
1684210284Sjmallett * @param usb     USB device state populated by
1685210284Sjmallett *                cvmx_usb_initialize().
1686210284Sjmallett * @param channel Channel to setup
1687210284Sjmallett * @param pipe    Pipe to start
1688210284Sjmallett */
1689210284Sjmallettstatic void __cvmx_usb_start_channel(cvmx_usb_internal_state_t *usb,
1690210284Sjmallett                                     int channel,
1691210284Sjmallett                                     cvmx_usb_pipe_t *pipe)
1692210284Sjmallett{
1693210284Sjmallett    cvmx_usb_transaction_t *transaction = pipe->head;
1694210284Sjmallett
1695210284Sjmallett    CVMX_USB_LOG_CALLED();
1696210284Sjmallett    CVMX_USB_LOG_PARAM("%p", usb);
1697210284Sjmallett    CVMX_USB_LOG_PARAM("%d", channel);
1698210284Sjmallett    CVMX_USB_LOG_PARAM("%p", pipe);
1699210284Sjmallett
1700210284Sjmallett    if (cvmx_unlikely((usb->init_flags & CVMX_USB_INITIALIZE_FLAGS_DEBUG_TRANSFERS) ||
1701210284Sjmallett        (pipe->flags & CVMX_USB_PIPE_FLAGS_DEBUG_TRANSFERS)))
1702210284Sjmallett        cvmx_dprintf("%s: Channel %d started. Pipe %d transaction %d stage %d\n",
1703210284Sjmallett                     __FUNCTION__, channel, __cvmx_usb_get_pipe_handle(usb, pipe),
1704210284Sjmallett                     __cvmx_usb_get_submit_handle(usb, transaction),
1705210284Sjmallett                     transaction->stage);
1706210284Sjmallett
1707210284Sjmallett    /* Make sure all writes to the DMA region get flushed */
1708210284Sjmallett    CVMX_SYNCW;
1709210284Sjmallett
1710210284Sjmallett    /* Attach the channel to the pipe */
1711210284Sjmallett    usb->pipe_for_channel[channel] = pipe;
1712210284Sjmallett    pipe->channel = channel;
1713210284Sjmallett    pipe->flags |= __CVMX_USB_PIPE_FLAGS_SCHEDULED;
1714210284Sjmallett
1715210284Sjmallett    /* Mark this channel as in use */
1716210284Sjmallett    usb->idle_hardware_channels &= ~(1<<channel);
1717210284Sjmallett
1718210284Sjmallett    /* Enable the channel interrupt bits */
1719210284Sjmallett    {
1720210284Sjmallett        cvmx_usbcx_hcintx_t usbc_hcint;
1721215990Sjmallett        cvmx_usbcx_hcintmskx_t usbc_hcintmsk;
1722215990Sjmallett        cvmx_usbcx_haintmsk_t usbc_haintmsk;
1723210284Sjmallett
1724210284Sjmallett        /* Clear all channel status bits */
1725210284Sjmallett        usbc_hcint.u32 = __cvmx_usb_read_csr32(usb, CVMX_USBCX_HCINTX(channel, usb->index));
1726210284Sjmallett        __cvmx_usb_write_csr32(usb, CVMX_USBCX_HCINTX(channel, usb->index), usbc_hcint.u32);
1727215990Sjmallett
1728215990Sjmallett        usbc_hcintmsk.u32 = 0;
1729215990Sjmallett        usbc_hcintmsk.s.chhltdmsk = 1;
1730215990Sjmallett        if (usb->init_flags & CVMX_USB_INITIALIZE_FLAGS_NO_DMA)
1731215990Sjmallett        {
1732215990Sjmallett            /* Channels need these extra interrupts when we aren't in DMA mode */
1733215990Sjmallett            usbc_hcintmsk.s.datatglerrmsk = 1;
1734215990Sjmallett            usbc_hcintmsk.s.frmovrunmsk = 1;
1735215990Sjmallett            usbc_hcintmsk.s.bblerrmsk = 1;
1736215990Sjmallett            usbc_hcintmsk.s.xacterrmsk = 1;
1737215990Sjmallett            if (__cvmx_usb_pipe_needs_split(usb, pipe))
1738215990Sjmallett            {
1739215990Sjmallett                /* Splits don't generate xfercompl, so we need ACK and NYET */
1740215990Sjmallett                usbc_hcintmsk.s.nyetmsk = 1;
1741215990Sjmallett                usbc_hcintmsk.s.ackmsk = 1;
1742215990Sjmallett            }
1743215990Sjmallett            usbc_hcintmsk.s.nakmsk = 1;
1744215990Sjmallett            usbc_hcintmsk.s.stallmsk = 1;
1745215990Sjmallett            usbc_hcintmsk.s.xfercomplmsk = 1;
1746215990Sjmallett        }
1747215990Sjmallett        __cvmx_usb_write_csr32(usb, CVMX_USBCX_HCINTMSKX(channel, usb->index), usbc_hcintmsk.u32);
1748215990Sjmallett
1749215990Sjmallett        /* Enable the channel interrupt to propagate */
1750215990Sjmallett        usbc_haintmsk.u32 = __cvmx_usb_read_csr32(usb, CVMX_USBCX_HAINTMSK(usb->index));
1751215990Sjmallett        usbc_haintmsk.s.haintmsk |= 1<<channel;
1752215990Sjmallett        __cvmx_usb_write_csr32(usb, CVMX_USBCX_HAINTMSK(usb->index), usbc_haintmsk.u32);
1753210284Sjmallett    }
1754210284Sjmallett
1755210284Sjmallett    /* Setup the locations the DMA engines use  */
1756210284Sjmallett    {
1757210284Sjmallett        uint64_t dma_address = transaction->buffer + transaction->actual_bytes;
1758210284Sjmallett        if (transaction->type == CVMX_USB_TRANSFER_ISOCHRONOUS)
1759210284Sjmallett            dma_address = transaction->buffer + transaction->iso_packets[0].offset + transaction->actual_bytes;
1760210284Sjmallett        __cvmx_usb_write_csr64(usb, CVMX_USBNX_DMA0_OUTB_CHN0(usb->index) + channel*8, dma_address);
1761210284Sjmallett        __cvmx_usb_write_csr64(usb, CVMX_USBNX_DMA0_INB_CHN0(usb->index) + channel*8, dma_address);
1762210284Sjmallett    }
1763210284Sjmallett
1764210284Sjmallett    /* Setup both the size of the transfer and the SPLIT characteristics */
1765210284Sjmallett    {
1766210284Sjmallett        cvmx_usbcx_hcspltx_t usbc_hcsplt = {.u32 = 0};
1767210284Sjmallett        cvmx_usbcx_hctsizx_t usbc_hctsiz = {.u32 = 0};
1768215990Sjmallett        int packets_to_transfer;
1769210284Sjmallett        int bytes_to_transfer = transaction->buffer_length - transaction->actual_bytes;
1770210284Sjmallett
1771210284Sjmallett        /* ISOCHRONOUS transactions store each individual transfer size in the
1772210284Sjmallett            packet structure, not the global buffer_length */
1773210284Sjmallett        if (transaction->type == CVMX_USB_TRANSFER_ISOCHRONOUS)
1774210284Sjmallett            bytes_to_transfer = transaction->iso_packets[0].length - transaction->actual_bytes;
1775210284Sjmallett
1776210284Sjmallett        /* We need to do split transactions when we are talking to non high
1777210284Sjmallett            speed devices that are behind a high speed hub */
1778210284Sjmallett        if (__cvmx_usb_pipe_needs_split(usb, pipe))
1779210284Sjmallett        {
1780210284Sjmallett            /* On the start split phase (stage is even) record the frame number we
1781210284Sjmallett                will need to send the split complete. We only store the lower two bits
1782210284Sjmallett                since the time ahead can only be two frames */
1783210284Sjmallett            if ((transaction->stage&1) == 0)
1784210284Sjmallett            {
1785210284Sjmallett                if (transaction->type == CVMX_USB_TRANSFER_BULK)
1786215990Sjmallett                    pipe->split_sc_frame = (usb->frame_number + 1) & 0x7f;
1787210284Sjmallett                else
1788215990Sjmallett                    pipe->split_sc_frame = (usb->frame_number + 2) & 0x7f;
1789210284Sjmallett            }
1790210284Sjmallett            else
1791210284Sjmallett                pipe->split_sc_frame = -1;
1792210284Sjmallett
1793210284Sjmallett            usbc_hcsplt.s.spltena = 1;
1794210284Sjmallett            usbc_hcsplt.s.hubaddr = pipe->hub_device_addr;
1795210284Sjmallett            usbc_hcsplt.s.prtaddr = pipe->hub_port;
1796210284Sjmallett            usbc_hcsplt.s.compsplt = (transaction->stage == CVMX_USB_STAGE_NON_CONTROL_SPLIT_COMPLETE);
1797210284Sjmallett
1798210284Sjmallett            /* SPLIT transactions can only ever transmit one data packet so
1799210284Sjmallett                limit the transfer size to the max packet size */
1800210284Sjmallett            if (bytes_to_transfer > pipe->max_packet)
1801210284Sjmallett                bytes_to_transfer = pipe->max_packet;
1802210284Sjmallett
1803210284Sjmallett            /* ISOCHRONOUS OUT splits are unique in that they limit
1804210284Sjmallett                data transfers to 188 byte chunks representing the
1805210284Sjmallett                begin/middle/end of the data or all */
1806210284Sjmallett            if (!usbc_hcsplt.s.compsplt &&
1807210284Sjmallett                (pipe->transfer_dir == CVMX_USB_DIRECTION_OUT) &&
1808210284Sjmallett                (pipe->transfer_type == CVMX_USB_TRANSFER_ISOCHRONOUS))
1809210284Sjmallett            {
1810215990Sjmallett                /* Clear the split complete frame number as there isn't going
1811215990Sjmallett                    to be a split complete */
1812215990Sjmallett                pipe->split_sc_frame = -1;
1813215990Sjmallett                /* See if we've started this transfer and sent data */
1814210284Sjmallett                if (transaction->actual_bytes == 0)
1815210284Sjmallett                {
1816210284Sjmallett                    /* Nothing sent yet, this is either a begin or the
1817210284Sjmallett                        entire payload */
1818210284Sjmallett                    if (bytes_to_transfer <= 188)
1819210284Sjmallett                        usbc_hcsplt.s.xactpos = 3; /* Entire payload in one go */
1820210284Sjmallett                    else
1821210284Sjmallett                        usbc_hcsplt.s.xactpos = 2; /* First part of payload */
1822210284Sjmallett                }
1823210284Sjmallett                else
1824210284Sjmallett                {
1825210284Sjmallett                    /* Continuing the previous data, we must either be
1826210284Sjmallett                        in the middle or at the end */
1827210284Sjmallett                    if (bytes_to_transfer <= 188)
1828210284Sjmallett                        usbc_hcsplt.s.xactpos = 1; /* End of payload */
1829210284Sjmallett                    else
1830210284Sjmallett                        usbc_hcsplt.s.xactpos = 0; /* Middle of payload */
1831210284Sjmallett                }
1832210284Sjmallett                /* Again, the transfer size is limited to 188 bytes */
1833210284Sjmallett                if (bytes_to_transfer > 188)
1834210284Sjmallett                    bytes_to_transfer = 188;
1835210284Sjmallett            }
1836210284Sjmallett        }
1837210284Sjmallett
1838215990Sjmallett        /* Make sure the transfer never exceeds the byte limit of the hardware.
1839215990Sjmallett            Further bytes will be sent as continued transactions */
1840215990Sjmallett        if (bytes_to_transfer > MAX_TRANSFER_BYTES)
1841215990Sjmallett        {
1842215990Sjmallett            /* Round MAX_TRANSFER_BYTES to a multiple of out packet size */
1843215990Sjmallett            bytes_to_transfer = MAX_TRANSFER_BYTES / pipe->max_packet;
1844215990Sjmallett            bytes_to_transfer *= pipe->max_packet;
1845215990Sjmallett        }
1846215990Sjmallett
1847215990Sjmallett        /* Calculate the number of packets to transfer. If the length is zero
1848215990Sjmallett            we still need to transfer one packet */
1849215990Sjmallett        packets_to_transfer = (bytes_to_transfer + pipe->max_packet - 1) / pipe->max_packet;
1850215990Sjmallett        if (packets_to_transfer == 0)
1851215990Sjmallett            packets_to_transfer = 1;
1852215990Sjmallett        else if ((packets_to_transfer>1) && (usb->init_flags & CVMX_USB_INITIALIZE_FLAGS_NO_DMA))
1853215990Sjmallett        {
1854215990Sjmallett            /* Limit to one packet when not using DMA. Channels must be restarted
1855215990Sjmallett                between every packet for IN transactions, so there is no reason to
1856215990Sjmallett                do multiple packets in a row */
1857215990Sjmallett            packets_to_transfer = 1;
1858215990Sjmallett            bytes_to_transfer = packets_to_transfer * pipe->max_packet;
1859215990Sjmallett        }
1860215990Sjmallett        else if (packets_to_transfer > MAX_TRANSFER_PACKETS)
1861215990Sjmallett        {
1862215990Sjmallett            /* Limit the number of packet and data transferred to what the
1863215990Sjmallett                hardware can handle */
1864215990Sjmallett            packets_to_transfer = MAX_TRANSFER_PACKETS;
1865215990Sjmallett            bytes_to_transfer = packets_to_transfer * pipe->max_packet;
1866215990Sjmallett        }
1867215990Sjmallett
1868210284Sjmallett        usbc_hctsiz.s.xfersize = bytes_to_transfer;
1869215990Sjmallett        usbc_hctsiz.s.pktcnt = packets_to_transfer;
1870210284Sjmallett
1871210284Sjmallett        /* Update the DATA0/DATA1 toggle */
1872210284Sjmallett        usbc_hctsiz.s.pid = __cvmx_usb_get_data_pid(pipe);
1873210284Sjmallett        /* High speed pipes may need a hardware ping before they start */
1874210284Sjmallett        if (pipe->flags & __CVMX_USB_PIPE_FLAGS_NEED_PING)
1875210284Sjmallett            usbc_hctsiz.s.dopng = 1;
1876210284Sjmallett
1877210284Sjmallett        __cvmx_usb_write_csr32(usb, CVMX_USBCX_HCSPLTX(channel, usb->index), usbc_hcsplt.u32);
1878210284Sjmallett        __cvmx_usb_write_csr32(usb, CVMX_USBCX_HCTSIZX(channel, usb->index), usbc_hctsiz.u32);
1879210284Sjmallett    }
1880210284Sjmallett
1881210284Sjmallett    /* Setup the Host Channel Characteristics Register */
1882210284Sjmallett    {
1883210284Sjmallett        cvmx_usbcx_hccharx_t usbc_hcchar = {.u32 = 0};
1884210284Sjmallett
1885215990Sjmallett        /* Set the startframe odd/even properly. This is only used for periodic */
1886215990Sjmallett        usbc_hcchar.s.oddfrm = usb->frame_number&1;
1887210284Sjmallett
1888210284Sjmallett        /* Set the number of back to back packets allowed by this endpoint.
1889210284Sjmallett            Split transactions interpret "ec" as the number of immediate
1890210284Sjmallett            retries of failure. These retries happen too quickly, so we
1891210284Sjmallett            disable these entirely for splits */
1892210284Sjmallett        if (__cvmx_usb_pipe_needs_split(usb, pipe))
1893210284Sjmallett            usbc_hcchar.s.ec = 1;
1894210284Sjmallett        else if (pipe->multi_count < 1)
1895210284Sjmallett            usbc_hcchar.s.ec = 1;
1896210284Sjmallett        else if (pipe->multi_count > 3)
1897210284Sjmallett            usbc_hcchar.s.ec = 3;
1898210284Sjmallett        else
1899210284Sjmallett            usbc_hcchar.s.ec = pipe->multi_count;
1900210284Sjmallett
1901210284Sjmallett        /* Set the rest of the endpoint specific settings */
1902210284Sjmallett        usbc_hcchar.s.devaddr = pipe->device_addr;
1903210284Sjmallett        usbc_hcchar.s.eptype = transaction->type;
1904210284Sjmallett        usbc_hcchar.s.lspddev = (pipe->device_speed == CVMX_USB_SPEED_LOW);
1905210284Sjmallett        usbc_hcchar.s.epdir = pipe->transfer_dir;
1906210284Sjmallett        usbc_hcchar.s.epnum = pipe->endpoint_num;
1907210284Sjmallett        usbc_hcchar.s.mps = pipe->max_packet;
1908210284Sjmallett        __cvmx_usb_write_csr32(usb, CVMX_USBCX_HCCHARX(channel, usb->index), usbc_hcchar.u32);
1909210284Sjmallett    }
1910210284Sjmallett
1911210284Sjmallett    /* Do transaction type specific fixups as needed */
1912210284Sjmallett    switch (transaction->type)
1913210284Sjmallett    {
1914210284Sjmallett        case CVMX_USB_TRANSFER_CONTROL:
1915210284Sjmallett            __cvmx_usb_start_channel_control(usb, channel, pipe);
1916210284Sjmallett            break;
1917210284Sjmallett        case CVMX_USB_TRANSFER_BULK:
1918210284Sjmallett        case CVMX_USB_TRANSFER_INTERRUPT:
1919210284Sjmallett            break;
1920210284Sjmallett        case CVMX_USB_TRANSFER_ISOCHRONOUS:
1921210284Sjmallett            if (!__cvmx_usb_pipe_needs_split(usb, pipe))
1922210284Sjmallett            {
1923215990Sjmallett                /* ISO transactions require different PIDs depending on direction
1924210284Sjmallett                    and how many packets are needed */
1925210284Sjmallett                if (pipe->transfer_dir == CVMX_USB_DIRECTION_OUT)
1926210284Sjmallett                {
1927210284Sjmallett                    if (pipe->multi_count < 2) /* Need DATA0 */
1928210284Sjmallett                        USB_SET_FIELD32(CVMX_USBCX_HCTSIZX(channel, usb->index), cvmx_usbcx_hctsizx_t, pid, 0);
1929210284Sjmallett                    else /* Need MDATA */
1930210284Sjmallett                        USB_SET_FIELD32(CVMX_USBCX_HCTSIZX(channel, usb->index), cvmx_usbcx_hctsizx_t, pid, 3);
1931210284Sjmallett                }
1932210284Sjmallett            }
1933210284Sjmallett            break;
1934210284Sjmallett    }
1935210284Sjmallett    {
1936210284Sjmallett        cvmx_usbcx_hctsizx_t usbc_hctsiz = {.u32 = __cvmx_usb_read_csr32(usb, CVMX_USBCX_HCTSIZX(channel, usb->index))};
1937210284Sjmallett        transaction->xfersize = usbc_hctsiz.s.xfersize;
1938210284Sjmallett        transaction->pktcnt = usbc_hctsiz.s.pktcnt;
1939210284Sjmallett    }
1940215990Sjmallett    /* Remeber when we start a split transaction */
1941215990Sjmallett    if (__cvmx_usb_pipe_needs_split(usb, pipe))
1942215990Sjmallett        usb->active_split = transaction;
1943210284Sjmallett    USB_SET_FIELD32(CVMX_USBCX_HCCHARX(channel, usb->index), cvmx_usbcx_hccharx_t, chena, 1);
1944215990Sjmallett    if (usb->init_flags & CVMX_USB_INITIALIZE_FLAGS_NO_DMA)
1945215990Sjmallett        __cvmx_usb_fill_tx_fifo(usb, channel);
1946210284Sjmallett    CVMX_USB_RETURN_NOTHING();
1947210284Sjmallett}
1948210284Sjmallett
1949210284Sjmallett
1950210284Sjmallett/**
1951210284Sjmallett * @INTERNAL
1952210284Sjmallett * Find a pipe that is ready to be scheduled to hardware.
1953215990Sjmallett * @param usb    USB device state populated by
1954215990Sjmallett *               cvmx_usb_initialize().
1955215990Sjmallett * @param list   Pipe list to search
1956215990Sjmallett * @param current_frame
1957215990Sjmallett *               Frame counter to use as a time reference.
1958210284Sjmallett *
1959210284Sjmallett * @return Pipe or NULL if none are ready
1960210284Sjmallett */
1961215990Sjmallettstatic cvmx_usb_pipe_t *__cvmx_usb_find_ready_pipe(cvmx_usb_internal_state_t *usb, cvmx_usb_pipe_list_t *list, uint64_t current_frame)
1962210284Sjmallett{
1963210284Sjmallett    cvmx_usb_pipe_t *pipe = list->head;
1964210284Sjmallett    while (pipe)
1965210284Sjmallett    {
1966210284Sjmallett        if (!(pipe->flags & __CVMX_USB_PIPE_FLAGS_SCHEDULED) && pipe->head &&
1967215990Sjmallett            (pipe->next_tx_frame <= current_frame) &&
1968215990Sjmallett            ((pipe->split_sc_frame == -1) || ((((int)current_frame - (int)pipe->split_sc_frame) & 0x7f) < 0x40)) &&
1969215990Sjmallett            (!usb->active_split || (usb->active_split == pipe->head)))
1970210284Sjmallett        {
1971210284Sjmallett            CVMX_PREFETCH(pipe, 128);
1972210284Sjmallett            CVMX_PREFETCH(pipe->head, 0);
1973210284Sjmallett            return pipe;
1974210284Sjmallett        }
1975210284Sjmallett        pipe = pipe->next;
1976210284Sjmallett    }
1977210284Sjmallett    return NULL;
1978210284Sjmallett}
1979210284Sjmallett
1980210284Sjmallett
1981210284Sjmallett/**
1982210284Sjmallett * @INTERNAL
1983210284Sjmallett * Called whenever a pipe might need to be scheduled to the
1984210284Sjmallett * hardware.
1985210284Sjmallett *
1986210284Sjmallett * @param usb    USB device state populated by
1987210284Sjmallett *               cvmx_usb_initialize().
1988210284Sjmallett * @param is_sof True if this schedule was called on a SOF interrupt.
1989210284Sjmallett */
1990210284Sjmallettstatic void __cvmx_usb_schedule(cvmx_usb_internal_state_t *usb, int is_sof)
1991210284Sjmallett{
1992210284Sjmallett    int channel;
1993210284Sjmallett    cvmx_usb_pipe_t *pipe;
1994215990Sjmallett    int need_sof;
1995215990Sjmallett    cvmx_usb_transfer_t ttype;
1996210284Sjmallett
1997210284Sjmallett    CVMX_USB_LOG_CALLED();
1998210284Sjmallett    CVMX_USB_LOG_PARAM("%p", usb);
1999210284Sjmallett
2000215990Sjmallett    if (usb->init_flags & CVMX_USB_INITIALIZE_FLAGS_NO_DMA)
2001215990Sjmallett    {
2002215990Sjmallett        /* Without DMA we need to be careful to not schedule something at the end of a frame and cause an overrun */
2003215990Sjmallett        cvmx_usbcx_hfnum_t hfnum = {.u32 = __cvmx_usb_read_csr32(usb, CVMX_USBCX_HFNUM(usb->index))};
2004215990Sjmallett        cvmx_usbcx_hfir_t hfir = {.u32 = __cvmx_usb_read_csr32(usb, CVMX_USBCX_HFIR(usb->index))};
2005215990Sjmallett        if (hfnum.s.frrem < hfir.s.frint/4)
2006215990Sjmallett            goto done;
2007215990Sjmallett    }
2008210284Sjmallett
2009210284Sjmallett    while (usb->idle_hardware_channels)
2010210284Sjmallett    {
2011210284Sjmallett        /* Find an idle channel */
2012210284Sjmallett        CVMX_CLZ(channel, usb->idle_hardware_channels);
2013210284Sjmallett        channel = 31 - channel;
2014210284Sjmallett        if (cvmx_unlikely(channel > 7))
2015210284Sjmallett        {
2016210284Sjmallett            if (cvmx_unlikely(usb->init_flags & CVMX_USB_INITIALIZE_FLAGS_DEBUG_INFO))
2017210284Sjmallett                cvmx_dprintf("%s: Idle hardware channels has a channel higher than 7. This is wrong\n", __FUNCTION__);
2018210284Sjmallett            break;
2019210284Sjmallett        }
2020210284Sjmallett
2021210284Sjmallett        /* Find a pipe needing service */
2022210284Sjmallett        pipe = NULL;
2023210284Sjmallett        if (is_sof)
2024210284Sjmallett        {
2025210284Sjmallett            /* Only process periodic pipes on SOF interrupts. This way we are
2026210284Sjmallett                sure that the periodic data is sent in the beginning of the
2027210284Sjmallett                frame */
2028215990Sjmallett            pipe = __cvmx_usb_find_ready_pipe(usb, usb->active_pipes + CVMX_USB_TRANSFER_ISOCHRONOUS, usb->frame_number);
2029210284Sjmallett            if (cvmx_likely(!pipe))
2030215990Sjmallett                pipe = __cvmx_usb_find_ready_pipe(usb, usb->active_pipes + CVMX_USB_TRANSFER_INTERRUPT, usb->frame_number);
2031210284Sjmallett        }
2032210284Sjmallett        if (cvmx_likely(!pipe))
2033210284Sjmallett        {
2034215990Sjmallett            pipe = __cvmx_usb_find_ready_pipe(usb, usb->active_pipes + CVMX_USB_TRANSFER_CONTROL, usb->frame_number);
2035210284Sjmallett            if (cvmx_likely(!pipe))
2036215990Sjmallett                pipe = __cvmx_usb_find_ready_pipe(usb, usb->active_pipes + CVMX_USB_TRANSFER_BULK, usb->frame_number);
2037210284Sjmallett        }
2038210284Sjmallett        if (!pipe)
2039210284Sjmallett            break;
2040210284Sjmallett
2041210284Sjmallett        CVMX_USB_LOG_PARAM("%d", channel);
2042210284Sjmallett        CVMX_USB_LOG_PARAM("%p", pipe);
2043210284Sjmallett
2044210284Sjmallett        if (cvmx_unlikely((usb->init_flags & CVMX_USB_INITIALIZE_FLAGS_DEBUG_TRANSFERS) ||
2045210284Sjmallett            (pipe->flags & CVMX_USB_PIPE_FLAGS_DEBUG_TRANSFERS)))
2046210284Sjmallett        {
2047210284Sjmallett            cvmx_usb_transaction_t *transaction = pipe->head;
2048210284Sjmallett            const cvmx_usb_control_header_t *header = (transaction->control_header) ? cvmx_phys_to_ptr(transaction->control_header) : NULL;
2049210284Sjmallett            const char *dir = (pipe->transfer_dir == CVMX_USB_DIRECTION_IN) ? "IN" : "OUT";
2050210284Sjmallett            const char *type;
2051210284Sjmallett            switch (pipe->transfer_type)
2052210284Sjmallett            {
2053210284Sjmallett                case CVMX_USB_TRANSFER_CONTROL:
2054210284Sjmallett                    type = "SETUP";
2055210284Sjmallett                    dir = (header->s.request_type & 0x80) ? "IN" : "OUT";
2056210284Sjmallett                    break;
2057210284Sjmallett                case CVMX_USB_TRANSFER_ISOCHRONOUS:
2058210284Sjmallett                    type = "ISOCHRONOUS";
2059210284Sjmallett                    break;
2060210284Sjmallett                case CVMX_USB_TRANSFER_BULK:
2061210284Sjmallett                    type = "BULK";
2062210284Sjmallett                    break;
2063210284Sjmallett                default: /* CVMX_USB_TRANSFER_INTERRUPT */
2064210284Sjmallett                    type = "INTERRUPT";
2065210284Sjmallett                    break;
2066210284Sjmallett            }
2067210284Sjmallett            cvmx_dprintf("%s: Starting pipe %d, transaction %d on channel %d. %s %s len=%d header=0x%llx\n",
2068210284Sjmallett                         __FUNCTION__, __cvmx_usb_get_pipe_handle(usb, pipe),
2069210284Sjmallett                         __cvmx_usb_get_submit_handle(usb, transaction),
2070210284Sjmallett                         channel, type, dir,
2071210284Sjmallett                         transaction->buffer_length,
2072210284Sjmallett                         (header) ? (unsigned long long)header->u64 : 0ull);
2073210284Sjmallett        }
2074210284Sjmallett        __cvmx_usb_start_channel(usb, channel, pipe);
2075210284Sjmallett    }
2076215990Sjmallett
2077215990Sjmallettdone:
2078215990Sjmallett    /* Only enable SOF interrupts when we have transactions pending in the
2079215990Sjmallett        future that might need to be scheduled */
2080215990Sjmallett    need_sof = 0;
2081215990Sjmallett    for (ttype=CVMX_USB_TRANSFER_CONTROL; ttype<=CVMX_USB_TRANSFER_INTERRUPT; ttype++)
2082215990Sjmallett    {
2083215990Sjmallett        pipe = usb->active_pipes[ttype].head;
2084215990Sjmallett        while (pipe)
2085215990Sjmallett        {
2086215990Sjmallett            if (pipe->next_tx_frame > usb->frame_number)
2087215990Sjmallett            {
2088215990Sjmallett                need_sof = 1;
2089215990Sjmallett                break;
2090215990Sjmallett            }
2091215990Sjmallett            pipe=pipe->next;
2092215990Sjmallett        }
2093215990Sjmallett    }
2094215990Sjmallett    USB_SET_FIELD32(CVMX_USBCX_GINTMSK(usb->index), cvmx_usbcx_gintmsk_t, sofmsk, need_sof);
2095210284Sjmallett    CVMX_USB_RETURN_NOTHING();
2096210284Sjmallett}
2097210284Sjmallett
2098210284Sjmallett
2099210284Sjmallett/**
2100210284Sjmallett * @INTERNAL
2101210284Sjmallett * Call a user's callback for a specific reason.
2102210284Sjmallett *
2103210284Sjmallett * @param usb    USB device state populated by
2104210284Sjmallett *               cvmx_usb_initialize().
2105210284Sjmallett * @param pipe   Pipe the callback is for or NULL
2106210284Sjmallett * @param transaction
2107210284Sjmallett *               Transaction the callback is for or NULL
2108210284Sjmallett * @param reason Reason this callback is being called
2109210284Sjmallett * @param complete_code
2110210284Sjmallett *               Completion code for the transaction, if any
2111210284Sjmallett */
2112210284Sjmallettstatic void __cvmx_usb_perform_callback(cvmx_usb_internal_state_t *usb,
2113210284Sjmallett                                        cvmx_usb_pipe_t *pipe,
2114210284Sjmallett                                        cvmx_usb_transaction_t *transaction,
2115210284Sjmallett                                        cvmx_usb_callback_t reason,
2116210284Sjmallett                                        cvmx_usb_complete_t complete_code)
2117210284Sjmallett{
2118210284Sjmallett    cvmx_usb_callback_func_t callback = usb->callback[reason];
2119210284Sjmallett    void *user_data = usb->callback_data[reason];
2120210284Sjmallett    int submit_handle = -1;
2121210284Sjmallett    int pipe_handle = -1;
2122210284Sjmallett    int bytes_transferred = 0;
2123210284Sjmallett
2124210284Sjmallett    if (pipe)
2125210284Sjmallett        pipe_handle = __cvmx_usb_get_pipe_handle(usb, pipe);
2126210284Sjmallett
2127210284Sjmallett    if (transaction)
2128210284Sjmallett    {
2129210284Sjmallett        submit_handle = __cvmx_usb_get_submit_handle(usb, transaction);
2130210284Sjmallett        bytes_transferred = transaction->actual_bytes;
2131210284Sjmallett        /* Transactions are allowed to override the default callback */
2132210284Sjmallett        if ((reason == CVMX_USB_CALLBACK_TRANSFER_COMPLETE) && transaction->callback)
2133210284Sjmallett        {
2134210284Sjmallett            callback = transaction->callback;
2135210284Sjmallett            user_data = transaction->callback_data;
2136210284Sjmallett        }
2137210284Sjmallett    }
2138210284Sjmallett
2139210284Sjmallett    if (!callback)
2140210284Sjmallett        return;
2141210284Sjmallett
2142210284Sjmallett    if (cvmx_unlikely(usb->init_flags & CVMX_USB_INITIALIZE_FLAGS_DEBUG_CALLBACKS))
2143210284Sjmallett        cvmx_dprintf("%*s%s: calling callback %p(usb=%p, complete_code=%s, "
2144210284Sjmallett                     "pipe_handle=%d, submit_handle=%d, bytes_transferred=%d, user_data=%p);\n",
2145210284Sjmallett                     2*usb->indent, "", __FUNCTION__, callback, usb,
2146210284Sjmallett                     __cvmx_usb_complete_to_string(complete_code),
2147210284Sjmallett                     pipe_handle, submit_handle, bytes_transferred, user_data);
2148210284Sjmallett
2149210284Sjmallett    callback((cvmx_usb_state_t *)usb, reason, complete_code, pipe_handle, submit_handle,
2150210284Sjmallett             bytes_transferred, user_data);
2151210284Sjmallett
2152210284Sjmallett    if (cvmx_unlikely(usb->init_flags & CVMX_USB_INITIALIZE_FLAGS_DEBUG_CALLBACKS))
2153210284Sjmallett        cvmx_dprintf("%*s%s: callback %p complete\n", 2*usb->indent, "",
2154210284Sjmallett                      __FUNCTION__, callback);
2155210284Sjmallett}
2156210284Sjmallett
2157210284Sjmallett
2158210284Sjmallett/**
2159210284Sjmallett * @INTERNAL
2160210284Sjmallett * Signal the completion of a transaction and free it. The
2161210284Sjmallett * transaction will be removed from the pipe transaction list.
2162210284Sjmallett *
2163210284Sjmallett * @param usb    USB device state populated by
2164210284Sjmallett *               cvmx_usb_initialize().
2165210284Sjmallett * @param pipe   Pipe the transaction is on
2166210284Sjmallett * @param transaction
2167210284Sjmallett *               Transaction that completed
2168210284Sjmallett * @param complete_code
2169210284Sjmallett *               Completion code
2170210284Sjmallett */
2171210284Sjmallettstatic void __cvmx_usb_perform_complete(cvmx_usb_internal_state_t * usb,
2172210284Sjmallett                                        cvmx_usb_pipe_t *pipe,
2173210284Sjmallett                                        cvmx_usb_transaction_t *transaction,
2174210284Sjmallett                                        cvmx_usb_complete_t complete_code)
2175210284Sjmallett{
2176210284Sjmallett    CVMX_USB_LOG_CALLED();
2177210284Sjmallett    CVMX_USB_LOG_PARAM("%p", usb);
2178210284Sjmallett    CVMX_USB_LOG_PARAM("%p", pipe);
2179210284Sjmallett    CVMX_USB_LOG_PARAM("%p", transaction);
2180210284Sjmallett    CVMX_USB_LOG_PARAM("%d", complete_code);
2181210284Sjmallett
2182215990Sjmallett    /* If this was a split then clear our split in progress marker */
2183215990Sjmallett    if (usb->active_split == transaction)
2184215990Sjmallett        usb->active_split = NULL;
2185215990Sjmallett
2186210284Sjmallett    /* Isochronous transactions need extra processing as they might not be done
2187210284Sjmallett        after a single data transfer */
2188210284Sjmallett    if (cvmx_unlikely(transaction->type == CVMX_USB_TRANSFER_ISOCHRONOUS))
2189210284Sjmallett    {
2190215990Sjmallett        /* Update the number of bytes transferred in this ISO packet */
2191210284Sjmallett        transaction->iso_packets[0].length = transaction->actual_bytes;
2192210284Sjmallett        transaction->iso_packets[0].status = complete_code;
2193210284Sjmallett
2194215990Sjmallett        /* If there are more ISOs pending and we succeeded, schedule the next
2195210284Sjmallett            one */
2196210284Sjmallett        if ((transaction->iso_number_packets > 1) && (complete_code == CVMX_USB_COMPLETE_SUCCESS))
2197210284Sjmallett        {
2198215990Sjmallett            transaction->actual_bytes = 0;      /* No bytes transferred for this packet as of yet */
2199210284Sjmallett            transaction->iso_number_packets--;  /* One less ISO waiting to transfer */
2200210284Sjmallett            transaction->iso_packets++;         /* Increment to the next location in our packet array */
2201210284Sjmallett            transaction->stage = CVMX_USB_STAGE_NON_CONTROL;
2202210284Sjmallett            goto done;
2203210284Sjmallett        }
2204210284Sjmallett    }
2205210284Sjmallett
2206210284Sjmallett    /* Remove the transaction from the pipe list */
2207210284Sjmallett    if (transaction->next)
2208210284Sjmallett        transaction->next->prev = transaction->prev;
2209210284Sjmallett    else
2210210284Sjmallett        pipe->tail = transaction->prev;
2211210284Sjmallett    if (transaction->prev)
2212210284Sjmallett        transaction->prev->next = transaction->next;
2213210284Sjmallett    else
2214210284Sjmallett        pipe->head = transaction->next;
2215210284Sjmallett    if (!pipe->head)
2216210284Sjmallett    {
2217210284Sjmallett        __cvmx_usb_remove_pipe(usb->active_pipes + pipe->transfer_type, pipe);
2218210284Sjmallett        __cvmx_usb_append_pipe(&usb->idle_pipes, pipe);
2219210284Sjmallett
2220210284Sjmallett    }
2221210284Sjmallett    __cvmx_usb_perform_callback(usb, pipe, transaction,
2222210284Sjmallett                                CVMX_USB_CALLBACK_TRANSFER_COMPLETE,
2223210284Sjmallett                                complete_code);
2224210284Sjmallett    __cvmx_usb_free_transaction(usb, transaction);
2225210284Sjmallettdone:
2226210284Sjmallett    CVMX_USB_RETURN_NOTHING();
2227210284Sjmallett}
2228210284Sjmallett
2229210284Sjmallett
2230210284Sjmallett/**
2231210284Sjmallett * @INTERNAL
2232210284Sjmallett * Submit a usb transaction to a pipe. Called for all types
2233210284Sjmallett * of transactions.
2234210284Sjmallett *
2235210284Sjmallett * @param usb
2236210284Sjmallett * @param pipe_handle
2237210284Sjmallett *                  Which pipe to submit to. Will be validated in this function.
2238210284Sjmallett * @param type      Transaction type
2239210284Sjmallett * @param flags     Flags for the transaction
2240210284Sjmallett * @param buffer    User buffer for the transaction
2241210284Sjmallett * @param buffer_length
2242210284Sjmallett *                  User buffer's length in bytes
2243210284Sjmallett * @param control_header
2244210284Sjmallett *                  For control transactions, the 8 byte standard header
2245210284Sjmallett * @param iso_start_frame
2246215990Sjmallett *                  For ISO transactions, the start frame
2247210284Sjmallett * @param iso_number_packets
2248210284Sjmallett *                  For ISO, the number of packet in the transaction.
2249210284Sjmallett * @param iso_packets
2250210284Sjmallett *                  A description of each ISO packet
2251210284Sjmallett * @param callback  User callback to call when the transaction completes
2252210284Sjmallett * @param user_data User's data for the callback
2253210284Sjmallett *
2254210284Sjmallett * @return Submit handle or negative on failure. Matches the result
2255210284Sjmallett *         in the external API.
2256210284Sjmallett */
2257210284Sjmallettstatic int __cvmx_usb_submit_transaction(cvmx_usb_internal_state_t *usb,
2258210284Sjmallett                                         int pipe_handle,
2259210284Sjmallett                                         cvmx_usb_transfer_t type,
2260210284Sjmallett                                         int flags,
2261210284Sjmallett                                         uint64_t buffer,
2262210284Sjmallett                                         int buffer_length,
2263210284Sjmallett                                         uint64_t control_header,
2264210284Sjmallett                                         int iso_start_frame,
2265210284Sjmallett                                         int iso_number_packets,
2266210284Sjmallett                                         cvmx_usb_iso_packet_t *iso_packets,
2267210284Sjmallett                                         cvmx_usb_callback_func_t callback,
2268210284Sjmallett                                         void *user_data)
2269210284Sjmallett{
2270210284Sjmallett    int submit_handle;
2271210284Sjmallett    cvmx_usb_transaction_t *transaction;
2272210284Sjmallett    cvmx_usb_pipe_t *pipe = usb->pipe + pipe_handle;
2273210284Sjmallett
2274210284Sjmallett    CVMX_USB_LOG_CALLED();
2275210284Sjmallett    if (cvmx_unlikely((pipe_handle < 0) || (pipe_handle >= MAX_PIPES)))
2276210284Sjmallett        CVMX_USB_RETURN(CVMX_USB_INVALID_PARAM);
2277210284Sjmallett    /* Fail if the pipe isn't open */
2278210284Sjmallett    if (cvmx_unlikely((pipe->flags & __CVMX_USB_PIPE_FLAGS_OPEN) == 0))
2279210284Sjmallett        CVMX_USB_RETURN(CVMX_USB_INVALID_PARAM);
2280210284Sjmallett    if (cvmx_unlikely(pipe->transfer_type != type))
2281210284Sjmallett        CVMX_USB_RETURN(CVMX_USB_INVALID_PARAM);
2282210284Sjmallett
2283210284Sjmallett    transaction = __cvmx_usb_alloc_transaction(usb);
2284210284Sjmallett    if (cvmx_unlikely(!transaction))
2285210284Sjmallett        CVMX_USB_RETURN(CVMX_USB_NO_MEMORY);
2286210284Sjmallett
2287210284Sjmallett    transaction->type = type;
2288210284Sjmallett    transaction->flags |= flags;
2289210284Sjmallett    transaction->buffer = buffer;
2290210284Sjmallett    transaction->buffer_length = buffer_length;
2291210284Sjmallett    transaction->control_header = control_header;
2292210284Sjmallett    transaction->iso_start_frame = iso_start_frame; // FIXME: This is not used, implement it
2293210284Sjmallett    transaction->iso_number_packets = iso_number_packets;
2294210284Sjmallett    transaction->iso_packets = iso_packets;
2295210284Sjmallett    transaction->callback = callback;
2296210284Sjmallett    transaction->callback_data = user_data;
2297210284Sjmallett    if (transaction->type == CVMX_USB_TRANSFER_CONTROL)
2298210284Sjmallett        transaction->stage = CVMX_USB_STAGE_SETUP;
2299210284Sjmallett    else
2300210284Sjmallett        transaction->stage = CVMX_USB_STAGE_NON_CONTROL;
2301210284Sjmallett
2302210284Sjmallett    transaction->next = NULL;
2303210284Sjmallett    if (pipe->tail)
2304210284Sjmallett    {
2305210284Sjmallett        transaction->prev = pipe->tail;
2306210284Sjmallett        transaction->prev->next = transaction;
2307210284Sjmallett    }
2308210284Sjmallett    else
2309210284Sjmallett    {
2310215990Sjmallett        if (pipe->next_tx_frame < usb->frame_number)
2311215990Sjmallett            pipe->next_tx_frame = usb->frame_number + pipe->interval -
2312215990Sjmallett                (usb->frame_number - pipe->next_tx_frame) % pipe->interval;
2313210284Sjmallett        transaction->prev = NULL;
2314210284Sjmallett        pipe->head = transaction;
2315210284Sjmallett        __cvmx_usb_remove_pipe(&usb->idle_pipes, pipe);
2316210284Sjmallett        __cvmx_usb_append_pipe(usb->active_pipes + pipe->transfer_type, pipe);
2317210284Sjmallett    }
2318210284Sjmallett    pipe->tail = transaction;
2319210284Sjmallett
2320210284Sjmallett    submit_handle = __cvmx_usb_get_submit_handle(usb, transaction);
2321210284Sjmallett
2322210284Sjmallett    /* We may need to schedule the pipe if this was the head of the pipe */
2323210284Sjmallett    if (!transaction->prev)
2324210284Sjmallett        __cvmx_usb_schedule(usb, 0);
2325210284Sjmallett
2326210284Sjmallett    CVMX_USB_RETURN(submit_handle);
2327210284Sjmallett}
2328210284Sjmallett
2329210284Sjmallett
2330210284Sjmallett/**
2331210284Sjmallett * Call to submit a USB Bulk transfer to a pipe.
2332210284Sjmallett *
2333210284Sjmallett * @param state     USB device state populated by
2334210284Sjmallett *                  cvmx_usb_initialize().
2335210284Sjmallett * @param pipe_handle
2336210284Sjmallett *                  Handle to the pipe for the transfer.
2337210284Sjmallett * @param buffer    Physical address of the data buffer in
2338210284Sjmallett *                  memory. Note that this is NOT A POINTER, but
2339210284Sjmallett *                  the full 64bit physical address of the
2340210284Sjmallett *                  buffer. This may be zero if buffer_length is
2341210284Sjmallett *                  zero.
2342210284Sjmallett * @param buffer_length
2343210284Sjmallett *                  Length of buffer in bytes.
2344210284Sjmallett * @param callback  Function to call when this transaction
2345210284Sjmallett *                  completes. If the return value of this
2346210284Sjmallett *                  function isn't an error, then this function
2347210284Sjmallett *                  is guaranteed to be called when the
2348210284Sjmallett *                  transaction completes. If this parameter is
2349210284Sjmallett *                  NULL, then the generic callback registered
2350210284Sjmallett *                  through cvmx_usb_register_callback is
2351210284Sjmallett *                  called. If both are NULL, then there is no
2352210284Sjmallett *                  way to know when a transaction completes.
2353210284Sjmallett * @param user_data User supplied data returned when the
2354210284Sjmallett *                  callback is called. This is only used if
2355210284Sjmallett *                  callback in not NULL.
2356210284Sjmallett *
2357210284Sjmallett * @return A submitted transaction handle or negative on
2358210284Sjmallett *         failure. Negative values are failure codes from
2359210284Sjmallett *         cvmx_usb_status_t.
2360210284Sjmallett */
2361210284Sjmallettint cvmx_usb_submit_bulk(cvmx_usb_state_t *state, int pipe_handle,
2362210284Sjmallett                                uint64_t buffer, int buffer_length,
2363210284Sjmallett                                cvmx_usb_callback_func_t callback,
2364210284Sjmallett                                void *user_data)
2365210284Sjmallett{
2366210284Sjmallett    int submit_handle;
2367210284Sjmallett    cvmx_usb_internal_state_t *usb = (cvmx_usb_internal_state_t*)state;
2368210284Sjmallett
2369210284Sjmallett    CVMX_USB_LOG_CALLED();
2370210284Sjmallett    CVMX_USB_LOG_PARAM("%p", state);
2371210284Sjmallett    CVMX_USB_LOG_PARAM("%d", pipe_handle);
2372210284Sjmallett    CVMX_USB_LOG_PARAM("0x%llx", (unsigned long long)buffer);
2373210284Sjmallett    CVMX_USB_LOG_PARAM("%d", buffer_length);
2374210284Sjmallett
2375210284Sjmallett    /* Pipe handle checking is done later in a common place */
2376210284Sjmallett    if (cvmx_unlikely(!buffer))
2377210284Sjmallett        CVMX_USB_RETURN(CVMX_USB_INVALID_PARAM);
2378210284Sjmallett    if (cvmx_unlikely(buffer_length < 0))
2379210284Sjmallett        CVMX_USB_RETURN(CVMX_USB_INVALID_PARAM);
2380210284Sjmallett
2381210284Sjmallett    submit_handle = __cvmx_usb_submit_transaction(usb, pipe_handle,
2382210284Sjmallett                                         CVMX_USB_TRANSFER_BULK,
2383210284Sjmallett                                         0, /* flags */
2384210284Sjmallett                                         buffer,
2385210284Sjmallett                                         buffer_length,
2386210284Sjmallett                                         0, /* control_header */
2387210284Sjmallett                                         0, /* iso_start_frame */
2388210284Sjmallett                                         0, /* iso_number_packets */
2389210284Sjmallett                                         NULL, /* iso_packets */
2390210284Sjmallett                                         callback,
2391210284Sjmallett                                         user_data);
2392210284Sjmallett    CVMX_USB_RETURN(submit_handle);
2393210284Sjmallett}
2394215990Sjmallett#ifdef CVMX_BUILD_FOR_LINUX_KERNEL
2395215990SjmallettEXPORT_SYMBOL(cvmx_usb_submit_bulk);
2396215990Sjmallett#endif
2397210284Sjmallett
2398210284Sjmallett
2399210284Sjmallett/**
2400210284Sjmallett * Call to submit a USB Interrupt transfer to a pipe.
2401210284Sjmallett *
2402210284Sjmallett * @param state     USB device state populated by
2403210284Sjmallett *                  cvmx_usb_initialize().
2404210284Sjmallett * @param pipe_handle
2405210284Sjmallett *                  Handle to the pipe for the transfer.
2406210284Sjmallett * @param buffer    Physical address of the data buffer in
2407210284Sjmallett *                  memory. Note that this is NOT A POINTER, but
2408210284Sjmallett *                  the full 64bit physical address of the
2409210284Sjmallett *                  buffer. This may be zero if buffer_length is
2410210284Sjmallett *                  zero.
2411210284Sjmallett * @param buffer_length
2412210284Sjmallett *                  Length of buffer in bytes.
2413210284Sjmallett * @param callback  Function to call when this transaction
2414210284Sjmallett *                  completes. If the return value of this
2415210284Sjmallett *                  function isn't an error, then this function
2416210284Sjmallett *                  is guaranteed to be called when the
2417210284Sjmallett *                  transaction completes. If this parameter is
2418210284Sjmallett *                  NULL, then the generic callback registered
2419210284Sjmallett *                  through cvmx_usb_register_callback is
2420210284Sjmallett *                  called. If both are NULL, then there is no
2421210284Sjmallett *                  way to know when a transaction completes.
2422210284Sjmallett * @param user_data User supplied data returned when the
2423210284Sjmallett *                  callback is called. This is only used if
2424210284Sjmallett *                  callback in not NULL.
2425210284Sjmallett *
2426210284Sjmallett * @return A submitted transaction handle or negative on
2427210284Sjmallett *         failure. Negative values are failure codes from
2428210284Sjmallett *         cvmx_usb_status_t.
2429210284Sjmallett */
2430210284Sjmallettint cvmx_usb_submit_interrupt(cvmx_usb_state_t *state, int pipe_handle,
2431210284Sjmallett                              uint64_t buffer, int buffer_length,
2432210284Sjmallett                              cvmx_usb_callback_func_t callback,
2433210284Sjmallett                              void *user_data)
2434210284Sjmallett{
2435210284Sjmallett    int submit_handle;
2436210284Sjmallett    cvmx_usb_internal_state_t *usb = (cvmx_usb_internal_state_t*)state;
2437210284Sjmallett
2438210284Sjmallett    CVMX_USB_LOG_CALLED();
2439210284Sjmallett    CVMX_USB_LOG_PARAM("%p", state);
2440210284Sjmallett    CVMX_USB_LOG_PARAM("%d", pipe_handle);
2441210284Sjmallett    CVMX_USB_LOG_PARAM("0x%llx", (unsigned long long)buffer);
2442210284Sjmallett    CVMX_USB_LOG_PARAM("%d", buffer_length);
2443210284Sjmallett
2444210284Sjmallett    /* Pipe handle checking is done later in a common place */
2445210284Sjmallett    if (cvmx_unlikely(!buffer))
2446210284Sjmallett        CVMX_USB_RETURN(CVMX_USB_INVALID_PARAM);
2447210284Sjmallett    if (cvmx_unlikely(buffer_length < 0))
2448210284Sjmallett        CVMX_USB_RETURN(CVMX_USB_INVALID_PARAM);
2449210284Sjmallett
2450210284Sjmallett    submit_handle = __cvmx_usb_submit_transaction(usb, pipe_handle,
2451210284Sjmallett                                         CVMX_USB_TRANSFER_INTERRUPT,
2452210284Sjmallett                                         0, /* flags */
2453210284Sjmallett                                         buffer,
2454210284Sjmallett                                         buffer_length,
2455210284Sjmallett                                         0, /* control_header */
2456210284Sjmallett                                         0, /* iso_start_frame */
2457210284Sjmallett                                         0, /* iso_number_packets */
2458210284Sjmallett                                         NULL, /* iso_packets */
2459210284Sjmallett                                         callback,
2460210284Sjmallett                                         user_data);
2461210284Sjmallett    CVMX_USB_RETURN(submit_handle);
2462210284Sjmallett}
2463215990Sjmallett#ifdef CVMX_BUILD_FOR_LINUX_KERNEL
2464215990SjmallettEXPORT_SYMBOL(cvmx_usb_submit_interrupt);
2465215990Sjmallett#endif
2466210284Sjmallett
2467210284Sjmallett
2468210284Sjmallett/**
2469210284Sjmallett * Call to submit a USB Control transfer to a pipe.
2470210284Sjmallett *
2471210284Sjmallett * @param state     USB device state populated by
2472210284Sjmallett *                  cvmx_usb_initialize().
2473210284Sjmallett * @param pipe_handle
2474210284Sjmallett *                  Handle to the pipe for the transfer.
2475210284Sjmallett * @param control_header
2476210284Sjmallett *                  USB 8 byte control header physical address.
2477210284Sjmallett *                  Note that this is NOT A POINTER, but the
2478210284Sjmallett *                  full 64bit physical address of the buffer.
2479210284Sjmallett * @param buffer    Physical address of the data buffer in
2480210284Sjmallett *                  memory. Note that this is NOT A POINTER, but
2481210284Sjmallett *                  the full 64bit physical address of the
2482210284Sjmallett *                  buffer. This may be zero if buffer_length is
2483210284Sjmallett *                  zero.
2484210284Sjmallett * @param buffer_length
2485210284Sjmallett *                  Length of buffer in bytes.
2486210284Sjmallett * @param callback  Function to call when this transaction
2487210284Sjmallett *                  completes. If the return value of this
2488210284Sjmallett *                  function isn't an error, then this function
2489210284Sjmallett *                  is guaranteed to be called when the
2490210284Sjmallett *                  transaction completes. If this parameter is
2491210284Sjmallett *                  NULL, then the generic callback registered
2492210284Sjmallett *                  through cvmx_usb_register_callback is
2493210284Sjmallett *                  called. If both are NULL, then there is no
2494210284Sjmallett *                  way to know when a transaction completes.
2495210284Sjmallett * @param user_data User supplied data returned when the
2496210284Sjmallett *                  callback is called. This is only used if
2497210284Sjmallett *                  callback in not NULL.
2498210284Sjmallett *
2499210284Sjmallett * @return A submitted transaction handle or negative on
2500210284Sjmallett *         failure. Negative values are failure codes from
2501210284Sjmallett *         cvmx_usb_status_t.
2502210284Sjmallett */
2503210284Sjmallettint cvmx_usb_submit_control(cvmx_usb_state_t *state, int pipe_handle,
2504210284Sjmallett                            uint64_t control_header,
2505210284Sjmallett                            uint64_t buffer, int buffer_length,
2506210284Sjmallett                            cvmx_usb_callback_func_t callback,
2507210284Sjmallett                            void *user_data)
2508210284Sjmallett{
2509210284Sjmallett    int submit_handle;
2510210284Sjmallett    cvmx_usb_internal_state_t *usb = (cvmx_usb_internal_state_t*)state;
2511210284Sjmallett    cvmx_usb_control_header_t *header = cvmx_phys_to_ptr(control_header);
2512210284Sjmallett
2513210284Sjmallett    CVMX_USB_LOG_CALLED();
2514210284Sjmallett    CVMX_USB_LOG_PARAM("%p", state);
2515210284Sjmallett    CVMX_USB_LOG_PARAM("%d", pipe_handle);
2516210284Sjmallett    CVMX_USB_LOG_PARAM("0x%llx", (unsigned long long)control_header);
2517210284Sjmallett    CVMX_USB_LOG_PARAM("0x%llx", (unsigned long long)buffer);
2518210284Sjmallett    CVMX_USB_LOG_PARAM("%d", buffer_length);
2519210284Sjmallett
2520210284Sjmallett    /* Pipe handle checking is done later in a common place */
2521210284Sjmallett    if (cvmx_unlikely(!control_header))
2522210284Sjmallett        CVMX_USB_RETURN(CVMX_USB_INVALID_PARAM);
2523210284Sjmallett    /* Some drivers send a buffer with a zero length. God only knows why */
2524210284Sjmallett    if (cvmx_unlikely(buffer && (buffer_length < 0)))
2525210284Sjmallett        CVMX_USB_RETURN(CVMX_USB_INVALID_PARAM);
2526210284Sjmallett    if (cvmx_unlikely(!buffer && (buffer_length != 0)))
2527210284Sjmallett        CVMX_USB_RETURN(CVMX_USB_INVALID_PARAM);
2528210284Sjmallett    if ((header->s.request_type & 0x80) == 0)
2529210284Sjmallett        buffer_length = cvmx_le16_to_cpu(header->s.length);
2530210284Sjmallett
2531210284Sjmallett    submit_handle = __cvmx_usb_submit_transaction(usb, pipe_handle,
2532210284Sjmallett                                         CVMX_USB_TRANSFER_CONTROL,
2533210284Sjmallett                                         0, /* flags */
2534210284Sjmallett                                         buffer,
2535210284Sjmallett                                         buffer_length,
2536210284Sjmallett                                         control_header,
2537210284Sjmallett                                         0, /* iso_start_frame */
2538210284Sjmallett                                         0, /* iso_number_packets */
2539210284Sjmallett                                         NULL, /* iso_packets */
2540210284Sjmallett                                         callback,
2541210284Sjmallett                                         user_data);
2542210284Sjmallett    CVMX_USB_RETURN(submit_handle);
2543210284Sjmallett}
2544215990Sjmallett#ifdef CVMX_BUILD_FOR_LINUX_KERNEL
2545215990SjmallettEXPORT_SYMBOL(cvmx_usb_submit_control);
2546215990Sjmallett#endif
2547210284Sjmallett
2548210284Sjmallett
2549210284Sjmallett/**
2550210284Sjmallett * Call to submit a USB Isochronous transfer to a pipe.
2551210284Sjmallett *
2552210284Sjmallett * @param state     USB device state populated by
2553210284Sjmallett *                  cvmx_usb_initialize().
2554210284Sjmallett * @param pipe_handle
2555210284Sjmallett *                  Handle to the pipe for the transfer.
2556210284Sjmallett * @param start_frame
2557210284Sjmallett *                  Number of frames into the future to schedule
2558210284Sjmallett *                  this transaction.
2559210284Sjmallett * @param flags     Flags to control the transfer. See
2560210284Sjmallett *                  cvmx_usb_isochronous_flags_t for the flag
2561210284Sjmallett *                  definitions.
2562210284Sjmallett * @param number_packets
2563210284Sjmallett *                  Number of sequential packets to transfer.
2564210284Sjmallett *                  "packets" is a pointer to an array of this
2565210284Sjmallett *                  many packet structures.
2566210284Sjmallett * @param packets   Description of each transfer packet as
2567210284Sjmallett *                  defined by cvmx_usb_iso_packet_t. The array
2568210284Sjmallett *                  pointed to here must stay valid until the
2569210284Sjmallett *                  complete callback is called.
2570210284Sjmallett * @param buffer    Physical address of the data buffer in
2571210284Sjmallett *                  memory. Note that this is NOT A POINTER, but
2572210284Sjmallett *                  the full 64bit physical address of the
2573210284Sjmallett *                  buffer. This may be zero if buffer_length is
2574210284Sjmallett *                  zero.
2575210284Sjmallett * @param buffer_length
2576210284Sjmallett *                  Length of buffer in bytes.
2577210284Sjmallett * @param callback  Function to call when this transaction
2578210284Sjmallett *                  completes. If the return value of this
2579210284Sjmallett *                  function isn't an error, then this function
2580210284Sjmallett *                  is guaranteed to be called when the
2581210284Sjmallett *                  transaction completes. If this parameter is
2582210284Sjmallett *                  NULL, then the generic callback registered
2583210284Sjmallett *                  through cvmx_usb_register_callback is
2584210284Sjmallett *                  called. If both are NULL, then there is no
2585210284Sjmallett *                  way to know when a transaction completes.
2586210284Sjmallett * @param user_data User supplied data returned when the
2587210284Sjmallett *                  callback is called. This is only used if
2588210284Sjmallett *                  callback in not NULL.
2589210284Sjmallett *
2590210284Sjmallett * @return A submitted transaction handle or negative on
2591210284Sjmallett *         failure. Negative values are failure codes from
2592210284Sjmallett *         cvmx_usb_status_t.
2593210284Sjmallett */
2594210284Sjmallettint cvmx_usb_submit_isochronous(cvmx_usb_state_t *state, int pipe_handle,
2595210284Sjmallett                                int start_frame, int flags,
2596210284Sjmallett                                int number_packets,
2597210284Sjmallett                                cvmx_usb_iso_packet_t packets[],
2598210284Sjmallett                                uint64_t buffer, int buffer_length,
2599210284Sjmallett                                cvmx_usb_callback_func_t callback,
2600210284Sjmallett                                void *user_data)
2601210284Sjmallett{
2602210284Sjmallett    int submit_handle;
2603210284Sjmallett    cvmx_usb_internal_state_t *usb = (cvmx_usb_internal_state_t*)state;
2604210284Sjmallett
2605210284Sjmallett    CVMX_USB_LOG_CALLED();
2606210284Sjmallett    CVMX_USB_LOG_PARAM("%p", state);
2607210284Sjmallett    CVMX_USB_LOG_PARAM("%d", pipe_handle);
2608210284Sjmallett    CVMX_USB_LOG_PARAM("%d", start_frame);
2609210284Sjmallett    CVMX_USB_LOG_PARAM("0x%x", flags);
2610210284Sjmallett    CVMX_USB_LOG_PARAM("%d", number_packets);
2611210284Sjmallett    CVMX_USB_LOG_PARAM("%p", packets);
2612210284Sjmallett    CVMX_USB_LOG_PARAM("0x%llx", (unsigned long long)buffer);
2613210284Sjmallett    CVMX_USB_LOG_PARAM("%d", buffer_length);
2614210284Sjmallett
2615210284Sjmallett    /* Pipe handle checking is done later in a common place */
2616210284Sjmallett    if (cvmx_unlikely(start_frame < 0))
2617210284Sjmallett        CVMX_USB_RETURN(CVMX_USB_INVALID_PARAM);
2618210284Sjmallett    if (cvmx_unlikely(flags & ~(CVMX_USB_ISOCHRONOUS_FLAGS_ALLOW_SHORT | CVMX_USB_ISOCHRONOUS_FLAGS_ASAP)))
2619210284Sjmallett        CVMX_USB_RETURN(CVMX_USB_INVALID_PARAM);
2620210284Sjmallett    if (cvmx_unlikely(number_packets < 1))
2621210284Sjmallett        CVMX_USB_RETURN(CVMX_USB_INVALID_PARAM);
2622210284Sjmallett    if (cvmx_unlikely(!packets))
2623210284Sjmallett        CVMX_USB_RETURN(CVMX_USB_INVALID_PARAM);
2624210284Sjmallett    if (cvmx_unlikely(!buffer))
2625210284Sjmallett        CVMX_USB_RETURN(CVMX_USB_INVALID_PARAM);
2626210284Sjmallett    if (cvmx_unlikely(buffer_length < 0))
2627210284Sjmallett        CVMX_USB_RETURN(CVMX_USB_INVALID_PARAM);
2628210284Sjmallett
2629210284Sjmallett    submit_handle = __cvmx_usb_submit_transaction(usb, pipe_handle,
2630210284Sjmallett                                         CVMX_USB_TRANSFER_ISOCHRONOUS,
2631210284Sjmallett                                         flags,
2632210284Sjmallett                                         buffer,
2633210284Sjmallett                                         buffer_length,
2634210284Sjmallett                                         0, /* control_header */
2635210284Sjmallett                                         start_frame,
2636210284Sjmallett                                         number_packets,
2637210284Sjmallett                                         packets,
2638210284Sjmallett                                         callback,
2639210284Sjmallett                                         user_data);
2640210284Sjmallett    CVMX_USB_RETURN(submit_handle);
2641210284Sjmallett}
2642215990Sjmallett#ifdef CVMX_BUILD_FOR_LINUX_KERNEL
2643215990SjmallettEXPORT_SYMBOL(cvmx_usb_submit_isochronous);
2644215990Sjmallett#endif
2645210284Sjmallett
2646210284Sjmallett
2647210284Sjmallett/**
2648210284Sjmallett * Cancel one outstanding request in a pipe. Canceling a request
2649210284Sjmallett * can fail if the transaction has already completed before cancel
2650210284Sjmallett * is called. Even after a successful cancel call, it may take
2651210284Sjmallett * a frame or two for the cvmx_usb_poll() function to call the
2652210284Sjmallett * associated callback.
2653210284Sjmallett *
2654210284Sjmallett * @param state  USB device state populated by
2655210284Sjmallett *               cvmx_usb_initialize().
2656210284Sjmallett * @param pipe_handle
2657210284Sjmallett *               Pipe handle to cancel requests in.
2658210284Sjmallett * @param submit_handle
2659210284Sjmallett *               Handle to transaction to cancel, returned by the submit function.
2660210284Sjmallett *
2661210284Sjmallett * @return CVMX_USB_SUCCESS or a negative error code defined in
2662210284Sjmallett *         cvmx_usb_status_t.
2663210284Sjmallett */
2664210284Sjmallettcvmx_usb_status_t cvmx_usb_cancel(cvmx_usb_state_t *state, int pipe_handle,
2665210284Sjmallett                                  int submit_handle)
2666210284Sjmallett{
2667210284Sjmallett    cvmx_usb_transaction_t *transaction;
2668210284Sjmallett    cvmx_usb_internal_state_t *usb = (cvmx_usb_internal_state_t*)state;
2669210284Sjmallett    cvmx_usb_pipe_t *pipe = usb->pipe + pipe_handle;
2670210284Sjmallett
2671210284Sjmallett    CVMX_USB_LOG_CALLED();
2672210284Sjmallett    CVMX_USB_LOG_PARAM("%p", state);
2673210284Sjmallett    CVMX_USB_LOG_PARAM("%d", pipe_handle);
2674210284Sjmallett    CVMX_USB_LOG_PARAM("%d", submit_handle);
2675210284Sjmallett
2676210284Sjmallett    if (cvmx_unlikely((pipe_handle < 0) || (pipe_handle >= MAX_PIPES)))
2677210284Sjmallett        CVMX_USB_RETURN(CVMX_USB_INVALID_PARAM);
2678210284Sjmallett    if (cvmx_unlikely((submit_handle < 0) || (submit_handle >= MAX_TRANSACTIONS)))
2679210284Sjmallett        CVMX_USB_RETURN(CVMX_USB_INVALID_PARAM);
2680210284Sjmallett
2681210284Sjmallett    /* Fail if the pipe isn't open */
2682210284Sjmallett    if (cvmx_unlikely((pipe->flags & __CVMX_USB_PIPE_FLAGS_OPEN) == 0))
2683210284Sjmallett        CVMX_USB_RETURN(CVMX_USB_INVALID_PARAM);
2684210284Sjmallett
2685210284Sjmallett    transaction = usb->transaction + submit_handle;
2686210284Sjmallett
2687210284Sjmallett    /* Fail if this transaction already completed */
2688210284Sjmallett    if (cvmx_unlikely((transaction->flags & __CVMX_USB_TRANSACTION_FLAGS_IN_USE) == 0))
2689210284Sjmallett        CVMX_USB_RETURN(CVMX_USB_INVALID_PARAM);
2690210284Sjmallett
2691210284Sjmallett    /* If the transaction is the HEAD of the queue and scheduled. We need to
2692210284Sjmallett        treat it special */
2693210284Sjmallett    if ((pipe->head == transaction) &&
2694210284Sjmallett        (pipe->flags & __CVMX_USB_PIPE_FLAGS_SCHEDULED))
2695210284Sjmallett    {
2696210284Sjmallett        cvmx_usbcx_hccharx_t usbc_hcchar;
2697210284Sjmallett
2698210284Sjmallett        usb->pipe_for_channel[pipe->channel] = NULL;
2699210284Sjmallett        pipe->flags &= ~__CVMX_USB_PIPE_FLAGS_SCHEDULED;
2700210284Sjmallett
2701210284Sjmallett        CVMX_SYNCW;
2702210284Sjmallett
2703210284Sjmallett        usbc_hcchar.u32 = __cvmx_usb_read_csr32(usb, CVMX_USBCX_HCCHARX(pipe->channel, usb->index));
2704210284Sjmallett        /* If the channel isn't enabled then the transaction already completed */
2705210284Sjmallett        if (usbc_hcchar.s.chena)
2706210284Sjmallett        {
2707210284Sjmallett            usbc_hcchar.s.chdis = 1;
2708210284Sjmallett            __cvmx_usb_write_csr32(usb, CVMX_USBCX_HCCHARX(pipe->channel, usb->index), usbc_hcchar.u32);
2709210284Sjmallett        }
2710210284Sjmallett    }
2711210284Sjmallett    __cvmx_usb_perform_complete(usb, pipe, transaction, CVMX_USB_COMPLETE_CANCEL);
2712210284Sjmallett    CVMX_USB_RETURN(CVMX_USB_SUCCESS);
2713210284Sjmallett}
2714215990Sjmallett#ifdef CVMX_BUILD_FOR_LINUX_KERNEL
2715215990SjmallettEXPORT_SYMBOL(cvmx_usb_cancel);
2716215990Sjmallett#endif
2717210284Sjmallett
2718210284Sjmallett
2719210284Sjmallett/**
2720210284Sjmallett * Cancel all outstanding requests in a pipe. Logically all this
2721210284Sjmallett * does is call cvmx_usb_cancel() in a loop.
2722210284Sjmallett *
2723210284Sjmallett * @param state  USB device state populated by
2724210284Sjmallett *               cvmx_usb_initialize().
2725210284Sjmallett * @param pipe_handle
2726210284Sjmallett *               Pipe handle to cancel requests in.
2727210284Sjmallett *
2728210284Sjmallett * @return CVMX_USB_SUCCESS or a negative error code defined in
2729210284Sjmallett *         cvmx_usb_status_t.
2730210284Sjmallett */
2731210284Sjmallettcvmx_usb_status_t cvmx_usb_cancel_all(cvmx_usb_state_t *state, int pipe_handle)
2732210284Sjmallett{
2733210284Sjmallett    cvmx_usb_internal_state_t *usb = (cvmx_usb_internal_state_t*)state;
2734210284Sjmallett    cvmx_usb_pipe_t *pipe = usb->pipe + pipe_handle;
2735210284Sjmallett
2736210284Sjmallett    CVMX_USB_LOG_CALLED();
2737210284Sjmallett    CVMX_USB_LOG_PARAM("%p", state);
2738210284Sjmallett    CVMX_USB_LOG_PARAM("%d", pipe_handle);
2739210284Sjmallett    if (cvmx_unlikely((pipe_handle < 0) || (pipe_handle >= MAX_PIPES)))
2740210284Sjmallett        CVMX_USB_RETURN(CVMX_USB_INVALID_PARAM);
2741210284Sjmallett
2742210284Sjmallett    /* Fail if the pipe isn't open */
2743210284Sjmallett    if (cvmx_unlikely((pipe->flags & __CVMX_USB_PIPE_FLAGS_OPEN) == 0))
2744210284Sjmallett        CVMX_USB_RETURN(CVMX_USB_INVALID_PARAM);
2745210284Sjmallett
2746210284Sjmallett    /* Simply loop through and attempt to cancel each transaction */
2747210284Sjmallett    while (pipe->head)
2748210284Sjmallett    {
2749210284Sjmallett        cvmx_usb_status_t result = cvmx_usb_cancel(state, pipe_handle,
2750210284Sjmallett            __cvmx_usb_get_submit_handle(usb, pipe->head));
2751210284Sjmallett        if (cvmx_unlikely(result != CVMX_USB_SUCCESS))
2752210284Sjmallett            CVMX_USB_RETURN(result);
2753210284Sjmallett    }
2754210284Sjmallett    CVMX_USB_RETURN(CVMX_USB_SUCCESS);
2755210284Sjmallett}
2756215990Sjmallett#ifdef CVMX_BUILD_FOR_LINUX_KERNEL
2757215990SjmallettEXPORT_SYMBOL(cvmx_usb_cancel_all);
2758215990Sjmallett#endif
2759210284Sjmallett
2760210284Sjmallett
2761210284Sjmallett/**
2762210284Sjmallett * Close a pipe created with cvmx_usb_open_pipe().
2763210284Sjmallett *
2764210284Sjmallett * @param state  USB device state populated by
2765210284Sjmallett *               cvmx_usb_initialize().
2766210284Sjmallett * @param pipe_handle
2767210284Sjmallett *               Pipe handle to close.
2768210284Sjmallett *
2769210284Sjmallett * @return CVMX_USB_SUCCESS or a negative error code defined in
2770210284Sjmallett *         cvmx_usb_status_t. CVMX_USB_BUSY is returned if the
2771210284Sjmallett *         pipe has outstanding transfers.
2772210284Sjmallett */
2773210284Sjmallettcvmx_usb_status_t cvmx_usb_close_pipe(cvmx_usb_state_t *state, int pipe_handle)
2774210284Sjmallett{
2775210284Sjmallett    cvmx_usb_internal_state_t *usb = (cvmx_usb_internal_state_t*)state;
2776210284Sjmallett    cvmx_usb_pipe_t *pipe = usb->pipe + pipe_handle;
2777210284Sjmallett
2778210284Sjmallett    CVMX_USB_LOG_CALLED();
2779210284Sjmallett    CVMX_USB_LOG_PARAM("%p", state);
2780210284Sjmallett    CVMX_USB_LOG_PARAM("%d", pipe_handle);
2781210284Sjmallett    if (cvmx_unlikely((pipe_handle < 0) || (pipe_handle >= MAX_PIPES)))
2782210284Sjmallett        CVMX_USB_RETURN(CVMX_USB_INVALID_PARAM);
2783210284Sjmallett
2784210284Sjmallett    /* Fail if the pipe isn't open */
2785210284Sjmallett    if (cvmx_unlikely((pipe->flags & __CVMX_USB_PIPE_FLAGS_OPEN) == 0))
2786210284Sjmallett        CVMX_USB_RETURN(CVMX_USB_INVALID_PARAM);
2787210284Sjmallett
2788210284Sjmallett    /* Fail if the pipe has pending transactions */
2789210284Sjmallett    if (cvmx_unlikely(pipe->head))
2790210284Sjmallett        CVMX_USB_RETURN(CVMX_USB_BUSY);
2791210284Sjmallett
2792210284Sjmallett    pipe->flags = 0;
2793210284Sjmallett    __cvmx_usb_remove_pipe(&usb->idle_pipes, pipe);
2794210284Sjmallett    __cvmx_usb_append_pipe(&usb->free_pipes, pipe);
2795210284Sjmallett
2796210284Sjmallett    CVMX_USB_RETURN(CVMX_USB_SUCCESS);
2797210284Sjmallett}
2798215990Sjmallett#ifdef CVMX_BUILD_FOR_LINUX_KERNEL
2799215990SjmallettEXPORT_SYMBOL(cvmx_usb_close_pipe);
2800215990Sjmallett#endif
2801210284Sjmallett
2802210284Sjmallett
2803210284Sjmallett/**
2804210284Sjmallett * Register a function to be called when various USB events occur.
2805210284Sjmallett *
2806210284Sjmallett * @param state     USB device state populated by
2807210284Sjmallett *                  cvmx_usb_initialize().
2808210284Sjmallett * @param reason    Which event to register for.
2809210284Sjmallett * @param callback  Function to call when the event occurs.
2810210284Sjmallett * @param user_data User data parameter to the function.
2811210284Sjmallett *
2812210284Sjmallett * @return CVMX_USB_SUCCESS or a negative error code defined in
2813210284Sjmallett *         cvmx_usb_status_t.
2814210284Sjmallett */
2815210284Sjmallettcvmx_usb_status_t cvmx_usb_register_callback(cvmx_usb_state_t *state,
2816210284Sjmallett                                             cvmx_usb_callback_t reason,
2817210284Sjmallett                                             cvmx_usb_callback_func_t callback,
2818210284Sjmallett                                             void *user_data)
2819210284Sjmallett{
2820210284Sjmallett    cvmx_usb_internal_state_t *usb = (cvmx_usb_internal_state_t*)state;
2821210284Sjmallett
2822210284Sjmallett    CVMX_USB_LOG_CALLED();
2823210284Sjmallett    CVMX_USB_LOG_PARAM("%p", state);
2824210284Sjmallett    CVMX_USB_LOG_PARAM("%d", reason);
2825210284Sjmallett    CVMX_USB_LOG_PARAM("%p", callback);
2826210284Sjmallett    CVMX_USB_LOG_PARAM("%p", user_data);
2827210284Sjmallett    if (cvmx_unlikely(reason >= __CVMX_USB_CALLBACK_END))
2828210284Sjmallett        CVMX_USB_RETURN(CVMX_USB_INVALID_PARAM);
2829210284Sjmallett    if (cvmx_unlikely(!callback))
2830210284Sjmallett        CVMX_USB_RETURN(CVMX_USB_INVALID_PARAM);
2831210284Sjmallett
2832210284Sjmallett    usb->callback[reason] = callback;
2833210284Sjmallett    usb->callback_data[reason] = user_data;
2834210284Sjmallett
2835210284Sjmallett    CVMX_USB_RETURN(CVMX_USB_SUCCESS);
2836210284Sjmallett}
2837215990Sjmallett#ifdef CVMX_BUILD_FOR_LINUX_KERNEL
2838215990SjmallettEXPORT_SYMBOL(cvmx_usb_register_callback);
2839215990Sjmallett#endif
2840210284Sjmallett
2841210284Sjmallett
2842210284Sjmallett/**
2843210284Sjmallett * Get the current USB protocol level frame number. The frame
2844210284Sjmallett * number is always in the range of 0-0x7ff.
2845210284Sjmallett *
2846210284Sjmallett * @param state  USB device state populated by
2847210284Sjmallett *               cvmx_usb_initialize().
2848210284Sjmallett *
2849210284Sjmallett * @return USB frame number
2850210284Sjmallett */
2851210284Sjmallettint cvmx_usb_get_frame_number(cvmx_usb_state_t *state)
2852210284Sjmallett{
2853210284Sjmallett    int frame_number;
2854210284Sjmallett    cvmx_usb_internal_state_t *usb = (cvmx_usb_internal_state_t*)state;
2855215990Sjmallett    cvmx_usbcx_hfnum_t usbc_hfnum;
2856210284Sjmallett
2857210284Sjmallett    CVMX_USB_LOG_CALLED();
2858210284Sjmallett    CVMX_USB_LOG_PARAM("%p", state);
2859210284Sjmallett
2860215990Sjmallett    usbc_hfnum.u32 = __cvmx_usb_read_csr32(usb, CVMX_USBCX_HFNUM(usb->index));
2861215990Sjmallett    frame_number = usbc_hfnum.s.frnum;
2862210284Sjmallett
2863210284Sjmallett    CVMX_USB_RETURN(frame_number);
2864210284Sjmallett}
2865215990Sjmallett#ifdef CVMX_BUILD_FOR_LINUX_KERNEL
2866215990SjmallettEXPORT_SYMBOL(cvmx_usb_get_frame_number);
2867215990Sjmallett#endif
2868210284Sjmallett
2869210284Sjmallett
2870210284Sjmallett/**
2871210284Sjmallett * @INTERNAL
2872210284Sjmallett * Poll a channel for status
2873210284Sjmallett *
2874210284Sjmallett * @param usb     USB device
2875210284Sjmallett * @param channel Channel to poll
2876210284Sjmallett *
2877210284Sjmallett * @return Zero on success
2878210284Sjmallett */
2879210284Sjmallettstatic int __cvmx_usb_poll_channel(cvmx_usb_internal_state_t *usb, int channel)
2880210284Sjmallett{
2881210284Sjmallett    cvmx_usbcx_hcintx_t usbc_hcint;
2882210284Sjmallett    cvmx_usbcx_hctsizx_t usbc_hctsiz;
2883210284Sjmallett    cvmx_usbcx_hccharx_t usbc_hcchar;
2884210284Sjmallett    cvmx_usb_pipe_t *pipe;
2885210284Sjmallett    cvmx_usb_transaction_t *transaction;
2886210284Sjmallett    int bytes_this_transfer;
2887210284Sjmallett    int bytes_in_last_packet;
2888210284Sjmallett    int packets_processed;
2889210284Sjmallett    int buffer_space_left;
2890210284Sjmallett    CVMX_USB_LOG_CALLED();
2891210284Sjmallett    CVMX_USB_LOG_PARAM("%p", usb);
2892210284Sjmallett    CVMX_USB_LOG_PARAM("%d", channel);
2893210284Sjmallett
2894210284Sjmallett    /* Read the interrupt status bits for the channel */
2895210284Sjmallett    usbc_hcint.u32 = __cvmx_usb_read_csr32(usb, CVMX_USBCX_HCINTX(channel, usb->index));
2896210284Sjmallett
2897215990Sjmallett#if 0
2898215990Sjmallett    cvmx_dprintf("Channel %d%s%s%s%s%s%s%s%s%s%s%s\n", channel,
2899215990Sjmallett        (usbc_hcint.s.datatglerr) ? " DATATGLERR" : "",
2900215990Sjmallett        (usbc_hcint.s.frmovrun) ? " FRMOVRUN" : "",
2901215990Sjmallett        (usbc_hcint.s.bblerr) ? " BBLERR" : "",
2902215990Sjmallett        (usbc_hcint.s.xacterr) ? " XACTERR" : "",
2903215990Sjmallett        (usbc_hcint.s.nyet) ? " NYET" : "",
2904215990Sjmallett        (usbc_hcint.s.ack) ? " ACK" : "",
2905215990Sjmallett        (usbc_hcint.s.nak) ? " NAK" : "",
2906215990Sjmallett        (usbc_hcint.s.stall) ? " STALL" : "",
2907215990Sjmallett        (usbc_hcint.s.ahberr) ? " AHBERR" : "",
2908215990Sjmallett        (usbc_hcint.s.chhltd) ? " CHHLTD" : "",
2909215990Sjmallett        (usbc_hcint.s.xfercompl) ? " XFERCOMPL" : "");
2910215990Sjmallett#endif
2911210284Sjmallett
2912215990Sjmallett    if (usb->init_flags & CVMX_USB_INITIALIZE_FLAGS_NO_DMA)
2913215990Sjmallett    {
2914215990Sjmallett        usbc_hcchar.u32 = __cvmx_usb_read_csr32(usb, CVMX_USBCX_HCCHARX(channel, usb->index));
2915210284Sjmallett
2916215990Sjmallett        if (usbc_hcchar.s.chena && usbc_hcchar.s.chdis)
2917215990Sjmallett        {
2918215990Sjmallett            /* There seems to be a bug in CN31XX which can cause interrupt
2919215990Sjmallett                IN transfers to get stuck until we do a write of HCCHARX
2920215990Sjmallett                without changing things */
2921215990Sjmallett            __cvmx_usb_write_csr32(usb, CVMX_USBCX_HCCHARX(channel, usb->index), usbc_hcchar.u32);
2922215990Sjmallett            CVMX_USB_RETURN(0);
2923215990Sjmallett        }
2924215990Sjmallett
2925215990Sjmallett        /* In non DMA mode the channels don't halt themselves. We need to
2926215990Sjmallett            manually disable channels that are left running */
2927215990Sjmallett        if (!usbc_hcint.s.chhltd)
2928215990Sjmallett        {
2929215990Sjmallett            if (usbc_hcchar.s.chena)
2930215990Sjmallett            {
2931215990Sjmallett                cvmx_usbcx_hcintmskx_t hcintmsk;
2932215990Sjmallett                /* Disable all interrupts except CHHLTD */
2933215990Sjmallett                hcintmsk.u32 = 0;
2934215990Sjmallett                hcintmsk.s.chhltdmsk = 1;
2935215990Sjmallett                __cvmx_usb_write_csr32(usb, CVMX_USBCX_HCINTMSKX(channel, usb->index), hcintmsk.u32);
2936215990Sjmallett                usbc_hcchar.s.chdis = 1;
2937215990Sjmallett                __cvmx_usb_write_csr32(usb, CVMX_USBCX_HCCHARX(channel, usb->index), usbc_hcchar.u32);
2938215990Sjmallett                CVMX_USB_RETURN(0);
2939215990Sjmallett            }
2940215990Sjmallett            else if (usbc_hcint.s.xfercompl)
2941215990Sjmallett            {
2942215990Sjmallett                /* Successful IN/OUT with transfer complete. Channel halt isn't needed */
2943215990Sjmallett            }
2944215990Sjmallett            else
2945215990Sjmallett            {
2946215990Sjmallett                cvmx_dprintf("USB%d: Channel %d interrupt without halt\n", usb->index, channel);
2947215990Sjmallett                CVMX_USB_RETURN(0);
2948215990Sjmallett            }
2949215990Sjmallett        }
2950215990Sjmallett    }
2951215990Sjmallett    else
2952215990Sjmallett    {
2953215990Sjmallett        /* There is are no interrupts that we need to process when the channel is
2954215990Sjmallett            still running */
2955215990Sjmallett        if (!usbc_hcint.s.chhltd)
2956215990Sjmallett            CVMX_USB_RETURN(0);
2957215990Sjmallett    }
2958215990Sjmallett
2959215990Sjmallett    /* Disable the channel interrupts now that it is done */
2960215990Sjmallett    __cvmx_usb_write_csr32(usb, CVMX_USBCX_HCINTMSKX(channel, usb->index), 0);
2961210284Sjmallett    usb->idle_hardware_channels |= (1<<channel);
2962210284Sjmallett
2963210284Sjmallett    /* Make sure this channel is tied to a valid pipe */
2964210284Sjmallett    pipe = usb->pipe_for_channel[channel];
2965210284Sjmallett    CVMX_PREFETCH(pipe, 0);
2966210284Sjmallett    CVMX_PREFETCH(pipe, 128);
2967210284Sjmallett    if (!pipe)
2968210284Sjmallett        CVMX_USB_RETURN(0);
2969210284Sjmallett    transaction = pipe->head;
2970210284Sjmallett    CVMX_PREFETCH0(transaction);
2971210284Sjmallett
2972210284Sjmallett    /* Disconnect this pipe from the HW channel. Later the schedule function will
2973210284Sjmallett        figure out which pipe needs to go */
2974210284Sjmallett    usb->pipe_for_channel[channel] = NULL;
2975210284Sjmallett    pipe->flags &= ~__CVMX_USB_PIPE_FLAGS_SCHEDULED;
2976210284Sjmallett
2977210284Sjmallett    /* Read the channel config info so we can figure out how much data
2978210284Sjmallett        transfered */
2979210284Sjmallett    usbc_hcchar.u32 = __cvmx_usb_read_csr32(usb, CVMX_USBCX_HCCHARX(channel, usb->index));
2980210284Sjmallett    usbc_hctsiz.u32 = __cvmx_usb_read_csr32(usb, CVMX_USBCX_HCTSIZX(channel, usb->index));
2981210284Sjmallett
2982215990Sjmallett    /* Calculating the number of bytes successfully transferred is dependent on
2983210284Sjmallett        the transfer direction */
2984210284Sjmallett    packets_processed = transaction->pktcnt - usbc_hctsiz.s.pktcnt;
2985210284Sjmallett    if (usbc_hcchar.s.epdir)
2986210284Sjmallett    {
2987210284Sjmallett        /* IN transactions are easy. For every byte received the hardware
2988210284Sjmallett            decrements xfersize. All we need to do is subtract the current
2989210284Sjmallett            value of xfersize from its starting value and we know how many
2990210284Sjmallett            bytes were written to the buffer */
2991210284Sjmallett        bytes_this_transfer = transaction->xfersize - usbc_hctsiz.s.xfersize;
2992210284Sjmallett    }
2993210284Sjmallett    else
2994210284Sjmallett    {
2995210284Sjmallett        /* OUT transaction don't decrement xfersize. Instead pktcnt is
2996210284Sjmallett            decremented on every successful packet send. The hardware does
2997210284Sjmallett            this when it receives an ACK, or NYET. If it doesn't
2998210284Sjmallett            receive one of these responses pktcnt doesn't change */
2999210284Sjmallett        bytes_this_transfer = packets_processed * usbc_hcchar.s.mps;
3000210284Sjmallett        /* The last packet may not be a full transfer if we didn't have
3001210284Sjmallett            enough data */
3002210284Sjmallett        if (bytes_this_transfer > transaction->xfersize)
3003210284Sjmallett            bytes_this_transfer = transaction->xfersize;
3004210284Sjmallett    }
3005210284Sjmallett    /* Figure out how many bytes were in the last packet of the transfer */
3006210284Sjmallett    if (packets_processed)
3007210284Sjmallett        bytes_in_last_packet = bytes_this_transfer - (packets_processed-1) * usbc_hcchar.s.mps;
3008210284Sjmallett    else
3009210284Sjmallett        bytes_in_last_packet = bytes_this_transfer;
3010210284Sjmallett
3011210284Sjmallett    /* As a special case, setup transactions output the setup header, not
3012210284Sjmallett        the user's data. For this reason we don't count setup data as bytes
3013215990Sjmallett        transferred */
3014210284Sjmallett    if ((transaction->stage == CVMX_USB_STAGE_SETUP) ||
3015210284Sjmallett        (transaction->stage == CVMX_USB_STAGE_SETUP_SPLIT_COMPLETE))
3016210284Sjmallett        bytes_this_transfer = 0;
3017210284Sjmallett
3018210284Sjmallett    /* Optional debug output */
3019210284Sjmallett    if (cvmx_unlikely((usb->init_flags & CVMX_USB_INITIALIZE_FLAGS_DEBUG_TRANSFERS) ||
3020210284Sjmallett        (pipe->flags & CVMX_USB_PIPE_FLAGS_DEBUG_TRANSFERS)))
3021210284Sjmallett        cvmx_dprintf("%s: Channel %d halted. Pipe %d transaction %d stage %d bytes=%d\n",
3022210284Sjmallett                     __FUNCTION__, channel,
3023210284Sjmallett                     __cvmx_usb_get_pipe_handle(usb, pipe),
3024210284Sjmallett                     __cvmx_usb_get_submit_handle(usb, transaction),
3025210284Sjmallett                     transaction->stage, bytes_this_transfer);
3026210284Sjmallett
3027215990Sjmallett    /* Add the bytes transferred to the running total. It is important that
3028210284Sjmallett        bytes_this_transfer doesn't count any data that needs to be
3029210284Sjmallett        retransmitted */
3030210284Sjmallett    transaction->actual_bytes += bytes_this_transfer;
3031210284Sjmallett    if (transaction->type == CVMX_USB_TRANSFER_ISOCHRONOUS)
3032210284Sjmallett        buffer_space_left = transaction->iso_packets[0].length - transaction->actual_bytes;
3033210284Sjmallett    else
3034210284Sjmallett        buffer_space_left = transaction->buffer_length - transaction->actual_bytes;
3035210284Sjmallett
3036210284Sjmallett    /* We need to remember the PID toggle state for the next transaction. The
3037210284Sjmallett        hardware already updated it for the next transaction */
3038210284Sjmallett    pipe->pid_toggle = !(usbc_hctsiz.s.pid == 0);
3039210284Sjmallett
3040210284Sjmallett    /* For high speed bulk out, assume the next transaction will need to do a
3041210284Sjmallett        ping before proceeding. If this isn't true the ACK processing below
3042210284Sjmallett        will clear this flag */
3043210284Sjmallett    if ((pipe->device_speed == CVMX_USB_SPEED_HIGH) &&
3044210284Sjmallett        (pipe->transfer_type == CVMX_USB_TRANSFER_BULK) &&
3045210284Sjmallett        (pipe->transfer_dir == CVMX_USB_DIRECTION_OUT))
3046210284Sjmallett        pipe->flags |= __CVMX_USB_PIPE_FLAGS_NEED_PING;
3047210284Sjmallett
3048210284Sjmallett    if (usbc_hcint.s.stall)
3049210284Sjmallett    {
3050210284Sjmallett        /* STALL as a response means this transaction cannot be completed
3051210284Sjmallett            because the device can't process transactions. Tell the user. Any
3052215990Sjmallett            data that was transferred will be counted on the actual bytes
3053215990Sjmallett            transferred */
3054210284Sjmallett        pipe->pid_toggle = 0;
3055210284Sjmallett        __cvmx_usb_perform_complete(usb, pipe, transaction, CVMX_USB_COMPLETE_STALL);
3056210284Sjmallett    }
3057210284Sjmallett    else if (usbc_hcint.s.xacterr)
3058210284Sjmallett    {
3059210284Sjmallett        /* We know at least one packet worked if we get a ACK or NAK. Reset the retry counter */
3060210284Sjmallett        if (usbc_hcint.s.nak || usbc_hcint.s.ack)
3061210284Sjmallett            transaction->retries = 0;
3062210284Sjmallett        transaction->retries++;
3063210284Sjmallett        if (transaction->retries > MAX_RETRIES)
3064210284Sjmallett        {
3065210284Sjmallett            /* XactErr as a response means the device signaled something wrong with
3066210284Sjmallett                the transfer. For example, PID toggle errors cause these */
3067210284Sjmallett            __cvmx_usb_perform_complete(usb, pipe, transaction, CVMX_USB_COMPLETE_XACTERR);
3068210284Sjmallett        }
3069210284Sjmallett        else
3070210284Sjmallett        {
3071215990Sjmallett            /* If this was a split then clear our split in progress marker */
3072215990Sjmallett            if (usb->active_split == transaction)
3073215990Sjmallett                usb->active_split = NULL;
3074210284Sjmallett            /* Rewind to the beginning of the transaction by anding off the
3075210284Sjmallett                split complete bit */
3076210284Sjmallett            transaction->stage &= ~1;
3077210284Sjmallett            pipe->split_sc_frame = -1;
3078215990Sjmallett            pipe->next_tx_frame += pipe->interval;
3079215990Sjmallett            if (pipe->next_tx_frame < usb->frame_number)
3080215990Sjmallett                pipe->next_tx_frame = usb->frame_number + pipe->interval -
3081215990Sjmallett                    (usb->frame_number - pipe->next_tx_frame) % pipe->interval;
3082210284Sjmallett        }
3083210284Sjmallett    }
3084210284Sjmallett    else if (usbc_hcint.s.bblerr)
3085210284Sjmallett    {
3086210284Sjmallett        /* Babble Error (BblErr) */
3087210284Sjmallett        __cvmx_usb_perform_complete(usb, pipe, transaction, CVMX_USB_COMPLETE_BABBLEERR);
3088210284Sjmallett    }
3089215990Sjmallett    else if (usbc_hcint.s.datatglerr)
3090210284Sjmallett    {
3091215990Sjmallett        /* We'll retry the exact same transaction again */
3092215990Sjmallett        transaction->retries++;
3093210284Sjmallett    }
3094210284Sjmallett    else if (usbc_hcint.s.nyet)
3095210284Sjmallett    {
3096210284Sjmallett        /* NYET as a response is only allowed in three cases: as a response to
3097210284Sjmallett            a ping, as a response to a split transaction, and as a response to
3098210284Sjmallett            a bulk out. The ping case is handled by hardware, so we only have
3099210284Sjmallett            splits and bulk out */
3100210284Sjmallett        if (!__cvmx_usb_pipe_needs_split(usb, pipe))
3101210284Sjmallett        {
3102210284Sjmallett            transaction->retries = 0;
3103210284Sjmallett            /* If there is more data to go then we need to try again. Otherwise
3104210284Sjmallett                this transaction is complete */
3105210284Sjmallett            if ((buffer_space_left == 0) || (bytes_in_last_packet < pipe->max_packet))
3106210284Sjmallett                __cvmx_usb_perform_complete(usb, pipe, transaction, CVMX_USB_COMPLETE_SUCCESS);
3107210284Sjmallett        }
3108210284Sjmallett        else
3109210284Sjmallett        {
3110210284Sjmallett            /* Split transactions retry the split complete 4 times then rewind
3111210284Sjmallett                to the start split and do the entire transactions again */
3112210284Sjmallett            transaction->retries++;
3113210284Sjmallett            if ((transaction->retries & 0x3) == 0)
3114210284Sjmallett            {
3115210284Sjmallett                /* Rewind to the beginning of the transaction by anding off the
3116210284Sjmallett                    split complete bit */
3117210284Sjmallett                transaction->stage &= ~1;
3118210284Sjmallett                pipe->split_sc_frame = -1;
3119210284Sjmallett            }
3120210284Sjmallett        }
3121210284Sjmallett    }
3122210284Sjmallett    else if (usbc_hcint.s.ack)
3123210284Sjmallett    {
3124210284Sjmallett        transaction->retries = 0;
3125210284Sjmallett        /* The ACK bit can only be checked after the other error bits. This is
3126210284Sjmallett            because a multi packet transfer may succeed in a number of packets
3127210284Sjmallett            and then get a different response on the last packet. In this case
3128210284Sjmallett            both ACK and the last response bit will be set. If none of the
3129210284Sjmallett            other response bits is set, then the last packet must have been an
3130210284Sjmallett            ACK */
3131210284Sjmallett
3132210284Sjmallett        /* Since we got an ACK, we know we don't need to do a ping on this
3133210284Sjmallett            pipe */
3134210284Sjmallett        pipe->flags &= ~__CVMX_USB_PIPE_FLAGS_NEED_PING;
3135210284Sjmallett
3136210284Sjmallett        switch (transaction->type)
3137210284Sjmallett        {
3138210284Sjmallett            case CVMX_USB_TRANSFER_CONTROL:
3139210284Sjmallett                switch (transaction->stage)
3140210284Sjmallett                {
3141210284Sjmallett                    case CVMX_USB_STAGE_NON_CONTROL:
3142210284Sjmallett                    case CVMX_USB_STAGE_NON_CONTROL_SPLIT_COMPLETE:
3143210284Sjmallett                        /* This should be impossible */
3144210284Sjmallett                        __cvmx_usb_perform_complete(usb, pipe, transaction, CVMX_USB_COMPLETE_ERROR);
3145210284Sjmallett                        break;
3146210284Sjmallett                    case CVMX_USB_STAGE_SETUP:
3147210284Sjmallett                        pipe->pid_toggle = 1;
3148210284Sjmallett                        if (__cvmx_usb_pipe_needs_split(usb, pipe))
3149210284Sjmallett                            transaction->stage = CVMX_USB_STAGE_SETUP_SPLIT_COMPLETE;
3150210284Sjmallett                        else
3151210284Sjmallett                        {
3152210284Sjmallett                            cvmx_usb_control_header_t *header = cvmx_phys_to_ptr(transaction->control_header);
3153210284Sjmallett                            if (header->s.length)
3154210284Sjmallett                                transaction->stage = CVMX_USB_STAGE_DATA;
3155210284Sjmallett                            else
3156210284Sjmallett                                transaction->stage = CVMX_USB_STAGE_STATUS;
3157210284Sjmallett                        }
3158210284Sjmallett                        break;
3159210284Sjmallett                    case CVMX_USB_STAGE_SETUP_SPLIT_COMPLETE:
3160210284Sjmallett                        {
3161210284Sjmallett                            cvmx_usb_control_header_t *header = cvmx_phys_to_ptr(transaction->control_header);
3162210284Sjmallett                            if (header->s.length)
3163210284Sjmallett                                transaction->stage = CVMX_USB_STAGE_DATA;
3164210284Sjmallett                            else
3165210284Sjmallett                                transaction->stage = CVMX_USB_STAGE_STATUS;
3166210284Sjmallett                        }
3167210284Sjmallett                        break;
3168210284Sjmallett                    case CVMX_USB_STAGE_DATA:
3169210284Sjmallett                        if (__cvmx_usb_pipe_needs_split(usb, pipe))
3170215990Sjmallett                        {
3171210284Sjmallett                            transaction->stage = CVMX_USB_STAGE_DATA_SPLIT_COMPLETE;
3172215990Sjmallett                            /* For setup OUT data that are splits, the hardware
3173215990Sjmallett                                doesn't appear to count transferred data. Here
3174215990Sjmallett                                we manually update the data transferred */
3175215990Sjmallett                            if (!usbc_hcchar.s.epdir)
3176215990Sjmallett                            {
3177215990Sjmallett                                if (buffer_space_left < pipe->max_packet)
3178215990Sjmallett                                    transaction->actual_bytes += buffer_space_left;
3179215990Sjmallett                                else
3180215990Sjmallett                                    transaction->actual_bytes += pipe->max_packet;
3181215990Sjmallett                            }
3182215990Sjmallett                        }
3183210284Sjmallett                        else if ((buffer_space_left == 0) || (bytes_in_last_packet < pipe->max_packet))
3184210284Sjmallett                        {
3185210284Sjmallett                            pipe->pid_toggle = 1;
3186210284Sjmallett                            transaction->stage = CVMX_USB_STAGE_STATUS;
3187210284Sjmallett                        }
3188210284Sjmallett                        break;
3189210284Sjmallett                    case CVMX_USB_STAGE_DATA_SPLIT_COMPLETE:
3190210284Sjmallett                        if ((buffer_space_left == 0) || (bytes_in_last_packet < pipe->max_packet))
3191210284Sjmallett                        {
3192210284Sjmallett                            pipe->pid_toggle = 1;
3193210284Sjmallett                            transaction->stage = CVMX_USB_STAGE_STATUS;
3194210284Sjmallett                        }
3195210284Sjmallett                        else
3196210284Sjmallett                        {
3197210284Sjmallett                            transaction->stage = CVMX_USB_STAGE_DATA;
3198210284Sjmallett                        }
3199210284Sjmallett                        break;
3200210284Sjmallett                    case CVMX_USB_STAGE_STATUS:
3201210284Sjmallett                        if (__cvmx_usb_pipe_needs_split(usb, pipe))
3202210284Sjmallett                            transaction->stage = CVMX_USB_STAGE_STATUS_SPLIT_COMPLETE;
3203210284Sjmallett                        else
3204210284Sjmallett                            __cvmx_usb_perform_complete(usb, pipe, transaction, CVMX_USB_COMPLETE_SUCCESS);
3205210284Sjmallett                        break;
3206210284Sjmallett                    case CVMX_USB_STAGE_STATUS_SPLIT_COMPLETE:
3207210284Sjmallett                        __cvmx_usb_perform_complete(usb, pipe, transaction, CVMX_USB_COMPLETE_SUCCESS);
3208210284Sjmallett                        break;
3209210284Sjmallett                }
3210210284Sjmallett                break;
3211210284Sjmallett            case CVMX_USB_TRANSFER_BULK:
3212210284Sjmallett            case CVMX_USB_TRANSFER_INTERRUPT:
3213210284Sjmallett                /* The only time a bulk transfer isn't complete when
3214210284Sjmallett                    it finishes with an ACK is during a split transaction. For
3215210284Sjmallett                    splits we need to continue the transfer if more data is
3216210284Sjmallett                    needed */
3217210284Sjmallett                if (__cvmx_usb_pipe_needs_split(usb, pipe))
3218210284Sjmallett                {
3219210284Sjmallett                    if (transaction->stage == CVMX_USB_STAGE_NON_CONTROL)
3220210284Sjmallett                        transaction->stage = CVMX_USB_STAGE_NON_CONTROL_SPLIT_COMPLETE;
3221210284Sjmallett                    else
3222210284Sjmallett                    {
3223210284Sjmallett                        if (buffer_space_left && (bytes_in_last_packet == pipe->max_packet))
3224210284Sjmallett                            transaction->stage = CVMX_USB_STAGE_NON_CONTROL;
3225210284Sjmallett                        else
3226210284Sjmallett                        {
3227210284Sjmallett                            if (transaction->type == CVMX_USB_TRANSFER_INTERRUPT)
3228215990Sjmallett                                pipe->next_tx_frame += pipe->interval;
3229210284Sjmallett                            __cvmx_usb_perform_complete(usb, pipe, transaction, CVMX_USB_COMPLETE_SUCCESS);
3230210284Sjmallett                        }
3231210284Sjmallett                    }
3232210284Sjmallett                }
3233210284Sjmallett                else
3234210284Sjmallett                {
3235210284Sjmallett                    if ((pipe->device_speed == CVMX_USB_SPEED_HIGH) &&
3236210284Sjmallett                        (pipe->transfer_type == CVMX_USB_TRANSFER_BULK) &&
3237210284Sjmallett                        (pipe->transfer_dir == CVMX_USB_DIRECTION_OUT) &&
3238210284Sjmallett                        (usbc_hcint.s.nak))
3239210284Sjmallett                        pipe->flags |= __CVMX_USB_PIPE_FLAGS_NEED_PING;
3240210284Sjmallett                    if (!buffer_space_left || (bytes_in_last_packet < pipe->max_packet))
3241210284Sjmallett                    {
3242210284Sjmallett                        if (transaction->type == CVMX_USB_TRANSFER_INTERRUPT)
3243215990Sjmallett                            pipe->next_tx_frame += pipe->interval;
3244210284Sjmallett                        __cvmx_usb_perform_complete(usb, pipe, transaction, CVMX_USB_COMPLETE_SUCCESS);
3245210284Sjmallett                    }
3246210284Sjmallett                }
3247210284Sjmallett                break;
3248210284Sjmallett            case CVMX_USB_TRANSFER_ISOCHRONOUS:
3249210284Sjmallett                if (__cvmx_usb_pipe_needs_split(usb, pipe))
3250210284Sjmallett                {
3251210284Sjmallett                    /* ISOCHRONOUS OUT splits don't require a complete split stage.
3252210284Sjmallett                        Instead they use a sequence of begin OUT splits to transfer
3253210284Sjmallett                        the data 188 bytes at a time. Once the transfer is complete,
3254210284Sjmallett                        the pipe sleeps until the next schedule interval */
3255210284Sjmallett                    if (pipe->transfer_dir == CVMX_USB_DIRECTION_OUT)
3256210284Sjmallett                    {
3257210284Sjmallett                        /* If no space left or this wasn't a max size packet then
3258210284Sjmallett                            this transfer is complete. Otherwise start it again
3259210284Sjmallett                            to send the next 188 bytes */
3260210284Sjmallett                        if (!buffer_space_left || (bytes_this_transfer < 188))
3261215990Sjmallett                        {
3262215990Sjmallett                            pipe->next_tx_frame += pipe->interval;
3263210284Sjmallett                            __cvmx_usb_perform_complete(usb, pipe, transaction, CVMX_USB_COMPLETE_SUCCESS);
3264215990Sjmallett                        }
3265210284Sjmallett                    }
3266210284Sjmallett                    else
3267210284Sjmallett                    {
3268210284Sjmallett                        if (transaction->stage == CVMX_USB_STAGE_NON_CONTROL_SPLIT_COMPLETE)
3269210284Sjmallett                        {
3270215990Sjmallett                            /* We are in the incoming data phase. Keep getting
3271210284Sjmallett                                data until we run out of space or get a small
3272210284Sjmallett                                packet */
3273210284Sjmallett                            if ((buffer_space_left == 0) || (bytes_in_last_packet < pipe->max_packet))
3274210284Sjmallett                            {
3275215990Sjmallett                                pipe->next_tx_frame += pipe->interval;
3276210284Sjmallett                                __cvmx_usb_perform_complete(usb, pipe, transaction, CVMX_USB_COMPLETE_SUCCESS);
3277210284Sjmallett                            }
3278210284Sjmallett                        }
3279210284Sjmallett                        else
3280210284Sjmallett                            transaction->stage = CVMX_USB_STAGE_NON_CONTROL_SPLIT_COMPLETE;
3281210284Sjmallett                    }
3282210284Sjmallett                }
3283210284Sjmallett                else
3284210284Sjmallett                {
3285215990Sjmallett                    pipe->next_tx_frame += pipe->interval;
3286210284Sjmallett                    __cvmx_usb_perform_complete(usb, pipe, transaction, CVMX_USB_COMPLETE_SUCCESS);
3287210284Sjmallett                }
3288210284Sjmallett                break;
3289210284Sjmallett        }
3290210284Sjmallett    }
3291210284Sjmallett    else if (usbc_hcint.s.nak)
3292210284Sjmallett    {
3293215990Sjmallett        /* If this was a split then clear our split in progress marker */
3294215990Sjmallett        if (usb->active_split == transaction)
3295215990Sjmallett            usb->active_split = NULL;
3296210284Sjmallett        /* NAK as a response means the device couldn't accept the transaction,
3297210284Sjmallett            but it should be retried in the future. Rewind to the beginning of
3298210284Sjmallett            the transaction by anding off the split complete bit. Retry in the
3299210284Sjmallett            next interval */
3300210284Sjmallett        transaction->retries = 0;
3301210284Sjmallett        transaction->stage &= ~1;
3302215990Sjmallett        pipe->next_tx_frame += pipe->interval;
3303215990Sjmallett        if (pipe->next_tx_frame < usb->frame_number)
3304215990Sjmallett            pipe->next_tx_frame = usb->frame_number + pipe->interval -
3305215990Sjmallett                (usb->frame_number - pipe->next_tx_frame) % pipe->interval;
3306210284Sjmallett    }
3307210284Sjmallett    else
3308210284Sjmallett    {
3309215990Sjmallett        cvmx_usb_port_status_t port;
3310215990Sjmallett        port = cvmx_usb_get_status((cvmx_usb_state_t *)usb);
3311215990Sjmallett        if (port.port_enabled)
3312215990Sjmallett        {
3313215990Sjmallett            /* We'll retry the exact same transaction again */
3314215990Sjmallett            transaction->retries++;
3315215990Sjmallett        }
3316215990Sjmallett        else
3317215990Sjmallett        {
3318215990Sjmallett            /* We get channel halted interrupts with no result bits sets when the
3319215990Sjmallett                cable is unplugged */
3320215990Sjmallett            __cvmx_usb_perform_complete(usb, pipe, transaction, CVMX_USB_COMPLETE_ERROR);
3321215990Sjmallett        }
3322210284Sjmallett    }
3323210284Sjmallett    CVMX_USB_RETURN(0);
3324210284Sjmallett}
3325210284Sjmallett
3326210284Sjmallett
3327210284Sjmallett/**
3328210284Sjmallett * Poll the USB block for status and call all needed callback
3329210284Sjmallett * handlers. This function is meant to be called in the interrupt
3330210284Sjmallett * handler for the USB controller. It can also be called
3331210284Sjmallett * periodically in a loop for non-interrupt based operation.
3332210284Sjmallett *
3333210284Sjmallett * @param state  USB device state populated by
3334210284Sjmallett *               cvmx_usb_initialize().
3335210284Sjmallett *
3336210284Sjmallett * @return CVMX_USB_SUCCESS or a negative error code defined in
3337210284Sjmallett *         cvmx_usb_status_t.
3338210284Sjmallett */
3339210284Sjmallettcvmx_usb_status_t cvmx_usb_poll(cvmx_usb_state_t *state)
3340210284Sjmallett{
3341215990Sjmallett    cvmx_usbcx_hfnum_t usbc_hfnum;
3342210284Sjmallett    cvmx_usbcx_gintsts_t usbc_gintsts;
3343210284Sjmallett    cvmx_usb_internal_state_t *usb = (cvmx_usb_internal_state_t*)state;
3344210284Sjmallett
3345210284Sjmallett    CVMX_PREFETCH(usb, 0);
3346210284Sjmallett    CVMX_PREFETCH(usb, 1*128);
3347210284Sjmallett    CVMX_PREFETCH(usb, 2*128);
3348210284Sjmallett    CVMX_PREFETCH(usb, 3*128);
3349210284Sjmallett    CVMX_PREFETCH(usb, 4*128);
3350210284Sjmallett
3351210284Sjmallett    CVMX_USB_LOG_CALLED();
3352210284Sjmallett    CVMX_USB_LOG_PARAM("%p", state);
3353210284Sjmallett
3354215990Sjmallett    /* Update the frame counter */
3355215990Sjmallett    usbc_hfnum.u32 = __cvmx_usb_read_csr32(usb, CVMX_USBCX_HFNUM(usb->index));
3356215990Sjmallett    if ((usb->frame_number&0x3fff) > usbc_hfnum.s.frnum)
3357215990Sjmallett        usb->frame_number += 0x4000;
3358215990Sjmallett    usb->frame_number &= ~0x3fffull;
3359215990Sjmallett    usb->frame_number |= usbc_hfnum.s.frnum;
3360215990Sjmallett
3361210284Sjmallett    /* Read the pending interrupts */
3362210284Sjmallett    usbc_gintsts.u32 = __cvmx_usb_read_csr32(usb, CVMX_USBCX_GINTSTS(usb->index));
3363210284Sjmallett
3364215990Sjmallett    /* Clear the interrupts now that we know about them */
3365215990Sjmallett    __cvmx_usb_write_csr32(usb, CVMX_USBCX_GINTSTS(usb->index), usbc_gintsts.u32);
3366215990Sjmallett
3367215990Sjmallett    if (usbc_gintsts.s.rxflvl)
3368210284Sjmallett    {
3369215990Sjmallett        /* RxFIFO Non-Empty (RxFLvl)
3370215990Sjmallett            Indicates that there is at least one packet pending to be read
3371215990Sjmallett            from the RxFIFO. */
3372215990Sjmallett        /* In DMA mode this is handled by hardware */
3373215990Sjmallett        if (usb->init_flags & CVMX_USB_INITIALIZE_FLAGS_NO_DMA)
3374215990Sjmallett            __cvmx_usb_poll_rx_fifo(usb);
3375210284Sjmallett    }
3376215990Sjmallett    if (usbc_gintsts.s.ptxfemp || usbc_gintsts.s.nptxfemp)
3377210284Sjmallett    {
3378215990Sjmallett        /* Fill the Tx FIFOs when not in DMA mode */
3379215990Sjmallett        if (usb->init_flags & CVMX_USB_INITIALIZE_FLAGS_NO_DMA)
3380215990Sjmallett            __cvmx_usb_poll_tx_fifo(usb);
3381210284Sjmallett    }
3382210284Sjmallett    if (usbc_gintsts.s.disconnint || usbc_gintsts.s.prtint)
3383210284Sjmallett    {
3384210284Sjmallett        cvmx_usbcx_hprt_t usbc_hprt;
3385210284Sjmallett        /* Disconnect Detected Interrupt (DisconnInt)
3386210284Sjmallett            Asserted when a device disconnect is detected. */
3387210284Sjmallett
3388210284Sjmallett        /* Host Port Interrupt (PrtInt)
3389210284Sjmallett            The core sets this bit to indicate a change in port status of one
3390210284Sjmallett            of the O2P USB core ports in Host mode. The application must
3391210284Sjmallett            read the Host Port Control and Status (HPRT) register to
3392210284Sjmallett            determine the exact event that caused this interrupt. The
3393210284Sjmallett            application must clear the appropriate status bit in the Host Port
3394210284Sjmallett            Control and Status register to clear this bit. */
3395210284Sjmallett
3396210284Sjmallett        /* Call the user's port callback */
3397210284Sjmallett        __cvmx_usb_perform_callback(usb, NULL, NULL,
3398210284Sjmallett                                    CVMX_USB_CALLBACK_PORT_CHANGED,
3399210284Sjmallett                                    CVMX_USB_COMPLETE_SUCCESS);
3400210284Sjmallett        /* Clear the port change bits */
3401210284Sjmallett        usbc_hprt.u32 = __cvmx_usb_read_csr32(usb, CVMX_USBCX_HPRT(usb->index));
3402210284Sjmallett        usbc_hprt.s.prtena = 0;
3403210284Sjmallett        __cvmx_usb_write_csr32(usb, CVMX_USBCX_HPRT(usb->index), usbc_hprt.u32);
3404210284Sjmallett    }
3405210284Sjmallett    if (usbc_gintsts.s.hchint)
3406210284Sjmallett    {
3407210284Sjmallett        /* Host Channels Interrupt (HChInt)
3408210284Sjmallett            The core sets this bit to indicate that an interrupt is pending on
3409210284Sjmallett            one of the channels of the core (in Host mode). The application
3410210284Sjmallett            must read the Host All Channels Interrupt (HAINT) register to
3411210284Sjmallett            determine the exact number of the channel on which the
3412210284Sjmallett            interrupt occurred, and then read the corresponding Host
3413210284Sjmallett            Channel-n Interrupt (HCINTn) register to determine the exact
3414210284Sjmallett            cause of the interrupt. The application must clear the
3415210284Sjmallett            appropriate status bit in the HCINTn register to clear this bit. */
3416210284Sjmallett        cvmx_usbcx_haint_t usbc_haint;
3417210284Sjmallett        usbc_haint.u32 = __cvmx_usb_read_csr32(usb, CVMX_USBCX_HAINT(usb->index));
3418210284Sjmallett        while (usbc_haint.u32)
3419210284Sjmallett        {
3420210284Sjmallett            int channel;
3421210284Sjmallett            CVMX_CLZ(channel, usbc_haint.u32);
3422210284Sjmallett            channel = 31 - channel;
3423210284Sjmallett            __cvmx_usb_poll_channel(usb, channel);
3424210284Sjmallett            usbc_haint.u32 ^= 1<<channel;
3425210284Sjmallett        }
3426210284Sjmallett    }
3427210284Sjmallett
3428210284Sjmallett    __cvmx_usb_schedule(usb, usbc_gintsts.s.sof);
3429210284Sjmallett
3430210284Sjmallett    CVMX_USB_RETURN(CVMX_USB_SUCCESS);
3431210284Sjmallett}
3432215990Sjmallett#ifdef CVMX_BUILD_FOR_LINUX_KERNEL
3433215990SjmallettEXPORT_SYMBOL(cvmx_usb_poll);
3434215990Sjmallett#endif
3435210284Sjmallett
3436210311Sjmallettextern void cvmx_usb_set_toggle(cvmx_usb_state_t *state, int endpoint_num, int toggle)
3437210311Sjmallett{
3438210311Sjmallett    cvmx_usb_internal_state_t *usb = (cvmx_usb_internal_state_t*)state;
3439210311Sjmallett    cvmx_usb_pipe_t *pipe = usb->pipe + endpoint_num;
3440210311Sjmallett
3441210311Sjmallett    pipe->pid_toggle = !!toggle;
3442210311Sjmallett}
3443210311Sjmallett
3444210311Sjmallettextern int cvmx_usb_get_toggle(cvmx_usb_state_t *state, int endpoint_num)
3445210311Sjmallett{
3446210311Sjmallett    cvmx_usb_internal_state_t *usb = (cvmx_usb_internal_state_t*)state;
3447210311Sjmallett    cvmx_usb_pipe_t *pipe = usb->pipe + endpoint_num;
3448210311Sjmallett
3449210311Sjmallett    if (pipe->pid_toggle)
3450210311Sjmallett	    return (1);
3451210311Sjmallett    return (0);
3452210311Sjmallett}
3453215990Sjmallett
3454