cvmx-usb.c revision 210311
1210284Sjmallett/***********************license start***************
2210284Sjmallett *  Copyright (c) 2003-2008 Cavium Networks (support@cavium.com). All rights
3210284Sjmallett *  reserved.
4210284Sjmallett *
5210284Sjmallett *
6210284Sjmallett *  Redistribution and use in source and binary forms, with or without
7210284Sjmallett *  modification, are permitted provided that the following conditions are
8210284Sjmallett *  met:
9210284Sjmallett *
10210284Sjmallett *      * Redistributions of source code must retain the above copyright
11210284Sjmallett *        notice, this list of conditions and the following disclaimer.
12210284Sjmallett *
13210284Sjmallett *      * Redistributions in binary form must reproduce the above
14210284Sjmallett *        copyright notice, this list of conditions and the following
15210284Sjmallett *        disclaimer in the documentation and/or other materials provided
16210284Sjmallett *        with the distribution.
17210284Sjmallett *
18210284Sjmallett *      * Neither the name of Cavium Networks nor the names of
19210284Sjmallett *        its contributors may be used to endorse or promote products
20210284Sjmallett *        derived from this software without specific prior written
21210284Sjmallett *        permission.
22210284Sjmallett *
23210284Sjmallett *  TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SOFTWARE IS PROVIDED "AS IS"
24210284Sjmallett *  AND WITH ALL FAULTS AND CAVIUM NETWORKS MAKES NO PROMISES, REPRESENTATIONS
25210284Sjmallett *  OR WARRANTIES, EITHER EXPRESS, IMPLIED, STATUTORY, OR OTHERWISE, WITH
26210284Sjmallett *  RESPECT TO THE SOFTWARE, INCLUDING ITS CONDITION, ITS CONFORMITY TO ANY
27210284Sjmallett *  REPRESENTATION OR DESCRIPTION, OR THE EXISTENCE OF ANY LATENT OR PATENT
28210284Sjmallett *  DEFECTS, AND CAVIUM SPECIFICALLY DISCLAIMS ALL IMPLIED (IF ANY) WARRANTIES
29210284Sjmallett *  OF TITLE, MERCHANTABILITY, NONINFRINGEMENT, FITNESS FOR A PARTICULAR
30210284Sjmallett *  PURPOSE, LACK OF VIRUSES, ACCURACY OR COMPLETENESS, QUIET ENJOYMENT, QUIET
31210284Sjmallett *  POSSESSION OR CORRESPONDENCE TO DESCRIPTION.  THE ENTIRE RISK ARISING OUT
32210284Sjmallett *  OF USE OR PERFORMANCE OF THE SOFTWARE LIES WITH YOU.
33210284Sjmallett *
34210284Sjmallett *
35210284Sjmallett *  For any questions regarding licensing please contact marketing@caviumnetworks.com
36210284Sjmallett *
37210284Sjmallett ***********************license end**************************************/
38210284Sjmallett
39210284Sjmallett/**
40210284Sjmallett * @file
41210284Sjmallett *
42210284Sjmallett * "cvmx-usb.c" defines a set of low level USB functions to help
43210284Sjmallett * developers create Octeon USB drivers for various operating
44210284Sjmallett * systems. These functions provide a generic API to the Octeon
45210284Sjmallett * USB blocks, hiding the internal hardware specific
46210284Sjmallett * operations.
47210284Sjmallett *
48210284Sjmallett * <hr>$Revision: 32636 $<hr>
49210284Sjmallett */
50210284Sjmallett#include "cvmx.h"
51210284Sjmallett#include "cvmx-sysinfo.h"
52210284Sjmallett#include "cvmx-usb.h"
53210284Sjmallett#include "cvmx-helper.h"
54210284Sjmallett#include "cvmx-helper-board.h"
55210284Sjmallett#include "cvmx-csr-db.h"
56210284Sjmallett#include "cvmx-swap.h"
57210284Sjmallett
58210284Sjmallett#define MAX_RETRIES         3   /* Maximum number of times to retry failed transactions */
59210284Sjmallett#define MAX_PIPES           32  /* Maximum number of pipes that can be open at once */
60210284Sjmallett#define MAX_TRANSACTIONS    256 /* Maximum number of outstanding transactions across all pipes */
61210284Sjmallett#define MAX_CHANNELS        8   /* Maximum number of hardware channels supported by the USB block */
62210284Sjmallett#define MAX_USB_ADDRESS     127 /* The highest valid USB device address */
63210284Sjmallett#define MAX_USB_ENDPOINT    15  /* The highest valid USB endpoint number */
64210284Sjmallett#define MAX_USB_HUB_PORT    15  /* The highest valid port number on a hub */
65210284Sjmallett#define ALLOW_CSR_DECODES   0   /* CSR decoding when CVMX_USB_INITIALIZE_FLAGS_DEBUG_CSRS is set
66210284Sjmallett                                    enlarges the code a lot. This define overrides the ability to do CSR
67210284Sjmallett                                    decoding since it isn't necessary 99% of the time. Change this to a
68210284Sjmallett                                    one if you need CSR decoding */
69210284Sjmallett
70210284Sjmallett/* These defines disable the normal read and write csr. This is so I can add
71210284Sjmallett    extra debug stuff to the usb specific version and I won't use the normal
72210284Sjmallett    version by mistake */
73210284Sjmallett#define cvmx_read_csr use_cvmx_usb_read_csr64_instead_of_cvmx_read_csr
74210284Sjmallett#define cvmx_write_csr use_cvmx_usb_write_csr64_instead_of_cvmx_write_csr
75210284Sjmallett
76210284Sjmalletttypedef enum
77210284Sjmallett{
78210284Sjmallett    __CVMX_USB_TRANSACTION_FLAGS_IN_USE = 1<<16,
79210284Sjmallett} cvmx_usb_transaction_flags_t;
80210284Sjmallett
81210284Sjmallett/**
82210284Sjmallett * Logical transactions may take numerous low level
83210284Sjmallett * transactions, especially when splits are concerned. This
84210284Sjmallett * enum represents all of the possible stages a transaction can
85210284Sjmallett * be in. Note that split completes are always even. This is so
86210284Sjmallett * the NAK handler can backup to the previous low level
87210284Sjmallett * transaction with a simple clearing of bit 0.
88210284Sjmallett */
89210284Sjmalletttypedef enum
90210284Sjmallett{
91210284Sjmallett    CVMX_USB_STAGE_NON_CONTROL,
92210284Sjmallett    CVMX_USB_STAGE_NON_CONTROL_SPLIT_COMPLETE,
93210284Sjmallett    CVMX_USB_STAGE_SETUP,
94210284Sjmallett    CVMX_USB_STAGE_SETUP_SPLIT_COMPLETE,
95210284Sjmallett    CVMX_USB_STAGE_DATA,
96210284Sjmallett    CVMX_USB_STAGE_DATA_SPLIT_COMPLETE,
97210284Sjmallett    CVMX_USB_STAGE_STATUS,
98210284Sjmallett    CVMX_USB_STAGE_STATUS_SPLIT_COMPLETE,
99210284Sjmallett} cvmx_usb_stage_t;
100210284Sjmallett
101210284Sjmallett/**
102210284Sjmallett * This structure describes each pending USB transaction
103210284Sjmallett * regardless of type. These are linked together to form a list
104210284Sjmallett * of pending requests for a pipe.
105210284Sjmallett */
106210284Sjmalletttypedef struct cvmx_usb_transaction
107210284Sjmallett{
108210284Sjmallett    struct cvmx_usb_transaction *prev;  /**< Transaction before this one in the pipe */
109210284Sjmallett    struct cvmx_usb_transaction *next;  /**< Transaction after this one in the pipe */
110210284Sjmallett    cvmx_usb_transfer_t type;           /**< Type of transaction, duplicated of the pipe */
111210284Sjmallett    cvmx_usb_transaction_flags_t flags; /**< State flags for this transaction */
112210284Sjmallett    uint64_t buffer;                    /**< User's physical buffer address to read/write */
113210284Sjmallett    int buffer_length;                  /**< Size of the user's buffer in bytes */
114210284Sjmallett    uint64_t control_header;            /**< For control transactions, physical address of the 8 byte standard header */
115210284Sjmallett    int iso_start_frame;                /**< For ISO transactions, the starting frame number */
116210284Sjmallett    int iso_number_packets;             /**< For ISO transactions, the number of packets in the request */
117210284Sjmallett    cvmx_usb_iso_packet_t *iso_packets; /**< For ISO transactions, the sub packets in the request */
118210284Sjmallett    int xfersize;
119210284Sjmallett    int pktcnt;
120210284Sjmallett    int retries;
121210284Sjmallett    int actual_bytes;                   /**< Actual bytes transfer for this transaction */
122210284Sjmallett    cvmx_usb_stage_t stage;             /**< For control transactions, the current stage */
123210284Sjmallett    cvmx_usb_callback_func_t callback;  /**< User's callback function when complete */
124210284Sjmallett    void *callback_data;                /**< User's data */
125210284Sjmallett} cvmx_usb_transaction_t;
126210284Sjmallett
127210284Sjmallett/**
128210284Sjmallett * A pipe represents a virtual connection between Octeon and some
129210284Sjmallett * USB device. It contains a list of pending request to the device.
130210284Sjmallett */
131210284Sjmalletttypedef struct cvmx_usb_pipe
132210284Sjmallett{
133210284Sjmallett    struct cvmx_usb_pipe *prev;         /**< Pipe before this one in the list */
134210284Sjmallett    struct cvmx_usb_pipe *next;         /**< Pipe after this one in the list */
135210284Sjmallett    cvmx_usb_transaction_t *head;       /**< The first pending transaction */
136210284Sjmallett    cvmx_usb_transaction_t *tail;       /**< The last pending transaction */
137210284Sjmallett    uint64_t interval;                  /**< For periodic pipes, the interval between packets in cycles */
138210284Sjmallett    uint64_t next_tx_cycle;             /**< The next cycle this pipe is allowed to transmit on */
139210284Sjmallett    cvmx_usb_pipe_flags_t flags;        /**< State flags for this pipe */
140210284Sjmallett    cvmx_usb_speed_t device_speed;      /**< Speed of device connected to this pipe */
141210284Sjmallett    cvmx_usb_transfer_t transfer_type;  /**< Type of transaction supported by this pipe */
142210284Sjmallett    cvmx_usb_direction_t transfer_dir;  /**< IN or OUT. Ignored for Control */
143210284Sjmallett    int multi_count;                    /**< Max packet in a row for the device */
144210284Sjmallett    uint16_t max_packet;                /**< The device's maximum packet size in bytes */
145210284Sjmallett    uint8_t device_addr;                /**< USB device address at other end of pipe */
146210284Sjmallett    uint8_t endpoint_num;               /**< USB endpoint number at other end of pipe */
147210284Sjmallett    uint8_t hub_device_addr;            /**< Hub address this device is connected to */
148210284Sjmallett    uint8_t hub_port;                   /**< Hub port this device is connected to */
149210284Sjmallett    uint8_t pid_toggle;                 /**< This toggles between 0/1 on every packet send to track the data pid needed */
150210284Sjmallett    uint8_t channel;                    /**< Hardware DMA channel for this pipe */
151210284Sjmallett    int8_t  split_sc_frame;             /**< The low order bits of the frame number the split complete should be sent on */
152210284Sjmallett} cvmx_usb_pipe_t;
153210284Sjmallett
154210284Sjmalletttypedef struct
155210284Sjmallett{
156210284Sjmallett    cvmx_usb_pipe_t *head;              /**< Head of the list, or NULL if empty */
157210284Sjmallett    cvmx_usb_pipe_t *tail;              /**< Tail if the list, or NULL if empty */
158210284Sjmallett} cvmx_usb_pipe_list_t;
159210284Sjmallett
160210284Sjmallett/**
161210284Sjmallett * The state of the USB block is stored in this structure
162210284Sjmallett */
163210284Sjmalletttypedef struct
164210284Sjmallett{
165210284Sjmallett    int init_flags;                     /**< Flags passed to initialize */
166210284Sjmallett    int index;                          /**< Which USB block this is for */
167210284Sjmallett    int idle_hardware_channels;         /**< Bit set for every idle hardware channel */
168210284Sjmallett    int active_transactions;            /**< Number of active transactions across all pipes */
169210284Sjmallett    cvmx_usbcx_hprt_t usbcx_hprt;       /**< Stored port status so we don't need to read a CSR to determine splits */
170210284Sjmallett    cvmx_usb_pipe_t *pipe_for_channel[MAX_CHANNELS];    /**< Map channels to pipes */
171210284Sjmallett    cvmx_usb_transaction_t *free_transaction_head;      /**< List of free transactions head */
172210284Sjmallett    cvmx_usb_transaction_t *free_transaction_tail;      /**< List of free transactions tail */
173210284Sjmallett    cvmx_usb_pipe_t pipe[MAX_PIPES];                    /**< Storage for pipes */
174210284Sjmallett    cvmx_usb_transaction_t transaction[MAX_TRANSACTIONS];       /**< Storage for transactions */
175210284Sjmallett    cvmx_usb_callback_func_t callback[__CVMX_USB_CALLBACK_END]; /**< User global callbacks */
176210284Sjmallett    void *callback_data[__CVMX_USB_CALLBACK_END];               /**< User data for each callback */
177210284Sjmallett    int indent;                         /**< Used by debug output to indent functions */
178210284Sjmallett    cvmx_usb_port_status_t port_status; /**< Last port status used for change notification */
179210284Sjmallett    cvmx_usb_pipe_list_t free_pipes;    /**< List of all pipes that are currently closed */
180210284Sjmallett    cvmx_usb_pipe_list_t idle_pipes;    /**< List of open pipes that have no transactions */
181210284Sjmallett    cvmx_usb_pipe_list_t active_pipes[4]; /**< Active pipes indexed by transfer type */
182210284Sjmallett} cvmx_usb_internal_state_t;
183210284Sjmallett
184210284Sjmallett/* This macro logs out whenever a function is called if debugging is on */
185210284Sjmallett#define CVMX_USB_LOG_CALLED() \
186210284Sjmallett    if (cvmx_unlikely(usb->init_flags & CVMX_USB_INITIALIZE_FLAGS_DEBUG_CALLS)) \
187210284Sjmallett        cvmx_dprintf("%*s%s: called\n", 2*usb->indent++, "", __FUNCTION__);
188210284Sjmallett
189210284Sjmallett/* This macro logs out each function parameter if debugging is on */
190210284Sjmallett#define CVMX_USB_LOG_PARAM(format, param) \
191210284Sjmallett    if (cvmx_unlikely(usb->init_flags & CVMX_USB_INITIALIZE_FLAGS_DEBUG_CALLS)) \
192210284Sjmallett        cvmx_dprintf("%*s%s: param %s = " format "\n", 2*usb->indent, "", __FUNCTION__, #param, param);
193210284Sjmallett
194210284Sjmallett/* This macro logs out when a function returns a value */
195210284Sjmallett#define CVMX_USB_RETURN(v)                                              \
196210284Sjmallett    do {                                                                \
197210311Sjmallett        __typeof(v) r = v;                                              \
198210284Sjmallett        if (cvmx_unlikely(usb->init_flags & CVMX_USB_INITIALIZE_FLAGS_DEBUG_CALLS))    \
199210284Sjmallett            cvmx_dprintf("%*s%s: returned %s(%d)\n", 2*--usb->indent, "", __FUNCTION__, #v, r); \
200210284Sjmallett        return r;                                                       \
201210284Sjmallett    } while (0);
202210284Sjmallett
203210284Sjmallett/* This macro logs out when a function doesn't return a value */
204210284Sjmallett#define CVMX_USB_RETURN_NOTHING()                                       \
205210284Sjmallett    do {                                                                \
206210284Sjmallett        if (cvmx_unlikely(usb->init_flags & CVMX_USB_INITIALIZE_FLAGS_DEBUG_CALLS))    \
207210284Sjmallett            cvmx_dprintf("%*s%s: returned\n", 2*--usb->indent, "", __FUNCTION__); \
208210284Sjmallett        return;                                                         \
209210284Sjmallett    } while (0);
210210284Sjmallett
211210284Sjmallett/* This macro spins on a field waiting for it to reach a value */
212210284Sjmallett#define CVMX_WAIT_FOR_FIELD32(address, type, field, op, value, timeout_usec)\
213210284Sjmallett    ({int result;                                                       \
214210284Sjmallett    do {                                                                \
215210284Sjmallett        uint64_t done = cvmx_get_cycle() + (uint64_t)timeout_usec *     \
216210284Sjmallett                           cvmx_sysinfo_get()->cpu_clock_hz / 1000000;  \
217210284Sjmallett        type c;                                                         \
218210284Sjmallett        while (1)                                                       \
219210284Sjmallett        {                                                               \
220210284Sjmallett            c.u32 = __cvmx_usb_read_csr32(usb, address);                \
221210284Sjmallett            if (c.s.field op (value)) {                                 \
222210284Sjmallett                result = 0;                                             \
223210284Sjmallett                break;                                                  \
224210284Sjmallett            } else if (cvmx_get_cycle() > done) {                       \
225210284Sjmallett                result = -1;                                            \
226210284Sjmallett                break;                                                  \
227210284Sjmallett            } else                                                      \
228210284Sjmallett                cvmx_wait(100);                                         \
229210284Sjmallett        }                                                               \
230210284Sjmallett    } while (0);                                                        \
231210284Sjmallett    result;})
232210284Sjmallett
233210284Sjmallett/* This macro logically sets a single field in a CSR. It does the sequence
234210284Sjmallett    read, modify, and write */
235210284Sjmallett#define USB_SET_FIELD32(address, type, field, value)\
236210284Sjmallett    do {                                            \
237210284Sjmallett        type c;                                     \
238210284Sjmallett        c.u32 = __cvmx_usb_read_csr32(usb, address);\
239210284Sjmallett        c.s.field = value;                          \
240210284Sjmallett        __cvmx_usb_write_csr32(usb, address, c.u32);\
241210284Sjmallett    } while (0)
242210284Sjmallett
243210284Sjmallett
244210284Sjmallett/**
245210284Sjmallett * @INTERNAL
246210284Sjmallett * Read a USB 32bit CSR. It performs the necessary address swizzle
247210284Sjmallett * for 32bit CSRs and logs the value in a readable format if
248210284Sjmallett * debugging is on.
249210284Sjmallett *
250210284Sjmallett * @param usb     USB block this access is for
251210284Sjmallett * @param address 64bit address to read
252210284Sjmallett *
253210284Sjmallett * @return Result of the read
254210284Sjmallett */
255210284Sjmallettstatic inline uint32_t __cvmx_usb_read_csr32(cvmx_usb_internal_state_t *usb,
256210284Sjmallett                                             uint64_t address)
257210284Sjmallett{
258210284Sjmallett    uint32_t result = cvmx_read64_uint32(address ^ 4);
259210284Sjmallett#if ALLOW_CSR_DECODES
260210284Sjmallett    if (cvmx_unlikely(usb->init_flags & CVMX_USB_INITIALIZE_FLAGS_DEBUG_CSRS))
261210284Sjmallett    {
262210284Sjmallett        cvmx_dprintf("Read: ");
263210284Sjmallett        cvmx_csr_db_decode(cvmx_get_proc_id(), address, result);
264210284Sjmallett    }
265210284Sjmallett#endif
266210284Sjmallett    return result;
267210284Sjmallett}
268210284Sjmallett
269210284Sjmallett
270210284Sjmallett/**
271210284Sjmallett * @INTERNAL
272210284Sjmallett * Write a USB 32bit CSR. It performs the necessary address
273210284Sjmallett * swizzle for 32bit CSRs and logs the value in a readable format
274210284Sjmallett * if debugging is on.
275210284Sjmallett *
276210284Sjmallett * @param usb     USB block this access is for
277210284Sjmallett * @param address 64bit address to write
278210284Sjmallett * @param value   Value to write
279210284Sjmallett */
280210284Sjmallettstatic inline void __cvmx_usb_write_csr32(cvmx_usb_internal_state_t *usb,
281210284Sjmallett                                          uint64_t address, uint32_t value)
282210284Sjmallett{
283210284Sjmallett#if ALLOW_CSR_DECODES
284210284Sjmallett    if (cvmx_unlikely(usb->init_flags & CVMX_USB_INITIALIZE_FLAGS_DEBUG_CSRS))
285210284Sjmallett    {
286210284Sjmallett        cvmx_dprintf("Write: ");
287210284Sjmallett        cvmx_csr_db_decode(cvmx_get_proc_id(), address, value);
288210284Sjmallett    }
289210284Sjmallett#endif
290210284Sjmallett    cvmx_write64_uint32(address ^ 4, value);
291210284Sjmallett}
292210284Sjmallett
293210284Sjmallett
294210284Sjmallett/**
295210284Sjmallett * @INTERNAL
296210284Sjmallett * Read a USB 64bit CSR. It logs the value in a readable format if
297210284Sjmallett * debugging is on.
298210284Sjmallett *
299210284Sjmallett * @param usb     USB block this access is for
300210284Sjmallett * @param address 64bit address to read
301210284Sjmallett *
302210284Sjmallett * @return Result of the read
303210284Sjmallett */
304210284Sjmallettstatic inline uint64_t __cvmx_usb_read_csr64(cvmx_usb_internal_state_t *usb,
305210284Sjmallett                                             uint64_t address)
306210284Sjmallett{
307210284Sjmallett    uint64_t result = cvmx_read64_uint64(address);
308210284Sjmallett#if ALLOW_CSR_DECODES
309210284Sjmallett    if (cvmx_unlikely(usb->init_flags & CVMX_USB_INITIALIZE_FLAGS_DEBUG_CSRS))
310210284Sjmallett    {
311210284Sjmallett        cvmx_dprintf("Read: ");
312210284Sjmallett        cvmx_csr_db_decode(cvmx_get_proc_id(), address, result);
313210284Sjmallett    }
314210284Sjmallett#endif
315210284Sjmallett    return result;
316210284Sjmallett}
317210284Sjmallett
318210284Sjmallett
319210284Sjmallett/**
320210284Sjmallett * @INTERNAL
321210284Sjmallett * Write a USB 64bit CSR. It logs the value in a readable format
322210284Sjmallett * if debugging is on.
323210284Sjmallett *
324210284Sjmallett * @param usb     USB block this access is for
325210284Sjmallett * @param address 64bit address to write
326210284Sjmallett * @param value   Value to write
327210284Sjmallett */
328210284Sjmallettstatic inline void __cvmx_usb_write_csr64(cvmx_usb_internal_state_t *usb,
329210284Sjmallett                                          uint64_t address, uint64_t value)
330210284Sjmallett{
331210284Sjmallett#if ALLOW_CSR_DECODES
332210284Sjmallett    if (cvmx_unlikely(usb->init_flags & CVMX_USB_INITIALIZE_FLAGS_DEBUG_CSRS))
333210284Sjmallett    {
334210284Sjmallett        cvmx_dprintf("Write: ");
335210284Sjmallett        cvmx_csr_db_decode(cvmx_get_proc_id(), address, value);
336210284Sjmallett    }
337210284Sjmallett#endif
338210284Sjmallett    cvmx_write64_uint64(address, value);
339210284Sjmallett}
340210284Sjmallett
341210284Sjmallett
342210284Sjmallett/**
343210284Sjmallett * @INTERNAL
344210284Sjmallett * Uitility function to convert complete codes into strings
345210284Sjmallett *
346210284Sjmallett * @param complete_code
347210284Sjmallett *               Code to convert
348210284Sjmallett *
349210284Sjmallett * @return Human readable string
350210284Sjmallett */
351210284Sjmallettstatic const char *__cvmx_usb_complete_to_string(cvmx_usb_complete_t complete_code)
352210284Sjmallett{
353210284Sjmallett    switch (complete_code)
354210284Sjmallett    {
355210284Sjmallett        case CVMX_USB_COMPLETE_SUCCESS: return "SUCCESS";
356210284Sjmallett        case CVMX_USB_COMPLETE_SHORT:   return "SHORT";
357210284Sjmallett        case CVMX_USB_COMPLETE_CANCEL:  return "CANCEL";
358210284Sjmallett        case CVMX_USB_COMPLETE_ERROR:   return "ERROR";
359210284Sjmallett        case CVMX_USB_COMPLETE_STALL:   return "STALL";
360210284Sjmallett        case CVMX_USB_COMPLETE_XACTERR: return "XACTERR";
361210284Sjmallett        case CVMX_USB_COMPLETE_DATATGLERR: return "DATATGLERR";
362210284Sjmallett        case CVMX_USB_COMPLETE_BABBLEERR: return "BABBLEERR";
363210284Sjmallett        case CVMX_USB_COMPLETE_FRAMEERR: return "FRAMEERR";
364210284Sjmallett    }
365210284Sjmallett    return "Update __cvmx_usb_complete_to_string";
366210284Sjmallett}
367210284Sjmallett
368210284Sjmallett
369210284Sjmallett/**
370210284Sjmallett * @INTERNAL
371210284Sjmallett * Return non zero if this pipe connects to a non HIGH speed
372210284Sjmallett * device through a high speed hub.
373210284Sjmallett *
374210284Sjmallett * @param usb    USB block this access is for
375210284Sjmallett * @param pipe   Pipe to check
376210284Sjmallett *
377210284Sjmallett * @return Non zero if we need to do split transactions
378210284Sjmallett */
379210284Sjmallettstatic inline int __cvmx_usb_pipe_needs_split(cvmx_usb_internal_state_t *usb, cvmx_usb_pipe_t *pipe)
380210284Sjmallett{
381210284Sjmallett    return ((pipe->device_speed != CVMX_USB_SPEED_HIGH) && (usb->usbcx_hprt.s.prtspd == CVMX_USB_SPEED_HIGH));
382210284Sjmallett}
383210284Sjmallett
384210284Sjmallett
385210284Sjmallett/**
386210284Sjmallett * @INTERNAL
387210284Sjmallett * Trivial utility function to return the correct PID for a pipe
388210284Sjmallett *
389210284Sjmallett * @param pipe   pipe to check
390210284Sjmallett *
391210284Sjmallett * @return PID for pipe
392210284Sjmallett */
393210284Sjmallettstatic inline int __cvmx_usb_get_data_pid(cvmx_usb_pipe_t *pipe)
394210284Sjmallett{
395210284Sjmallett    if (pipe->pid_toggle)
396210284Sjmallett        return 2; /* Data1 */
397210284Sjmallett    else
398210284Sjmallett        return 0; /* Data0 */
399210284Sjmallett}
400210284Sjmallett
401210284Sjmallett
402210284Sjmallett/**
403210284Sjmallett * Return the number of USB ports supported by this Octeon
404210284Sjmallett * chip. If the chip doesn't support USB, or is not supported
405210284Sjmallett * by this API, a zero will be returned. Most Octeon chips
406210284Sjmallett * support one usb port, but some support two ports.
407210284Sjmallett * cvmx_usb_initialize() must be called on independent
408210284Sjmallett * cvmx_usb_state_t structures.
409210284Sjmallett *
410210284Sjmallett * This utilizes cvmx_helper_board_usb_get_num_ports()
411210284Sjmallett * to get any board specific variatons.
412210284Sjmallett *
413210284Sjmallett * @return Number of port, zero if usb isn't supported
414210284Sjmallett */
415210284Sjmallettint cvmx_usb_get_num_ports(void)
416210284Sjmallett{
417210284Sjmallett    int arch_ports = 0;
418210284Sjmallett
419210284Sjmallett    if (OCTEON_IS_MODEL(OCTEON_CN52XX))
420210284Sjmallett        arch_ports = 2;
421210284Sjmallett    else if (OCTEON_IS_MODEL(OCTEON_CN31XX))
422210284Sjmallett        arch_ports = 0; /* This chip has USB but it doesn't support DMA */
423210284Sjmallett    else if (OCTEON_IS_MODEL(OCTEON_CN38XX) || OCTEON_IS_MODEL(OCTEON_CN58XX))
424210284Sjmallett        arch_ports = 0;
425210284Sjmallett    else
426210284Sjmallett        arch_ports = 1;
427210284Sjmallett
428210284Sjmallett    return __cvmx_helper_board_usb_get_num_ports(arch_ports);
429210284Sjmallett}
430210284Sjmallett
431210284Sjmallett
432210284Sjmallett/**
433210284Sjmallett * @INTERNAL
434210284Sjmallett * Allocate a usb transaction for use
435210284Sjmallett *
436210284Sjmallett * @param usb    USB device state populated by
437210284Sjmallett *               cvmx_usb_initialize().
438210284Sjmallett *
439210284Sjmallett * @return Transaction or NULL
440210284Sjmallett */
441210284Sjmallettstatic inline cvmx_usb_transaction_t *__cvmx_usb_alloc_transaction(cvmx_usb_internal_state_t *usb)
442210284Sjmallett{
443210284Sjmallett    cvmx_usb_transaction_t *t;
444210284Sjmallett    t = usb->free_transaction_head;
445210284Sjmallett    if (t)
446210284Sjmallett    {
447210284Sjmallett        usb->free_transaction_head = t->next;
448210284Sjmallett        if (!usb->free_transaction_head)
449210284Sjmallett            usb->free_transaction_tail = NULL;
450210284Sjmallett    }
451210284Sjmallett    else if (cvmx_unlikely(usb->init_flags & CVMX_USB_INITIALIZE_FLAGS_DEBUG_INFO))
452210284Sjmallett        cvmx_dprintf("%s: Failed to allocate a transaction\n", __FUNCTION__);
453210284Sjmallett    if (t)
454210284Sjmallett    {
455210284Sjmallett        memset(t, 0, sizeof(*t));
456210284Sjmallett        t->flags = __CVMX_USB_TRANSACTION_FLAGS_IN_USE;
457210284Sjmallett    }
458210284Sjmallett    return t;
459210284Sjmallett}
460210284Sjmallett
461210284Sjmallett
462210284Sjmallett/**
463210284Sjmallett * @INTERNAL
464210284Sjmallett * Free a usb transaction
465210284Sjmallett *
466210284Sjmallett * @param usb    USB device state populated by
467210284Sjmallett *               cvmx_usb_initialize().
468210284Sjmallett * @param transaction
469210284Sjmallett *               Transaction to free
470210284Sjmallett */
471210284Sjmallettstatic inline void __cvmx_usb_free_transaction(cvmx_usb_internal_state_t *usb,
472210284Sjmallett                                        cvmx_usb_transaction_t *transaction)
473210284Sjmallett{
474210284Sjmallett    transaction->flags = 0;
475210284Sjmallett    transaction->prev = NULL;
476210284Sjmallett    transaction->next = NULL;
477210284Sjmallett    if (usb->free_transaction_tail)
478210284Sjmallett        usb->free_transaction_tail->next = transaction;
479210284Sjmallett    else
480210284Sjmallett        usb->free_transaction_head = transaction;
481210284Sjmallett    usb->free_transaction_tail = transaction;
482210284Sjmallett}
483210284Sjmallett
484210284Sjmallett
485210284Sjmallett/**
486210284Sjmallett * @INTERNAL
487210284Sjmallett * Add a pipe to the tail of a list
488210284Sjmallett * @param list   List to add pipe to
489210284Sjmallett * @param pipe   Pipe to add
490210284Sjmallett */
491210284Sjmallettstatic inline void __cvmx_usb_append_pipe(cvmx_usb_pipe_list_t *list, cvmx_usb_pipe_t *pipe)
492210284Sjmallett{
493210284Sjmallett    pipe->next = NULL;
494210284Sjmallett    pipe->prev = list->tail;
495210284Sjmallett    if (list->tail)
496210284Sjmallett        list->tail->next = pipe;
497210284Sjmallett    else
498210284Sjmallett        list->head = pipe;
499210284Sjmallett    list->tail = pipe;
500210284Sjmallett}
501210284Sjmallett
502210284Sjmallett
503210284Sjmallett/**
504210284Sjmallett * @INTERNAL
505210284Sjmallett * Remove a pipe from a list
506210284Sjmallett * @param list   List to remove pipe from
507210284Sjmallett * @param pipe   Pipe to remove
508210284Sjmallett */
509210284Sjmallettstatic inline void __cvmx_usb_remove_pipe(cvmx_usb_pipe_list_t *list, cvmx_usb_pipe_t *pipe)
510210284Sjmallett{
511210284Sjmallett    if (list->head == pipe)
512210284Sjmallett    {
513210284Sjmallett        list->head = pipe->next;
514210284Sjmallett        pipe->next = NULL;
515210284Sjmallett        if (list->head)
516210284Sjmallett            list->head->prev = NULL;
517210284Sjmallett        else
518210284Sjmallett            list->tail = NULL;
519210284Sjmallett    }
520210284Sjmallett    else if (list->tail == pipe)
521210284Sjmallett    {
522210284Sjmallett        list->tail = pipe->prev;
523210284Sjmallett        list->tail->next = NULL;
524210284Sjmallett        pipe->prev = NULL;
525210284Sjmallett    }
526210284Sjmallett    else
527210284Sjmallett    {
528210284Sjmallett        pipe->prev->next = pipe->next;
529210284Sjmallett        pipe->next->prev = pipe->prev;
530210284Sjmallett        pipe->prev = NULL;
531210284Sjmallett        pipe->next = NULL;
532210284Sjmallett    }
533210284Sjmallett}
534210284Sjmallett
535210284Sjmallett
536210284Sjmallett/**
537210284Sjmallett * @INTERNAL
538210284Sjmallett * Perfrom USB device mode initialization after a reset completes.
539210284Sjmallett * This should be called after USBC0/1_GINTSTS[USBRESET] and
540210284Sjmallett * coresponds to section 22.6.1.1, "Initialization on USB Reset",
541210284Sjmallett * in the manual.
542210284Sjmallett *
543210284Sjmallett * @param usb    USB device state populated by
544210284Sjmallett *               cvmx_usb_initialize().
545210284Sjmallett *
546210284Sjmallett * @return CVMX_USB_SUCCESS or a negative error code defined in
547210284Sjmallett *         cvmx_usb_status_t.
548210284Sjmallett */
549210284Sjmallettstatic cvmx_usb_status_t __cvmx_usb_device_reset_complete(cvmx_usb_internal_state_t *usb)
550210284Sjmallett{
551210284Sjmallett    cvmx_usbcx_ghwcfg3_t usbcx_ghwcfg3;
552210284Sjmallett    int i;
553210284Sjmallett
554210284Sjmallett    CVMX_USB_LOG_CALLED();
555210284Sjmallett    CVMX_USB_LOG_PARAM("%p", usb);
556210284Sjmallett
557210284Sjmallett    /* 1. Set USBC0/1_DOEPCTLn[SNAK] = 1 (for all OUT endpoints, n = 0-4). */
558210284Sjmallett    for (i=0; i<5; i++)
559210284Sjmallett    {
560210284Sjmallett        USB_SET_FIELD32(CVMX_USBCX_DOEPCTLX(i, usb->index),
561210284Sjmallett                        cvmx_usbcx_doepctlx_t, snak, 1);
562210284Sjmallett    }
563210284Sjmallett
564210284Sjmallett    /* 2. Unmask the following interrupt bits:
565210284Sjmallett        USBC0/1_DAINTMSK[INEPMSK] = 1 (control 0 IN endpoint)
566210284Sjmallett        USBC0/1_DAINTMSK[OUTEPMSK] = 1 (control 0 OUT endpoint)
567210284Sjmallett        USBC0/1_DOEPMSK[SETUPMSK] = 1
568210284Sjmallett        USBC0/1_DOEPMSK[XFERCOMPLMSK] = 1
569210284Sjmallett        USBC0/1_DIEPMSK[XFERCOMPLMSK] = 1
570210284Sjmallett        USBC0/1_DIEPMSK[TIMEOUTMSK] = 1 */
571210284Sjmallett    USB_SET_FIELD32(CVMX_USBCX_DAINTMSK(usb->index), cvmx_usbcx_daintmsk_t,
572210284Sjmallett                    inepmsk, 1);
573210284Sjmallett    USB_SET_FIELD32(CVMX_USBCX_DAINTMSK(usb->index), cvmx_usbcx_daintmsk_t,
574210284Sjmallett                    outepmsk, 1);
575210284Sjmallett    USB_SET_FIELD32(CVMX_USBCX_DOEPMSK(usb->index), cvmx_usbcx_doepmsk_t,
576210284Sjmallett                    setupmsk, 1);
577210284Sjmallett    USB_SET_FIELD32(CVMX_USBCX_DOEPMSK(usb->index), cvmx_usbcx_doepmsk_t,
578210284Sjmallett                    xfercomplmsk, 1);
579210284Sjmallett    USB_SET_FIELD32(CVMX_USBCX_DIEPMSK(usb->index), cvmx_usbcx_diepmsk_t,
580210284Sjmallett                    xfercomplmsk, 1);
581210284Sjmallett    USB_SET_FIELD32(CVMX_USBCX_DIEPMSK(usb->index), cvmx_usbcx_diepmsk_t,
582210284Sjmallett                    timeoutmsk, 1);
583210284Sjmallett
584210284Sjmallett    /* 3. To transmit or receive data, the device must initialize more
585210284Sjmallett        registers as specified in Section 22.6.1.7 */
586210284Sjmallett    /* Nothing needed */
587210284Sjmallett
588210284Sjmallett    /* 4. Set up the data FIFO RAM for each of the FIFOs:
589210284Sjmallett        Program USBC0/1_GRXFSIZ to be able to receive control OUT data and
590210284Sjmallett        SETUP data. This must equal at least one maximum packet size of
591210284Sjmallett        control endpoint 0 + 2 Dwords (for the status of the control OUT
592210284Sjmallett        data packet) + 10 Dwords (for SETUP packets).
593210284Sjmallett        Program USBC0/1_GNPTXFSIZ to be able to transmit control IN data. This
594210284Sjmallett        must equal at least one maximum packet size of control endpoint 0. */
595210284Sjmallett
596210284Sjmallett    /* Read the HWCFG3 register so we know how much space is in the FIFO */
597210284Sjmallett    usbcx_ghwcfg3.u32 = __cvmx_usb_read_csr32(usb, CVMX_USBCX_GHWCFG3(usb->index));
598210284Sjmallett
599210284Sjmallett    {
600210284Sjmallett        cvmx_usbcx_gnptxfsiz_t gnptxfsiz;
601210284Sjmallett        int fifo_space = usbcx_ghwcfg3.s.dfifodepth;
602210284Sjmallett        int i;
603210284Sjmallett
604210284Sjmallett        /* Start at the top of the FIFO and assign space for each periodic
605210284Sjmallett            fifo */
606210284Sjmallett        for (i=4;i>0;i--)
607210284Sjmallett        {
608210284Sjmallett            cvmx_usbcx_dptxfsizx_t siz;
609210284Sjmallett            siz.u32 = __cvmx_usb_read_csr32(usb, CVMX_USBCX_DPTXFSIZX(i, usb->index));
610210284Sjmallett            fifo_space -= siz.s.dptxfsize;
611210284Sjmallett            siz.s.dptxfstaddr = fifo_space;
612210284Sjmallett            __cvmx_usb_write_csr32(usb, CVMX_USBCX_DPTXFSIZX(i, usb->index), siz.u32);
613210284Sjmallett        }
614210284Sjmallett
615210284Sjmallett        /* Assign half the leftover space to the non periodic tx fifo */
616210284Sjmallett        gnptxfsiz.u32 = __cvmx_usb_read_csr32(usb, CVMX_USBCX_GNPTXFSIZ(usb->index));
617210284Sjmallett        gnptxfsiz.s.nptxfdep = fifo_space / 2;
618210284Sjmallett        fifo_space -= gnptxfsiz.s.nptxfdep;
619210284Sjmallett        gnptxfsiz.s.nptxfstaddr = fifo_space;
620210284Sjmallett        __cvmx_usb_write_csr32(usb, CVMX_USBCX_GNPTXFSIZ(usb->index), gnptxfsiz.u32);
621210284Sjmallett
622210284Sjmallett        /* Assign the remain space to the RX fifo */
623210284Sjmallett        USB_SET_FIELD32(CVMX_USBCX_GRXFSIZ(usb->index), cvmx_usbcx_grxfsiz_t,
624210284Sjmallett                        rxfdep, fifo_space);
625210284Sjmallett    }
626210284Sjmallett
627210284Sjmallett    /* 5. Program the following fields in the endpoint-specific registers for
628210284Sjmallett        control OUT endpoint 0 to receive a SETUP packet
629210284Sjmallett        USBC0/1_DOEPTSIZ0[SUPCNT] = 0x3 (to receive up to three back-to-back
630210284Sjmallett        SETUP packets)
631210284Sjmallett        In DMA mode, USBC0/1_DOEPDMA0 register with a memory address to
632210284Sjmallett        store any SETUP packets received */
633210284Sjmallett    USB_SET_FIELD32(CVMX_USBCX_DOEPTSIZX(0, usb->index),
634210284Sjmallett                    cvmx_usbcx_doeptsizx_t, mc, 3);
635210284Sjmallett    // FIXME
636210284Sjmallett
637210284Sjmallett    /* At this point, all initialization required to receive SETUP packets is
638210284Sjmallett        done. */
639210284Sjmallett
640210284Sjmallett    CVMX_USB_RETURN(CVMX_USB_SUCCESS);
641210284Sjmallett}
642210284Sjmallett
643210284Sjmallett
644210284Sjmallett/**
645210284Sjmallett * Initialize a USB port for use. This must be called before any
646210284Sjmallett * other access to the Octeon USB port is made. The port starts
647210284Sjmallett * off in the disabled state.
648210284Sjmallett *
649210284Sjmallett * @param state  Pointer to an empty cvmx_usb_state_t structure
650210284Sjmallett *               that will be populated by the initialize call.
651210284Sjmallett *               This structure is then passed to all other USB
652210284Sjmallett *               functions.
653210284Sjmallett * @param usb_port_number
654210284Sjmallett *               Which Octeon USB port to initialize.
655210284Sjmallett * @param flags  Flags to control hardware initialization. See
656210284Sjmallett *               cvmx_usb_initialize_flags_t for the flag
657210284Sjmallett *               definitions. Some flags are mandatory.
658210284Sjmallett *
659210284Sjmallett * @return CVMX_USB_SUCCESS or a negative error code defined in
660210284Sjmallett *         cvmx_usb_status_t.
661210284Sjmallett */
662210284Sjmallettcvmx_usb_status_t cvmx_usb_initialize(cvmx_usb_state_t *state,
663210284Sjmallett                                      int usb_port_number,
664210284Sjmallett                                      cvmx_usb_initialize_flags_t flags)
665210284Sjmallett{
666210284Sjmallett    cvmx_usbnx_clk_ctl_t usbn_clk_ctl;
667210284Sjmallett    cvmx_usbnx_usbp_ctl_status_t usbn_usbp_ctl_status;
668210284Sjmallett    cvmx_usb_internal_state_t *usb = (cvmx_usb_internal_state_t*)state;
669210284Sjmallett
670210284Sjmallett    usb->init_flags = flags;
671210284Sjmallett    CVMX_USB_LOG_CALLED();
672210284Sjmallett    CVMX_USB_LOG_PARAM("%p", state);
673210284Sjmallett    CVMX_USB_LOG_PARAM("%d", usb_port_number);
674210284Sjmallett    CVMX_USB_LOG_PARAM("0x%x", flags);
675210284Sjmallett
676210284Sjmallett    /* Make sure that state is large enough to store the internal state */
677210284Sjmallett    if (sizeof(*state) < sizeof(*usb))
678210284Sjmallett        CVMX_USB_RETURN(CVMX_USB_INVALID_PARAM);
679210284Sjmallett    /* At first allow 0-1 for the usb port number */
680210284Sjmallett    if ((usb_port_number < 0) || (usb_port_number > 1))
681210284Sjmallett        CVMX_USB_RETURN(CVMX_USB_INVALID_PARAM);
682210284Sjmallett    /* For all chips except 52XX there is only one port */
683210284Sjmallett    if (!OCTEON_IS_MODEL(OCTEON_CN52XX) && (usb_port_number > 0))
684210284Sjmallett        CVMX_USB_RETURN(CVMX_USB_INVALID_PARAM);
685210284Sjmallett    /* Try to determine clock type automatically */
686210284Sjmallett    if ((flags & (CVMX_USB_INITIALIZE_FLAGS_CLOCK_XO_XI |
687210284Sjmallett                  CVMX_USB_INITIALIZE_FLAGS_CLOCK_XO_GND)) == 0)
688210284Sjmallett    {
689210284Sjmallett        if (__cvmx_helper_board_usb_get_clock_type() == USB_CLOCK_TYPE_CRYSTAL_12)
690210284Sjmallett            flags |= CVMX_USB_INITIALIZE_FLAGS_CLOCK_XO_XI;  /* Only 12 MHZ crystals are supported */
691210284Sjmallett        else
692210284Sjmallett            flags |= CVMX_USB_INITIALIZE_FLAGS_CLOCK_XO_GND;
693210284Sjmallett    }
694210284Sjmallett
695210284Sjmallett    if (flags & CVMX_USB_INITIALIZE_FLAGS_CLOCK_XO_GND)
696210284Sjmallett    {
697210284Sjmallett        /* Check for auto ref clock frequency */
698210284Sjmallett        if (!(flags & CVMX_USB_INITIALIZE_FLAGS_CLOCK_MHZ_MASK))
699210284Sjmallett            switch (__cvmx_helper_board_usb_get_clock_type())
700210284Sjmallett            {
701210284Sjmallett                case USB_CLOCK_TYPE_REF_12:
702210284Sjmallett                    flags |= CVMX_USB_INITIALIZE_FLAGS_CLOCK_12MHZ;
703210284Sjmallett                    break;
704210284Sjmallett                case USB_CLOCK_TYPE_REF_24:
705210284Sjmallett                    flags |= CVMX_USB_INITIALIZE_FLAGS_CLOCK_24MHZ;
706210284Sjmallett                    break;
707210284Sjmallett                case USB_CLOCK_TYPE_REF_48:
708210284Sjmallett                    flags |= CVMX_USB_INITIALIZE_FLAGS_CLOCK_48MHZ;
709210284Sjmallett                    break;
710210284Sjmallett                default:
711210284Sjmallett                    CVMX_USB_RETURN(CVMX_USB_INVALID_PARAM);
712210284Sjmallett                    break;
713210284Sjmallett            }
714210284Sjmallett    }
715210284Sjmallett
716210284Sjmallett    memset(usb, 0, sizeof(usb));
717210284Sjmallett    usb->init_flags = flags;
718210284Sjmallett
719210284Sjmallett    /* Initialize the USB state structure */
720210284Sjmallett    {
721210284Sjmallett        int i;
722210284Sjmallett        usb->index = usb_port_number;
723210284Sjmallett
724210284Sjmallett        /* Initialize the transaction double linked list */
725210284Sjmallett        usb->free_transaction_head = NULL;
726210284Sjmallett        usb->free_transaction_tail = NULL;
727210284Sjmallett        for (i=0; i<MAX_TRANSACTIONS; i++)
728210284Sjmallett            __cvmx_usb_free_transaction(usb, usb->transaction + i);
729210284Sjmallett        for (i=0; i<MAX_PIPES; i++)
730210284Sjmallett            __cvmx_usb_append_pipe(&usb->free_pipes, usb->pipe + i);
731210284Sjmallett    }
732210284Sjmallett
733210284Sjmallett    /* Power On Reset and PHY Initialization */
734210284Sjmallett
735210284Sjmallett    /* 1. Wait for DCOK to assert (nothing to do) */
736210284Sjmallett    /* 2a. Write USBN0/1_CLK_CTL[POR] = 1 and
737210284Sjmallett        USBN0/1_CLK_CTL[HRST,PRST,HCLK_RST] = 0 */
738210284Sjmallett    usbn_clk_ctl.u64 = __cvmx_usb_read_csr64(usb, CVMX_USBNX_CLK_CTL(usb->index));
739210284Sjmallett    usbn_clk_ctl.s.por = 1;
740210284Sjmallett    usbn_clk_ctl.s.hrst = 0;
741210284Sjmallett    usbn_clk_ctl.s.prst = 0;
742210284Sjmallett    usbn_clk_ctl.s.hclk_rst = 0;
743210284Sjmallett    usbn_clk_ctl.s.enable = 0;
744210284Sjmallett    /* 2b. Select the USB reference clock/crystal parameters by writing
745210284Sjmallett        appropriate values to USBN0/1_CLK_CTL[P_C_SEL, P_RTYPE, P_COM_ON] */
746210284Sjmallett    if (usb->init_flags & CVMX_USB_INITIALIZE_FLAGS_CLOCK_XO_GND)
747210284Sjmallett    {
748210284Sjmallett        /* The USB port uses 12/24/48MHz 2.5V board clock
749210284Sjmallett            source at USB_XO. USB_XI should be tied to GND.
750210284Sjmallett            Most Octeon evaluation boards require this setting */
751210284Sjmallett        if (OCTEON_IS_MODEL(OCTEON_CN3XXX))
752210284Sjmallett        {
753210284Sjmallett            usbn_clk_ctl.cn31xx.p_rclk  = 1; /* From CN31XX,CN30XX manual */
754210284Sjmallett            usbn_clk_ctl.cn31xx.p_xenbn = 0;
755210284Sjmallett        }
756210284Sjmallett        else if (OCTEON_IS_MODEL(OCTEON_CN56XX) || OCTEON_IS_MODEL(OCTEON_CN50XX))
757210284Sjmallett            usbn_clk_ctl.cn56xx.p_rtype = 2; /* From CN56XX,CN50XX manual */
758210284Sjmallett        else
759210284Sjmallett            usbn_clk_ctl.cn52xx.p_rtype = 1; /* From CN52XX manual */
760210284Sjmallett
761210284Sjmallett        switch (flags & CVMX_USB_INITIALIZE_FLAGS_CLOCK_MHZ_MASK)
762210284Sjmallett        {
763210284Sjmallett            case CVMX_USB_INITIALIZE_FLAGS_CLOCK_12MHZ:
764210284Sjmallett                usbn_clk_ctl.s.p_c_sel = 0;
765210284Sjmallett                break;
766210284Sjmallett            case CVMX_USB_INITIALIZE_FLAGS_CLOCK_24MHZ:
767210284Sjmallett                usbn_clk_ctl.s.p_c_sel = 1;
768210284Sjmallett                break;
769210284Sjmallett            case CVMX_USB_INITIALIZE_FLAGS_CLOCK_48MHZ:
770210284Sjmallett                usbn_clk_ctl.s.p_c_sel = 2;
771210284Sjmallett                break;
772210284Sjmallett        }
773210284Sjmallett    }
774210284Sjmallett    else
775210284Sjmallett    {
776210284Sjmallett        /* The USB port uses a 12MHz crystal as clock source
777210284Sjmallett            at USB_XO and USB_XI */
778210284Sjmallett        if (OCTEON_IS_MODEL(OCTEON_CN3XXX))
779210284Sjmallett        {
780210284Sjmallett            usbn_clk_ctl.cn31xx.p_rclk  = 1; /* From CN31XX,CN30XX manual */
781210284Sjmallett            usbn_clk_ctl.cn31xx.p_xenbn = 1;
782210284Sjmallett        }
783210284Sjmallett        else if (OCTEON_IS_MODEL(OCTEON_CN56XX) || OCTEON_IS_MODEL(OCTEON_CN50XX))
784210284Sjmallett            usbn_clk_ctl.cn56xx.p_rtype = 0; /* From CN56XX,CN50XX manual */
785210284Sjmallett        else
786210284Sjmallett            usbn_clk_ctl.cn52xx.p_rtype = 0; /* From CN52XX manual */
787210284Sjmallett
788210284Sjmallett        usbn_clk_ctl.s.p_c_sel = 0;
789210284Sjmallett    }
790210284Sjmallett    /* 2c. Select the HCLK via writing USBN0/1_CLK_CTL[DIVIDE, DIVIDE2] and
791210284Sjmallett        setting USBN0/1_CLK_CTL[ENABLE] = 1.  Divide the core clock down such
792210284Sjmallett        that USB is as close as possible to 125Mhz */
793210284Sjmallett    {
794210284Sjmallett        int divisor = (cvmx_sysinfo_get()->cpu_clock_hz+125000000-1)/125000000;
795210284Sjmallett        if (divisor < 4)  /* Lower than 4 doesn't seem to work properly */
796210284Sjmallett            divisor = 4;
797210284Sjmallett        usbn_clk_ctl.s.divide = divisor;
798210284Sjmallett        usbn_clk_ctl.s.divide2 = 0;
799210284Sjmallett    }
800210284Sjmallett    __cvmx_usb_write_csr64(usb, CVMX_USBNX_CLK_CTL(usb->index),
801210284Sjmallett                           usbn_clk_ctl.u64);
802210284Sjmallett    /* 2d. Write USBN0/1_CLK_CTL[HCLK_RST] = 1 */
803210284Sjmallett    usbn_clk_ctl.s.hclk_rst = 1;
804210284Sjmallett    __cvmx_usb_write_csr64(usb, CVMX_USBNX_CLK_CTL(usb->index),
805210284Sjmallett                           usbn_clk_ctl.u64);
806210284Sjmallett    /* 2e.  Wait 64 core-clock cycles for HCLK to stabilize */
807210284Sjmallett    cvmx_wait(64);
808210284Sjmallett    /* 3. Program the power-on reset field in the USBN clock-control register:
809210284Sjmallett        USBN_CLK_CTL[POR] = 0 */
810210284Sjmallett    usbn_clk_ctl.s.por = 0;
811210284Sjmallett    __cvmx_usb_write_csr64(usb, CVMX_USBNX_CLK_CTL(usb->index),
812210284Sjmallett                           usbn_clk_ctl.u64);
813210284Sjmallett    /* 4. Wait 1 ms for PHY clock to start */
814210284Sjmallett    cvmx_wait_usec(1000);
815210284Sjmallett    /* 5. Program the Reset input from automatic test equipment field in the
816210284Sjmallett        USBP control and status register: USBN_USBP_CTL_STATUS[ATE_RESET] = 1 */
817210284Sjmallett    usbn_usbp_ctl_status.u64 = __cvmx_usb_read_csr64(usb, CVMX_USBNX_USBP_CTL_STATUS(usb->index));
818210284Sjmallett    usbn_usbp_ctl_status.s.ate_reset = 1;
819210284Sjmallett    __cvmx_usb_write_csr64(usb, CVMX_USBNX_USBP_CTL_STATUS(usb->index),
820210284Sjmallett                           usbn_usbp_ctl_status.u64);
821210284Sjmallett    /* 6. Wait 10 cycles */
822210284Sjmallett    cvmx_wait(10);
823210284Sjmallett    /* 7. Clear ATE_RESET field in the USBN clock-control register:
824210284Sjmallett        USBN_USBP_CTL_STATUS[ATE_RESET] = 0 */
825210284Sjmallett    usbn_usbp_ctl_status.s.ate_reset = 0;
826210284Sjmallett    __cvmx_usb_write_csr64(usb, CVMX_USBNX_USBP_CTL_STATUS(usb->index),
827210284Sjmallett                           usbn_usbp_ctl_status.u64);
828210284Sjmallett    /* 8. Program the PHY reset field in the USBN clock-control register:
829210284Sjmallett        USBN_CLK_CTL[PRST] = 1 */
830210284Sjmallett    usbn_clk_ctl.s.prst = 1;
831210284Sjmallett    __cvmx_usb_write_csr64(usb, CVMX_USBNX_CLK_CTL(usb->index),
832210284Sjmallett                           usbn_clk_ctl.u64);
833210284Sjmallett    /* 9. Program the USBP control and status register to select host or
834210284Sjmallett        device mode. USBN_USBP_CTL_STATUS[HST_MODE] = 0 for host, = 1 for
835210284Sjmallett        device */
836210284Sjmallett    if (usb->init_flags & CVMX_USB_INITIALIZE_FLAGS_DEVICE_MODE)
837210284Sjmallett    {
838210284Sjmallett        usbn_usbp_ctl_status.s.hst_mode = 1;
839210284Sjmallett        usbn_usbp_ctl_status.s.dm_pulld = 0;
840210284Sjmallett        usbn_usbp_ctl_status.s.dp_pulld = 0;
841210284Sjmallett    }
842210284Sjmallett    else
843210284Sjmallett    {
844210284Sjmallett        usbn_usbp_ctl_status.s.hst_mode = 0;
845210284Sjmallett    }
846210284Sjmallett    __cvmx_usb_write_csr64(usb, CVMX_USBNX_USBP_CTL_STATUS(usb->index),
847210284Sjmallett                           usbn_usbp_ctl_status.u64);
848210284Sjmallett    /* 10. Wait 1 �s */
849210284Sjmallett    cvmx_wait_usec(1);
850210284Sjmallett    /* 11. Program the hreset_n field in the USBN clock-control register:
851210284Sjmallett        USBN_CLK_CTL[HRST] = 1 */
852210284Sjmallett    usbn_clk_ctl.s.hrst = 1;
853210284Sjmallett    __cvmx_usb_write_csr64(usb, CVMX_USBNX_CLK_CTL(usb->index),
854210284Sjmallett                           usbn_clk_ctl.u64);
855210284Sjmallett    /* 12. Proceed to USB core initialization */
856210284Sjmallett    usbn_clk_ctl.s.enable = 1;
857210284Sjmallett    __cvmx_usb_write_csr64(usb, CVMX_USBNX_CLK_CTL(usb->index),
858210284Sjmallett                           usbn_clk_ctl.u64);
859210284Sjmallett    cvmx_wait_usec(1);
860210284Sjmallett
861210284Sjmallett    /* USB Core Initialization */
862210284Sjmallett
863210284Sjmallett    /* 1. Read USBC_GHWCFG1, USBC_GHWCFG2, USBC_GHWCFG3, USBC_GHWCFG4 to
864210284Sjmallett        determine USB core configuration parameters. */
865210284Sjmallett    /* Nothing needed */
866210284Sjmallett    /* 2. Program the following fields in the global AHB configuration
867210284Sjmallett        register (USBC_GAHBCFG)
868210284Sjmallett        DMA mode, USBC_GAHBCFG[DMAEn]: 1 = DMA mode, 0 = slave mode
869210284Sjmallett        Burst length, USBC_GAHBCFG[HBSTLEN] = 0
870210284Sjmallett        Nonperiodic TxFIFO empty level (slave mode only),
871210284Sjmallett        USBC_GAHBCFG[NPTXFEMPLVL]
872210284Sjmallett        Periodic TxFIFO empty level (slave mode only),
873210284Sjmallett        USBC_GAHBCFG[PTXFEMPLVL]
874210284Sjmallett        Global interrupt mask, USBC_GAHBCFG[GLBLINTRMSK] = 1 */
875210284Sjmallett    {
876210284Sjmallett        cvmx_usbcx_gahbcfg_t usbcx_gahbcfg;
877210284Sjmallett        usbcx_gahbcfg.u32 = 0;
878210284Sjmallett        usbcx_gahbcfg.s.dmaen = !OCTEON_IS_MODEL(OCTEON_CN31XX);
879210284Sjmallett        /* If we are using DMA, start off with 8 idle channels. Without
880210284Sjmallett            DMA we emulate a single channel */
881210284Sjmallett        if (usbcx_gahbcfg.s.dmaen)
882210284Sjmallett            usb->idle_hardware_channels = 0xff;
883210284Sjmallett        else
884210284Sjmallett            usb->idle_hardware_channels = 0x1;
885210284Sjmallett        usbcx_gahbcfg.s.hbstlen = 0;
886210284Sjmallett        usbcx_gahbcfg.s.nptxfemplvl = 1;
887210284Sjmallett        usbcx_gahbcfg.s.ptxfemplvl = 1;
888210284Sjmallett        usbcx_gahbcfg.s.glblintrmsk = 1;
889210284Sjmallett        __cvmx_usb_write_csr32(usb, CVMX_USBCX_GAHBCFG(usb->index),
890210284Sjmallett                               usbcx_gahbcfg.u32);
891210284Sjmallett    }
892210284Sjmallett    /* 3. Program the following fields in USBC_GUSBCFG register.
893210284Sjmallett        HS/FS timeout calibration, USBC_GUSBCFG[TOUTCAL] = 0
894210284Sjmallett        ULPI DDR select, USBC_GUSBCFG[DDRSEL] = 0
895210284Sjmallett        USB turnaround time, USBC_GUSBCFG[USBTRDTIM] = 0x5
896210284Sjmallett        PHY low-power clock select, USBC_GUSBCFG[PHYLPWRCLKSEL] = 0 */
897210284Sjmallett    {
898210284Sjmallett        cvmx_usbcx_gusbcfg_t usbcx_gusbcfg;
899210284Sjmallett        usbcx_gusbcfg.u32 = __cvmx_usb_read_csr32(usb, CVMX_USBCX_GUSBCFG(usb->index));
900210284Sjmallett        usbcx_gusbcfg.s.toutcal = 0;
901210284Sjmallett        usbcx_gusbcfg.s.ddrsel = 0;
902210284Sjmallett        usbcx_gusbcfg.s.usbtrdtim = 0x5;
903210284Sjmallett        usbcx_gusbcfg.s.phylpwrclksel = 0;
904210284Sjmallett        __cvmx_usb_write_csr32(usb, CVMX_USBCX_GUSBCFG(usb->index),
905210284Sjmallett                               usbcx_gusbcfg.u32);
906210284Sjmallett    }
907210284Sjmallett    /* 4. The software must unmask the following bits in the USBC_GINTMSK
908210284Sjmallett        register.
909210284Sjmallett        OTG interrupt mask, USBC_GINTMSK[OTGINTMSK] = 1
910210284Sjmallett        Mode mismatch interrupt mask, USBC_GINTMSK[MODEMISMSK] = 1 */
911210284Sjmallett    {
912210284Sjmallett        cvmx_usbcx_gintmsk_t usbcx_gintmsk;
913210284Sjmallett        cvmx_usbcx_hcintmskx_t usbc_hcintmsk;
914210284Sjmallett        cvmx_usbcx_haintmsk_t usbc_haintmsk;
915210284Sjmallett        int channel;
916210284Sjmallett
917210284Sjmallett        usbcx_gintmsk.u32 = __cvmx_usb_read_csr32(usb, CVMX_USBCX_GINTMSK(usb->index));
918210284Sjmallett        usbcx_gintmsk.s.otgintmsk = 1;
919210284Sjmallett        usbcx_gintmsk.s.modemismsk = 1;
920210284Sjmallett        usbcx_gintmsk.s.hchintmsk = 1;
921210284Sjmallett        usbcx_gintmsk.s.sofmsk = 0;
922210284Sjmallett        __cvmx_usb_write_csr32(usb, CVMX_USBCX_GINTMSK(usb->index),
923210284Sjmallett                               usbcx_gintmsk.u32);
924210284Sjmallett
925210284Sjmallett        /* Enable the channel halt interrupt */
926210284Sjmallett        usbc_hcintmsk.u32 = 0;
927210284Sjmallett        usbc_hcintmsk.s.chhltdmsk = 1;
928210284Sjmallett        for (channel=0; channel<8; channel++)
929210284Sjmallett            if (usb->idle_hardware_channels & (1<<channel))
930210284Sjmallett                __cvmx_usb_write_csr32(usb, CVMX_USBCX_HCINTMSKX(channel, usb->index), usbc_hcintmsk.u32);
931210284Sjmallett
932210284Sjmallett        /* Enable the channel interrupt to propagate */
933210284Sjmallett        usbc_haintmsk.u32 = 0;
934210284Sjmallett        usbc_haintmsk.s.haintmsk = usb->idle_hardware_channels;
935210284Sjmallett        __cvmx_usb_write_csr32(usb, CVMX_USBCX_HAINTMSK(usb->index), usbc_haintmsk.u32);
936210284Sjmallett    }
937210284Sjmallett
938210284Sjmallett    if ((usb->init_flags & CVMX_USB_INITIALIZE_FLAGS_DEVICE_MODE) == 0)
939210284Sjmallett    {
940210284Sjmallett        /* Host Port Initialization */
941210284Sjmallett        if (cvmx_unlikely(usb->init_flags & CVMX_USB_INITIALIZE_FLAGS_DEBUG_INFO))
942210284Sjmallett            cvmx_dprintf("%s: USB%d is in host mode\n", __FUNCTION__, usb->index);
943210284Sjmallett
944210284Sjmallett        /* 1. Program the host-port interrupt-mask field to unmask,
945210284Sjmallett            USBC_GINTMSK[PRTINT] = 1 */
946210284Sjmallett        USB_SET_FIELD32(CVMX_USBCX_GINTMSK(usb->index), cvmx_usbcx_gintmsk_t,
947210284Sjmallett                        prtintmsk, 1);
948210284Sjmallett        USB_SET_FIELD32(CVMX_USBCX_GINTMSK(usb->index), cvmx_usbcx_gintmsk_t,
949210284Sjmallett                        disconnintmsk, 1);
950210284Sjmallett        /* 2. Program the USBC_HCFG register to select full-speed host or
951210284Sjmallett            high-speed host. */
952210284Sjmallett        {
953210284Sjmallett            cvmx_usbcx_hcfg_t usbcx_hcfg;
954210284Sjmallett            usbcx_hcfg.u32 = __cvmx_usb_read_csr32(usb, CVMX_USBCX_HCFG(usb->index));
955210284Sjmallett            usbcx_hcfg.s.fslssupp = 0;
956210284Sjmallett            usbcx_hcfg.s.fslspclksel = 0;
957210284Sjmallett            __cvmx_usb_write_csr32(usb, CVMX_USBCX_HCFG(usb->index), usbcx_hcfg.u32);
958210284Sjmallett        }
959210284Sjmallett        /* 3. Program the port power bit to drive VBUS on the USB,
960210284Sjmallett            USBC_HPRT[PRTPWR] = 1 */
961210284Sjmallett        USB_SET_FIELD32(CVMX_USBCX_HPRT(usb->index), cvmx_usbcx_hprt_t, prtpwr, 1);
962210284Sjmallett
963210284Sjmallett        /* Steps 4-15 from the manual are done later in the port enable */
964210284Sjmallett    }
965210284Sjmallett    else
966210284Sjmallett    {
967210284Sjmallett        /* Device Port Initialization */
968210284Sjmallett        if (cvmx_unlikely(usb->init_flags & CVMX_USB_INITIALIZE_FLAGS_DEBUG_INFO))
969210284Sjmallett            cvmx_dprintf("%s: USB%d is in device mode\n", __FUNCTION__, usb->index);
970210284Sjmallett
971210284Sjmallett        /* 1. Program the following fields in the USBC0/1_DCFG register:
972210284Sjmallett            Device speed, USBC0/1_DCFG[DEVSPD] = 0 (high speed)
973210284Sjmallett            Non-zero-length status OUT handshake, USBC0/1_DCFG[NZSTSOUTHSHK]=0
974210284Sjmallett            Periodic frame interval (if periodic endpoints are supported),
975210284Sjmallett            USBC0/1_DCFG[PERFRINT] = 1 */
976210284Sjmallett        USB_SET_FIELD32(CVMX_USBCX_DCFG(usb->index), cvmx_usbcx_dcfg_t,
977210284Sjmallett                        devspd, 0);
978210284Sjmallett        USB_SET_FIELD32(CVMX_USBCX_DCFG(usb->index), cvmx_usbcx_dcfg_t,
979210284Sjmallett                        nzstsouthshk, 0);
980210284Sjmallett        USB_SET_FIELD32(CVMX_USBCX_DCFG(usb->index), cvmx_usbcx_dcfg_t,
981210284Sjmallett                        perfrint, 1);
982210284Sjmallett
983210284Sjmallett        /* 2. Program the USBC0/1_GINTMSK register to unmask the following
984210284Sjmallett            interrupts:
985210284Sjmallett            USB Reset, USBC0/1_GINTMSK[USBRSTMSK] = 1
986210284Sjmallett            Enumeration done, USBC0/1_GINTMSK[ENUMDONEMSK] = 1
987210284Sjmallett            SOF, USBC0/1_GINTMSK[SOFMSK] = 1 */
988210284Sjmallett        USB_SET_FIELD32(CVMX_USBCX_GINTMSK(usb->index), cvmx_usbcx_gintmsk_t,
989210284Sjmallett                        usbrstmsk, 1);
990210284Sjmallett        USB_SET_FIELD32(CVMX_USBCX_GINTMSK(usb->index), cvmx_usbcx_gintmsk_t,
991210284Sjmallett                        enumdonemsk, 1);
992210284Sjmallett        USB_SET_FIELD32(CVMX_USBCX_GINTMSK(usb->index), cvmx_usbcx_gintmsk_t,
993210284Sjmallett                        sofmsk, 1);
994210284Sjmallett
995210284Sjmallett        /* 3. Wait for the USBC0/1_GINTSTS[USBRESET] interrupt, which
996210284Sjmallett            indicates a reset has been detected on the USB and lasts for
997210284Sjmallett            about 10 ms. On receiving this interrupt, the application must
998210284Sjmallett            perform the steps listed in Section 22.6.1.1, "Initialization on
999210284Sjmallett            USB Reset". */
1000210284Sjmallett        /* Handled in cvmx_poll() usbc_gintsts.s.usbrst processing */
1001210284Sjmallett
1002210284Sjmallett        /* 4. Wait for the USBC0/1_GINTSTS[ENUMERATIONDONE] interrupt, which
1003210284Sjmallett            indicates the end of reset on the USB. On receiving this interrupt,
1004210284Sjmallett            the application must read the USBC0/1_DSTS register to determine
1005210284Sjmallett            the enumeration speed and perform the steps listed in Section
1006210284Sjmallett            22.6.1.2, "Initialization on Enumeration Completion". */
1007210284Sjmallett        /* Handled in cvmx_poll() usbc_gintsts.s.enumdone processing */
1008210284Sjmallett    }
1009210284Sjmallett
1010210284Sjmallett    CVMX_USB_RETURN(CVMX_USB_SUCCESS);
1011210284Sjmallett}
1012210284Sjmallett
1013210284Sjmallett
1014210284Sjmallett/**
1015210284Sjmallett * Shutdown a USB port after a call to cvmx_usb_initialize().
1016210284Sjmallett * The port should be disabled with all pipes closed when this
1017210284Sjmallett * function is called.
1018210284Sjmallett *
1019210284Sjmallett * @param state  USB device state populated by
1020210284Sjmallett *               cvmx_usb_initialize().
1021210284Sjmallett *
1022210284Sjmallett * @return CVMX_USB_SUCCESS or a negative error code defined in
1023210284Sjmallett *         cvmx_usb_status_t.
1024210284Sjmallett */
1025210284Sjmallettcvmx_usb_status_t cvmx_usb_shutdown(cvmx_usb_state_t *state)
1026210284Sjmallett{
1027210284Sjmallett    cvmx_usbnx_clk_ctl_t usbn_clk_ctl;
1028210284Sjmallett    cvmx_usb_internal_state_t *usb = (cvmx_usb_internal_state_t*)state;
1029210284Sjmallett
1030210284Sjmallett    CVMX_USB_LOG_CALLED();
1031210284Sjmallett    CVMX_USB_LOG_PARAM("%p", state);
1032210284Sjmallett
1033210284Sjmallett    /* Make sure all pipes are closed */
1034210284Sjmallett    if (usb->idle_pipes.head ||
1035210284Sjmallett        usb->active_pipes[CVMX_USB_TRANSFER_ISOCHRONOUS].head ||
1036210284Sjmallett        usb->active_pipes[CVMX_USB_TRANSFER_INTERRUPT].head ||
1037210284Sjmallett        usb->active_pipes[CVMX_USB_TRANSFER_CONTROL].head ||
1038210284Sjmallett        usb->active_pipes[CVMX_USB_TRANSFER_BULK].head)
1039210284Sjmallett        CVMX_USB_RETURN(CVMX_USB_BUSY);
1040210284Sjmallett
1041210284Sjmallett    /* Disable the clocks and put them in power on reset */
1042210284Sjmallett    usbn_clk_ctl.u64 = __cvmx_usb_read_csr64(usb, CVMX_USBNX_CLK_CTL(usb->index));
1043210284Sjmallett    usbn_clk_ctl.s.enable = 1;
1044210284Sjmallett    usbn_clk_ctl.s.por = 1;
1045210284Sjmallett    usbn_clk_ctl.s.hclk_rst = 1;
1046210284Sjmallett    usbn_clk_ctl.s.prst = 0;
1047210284Sjmallett    usbn_clk_ctl.s.hrst = 0;
1048210284Sjmallett    __cvmx_usb_write_csr64(usb, CVMX_USBNX_CLK_CTL(usb->index),
1049210284Sjmallett                           usbn_clk_ctl.u64);
1050210284Sjmallett    CVMX_USB_RETURN(CVMX_USB_SUCCESS);
1051210284Sjmallett}
1052210284Sjmallett
1053210284Sjmallett
1054210284Sjmallett/**
1055210284Sjmallett * Enable a USB port. After this call succeeds, the USB port is
1056210284Sjmallett * online and servicing requests.
1057210284Sjmallett *
1058210284Sjmallett * @param state  USB device state populated by
1059210284Sjmallett *               cvmx_usb_initialize().
1060210284Sjmallett *
1061210284Sjmallett * @return CVMX_USB_SUCCESS or a negative error code defined in
1062210284Sjmallett *         cvmx_usb_status_t.
1063210284Sjmallett */
1064210284Sjmallettcvmx_usb_status_t cvmx_usb_enable(cvmx_usb_state_t *state)
1065210284Sjmallett{
1066210284Sjmallett    cvmx_usbcx_ghwcfg3_t usbcx_ghwcfg3;
1067210284Sjmallett    cvmx_usb_internal_state_t *usb = (cvmx_usb_internal_state_t*)state;
1068210284Sjmallett
1069210284Sjmallett    CVMX_USB_LOG_CALLED();
1070210284Sjmallett    CVMX_USB_LOG_PARAM("%p", state);
1071210284Sjmallett    if (cvmx_unlikely(usb->init_flags & CVMX_USB_INITIALIZE_FLAGS_DEVICE_MODE))
1072210284Sjmallett        CVMX_USB_RETURN(CVMX_USB_INCORRECT_MODE);
1073210284Sjmallett
1074210284Sjmallett    usb->usbcx_hprt.u32 = __cvmx_usb_read_csr32(usb, CVMX_USBCX_HPRT(usb->index));
1075210284Sjmallett
1076210284Sjmallett    /* If the port is already enabled the just return. We don't need to do
1077210284Sjmallett        anything */
1078210284Sjmallett    if (usb->usbcx_hprt.s.prtena)
1079210284Sjmallett        CVMX_USB_RETURN(CVMX_USB_SUCCESS);
1080210284Sjmallett
1081210284Sjmallett    /* If there is nothing plugged into the port then fail immediately */
1082210284Sjmallett    if (!usb->usbcx_hprt.s.prtconnsts)
1083210284Sjmallett    {
1084210284Sjmallett        if (cvmx_unlikely(usb->init_flags & CVMX_USB_INITIALIZE_FLAGS_DEBUG_INFO))
1085210284Sjmallett            cvmx_dprintf("%s: USB%d Nothing plugged into the port\n", __FUNCTION__, usb->index);
1086210284Sjmallett        CVMX_USB_RETURN(CVMX_USB_TIMEOUT);
1087210284Sjmallett    }
1088210284Sjmallett
1089210284Sjmallett    /* Program the port reset bit to start the reset process */
1090210284Sjmallett    USB_SET_FIELD32(CVMX_USBCX_HPRT(usb->index), cvmx_usbcx_hprt_t, prtrst, 1);
1091210284Sjmallett
1092210284Sjmallett    /* Wait at least 50ms (high speed), or 10ms (full speed) for the reset
1093210284Sjmallett        process to complete. */
1094210284Sjmallett    cvmx_wait_usec(50000);
1095210284Sjmallett
1096210284Sjmallett    /* Program the port reset bit to 0, USBC_HPRT[PRTRST] = 0 */
1097210284Sjmallett    USB_SET_FIELD32(CVMX_USBCX_HPRT(usb->index), cvmx_usbcx_hprt_t, prtrst, 0);
1098210284Sjmallett
1099210284Sjmallett    /* Wait for the USBC_HPRT[PRTENA]. */
1100210284Sjmallett    if (CVMX_WAIT_FOR_FIELD32(CVMX_USBCX_HPRT(usb->index), cvmx_usbcx_hprt_t,
1101210284Sjmallett                              prtena, ==, 1, 100000))
1102210284Sjmallett    {
1103210284Sjmallett        if (cvmx_unlikely(usb->init_flags & CVMX_USB_INITIALIZE_FLAGS_DEBUG_INFO))
1104210284Sjmallett            cvmx_dprintf("%s: Timeout waiting for the port to finish reset\n",
1105210284Sjmallett                         __FUNCTION__);
1106210284Sjmallett        CVMX_USB_RETURN(CVMX_USB_TIMEOUT);
1107210284Sjmallett    }
1108210284Sjmallett
1109210284Sjmallett    /* Read the port speed field to get the enumerated speed, USBC_HPRT[PRTSPD]. */
1110210284Sjmallett    usb->usbcx_hprt.u32 = __cvmx_usb_read_csr32(usb, CVMX_USBCX_HPRT(usb->index));
1111210284Sjmallett    if (cvmx_unlikely(usb->init_flags & CVMX_USB_INITIALIZE_FLAGS_DEBUG_INFO))
1112210284Sjmallett        cvmx_dprintf("%s: USB%d is in %s speed mode\n", __FUNCTION__, usb->index,
1113210284Sjmallett                     (usb->usbcx_hprt.s.prtspd == CVMX_USB_SPEED_HIGH) ? "high" :
1114210284Sjmallett                     (usb->usbcx_hprt.s.prtspd == CVMX_USB_SPEED_FULL) ? "full" :
1115210284Sjmallett                     "low");
1116210284Sjmallett
1117210284Sjmallett    usbcx_ghwcfg3.u32 = __cvmx_usb_read_csr32(usb, CVMX_USBCX_GHWCFG3(usb->index));
1118210284Sjmallett
1119210284Sjmallett    /* 13. Program the USBC_GRXFSIZ register to select the size of the receive
1120210284Sjmallett        FIFO (25%). */
1121210284Sjmallett    USB_SET_FIELD32(CVMX_USBCX_GRXFSIZ(usb->index), cvmx_usbcx_grxfsiz_t,
1122210284Sjmallett                    rxfdep, usbcx_ghwcfg3.s.dfifodepth / 4);
1123210284Sjmallett    /* 14. Program the USBC_GNPTXFSIZ register to select the size and the
1124210284Sjmallett        start address of the non- periodic transmit FIFO for nonperiodic
1125210284Sjmallett        transactions (50%). */
1126210284Sjmallett    {
1127210284Sjmallett        cvmx_usbcx_gnptxfsiz_t siz;
1128210284Sjmallett        siz.u32 = __cvmx_usb_read_csr32(usb, CVMX_USBCX_GNPTXFSIZ(usb->index));
1129210284Sjmallett        siz.s.nptxfdep = usbcx_ghwcfg3.s.dfifodepth / 2;
1130210284Sjmallett        siz.s.nptxfstaddr = usbcx_ghwcfg3.s.dfifodepth / 4;
1131210284Sjmallett        __cvmx_usb_write_csr32(usb, CVMX_USBCX_GNPTXFSIZ(usb->index), siz.u32);
1132210284Sjmallett    }
1133210284Sjmallett    /* 15. Program the USBC_HPTXFSIZ register to select the size and start
1134210284Sjmallett        address of the periodic transmit FIFO for periodic transactions (25%). */
1135210284Sjmallett    {
1136210284Sjmallett        cvmx_usbcx_hptxfsiz_t siz;
1137210284Sjmallett        siz.u32 = __cvmx_usb_read_csr32(usb, CVMX_USBCX_HPTXFSIZ(usb->index));
1138210284Sjmallett        siz.s.ptxfsize = usbcx_ghwcfg3.s.dfifodepth / 4;
1139210284Sjmallett        siz.s.ptxfstaddr = 3 * usbcx_ghwcfg3.s.dfifodepth / 4;
1140210284Sjmallett        __cvmx_usb_write_csr32(usb, CVMX_USBCX_HPTXFSIZ(usb->index), siz.u32);
1141210284Sjmallett    }
1142210284Sjmallett    /* Flush all FIFOs */
1143210284Sjmallett    USB_SET_FIELD32(CVMX_USBCX_GRSTCTL(usb->index), cvmx_usbcx_grstctl_t, txfnum, 0x10);
1144210284Sjmallett    USB_SET_FIELD32(CVMX_USBCX_GRSTCTL(usb->index), cvmx_usbcx_grstctl_t, txfflsh, 1);
1145210284Sjmallett    CVMX_WAIT_FOR_FIELD32(CVMX_USBCX_GRSTCTL(usb->index), cvmx_usbcx_grstctl_t,
1146210284Sjmallett                          txfflsh, ==, 0, 100);
1147210284Sjmallett    USB_SET_FIELD32(CVMX_USBCX_GRSTCTL(usb->index), cvmx_usbcx_grstctl_t, rxfflsh, 1);
1148210284Sjmallett    CVMX_WAIT_FOR_FIELD32(CVMX_USBCX_GRSTCTL(usb->index), cvmx_usbcx_grstctl_t,
1149210284Sjmallett                          rxfflsh, ==, 0, 100);
1150210284Sjmallett
1151210284Sjmallett    CVMX_USB_RETURN(CVMX_USB_SUCCESS);
1152210284Sjmallett}
1153210284Sjmallett
1154210284Sjmallett
1155210284Sjmallett/**
1156210284Sjmallett * Disable a USB port. After this call the USB port will not
1157210284Sjmallett * generate data transfers and will not generate events.
1158210284Sjmallett * Transactions in process will fail and call their
1159210284Sjmallett * associated callbacks.
1160210284Sjmallett *
1161210284Sjmallett * @param state  USB device state populated by
1162210284Sjmallett *               cvmx_usb_initialize().
1163210284Sjmallett *
1164210284Sjmallett * @return CVMX_USB_SUCCESS or a negative error code defined in
1165210284Sjmallett *         cvmx_usb_status_t.
1166210284Sjmallett */
1167210284Sjmallettcvmx_usb_status_t cvmx_usb_disable(cvmx_usb_state_t *state)
1168210284Sjmallett{
1169210284Sjmallett    cvmx_usb_internal_state_t *usb = (cvmx_usb_internal_state_t*)state;
1170210284Sjmallett
1171210284Sjmallett    CVMX_USB_LOG_CALLED();
1172210284Sjmallett    CVMX_USB_LOG_PARAM("%p", state);
1173210284Sjmallett    if (cvmx_unlikely(usb->init_flags & CVMX_USB_INITIALIZE_FLAGS_DEVICE_MODE))
1174210284Sjmallett        CVMX_USB_RETURN(CVMX_USB_INCORRECT_MODE);
1175210284Sjmallett
1176210284Sjmallett    /* Disable the port */
1177210284Sjmallett    USB_SET_FIELD32(CVMX_USBCX_HPRT(usb->index), cvmx_usbcx_hprt_t, prtena, 1);
1178210284Sjmallett    CVMX_USB_RETURN(CVMX_USB_SUCCESS);
1179210284Sjmallett}
1180210284Sjmallett
1181210284Sjmallett
1182210284Sjmallett/**
1183210284Sjmallett * Get the current state of the USB port. Use this call to
1184210284Sjmallett * determine if the usb port has anything connected, is enabled,
1185210284Sjmallett * or has some sort of error condition. The return value of this
1186210284Sjmallett * call has "changed" bits to signal of the value of some fields
1187210284Sjmallett * have changed between calls. These "changed" fields are based
1188210284Sjmallett * on the last call to cvmx_usb_set_status(). In order to clear
1189210284Sjmallett * them, you must update the status through cvmx_usb_set_status().
1190210284Sjmallett *
1191210284Sjmallett * @param state  USB device state populated by
1192210284Sjmallett *               cvmx_usb_initialize().
1193210284Sjmallett *
1194210284Sjmallett * @return Port status information
1195210284Sjmallett */
1196210284Sjmallettcvmx_usb_port_status_t cvmx_usb_get_status(cvmx_usb_state_t *state)
1197210284Sjmallett{
1198210284Sjmallett    cvmx_usbcx_hprt_t usbc_hprt;
1199210284Sjmallett    cvmx_usb_port_status_t result;
1200210284Sjmallett    cvmx_usb_internal_state_t *usb = (cvmx_usb_internal_state_t*)state;
1201210284Sjmallett
1202210284Sjmallett    memset(&result, 0, sizeof(result));
1203210284Sjmallett
1204210284Sjmallett    CVMX_USB_LOG_CALLED();
1205210284Sjmallett    CVMX_USB_LOG_PARAM("%p", state);
1206210284Sjmallett
1207210284Sjmallett    usbc_hprt.u32 = __cvmx_usb_read_csr32(usb, CVMX_USBCX_HPRT(usb->index));
1208210284Sjmallett    result.port_enabled = usbc_hprt.s.prtena;
1209210284Sjmallett    result.port_over_current = usbc_hprt.s.prtovrcurract;
1210210284Sjmallett    result.port_powered = usbc_hprt.s.prtpwr;
1211210284Sjmallett    result.port_speed = usbc_hprt.s.prtspd;
1212210284Sjmallett    result.connected = usbc_hprt.s.prtconnsts;
1213210284Sjmallett    result.connect_change = (result.connected != usb->port_status.connected);
1214210284Sjmallett
1215210284Sjmallett    if (cvmx_unlikely(usb->init_flags & CVMX_USB_INITIALIZE_FLAGS_DEBUG_CALLS))
1216210284Sjmallett        cvmx_dprintf("%*s%s: returned port enabled=%d, over_current=%d, powered=%d, speed=%d, connected=%d, connect_change=%d\n",
1217210284Sjmallett                     2*(--usb->indent), "", __FUNCTION__,
1218210284Sjmallett                     result.port_enabled,
1219210284Sjmallett                     result.port_over_current,
1220210284Sjmallett                     result.port_powered,
1221210284Sjmallett                     result.port_speed,
1222210284Sjmallett                     result.connected,
1223210284Sjmallett                     result.connect_change);
1224210284Sjmallett    return result;
1225210284Sjmallett}
1226210284Sjmallett
1227210284Sjmallett
1228210284Sjmallett/**
1229210284Sjmallett * Set the current state of the USB port. The status is used as
1230210284Sjmallett * a reference for the "changed" bits returned by
1231210284Sjmallett * cvmx_usb_get_status(). Other than serving as a reference, the
1232210284Sjmallett * status passed to this function is not used. No fields can be
1233210284Sjmallett * changed through this call.
1234210284Sjmallett *
1235210284Sjmallett * @param state  USB device state populated by
1236210284Sjmallett *               cvmx_usb_initialize().
1237210284Sjmallett * @param port_status
1238210284Sjmallett *               Port status to set, most like returned by cvmx_usb_get_status()
1239210284Sjmallett */
1240210284Sjmallettvoid cvmx_usb_set_status(cvmx_usb_state_t *state, cvmx_usb_port_status_t port_status)
1241210284Sjmallett{
1242210284Sjmallett    cvmx_usb_internal_state_t *usb = (cvmx_usb_internal_state_t*)state;
1243210284Sjmallett    CVMX_USB_LOG_CALLED();
1244210284Sjmallett    CVMX_USB_LOG_PARAM("%p", state);
1245210284Sjmallett    usb->port_status = port_status;
1246210284Sjmallett    CVMX_USB_RETURN_NOTHING();
1247210284Sjmallett}
1248210284Sjmallett
1249210284Sjmallett
1250210284Sjmallett/**
1251210284Sjmallett * @INTERNAL
1252210284Sjmallett * Convert a USB transaction into a handle
1253210284Sjmallett *
1254210284Sjmallett * @param usb    USB device state populated by
1255210284Sjmallett *               cvmx_usb_initialize().
1256210284Sjmallett * @param transaction
1257210284Sjmallett *               Transaction to get handle for
1258210284Sjmallett *
1259210284Sjmallett * @return Handle
1260210284Sjmallett */
1261210284Sjmallettstatic inline int __cvmx_usb_get_submit_handle(cvmx_usb_internal_state_t *usb,
1262210284Sjmallett                                        cvmx_usb_transaction_t *transaction)
1263210284Sjmallett{
1264210284Sjmallett    return ((unsigned long)transaction - (unsigned long)usb->transaction) /
1265210284Sjmallett            sizeof(*transaction);
1266210284Sjmallett}
1267210284Sjmallett
1268210284Sjmallett
1269210284Sjmallett/**
1270210284Sjmallett * @INTERNAL
1271210284Sjmallett * Convert a USB pipe into a handle
1272210284Sjmallett *
1273210284Sjmallett * @param usb    USB device state populated by
1274210284Sjmallett *               cvmx_usb_initialize().
1275210284Sjmallett * @param pipe   Pipe to get handle for
1276210284Sjmallett *
1277210284Sjmallett * @return Handle
1278210284Sjmallett */
1279210284Sjmallettstatic inline int __cvmx_usb_get_pipe_handle(cvmx_usb_internal_state_t *usb,
1280210284Sjmallett                                        cvmx_usb_pipe_t *pipe)
1281210284Sjmallett{
1282210284Sjmallett    return ((unsigned long)pipe - (unsigned long)usb->pipe) / sizeof(*pipe);
1283210284Sjmallett}
1284210284Sjmallett
1285210284Sjmallett
1286210284Sjmallett/**
1287210284Sjmallett * Open a virtual pipe between the host and a USB device. A pipe
1288210284Sjmallett * must be opened before data can be transferred between a device
1289210284Sjmallett * and Octeon.
1290210284Sjmallett *
1291210284Sjmallett * @param state      USB device state populated by
1292210284Sjmallett *                   cvmx_usb_initialize().
1293210284Sjmallett * @param flags      Optional pipe flags defined in
1294210284Sjmallett *                   cvmx_usb_pipe_flags_t.
1295210284Sjmallett * @param device_addr
1296210284Sjmallett *                   USB device address to open the pipe to
1297210284Sjmallett *                   (0-127).
1298210284Sjmallett * @param endpoint_num
1299210284Sjmallett *                   USB endpoint number to open the pipe to
1300210284Sjmallett *                   (0-15).
1301210284Sjmallett * @param device_speed
1302210284Sjmallett *                   The speed of the device the pipe is going
1303210284Sjmallett *                   to. This must match the device's speed,
1304210284Sjmallett *                   which may be different than the port speed.
1305210284Sjmallett * @param max_packet The maximum packet length the device can
1306210284Sjmallett *                   transmit/receive (low speed=0-8, full
1307210284Sjmallett *                   speed=0-1023, high speed=0-1024). This value
1308210284Sjmallett *                   comes from the stadnard endpoint descriptor
1309210284Sjmallett *                   field wMaxPacketSize bits <10:0>.
1310210284Sjmallett * @param transfer_type
1311210284Sjmallett *                   The type of transfer this pipe is for.
1312210284Sjmallett * @param transfer_dir
1313210284Sjmallett *                   The direction the pipe is in. This is not
1314210284Sjmallett *                   used for control pipes.
1315210284Sjmallett * @param interval   For ISOCHRONOUS and INTERRUPT transfers,
1316210284Sjmallett *                   this is how often the transfer is scheduled
1317210284Sjmallett *                   for. All other transfers should specify
1318210284Sjmallett *                   zero. The units are in frames (8000/sec at
1319210284Sjmallett *                   high speed, 1000/sec for full speed).
1320210284Sjmallett * @param multi_count
1321210284Sjmallett *                   For high speed devices, this is the maximum
1322210284Sjmallett *                   allowed number of packet per microframe.
1323210284Sjmallett *                   Specify zero for non high speed devices. This
1324210284Sjmallett *                   value comes from the stadnard endpoint descriptor
1325210284Sjmallett *                   field wMaxPacketSize bits <12:11>.
1326210284Sjmallett * @param hub_device_addr
1327210284Sjmallett *                   Hub device address this device is connected
1328210284Sjmallett *                   to. Devices connected directly to Octeon
1329210284Sjmallett *                   use zero. This is only used when the device
1330210284Sjmallett *                   is full/low speed behind a high speed hub.
1331210284Sjmallett *                   The address will be of the high speed hub,
1332210284Sjmallett *                   not and full speed hubs after it.
1333210284Sjmallett * @param hub_port   Which port on the hub the device is
1334210284Sjmallett *                   connected. Use zero for devices connected
1335210284Sjmallett *                   directly to Octeon. Like hub_device_addr,
1336210284Sjmallett *                   this is only used for full/low speed
1337210284Sjmallett *                   devices behind a high speed hub.
1338210284Sjmallett *
1339210284Sjmallett * @return A non negative value is a pipe handle. Negative
1340210284Sjmallett *         values are failure codes from cvmx_usb_status_t.
1341210284Sjmallett */
1342210284Sjmallettint cvmx_usb_open_pipe(cvmx_usb_state_t *state, cvmx_usb_pipe_flags_t flags,
1343210284Sjmallett                       int device_addr, int endpoint_num,
1344210284Sjmallett                       cvmx_usb_speed_t device_speed, int max_packet,
1345210284Sjmallett                       cvmx_usb_transfer_t transfer_type,
1346210284Sjmallett                       cvmx_usb_direction_t transfer_dir, int interval,
1347210284Sjmallett                       int multi_count, int hub_device_addr, int hub_port)
1348210284Sjmallett{
1349210284Sjmallett    cvmx_usb_pipe_t *pipe;
1350210284Sjmallett    cvmx_usb_internal_state_t *usb = (cvmx_usb_internal_state_t*)state;
1351210284Sjmallett
1352210284Sjmallett    CVMX_USB_LOG_CALLED();
1353210284Sjmallett    CVMX_USB_LOG_PARAM("%p", state);
1354210284Sjmallett    CVMX_USB_LOG_PARAM("0x%x", flags);
1355210284Sjmallett    CVMX_USB_LOG_PARAM("%d", device_addr);
1356210284Sjmallett    CVMX_USB_LOG_PARAM("%d", endpoint_num);
1357210284Sjmallett    CVMX_USB_LOG_PARAM("%d", device_speed);
1358210284Sjmallett    CVMX_USB_LOG_PARAM("%d", max_packet);
1359210284Sjmallett    CVMX_USB_LOG_PARAM("%d", transfer_type);
1360210284Sjmallett    CVMX_USB_LOG_PARAM("%d", transfer_dir);
1361210284Sjmallett    CVMX_USB_LOG_PARAM("%d", interval);
1362210284Sjmallett    CVMX_USB_LOG_PARAM("%d", multi_count);
1363210284Sjmallett    CVMX_USB_LOG_PARAM("%d", hub_device_addr);
1364210284Sjmallett    CVMX_USB_LOG_PARAM("%d", hub_port);
1365210284Sjmallett
1366210284Sjmallett    if (cvmx_unlikely((device_addr < 0) || (device_addr > MAX_USB_ADDRESS)))
1367210284Sjmallett        CVMX_USB_RETURN(CVMX_USB_INVALID_PARAM);
1368210284Sjmallett    if (cvmx_unlikely((endpoint_num < 0) || (endpoint_num > MAX_USB_ENDPOINT)))
1369210284Sjmallett        CVMX_USB_RETURN(CVMX_USB_INVALID_PARAM);
1370210284Sjmallett    if (cvmx_unlikely(device_speed > CVMX_USB_SPEED_LOW))
1371210284Sjmallett        CVMX_USB_RETURN(CVMX_USB_INVALID_PARAM);
1372210284Sjmallett    if (cvmx_unlikely((max_packet <= 0) || (max_packet > 1024)))
1373210284Sjmallett        CVMX_USB_RETURN(CVMX_USB_INVALID_PARAM);
1374210284Sjmallett    if (cvmx_unlikely(transfer_type > CVMX_USB_TRANSFER_INTERRUPT))
1375210284Sjmallett        CVMX_USB_RETURN(CVMX_USB_INVALID_PARAM);
1376210284Sjmallett    if (cvmx_unlikely((transfer_dir != CVMX_USB_DIRECTION_OUT) &&
1377210284Sjmallett        (transfer_dir != CVMX_USB_DIRECTION_IN)))
1378210284Sjmallett        CVMX_USB_RETURN(CVMX_USB_INVALID_PARAM);
1379210284Sjmallett    if (cvmx_unlikely(interval < 0))
1380210284Sjmallett        CVMX_USB_RETURN(CVMX_USB_INVALID_PARAM);
1381210284Sjmallett    if (cvmx_unlikely((transfer_type == CVMX_USB_TRANSFER_CONTROL) && interval))
1382210284Sjmallett        CVMX_USB_RETURN(CVMX_USB_INVALID_PARAM);
1383210284Sjmallett    if (cvmx_unlikely(multi_count < 0))
1384210284Sjmallett        CVMX_USB_RETURN(CVMX_USB_INVALID_PARAM);
1385210284Sjmallett    if (cvmx_unlikely((device_speed != CVMX_USB_SPEED_HIGH) &&
1386210284Sjmallett        (multi_count != 0)))
1387210284Sjmallett        CVMX_USB_RETURN(CVMX_USB_INVALID_PARAM);
1388210284Sjmallett    if (cvmx_unlikely((hub_device_addr < 0) || (hub_device_addr > MAX_USB_ADDRESS)))
1389210284Sjmallett        CVMX_USB_RETURN(CVMX_USB_INVALID_PARAM);
1390210284Sjmallett    if (cvmx_unlikely((hub_port < 0) || (hub_port > MAX_USB_HUB_PORT)))
1391210284Sjmallett        CVMX_USB_RETURN(CVMX_USB_INVALID_PARAM);
1392210284Sjmallett    if (cvmx_unlikely(usb->init_flags & CVMX_USB_INITIALIZE_FLAGS_DEVICE_MODE))
1393210284Sjmallett        CVMX_USB_RETURN(CVMX_USB_INCORRECT_MODE);
1394210284Sjmallett
1395210284Sjmallett    /* Find a free pipe */
1396210284Sjmallett    pipe = usb->free_pipes.head;
1397210284Sjmallett    if (!pipe)
1398210284Sjmallett        CVMX_USB_RETURN(CVMX_USB_NO_MEMORY);
1399210284Sjmallett    __cvmx_usb_remove_pipe(&usb->free_pipes, pipe);
1400210284Sjmallett    pipe->flags = flags | __CVMX_USB_PIPE_FLAGS_OPEN;
1401210284Sjmallett    if ((device_speed == CVMX_USB_SPEED_HIGH) &&
1402210284Sjmallett        (transfer_dir == CVMX_USB_DIRECTION_OUT) &&
1403210284Sjmallett        (transfer_type == CVMX_USB_TRANSFER_BULK))
1404210284Sjmallett        pipe->flags |= __CVMX_USB_PIPE_FLAGS_NEED_PING;
1405210284Sjmallett    pipe->device_addr = device_addr;
1406210284Sjmallett    pipe->endpoint_num = endpoint_num;
1407210284Sjmallett    pipe->device_speed = device_speed;
1408210284Sjmallett    pipe->max_packet = max_packet;
1409210284Sjmallett    pipe->transfer_type = transfer_type;
1410210284Sjmallett    pipe->transfer_dir = transfer_dir;
1411210284Sjmallett    /* All pipes use interval to rate limit NAK processing. Force an interval
1412210284Sjmallett        if one wasn't supplied */
1413210284Sjmallett    if (!interval)
1414210284Sjmallett        interval = 1;
1415210284Sjmallett    if (device_speed == CVMX_USB_SPEED_HIGH)
1416210284Sjmallett        pipe->interval = (uint64_t)interval * cvmx_sysinfo_get()->cpu_clock_hz / 8000;
1417210284Sjmallett    else
1418210284Sjmallett        pipe->interval = (uint64_t)interval * cvmx_sysinfo_get()->cpu_clock_hz / 1000;
1419210284Sjmallett    pipe->multi_count = multi_count;
1420210284Sjmallett    pipe->hub_device_addr = hub_device_addr;
1421210284Sjmallett    pipe->hub_port = hub_port;
1422210284Sjmallett    pipe->pid_toggle = 0;
1423210284Sjmallett    pipe->next_tx_cycle = cvmx_read64_uint64(CVMX_IPD_CLK_COUNT) + pipe->interval;
1424210284Sjmallett    pipe->split_sc_frame = -1;
1425210284Sjmallett    __cvmx_usb_append_pipe(&usb->idle_pipes, pipe);
1426210284Sjmallett
1427210284Sjmallett    /* We don't need to tell the hardware about this pipe yet since
1428210284Sjmallett        it doesn't have any submitted requests */
1429210284Sjmallett
1430210284Sjmallett    CVMX_USB_RETURN(__cvmx_usb_get_pipe_handle(usb, pipe));
1431210284Sjmallett}
1432210284Sjmallett
1433210284Sjmallett
1434210284Sjmallett/**
1435210284Sjmallett * @INTERNAL
1436210284Sjmallett * Perform channel specific setup for Control transactions. All
1437210284Sjmallett * the generic stuff will already have been done in
1438210284Sjmallett * __cvmx_usb_start_channel()
1439210284Sjmallett *
1440210284Sjmallett * @param usb     USB device state populated by
1441210284Sjmallett *                cvmx_usb_initialize().
1442210284Sjmallett * @param channel Channel to setup
1443210284Sjmallett * @param pipe    Pipe for control transaction
1444210284Sjmallett */
1445210284Sjmallettstatic void __cvmx_usb_start_channel_control(cvmx_usb_internal_state_t *usb,
1446210284Sjmallett                                             int channel,
1447210284Sjmallett                                             cvmx_usb_pipe_t *pipe)
1448210284Sjmallett{
1449210284Sjmallett    cvmx_usb_transaction_t *transaction = pipe->head;
1450210284Sjmallett    cvmx_usb_control_header_t *header = cvmx_phys_to_ptr(transaction->control_header);
1451210284Sjmallett    int bytes_to_transfer = transaction->buffer_length - transaction->actual_bytes;
1452210284Sjmallett    cvmx_usbcx_hctsizx_t usbc_hctsiz;
1453210284Sjmallett
1454210284Sjmallett    CVMX_USB_LOG_CALLED();
1455210284Sjmallett    CVMX_USB_LOG_PARAM("%p", usb);
1456210284Sjmallett    CVMX_USB_LOG_PARAM("%d", channel);
1457210284Sjmallett    CVMX_USB_LOG_PARAM("%p", pipe);
1458210284Sjmallett
1459210284Sjmallett    usbc_hctsiz.u32 = __cvmx_usb_read_csr32(usb, CVMX_USBCX_HCTSIZX(channel, usb->index));
1460210284Sjmallett
1461210284Sjmallett    switch (transaction->stage)
1462210284Sjmallett    {
1463210284Sjmallett        case CVMX_USB_STAGE_NON_CONTROL:
1464210284Sjmallett        case CVMX_USB_STAGE_NON_CONTROL_SPLIT_COMPLETE:
1465210284Sjmallett            cvmx_dprintf("%s: ERROR - Non control stage\n", __FUNCTION__);
1466210284Sjmallett            break;
1467210284Sjmallett        case CVMX_USB_STAGE_SETUP:
1468210284Sjmallett            usbc_hctsiz.s.pid = 3; /* Setup */
1469210284Sjmallett            usbc_hctsiz.s.xfersize = sizeof(*header);
1470210284Sjmallett            /* All Control operations start with a setup going OUT */
1471210284Sjmallett            USB_SET_FIELD32(CVMX_USBCX_HCCHARX(channel, usb->index), cvmx_usbcx_hccharx_t, epdir, CVMX_USB_DIRECTION_OUT);
1472210284Sjmallett            /* Setup send the control header instead of the buffer data. The
1473210284Sjmallett                buffer data will be used in the next stage */
1474210284Sjmallett            __cvmx_usb_write_csr64(usb, CVMX_USBNX_DMA0_OUTB_CHN0(usb->index) + channel*8, transaction->control_header);
1475210284Sjmallett            break;
1476210284Sjmallett        case CVMX_USB_STAGE_SETUP_SPLIT_COMPLETE:
1477210284Sjmallett            usbc_hctsiz.s.pid = 3; /* Setup */
1478210284Sjmallett            usbc_hctsiz.s.xfersize = 0;
1479210284Sjmallett            /* All Control operations start with a setup going OUT */
1480210284Sjmallett            USB_SET_FIELD32(CVMX_USBCX_HCCHARX(channel, usb->index), cvmx_usbcx_hccharx_t, epdir, CVMX_USB_DIRECTION_OUT);
1481210284Sjmallett            USB_SET_FIELD32(CVMX_USBCX_HCSPLTX(channel, usb->index), cvmx_usbcx_hcspltx_t, compsplt, 1);
1482210284Sjmallett            break;
1483210284Sjmallett        case CVMX_USB_STAGE_DATA:
1484210284Sjmallett            usbc_hctsiz.s.pid = __cvmx_usb_get_data_pid(pipe);
1485210284Sjmallett            if (__cvmx_usb_pipe_needs_split(usb, pipe))
1486210284Sjmallett            {
1487210284Sjmallett                usbc_hctsiz.s.xfersize = (header->s.request_type & 0x80) ? 0 : bytes_to_transfer;
1488210284Sjmallett                if (usbc_hctsiz.s.xfersize > pipe->max_packet)
1489210284Sjmallett                    usbc_hctsiz.s.xfersize = pipe->max_packet;
1490210284Sjmallett            }
1491210284Sjmallett            else
1492210284Sjmallett                usbc_hctsiz.s.xfersize = bytes_to_transfer;
1493210284Sjmallett            USB_SET_FIELD32(CVMX_USBCX_HCCHARX(channel, usb->index),
1494210284Sjmallett                            cvmx_usbcx_hccharx_t, epdir,
1495210284Sjmallett                            ((header->s.request_type & 0x80) ?
1496210284Sjmallett                             CVMX_USB_DIRECTION_IN :
1497210284Sjmallett                             CVMX_USB_DIRECTION_OUT));
1498210284Sjmallett            break;
1499210284Sjmallett        case CVMX_USB_STAGE_DATA_SPLIT_COMPLETE:
1500210284Sjmallett            usbc_hctsiz.s.pid = __cvmx_usb_get_data_pid(pipe);
1501210284Sjmallett            usbc_hctsiz.s.xfersize = (header->s.request_type & 0x80) ? bytes_to_transfer : 0;
1502210284Sjmallett            USB_SET_FIELD32(CVMX_USBCX_HCCHARX(channel, usb->index),
1503210284Sjmallett                            cvmx_usbcx_hccharx_t, epdir,
1504210284Sjmallett                            ((header->s.request_type & 0x80) ?
1505210284Sjmallett                             CVMX_USB_DIRECTION_IN :
1506210284Sjmallett                             CVMX_USB_DIRECTION_OUT));
1507210284Sjmallett            USB_SET_FIELD32(CVMX_USBCX_HCSPLTX(channel, usb->index), cvmx_usbcx_hcspltx_t, compsplt, 1);
1508210284Sjmallett            break;
1509210284Sjmallett        case CVMX_USB_STAGE_STATUS:
1510210284Sjmallett            usbc_hctsiz.s.pid = __cvmx_usb_get_data_pid(pipe);
1511210284Sjmallett            usbc_hctsiz.s.xfersize = 0;
1512210284Sjmallett            USB_SET_FIELD32(CVMX_USBCX_HCCHARX(channel, usb->index), cvmx_usbcx_hccharx_t, epdir,
1513210284Sjmallett                            ((header->s.request_type & 0x80) ?
1514210284Sjmallett                             CVMX_USB_DIRECTION_OUT :
1515210284Sjmallett                             CVMX_USB_DIRECTION_IN));
1516210284Sjmallett            break;
1517210284Sjmallett        case CVMX_USB_STAGE_STATUS_SPLIT_COMPLETE:
1518210284Sjmallett            usbc_hctsiz.s.pid = __cvmx_usb_get_data_pid(pipe);
1519210284Sjmallett            usbc_hctsiz.s.xfersize = 0;
1520210284Sjmallett            USB_SET_FIELD32(CVMX_USBCX_HCCHARX(channel, usb->index), cvmx_usbcx_hccharx_t, epdir,
1521210284Sjmallett                            ((header->s.request_type & 0x80) ?
1522210284Sjmallett                             CVMX_USB_DIRECTION_OUT :
1523210284Sjmallett                             CVMX_USB_DIRECTION_IN));
1524210284Sjmallett            USB_SET_FIELD32(CVMX_USBCX_HCSPLTX(channel, usb->index), cvmx_usbcx_hcspltx_t, compsplt, 1);
1525210284Sjmallett            break;
1526210284Sjmallett    }
1527210284Sjmallett
1528210284Sjmallett    /* Set the number of packets needed for this transfer */
1529210284Sjmallett    usbc_hctsiz.s.pktcnt = (usbc_hctsiz.s.xfersize + pipe->max_packet - 1) / pipe->max_packet;
1530210284Sjmallett    if (!usbc_hctsiz.s.pktcnt)
1531210284Sjmallett        usbc_hctsiz.s.pktcnt = 1;
1532210284Sjmallett
1533210284Sjmallett    __cvmx_usb_write_csr32(usb, CVMX_USBCX_HCTSIZX(channel, usb->index), usbc_hctsiz.u32);
1534210284Sjmallett    CVMX_USB_RETURN_NOTHING();
1535210284Sjmallett}
1536210284Sjmallett
1537210284Sjmallett
1538210284Sjmallett/**
1539210284Sjmallett * @INTERNAL
1540210284Sjmallett * Start a channel to perform the pipe's head transaction
1541210284Sjmallett *
1542210284Sjmallett * @param usb     USB device state populated by
1543210284Sjmallett *                cvmx_usb_initialize().
1544210284Sjmallett * @param channel Channel to setup
1545210284Sjmallett * @param pipe    Pipe to start
1546210284Sjmallett */
1547210284Sjmallettstatic void __cvmx_usb_start_channel(cvmx_usb_internal_state_t *usb,
1548210284Sjmallett                                     int channel,
1549210284Sjmallett                                     cvmx_usb_pipe_t *pipe)
1550210284Sjmallett{
1551210284Sjmallett    cvmx_usb_transaction_t *transaction = pipe->head;
1552210284Sjmallett    cvmx_usbcx_hfnum_t usbc_hfnum;
1553210284Sjmallett
1554210284Sjmallett    CVMX_USB_LOG_CALLED();
1555210284Sjmallett    CVMX_USB_LOG_PARAM("%p", usb);
1556210284Sjmallett    CVMX_USB_LOG_PARAM("%d", channel);
1557210284Sjmallett    CVMX_USB_LOG_PARAM("%p", pipe);
1558210284Sjmallett
1559210284Sjmallett    if (cvmx_unlikely((usb->init_flags & CVMX_USB_INITIALIZE_FLAGS_DEBUG_TRANSFERS) ||
1560210284Sjmallett        (pipe->flags & CVMX_USB_PIPE_FLAGS_DEBUG_TRANSFERS)))
1561210284Sjmallett        cvmx_dprintf("%s: Channel %d started. Pipe %d transaction %d stage %d\n",
1562210284Sjmallett                     __FUNCTION__, channel, __cvmx_usb_get_pipe_handle(usb, pipe),
1563210284Sjmallett                     __cvmx_usb_get_submit_handle(usb, transaction),
1564210284Sjmallett                     transaction->stage);
1565210284Sjmallett
1566210284Sjmallett    /* Make sure all writes to the DMA region get flushed */
1567210284Sjmallett    CVMX_SYNCW;
1568210284Sjmallett
1569210284Sjmallett    /* Read the current frame number for use with split, INTERRUPT,  and ISO
1570210284Sjmallett        transactions */
1571210284Sjmallett    usbc_hfnum.u32 = __cvmx_usb_read_csr32(usb, CVMX_USBCX_HFNUM(usb->index));
1572210284Sjmallett
1573210284Sjmallett    /* Attach the channel to the pipe */
1574210284Sjmallett    usb->pipe_for_channel[channel] = pipe;
1575210284Sjmallett    pipe->channel = channel;
1576210284Sjmallett    pipe->flags |= __CVMX_USB_PIPE_FLAGS_SCHEDULED;
1577210284Sjmallett
1578210284Sjmallett    /* Mark this channel as in use */
1579210284Sjmallett    usb->idle_hardware_channels &= ~(1<<channel);
1580210284Sjmallett
1581210284Sjmallett    /* Enable the channel interrupt bits */
1582210284Sjmallett    {
1583210284Sjmallett        cvmx_usbcx_hcintx_t usbc_hcint;
1584210284Sjmallett
1585210284Sjmallett        /* Clear all channel status bits */
1586210284Sjmallett        usbc_hcint.u32 = __cvmx_usb_read_csr32(usb, CVMX_USBCX_HCINTX(channel, usb->index));
1587210284Sjmallett        __cvmx_usb_write_csr32(usb, CVMX_USBCX_HCINTX(channel, usb->index), usbc_hcint.u32);
1588210284Sjmallett    }
1589210284Sjmallett
1590210284Sjmallett    /* Setup the locations the DMA engines use  */
1591210284Sjmallett    {
1592210284Sjmallett        uint64_t dma_address = transaction->buffer + transaction->actual_bytes;
1593210284Sjmallett        if (transaction->type == CVMX_USB_TRANSFER_ISOCHRONOUS)
1594210284Sjmallett            dma_address = transaction->buffer + transaction->iso_packets[0].offset + transaction->actual_bytes;
1595210284Sjmallett        __cvmx_usb_write_csr64(usb, CVMX_USBNX_DMA0_OUTB_CHN0(usb->index) + channel*8, dma_address);
1596210284Sjmallett        __cvmx_usb_write_csr64(usb, CVMX_USBNX_DMA0_INB_CHN0(usb->index) + channel*8, dma_address);
1597210284Sjmallett    }
1598210284Sjmallett
1599210284Sjmallett    /* Setup both the size of the transfer and the SPLIT characteristics */
1600210284Sjmallett    {
1601210284Sjmallett        cvmx_usbcx_hcspltx_t usbc_hcsplt = {.u32 = 0};
1602210284Sjmallett        cvmx_usbcx_hctsizx_t usbc_hctsiz = {.u32 = 0};
1603210284Sjmallett        int bytes_to_transfer = transaction->buffer_length - transaction->actual_bytes;
1604210284Sjmallett
1605210284Sjmallett        /* ISOCHRONOUS transactions store each individual transfer size in the
1606210284Sjmallett            packet structure, not the global buffer_length */
1607210284Sjmallett        if (transaction->type == CVMX_USB_TRANSFER_ISOCHRONOUS)
1608210284Sjmallett            bytes_to_transfer = transaction->iso_packets[0].length - transaction->actual_bytes;
1609210284Sjmallett
1610210284Sjmallett        /* We need to do split transactions when we are talking to non high
1611210284Sjmallett            speed devices that are behind a high speed hub */
1612210284Sjmallett        if (__cvmx_usb_pipe_needs_split(usb, pipe))
1613210284Sjmallett        {
1614210284Sjmallett            /* On the start split phase (stage is even) record the frame number we
1615210284Sjmallett                will need to send the split complete. We only store the lower two bits
1616210284Sjmallett                since the time ahead can only be two frames */
1617210284Sjmallett            if ((transaction->stage&1) == 0)
1618210284Sjmallett            {
1619210284Sjmallett                if (transaction->type == CVMX_USB_TRANSFER_BULK)
1620210284Sjmallett                    pipe->split_sc_frame = (usbc_hfnum.s.frnum + 1) & 0x7f;
1621210284Sjmallett                else
1622210284Sjmallett                    pipe->split_sc_frame = (usbc_hfnum.s.frnum + 2) & 0x7f;
1623210284Sjmallett            }
1624210284Sjmallett            else
1625210284Sjmallett                pipe->split_sc_frame = -1;
1626210284Sjmallett
1627210284Sjmallett            usbc_hcsplt.s.spltena = 1;
1628210284Sjmallett            usbc_hcsplt.s.hubaddr = pipe->hub_device_addr;
1629210284Sjmallett            usbc_hcsplt.s.prtaddr = pipe->hub_port;
1630210284Sjmallett            usbc_hcsplt.s.compsplt = (transaction->stage == CVMX_USB_STAGE_NON_CONTROL_SPLIT_COMPLETE);
1631210284Sjmallett
1632210284Sjmallett            /* SPLIT transactions can only ever transmit one data packet so
1633210284Sjmallett                limit the transfer size to the max packet size */
1634210284Sjmallett            if (bytes_to_transfer > pipe->max_packet)
1635210284Sjmallett                bytes_to_transfer = pipe->max_packet;
1636210284Sjmallett
1637210284Sjmallett            /* ISOCHRONOUS OUT splits are unique in that they limit
1638210284Sjmallett                data transfers to 188 byte chunks representing the
1639210284Sjmallett                begin/middle/end of the data or all */
1640210284Sjmallett            if (!usbc_hcsplt.s.compsplt &&
1641210284Sjmallett                (pipe->transfer_dir == CVMX_USB_DIRECTION_OUT) &&
1642210284Sjmallett                (pipe->transfer_type == CVMX_USB_TRANSFER_ISOCHRONOUS))
1643210284Sjmallett            {
1644210284Sjmallett                /* See if we've started this tranfer and sent data */
1645210284Sjmallett                if (transaction->actual_bytes == 0)
1646210284Sjmallett                {
1647210284Sjmallett                    /* Nothing sent yet, this is either a begin or the
1648210284Sjmallett                        entire payload */
1649210284Sjmallett                    if (bytes_to_transfer <= 188)
1650210284Sjmallett                        usbc_hcsplt.s.xactpos = 3; /* Entire payload in one go */
1651210284Sjmallett                    else
1652210284Sjmallett                        usbc_hcsplt.s.xactpos = 2; /* First part of payload */
1653210284Sjmallett                }
1654210284Sjmallett                else
1655210284Sjmallett                {
1656210284Sjmallett                    /* Continuing the previous data, we must either be
1657210284Sjmallett                        in the middle or at the end */
1658210284Sjmallett                    if (bytes_to_transfer <= 188)
1659210284Sjmallett                        usbc_hcsplt.s.xactpos = 1; /* End of payload */
1660210284Sjmallett                    else
1661210284Sjmallett                        usbc_hcsplt.s.xactpos = 0; /* Middle of payload */
1662210284Sjmallett                }
1663210284Sjmallett                /* Again, the transfer size is limited to 188 bytes */
1664210284Sjmallett                if (bytes_to_transfer > 188)
1665210284Sjmallett                    bytes_to_transfer = 188;
1666210284Sjmallett            }
1667210284Sjmallett        }
1668210284Sjmallett
1669210284Sjmallett        usbc_hctsiz.s.xfersize = bytes_to_transfer;
1670210284Sjmallett        usbc_hctsiz.s.pktcnt = (bytes_to_transfer + pipe->max_packet - 1) / pipe->max_packet;
1671210284Sjmallett        if (!usbc_hctsiz.s.pktcnt)
1672210284Sjmallett            usbc_hctsiz.s.pktcnt = 1;
1673210284Sjmallett
1674210284Sjmallett        /* Update the DATA0/DATA1 toggle */
1675210284Sjmallett        usbc_hctsiz.s.pid = __cvmx_usb_get_data_pid(pipe);
1676210284Sjmallett        /* High speed pipes may need a hardware ping before they start */
1677210284Sjmallett        if (pipe->flags & __CVMX_USB_PIPE_FLAGS_NEED_PING)
1678210284Sjmallett            usbc_hctsiz.s.dopng = 1;
1679210284Sjmallett
1680210284Sjmallett        __cvmx_usb_write_csr32(usb, CVMX_USBCX_HCSPLTX(channel, usb->index), usbc_hcsplt.u32);
1681210284Sjmallett        __cvmx_usb_write_csr32(usb, CVMX_USBCX_HCTSIZX(channel, usb->index), usbc_hctsiz.u32);
1682210284Sjmallett    }
1683210284Sjmallett
1684210284Sjmallett    /* Setup the Host Channel Characteristics Register */
1685210284Sjmallett    {
1686210284Sjmallett        cvmx_usbcx_hccharx_t usbc_hcchar = {.u32 = 0};
1687210284Sjmallett
1688210284Sjmallett        /* Make all transfers start on the next frame and not this one. This
1689210284Sjmallett            way the time we spend processing doesn't affect USB timing */
1690210284Sjmallett        usbc_hcchar.s.oddfrm = !(usbc_hfnum.s.frnum&1);
1691210284Sjmallett
1692210284Sjmallett        /* Set the number of back to back packets allowed by this endpoint.
1693210284Sjmallett            Split transactions interpret "ec" as the number of immediate
1694210284Sjmallett            retries of failure. These retries happen too quickly, so we
1695210284Sjmallett            disable these entirely for splits */
1696210284Sjmallett        if (__cvmx_usb_pipe_needs_split(usb, pipe))
1697210284Sjmallett            usbc_hcchar.s.ec = 1;
1698210284Sjmallett        else if (pipe->multi_count < 1)
1699210284Sjmallett            usbc_hcchar.s.ec = 1;
1700210284Sjmallett        else if (pipe->multi_count > 3)
1701210284Sjmallett            usbc_hcchar.s.ec = 3;
1702210284Sjmallett        else
1703210284Sjmallett            usbc_hcchar.s.ec = pipe->multi_count;
1704210284Sjmallett
1705210284Sjmallett        /* Set the rest of the endpoint specific settings */
1706210284Sjmallett        usbc_hcchar.s.devaddr = pipe->device_addr;
1707210284Sjmallett        usbc_hcchar.s.eptype = transaction->type;
1708210284Sjmallett        usbc_hcchar.s.lspddev = (pipe->device_speed == CVMX_USB_SPEED_LOW);
1709210284Sjmallett        usbc_hcchar.s.epdir = pipe->transfer_dir;
1710210284Sjmallett        usbc_hcchar.s.epnum = pipe->endpoint_num;
1711210284Sjmallett        usbc_hcchar.s.mps = pipe->max_packet;
1712210284Sjmallett        __cvmx_usb_write_csr32(usb, CVMX_USBCX_HCCHARX(channel, usb->index), usbc_hcchar.u32);
1713210284Sjmallett    }
1714210284Sjmallett
1715210284Sjmallett    /* Do transaction type specific fixups as needed */
1716210284Sjmallett    switch (transaction->type)
1717210284Sjmallett    {
1718210284Sjmallett        case CVMX_USB_TRANSFER_CONTROL:
1719210284Sjmallett            __cvmx_usb_start_channel_control(usb, channel, pipe);
1720210284Sjmallett            break;
1721210284Sjmallett        case CVMX_USB_TRANSFER_BULK:
1722210284Sjmallett        case CVMX_USB_TRANSFER_INTERRUPT:
1723210284Sjmallett            break;
1724210284Sjmallett        case CVMX_USB_TRANSFER_ISOCHRONOUS:
1725210284Sjmallett            if (!__cvmx_usb_pipe_needs_split(usb, pipe))
1726210284Sjmallett            {
1727210284Sjmallett                /* ISO transactions require differnet PIDs depending on direction
1728210284Sjmallett                    and how many packets are needed */
1729210284Sjmallett                if (pipe->transfer_dir == CVMX_USB_DIRECTION_OUT)
1730210284Sjmallett                {
1731210284Sjmallett                    if (pipe->multi_count < 2) /* Need DATA0 */
1732210284Sjmallett                        USB_SET_FIELD32(CVMX_USBCX_HCTSIZX(channel, usb->index), cvmx_usbcx_hctsizx_t, pid, 0);
1733210284Sjmallett                    else /* Need MDATA */
1734210284Sjmallett                        USB_SET_FIELD32(CVMX_USBCX_HCTSIZX(channel, usb->index), cvmx_usbcx_hctsizx_t, pid, 3);
1735210284Sjmallett                }
1736210284Sjmallett            }
1737210284Sjmallett            break;
1738210284Sjmallett    }
1739210284Sjmallett    {
1740210284Sjmallett        cvmx_usbcx_hctsizx_t usbc_hctsiz = {.u32 = __cvmx_usb_read_csr32(usb, CVMX_USBCX_HCTSIZX(channel, usb->index))};
1741210284Sjmallett        transaction->xfersize = usbc_hctsiz.s.xfersize;
1742210284Sjmallett        transaction->pktcnt = usbc_hctsiz.s.pktcnt;
1743210284Sjmallett    }
1744210284Sjmallett    USB_SET_FIELD32(CVMX_USBCX_HCCHARX(channel, usb->index), cvmx_usbcx_hccharx_t, chena, 1);
1745210284Sjmallett    CVMX_USB_RETURN_NOTHING();
1746210284Sjmallett}
1747210284Sjmallett
1748210284Sjmallett
1749210284Sjmallett/**
1750210284Sjmallett * @INTERNAL
1751210284Sjmallett * Find a pipe that is ready to be scheduled to hardware.
1752210284Sjmallett *
1753210284Sjmallett * @param list       Pipe list to search
1754210284Sjmallett * @param usbc_hfnum Current USB frame number
1755210284Sjmallett * @param current_cycle
1756210284Sjmallett *                   Cycle counter to use as a time reference.
1757210284Sjmallett *
1758210284Sjmallett * @return Pipe or NULL if none are ready
1759210284Sjmallett */
1760210284Sjmallettstatic cvmx_usb_pipe_t *__cvmx_usb_find_ready_pipe(cvmx_usb_pipe_list_t *list, cvmx_usbcx_hfnum_t usbc_hfnum, uint64_t current_cycle)
1761210284Sjmallett{
1762210284Sjmallett    cvmx_usb_pipe_t *pipe = list->head;
1763210284Sjmallett    while (pipe)
1764210284Sjmallett    {
1765210284Sjmallett        if (!(pipe->flags & __CVMX_USB_PIPE_FLAGS_SCHEDULED) && pipe->head &&
1766210284Sjmallett            (pipe->next_tx_cycle <= current_cycle) &&
1767210284Sjmallett            ((pipe->split_sc_frame == -1) || ((((int)usbc_hfnum.s.frnum - (int)pipe->split_sc_frame) & 0x7f) < 0x40)))
1768210284Sjmallett        {
1769210284Sjmallett            CVMX_PREFETCH(pipe, 128);
1770210284Sjmallett            CVMX_PREFETCH(pipe->head, 0);
1771210284Sjmallett            return pipe;
1772210284Sjmallett        }
1773210284Sjmallett        pipe = pipe->next;
1774210284Sjmallett    }
1775210284Sjmallett    return NULL;
1776210284Sjmallett}
1777210284Sjmallett
1778210284Sjmallett
1779210284Sjmallett/**
1780210284Sjmallett * @INTERNAL
1781210284Sjmallett * Called whenever a pipe might need to be scheduled to the
1782210284Sjmallett * hardware.
1783210284Sjmallett *
1784210284Sjmallett * @param usb    USB device state populated by
1785210284Sjmallett *               cvmx_usb_initialize().
1786210284Sjmallett * @param is_sof True if this schedule was called on a SOF interrupt.
1787210284Sjmallett */
1788210284Sjmallettstatic void __cvmx_usb_schedule(cvmx_usb_internal_state_t *usb, int is_sof)
1789210284Sjmallett{
1790210284Sjmallett    int channel;
1791210284Sjmallett    cvmx_usb_pipe_t *pipe;
1792210284Sjmallett    cvmx_usbcx_hfnum_t usbc_hfnum;
1793210284Sjmallett    uint64_t current_cycle = cvmx_read64_uint64(CVMX_IPD_CLK_COUNT);
1794210284Sjmallett
1795210284Sjmallett    CVMX_USB_LOG_CALLED();
1796210284Sjmallett    CVMX_USB_LOG_PARAM("%p", usb);
1797210284Sjmallett
1798210284Sjmallett    usbc_hfnum.u32 = __cvmx_usb_read_csr32(usb, CVMX_USBCX_HFNUM(usb->index));
1799210284Sjmallett
1800210284Sjmallett    while (usb->idle_hardware_channels)
1801210284Sjmallett    {
1802210284Sjmallett        /* Find an idle channel */
1803210284Sjmallett        CVMX_CLZ(channel, usb->idle_hardware_channels);
1804210284Sjmallett        channel = 31 - channel;
1805210284Sjmallett        if (cvmx_unlikely(channel > 7))
1806210284Sjmallett        {
1807210284Sjmallett            if (cvmx_unlikely(usb->init_flags & CVMX_USB_INITIALIZE_FLAGS_DEBUG_INFO))
1808210284Sjmallett                cvmx_dprintf("%s: Idle hardware channels has a channel higher than 7. This is wrong\n", __FUNCTION__);
1809210284Sjmallett            break;
1810210284Sjmallett        }
1811210284Sjmallett
1812210284Sjmallett        /* Find a pipe needing service */
1813210284Sjmallett        pipe = NULL;
1814210284Sjmallett        if (is_sof)
1815210284Sjmallett        {
1816210284Sjmallett            /* Only process periodic pipes on SOF interrupts. This way we are
1817210284Sjmallett                sure that the periodic data is sent in the beginning of the
1818210284Sjmallett                frame */
1819210284Sjmallett            pipe = __cvmx_usb_find_ready_pipe(usb->active_pipes + CVMX_USB_TRANSFER_ISOCHRONOUS, usbc_hfnum, current_cycle);
1820210284Sjmallett            if (cvmx_likely(!pipe))
1821210284Sjmallett                pipe = __cvmx_usb_find_ready_pipe(usb->active_pipes + CVMX_USB_TRANSFER_INTERRUPT, usbc_hfnum, current_cycle);
1822210284Sjmallett        }
1823210284Sjmallett        if (cvmx_likely(!pipe))
1824210284Sjmallett        {
1825210284Sjmallett            pipe = __cvmx_usb_find_ready_pipe(usb->active_pipes + CVMX_USB_TRANSFER_CONTROL, usbc_hfnum, current_cycle);
1826210284Sjmallett            if (cvmx_likely(!pipe))
1827210284Sjmallett                pipe = __cvmx_usb_find_ready_pipe(usb->active_pipes + CVMX_USB_TRANSFER_BULK, usbc_hfnum, current_cycle);
1828210284Sjmallett        }
1829210284Sjmallett        if (!pipe)
1830210284Sjmallett            break;
1831210284Sjmallett
1832210284Sjmallett        CVMX_USB_LOG_PARAM("%d", channel);
1833210284Sjmallett        CVMX_USB_LOG_PARAM("%p", pipe);
1834210284Sjmallett
1835210284Sjmallett        if (cvmx_unlikely((usb->init_flags & CVMX_USB_INITIALIZE_FLAGS_DEBUG_TRANSFERS) ||
1836210284Sjmallett            (pipe->flags & CVMX_USB_PIPE_FLAGS_DEBUG_TRANSFERS)))
1837210284Sjmallett        {
1838210284Sjmallett            cvmx_usb_transaction_t *transaction = pipe->head;
1839210284Sjmallett            const cvmx_usb_control_header_t *header = (transaction->control_header) ? cvmx_phys_to_ptr(transaction->control_header) : NULL;
1840210284Sjmallett            const char *dir = (pipe->transfer_dir == CVMX_USB_DIRECTION_IN) ? "IN" : "OUT";
1841210284Sjmallett            const char *type;
1842210284Sjmallett            switch (pipe->transfer_type)
1843210284Sjmallett            {
1844210284Sjmallett                case CVMX_USB_TRANSFER_CONTROL:
1845210284Sjmallett                    type = "SETUP";
1846210284Sjmallett                    dir = (header->s.request_type & 0x80) ? "IN" : "OUT";
1847210284Sjmallett                    break;
1848210284Sjmallett                case CVMX_USB_TRANSFER_ISOCHRONOUS:
1849210284Sjmallett                    type = "ISOCHRONOUS";
1850210284Sjmallett                    break;
1851210284Sjmallett                case CVMX_USB_TRANSFER_BULK:
1852210284Sjmallett                    type = "BULK";
1853210284Sjmallett                    break;
1854210284Sjmallett                default: /* CVMX_USB_TRANSFER_INTERRUPT */
1855210284Sjmallett                    type = "INTERRUPT";
1856210284Sjmallett                    break;
1857210284Sjmallett            }
1858210284Sjmallett            cvmx_dprintf("%s: Starting pipe %d, transaction %d on channel %d. %s %s len=%d header=0x%llx\n",
1859210284Sjmallett                         __FUNCTION__, __cvmx_usb_get_pipe_handle(usb, pipe),
1860210284Sjmallett                         __cvmx_usb_get_submit_handle(usb, transaction),
1861210284Sjmallett                         channel, type, dir,
1862210284Sjmallett                         transaction->buffer_length,
1863210284Sjmallett                         (header) ? (unsigned long long)header->u64 : 0ull);
1864210284Sjmallett        }
1865210284Sjmallett        __cvmx_usb_start_channel(usb, channel, pipe);
1866210284Sjmallett    }
1867210284Sjmallett    CVMX_USB_RETURN_NOTHING();
1868210284Sjmallett}
1869210284Sjmallett
1870210284Sjmallett
1871210284Sjmallett/**
1872210284Sjmallett * @INTERNAL
1873210284Sjmallett * Call a user's callback for a specific reason.
1874210284Sjmallett *
1875210284Sjmallett * @param usb    USB device state populated by
1876210284Sjmallett *               cvmx_usb_initialize().
1877210284Sjmallett * @param pipe   Pipe the callback is for or NULL
1878210284Sjmallett * @param transaction
1879210284Sjmallett *               Transaction the callback is for or NULL
1880210284Sjmallett * @param reason Reason this callback is being called
1881210284Sjmallett * @param complete_code
1882210284Sjmallett *               Completion code for the transaction, if any
1883210284Sjmallett */
1884210284Sjmallettstatic void __cvmx_usb_perform_callback(cvmx_usb_internal_state_t *usb,
1885210284Sjmallett                                        cvmx_usb_pipe_t *pipe,
1886210284Sjmallett                                        cvmx_usb_transaction_t *transaction,
1887210284Sjmallett                                        cvmx_usb_callback_t reason,
1888210284Sjmallett                                        cvmx_usb_complete_t complete_code)
1889210284Sjmallett{
1890210284Sjmallett    cvmx_usb_callback_func_t callback = usb->callback[reason];
1891210284Sjmallett    void *user_data = usb->callback_data[reason];
1892210284Sjmallett    int submit_handle = -1;
1893210284Sjmallett    int pipe_handle = -1;
1894210284Sjmallett    int bytes_transferred = 0;
1895210284Sjmallett
1896210284Sjmallett    if (pipe)
1897210284Sjmallett        pipe_handle = __cvmx_usb_get_pipe_handle(usb, pipe);
1898210284Sjmallett
1899210284Sjmallett    if (transaction)
1900210284Sjmallett    {
1901210284Sjmallett        submit_handle = __cvmx_usb_get_submit_handle(usb, transaction);
1902210284Sjmallett        bytes_transferred = transaction->actual_bytes;
1903210284Sjmallett        /* Transactions are allowed to override the default callback */
1904210284Sjmallett        if ((reason == CVMX_USB_CALLBACK_TRANSFER_COMPLETE) && transaction->callback)
1905210284Sjmallett        {
1906210284Sjmallett            callback = transaction->callback;
1907210284Sjmallett            user_data = transaction->callback_data;
1908210284Sjmallett        }
1909210284Sjmallett    }
1910210284Sjmallett
1911210284Sjmallett    if (!callback)
1912210284Sjmallett        return;
1913210284Sjmallett
1914210284Sjmallett    if (cvmx_unlikely(usb->init_flags & CVMX_USB_INITIALIZE_FLAGS_DEBUG_CALLBACKS))
1915210284Sjmallett        cvmx_dprintf("%*s%s: calling callback %p(usb=%p, complete_code=%s, "
1916210284Sjmallett                     "pipe_handle=%d, submit_handle=%d, bytes_transferred=%d, user_data=%p);\n",
1917210284Sjmallett                     2*usb->indent, "", __FUNCTION__, callback, usb,
1918210284Sjmallett                     __cvmx_usb_complete_to_string(complete_code),
1919210284Sjmallett                     pipe_handle, submit_handle, bytes_transferred, user_data);
1920210284Sjmallett
1921210284Sjmallett    callback((cvmx_usb_state_t *)usb, reason, complete_code, pipe_handle, submit_handle,
1922210284Sjmallett             bytes_transferred, user_data);
1923210284Sjmallett
1924210284Sjmallett    if (cvmx_unlikely(usb->init_flags & CVMX_USB_INITIALIZE_FLAGS_DEBUG_CALLBACKS))
1925210284Sjmallett        cvmx_dprintf("%*s%s: callback %p complete\n", 2*usb->indent, "",
1926210284Sjmallett                      __FUNCTION__, callback);
1927210284Sjmallett}
1928210284Sjmallett
1929210284Sjmallett
1930210284Sjmallett/**
1931210284Sjmallett * @INTERNAL
1932210284Sjmallett * Signal the completion of a transaction and free it. The
1933210284Sjmallett * transaction will be removed from the pipe transaction list.
1934210284Sjmallett *
1935210284Sjmallett * @param usb    USB device state populated by
1936210284Sjmallett *               cvmx_usb_initialize().
1937210284Sjmallett * @param pipe   Pipe the transaction is on
1938210284Sjmallett * @param transaction
1939210284Sjmallett *               Transaction that completed
1940210284Sjmallett * @param complete_code
1941210284Sjmallett *               Completion code
1942210284Sjmallett */
1943210284Sjmallettstatic void __cvmx_usb_perform_complete(cvmx_usb_internal_state_t * usb,
1944210284Sjmallett                                        cvmx_usb_pipe_t *pipe,
1945210284Sjmallett                                        cvmx_usb_transaction_t *transaction,
1946210284Sjmallett                                        cvmx_usb_complete_t complete_code)
1947210284Sjmallett{
1948210284Sjmallett    CVMX_USB_LOG_CALLED();
1949210284Sjmallett    CVMX_USB_LOG_PARAM("%p", usb);
1950210284Sjmallett    CVMX_USB_LOG_PARAM("%p", pipe);
1951210284Sjmallett    CVMX_USB_LOG_PARAM("%p", transaction);
1952210284Sjmallett    CVMX_USB_LOG_PARAM("%d", complete_code);
1953210284Sjmallett
1954210284Sjmallett    /* Isochronous transactions need extra processing as they might not be done
1955210284Sjmallett        after a single data transfer */
1956210284Sjmallett    if (cvmx_unlikely(transaction->type == CVMX_USB_TRANSFER_ISOCHRONOUS))
1957210284Sjmallett    {
1958210284Sjmallett        /* Update the number of bytes transfered in this ISO packet */
1959210284Sjmallett        transaction->iso_packets[0].length = transaction->actual_bytes;
1960210284Sjmallett        transaction->iso_packets[0].status = complete_code;
1961210284Sjmallett
1962210284Sjmallett        /* If there are more ISOs pending and we suceeded, schedule the next
1963210284Sjmallett            one */
1964210284Sjmallett        if ((transaction->iso_number_packets > 1) && (complete_code == CVMX_USB_COMPLETE_SUCCESS))
1965210284Sjmallett        {
1966210284Sjmallett            transaction->actual_bytes = 0;      /* No bytes transfered for this packet as of yet */
1967210284Sjmallett            transaction->iso_number_packets--;  /* One less ISO waiting to transfer */
1968210284Sjmallett            transaction->iso_packets++;         /* Increment to the next location in our packet array */
1969210284Sjmallett            transaction->stage = CVMX_USB_STAGE_NON_CONTROL;
1970210284Sjmallett            goto done;
1971210284Sjmallett        }
1972210284Sjmallett    }
1973210284Sjmallett
1974210284Sjmallett    /* Remove the transaction from the pipe list */
1975210284Sjmallett    if (transaction->next)
1976210284Sjmallett        transaction->next->prev = transaction->prev;
1977210284Sjmallett    else
1978210284Sjmallett        pipe->tail = transaction->prev;
1979210284Sjmallett    if (transaction->prev)
1980210284Sjmallett        transaction->prev->next = transaction->next;
1981210284Sjmallett    else
1982210284Sjmallett        pipe->head = transaction->next;
1983210284Sjmallett    if (!pipe->head)
1984210284Sjmallett    {
1985210284Sjmallett        __cvmx_usb_remove_pipe(usb->active_pipes + pipe->transfer_type, pipe);
1986210284Sjmallett        __cvmx_usb_append_pipe(&usb->idle_pipes, pipe);
1987210284Sjmallett
1988210284Sjmallett    }
1989210284Sjmallett    __cvmx_usb_perform_callback(usb, pipe, transaction,
1990210284Sjmallett                                CVMX_USB_CALLBACK_TRANSFER_COMPLETE,
1991210284Sjmallett                                complete_code);
1992210284Sjmallett    __cvmx_usb_free_transaction(usb, transaction);
1993210284Sjmallett    /* Disable SOF interrupts if we don't have any pending transactions */
1994210284Sjmallett    usb->active_transactions--;
1995210284Sjmallett    if (usb->active_transactions == 0)
1996210284Sjmallett        USB_SET_FIELD32(CVMX_USBCX_GINTMSK(usb->index), cvmx_usbcx_gintmsk_t, sofmsk, 0);
1997210284Sjmallettdone:
1998210284Sjmallett    CVMX_USB_RETURN_NOTHING();
1999210284Sjmallett}
2000210284Sjmallett
2001210284Sjmallett
2002210284Sjmallett/**
2003210284Sjmallett * @INTERNAL
2004210284Sjmallett * Submit a usb transaction to a pipe. Called for all types
2005210284Sjmallett * of transactions.
2006210284Sjmallett *
2007210284Sjmallett * @param usb
2008210284Sjmallett * @param pipe_handle
2009210284Sjmallett *                  Which pipe to submit to. Will be validated in this function.
2010210284Sjmallett * @param type      Transaction type
2011210284Sjmallett * @param flags     Flags for the transaction
2012210284Sjmallett * @param buffer    User buffer for the transaction
2013210284Sjmallett * @param buffer_length
2014210284Sjmallett *                  User buffer's length in bytes
2015210284Sjmallett * @param control_header
2016210284Sjmallett *                  For control transactions, the 8 byte standard header
2017210284Sjmallett * @param iso_start_frame
2018210284Sjmallett *                  For ISO transactiosn, the start frame
2019210284Sjmallett * @param iso_number_packets
2020210284Sjmallett *                  For ISO, the number of packet in the transaction.
2021210284Sjmallett * @param iso_packets
2022210284Sjmallett *                  A description of each ISO packet
2023210284Sjmallett * @param callback  User callback to call when the transaction completes
2024210284Sjmallett * @param user_data User's data for the callback
2025210284Sjmallett *
2026210284Sjmallett * @return Submit handle or negative on failure. Matches the result
2027210284Sjmallett *         in the external API.
2028210284Sjmallett */
2029210284Sjmallettstatic int __cvmx_usb_submit_transaction(cvmx_usb_internal_state_t *usb,
2030210284Sjmallett                                         int pipe_handle,
2031210284Sjmallett                                         cvmx_usb_transfer_t type,
2032210284Sjmallett                                         int flags,
2033210284Sjmallett                                         uint64_t buffer,
2034210284Sjmallett                                         int buffer_length,
2035210284Sjmallett                                         uint64_t control_header,
2036210284Sjmallett                                         int iso_start_frame,
2037210284Sjmallett                                         int iso_number_packets,
2038210284Sjmallett                                         cvmx_usb_iso_packet_t *iso_packets,
2039210284Sjmallett                                         cvmx_usb_callback_func_t callback,
2040210284Sjmallett                                         void *user_data)
2041210284Sjmallett{
2042210284Sjmallett    int submit_handle;
2043210284Sjmallett    cvmx_usb_transaction_t *transaction;
2044210284Sjmallett    cvmx_usb_pipe_t *pipe = usb->pipe + pipe_handle;
2045210284Sjmallett
2046210284Sjmallett    CVMX_USB_LOG_CALLED();
2047210284Sjmallett    if (cvmx_unlikely((pipe_handle < 0) || (pipe_handle >= MAX_PIPES)))
2048210284Sjmallett        CVMX_USB_RETURN(CVMX_USB_INVALID_PARAM);
2049210284Sjmallett    /* Fail if the pipe isn't open */
2050210284Sjmallett    if (cvmx_unlikely((pipe->flags & __CVMX_USB_PIPE_FLAGS_OPEN) == 0))
2051210284Sjmallett        CVMX_USB_RETURN(CVMX_USB_INVALID_PARAM);
2052210284Sjmallett    if (cvmx_unlikely(pipe->transfer_type != type))
2053210284Sjmallett        CVMX_USB_RETURN(CVMX_USB_INVALID_PARAM);
2054210284Sjmallett
2055210284Sjmallett    transaction = __cvmx_usb_alloc_transaction(usb);
2056210284Sjmallett    if (cvmx_unlikely(!transaction))
2057210284Sjmallett        CVMX_USB_RETURN(CVMX_USB_NO_MEMORY);
2058210284Sjmallett
2059210284Sjmallett    /* Enable SOF interrupts now that we have pending transactions */
2060210284Sjmallett    if (usb->active_transactions == 0)
2061210284Sjmallett        USB_SET_FIELD32(CVMX_USBCX_GINTMSK(usb->index), cvmx_usbcx_gintmsk_t, sofmsk, 1);
2062210284Sjmallett    usb->active_transactions++;
2063210284Sjmallett
2064210284Sjmallett    transaction->type = type;
2065210284Sjmallett    transaction->flags |= flags;
2066210284Sjmallett    transaction->buffer = buffer;
2067210284Sjmallett    transaction->buffer_length = buffer_length;
2068210284Sjmallett    transaction->control_header = control_header;
2069210284Sjmallett    transaction->iso_start_frame = iso_start_frame; // FIXME: This is not used, implement it
2070210284Sjmallett    transaction->iso_number_packets = iso_number_packets;
2071210284Sjmallett    transaction->iso_packets = iso_packets;
2072210284Sjmallett    transaction->callback = callback;
2073210284Sjmallett    transaction->callback_data = user_data;
2074210284Sjmallett    if (transaction->type == CVMX_USB_TRANSFER_CONTROL)
2075210284Sjmallett        transaction->stage = CVMX_USB_STAGE_SETUP;
2076210284Sjmallett    else
2077210284Sjmallett        transaction->stage = CVMX_USB_STAGE_NON_CONTROL;
2078210284Sjmallett
2079210284Sjmallett    transaction->next = NULL;
2080210284Sjmallett    if (pipe->tail)
2081210284Sjmallett    {
2082210284Sjmallett        transaction->prev = pipe->tail;
2083210284Sjmallett        transaction->prev->next = transaction;
2084210284Sjmallett    }
2085210284Sjmallett    else
2086210284Sjmallett    {
2087210284Sjmallett        transaction->prev = NULL;
2088210284Sjmallett        pipe->head = transaction;
2089210284Sjmallett        __cvmx_usb_remove_pipe(&usb->idle_pipes, pipe);
2090210284Sjmallett        __cvmx_usb_append_pipe(usb->active_pipes + pipe->transfer_type, pipe);
2091210284Sjmallett    }
2092210284Sjmallett    pipe->tail = transaction;
2093210284Sjmallett
2094210284Sjmallett    submit_handle = __cvmx_usb_get_submit_handle(usb, transaction);
2095210284Sjmallett
2096210284Sjmallett    /* We may need to schedule the pipe if this was the head of the pipe */
2097210284Sjmallett    if (!transaction->prev)
2098210284Sjmallett        __cvmx_usb_schedule(usb, 0);
2099210284Sjmallett
2100210284Sjmallett    CVMX_USB_RETURN(submit_handle);
2101210284Sjmallett}
2102210284Sjmallett
2103210284Sjmallett
2104210284Sjmallett/**
2105210284Sjmallett * Call to submit a USB Bulk transfer to a pipe.
2106210284Sjmallett *
2107210284Sjmallett * @param state     USB device state populated by
2108210284Sjmallett *                  cvmx_usb_initialize().
2109210284Sjmallett * @param pipe_handle
2110210284Sjmallett *                  Handle to the pipe for the transfer.
2111210284Sjmallett * @param buffer    Physical address of the data buffer in
2112210284Sjmallett *                  memory. Note that this is NOT A POINTER, but
2113210284Sjmallett *                  the full 64bit physical address of the
2114210284Sjmallett *                  buffer. This may be zero if buffer_length is
2115210284Sjmallett *                  zero.
2116210284Sjmallett * @param buffer_length
2117210284Sjmallett *                  Length of buffer in bytes.
2118210284Sjmallett * @param callback  Function to call when this transaction
2119210284Sjmallett *                  completes. If the return value of this
2120210284Sjmallett *                  function isn't an error, then this function
2121210284Sjmallett *                  is guaranteed to be called when the
2122210284Sjmallett *                  transaction completes. If this parameter is
2123210284Sjmallett *                  NULL, then the generic callback registered
2124210284Sjmallett *                  through cvmx_usb_register_callback is
2125210284Sjmallett *                  called. If both are NULL, then there is no
2126210284Sjmallett *                  way to know when a transaction completes.
2127210284Sjmallett * @param user_data User supplied data returned when the
2128210284Sjmallett *                  callback is called. This is only used if
2129210284Sjmallett *                  callback in not NULL.
2130210284Sjmallett *
2131210284Sjmallett * @return A submitted transaction handle or negative on
2132210284Sjmallett *         failure. Negative values are failure codes from
2133210284Sjmallett *         cvmx_usb_status_t.
2134210284Sjmallett */
2135210284Sjmallettint cvmx_usb_submit_bulk(cvmx_usb_state_t *state, int pipe_handle,
2136210284Sjmallett                                uint64_t buffer, int buffer_length,
2137210284Sjmallett                                cvmx_usb_callback_func_t callback,
2138210284Sjmallett                                void *user_data)
2139210284Sjmallett{
2140210284Sjmallett    int submit_handle;
2141210284Sjmallett    cvmx_usb_internal_state_t *usb = (cvmx_usb_internal_state_t*)state;
2142210284Sjmallett
2143210284Sjmallett    CVMX_USB_LOG_CALLED();
2144210284Sjmallett    CVMX_USB_LOG_PARAM("%p", state);
2145210284Sjmallett    CVMX_USB_LOG_PARAM("%d", pipe_handle);
2146210284Sjmallett    CVMX_USB_LOG_PARAM("0x%llx", (unsigned long long)buffer);
2147210284Sjmallett    CVMX_USB_LOG_PARAM("%d", buffer_length);
2148210284Sjmallett
2149210284Sjmallett    /* Pipe handle checking is done later in a common place */
2150210284Sjmallett    if (cvmx_unlikely(!buffer))
2151210284Sjmallett        CVMX_USB_RETURN(CVMX_USB_INVALID_PARAM);
2152210284Sjmallett    if (cvmx_unlikely(buffer_length < 0))
2153210284Sjmallett        CVMX_USB_RETURN(CVMX_USB_INVALID_PARAM);
2154210284Sjmallett    if (cvmx_unlikely(usb->init_flags & CVMX_USB_INITIALIZE_FLAGS_DEVICE_MODE))
2155210284Sjmallett        CVMX_USB_RETURN(CVMX_USB_INCORRECT_MODE);
2156210284Sjmallett
2157210284Sjmallett    submit_handle = __cvmx_usb_submit_transaction(usb, pipe_handle,
2158210284Sjmallett                                         CVMX_USB_TRANSFER_BULK,
2159210284Sjmallett                                         0, /* flags */
2160210284Sjmallett                                         buffer,
2161210284Sjmallett                                         buffer_length,
2162210284Sjmallett                                         0, /* control_header */
2163210284Sjmallett                                         0, /* iso_start_frame */
2164210284Sjmallett                                         0, /* iso_number_packets */
2165210284Sjmallett                                         NULL, /* iso_packets */
2166210284Sjmallett                                         callback,
2167210284Sjmallett                                         user_data);
2168210284Sjmallett    CVMX_USB_RETURN(submit_handle);
2169210284Sjmallett}
2170210284Sjmallett
2171210284Sjmallett
2172210284Sjmallett/**
2173210284Sjmallett * Call to submit a USB Interrupt transfer to a pipe.
2174210284Sjmallett *
2175210284Sjmallett * @param state     USB device state populated by
2176210284Sjmallett *                  cvmx_usb_initialize().
2177210284Sjmallett * @param pipe_handle
2178210284Sjmallett *                  Handle to the pipe for the transfer.
2179210284Sjmallett * @param buffer    Physical address of the data buffer in
2180210284Sjmallett *                  memory. Note that this is NOT A POINTER, but
2181210284Sjmallett *                  the full 64bit physical address of the
2182210284Sjmallett *                  buffer. This may be zero if buffer_length is
2183210284Sjmallett *                  zero.
2184210284Sjmallett * @param buffer_length
2185210284Sjmallett *                  Length of buffer in bytes.
2186210284Sjmallett * @param callback  Function to call when this transaction
2187210284Sjmallett *                  completes. If the return value of this
2188210284Sjmallett *                  function isn't an error, then this function
2189210284Sjmallett *                  is guaranteed to be called when the
2190210284Sjmallett *                  transaction completes. If this parameter is
2191210284Sjmallett *                  NULL, then the generic callback registered
2192210284Sjmallett *                  through cvmx_usb_register_callback is
2193210284Sjmallett *                  called. If both are NULL, then there is no
2194210284Sjmallett *                  way to know when a transaction completes.
2195210284Sjmallett * @param user_data User supplied data returned when the
2196210284Sjmallett *                  callback is called. This is only used if
2197210284Sjmallett *                  callback in not NULL.
2198210284Sjmallett *
2199210284Sjmallett * @return A submitted transaction handle or negative on
2200210284Sjmallett *         failure. Negative values are failure codes from
2201210284Sjmallett *         cvmx_usb_status_t.
2202210284Sjmallett */
2203210284Sjmallettint cvmx_usb_submit_interrupt(cvmx_usb_state_t *state, int pipe_handle,
2204210284Sjmallett                              uint64_t buffer, int buffer_length,
2205210284Sjmallett                              cvmx_usb_callback_func_t callback,
2206210284Sjmallett                              void *user_data)
2207210284Sjmallett{
2208210284Sjmallett    int submit_handle;
2209210284Sjmallett    cvmx_usb_internal_state_t *usb = (cvmx_usb_internal_state_t*)state;
2210210284Sjmallett
2211210284Sjmallett    CVMX_USB_LOG_CALLED();
2212210284Sjmallett    CVMX_USB_LOG_PARAM("%p", state);
2213210284Sjmallett    CVMX_USB_LOG_PARAM("%d", pipe_handle);
2214210284Sjmallett    CVMX_USB_LOG_PARAM("0x%llx", (unsigned long long)buffer);
2215210284Sjmallett    CVMX_USB_LOG_PARAM("%d", buffer_length);
2216210284Sjmallett
2217210284Sjmallett    /* Pipe handle checking is done later in a common place */
2218210284Sjmallett    if (cvmx_unlikely(!buffer))
2219210284Sjmallett        CVMX_USB_RETURN(CVMX_USB_INVALID_PARAM);
2220210284Sjmallett    if (cvmx_unlikely(buffer_length < 0))
2221210284Sjmallett        CVMX_USB_RETURN(CVMX_USB_INVALID_PARAM);
2222210284Sjmallett    if (cvmx_unlikely(usb->init_flags & CVMX_USB_INITIALIZE_FLAGS_DEVICE_MODE))
2223210284Sjmallett        CVMX_USB_RETURN(CVMX_USB_INCORRECT_MODE);
2224210284Sjmallett
2225210284Sjmallett    submit_handle = __cvmx_usb_submit_transaction(usb, pipe_handle,
2226210284Sjmallett                                         CVMX_USB_TRANSFER_INTERRUPT,
2227210284Sjmallett                                         0, /* flags */
2228210284Sjmallett                                         buffer,
2229210284Sjmallett                                         buffer_length,
2230210284Sjmallett                                         0, /* control_header */
2231210284Sjmallett                                         0, /* iso_start_frame */
2232210284Sjmallett                                         0, /* iso_number_packets */
2233210284Sjmallett                                         NULL, /* iso_packets */
2234210284Sjmallett                                         callback,
2235210284Sjmallett                                         user_data);
2236210284Sjmallett    CVMX_USB_RETURN(submit_handle);
2237210284Sjmallett}
2238210284Sjmallett
2239210284Sjmallett
2240210284Sjmallett/**
2241210284Sjmallett * Call to submit a USB Control transfer to a pipe.
2242210284Sjmallett *
2243210284Sjmallett * @param state     USB device state populated by
2244210284Sjmallett *                  cvmx_usb_initialize().
2245210284Sjmallett * @param pipe_handle
2246210284Sjmallett *                  Handle to the pipe for the transfer.
2247210284Sjmallett * @param control_header
2248210284Sjmallett *                  USB 8 byte control header physical address.
2249210284Sjmallett *                  Note that this is NOT A POINTER, but the
2250210284Sjmallett *                  full 64bit physical address of the buffer.
2251210284Sjmallett * @param buffer    Physical address of the data buffer in
2252210284Sjmallett *                  memory. Note that this is NOT A POINTER, but
2253210284Sjmallett *                  the full 64bit physical address of the
2254210284Sjmallett *                  buffer. This may be zero if buffer_length is
2255210284Sjmallett *                  zero.
2256210284Sjmallett * @param buffer_length
2257210284Sjmallett *                  Length of buffer in bytes.
2258210284Sjmallett * @param callback  Function to call when this transaction
2259210284Sjmallett *                  completes. If the return value of this
2260210284Sjmallett *                  function isn't an error, then this function
2261210284Sjmallett *                  is guaranteed to be called when the
2262210284Sjmallett *                  transaction completes. If this parameter is
2263210284Sjmallett *                  NULL, then the generic callback registered
2264210284Sjmallett *                  through cvmx_usb_register_callback is
2265210284Sjmallett *                  called. If both are NULL, then there is no
2266210284Sjmallett *                  way to know when a transaction completes.
2267210284Sjmallett * @param user_data User supplied data returned when the
2268210284Sjmallett *                  callback is called. This is only used if
2269210284Sjmallett *                  callback in not NULL.
2270210284Sjmallett *
2271210284Sjmallett * @return A submitted transaction handle or negative on
2272210284Sjmallett *         failure. Negative values are failure codes from
2273210284Sjmallett *         cvmx_usb_status_t.
2274210284Sjmallett */
2275210284Sjmallettint cvmx_usb_submit_control(cvmx_usb_state_t *state, int pipe_handle,
2276210284Sjmallett                            uint64_t control_header,
2277210284Sjmallett                            uint64_t buffer, int buffer_length,
2278210284Sjmallett                            cvmx_usb_callback_func_t callback,
2279210284Sjmallett                            void *user_data)
2280210284Sjmallett{
2281210284Sjmallett    int submit_handle;
2282210284Sjmallett    cvmx_usb_internal_state_t *usb = (cvmx_usb_internal_state_t*)state;
2283210284Sjmallett    cvmx_usb_control_header_t *header = cvmx_phys_to_ptr(control_header);
2284210284Sjmallett
2285210284Sjmallett    CVMX_USB_LOG_CALLED();
2286210284Sjmallett    CVMX_USB_LOG_PARAM("%p", state);
2287210284Sjmallett    CVMX_USB_LOG_PARAM("%d", pipe_handle);
2288210284Sjmallett    CVMX_USB_LOG_PARAM("0x%llx", (unsigned long long)control_header);
2289210284Sjmallett    CVMX_USB_LOG_PARAM("0x%llx", (unsigned long long)buffer);
2290210284Sjmallett    CVMX_USB_LOG_PARAM("%d", buffer_length);
2291210284Sjmallett
2292210284Sjmallett    /* Pipe handle checking is done later in a common place */
2293210284Sjmallett    if (cvmx_unlikely(!control_header))
2294210284Sjmallett        CVMX_USB_RETURN(CVMX_USB_INVALID_PARAM);
2295210284Sjmallett    /* Some drivers send a buffer with a zero length. God only knows why */
2296210284Sjmallett    if (cvmx_unlikely(buffer && (buffer_length < 0)))
2297210284Sjmallett        CVMX_USB_RETURN(CVMX_USB_INVALID_PARAM);
2298210284Sjmallett    if (cvmx_unlikely(!buffer && (buffer_length != 0)))
2299210284Sjmallett        CVMX_USB_RETURN(CVMX_USB_INVALID_PARAM);
2300210284Sjmallett    if (cvmx_unlikely(usb->init_flags & CVMX_USB_INITIALIZE_FLAGS_DEVICE_MODE))
2301210284Sjmallett        CVMX_USB_RETURN(CVMX_USB_INCORRECT_MODE);
2302210284Sjmallett    if ((header->s.request_type & 0x80) == 0)
2303210284Sjmallett        buffer_length = cvmx_le16_to_cpu(header->s.length);
2304210284Sjmallett
2305210284Sjmallett    submit_handle = __cvmx_usb_submit_transaction(usb, pipe_handle,
2306210284Sjmallett                                         CVMX_USB_TRANSFER_CONTROL,
2307210284Sjmallett                                         0, /* flags */
2308210284Sjmallett                                         buffer,
2309210284Sjmallett                                         buffer_length,
2310210284Sjmallett                                         control_header,
2311210284Sjmallett                                         0, /* iso_start_frame */
2312210284Sjmallett                                         0, /* iso_number_packets */
2313210284Sjmallett                                         NULL, /* iso_packets */
2314210284Sjmallett                                         callback,
2315210284Sjmallett                                         user_data);
2316210284Sjmallett    CVMX_USB_RETURN(submit_handle);
2317210284Sjmallett}
2318210284Sjmallett
2319210284Sjmallett
2320210284Sjmallett/**
2321210284Sjmallett * Call to submit a USB Isochronous transfer to a pipe.
2322210284Sjmallett *
2323210284Sjmallett * @param state     USB device state populated by
2324210284Sjmallett *                  cvmx_usb_initialize().
2325210284Sjmallett * @param pipe_handle
2326210284Sjmallett *                  Handle to the pipe for the transfer.
2327210284Sjmallett * @param start_frame
2328210284Sjmallett *                  Number of frames into the future to schedule
2329210284Sjmallett *                  this transaction.
2330210284Sjmallett * @param flags     Flags to control the transfer. See
2331210284Sjmallett *                  cvmx_usb_isochronous_flags_t for the flag
2332210284Sjmallett *                  definitions.
2333210284Sjmallett * @param number_packets
2334210284Sjmallett *                  Number of sequential packets to transfer.
2335210284Sjmallett *                  "packets" is a pointer to an array of this
2336210284Sjmallett *                  many packet structures.
2337210284Sjmallett * @param packets   Description of each transfer packet as
2338210284Sjmallett *                  defined by cvmx_usb_iso_packet_t. The array
2339210284Sjmallett *                  pointed to here must stay valid until the
2340210284Sjmallett *                  complete callback is called.
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_isochronous(cvmx_usb_state_t *state, int pipe_handle,
2366210284Sjmallett                                int start_frame, int flags,
2367210284Sjmallett                                int number_packets,
2368210284Sjmallett                                cvmx_usb_iso_packet_t packets[],
2369210284Sjmallett                                uint64_t buffer, int buffer_length,
2370210284Sjmallett                                cvmx_usb_callback_func_t callback,
2371210284Sjmallett                                void *user_data)
2372210284Sjmallett{
2373210284Sjmallett    int submit_handle;
2374210284Sjmallett    cvmx_usb_internal_state_t *usb = (cvmx_usb_internal_state_t*)state;
2375210284Sjmallett
2376210284Sjmallett    CVMX_USB_LOG_CALLED();
2377210284Sjmallett    CVMX_USB_LOG_PARAM("%p", state);
2378210284Sjmallett    CVMX_USB_LOG_PARAM("%d", pipe_handle);
2379210284Sjmallett    CVMX_USB_LOG_PARAM("%d", start_frame);
2380210284Sjmallett    CVMX_USB_LOG_PARAM("0x%x", flags);
2381210284Sjmallett    CVMX_USB_LOG_PARAM("%d", number_packets);
2382210284Sjmallett    CVMX_USB_LOG_PARAM("%p", packets);
2383210284Sjmallett    CVMX_USB_LOG_PARAM("0x%llx", (unsigned long long)buffer);
2384210284Sjmallett    CVMX_USB_LOG_PARAM("%d", buffer_length);
2385210284Sjmallett
2386210284Sjmallett    /* Pipe handle checking is done later in a common place */
2387210284Sjmallett    if (cvmx_unlikely(start_frame < 0))
2388210284Sjmallett        CVMX_USB_RETURN(CVMX_USB_INVALID_PARAM);
2389210284Sjmallett    if (cvmx_unlikely(flags & ~(CVMX_USB_ISOCHRONOUS_FLAGS_ALLOW_SHORT | CVMX_USB_ISOCHRONOUS_FLAGS_ASAP)))
2390210284Sjmallett        CVMX_USB_RETURN(CVMX_USB_INVALID_PARAM);
2391210284Sjmallett    if (cvmx_unlikely(number_packets < 1))
2392210284Sjmallett        CVMX_USB_RETURN(CVMX_USB_INVALID_PARAM);
2393210284Sjmallett    if (cvmx_unlikely(!packets))
2394210284Sjmallett        CVMX_USB_RETURN(CVMX_USB_INVALID_PARAM);
2395210284Sjmallett    if (cvmx_unlikely(!buffer))
2396210284Sjmallett        CVMX_USB_RETURN(CVMX_USB_INVALID_PARAM);
2397210284Sjmallett    if (cvmx_unlikely(buffer_length < 0))
2398210284Sjmallett        CVMX_USB_RETURN(CVMX_USB_INVALID_PARAM);
2399210284Sjmallett    if (cvmx_unlikely(usb->init_flags & CVMX_USB_INITIALIZE_FLAGS_DEVICE_MODE))
2400210284Sjmallett        CVMX_USB_RETURN(CVMX_USB_INCORRECT_MODE);
2401210284Sjmallett
2402210284Sjmallett    submit_handle = __cvmx_usb_submit_transaction(usb, pipe_handle,
2403210284Sjmallett                                         CVMX_USB_TRANSFER_ISOCHRONOUS,
2404210284Sjmallett                                         flags,
2405210284Sjmallett                                         buffer,
2406210284Sjmallett                                         buffer_length,
2407210284Sjmallett                                         0, /* control_header */
2408210284Sjmallett                                         start_frame,
2409210284Sjmallett                                         number_packets,
2410210284Sjmallett                                         packets,
2411210284Sjmallett                                         callback,
2412210284Sjmallett                                         user_data);
2413210284Sjmallett    CVMX_USB_RETURN(submit_handle);
2414210284Sjmallett}
2415210284Sjmallett
2416210284Sjmallett
2417210284Sjmallett/**
2418210284Sjmallett * Cancel one outstanding request in a pipe. Canceling a request
2419210284Sjmallett * can fail if the transaction has already completed before cancel
2420210284Sjmallett * is called. Even after a successful cancel call, it may take
2421210284Sjmallett * a frame or two for the cvmx_usb_poll() function to call the
2422210284Sjmallett * associated callback.
2423210284Sjmallett *
2424210284Sjmallett * @param state  USB device state populated by
2425210284Sjmallett *               cvmx_usb_initialize().
2426210284Sjmallett * @param pipe_handle
2427210284Sjmallett *               Pipe handle to cancel requests in.
2428210284Sjmallett * @param submit_handle
2429210284Sjmallett *               Handle to transaction to cancel, returned by the submit function.
2430210284Sjmallett *
2431210284Sjmallett * @return CVMX_USB_SUCCESS or a negative error code defined in
2432210284Sjmallett *         cvmx_usb_status_t.
2433210284Sjmallett */
2434210284Sjmallettcvmx_usb_status_t cvmx_usb_cancel(cvmx_usb_state_t *state, int pipe_handle,
2435210284Sjmallett                                  int submit_handle)
2436210284Sjmallett{
2437210284Sjmallett    cvmx_usb_transaction_t *transaction;
2438210284Sjmallett    cvmx_usb_internal_state_t *usb = (cvmx_usb_internal_state_t*)state;
2439210284Sjmallett    cvmx_usb_pipe_t *pipe = usb->pipe + pipe_handle;
2440210284Sjmallett
2441210284Sjmallett    CVMX_USB_LOG_CALLED();
2442210284Sjmallett    CVMX_USB_LOG_PARAM("%p", state);
2443210284Sjmallett    CVMX_USB_LOG_PARAM("%d", pipe_handle);
2444210284Sjmallett    CVMX_USB_LOG_PARAM("%d", submit_handle);
2445210284Sjmallett
2446210284Sjmallett    if (cvmx_unlikely((pipe_handle < 0) || (pipe_handle >= MAX_PIPES)))
2447210284Sjmallett        CVMX_USB_RETURN(CVMX_USB_INVALID_PARAM);
2448210284Sjmallett    if (cvmx_unlikely((submit_handle < 0) || (submit_handle >= MAX_TRANSACTIONS)))
2449210284Sjmallett        CVMX_USB_RETURN(CVMX_USB_INVALID_PARAM);
2450210284Sjmallett    if (cvmx_unlikely(usb->init_flags & CVMX_USB_INITIALIZE_FLAGS_DEVICE_MODE))
2451210284Sjmallett        CVMX_USB_RETURN(CVMX_USB_INCORRECT_MODE);
2452210284Sjmallett
2453210284Sjmallett    /* Fail if the pipe isn't open */
2454210284Sjmallett    if (cvmx_unlikely((pipe->flags & __CVMX_USB_PIPE_FLAGS_OPEN) == 0))
2455210284Sjmallett        CVMX_USB_RETURN(CVMX_USB_INVALID_PARAM);
2456210284Sjmallett
2457210284Sjmallett    transaction = usb->transaction + submit_handle;
2458210284Sjmallett
2459210284Sjmallett    /* Fail if this transaction already completed */
2460210284Sjmallett    if (cvmx_unlikely((transaction->flags & __CVMX_USB_TRANSACTION_FLAGS_IN_USE) == 0))
2461210284Sjmallett        CVMX_USB_RETURN(CVMX_USB_INVALID_PARAM);
2462210284Sjmallett
2463210284Sjmallett    /* If the transaction is the HEAD of the queue and scheduled. We need to
2464210284Sjmallett        treat it special */
2465210284Sjmallett    if ((pipe->head == transaction) &&
2466210284Sjmallett        (pipe->flags & __CVMX_USB_PIPE_FLAGS_SCHEDULED))
2467210284Sjmallett    {
2468210284Sjmallett        cvmx_usbcx_hccharx_t usbc_hcchar;
2469210284Sjmallett
2470210284Sjmallett        usb->pipe_for_channel[pipe->channel] = NULL;
2471210284Sjmallett        pipe->flags &= ~__CVMX_USB_PIPE_FLAGS_SCHEDULED;
2472210284Sjmallett
2473210284Sjmallett        CVMX_SYNCW;
2474210284Sjmallett
2475210284Sjmallett        usbc_hcchar.u32 = __cvmx_usb_read_csr32(usb, CVMX_USBCX_HCCHARX(pipe->channel, usb->index));
2476210284Sjmallett        /* If the channel isn't enabled then the transaction already completed */
2477210284Sjmallett        if (usbc_hcchar.s.chena)
2478210284Sjmallett        {
2479210284Sjmallett            usbc_hcchar.s.chdis = 1;
2480210284Sjmallett            __cvmx_usb_write_csr32(usb, CVMX_USBCX_HCCHARX(pipe->channel, usb->index), usbc_hcchar.u32);
2481210284Sjmallett        }
2482210284Sjmallett    }
2483210284Sjmallett    __cvmx_usb_perform_complete(usb, pipe, transaction, CVMX_USB_COMPLETE_CANCEL);
2484210284Sjmallett    CVMX_USB_RETURN(CVMX_USB_SUCCESS);
2485210284Sjmallett}
2486210284Sjmallett
2487210284Sjmallett
2488210284Sjmallett/**
2489210284Sjmallett * Cancel all outstanding requests in a pipe. Logically all this
2490210284Sjmallett * does is call cvmx_usb_cancel() in a loop.
2491210284Sjmallett *
2492210284Sjmallett * @param state  USB device state populated by
2493210284Sjmallett *               cvmx_usb_initialize().
2494210284Sjmallett * @param pipe_handle
2495210284Sjmallett *               Pipe handle to cancel requests in.
2496210284Sjmallett *
2497210284Sjmallett * @return CVMX_USB_SUCCESS or a negative error code defined in
2498210284Sjmallett *         cvmx_usb_status_t.
2499210284Sjmallett */
2500210284Sjmallettcvmx_usb_status_t cvmx_usb_cancel_all(cvmx_usb_state_t *state, int pipe_handle)
2501210284Sjmallett{
2502210284Sjmallett    cvmx_usb_internal_state_t *usb = (cvmx_usb_internal_state_t*)state;
2503210284Sjmallett    cvmx_usb_pipe_t *pipe = usb->pipe + pipe_handle;
2504210284Sjmallett
2505210284Sjmallett    CVMX_USB_LOG_CALLED();
2506210284Sjmallett    CVMX_USB_LOG_PARAM("%p", state);
2507210284Sjmallett    CVMX_USB_LOG_PARAM("%d", pipe_handle);
2508210284Sjmallett    if (cvmx_unlikely((pipe_handle < 0) || (pipe_handle >= MAX_PIPES)))
2509210284Sjmallett        CVMX_USB_RETURN(CVMX_USB_INVALID_PARAM);
2510210284Sjmallett    if (cvmx_unlikely(usb->init_flags & CVMX_USB_INITIALIZE_FLAGS_DEVICE_MODE))
2511210284Sjmallett        CVMX_USB_RETURN(CVMX_USB_INCORRECT_MODE);
2512210284Sjmallett
2513210284Sjmallett    /* Fail if the pipe isn't open */
2514210284Sjmallett    if (cvmx_unlikely((pipe->flags & __CVMX_USB_PIPE_FLAGS_OPEN) == 0))
2515210284Sjmallett        CVMX_USB_RETURN(CVMX_USB_INVALID_PARAM);
2516210284Sjmallett
2517210284Sjmallett    /* Simply loop through and attempt to cancel each transaction */
2518210284Sjmallett    while (pipe->head)
2519210284Sjmallett    {
2520210284Sjmallett        cvmx_usb_status_t result = cvmx_usb_cancel(state, pipe_handle,
2521210284Sjmallett            __cvmx_usb_get_submit_handle(usb, pipe->head));
2522210284Sjmallett        if (cvmx_unlikely(result != CVMX_USB_SUCCESS))
2523210284Sjmallett            CVMX_USB_RETURN(result);
2524210284Sjmallett    }
2525210284Sjmallett    CVMX_USB_RETURN(CVMX_USB_SUCCESS);
2526210284Sjmallett}
2527210284Sjmallett
2528210284Sjmallett
2529210284Sjmallett/**
2530210284Sjmallett * Close a pipe created with cvmx_usb_open_pipe().
2531210284Sjmallett *
2532210284Sjmallett * @param state  USB device state populated by
2533210284Sjmallett *               cvmx_usb_initialize().
2534210284Sjmallett * @param pipe_handle
2535210284Sjmallett *               Pipe handle to close.
2536210284Sjmallett *
2537210284Sjmallett * @return CVMX_USB_SUCCESS or a negative error code defined in
2538210284Sjmallett *         cvmx_usb_status_t. CVMX_USB_BUSY is returned if the
2539210284Sjmallett *         pipe has outstanding transfers.
2540210284Sjmallett */
2541210284Sjmallettcvmx_usb_status_t cvmx_usb_close_pipe(cvmx_usb_state_t *state, int pipe_handle)
2542210284Sjmallett{
2543210284Sjmallett    cvmx_usb_internal_state_t *usb = (cvmx_usb_internal_state_t*)state;
2544210284Sjmallett    cvmx_usb_pipe_t *pipe = usb->pipe + pipe_handle;
2545210284Sjmallett
2546210284Sjmallett    CVMX_USB_LOG_CALLED();
2547210284Sjmallett    CVMX_USB_LOG_PARAM("%p", state);
2548210284Sjmallett    CVMX_USB_LOG_PARAM("%d", pipe_handle);
2549210284Sjmallett    if (cvmx_unlikely((pipe_handle < 0) || (pipe_handle >= MAX_PIPES)))
2550210284Sjmallett        CVMX_USB_RETURN(CVMX_USB_INVALID_PARAM);
2551210284Sjmallett    if (cvmx_unlikely(usb->init_flags & CVMX_USB_INITIALIZE_FLAGS_DEVICE_MODE))
2552210284Sjmallett        CVMX_USB_RETURN(CVMX_USB_INCORRECT_MODE);
2553210284Sjmallett
2554210284Sjmallett    /* Fail if the pipe isn't open */
2555210284Sjmallett    if (cvmx_unlikely((pipe->flags & __CVMX_USB_PIPE_FLAGS_OPEN) == 0))
2556210284Sjmallett        CVMX_USB_RETURN(CVMX_USB_INVALID_PARAM);
2557210284Sjmallett
2558210284Sjmallett    /* Fail if the pipe has pending transactions */
2559210284Sjmallett    if (cvmx_unlikely(pipe->head))
2560210284Sjmallett        CVMX_USB_RETURN(CVMX_USB_BUSY);
2561210284Sjmallett
2562210284Sjmallett    pipe->flags = 0;
2563210284Sjmallett    __cvmx_usb_remove_pipe(&usb->idle_pipes, pipe);
2564210284Sjmallett    __cvmx_usb_append_pipe(&usb->free_pipes, pipe);
2565210284Sjmallett
2566210284Sjmallett    CVMX_USB_RETURN(CVMX_USB_SUCCESS);
2567210284Sjmallett}
2568210284Sjmallett
2569210284Sjmallett
2570210284Sjmallett/**
2571210284Sjmallett * Register a function to be called when various USB events occur.
2572210284Sjmallett *
2573210284Sjmallett * @param state     USB device state populated by
2574210284Sjmallett *                  cvmx_usb_initialize().
2575210284Sjmallett * @param reason    Which event to register for.
2576210284Sjmallett * @param callback  Function to call when the event occurs.
2577210284Sjmallett * @param user_data User data parameter to the function.
2578210284Sjmallett *
2579210284Sjmallett * @return CVMX_USB_SUCCESS or a negative error code defined in
2580210284Sjmallett *         cvmx_usb_status_t.
2581210284Sjmallett */
2582210284Sjmallettcvmx_usb_status_t cvmx_usb_register_callback(cvmx_usb_state_t *state,
2583210284Sjmallett                                             cvmx_usb_callback_t reason,
2584210284Sjmallett                                             cvmx_usb_callback_func_t callback,
2585210284Sjmallett                                             void *user_data)
2586210284Sjmallett{
2587210284Sjmallett    cvmx_usb_internal_state_t *usb = (cvmx_usb_internal_state_t*)state;
2588210284Sjmallett
2589210284Sjmallett    CVMX_USB_LOG_CALLED();
2590210284Sjmallett    CVMX_USB_LOG_PARAM("%p", state);
2591210284Sjmallett    CVMX_USB_LOG_PARAM("%d", reason);
2592210284Sjmallett    CVMX_USB_LOG_PARAM("%p", callback);
2593210284Sjmallett    CVMX_USB_LOG_PARAM("%p", user_data);
2594210284Sjmallett    if (cvmx_unlikely(reason >= __CVMX_USB_CALLBACK_END))
2595210284Sjmallett        CVMX_USB_RETURN(CVMX_USB_INVALID_PARAM);
2596210284Sjmallett    if (cvmx_unlikely(!callback))
2597210284Sjmallett        CVMX_USB_RETURN(CVMX_USB_INVALID_PARAM);
2598210284Sjmallett
2599210284Sjmallett    usb->callback[reason] = callback;
2600210284Sjmallett    usb->callback_data[reason] = user_data;
2601210284Sjmallett
2602210284Sjmallett    CVMX_USB_RETURN(CVMX_USB_SUCCESS);
2603210284Sjmallett}
2604210284Sjmallett
2605210284Sjmallett
2606210284Sjmallett/**
2607210284Sjmallett * Get the current USB protocol level frame number. The frame
2608210284Sjmallett * number is always in the range of 0-0x7ff.
2609210284Sjmallett *
2610210284Sjmallett * @param state  USB device state populated by
2611210284Sjmallett *               cvmx_usb_initialize().
2612210284Sjmallett *
2613210284Sjmallett * @return USB frame number
2614210284Sjmallett */
2615210284Sjmallettint cvmx_usb_get_frame_number(cvmx_usb_state_t *state)
2616210284Sjmallett{
2617210284Sjmallett    int frame_number;
2618210284Sjmallett    cvmx_usb_internal_state_t *usb = (cvmx_usb_internal_state_t*)state;
2619210284Sjmallett
2620210284Sjmallett    CVMX_USB_LOG_CALLED();
2621210284Sjmallett    CVMX_USB_LOG_PARAM("%p", state);
2622210284Sjmallett
2623210284Sjmallett    if (cvmx_unlikely(usb->init_flags & CVMX_USB_INITIALIZE_FLAGS_DEVICE_MODE))
2624210284Sjmallett    {
2625210284Sjmallett        cvmx_usbcx_dsts_t usbc_dsts;
2626210284Sjmallett        usbc_dsts.u32 = __cvmx_usb_read_csr32(usb, CVMX_USBCX_DSTS(usb->index));
2627210284Sjmallett        frame_number = usbc_dsts.s.soffn;
2628210284Sjmallett    }
2629210284Sjmallett    else
2630210284Sjmallett    {
2631210284Sjmallett        cvmx_usbcx_hfnum_t usbc_hfnum;
2632210284Sjmallett        usbc_hfnum.u32 = __cvmx_usb_read_csr32(usb, CVMX_USBCX_HFNUM(usb->index));
2633210284Sjmallett        frame_number = usbc_hfnum.s.frnum;
2634210284Sjmallett    }
2635210284Sjmallett
2636210284Sjmallett    CVMX_USB_RETURN(frame_number);
2637210284Sjmallett}
2638210284Sjmallett
2639210284Sjmallett
2640210284Sjmallett/**
2641210284Sjmallett * @INTERNAL
2642210284Sjmallett * Poll a channel for status
2643210284Sjmallett *
2644210284Sjmallett * @param usb     USB device
2645210284Sjmallett * @param channel Channel to poll
2646210284Sjmallett *
2647210284Sjmallett * @return Zero on success
2648210284Sjmallett */
2649210284Sjmallettstatic int __cvmx_usb_poll_channel(cvmx_usb_internal_state_t *usb, int channel)
2650210284Sjmallett{
2651210284Sjmallett    cvmx_usbcx_hcintx_t usbc_hcint;
2652210284Sjmallett    cvmx_usbcx_hctsizx_t usbc_hctsiz;
2653210284Sjmallett    cvmx_usbcx_hccharx_t usbc_hcchar;
2654210284Sjmallett    cvmx_usb_pipe_t *pipe;
2655210284Sjmallett    cvmx_usb_transaction_t *transaction;
2656210284Sjmallett    int bytes_this_transfer;
2657210284Sjmallett    int bytes_in_last_packet;
2658210284Sjmallett    int packets_processed;
2659210284Sjmallett    int buffer_space_left;
2660210284Sjmallett    CVMX_USB_LOG_CALLED();
2661210284Sjmallett    CVMX_USB_LOG_PARAM("%p", usb);
2662210284Sjmallett    CVMX_USB_LOG_PARAM("%d", channel);
2663210284Sjmallett
2664210284Sjmallett    /* Read the interrupt status bits for the channel */
2665210284Sjmallett    usbc_hcint.u32 = __cvmx_usb_read_csr32(usb, CVMX_USBCX_HCINTX(channel, usb->index));
2666210284Sjmallett
2667210284Sjmallett    /* We ignore any interrupts where the channel hasn't halted yet. These
2668210284Sjmallett        should be impossible since we don't enable any interrupts except for
2669210284Sjmallett        channel halted */
2670210284Sjmallett    if (!usbc_hcint.s.chhltd)
2671210284Sjmallett        CVMX_USB_RETURN(0);
2672210284Sjmallett
2673210284Sjmallett    /* Now that the channel has halted, clear all status bits before
2674210284Sjmallett        processing. This way we don't have any race conditions caused by the
2675210284Sjmallett        channel starting up and finishing before we clear the bits */
2676210284Sjmallett    __cvmx_usb_write_csr32(usb, CVMX_USBCX_HCINTX(channel, usb->index), usbc_hcint.u32);
2677210284Sjmallett    //cvmx_csr_db_decode(cvmx_get_proc_id(), CVMX_USBCX_HCINTX(channel, usb->index), usbc_hcint.u32);
2678210284Sjmallett
2679210284Sjmallett    usb->idle_hardware_channels |= (1<<channel);
2680210284Sjmallett
2681210284Sjmallett    /* Make sure this channel is tied to a valid pipe */
2682210284Sjmallett    pipe = usb->pipe_for_channel[channel];
2683210284Sjmallett    CVMX_PREFETCH(pipe, 0);
2684210284Sjmallett    CVMX_PREFETCH(pipe, 128);
2685210284Sjmallett    if (!pipe)
2686210284Sjmallett        CVMX_USB_RETURN(0);
2687210284Sjmallett    transaction = pipe->head;
2688210284Sjmallett    CVMX_PREFETCH0(transaction);
2689210284Sjmallett
2690210284Sjmallett    /* Disconnect this pipe from the HW channel. Later the schedule function will
2691210284Sjmallett        figure out which pipe needs to go */
2692210284Sjmallett    usb->pipe_for_channel[channel] = NULL;
2693210284Sjmallett    pipe->flags &= ~__CVMX_USB_PIPE_FLAGS_SCHEDULED;
2694210284Sjmallett
2695210284Sjmallett    /* Read the channel config info so we can figure out how much data
2696210284Sjmallett        transfered */
2697210284Sjmallett    usbc_hcchar.u32 = __cvmx_usb_read_csr32(usb, CVMX_USBCX_HCCHARX(channel, usb->index));
2698210284Sjmallett    usbc_hctsiz.u32 = __cvmx_usb_read_csr32(usb, CVMX_USBCX_HCTSIZX(channel, usb->index));
2699210284Sjmallett
2700210284Sjmallett    /* Calculating the number of bytes successfully transfered is dependent on
2701210284Sjmallett        the transfer direction */
2702210284Sjmallett    packets_processed = transaction->pktcnt - usbc_hctsiz.s.pktcnt;
2703210284Sjmallett    if (usbc_hcchar.s.epdir)
2704210284Sjmallett    {
2705210284Sjmallett        /* IN transactions are easy. For every byte received the hardware
2706210284Sjmallett            decrements xfersize. All we need to do is subtract the current
2707210284Sjmallett            value of xfersize from its starting value and we know how many
2708210284Sjmallett            bytes were written to the buffer */
2709210284Sjmallett        bytes_this_transfer = transaction->xfersize - usbc_hctsiz.s.xfersize;
2710210284Sjmallett    }
2711210284Sjmallett    else
2712210284Sjmallett    {
2713210284Sjmallett        /* OUT transaction don't decrement xfersize. Instead pktcnt is
2714210284Sjmallett            decremented on every successful packet send. The hardware does
2715210284Sjmallett            this when it receives an ACK, or NYET. If it doesn't
2716210284Sjmallett            receive one of these responses pktcnt doesn't change */
2717210284Sjmallett        bytes_this_transfer = packets_processed * usbc_hcchar.s.mps;
2718210284Sjmallett        /* The last packet may not be a full transfer if we didn't have
2719210284Sjmallett            enough data */
2720210284Sjmallett        if (bytes_this_transfer > transaction->xfersize)
2721210284Sjmallett            bytes_this_transfer = transaction->xfersize;
2722210284Sjmallett    }
2723210284Sjmallett    /* Figure out how many bytes were in the last packet of the transfer */
2724210284Sjmallett    if (packets_processed)
2725210284Sjmallett        bytes_in_last_packet = bytes_this_transfer - (packets_processed-1) * usbc_hcchar.s.mps;
2726210284Sjmallett    else
2727210284Sjmallett        bytes_in_last_packet = bytes_this_transfer;
2728210284Sjmallett
2729210284Sjmallett    /* As a special case, setup transactions output the setup header, not
2730210284Sjmallett        the user's data. For this reason we don't count setup data as bytes
2731210284Sjmallett        transfered */
2732210284Sjmallett    if ((transaction->stage == CVMX_USB_STAGE_SETUP) ||
2733210284Sjmallett        (transaction->stage == CVMX_USB_STAGE_SETUP_SPLIT_COMPLETE))
2734210284Sjmallett        bytes_this_transfer = 0;
2735210284Sjmallett
2736210284Sjmallett    /* Optional debug output */
2737210284Sjmallett    if (cvmx_unlikely((usb->init_flags & CVMX_USB_INITIALIZE_FLAGS_DEBUG_TRANSFERS) ||
2738210284Sjmallett        (pipe->flags & CVMX_USB_PIPE_FLAGS_DEBUG_TRANSFERS)))
2739210284Sjmallett        cvmx_dprintf("%s: Channel %d halted. Pipe %d transaction %d stage %d bytes=%d\n",
2740210284Sjmallett                     __FUNCTION__, channel,
2741210284Sjmallett                     __cvmx_usb_get_pipe_handle(usb, pipe),
2742210284Sjmallett                     __cvmx_usb_get_submit_handle(usb, transaction),
2743210284Sjmallett                     transaction->stage, bytes_this_transfer);
2744210284Sjmallett
2745210284Sjmallett    /* Add the bytes transfered to the running total. It is important that
2746210284Sjmallett        bytes_this_transfer doesn't count any data that needs to be
2747210284Sjmallett        retransmitted */
2748210284Sjmallett    transaction->actual_bytes += bytes_this_transfer;
2749210284Sjmallett    if (transaction->type == CVMX_USB_TRANSFER_ISOCHRONOUS)
2750210284Sjmallett        buffer_space_left = transaction->iso_packets[0].length - transaction->actual_bytes;
2751210284Sjmallett    else
2752210284Sjmallett        buffer_space_left = transaction->buffer_length - transaction->actual_bytes;
2753210284Sjmallett
2754210284Sjmallett    /* We need to remember the PID toggle state for the next transaction. The
2755210284Sjmallett        hardware already updated it for the next transaction */
2756210284Sjmallett    pipe->pid_toggle = !(usbc_hctsiz.s.pid == 0);
2757210284Sjmallett
2758210284Sjmallett    /* For high speed bulk out, assume the next transaction will need to do a
2759210284Sjmallett        ping before proceeding. If this isn't true the ACK processing below
2760210284Sjmallett        will clear this flag */
2761210284Sjmallett    if ((pipe->device_speed == CVMX_USB_SPEED_HIGH) &&
2762210284Sjmallett        (pipe->transfer_type == CVMX_USB_TRANSFER_BULK) &&
2763210284Sjmallett        (pipe->transfer_dir == CVMX_USB_DIRECTION_OUT))
2764210284Sjmallett        pipe->flags |= __CVMX_USB_PIPE_FLAGS_NEED_PING;
2765210284Sjmallett
2766210284Sjmallett    if (usbc_hcint.s.stall)
2767210284Sjmallett    {
2768210284Sjmallett        /* STALL as a response means this transaction cannot be completed
2769210284Sjmallett            because the device can't process transactions. Tell the user. Any
2770210284Sjmallett            data that was transfered will be counted on the actual bytes
2771210284Sjmallett            transfered */
2772210284Sjmallett        pipe->pid_toggle = 0;
2773210284Sjmallett        __cvmx_usb_perform_complete(usb, pipe, transaction, CVMX_USB_COMPLETE_STALL);
2774210284Sjmallett    }
2775210284Sjmallett    else if (0 && usbc_hcint.s.xfercompl)
2776210284Sjmallett    {
2777210284Sjmallett        /* XferCompl is only useful in non DMA mode */
2778210284Sjmallett    }
2779210284Sjmallett    else if (usbc_hcint.s.xacterr)
2780210284Sjmallett    {
2781210284Sjmallett        /* We know at least one packet worked if we get a ACK or NAK. Reset the retry counter */
2782210284Sjmallett        if (usbc_hcint.s.nak || usbc_hcint.s.ack)
2783210284Sjmallett            transaction->retries = 0;
2784210284Sjmallett        transaction->retries++;
2785210284Sjmallett        if (transaction->retries > MAX_RETRIES)
2786210284Sjmallett        {
2787210284Sjmallett            /* XactErr as a response means the device signaled something wrong with
2788210284Sjmallett                the transfer. For example, PID toggle errors cause these */
2789210284Sjmallett            __cvmx_usb_perform_complete(usb, pipe, transaction, CVMX_USB_COMPLETE_XACTERR);
2790210284Sjmallett        }
2791210284Sjmallett        else
2792210284Sjmallett        {
2793210284Sjmallett            /* Rewind to the beginning of the transaction by anding off the
2794210284Sjmallett                split complete bit */
2795210284Sjmallett            transaction->stage &= ~1;
2796210284Sjmallett            pipe->split_sc_frame = -1;
2797210284Sjmallett            pipe->next_tx_cycle = cvmx_read64_uint64(CVMX_IPD_CLK_COUNT) + pipe->interval;
2798210284Sjmallett        }
2799210284Sjmallett    }
2800210284Sjmallett    else if (0 && usbc_hcint.s.datatglerr)
2801210284Sjmallett    {
2802210284Sjmallett        /* The hardware automatically handles Data Toggle Errors for us */
2803210284Sjmallett    }
2804210284Sjmallett    else if (usbc_hcint.s.bblerr)
2805210284Sjmallett    {
2806210284Sjmallett        /* Babble Error (BblErr) */
2807210284Sjmallett        __cvmx_usb_perform_complete(usb, pipe, transaction, CVMX_USB_COMPLETE_BABBLEERR);
2808210284Sjmallett    }
2809210284Sjmallett    else if (usbc_hcint.s.frmovrun)
2810210284Sjmallett    {
2811210284Sjmallett        /* Frame Overrun (FrmOvrun) */
2812210284Sjmallett        /* Rewind to the beginning of the transaction by anding off the
2813210284Sjmallett            split complete bit */
2814210284Sjmallett        transaction->stage &= ~1;
2815210284Sjmallett        pipe->split_sc_frame = -1;
2816210284Sjmallett    }
2817210284Sjmallett    else if (usbc_hcint.s.nyet)
2818210284Sjmallett    {
2819210284Sjmallett        /* NYET as a response is only allowed in three cases: as a response to
2820210284Sjmallett            a ping, as a response to a split transaction, and as a response to
2821210284Sjmallett            a bulk out. The ping case is handled by hardware, so we only have
2822210284Sjmallett            splits and bulk out */
2823210284Sjmallett        if (!__cvmx_usb_pipe_needs_split(usb, pipe))
2824210284Sjmallett        {
2825210284Sjmallett            transaction->retries = 0;
2826210284Sjmallett            /* If there is more data to go then we need to try again. Otherwise
2827210284Sjmallett                this transaction is complete */
2828210284Sjmallett            if ((buffer_space_left == 0) || (bytes_in_last_packet < pipe->max_packet))
2829210284Sjmallett                __cvmx_usb_perform_complete(usb, pipe, transaction, CVMX_USB_COMPLETE_SUCCESS);
2830210284Sjmallett        }
2831210284Sjmallett        else
2832210284Sjmallett        {
2833210284Sjmallett            /* Split transactions retry the split complete 4 times then rewind
2834210284Sjmallett                to the start split and do the entire transactions again */
2835210284Sjmallett            transaction->retries++;
2836210284Sjmallett            if ((transaction->retries & 0x3) == 0)
2837210284Sjmallett            {
2838210284Sjmallett                /* Rewind to the beginning of the transaction by anding off the
2839210284Sjmallett                    split complete bit */
2840210284Sjmallett                transaction->stage &= ~1;
2841210284Sjmallett                pipe->split_sc_frame = -1;
2842210284Sjmallett            }
2843210284Sjmallett        }
2844210284Sjmallett    }
2845210284Sjmallett    else if (usbc_hcint.s.ack)
2846210284Sjmallett    {
2847210284Sjmallett        transaction->retries = 0;
2848210284Sjmallett        /* The ACK bit can only be checked after the other error bits. This is
2849210284Sjmallett            because a multi packet transfer may succeed in a number of packets
2850210284Sjmallett            and then get a different response on the last packet. In this case
2851210284Sjmallett            both ACK and the last response bit will be set. If none of the
2852210284Sjmallett            other response bits is set, then the last packet must have been an
2853210284Sjmallett            ACK */
2854210284Sjmallett
2855210284Sjmallett        /* Since we got an ACK, we know we don't need to do a ping on this
2856210284Sjmallett            pipe */
2857210284Sjmallett        pipe->flags &= ~__CVMX_USB_PIPE_FLAGS_NEED_PING;
2858210284Sjmallett
2859210284Sjmallett        switch (transaction->type)
2860210284Sjmallett        {
2861210284Sjmallett            case CVMX_USB_TRANSFER_CONTROL:
2862210284Sjmallett                switch (transaction->stage)
2863210284Sjmallett                {
2864210284Sjmallett                    case CVMX_USB_STAGE_NON_CONTROL:
2865210284Sjmallett                    case CVMX_USB_STAGE_NON_CONTROL_SPLIT_COMPLETE:
2866210284Sjmallett                        /* This should be impossible */
2867210284Sjmallett                        __cvmx_usb_perform_complete(usb, pipe, transaction, CVMX_USB_COMPLETE_ERROR);
2868210284Sjmallett                        break;
2869210284Sjmallett                    case CVMX_USB_STAGE_SETUP:
2870210284Sjmallett                        pipe->pid_toggle = 1;
2871210284Sjmallett                        if (__cvmx_usb_pipe_needs_split(usb, pipe))
2872210284Sjmallett                            transaction->stage = CVMX_USB_STAGE_SETUP_SPLIT_COMPLETE;
2873210284Sjmallett                        else
2874210284Sjmallett                        {
2875210284Sjmallett                            cvmx_usb_control_header_t *header = cvmx_phys_to_ptr(transaction->control_header);
2876210284Sjmallett                            if (header->s.length)
2877210284Sjmallett                                transaction->stage = CVMX_USB_STAGE_DATA;
2878210284Sjmallett                            else
2879210284Sjmallett                                transaction->stage = CVMX_USB_STAGE_STATUS;
2880210284Sjmallett                        }
2881210284Sjmallett                        break;
2882210284Sjmallett                    case CVMX_USB_STAGE_SETUP_SPLIT_COMPLETE:
2883210284Sjmallett                        {
2884210284Sjmallett                            cvmx_usb_control_header_t *header = cvmx_phys_to_ptr(transaction->control_header);
2885210284Sjmallett                            if (header->s.length)
2886210284Sjmallett                                transaction->stage = CVMX_USB_STAGE_DATA;
2887210284Sjmallett                            else
2888210284Sjmallett                                transaction->stage = CVMX_USB_STAGE_STATUS;
2889210284Sjmallett                        }
2890210284Sjmallett                        break;
2891210284Sjmallett                    case CVMX_USB_STAGE_DATA:
2892210284Sjmallett                        if (__cvmx_usb_pipe_needs_split(usb, pipe))
2893210284Sjmallett                            transaction->stage = CVMX_USB_STAGE_DATA_SPLIT_COMPLETE;
2894210284Sjmallett                        else if ((buffer_space_left == 0) || (bytes_in_last_packet < pipe->max_packet))
2895210284Sjmallett                        {
2896210284Sjmallett                            pipe->pid_toggle = 1;
2897210284Sjmallett                            transaction->stage = CVMX_USB_STAGE_STATUS;
2898210284Sjmallett                        }
2899210284Sjmallett                        break;
2900210284Sjmallett                    case CVMX_USB_STAGE_DATA_SPLIT_COMPLETE:
2901210284Sjmallett                        if ((buffer_space_left == 0) || (bytes_in_last_packet < pipe->max_packet))
2902210284Sjmallett                        {
2903210284Sjmallett                            pipe->pid_toggle = 1;
2904210284Sjmallett                            transaction->stage = CVMX_USB_STAGE_STATUS;
2905210284Sjmallett                        }
2906210284Sjmallett                        else
2907210284Sjmallett                        {
2908210284Sjmallett                            transaction->stage = CVMX_USB_STAGE_DATA;
2909210284Sjmallett                        }
2910210284Sjmallett                        break;
2911210284Sjmallett                    case CVMX_USB_STAGE_STATUS:
2912210284Sjmallett                        if (__cvmx_usb_pipe_needs_split(usb, pipe))
2913210284Sjmallett                            transaction->stage = CVMX_USB_STAGE_STATUS_SPLIT_COMPLETE;
2914210284Sjmallett                        else
2915210284Sjmallett                            __cvmx_usb_perform_complete(usb, pipe, transaction, CVMX_USB_COMPLETE_SUCCESS);
2916210284Sjmallett                        break;
2917210284Sjmallett                    case CVMX_USB_STAGE_STATUS_SPLIT_COMPLETE:
2918210284Sjmallett                        __cvmx_usb_perform_complete(usb, pipe, transaction, CVMX_USB_COMPLETE_SUCCESS);
2919210284Sjmallett                        break;
2920210284Sjmallett                }
2921210284Sjmallett                break;
2922210284Sjmallett            case CVMX_USB_TRANSFER_BULK:
2923210284Sjmallett            case CVMX_USB_TRANSFER_INTERRUPT:
2924210284Sjmallett                /* The only time a bulk transfer isn't complete when
2925210284Sjmallett                    it finishes with an ACK is during a split transaction. For
2926210284Sjmallett                    splits we need to continue the transfer if more data is
2927210284Sjmallett                    needed */
2928210284Sjmallett                if (__cvmx_usb_pipe_needs_split(usb, pipe))
2929210284Sjmallett                {
2930210284Sjmallett                    if (transaction->stage == CVMX_USB_STAGE_NON_CONTROL)
2931210284Sjmallett                        transaction->stage = CVMX_USB_STAGE_NON_CONTROL_SPLIT_COMPLETE;
2932210284Sjmallett                    else
2933210284Sjmallett                    {
2934210284Sjmallett                        if (buffer_space_left && (bytes_in_last_packet == pipe->max_packet))
2935210284Sjmallett                            transaction->stage = CVMX_USB_STAGE_NON_CONTROL;
2936210284Sjmallett                        else
2937210284Sjmallett                        {
2938210284Sjmallett                            if (transaction->type == CVMX_USB_TRANSFER_INTERRUPT)
2939210284Sjmallett                                pipe->next_tx_cycle += pipe->interval;
2940210284Sjmallett                            __cvmx_usb_perform_complete(usb, pipe, transaction, CVMX_USB_COMPLETE_SUCCESS);
2941210284Sjmallett                        }
2942210284Sjmallett                    }
2943210284Sjmallett                }
2944210284Sjmallett                else
2945210284Sjmallett                {
2946210284Sjmallett                    if ((pipe->device_speed == CVMX_USB_SPEED_HIGH) &&
2947210284Sjmallett                        (pipe->transfer_type == CVMX_USB_TRANSFER_BULK) &&
2948210284Sjmallett                        (pipe->transfer_dir == CVMX_USB_DIRECTION_OUT) &&
2949210284Sjmallett                        (usbc_hcint.s.nak))
2950210284Sjmallett                        pipe->flags |= __CVMX_USB_PIPE_FLAGS_NEED_PING;
2951210284Sjmallett                    if (!buffer_space_left || (bytes_in_last_packet < pipe->max_packet))
2952210284Sjmallett                    {
2953210284Sjmallett                        if (transaction->type == CVMX_USB_TRANSFER_INTERRUPT)
2954210284Sjmallett                            pipe->next_tx_cycle += pipe->interval;
2955210284Sjmallett                        __cvmx_usb_perform_complete(usb, pipe, transaction, CVMX_USB_COMPLETE_SUCCESS);
2956210284Sjmallett                    }
2957210284Sjmallett                }
2958210284Sjmallett                break;
2959210284Sjmallett            case CVMX_USB_TRANSFER_ISOCHRONOUS:
2960210284Sjmallett                if (__cvmx_usb_pipe_needs_split(usb, pipe))
2961210284Sjmallett                {
2962210284Sjmallett                    /* ISOCHRONOUS OUT splits don't require a complete split stage.
2963210284Sjmallett                        Instead they use a sequence of begin OUT splits to transfer
2964210284Sjmallett                        the data 188 bytes at a time. Once the transfer is complete,
2965210284Sjmallett                        the pipe sleeps until the next schedule interval */
2966210284Sjmallett                    if (pipe->transfer_dir == CVMX_USB_DIRECTION_OUT)
2967210284Sjmallett                    {
2968210284Sjmallett                        pipe->next_tx_cycle += pipe->interval;
2969210284Sjmallett                        /* If no space left or this wasn't a max size packet then
2970210284Sjmallett                            this transfer is complete. Otherwise start it again
2971210284Sjmallett                            to send the next 188 bytes */
2972210284Sjmallett                        if (!buffer_space_left || (bytes_this_transfer < 188))
2973210284Sjmallett                            __cvmx_usb_perform_complete(usb, pipe, transaction, CVMX_USB_COMPLETE_SUCCESS);
2974210284Sjmallett                    }
2975210284Sjmallett                    else
2976210284Sjmallett                    {
2977210284Sjmallett                        if (transaction->stage == CVMX_USB_STAGE_NON_CONTROL_SPLIT_COMPLETE)
2978210284Sjmallett                        {
2979210284Sjmallett                            /* We are in the incomming data phase. Keep getting
2980210284Sjmallett                                data until we run out of space or get a small
2981210284Sjmallett                                packet */
2982210284Sjmallett                            if ((buffer_space_left == 0) || (bytes_in_last_packet < pipe->max_packet))
2983210284Sjmallett                            {
2984210284Sjmallett                                pipe->next_tx_cycle += pipe->interval;
2985210284Sjmallett                                __cvmx_usb_perform_complete(usb, pipe, transaction, CVMX_USB_COMPLETE_SUCCESS);
2986210284Sjmallett                            }
2987210284Sjmallett                        }
2988210284Sjmallett                        else
2989210284Sjmallett                            transaction->stage = CVMX_USB_STAGE_NON_CONTROL_SPLIT_COMPLETE;
2990210284Sjmallett                    }
2991210284Sjmallett                }
2992210284Sjmallett                else
2993210284Sjmallett                {
2994210284Sjmallett                    pipe->next_tx_cycle += pipe->interval;
2995210284Sjmallett                    __cvmx_usb_perform_complete(usb, pipe, transaction, CVMX_USB_COMPLETE_SUCCESS);
2996210284Sjmallett                }
2997210284Sjmallett                break;
2998210284Sjmallett        }
2999210284Sjmallett    }
3000210284Sjmallett    else if (usbc_hcint.s.nak)
3001210284Sjmallett    {
3002210284Sjmallett        uint64_t ipd_clk_count;
3003210284Sjmallett        /* NAK as a response means the device couldn't accept the transaction,
3004210284Sjmallett            but it should be retried in the future. Rewind to the beginning of
3005210284Sjmallett            the transaction by anding off the split complete bit. Retry in the
3006210284Sjmallett            next interval */
3007210284Sjmallett        transaction->retries = 0;
3008210284Sjmallett        transaction->stage &= ~1;
3009210284Sjmallett        pipe->next_tx_cycle += pipe->interval;
3010210284Sjmallett        ipd_clk_count = cvmx_read64_uint64(CVMX_IPD_CLK_COUNT);
3011210284Sjmallett        if (pipe->next_tx_cycle < ipd_clk_count)
3012210284Sjmallett            pipe->next_tx_cycle = ipd_clk_count + pipe->interval;
3013210284Sjmallett    }
3014210284Sjmallett    else
3015210284Sjmallett    {
3016210284Sjmallett        /* We get channel halted interrupts with no result bits sets when the
3017210284Sjmallett            cable is unplugged */
3018210284Sjmallett        __cvmx_usb_perform_complete(usb, pipe, transaction, CVMX_USB_COMPLETE_ERROR);
3019210284Sjmallett    }
3020210284Sjmallett    CVMX_USB_RETURN(0);
3021210284Sjmallett}
3022210284Sjmallett
3023210284Sjmallett
3024210284Sjmallett/**
3025210284Sjmallett * Poll a device mode endpoint for status
3026210284Sjmallett *
3027210284Sjmallett * @param usb    USB device state populated by
3028210284Sjmallett *               cvmx_usb_initialize().
3029210284Sjmallett * @param endpoint_num
3030210284Sjmallett *               Endpoint to poll
3031210284Sjmallett *
3032210284Sjmallett * @return Zero on success
3033210284Sjmallett */
3034210284Sjmallettstatic int __cvmx_usb_poll_endpoint(cvmx_usb_internal_state_t *usb, int endpoint_num)
3035210284Sjmallett{
3036210284Sjmallett    cvmx_usbcx_diepintx_t usbc_diepint;
3037210284Sjmallett    cvmx_usbcx_doepintx_t usbc_doepint;
3038210284Sjmallett
3039210284Sjmallett    CVMX_USB_LOG_CALLED();
3040210284Sjmallett    CVMX_USB_LOG_PARAM("%p", usb);
3041210284Sjmallett    CVMX_USB_LOG_PARAM("%d", endpoint_num);
3042210284Sjmallett
3043210284Sjmallett    usbc_diepint.u32 = __cvmx_usb_read_csr32(usb, CVMX_USBCX_DIEPINTX(endpoint_num, usb->index));
3044210284Sjmallett    __cvmx_usb_write_csr32(usb, CVMX_USBCX_DIEPINTX(endpoint_num, usb->index), usbc_diepint.u32);
3045210284Sjmallett    //cvmx_csr_db_decode(cvmx_get_proc_id(), CVMX_USBCX_DIEPINTX(endpoint_num, usb->index), usbc_diepint.u32);
3046210284Sjmallett    if (usbc_diepint.s.inepnakeff)
3047210284Sjmallett    {
3048210284Sjmallett        /* IN Endpoint NAK Effective (INEPNakEff)
3049210284Sjmallett            Applies to periodic IN endpoints only.
3050210284Sjmallett            Indicates that the IN endpoint NAK bit set by the application has
3051210284Sjmallett            taken effect in the core. This bit can be cleared when the
3052210284Sjmallett            application clears the IN endpoint NAK by writing to
3053210284Sjmallett            DIEPCTLn.CNAK.
3054210284Sjmallett            This interrupt indicates that the core has sampled the NAK bit
3055210284Sjmallett            set (either by the application or by the core).
3056210284Sjmallett            This interrupt does not necessarily mean that a NAK handshake
3057210284Sjmallett            is sent on the USB. A STALL bit takes priority over a NAK bit. */
3058210284Sjmallett        /* Nothing to do */
3059210284Sjmallett    }
3060210284Sjmallett    if (usbc_diepint.s.intknepmis)
3061210284Sjmallett    {
3062210284Sjmallett        /* IN Token Received with EP Mismatch (INTknEPMis)
3063210284Sjmallett            Applies to non-periodic IN endpoints only.
3064210284Sjmallett            Indicates that the data in the top of the non-periodic TxFIFO
3065210284Sjmallett            belongs to an endpoint other than the one for which the IN
3066210284Sjmallett            token was received. This interrupt is asserted on the endpoint
3067210284Sjmallett            for which the IN token was received. */
3068210284Sjmallett        if (cvmx_unlikely(usb->init_flags & CVMX_USB_INITIALIZE_FLAGS_DEBUG_INFO))
3069210284Sjmallett            cvmx_dprintf("%s: Endpoint %d mismatch\n", __FUNCTION__, endpoint_num);
3070210284Sjmallett    }
3071210284Sjmallett    if (usbc_diepint.s.intkntxfemp)
3072210284Sjmallett    {
3073210284Sjmallett        /* IN Token Received When TxFIFO is Empty (INTknTXFEmp)
3074210284Sjmallett            Applies only to non-periodic IN endpoints.
3075210284Sjmallett            Indicates that an IN token was received when the associated
3076210284Sjmallett            TxFIFO (periodic/non-periodic) was empty. This interrupt is
3077210284Sjmallett            asserted on the endpoint for which the IN token was received. */
3078210284Sjmallett        if (cvmx_unlikely(usb->init_flags & CVMX_USB_INITIALIZE_FLAGS_DEBUG_INFO))
3079210284Sjmallett            cvmx_dprintf("%s: Received IN token on endpoint %d without data\n", __FUNCTION__, endpoint_num);
3080210284Sjmallett    }
3081210284Sjmallett    if (usbc_diepint.s.timeout)
3082210284Sjmallett    {
3083210284Sjmallett        /* Timeout Condition (TimeOUT)
3084210284Sjmallett            Applies to non-isochronous IN endpoints only.
3085210284Sjmallett            Indicates that the core has detected a timeout condition on the
3086210284Sjmallett            USB for the last IN token on this endpoint. */
3087210284Sjmallett        if (cvmx_unlikely(usb->init_flags & CVMX_USB_INITIALIZE_FLAGS_DEBUG_INFO))
3088210284Sjmallett            cvmx_dprintf("%s: Received timeout on endpoint %d\n", __FUNCTION__, endpoint_num);
3089210284Sjmallett    }
3090210284Sjmallett    if (usbc_diepint.s.ahberr)
3091210284Sjmallett    {
3092210284Sjmallett        /* AHB Error (AHBErr)
3093210284Sjmallett            This is generated only in Internal DMA mode when there is an
3094210284Sjmallett            AHB error during an AHB read/write. The application can read
3095210284Sjmallett            the corresponding endpoint DMA address register to get the
3096210284Sjmallett            error address. */
3097210284Sjmallett        if (cvmx_unlikely(usb->init_flags & CVMX_USB_INITIALIZE_FLAGS_DEBUG_INFO))
3098210284Sjmallett            cvmx_dprintf("%s: AHB error on endpoint %d\n", __FUNCTION__, endpoint_num);
3099210284Sjmallett    }
3100210284Sjmallett    if (usbc_diepint.s.epdisbld)
3101210284Sjmallett    {
3102210284Sjmallett        /* Endpoint Disabled Interrupt (EPDisbld)
3103210284Sjmallett            This bit indicates that the endpoint is disabled per the
3104210284Sjmallett            application's request. */
3105210284Sjmallett        /* Nothing to do */
3106210284Sjmallett    }
3107210284Sjmallett    if (usbc_diepint.s.xfercompl)
3108210284Sjmallett    {
3109210284Sjmallett        /* Transfer Completed Interrupt (XferCompl)
3110210284Sjmallett            Indicates that the programmed transfer is complete on the AHB
3111210284Sjmallett            as well as on the USB, for this endpoint. */
3112210284Sjmallett        __cvmx_usb_perform_callback(usb, usb->pipe + endpoint_num, NULL,
3113210284Sjmallett                                    CVMX_USB_CALLBACK_TRANSFER_COMPLETE,
3114210284Sjmallett                                    CVMX_USB_COMPLETE_SUCCESS);
3115210284Sjmallett    }
3116210284Sjmallett
3117210284Sjmallett    usbc_doepint.u32 = __cvmx_usb_read_csr32(usb, CVMX_USBCX_DOEPINTX(endpoint_num, usb->index));
3118210284Sjmallett    __cvmx_usb_write_csr32(usb, CVMX_USBCX_DOEPINTX(endpoint_num, usb->index), usbc_doepint.u32);
3119210284Sjmallett    //cvmx_csr_db_decode(cvmx_get_proc_id(), CVMX_USBCX_DOEPINTX(endpoint_num, usb->index), usbc_doepint.u32);
3120210284Sjmallett    if (usbc_doepint.s.outtknepdis)
3121210284Sjmallett    {
3122210284Sjmallett        /* OUT Token Received When Endpoint Disabled (OUTTknEPdis)
3123210284Sjmallett            Applies only to control OUT endpoints.
3124210284Sjmallett            Indicates that an OUT token was received when the endpoint
3125210284Sjmallett            was not yet enabled. This interrupt is asserted on the endpoint
3126210284Sjmallett            for which the OUT token was received. */
3127210284Sjmallett        if (cvmx_unlikely(usb->init_flags & CVMX_USB_INITIALIZE_FLAGS_DEBUG_INFO))
3128210284Sjmallett            cvmx_dprintf("%s: Received OUT token on disabled endpoint %d\n", __FUNCTION__, endpoint_num);
3129210284Sjmallett    }
3130210284Sjmallett    if (usbc_doepint.s.setup)
3131210284Sjmallett    {
3132210284Sjmallett        /* SETUP Phase Done (SetUp)
3133210284Sjmallett            Applies to control OUT endpoints only.
3134210284Sjmallett            Indicates that the SETUP phase for the control endpoint is
3135210284Sjmallett            complete and no more back-to-back SETUP packets were
3136210284Sjmallett            received for the current control transfer. On this interrupt, the
3137210284Sjmallett            application can decode the received SETUP data packet. */
3138210284Sjmallett        __cvmx_usb_perform_callback(usb, usb->pipe + endpoint_num, NULL,
3139210284Sjmallett                                    CVMX_USB_CALLBACK_DEVICE_SETUP,
3140210284Sjmallett                                    CVMX_USB_COMPLETE_SUCCESS);
3141210284Sjmallett    }
3142210284Sjmallett    if (usbc_doepint.s.ahberr)
3143210284Sjmallett    {
3144210284Sjmallett        /* AHB Error (AHBErr)
3145210284Sjmallett            This is generated only in Internal DMA mode when there is an
3146210284Sjmallett            AHB error during an AHB read/write. The application can read
3147210284Sjmallett            the corresponding endpoint DMA address register to get the
3148210284Sjmallett            error address. */
3149210284Sjmallett        if (cvmx_unlikely(usb->init_flags & CVMX_USB_INITIALIZE_FLAGS_DEBUG_INFO))
3150210284Sjmallett            cvmx_dprintf("%s: AHB error on endpoint %d\n", __FUNCTION__, endpoint_num);
3151210284Sjmallett    }
3152210284Sjmallett    if (usbc_doepint.s.epdisbld)
3153210284Sjmallett    {
3154210284Sjmallett        /* Endpoint Disabled Interrupt (EPDisbld)
3155210284Sjmallett            This bit indicates that the endpoint is disabled per the
3156210284Sjmallett            application's request. */
3157210284Sjmallett        /* Nothing to do */
3158210284Sjmallett    }
3159210284Sjmallett    if (usbc_doepint.s.xfercompl)
3160210284Sjmallett    {
3161210284Sjmallett        /* Transfer Completed Interrupt (XferCompl)
3162210284Sjmallett            Indicates that the programmed transfer is complete on the AHB
3163210284Sjmallett            as well as on the USB, for this endpoint. */
3164210284Sjmallett        __cvmx_usb_perform_callback(usb, usb->pipe + endpoint_num, NULL,
3165210284Sjmallett                                    CVMX_USB_CALLBACK_TRANSFER_COMPLETE,
3166210284Sjmallett                                    CVMX_USB_COMPLETE_SUCCESS);
3167210284Sjmallett    }
3168210284Sjmallett
3169210284Sjmallett    CVMX_USB_RETURN(0);
3170210284Sjmallett}
3171210284Sjmallett
3172210284Sjmallett
3173210284Sjmallett/**
3174210284Sjmallett * Poll the device mode endpoints for status
3175210284Sjmallett *
3176210284Sjmallett * @param usb  USB device state populated by
3177210284Sjmallett *               cvmx_usb_initialize().
3178210284Sjmallett *
3179210284Sjmallett * @return Zero on success
3180210284Sjmallett */
3181210284Sjmallettstatic int __cvmx_usb_poll_endpoints(cvmx_usb_internal_state_t *usb)
3182210284Sjmallett{
3183210284Sjmallett    cvmx_usbcx_daint_t usbc_daint;
3184210284Sjmallett    int active_endpoints;
3185210284Sjmallett
3186210284Sjmallett    CVMX_USB_LOG_CALLED();
3187210284Sjmallett    CVMX_USB_LOG_PARAM("%p", usb);
3188210284Sjmallett
3189210284Sjmallett    usbc_daint.u32 = __cvmx_usb_read_csr32(usb, CVMX_USBCX_DAINT(usb->index));
3190210284Sjmallett    active_endpoints = usbc_daint.s.inepint | usbc_daint.s.outepint;
3191210284Sjmallett
3192210284Sjmallett    while (active_endpoints)
3193210284Sjmallett    {
3194210284Sjmallett        int endpoint;
3195210284Sjmallett        CVMX_CLZ(endpoint, active_endpoints);
3196210284Sjmallett        endpoint = 31 - endpoint;
3197210284Sjmallett        __cvmx_usb_poll_endpoint(usb, endpoint);
3198210284Sjmallett        active_endpoints ^= 1<<endpoint;
3199210284Sjmallett    }
3200210284Sjmallett
3201210284Sjmallett    CVMX_USB_RETURN(0);
3202210284Sjmallett}
3203210284Sjmallett
3204210284Sjmallett
3205210284Sjmallett/**
3206210284Sjmallett * Poll the USB block for status and call all needed callback
3207210284Sjmallett * handlers. This function is meant to be called in the interrupt
3208210284Sjmallett * handler for the USB controller. It can also be called
3209210284Sjmallett * periodically in a loop for non-interrupt based operation.
3210210284Sjmallett *
3211210284Sjmallett * @param state  USB device state populated by
3212210284Sjmallett *               cvmx_usb_initialize().
3213210284Sjmallett *
3214210284Sjmallett * @return CVMX_USB_SUCCESS or a negative error code defined in
3215210284Sjmallett *         cvmx_usb_status_t.
3216210284Sjmallett */
3217210284Sjmallettcvmx_usb_status_t cvmx_usb_poll(cvmx_usb_state_t *state)
3218210284Sjmallett{
3219210284Sjmallett    cvmx_usbcx_gintsts_t usbc_gintsts;
3220210284Sjmallett    cvmx_usb_internal_state_t *usb = (cvmx_usb_internal_state_t*)state;
3221210284Sjmallett
3222210284Sjmallett    CVMX_PREFETCH(usb, 0);
3223210284Sjmallett    CVMX_PREFETCH(usb, 1*128);
3224210284Sjmallett    CVMX_PREFETCH(usb, 2*128);
3225210284Sjmallett    CVMX_PREFETCH(usb, 3*128);
3226210284Sjmallett    CVMX_PREFETCH(usb, 4*128);
3227210284Sjmallett
3228210284Sjmallett    CVMX_USB_LOG_CALLED();
3229210284Sjmallett    CVMX_USB_LOG_PARAM("%p", state);
3230210284Sjmallett
3231210284Sjmallett    /* Read the pending interrupts */
3232210284Sjmallett    usbc_gintsts.u32 = __cvmx_usb_read_csr32(usb, CVMX_USBCX_GINTSTS(usb->index));
3233210284Sjmallett
3234210284Sjmallett    if (usbc_gintsts.s.wkupint)
3235210284Sjmallett    {
3236210284Sjmallett        /* Resume/Remote Wakeup Detected Interrupt (WkUpInt)
3237210284Sjmallett            In Device mode, this interrupt is asserted when a resume is
3238210284Sjmallett            detected on the USB. In Host mode, this interrupt is asserted
3239210284Sjmallett            when a remote wakeup is detected on the USB. */
3240210284Sjmallett        /* Octeon doesn't support suspend / resume */
3241210284Sjmallett    }
3242210284Sjmallett    if (usbc_gintsts.s.sessreqint)
3243210284Sjmallett    {
3244210284Sjmallett        /* Session Request/New Session Detected Interrupt (SessReqInt)
3245210284Sjmallett            In Host mode, this interrupt is asserted when a session request
3246210284Sjmallett            is detected from the device. In Device mode, this interrupt is
3247210284Sjmallett            asserted when the utmiotg_bvalid signal goes high. */
3248210284Sjmallett        /* Octeon doesn't support OTG */
3249210284Sjmallett    }
3250210284Sjmallett    if (usbc_gintsts.s.disconnint || usbc_gintsts.s.prtint)
3251210284Sjmallett    {
3252210284Sjmallett        cvmx_usbcx_hprt_t usbc_hprt;
3253210284Sjmallett        /* Disconnect Detected Interrupt (DisconnInt)
3254210284Sjmallett            Asserted when a device disconnect is detected. */
3255210284Sjmallett
3256210284Sjmallett        /* Host Port Interrupt (PrtInt)
3257210284Sjmallett            The core sets this bit to indicate a change in port status of one
3258210284Sjmallett            of the O2P USB core ports in Host mode. The application must
3259210284Sjmallett            read the Host Port Control and Status (HPRT) register to
3260210284Sjmallett            determine the exact event that caused this interrupt. The
3261210284Sjmallett            application must clear the appropriate status bit in the Host Port
3262210284Sjmallett            Control and Status register to clear this bit. */
3263210284Sjmallett
3264210284Sjmallett        /* Call the user's port callback */
3265210284Sjmallett        __cvmx_usb_perform_callback(usb, NULL, NULL,
3266210284Sjmallett                                    CVMX_USB_CALLBACK_PORT_CHANGED,
3267210284Sjmallett                                    CVMX_USB_COMPLETE_SUCCESS);
3268210284Sjmallett        /* Clear the port change bits */
3269210284Sjmallett        usbc_hprt.u32 = __cvmx_usb_read_csr32(usb, CVMX_USBCX_HPRT(usb->index));
3270210284Sjmallett        usbc_hprt.s.prtena = 0;
3271210284Sjmallett        __cvmx_usb_write_csr32(usb, CVMX_USBCX_HPRT(usb->index), usbc_hprt.u32);
3272210284Sjmallett    }
3273210284Sjmallett    if (usbc_gintsts.s.conidstschng)
3274210284Sjmallett    {
3275210284Sjmallett        /* Connector ID Status Change (ConIDStsChng)
3276210284Sjmallett            The core sets this bit when there is a change in connector ID
3277210284Sjmallett            status. */
3278210284Sjmallett        /* The USB core currently doesn't support dynamically changing from
3279210284Sjmallett            host to device mode */
3280210284Sjmallett    }
3281210284Sjmallett    if (usbc_gintsts.s.ptxfemp)
3282210284Sjmallett    {
3283210284Sjmallett        /* Periodic TxFIFO Empty (PTxFEmp)
3284210284Sjmallett            Asserted when the Periodic Transmit FIFO is either half or
3285210284Sjmallett            completely empty and there is space for at least one entry to be
3286210284Sjmallett            written in the Periodic Request Queue. The half or completely
3287210284Sjmallett            empty status is determined by the Periodic TxFIFO Empty Level
3288210284Sjmallett            bit in the Core AHB Configuration register
3289210284Sjmallett            (GAHBCFG.PTxFEmpLvl). */
3290210284Sjmallett        /* In DMA mode we don't care */
3291210284Sjmallett    }
3292210284Sjmallett    if (usbc_gintsts.s.hchint)
3293210284Sjmallett    {
3294210284Sjmallett        /* Host Channels Interrupt (HChInt)
3295210284Sjmallett            The core sets this bit to indicate that an interrupt is pending on
3296210284Sjmallett            one of the channels of the core (in Host mode). The application
3297210284Sjmallett            must read the Host All Channels Interrupt (HAINT) register to
3298210284Sjmallett            determine the exact number of the channel on which the
3299210284Sjmallett            interrupt occurred, and then read the corresponding Host
3300210284Sjmallett            Channel-n Interrupt (HCINTn) register to determine the exact
3301210284Sjmallett            cause of the interrupt. The application must clear the
3302210284Sjmallett            appropriate status bit in the HCINTn register to clear this bit. */
3303210284Sjmallett        cvmx_usbcx_haint_t usbc_haint;
3304210284Sjmallett        usbc_haint.u32 = __cvmx_usb_read_csr32(usb, CVMX_USBCX_HAINT(usb->index));
3305210284Sjmallett        while (usbc_haint.u32)
3306210284Sjmallett        {
3307210284Sjmallett            int channel;
3308210284Sjmallett            CVMX_CLZ(channel, usbc_haint.u32);
3309210284Sjmallett            channel = 31 - channel;
3310210284Sjmallett            __cvmx_usb_poll_channel(usb, channel);
3311210284Sjmallett            usbc_haint.u32 ^= 1<<channel;
3312210284Sjmallett        }
3313210284Sjmallett    }
3314210284Sjmallett    if (usbc_gintsts.s.fetsusp)
3315210284Sjmallett    {
3316210284Sjmallett        /* Data Fetch Suspended (FetSusp)
3317210284Sjmallett            This interrupt is valid only in DMA mode. This interrupt indicates
3318210284Sjmallett            that the core has stopped fetching data for IN endpoints due to
3319210284Sjmallett            the unavailability of TxFIFO space or Request Queue space.
3320210284Sjmallett            This interrupt is used by the application for an endpoint
3321210284Sjmallett            mismatch algorithm. */
3322210284Sjmallett        // FIXME
3323210284Sjmallett    }
3324210284Sjmallett    if (usbc_gintsts.s.incomplp)
3325210284Sjmallett    {
3326210284Sjmallett        /* Incomplete Periodic Transfer (incomplP)
3327210284Sjmallett            In Host mode, the core sets this interrupt bit when there are
3328210284Sjmallett            incomplete periodic transactions still pending which are
3329210284Sjmallett            scheduled for the current microframe.
3330210284Sjmallett            Incomplete Isochronous OUT Transfer (incompISOOUT)
3331210284Sjmallett            The Device mode, the core sets this interrupt to indicate that
3332210284Sjmallett            there is at least one isochronous OUT endpoint on which the
3333210284Sjmallett            transfer is not completed in the current microframe. This
3334210284Sjmallett            interrupt is asserted along with the End of Periodic Frame
3335210284Sjmallett            Interrupt (EOPF) bit in this register. */
3336210284Sjmallett        // FIXME
3337210284Sjmallett    }
3338210284Sjmallett    if (usbc_gintsts.s.incompisoin)
3339210284Sjmallett    {
3340210284Sjmallett        /* Incomplete Isochronous IN Transfer (incompISOIN)
3341210284Sjmallett            The core sets this interrupt to indicate that there is at least one
3342210284Sjmallett            isochronous IN endpoint on which the transfer is not completed
3343210284Sjmallett            in the current microframe. This interrupt is asserted along with
3344210284Sjmallett            the End of Periodic Frame Interrupt (EOPF) bit in this register. */
3345210284Sjmallett        // FIXME
3346210284Sjmallett    }
3347210284Sjmallett    if (usbc_gintsts.s.oepint)
3348210284Sjmallett    {
3349210284Sjmallett        /* OUT Endpoints Interrupt (OEPInt)
3350210284Sjmallett            The core sets this bit to indicate that an interrupt is pending on
3351210284Sjmallett            one of the OUT endpoints of the core (in Device mode). The
3352210284Sjmallett            application must read the Device All Endpoints Interrupt
3353210284Sjmallett            (DAINT) register to determine the exact number of the OUT
3354210284Sjmallett            endpoint on which the interrupt occurred, and then read the
3355210284Sjmallett            corresponding Device OUT Endpoint-n Interrupt (DOEPINTn)
3356210284Sjmallett            register to determine the exact cause of the interrupt. The
3357210284Sjmallett            application must clear the appropriate status bit in the
3358210284Sjmallett            corresponding DOEPINTn register to clear this bit. */
3359210284Sjmallett        __cvmx_usb_poll_endpoints(usb);
3360210284Sjmallett    }
3361210284Sjmallett    if (usbc_gintsts.s.iepint)
3362210284Sjmallett    {
3363210284Sjmallett        /* IN Endpoints Interrupt (IEPInt)
3364210284Sjmallett            The core sets this bit to indicate that an interrupt is pending on
3365210284Sjmallett            one of the IN endpoints of the core (in Device mode). The
3366210284Sjmallett            application must read the Device All Endpoints Interrupt
3367210284Sjmallett            (DAINT) register to determine the exact number of the IN
3368210284Sjmallett            endpoint on which the interrupt occurred, and then read the
3369210284Sjmallett            corresponding Device IN Endpoint-n Interrupt (DIEPINTn)
3370210284Sjmallett            register to determine the exact cause of the interrupt. The
3371210284Sjmallett            application must clear the appropriate status bit in the
3372210284Sjmallett            corresponding DIEPINTn register to clear this bit. */
3373210284Sjmallett        __cvmx_usb_poll_endpoints(usb);
3374210284Sjmallett    }
3375210284Sjmallett    if (usbc_gintsts.s.epmis)
3376210284Sjmallett    {
3377210284Sjmallett        /* Endpoint Mismatch Interrupt (EPMis)
3378210284Sjmallett            Indicates that an IN token has been received for a non-periodic
3379210284Sjmallett            endpoint, but the data for another endpoint is present in the top
3380210284Sjmallett            of the Non-Periodic Transmit FIFO and the IN endpoint
3381210284Sjmallett            mismatch count programmed by the application has expired. */
3382210284Sjmallett        // FIXME
3383210284Sjmallett    }
3384210284Sjmallett    if (usbc_gintsts.s.eopf)
3385210284Sjmallett    {
3386210284Sjmallett        /* End of Periodic Frame Interrupt (EOPF)
3387210284Sjmallett            Indicates that the period specified in the Periodic Frame Interval
3388210284Sjmallett            field of the Device Configuration register (DCFG.PerFrInt) has
3389210284Sjmallett            been reached in the current microframe. */
3390210284Sjmallett        // FIXME
3391210284Sjmallett    }
3392210284Sjmallett    if (usbc_gintsts.s.isooutdrop)
3393210284Sjmallett    {
3394210284Sjmallett        /* Isochronous OUT Packet Dropped Interrupt (ISOOutDrop)
3395210284Sjmallett            The core sets this bit when it fails to write an isochronous OUT
3396210284Sjmallett            packet into the RxFIFO because the RxFIFO doesn't have
3397210284Sjmallett            enough space to accommodate a maximum packet size packet
3398210284Sjmallett            for the isochronous OUT endpoint. */
3399210284Sjmallett        // FIXME
3400210284Sjmallett    }
3401210284Sjmallett    if (usbc_gintsts.s.enumdone)
3402210284Sjmallett    {
3403210284Sjmallett        /* Enumeration Done (EnumDone)
3404210284Sjmallett            The core sets this bit to indicate that speed enumeration is
3405210284Sjmallett            complete. The application must read the Device Status (DSTS)
3406210284Sjmallett            register to obtain the enumerated speed. */
3407210284Sjmallett        if (usb->init_flags & CVMX_USB_INITIALIZE_FLAGS_DEVICE_MODE)
3408210284Sjmallett        {
3409210284Sjmallett            cvmx_usbcx_dsts_t usbc_dsts;
3410210284Sjmallett            usbc_dsts.u32 = __cvmx_usb_read_csr32(usb, CVMX_USBCX_DSTS(usb->index));
3411210284Sjmallett            if (cvmx_unlikely(usb->init_flags & CVMX_USB_INITIALIZE_FLAGS_DEBUG_INFO))
3412210284Sjmallett                cvmx_dprintf("%s: USB%d Enumeration complete with %s speed\n",
3413210284Sjmallett                             __FUNCTION__, usb->index,
3414210284Sjmallett                             (usbc_dsts.s.enumspd == CVMX_USB_SPEED_HIGH) ? "high" :
3415210284Sjmallett                             (usbc_dsts.s.enumspd == CVMX_USB_SPEED_FULL) ? "full" :
3416210284Sjmallett                             "low");
3417210284Sjmallett            USB_SET_FIELD32(CVMX_USBCX_DIEPCTLX(0, usb->index),
3418210284Sjmallett                            cvmx_usbcx_diepctlx_t, mps,
3419210284Sjmallett                            (usbc_dsts.s.enumspd == CVMX_USB_SPEED_LOW) ? 3 : 0);
3420210284Sjmallett            USB_SET_FIELD32(CVMX_USBCX_DOEPCTLX(0, usb->index),
3421210284Sjmallett                            cvmx_usbcx_doepctlx_t, epena, 1);
3422210284Sjmallett        }
3423210284Sjmallett    }
3424210284Sjmallett    if (usbc_gintsts.s.usbrst)
3425210284Sjmallett    {
3426210284Sjmallett        /* USB Reset (USBRst)
3427210284Sjmallett            The core sets this bit to indicate that a reset is
3428210284Sjmallett            detected on the USB. */
3429210284Sjmallett        if (usb->init_flags & CVMX_USB_INITIALIZE_FLAGS_DEVICE_MODE)
3430210284Sjmallett        {
3431210284Sjmallett            if (cvmx_unlikely(usb->init_flags & CVMX_USB_INITIALIZE_FLAGS_DEBUG_INFO))
3432210284Sjmallett                cvmx_dprintf("%s: USB%d Reset complete\n", __FUNCTION__, usb->index);
3433210284Sjmallett            __cvmx_usb_device_reset_complete(usb);
3434210284Sjmallett        }
3435210284Sjmallett    }
3436210284Sjmallett    if (usbc_gintsts.s.nptxfemp)
3437210284Sjmallett    {
3438210284Sjmallett        /* Non-Periodic TxFIFO Empty (NPTxFEmp)
3439210284Sjmallett            This interrupt is asserted when the Non-Periodic TxFIFO is
3440210284Sjmallett            either half or completely empty, and there is space for at least
3441210284Sjmallett            one entry to be written to the Non-Periodic Transmit Request
3442210284Sjmallett            Queue. The half or completely empty status is determined by
3443210284Sjmallett            the Non-Periodic TxFIFO Empty Level bit in the Core AHB
3444210284Sjmallett            Configuration register (GAHBCFG.NPTxFEmpLvl). */
3445210284Sjmallett        /* In DMA mode this is handled by hardware */
3446210284Sjmallett    }
3447210284Sjmallett    if (usbc_gintsts.s.rxflvl)
3448210284Sjmallett    {
3449210284Sjmallett        /* RxFIFO Non-Empty (RxFLvl)
3450210284Sjmallett            Indicates that there is at least one packet pending to be read
3451210284Sjmallett            from the RxFIFO. */
3452210284Sjmallett        /* In DMA mode this is handled by hardware */
3453210284Sjmallett    }
3454210284Sjmallett    if (usbc_gintsts.s.sof)
3455210284Sjmallett    {
3456210284Sjmallett        /* Start of (micro)Frame (Sof)
3457210284Sjmallett            In Host mode, the core sets this bit to indicate that an SOF
3458210284Sjmallett            (FS), micro-SOF (HS), or Keep-Alive (LS) is transmitted on the
3459210284Sjmallett            USB. The application must write a 1 to this bit to clear the
3460210284Sjmallett            interrupt.
3461210284Sjmallett            In Device mode, in the core sets this bit to indicate that an SOF
3462210284Sjmallett            token has been received on the USB. The application can read
3463210284Sjmallett            the Device Status register to get the current (micro)frame
3464210284Sjmallett            number. This interrupt is seen only when the core is operating
3465210284Sjmallett            at either HS or FS. */
3466210284Sjmallett    }
3467210284Sjmallett    if (usbc_gintsts.s.otgint)
3468210284Sjmallett    {
3469210284Sjmallett        /* OTG Interrupt (OTGInt)
3470210284Sjmallett            The core sets this bit to indicate an OTG protocol event. The
3471210284Sjmallett            application must read the OTG Interrupt Status (GOTGINT)
3472210284Sjmallett            register to determine the exact event that caused this interrupt.
3473210284Sjmallett            The application must clear the appropriate status bit in the
3474210284Sjmallett            GOTGINT register to clear this bit. */
3475210284Sjmallett        /* Octeon doesn't support OTG, so ignore */
3476210284Sjmallett    }
3477210284Sjmallett    if (usbc_gintsts.s.modemis)
3478210284Sjmallett    {
3479210284Sjmallett        /* Mode Mismatch Interrupt (ModeMis)
3480210284Sjmallett            The core sets this bit when the application is trying to access:
3481210284Sjmallett            * A Host mode register, when the core is operating in Device
3482210284Sjmallett            mode
3483210284Sjmallett            * A Device mode register, when the core is operating in Host
3484210284Sjmallett            mode
3485210284Sjmallett            The register access is completed on the AHB with an OKAY
3486210284Sjmallett            response, but is ignored by the core internally and doesn't
3487210284Sjmallett            affect the operation of the core. */
3488210284Sjmallett        /* Ignored for now */
3489210284Sjmallett    }
3490210284Sjmallett
3491210284Sjmallett    __cvmx_usb_schedule(usb, usbc_gintsts.s.sof);
3492210284Sjmallett
3493210284Sjmallett    /* Clear the interrupts now that we know about them */
3494210284Sjmallett    __cvmx_usb_write_csr32(usb, CVMX_USBCX_GINTSTS(usb->index), usbc_gintsts.u32);
3495210284Sjmallett
3496210284Sjmallett    CVMX_USB_RETURN(CVMX_USB_SUCCESS);
3497210284Sjmallett}
3498210284Sjmallett
3499210284Sjmallett
3500210284Sjmallett/**
3501210284Sjmallett * Enable an endpoint for use in device mode. After this call
3502210284Sjmallett * transactions will be allowed over the endpoint. This must be
3503210284Sjmallett * called after every usb reset.
3504210284Sjmallett *
3505210284Sjmallett * @param state  USB device state populated by
3506210284Sjmallett *               cvmx_usb_initialize().
3507210284Sjmallett * @param endpoint_num
3508210284Sjmallett *               The endpoint number to enable (0-4)
3509210284Sjmallett * @param transfer_type
3510210284Sjmallett *               USB transfer type of this endpoint
3511210284Sjmallett * @param transfer_dir
3512210284Sjmallett *               Direction of transfer relative to Octeon
3513210284Sjmallett * @param max_packet_size
3514210284Sjmallett *               Maximum packet size support by this endpoint
3515210284Sjmallett * @param buffer Buffer to send/receive
3516210284Sjmallett * @param buffer_length
3517210284Sjmallett *               Length of the buffer in bytes
3518210284Sjmallett *
3519210284Sjmallett * @return CVMX_USB_SUCCESS or a negative error code defined in
3520210284Sjmallett *         cvmx_usb_status_t.
3521210284Sjmallett */
3522210284Sjmallettcvmx_usb_status_t cvmx_usb_device_enable_endpoint(cvmx_usb_state_t *state,
3523210284Sjmallett                                                  int endpoint_num,
3524210284Sjmallett                                                  cvmx_usb_transfer_t transfer_type,
3525210284Sjmallett                                                  cvmx_usb_direction_t transfer_dir,
3526210284Sjmallett                                                  int max_packet_size,
3527210284Sjmallett                                                  uint64_t buffer,
3528210284Sjmallett                                                  int buffer_length)
3529210284Sjmallett{
3530210284Sjmallett    cvmx_usb_internal_state_t *usb = (cvmx_usb_internal_state_t*)state;
3531210284Sjmallett
3532210284Sjmallett    CVMX_USB_LOG_CALLED();
3533210284Sjmallett    CVMX_USB_LOG_PARAM("%p", state);
3534210284Sjmallett    CVMX_USB_LOG_PARAM("%d", endpoint_num);
3535210284Sjmallett    CVMX_USB_LOG_PARAM("%d", transfer_type);
3536210284Sjmallett    CVMX_USB_LOG_PARAM("%d", transfer_dir);
3537210284Sjmallett    CVMX_USB_LOG_PARAM("%d", max_packet_size);
3538210284Sjmallett    CVMX_USB_LOG_PARAM("0x%llx", (unsigned long long)buffer);
3539210284Sjmallett    CVMX_USB_LOG_PARAM("%d", buffer_length);
3540210284Sjmallett
3541210284Sjmallett    if (cvmx_unlikely((endpoint_num < 0) || (endpoint_num > 4)))
3542210284Sjmallett        CVMX_USB_RETURN(CVMX_USB_INVALID_PARAM);
3543210284Sjmallett    if (cvmx_unlikely(transfer_type > CVMX_USB_TRANSFER_INTERRUPT))
3544210284Sjmallett        CVMX_USB_RETURN(CVMX_USB_INVALID_PARAM);
3545210284Sjmallett    if (cvmx_unlikely((transfer_dir != CVMX_USB_DIRECTION_OUT) &&
3546210284Sjmallett        (transfer_dir != CVMX_USB_DIRECTION_IN)))
3547210284Sjmallett        CVMX_USB_RETURN(CVMX_USB_INVALID_PARAM);
3548210284Sjmallett    if (cvmx_unlikely((max_packet_size < 0) || (max_packet_size > 512)))
3549210284Sjmallett        CVMX_USB_RETURN(CVMX_USB_INVALID_PARAM);
3550210284Sjmallett    if (cvmx_unlikely(!buffer))
3551210284Sjmallett        CVMX_USB_RETURN(CVMX_USB_INVALID_PARAM);
3552210284Sjmallett    if (cvmx_unlikely(buffer_length < 0))
3553210284Sjmallett        CVMX_USB_RETURN(CVMX_USB_INVALID_PARAM);
3554210284Sjmallett    if (cvmx_unlikely((usb->init_flags & CVMX_USB_INITIALIZE_FLAGS_DEVICE_MODE) == 0))
3555210284Sjmallett        CVMX_USB_RETURN(CVMX_USB_INCORRECT_MODE);
3556210284Sjmallett
3557210284Sjmallett    if (transfer_dir == CVMX_USB_DIRECTION_IN)
3558210284Sjmallett    {
3559210284Sjmallett        cvmx_usbcx_doepctlx_t usbc_doepctl;
3560210284Sjmallett        cvmx_usbcx_doeptsizx_t usbc_doeptsiz;
3561210284Sjmallett
3562210284Sjmallett        /* Setup the locations the DMA engines use  */
3563210284Sjmallett        __cvmx_usb_write_csr64(usb, CVMX_USBNX_DMA0_INB_CHN0(usb->index) + endpoint_num*8, buffer);
3564210284Sjmallett        usbc_doeptsiz.u32 = 0;
3565210284Sjmallett        usbc_doeptsiz.s.mc = 1; // FIXME
3566210284Sjmallett        usbc_doeptsiz.s.pktcnt = (buffer_length + max_packet_size - 1) / max_packet_size;
3567210284Sjmallett        if (usbc_doeptsiz.s.pktcnt == 0)
3568210284Sjmallett            usbc_doeptsiz.s.pktcnt = 1;
3569210284Sjmallett        usbc_doeptsiz.s.xfersize = buffer_length;
3570210284Sjmallett        __cvmx_usb_write_csr32(usb, CVMX_USBCX_DOEPTSIZX(endpoint_num, usb->index), usbc_doeptsiz.u32);
3571210284Sjmallett
3572210284Sjmallett        usbc_doepctl.u32 = 0;
3573210284Sjmallett        usbc_doepctl.s.epena = 1;
3574210284Sjmallett        usbc_doepctl.s.setd1pid = 0; // FIXME
3575210284Sjmallett        usbc_doepctl.s.setd0pid = 0; // FIXME
3576210284Sjmallett        usbc_doepctl.s.cnak = 1;
3577210284Sjmallett        usbc_doepctl.s.eptype = transfer_type;
3578210284Sjmallett        usbc_doepctl.s.usbactep = 1;
3579210284Sjmallett        usbc_doepctl.s.mps = max_packet_size;
3580210284Sjmallett        __cvmx_usb_write_csr32(usb, CVMX_USBCX_DOEPCTLX(endpoint_num, usb->index), usbc_doepctl.u32);
3581210284Sjmallett    }
3582210284Sjmallett    else
3583210284Sjmallett    {
3584210284Sjmallett        cvmx_usbcx_diepctlx_t usbc_diepctl;
3585210284Sjmallett        cvmx_usbcx_dieptsizx_t usbc_dieptsiz;
3586210284Sjmallett
3587210284Sjmallett        /* Setup the locations the DMA engines use  */
3588210284Sjmallett        __cvmx_usb_write_csr64(usb, CVMX_USBNX_DMA0_OUTB_CHN0(usb->index) + endpoint_num*8, buffer);
3589210284Sjmallett        usbc_dieptsiz.u32 = 0;
3590210284Sjmallett        usbc_dieptsiz.s.mc = 1; // FIXME
3591210284Sjmallett        usbc_dieptsiz.s.pktcnt = (buffer_length + max_packet_size - 1) / max_packet_size;
3592210284Sjmallett        if (usbc_dieptsiz.s.pktcnt == 0)
3593210284Sjmallett            usbc_dieptsiz.s.pktcnt = 1;
3594210284Sjmallett        usbc_dieptsiz.s.xfersize = buffer_length;
3595210284Sjmallett        __cvmx_usb_write_csr32(usb, CVMX_USBCX_DIEPTSIZX(endpoint_num, usb->index), usbc_dieptsiz.u32);
3596210284Sjmallett
3597210284Sjmallett        usbc_diepctl.u32 = 0;
3598210284Sjmallett        usbc_diepctl.s.epena = 1;
3599210284Sjmallett        usbc_diepctl.s.setd1pid = 0; // FIXME
3600210284Sjmallett        usbc_diepctl.s.setd0pid = 0; // FIXME
3601210284Sjmallett        usbc_diepctl.s.cnak = 1;
3602210284Sjmallett        if ((transfer_type == CVMX_USB_TRANSFER_INTERRUPT) ||
3603210284Sjmallett            (transfer_type == CVMX_USB_TRANSFER_ISOCHRONOUS))
3604210284Sjmallett            usbc_diepctl.s.txfnum = endpoint_num; // FIXME
3605210284Sjmallett        else
3606210284Sjmallett            usbc_diepctl.s.txfnum = 0;
3607210284Sjmallett        usbc_diepctl.s.eptype = transfer_type;
3608210284Sjmallett        usbc_diepctl.s.usbactep = 1;
3609210284Sjmallett        usbc_diepctl.s.nextep = endpoint_num - 1; // FIXME
3610210284Sjmallett        usbc_diepctl.s.mps = max_packet_size;
3611210284Sjmallett        __cvmx_usb_write_csr32(usb, CVMX_USBCX_DIEPCTLX(endpoint_num, usb->index), usbc_diepctl.u32);
3612210284Sjmallett    }
3613210284Sjmallett
3614210284Sjmallett    CVMX_USB_RETURN(CVMX_USB_SUCCESS);
3615210284Sjmallett}
3616210284Sjmallett
3617210284Sjmallett
3618210284Sjmallett/**
3619210284Sjmallett * Disable an endpoint in device mode.
3620210284Sjmallett *
3621210284Sjmallett * @param state  USB device state populated by
3622210284Sjmallett *               cvmx_usb_initialize().
3623210284Sjmallett * @param endpoint_num
3624210284Sjmallett *               The endpoint number to disable (0-4)
3625210284Sjmallett *
3626210284Sjmallett * @return CVMX_USB_SUCCESS or a negative error code defined in
3627210284Sjmallett *         cvmx_usb_status_t.
3628210284Sjmallett */
3629210284Sjmallettcvmx_usb_status_t cvmx_usb_device_disable_endpoint(cvmx_usb_state_t *state,
3630210284Sjmallett                                                   int endpoint_num)
3631210284Sjmallett{
3632210284Sjmallett    cvmx_usb_internal_state_t *usb = (cvmx_usb_internal_state_t*)state;
3633210284Sjmallett
3634210284Sjmallett    CVMX_USB_LOG_CALLED();
3635210284Sjmallett    CVMX_USB_LOG_PARAM("%p", state);
3636210284Sjmallett    CVMX_USB_LOG_PARAM("%d", endpoint_num);
3637210284Sjmallett
3638210284Sjmallett    if (cvmx_unlikely((endpoint_num < 0) || (endpoint_num > 4)))
3639210284Sjmallett        CVMX_USB_RETURN(CVMX_USB_INVALID_PARAM);
3640210284Sjmallett    if (cvmx_unlikely((usb->init_flags & CVMX_USB_INITIALIZE_FLAGS_DEVICE_MODE) == 0))
3641210284Sjmallett        CVMX_USB_RETURN(CVMX_USB_INCORRECT_MODE);
3642210284Sjmallett
3643210284Sjmallett    USB_SET_FIELD32(CVMX_USBCX_DOEPCTLX(endpoint_num, usb->index),
3644210284Sjmallett                    cvmx_usbcx_doepctlx_t, epdis, 1);
3645210284Sjmallett    USB_SET_FIELD32(CVMX_USBCX_DIEPCTLX(endpoint_num, usb->index),
3646210284Sjmallett                    cvmx_usbcx_diepctlx_t, epdis, 1);
3647210284Sjmallett
3648210284Sjmallett    CVMX_USB_RETURN(CVMX_USB_SUCCESS);
3649210284Sjmallett}
3650210284Sjmallett
3651210311Sjmallettextern void cvmx_usb_set_toggle(cvmx_usb_state_t *state, int endpoint_num, int toggle)
3652210311Sjmallett{
3653210311Sjmallett    cvmx_usb_internal_state_t *usb = (cvmx_usb_internal_state_t*)state;
3654210311Sjmallett    cvmx_usb_pipe_t *pipe = usb->pipe + endpoint_num;
3655210311Sjmallett
3656210311Sjmallett    pipe->pid_toggle = !!toggle;
3657210311Sjmallett}
3658210311Sjmallett
3659210311Sjmallettextern int cvmx_usb_get_toggle(cvmx_usb_state_t *state, int endpoint_num)
3660210311Sjmallett{
3661210311Sjmallett    cvmx_usb_internal_state_t *usb = (cvmx_usb_internal_state_t*)state;
3662210311Sjmallett    cvmx_usb_pipe_t *pipe = usb->pipe + endpoint_num;
3663210311Sjmallett
3664210311Sjmallett    if (pipe->pid_toggle)
3665210311Sjmallett	    return (1);
3666210311Sjmallett    return (0);
3667210311Sjmallett}
3668