1210284Sjmallett/***********************license start***************
2232812Sjmallett * Copyright (c) 2003-2010  Cavium Inc. (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
18232812Sjmallett *   * Neither the name of Cavium Inc. 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"
29232812Sjmallett * AND WITH ALL FAULTS AND CAVIUM INC. 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"
75232915Sjmallett#if !defined(CVMX_BUILD_FOR_FREEBSD_KERNEL)
76210284Sjmallett#include "cvmx-csr-db.h"
77232915Sjmallett#endif
78210284Sjmallett#include "cvmx-swap.h"
79232816Sjmallett#if !defined(CVMX_BUILD_FOR_FREEBSD_KERNEL)
80215990Sjmallett#include "cvmx-error.h"
81215990Sjmallett#endif
82232816Sjmallett#endif
83210284Sjmallett
84210284Sjmallett#define MAX_RETRIES         3   /* Maximum number of times to retry failed transactions */
85210284Sjmallett#define MAX_PIPES           32  /* Maximum number of pipes that can be open at once */
86210284Sjmallett#define MAX_TRANSACTIONS    256 /* Maximum number of outstanding transactions across all pipes */
87210284Sjmallett#define MAX_CHANNELS        8   /* Maximum number of hardware channels supported by the USB block */
88210284Sjmallett#define MAX_USB_ADDRESS     127 /* The highest valid USB device address */
89210284Sjmallett#define MAX_USB_ENDPOINT    15  /* The highest valid USB endpoint number */
90210284Sjmallett#define MAX_USB_HUB_PORT    15  /* The highest valid port number on a hub */
91215990Sjmallett#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 */
92215990Sjmallett#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 */
93210284Sjmallett#define ALLOW_CSR_DECODES   0   /* CSR decoding when CVMX_USB_INITIALIZE_FLAGS_DEBUG_CSRS is set
94210284Sjmallett                                    enlarges the code a lot. This define overrides the ability to do CSR
95210284Sjmallett                                    decoding since it isn't necessary 99% of the time. Change this to a
96210284Sjmallett                                    one if you need CSR decoding */
97210284Sjmallett
98210284Sjmallett/* These defines disable the normal read and write csr. This is so I can add
99210284Sjmallett    extra debug stuff to the usb specific version and I won't use the normal
100210284Sjmallett    version by mistake */
101210284Sjmallett#define cvmx_read_csr use_cvmx_usb_read_csr64_instead_of_cvmx_read_csr
102210284Sjmallett#define cvmx_write_csr use_cvmx_usb_write_csr64_instead_of_cvmx_write_csr
103210284Sjmallett
104210284Sjmalletttypedef enum
105210284Sjmallett{
106210284Sjmallett    __CVMX_USB_TRANSACTION_FLAGS_IN_USE = 1<<16,
107210284Sjmallett} cvmx_usb_transaction_flags_t;
108210284Sjmallett
109210284Sjmallett/**
110210284Sjmallett * Logical transactions may take numerous low level
111210284Sjmallett * transactions, especially when splits are concerned. This
112210284Sjmallett * enum represents all of the possible stages a transaction can
113210284Sjmallett * be in. Note that split completes are always even. This is so
114210284Sjmallett * the NAK handler can backup to the previous low level
115210284Sjmallett * transaction with a simple clearing of bit 0.
116210284Sjmallett */
117210284Sjmalletttypedef enum
118210284Sjmallett{
119210284Sjmallett    CVMX_USB_STAGE_NON_CONTROL,
120210284Sjmallett    CVMX_USB_STAGE_NON_CONTROL_SPLIT_COMPLETE,
121210284Sjmallett    CVMX_USB_STAGE_SETUP,
122210284Sjmallett    CVMX_USB_STAGE_SETUP_SPLIT_COMPLETE,
123210284Sjmallett    CVMX_USB_STAGE_DATA,
124210284Sjmallett    CVMX_USB_STAGE_DATA_SPLIT_COMPLETE,
125210284Sjmallett    CVMX_USB_STAGE_STATUS,
126210284Sjmallett    CVMX_USB_STAGE_STATUS_SPLIT_COMPLETE,
127210284Sjmallett} cvmx_usb_stage_t;
128210284Sjmallett
129210284Sjmallett/**
130210284Sjmallett * This structure describes each pending USB transaction
131210284Sjmallett * regardless of type. These are linked together to form a list
132210284Sjmallett * of pending requests for a pipe.
133210284Sjmallett */
134210284Sjmalletttypedef struct cvmx_usb_transaction
135210284Sjmallett{
136210284Sjmallett    struct cvmx_usb_transaction *prev;  /**< Transaction before this one in the pipe */
137210284Sjmallett    struct cvmx_usb_transaction *next;  /**< Transaction after this one in the pipe */
138210284Sjmallett    cvmx_usb_transfer_t type;           /**< Type of transaction, duplicated of the pipe */
139210284Sjmallett    cvmx_usb_transaction_flags_t flags; /**< State flags for this transaction */
140210284Sjmallett    uint64_t buffer;                    /**< User's physical buffer address to read/write */
141210284Sjmallett    int buffer_length;                  /**< Size of the user's buffer in bytes */
142210284Sjmallett    uint64_t control_header;            /**< For control transactions, physical address of the 8 byte standard header */
143210284Sjmallett    int iso_start_frame;                /**< For ISO transactions, the starting frame number */
144210284Sjmallett    int iso_number_packets;             /**< For ISO transactions, the number of packets in the request */
145210284Sjmallett    cvmx_usb_iso_packet_t *iso_packets; /**< For ISO transactions, the sub packets in the request */
146210284Sjmallett    int xfersize;
147210284Sjmallett    int pktcnt;
148210284Sjmallett    int retries;
149210284Sjmallett    int actual_bytes;                   /**< Actual bytes transfer for this transaction */
150210284Sjmallett    cvmx_usb_stage_t stage;             /**< For control transactions, the current stage */
151210284Sjmallett    cvmx_usb_callback_func_t callback;  /**< User's callback function when complete */
152210284Sjmallett    void *callback_data;                /**< User's data */
153210284Sjmallett} cvmx_usb_transaction_t;
154210284Sjmallett
155210284Sjmallett/**
156210284Sjmallett * A pipe represents a virtual connection between Octeon and some
157210284Sjmallett * USB device. It contains a list of pending request to the device.
158210284Sjmallett */
159210284Sjmalletttypedef struct cvmx_usb_pipe
160210284Sjmallett{
161210284Sjmallett    struct cvmx_usb_pipe *prev;         /**< Pipe before this one in the list */
162210284Sjmallett    struct cvmx_usb_pipe *next;         /**< Pipe after this one in the list */
163210284Sjmallett    cvmx_usb_transaction_t *head;       /**< The first pending transaction */
164210284Sjmallett    cvmx_usb_transaction_t *tail;       /**< The last pending transaction */
165215990Sjmallett    uint64_t interval;                  /**< For periodic pipes, the interval between packets in frames */
166215990Sjmallett    uint64_t next_tx_frame;             /**< The next frame this pipe is allowed to transmit on */
167210284Sjmallett    cvmx_usb_pipe_flags_t flags;        /**< State flags for this pipe */
168210284Sjmallett    cvmx_usb_speed_t device_speed;      /**< Speed of device connected to this pipe */
169210284Sjmallett    cvmx_usb_transfer_t transfer_type;  /**< Type of transaction supported by this pipe */
170210284Sjmallett    cvmx_usb_direction_t transfer_dir;  /**< IN or OUT. Ignored for Control */
171210284Sjmallett    int multi_count;                    /**< Max packet in a row for the device */
172210284Sjmallett    uint16_t max_packet;                /**< The device's maximum packet size in bytes */
173210284Sjmallett    uint8_t device_addr;                /**< USB device address at other end of pipe */
174210284Sjmallett    uint8_t endpoint_num;               /**< USB endpoint number at other end of pipe */
175210284Sjmallett    uint8_t hub_device_addr;            /**< Hub address this device is connected to */
176210284Sjmallett    uint8_t hub_port;                   /**< Hub port this device is connected to */
177210284Sjmallett    uint8_t pid_toggle;                 /**< This toggles between 0/1 on every packet send to track the data pid needed */
178210284Sjmallett    uint8_t channel;                    /**< Hardware DMA channel for this pipe */
179210284Sjmallett    int8_t  split_sc_frame;             /**< The low order bits of the frame number the split complete should be sent on */
180210284Sjmallett} cvmx_usb_pipe_t;
181210284Sjmallett
182210284Sjmalletttypedef struct
183210284Sjmallett{
184210284Sjmallett    cvmx_usb_pipe_t *head;              /**< Head of the list, or NULL if empty */
185210284Sjmallett    cvmx_usb_pipe_t *tail;              /**< Tail if the list, or NULL if empty */
186210284Sjmallett} cvmx_usb_pipe_list_t;
187210284Sjmallett
188215990Sjmalletttypedef struct
189215990Sjmallett{
190215990Sjmallett    struct
191215990Sjmallett    {
192215990Sjmallett        int channel;
193215990Sjmallett        int size;
194215990Sjmallett        uint64_t address;
195215990Sjmallett    } entry[MAX_CHANNELS+1];
196215990Sjmallett    int head;
197215990Sjmallett    int tail;
198215990Sjmallett} cvmx_usb_tx_fifo_t;
199215990Sjmallett
200210284Sjmallett/**
201210284Sjmallett * The state of the USB block is stored in this structure
202210284Sjmallett */
203210284Sjmalletttypedef struct
204210284Sjmallett{
205210284Sjmallett    int init_flags;                     /**< Flags passed to initialize */
206210284Sjmallett    int index;                          /**< Which USB block this is for */
207210284Sjmallett    int idle_hardware_channels;         /**< Bit set for every idle hardware channel */
208210284Sjmallett    cvmx_usbcx_hprt_t usbcx_hprt;       /**< Stored port status so we don't need to read a CSR to determine splits */
209210284Sjmallett    cvmx_usb_pipe_t *pipe_for_channel[MAX_CHANNELS];    /**< Map channels to pipes */
210210284Sjmallett    cvmx_usb_transaction_t *free_transaction_head;      /**< List of free transactions head */
211210284Sjmallett    cvmx_usb_transaction_t *free_transaction_tail;      /**< List of free transactions tail */
212210284Sjmallett    cvmx_usb_pipe_t pipe[MAX_PIPES];                    /**< Storage for pipes */
213210284Sjmallett    cvmx_usb_transaction_t transaction[MAX_TRANSACTIONS];       /**< Storage for transactions */
214210284Sjmallett    cvmx_usb_callback_func_t callback[__CVMX_USB_CALLBACK_END]; /**< User global callbacks */
215210284Sjmallett    void *callback_data[__CVMX_USB_CALLBACK_END];               /**< User data for each callback */
216210284Sjmallett    int indent;                         /**< Used by debug output to indent functions */
217210284Sjmallett    cvmx_usb_port_status_t port_status; /**< Last port status used for change notification */
218210284Sjmallett    cvmx_usb_pipe_list_t free_pipes;    /**< List of all pipes that are currently closed */
219210284Sjmallett    cvmx_usb_pipe_list_t idle_pipes;    /**< List of open pipes that have no transactions */
220210284Sjmallett    cvmx_usb_pipe_list_t active_pipes[4]; /**< Active pipes indexed by transfer type */
221215990Sjmallett    uint64_t frame_number;              /**< Increments every SOF interrupt for time keeping */
222215990Sjmallett    cvmx_usb_transaction_t *active_split; /**< Points to the current active split, or NULL */
223215990Sjmallett    cvmx_usb_tx_fifo_t periodic;
224215990Sjmallett    cvmx_usb_tx_fifo_t nonperiodic;
225210284Sjmallett} cvmx_usb_internal_state_t;
226210284Sjmallett
227210284Sjmallett/* This macro logs out whenever a function is called if debugging is on */
228210284Sjmallett#define CVMX_USB_LOG_CALLED() \
229210284Sjmallett    if (cvmx_unlikely(usb->init_flags & CVMX_USB_INITIALIZE_FLAGS_DEBUG_CALLS)) \
230210284Sjmallett        cvmx_dprintf("%*s%s: called\n", 2*usb->indent++, "", __FUNCTION__);
231210284Sjmallett
232210284Sjmallett/* This macro logs out each function parameter if debugging is on */
233210284Sjmallett#define CVMX_USB_LOG_PARAM(format, param) \
234210284Sjmallett    if (cvmx_unlikely(usb->init_flags & CVMX_USB_INITIALIZE_FLAGS_DEBUG_CALLS)) \
235210284Sjmallett        cvmx_dprintf("%*s%s: param %s = " format "\n", 2*usb->indent, "", __FUNCTION__, #param, param);
236210284Sjmallett
237210284Sjmallett/* This macro logs out when a function returns a value */
238210284Sjmallett#define CVMX_USB_RETURN(v)                                              \
239210284Sjmallett    do {                                                                \
240210311Sjmallett        __typeof(v) r = v;                                              \
241210284Sjmallett        if (cvmx_unlikely(usb->init_flags & CVMX_USB_INITIALIZE_FLAGS_DEBUG_CALLS))    \
242210284Sjmallett            cvmx_dprintf("%*s%s: returned %s(%d)\n", 2*--usb->indent, "", __FUNCTION__, #v, r); \
243210284Sjmallett        return r;                                                       \
244210284Sjmallett    } while (0);
245210284Sjmallett
246210284Sjmallett/* This macro logs out when a function doesn't return a value */
247210284Sjmallett#define CVMX_USB_RETURN_NOTHING()                                       \
248210284Sjmallett    do {                                                                \
249210284Sjmallett        if (cvmx_unlikely(usb->init_flags & CVMX_USB_INITIALIZE_FLAGS_DEBUG_CALLS))    \
250210284Sjmallett            cvmx_dprintf("%*s%s: returned\n", 2*--usb->indent, "", __FUNCTION__); \
251210284Sjmallett        return;                                                         \
252210284Sjmallett    } while (0);
253210284Sjmallett
254210284Sjmallett/* This macro spins on a field waiting for it to reach a value */
255210284Sjmallett#define CVMX_WAIT_FOR_FIELD32(address, type, field, op, value, timeout_usec)\
256210284Sjmallett    ({int result;                                                       \
257210284Sjmallett    do {                                                                \
258210284Sjmallett        uint64_t done = cvmx_get_cycle() + (uint64_t)timeout_usec *     \
259215990Sjmallett                           cvmx_clock_get_rate(CVMX_CLOCK_CORE) / 1000000;  \
260210284Sjmallett        type c;                                                         \
261210284Sjmallett        while (1)                                                       \
262210284Sjmallett        {                                                               \
263210284Sjmallett            c.u32 = __cvmx_usb_read_csr32(usb, address);                \
264210284Sjmallett            if (c.s.field op (value)) {                                 \
265210284Sjmallett                result = 0;                                             \
266210284Sjmallett                break;                                                  \
267210284Sjmallett            } else if (cvmx_get_cycle() > done) {                       \
268210284Sjmallett                result = -1;                                            \
269210284Sjmallett                break;                                                  \
270210284Sjmallett            } else                                                      \
271210284Sjmallett                cvmx_wait(100);                                         \
272210284Sjmallett        }                                                               \
273210284Sjmallett    } while (0);                                                        \
274210284Sjmallett    result;})
275210284Sjmallett
276210284Sjmallett/* This macro logically sets a single field in a CSR. It does the sequence
277210284Sjmallett    read, modify, and write */
278210284Sjmallett#define USB_SET_FIELD32(address, type, field, value)\
279210284Sjmallett    do {                                            \
280210284Sjmallett        type c;                                     \
281210284Sjmallett        c.u32 = __cvmx_usb_read_csr32(usb, address);\
282210284Sjmallett        c.s.field = value;                          \
283210284Sjmallett        __cvmx_usb_write_csr32(usb, address, c.u32);\
284210284Sjmallett    } while (0)
285210284Sjmallett
286215990Sjmallett/* Returns the IO address to push/pop stuff data from the FIFOs */
287215990Sjmallett#define USB_FIFO_ADDRESS(channel, usb_index) (CVMX_USBCX_GOTGCTL(usb_index) + ((channel)+1)*0x1000)
288210284Sjmallett
289210284Sjmallett/**
290210284Sjmallett * @INTERNAL
291210284Sjmallett * Read a USB 32bit CSR. It performs the necessary address swizzle
292210284Sjmallett * for 32bit CSRs and logs the value in a readable format if
293210284Sjmallett * debugging is on.
294210284Sjmallett *
295210284Sjmallett * @param usb     USB block this access is for
296210284Sjmallett * @param address 64bit address to read
297210284Sjmallett *
298210284Sjmallett * @return Result of the read
299210284Sjmallett */
300210284Sjmallettstatic inline uint32_t __cvmx_usb_read_csr32(cvmx_usb_internal_state_t *usb,
301210284Sjmallett                                             uint64_t address)
302210284Sjmallett{
303210284Sjmallett    uint32_t result = cvmx_read64_uint32(address ^ 4);
304210284Sjmallett#if ALLOW_CSR_DECODES
305210284Sjmallett    if (cvmx_unlikely(usb->init_flags & CVMX_USB_INITIALIZE_FLAGS_DEBUG_CSRS))
306210284Sjmallett    {
307210284Sjmallett        cvmx_dprintf("Read: ");
308210284Sjmallett        cvmx_csr_db_decode(cvmx_get_proc_id(), address, result);
309210284Sjmallett    }
310210284Sjmallett#endif
311210284Sjmallett    return result;
312210284Sjmallett}
313210284Sjmallett
314210284Sjmallett
315210284Sjmallett/**
316210284Sjmallett * @INTERNAL
317210284Sjmallett * Write a USB 32bit CSR. It performs the necessary address
318210284Sjmallett * swizzle for 32bit CSRs and logs the value in a readable format
319210284Sjmallett * if debugging is on.
320210284Sjmallett *
321210284Sjmallett * @param usb     USB block this access is for
322210284Sjmallett * @param address 64bit address to write
323210284Sjmallett * @param value   Value to write
324210284Sjmallett */
325210284Sjmallettstatic inline void __cvmx_usb_write_csr32(cvmx_usb_internal_state_t *usb,
326210284Sjmallett                                          uint64_t address, uint32_t value)
327210284Sjmallett{
328210284Sjmallett#if ALLOW_CSR_DECODES
329210284Sjmallett    if (cvmx_unlikely(usb->init_flags & CVMX_USB_INITIALIZE_FLAGS_DEBUG_CSRS))
330210284Sjmallett    {
331210284Sjmallett        cvmx_dprintf("Write: ");
332210284Sjmallett        cvmx_csr_db_decode(cvmx_get_proc_id(), address, value);
333210284Sjmallett    }
334210284Sjmallett#endif
335210284Sjmallett    cvmx_write64_uint32(address ^ 4, value);
336215990Sjmallett    cvmx_read64_uint64(CVMX_USBNX_DMA0_INB_CHN0(usb->index));
337210284Sjmallett}
338210284Sjmallett
339210284Sjmallett
340210284Sjmallett/**
341210284Sjmallett * @INTERNAL
342210284Sjmallett * Read a USB 64bit CSR. It logs the value in a readable format if
343210284Sjmallett * debugging is on.
344210284Sjmallett *
345210284Sjmallett * @param usb     USB block this access is for
346210284Sjmallett * @param address 64bit address to read
347210284Sjmallett *
348210284Sjmallett * @return Result of the read
349210284Sjmallett */
350210284Sjmallettstatic inline uint64_t __cvmx_usb_read_csr64(cvmx_usb_internal_state_t *usb,
351210284Sjmallett                                             uint64_t address)
352210284Sjmallett{
353210284Sjmallett    uint64_t result = cvmx_read64_uint64(address);
354210284Sjmallett#if ALLOW_CSR_DECODES
355210284Sjmallett    if (cvmx_unlikely(usb->init_flags & CVMX_USB_INITIALIZE_FLAGS_DEBUG_CSRS))
356210284Sjmallett    {
357210284Sjmallett        cvmx_dprintf("Read: ");
358210284Sjmallett        cvmx_csr_db_decode(cvmx_get_proc_id(), address, result);
359210284Sjmallett    }
360210284Sjmallett#endif
361210284Sjmallett    return result;
362210284Sjmallett}
363210284Sjmallett
364210284Sjmallett
365210284Sjmallett/**
366210284Sjmallett * @INTERNAL
367210284Sjmallett * Write a USB 64bit CSR. It logs the value in a readable format
368210284Sjmallett * if debugging is on.
369210284Sjmallett *
370210284Sjmallett * @param usb     USB block this access is for
371210284Sjmallett * @param address 64bit address to write
372210284Sjmallett * @param value   Value to write
373210284Sjmallett */
374210284Sjmallettstatic inline void __cvmx_usb_write_csr64(cvmx_usb_internal_state_t *usb,
375210284Sjmallett                                          uint64_t address, uint64_t value)
376210284Sjmallett{
377210284Sjmallett#if ALLOW_CSR_DECODES
378210284Sjmallett    if (cvmx_unlikely(usb->init_flags & CVMX_USB_INITIALIZE_FLAGS_DEBUG_CSRS))
379210284Sjmallett    {
380210284Sjmallett        cvmx_dprintf("Write: ");
381210284Sjmallett        cvmx_csr_db_decode(cvmx_get_proc_id(), address, value);
382210284Sjmallett    }
383210284Sjmallett#endif
384210284Sjmallett    cvmx_write64_uint64(address, value);
385210284Sjmallett}
386210284Sjmallett
387210284Sjmallett
388210284Sjmallett/**
389210284Sjmallett * @INTERNAL
390215990Sjmallett * Utility function to convert complete codes into strings
391210284Sjmallett *
392210284Sjmallett * @param complete_code
393210284Sjmallett *               Code to convert
394210284Sjmallett *
395210284Sjmallett * @return Human readable string
396210284Sjmallett */
397210284Sjmallettstatic const char *__cvmx_usb_complete_to_string(cvmx_usb_complete_t complete_code)
398210284Sjmallett{
399210284Sjmallett    switch (complete_code)
400210284Sjmallett    {
401210284Sjmallett        case CVMX_USB_COMPLETE_SUCCESS: return "SUCCESS";
402210284Sjmallett        case CVMX_USB_COMPLETE_SHORT:   return "SHORT";
403210284Sjmallett        case CVMX_USB_COMPLETE_CANCEL:  return "CANCEL";
404210284Sjmallett        case CVMX_USB_COMPLETE_ERROR:   return "ERROR";
405210284Sjmallett        case CVMX_USB_COMPLETE_STALL:   return "STALL";
406210284Sjmallett        case CVMX_USB_COMPLETE_XACTERR: return "XACTERR";
407210284Sjmallett        case CVMX_USB_COMPLETE_DATATGLERR: return "DATATGLERR";
408210284Sjmallett        case CVMX_USB_COMPLETE_BABBLEERR: return "BABBLEERR";
409210284Sjmallett        case CVMX_USB_COMPLETE_FRAMEERR: return "FRAMEERR";
410210284Sjmallett    }
411210284Sjmallett    return "Update __cvmx_usb_complete_to_string";
412210284Sjmallett}
413210284Sjmallett
414210284Sjmallett
415210284Sjmallett/**
416210284Sjmallett * @INTERNAL
417210284Sjmallett * Return non zero if this pipe connects to a non HIGH speed
418210284Sjmallett * device through a high speed hub.
419210284Sjmallett *
420210284Sjmallett * @param usb    USB block this access is for
421210284Sjmallett * @param pipe   Pipe to check
422210284Sjmallett *
423210284Sjmallett * @return Non zero if we need to do split transactions
424210284Sjmallett */
425210284Sjmallettstatic inline int __cvmx_usb_pipe_needs_split(cvmx_usb_internal_state_t *usb, cvmx_usb_pipe_t *pipe)
426210284Sjmallett{
427210284Sjmallett    return ((pipe->device_speed != CVMX_USB_SPEED_HIGH) && (usb->usbcx_hprt.s.prtspd == CVMX_USB_SPEED_HIGH));
428210284Sjmallett}
429210284Sjmallett
430210284Sjmallett
431210284Sjmallett/**
432210284Sjmallett * @INTERNAL
433210284Sjmallett * Trivial utility function to return the correct PID for a pipe
434210284Sjmallett *
435210284Sjmallett * @param pipe   pipe to check
436210284Sjmallett *
437210284Sjmallett * @return PID for pipe
438210284Sjmallett */
439210284Sjmallettstatic inline int __cvmx_usb_get_data_pid(cvmx_usb_pipe_t *pipe)
440210284Sjmallett{
441210284Sjmallett    if (pipe->pid_toggle)
442210284Sjmallett        return 2; /* Data1 */
443210284Sjmallett    else
444210284Sjmallett        return 0; /* Data0 */
445210284Sjmallett}
446210284Sjmallett
447210284Sjmallett
448210284Sjmallett/**
449210284Sjmallett * Return the number of USB ports supported by this Octeon
450210284Sjmallett * chip. If the chip doesn't support USB, or is not supported
451210284Sjmallett * by this API, a zero will be returned. Most Octeon chips
452210284Sjmallett * support one usb port, but some support two ports.
453210284Sjmallett * cvmx_usb_initialize() must be called on independent
454210284Sjmallett * cvmx_usb_state_t structures.
455210284Sjmallett *
456210284Sjmallett * This utilizes cvmx_helper_board_usb_get_num_ports()
457215990Sjmallett * to get any board specific variations.
458210284Sjmallett *
459210284Sjmallett * @return Number of port, zero if usb isn't supported
460210284Sjmallett */
461210284Sjmallettint cvmx_usb_get_num_ports(void)
462210284Sjmallett{
463210284Sjmallett    int arch_ports = 0;
464210284Sjmallett
465215990Sjmallett    if (OCTEON_IS_MODEL(OCTEON_CN56XX))
466215990Sjmallett        arch_ports = 1;
467215990Sjmallett    else if (OCTEON_IS_MODEL(OCTEON_CN52XX))
468210284Sjmallett        arch_ports = 2;
469215990Sjmallett    else if (OCTEON_IS_MODEL(OCTEON_CN50XX))
470215990Sjmallett        arch_ports = 1;
471210284Sjmallett    else if (OCTEON_IS_MODEL(OCTEON_CN31XX))
472215990Sjmallett        arch_ports = 1;
473215990Sjmallett    else if (OCTEON_IS_MODEL(OCTEON_CN30XX))
474215990Sjmallett        arch_ports = 1;
475215990Sjmallett    else
476210284Sjmallett        arch_ports = 0;
477210284Sjmallett
478210284Sjmallett    return __cvmx_helper_board_usb_get_num_ports(arch_ports);
479210284Sjmallett}
480215990Sjmallett#ifdef CVMX_BUILD_FOR_LINUX_KERNEL
481215990SjmallettEXPORT_SYMBOL(cvmx_usb_get_num_ports);
482215990Sjmallett#endif
483210284Sjmallett
484210284Sjmallett
485210284Sjmallett/**
486210284Sjmallett * @INTERNAL
487210284Sjmallett * Allocate a usb transaction for use
488210284Sjmallett *
489210284Sjmallett * @param usb    USB device state populated by
490210284Sjmallett *               cvmx_usb_initialize().
491210284Sjmallett *
492210284Sjmallett * @return Transaction or NULL
493210284Sjmallett */
494210284Sjmallettstatic inline cvmx_usb_transaction_t *__cvmx_usb_alloc_transaction(cvmx_usb_internal_state_t *usb)
495210284Sjmallett{
496210284Sjmallett    cvmx_usb_transaction_t *t;
497210284Sjmallett    t = usb->free_transaction_head;
498210284Sjmallett    if (t)
499210284Sjmallett    {
500210284Sjmallett        usb->free_transaction_head = t->next;
501210284Sjmallett        if (!usb->free_transaction_head)
502210284Sjmallett            usb->free_transaction_tail = NULL;
503210284Sjmallett    }
504210284Sjmallett    else if (cvmx_unlikely(usb->init_flags & CVMX_USB_INITIALIZE_FLAGS_DEBUG_INFO))
505210284Sjmallett        cvmx_dprintf("%s: Failed to allocate a transaction\n", __FUNCTION__);
506210284Sjmallett    if (t)
507210284Sjmallett    {
508210284Sjmallett        memset(t, 0, sizeof(*t));
509210284Sjmallett        t->flags = __CVMX_USB_TRANSACTION_FLAGS_IN_USE;
510210284Sjmallett    }
511210284Sjmallett    return t;
512210284Sjmallett}
513210284Sjmallett
514210284Sjmallett
515210284Sjmallett/**
516210284Sjmallett * @INTERNAL
517210284Sjmallett * Free a usb transaction
518210284Sjmallett *
519210284Sjmallett * @param usb    USB device state populated by
520210284Sjmallett *               cvmx_usb_initialize().
521210284Sjmallett * @param transaction
522210284Sjmallett *               Transaction to free
523210284Sjmallett */
524210284Sjmallettstatic inline void __cvmx_usb_free_transaction(cvmx_usb_internal_state_t *usb,
525210284Sjmallett                                        cvmx_usb_transaction_t *transaction)
526210284Sjmallett{
527210284Sjmallett    transaction->flags = 0;
528210284Sjmallett    transaction->prev = NULL;
529210284Sjmallett    transaction->next = NULL;
530210284Sjmallett    if (usb->free_transaction_tail)
531210284Sjmallett        usb->free_transaction_tail->next = transaction;
532210284Sjmallett    else
533210284Sjmallett        usb->free_transaction_head = transaction;
534210284Sjmallett    usb->free_transaction_tail = transaction;
535210284Sjmallett}
536210284Sjmallett
537210284Sjmallett
538210284Sjmallett/**
539210284Sjmallett * @INTERNAL
540210284Sjmallett * Add a pipe to the tail of a list
541210284Sjmallett * @param list   List to add pipe to
542210284Sjmallett * @param pipe   Pipe to add
543210284Sjmallett */
544210284Sjmallettstatic inline void __cvmx_usb_append_pipe(cvmx_usb_pipe_list_t *list, cvmx_usb_pipe_t *pipe)
545210284Sjmallett{
546210284Sjmallett    pipe->next = NULL;
547210284Sjmallett    pipe->prev = list->tail;
548210284Sjmallett    if (list->tail)
549210284Sjmallett        list->tail->next = pipe;
550210284Sjmallett    else
551210284Sjmallett        list->head = pipe;
552210284Sjmallett    list->tail = pipe;
553210284Sjmallett}
554210284Sjmallett
555210284Sjmallett
556210284Sjmallett/**
557210284Sjmallett * @INTERNAL
558210284Sjmallett * Remove a pipe from a list
559210284Sjmallett * @param list   List to remove pipe from
560210284Sjmallett * @param pipe   Pipe to remove
561210284Sjmallett */
562210284Sjmallettstatic inline void __cvmx_usb_remove_pipe(cvmx_usb_pipe_list_t *list, cvmx_usb_pipe_t *pipe)
563210284Sjmallett{
564210284Sjmallett    if (list->head == pipe)
565210284Sjmallett    {
566210284Sjmallett        list->head = pipe->next;
567210284Sjmallett        pipe->next = NULL;
568210284Sjmallett        if (list->head)
569210284Sjmallett            list->head->prev = NULL;
570210284Sjmallett        else
571210284Sjmallett            list->tail = NULL;
572210284Sjmallett    }
573210284Sjmallett    else if (list->tail == pipe)
574210284Sjmallett    {
575210284Sjmallett        list->tail = pipe->prev;
576210284Sjmallett        list->tail->next = NULL;
577210284Sjmallett        pipe->prev = NULL;
578210284Sjmallett    }
579210284Sjmallett    else
580210284Sjmallett    {
581210284Sjmallett        pipe->prev->next = pipe->next;
582210284Sjmallett        pipe->next->prev = pipe->prev;
583210284Sjmallett        pipe->prev = NULL;
584210284Sjmallett        pipe->next = NULL;
585210284Sjmallett    }
586210284Sjmallett}
587210284Sjmallett
588210284Sjmallett
589210284Sjmallett/**
590210284Sjmallett * Initialize a USB port for use. This must be called before any
591210284Sjmallett * other access to the Octeon USB port is made. The port starts
592210284Sjmallett * off in the disabled state.
593210284Sjmallett *
594210284Sjmallett * @param state  Pointer to an empty cvmx_usb_state_t structure
595210284Sjmallett *               that will be populated by the initialize call.
596210284Sjmallett *               This structure is then passed to all other USB
597210284Sjmallett *               functions.
598210284Sjmallett * @param usb_port_number
599210284Sjmallett *               Which Octeon USB port to initialize.
600210284Sjmallett * @param flags  Flags to control hardware initialization. See
601210284Sjmallett *               cvmx_usb_initialize_flags_t for the flag
602210284Sjmallett *               definitions. Some flags are mandatory.
603210284Sjmallett *
604210284Sjmallett * @return CVMX_USB_SUCCESS or a negative error code defined in
605210284Sjmallett *         cvmx_usb_status_t.
606210284Sjmallett */
607210284Sjmallettcvmx_usb_status_t cvmx_usb_initialize(cvmx_usb_state_t *state,
608210284Sjmallett                                      int usb_port_number,
609210284Sjmallett                                      cvmx_usb_initialize_flags_t flags)
610210284Sjmallett{
611210284Sjmallett    cvmx_usbnx_clk_ctl_t usbn_clk_ctl;
612210284Sjmallett    cvmx_usbnx_usbp_ctl_status_t usbn_usbp_ctl_status;
613210284Sjmallett    cvmx_usb_internal_state_t *usb = (cvmx_usb_internal_state_t*)state;
614210284Sjmallett
615210284Sjmallett    usb->init_flags = flags;
616210284Sjmallett    CVMX_USB_LOG_CALLED();
617210284Sjmallett    CVMX_USB_LOG_PARAM("%p", state);
618210284Sjmallett    CVMX_USB_LOG_PARAM("%d", usb_port_number);
619210284Sjmallett    CVMX_USB_LOG_PARAM("0x%x", flags);
620210284Sjmallett
621210284Sjmallett    /* Make sure that state is large enough to store the internal state */
622210284Sjmallett    if (sizeof(*state) < sizeof(*usb))
623210284Sjmallett        CVMX_USB_RETURN(CVMX_USB_INVALID_PARAM);
624210284Sjmallett    /* At first allow 0-1 for the usb port number */
625210284Sjmallett    if ((usb_port_number < 0) || (usb_port_number > 1))
626210284Sjmallett        CVMX_USB_RETURN(CVMX_USB_INVALID_PARAM);
627210284Sjmallett    /* For all chips except 52XX there is only one port */
628210284Sjmallett    if (!OCTEON_IS_MODEL(OCTEON_CN52XX) && (usb_port_number > 0))
629210284Sjmallett        CVMX_USB_RETURN(CVMX_USB_INVALID_PARAM);
630210284Sjmallett    /* Try to determine clock type automatically */
631210284Sjmallett    if ((flags & (CVMX_USB_INITIALIZE_FLAGS_CLOCK_XO_XI |
632210284Sjmallett                  CVMX_USB_INITIALIZE_FLAGS_CLOCK_XO_GND)) == 0)
633210284Sjmallett    {
634210284Sjmallett        if (__cvmx_helper_board_usb_get_clock_type() == USB_CLOCK_TYPE_CRYSTAL_12)
635210284Sjmallett            flags |= CVMX_USB_INITIALIZE_FLAGS_CLOCK_XO_XI;  /* Only 12 MHZ crystals are supported */
636210284Sjmallett        else
637210284Sjmallett            flags |= CVMX_USB_INITIALIZE_FLAGS_CLOCK_XO_GND;
638210284Sjmallett    }
639210284Sjmallett
640210284Sjmallett    if (flags & CVMX_USB_INITIALIZE_FLAGS_CLOCK_XO_GND)
641210284Sjmallett    {
642210284Sjmallett        /* Check for auto ref clock frequency */
643210284Sjmallett        if (!(flags & CVMX_USB_INITIALIZE_FLAGS_CLOCK_MHZ_MASK))
644210284Sjmallett            switch (__cvmx_helper_board_usb_get_clock_type())
645210284Sjmallett            {
646210284Sjmallett                case USB_CLOCK_TYPE_REF_12:
647210284Sjmallett                    flags |= CVMX_USB_INITIALIZE_FLAGS_CLOCK_12MHZ;
648210284Sjmallett                    break;
649210284Sjmallett                case USB_CLOCK_TYPE_REF_24:
650210284Sjmallett                    flags |= CVMX_USB_INITIALIZE_FLAGS_CLOCK_24MHZ;
651210284Sjmallett                    break;
652210284Sjmallett                case USB_CLOCK_TYPE_REF_48:
653210284Sjmallett                    flags |= CVMX_USB_INITIALIZE_FLAGS_CLOCK_48MHZ;
654210284Sjmallett                    break;
655210284Sjmallett                default:
656210284Sjmallett                    CVMX_USB_RETURN(CVMX_USB_INVALID_PARAM);
657210284Sjmallett                    break;
658210284Sjmallett            }
659210284Sjmallett    }
660210284Sjmallett
661229071Sgonzo    memset(usb, 0, sizeof(*usb));
662210284Sjmallett    usb->init_flags = flags;
663210284Sjmallett
664210284Sjmallett    /* Initialize the USB state structure */
665210284Sjmallett    {
666210284Sjmallett        int i;
667210284Sjmallett        usb->index = usb_port_number;
668210284Sjmallett
669210284Sjmallett        /* Initialize the transaction double linked list */
670210284Sjmallett        usb->free_transaction_head = NULL;
671210284Sjmallett        usb->free_transaction_tail = NULL;
672210284Sjmallett        for (i=0; i<MAX_TRANSACTIONS; i++)
673210284Sjmallett            __cvmx_usb_free_transaction(usb, usb->transaction + i);
674210284Sjmallett        for (i=0; i<MAX_PIPES; i++)
675210284Sjmallett            __cvmx_usb_append_pipe(&usb->free_pipes, usb->pipe + i);
676210284Sjmallett    }
677210284Sjmallett
678210284Sjmallett    /* Power On Reset and PHY Initialization */
679210284Sjmallett
680210284Sjmallett    /* 1. Wait for DCOK to assert (nothing to do) */
681210284Sjmallett    /* 2a. Write USBN0/1_CLK_CTL[POR] = 1 and
682210284Sjmallett        USBN0/1_CLK_CTL[HRST,PRST,HCLK_RST] = 0 */
683210284Sjmallett    usbn_clk_ctl.u64 = __cvmx_usb_read_csr64(usb, CVMX_USBNX_CLK_CTL(usb->index));
684210284Sjmallett    usbn_clk_ctl.s.por = 1;
685210284Sjmallett    usbn_clk_ctl.s.hrst = 0;
686210284Sjmallett    usbn_clk_ctl.s.prst = 0;
687210284Sjmallett    usbn_clk_ctl.s.hclk_rst = 0;
688210284Sjmallett    usbn_clk_ctl.s.enable = 0;
689210284Sjmallett    /* 2b. Select the USB reference clock/crystal parameters by writing
690210284Sjmallett        appropriate values to USBN0/1_CLK_CTL[P_C_SEL, P_RTYPE, P_COM_ON] */
691210284Sjmallett    if (usb->init_flags & CVMX_USB_INITIALIZE_FLAGS_CLOCK_XO_GND)
692210284Sjmallett    {
693210284Sjmallett        /* The USB port uses 12/24/48MHz 2.5V board clock
694210284Sjmallett            source at USB_XO. USB_XI should be tied to GND.
695210284Sjmallett            Most Octeon evaluation boards require this setting */
696210284Sjmallett        if (OCTEON_IS_MODEL(OCTEON_CN3XXX))
697210284Sjmallett        {
698210284Sjmallett            usbn_clk_ctl.cn31xx.p_rclk  = 1; /* From CN31XX,CN30XX manual */
699210284Sjmallett            usbn_clk_ctl.cn31xx.p_xenbn = 0;
700210284Sjmallett        }
701210284Sjmallett        else if (OCTEON_IS_MODEL(OCTEON_CN56XX) || OCTEON_IS_MODEL(OCTEON_CN50XX))
702210284Sjmallett            usbn_clk_ctl.cn56xx.p_rtype = 2; /* From CN56XX,CN50XX manual */
703210284Sjmallett        else
704210284Sjmallett            usbn_clk_ctl.cn52xx.p_rtype = 1; /* From CN52XX manual */
705210284Sjmallett
706210284Sjmallett        switch (flags & CVMX_USB_INITIALIZE_FLAGS_CLOCK_MHZ_MASK)
707210284Sjmallett        {
708210284Sjmallett            case CVMX_USB_INITIALIZE_FLAGS_CLOCK_12MHZ:
709210284Sjmallett                usbn_clk_ctl.s.p_c_sel = 0;
710210284Sjmallett                break;
711210284Sjmallett            case CVMX_USB_INITIALIZE_FLAGS_CLOCK_24MHZ:
712210284Sjmallett                usbn_clk_ctl.s.p_c_sel = 1;
713210284Sjmallett                break;
714210284Sjmallett            case CVMX_USB_INITIALIZE_FLAGS_CLOCK_48MHZ:
715210284Sjmallett                usbn_clk_ctl.s.p_c_sel = 2;
716210284Sjmallett                break;
717210284Sjmallett        }
718210284Sjmallett    }
719210284Sjmallett    else
720210284Sjmallett    {
721210284Sjmallett        /* The USB port uses a 12MHz crystal as clock source
722210284Sjmallett            at USB_XO and USB_XI */
723210284Sjmallett        if (OCTEON_IS_MODEL(OCTEON_CN3XXX))
724210284Sjmallett        {
725210284Sjmallett            usbn_clk_ctl.cn31xx.p_rclk  = 1; /* From CN31XX,CN30XX manual */
726210284Sjmallett            usbn_clk_ctl.cn31xx.p_xenbn = 1;
727210284Sjmallett        }
728210284Sjmallett        else if (OCTEON_IS_MODEL(OCTEON_CN56XX) || OCTEON_IS_MODEL(OCTEON_CN50XX))
729210284Sjmallett            usbn_clk_ctl.cn56xx.p_rtype = 0; /* From CN56XX,CN50XX manual */
730210284Sjmallett        else
731210284Sjmallett            usbn_clk_ctl.cn52xx.p_rtype = 0; /* From CN52XX manual */
732210284Sjmallett
733210284Sjmallett        usbn_clk_ctl.s.p_c_sel = 0;
734210284Sjmallett    }
735210284Sjmallett    /* 2c. Select the HCLK via writing USBN0/1_CLK_CTL[DIVIDE, DIVIDE2] and
736210284Sjmallett        setting USBN0/1_CLK_CTL[ENABLE] = 1.  Divide the core clock down such
737210284Sjmallett        that USB is as close as possible to 125Mhz */
738210284Sjmallett    {
739215990Sjmallett        int divisor = (cvmx_clock_get_rate(CVMX_CLOCK_CORE)+125000000-1)/125000000;
740210284Sjmallett        if (divisor < 4)  /* Lower than 4 doesn't seem to work properly */
741210284Sjmallett            divisor = 4;
742210284Sjmallett        usbn_clk_ctl.s.divide = divisor;
743210284Sjmallett        usbn_clk_ctl.s.divide2 = 0;
744210284Sjmallett    }
745210284Sjmallett    __cvmx_usb_write_csr64(usb, CVMX_USBNX_CLK_CTL(usb->index),
746210284Sjmallett                           usbn_clk_ctl.u64);
747210284Sjmallett    /* 2d. Write USBN0/1_CLK_CTL[HCLK_RST] = 1 */
748210284Sjmallett    usbn_clk_ctl.s.hclk_rst = 1;
749210284Sjmallett    __cvmx_usb_write_csr64(usb, CVMX_USBNX_CLK_CTL(usb->index),
750210284Sjmallett                           usbn_clk_ctl.u64);
751210284Sjmallett    /* 2e.  Wait 64 core-clock cycles for HCLK to stabilize */
752210284Sjmallett    cvmx_wait(64);
753210284Sjmallett    /* 3. Program the power-on reset field in the USBN clock-control register:
754210284Sjmallett        USBN_CLK_CTL[POR] = 0 */
755210284Sjmallett    usbn_clk_ctl.s.por = 0;
756210284Sjmallett    __cvmx_usb_write_csr64(usb, CVMX_USBNX_CLK_CTL(usb->index),
757210284Sjmallett                           usbn_clk_ctl.u64);
758210284Sjmallett    /* 4. Wait 1 ms for PHY clock to start */
759210284Sjmallett    cvmx_wait_usec(1000);
760210284Sjmallett    /* 5. Program the Reset input from automatic test equipment field in the
761210284Sjmallett        USBP control and status register: USBN_USBP_CTL_STATUS[ATE_RESET] = 1 */
762210284Sjmallett    usbn_usbp_ctl_status.u64 = __cvmx_usb_read_csr64(usb, CVMX_USBNX_USBP_CTL_STATUS(usb->index));
763210284Sjmallett    usbn_usbp_ctl_status.s.ate_reset = 1;
764210284Sjmallett    __cvmx_usb_write_csr64(usb, CVMX_USBNX_USBP_CTL_STATUS(usb->index),
765210284Sjmallett                           usbn_usbp_ctl_status.u64);
766210284Sjmallett    /* 6. Wait 10 cycles */
767210284Sjmallett    cvmx_wait(10);
768210284Sjmallett    /* 7. Clear ATE_RESET field in the USBN clock-control register:
769210284Sjmallett        USBN_USBP_CTL_STATUS[ATE_RESET] = 0 */
770210284Sjmallett    usbn_usbp_ctl_status.s.ate_reset = 0;
771210284Sjmallett    __cvmx_usb_write_csr64(usb, CVMX_USBNX_USBP_CTL_STATUS(usb->index),
772210284Sjmallett                           usbn_usbp_ctl_status.u64);
773210284Sjmallett    /* 8. Program the PHY reset field in the USBN clock-control register:
774210284Sjmallett        USBN_CLK_CTL[PRST] = 1 */
775210284Sjmallett    usbn_clk_ctl.s.prst = 1;
776210284Sjmallett    __cvmx_usb_write_csr64(usb, CVMX_USBNX_CLK_CTL(usb->index),
777210284Sjmallett                           usbn_clk_ctl.u64);
778210284Sjmallett    /* 9. Program the USBP control and status register to select host or
779210284Sjmallett        device mode. USBN_USBP_CTL_STATUS[HST_MODE] = 0 for host, = 1 for
780210284Sjmallett        device */
781215990Sjmallett    usbn_usbp_ctl_status.s.hst_mode = 0;
782210284Sjmallett    __cvmx_usb_write_csr64(usb, CVMX_USBNX_USBP_CTL_STATUS(usb->index),
783210284Sjmallett                           usbn_usbp_ctl_status.u64);
784210284Sjmallett    /* 10. Wait 1 �s */
785210284Sjmallett    cvmx_wait_usec(1);
786210284Sjmallett    /* 11. Program the hreset_n field in the USBN clock-control register:
787210284Sjmallett        USBN_CLK_CTL[HRST] = 1 */
788210284Sjmallett    usbn_clk_ctl.s.hrst = 1;
789210284Sjmallett    __cvmx_usb_write_csr64(usb, CVMX_USBNX_CLK_CTL(usb->index),
790210284Sjmallett                           usbn_clk_ctl.u64);
791210284Sjmallett    /* 12. Proceed to USB core initialization */
792210284Sjmallett    usbn_clk_ctl.s.enable = 1;
793210284Sjmallett    __cvmx_usb_write_csr64(usb, CVMX_USBNX_CLK_CTL(usb->index),
794210284Sjmallett                           usbn_clk_ctl.u64);
795210284Sjmallett    cvmx_wait_usec(1);
796210284Sjmallett
797210284Sjmallett    /* USB Core Initialization */
798210284Sjmallett
799210284Sjmallett    /* 1. Read USBC_GHWCFG1, USBC_GHWCFG2, USBC_GHWCFG3, USBC_GHWCFG4 to
800210284Sjmallett        determine USB core configuration parameters. */
801210284Sjmallett    /* Nothing needed */
802210284Sjmallett    /* 2. Program the following fields in the global AHB configuration
803210284Sjmallett        register (USBC_GAHBCFG)
804210284Sjmallett        DMA mode, USBC_GAHBCFG[DMAEn]: 1 = DMA mode, 0 = slave mode
805210284Sjmallett        Burst length, USBC_GAHBCFG[HBSTLEN] = 0
806210284Sjmallett        Nonperiodic TxFIFO empty level (slave mode only),
807210284Sjmallett        USBC_GAHBCFG[NPTXFEMPLVL]
808210284Sjmallett        Periodic TxFIFO empty level (slave mode only),
809210284Sjmallett        USBC_GAHBCFG[PTXFEMPLVL]
810210284Sjmallett        Global interrupt mask, USBC_GAHBCFG[GLBLINTRMSK] = 1 */
811210284Sjmallett    {
812210284Sjmallett        cvmx_usbcx_gahbcfg_t usbcx_gahbcfg;
813215990Sjmallett        /* Due to an errata, CN31XX doesn't support DMA */
814215990Sjmallett        if (OCTEON_IS_MODEL(OCTEON_CN31XX))
815215990Sjmallett            usb->init_flags |= CVMX_USB_INITIALIZE_FLAGS_NO_DMA;
816210284Sjmallett        usbcx_gahbcfg.u32 = 0;
817215990Sjmallett        usbcx_gahbcfg.s.dmaen = !(usb->init_flags & CVMX_USB_INITIALIZE_FLAGS_NO_DMA);
818215990Sjmallett        if (usb->init_flags & CVMX_USB_INITIALIZE_FLAGS_NO_DMA)
819215990Sjmallett            usb->idle_hardware_channels = 0x1;  /* Only use one channel with non DMA */
820215990Sjmallett        else if (OCTEON_IS_MODEL(OCTEON_CN5XXX))
821215990Sjmallett            usb->idle_hardware_channels = 0xf7; /* CN5XXX have an errata with channel 3 */
822215990Sjmallett        else
823210284Sjmallett            usb->idle_hardware_channels = 0xff;
824210284Sjmallett        usbcx_gahbcfg.s.hbstlen = 0;
825210284Sjmallett        usbcx_gahbcfg.s.nptxfemplvl = 1;
826210284Sjmallett        usbcx_gahbcfg.s.ptxfemplvl = 1;
827210284Sjmallett        usbcx_gahbcfg.s.glblintrmsk = 1;
828210284Sjmallett        __cvmx_usb_write_csr32(usb, CVMX_USBCX_GAHBCFG(usb->index),
829210284Sjmallett                               usbcx_gahbcfg.u32);
830210284Sjmallett    }
831210284Sjmallett    /* 3. Program the following fields in USBC_GUSBCFG register.
832210284Sjmallett        HS/FS timeout calibration, USBC_GUSBCFG[TOUTCAL] = 0
833210284Sjmallett        ULPI DDR select, USBC_GUSBCFG[DDRSEL] = 0
834210284Sjmallett        USB turnaround time, USBC_GUSBCFG[USBTRDTIM] = 0x5
835210284Sjmallett        PHY low-power clock select, USBC_GUSBCFG[PHYLPWRCLKSEL] = 0 */
836210284Sjmallett    {
837210284Sjmallett        cvmx_usbcx_gusbcfg_t usbcx_gusbcfg;
838210284Sjmallett        usbcx_gusbcfg.u32 = __cvmx_usb_read_csr32(usb, CVMX_USBCX_GUSBCFG(usb->index));
839210284Sjmallett        usbcx_gusbcfg.s.toutcal = 0;
840210284Sjmallett        usbcx_gusbcfg.s.ddrsel = 0;
841210284Sjmallett        usbcx_gusbcfg.s.usbtrdtim = 0x5;
842210284Sjmallett        usbcx_gusbcfg.s.phylpwrclksel = 0;
843210284Sjmallett        __cvmx_usb_write_csr32(usb, CVMX_USBCX_GUSBCFG(usb->index),
844210284Sjmallett                               usbcx_gusbcfg.u32);
845210284Sjmallett    }
846210284Sjmallett    /* 4. The software must unmask the following bits in the USBC_GINTMSK
847210284Sjmallett        register.
848210284Sjmallett        OTG interrupt mask, USBC_GINTMSK[OTGINTMSK] = 1
849210284Sjmallett        Mode mismatch interrupt mask, USBC_GINTMSK[MODEMISMSK] = 1 */
850210284Sjmallett    {
851210284Sjmallett        cvmx_usbcx_gintmsk_t usbcx_gintmsk;
852210284Sjmallett        int channel;
853210284Sjmallett
854210284Sjmallett        usbcx_gintmsk.u32 = __cvmx_usb_read_csr32(usb, CVMX_USBCX_GINTMSK(usb->index));
855210284Sjmallett        usbcx_gintmsk.s.otgintmsk = 1;
856210284Sjmallett        usbcx_gintmsk.s.modemismsk = 1;
857210284Sjmallett        usbcx_gintmsk.s.hchintmsk = 1;
858210284Sjmallett        usbcx_gintmsk.s.sofmsk = 0;
859215990Sjmallett        /* We need RX FIFO interrupts if we don't have DMA */
860215990Sjmallett        if (usb->init_flags & CVMX_USB_INITIALIZE_FLAGS_NO_DMA)
861215990Sjmallett            usbcx_gintmsk.s.rxflvlmsk = 1;
862210284Sjmallett        __cvmx_usb_write_csr32(usb, CVMX_USBCX_GINTMSK(usb->index),
863210284Sjmallett                               usbcx_gintmsk.u32);
864210284Sjmallett
865215990Sjmallett        /* Disable all channel interrupts. We'll enable them per channel later */
866210284Sjmallett        for (channel=0; channel<8; channel++)
867215990Sjmallett            __cvmx_usb_write_csr32(usb, CVMX_USBCX_HCINTMSKX(channel, usb->index), 0);
868210284Sjmallett    }
869210284Sjmallett
870210284Sjmallett    {
871210284Sjmallett        /* Host Port Initialization */
872210284Sjmallett        if (cvmx_unlikely(usb->init_flags & CVMX_USB_INITIALIZE_FLAGS_DEBUG_INFO))
873210284Sjmallett            cvmx_dprintf("%s: USB%d is in host mode\n", __FUNCTION__, usb->index);
874210284Sjmallett
875210284Sjmallett        /* 1. Program the host-port interrupt-mask field to unmask,
876210284Sjmallett            USBC_GINTMSK[PRTINT] = 1 */
877210284Sjmallett        USB_SET_FIELD32(CVMX_USBCX_GINTMSK(usb->index), cvmx_usbcx_gintmsk_t,
878210284Sjmallett                        prtintmsk, 1);
879210284Sjmallett        USB_SET_FIELD32(CVMX_USBCX_GINTMSK(usb->index), cvmx_usbcx_gintmsk_t,
880210284Sjmallett                        disconnintmsk, 1);
881210284Sjmallett        /* 2. Program the USBC_HCFG register to select full-speed host or
882210284Sjmallett            high-speed host. */
883210284Sjmallett        {
884210284Sjmallett            cvmx_usbcx_hcfg_t usbcx_hcfg;
885210284Sjmallett            usbcx_hcfg.u32 = __cvmx_usb_read_csr32(usb, CVMX_USBCX_HCFG(usb->index));
886210284Sjmallett            usbcx_hcfg.s.fslssupp = 0;
887210284Sjmallett            usbcx_hcfg.s.fslspclksel = 0;
888210284Sjmallett            __cvmx_usb_write_csr32(usb, CVMX_USBCX_HCFG(usb->index), usbcx_hcfg.u32);
889210284Sjmallett        }
890210284Sjmallett        /* 3. Program the port power bit to drive VBUS on the USB,
891210284Sjmallett            USBC_HPRT[PRTPWR] = 1 */
892210284Sjmallett        USB_SET_FIELD32(CVMX_USBCX_HPRT(usb->index), cvmx_usbcx_hprt_t, prtpwr, 1);
893210284Sjmallett
894210284Sjmallett        /* Steps 4-15 from the manual are done later in the port enable */
895210284Sjmallett    }
896210284Sjmallett
897215990Sjmallett#ifdef __CVMX_ERROR_H__
898215990Sjmallett    cvmx_error_enable_group(CVMX_ERROR_GROUP_USB, usb->index);
899215990Sjmallett#endif
900210284Sjmallett    CVMX_USB_RETURN(CVMX_USB_SUCCESS);
901210284Sjmallett}
902215990Sjmallett#ifdef CVMX_BUILD_FOR_LINUX_KERNEL
903215990SjmallettEXPORT_SYMBOL(cvmx_usb_initialize);
904215990Sjmallett#endif
905210284Sjmallett
906210284Sjmallett
907210284Sjmallett/**
908210284Sjmallett * Shutdown a USB port after a call to cvmx_usb_initialize().
909210284Sjmallett * The port should be disabled with all pipes closed when this
910210284Sjmallett * function is called.
911210284Sjmallett *
912210284Sjmallett * @param state  USB device state populated by
913210284Sjmallett *               cvmx_usb_initialize().
914210284Sjmallett *
915210284Sjmallett * @return CVMX_USB_SUCCESS or a negative error code defined in
916210284Sjmallett *         cvmx_usb_status_t.
917210284Sjmallett */
918210284Sjmallettcvmx_usb_status_t cvmx_usb_shutdown(cvmx_usb_state_t *state)
919210284Sjmallett{
920210284Sjmallett    cvmx_usbnx_clk_ctl_t usbn_clk_ctl;
921210284Sjmallett    cvmx_usb_internal_state_t *usb = (cvmx_usb_internal_state_t*)state;
922210284Sjmallett
923210284Sjmallett    CVMX_USB_LOG_CALLED();
924210284Sjmallett    CVMX_USB_LOG_PARAM("%p", state);
925210284Sjmallett
926210284Sjmallett    /* Make sure all pipes are closed */
927210284Sjmallett    if (usb->idle_pipes.head ||
928210284Sjmallett        usb->active_pipes[CVMX_USB_TRANSFER_ISOCHRONOUS].head ||
929210284Sjmallett        usb->active_pipes[CVMX_USB_TRANSFER_INTERRUPT].head ||
930210284Sjmallett        usb->active_pipes[CVMX_USB_TRANSFER_CONTROL].head ||
931210284Sjmallett        usb->active_pipes[CVMX_USB_TRANSFER_BULK].head)
932210284Sjmallett        CVMX_USB_RETURN(CVMX_USB_BUSY);
933210284Sjmallett
934215990Sjmallett#ifdef __CVMX_ERROR_H__
935215990Sjmallett    cvmx_error_disable_group(CVMX_ERROR_GROUP_USB, usb->index);
936215990Sjmallett#endif
937215990Sjmallett
938210284Sjmallett    /* Disable the clocks and put them in power on reset */
939210284Sjmallett    usbn_clk_ctl.u64 = __cvmx_usb_read_csr64(usb, CVMX_USBNX_CLK_CTL(usb->index));
940210284Sjmallett    usbn_clk_ctl.s.enable = 1;
941210284Sjmallett    usbn_clk_ctl.s.por = 1;
942210284Sjmallett    usbn_clk_ctl.s.hclk_rst = 1;
943210284Sjmallett    usbn_clk_ctl.s.prst = 0;
944210284Sjmallett    usbn_clk_ctl.s.hrst = 0;
945210284Sjmallett    __cvmx_usb_write_csr64(usb, CVMX_USBNX_CLK_CTL(usb->index),
946210284Sjmallett                           usbn_clk_ctl.u64);
947210284Sjmallett    CVMX_USB_RETURN(CVMX_USB_SUCCESS);
948210284Sjmallett}
949215990Sjmallett#ifdef CVMX_BUILD_FOR_LINUX_KERNEL
950215990SjmallettEXPORT_SYMBOL(cvmx_usb_shutdown);
951215990Sjmallett#endif
952210284Sjmallett
953210284Sjmallett
954210284Sjmallett/**
955210284Sjmallett * Enable a USB port. After this call succeeds, the USB port is
956210284Sjmallett * online and servicing requests.
957210284Sjmallett *
958210284Sjmallett * @param state  USB device state populated by
959210284Sjmallett *               cvmx_usb_initialize().
960210284Sjmallett *
961210284Sjmallett * @return CVMX_USB_SUCCESS or a negative error code defined in
962210284Sjmallett *         cvmx_usb_status_t.
963210284Sjmallett */
964210284Sjmallettcvmx_usb_status_t cvmx_usb_enable(cvmx_usb_state_t *state)
965210284Sjmallett{
966210284Sjmallett    cvmx_usbcx_ghwcfg3_t usbcx_ghwcfg3;
967210284Sjmallett    cvmx_usb_internal_state_t *usb = (cvmx_usb_internal_state_t*)state;
968210284Sjmallett
969210284Sjmallett    CVMX_USB_LOG_CALLED();
970210284Sjmallett    CVMX_USB_LOG_PARAM("%p", state);
971210284Sjmallett
972210284Sjmallett    usb->usbcx_hprt.u32 = __cvmx_usb_read_csr32(usb, CVMX_USBCX_HPRT(usb->index));
973210284Sjmallett
974210284Sjmallett    /* If the port is already enabled the just return. We don't need to do
975210284Sjmallett        anything */
976210284Sjmallett    if (usb->usbcx_hprt.s.prtena)
977210284Sjmallett        CVMX_USB_RETURN(CVMX_USB_SUCCESS);
978210284Sjmallett
979210284Sjmallett    /* If there is nothing plugged into the port then fail immediately */
980210284Sjmallett    if (!usb->usbcx_hprt.s.prtconnsts)
981210284Sjmallett    {
982210284Sjmallett        if (cvmx_unlikely(usb->init_flags & CVMX_USB_INITIALIZE_FLAGS_DEBUG_INFO))
983210284Sjmallett            cvmx_dprintf("%s: USB%d Nothing plugged into the port\n", __FUNCTION__, usb->index);
984210284Sjmallett        CVMX_USB_RETURN(CVMX_USB_TIMEOUT);
985210284Sjmallett    }
986210284Sjmallett
987210284Sjmallett    /* Program the port reset bit to start the reset process */
988210284Sjmallett    USB_SET_FIELD32(CVMX_USBCX_HPRT(usb->index), cvmx_usbcx_hprt_t, prtrst, 1);
989210284Sjmallett
990210284Sjmallett    /* Wait at least 50ms (high speed), or 10ms (full speed) for the reset
991210284Sjmallett        process to complete. */
992210284Sjmallett    cvmx_wait_usec(50000);
993210284Sjmallett
994210284Sjmallett    /* Program the port reset bit to 0, USBC_HPRT[PRTRST] = 0 */
995210284Sjmallett    USB_SET_FIELD32(CVMX_USBCX_HPRT(usb->index), cvmx_usbcx_hprt_t, prtrst, 0);
996210284Sjmallett
997210284Sjmallett    /* Wait for the USBC_HPRT[PRTENA]. */
998210284Sjmallett    if (CVMX_WAIT_FOR_FIELD32(CVMX_USBCX_HPRT(usb->index), cvmx_usbcx_hprt_t,
999210284Sjmallett                              prtena, ==, 1, 100000))
1000210284Sjmallett    {
1001210284Sjmallett        if (cvmx_unlikely(usb->init_flags & CVMX_USB_INITIALIZE_FLAGS_DEBUG_INFO))
1002210284Sjmallett            cvmx_dprintf("%s: Timeout waiting for the port to finish reset\n",
1003210284Sjmallett                         __FUNCTION__);
1004210284Sjmallett        CVMX_USB_RETURN(CVMX_USB_TIMEOUT);
1005210284Sjmallett    }
1006210284Sjmallett
1007210284Sjmallett    /* Read the port speed field to get the enumerated speed, USBC_HPRT[PRTSPD]. */
1008210284Sjmallett    usb->usbcx_hprt.u32 = __cvmx_usb_read_csr32(usb, CVMX_USBCX_HPRT(usb->index));
1009210284Sjmallett    if (cvmx_unlikely(usb->init_flags & CVMX_USB_INITIALIZE_FLAGS_DEBUG_INFO))
1010210284Sjmallett        cvmx_dprintf("%s: USB%d is in %s speed mode\n", __FUNCTION__, usb->index,
1011210284Sjmallett                     (usb->usbcx_hprt.s.prtspd == CVMX_USB_SPEED_HIGH) ? "high" :
1012210284Sjmallett                     (usb->usbcx_hprt.s.prtspd == CVMX_USB_SPEED_FULL) ? "full" :
1013210284Sjmallett                     "low");
1014210284Sjmallett
1015210284Sjmallett    usbcx_ghwcfg3.u32 = __cvmx_usb_read_csr32(usb, CVMX_USBCX_GHWCFG3(usb->index));
1016210284Sjmallett
1017210284Sjmallett    /* 13. Program the USBC_GRXFSIZ register to select the size of the receive
1018210284Sjmallett        FIFO (25%). */
1019210284Sjmallett    USB_SET_FIELD32(CVMX_USBCX_GRXFSIZ(usb->index), cvmx_usbcx_grxfsiz_t,
1020210284Sjmallett                    rxfdep, usbcx_ghwcfg3.s.dfifodepth / 4);
1021210284Sjmallett    /* 14. Program the USBC_GNPTXFSIZ register to select the size and the
1022210284Sjmallett        start address of the non- periodic transmit FIFO for nonperiodic
1023210284Sjmallett        transactions (50%). */
1024210284Sjmallett    {
1025210284Sjmallett        cvmx_usbcx_gnptxfsiz_t siz;
1026210284Sjmallett        siz.u32 = __cvmx_usb_read_csr32(usb, CVMX_USBCX_GNPTXFSIZ(usb->index));
1027210284Sjmallett        siz.s.nptxfdep = usbcx_ghwcfg3.s.dfifodepth / 2;
1028210284Sjmallett        siz.s.nptxfstaddr = usbcx_ghwcfg3.s.dfifodepth / 4;
1029210284Sjmallett        __cvmx_usb_write_csr32(usb, CVMX_USBCX_GNPTXFSIZ(usb->index), siz.u32);
1030210284Sjmallett    }
1031210284Sjmallett    /* 15. Program the USBC_HPTXFSIZ register to select the size and start
1032210284Sjmallett        address of the periodic transmit FIFO for periodic transactions (25%). */
1033210284Sjmallett    {
1034210284Sjmallett        cvmx_usbcx_hptxfsiz_t siz;
1035210284Sjmallett        siz.u32 = __cvmx_usb_read_csr32(usb, CVMX_USBCX_HPTXFSIZ(usb->index));
1036210284Sjmallett        siz.s.ptxfsize = usbcx_ghwcfg3.s.dfifodepth / 4;
1037210284Sjmallett        siz.s.ptxfstaddr = 3 * usbcx_ghwcfg3.s.dfifodepth / 4;
1038210284Sjmallett        __cvmx_usb_write_csr32(usb, CVMX_USBCX_HPTXFSIZ(usb->index), siz.u32);
1039210284Sjmallett    }
1040210284Sjmallett    /* Flush all FIFOs */
1041210284Sjmallett    USB_SET_FIELD32(CVMX_USBCX_GRSTCTL(usb->index), cvmx_usbcx_grstctl_t, txfnum, 0x10);
1042210284Sjmallett    USB_SET_FIELD32(CVMX_USBCX_GRSTCTL(usb->index), cvmx_usbcx_grstctl_t, txfflsh, 1);
1043210284Sjmallett    CVMX_WAIT_FOR_FIELD32(CVMX_USBCX_GRSTCTL(usb->index), cvmx_usbcx_grstctl_t,
1044210284Sjmallett                          txfflsh, ==, 0, 100);
1045210284Sjmallett    USB_SET_FIELD32(CVMX_USBCX_GRSTCTL(usb->index), cvmx_usbcx_grstctl_t, rxfflsh, 1);
1046210284Sjmallett    CVMX_WAIT_FOR_FIELD32(CVMX_USBCX_GRSTCTL(usb->index), cvmx_usbcx_grstctl_t,
1047210284Sjmallett                          rxfflsh, ==, 0, 100);
1048210284Sjmallett
1049210284Sjmallett    CVMX_USB_RETURN(CVMX_USB_SUCCESS);
1050210284Sjmallett}
1051215990Sjmallett#ifdef CVMX_BUILD_FOR_LINUX_KERNEL
1052215990SjmallettEXPORT_SYMBOL(cvmx_usb_enable);
1053215990Sjmallett#endif
1054210284Sjmallett
1055210284Sjmallett
1056210284Sjmallett/**
1057210284Sjmallett * Disable a USB port. After this call the USB port will not
1058210284Sjmallett * generate data transfers and will not generate events.
1059210284Sjmallett * Transactions in process will fail and call their
1060210284Sjmallett * associated callbacks.
1061210284Sjmallett *
1062210284Sjmallett * @param state  USB device state populated by
1063210284Sjmallett *               cvmx_usb_initialize().
1064210284Sjmallett *
1065210284Sjmallett * @return CVMX_USB_SUCCESS or a negative error code defined in
1066210284Sjmallett *         cvmx_usb_status_t.
1067210284Sjmallett */
1068210284Sjmallettcvmx_usb_status_t cvmx_usb_disable(cvmx_usb_state_t *state)
1069210284Sjmallett{
1070210284Sjmallett    cvmx_usb_internal_state_t *usb = (cvmx_usb_internal_state_t*)state;
1071210284Sjmallett
1072210284Sjmallett    CVMX_USB_LOG_CALLED();
1073210284Sjmallett    CVMX_USB_LOG_PARAM("%p", state);
1074210284Sjmallett
1075210284Sjmallett    /* Disable the port */
1076210284Sjmallett    USB_SET_FIELD32(CVMX_USBCX_HPRT(usb->index), cvmx_usbcx_hprt_t, prtena, 1);
1077210284Sjmallett    CVMX_USB_RETURN(CVMX_USB_SUCCESS);
1078210284Sjmallett}
1079215990Sjmallett#ifdef CVMX_BUILD_FOR_LINUX_KERNEL
1080215990SjmallettEXPORT_SYMBOL(cvmx_usb_disable);
1081215990Sjmallett#endif
1082210284Sjmallett
1083210284Sjmallett
1084210284Sjmallett/**
1085210284Sjmallett * Get the current state of the USB port. Use this call to
1086210284Sjmallett * determine if the usb port has anything connected, is enabled,
1087210284Sjmallett * or has some sort of error condition. The return value of this
1088210284Sjmallett * call has "changed" bits to signal of the value of some fields
1089210284Sjmallett * have changed between calls. These "changed" fields are based
1090210284Sjmallett * on the last call to cvmx_usb_set_status(). In order to clear
1091210284Sjmallett * them, you must update the status through cvmx_usb_set_status().
1092210284Sjmallett *
1093210284Sjmallett * @param state  USB device state populated by
1094210284Sjmallett *               cvmx_usb_initialize().
1095210284Sjmallett *
1096210284Sjmallett * @return Port status information
1097210284Sjmallett */
1098210284Sjmallettcvmx_usb_port_status_t cvmx_usb_get_status(cvmx_usb_state_t *state)
1099210284Sjmallett{
1100210284Sjmallett    cvmx_usbcx_hprt_t usbc_hprt;
1101210284Sjmallett    cvmx_usb_port_status_t result;
1102210284Sjmallett    cvmx_usb_internal_state_t *usb = (cvmx_usb_internal_state_t*)state;
1103210284Sjmallett
1104210284Sjmallett    memset(&result, 0, sizeof(result));
1105210284Sjmallett
1106210284Sjmallett    CVMX_USB_LOG_CALLED();
1107210284Sjmallett    CVMX_USB_LOG_PARAM("%p", state);
1108210284Sjmallett
1109210284Sjmallett    usbc_hprt.u32 = __cvmx_usb_read_csr32(usb, CVMX_USBCX_HPRT(usb->index));
1110210284Sjmallett    result.port_enabled = usbc_hprt.s.prtena;
1111210284Sjmallett    result.port_over_current = usbc_hprt.s.prtovrcurract;
1112210284Sjmallett    result.port_powered = usbc_hprt.s.prtpwr;
1113210284Sjmallett    result.port_speed = usbc_hprt.s.prtspd;
1114210284Sjmallett    result.connected = usbc_hprt.s.prtconnsts;
1115210284Sjmallett    result.connect_change = (result.connected != usb->port_status.connected);
1116210284Sjmallett
1117210284Sjmallett    if (cvmx_unlikely(usb->init_flags & CVMX_USB_INITIALIZE_FLAGS_DEBUG_CALLS))
1118210284Sjmallett        cvmx_dprintf("%*s%s: returned port enabled=%d, over_current=%d, powered=%d, speed=%d, connected=%d, connect_change=%d\n",
1119210284Sjmallett                     2*(--usb->indent), "", __FUNCTION__,
1120210284Sjmallett                     result.port_enabled,
1121210284Sjmallett                     result.port_over_current,
1122210284Sjmallett                     result.port_powered,
1123210284Sjmallett                     result.port_speed,
1124210284Sjmallett                     result.connected,
1125210284Sjmallett                     result.connect_change);
1126210284Sjmallett    return result;
1127210284Sjmallett}
1128215990Sjmallett#ifdef CVMX_BUILD_FOR_LINUX_KERNEL
1129215990SjmallettEXPORT_SYMBOL(cvmx_usb_get_status);
1130215990Sjmallett#endif
1131210284Sjmallett
1132210284Sjmallett
1133210284Sjmallett/**
1134210284Sjmallett * Set the current state of the USB port. The status is used as
1135210284Sjmallett * a reference for the "changed" bits returned by
1136210284Sjmallett * cvmx_usb_get_status(). Other than serving as a reference, the
1137210284Sjmallett * status passed to this function is not used. No fields can be
1138210284Sjmallett * changed through this call.
1139210284Sjmallett *
1140210284Sjmallett * @param state  USB device state populated by
1141210284Sjmallett *               cvmx_usb_initialize().
1142210284Sjmallett * @param port_status
1143210284Sjmallett *               Port status to set, most like returned by cvmx_usb_get_status()
1144210284Sjmallett */
1145210284Sjmallettvoid cvmx_usb_set_status(cvmx_usb_state_t *state, cvmx_usb_port_status_t port_status)
1146210284Sjmallett{
1147210284Sjmallett    cvmx_usb_internal_state_t *usb = (cvmx_usb_internal_state_t*)state;
1148210284Sjmallett    CVMX_USB_LOG_CALLED();
1149210284Sjmallett    CVMX_USB_LOG_PARAM("%p", state);
1150210284Sjmallett    usb->port_status = port_status;
1151210284Sjmallett    CVMX_USB_RETURN_NOTHING();
1152210284Sjmallett}
1153215990Sjmallett#ifdef CVMX_BUILD_FOR_LINUX_KERNEL
1154215990SjmallettEXPORT_SYMBOL(cvmx_usb_set_status);
1155215990Sjmallett#endif
1156210284Sjmallett
1157210284Sjmallett
1158210284Sjmallett/**
1159210284Sjmallett * @INTERNAL
1160210284Sjmallett * Convert a USB transaction into a handle
1161210284Sjmallett *
1162210284Sjmallett * @param usb    USB device state populated by
1163210284Sjmallett *               cvmx_usb_initialize().
1164210284Sjmallett * @param transaction
1165210284Sjmallett *               Transaction to get handle for
1166210284Sjmallett *
1167210284Sjmallett * @return Handle
1168210284Sjmallett */
1169210284Sjmallettstatic inline int __cvmx_usb_get_submit_handle(cvmx_usb_internal_state_t *usb,
1170210284Sjmallett                                        cvmx_usb_transaction_t *transaction)
1171210284Sjmallett{
1172210284Sjmallett    return ((unsigned long)transaction - (unsigned long)usb->transaction) /
1173210284Sjmallett            sizeof(*transaction);
1174210284Sjmallett}
1175210284Sjmallett
1176210284Sjmallett
1177210284Sjmallett/**
1178210284Sjmallett * @INTERNAL
1179210284Sjmallett * Convert a USB pipe into a handle
1180210284Sjmallett *
1181210284Sjmallett * @param usb    USB device state populated by
1182210284Sjmallett *               cvmx_usb_initialize().
1183210284Sjmallett * @param pipe   Pipe to get handle for
1184210284Sjmallett *
1185210284Sjmallett * @return Handle
1186210284Sjmallett */
1187210284Sjmallettstatic inline int __cvmx_usb_get_pipe_handle(cvmx_usb_internal_state_t *usb,
1188210284Sjmallett                                        cvmx_usb_pipe_t *pipe)
1189210284Sjmallett{
1190210284Sjmallett    return ((unsigned long)pipe - (unsigned long)usb->pipe) / sizeof(*pipe);
1191210284Sjmallett}
1192210284Sjmallett
1193210284Sjmallett
1194210284Sjmallett/**
1195210284Sjmallett * Open a virtual pipe between the host and a USB device. A pipe
1196210284Sjmallett * must be opened before data can be transferred between a device
1197210284Sjmallett * and Octeon.
1198210284Sjmallett *
1199210284Sjmallett * @param state      USB device state populated by
1200210284Sjmallett *                   cvmx_usb_initialize().
1201210284Sjmallett * @param flags      Optional pipe flags defined in
1202210284Sjmallett *                   cvmx_usb_pipe_flags_t.
1203210284Sjmallett * @param device_addr
1204210284Sjmallett *                   USB device address to open the pipe to
1205210284Sjmallett *                   (0-127).
1206210284Sjmallett * @param endpoint_num
1207210284Sjmallett *                   USB endpoint number to open the pipe to
1208210284Sjmallett *                   (0-15).
1209210284Sjmallett * @param device_speed
1210210284Sjmallett *                   The speed of the device the pipe is going
1211210284Sjmallett *                   to. This must match the device's speed,
1212210284Sjmallett *                   which may be different than the port speed.
1213210284Sjmallett * @param max_packet The maximum packet length the device can
1214210284Sjmallett *                   transmit/receive (low speed=0-8, full
1215210284Sjmallett *                   speed=0-1023, high speed=0-1024). This value
1216215990Sjmallett *                   comes from the standard endpoint descriptor
1217210284Sjmallett *                   field wMaxPacketSize bits <10:0>.
1218210284Sjmallett * @param transfer_type
1219210284Sjmallett *                   The type of transfer this pipe is for.
1220210284Sjmallett * @param transfer_dir
1221210284Sjmallett *                   The direction the pipe is in. This is not
1222210284Sjmallett *                   used for control pipes.
1223210284Sjmallett * @param interval   For ISOCHRONOUS and INTERRUPT transfers,
1224210284Sjmallett *                   this is how often the transfer is scheduled
1225210284Sjmallett *                   for. All other transfers should specify
1226210284Sjmallett *                   zero. The units are in frames (8000/sec at
1227210284Sjmallett *                   high speed, 1000/sec for full speed).
1228210284Sjmallett * @param multi_count
1229210284Sjmallett *                   For high speed devices, this is the maximum
1230210284Sjmallett *                   allowed number of packet per microframe.
1231210284Sjmallett *                   Specify zero for non high speed devices. This
1232215990Sjmallett *                   value comes from the standard endpoint descriptor
1233210284Sjmallett *                   field wMaxPacketSize bits <12:11>.
1234210284Sjmallett * @param hub_device_addr
1235210284Sjmallett *                   Hub device address this device is connected
1236210284Sjmallett *                   to. Devices connected directly to Octeon
1237210284Sjmallett *                   use zero. This is only used when the device
1238210284Sjmallett *                   is full/low speed behind a high speed hub.
1239210284Sjmallett *                   The address will be of the high speed hub,
1240210284Sjmallett *                   not and full speed hubs after it.
1241210284Sjmallett * @param hub_port   Which port on the hub the device is
1242210284Sjmallett *                   connected. Use zero for devices connected
1243210284Sjmallett *                   directly to Octeon. Like hub_device_addr,
1244210284Sjmallett *                   this is only used for full/low speed
1245210284Sjmallett *                   devices behind a high speed hub.
1246210284Sjmallett *
1247210284Sjmallett * @return A non negative value is a pipe handle. Negative
1248210284Sjmallett *         values are failure codes from cvmx_usb_status_t.
1249210284Sjmallett */
1250210284Sjmallettint cvmx_usb_open_pipe(cvmx_usb_state_t *state, cvmx_usb_pipe_flags_t flags,
1251210284Sjmallett                       int device_addr, int endpoint_num,
1252210284Sjmallett                       cvmx_usb_speed_t device_speed, int max_packet,
1253210284Sjmallett                       cvmx_usb_transfer_t transfer_type,
1254210284Sjmallett                       cvmx_usb_direction_t transfer_dir, int interval,
1255210284Sjmallett                       int multi_count, int hub_device_addr, int hub_port)
1256210284Sjmallett{
1257210284Sjmallett    cvmx_usb_pipe_t *pipe;
1258210284Sjmallett    cvmx_usb_internal_state_t *usb = (cvmx_usb_internal_state_t*)state;
1259210284Sjmallett
1260210284Sjmallett    CVMX_USB_LOG_CALLED();
1261210284Sjmallett    CVMX_USB_LOG_PARAM("%p", state);
1262210284Sjmallett    CVMX_USB_LOG_PARAM("0x%x", flags);
1263210284Sjmallett    CVMX_USB_LOG_PARAM("%d", device_addr);
1264210284Sjmallett    CVMX_USB_LOG_PARAM("%d", endpoint_num);
1265210284Sjmallett    CVMX_USB_LOG_PARAM("%d", device_speed);
1266210284Sjmallett    CVMX_USB_LOG_PARAM("%d", max_packet);
1267210284Sjmallett    CVMX_USB_LOG_PARAM("%d", transfer_type);
1268210284Sjmallett    CVMX_USB_LOG_PARAM("%d", transfer_dir);
1269210284Sjmallett    CVMX_USB_LOG_PARAM("%d", interval);
1270210284Sjmallett    CVMX_USB_LOG_PARAM("%d", multi_count);
1271210284Sjmallett    CVMX_USB_LOG_PARAM("%d", hub_device_addr);
1272210284Sjmallett    CVMX_USB_LOG_PARAM("%d", hub_port);
1273210284Sjmallett
1274210284Sjmallett    if (cvmx_unlikely((device_addr < 0) || (device_addr > MAX_USB_ADDRESS)))
1275210284Sjmallett        CVMX_USB_RETURN(CVMX_USB_INVALID_PARAM);
1276210284Sjmallett    if (cvmx_unlikely((endpoint_num < 0) || (endpoint_num > MAX_USB_ENDPOINT)))
1277210284Sjmallett        CVMX_USB_RETURN(CVMX_USB_INVALID_PARAM);
1278210284Sjmallett    if (cvmx_unlikely(device_speed > CVMX_USB_SPEED_LOW))
1279210284Sjmallett        CVMX_USB_RETURN(CVMX_USB_INVALID_PARAM);
1280210284Sjmallett    if (cvmx_unlikely((max_packet <= 0) || (max_packet > 1024)))
1281210284Sjmallett        CVMX_USB_RETURN(CVMX_USB_INVALID_PARAM);
1282210284Sjmallett    if (cvmx_unlikely(transfer_type > CVMX_USB_TRANSFER_INTERRUPT))
1283210284Sjmallett        CVMX_USB_RETURN(CVMX_USB_INVALID_PARAM);
1284210284Sjmallett    if (cvmx_unlikely((transfer_dir != CVMX_USB_DIRECTION_OUT) &&
1285210284Sjmallett        (transfer_dir != CVMX_USB_DIRECTION_IN)))
1286210284Sjmallett        CVMX_USB_RETURN(CVMX_USB_INVALID_PARAM);
1287210284Sjmallett    if (cvmx_unlikely(interval < 0))
1288210284Sjmallett        CVMX_USB_RETURN(CVMX_USB_INVALID_PARAM);
1289210284Sjmallett    if (cvmx_unlikely((transfer_type == CVMX_USB_TRANSFER_CONTROL) && interval))
1290210284Sjmallett        CVMX_USB_RETURN(CVMX_USB_INVALID_PARAM);
1291210284Sjmallett    if (cvmx_unlikely(multi_count < 0))
1292210284Sjmallett        CVMX_USB_RETURN(CVMX_USB_INVALID_PARAM);
1293210284Sjmallett    if (cvmx_unlikely((device_speed != CVMX_USB_SPEED_HIGH) &&
1294210284Sjmallett        (multi_count != 0)))
1295210284Sjmallett        CVMX_USB_RETURN(CVMX_USB_INVALID_PARAM);
1296210284Sjmallett    if (cvmx_unlikely((hub_device_addr < 0) || (hub_device_addr > MAX_USB_ADDRESS)))
1297210284Sjmallett        CVMX_USB_RETURN(CVMX_USB_INVALID_PARAM);
1298210284Sjmallett    if (cvmx_unlikely((hub_port < 0) || (hub_port > MAX_USB_HUB_PORT)))
1299210284Sjmallett        CVMX_USB_RETURN(CVMX_USB_INVALID_PARAM);
1300210284Sjmallett
1301210284Sjmallett    /* Find a free pipe */
1302210284Sjmallett    pipe = usb->free_pipes.head;
1303210284Sjmallett    if (!pipe)
1304210284Sjmallett        CVMX_USB_RETURN(CVMX_USB_NO_MEMORY);
1305210284Sjmallett    __cvmx_usb_remove_pipe(&usb->free_pipes, pipe);
1306210284Sjmallett    pipe->flags = flags | __CVMX_USB_PIPE_FLAGS_OPEN;
1307210284Sjmallett    if ((device_speed == CVMX_USB_SPEED_HIGH) &&
1308210284Sjmallett        (transfer_dir == CVMX_USB_DIRECTION_OUT) &&
1309210284Sjmallett        (transfer_type == CVMX_USB_TRANSFER_BULK))
1310210284Sjmallett        pipe->flags |= __CVMX_USB_PIPE_FLAGS_NEED_PING;
1311210284Sjmallett    pipe->device_addr = device_addr;
1312210284Sjmallett    pipe->endpoint_num = endpoint_num;
1313210284Sjmallett    pipe->device_speed = device_speed;
1314210284Sjmallett    pipe->max_packet = max_packet;
1315210284Sjmallett    pipe->transfer_type = transfer_type;
1316210284Sjmallett    pipe->transfer_dir = transfer_dir;
1317210284Sjmallett    /* All pipes use interval to rate limit NAK processing. Force an interval
1318210284Sjmallett        if one wasn't supplied */
1319210284Sjmallett    if (!interval)
1320210284Sjmallett        interval = 1;
1321215990Sjmallett    if (__cvmx_usb_pipe_needs_split(usb, pipe))
1322215990Sjmallett    {
1323215990Sjmallett        pipe->interval = interval*8;
1324215990Sjmallett        /* Force start splits to be schedule on uFrame 0 */
1325215990Sjmallett        pipe->next_tx_frame = ((usb->frame_number+7)&~7) + pipe->interval;
1326215990Sjmallett    }
1327210284Sjmallett    else
1328215990Sjmallett    {
1329215990Sjmallett        pipe->interval = interval;
1330215990Sjmallett        pipe->next_tx_frame = usb->frame_number + pipe->interval;
1331215990Sjmallett    }
1332210284Sjmallett    pipe->multi_count = multi_count;
1333210284Sjmallett    pipe->hub_device_addr = hub_device_addr;
1334210284Sjmallett    pipe->hub_port = hub_port;
1335210284Sjmallett    pipe->pid_toggle = 0;
1336210284Sjmallett    pipe->split_sc_frame = -1;
1337210284Sjmallett    __cvmx_usb_append_pipe(&usb->idle_pipes, pipe);
1338210284Sjmallett
1339210284Sjmallett    /* We don't need to tell the hardware about this pipe yet since
1340210284Sjmallett        it doesn't have any submitted requests */
1341210284Sjmallett
1342210284Sjmallett    CVMX_USB_RETURN(__cvmx_usb_get_pipe_handle(usb, pipe));
1343210284Sjmallett}
1344215990Sjmallett#ifdef CVMX_BUILD_FOR_LINUX_KERNEL
1345215990SjmallettEXPORT_SYMBOL(cvmx_usb_open_pipe);
1346215990Sjmallett#endif
1347210284Sjmallett
1348210284Sjmallett
1349210284Sjmallett/**
1350210284Sjmallett * @INTERNAL
1351215990Sjmallett * Poll the RX FIFOs and remove data as needed. This function is only used
1352215990Sjmallett * in non DMA mode. It is very important that this function be called quickly
1353215990Sjmallett * enough to prevent FIFO overflow.
1354215990Sjmallett *
1355215990Sjmallett * @param usb     USB device state populated by
1356215990Sjmallett *                cvmx_usb_initialize().
1357215990Sjmallett */
1358215990Sjmallettstatic void __cvmx_usb_poll_rx_fifo(cvmx_usb_internal_state_t *usb)
1359215990Sjmallett{
1360215990Sjmallett    cvmx_usbcx_grxstsph_t rx_status;
1361215990Sjmallett    int channel;
1362215990Sjmallett    int bytes;
1363215990Sjmallett    uint64_t address;
1364215990Sjmallett    uint32_t *ptr;
1365215990Sjmallett
1366215990Sjmallett    CVMX_USB_LOG_CALLED();
1367215990Sjmallett    CVMX_USB_LOG_PARAM("%p", usb);
1368215990Sjmallett
1369215990Sjmallett    rx_status.u32 = __cvmx_usb_read_csr32(usb, CVMX_USBCX_GRXSTSPH(usb->index));
1370215990Sjmallett    /* Only read data if IN data is there */
1371215990Sjmallett    if (rx_status.s.pktsts != 2)
1372215990Sjmallett        CVMX_USB_RETURN_NOTHING();
1373215990Sjmallett    /* Check if no data is available */
1374215990Sjmallett    if (!rx_status.s.bcnt)
1375215990Sjmallett        CVMX_USB_RETURN_NOTHING();
1376215990Sjmallett
1377215990Sjmallett    channel = rx_status.s.chnum;
1378215990Sjmallett    bytes = rx_status.s.bcnt;
1379215990Sjmallett    if (!bytes)
1380215990Sjmallett        CVMX_USB_RETURN_NOTHING();
1381215990Sjmallett
1382215990Sjmallett    /* Get where the DMA engine would have written this data */
1383215990Sjmallett    address = __cvmx_usb_read_csr64(usb, CVMX_USBNX_DMA0_INB_CHN0(usb->index) + channel*8);
1384215990Sjmallett    ptr = cvmx_phys_to_ptr(address);
1385215990Sjmallett    __cvmx_usb_write_csr64(usb, CVMX_USBNX_DMA0_INB_CHN0(usb->index) + channel*8, address + bytes);
1386215990Sjmallett
1387215990Sjmallett    /* Loop writing the FIFO data for this packet into memory */
1388215990Sjmallett    while (bytes > 0)
1389215990Sjmallett    {
1390215990Sjmallett        *ptr++ = __cvmx_usb_read_csr32(usb, USB_FIFO_ADDRESS(channel, usb->index));
1391215990Sjmallett        bytes -= 4;
1392215990Sjmallett    }
1393215990Sjmallett    CVMX_SYNCW;
1394215990Sjmallett
1395215990Sjmallett    CVMX_USB_RETURN_NOTHING();
1396215990Sjmallett}
1397215990Sjmallett
1398215990Sjmallett
1399215990Sjmallett/**
1400215990Sjmallett * Fill the TX hardware fifo with data out of the software
1401215990Sjmallett * fifos
1402215990Sjmallett *
1403215990Sjmallett * @param usb       USB device state populated by
1404215990Sjmallett *                  cvmx_usb_initialize().
1405215990Sjmallett * @param fifo      Software fifo to use
1406215990Sjmallett * @param available Amount of space in the hardware fifo
1407215990Sjmallett *
1408215990Sjmallett * @return Non zero if the hardware fifo was too small and needs
1409215990Sjmallett *         to be serviced again.
1410215990Sjmallett */
1411215990Sjmallettstatic int __cvmx_usb_fill_tx_hw(cvmx_usb_internal_state_t *usb, cvmx_usb_tx_fifo_t *fifo, int available)
1412215990Sjmallett{
1413215990Sjmallett    CVMX_USB_LOG_CALLED();
1414215990Sjmallett    CVMX_USB_LOG_PARAM("%p", usb);
1415215990Sjmallett    CVMX_USB_LOG_PARAM("%p", fifo);
1416215990Sjmallett    CVMX_USB_LOG_PARAM("%d", available);
1417215990Sjmallett
1418215990Sjmallett    /* We're done either when there isn't anymore space or the software FIFO
1419215990Sjmallett        is empty */
1420215990Sjmallett    while (available && (fifo->head != fifo->tail))
1421215990Sjmallett    {
1422215990Sjmallett        int i = fifo->tail;
1423215990Sjmallett        const uint32_t *ptr = cvmx_phys_to_ptr(fifo->entry[i].address);
1424215990Sjmallett        uint64_t csr_address = USB_FIFO_ADDRESS(fifo->entry[i].channel, usb->index) ^ 4;
1425215990Sjmallett        int words = available;
1426215990Sjmallett
1427215990Sjmallett        /* Limit the amount of data to waht the SW fifo has */
1428215990Sjmallett        if (fifo->entry[i].size <= available)
1429215990Sjmallett        {
1430215990Sjmallett            words = fifo->entry[i].size;
1431215990Sjmallett            fifo->tail++;
1432215990Sjmallett            if (fifo->tail > MAX_CHANNELS)
1433215990Sjmallett                fifo->tail = 0;
1434215990Sjmallett        }
1435215990Sjmallett
1436215990Sjmallett        /* Update the next locations and counts */
1437215990Sjmallett        available -= words;
1438215990Sjmallett        fifo->entry[i].address += words * 4;
1439215990Sjmallett        fifo->entry[i].size -= words;
1440215990Sjmallett
1441215990Sjmallett        /* Write the HW fifo data. The read every three writes is due
1442215990Sjmallett            to an errata on CN3XXX chips */
1443215990Sjmallett        while (words > 3)
1444215990Sjmallett        {
1445215990Sjmallett            cvmx_write64_uint32(csr_address, *ptr++);
1446215990Sjmallett            cvmx_write64_uint32(csr_address, *ptr++);
1447215990Sjmallett            cvmx_write64_uint32(csr_address, *ptr++);
1448215990Sjmallett            cvmx_read64_uint64(CVMX_USBNX_DMA0_INB_CHN0(usb->index));
1449215990Sjmallett            words -= 3;
1450215990Sjmallett        }
1451215990Sjmallett        cvmx_write64_uint32(csr_address, *ptr++);
1452215990Sjmallett        if (--words)
1453215990Sjmallett        {
1454215990Sjmallett            cvmx_write64_uint32(csr_address, *ptr++);
1455215990Sjmallett            if (--words)
1456215990Sjmallett                cvmx_write64_uint32(csr_address, *ptr++);
1457215990Sjmallett        }
1458215990Sjmallett        cvmx_read64_uint64(CVMX_USBNX_DMA0_INB_CHN0(usb->index));
1459215990Sjmallett    }
1460215990Sjmallett    CVMX_USB_RETURN(fifo->head != fifo->tail);
1461215990Sjmallett}
1462215990Sjmallett
1463215990Sjmallett
1464215990Sjmallett/**
1465215990Sjmallett * Check the hardware FIFOs and fill them as needed
1466215990Sjmallett *
1467215990Sjmallett * @param usb    USB device state populated by
1468215990Sjmallett *               cvmx_usb_initialize().
1469215990Sjmallett */
1470215990Sjmallettstatic void __cvmx_usb_poll_tx_fifo(cvmx_usb_internal_state_t *usb)
1471215990Sjmallett{
1472215990Sjmallett    CVMX_USB_LOG_CALLED();
1473215990Sjmallett    CVMX_USB_LOG_PARAM("%p", usb);
1474215990Sjmallett
1475215990Sjmallett    if (usb->periodic.head != usb->periodic.tail)
1476215990Sjmallett    {
1477215990Sjmallett        cvmx_usbcx_hptxsts_t tx_status;
1478215990Sjmallett        tx_status.u32 = __cvmx_usb_read_csr32(usb, CVMX_USBCX_HPTXSTS(usb->index));
1479215990Sjmallett        if (__cvmx_usb_fill_tx_hw(usb, &usb->periodic, tx_status.s.ptxfspcavail))
1480215990Sjmallett            USB_SET_FIELD32(CVMX_USBCX_GINTMSK(usb->index), cvmx_usbcx_gintmsk_t, ptxfempmsk, 1);
1481215990Sjmallett        else
1482215990Sjmallett            USB_SET_FIELD32(CVMX_USBCX_GINTMSK(usb->index), cvmx_usbcx_gintmsk_t, ptxfempmsk, 0);
1483215990Sjmallett    }
1484215990Sjmallett
1485215990Sjmallett    if (usb->nonperiodic.head != usb->nonperiodic.tail)
1486215990Sjmallett    {
1487215990Sjmallett        cvmx_usbcx_gnptxsts_t tx_status;
1488215990Sjmallett        tx_status.u32 = __cvmx_usb_read_csr32(usb, CVMX_USBCX_GNPTXSTS(usb->index));
1489215990Sjmallett        if (__cvmx_usb_fill_tx_hw(usb, &usb->nonperiodic, tx_status.s.nptxfspcavail))
1490215990Sjmallett            USB_SET_FIELD32(CVMX_USBCX_GINTMSK(usb->index), cvmx_usbcx_gintmsk_t, nptxfempmsk, 1);
1491215990Sjmallett        else
1492215990Sjmallett            USB_SET_FIELD32(CVMX_USBCX_GINTMSK(usb->index), cvmx_usbcx_gintmsk_t, nptxfempmsk, 0);
1493215990Sjmallett    }
1494215990Sjmallett
1495215990Sjmallett    CVMX_USB_RETURN_NOTHING();
1496215990Sjmallett}
1497215990Sjmallett
1498215990Sjmallett
1499215990Sjmallett/**
1500215990Sjmallett * @INTERNAL
1501215990Sjmallett * Fill the TX FIFO with an outgoing packet
1502215990Sjmallett *
1503215990Sjmallett * @param usb     USB device state populated by
1504215990Sjmallett *                cvmx_usb_initialize().
1505215990Sjmallett * @param channel Channel number to get packet from
1506215990Sjmallett */
1507215990Sjmallettstatic void __cvmx_usb_fill_tx_fifo(cvmx_usb_internal_state_t *usb, int channel)
1508215990Sjmallett{
1509215990Sjmallett    cvmx_usbcx_hccharx_t hcchar;
1510215990Sjmallett    cvmx_usbcx_hcspltx_t usbc_hcsplt;
1511215990Sjmallett    cvmx_usbcx_hctsizx_t usbc_hctsiz;
1512215990Sjmallett    cvmx_usb_tx_fifo_t *fifo;
1513215990Sjmallett
1514215990Sjmallett    CVMX_USB_LOG_CALLED();
1515215990Sjmallett    CVMX_USB_LOG_PARAM("%p", usb);
1516215990Sjmallett    CVMX_USB_LOG_PARAM("%d", channel);
1517215990Sjmallett
1518215990Sjmallett    /* We only need to fill data on outbound channels */
1519215990Sjmallett    hcchar.u32 = __cvmx_usb_read_csr32(usb, CVMX_USBCX_HCCHARX(channel, usb->index));
1520215990Sjmallett    if (hcchar.s.epdir != CVMX_USB_DIRECTION_OUT)
1521215990Sjmallett        CVMX_USB_RETURN_NOTHING();
1522215990Sjmallett
1523215990Sjmallett    /* OUT Splits only have data on the start and not the complete */
1524215990Sjmallett    usbc_hcsplt.u32 = __cvmx_usb_read_csr32(usb, CVMX_USBCX_HCSPLTX(channel, usb->index));
1525215990Sjmallett    if (usbc_hcsplt.s.spltena && usbc_hcsplt.s.compsplt)
1526215990Sjmallett        CVMX_USB_RETURN_NOTHING();
1527215990Sjmallett
1528215990Sjmallett    /* Find out how many bytes we need to fill and convert it into 32bit words */
1529215990Sjmallett    usbc_hctsiz.u32 = __cvmx_usb_read_csr32(usb, CVMX_USBCX_HCTSIZX(channel, usb->index));
1530215990Sjmallett    if (!usbc_hctsiz.s.xfersize)
1531215990Sjmallett        CVMX_USB_RETURN_NOTHING();
1532215990Sjmallett
1533215990Sjmallett    if ((hcchar.s.eptype == CVMX_USB_TRANSFER_INTERRUPT) ||
1534215990Sjmallett        (hcchar.s.eptype == CVMX_USB_TRANSFER_ISOCHRONOUS))
1535215990Sjmallett        fifo = &usb->periodic;
1536215990Sjmallett    else
1537215990Sjmallett        fifo = &usb->nonperiodic;
1538215990Sjmallett
1539215990Sjmallett    fifo->entry[fifo->head].channel = channel;
1540215990Sjmallett    fifo->entry[fifo->head].address = __cvmx_usb_read_csr64(usb, CVMX_USBNX_DMA0_OUTB_CHN0(usb->index) + channel*8);
1541215990Sjmallett    fifo->entry[fifo->head].size = (usbc_hctsiz.s.xfersize+3)>>2;
1542215990Sjmallett    fifo->head++;
1543215990Sjmallett    if (fifo->head > MAX_CHANNELS)
1544215990Sjmallett        fifo->head = 0;
1545215990Sjmallett
1546215990Sjmallett    __cvmx_usb_poll_tx_fifo(usb);
1547215990Sjmallett
1548215990Sjmallett    CVMX_USB_RETURN_NOTHING();
1549215990Sjmallett}
1550215990Sjmallett
1551215990Sjmallett/**
1552215990Sjmallett * @INTERNAL
1553210284Sjmallett * Perform channel specific setup for Control transactions. All
1554210284Sjmallett * the generic stuff will already have been done in
1555210284Sjmallett * __cvmx_usb_start_channel()
1556210284Sjmallett *
1557210284Sjmallett * @param usb     USB device state populated by
1558210284Sjmallett *                cvmx_usb_initialize().
1559210284Sjmallett * @param channel Channel to setup
1560210284Sjmallett * @param pipe    Pipe for control transaction
1561210284Sjmallett */
1562210284Sjmallettstatic void __cvmx_usb_start_channel_control(cvmx_usb_internal_state_t *usb,
1563210284Sjmallett                                             int channel,
1564210284Sjmallett                                             cvmx_usb_pipe_t *pipe)
1565210284Sjmallett{
1566210284Sjmallett    cvmx_usb_transaction_t *transaction = pipe->head;
1567210284Sjmallett    cvmx_usb_control_header_t *header = cvmx_phys_to_ptr(transaction->control_header);
1568210284Sjmallett    int bytes_to_transfer = transaction->buffer_length - transaction->actual_bytes;
1569215990Sjmallett    int packets_to_transfer;
1570210284Sjmallett    cvmx_usbcx_hctsizx_t usbc_hctsiz;
1571210284Sjmallett
1572210284Sjmallett    CVMX_USB_LOG_CALLED();
1573210284Sjmallett    CVMX_USB_LOG_PARAM("%p", usb);
1574210284Sjmallett    CVMX_USB_LOG_PARAM("%d", channel);
1575210284Sjmallett    CVMX_USB_LOG_PARAM("%p", pipe);
1576210284Sjmallett
1577210284Sjmallett    usbc_hctsiz.u32 = __cvmx_usb_read_csr32(usb, CVMX_USBCX_HCTSIZX(channel, usb->index));
1578210284Sjmallett
1579210284Sjmallett    switch (transaction->stage)
1580210284Sjmallett    {
1581210284Sjmallett        case CVMX_USB_STAGE_NON_CONTROL:
1582210284Sjmallett        case CVMX_USB_STAGE_NON_CONTROL_SPLIT_COMPLETE:
1583210284Sjmallett            cvmx_dprintf("%s: ERROR - Non control stage\n", __FUNCTION__);
1584210284Sjmallett            break;
1585210284Sjmallett        case CVMX_USB_STAGE_SETUP:
1586210284Sjmallett            usbc_hctsiz.s.pid = 3; /* Setup */
1587215990Sjmallett            bytes_to_transfer = sizeof(*header);
1588210284Sjmallett            /* All Control operations start with a setup going OUT */
1589210284Sjmallett            USB_SET_FIELD32(CVMX_USBCX_HCCHARX(channel, usb->index), cvmx_usbcx_hccharx_t, epdir, CVMX_USB_DIRECTION_OUT);
1590210284Sjmallett            /* Setup send the control header instead of the buffer data. The
1591210284Sjmallett                buffer data will be used in the next stage */
1592210284Sjmallett            __cvmx_usb_write_csr64(usb, CVMX_USBNX_DMA0_OUTB_CHN0(usb->index) + channel*8, transaction->control_header);
1593210284Sjmallett            break;
1594210284Sjmallett        case CVMX_USB_STAGE_SETUP_SPLIT_COMPLETE:
1595210284Sjmallett            usbc_hctsiz.s.pid = 3; /* Setup */
1596215990Sjmallett            bytes_to_transfer = 0;
1597210284Sjmallett            /* All Control operations start with a setup going OUT */
1598210284Sjmallett            USB_SET_FIELD32(CVMX_USBCX_HCCHARX(channel, usb->index), cvmx_usbcx_hccharx_t, epdir, CVMX_USB_DIRECTION_OUT);
1599210284Sjmallett            USB_SET_FIELD32(CVMX_USBCX_HCSPLTX(channel, usb->index), cvmx_usbcx_hcspltx_t, compsplt, 1);
1600210284Sjmallett            break;
1601210284Sjmallett        case CVMX_USB_STAGE_DATA:
1602210284Sjmallett            usbc_hctsiz.s.pid = __cvmx_usb_get_data_pid(pipe);
1603210284Sjmallett            if (__cvmx_usb_pipe_needs_split(usb, pipe))
1604210284Sjmallett            {
1605215990Sjmallett                if (header->s.request_type & 0x80)
1606215990Sjmallett                    bytes_to_transfer = 0;
1607215990Sjmallett                else if (bytes_to_transfer > pipe->max_packet)
1608215990Sjmallett                    bytes_to_transfer = pipe->max_packet;
1609210284Sjmallett            }
1610210284Sjmallett            USB_SET_FIELD32(CVMX_USBCX_HCCHARX(channel, usb->index),
1611210284Sjmallett                            cvmx_usbcx_hccharx_t, epdir,
1612210284Sjmallett                            ((header->s.request_type & 0x80) ?
1613210284Sjmallett                             CVMX_USB_DIRECTION_IN :
1614210284Sjmallett                             CVMX_USB_DIRECTION_OUT));
1615210284Sjmallett            break;
1616210284Sjmallett        case CVMX_USB_STAGE_DATA_SPLIT_COMPLETE:
1617210284Sjmallett            usbc_hctsiz.s.pid = __cvmx_usb_get_data_pid(pipe);
1618215990Sjmallett            if (!(header->s.request_type & 0x80))
1619215990Sjmallett                bytes_to_transfer = 0;
1620210284Sjmallett            USB_SET_FIELD32(CVMX_USBCX_HCCHARX(channel, usb->index),
1621210284Sjmallett                            cvmx_usbcx_hccharx_t, epdir,
1622210284Sjmallett                            ((header->s.request_type & 0x80) ?
1623210284Sjmallett                             CVMX_USB_DIRECTION_IN :
1624210284Sjmallett                             CVMX_USB_DIRECTION_OUT));
1625210284Sjmallett            USB_SET_FIELD32(CVMX_USBCX_HCSPLTX(channel, usb->index), cvmx_usbcx_hcspltx_t, compsplt, 1);
1626210284Sjmallett            break;
1627210284Sjmallett        case CVMX_USB_STAGE_STATUS:
1628210284Sjmallett            usbc_hctsiz.s.pid = __cvmx_usb_get_data_pid(pipe);
1629215990Sjmallett            bytes_to_transfer = 0;
1630210284Sjmallett            USB_SET_FIELD32(CVMX_USBCX_HCCHARX(channel, usb->index), cvmx_usbcx_hccharx_t, epdir,
1631210284Sjmallett                            ((header->s.request_type & 0x80) ?
1632210284Sjmallett                             CVMX_USB_DIRECTION_OUT :
1633210284Sjmallett                             CVMX_USB_DIRECTION_IN));
1634210284Sjmallett            break;
1635210284Sjmallett        case CVMX_USB_STAGE_STATUS_SPLIT_COMPLETE:
1636210284Sjmallett            usbc_hctsiz.s.pid = __cvmx_usb_get_data_pid(pipe);
1637215990Sjmallett            bytes_to_transfer = 0;
1638210284Sjmallett            USB_SET_FIELD32(CVMX_USBCX_HCCHARX(channel, usb->index), cvmx_usbcx_hccharx_t, epdir,
1639210284Sjmallett                            ((header->s.request_type & 0x80) ?
1640210284Sjmallett                             CVMX_USB_DIRECTION_OUT :
1641210284Sjmallett                             CVMX_USB_DIRECTION_IN));
1642210284Sjmallett            USB_SET_FIELD32(CVMX_USBCX_HCSPLTX(channel, usb->index), cvmx_usbcx_hcspltx_t, compsplt, 1);
1643210284Sjmallett            break;
1644210284Sjmallett    }
1645210284Sjmallett
1646215990Sjmallett    /* Make sure the transfer never exceeds the byte limit of the hardware.
1647215990Sjmallett        Further bytes will be sent as continued transactions */
1648215990Sjmallett    if (bytes_to_transfer > MAX_TRANSFER_BYTES)
1649215990Sjmallett    {
1650215990Sjmallett        /* Round MAX_TRANSFER_BYTES to a multiple of out packet size */
1651215990Sjmallett        bytes_to_transfer = MAX_TRANSFER_BYTES / pipe->max_packet;
1652215990Sjmallett        bytes_to_transfer *= pipe->max_packet;
1653215990Sjmallett    }
1654210284Sjmallett
1655215990Sjmallett    /* Calculate the number of packets to transfer. If the length is zero
1656215990Sjmallett        we still need to transfer one packet */
1657215990Sjmallett    packets_to_transfer = (bytes_to_transfer + pipe->max_packet - 1) / pipe->max_packet;
1658215990Sjmallett    if (packets_to_transfer == 0)
1659215990Sjmallett        packets_to_transfer = 1;
1660215990Sjmallett    else if ((packets_to_transfer>1) && (usb->init_flags & CVMX_USB_INITIALIZE_FLAGS_NO_DMA))
1661215990Sjmallett    {
1662215990Sjmallett        /* Limit to one packet when not using DMA. Channels must be restarted
1663215990Sjmallett            between every packet for IN transactions, so there is no reason to
1664215990Sjmallett            do multiple packets in a row */
1665215990Sjmallett        packets_to_transfer = 1;
1666215990Sjmallett        bytes_to_transfer = packets_to_transfer * pipe->max_packet;
1667215990Sjmallett    }
1668215990Sjmallett    else if (packets_to_transfer > MAX_TRANSFER_PACKETS)
1669215990Sjmallett    {
1670215990Sjmallett        /* Limit the number of packet and data transferred to what the
1671215990Sjmallett            hardware can handle */
1672215990Sjmallett        packets_to_transfer = MAX_TRANSFER_PACKETS;
1673215990Sjmallett        bytes_to_transfer = packets_to_transfer * pipe->max_packet;
1674215990Sjmallett    }
1675215990Sjmallett
1676215990Sjmallett    usbc_hctsiz.s.xfersize = bytes_to_transfer;
1677215990Sjmallett    usbc_hctsiz.s.pktcnt = packets_to_transfer;
1678215990Sjmallett
1679210284Sjmallett    __cvmx_usb_write_csr32(usb, CVMX_USBCX_HCTSIZX(channel, usb->index), usbc_hctsiz.u32);
1680210284Sjmallett    CVMX_USB_RETURN_NOTHING();
1681210284Sjmallett}
1682210284Sjmallett
1683210284Sjmallett
1684210284Sjmallett/**
1685210284Sjmallett * @INTERNAL
1686210284Sjmallett * Start a channel to perform the pipe's head transaction
1687210284Sjmallett *
1688210284Sjmallett * @param usb     USB device state populated by
1689210284Sjmallett *                cvmx_usb_initialize().
1690210284Sjmallett * @param channel Channel to setup
1691210284Sjmallett * @param pipe    Pipe to start
1692210284Sjmallett */
1693210284Sjmallettstatic void __cvmx_usb_start_channel(cvmx_usb_internal_state_t *usb,
1694210284Sjmallett                                     int channel,
1695210284Sjmallett                                     cvmx_usb_pipe_t *pipe)
1696210284Sjmallett{
1697210284Sjmallett    cvmx_usb_transaction_t *transaction = pipe->head;
1698210284Sjmallett
1699210284Sjmallett    CVMX_USB_LOG_CALLED();
1700210284Sjmallett    CVMX_USB_LOG_PARAM("%p", usb);
1701210284Sjmallett    CVMX_USB_LOG_PARAM("%d", channel);
1702210284Sjmallett    CVMX_USB_LOG_PARAM("%p", pipe);
1703210284Sjmallett
1704210284Sjmallett    if (cvmx_unlikely((usb->init_flags & CVMX_USB_INITIALIZE_FLAGS_DEBUG_TRANSFERS) ||
1705210284Sjmallett        (pipe->flags & CVMX_USB_PIPE_FLAGS_DEBUG_TRANSFERS)))
1706210284Sjmallett        cvmx_dprintf("%s: Channel %d started. Pipe %d transaction %d stage %d\n",
1707210284Sjmallett                     __FUNCTION__, channel, __cvmx_usb_get_pipe_handle(usb, pipe),
1708210284Sjmallett                     __cvmx_usb_get_submit_handle(usb, transaction),
1709210284Sjmallett                     transaction->stage);
1710210284Sjmallett
1711210284Sjmallett    /* Make sure all writes to the DMA region get flushed */
1712210284Sjmallett    CVMX_SYNCW;
1713210284Sjmallett
1714210284Sjmallett    /* Attach the channel to the pipe */
1715210284Sjmallett    usb->pipe_for_channel[channel] = pipe;
1716210284Sjmallett    pipe->channel = channel;
1717210284Sjmallett    pipe->flags |= __CVMX_USB_PIPE_FLAGS_SCHEDULED;
1718210284Sjmallett
1719210284Sjmallett    /* Mark this channel as in use */
1720210284Sjmallett    usb->idle_hardware_channels &= ~(1<<channel);
1721210284Sjmallett
1722210284Sjmallett    /* Enable the channel interrupt bits */
1723210284Sjmallett    {
1724210284Sjmallett        cvmx_usbcx_hcintx_t usbc_hcint;
1725215990Sjmallett        cvmx_usbcx_hcintmskx_t usbc_hcintmsk;
1726215990Sjmallett        cvmx_usbcx_haintmsk_t usbc_haintmsk;
1727210284Sjmallett
1728210284Sjmallett        /* Clear all channel status bits */
1729210284Sjmallett        usbc_hcint.u32 = __cvmx_usb_read_csr32(usb, CVMX_USBCX_HCINTX(channel, usb->index));
1730210284Sjmallett        __cvmx_usb_write_csr32(usb, CVMX_USBCX_HCINTX(channel, usb->index), usbc_hcint.u32);
1731215990Sjmallett
1732215990Sjmallett        usbc_hcintmsk.u32 = 0;
1733215990Sjmallett        usbc_hcintmsk.s.chhltdmsk = 1;
1734215990Sjmallett        if (usb->init_flags & CVMX_USB_INITIALIZE_FLAGS_NO_DMA)
1735215990Sjmallett        {
1736215990Sjmallett            /* Channels need these extra interrupts when we aren't in DMA mode */
1737215990Sjmallett            usbc_hcintmsk.s.datatglerrmsk = 1;
1738215990Sjmallett            usbc_hcintmsk.s.frmovrunmsk = 1;
1739215990Sjmallett            usbc_hcintmsk.s.bblerrmsk = 1;
1740215990Sjmallett            usbc_hcintmsk.s.xacterrmsk = 1;
1741215990Sjmallett            if (__cvmx_usb_pipe_needs_split(usb, pipe))
1742215990Sjmallett            {
1743215990Sjmallett                /* Splits don't generate xfercompl, so we need ACK and NYET */
1744215990Sjmallett                usbc_hcintmsk.s.nyetmsk = 1;
1745215990Sjmallett                usbc_hcintmsk.s.ackmsk = 1;
1746215990Sjmallett            }
1747215990Sjmallett            usbc_hcintmsk.s.nakmsk = 1;
1748215990Sjmallett            usbc_hcintmsk.s.stallmsk = 1;
1749215990Sjmallett            usbc_hcintmsk.s.xfercomplmsk = 1;
1750215990Sjmallett        }
1751215990Sjmallett        __cvmx_usb_write_csr32(usb, CVMX_USBCX_HCINTMSKX(channel, usb->index), usbc_hcintmsk.u32);
1752215990Sjmallett
1753215990Sjmallett        /* Enable the channel interrupt to propagate */
1754215990Sjmallett        usbc_haintmsk.u32 = __cvmx_usb_read_csr32(usb, CVMX_USBCX_HAINTMSK(usb->index));
1755215990Sjmallett        usbc_haintmsk.s.haintmsk |= 1<<channel;
1756215990Sjmallett        __cvmx_usb_write_csr32(usb, CVMX_USBCX_HAINTMSK(usb->index), usbc_haintmsk.u32);
1757210284Sjmallett    }
1758210284Sjmallett
1759210284Sjmallett    /* Setup the locations the DMA engines use  */
1760210284Sjmallett    {
1761210284Sjmallett        uint64_t dma_address = transaction->buffer + transaction->actual_bytes;
1762210284Sjmallett        if (transaction->type == CVMX_USB_TRANSFER_ISOCHRONOUS)
1763210284Sjmallett            dma_address = transaction->buffer + transaction->iso_packets[0].offset + transaction->actual_bytes;
1764210284Sjmallett        __cvmx_usb_write_csr64(usb, CVMX_USBNX_DMA0_OUTB_CHN0(usb->index) + channel*8, dma_address);
1765210284Sjmallett        __cvmx_usb_write_csr64(usb, CVMX_USBNX_DMA0_INB_CHN0(usb->index) + channel*8, dma_address);
1766210284Sjmallett    }
1767210284Sjmallett
1768210284Sjmallett    /* Setup both the size of the transfer and the SPLIT characteristics */
1769210284Sjmallett    {
1770210284Sjmallett        cvmx_usbcx_hcspltx_t usbc_hcsplt = {.u32 = 0};
1771210284Sjmallett        cvmx_usbcx_hctsizx_t usbc_hctsiz = {.u32 = 0};
1772215990Sjmallett        int packets_to_transfer;
1773210284Sjmallett        int bytes_to_transfer = transaction->buffer_length - transaction->actual_bytes;
1774210284Sjmallett
1775210284Sjmallett        /* ISOCHRONOUS transactions store each individual transfer size in the
1776210284Sjmallett            packet structure, not the global buffer_length */
1777210284Sjmallett        if (transaction->type == CVMX_USB_TRANSFER_ISOCHRONOUS)
1778210284Sjmallett            bytes_to_transfer = transaction->iso_packets[0].length - transaction->actual_bytes;
1779210284Sjmallett
1780210284Sjmallett        /* We need to do split transactions when we are talking to non high
1781210284Sjmallett            speed devices that are behind a high speed hub */
1782210284Sjmallett        if (__cvmx_usb_pipe_needs_split(usb, pipe))
1783210284Sjmallett        {
1784210284Sjmallett            /* On the start split phase (stage is even) record the frame number we
1785210284Sjmallett                will need to send the split complete. We only store the lower two bits
1786210284Sjmallett                since the time ahead can only be two frames */
1787210284Sjmallett            if ((transaction->stage&1) == 0)
1788210284Sjmallett            {
1789210284Sjmallett                if (transaction->type == CVMX_USB_TRANSFER_BULK)
1790215990Sjmallett                    pipe->split_sc_frame = (usb->frame_number + 1) & 0x7f;
1791210284Sjmallett                else
1792215990Sjmallett                    pipe->split_sc_frame = (usb->frame_number + 2) & 0x7f;
1793210284Sjmallett            }
1794210284Sjmallett            else
1795210284Sjmallett                pipe->split_sc_frame = -1;
1796210284Sjmallett
1797210284Sjmallett            usbc_hcsplt.s.spltena = 1;
1798210284Sjmallett            usbc_hcsplt.s.hubaddr = pipe->hub_device_addr;
1799210284Sjmallett            usbc_hcsplt.s.prtaddr = pipe->hub_port;
1800210284Sjmallett            usbc_hcsplt.s.compsplt = (transaction->stage == CVMX_USB_STAGE_NON_CONTROL_SPLIT_COMPLETE);
1801210284Sjmallett
1802210284Sjmallett            /* SPLIT transactions can only ever transmit one data packet so
1803210284Sjmallett                limit the transfer size to the max packet size */
1804210284Sjmallett            if (bytes_to_transfer > pipe->max_packet)
1805210284Sjmallett                bytes_to_transfer = pipe->max_packet;
1806210284Sjmallett
1807210284Sjmallett            /* ISOCHRONOUS OUT splits are unique in that they limit
1808210284Sjmallett                data transfers to 188 byte chunks representing the
1809210284Sjmallett                begin/middle/end of the data or all */
1810210284Sjmallett            if (!usbc_hcsplt.s.compsplt &&
1811210284Sjmallett                (pipe->transfer_dir == CVMX_USB_DIRECTION_OUT) &&
1812210284Sjmallett                (pipe->transfer_type == CVMX_USB_TRANSFER_ISOCHRONOUS))
1813210284Sjmallett            {
1814215990Sjmallett                /* Clear the split complete frame number as there isn't going
1815215990Sjmallett                    to be a split complete */
1816215990Sjmallett                pipe->split_sc_frame = -1;
1817215990Sjmallett                /* See if we've started this transfer and sent data */
1818210284Sjmallett                if (transaction->actual_bytes == 0)
1819210284Sjmallett                {
1820210284Sjmallett                    /* Nothing sent yet, this is either a begin or the
1821210284Sjmallett                        entire payload */
1822210284Sjmallett                    if (bytes_to_transfer <= 188)
1823210284Sjmallett                        usbc_hcsplt.s.xactpos = 3; /* Entire payload in one go */
1824210284Sjmallett                    else
1825210284Sjmallett                        usbc_hcsplt.s.xactpos = 2; /* First part of payload */
1826210284Sjmallett                }
1827210284Sjmallett                else
1828210284Sjmallett                {
1829210284Sjmallett                    /* Continuing the previous data, we must either be
1830210284Sjmallett                        in the middle or at the end */
1831210284Sjmallett                    if (bytes_to_transfer <= 188)
1832210284Sjmallett                        usbc_hcsplt.s.xactpos = 1; /* End of payload */
1833210284Sjmallett                    else
1834210284Sjmallett                        usbc_hcsplt.s.xactpos = 0; /* Middle of payload */
1835210284Sjmallett                }
1836210284Sjmallett                /* Again, the transfer size is limited to 188 bytes */
1837210284Sjmallett                if (bytes_to_transfer > 188)
1838210284Sjmallett                    bytes_to_transfer = 188;
1839210284Sjmallett            }
1840210284Sjmallett        }
1841210284Sjmallett
1842215990Sjmallett        /* Make sure the transfer never exceeds the byte limit of the hardware.
1843215990Sjmallett            Further bytes will be sent as continued transactions */
1844215990Sjmallett        if (bytes_to_transfer > MAX_TRANSFER_BYTES)
1845215990Sjmallett        {
1846215990Sjmallett            /* Round MAX_TRANSFER_BYTES to a multiple of out packet size */
1847215990Sjmallett            bytes_to_transfer = MAX_TRANSFER_BYTES / pipe->max_packet;
1848215990Sjmallett            bytes_to_transfer *= pipe->max_packet;
1849215990Sjmallett        }
1850215990Sjmallett
1851215990Sjmallett        /* Calculate the number of packets to transfer. If the length is zero
1852215990Sjmallett            we still need to transfer one packet */
1853215990Sjmallett        packets_to_transfer = (bytes_to_transfer + pipe->max_packet - 1) / pipe->max_packet;
1854215990Sjmallett        if (packets_to_transfer == 0)
1855215990Sjmallett            packets_to_transfer = 1;
1856215990Sjmallett        else if ((packets_to_transfer>1) && (usb->init_flags & CVMX_USB_INITIALIZE_FLAGS_NO_DMA))
1857215990Sjmallett        {
1858215990Sjmallett            /* Limit to one packet when not using DMA. Channels must be restarted
1859215990Sjmallett                between every packet for IN transactions, so there is no reason to
1860215990Sjmallett                do multiple packets in a row */
1861215990Sjmallett            packets_to_transfer = 1;
1862215990Sjmallett            bytes_to_transfer = packets_to_transfer * pipe->max_packet;
1863215990Sjmallett        }
1864215990Sjmallett        else if (packets_to_transfer > MAX_TRANSFER_PACKETS)
1865215990Sjmallett        {
1866215990Sjmallett            /* Limit the number of packet and data transferred to what the
1867215990Sjmallett                hardware can handle */
1868215990Sjmallett            packets_to_transfer = MAX_TRANSFER_PACKETS;
1869215990Sjmallett            bytes_to_transfer = packets_to_transfer * pipe->max_packet;
1870215990Sjmallett        }
1871215990Sjmallett
1872210284Sjmallett        usbc_hctsiz.s.xfersize = bytes_to_transfer;
1873215990Sjmallett        usbc_hctsiz.s.pktcnt = packets_to_transfer;
1874210284Sjmallett
1875210284Sjmallett        /* Update the DATA0/DATA1 toggle */
1876210284Sjmallett        usbc_hctsiz.s.pid = __cvmx_usb_get_data_pid(pipe);
1877210284Sjmallett        /* High speed pipes may need a hardware ping before they start */
1878210284Sjmallett        if (pipe->flags & __CVMX_USB_PIPE_FLAGS_NEED_PING)
1879210284Sjmallett            usbc_hctsiz.s.dopng = 1;
1880210284Sjmallett
1881210284Sjmallett        __cvmx_usb_write_csr32(usb, CVMX_USBCX_HCSPLTX(channel, usb->index), usbc_hcsplt.u32);
1882210284Sjmallett        __cvmx_usb_write_csr32(usb, CVMX_USBCX_HCTSIZX(channel, usb->index), usbc_hctsiz.u32);
1883210284Sjmallett    }
1884210284Sjmallett
1885210284Sjmallett    /* Setup the Host Channel Characteristics Register */
1886210284Sjmallett    {
1887210284Sjmallett        cvmx_usbcx_hccharx_t usbc_hcchar = {.u32 = 0};
1888210284Sjmallett
1889215990Sjmallett        /* Set the startframe odd/even properly. This is only used for periodic */
1890215990Sjmallett        usbc_hcchar.s.oddfrm = usb->frame_number&1;
1891210284Sjmallett
1892210284Sjmallett        /* Set the number of back to back packets allowed by this endpoint.
1893210284Sjmallett            Split transactions interpret "ec" as the number of immediate
1894210284Sjmallett            retries of failure. These retries happen too quickly, so we
1895210284Sjmallett            disable these entirely for splits */
1896210284Sjmallett        if (__cvmx_usb_pipe_needs_split(usb, pipe))
1897210284Sjmallett            usbc_hcchar.s.ec = 1;
1898210284Sjmallett        else if (pipe->multi_count < 1)
1899210284Sjmallett            usbc_hcchar.s.ec = 1;
1900210284Sjmallett        else if (pipe->multi_count > 3)
1901210284Sjmallett            usbc_hcchar.s.ec = 3;
1902210284Sjmallett        else
1903210284Sjmallett            usbc_hcchar.s.ec = pipe->multi_count;
1904210284Sjmallett
1905210284Sjmallett        /* Set the rest of the endpoint specific settings */
1906210284Sjmallett        usbc_hcchar.s.devaddr = pipe->device_addr;
1907210284Sjmallett        usbc_hcchar.s.eptype = transaction->type;
1908210284Sjmallett        usbc_hcchar.s.lspddev = (pipe->device_speed == CVMX_USB_SPEED_LOW);
1909210284Sjmallett        usbc_hcchar.s.epdir = pipe->transfer_dir;
1910210284Sjmallett        usbc_hcchar.s.epnum = pipe->endpoint_num;
1911210284Sjmallett        usbc_hcchar.s.mps = pipe->max_packet;
1912210284Sjmallett        __cvmx_usb_write_csr32(usb, CVMX_USBCX_HCCHARX(channel, usb->index), usbc_hcchar.u32);
1913210284Sjmallett    }
1914210284Sjmallett
1915210284Sjmallett    /* Do transaction type specific fixups as needed */
1916210284Sjmallett    switch (transaction->type)
1917210284Sjmallett    {
1918210284Sjmallett        case CVMX_USB_TRANSFER_CONTROL:
1919210284Sjmallett            __cvmx_usb_start_channel_control(usb, channel, pipe);
1920210284Sjmallett            break;
1921210284Sjmallett        case CVMX_USB_TRANSFER_BULK:
1922210284Sjmallett        case CVMX_USB_TRANSFER_INTERRUPT:
1923210284Sjmallett            break;
1924210284Sjmallett        case CVMX_USB_TRANSFER_ISOCHRONOUS:
1925210284Sjmallett            if (!__cvmx_usb_pipe_needs_split(usb, pipe))
1926210284Sjmallett            {
1927215990Sjmallett                /* ISO transactions require different PIDs depending on direction
1928210284Sjmallett                    and how many packets are needed */
1929210284Sjmallett                if (pipe->transfer_dir == CVMX_USB_DIRECTION_OUT)
1930210284Sjmallett                {
1931210284Sjmallett                    if (pipe->multi_count < 2) /* Need DATA0 */
1932210284Sjmallett                        USB_SET_FIELD32(CVMX_USBCX_HCTSIZX(channel, usb->index), cvmx_usbcx_hctsizx_t, pid, 0);
1933210284Sjmallett                    else /* Need MDATA */
1934210284Sjmallett                        USB_SET_FIELD32(CVMX_USBCX_HCTSIZX(channel, usb->index), cvmx_usbcx_hctsizx_t, pid, 3);
1935210284Sjmallett                }
1936210284Sjmallett            }
1937210284Sjmallett            break;
1938210284Sjmallett    }
1939210284Sjmallett    {
1940210284Sjmallett        cvmx_usbcx_hctsizx_t usbc_hctsiz = {.u32 = __cvmx_usb_read_csr32(usb, CVMX_USBCX_HCTSIZX(channel, usb->index))};
1941210284Sjmallett        transaction->xfersize = usbc_hctsiz.s.xfersize;
1942210284Sjmallett        transaction->pktcnt = usbc_hctsiz.s.pktcnt;
1943210284Sjmallett    }
1944215990Sjmallett    /* Remeber when we start a split transaction */
1945215990Sjmallett    if (__cvmx_usb_pipe_needs_split(usb, pipe))
1946215990Sjmallett        usb->active_split = transaction;
1947210284Sjmallett    USB_SET_FIELD32(CVMX_USBCX_HCCHARX(channel, usb->index), cvmx_usbcx_hccharx_t, chena, 1);
1948215990Sjmallett    if (usb->init_flags & CVMX_USB_INITIALIZE_FLAGS_NO_DMA)
1949215990Sjmallett        __cvmx_usb_fill_tx_fifo(usb, channel);
1950210284Sjmallett    CVMX_USB_RETURN_NOTHING();
1951210284Sjmallett}
1952210284Sjmallett
1953210284Sjmallett
1954210284Sjmallett/**
1955210284Sjmallett * @INTERNAL
1956210284Sjmallett * Find a pipe that is ready to be scheduled to hardware.
1957215990Sjmallett * @param usb    USB device state populated by
1958215990Sjmallett *               cvmx_usb_initialize().
1959215990Sjmallett * @param list   Pipe list to search
1960215990Sjmallett * @param current_frame
1961215990Sjmallett *               Frame counter to use as a time reference.
1962210284Sjmallett *
1963210284Sjmallett * @return Pipe or NULL if none are ready
1964210284Sjmallett */
1965215990Sjmallettstatic 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)
1966210284Sjmallett{
1967210284Sjmallett    cvmx_usb_pipe_t *pipe = list->head;
1968210284Sjmallett    while (pipe)
1969210284Sjmallett    {
1970210284Sjmallett        if (!(pipe->flags & __CVMX_USB_PIPE_FLAGS_SCHEDULED) && pipe->head &&
1971215990Sjmallett            (pipe->next_tx_frame <= current_frame) &&
1972215990Sjmallett            ((pipe->split_sc_frame == -1) || ((((int)current_frame - (int)pipe->split_sc_frame) & 0x7f) < 0x40)) &&
1973215990Sjmallett            (!usb->active_split || (usb->active_split == pipe->head)))
1974210284Sjmallett        {
1975210284Sjmallett            CVMX_PREFETCH(pipe, 128);
1976210284Sjmallett            CVMX_PREFETCH(pipe->head, 0);
1977210284Sjmallett            return pipe;
1978210284Sjmallett        }
1979210284Sjmallett        pipe = pipe->next;
1980210284Sjmallett    }
1981210284Sjmallett    return NULL;
1982210284Sjmallett}
1983210284Sjmallett
1984210284Sjmallett
1985210284Sjmallett/**
1986210284Sjmallett * @INTERNAL
1987210284Sjmallett * Called whenever a pipe might need to be scheduled to the
1988210284Sjmallett * hardware.
1989210284Sjmallett *
1990210284Sjmallett * @param usb    USB device state populated by
1991210284Sjmallett *               cvmx_usb_initialize().
1992210284Sjmallett * @param is_sof True if this schedule was called on a SOF interrupt.
1993210284Sjmallett */
1994210284Sjmallettstatic void __cvmx_usb_schedule(cvmx_usb_internal_state_t *usb, int is_sof)
1995210284Sjmallett{
1996210284Sjmallett    int channel;
1997210284Sjmallett    cvmx_usb_pipe_t *pipe;
1998215990Sjmallett    int need_sof;
1999215990Sjmallett    cvmx_usb_transfer_t ttype;
2000210284Sjmallett
2001210284Sjmallett    CVMX_USB_LOG_CALLED();
2002210284Sjmallett    CVMX_USB_LOG_PARAM("%p", usb);
2003210284Sjmallett
2004215990Sjmallett    if (usb->init_flags & CVMX_USB_INITIALIZE_FLAGS_NO_DMA)
2005215990Sjmallett    {
2006215990Sjmallett        /* Without DMA we need to be careful to not schedule something at the end of a frame and cause an overrun */
2007215990Sjmallett        cvmx_usbcx_hfnum_t hfnum = {.u32 = __cvmx_usb_read_csr32(usb, CVMX_USBCX_HFNUM(usb->index))};
2008215990Sjmallett        cvmx_usbcx_hfir_t hfir = {.u32 = __cvmx_usb_read_csr32(usb, CVMX_USBCX_HFIR(usb->index))};
2009215990Sjmallett        if (hfnum.s.frrem < hfir.s.frint/4)
2010215990Sjmallett            goto done;
2011215990Sjmallett    }
2012210284Sjmallett
2013210284Sjmallett    while (usb->idle_hardware_channels)
2014210284Sjmallett    {
2015210284Sjmallett        /* Find an idle channel */
2016210284Sjmallett        CVMX_CLZ(channel, usb->idle_hardware_channels);
2017210284Sjmallett        channel = 31 - channel;
2018210284Sjmallett        if (cvmx_unlikely(channel > 7))
2019210284Sjmallett        {
2020210284Sjmallett            if (cvmx_unlikely(usb->init_flags & CVMX_USB_INITIALIZE_FLAGS_DEBUG_INFO))
2021210284Sjmallett                cvmx_dprintf("%s: Idle hardware channels has a channel higher than 7. This is wrong\n", __FUNCTION__);
2022210284Sjmallett            break;
2023210284Sjmallett        }
2024210284Sjmallett
2025210284Sjmallett        /* Find a pipe needing service */
2026210284Sjmallett        pipe = NULL;
2027210284Sjmallett        if (is_sof)
2028210284Sjmallett        {
2029210284Sjmallett            /* Only process periodic pipes on SOF interrupts. This way we are
2030210284Sjmallett                sure that the periodic data is sent in the beginning of the
2031210284Sjmallett                frame */
2032215990Sjmallett            pipe = __cvmx_usb_find_ready_pipe(usb, usb->active_pipes + CVMX_USB_TRANSFER_ISOCHRONOUS, usb->frame_number);
2033210284Sjmallett            if (cvmx_likely(!pipe))
2034215990Sjmallett                pipe = __cvmx_usb_find_ready_pipe(usb, usb->active_pipes + CVMX_USB_TRANSFER_INTERRUPT, usb->frame_number);
2035210284Sjmallett        }
2036210284Sjmallett        if (cvmx_likely(!pipe))
2037210284Sjmallett        {
2038215990Sjmallett            pipe = __cvmx_usb_find_ready_pipe(usb, usb->active_pipes + CVMX_USB_TRANSFER_CONTROL, usb->frame_number);
2039210284Sjmallett            if (cvmx_likely(!pipe))
2040215990Sjmallett                pipe = __cvmx_usb_find_ready_pipe(usb, usb->active_pipes + CVMX_USB_TRANSFER_BULK, usb->frame_number);
2041210284Sjmallett        }
2042210284Sjmallett        if (!pipe)
2043210284Sjmallett            break;
2044210284Sjmallett
2045210284Sjmallett        CVMX_USB_LOG_PARAM("%d", channel);
2046210284Sjmallett        CVMX_USB_LOG_PARAM("%p", pipe);
2047210284Sjmallett
2048210284Sjmallett        if (cvmx_unlikely((usb->init_flags & CVMX_USB_INITIALIZE_FLAGS_DEBUG_TRANSFERS) ||
2049210284Sjmallett            (pipe->flags & CVMX_USB_PIPE_FLAGS_DEBUG_TRANSFERS)))
2050210284Sjmallett        {
2051210284Sjmallett            cvmx_usb_transaction_t *transaction = pipe->head;
2052210284Sjmallett            const cvmx_usb_control_header_t *header = (transaction->control_header) ? cvmx_phys_to_ptr(transaction->control_header) : NULL;
2053210284Sjmallett            const char *dir = (pipe->transfer_dir == CVMX_USB_DIRECTION_IN) ? "IN" : "OUT";
2054210284Sjmallett            const char *type;
2055210284Sjmallett            switch (pipe->transfer_type)
2056210284Sjmallett            {
2057210284Sjmallett                case CVMX_USB_TRANSFER_CONTROL:
2058210284Sjmallett                    type = "SETUP";
2059210284Sjmallett                    dir = (header->s.request_type & 0x80) ? "IN" : "OUT";
2060210284Sjmallett                    break;
2061210284Sjmallett                case CVMX_USB_TRANSFER_ISOCHRONOUS:
2062210284Sjmallett                    type = "ISOCHRONOUS";
2063210284Sjmallett                    break;
2064210284Sjmallett                case CVMX_USB_TRANSFER_BULK:
2065210284Sjmallett                    type = "BULK";
2066210284Sjmallett                    break;
2067210284Sjmallett                default: /* CVMX_USB_TRANSFER_INTERRUPT */
2068210284Sjmallett                    type = "INTERRUPT";
2069210284Sjmallett                    break;
2070210284Sjmallett            }
2071210284Sjmallett            cvmx_dprintf("%s: Starting pipe %d, transaction %d on channel %d. %s %s len=%d header=0x%llx\n",
2072210284Sjmallett                         __FUNCTION__, __cvmx_usb_get_pipe_handle(usb, pipe),
2073210284Sjmallett                         __cvmx_usb_get_submit_handle(usb, transaction),
2074210284Sjmallett                         channel, type, dir,
2075210284Sjmallett                         transaction->buffer_length,
2076210284Sjmallett                         (header) ? (unsigned long long)header->u64 : 0ull);
2077210284Sjmallett        }
2078210284Sjmallett        __cvmx_usb_start_channel(usb, channel, pipe);
2079210284Sjmallett    }
2080215990Sjmallett
2081215990Sjmallettdone:
2082215990Sjmallett    /* Only enable SOF interrupts when we have transactions pending in the
2083215990Sjmallett        future that might need to be scheduled */
2084215990Sjmallett    need_sof = 0;
2085215990Sjmallett    for (ttype=CVMX_USB_TRANSFER_CONTROL; ttype<=CVMX_USB_TRANSFER_INTERRUPT; ttype++)
2086215990Sjmallett    {
2087215990Sjmallett        pipe = usb->active_pipes[ttype].head;
2088215990Sjmallett        while (pipe)
2089215990Sjmallett        {
2090215990Sjmallett            if (pipe->next_tx_frame > usb->frame_number)
2091215990Sjmallett            {
2092215990Sjmallett                need_sof = 1;
2093215990Sjmallett                break;
2094215990Sjmallett            }
2095215990Sjmallett            pipe=pipe->next;
2096215990Sjmallett        }
2097215990Sjmallett    }
2098215990Sjmallett    USB_SET_FIELD32(CVMX_USBCX_GINTMSK(usb->index), cvmx_usbcx_gintmsk_t, sofmsk, need_sof);
2099210284Sjmallett    CVMX_USB_RETURN_NOTHING();
2100210284Sjmallett}
2101210284Sjmallett
2102210284Sjmallett
2103210284Sjmallett/**
2104210284Sjmallett * @INTERNAL
2105210284Sjmallett * Call a user's callback for a specific reason.
2106210284Sjmallett *
2107210284Sjmallett * @param usb    USB device state populated by
2108210284Sjmallett *               cvmx_usb_initialize().
2109210284Sjmallett * @param pipe   Pipe the callback is for or NULL
2110210284Sjmallett * @param transaction
2111210284Sjmallett *               Transaction the callback is for or NULL
2112210284Sjmallett * @param reason Reason this callback is being called
2113210284Sjmallett * @param complete_code
2114210284Sjmallett *               Completion code for the transaction, if any
2115210284Sjmallett */
2116210284Sjmallettstatic void __cvmx_usb_perform_callback(cvmx_usb_internal_state_t *usb,
2117210284Sjmallett                                        cvmx_usb_pipe_t *pipe,
2118210284Sjmallett                                        cvmx_usb_transaction_t *transaction,
2119210284Sjmallett                                        cvmx_usb_callback_t reason,
2120210284Sjmallett                                        cvmx_usb_complete_t complete_code)
2121210284Sjmallett{
2122210284Sjmallett    cvmx_usb_callback_func_t callback = usb->callback[reason];
2123210284Sjmallett    void *user_data = usb->callback_data[reason];
2124210284Sjmallett    int submit_handle = -1;
2125210284Sjmallett    int pipe_handle = -1;
2126210284Sjmallett    int bytes_transferred = 0;
2127210284Sjmallett
2128210284Sjmallett    if (pipe)
2129210284Sjmallett        pipe_handle = __cvmx_usb_get_pipe_handle(usb, pipe);
2130210284Sjmallett
2131210284Sjmallett    if (transaction)
2132210284Sjmallett    {
2133210284Sjmallett        submit_handle = __cvmx_usb_get_submit_handle(usb, transaction);
2134210284Sjmallett        bytes_transferred = transaction->actual_bytes;
2135210284Sjmallett        /* Transactions are allowed to override the default callback */
2136210284Sjmallett        if ((reason == CVMX_USB_CALLBACK_TRANSFER_COMPLETE) && transaction->callback)
2137210284Sjmallett        {
2138210284Sjmallett            callback = transaction->callback;
2139210284Sjmallett            user_data = transaction->callback_data;
2140210284Sjmallett        }
2141210284Sjmallett    }
2142210284Sjmallett
2143210284Sjmallett    if (!callback)
2144210284Sjmallett        return;
2145210284Sjmallett
2146210284Sjmallett    if (cvmx_unlikely(usb->init_flags & CVMX_USB_INITIALIZE_FLAGS_DEBUG_CALLBACKS))
2147210284Sjmallett        cvmx_dprintf("%*s%s: calling callback %p(usb=%p, complete_code=%s, "
2148210284Sjmallett                     "pipe_handle=%d, submit_handle=%d, bytes_transferred=%d, user_data=%p);\n",
2149210284Sjmallett                     2*usb->indent, "", __FUNCTION__, callback, usb,
2150210284Sjmallett                     __cvmx_usb_complete_to_string(complete_code),
2151210284Sjmallett                     pipe_handle, submit_handle, bytes_transferred, user_data);
2152210284Sjmallett
2153210284Sjmallett    callback((cvmx_usb_state_t *)usb, reason, complete_code, pipe_handle, submit_handle,
2154210284Sjmallett             bytes_transferred, user_data);
2155210284Sjmallett
2156210284Sjmallett    if (cvmx_unlikely(usb->init_flags & CVMX_USB_INITIALIZE_FLAGS_DEBUG_CALLBACKS))
2157210284Sjmallett        cvmx_dprintf("%*s%s: callback %p complete\n", 2*usb->indent, "",
2158210284Sjmallett                      __FUNCTION__, callback);
2159210284Sjmallett}
2160210284Sjmallett
2161210284Sjmallett
2162210284Sjmallett/**
2163210284Sjmallett * @INTERNAL
2164210284Sjmallett * Signal the completion of a transaction and free it. The
2165210284Sjmallett * transaction will be removed from the pipe transaction list.
2166210284Sjmallett *
2167210284Sjmallett * @param usb    USB device state populated by
2168210284Sjmallett *               cvmx_usb_initialize().
2169210284Sjmallett * @param pipe   Pipe the transaction is on
2170210284Sjmallett * @param transaction
2171210284Sjmallett *               Transaction that completed
2172210284Sjmallett * @param complete_code
2173210284Sjmallett *               Completion code
2174210284Sjmallett */
2175210284Sjmallettstatic void __cvmx_usb_perform_complete(cvmx_usb_internal_state_t * usb,
2176210284Sjmallett                                        cvmx_usb_pipe_t *pipe,
2177210284Sjmallett                                        cvmx_usb_transaction_t *transaction,
2178210284Sjmallett                                        cvmx_usb_complete_t complete_code)
2179210284Sjmallett{
2180210284Sjmallett    CVMX_USB_LOG_CALLED();
2181210284Sjmallett    CVMX_USB_LOG_PARAM("%p", usb);
2182210284Sjmallett    CVMX_USB_LOG_PARAM("%p", pipe);
2183210284Sjmallett    CVMX_USB_LOG_PARAM("%p", transaction);
2184210284Sjmallett    CVMX_USB_LOG_PARAM("%d", complete_code);
2185210284Sjmallett
2186215990Sjmallett    /* If this was a split then clear our split in progress marker */
2187215990Sjmallett    if (usb->active_split == transaction)
2188215990Sjmallett        usb->active_split = NULL;
2189215990Sjmallett
2190210284Sjmallett    /* Isochronous transactions need extra processing as they might not be done
2191210284Sjmallett        after a single data transfer */
2192210284Sjmallett    if (cvmx_unlikely(transaction->type == CVMX_USB_TRANSFER_ISOCHRONOUS))
2193210284Sjmallett    {
2194215990Sjmallett        /* Update the number of bytes transferred in this ISO packet */
2195210284Sjmallett        transaction->iso_packets[0].length = transaction->actual_bytes;
2196210284Sjmallett        transaction->iso_packets[0].status = complete_code;
2197210284Sjmallett
2198215990Sjmallett        /* If there are more ISOs pending and we succeeded, schedule the next
2199210284Sjmallett            one */
2200210284Sjmallett        if ((transaction->iso_number_packets > 1) && (complete_code == CVMX_USB_COMPLETE_SUCCESS))
2201210284Sjmallett        {
2202215990Sjmallett            transaction->actual_bytes = 0;      /* No bytes transferred for this packet as of yet */
2203210284Sjmallett            transaction->iso_number_packets--;  /* One less ISO waiting to transfer */
2204210284Sjmallett            transaction->iso_packets++;         /* Increment to the next location in our packet array */
2205210284Sjmallett            transaction->stage = CVMX_USB_STAGE_NON_CONTROL;
2206210284Sjmallett            goto done;
2207210284Sjmallett        }
2208210284Sjmallett    }
2209210284Sjmallett
2210210284Sjmallett    /* Remove the transaction from the pipe list */
2211210284Sjmallett    if (transaction->next)
2212210284Sjmallett        transaction->next->prev = transaction->prev;
2213210284Sjmallett    else
2214210284Sjmallett        pipe->tail = transaction->prev;
2215210284Sjmallett    if (transaction->prev)
2216210284Sjmallett        transaction->prev->next = transaction->next;
2217210284Sjmallett    else
2218210284Sjmallett        pipe->head = transaction->next;
2219210284Sjmallett    if (!pipe->head)
2220210284Sjmallett    {
2221210284Sjmallett        __cvmx_usb_remove_pipe(usb->active_pipes + pipe->transfer_type, pipe);
2222210284Sjmallett        __cvmx_usb_append_pipe(&usb->idle_pipes, pipe);
2223210284Sjmallett
2224210284Sjmallett    }
2225210284Sjmallett    __cvmx_usb_perform_callback(usb, pipe, transaction,
2226210284Sjmallett                                CVMX_USB_CALLBACK_TRANSFER_COMPLETE,
2227210284Sjmallett                                complete_code);
2228210284Sjmallett    __cvmx_usb_free_transaction(usb, transaction);
2229210284Sjmallettdone:
2230210284Sjmallett    CVMX_USB_RETURN_NOTHING();
2231210284Sjmallett}
2232210284Sjmallett
2233210284Sjmallett
2234210284Sjmallett/**
2235210284Sjmallett * @INTERNAL
2236210284Sjmallett * Submit a usb transaction to a pipe. Called for all types
2237210284Sjmallett * of transactions.
2238210284Sjmallett *
2239210284Sjmallett * @param usb
2240210284Sjmallett * @param pipe_handle
2241210284Sjmallett *                  Which pipe to submit to. Will be validated in this function.
2242210284Sjmallett * @param type      Transaction type
2243210284Sjmallett * @param flags     Flags for the transaction
2244210284Sjmallett * @param buffer    User buffer for the transaction
2245210284Sjmallett * @param buffer_length
2246210284Sjmallett *                  User buffer's length in bytes
2247210284Sjmallett * @param control_header
2248210284Sjmallett *                  For control transactions, the 8 byte standard header
2249210284Sjmallett * @param iso_start_frame
2250215990Sjmallett *                  For ISO transactions, the start frame
2251210284Sjmallett * @param iso_number_packets
2252210284Sjmallett *                  For ISO, the number of packet in the transaction.
2253210284Sjmallett * @param iso_packets
2254210284Sjmallett *                  A description of each ISO packet
2255210284Sjmallett * @param callback  User callback to call when the transaction completes
2256210284Sjmallett * @param user_data User's data for the callback
2257210284Sjmallett *
2258210284Sjmallett * @return Submit handle or negative on failure. Matches the result
2259210284Sjmallett *         in the external API.
2260210284Sjmallett */
2261210284Sjmallettstatic int __cvmx_usb_submit_transaction(cvmx_usb_internal_state_t *usb,
2262210284Sjmallett                                         int pipe_handle,
2263210284Sjmallett                                         cvmx_usb_transfer_t type,
2264210284Sjmallett                                         int flags,
2265210284Sjmallett                                         uint64_t buffer,
2266210284Sjmallett                                         int buffer_length,
2267210284Sjmallett                                         uint64_t control_header,
2268210284Sjmallett                                         int iso_start_frame,
2269210284Sjmallett                                         int iso_number_packets,
2270210284Sjmallett                                         cvmx_usb_iso_packet_t *iso_packets,
2271210284Sjmallett                                         cvmx_usb_callback_func_t callback,
2272210284Sjmallett                                         void *user_data)
2273210284Sjmallett{
2274210284Sjmallett    int submit_handle;
2275210284Sjmallett    cvmx_usb_transaction_t *transaction;
2276210284Sjmallett    cvmx_usb_pipe_t *pipe = usb->pipe + pipe_handle;
2277210284Sjmallett
2278210284Sjmallett    CVMX_USB_LOG_CALLED();
2279210284Sjmallett    if (cvmx_unlikely((pipe_handle < 0) || (pipe_handle >= MAX_PIPES)))
2280210284Sjmallett        CVMX_USB_RETURN(CVMX_USB_INVALID_PARAM);
2281210284Sjmallett    /* Fail if the pipe isn't open */
2282210284Sjmallett    if (cvmx_unlikely((pipe->flags & __CVMX_USB_PIPE_FLAGS_OPEN) == 0))
2283210284Sjmallett        CVMX_USB_RETURN(CVMX_USB_INVALID_PARAM);
2284210284Sjmallett    if (cvmx_unlikely(pipe->transfer_type != type))
2285210284Sjmallett        CVMX_USB_RETURN(CVMX_USB_INVALID_PARAM);
2286210284Sjmallett
2287210284Sjmallett    transaction = __cvmx_usb_alloc_transaction(usb);
2288210284Sjmallett    if (cvmx_unlikely(!transaction))
2289210284Sjmallett        CVMX_USB_RETURN(CVMX_USB_NO_MEMORY);
2290210284Sjmallett
2291210284Sjmallett    transaction->type = type;
2292210284Sjmallett    transaction->flags |= flags;
2293210284Sjmallett    transaction->buffer = buffer;
2294210284Sjmallett    transaction->buffer_length = buffer_length;
2295210284Sjmallett    transaction->control_header = control_header;
2296210284Sjmallett    transaction->iso_start_frame = iso_start_frame; // FIXME: This is not used, implement it
2297210284Sjmallett    transaction->iso_number_packets = iso_number_packets;
2298210284Sjmallett    transaction->iso_packets = iso_packets;
2299210284Sjmallett    transaction->callback = callback;
2300210284Sjmallett    transaction->callback_data = user_data;
2301210284Sjmallett    if (transaction->type == CVMX_USB_TRANSFER_CONTROL)
2302210284Sjmallett        transaction->stage = CVMX_USB_STAGE_SETUP;
2303210284Sjmallett    else
2304210284Sjmallett        transaction->stage = CVMX_USB_STAGE_NON_CONTROL;
2305210284Sjmallett
2306210284Sjmallett    transaction->next = NULL;
2307210284Sjmallett    if (pipe->tail)
2308210284Sjmallett    {
2309210284Sjmallett        transaction->prev = pipe->tail;
2310210284Sjmallett        transaction->prev->next = transaction;
2311210284Sjmallett    }
2312210284Sjmallett    else
2313210284Sjmallett    {
2314215990Sjmallett        if (pipe->next_tx_frame < usb->frame_number)
2315215990Sjmallett            pipe->next_tx_frame = usb->frame_number + pipe->interval -
2316215990Sjmallett                (usb->frame_number - pipe->next_tx_frame) % pipe->interval;
2317210284Sjmallett        transaction->prev = NULL;
2318210284Sjmallett        pipe->head = transaction;
2319210284Sjmallett        __cvmx_usb_remove_pipe(&usb->idle_pipes, pipe);
2320210284Sjmallett        __cvmx_usb_append_pipe(usb->active_pipes + pipe->transfer_type, pipe);
2321210284Sjmallett    }
2322210284Sjmallett    pipe->tail = transaction;
2323210284Sjmallett
2324210284Sjmallett    submit_handle = __cvmx_usb_get_submit_handle(usb, transaction);
2325210284Sjmallett
2326210284Sjmallett    /* We may need to schedule the pipe if this was the head of the pipe */
2327210284Sjmallett    if (!transaction->prev)
2328210284Sjmallett        __cvmx_usb_schedule(usb, 0);
2329210284Sjmallett
2330210284Sjmallett    CVMX_USB_RETURN(submit_handle);
2331210284Sjmallett}
2332210284Sjmallett
2333210284Sjmallett
2334210284Sjmallett/**
2335210284Sjmallett * Call to submit a USB Bulk transfer to a pipe.
2336210284Sjmallett *
2337210284Sjmallett * @param state     USB device state populated by
2338210284Sjmallett *                  cvmx_usb_initialize().
2339210284Sjmallett * @param pipe_handle
2340210284Sjmallett *                  Handle to the pipe for the transfer.
2341210284Sjmallett * @param buffer    Physical address of the data buffer in
2342210284Sjmallett *                  memory. Note that this is NOT A POINTER, but
2343210284Sjmallett *                  the full 64bit physical address of the
2344210284Sjmallett *                  buffer. This may be zero if buffer_length is
2345210284Sjmallett *                  zero.
2346210284Sjmallett * @param buffer_length
2347210284Sjmallett *                  Length of buffer in bytes.
2348210284Sjmallett * @param callback  Function to call when this transaction
2349210284Sjmallett *                  completes. If the return value of this
2350210284Sjmallett *                  function isn't an error, then this function
2351210284Sjmallett *                  is guaranteed to be called when the
2352210284Sjmallett *                  transaction completes. If this parameter is
2353210284Sjmallett *                  NULL, then the generic callback registered
2354210284Sjmallett *                  through cvmx_usb_register_callback is
2355210284Sjmallett *                  called. If both are NULL, then there is no
2356210284Sjmallett *                  way to know when a transaction completes.
2357210284Sjmallett * @param user_data User supplied data returned when the
2358210284Sjmallett *                  callback is called. This is only used if
2359210284Sjmallett *                  callback in not NULL.
2360210284Sjmallett *
2361210284Sjmallett * @return A submitted transaction handle or negative on
2362210284Sjmallett *         failure. Negative values are failure codes from
2363210284Sjmallett *         cvmx_usb_status_t.
2364210284Sjmallett */
2365210284Sjmallettint cvmx_usb_submit_bulk(cvmx_usb_state_t *state, int pipe_handle,
2366210284Sjmallett                                uint64_t buffer, int buffer_length,
2367210284Sjmallett                                cvmx_usb_callback_func_t callback,
2368210284Sjmallett                                void *user_data)
2369210284Sjmallett{
2370210284Sjmallett    int submit_handle;
2371210284Sjmallett    cvmx_usb_internal_state_t *usb = (cvmx_usb_internal_state_t*)state;
2372210284Sjmallett
2373210284Sjmallett    CVMX_USB_LOG_CALLED();
2374210284Sjmallett    CVMX_USB_LOG_PARAM("%p", state);
2375210284Sjmallett    CVMX_USB_LOG_PARAM("%d", pipe_handle);
2376210284Sjmallett    CVMX_USB_LOG_PARAM("0x%llx", (unsigned long long)buffer);
2377210284Sjmallett    CVMX_USB_LOG_PARAM("%d", buffer_length);
2378210284Sjmallett
2379210284Sjmallett    /* Pipe handle checking is done later in a common place */
2380210284Sjmallett    if (cvmx_unlikely(!buffer))
2381210284Sjmallett        CVMX_USB_RETURN(CVMX_USB_INVALID_PARAM);
2382210284Sjmallett    if (cvmx_unlikely(buffer_length < 0))
2383210284Sjmallett        CVMX_USB_RETURN(CVMX_USB_INVALID_PARAM);
2384210284Sjmallett
2385210284Sjmallett    submit_handle = __cvmx_usb_submit_transaction(usb, pipe_handle,
2386210284Sjmallett                                         CVMX_USB_TRANSFER_BULK,
2387210284Sjmallett                                         0, /* flags */
2388210284Sjmallett                                         buffer,
2389210284Sjmallett                                         buffer_length,
2390210284Sjmallett                                         0, /* control_header */
2391210284Sjmallett                                         0, /* iso_start_frame */
2392210284Sjmallett                                         0, /* iso_number_packets */
2393210284Sjmallett                                         NULL, /* iso_packets */
2394210284Sjmallett                                         callback,
2395210284Sjmallett                                         user_data);
2396210284Sjmallett    CVMX_USB_RETURN(submit_handle);
2397210284Sjmallett}
2398215990Sjmallett#ifdef CVMX_BUILD_FOR_LINUX_KERNEL
2399215990SjmallettEXPORT_SYMBOL(cvmx_usb_submit_bulk);
2400215990Sjmallett#endif
2401210284Sjmallett
2402210284Sjmallett
2403210284Sjmallett/**
2404210284Sjmallett * Call to submit a USB Interrupt transfer to a pipe.
2405210284Sjmallett *
2406210284Sjmallett * @param state     USB device state populated by
2407210284Sjmallett *                  cvmx_usb_initialize().
2408210284Sjmallett * @param pipe_handle
2409210284Sjmallett *                  Handle to the pipe for the transfer.
2410210284Sjmallett * @param buffer    Physical address of the data buffer in
2411210284Sjmallett *                  memory. Note that this is NOT A POINTER, but
2412210284Sjmallett *                  the full 64bit physical address of the
2413210284Sjmallett *                  buffer. This may be zero if buffer_length is
2414210284Sjmallett *                  zero.
2415210284Sjmallett * @param buffer_length
2416210284Sjmallett *                  Length of buffer in bytes.
2417210284Sjmallett * @param callback  Function to call when this transaction
2418210284Sjmallett *                  completes. If the return value of this
2419210284Sjmallett *                  function isn't an error, then this function
2420210284Sjmallett *                  is guaranteed to be called when the
2421210284Sjmallett *                  transaction completes. If this parameter is
2422210284Sjmallett *                  NULL, then the generic callback registered
2423210284Sjmallett *                  through cvmx_usb_register_callback is
2424210284Sjmallett *                  called. If both are NULL, then there is no
2425210284Sjmallett *                  way to know when a transaction completes.
2426210284Sjmallett * @param user_data User supplied data returned when the
2427210284Sjmallett *                  callback is called. This is only used if
2428210284Sjmallett *                  callback in not NULL.
2429210284Sjmallett *
2430210284Sjmallett * @return A submitted transaction handle or negative on
2431210284Sjmallett *         failure. Negative values are failure codes from
2432210284Sjmallett *         cvmx_usb_status_t.
2433210284Sjmallett */
2434210284Sjmallettint cvmx_usb_submit_interrupt(cvmx_usb_state_t *state, int pipe_handle,
2435210284Sjmallett                              uint64_t buffer, int buffer_length,
2436210284Sjmallett                              cvmx_usb_callback_func_t callback,
2437210284Sjmallett                              void *user_data)
2438210284Sjmallett{
2439210284Sjmallett    int submit_handle;
2440210284Sjmallett    cvmx_usb_internal_state_t *usb = (cvmx_usb_internal_state_t*)state;
2441210284Sjmallett
2442210284Sjmallett    CVMX_USB_LOG_CALLED();
2443210284Sjmallett    CVMX_USB_LOG_PARAM("%p", state);
2444210284Sjmallett    CVMX_USB_LOG_PARAM("%d", pipe_handle);
2445210284Sjmallett    CVMX_USB_LOG_PARAM("0x%llx", (unsigned long long)buffer);
2446210284Sjmallett    CVMX_USB_LOG_PARAM("%d", buffer_length);
2447210284Sjmallett
2448210284Sjmallett    /* Pipe handle checking is done later in a common place */
2449210284Sjmallett    if (cvmx_unlikely(!buffer))
2450210284Sjmallett        CVMX_USB_RETURN(CVMX_USB_INVALID_PARAM);
2451210284Sjmallett    if (cvmx_unlikely(buffer_length < 0))
2452210284Sjmallett        CVMX_USB_RETURN(CVMX_USB_INVALID_PARAM);
2453210284Sjmallett
2454210284Sjmallett    submit_handle = __cvmx_usb_submit_transaction(usb, pipe_handle,
2455210284Sjmallett                                         CVMX_USB_TRANSFER_INTERRUPT,
2456210284Sjmallett                                         0, /* flags */
2457210284Sjmallett                                         buffer,
2458210284Sjmallett                                         buffer_length,
2459210284Sjmallett                                         0, /* control_header */
2460210284Sjmallett                                         0, /* iso_start_frame */
2461210284Sjmallett                                         0, /* iso_number_packets */
2462210284Sjmallett                                         NULL, /* iso_packets */
2463210284Sjmallett                                         callback,
2464210284Sjmallett                                         user_data);
2465210284Sjmallett    CVMX_USB_RETURN(submit_handle);
2466210284Sjmallett}
2467215990Sjmallett#ifdef CVMX_BUILD_FOR_LINUX_KERNEL
2468215990SjmallettEXPORT_SYMBOL(cvmx_usb_submit_interrupt);
2469215990Sjmallett#endif
2470210284Sjmallett
2471210284Sjmallett
2472210284Sjmallett/**
2473210284Sjmallett * Call to submit a USB Control transfer to a pipe.
2474210284Sjmallett *
2475210284Sjmallett * @param state     USB device state populated by
2476210284Sjmallett *                  cvmx_usb_initialize().
2477210284Sjmallett * @param pipe_handle
2478210284Sjmallett *                  Handle to the pipe for the transfer.
2479210284Sjmallett * @param control_header
2480210284Sjmallett *                  USB 8 byte control header physical address.
2481210284Sjmallett *                  Note that this is NOT A POINTER, but the
2482210284Sjmallett *                  full 64bit physical address of the buffer.
2483210284Sjmallett * @param buffer    Physical address of the data buffer in
2484210284Sjmallett *                  memory. Note that this is NOT A POINTER, but
2485210284Sjmallett *                  the full 64bit physical address of the
2486210284Sjmallett *                  buffer. This may be zero if buffer_length is
2487210284Sjmallett *                  zero.
2488210284Sjmallett * @param buffer_length
2489210284Sjmallett *                  Length of buffer in bytes.
2490210284Sjmallett * @param callback  Function to call when this transaction
2491210284Sjmallett *                  completes. If the return value of this
2492210284Sjmallett *                  function isn't an error, then this function
2493210284Sjmallett *                  is guaranteed to be called when the
2494210284Sjmallett *                  transaction completes. If this parameter is
2495210284Sjmallett *                  NULL, then the generic callback registered
2496210284Sjmallett *                  through cvmx_usb_register_callback is
2497210284Sjmallett *                  called. If both are NULL, then there is no
2498210284Sjmallett *                  way to know when a transaction completes.
2499210284Sjmallett * @param user_data User supplied data returned when the
2500210284Sjmallett *                  callback is called. This is only used if
2501210284Sjmallett *                  callback in not NULL.
2502210284Sjmallett *
2503210284Sjmallett * @return A submitted transaction handle or negative on
2504210284Sjmallett *         failure. Negative values are failure codes from
2505210284Sjmallett *         cvmx_usb_status_t.
2506210284Sjmallett */
2507210284Sjmallettint cvmx_usb_submit_control(cvmx_usb_state_t *state, int pipe_handle,
2508210284Sjmallett                            uint64_t control_header,
2509210284Sjmallett                            uint64_t buffer, int buffer_length,
2510210284Sjmallett                            cvmx_usb_callback_func_t callback,
2511210284Sjmallett                            void *user_data)
2512210284Sjmallett{
2513210284Sjmallett    int submit_handle;
2514210284Sjmallett    cvmx_usb_internal_state_t *usb = (cvmx_usb_internal_state_t*)state;
2515210284Sjmallett    cvmx_usb_control_header_t *header = cvmx_phys_to_ptr(control_header);
2516210284Sjmallett
2517210284Sjmallett    CVMX_USB_LOG_CALLED();
2518210284Sjmallett    CVMX_USB_LOG_PARAM("%p", state);
2519210284Sjmallett    CVMX_USB_LOG_PARAM("%d", pipe_handle);
2520210284Sjmallett    CVMX_USB_LOG_PARAM("0x%llx", (unsigned long long)control_header);
2521210284Sjmallett    CVMX_USB_LOG_PARAM("0x%llx", (unsigned long long)buffer);
2522210284Sjmallett    CVMX_USB_LOG_PARAM("%d", buffer_length);
2523210284Sjmallett
2524210284Sjmallett    /* Pipe handle checking is done later in a common place */
2525210284Sjmallett    if (cvmx_unlikely(!control_header))
2526210284Sjmallett        CVMX_USB_RETURN(CVMX_USB_INVALID_PARAM);
2527210284Sjmallett    /* Some drivers send a buffer with a zero length. God only knows why */
2528210284Sjmallett    if (cvmx_unlikely(buffer && (buffer_length < 0)))
2529210284Sjmallett        CVMX_USB_RETURN(CVMX_USB_INVALID_PARAM);
2530210284Sjmallett    if (cvmx_unlikely(!buffer && (buffer_length != 0)))
2531210284Sjmallett        CVMX_USB_RETURN(CVMX_USB_INVALID_PARAM);
2532210284Sjmallett    if ((header->s.request_type & 0x80) == 0)
2533210284Sjmallett        buffer_length = cvmx_le16_to_cpu(header->s.length);
2534210284Sjmallett
2535210284Sjmallett    submit_handle = __cvmx_usb_submit_transaction(usb, pipe_handle,
2536210284Sjmallett                                         CVMX_USB_TRANSFER_CONTROL,
2537210284Sjmallett                                         0, /* flags */
2538210284Sjmallett                                         buffer,
2539210284Sjmallett                                         buffer_length,
2540210284Sjmallett                                         control_header,
2541210284Sjmallett                                         0, /* iso_start_frame */
2542210284Sjmallett                                         0, /* iso_number_packets */
2543210284Sjmallett                                         NULL, /* iso_packets */
2544210284Sjmallett                                         callback,
2545210284Sjmallett                                         user_data);
2546210284Sjmallett    CVMX_USB_RETURN(submit_handle);
2547210284Sjmallett}
2548215990Sjmallett#ifdef CVMX_BUILD_FOR_LINUX_KERNEL
2549215990SjmallettEXPORT_SYMBOL(cvmx_usb_submit_control);
2550215990Sjmallett#endif
2551210284Sjmallett
2552210284Sjmallett
2553210284Sjmallett/**
2554210284Sjmallett * Call to submit a USB Isochronous transfer to a pipe.
2555210284Sjmallett *
2556210284Sjmallett * @param state     USB device state populated by
2557210284Sjmallett *                  cvmx_usb_initialize().
2558210284Sjmallett * @param pipe_handle
2559210284Sjmallett *                  Handle to the pipe for the transfer.
2560210284Sjmallett * @param start_frame
2561210284Sjmallett *                  Number of frames into the future to schedule
2562210284Sjmallett *                  this transaction.
2563210284Sjmallett * @param flags     Flags to control the transfer. See
2564210284Sjmallett *                  cvmx_usb_isochronous_flags_t for the flag
2565210284Sjmallett *                  definitions.
2566210284Sjmallett * @param number_packets
2567210284Sjmallett *                  Number of sequential packets to transfer.
2568210284Sjmallett *                  "packets" is a pointer to an array of this
2569210284Sjmallett *                  many packet structures.
2570210284Sjmallett * @param packets   Description of each transfer packet as
2571210284Sjmallett *                  defined by cvmx_usb_iso_packet_t. The array
2572210284Sjmallett *                  pointed to here must stay valid until the
2573210284Sjmallett *                  complete callback is called.
2574210284Sjmallett * @param buffer    Physical address of the data buffer in
2575210284Sjmallett *                  memory. Note that this is NOT A POINTER, but
2576210284Sjmallett *                  the full 64bit physical address of the
2577210284Sjmallett *                  buffer. This may be zero if buffer_length is
2578210284Sjmallett *                  zero.
2579210284Sjmallett * @param buffer_length
2580210284Sjmallett *                  Length of buffer in bytes.
2581210284Sjmallett * @param callback  Function to call when this transaction
2582210284Sjmallett *                  completes. If the return value of this
2583210284Sjmallett *                  function isn't an error, then this function
2584210284Sjmallett *                  is guaranteed to be called when the
2585210284Sjmallett *                  transaction completes. If this parameter is
2586210284Sjmallett *                  NULL, then the generic callback registered
2587210284Sjmallett *                  through cvmx_usb_register_callback is
2588210284Sjmallett *                  called. If both are NULL, then there is no
2589210284Sjmallett *                  way to know when a transaction completes.
2590210284Sjmallett * @param user_data User supplied data returned when the
2591210284Sjmallett *                  callback is called. This is only used if
2592210284Sjmallett *                  callback in not NULL.
2593210284Sjmallett *
2594210284Sjmallett * @return A submitted transaction handle or negative on
2595210284Sjmallett *         failure. Negative values are failure codes from
2596210284Sjmallett *         cvmx_usb_status_t.
2597210284Sjmallett */
2598210284Sjmallettint cvmx_usb_submit_isochronous(cvmx_usb_state_t *state, int pipe_handle,
2599210284Sjmallett                                int start_frame, int flags,
2600210284Sjmallett                                int number_packets,
2601210284Sjmallett                                cvmx_usb_iso_packet_t packets[],
2602210284Sjmallett                                uint64_t buffer, int buffer_length,
2603210284Sjmallett                                cvmx_usb_callback_func_t callback,
2604210284Sjmallett                                void *user_data)
2605210284Sjmallett{
2606210284Sjmallett    int submit_handle;
2607210284Sjmallett    cvmx_usb_internal_state_t *usb = (cvmx_usb_internal_state_t*)state;
2608210284Sjmallett
2609210284Sjmallett    CVMX_USB_LOG_CALLED();
2610210284Sjmallett    CVMX_USB_LOG_PARAM("%p", state);
2611210284Sjmallett    CVMX_USB_LOG_PARAM("%d", pipe_handle);
2612210284Sjmallett    CVMX_USB_LOG_PARAM("%d", start_frame);
2613210284Sjmallett    CVMX_USB_LOG_PARAM("0x%x", flags);
2614210284Sjmallett    CVMX_USB_LOG_PARAM("%d", number_packets);
2615210284Sjmallett    CVMX_USB_LOG_PARAM("%p", packets);
2616210284Sjmallett    CVMX_USB_LOG_PARAM("0x%llx", (unsigned long long)buffer);
2617210284Sjmallett    CVMX_USB_LOG_PARAM("%d", buffer_length);
2618210284Sjmallett
2619210284Sjmallett    /* Pipe handle checking is done later in a common place */
2620210284Sjmallett    if (cvmx_unlikely(start_frame < 0))
2621210284Sjmallett        CVMX_USB_RETURN(CVMX_USB_INVALID_PARAM);
2622210284Sjmallett    if (cvmx_unlikely(flags & ~(CVMX_USB_ISOCHRONOUS_FLAGS_ALLOW_SHORT | CVMX_USB_ISOCHRONOUS_FLAGS_ASAP)))
2623210284Sjmallett        CVMX_USB_RETURN(CVMX_USB_INVALID_PARAM);
2624210284Sjmallett    if (cvmx_unlikely(number_packets < 1))
2625210284Sjmallett        CVMX_USB_RETURN(CVMX_USB_INVALID_PARAM);
2626210284Sjmallett    if (cvmx_unlikely(!packets))
2627210284Sjmallett        CVMX_USB_RETURN(CVMX_USB_INVALID_PARAM);
2628210284Sjmallett    if (cvmx_unlikely(!buffer))
2629210284Sjmallett        CVMX_USB_RETURN(CVMX_USB_INVALID_PARAM);
2630210284Sjmallett    if (cvmx_unlikely(buffer_length < 0))
2631210284Sjmallett        CVMX_USB_RETURN(CVMX_USB_INVALID_PARAM);
2632210284Sjmallett
2633210284Sjmallett    submit_handle = __cvmx_usb_submit_transaction(usb, pipe_handle,
2634210284Sjmallett                                         CVMX_USB_TRANSFER_ISOCHRONOUS,
2635210284Sjmallett                                         flags,
2636210284Sjmallett                                         buffer,
2637210284Sjmallett                                         buffer_length,
2638210284Sjmallett                                         0, /* control_header */
2639210284Sjmallett                                         start_frame,
2640210284Sjmallett                                         number_packets,
2641210284Sjmallett                                         packets,
2642210284Sjmallett                                         callback,
2643210284Sjmallett                                         user_data);
2644210284Sjmallett    CVMX_USB_RETURN(submit_handle);
2645210284Sjmallett}
2646215990Sjmallett#ifdef CVMX_BUILD_FOR_LINUX_KERNEL
2647215990SjmallettEXPORT_SYMBOL(cvmx_usb_submit_isochronous);
2648215990Sjmallett#endif
2649210284Sjmallett
2650210284Sjmallett
2651210284Sjmallett/**
2652210284Sjmallett * Cancel one outstanding request in a pipe. Canceling a request
2653210284Sjmallett * can fail if the transaction has already completed before cancel
2654210284Sjmallett * is called. Even after a successful cancel call, it may take
2655210284Sjmallett * a frame or two for the cvmx_usb_poll() function to call the
2656210284Sjmallett * associated callback.
2657210284Sjmallett *
2658210284Sjmallett * @param state  USB device state populated by
2659210284Sjmallett *               cvmx_usb_initialize().
2660210284Sjmallett * @param pipe_handle
2661210284Sjmallett *               Pipe handle to cancel requests in.
2662210284Sjmallett * @param submit_handle
2663210284Sjmallett *               Handle to transaction to cancel, returned by the submit function.
2664210284Sjmallett *
2665210284Sjmallett * @return CVMX_USB_SUCCESS or a negative error code defined in
2666210284Sjmallett *         cvmx_usb_status_t.
2667210284Sjmallett */
2668210284Sjmallettcvmx_usb_status_t cvmx_usb_cancel(cvmx_usb_state_t *state, int pipe_handle,
2669210284Sjmallett                                  int submit_handle)
2670210284Sjmallett{
2671210284Sjmallett    cvmx_usb_transaction_t *transaction;
2672210284Sjmallett    cvmx_usb_internal_state_t *usb = (cvmx_usb_internal_state_t*)state;
2673210284Sjmallett    cvmx_usb_pipe_t *pipe = usb->pipe + pipe_handle;
2674210284Sjmallett
2675210284Sjmallett    CVMX_USB_LOG_CALLED();
2676210284Sjmallett    CVMX_USB_LOG_PARAM("%p", state);
2677210284Sjmallett    CVMX_USB_LOG_PARAM("%d", pipe_handle);
2678210284Sjmallett    CVMX_USB_LOG_PARAM("%d", submit_handle);
2679210284Sjmallett
2680210284Sjmallett    if (cvmx_unlikely((pipe_handle < 0) || (pipe_handle >= MAX_PIPES)))
2681210284Sjmallett        CVMX_USB_RETURN(CVMX_USB_INVALID_PARAM);
2682210284Sjmallett    if (cvmx_unlikely((submit_handle < 0) || (submit_handle >= MAX_TRANSACTIONS)))
2683210284Sjmallett        CVMX_USB_RETURN(CVMX_USB_INVALID_PARAM);
2684210284Sjmallett
2685210284Sjmallett    /* Fail if the pipe isn't open */
2686210284Sjmallett    if (cvmx_unlikely((pipe->flags & __CVMX_USB_PIPE_FLAGS_OPEN) == 0))
2687210284Sjmallett        CVMX_USB_RETURN(CVMX_USB_INVALID_PARAM);
2688210284Sjmallett
2689210284Sjmallett    transaction = usb->transaction + submit_handle;
2690210284Sjmallett
2691210284Sjmallett    /* Fail if this transaction already completed */
2692210284Sjmallett    if (cvmx_unlikely((transaction->flags & __CVMX_USB_TRANSACTION_FLAGS_IN_USE) == 0))
2693210284Sjmallett        CVMX_USB_RETURN(CVMX_USB_INVALID_PARAM);
2694210284Sjmallett
2695210284Sjmallett    /* If the transaction is the HEAD of the queue and scheduled. We need to
2696210284Sjmallett        treat it special */
2697210284Sjmallett    if ((pipe->head == transaction) &&
2698210284Sjmallett        (pipe->flags & __CVMX_USB_PIPE_FLAGS_SCHEDULED))
2699210284Sjmallett    {
2700210284Sjmallett        cvmx_usbcx_hccharx_t usbc_hcchar;
2701210284Sjmallett
2702210284Sjmallett        usb->pipe_for_channel[pipe->channel] = NULL;
2703210284Sjmallett        pipe->flags &= ~__CVMX_USB_PIPE_FLAGS_SCHEDULED;
2704210284Sjmallett
2705210284Sjmallett        CVMX_SYNCW;
2706210284Sjmallett
2707210284Sjmallett        usbc_hcchar.u32 = __cvmx_usb_read_csr32(usb, CVMX_USBCX_HCCHARX(pipe->channel, usb->index));
2708210284Sjmallett        /* If the channel isn't enabled then the transaction already completed */
2709210284Sjmallett        if (usbc_hcchar.s.chena)
2710210284Sjmallett        {
2711210284Sjmallett            usbc_hcchar.s.chdis = 1;
2712210284Sjmallett            __cvmx_usb_write_csr32(usb, CVMX_USBCX_HCCHARX(pipe->channel, usb->index), usbc_hcchar.u32);
2713210284Sjmallett        }
2714210284Sjmallett    }
2715210284Sjmallett    __cvmx_usb_perform_complete(usb, pipe, transaction, CVMX_USB_COMPLETE_CANCEL);
2716210284Sjmallett    CVMX_USB_RETURN(CVMX_USB_SUCCESS);
2717210284Sjmallett}
2718215990Sjmallett#ifdef CVMX_BUILD_FOR_LINUX_KERNEL
2719215990SjmallettEXPORT_SYMBOL(cvmx_usb_cancel);
2720215990Sjmallett#endif
2721210284Sjmallett
2722210284Sjmallett
2723210284Sjmallett/**
2724210284Sjmallett * Cancel all outstanding requests in a pipe. Logically all this
2725210284Sjmallett * does is call cvmx_usb_cancel() in a loop.
2726210284Sjmallett *
2727210284Sjmallett * @param state  USB device state populated by
2728210284Sjmallett *               cvmx_usb_initialize().
2729210284Sjmallett * @param pipe_handle
2730210284Sjmallett *               Pipe handle to cancel requests in.
2731210284Sjmallett *
2732210284Sjmallett * @return CVMX_USB_SUCCESS or a negative error code defined in
2733210284Sjmallett *         cvmx_usb_status_t.
2734210284Sjmallett */
2735210284Sjmallettcvmx_usb_status_t cvmx_usb_cancel_all(cvmx_usb_state_t *state, int pipe_handle)
2736210284Sjmallett{
2737210284Sjmallett    cvmx_usb_internal_state_t *usb = (cvmx_usb_internal_state_t*)state;
2738210284Sjmallett    cvmx_usb_pipe_t *pipe = usb->pipe + pipe_handle;
2739210284Sjmallett
2740210284Sjmallett    CVMX_USB_LOG_CALLED();
2741210284Sjmallett    CVMX_USB_LOG_PARAM("%p", state);
2742210284Sjmallett    CVMX_USB_LOG_PARAM("%d", pipe_handle);
2743210284Sjmallett    if (cvmx_unlikely((pipe_handle < 0) || (pipe_handle >= MAX_PIPES)))
2744210284Sjmallett        CVMX_USB_RETURN(CVMX_USB_INVALID_PARAM);
2745210284Sjmallett
2746210284Sjmallett    /* Fail if the pipe isn't open */
2747210284Sjmallett    if (cvmx_unlikely((pipe->flags & __CVMX_USB_PIPE_FLAGS_OPEN) == 0))
2748210284Sjmallett        CVMX_USB_RETURN(CVMX_USB_INVALID_PARAM);
2749210284Sjmallett
2750210284Sjmallett    /* Simply loop through and attempt to cancel each transaction */
2751210284Sjmallett    while (pipe->head)
2752210284Sjmallett    {
2753210284Sjmallett        cvmx_usb_status_t result = cvmx_usb_cancel(state, pipe_handle,
2754210284Sjmallett            __cvmx_usb_get_submit_handle(usb, pipe->head));
2755210284Sjmallett        if (cvmx_unlikely(result != CVMX_USB_SUCCESS))
2756210284Sjmallett            CVMX_USB_RETURN(result);
2757210284Sjmallett    }
2758210284Sjmallett    CVMX_USB_RETURN(CVMX_USB_SUCCESS);
2759210284Sjmallett}
2760215990Sjmallett#ifdef CVMX_BUILD_FOR_LINUX_KERNEL
2761215990SjmallettEXPORT_SYMBOL(cvmx_usb_cancel_all);
2762215990Sjmallett#endif
2763210284Sjmallett
2764210284Sjmallett
2765210284Sjmallett/**
2766210284Sjmallett * Close a pipe created with cvmx_usb_open_pipe().
2767210284Sjmallett *
2768210284Sjmallett * @param state  USB device state populated by
2769210284Sjmallett *               cvmx_usb_initialize().
2770210284Sjmallett * @param pipe_handle
2771210284Sjmallett *               Pipe handle to close.
2772210284Sjmallett *
2773210284Sjmallett * @return CVMX_USB_SUCCESS or a negative error code defined in
2774210284Sjmallett *         cvmx_usb_status_t. CVMX_USB_BUSY is returned if the
2775210284Sjmallett *         pipe has outstanding transfers.
2776210284Sjmallett */
2777210284Sjmallettcvmx_usb_status_t cvmx_usb_close_pipe(cvmx_usb_state_t *state, int pipe_handle)
2778210284Sjmallett{
2779210284Sjmallett    cvmx_usb_internal_state_t *usb = (cvmx_usb_internal_state_t*)state;
2780210284Sjmallett    cvmx_usb_pipe_t *pipe = usb->pipe + pipe_handle;
2781210284Sjmallett
2782210284Sjmallett    CVMX_USB_LOG_CALLED();
2783210284Sjmallett    CVMX_USB_LOG_PARAM("%p", state);
2784210284Sjmallett    CVMX_USB_LOG_PARAM("%d", pipe_handle);
2785210284Sjmallett    if (cvmx_unlikely((pipe_handle < 0) || (pipe_handle >= MAX_PIPES)))
2786210284Sjmallett        CVMX_USB_RETURN(CVMX_USB_INVALID_PARAM);
2787210284Sjmallett
2788210284Sjmallett    /* Fail if the pipe isn't open */
2789210284Sjmallett    if (cvmx_unlikely((pipe->flags & __CVMX_USB_PIPE_FLAGS_OPEN) == 0))
2790210284Sjmallett        CVMX_USB_RETURN(CVMX_USB_INVALID_PARAM);
2791210284Sjmallett
2792210284Sjmallett    /* Fail if the pipe has pending transactions */
2793210284Sjmallett    if (cvmx_unlikely(pipe->head))
2794210284Sjmallett        CVMX_USB_RETURN(CVMX_USB_BUSY);
2795210284Sjmallett
2796210284Sjmallett    pipe->flags = 0;
2797210284Sjmallett    __cvmx_usb_remove_pipe(&usb->idle_pipes, pipe);
2798210284Sjmallett    __cvmx_usb_append_pipe(&usb->free_pipes, pipe);
2799210284Sjmallett
2800210284Sjmallett    CVMX_USB_RETURN(CVMX_USB_SUCCESS);
2801210284Sjmallett}
2802215990Sjmallett#ifdef CVMX_BUILD_FOR_LINUX_KERNEL
2803215990SjmallettEXPORT_SYMBOL(cvmx_usb_close_pipe);
2804215990Sjmallett#endif
2805210284Sjmallett
2806210284Sjmallett
2807210284Sjmallett/**
2808210284Sjmallett * Register a function to be called when various USB events occur.
2809210284Sjmallett *
2810210284Sjmallett * @param state     USB device state populated by
2811210284Sjmallett *                  cvmx_usb_initialize().
2812210284Sjmallett * @param reason    Which event to register for.
2813210284Sjmallett * @param callback  Function to call when the event occurs.
2814210284Sjmallett * @param user_data User data parameter to the function.
2815210284Sjmallett *
2816210284Sjmallett * @return CVMX_USB_SUCCESS or a negative error code defined in
2817210284Sjmallett *         cvmx_usb_status_t.
2818210284Sjmallett */
2819210284Sjmallettcvmx_usb_status_t cvmx_usb_register_callback(cvmx_usb_state_t *state,
2820210284Sjmallett                                             cvmx_usb_callback_t reason,
2821210284Sjmallett                                             cvmx_usb_callback_func_t callback,
2822210284Sjmallett                                             void *user_data)
2823210284Sjmallett{
2824210284Sjmallett    cvmx_usb_internal_state_t *usb = (cvmx_usb_internal_state_t*)state;
2825210284Sjmallett
2826210284Sjmallett    CVMX_USB_LOG_CALLED();
2827210284Sjmallett    CVMX_USB_LOG_PARAM("%p", state);
2828210284Sjmallett    CVMX_USB_LOG_PARAM("%d", reason);
2829210284Sjmallett    CVMX_USB_LOG_PARAM("%p", callback);
2830210284Sjmallett    CVMX_USB_LOG_PARAM("%p", user_data);
2831210284Sjmallett    if (cvmx_unlikely(reason >= __CVMX_USB_CALLBACK_END))
2832210284Sjmallett        CVMX_USB_RETURN(CVMX_USB_INVALID_PARAM);
2833210284Sjmallett    if (cvmx_unlikely(!callback))
2834210284Sjmallett        CVMX_USB_RETURN(CVMX_USB_INVALID_PARAM);
2835210284Sjmallett
2836210284Sjmallett    usb->callback[reason] = callback;
2837210284Sjmallett    usb->callback_data[reason] = user_data;
2838210284Sjmallett
2839210284Sjmallett    CVMX_USB_RETURN(CVMX_USB_SUCCESS);
2840210284Sjmallett}
2841215990Sjmallett#ifdef CVMX_BUILD_FOR_LINUX_KERNEL
2842215990SjmallettEXPORT_SYMBOL(cvmx_usb_register_callback);
2843215990Sjmallett#endif
2844210284Sjmallett
2845210284Sjmallett
2846210284Sjmallett/**
2847210284Sjmallett * Get the current USB protocol level frame number. The frame
2848210284Sjmallett * number is always in the range of 0-0x7ff.
2849210284Sjmallett *
2850210284Sjmallett * @param state  USB device state populated by
2851210284Sjmallett *               cvmx_usb_initialize().
2852210284Sjmallett *
2853210284Sjmallett * @return USB frame number
2854210284Sjmallett */
2855210284Sjmallettint cvmx_usb_get_frame_number(cvmx_usb_state_t *state)
2856210284Sjmallett{
2857210284Sjmallett    int frame_number;
2858210284Sjmallett    cvmx_usb_internal_state_t *usb = (cvmx_usb_internal_state_t*)state;
2859215990Sjmallett    cvmx_usbcx_hfnum_t usbc_hfnum;
2860210284Sjmallett
2861210284Sjmallett    CVMX_USB_LOG_CALLED();
2862210284Sjmallett    CVMX_USB_LOG_PARAM("%p", state);
2863210284Sjmallett
2864215990Sjmallett    usbc_hfnum.u32 = __cvmx_usb_read_csr32(usb, CVMX_USBCX_HFNUM(usb->index));
2865215990Sjmallett    frame_number = usbc_hfnum.s.frnum;
2866210284Sjmallett
2867210284Sjmallett    CVMX_USB_RETURN(frame_number);
2868210284Sjmallett}
2869215990Sjmallett#ifdef CVMX_BUILD_FOR_LINUX_KERNEL
2870215990SjmallettEXPORT_SYMBOL(cvmx_usb_get_frame_number);
2871215990Sjmallett#endif
2872210284Sjmallett
2873210284Sjmallett
2874210284Sjmallett/**
2875210284Sjmallett * @INTERNAL
2876210284Sjmallett * Poll a channel for status
2877210284Sjmallett *
2878210284Sjmallett * @param usb     USB device
2879210284Sjmallett * @param channel Channel to poll
2880210284Sjmallett *
2881210284Sjmallett * @return Zero on success
2882210284Sjmallett */
2883210284Sjmallettstatic int __cvmx_usb_poll_channel(cvmx_usb_internal_state_t *usb, int channel)
2884210284Sjmallett{
2885210284Sjmallett    cvmx_usbcx_hcintx_t usbc_hcint;
2886210284Sjmallett    cvmx_usbcx_hctsizx_t usbc_hctsiz;
2887210284Sjmallett    cvmx_usbcx_hccharx_t usbc_hcchar;
2888210284Sjmallett    cvmx_usb_pipe_t *pipe;
2889210284Sjmallett    cvmx_usb_transaction_t *transaction;
2890210284Sjmallett    int bytes_this_transfer;
2891210284Sjmallett    int bytes_in_last_packet;
2892210284Sjmallett    int packets_processed;
2893210284Sjmallett    int buffer_space_left;
2894210284Sjmallett    CVMX_USB_LOG_CALLED();
2895210284Sjmallett    CVMX_USB_LOG_PARAM("%p", usb);
2896210284Sjmallett    CVMX_USB_LOG_PARAM("%d", channel);
2897210284Sjmallett
2898210284Sjmallett    /* Read the interrupt status bits for the channel */
2899210284Sjmallett    usbc_hcint.u32 = __cvmx_usb_read_csr32(usb, CVMX_USBCX_HCINTX(channel, usb->index));
2900210284Sjmallett
2901215990Sjmallett#if 0
2902215990Sjmallett    cvmx_dprintf("Channel %d%s%s%s%s%s%s%s%s%s%s%s\n", channel,
2903215990Sjmallett        (usbc_hcint.s.datatglerr) ? " DATATGLERR" : "",
2904215990Sjmallett        (usbc_hcint.s.frmovrun) ? " FRMOVRUN" : "",
2905215990Sjmallett        (usbc_hcint.s.bblerr) ? " BBLERR" : "",
2906215990Sjmallett        (usbc_hcint.s.xacterr) ? " XACTERR" : "",
2907215990Sjmallett        (usbc_hcint.s.nyet) ? " NYET" : "",
2908215990Sjmallett        (usbc_hcint.s.ack) ? " ACK" : "",
2909215990Sjmallett        (usbc_hcint.s.nak) ? " NAK" : "",
2910215990Sjmallett        (usbc_hcint.s.stall) ? " STALL" : "",
2911215990Sjmallett        (usbc_hcint.s.ahberr) ? " AHBERR" : "",
2912215990Sjmallett        (usbc_hcint.s.chhltd) ? " CHHLTD" : "",
2913215990Sjmallett        (usbc_hcint.s.xfercompl) ? " XFERCOMPL" : "");
2914215990Sjmallett#endif
2915210284Sjmallett
2916215990Sjmallett    if (usb->init_flags & CVMX_USB_INITIALIZE_FLAGS_NO_DMA)
2917215990Sjmallett    {
2918215990Sjmallett        usbc_hcchar.u32 = __cvmx_usb_read_csr32(usb, CVMX_USBCX_HCCHARX(channel, usb->index));
2919210284Sjmallett
2920215990Sjmallett        if (usbc_hcchar.s.chena && usbc_hcchar.s.chdis)
2921215990Sjmallett        {
2922215990Sjmallett            /* There seems to be a bug in CN31XX which can cause interrupt
2923215990Sjmallett                IN transfers to get stuck until we do a write of HCCHARX
2924215990Sjmallett                without changing things */
2925215990Sjmallett            __cvmx_usb_write_csr32(usb, CVMX_USBCX_HCCHARX(channel, usb->index), usbc_hcchar.u32);
2926215990Sjmallett            CVMX_USB_RETURN(0);
2927215990Sjmallett        }
2928215990Sjmallett
2929215990Sjmallett        /* In non DMA mode the channels don't halt themselves. We need to
2930215990Sjmallett            manually disable channels that are left running */
2931215990Sjmallett        if (!usbc_hcint.s.chhltd)
2932215990Sjmallett        {
2933215990Sjmallett            if (usbc_hcchar.s.chena)
2934215990Sjmallett            {
2935215990Sjmallett                cvmx_usbcx_hcintmskx_t hcintmsk;
2936215990Sjmallett                /* Disable all interrupts except CHHLTD */
2937215990Sjmallett                hcintmsk.u32 = 0;
2938215990Sjmallett                hcintmsk.s.chhltdmsk = 1;
2939215990Sjmallett                __cvmx_usb_write_csr32(usb, CVMX_USBCX_HCINTMSKX(channel, usb->index), hcintmsk.u32);
2940215990Sjmallett                usbc_hcchar.s.chdis = 1;
2941215990Sjmallett                __cvmx_usb_write_csr32(usb, CVMX_USBCX_HCCHARX(channel, usb->index), usbc_hcchar.u32);
2942215990Sjmallett                CVMX_USB_RETURN(0);
2943215990Sjmallett            }
2944215990Sjmallett            else if (usbc_hcint.s.xfercompl)
2945215990Sjmallett            {
2946215990Sjmallett                /* Successful IN/OUT with transfer complete. Channel halt isn't needed */
2947215990Sjmallett            }
2948215990Sjmallett            else
2949215990Sjmallett            {
2950215990Sjmallett                cvmx_dprintf("USB%d: Channel %d interrupt without halt\n", usb->index, channel);
2951215990Sjmallett                CVMX_USB_RETURN(0);
2952215990Sjmallett            }
2953215990Sjmallett        }
2954215990Sjmallett    }
2955215990Sjmallett    else
2956215990Sjmallett    {
2957215990Sjmallett        /* There is are no interrupts that we need to process when the channel is
2958215990Sjmallett            still running */
2959215990Sjmallett        if (!usbc_hcint.s.chhltd)
2960215990Sjmallett            CVMX_USB_RETURN(0);
2961215990Sjmallett    }
2962215990Sjmallett
2963215990Sjmallett    /* Disable the channel interrupts now that it is done */
2964215990Sjmallett    __cvmx_usb_write_csr32(usb, CVMX_USBCX_HCINTMSKX(channel, usb->index), 0);
2965210284Sjmallett    usb->idle_hardware_channels |= (1<<channel);
2966210284Sjmallett
2967210284Sjmallett    /* Make sure this channel is tied to a valid pipe */
2968210284Sjmallett    pipe = usb->pipe_for_channel[channel];
2969210284Sjmallett    CVMX_PREFETCH(pipe, 0);
2970210284Sjmallett    CVMX_PREFETCH(pipe, 128);
2971210284Sjmallett    if (!pipe)
2972210284Sjmallett        CVMX_USB_RETURN(0);
2973210284Sjmallett    transaction = pipe->head;
2974210284Sjmallett    CVMX_PREFETCH0(transaction);
2975210284Sjmallett
2976210284Sjmallett    /* Disconnect this pipe from the HW channel. Later the schedule function will
2977210284Sjmallett        figure out which pipe needs to go */
2978210284Sjmallett    usb->pipe_for_channel[channel] = NULL;
2979210284Sjmallett    pipe->flags &= ~__CVMX_USB_PIPE_FLAGS_SCHEDULED;
2980210284Sjmallett
2981210284Sjmallett    /* Read the channel config info so we can figure out how much data
2982210284Sjmallett        transfered */
2983210284Sjmallett    usbc_hcchar.u32 = __cvmx_usb_read_csr32(usb, CVMX_USBCX_HCCHARX(channel, usb->index));
2984210284Sjmallett    usbc_hctsiz.u32 = __cvmx_usb_read_csr32(usb, CVMX_USBCX_HCTSIZX(channel, usb->index));
2985210284Sjmallett
2986215990Sjmallett    /* Calculating the number of bytes successfully transferred is dependent on
2987210284Sjmallett        the transfer direction */
2988210284Sjmallett    packets_processed = transaction->pktcnt - usbc_hctsiz.s.pktcnt;
2989210284Sjmallett    if (usbc_hcchar.s.epdir)
2990210284Sjmallett    {
2991210284Sjmallett        /* IN transactions are easy. For every byte received the hardware
2992210284Sjmallett            decrements xfersize. All we need to do is subtract the current
2993210284Sjmallett            value of xfersize from its starting value and we know how many
2994210284Sjmallett            bytes were written to the buffer */
2995210284Sjmallett        bytes_this_transfer = transaction->xfersize - usbc_hctsiz.s.xfersize;
2996210284Sjmallett    }
2997210284Sjmallett    else
2998210284Sjmallett    {
2999210284Sjmallett        /* OUT transaction don't decrement xfersize. Instead pktcnt is
3000210284Sjmallett            decremented on every successful packet send. The hardware does
3001210284Sjmallett            this when it receives an ACK, or NYET. If it doesn't
3002210284Sjmallett            receive one of these responses pktcnt doesn't change */
3003210284Sjmallett        bytes_this_transfer = packets_processed * usbc_hcchar.s.mps;
3004210284Sjmallett        /* The last packet may not be a full transfer if we didn't have
3005210284Sjmallett            enough data */
3006210284Sjmallett        if (bytes_this_transfer > transaction->xfersize)
3007210284Sjmallett            bytes_this_transfer = transaction->xfersize;
3008210284Sjmallett    }
3009210284Sjmallett    /* Figure out how many bytes were in the last packet of the transfer */
3010210284Sjmallett    if (packets_processed)
3011210284Sjmallett        bytes_in_last_packet = bytes_this_transfer - (packets_processed-1) * usbc_hcchar.s.mps;
3012210284Sjmallett    else
3013210284Sjmallett        bytes_in_last_packet = bytes_this_transfer;
3014210284Sjmallett
3015210284Sjmallett    /* As a special case, setup transactions output the setup header, not
3016210284Sjmallett        the user's data. For this reason we don't count setup data as bytes
3017215990Sjmallett        transferred */
3018210284Sjmallett    if ((transaction->stage == CVMX_USB_STAGE_SETUP) ||
3019210284Sjmallett        (transaction->stage == CVMX_USB_STAGE_SETUP_SPLIT_COMPLETE))
3020210284Sjmallett        bytes_this_transfer = 0;
3021210284Sjmallett
3022210284Sjmallett    /* Optional debug output */
3023210284Sjmallett    if (cvmx_unlikely((usb->init_flags & CVMX_USB_INITIALIZE_FLAGS_DEBUG_TRANSFERS) ||
3024210284Sjmallett        (pipe->flags & CVMX_USB_PIPE_FLAGS_DEBUG_TRANSFERS)))
3025210284Sjmallett        cvmx_dprintf("%s: Channel %d halted. Pipe %d transaction %d stage %d bytes=%d\n",
3026210284Sjmallett                     __FUNCTION__, channel,
3027210284Sjmallett                     __cvmx_usb_get_pipe_handle(usb, pipe),
3028210284Sjmallett                     __cvmx_usb_get_submit_handle(usb, transaction),
3029210284Sjmallett                     transaction->stage, bytes_this_transfer);
3030210284Sjmallett
3031215990Sjmallett    /* Add the bytes transferred to the running total. It is important that
3032210284Sjmallett        bytes_this_transfer doesn't count any data that needs to be
3033210284Sjmallett        retransmitted */
3034210284Sjmallett    transaction->actual_bytes += bytes_this_transfer;
3035210284Sjmallett    if (transaction->type == CVMX_USB_TRANSFER_ISOCHRONOUS)
3036210284Sjmallett        buffer_space_left = transaction->iso_packets[0].length - transaction->actual_bytes;
3037210284Sjmallett    else
3038210284Sjmallett        buffer_space_left = transaction->buffer_length - transaction->actual_bytes;
3039210284Sjmallett
3040210284Sjmallett    /* We need to remember the PID toggle state for the next transaction. The
3041210284Sjmallett        hardware already updated it for the next transaction */
3042210284Sjmallett    pipe->pid_toggle = !(usbc_hctsiz.s.pid == 0);
3043210284Sjmallett
3044210284Sjmallett    /* For high speed bulk out, assume the next transaction will need to do a
3045210284Sjmallett        ping before proceeding. If this isn't true the ACK processing below
3046210284Sjmallett        will clear this flag */
3047210284Sjmallett    if ((pipe->device_speed == CVMX_USB_SPEED_HIGH) &&
3048210284Sjmallett        (pipe->transfer_type == CVMX_USB_TRANSFER_BULK) &&
3049210284Sjmallett        (pipe->transfer_dir == CVMX_USB_DIRECTION_OUT))
3050210284Sjmallett        pipe->flags |= __CVMX_USB_PIPE_FLAGS_NEED_PING;
3051210284Sjmallett
3052210284Sjmallett    if (usbc_hcint.s.stall)
3053210284Sjmallett    {
3054210284Sjmallett        /* STALL as a response means this transaction cannot be completed
3055210284Sjmallett            because the device can't process transactions. Tell the user. Any
3056215990Sjmallett            data that was transferred will be counted on the actual bytes
3057215990Sjmallett            transferred */
3058210284Sjmallett        pipe->pid_toggle = 0;
3059210284Sjmallett        __cvmx_usb_perform_complete(usb, pipe, transaction, CVMX_USB_COMPLETE_STALL);
3060210284Sjmallett    }
3061210284Sjmallett    else if (usbc_hcint.s.xacterr)
3062210284Sjmallett    {
3063210284Sjmallett        /* We know at least one packet worked if we get a ACK or NAK. Reset the retry counter */
3064210284Sjmallett        if (usbc_hcint.s.nak || usbc_hcint.s.ack)
3065210284Sjmallett            transaction->retries = 0;
3066210284Sjmallett        transaction->retries++;
3067210284Sjmallett        if (transaction->retries > MAX_RETRIES)
3068210284Sjmallett        {
3069210284Sjmallett            /* XactErr as a response means the device signaled something wrong with
3070210284Sjmallett                the transfer. For example, PID toggle errors cause these */
3071210284Sjmallett            __cvmx_usb_perform_complete(usb, pipe, transaction, CVMX_USB_COMPLETE_XACTERR);
3072210284Sjmallett        }
3073210284Sjmallett        else
3074210284Sjmallett        {
3075215990Sjmallett            /* If this was a split then clear our split in progress marker */
3076215990Sjmallett            if (usb->active_split == transaction)
3077215990Sjmallett                usb->active_split = NULL;
3078210284Sjmallett            /* Rewind to the beginning of the transaction by anding off the
3079210284Sjmallett                split complete bit */
3080210284Sjmallett            transaction->stage &= ~1;
3081210284Sjmallett            pipe->split_sc_frame = -1;
3082215990Sjmallett            pipe->next_tx_frame += pipe->interval;
3083215990Sjmallett            if (pipe->next_tx_frame < usb->frame_number)
3084215990Sjmallett                pipe->next_tx_frame = usb->frame_number + pipe->interval -
3085215990Sjmallett                    (usb->frame_number - pipe->next_tx_frame) % pipe->interval;
3086210284Sjmallett        }
3087210284Sjmallett    }
3088210284Sjmallett    else if (usbc_hcint.s.bblerr)
3089210284Sjmallett    {
3090210284Sjmallett        /* Babble Error (BblErr) */
3091210284Sjmallett        __cvmx_usb_perform_complete(usb, pipe, transaction, CVMX_USB_COMPLETE_BABBLEERR);
3092210284Sjmallett    }
3093215990Sjmallett    else if (usbc_hcint.s.datatglerr)
3094210284Sjmallett    {
3095215990Sjmallett        /* We'll retry the exact same transaction again */
3096215990Sjmallett        transaction->retries++;
3097210284Sjmallett    }
3098210284Sjmallett    else if (usbc_hcint.s.nyet)
3099210284Sjmallett    {
3100210284Sjmallett        /* NYET as a response is only allowed in three cases: as a response to
3101210284Sjmallett            a ping, as a response to a split transaction, and as a response to
3102210284Sjmallett            a bulk out. The ping case is handled by hardware, so we only have
3103210284Sjmallett            splits and bulk out */
3104210284Sjmallett        if (!__cvmx_usb_pipe_needs_split(usb, pipe))
3105210284Sjmallett        {
3106210284Sjmallett            transaction->retries = 0;
3107210284Sjmallett            /* If there is more data to go then we need to try again. Otherwise
3108210284Sjmallett                this transaction is complete */
3109210284Sjmallett            if ((buffer_space_left == 0) || (bytes_in_last_packet < pipe->max_packet))
3110210284Sjmallett                __cvmx_usb_perform_complete(usb, pipe, transaction, CVMX_USB_COMPLETE_SUCCESS);
3111210284Sjmallett        }
3112210284Sjmallett        else
3113210284Sjmallett        {
3114210284Sjmallett            /* Split transactions retry the split complete 4 times then rewind
3115210284Sjmallett                to the start split and do the entire transactions again */
3116210284Sjmallett            transaction->retries++;
3117210284Sjmallett            if ((transaction->retries & 0x3) == 0)
3118210284Sjmallett            {
3119210284Sjmallett                /* Rewind to the beginning of the transaction by anding off the
3120210284Sjmallett                    split complete bit */
3121210284Sjmallett                transaction->stage &= ~1;
3122210284Sjmallett                pipe->split_sc_frame = -1;
3123210284Sjmallett            }
3124210284Sjmallett        }
3125210284Sjmallett    }
3126210284Sjmallett    else if (usbc_hcint.s.ack)
3127210284Sjmallett    {
3128210284Sjmallett        transaction->retries = 0;
3129210284Sjmallett        /* The ACK bit can only be checked after the other error bits. This is
3130210284Sjmallett            because a multi packet transfer may succeed in a number of packets
3131210284Sjmallett            and then get a different response on the last packet. In this case
3132210284Sjmallett            both ACK and the last response bit will be set. If none of the
3133210284Sjmallett            other response bits is set, then the last packet must have been an
3134210284Sjmallett            ACK */
3135210284Sjmallett
3136210284Sjmallett        /* Since we got an ACK, we know we don't need to do a ping on this
3137210284Sjmallett            pipe */
3138210284Sjmallett        pipe->flags &= ~__CVMX_USB_PIPE_FLAGS_NEED_PING;
3139210284Sjmallett
3140210284Sjmallett        switch (transaction->type)
3141210284Sjmallett        {
3142210284Sjmallett            case CVMX_USB_TRANSFER_CONTROL:
3143210284Sjmallett                switch (transaction->stage)
3144210284Sjmallett                {
3145210284Sjmallett                    case CVMX_USB_STAGE_NON_CONTROL:
3146210284Sjmallett                    case CVMX_USB_STAGE_NON_CONTROL_SPLIT_COMPLETE:
3147210284Sjmallett                        /* This should be impossible */
3148210284Sjmallett                        __cvmx_usb_perform_complete(usb, pipe, transaction, CVMX_USB_COMPLETE_ERROR);
3149210284Sjmallett                        break;
3150210284Sjmallett                    case CVMX_USB_STAGE_SETUP:
3151210284Sjmallett                        pipe->pid_toggle = 1;
3152210284Sjmallett                        if (__cvmx_usb_pipe_needs_split(usb, pipe))
3153210284Sjmallett                            transaction->stage = CVMX_USB_STAGE_SETUP_SPLIT_COMPLETE;
3154210284Sjmallett                        else
3155210284Sjmallett                        {
3156210284Sjmallett                            cvmx_usb_control_header_t *header = cvmx_phys_to_ptr(transaction->control_header);
3157210284Sjmallett                            if (header->s.length)
3158210284Sjmallett                                transaction->stage = CVMX_USB_STAGE_DATA;
3159210284Sjmallett                            else
3160210284Sjmallett                                transaction->stage = CVMX_USB_STAGE_STATUS;
3161210284Sjmallett                        }
3162210284Sjmallett                        break;
3163210284Sjmallett                    case CVMX_USB_STAGE_SETUP_SPLIT_COMPLETE:
3164210284Sjmallett                        {
3165210284Sjmallett                            cvmx_usb_control_header_t *header = cvmx_phys_to_ptr(transaction->control_header);
3166210284Sjmallett                            if (header->s.length)
3167210284Sjmallett                                transaction->stage = CVMX_USB_STAGE_DATA;
3168210284Sjmallett                            else
3169210284Sjmallett                                transaction->stage = CVMX_USB_STAGE_STATUS;
3170210284Sjmallett                        }
3171210284Sjmallett                        break;
3172210284Sjmallett                    case CVMX_USB_STAGE_DATA:
3173210284Sjmallett                        if (__cvmx_usb_pipe_needs_split(usb, pipe))
3174215990Sjmallett                        {
3175210284Sjmallett                            transaction->stage = CVMX_USB_STAGE_DATA_SPLIT_COMPLETE;
3176215990Sjmallett                            /* For setup OUT data that are splits, the hardware
3177215990Sjmallett                                doesn't appear to count transferred data. Here
3178215990Sjmallett                                we manually update the data transferred */
3179215990Sjmallett                            if (!usbc_hcchar.s.epdir)
3180215990Sjmallett                            {
3181215990Sjmallett                                if (buffer_space_left < pipe->max_packet)
3182215990Sjmallett                                    transaction->actual_bytes += buffer_space_left;
3183215990Sjmallett                                else
3184215990Sjmallett                                    transaction->actual_bytes += pipe->max_packet;
3185215990Sjmallett                            }
3186215990Sjmallett                        }
3187210284Sjmallett                        else if ((buffer_space_left == 0) || (bytes_in_last_packet < pipe->max_packet))
3188210284Sjmallett                        {
3189210284Sjmallett                            pipe->pid_toggle = 1;
3190210284Sjmallett                            transaction->stage = CVMX_USB_STAGE_STATUS;
3191210284Sjmallett                        }
3192210284Sjmallett                        break;
3193210284Sjmallett                    case CVMX_USB_STAGE_DATA_SPLIT_COMPLETE:
3194210284Sjmallett                        if ((buffer_space_left == 0) || (bytes_in_last_packet < pipe->max_packet))
3195210284Sjmallett                        {
3196210284Sjmallett                            pipe->pid_toggle = 1;
3197210284Sjmallett                            transaction->stage = CVMX_USB_STAGE_STATUS;
3198210284Sjmallett                        }
3199210284Sjmallett                        else
3200210284Sjmallett                        {
3201210284Sjmallett                            transaction->stage = CVMX_USB_STAGE_DATA;
3202210284Sjmallett                        }
3203210284Sjmallett                        break;
3204210284Sjmallett                    case CVMX_USB_STAGE_STATUS:
3205210284Sjmallett                        if (__cvmx_usb_pipe_needs_split(usb, pipe))
3206210284Sjmallett                            transaction->stage = CVMX_USB_STAGE_STATUS_SPLIT_COMPLETE;
3207210284Sjmallett                        else
3208210284Sjmallett                            __cvmx_usb_perform_complete(usb, pipe, transaction, CVMX_USB_COMPLETE_SUCCESS);
3209210284Sjmallett                        break;
3210210284Sjmallett                    case CVMX_USB_STAGE_STATUS_SPLIT_COMPLETE:
3211210284Sjmallett                        __cvmx_usb_perform_complete(usb, pipe, transaction, CVMX_USB_COMPLETE_SUCCESS);
3212210284Sjmallett                        break;
3213210284Sjmallett                }
3214210284Sjmallett                break;
3215210284Sjmallett            case CVMX_USB_TRANSFER_BULK:
3216210284Sjmallett            case CVMX_USB_TRANSFER_INTERRUPT:
3217210284Sjmallett                /* The only time a bulk transfer isn't complete when
3218210284Sjmallett                    it finishes with an ACK is during a split transaction. For
3219210284Sjmallett                    splits we need to continue the transfer if more data is
3220210284Sjmallett                    needed */
3221210284Sjmallett                if (__cvmx_usb_pipe_needs_split(usb, pipe))
3222210284Sjmallett                {
3223210284Sjmallett                    if (transaction->stage == CVMX_USB_STAGE_NON_CONTROL)
3224210284Sjmallett                        transaction->stage = CVMX_USB_STAGE_NON_CONTROL_SPLIT_COMPLETE;
3225210284Sjmallett                    else
3226210284Sjmallett                    {
3227210284Sjmallett                        if (buffer_space_left && (bytes_in_last_packet == pipe->max_packet))
3228210284Sjmallett                            transaction->stage = CVMX_USB_STAGE_NON_CONTROL;
3229210284Sjmallett                        else
3230210284Sjmallett                        {
3231210284Sjmallett                            if (transaction->type == CVMX_USB_TRANSFER_INTERRUPT)
3232215990Sjmallett                                pipe->next_tx_frame += pipe->interval;
3233210284Sjmallett                            __cvmx_usb_perform_complete(usb, pipe, transaction, CVMX_USB_COMPLETE_SUCCESS);
3234210284Sjmallett                        }
3235210284Sjmallett                    }
3236210284Sjmallett                }
3237210284Sjmallett                else
3238210284Sjmallett                {
3239210284Sjmallett                    if ((pipe->device_speed == CVMX_USB_SPEED_HIGH) &&
3240210284Sjmallett                        (pipe->transfer_type == CVMX_USB_TRANSFER_BULK) &&
3241210284Sjmallett                        (pipe->transfer_dir == CVMX_USB_DIRECTION_OUT) &&
3242210284Sjmallett                        (usbc_hcint.s.nak))
3243210284Sjmallett                        pipe->flags |= __CVMX_USB_PIPE_FLAGS_NEED_PING;
3244210284Sjmallett                    if (!buffer_space_left || (bytes_in_last_packet < pipe->max_packet))
3245210284Sjmallett                    {
3246210284Sjmallett                        if (transaction->type == CVMX_USB_TRANSFER_INTERRUPT)
3247215990Sjmallett                            pipe->next_tx_frame += pipe->interval;
3248210284Sjmallett                        __cvmx_usb_perform_complete(usb, pipe, transaction, CVMX_USB_COMPLETE_SUCCESS);
3249210284Sjmallett                    }
3250210284Sjmallett                }
3251210284Sjmallett                break;
3252210284Sjmallett            case CVMX_USB_TRANSFER_ISOCHRONOUS:
3253210284Sjmallett                if (__cvmx_usb_pipe_needs_split(usb, pipe))
3254210284Sjmallett                {
3255210284Sjmallett                    /* ISOCHRONOUS OUT splits don't require a complete split stage.
3256210284Sjmallett                        Instead they use a sequence of begin OUT splits to transfer
3257210284Sjmallett                        the data 188 bytes at a time. Once the transfer is complete,
3258210284Sjmallett                        the pipe sleeps until the next schedule interval */
3259210284Sjmallett                    if (pipe->transfer_dir == CVMX_USB_DIRECTION_OUT)
3260210284Sjmallett                    {
3261210284Sjmallett                        /* If no space left or this wasn't a max size packet then
3262210284Sjmallett                            this transfer is complete. Otherwise start it again
3263210284Sjmallett                            to send the next 188 bytes */
3264210284Sjmallett                        if (!buffer_space_left || (bytes_this_transfer < 188))
3265215990Sjmallett                        {
3266215990Sjmallett                            pipe->next_tx_frame += pipe->interval;
3267210284Sjmallett                            __cvmx_usb_perform_complete(usb, pipe, transaction, CVMX_USB_COMPLETE_SUCCESS);
3268215990Sjmallett                        }
3269210284Sjmallett                    }
3270210284Sjmallett                    else
3271210284Sjmallett                    {
3272210284Sjmallett                        if (transaction->stage == CVMX_USB_STAGE_NON_CONTROL_SPLIT_COMPLETE)
3273210284Sjmallett                        {
3274215990Sjmallett                            /* We are in the incoming data phase. Keep getting
3275210284Sjmallett                                data until we run out of space or get a small
3276210284Sjmallett                                packet */
3277210284Sjmallett                            if ((buffer_space_left == 0) || (bytes_in_last_packet < pipe->max_packet))
3278210284Sjmallett                            {
3279215990Sjmallett                                pipe->next_tx_frame += pipe->interval;
3280210284Sjmallett                                __cvmx_usb_perform_complete(usb, pipe, transaction, CVMX_USB_COMPLETE_SUCCESS);
3281210284Sjmallett                            }
3282210284Sjmallett                        }
3283210284Sjmallett                        else
3284210284Sjmallett                            transaction->stage = CVMX_USB_STAGE_NON_CONTROL_SPLIT_COMPLETE;
3285210284Sjmallett                    }
3286210284Sjmallett                }
3287210284Sjmallett                else
3288210284Sjmallett                {
3289215990Sjmallett                    pipe->next_tx_frame += pipe->interval;
3290210284Sjmallett                    __cvmx_usb_perform_complete(usb, pipe, transaction, CVMX_USB_COMPLETE_SUCCESS);
3291210284Sjmallett                }
3292210284Sjmallett                break;
3293210284Sjmallett        }
3294210284Sjmallett    }
3295210284Sjmallett    else if (usbc_hcint.s.nak)
3296210284Sjmallett    {
3297215990Sjmallett        /* If this was a split then clear our split in progress marker */
3298215990Sjmallett        if (usb->active_split == transaction)
3299215990Sjmallett            usb->active_split = NULL;
3300210284Sjmallett        /* NAK as a response means the device couldn't accept the transaction,
3301210284Sjmallett            but it should be retried in the future. Rewind to the beginning of
3302210284Sjmallett            the transaction by anding off the split complete bit. Retry in the
3303210284Sjmallett            next interval */
3304210284Sjmallett        transaction->retries = 0;
3305210284Sjmallett        transaction->stage &= ~1;
3306215990Sjmallett        pipe->next_tx_frame += pipe->interval;
3307215990Sjmallett        if (pipe->next_tx_frame < usb->frame_number)
3308215990Sjmallett            pipe->next_tx_frame = usb->frame_number + pipe->interval -
3309215990Sjmallett                (usb->frame_number - pipe->next_tx_frame) % pipe->interval;
3310210284Sjmallett    }
3311210284Sjmallett    else
3312210284Sjmallett    {
3313215990Sjmallett        cvmx_usb_port_status_t port;
3314215990Sjmallett        port = cvmx_usb_get_status((cvmx_usb_state_t *)usb);
3315215990Sjmallett        if (port.port_enabled)
3316215990Sjmallett        {
3317215990Sjmallett            /* We'll retry the exact same transaction again */
3318215990Sjmallett            transaction->retries++;
3319215990Sjmallett        }
3320215990Sjmallett        else
3321215990Sjmallett        {
3322215990Sjmallett            /* We get channel halted interrupts with no result bits sets when the
3323215990Sjmallett                cable is unplugged */
3324215990Sjmallett            __cvmx_usb_perform_complete(usb, pipe, transaction, CVMX_USB_COMPLETE_ERROR);
3325215990Sjmallett        }
3326210284Sjmallett    }
3327210284Sjmallett    CVMX_USB_RETURN(0);
3328210284Sjmallett}
3329210284Sjmallett
3330210284Sjmallett
3331210284Sjmallett/**
3332210284Sjmallett * Poll the USB block for status and call all needed callback
3333210284Sjmallett * handlers. This function is meant to be called in the interrupt
3334210284Sjmallett * handler for the USB controller. It can also be called
3335210284Sjmallett * periodically in a loop for non-interrupt based operation.
3336210284Sjmallett *
3337210284Sjmallett * @param state  USB device state populated by
3338210284Sjmallett *               cvmx_usb_initialize().
3339210284Sjmallett *
3340210284Sjmallett * @return CVMX_USB_SUCCESS or a negative error code defined in
3341210284Sjmallett *         cvmx_usb_status_t.
3342210284Sjmallett */
3343210284Sjmallettcvmx_usb_status_t cvmx_usb_poll(cvmx_usb_state_t *state)
3344210284Sjmallett{
3345215990Sjmallett    cvmx_usbcx_hfnum_t usbc_hfnum;
3346210284Sjmallett    cvmx_usbcx_gintsts_t usbc_gintsts;
3347210284Sjmallett    cvmx_usb_internal_state_t *usb = (cvmx_usb_internal_state_t*)state;
3348210284Sjmallett
3349210284Sjmallett    CVMX_PREFETCH(usb, 0);
3350210284Sjmallett    CVMX_PREFETCH(usb, 1*128);
3351210284Sjmallett    CVMX_PREFETCH(usb, 2*128);
3352210284Sjmallett    CVMX_PREFETCH(usb, 3*128);
3353210284Sjmallett    CVMX_PREFETCH(usb, 4*128);
3354210284Sjmallett
3355210284Sjmallett    CVMX_USB_LOG_CALLED();
3356210284Sjmallett    CVMX_USB_LOG_PARAM("%p", state);
3357210284Sjmallett
3358215990Sjmallett    /* Update the frame counter */
3359215990Sjmallett    usbc_hfnum.u32 = __cvmx_usb_read_csr32(usb, CVMX_USBCX_HFNUM(usb->index));
3360215990Sjmallett    if ((usb->frame_number&0x3fff) > usbc_hfnum.s.frnum)
3361215990Sjmallett        usb->frame_number += 0x4000;
3362215990Sjmallett    usb->frame_number &= ~0x3fffull;
3363215990Sjmallett    usb->frame_number |= usbc_hfnum.s.frnum;
3364215990Sjmallett
3365210284Sjmallett    /* Read the pending interrupts */
3366210284Sjmallett    usbc_gintsts.u32 = __cvmx_usb_read_csr32(usb, CVMX_USBCX_GINTSTS(usb->index));
3367210284Sjmallett
3368215990Sjmallett    /* Clear the interrupts now that we know about them */
3369215990Sjmallett    __cvmx_usb_write_csr32(usb, CVMX_USBCX_GINTSTS(usb->index), usbc_gintsts.u32);
3370215990Sjmallett
3371215990Sjmallett    if (usbc_gintsts.s.rxflvl)
3372210284Sjmallett    {
3373215990Sjmallett        /* RxFIFO Non-Empty (RxFLvl)
3374215990Sjmallett            Indicates that there is at least one packet pending to be read
3375215990Sjmallett            from the RxFIFO. */
3376215990Sjmallett        /* In DMA mode this is handled by hardware */
3377215990Sjmallett        if (usb->init_flags & CVMX_USB_INITIALIZE_FLAGS_NO_DMA)
3378215990Sjmallett            __cvmx_usb_poll_rx_fifo(usb);
3379210284Sjmallett    }
3380215990Sjmallett    if (usbc_gintsts.s.ptxfemp || usbc_gintsts.s.nptxfemp)
3381210284Sjmallett    {
3382215990Sjmallett        /* Fill the Tx FIFOs when not in DMA mode */
3383215990Sjmallett        if (usb->init_flags & CVMX_USB_INITIALIZE_FLAGS_NO_DMA)
3384215990Sjmallett            __cvmx_usb_poll_tx_fifo(usb);
3385210284Sjmallett    }
3386210284Sjmallett    if (usbc_gintsts.s.disconnint || usbc_gintsts.s.prtint)
3387210284Sjmallett    {
3388210284Sjmallett        cvmx_usbcx_hprt_t usbc_hprt;
3389210284Sjmallett        /* Disconnect Detected Interrupt (DisconnInt)
3390210284Sjmallett            Asserted when a device disconnect is detected. */
3391210284Sjmallett
3392210284Sjmallett        /* Host Port Interrupt (PrtInt)
3393210284Sjmallett            The core sets this bit to indicate a change in port status of one
3394210284Sjmallett            of the O2P USB core ports in Host mode. The application must
3395210284Sjmallett            read the Host Port Control and Status (HPRT) register to
3396210284Sjmallett            determine the exact event that caused this interrupt. The
3397210284Sjmallett            application must clear the appropriate status bit in the Host Port
3398210284Sjmallett            Control and Status register to clear this bit. */
3399210284Sjmallett
3400210284Sjmallett        /* Call the user's port callback */
3401210284Sjmallett        __cvmx_usb_perform_callback(usb, NULL, NULL,
3402210284Sjmallett                                    CVMX_USB_CALLBACK_PORT_CHANGED,
3403210284Sjmallett                                    CVMX_USB_COMPLETE_SUCCESS);
3404210284Sjmallett        /* Clear the port change bits */
3405210284Sjmallett        usbc_hprt.u32 = __cvmx_usb_read_csr32(usb, CVMX_USBCX_HPRT(usb->index));
3406210284Sjmallett        usbc_hprt.s.prtena = 0;
3407210284Sjmallett        __cvmx_usb_write_csr32(usb, CVMX_USBCX_HPRT(usb->index), usbc_hprt.u32);
3408210284Sjmallett    }
3409210284Sjmallett    if (usbc_gintsts.s.hchint)
3410210284Sjmallett    {
3411210284Sjmallett        /* Host Channels Interrupt (HChInt)
3412210284Sjmallett            The core sets this bit to indicate that an interrupt is pending on
3413210284Sjmallett            one of the channels of the core (in Host mode). The application
3414210284Sjmallett            must read the Host All Channels Interrupt (HAINT) register to
3415210284Sjmallett            determine the exact number of the channel on which the
3416210284Sjmallett            interrupt occurred, and then read the corresponding Host
3417210284Sjmallett            Channel-n Interrupt (HCINTn) register to determine the exact
3418210284Sjmallett            cause of the interrupt. The application must clear the
3419210284Sjmallett            appropriate status bit in the HCINTn register to clear this bit. */
3420210284Sjmallett        cvmx_usbcx_haint_t usbc_haint;
3421210284Sjmallett        usbc_haint.u32 = __cvmx_usb_read_csr32(usb, CVMX_USBCX_HAINT(usb->index));
3422210284Sjmallett        while (usbc_haint.u32)
3423210284Sjmallett        {
3424210284Sjmallett            int channel;
3425210284Sjmallett            CVMX_CLZ(channel, usbc_haint.u32);
3426210284Sjmallett            channel = 31 - channel;
3427210284Sjmallett            __cvmx_usb_poll_channel(usb, channel);
3428210284Sjmallett            usbc_haint.u32 ^= 1<<channel;
3429210284Sjmallett        }
3430210284Sjmallett    }
3431210284Sjmallett
3432210284Sjmallett    __cvmx_usb_schedule(usb, usbc_gintsts.s.sof);
3433210284Sjmallett
3434210284Sjmallett    CVMX_USB_RETURN(CVMX_USB_SUCCESS);
3435210284Sjmallett}
3436215990Sjmallett#ifdef CVMX_BUILD_FOR_LINUX_KERNEL
3437215990SjmallettEXPORT_SYMBOL(cvmx_usb_poll);
3438215990Sjmallett#endif
3439210284Sjmallett
3440210311Sjmallettextern void cvmx_usb_set_toggle(cvmx_usb_state_t *state, int endpoint_num, int toggle)
3441210311Sjmallett{
3442210311Sjmallett    cvmx_usb_internal_state_t *usb = (cvmx_usb_internal_state_t*)state;
3443210311Sjmallett    cvmx_usb_pipe_t *pipe = usb->pipe + endpoint_num;
3444210311Sjmallett
3445210311Sjmallett    pipe->pid_toggle = !!toggle;
3446210311Sjmallett}
3447210311Sjmallett
3448210311Sjmallettextern int cvmx_usb_get_toggle(cvmx_usb_state_t *state, int endpoint_num)
3449210311Sjmallett{
3450210311Sjmallett    cvmx_usb_internal_state_t *usb = (cvmx_usb_internal_state_t*)state;
3451210311Sjmallett    cvmx_usb_pipe_t *pipe = usb->pipe + endpoint_num;
3452210311Sjmallett
3453210311Sjmallett    if (pipe->pid_toggle)
3454210311Sjmallett	    return (1);
3455210311Sjmallett    return (0);
3456210311Sjmallett}
3457215990Sjmallett
3458