提交 b164935b 编写于 作者: A Aaro Koskinen 提交者: Greg Kroah-Hartman

staging: MIPS: add Octeon USB HCD support

Add support for Octeon USB HCD. Tested on EdgeRouter Lite with USB
mass storage.

The driver has been extracted from GPL sources of EdgeRouter Lite firmware
(based on Linux 2.6.32.13). Some minor fixes and cleanups have been done
to make it work with 3.10-rc3.

$ uname -a
Linux (none) 3.10.0-rc3-edge-00005-g86cb5bc #41 SMP PREEMPT Sat Jun 1 20:41:46 EEST 2013 mips64 GNU/Linux
$ modprobe octeon-usb
[   37.971683] octeon_usb: module is from the staging directory, the quality is unknown, you have been warned.
[   37.983649] OcteonUSB: Detected 1 ports
[   37.999360] OcteonUSB OcteonUSB.0: Octeon Host Controller
[   38.004847] OcteonUSB OcteonUSB.0: new USB bus registered, assigned bus number 1
[   38.012332] OcteonUSB OcteonUSB.0: irq 122, io mem 0x00000000
[   38.019970] hub 1-0:1.0: USB hub found
[   38.023851] hub 1-0:1.0: 1 port detected
[   38.028101] OcteonUSB: Registered HCD for port 0 on irq 122
[   38.391443] usb 1-1: new high-speed USB device number 2 using OcteonUSB
[   38.586922] usb-storage 1-1:1.0: USB Mass Storage device detected
[   38.597375] scsi0 : usb-storage 1-1:1.0
[   39.604111] scsi 0:0:0:0: Direct-Access              USB DISK 2.0     PMAP PQ: 0 ANSI: 4
[   39.619113] sd 0:0:0:0: [sda] 7579008 512-byte logical blocks: (3.88 GB/3.61 GiB)
[   39.630696] sd 0:0:0:0: [sda] Write Protect is off
[   39.635945] sd 0:0:0:0: [sda] No Caching mode page present
[   39.641464] sd 0:0:0:0: [sda] Assuming drive cache: write through
[   39.651341] sd 0:0:0:0: [sda] No Caching mode page present
[   39.656917] sd 0:0:0:0: [sda] Assuming drive cache: write through
[   39.664296]  sda: sda1 sda2
[   39.675574] sd 0:0:0:0: [sda] No Caching mode page present
[   39.681093] sd 0:0:0:0: [sda] Assuming drive cache: write through
[   39.687223] sd 0:0:0:0: [sda] Attached SCSI removable disk
Signed-off-by: NAaro Koskinen <aaro.koskinen@iki.fi>
Cc: David Daney <ddaney.cavm@gmail.com>
Signed-off-by: NGreg Kroah-Hartman <gregkh@linuxfoundation.org>
上级 51dd7d29
......@@ -62,6 +62,8 @@ source "drivers/staging/line6/Kconfig"
source "drivers/staging/octeon/Kconfig"
source "drivers/staging/octeon-usb/Kconfig"
source "drivers/staging/serqt_usb2/Kconfig"
source "drivers/staging/vt6655/Kconfig"
......
......@@ -25,6 +25,7 @@ obj-$(CONFIG_LINE6_USB) += line6/
obj-$(CONFIG_NETLOGIC_XLR_NET) += netlogic/
obj-$(CONFIG_USB_SERIAL_QUATECH2) += serqt_usb2/
obj-$(CONFIG_OCTEON_ETHERNET) += octeon/
obj-$(CONFIG_OCTEON_USB) += octeon-usb/
obj-$(CONFIG_VT6655) += vt6655/
obj-$(CONFIG_VT6656) += vt6656/
obj-$(CONFIG_VME_BUS) += vme/
......
config OCTEON_USB
tristate "Cavium Networks Octeon USB support"
depends on CPU_CAVIUM_OCTEON && USB
help
This driver supports USB host controller on some Cavium
Networks' products in the Octeon family.
To compile this driver as a module, choose M here. The module
will be called octeon-usb.
obj-${CONFIG_OCTEON_USB} := octeon-usb.o
octeon-usb-y := octeon-hcd.o
octeon-usb-y += cvmx-usb.o
This driver is functional and has been tested on EdgeRouter Lite with
USB mass storage.
TODO:
- kernel coding style
- checkpatch warnings
- dead code elimination
- device tree bindings
- possibly eliminate the extra "hardware abstraction layer"
Contact: Aaro Koskinen <aaro.koskinen@iki.fi>
/***********************license start***************
* Copyright (c) 2003-2010 Cavium Networks (support@cavium.com). All rights
* reserved.
*
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided
* with the distribution.
* * Neither the name of Cavium Networks nor the names of
* its contributors may be used to endorse or promote products
* derived from this software without specific prior written
* permission.
* This Software, including technical data, may be subject to U.S. export control
* laws, including the U.S. Export Administration Act and its associated
* regulations, and may be subject to export or import regulations in other
* countries.
* TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SOFTWARE IS PROVIDED "AS IS"
* AND WITH ALL FAULTS AND CAVIUM NETWORKS MAKES NO PROMISES, REPRESENTATIONS OR
* WARRANTIES, EITHER EXPRESS, IMPLIED, STATUTORY, OR OTHERWISE, WITH RESPECT TO
* THE SOFTWARE, INCLUDING ITS CONDITION, ITS CONFORMITY TO ANY REPRESENTATION OR
* DESCRIPTION, OR THE EXISTENCE OF ANY LATENT OR PATENT DEFECTS, AND CAVIUM
* SPECIFICALLY DISCLAIMS ALL IMPLIED (IF ANY) WARRANTIES OF TITLE,
* MERCHANTABILITY, NONINFRINGEMENT, FITNESS FOR A PARTICULAR PURPOSE, LACK OF
* VIRUSES, ACCURACY OR COMPLETENESS, QUIET ENJOYMENT, QUIET POSSESSION OR
* CORRESPONDENCE TO DESCRIPTION. THE ENTIRE RISK ARISING OUT OF USE OR
* PERFORMANCE OF THE SOFTWARE LIES WITH YOU.
***********************license end**************************************/
/**
* @file
*
* "cvmx-usb.c" defines a set of low level USB functions to help
* developers create Octeon USB drivers for various operating
* systems. These functions provide a generic API to the Octeon
* USB blocks, hiding the internal hardware specific
* operations.
*
* <hr>$Revision: 32636 $<hr>
*/
#include <linux/delay.h>
#include <asm/octeon/cvmx.h>
#include <asm/octeon/octeon.h>
#include <asm/octeon/cvmx-sysinfo.h>
#include "cvmx-usbnx-defs.h"
#include "cvmx-usbcx-defs.h"
#include "cvmx-usb.h"
#include <asm/octeon/cvmx-helper.h>
#include <asm/octeon/cvmx-helper-board.h>
#define CVMX_PREFETCH0(address) CVMX_PREFETCH(address, 0)
#define CVMX_PREFETCH128(address) CVMX_PREFETCH(address, 128)
// a normal prefetch
#define CVMX_PREFETCH(address, offset) CVMX_PREFETCH_PREF0(address, offset)
// normal prefetches that use the pref instruction
#define CVMX_PREFETCH_PREFX(X, address, offset) asm volatile ("pref %[type], %[off](%[rbase])" : : [rbase] "d" (address), [off] "I" (offset), [type] "n" (X))
#define CVMX_PREFETCH_PREF0(address, offset) CVMX_PREFETCH_PREFX(0, address, offset)
#define CVMX_CLZ(result, input) asm ("clz %[rd],%[rs]" : [rd] "=d" (result) : [rs] "d" (input))
#define cvmx_likely likely
#define cvmx_wait_usec udelay
#define cvmx_unlikely unlikely
#define cvmx_le16_to_cpu le16_to_cpu
#define MAX_RETRIES 3 /* Maximum number of times to retry failed transactions */
#define MAX_PIPES 32 /* Maximum number of pipes that can be open at once */
#define MAX_TRANSACTIONS 256 /* Maximum number of outstanding transactions across all pipes */
#define MAX_CHANNELS 8 /* Maximum number of hardware channels supported by the USB block */
#define MAX_USB_ADDRESS 127 /* The highest valid USB device address */
#define MAX_USB_ENDPOINT 15 /* The highest valid USB endpoint number */
#define MAX_USB_HUB_PORT 15 /* The highest valid port number on a hub */
#define MAX_TRANSFER_BYTES ((1<<19)-1) /* The low level hardware can transfer a maximum of this number of bytes in each transfer. The field is 19 bits wide */
#define MAX_TRANSFER_PACKETS ((1<<10)-1) /* The low level hardware can transfer a maximum of this number of packets in each transfer. The field is 10 bits wide */
/* These defines disable the normal read and write csr. This is so I can add
extra debug stuff to the usb specific version and I won't use the normal
version by mistake */
#define cvmx_read_csr use_cvmx_usb_read_csr64_instead_of_cvmx_read_csr
#define cvmx_write_csr use_cvmx_usb_write_csr64_instead_of_cvmx_write_csr
typedef enum
{
__CVMX_USB_TRANSACTION_FLAGS_IN_USE = 1<<16,
} cvmx_usb_transaction_flags_t;
enum {
USB_CLOCK_TYPE_REF_12,
USB_CLOCK_TYPE_REF_24,
USB_CLOCK_TYPE_REF_48,
USB_CLOCK_TYPE_CRYSTAL_12,
};
/**
* Logical transactions may take numerous low level
* transactions, especially when splits are concerned. This
* enum represents all of the possible stages a transaction can
* be in. Note that split completes are always even. This is so
* the NAK handler can backup to the previous low level
* transaction with a simple clearing of bit 0.
*/
typedef enum
{
CVMX_USB_STAGE_NON_CONTROL,
CVMX_USB_STAGE_NON_CONTROL_SPLIT_COMPLETE,
CVMX_USB_STAGE_SETUP,
CVMX_USB_STAGE_SETUP_SPLIT_COMPLETE,
CVMX_USB_STAGE_DATA,
CVMX_USB_STAGE_DATA_SPLIT_COMPLETE,
CVMX_USB_STAGE_STATUS,
CVMX_USB_STAGE_STATUS_SPLIT_COMPLETE,
} cvmx_usb_stage_t;
/**
* This structure describes each pending USB transaction
* regardless of type. These are linked together to form a list
* of pending requests for a pipe.
*/
typedef struct cvmx_usb_transaction
{
struct cvmx_usb_transaction *prev; /**< Transaction before this one in the pipe */
struct cvmx_usb_transaction *next; /**< Transaction after this one in the pipe */
cvmx_usb_transfer_t type; /**< Type of transaction, duplicated of the pipe */
cvmx_usb_transaction_flags_t flags; /**< State flags for this transaction */
uint64_t buffer; /**< User's physical buffer address to read/write */
int buffer_length; /**< Size of the user's buffer in bytes */
uint64_t control_header; /**< For control transactions, physical address of the 8 byte standard header */
int iso_start_frame; /**< For ISO transactions, the starting frame number */
int iso_number_packets; /**< For ISO transactions, the number of packets in the request */
cvmx_usb_iso_packet_t *iso_packets; /**< For ISO transactions, the sub packets in the request */
int xfersize;
int pktcnt;
int retries;
int actual_bytes; /**< Actual bytes transfer for this transaction */
cvmx_usb_stage_t stage; /**< For control transactions, the current stage */
cvmx_usb_callback_func_t callback; /**< User's callback function when complete */
void *callback_data; /**< User's data */
} cvmx_usb_transaction_t;
/**
* A pipe represents a virtual connection between Octeon and some
* USB device. It contains a list of pending request to the device.
*/
typedef struct cvmx_usb_pipe
{
struct cvmx_usb_pipe *prev; /**< Pipe before this one in the list */
struct cvmx_usb_pipe *next; /**< Pipe after this one in the list */
cvmx_usb_transaction_t *head; /**< The first pending transaction */
cvmx_usb_transaction_t *tail; /**< The last pending transaction */
uint64_t interval; /**< For periodic pipes, the interval between packets in frames */
uint64_t next_tx_frame; /**< The next frame this pipe is allowed to transmit on */
cvmx_usb_pipe_flags_t flags; /**< State flags for this pipe */
cvmx_usb_speed_t device_speed; /**< Speed of device connected to this pipe */
cvmx_usb_transfer_t transfer_type; /**< Type of transaction supported by this pipe */
cvmx_usb_direction_t transfer_dir; /**< IN or OUT. Ignored for Control */
int multi_count; /**< Max packet in a row for the device */
uint16_t max_packet; /**< The device's maximum packet size in bytes */
uint8_t device_addr; /**< USB device address at other end of pipe */
uint8_t endpoint_num; /**< USB endpoint number at other end of pipe */
uint8_t hub_device_addr; /**< Hub address this device is connected to */
uint8_t hub_port; /**< Hub port this device is connected to */
uint8_t pid_toggle; /**< This toggles between 0/1 on every packet send to track the data pid needed */
uint8_t channel; /**< Hardware DMA channel for this pipe */
int8_t split_sc_frame; /**< The low order bits of the frame number the split complete should be sent on */
} cvmx_usb_pipe_t;
typedef struct
{
cvmx_usb_pipe_t *head; /**< Head of the list, or NULL if empty */
cvmx_usb_pipe_t *tail; /**< Tail if the list, or NULL if empty */
} cvmx_usb_pipe_list_t;
typedef struct
{
struct
{
int channel;
int size;
uint64_t address;
} entry[MAX_CHANNELS+1];
int head;
int tail;
} cvmx_usb_tx_fifo_t;
/**
* The state of the USB block is stored in this structure
*/
typedef struct
{
int init_flags; /**< Flags passed to initialize */
int index; /**< Which USB block this is for */
int idle_hardware_channels; /**< Bit set for every idle hardware channel */
cvmx_usbcx_hprt_t usbcx_hprt; /**< Stored port status so we don't need to read a CSR to determine splits */
cvmx_usb_pipe_t *pipe_for_channel[MAX_CHANNELS]; /**< Map channels to pipes */
cvmx_usb_transaction_t *free_transaction_head; /**< List of free transactions head */
cvmx_usb_transaction_t *free_transaction_tail; /**< List of free transactions tail */
cvmx_usb_pipe_t pipe[MAX_PIPES]; /**< Storage for pipes */
cvmx_usb_transaction_t transaction[MAX_TRANSACTIONS]; /**< Storage for transactions */
cvmx_usb_callback_func_t callback[__CVMX_USB_CALLBACK_END]; /**< User global callbacks */
void *callback_data[__CVMX_USB_CALLBACK_END]; /**< User data for each callback */
int indent; /**< Used by debug output to indent functions */
cvmx_usb_port_status_t port_status; /**< Last port status used for change notification */
cvmx_usb_pipe_list_t free_pipes; /**< List of all pipes that are currently closed */
cvmx_usb_pipe_list_t idle_pipes; /**< List of open pipes that have no transactions */
cvmx_usb_pipe_list_t active_pipes[4]; /**< Active pipes indexed by transfer type */
uint64_t frame_number; /**< Increments every SOF interrupt for time keeping */
cvmx_usb_transaction_t *active_split; /**< Points to the current active split, or NULL */
cvmx_usb_tx_fifo_t periodic;
cvmx_usb_tx_fifo_t nonperiodic;
} cvmx_usb_internal_state_t;
/* This macro logs out whenever a function is called if debugging is on */
#define CVMX_USB_LOG_CALLED() \
if (cvmx_unlikely(usb->init_flags & CVMX_USB_INITIALIZE_FLAGS_DEBUG_CALLS)) \
cvmx_dprintf("%*s%s: called\n", 2*usb->indent++, "", __FUNCTION__);
/* This macro logs out each function parameter if debugging is on */
#define CVMX_USB_LOG_PARAM(format, param) \
if (cvmx_unlikely(usb->init_flags & CVMX_USB_INITIALIZE_FLAGS_DEBUG_CALLS)) \
cvmx_dprintf("%*s%s: param %s = " format "\n", 2*usb->indent, "", __FUNCTION__, #param, param);
/* This macro logs out when a function returns a value */
#define CVMX_USB_RETURN(v) \
do { \
typeof(v) r = v; \
if (cvmx_unlikely(usb->init_flags & CVMX_USB_INITIALIZE_FLAGS_DEBUG_CALLS)) \
cvmx_dprintf("%*s%s: returned %s(%d)\n", 2*--usb->indent, "", __FUNCTION__, #v, r); \
return r; \
} while (0);
/* This macro logs out when a function doesn't return a value */
#define CVMX_USB_RETURN_NOTHING() \
do { \
if (cvmx_unlikely(usb->init_flags & CVMX_USB_INITIALIZE_FLAGS_DEBUG_CALLS)) \
cvmx_dprintf("%*s%s: returned\n", 2*--usb->indent, "", __FUNCTION__); \
return; \
} while (0);
/* This macro spins on a field waiting for it to reach a value */
#define CVMX_WAIT_FOR_FIELD32(address, type, field, op, value, timeout_usec)\
({int result; \
do { \
uint64_t done = cvmx_get_cycle() + (uint64_t)timeout_usec * \
octeon_get_clock_rate() / 1000000; \
type c; \
while (1) \
{ \
c.u32 = __cvmx_usb_read_csr32(usb, address); \
if (c.s.field op (value)) { \
result = 0; \
break; \
} else if (cvmx_get_cycle() > done) { \
result = -1; \
break; \
} else \
cvmx_wait(100); \
} \
} while (0); \
result;})
/* This macro logically sets a single field in a CSR. It does the sequence
read, modify, and write */
#define USB_SET_FIELD32(address, type, field, value)\
do { \
type c; \
c.u32 = __cvmx_usb_read_csr32(usb, address);\
c.s.field = value; \
__cvmx_usb_write_csr32(usb, address, c.u32);\
} while (0)
/* Returns the IO address to push/pop stuff data from the FIFOs */
#define USB_FIFO_ADDRESS(channel, usb_index) (CVMX_USBCX_GOTGCTL(usb_index) + ((channel)+1)*0x1000)
static int octeon_usb_get_clock_type(void)
{
switch (cvmx_sysinfo_get()->board_type) {
case CVMX_BOARD_TYPE_BBGW_REF:
case CVMX_BOARD_TYPE_LANAI2_A:
case CVMX_BOARD_TYPE_LANAI2_U:
case CVMX_BOARD_TYPE_LANAI2_G:
return USB_CLOCK_TYPE_CRYSTAL_12;
}
/* FIXME: This should use CVMX_BOARD_TYPE_UBNT_E100 */
if (OCTEON_IS_MODEL(OCTEON_CN50XX) &&
cvmx_sysinfo_get()->board_type == 20002)
return USB_CLOCK_TYPE_CRYSTAL_12;
return USB_CLOCK_TYPE_REF_48;
}
/**
* @INTERNAL
* Read a USB 32bit CSR. It performs the necessary address swizzle
* for 32bit CSRs and logs the value in a readable format if
* debugging is on.
*
* @param usb USB block this access is for
* @param address 64bit address to read
*
* @return Result of the read
*/
static inline uint32_t __cvmx_usb_read_csr32(cvmx_usb_internal_state_t *usb,
uint64_t address)
{
uint32_t result = cvmx_read64_uint32(address ^ 4);
return result;
}
/**
* @INTERNAL
* Write a USB 32bit CSR. It performs the necessary address
* swizzle for 32bit CSRs and logs the value in a readable format
* if debugging is on.
*
* @param usb USB block this access is for
* @param address 64bit address to write
* @param value Value to write
*/
static inline void __cvmx_usb_write_csr32(cvmx_usb_internal_state_t *usb,
uint64_t address, uint32_t value)
{
cvmx_write64_uint32(address ^ 4, value);
cvmx_read64_uint64(CVMX_USBNX_DMA0_INB_CHN0(usb->index));
}
/**
* @INTERNAL
* Read a USB 64bit CSR. It logs the value in a readable format if
* debugging is on.
*
* @param usb USB block this access is for
* @param address 64bit address to read
*
* @return Result of the read
*/
static inline uint64_t __cvmx_usb_read_csr64(cvmx_usb_internal_state_t *usb,
uint64_t address)
{
uint64_t result = cvmx_read64_uint64(address);
return result;
}
/**
* @INTERNAL
* Write a USB 64bit CSR. It logs the value in a readable format
* if debugging is on.
*
* @param usb USB block this access is for
* @param address 64bit address to write
* @param value Value to write
*/
static inline void __cvmx_usb_write_csr64(cvmx_usb_internal_state_t *usb,
uint64_t address, uint64_t value)
{
cvmx_write64_uint64(address, value);
}
/**
* @INTERNAL
* Utility function to convert complete codes into strings
*
* @param complete_code
* Code to convert
*
* @return Human readable string
*/
static const char *__cvmx_usb_complete_to_string(cvmx_usb_complete_t complete_code)
{
switch (complete_code)
{
case CVMX_USB_COMPLETE_SUCCESS: return "SUCCESS";
case CVMX_USB_COMPLETE_SHORT: return "SHORT";
case CVMX_USB_COMPLETE_CANCEL: return "CANCEL";
case CVMX_USB_COMPLETE_ERROR: return "ERROR";
case CVMX_USB_COMPLETE_STALL: return "STALL";
case CVMX_USB_COMPLETE_XACTERR: return "XACTERR";
case CVMX_USB_COMPLETE_DATATGLERR: return "DATATGLERR";
case CVMX_USB_COMPLETE_BABBLEERR: return "BABBLEERR";
case CVMX_USB_COMPLETE_FRAMEERR: return "FRAMEERR";
}
return "Update __cvmx_usb_complete_to_string";
}
/**
* @INTERNAL
* Return non zero if this pipe connects to a non HIGH speed
* device through a high speed hub.
*
* @param usb USB block this access is for
* @param pipe Pipe to check
*
* @return Non zero if we need to do split transactions
*/
static inline int __cvmx_usb_pipe_needs_split(cvmx_usb_internal_state_t *usb, cvmx_usb_pipe_t *pipe)
{
return ((pipe->device_speed != CVMX_USB_SPEED_HIGH) && (usb->usbcx_hprt.s.prtspd == CVMX_USB_SPEED_HIGH));
}
/**
* @INTERNAL
* Trivial utility function to return the correct PID for a pipe
*
* @param pipe pipe to check
*
* @return PID for pipe
*/
static inline int __cvmx_usb_get_data_pid(cvmx_usb_pipe_t *pipe)
{
if (pipe->pid_toggle)
return 2; /* Data1 */
else
return 0; /* Data0 */
}
/**
* Return the number of USB ports supported by this Octeon
* chip. If the chip doesn't support USB, or is not supported
* by this API, a zero will be returned. Most Octeon chips
* support one usb port, but some support two ports.
* cvmx_usb_initialize() must be called on independent
* cvmx_usb_state_t structures.
*
* @return Number of port, zero if usb isn't supported
*/
int cvmx_usb_get_num_ports(void)
{
int arch_ports = 0;
if (OCTEON_IS_MODEL(OCTEON_CN56XX))
arch_ports = 1;
else if (OCTEON_IS_MODEL(OCTEON_CN52XX))
arch_ports = 2;
else if (OCTEON_IS_MODEL(OCTEON_CN50XX))
arch_ports = 1;
else if (OCTEON_IS_MODEL(OCTEON_CN31XX))
arch_ports = 1;
else if (OCTEON_IS_MODEL(OCTEON_CN30XX))
arch_ports = 1;
else
arch_ports = 0;
return arch_ports;
}
/**
* @INTERNAL
* Allocate a usb transaction for use
*
* @param usb USB device state populated by
* cvmx_usb_initialize().
*
* @return Transaction or NULL
*/
static inline cvmx_usb_transaction_t *__cvmx_usb_alloc_transaction(cvmx_usb_internal_state_t *usb)
{
cvmx_usb_transaction_t *t;
t = usb->free_transaction_head;
if (t)
{
usb->free_transaction_head = t->next;
if (!usb->free_transaction_head)
usb->free_transaction_tail = NULL;
}
else if (cvmx_unlikely(usb->init_flags & CVMX_USB_INITIALIZE_FLAGS_DEBUG_INFO))
cvmx_dprintf("%s: Failed to allocate a transaction\n", __FUNCTION__);
if (t)
{
memset(t, 0, sizeof(*t));
t->flags = __CVMX_USB_TRANSACTION_FLAGS_IN_USE;
}
return t;
}
/**
* @INTERNAL
* Free a usb transaction
*
* @param usb USB device state populated by
* cvmx_usb_initialize().
* @param transaction
* Transaction to free
*/
static inline void __cvmx_usb_free_transaction(cvmx_usb_internal_state_t *usb,
cvmx_usb_transaction_t *transaction)
{
transaction->flags = 0;
transaction->prev = NULL;
transaction->next = NULL;
if (usb->free_transaction_tail)
usb->free_transaction_tail->next = transaction;
else
usb->free_transaction_head = transaction;
usb->free_transaction_tail = transaction;
}
/**
* @INTERNAL
* Add a pipe to the tail of a list
* @param list List to add pipe to
* @param pipe Pipe to add
*/
static inline void __cvmx_usb_append_pipe(cvmx_usb_pipe_list_t *list, cvmx_usb_pipe_t *pipe)
{
pipe->next = NULL;
pipe->prev = list->tail;
if (list->tail)
list->tail->next = pipe;
else
list->head = pipe;
list->tail = pipe;
}
/**
* @INTERNAL
* Remove a pipe from a list
* @param list List to remove pipe from
* @param pipe Pipe to remove
*/
static inline void __cvmx_usb_remove_pipe(cvmx_usb_pipe_list_t *list, cvmx_usb_pipe_t *pipe)
{
if (list->head == pipe)
{
list->head = pipe->next;
pipe->next = NULL;
if (list->head)
list->head->prev = NULL;
else
list->tail = NULL;
}
else if (list->tail == pipe)
{
list->tail = pipe->prev;
list->tail->next = NULL;
pipe->prev = NULL;
}
else
{
pipe->prev->next = pipe->next;
pipe->next->prev = pipe->prev;
pipe->prev = NULL;
pipe->next = NULL;
}
}
/**
* Initialize a USB port for use. This must be called before any
* other access to the Octeon USB port is made. The port starts
* off in the disabled state.
*
* @param state Pointer to an empty cvmx_usb_state_t structure
* that will be populated by the initialize call.
* This structure is then passed to all other USB
* functions.
* @param usb_port_number
* Which Octeon USB port to initialize.
* @param flags Flags to control hardware initialization. See
* cvmx_usb_initialize_flags_t for the flag
* definitions. Some flags are mandatory.
*
* @return CVMX_USB_SUCCESS or a negative error code defined in
* cvmx_usb_status_t.
*/
cvmx_usb_status_t cvmx_usb_initialize(cvmx_usb_state_t *state,
int usb_port_number,
cvmx_usb_initialize_flags_t flags)
{
cvmx_usbnx_clk_ctl_t usbn_clk_ctl;
cvmx_usbnx_usbp_ctl_status_t usbn_usbp_ctl_status;
cvmx_usb_internal_state_t *usb = (cvmx_usb_internal_state_t*)state;
usb->init_flags = flags;
CVMX_USB_LOG_CALLED();
CVMX_USB_LOG_PARAM("%p", state);
CVMX_USB_LOG_PARAM("%d", usb_port_number);
CVMX_USB_LOG_PARAM("0x%x", flags);
/* Make sure that state is large enough to store the internal state */
if (sizeof(*state) < sizeof(*usb))
CVMX_USB_RETURN(CVMX_USB_INVALID_PARAM);
/* At first allow 0-1 for the usb port number */
if ((usb_port_number < 0) || (usb_port_number > 1))
CVMX_USB_RETURN(CVMX_USB_INVALID_PARAM);
/* For all chips except 52XX there is only one port */
if (!OCTEON_IS_MODEL(OCTEON_CN52XX) && (usb_port_number > 0))
CVMX_USB_RETURN(CVMX_USB_INVALID_PARAM);
/* Try to determine clock type automatically */
if ((flags & (CVMX_USB_INITIALIZE_FLAGS_CLOCK_XO_XI |
CVMX_USB_INITIALIZE_FLAGS_CLOCK_XO_GND)) == 0)
{
if (octeon_usb_get_clock_type() == USB_CLOCK_TYPE_CRYSTAL_12)
flags |= CVMX_USB_INITIALIZE_FLAGS_CLOCK_XO_XI; /* Only 12 MHZ crystals are supported */
else
flags |= CVMX_USB_INITIALIZE_FLAGS_CLOCK_XO_GND;
}
if (flags & CVMX_USB_INITIALIZE_FLAGS_CLOCK_XO_GND)
{
/* Check for auto ref clock frequency */
if (!(flags & CVMX_USB_INITIALIZE_FLAGS_CLOCK_MHZ_MASK))
switch (octeon_usb_get_clock_type())
{
case USB_CLOCK_TYPE_REF_12:
flags |= CVMX_USB_INITIALIZE_FLAGS_CLOCK_12MHZ;
break;
case USB_CLOCK_TYPE_REF_24:
flags |= CVMX_USB_INITIALIZE_FLAGS_CLOCK_24MHZ;
break;
case USB_CLOCK_TYPE_REF_48:
flags |= CVMX_USB_INITIALIZE_FLAGS_CLOCK_48MHZ;
break;
default:
CVMX_USB_RETURN(CVMX_USB_INVALID_PARAM);
break;
}
}
memset(usb, 0, sizeof(usb));
usb->init_flags = flags;
/* Initialize the USB state structure */
{
int i;
usb->index = usb_port_number;
/* Initialize the transaction double linked list */
usb->free_transaction_head = NULL;
usb->free_transaction_tail = NULL;
for (i=0; i<MAX_TRANSACTIONS; i++)
__cvmx_usb_free_transaction(usb, usb->transaction + i);
for (i=0; i<MAX_PIPES; i++)
__cvmx_usb_append_pipe(&usb->free_pipes, usb->pipe + i);
}
/* Power On Reset and PHY Initialization */
/* 1. Wait for DCOK to assert (nothing to do) */
/* 2a. Write USBN0/1_CLK_CTL[POR] = 1 and
USBN0/1_CLK_CTL[HRST,PRST,HCLK_RST] = 0 */
usbn_clk_ctl.u64 = __cvmx_usb_read_csr64(usb, CVMX_USBNX_CLK_CTL(usb->index));
usbn_clk_ctl.s.por = 1;
usbn_clk_ctl.s.hrst = 0;
usbn_clk_ctl.s.prst = 0;
usbn_clk_ctl.s.hclk_rst = 0;
usbn_clk_ctl.s.enable = 0;
/* 2b. Select the USB reference clock/crystal parameters by writing
appropriate values to USBN0/1_CLK_CTL[P_C_SEL, P_RTYPE, P_COM_ON] */
if (usb->init_flags & CVMX_USB_INITIALIZE_FLAGS_CLOCK_XO_GND)
{
/* The USB port uses 12/24/48MHz 2.5V board clock
source at USB_XO. USB_XI should be tied to GND.
Most Octeon evaluation boards require this setting */
if (OCTEON_IS_MODEL(OCTEON_CN3XXX))
{
usbn_clk_ctl.cn31xx.p_rclk = 1; /* From CN31XX,CN30XX manual */
usbn_clk_ctl.cn31xx.p_xenbn = 0;
}
else if (OCTEON_IS_MODEL(OCTEON_CN56XX) || OCTEON_IS_MODEL(OCTEON_CN50XX))
usbn_clk_ctl.cn56xx.p_rtype = 2; /* From CN56XX,CN50XX manual */
else
usbn_clk_ctl.cn52xx.p_rtype = 1; /* From CN52XX manual */
switch (flags & CVMX_USB_INITIALIZE_FLAGS_CLOCK_MHZ_MASK)
{
case CVMX_USB_INITIALIZE_FLAGS_CLOCK_12MHZ:
usbn_clk_ctl.s.p_c_sel = 0;
break;
case CVMX_USB_INITIALIZE_FLAGS_CLOCK_24MHZ:
usbn_clk_ctl.s.p_c_sel = 1;
break;
case CVMX_USB_INITIALIZE_FLAGS_CLOCK_48MHZ:
usbn_clk_ctl.s.p_c_sel = 2;
break;
}
}
else
{
/* The USB port uses a 12MHz crystal as clock source
at USB_XO and USB_XI */
if (OCTEON_IS_MODEL(OCTEON_CN3XXX))
{
usbn_clk_ctl.cn31xx.p_rclk = 1; /* From CN31XX,CN30XX manual */
usbn_clk_ctl.cn31xx.p_xenbn = 1;
}
else if (OCTEON_IS_MODEL(OCTEON_CN56XX) || OCTEON_IS_MODEL(OCTEON_CN50XX))
usbn_clk_ctl.cn56xx.p_rtype = 0; /* From CN56XX,CN50XX manual */
else
usbn_clk_ctl.cn52xx.p_rtype = 0; /* From CN52XX manual */
usbn_clk_ctl.s.p_c_sel = 0;
}
/* 2c. Select the HCLK via writing USBN0/1_CLK_CTL[DIVIDE, DIVIDE2] and
setting USBN0/1_CLK_CTL[ENABLE] = 1. Divide the core clock down such
that USB is as close as possible to 125Mhz */
{
int divisor = (octeon_get_clock_rate()+125000000-1)/125000000;
if (divisor < 4) /* Lower than 4 doesn't seem to work properly */
divisor = 4;
usbn_clk_ctl.s.divide = divisor;
usbn_clk_ctl.s.divide2 = 0;
}
__cvmx_usb_write_csr64(usb, CVMX_USBNX_CLK_CTL(usb->index),
usbn_clk_ctl.u64);
/* 2d. Write USBN0/1_CLK_CTL[HCLK_RST] = 1 */
usbn_clk_ctl.s.hclk_rst = 1;
__cvmx_usb_write_csr64(usb, CVMX_USBNX_CLK_CTL(usb->index),
usbn_clk_ctl.u64);
/* 2e. Wait 64 core-clock cycles for HCLK to stabilize */
cvmx_wait(64);
/* 3. Program the power-on reset field in the USBN clock-control register:
USBN_CLK_CTL[POR] = 0 */
usbn_clk_ctl.s.por = 0;
__cvmx_usb_write_csr64(usb, CVMX_USBNX_CLK_CTL(usb->index),
usbn_clk_ctl.u64);
/* 4. Wait 1 ms for PHY clock to start */
cvmx_wait_usec(1000);
/* 5. Program the Reset input from automatic test equipment field in the
USBP control and status register: USBN_USBP_CTL_STATUS[ATE_RESET] = 1 */
usbn_usbp_ctl_status.u64 = __cvmx_usb_read_csr64(usb, CVMX_USBNX_USBP_CTL_STATUS(usb->index));
usbn_usbp_ctl_status.s.ate_reset = 1;
__cvmx_usb_write_csr64(usb, CVMX_USBNX_USBP_CTL_STATUS(usb->index),
usbn_usbp_ctl_status.u64);
/* 6. Wait 10 cycles */
cvmx_wait(10);
/* 7. Clear ATE_RESET field in the USBN clock-control register:
USBN_USBP_CTL_STATUS[ATE_RESET] = 0 */
usbn_usbp_ctl_status.s.ate_reset = 0;
__cvmx_usb_write_csr64(usb, CVMX_USBNX_USBP_CTL_STATUS(usb->index),
usbn_usbp_ctl_status.u64);
/* 8. Program the PHY reset field in the USBN clock-control register:
USBN_CLK_CTL[PRST] = 1 */
usbn_clk_ctl.s.prst = 1;
__cvmx_usb_write_csr64(usb, CVMX_USBNX_CLK_CTL(usb->index),
usbn_clk_ctl.u64);
/* 9. Program the USBP control and status register to select host or
device mode. USBN_USBP_CTL_STATUS[HST_MODE] = 0 for host, = 1 for
device */
usbn_usbp_ctl_status.s.hst_mode = 0;
__cvmx_usb_write_csr64(usb, CVMX_USBNX_USBP_CTL_STATUS(usb->index),
usbn_usbp_ctl_status.u64);
/* 10. Wait 1 us */
cvmx_wait_usec(1);
/* 11. Program the hreset_n field in the USBN clock-control register:
USBN_CLK_CTL[HRST] = 1 */
usbn_clk_ctl.s.hrst = 1;
__cvmx_usb_write_csr64(usb, CVMX_USBNX_CLK_CTL(usb->index),
usbn_clk_ctl.u64);
/* 12. Proceed to USB core initialization */
usbn_clk_ctl.s.enable = 1;
__cvmx_usb_write_csr64(usb, CVMX_USBNX_CLK_CTL(usb->index),
usbn_clk_ctl.u64);
cvmx_wait_usec(1);
/* USB Core Initialization */
/* 1. Read USBC_GHWCFG1, USBC_GHWCFG2, USBC_GHWCFG3, USBC_GHWCFG4 to
determine USB core configuration parameters. */
/* Nothing needed */
/* 2. Program the following fields in the global AHB configuration
register (USBC_GAHBCFG)
DMA mode, USBC_GAHBCFG[DMAEn]: 1 = DMA mode, 0 = slave mode
Burst length, USBC_GAHBCFG[HBSTLEN] = 0
Nonperiodic TxFIFO empty level (slave mode only),
USBC_GAHBCFG[NPTXFEMPLVL]
Periodic TxFIFO empty level (slave mode only),
USBC_GAHBCFG[PTXFEMPLVL]
Global interrupt mask, USBC_GAHBCFG[GLBLINTRMSK] = 1 */
{
cvmx_usbcx_gahbcfg_t usbcx_gahbcfg;
/* Due to an errata, CN31XX doesn't support DMA */
if (OCTEON_IS_MODEL(OCTEON_CN31XX))
usb->init_flags |= CVMX_USB_INITIALIZE_FLAGS_NO_DMA;
usbcx_gahbcfg.u32 = 0;
usbcx_gahbcfg.s.dmaen = !(usb->init_flags & CVMX_USB_INITIALIZE_FLAGS_NO_DMA);
if (usb->init_flags & CVMX_USB_INITIALIZE_FLAGS_NO_DMA)
usb->idle_hardware_channels = 0x1; /* Only use one channel with non DMA */
else if (OCTEON_IS_MODEL(OCTEON_CN5XXX))
usb->idle_hardware_channels = 0xf7; /* CN5XXX have an errata with channel 3 */
else
usb->idle_hardware_channels = 0xff;
usbcx_gahbcfg.s.hbstlen = 0;
usbcx_gahbcfg.s.nptxfemplvl = 1;
usbcx_gahbcfg.s.ptxfemplvl = 1;
usbcx_gahbcfg.s.glblintrmsk = 1;
__cvmx_usb_write_csr32(usb, CVMX_USBCX_GAHBCFG(usb->index),
usbcx_gahbcfg.u32);
}
/* 3. Program the following fields in USBC_GUSBCFG register.
HS/FS timeout calibration, USBC_GUSBCFG[TOUTCAL] = 0
ULPI DDR select, USBC_GUSBCFG[DDRSEL] = 0
USB turnaround time, USBC_GUSBCFG[USBTRDTIM] = 0x5
PHY low-power clock select, USBC_GUSBCFG[PHYLPWRCLKSEL] = 0 */
{
cvmx_usbcx_gusbcfg_t usbcx_gusbcfg;
usbcx_gusbcfg.u32 = __cvmx_usb_read_csr32(usb, CVMX_USBCX_GUSBCFG(usb->index));
usbcx_gusbcfg.s.toutcal = 0;
usbcx_gusbcfg.s.ddrsel = 0;
usbcx_gusbcfg.s.usbtrdtim = 0x5;
usbcx_gusbcfg.s.phylpwrclksel = 0;
__cvmx_usb_write_csr32(usb, CVMX_USBCX_GUSBCFG(usb->index),
usbcx_gusbcfg.u32);
}
/* 4. The software must unmask the following bits in the USBC_GINTMSK
register.
OTG interrupt mask, USBC_GINTMSK[OTGINTMSK] = 1
Mode mismatch interrupt mask, USBC_GINTMSK[MODEMISMSK] = 1 */
{
cvmx_usbcx_gintmsk_t usbcx_gintmsk;
int channel;
usbcx_gintmsk.u32 = __cvmx_usb_read_csr32(usb, CVMX_USBCX_GINTMSK(usb->index));
usbcx_gintmsk.s.otgintmsk = 1;
usbcx_gintmsk.s.modemismsk = 1;
usbcx_gintmsk.s.hchintmsk = 1;
usbcx_gintmsk.s.sofmsk = 0;
/* We need RX FIFO interrupts if we don't have DMA */
if (usb->init_flags & CVMX_USB_INITIALIZE_FLAGS_NO_DMA)
usbcx_gintmsk.s.rxflvlmsk = 1;
__cvmx_usb_write_csr32(usb, CVMX_USBCX_GINTMSK(usb->index),
usbcx_gintmsk.u32);
/* Disable all channel interrupts. We'll enable them per channel later */
for (channel=0; channel<8; channel++)
__cvmx_usb_write_csr32(usb, CVMX_USBCX_HCINTMSKX(channel, usb->index), 0);
}
{
/* Host Port Initialization */
if (cvmx_unlikely(usb->init_flags & CVMX_USB_INITIALIZE_FLAGS_DEBUG_INFO))
cvmx_dprintf("%s: USB%d is in host mode\n", __FUNCTION__, usb->index);
/* 1. Program the host-port interrupt-mask field to unmask,
USBC_GINTMSK[PRTINT] = 1 */
USB_SET_FIELD32(CVMX_USBCX_GINTMSK(usb->index), cvmx_usbcx_gintmsk_t,
prtintmsk, 1);
USB_SET_FIELD32(CVMX_USBCX_GINTMSK(usb->index), cvmx_usbcx_gintmsk_t,
disconnintmsk, 1);
/* 2. Program the USBC_HCFG register to select full-speed host or
high-speed host. */
{
cvmx_usbcx_hcfg_t usbcx_hcfg;
usbcx_hcfg.u32 = __cvmx_usb_read_csr32(usb, CVMX_USBCX_HCFG(usb->index));
usbcx_hcfg.s.fslssupp = 0;
usbcx_hcfg.s.fslspclksel = 0;
__cvmx_usb_write_csr32(usb, CVMX_USBCX_HCFG(usb->index), usbcx_hcfg.u32);
}
/* 3. Program the port power bit to drive VBUS on the USB,
USBC_HPRT[PRTPWR] = 1 */
USB_SET_FIELD32(CVMX_USBCX_HPRT(usb->index), cvmx_usbcx_hprt_t, prtpwr, 1);
/* Steps 4-15 from the manual are done later in the port enable */
}
CVMX_USB_RETURN(CVMX_USB_SUCCESS);
}
/**
* Shutdown a USB port after a call to cvmx_usb_initialize().
* The port should be disabled with all pipes closed when this
* function is called.
*
* @param state USB device state populated by
* cvmx_usb_initialize().
*
* @return CVMX_USB_SUCCESS or a negative error code defined in
* cvmx_usb_status_t.
*/
cvmx_usb_status_t cvmx_usb_shutdown(cvmx_usb_state_t *state)
{
cvmx_usbnx_clk_ctl_t usbn_clk_ctl;
cvmx_usb_internal_state_t *usb = (cvmx_usb_internal_state_t*)state;
CVMX_USB_LOG_CALLED();
CVMX_USB_LOG_PARAM("%p", state);
/* Make sure all pipes are closed */
if (usb->idle_pipes.head ||
usb->active_pipes[CVMX_USB_TRANSFER_ISOCHRONOUS].head ||
usb->active_pipes[CVMX_USB_TRANSFER_INTERRUPT].head ||
usb->active_pipes[CVMX_USB_TRANSFER_CONTROL].head ||
usb->active_pipes[CVMX_USB_TRANSFER_BULK].head)
CVMX_USB_RETURN(CVMX_USB_BUSY);
/* Disable the clocks and put them in power on reset */
usbn_clk_ctl.u64 = __cvmx_usb_read_csr64(usb, CVMX_USBNX_CLK_CTL(usb->index));
usbn_clk_ctl.s.enable = 1;
usbn_clk_ctl.s.por = 1;
usbn_clk_ctl.s.hclk_rst = 1;
usbn_clk_ctl.s.prst = 0;
usbn_clk_ctl.s.hrst = 0;
__cvmx_usb_write_csr64(usb, CVMX_USBNX_CLK_CTL(usb->index),
usbn_clk_ctl.u64);
CVMX_USB_RETURN(CVMX_USB_SUCCESS);
}
/**
* Enable a USB port. After this call succeeds, the USB port is
* online and servicing requests.
*
* @param state USB device state populated by
* cvmx_usb_initialize().
*
* @return CVMX_USB_SUCCESS or a negative error code defined in
* cvmx_usb_status_t.
*/
cvmx_usb_status_t cvmx_usb_enable(cvmx_usb_state_t *state)
{
cvmx_usbcx_ghwcfg3_t usbcx_ghwcfg3;
cvmx_usb_internal_state_t *usb = (cvmx_usb_internal_state_t*)state;
CVMX_USB_LOG_CALLED();
CVMX_USB_LOG_PARAM("%p", state);
usb->usbcx_hprt.u32 = __cvmx_usb_read_csr32(usb, CVMX_USBCX_HPRT(usb->index));
/* If the port is already enabled the just return. We don't need to do
anything */
if (usb->usbcx_hprt.s.prtena)
CVMX_USB_RETURN(CVMX_USB_SUCCESS);
/* If there is nothing plugged into the port then fail immediately */
if (!usb->usbcx_hprt.s.prtconnsts)
{
if (cvmx_unlikely(usb->init_flags & CVMX_USB_INITIALIZE_FLAGS_DEBUG_INFO))
cvmx_dprintf("%s: USB%d Nothing plugged into the port\n", __FUNCTION__, usb->index);
CVMX_USB_RETURN(CVMX_USB_TIMEOUT);
}
/* Program the port reset bit to start the reset process */
USB_SET_FIELD32(CVMX_USBCX_HPRT(usb->index), cvmx_usbcx_hprt_t, prtrst, 1);
/* Wait at least 50ms (high speed), or 10ms (full speed) for the reset
process to complete. */
cvmx_wait_usec(50000);
/* Program the port reset bit to 0, USBC_HPRT[PRTRST] = 0 */
USB_SET_FIELD32(CVMX_USBCX_HPRT(usb->index), cvmx_usbcx_hprt_t, prtrst, 0);
/* Wait for the USBC_HPRT[PRTENA]. */
if (CVMX_WAIT_FOR_FIELD32(CVMX_USBCX_HPRT(usb->index), cvmx_usbcx_hprt_t,
prtena, ==, 1, 100000))
{
if (cvmx_unlikely(usb->init_flags & CVMX_USB_INITIALIZE_FLAGS_DEBUG_INFO))
cvmx_dprintf("%s: Timeout waiting for the port to finish reset\n",
__FUNCTION__);
CVMX_USB_RETURN(CVMX_USB_TIMEOUT);
}
/* Read the port speed field to get the enumerated speed, USBC_HPRT[PRTSPD]. */
usb->usbcx_hprt.u32 = __cvmx_usb_read_csr32(usb, CVMX_USBCX_HPRT(usb->index));
if (cvmx_unlikely(usb->init_flags & CVMX_USB_INITIALIZE_FLAGS_DEBUG_INFO))
cvmx_dprintf("%s: USB%d is in %s speed mode\n", __FUNCTION__, usb->index,
(usb->usbcx_hprt.s.prtspd == CVMX_USB_SPEED_HIGH) ? "high" :
(usb->usbcx_hprt.s.prtspd == CVMX_USB_SPEED_FULL) ? "full" :
"low");
usbcx_ghwcfg3.u32 = __cvmx_usb_read_csr32(usb, CVMX_USBCX_GHWCFG3(usb->index));
/* 13. Program the USBC_GRXFSIZ register to select the size of the receive
FIFO (25%). */
USB_SET_FIELD32(CVMX_USBCX_GRXFSIZ(usb->index), cvmx_usbcx_grxfsiz_t,
rxfdep, usbcx_ghwcfg3.s.dfifodepth / 4);
/* 14. Program the USBC_GNPTXFSIZ register to select the size and the
start address of the non- periodic transmit FIFO for nonperiodic
transactions (50%). */
{
cvmx_usbcx_gnptxfsiz_t siz;
siz.u32 = __cvmx_usb_read_csr32(usb, CVMX_USBCX_GNPTXFSIZ(usb->index));
siz.s.nptxfdep = usbcx_ghwcfg3.s.dfifodepth / 2;
siz.s.nptxfstaddr = usbcx_ghwcfg3.s.dfifodepth / 4;
__cvmx_usb_write_csr32(usb, CVMX_USBCX_GNPTXFSIZ(usb->index), siz.u32);
}
/* 15. Program the USBC_HPTXFSIZ register to select the size and start
address of the periodic transmit FIFO for periodic transactions (25%). */
{
cvmx_usbcx_hptxfsiz_t siz;
siz.u32 = __cvmx_usb_read_csr32(usb, CVMX_USBCX_HPTXFSIZ(usb->index));
siz.s.ptxfsize = usbcx_ghwcfg3.s.dfifodepth / 4;
siz.s.ptxfstaddr = 3 * usbcx_ghwcfg3.s.dfifodepth / 4;
__cvmx_usb_write_csr32(usb, CVMX_USBCX_HPTXFSIZ(usb->index), siz.u32);
}
/* Flush all FIFOs */
USB_SET_FIELD32(CVMX_USBCX_GRSTCTL(usb->index), cvmx_usbcx_grstctl_t, txfnum, 0x10);
USB_SET_FIELD32(CVMX_USBCX_GRSTCTL(usb->index), cvmx_usbcx_grstctl_t, txfflsh, 1);
CVMX_WAIT_FOR_FIELD32(CVMX_USBCX_GRSTCTL(usb->index), cvmx_usbcx_grstctl_t,
txfflsh, ==, 0, 100);
USB_SET_FIELD32(CVMX_USBCX_GRSTCTL(usb->index), cvmx_usbcx_grstctl_t, rxfflsh, 1);
CVMX_WAIT_FOR_FIELD32(CVMX_USBCX_GRSTCTL(usb->index), cvmx_usbcx_grstctl_t,
rxfflsh, ==, 0, 100);
CVMX_USB_RETURN(CVMX_USB_SUCCESS);
}
/**
* Disable a USB port. After this call the USB port will not
* generate data transfers and will not generate events.
* Transactions in process will fail and call their
* associated callbacks.
*
* @param state USB device state populated by
* cvmx_usb_initialize().
*
* @return CVMX_USB_SUCCESS or a negative error code defined in
* cvmx_usb_status_t.
*/
cvmx_usb_status_t cvmx_usb_disable(cvmx_usb_state_t *state)
{
cvmx_usb_internal_state_t *usb = (cvmx_usb_internal_state_t*)state;
CVMX_USB_LOG_CALLED();
CVMX_USB_LOG_PARAM("%p", state);
/* Disable the port */
USB_SET_FIELD32(CVMX_USBCX_HPRT(usb->index), cvmx_usbcx_hprt_t, prtena, 1);
CVMX_USB_RETURN(CVMX_USB_SUCCESS);
}
/**
* Get the current state of the USB port. Use this call to
* determine if the usb port has anything connected, is enabled,
* or has some sort of error condition. The return value of this
* call has "changed" bits to signal of the value of some fields
* have changed between calls. These "changed" fields are based
* on the last call to cvmx_usb_set_status(). In order to clear
* them, you must update the status through cvmx_usb_set_status().
*
* @param state USB device state populated by
* cvmx_usb_initialize().
*
* @return Port status information
*/
cvmx_usb_port_status_t cvmx_usb_get_status(cvmx_usb_state_t *state)
{
cvmx_usbcx_hprt_t usbc_hprt;
cvmx_usb_port_status_t result;
cvmx_usb_internal_state_t *usb = (cvmx_usb_internal_state_t*)state;
memset(&result, 0, sizeof(result));
CVMX_USB_LOG_CALLED();
CVMX_USB_LOG_PARAM("%p", state);
usbc_hprt.u32 = __cvmx_usb_read_csr32(usb, CVMX_USBCX_HPRT(usb->index));
result.port_enabled = usbc_hprt.s.prtena;
result.port_over_current = usbc_hprt.s.prtovrcurract;
result.port_powered = usbc_hprt.s.prtpwr;
result.port_speed = usbc_hprt.s.prtspd;
result.connected = usbc_hprt.s.prtconnsts;
result.connect_change = (result.connected != usb->port_status.connected);
if (cvmx_unlikely(usb->init_flags & CVMX_USB_INITIALIZE_FLAGS_DEBUG_CALLS))
cvmx_dprintf("%*s%s: returned port enabled=%d, over_current=%d, powered=%d, speed=%d, connected=%d, connect_change=%d\n",
2*(--usb->indent), "", __FUNCTION__,
result.port_enabled,
result.port_over_current,
result.port_powered,
result.port_speed,
result.connected,
result.connect_change);
return result;
}
/**
* Set the current state of the USB port. The status is used as
* a reference for the "changed" bits returned by
* cvmx_usb_get_status(). Other than serving as a reference, the
* status passed to this function is not used. No fields can be
* changed through this call.
*
* @param state USB device state populated by
* cvmx_usb_initialize().
* @param port_status
* Port status to set, most like returned by cvmx_usb_get_status()
*/
void cvmx_usb_set_status(cvmx_usb_state_t *state, cvmx_usb_port_status_t port_status)
{
cvmx_usb_internal_state_t *usb = (cvmx_usb_internal_state_t*)state;
CVMX_USB_LOG_CALLED();
CVMX_USB_LOG_PARAM("%p", state);
usb->port_status = port_status;
CVMX_USB_RETURN_NOTHING();
}
/**
* @INTERNAL
* Convert a USB transaction into a handle
*
* @param usb USB device state populated by
* cvmx_usb_initialize().
* @param transaction
* Transaction to get handle for
*
* @return Handle
*/
static inline int __cvmx_usb_get_submit_handle(cvmx_usb_internal_state_t *usb,
cvmx_usb_transaction_t *transaction)
{
return ((unsigned long)transaction - (unsigned long)usb->transaction) /
sizeof(*transaction);
}
/**
* @INTERNAL
* Convert a USB pipe into a handle
*
* @param usb USB device state populated by
* cvmx_usb_initialize().
* @param pipe Pipe to get handle for
*
* @return Handle
*/
static inline int __cvmx_usb_get_pipe_handle(cvmx_usb_internal_state_t *usb,
cvmx_usb_pipe_t *pipe)
{
return ((unsigned long)pipe - (unsigned long)usb->pipe) / sizeof(*pipe);
}
/**
* Open a virtual pipe between the host and a USB device. A pipe
* must be opened before data can be transferred between a device
* and Octeon.
*
* @param state USB device state populated by
* cvmx_usb_initialize().
* @param flags Optional pipe flags defined in
* cvmx_usb_pipe_flags_t.
* @param device_addr
* USB device address to open the pipe to
* (0-127).
* @param endpoint_num
* USB endpoint number to open the pipe to
* (0-15).
* @param device_speed
* The speed of the device the pipe is going
* to. This must match the device's speed,
* which may be different than the port speed.
* @param max_packet The maximum packet length the device can
* transmit/receive (low speed=0-8, full
* speed=0-1023, high speed=0-1024). This value
* comes from the standard endpoint descriptor
* field wMaxPacketSize bits <10:0>.
* @param transfer_type
* The type of transfer this pipe is for.
* @param transfer_dir
* The direction the pipe is in. This is not
* used for control pipes.
* @param interval For ISOCHRONOUS and INTERRUPT transfers,
* this is how often the transfer is scheduled
* for. All other transfers should specify
* zero. The units are in frames (8000/sec at
* high speed, 1000/sec for full speed).
* @param multi_count
* For high speed devices, this is the maximum
* allowed number of packet per microframe.
* Specify zero for non high speed devices. This
* value comes from the standard endpoint descriptor
* field wMaxPacketSize bits <12:11>.
* @param hub_device_addr
* Hub device address this device is connected
* to. Devices connected directly to Octeon
* use zero. This is only used when the device
* is full/low speed behind a high speed hub.
* The address will be of the high speed hub,
* not and full speed hubs after it.
* @param hub_port Which port on the hub the device is
* connected. Use zero for devices connected
* directly to Octeon. Like hub_device_addr,
* this is only used for full/low speed
* devices behind a high speed hub.
*
* @return A non negative value is a pipe handle. Negative
* values are failure codes from cvmx_usb_status_t.
*/
int cvmx_usb_open_pipe(cvmx_usb_state_t *state, cvmx_usb_pipe_flags_t flags,
int device_addr, int endpoint_num,
cvmx_usb_speed_t device_speed, int max_packet,
cvmx_usb_transfer_t transfer_type,
cvmx_usb_direction_t transfer_dir, int interval,
int multi_count, int hub_device_addr, int hub_port)
{
cvmx_usb_pipe_t *pipe;
cvmx_usb_internal_state_t *usb = (cvmx_usb_internal_state_t*)state;
CVMX_USB_LOG_CALLED();
CVMX_USB_LOG_PARAM("%p", state);
CVMX_USB_LOG_PARAM("0x%x", flags);
CVMX_USB_LOG_PARAM("%d", device_addr);
CVMX_USB_LOG_PARAM("%d", endpoint_num);
CVMX_USB_LOG_PARAM("%d", device_speed);
CVMX_USB_LOG_PARAM("%d", max_packet);
CVMX_USB_LOG_PARAM("%d", transfer_type);
CVMX_USB_LOG_PARAM("%d", transfer_dir);
CVMX_USB_LOG_PARAM("%d", interval);
CVMX_USB_LOG_PARAM("%d", multi_count);
CVMX_USB_LOG_PARAM("%d", hub_device_addr);
CVMX_USB_LOG_PARAM("%d", hub_port);
if (cvmx_unlikely((device_addr < 0) || (device_addr > MAX_USB_ADDRESS)))
CVMX_USB_RETURN(CVMX_USB_INVALID_PARAM);
if (cvmx_unlikely((endpoint_num < 0) || (endpoint_num > MAX_USB_ENDPOINT)))
CVMX_USB_RETURN(CVMX_USB_INVALID_PARAM);
if (cvmx_unlikely(device_speed > CVMX_USB_SPEED_LOW))
CVMX_USB_RETURN(CVMX_USB_INVALID_PARAM);
if (cvmx_unlikely((max_packet <= 0) || (max_packet > 1024)))
CVMX_USB_RETURN(CVMX_USB_INVALID_PARAM);
if (cvmx_unlikely(transfer_type > CVMX_USB_TRANSFER_INTERRUPT))
CVMX_USB_RETURN(CVMX_USB_INVALID_PARAM);
if (cvmx_unlikely((transfer_dir != CVMX_USB_DIRECTION_OUT) &&
(transfer_dir != CVMX_USB_DIRECTION_IN)))
CVMX_USB_RETURN(CVMX_USB_INVALID_PARAM);
if (cvmx_unlikely(interval < 0))
CVMX_USB_RETURN(CVMX_USB_INVALID_PARAM);
if (cvmx_unlikely((transfer_type == CVMX_USB_TRANSFER_CONTROL) && interval))
CVMX_USB_RETURN(CVMX_USB_INVALID_PARAM);
if (cvmx_unlikely(multi_count < 0))
CVMX_USB_RETURN(CVMX_USB_INVALID_PARAM);
if (cvmx_unlikely((device_speed != CVMX_USB_SPEED_HIGH) &&
(multi_count != 0)))
CVMX_USB_RETURN(CVMX_USB_INVALID_PARAM);
if (cvmx_unlikely((hub_device_addr < 0) || (hub_device_addr > MAX_USB_ADDRESS)))
CVMX_USB_RETURN(CVMX_USB_INVALID_PARAM);
if (cvmx_unlikely((hub_port < 0) || (hub_port > MAX_USB_HUB_PORT)))
CVMX_USB_RETURN(CVMX_USB_INVALID_PARAM);
/* Find a free pipe */
pipe = usb->free_pipes.head;
if (!pipe)
CVMX_USB_RETURN(CVMX_USB_NO_MEMORY);
__cvmx_usb_remove_pipe(&usb->free_pipes, pipe);
pipe->flags = flags | __CVMX_USB_PIPE_FLAGS_OPEN;
if ((device_speed == CVMX_USB_SPEED_HIGH) &&
(transfer_dir == CVMX_USB_DIRECTION_OUT) &&
(transfer_type == CVMX_USB_TRANSFER_BULK))
pipe->flags |= __CVMX_USB_PIPE_FLAGS_NEED_PING;
pipe->device_addr = device_addr;
pipe->endpoint_num = endpoint_num;
pipe->device_speed = device_speed;
pipe->max_packet = max_packet;
pipe->transfer_type = transfer_type;
pipe->transfer_dir = transfer_dir;
/* All pipes use interval to rate limit NAK processing. Force an interval
if one wasn't supplied */
if (!interval)
interval = 1;
if (__cvmx_usb_pipe_needs_split(usb, pipe))
{
pipe->interval = interval*8;
/* Force start splits to be schedule on uFrame 0 */
pipe->next_tx_frame = ((usb->frame_number+7)&~7) + pipe->interval;
}
else
{
pipe->interval = interval;
pipe->next_tx_frame = usb->frame_number + pipe->interval;
}
pipe->multi_count = multi_count;
pipe->hub_device_addr = hub_device_addr;
pipe->hub_port = hub_port;
pipe->pid_toggle = 0;
pipe->split_sc_frame = -1;
__cvmx_usb_append_pipe(&usb->idle_pipes, pipe);
/* We don't need to tell the hardware about this pipe yet since
it doesn't have any submitted requests */
CVMX_USB_RETURN(__cvmx_usb_get_pipe_handle(usb, pipe));
}
/**
* @INTERNAL
* Poll the RX FIFOs and remove data as needed. This function is only used
* in non DMA mode. It is very important that this function be called quickly
* enough to prevent FIFO overflow.
*
* @param usb USB device state populated by
* cvmx_usb_initialize().
*/
static void __cvmx_usb_poll_rx_fifo(cvmx_usb_internal_state_t *usb)
{
cvmx_usbcx_grxstsph_t rx_status;
int channel;
int bytes;
uint64_t address;
uint32_t *ptr;
CVMX_USB_LOG_CALLED();
CVMX_USB_LOG_PARAM("%p", usb);
rx_status.u32 = __cvmx_usb_read_csr32(usb, CVMX_USBCX_GRXSTSPH(usb->index));
/* Only read data if IN data is there */
if (rx_status.s.pktsts != 2)
CVMX_USB_RETURN_NOTHING();
/* Check if no data is available */
if (!rx_status.s.bcnt)
CVMX_USB_RETURN_NOTHING();
channel = rx_status.s.chnum;
bytes = rx_status.s.bcnt;
if (!bytes)
CVMX_USB_RETURN_NOTHING();
/* Get where the DMA engine would have written this data */
address = __cvmx_usb_read_csr64(usb, CVMX_USBNX_DMA0_INB_CHN0(usb->index) + channel*8);
ptr = cvmx_phys_to_ptr(address);
__cvmx_usb_write_csr64(usb, CVMX_USBNX_DMA0_INB_CHN0(usb->index) + channel*8, address + bytes);
/* Loop writing the FIFO data for this packet into memory */
while (bytes > 0)
{
*ptr++ = __cvmx_usb_read_csr32(usb, USB_FIFO_ADDRESS(channel, usb->index));
bytes -= 4;
}
CVMX_SYNCW;
CVMX_USB_RETURN_NOTHING();
}
/**
* Fill the TX hardware fifo with data out of the software
* fifos
*
* @param usb USB device state populated by
* cvmx_usb_initialize().
* @param fifo Software fifo to use
* @param available Amount of space in the hardware fifo
*
* @return Non zero if the hardware fifo was too small and needs
* to be serviced again.
*/
static int __cvmx_usb_fill_tx_hw(cvmx_usb_internal_state_t *usb, cvmx_usb_tx_fifo_t *fifo, int available)
{
CVMX_USB_LOG_CALLED();
CVMX_USB_LOG_PARAM("%p", usb);
CVMX_USB_LOG_PARAM("%p", fifo);
CVMX_USB_LOG_PARAM("%d", available);
/* We're done either when there isn't anymore space or the software FIFO
is empty */
while (available && (fifo->head != fifo->tail))
{
int i = fifo->tail;
const uint32_t *ptr = cvmx_phys_to_ptr(fifo->entry[i].address);
uint64_t csr_address = USB_FIFO_ADDRESS(fifo->entry[i].channel, usb->index) ^ 4;
int words = available;
/* Limit the amount of data to waht the SW fifo has */
if (fifo->entry[i].size <= available)
{
words = fifo->entry[i].size;
fifo->tail++;
if (fifo->tail > MAX_CHANNELS)
fifo->tail = 0;
}
/* Update the next locations and counts */
available -= words;
fifo->entry[i].address += words * 4;
fifo->entry[i].size -= words;
/* Write the HW fifo data. The read every three writes is due
to an errata on CN3XXX chips */
while (words > 3)
{
cvmx_write64_uint32(csr_address, *ptr++);
cvmx_write64_uint32(csr_address, *ptr++);
cvmx_write64_uint32(csr_address, *ptr++);
cvmx_read64_uint64(CVMX_USBNX_DMA0_INB_CHN0(usb->index));
words -= 3;
}
cvmx_write64_uint32(csr_address, *ptr++);
if (--words)
{
cvmx_write64_uint32(csr_address, *ptr++);
if (--words)
cvmx_write64_uint32(csr_address, *ptr++);
}
cvmx_read64_uint64(CVMX_USBNX_DMA0_INB_CHN0(usb->index));
}
CVMX_USB_RETURN(fifo->head != fifo->tail);
}
/**
* Check the hardware FIFOs and fill them as needed
*
* @param usb USB device state populated by
* cvmx_usb_initialize().
*/
static void __cvmx_usb_poll_tx_fifo(cvmx_usb_internal_state_t *usb)
{
CVMX_USB_LOG_CALLED();
CVMX_USB_LOG_PARAM("%p", usb);
if (usb->periodic.head != usb->periodic.tail)
{
cvmx_usbcx_hptxsts_t tx_status;
tx_status.u32 = __cvmx_usb_read_csr32(usb, CVMX_USBCX_HPTXSTS(usb->index));
if (__cvmx_usb_fill_tx_hw(usb, &usb->periodic, tx_status.s.ptxfspcavail))
USB_SET_FIELD32(CVMX_USBCX_GINTMSK(usb->index), cvmx_usbcx_gintmsk_t, ptxfempmsk, 1);
else
USB_SET_FIELD32(CVMX_USBCX_GINTMSK(usb->index), cvmx_usbcx_gintmsk_t, ptxfempmsk, 0);
}
if (usb->nonperiodic.head != usb->nonperiodic.tail)
{
cvmx_usbcx_gnptxsts_t tx_status;
tx_status.u32 = __cvmx_usb_read_csr32(usb, CVMX_USBCX_GNPTXSTS(usb->index));
if (__cvmx_usb_fill_tx_hw(usb, &usb->nonperiodic, tx_status.s.nptxfspcavail))
USB_SET_FIELD32(CVMX_USBCX_GINTMSK(usb->index), cvmx_usbcx_gintmsk_t, nptxfempmsk, 1);
else
USB_SET_FIELD32(CVMX_USBCX_GINTMSK(usb->index), cvmx_usbcx_gintmsk_t, nptxfempmsk, 0);
}
CVMX_USB_RETURN_NOTHING();
}
/**
* @INTERNAL
* Fill the TX FIFO with an outgoing packet
*
* @param usb USB device state populated by
* cvmx_usb_initialize().
* @param channel Channel number to get packet from
*/
static void __cvmx_usb_fill_tx_fifo(cvmx_usb_internal_state_t *usb, int channel)
{
cvmx_usbcx_hccharx_t hcchar;
cvmx_usbcx_hcspltx_t usbc_hcsplt;
cvmx_usbcx_hctsizx_t usbc_hctsiz;
cvmx_usb_tx_fifo_t *fifo;
CVMX_USB_LOG_CALLED();
CVMX_USB_LOG_PARAM("%p", usb);
CVMX_USB_LOG_PARAM("%d", channel);
/* We only need to fill data on outbound channels */
hcchar.u32 = __cvmx_usb_read_csr32(usb, CVMX_USBCX_HCCHARX(channel, usb->index));
if (hcchar.s.epdir != CVMX_USB_DIRECTION_OUT)
CVMX_USB_RETURN_NOTHING();
/* OUT Splits only have data on the start and not the complete */
usbc_hcsplt.u32 = __cvmx_usb_read_csr32(usb, CVMX_USBCX_HCSPLTX(channel, usb->index));
if (usbc_hcsplt.s.spltena && usbc_hcsplt.s.compsplt)
CVMX_USB_RETURN_NOTHING();
/* Find out how many bytes we need to fill and convert it into 32bit words */
usbc_hctsiz.u32 = __cvmx_usb_read_csr32(usb, CVMX_USBCX_HCTSIZX(channel, usb->index));
if (!usbc_hctsiz.s.xfersize)
CVMX_USB_RETURN_NOTHING();
if ((hcchar.s.eptype == CVMX_USB_TRANSFER_INTERRUPT) ||
(hcchar.s.eptype == CVMX_USB_TRANSFER_ISOCHRONOUS))
fifo = &usb->periodic;
else
fifo = &usb->nonperiodic;
fifo->entry[fifo->head].channel = channel;
fifo->entry[fifo->head].address = __cvmx_usb_read_csr64(usb, CVMX_USBNX_DMA0_OUTB_CHN0(usb->index) + channel*8);
fifo->entry[fifo->head].size = (usbc_hctsiz.s.xfersize+3)>>2;
fifo->head++;
if (fifo->head > MAX_CHANNELS)
fifo->head = 0;
__cvmx_usb_poll_tx_fifo(usb);
CVMX_USB_RETURN_NOTHING();
}
/**
* @INTERNAL
* Perform channel specific setup for Control transactions. All
* the generic stuff will already have been done in
* __cvmx_usb_start_channel()
*
* @param usb USB device state populated by
* cvmx_usb_initialize().
* @param channel Channel to setup
* @param pipe Pipe for control transaction
*/
static void __cvmx_usb_start_channel_control(cvmx_usb_internal_state_t *usb,
int channel,
cvmx_usb_pipe_t *pipe)
{
cvmx_usb_transaction_t *transaction = pipe->head;
cvmx_usb_control_header_t *header = cvmx_phys_to_ptr(transaction->control_header);
int bytes_to_transfer = transaction->buffer_length - transaction->actual_bytes;
int packets_to_transfer;
cvmx_usbcx_hctsizx_t usbc_hctsiz;
CVMX_USB_LOG_CALLED();
CVMX_USB_LOG_PARAM("%p", usb);
CVMX_USB_LOG_PARAM("%d", channel);
CVMX_USB_LOG_PARAM("%p", pipe);
usbc_hctsiz.u32 = __cvmx_usb_read_csr32(usb, CVMX_USBCX_HCTSIZX(channel, usb->index));
switch (transaction->stage)
{
case CVMX_USB_STAGE_NON_CONTROL:
case CVMX_USB_STAGE_NON_CONTROL_SPLIT_COMPLETE:
cvmx_dprintf("%s: ERROR - Non control stage\n", __FUNCTION__);
break;
case CVMX_USB_STAGE_SETUP:
usbc_hctsiz.s.pid = 3; /* Setup */
bytes_to_transfer = sizeof(*header);
/* All Control operations start with a setup going OUT */
USB_SET_FIELD32(CVMX_USBCX_HCCHARX(channel, usb->index), cvmx_usbcx_hccharx_t, epdir, CVMX_USB_DIRECTION_OUT);
/* Setup send the control header instead of the buffer data. The
buffer data will be used in the next stage */
__cvmx_usb_write_csr64(usb, CVMX_USBNX_DMA0_OUTB_CHN0(usb->index) + channel*8, transaction->control_header);
break;
case CVMX_USB_STAGE_SETUP_SPLIT_COMPLETE:
usbc_hctsiz.s.pid = 3; /* Setup */
bytes_to_transfer = 0;
/* All Control operations start with a setup going OUT */
USB_SET_FIELD32(CVMX_USBCX_HCCHARX(channel, usb->index), cvmx_usbcx_hccharx_t, epdir, CVMX_USB_DIRECTION_OUT);
USB_SET_FIELD32(CVMX_USBCX_HCSPLTX(channel, usb->index), cvmx_usbcx_hcspltx_t, compsplt, 1);
break;
case CVMX_USB_STAGE_DATA:
usbc_hctsiz.s.pid = __cvmx_usb_get_data_pid(pipe);
if (__cvmx_usb_pipe_needs_split(usb, pipe))
{
if (header->s.request_type & 0x80)
bytes_to_transfer = 0;
else if (bytes_to_transfer > pipe->max_packet)
bytes_to_transfer = pipe->max_packet;
}
USB_SET_FIELD32(CVMX_USBCX_HCCHARX(channel, usb->index),
cvmx_usbcx_hccharx_t, epdir,
((header->s.request_type & 0x80) ?
CVMX_USB_DIRECTION_IN :
CVMX_USB_DIRECTION_OUT));
break;
case CVMX_USB_STAGE_DATA_SPLIT_COMPLETE:
usbc_hctsiz.s.pid = __cvmx_usb_get_data_pid(pipe);
if (!(header->s.request_type & 0x80))
bytes_to_transfer = 0;
USB_SET_FIELD32(CVMX_USBCX_HCCHARX(channel, usb->index),
cvmx_usbcx_hccharx_t, epdir,
((header->s.request_type & 0x80) ?
CVMX_USB_DIRECTION_IN :
CVMX_USB_DIRECTION_OUT));
USB_SET_FIELD32(CVMX_USBCX_HCSPLTX(channel, usb->index), cvmx_usbcx_hcspltx_t, compsplt, 1);
break;
case CVMX_USB_STAGE_STATUS:
usbc_hctsiz.s.pid = __cvmx_usb_get_data_pid(pipe);
bytes_to_transfer = 0;
USB_SET_FIELD32(CVMX_USBCX_HCCHARX(channel, usb->index), cvmx_usbcx_hccharx_t, epdir,
((header->s.request_type & 0x80) ?
CVMX_USB_DIRECTION_OUT :
CVMX_USB_DIRECTION_IN));
break;
case CVMX_USB_STAGE_STATUS_SPLIT_COMPLETE:
usbc_hctsiz.s.pid = __cvmx_usb_get_data_pid(pipe);
bytes_to_transfer = 0;
USB_SET_FIELD32(CVMX_USBCX_HCCHARX(channel, usb->index), cvmx_usbcx_hccharx_t, epdir,
((header->s.request_type & 0x80) ?
CVMX_USB_DIRECTION_OUT :
CVMX_USB_DIRECTION_IN));
USB_SET_FIELD32(CVMX_USBCX_HCSPLTX(channel, usb->index), cvmx_usbcx_hcspltx_t, compsplt, 1);
break;
}
/* Make sure the transfer never exceeds the byte limit of the hardware.
Further bytes will be sent as continued transactions */
if (bytes_to_transfer > MAX_TRANSFER_BYTES)
{
/* Round MAX_TRANSFER_BYTES to a multiple of out packet size */
bytes_to_transfer = MAX_TRANSFER_BYTES / pipe->max_packet;
bytes_to_transfer *= pipe->max_packet;
}
/* Calculate the number of packets to transfer. If the length is zero
we still need to transfer one packet */
packets_to_transfer = (bytes_to_transfer + pipe->max_packet - 1) / pipe->max_packet;
if (packets_to_transfer == 0)
packets_to_transfer = 1;
else if ((packets_to_transfer>1) && (usb->init_flags & CVMX_USB_INITIALIZE_FLAGS_NO_DMA))
{
/* Limit to one packet when not using DMA. Channels must be restarted
between every packet for IN transactions, so there is no reason to
do multiple packets in a row */
packets_to_transfer = 1;
bytes_to_transfer = packets_to_transfer * pipe->max_packet;
}
else if (packets_to_transfer > MAX_TRANSFER_PACKETS)
{
/* Limit the number of packet and data transferred to what the
hardware can handle */
packets_to_transfer = MAX_TRANSFER_PACKETS;
bytes_to_transfer = packets_to_transfer * pipe->max_packet;
}
usbc_hctsiz.s.xfersize = bytes_to_transfer;
usbc_hctsiz.s.pktcnt = packets_to_transfer;
__cvmx_usb_write_csr32(usb, CVMX_USBCX_HCTSIZX(channel, usb->index), usbc_hctsiz.u32);
CVMX_USB_RETURN_NOTHING();
}
/**
* @INTERNAL
* Start a channel to perform the pipe's head transaction
*
* @param usb USB device state populated by
* cvmx_usb_initialize().
* @param channel Channel to setup
* @param pipe Pipe to start
*/
static void __cvmx_usb_start_channel(cvmx_usb_internal_state_t *usb,
int channel,
cvmx_usb_pipe_t *pipe)
{
cvmx_usb_transaction_t *transaction = pipe->head;
CVMX_USB_LOG_CALLED();
CVMX_USB_LOG_PARAM("%p", usb);
CVMX_USB_LOG_PARAM("%d", channel);
CVMX_USB_LOG_PARAM("%p", pipe);
if (cvmx_unlikely((usb->init_flags & CVMX_USB_INITIALIZE_FLAGS_DEBUG_TRANSFERS) ||
(pipe->flags & CVMX_USB_PIPE_FLAGS_DEBUG_TRANSFERS)))
cvmx_dprintf("%s: Channel %d started. Pipe %d transaction %d stage %d\n",
__FUNCTION__, channel, __cvmx_usb_get_pipe_handle(usb, pipe),
__cvmx_usb_get_submit_handle(usb, transaction),
transaction->stage);
/* Make sure all writes to the DMA region get flushed */
CVMX_SYNCW;
/* Attach the channel to the pipe */
usb->pipe_for_channel[channel] = pipe;
pipe->channel = channel;
pipe->flags |= __CVMX_USB_PIPE_FLAGS_SCHEDULED;
/* Mark this channel as in use */
usb->idle_hardware_channels &= ~(1<<channel);
/* Enable the channel interrupt bits */
{
cvmx_usbcx_hcintx_t usbc_hcint;
cvmx_usbcx_hcintmskx_t usbc_hcintmsk;
cvmx_usbcx_haintmsk_t usbc_haintmsk;
/* Clear all channel status bits */
usbc_hcint.u32 = __cvmx_usb_read_csr32(usb, CVMX_USBCX_HCINTX(channel, usb->index));
__cvmx_usb_write_csr32(usb, CVMX_USBCX_HCINTX(channel, usb->index), usbc_hcint.u32);
usbc_hcintmsk.u32 = 0;
usbc_hcintmsk.s.chhltdmsk = 1;
if (usb->init_flags & CVMX_USB_INITIALIZE_FLAGS_NO_DMA)
{
/* Channels need these extra interrupts when we aren't in DMA mode */
usbc_hcintmsk.s.datatglerrmsk = 1;
usbc_hcintmsk.s.frmovrunmsk = 1;
usbc_hcintmsk.s.bblerrmsk = 1;
usbc_hcintmsk.s.xacterrmsk = 1;
if (__cvmx_usb_pipe_needs_split(usb, pipe))
{
/* Splits don't generate xfercompl, so we need ACK and NYET */
usbc_hcintmsk.s.nyetmsk = 1;
usbc_hcintmsk.s.ackmsk = 1;
}
usbc_hcintmsk.s.nakmsk = 1;
usbc_hcintmsk.s.stallmsk = 1;
usbc_hcintmsk.s.xfercomplmsk = 1;
}
__cvmx_usb_write_csr32(usb, CVMX_USBCX_HCINTMSKX(channel, usb->index), usbc_hcintmsk.u32);
/* Enable the channel interrupt to propagate */
usbc_haintmsk.u32 = __cvmx_usb_read_csr32(usb, CVMX_USBCX_HAINTMSK(usb->index));
usbc_haintmsk.s.haintmsk |= 1<<channel;
__cvmx_usb_write_csr32(usb, CVMX_USBCX_HAINTMSK(usb->index), usbc_haintmsk.u32);
}
/* Setup the locations the DMA engines use */
{
uint64_t dma_address = transaction->buffer + transaction->actual_bytes;
if (transaction->type == CVMX_USB_TRANSFER_ISOCHRONOUS)
dma_address = transaction->buffer + transaction->iso_packets[0].offset + transaction->actual_bytes;
__cvmx_usb_write_csr64(usb, CVMX_USBNX_DMA0_OUTB_CHN0(usb->index) + channel*8, dma_address);
__cvmx_usb_write_csr64(usb, CVMX_USBNX_DMA0_INB_CHN0(usb->index) + channel*8, dma_address);
}
/* Setup both the size of the transfer and the SPLIT characteristics */
{
cvmx_usbcx_hcspltx_t usbc_hcsplt = {.u32 = 0};
cvmx_usbcx_hctsizx_t usbc_hctsiz = {.u32 = 0};
int packets_to_transfer;
int bytes_to_transfer = transaction->buffer_length - transaction->actual_bytes;
/* ISOCHRONOUS transactions store each individual transfer size in the
packet structure, not the global buffer_length */
if (transaction->type == CVMX_USB_TRANSFER_ISOCHRONOUS)
bytes_to_transfer = transaction->iso_packets[0].length - transaction->actual_bytes;
/* We need to do split transactions when we are talking to non high
speed devices that are behind a high speed hub */
if (__cvmx_usb_pipe_needs_split(usb, pipe))
{
/* On the start split phase (stage is even) record the frame number we
will need to send the split complete. We only store the lower two bits
since the time ahead can only be two frames */
if ((transaction->stage&1) == 0)
{
if (transaction->type == CVMX_USB_TRANSFER_BULK)
pipe->split_sc_frame = (usb->frame_number + 1) & 0x7f;
else
pipe->split_sc_frame = (usb->frame_number + 2) & 0x7f;
}
else
pipe->split_sc_frame = -1;
usbc_hcsplt.s.spltena = 1;
usbc_hcsplt.s.hubaddr = pipe->hub_device_addr;
usbc_hcsplt.s.prtaddr = pipe->hub_port;
usbc_hcsplt.s.compsplt = (transaction->stage == CVMX_USB_STAGE_NON_CONTROL_SPLIT_COMPLETE);
/* SPLIT transactions can only ever transmit one data packet so
limit the transfer size to the max packet size */
if (bytes_to_transfer > pipe->max_packet)
bytes_to_transfer = pipe->max_packet;
/* ISOCHRONOUS OUT splits are unique in that they limit
data transfers to 188 byte chunks representing the
begin/middle/end of the data or all */
if (!usbc_hcsplt.s.compsplt &&
(pipe->transfer_dir == CVMX_USB_DIRECTION_OUT) &&
(pipe->transfer_type == CVMX_USB_TRANSFER_ISOCHRONOUS))
{
/* Clear the split complete frame number as there isn't going
to be a split complete */
pipe->split_sc_frame = -1;
/* See if we've started this transfer and sent data */
if (transaction->actual_bytes == 0)
{
/* Nothing sent yet, this is either a begin or the
entire payload */
if (bytes_to_transfer <= 188)
usbc_hcsplt.s.xactpos = 3; /* Entire payload in one go */
else
usbc_hcsplt.s.xactpos = 2; /* First part of payload */
}
else
{
/* Continuing the previous data, we must either be
in the middle or at the end */
if (bytes_to_transfer <= 188)
usbc_hcsplt.s.xactpos = 1; /* End of payload */
else
usbc_hcsplt.s.xactpos = 0; /* Middle of payload */
}
/* Again, the transfer size is limited to 188 bytes */
if (bytes_to_transfer > 188)
bytes_to_transfer = 188;
}
}
/* Make sure the transfer never exceeds the byte limit of the hardware.
Further bytes will be sent as continued transactions */
if (bytes_to_transfer > MAX_TRANSFER_BYTES)
{
/* Round MAX_TRANSFER_BYTES to a multiple of out packet size */
bytes_to_transfer = MAX_TRANSFER_BYTES / pipe->max_packet;
bytes_to_transfer *= pipe->max_packet;
}
/* Calculate the number of packets to transfer. If the length is zero
we still need to transfer one packet */
packets_to_transfer = (bytes_to_transfer + pipe->max_packet - 1) / pipe->max_packet;
if (packets_to_transfer == 0)
packets_to_transfer = 1;
else if ((packets_to_transfer>1) && (usb->init_flags & CVMX_USB_INITIALIZE_FLAGS_NO_DMA))
{
/* Limit to one packet when not using DMA. Channels must be restarted
between every packet for IN transactions, so there is no reason to
do multiple packets in a row */
packets_to_transfer = 1;
bytes_to_transfer = packets_to_transfer * pipe->max_packet;
}
else if (packets_to_transfer > MAX_TRANSFER_PACKETS)
{
/* Limit the number of packet and data transferred to what the
hardware can handle */
packets_to_transfer = MAX_TRANSFER_PACKETS;
bytes_to_transfer = packets_to_transfer * pipe->max_packet;
}
usbc_hctsiz.s.xfersize = bytes_to_transfer;
usbc_hctsiz.s.pktcnt = packets_to_transfer;
/* Update the DATA0/DATA1 toggle */
usbc_hctsiz.s.pid = __cvmx_usb_get_data_pid(pipe);
/* High speed pipes may need a hardware ping before they start */
if (pipe->flags & __CVMX_USB_PIPE_FLAGS_NEED_PING)
usbc_hctsiz.s.dopng = 1;
__cvmx_usb_write_csr32(usb, CVMX_USBCX_HCSPLTX(channel, usb->index), usbc_hcsplt.u32);
__cvmx_usb_write_csr32(usb, CVMX_USBCX_HCTSIZX(channel, usb->index), usbc_hctsiz.u32);
}
/* Setup the Host Channel Characteristics Register */
{
cvmx_usbcx_hccharx_t usbc_hcchar = {.u32 = 0};
/* Set the startframe odd/even properly. This is only used for periodic */
usbc_hcchar.s.oddfrm = usb->frame_number&1;
/* Set the number of back to back packets allowed by this endpoint.
Split transactions interpret "ec" as the number of immediate
retries of failure. These retries happen too quickly, so we
disable these entirely for splits */
if (__cvmx_usb_pipe_needs_split(usb, pipe))
usbc_hcchar.s.ec = 1;
else if (pipe->multi_count < 1)
usbc_hcchar.s.ec = 1;
else if (pipe->multi_count > 3)
usbc_hcchar.s.ec = 3;
else
usbc_hcchar.s.ec = pipe->multi_count;
/* Set the rest of the endpoint specific settings */
usbc_hcchar.s.devaddr = pipe->device_addr;
usbc_hcchar.s.eptype = transaction->type;
usbc_hcchar.s.lspddev = (pipe->device_speed == CVMX_USB_SPEED_LOW);
usbc_hcchar.s.epdir = pipe->transfer_dir;
usbc_hcchar.s.epnum = pipe->endpoint_num;
usbc_hcchar.s.mps = pipe->max_packet;
__cvmx_usb_write_csr32(usb, CVMX_USBCX_HCCHARX(channel, usb->index), usbc_hcchar.u32);
}
/* Do transaction type specific fixups as needed */
switch (transaction->type)
{
case CVMX_USB_TRANSFER_CONTROL:
__cvmx_usb_start_channel_control(usb, channel, pipe);
break;
case CVMX_USB_TRANSFER_BULK:
case CVMX_USB_TRANSFER_INTERRUPT:
break;
case CVMX_USB_TRANSFER_ISOCHRONOUS:
if (!__cvmx_usb_pipe_needs_split(usb, pipe))
{
/* ISO transactions require different PIDs depending on direction
and how many packets are needed */
if (pipe->transfer_dir == CVMX_USB_DIRECTION_OUT)
{
if (pipe->multi_count < 2) /* Need DATA0 */
USB_SET_FIELD32(CVMX_USBCX_HCTSIZX(channel, usb->index), cvmx_usbcx_hctsizx_t, pid, 0);
else /* Need MDATA */
USB_SET_FIELD32(CVMX_USBCX_HCTSIZX(channel, usb->index), cvmx_usbcx_hctsizx_t, pid, 3);
}
}
break;
}
{
cvmx_usbcx_hctsizx_t usbc_hctsiz = {.u32 = __cvmx_usb_read_csr32(usb, CVMX_USBCX_HCTSIZX(channel, usb->index))};
transaction->xfersize = usbc_hctsiz.s.xfersize;
transaction->pktcnt = usbc_hctsiz.s.pktcnt;
}
/* Remeber when we start a split transaction */
if (__cvmx_usb_pipe_needs_split(usb, pipe))
usb->active_split = transaction;
USB_SET_FIELD32(CVMX_USBCX_HCCHARX(channel, usb->index), cvmx_usbcx_hccharx_t, chena, 1);
if (usb->init_flags & CVMX_USB_INITIALIZE_FLAGS_NO_DMA)
__cvmx_usb_fill_tx_fifo(usb, channel);
CVMX_USB_RETURN_NOTHING();
}
/**
* @INTERNAL
* Find a pipe that is ready to be scheduled to hardware.
* @param usb USB device state populated by
* cvmx_usb_initialize().
* @param list Pipe list to search
* @param current_frame
* Frame counter to use as a time reference.
*
* @return Pipe or NULL if none are ready
*/
static cvmx_usb_pipe_t *__cvmx_usb_find_ready_pipe(cvmx_usb_internal_state_t *usb, cvmx_usb_pipe_list_t *list, uint64_t current_frame)
{
cvmx_usb_pipe_t *pipe = list->head;
while (pipe)
{
if (!(pipe->flags & __CVMX_USB_PIPE_FLAGS_SCHEDULED) && pipe->head &&
(pipe->next_tx_frame <= current_frame) &&
((pipe->split_sc_frame == -1) || ((((int)current_frame - (int)pipe->split_sc_frame) & 0x7f) < 0x40)) &&
(!usb->active_split || (usb->active_split == pipe->head)))
{
CVMX_PREFETCH(pipe, 128);
CVMX_PREFETCH(pipe->head, 0);
return pipe;
}
pipe = pipe->next;
}
return NULL;
}
/**
* @INTERNAL
* Called whenever a pipe might need to be scheduled to the
* hardware.
*
* @param usb USB device state populated by
* cvmx_usb_initialize().
* @param is_sof True if this schedule was called on a SOF interrupt.
*/
static void __cvmx_usb_schedule(cvmx_usb_internal_state_t *usb, int is_sof)
{
int channel;
cvmx_usb_pipe_t *pipe;
int need_sof;
cvmx_usb_transfer_t ttype;
CVMX_USB_LOG_CALLED();
CVMX_USB_LOG_PARAM("%p", usb);
if (usb->init_flags & CVMX_USB_INITIALIZE_FLAGS_NO_DMA)
{
/* Without DMA we need to be careful to not schedule something at the end of a frame and cause an overrun */
cvmx_usbcx_hfnum_t hfnum = {.u32 = __cvmx_usb_read_csr32(usb, CVMX_USBCX_HFNUM(usb->index))};
cvmx_usbcx_hfir_t hfir = {.u32 = __cvmx_usb_read_csr32(usb, CVMX_USBCX_HFIR(usb->index))};
if (hfnum.s.frrem < hfir.s.frint/4)
goto done;
}
while (usb->idle_hardware_channels)
{
/* Find an idle channel */
CVMX_CLZ(channel, usb->idle_hardware_channels);
channel = 31 - channel;
if (cvmx_unlikely(channel > 7))
{
if (cvmx_unlikely(usb->init_flags & CVMX_USB_INITIALIZE_FLAGS_DEBUG_INFO))
cvmx_dprintf("%s: Idle hardware channels has a channel higher than 7. This is wrong\n", __FUNCTION__);
break;
}
/* Find a pipe needing service */
pipe = NULL;
if (is_sof)
{
/* Only process periodic pipes on SOF interrupts. This way we are
sure that the periodic data is sent in the beginning of the
frame */
pipe = __cvmx_usb_find_ready_pipe(usb, usb->active_pipes + CVMX_USB_TRANSFER_ISOCHRONOUS, usb->frame_number);
if (cvmx_likely(!pipe))
pipe = __cvmx_usb_find_ready_pipe(usb, usb->active_pipes + CVMX_USB_TRANSFER_INTERRUPT, usb->frame_number);
}
if (cvmx_likely(!pipe))
{
pipe = __cvmx_usb_find_ready_pipe(usb, usb->active_pipes + CVMX_USB_TRANSFER_CONTROL, usb->frame_number);
if (cvmx_likely(!pipe))
pipe = __cvmx_usb_find_ready_pipe(usb, usb->active_pipes + CVMX_USB_TRANSFER_BULK, usb->frame_number);
}
if (!pipe)
break;
CVMX_USB_LOG_PARAM("%d", channel);
CVMX_USB_LOG_PARAM("%p", pipe);
if (cvmx_unlikely((usb->init_flags & CVMX_USB_INITIALIZE_FLAGS_DEBUG_TRANSFERS) ||
(pipe->flags & CVMX_USB_PIPE_FLAGS_DEBUG_TRANSFERS)))
{
cvmx_usb_transaction_t *transaction = pipe->head;
const cvmx_usb_control_header_t *header = (transaction->control_header) ? cvmx_phys_to_ptr(transaction->control_header) : NULL;
const char *dir = (pipe->transfer_dir == CVMX_USB_DIRECTION_IN) ? "IN" : "OUT";
const char *type;
switch (pipe->transfer_type)
{
case CVMX_USB_TRANSFER_CONTROL:
type = "SETUP";
dir = (header->s.request_type & 0x80) ? "IN" : "OUT";
break;
case CVMX_USB_TRANSFER_ISOCHRONOUS:
type = "ISOCHRONOUS";
break;
case CVMX_USB_TRANSFER_BULK:
type = "BULK";
break;
default: /* CVMX_USB_TRANSFER_INTERRUPT */
type = "INTERRUPT";
break;
}
cvmx_dprintf("%s: Starting pipe %d, transaction %d on channel %d. %s %s len=%d header=0x%llx\n",
__FUNCTION__, __cvmx_usb_get_pipe_handle(usb, pipe),
__cvmx_usb_get_submit_handle(usb, transaction),
channel, type, dir,
transaction->buffer_length,
(header) ? (unsigned long long)header->u64 : 0ull);
}
__cvmx_usb_start_channel(usb, channel, pipe);
}
done:
/* Only enable SOF interrupts when we have transactions pending in the
future that might need to be scheduled */
need_sof = 0;
for (ttype=CVMX_USB_TRANSFER_CONTROL; ttype<=CVMX_USB_TRANSFER_INTERRUPT; ttype++)
{
pipe = usb->active_pipes[ttype].head;
while (pipe)
{
if (pipe->next_tx_frame > usb->frame_number)
{
need_sof = 1;
break;
}
pipe=pipe->next;
}
}
USB_SET_FIELD32(CVMX_USBCX_GINTMSK(usb->index), cvmx_usbcx_gintmsk_t, sofmsk, need_sof);
CVMX_USB_RETURN_NOTHING();
}
/**
* @INTERNAL
* Call a user's callback for a specific reason.
*
* @param usb USB device state populated by
* cvmx_usb_initialize().
* @param pipe Pipe the callback is for or NULL
* @param transaction
* Transaction the callback is for or NULL
* @param reason Reason this callback is being called
* @param complete_code
* Completion code for the transaction, if any
*/
static void __cvmx_usb_perform_callback(cvmx_usb_internal_state_t *usb,
cvmx_usb_pipe_t *pipe,
cvmx_usb_transaction_t *transaction,
cvmx_usb_callback_t reason,
cvmx_usb_complete_t complete_code)
{
cvmx_usb_callback_func_t callback = usb->callback[reason];
void *user_data = usb->callback_data[reason];
int submit_handle = -1;
int pipe_handle = -1;
int bytes_transferred = 0;
if (pipe)
pipe_handle = __cvmx_usb_get_pipe_handle(usb, pipe);
if (transaction)
{
submit_handle = __cvmx_usb_get_submit_handle(usb, transaction);
bytes_transferred = transaction->actual_bytes;
/* Transactions are allowed to override the default callback */
if ((reason == CVMX_USB_CALLBACK_TRANSFER_COMPLETE) && transaction->callback)
{
callback = transaction->callback;
user_data = transaction->callback_data;
}
}
if (!callback)
return;
if (cvmx_unlikely(usb->init_flags & CVMX_USB_INITIALIZE_FLAGS_DEBUG_CALLBACKS))
cvmx_dprintf("%*s%s: calling callback %p(usb=%p, complete_code=%s, "
"pipe_handle=%d, submit_handle=%d, bytes_transferred=%d, user_data=%p);\n",
2*usb->indent, "", __FUNCTION__, callback, usb,
__cvmx_usb_complete_to_string(complete_code),
pipe_handle, submit_handle, bytes_transferred, user_data);
callback((cvmx_usb_state_t *)usb, reason, complete_code, pipe_handle, submit_handle,
bytes_transferred, user_data);
if (cvmx_unlikely(usb->init_flags & CVMX_USB_INITIALIZE_FLAGS_DEBUG_CALLBACKS))
cvmx_dprintf("%*s%s: callback %p complete\n", 2*usb->indent, "",
__FUNCTION__, callback);
}
/**
* @INTERNAL
* Signal the completion of a transaction and free it. The
* transaction will be removed from the pipe transaction list.
*
* @param usb USB device state populated by
* cvmx_usb_initialize().
* @param pipe Pipe the transaction is on
* @param transaction
* Transaction that completed
* @param complete_code
* Completion code
*/
static void __cvmx_usb_perform_complete(cvmx_usb_internal_state_t * usb,
cvmx_usb_pipe_t *pipe,
cvmx_usb_transaction_t *transaction,
cvmx_usb_complete_t complete_code)
{
CVMX_USB_LOG_CALLED();
CVMX_USB_LOG_PARAM("%p", usb);
CVMX_USB_LOG_PARAM("%p", pipe);
CVMX_USB_LOG_PARAM("%p", transaction);
CVMX_USB_LOG_PARAM("%d", complete_code);
/* If this was a split then clear our split in progress marker */
if (usb->active_split == transaction)
usb->active_split = NULL;
/* Isochronous transactions need extra processing as they might not be done
after a single data transfer */
if (cvmx_unlikely(transaction->type == CVMX_USB_TRANSFER_ISOCHRONOUS))
{
/* Update the number of bytes transferred in this ISO packet */
transaction->iso_packets[0].length = transaction->actual_bytes;
transaction->iso_packets[0].status = complete_code;
/* If there are more ISOs pending and we succeeded, schedule the next
one */
if ((transaction->iso_number_packets > 1) && (complete_code == CVMX_USB_COMPLETE_SUCCESS))
{
transaction->actual_bytes = 0; /* No bytes transferred for this packet as of yet */
transaction->iso_number_packets--; /* One less ISO waiting to transfer */
transaction->iso_packets++; /* Increment to the next location in our packet array */
transaction->stage = CVMX_USB_STAGE_NON_CONTROL;
goto done;
}
}
/* Remove the transaction from the pipe list */
if (transaction->next)
transaction->next->prev = transaction->prev;
else
pipe->tail = transaction->prev;
if (transaction->prev)
transaction->prev->next = transaction->next;
else
pipe->head = transaction->next;
if (!pipe->head)
{
__cvmx_usb_remove_pipe(usb->active_pipes + pipe->transfer_type, pipe);
__cvmx_usb_append_pipe(&usb->idle_pipes, pipe);
}
__cvmx_usb_perform_callback(usb, pipe, transaction,
CVMX_USB_CALLBACK_TRANSFER_COMPLETE,
complete_code);
__cvmx_usb_free_transaction(usb, transaction);
done:
CVMX_USB_RETURN_NOTHING();
}
/**
* @INTERNAL
* Submit a usb transaction to a pipe. Called for all types
* of transactions.
*
* @param usb
* @param pipe_handle
* Which pipe to submit to. Will be validated in this function.
* @param type Transaction type
* @param flags Flags for the transaction
* @param buffer User buffer for the transaction
* @param buffer_length
* User buffer's length in bytes
* @param control_header
* For control transactions, the 8 byte standard header
* @param iso_start_frame
* For ISO transactions, the start frame
* @param iso_number_packets
* For ISO, the number of packet in the transaction.
* @param iso_packets
* A description of each ISO packet
* @param callback User callback to call when the transaction completes
* @param user_data User's data for the callback
*
* @return Submit handle or negative on failure. Matches the result
* in the external API.
*/
static int __cvmx_usb_submit_transaction(cvmx_usb_internal_state_t *usb,
int pipe_handle,
cvmx_usb_transfer_t type,
int flags,
uint64_t buffer,
int buffer_length,
uint64_t control_header,
int iso_start_frame,
int iso_number_packets,
cvmx_usb_iso_packet_t *iso_packets,
cvmx_usb_callback_func_t callback,
void *user_data)
{
int submit_handle;
cvmx_usb_transaction_t *transaction;
cvmx_usb_pipe_t *pipe = usb->pipe + pipe_handle;
CVMX_USB_LOG_CALLED();
if (cvmx_unlikely((pipe_handle < 0) || (pipe_handle >= MAX_PIPES)))
CVMX_USB_RETURN(CVMX_USB_INVALID_PARAM);
/* Fail if the pipe isn't open */
if (cvmx_unlikely((pipe->flags & __CVMX_USB_PIPE_FLAGS_OPEN) == 0))
CVMX_USB_RETURN(CVMX_USB_INVALID_PARAM);
if (cvmx_unlikely(pipe->transfer_type != type))
CVMX_USB_RETURN(CVMX_USB_INVALID_PARAM);
transaction = __cvmx_usb_alloc_transaction(usb);
if (cvmx_unlikely(!transaction))
CVMX_USB_RETURN(CVMX_USB_NO_MEMORY);
transaction->type = type;
transaction->flags |= flags;
transaction->buffer = buffer;
transaction->buffer_length = buffer_length;
transaction->control_header = control_header;
transaction->iso_start_frame = iso_start_frame; // FIXME: This is not used, implement it
transaction->iso_number_packets = iso_number_packets;
transaction->iso_packets = iso_packets;
transaction->callback = callback;
transaction->callback_data = user_data;
if (transaction->type == CVMX_USB_TRANSFER_CONTROL)
transaction->stage = CVMX_USB_STAGE_SETUP;
else
transaction->stage = CVMX_USB_STAGE_NON_CONTROL;
transaction->next = NULL;
if (pipe->tail)
{
transaction->prev = pipe->tail;
transaction->prev->next = transaction;
}
else
{
if (pipe->next_tx_frame < usb->frame_number)
pipe->next_tx_frame = usb->frame_number + pipe->interval -
(usb->frame_number - pipe->next_tx_frame) % pipe->interval;
transaction->prev = NULL;
pipe->head = transaction;
__cvmx_usb_remove_pipe(&usb->idle_pipes, pipe);
__cvmx_usb_append_pipe(usb->active_pipes + pipe->transfer_type, pipe);
}
pipe->tail = transaction;
submit_handle = __cvmx_usb_get_submit_handle(usb, transaction);
/* We may need to schedule the pipe if this was the head of the pipe */
if (!transaction->prev)
__cvmx_usb_schedule(usb, 0);
CVMX_USB_RETURN(submit_handle);
}
/**
* Call to submit a USB Bulk transfer to a pipe.
*
* @param state USB device state populated by
* cvmx_usb_initialize().
* @param pipe_handle
* Handle to the pipe for the transfer.
* @param buffer Physical address of the data buffer in
* memory. Note that this is NOT A POINTER, but
* the full 64bit physical address of the
* buffer. This may be zero if buffer_length is
* zero.
* @param buffer_length
* Length of buffer in bytes.
* @param callback Function to call when this transaction
* completes. If the return value of this
* function isn't an error, then this function
* is guaranteed to be called when the
* transaction completes. If this parameter is
* NULL, then the generic callback registered
* through cvmx_usb_register_callback is
* called. If both are NULL, then there is no
* way to know when a transaction completes.
* @param user_data User supplied data returned when the
* callback is called. This is only used if
* callback in not NULL.
*
* @return A submitted transaction handle or negative on
* failure. Negative values are failure codes from
* cvmx_usb_status_t.
*/
int cvmx_usb_submit_bulk(cvmx_usb_state_t *state, int pipe_handle,
uint64_t buffer, int buffer_length,
cvmx_usb_callback_func_t callback,
void *user_data)
{
int submit_handle;
cvmx_usb_internal_state_t *usb = (cvmx_usb_internal_state_t*)state;
CVMX_USB_LOG_CALLED();
CVMX_USB_LOG_PARAM("%p", state);
CVMX_USB_LOG_PARAM("%d", pipe_handle);
CVMX_USB_LOG_PARAM("0x%llx", (unsigned long long)buffer);
CVMX_USB_LOG_PARAM("%d", buffer_length);
/* Pipe handle checking is done later in a common place */
if (cvmx_unlikely(!buffer))
CVMX_USB_RETURN(CVMX_USB_INVALID_PARAM);
if (cvmx_unlikely(buffer_length < 0))
CVMX_USB_RETURN(CVMX_USB_INVALID_PARAM);
submit_handle = __cvmx_usb_submit_transaction(usb, pipe_handle,
CVMX_USB_TRANSFER_BULK,
0, /* flags */
buffer,
buffer_length,
0, /* control_header */
0, /* iso_start_frame */
0, /* iso_number_packets */
NULL, /* iso_packets */
callback,
user_data);
CVMX_USB_RETURN(submit_handle);
}
/**
* Call to submit a USB Interrupt transfer to a pipe.
*
* @param state USB device state populated by
* cvmx_usb_initialize().
* @param pipe_handle
* Handle to the pipe for the transfer.
* @param buffer Physical address of the data buffer in
* memory. Note that this is NOT A POINTER, but
* the full 64bit physical address of the
* buffer. This may be zero if buffer_length is
* zero.
* @param buffer_length
* Length of buffer in bytes.
* @param callback Function to call when this transaction
* completes. If the return value of this
* function isn't an error, then this function
* is guaranteed to be called when the
* transaction completes. If this parameter is
* NULL, then the generic callback registered
* through cvmx_usb_register_callback is
* called. If both are NULL, then there is no
* way to know when a transaction completes.
* @param user_data User supplied data returned when the
* callback is called. This is only used if
* callback in not NULL.
*
* @return A submitted transaction handle or negative on
* failure. Negative values are failure codes from
* cvmx_usb_status_t.
*/
int cvmx_usb_submit_interrupt(cvmx_usb_state_t *state, int pipe_handle,
uint64_t buffer, int buffer_length,
cvmx_usb_callback_func_t callback,
void *user_data)
{
int submit_handle;
cvmx_usb_internal_state_t *usb = (cvmx_usb_internal_state_t*)state;
CVMX_USB_LOG_CALLED();
CVMX_USB_LOG_PARAM("%p", state);
CVMX_USB_LOG_PARAM("%d", pipe_handle);
CVMX_USB_LOG_PARAM("0x%llx", (unsigned long long)buffer);
CVMX_USB_LOG_PARAM("%d", buffer_length);
/* Pipe handle checking is done later in a common place */
if (cvmx_unlikely(!buffer))
CVMX_USB_RETURN(CVMX_USB_INVALID_PARAM);
if (cvmx_unlikely(buffer_length < 0))
CVMX_USB_RETURN(CVMX_USB_INVALID_PARAM);
submit_handle = __cvmx_usb_submit_transaction(usb, pipe_handle,
CVMX_USB_TRANSFER_INTERRUPT,
0, /* flags */
buffer,
buffer_length,
0, /* control_header */
0, /* iso_start_frame */
0, /* iso_number_packets */
NULL, /* iso_packets */
callback,
user_data);
CVMX_USB_RETURN(submit_handle);
}
/**
* Call to submit a USB Control transfer to a pipe.
*
* @param state USB device state populated by
* cvmx_usb_initialize().
* @param pipe_handle
* Handle to the pipe for the transfer.
* @param control_header
* USB 8 byte control header physical address.
* Note that this is NOT A POINTER, but the
* full 64bit physical address of the buffer.
* @param buffer Physical address of the data buffer in
* memory. Note that this is NOT A POINTER, but
* the full 64bit physical address of the
* buffer. This may be zero if buffer_length is
* zero.
* @param buffer_length
* Length of buffer in bytes.
* @param callback Function to call when this transaction
* completes. If the return value of this
* function isn't an error, then this function
* is guaranteed to be called when the
* transaction completes. If this parameter is
* NULL, then the generic callback registered
* through cvmx_usb_register_callback is
* called. If both are NULL, then there is no
* way to know when a transaction completes.
* @param user_data User supplied data returned when the
* callback is called. This is only used if
* callback in not NULL.
*
* @return A submitted transaction handle or negative on
* failure. Negative values are failure codes from
* cvmx_usb_status_t.
*/
int cvmx_usb_submit_control(cvmx_usb_state_t *state, int pipe_handle,
uint64_t control_header,
uint64_t buffer, int buffer_length,
cvmx_usb_callback_func_t callback,
void *user_data)
{
int submit_handle;
cvmx_usb_internal_state_t *usb = (cvmx_usb_internal_state_t*)state;
cvmx_usb_control_header_t *header = cvmx_phys_to_ptr(control_header);
CVMX_USB_LOG_CALLED();
CVMX_USB_LOG_PARAM("%p", state);
CVMX_USB_LOG_PARAM("%d", pipe_handle);
CVMX_USB_LOG_PARAM("0x%llx", (unsigned long long)control_header);
CVMX_USB_LOG_PARAM("0x%llx", (unsigned long long)buffer);
CVMX_USB_LOG_PARAM("%d", buffer_length);
/* Pipe handle checking is done later in a common place */
if (cvmx_unlikely(!control_header))
CVMX_USB_RETURN(CVMX_USB_INVALID_PARAM);
/* Some drivers send a buffer with a zero length. God only knows why */
if (cvmx_unlikely(buffer && (buffer_length < 0)))
CVMX_USB_RETURN(CVMX_USB_INVALID_PARAM);
if (cvmx_unlikely(!buffer && (buffer_length != 0)))
CVMX_USB_RETURN(CVMX_USB_INVALID_PARAM);
if ((header->s.request_type & 0x80) == 0)
buffer_length = cvmx_le16_to_cpu(header->s.length);
submit_handle = __cvmx_usb_submit_transaction(usb, pipe_handle,
CVMX_USB_TRANSFER_CONTROL,
0, /* flags */
buffer,
buffer_length,
control_header,
0, /* iso_start_frame */
0, /* iso_number_packets */
NULL, /* iso_packets */
callback,
user_data);
CVMX_USB_RETURN(submit_handle);
}
/**
* Call to submit a USB Isochronous transfer to a pipe.
*
* @param state USB device state populated by
* cvmx_usb_initialize().
* @param pipe_handle
* Handle to the pipe for the transfer.
* @param start_frame
* Number of frames into the future to schedule
* this transaction.
* @param flags Flags to control the transfer. See
* cvmx_usb_isochronous_flags_t for the flag
* definitions.
* @param number_packets
* Number of sequential packets to transfer.
* "packets" is a pointer to an array of this
* many packet structures.
* @param packets Description of each transfer packet as
* defined by cvmx_usb_iso_packet_t. The array
* pointed to here must stay valid until the
* complete callback is called.
* @param buffer Physical address of the data buffer in
* memory. Note that this is NOT A POINTER, but
* the full 64bit physical address of the
* buffer. This may be zero if buffer_length is
* zero.
* @param buffer_length
* Length of buffer in bytes.
* @param callback Function to call when this transaction
* completes. If the return value of this
* function isn't an error, then this function
* is guaranteed to be called when the
* transaction completes. If this parameter is
* NULL, then the generic callback registered
* through cvmx_usb_register_callback is
* called. If both are NULL, then there is no
* way to know when a transaction completes.
* @param user_data User supplied data returned when the
* callback is called. This is only used if
* callback in not NULL.
*
* @return A submitted transaction handle or negative on
* failure. Negative values are failure codes from
* cvmx_usb_status_t.
*/
int cvmx_usb_submit_isochronous(cvmx_usb_state_t *state, int pipe_handle,
int start_frame, int flags,
int number_packets,
cvmx_usb_iso_packet_t packets[],
uint64_t buffer, int buffer_length,
cvmx_usb_callback_func_t callback,
void *user_data)
{
int submit_handle;
cvmx_usb_internal_state_t *usb = (cvmx_usb_internal_state_t*)state;
CVMX_USB_LOG_CALLED();
CVMX_USB_LOG_PARAM("%p", state);
CVMX_USB_LOG_PARAM("%d", pipe_handle);
CVMX_USB_LOG_PARAM("%d", start_frame);
CVMX_USB_LOG_PARAM("0x%x", flags);
CVMX_USB_LOG_PARAM("%d", number_packets);
CVMX_USB_LOG_PARAM("%p", packets);
CVMX_USB_LOG_PARAM("0x%llx", (unsigned long long)buffer);
CVMX_USB_LOG_PARAM("%d", buffer_length);
/* Pipe handle checking is done later in a common place */
if (cvmx_unlikely(start_frame < 0))
CVMX_USB_RETURN(CVMX_USB_INVALID_PARAM);
if (cvmx_unlikely(flags & ~(CVMX_USB_ISOCHRONOUS_FLAGS_ALLOW_SHORT | CVMX_USB_ISOCHRONOUS_FLAGS_ASAP)))
CVMX_USB_RETURN(CVMX_USB_INVALID_PARAM);
if (cvmx_unlikely(number_packets < 1))
CVMX_USB_RETURN(CVMX_USB_INVALID_PARAM);
if (cvmx_unlikely(!packets))
CVMX_USB_RETURN(CVMX_USB_INVALID_PARAM);
if (cvmx_unlikely(!buffer))
CVMX_USB_RETURN(CVMX_USB_INVALID_PARAM);
if (cvmx_unlikely(buffer_length < 0))
CVMX_USB_RETURN(CVMX_USB_INVALID_PARAM);
submit_handle = __cvmx_usb_submit_transaction(usb, pipe_handle,
CVMX_USB_TRANSFER_ISOCHRONOUS,
flags,
buffer,
buffer_length,
0, /* control_header */
start_frame,
number_packets,
packets,
callback,
user_data);
CVMX_USB_RETURN(submit_handle);
}
/**
* Cancel one outstanding request in a pipe. Canceling a request
* can fail if the transaction has already completed before cancel
* is called. Even after a successful cancel call, it may take
* a frame or two for the cvmx_usb_poll() function to call the
* associated callback.
*
* @param state USB device state populated by
* cvmx_usb_initialize().
* @param pipe_handle
* Pipe handle to cancel requests in.
* @param submit_handle
* Handle to transaction to cancel, returned by the submit function.
*
* @return CVMX_USB_SUCCESS or a negative error code defined in
* cvmx_usb_status_t.
*/
cvmx_usb_status_t cvmx_usb_cancel(cvmx_usb_state_t *state, int pipe_handle,
int submit_handle)
{
cvmx_usb_transaction_t *transaction;
cvmx_usb_internal_state_t *usb = (cvmx_usb_internal_state_t*)state;
cvmx_usb_pipe_t *pipe = usb->pipe + pipe_handle;
CVMX_USB_LOG_CALLED();
CVMX_USB_LOG_PARAM("%p", state);
CVMX_USB_LOG_PARAM("%d", pipe_handle);
CVMX_USB_LOG_PARAM("%d", submit_handle);
if (cvmx_unlikely((pipe_handle < 0) || (pipe_handle >= MAX_PIPES)))
CVMX_USB_RETURN(CVMX_USB_INVALID_PARAM);
if (cvmx_unlikely((submit_handle < 0) || (submit_handle >= MAX_TRANSACTIONS)))
CVMX_USB_RETURN(CVMX_USB_INVALID_PARAM);
/* Fail if the pipe isn't open */
if (cvmx_unlikely((pipe->flags & __CVMX_USB_PIPE_FLAGS_OPEN) == 0))
CVMX_USB_RETURN(CVMX_USB_INVALID_PARAM);
transaction = usb->transaction + submit_handle;
/* Fail if this transaction already completed */
if (cvmx_unlikely((transaction->flags & __CVMX_USB_TRANSACTION_FLAGS_IN_USE) == 0))
CVMX_USB_RETURN(CVMX_USB_INVALID_PARAM);
/* If the transaction is the HEAD of the queue and scheduled. We need to
treat it special */
if ((pipe->head == transaction) &&
(pipe->flags & __CVMX_USB_PIPE_FLAGS_SCHEDULED))
{
cvmx_usbcx_hccharx_t usbc_hcchar;
usb->pipe_for_channel[pipe->channel] = NULL;
pipe->flags &= ~__CVMX_USB_PIPE_FLAGS_SCHEDULED;
CVMX_SYNCW;
usbc_hcchar.u32 = __cvmx_usb_read_csr32(usb, CVMX_USBCX_HCCHARX(pipe->channel, usb->index));
/* If the channel isn't enabled then the transaction already completed */
if (usbc_hcchar.s.chena)
{
usbc_hcchar.s.chdis = 1;
__cvmx_usb_write_csr32(usb, CVMX_USBCX_HCCHARX(pipe->channel, usb->index), usbc_hcchar.u32);
}
}
__cvmx_usb_perform_complete(usb, pipe, transaction, CVMX_USB_COMPLETE_CANCEL);
CVMX_USB_RETURN(CVMX_USB_SUCCESS);
}
/**
* Cancel all outstanding requests in a pipe. Logically all this
* does is call cvmx_usb_cancel() in a loop.
*
* @param state USB device state populated by
* cvmx_usb_initialize().
* @param pipe_handle
* Pipe handle to cancel requests in.
*
* @return CVMX_USB_SUCCESS or a negative error code defined in
* cvmx_usb_status_t.
*/
cvmx_usb_status_t cvmx_usb_cancel_all(cvmx_usb_state_t *state, int pipe_handle)
{
cvmx_usb_internal_state_t *usb = (cvmx_usb_internal_state_t*)state;
cvmx_usb_pipe_t *pipe = usb->pipe + pipe_handle;
CVMX_USB_LOG_CALLED();
CVMX_USB_LOG_PARAM("%p", state);
CVMX_USB_LOG_PARAM("%d", pipe_handle);
if (cvmx_unlikely((pipe_handle < 0) || (pipe_handle >= MAX_PIPES)))
CVMX_USB_RETURN(CVMX_USB_INVALID_PARAM);
/* Fail if the pipe isn't open */
if (cvmx_unlikely((pipe->flags & __CVMX_USB_PIPE_FLAGS_OPEN) == 0))
CVMX_USB_RETURN(CVMX_USB_INVALID_PARAM);
/* Simply loop through and attempt to cancel each transaction */
while (pipe->head)
{
cvmx_usb_status_t result = cvmx_usb_cancel(state, pipe_handle,
__cvmx_usb_get_submit_handle(usb, pipe->head));
if (cvmx_unlikely(result != CVMX_USB_SUCCESS))
CVMX_USB_RETURN(result);
}
CVMX_USB_RETURN(CVMX_USB_SUCCESS);
}
/**
* Close a pipe created with cvmx_usb_open_pipe().
*
* @param state USB device state populated by
* cvmx_usb_initialize().
* @param pipe_handle
* Pipe handle to close.
*
* @return CVMX_USB_SUCCESS or a negative error code defined in
* cvmx_usb_status_t. CVMX_USB_BUSY is returned if the
* pipe has outstanding transfers.
*/
cvmx_usb_status_t cvmx_usb_close_pipe(cvmx_usb_state_t *state, int pipe_handle)
{
cvmx_usb_internal_state_t *usb = (cvmx_usb_internal_state_t*)state;
cvmx_usb_pipe_t *pipe = usb->pipe + pipe_handle;
CVMX_USB_LOG_CALLED();
CVMX_USB_LOG_PARAM("%p", state);
CVMX_USB_LOG_PARAM("%d", pipe_handle);
if (cvmx_unlikely((pipe_handle < 0) || (pipe_handle >= MAX_PIPES)))
CVMX_USB_RETURN(CVMX_USB_INVALID_PARAM);
/* Fail if the pipe isn't open */
if (cvmx_unlikely((pipe->flags & __CVMX_USB_PIPE_FLAGS_OPEN) == 0))
CVMX_USB_RETURN(CVMX_USB_INVALID_PARAM);
/* Fail if the pipe has pending transactions */
if (cvmx_unlikely(pipe->head))
CVMX_USB_RETURN(CVMX_USB_BUSY);
pipe->flags = 0;
__cvmx_usb_remove_pipe(&usb->idle_pipes, pipe);
__cvmx_usb_append_pipe(&usb->free_pipes, pipe);
CVMX_USB_RETURN(CVMX_USB_SUCCESS);
}
/**
* Register a function to be called when various USB events occur.
*
* @param state USB device state populated by
* cvmx_usb_initialize().
* @param reason Which event to register for.
* @param callback Function to call when the event occurs.
* @param user_data User data parameter to the function.
*
* @return CVMX_USB_SUCCESS or a negative error code defined in
* cvmx_usb_status_t.
*/
cvmx_usb_status_t cvmx_usb_register_callback(cvmx_usb_state_t *state,
cvmx_usb_callback_t reason,
cvmx_usb_callback_func_t callback,
void *user_data)
{
cvmx_usb_internal_state_t *usb = (cvmx_usb_internal_state_t*)state;
CVMX_USB_LOG_CALLED();
CVMX_USB_LOG_PARAM("%p", state);
CVMX_USB_LOG_PARAM("%d", reason);
CVMX_USB_LOG_PARAM("%p", callback);
CVMX_USB_LOG_PARAM("%p", user_data);
if (cvmx_unlikely(reason >= __CVMX_USB_CALLBACK_END))
CVMX_USB_RETURN(CVMX_USB_INVALID_PARAM);
if (cvmx_unlikely(!callback))
CVMX_USB_RETURN(CVMX_USB_INVALID_PARAM);
usb->callback[reason] = callback;
usb->callback_data[reason] = user_data;
CVMX_USB_RETURN(CVMX_USB_SUCCESS);
}
/**
* Get the current USB protocol level frame number. The frame
* number is always in the range of 0-0x7ff.
*
* @param state USB device state populated by
* cvmx_usb_initialize().
*
* @return USB frame number
*/
int cvmx_usb_get_frame_number(cvmx_usb_state_t *state)
{
int frame_number;
cvmx_usb_internal_state_t *usb = (cvmx_usb_internal_state_t*)state;
cvmx_usbcx_hfnum_t usbc_hfnum;
CVMX_USB_LOG_CALLED();
CVMX_USB_LOG_PARAM("%p", state);
usbc_hfnum.u32 = __cvmx_usb_read_csr32(usb, CVMX_USBCX_HFNUM(usb->index));
frame_number = usbc_hfnum.s.frnum;
CVMX_USB_RETURN(frame_number);
}
/**
* @INTERNAL
* Poll a channel for status
*
* @param usb USB device
* @param channel Channel to poll
*
* @return Zero on success
*/
static int __cvmx_usb_poll_channel(cvmx_usb_internal_state_t *usb, int channel)
{
cvmx_usbcx_hcintx_t usbc_hcint;
cvmx_usbcx_hctsizx_t usbc_hctsiz;
cvmx_usbcx_hccharx_t usbc_hcchar;
cvmx_usb_pipe_t *pipe;
cvmx_usb_transaction_t *transaction;
int bytes_this_transfer;
int bytes_in_last_packet;
int packets_processed;
int buffer_space_left;
CVMX_USB_LOG_CALLED();
CVMX_USB_LOG_PARAM("%p", usb);
CVMX_USB_LOG_PARAM("%d", channel);
/* Read the interrupt status bits for the channel */
usbc_hcint.u32 = __cvmx_usb_read_csr32(usb, CVMX_USBCX_HCINTX(channel, usb->index));
if (usb->init_flags & CVMX_USB_INITIALIZE_FLAGS_NO_DMA)
{
usbc_hcchar.u32 = __cvmx_usb_read_csr32(usb, CVMX_USBCX_HCCHARX(channel, usb->index));
if (usbc_hcchar.s.chena && usbc_hcchar.s.chdis)
{
/* There seems to be a bug in CN31XX which can cause interrupt
IN transfers to get stuck until we do a write of HCCHARX
without changing things */
__cvmx_usb_write_csr32(usb, CVMX_USBCX_HCCHARX(channel, usb->index), usbc_hcchar.u32);
CVMX_USB_RETURN(0);
}
/* In non DMA mode the channels don't halt themselves. We need to
manually disable channels that are left running */
if (!usbc_hcint.s.chhltd)
{
if (usbc_hcchar.s.chena)
{
cvmx_usbcx_hcintmskx_t hcintmsk;
/* Disable all interrupts except CHHLTD */
hcintmsk.u32 = 0;
hcintmsk.s.chhltdmsk = 1;
__cvmx_usb_write_csr32(usb, CVMX_USBCX_HCINTMSKX(channel, usb->index), hcintmsk.u32);
usbc_hcchar.s.chdis = 1;
__cvmx_usb_write_csr32(usb, CVMX_USBCX_HCCHARX(channel, usb->index), usbc_hcchar.u32);
CVMX_USB_RETURN(0);
}
else if (usbc_hcint.s.xfercompl)
{
/* Successful IN/OUT with transfer complete. Channel halt isn't needed */
}
else
{
cvmx_dprintf("USB%d: Channel %d interrupt without halt\n", usb->index, channel);
CVMX_USB_RETURN(0);
}
}
}
else
{
/* There is are no interrupts that we need to process when the channel is
still running */
if (!usbc_hcint.s.chhltd)
CVMX_USB_RETURN(0);
}
/* Disable the channel interrupts now that it is done */
__cvmx_usb_write_csr32(usb, CVMX_USBCX_HCINTMSKX(channel, usb->index), 0);
usb->idle_hardware_channels |= (1<<channel);
/* Make sure this channel is tied to a valid pipe */
pipe = usb->pipe_for_channel[channel];
CVMX_PREFETCH(pipe, 0);
CVMX_PREFETCH(pipe, 128);
if (!pipe)
CVMX_USB_RETURN(0);
transaction = pipe->head;
CVMX_PREFETCH0(transaction);
/* Disconnect this pipe from the HW channel. Later the schedule function will
figure out which pipe needs to go */
usb->pipe_for_channel[channel] = NULL;
pipe->flags &= ~__CVMX_USB_PIPE_FLAGS_SCHEDULED;
/* Read the channel config info so we can figure out how much data
transfered */
usbc_hcchar.u32 = __cvmx_usb_read_csr32(usb, CVMX_USBCX_HCCHARX(channel, usb->index));
usbc_hctsiz.u32 = __cvmx_usb_read_csr32(usb, CVMX_USBCX_HCTSIZX(channel, usb->index));
/* Calculating the number of bytes successfully transferred is dependent on
the transfer direction */
packets_processed = transaction->pktcnt - usbc_hctsiz.s.pktcnt;
if (usbc_hcchar.s.epdir)
{
/* IN transactions are easy. For every byte received the hardware
decrements xfersize. All we need to do is subtract the current
value of xfersize from its starting value and we know how many
bytes were written to the buffer */
bytes_this_transfer = transaction->xfersize - usbc_hctsiz.s.xfersize;
}
else
{
/* OUT transaction don't decrement xfersize. Instead pktcnt is
decremented on every successful packet send. The hardware does
this when it receives an ACK, or NYET. If it doesn't
receive one of these responses pktcnt doesn't change */
bytes_this_transfer = packets_processed * usbc_hcchar.s.mps;
/* The last packet may not be a full transfer if we didn't have
enough data */
if (bytes_this_transfer > transaction->xfersize)
bytes_this_transfer = transaction->xfersize;
}
/* Figure out how many bytes were in the last packet of the transfer */
if (packets_processed)
bytes_in_last_packet = bytes_this_transfer - (packets_processed-1) * usbc_hcchar.s.mps;
else
bytes_in_last_packet = bytes_this_transfer;
/* As a special case, setup transactions output the setup header, not
the user's data. For this reason we don't count setup data as bytes
transferred */
if ((transaction->stage == CVMX_USB_STAGE_SETUP) ||
(transaction->stage == CVMX_USB_STAGE_SETUP_SPLIT_COMPLETE))
bytes_this_transfer = 0;
/* Optional debug output */
if (cvmx_unlikely((usb->init_flags & CVMX_USB_INITIALIZE_FLAGS_DEBUG_TRANSFERS) ||
(pipe->flags & CVMX_USB_PIPE_FLAGS_DEBUG_TRANSFERS)))
cvmx_dprintf("%s: Channel %d halted. Pipe %d transaction %d stage %d bytes=%d\n",
__FUNCTION__, channel,
__cvmx_usb_get_pipe_handle(usb, pipe),
__cvmx_usb_get_submit_handle(usb, transaction),
transaction->stage, bytes_this_transfer);
/* Add the bytes transferred to the running total. It is important that
bytes_this_transfer doesn't count any data that needs to be
retransmitted */
transaction->actual_bytes += bytes_this_transfer;
if (transaction->type == CVMX_USB_TRANSFER_ISOCHRONOUS)
buffer_space_left = transaction->iso_packets[0].length - transaction->actual_bytes;
else
buffer_space_left = transaction->buffer_length - transaction->actual_bytes;
/* We need to remember the PID toggle state for the next transaction. The
hardware already updated it for the next transaction */
pipe->pid_toggle = !(usbc_hctsiz.s.pid == 0);
/* For high speed bulk out, assume the next transaction will need to do a
ping before proceeding. If this isn't true the ACK processing below
will clear this flag */
if ((pipe->device_speed == CVMX_USB_SPEED_HIGH) &&
(pipe->transfer_type == CVMX_USB_TRANSFER_BULK) &&
(pipe->transfer_dir == CVMX_USB_DIRECTION_OUT))
pipe->flags |= __CVMX_USB_PIPE_FLAGS_NEED_PING;
if (usbc_hcint.s.stall)
{
/* STALL as a response means this transaction cannot be completed
because the device can't process transactions. Tell the user. Any
data that was transferred will be counted on the actual bytes
transferred */
pipe->pid_toggle = 0;
__cvmx_usb_perform_complete(usb, pipe, transaction, CVMX_USB_COMPLETE_STALL);
}
else if (usbc_hcint.s.xacterr)
{
/* We know at least one packet worked if we get a ACK or NAK. Reset the retry counter */
if (usbc_hcint.s.nak || usbc_hcint.s.ack)
transaction->retries = 0;
transaction->retries++;
if (transaction->retries > MAX_RETRIES)
{
/* XactErr as a response means the device signaled something wrong with
the transfer. For example, PID toggle errors cause these */
__cvmx_usb_perform_complete(usb, pipe, transaction, CVMX_USB_COMPLETE_XACTERR);
}
else
{
/* If this was a split then clear our split in progress marker */
if (usb->active_split == transaction)
usb->active_split = NULL;
/* Rewind to the beginning of the transaction by anding off the
split complete bit */
transaction->stage &= ~1;
pipe->split_sc_frame = -1;
pipe->next_tx_frame += pipe->interval;
if (pipe->next_tx_frame < usb->frame_number)
pipe->next_tx_frame = usb->frame_number + pipe->interval -
(usb->frame_number - pipe->next_tx_frame) % pipe->interval;
}
}
else if (usbc_hcint.s.bblerr)
{
/* Babble Error (BblErr) */
__cvmx_usb_perform_complete(usb, pipe, transaction, CVMX_USB_COMPLETE_BABBLEERR);
}
else if (usbc_hcint.s.datatglerr)
{
/* We'll retry the exact same transaction again */
transaction->retries++;
}
else if (usbc_hcint.s.nyet)
{
/* NYET as a response is only allowed in three cases: as a response to
a ping, as a response to a split transaction, and as a response to
a bulk out. The ping case is handled by hardware, so we only have
splits and bulk out */
if (!__cvmx_usb_pipe_needs_split(usb, pipe))
{
transaction->retries = 0;
/* If there is more data to go then we need to try again. Otherwise
this transaction is complete */
if ((buffer_space_left == 0) || (bytes_in_last_packet < pipe->max_packet))
__cvmx_usb_perform_complete(usb, pipe, transaction, CVMX_USB_COMPLETE_SUCCESS);
}
else
{
/* Split transactions retry the split complete 4 times then rewind
to the start split and do the entire transactions again */
transaction->retries++;
if ((transaction->retries & 0x3) == 0)
{
/* Rewind to the beginning of the transaction by anding off the
split complete bit */
transaction->stage &= ~1;
pipe->split_sc_frame = -1;
}
}
}
else if (usbc_hcint.s.ack)
{
transaction->retries = 0;
/* The ACK bit can only be checked after the other error bits. This is
because a multi packet transfer may succeed in a number of packets
and then get a different response on the last packet. In this case
both ACK and the last response bit will be set. If none of the
other response bits is set, then the last packet must have been an
ACK */
/* Since we got an ACK, we know we don't need to do a ping on this
pipe */
pipe->flags &= ~__CVMX_USB_PIPE_FLAGS_NEED_PING;
switch (transaction->type)
{
case CVMX_USB_TRANSFER_CONTROL:
switch (transaction->stage)
{
case CVMX_USB_STAGE_NON_CONTROL:
case CVMX_USB_STAGE_NON_CONTROL_SPLIT_COMPLETE:
/* This should be impossible */
__cvmx_usb_perform_complete(usb, pipe, transaction, CVMX_USB_COMPLETE_ERROR);
break;
case CVMX_USB_STAGE_SETUP:
pipe->pid_toggle = 1;
if (__cvmx_usb_pipe_needs_split(usb, pipe))
transaction->stage = CVMX_USB_STAGE_SETUP_SPLIT_COMPLETE;
else
{
cvmx_usb_control_header_t *header = cvmx_phys_to_ptr(transaction->control_header);
if (header->s.length)
transaction->stage = CVMX_USB_STAGE_DATA;
else
transaction->stage = CVMX_USB_STAGE_STATUS;
}
break;
case CVMX_USB_STAGE_SETUP_SPLIT_COMPLETE:
{
cvmx_usb_control_header_t *header = cvmx_phys_to_ptr(transaction->control_header);
if (header->s.length)
transaction->stage = CVMX_USB_STAGE_DATA;
else
transaction->stage = CVMX_USB_STAGE_STATUS;
}
break;
case CVMX_USB_STAGE_DATA:
if (__cvmx_usb_pipe_needs_split(usb, pipe))
{
transaction->stage = CVMX_USB_STAGE_DATA_SPLIT_COMPLETE;
/* For setup OUT data that are splits, the hardware
doesn't appear to count transferred data. Here
we manually update the data transferred */
if (!usbc_hcchar.s.epdir)
{
if (buffer_space_left < pipe->max_packet)
transaction->actual_bytes += buffer_space_left;
else
transaction->actual_bytes += pipe->max_packet;
}
}
else if ((buffer_space_left == 0) || (bytes_in_last_packet < pipe->max_packet))
{
pipe->pid_toggle = 1;
transaction->stage = CVMX_USB_STAGE_STATUS;
}
break;
case CVMX_USB_STAGE_DATA_SPLIT_COMPLETE:
if ((buffer_space_left == 0) || (bytes_in_last_packet < pipe->max_packet))
{
pipe->pid_toggle = 1;
transaction->stage = CVMX_USB_STAGE_STATUS;
}
else
{
transaction->stage = CVMX_USB_STAGE_DATA;
}
break;
case CVMX_USB_STAGE_STATUS:
if (__cvmx_usb_pipe_needs_split(usb, pipe))
transaction->stage = CVMX_USB_STAGE_STATUS_SPLIT_COMPLETE;
else
__cvmx_usb_perform_complete(usb, pipe, transaction, CVMX_USB_COMPLETE_SUCCESS);
break;
case CVMX_USB_STAGE_STATUS_SPLIT_COMPLETE:
__cvmx_usb_perform_complete(usb, pipe, transaction, CVMX_USB_COMPLETE_SUCCESS);
break;
}
break;
case CVMX_USB_TRANSFER_BULK:
case CVMX_USB_TRANSFER_INTERRUPT:
/* The only time a bulk transfer isn't complete when
it finishes with an ACK is during a split transaction. For
splits we need to continue the transfer if more data is
needed */
if (__cvmx_usb_pipe_needs_split(usb, pipe))
{
if (transaction->stage == CVMX_USB_STAGE_NON_CONTROL)
transaction->stage = CVMX_USB_STAGE_NON_CONTROL_SPLIT_COMPLETE;
else
{
if (buffer_space_left && (bytes_in_last_packet == pipe->max_packet))
transaction->stage = CVMX_USB_STAGE_NON_CONTROL;
else
{
if (transaction->type == CVMX_USB_TRANSFER_INTERRUPT)
pipe->next_tx_frame += pipe->interval;
__cvmx_usb_perform_complete(usb, pipe, transaction, CVMX_USB_COMPLETE_SUCCESS);
}
}
}
else
{
if ((pipe->device_speed == CVMX_USB_SPEED_HIGH) &&
(pipe->transfer_type == CVMX_USB_TRANSFER_BULK) &&
(pipe->transfer_dir == CVMX_USB_DIRECTION_OUT) &&
(usbc_hcint.s.nak))
pipe->flags |= __CVMX_USB_PIPE_FLAGS_NEED_PING;
if (!buffer_space_left || (bytes_in_last_packet < pipe->max_packet))
{
if (transaction->type == CVMX_USB_TRANSFER_INTERRUPT)
pipe->next_tx_frame += pipe->interval;
__cvmx_usb_perform_complete(usb, pipe, transaction, CVMX_USB_COMPLETE_SUCCESS);
}
}
break;
case CVMX_USB_TRANSFER_ISOCHRONOUS:
if (__cvmx_usb_pipe_needs_split(usb, pipe))
{
/* ISOCHRONOUS OUT splits don't require a complete split stage.
Instead they use a sequence of begin OUT splits to transfer
the data 188 bytes at a time. Once the transfer is complete,
the pipe sleeps until the next schedule interval */
if (pipe->transfer_dir == CVMX_USB_DIRECTION_OUT)
{
/* If no space left or this wasn't a max size packet then
this transfer is complete. Otherwise start it again
to send the next 188 bytes */
if (!buffer_space_left || (bytes_this_transfer < 188))
{
pipe->next_tx_frame += pipe->interval;
__cvmx_usb_perform_complete(usb, pipe, transaction, CVMX_USB_COMPLETE_SUCCESS);
}
}
else
{
if (transaction->stage == CVMX_USB_STAGE_NON_CONTROL_SPLIT_COMPLETE)
{
/* We are in the incoming data phase. Keep getting
data until we run out of space or get a small
packet */
if ((buffer_space_left == 0) || (bytes_in_last_packet < pipe->max_packet))
{
pipe->next_tx_frame += pipe->interval;
__cvmx_usb_perform_complete(usb, pipe, transaction, CVMX_USB_COMPLETE_SUCCESS);
}
}
else
transaction->stage = CVMX_USB_STAGE_NON_CONTROL_SPLIT_COMPLETE;
}
}
else
{
pipe->next_tx_frame += pipe->interval;
__cvmx_usb_perform_complete(usb, pipe, transaction, CVMX_USB_COMPLETE_SUCCESS);
}
break;
}
}
else if (usbc_hcint.s.nak)
{
/* If this was a split then clear our split in progress marker */
if (usb->active_split == transaction)
usb->active_split = NULL;
/* NAK as a response means the device couldn't accept the transaction,
but it should be retried in the future. Rewind to the beginning of
the transaction by anding off the split complete bit. Retry in the
next interval */
transaction->retries = 0;
transaction->stage &= ~1;
pipe->next_tx_frame += pipe->interval;
if (pipe->next_tx_frame < usb->frame_number)
pipe->next_tx_frame = usb->frame_number + pipe->interval -
(usb->frame_number - pipe->next_tx_frame) % pipe->interval;
}
else
{
cvmx_usb_port_status_t port;
port = cvmx_usb_get_status((cvmx_usb_state_t *)usb);
if (port.port_enabled)
{
/* We'll retry the exact same transaction again */
transaction->retries++;
}
else
{
/* We get channel halted interrupts with no result bits sets when the
cable is unplugged */
__cvmx_usb_perform_complete(usb, pipe, transaction, CVMX_USB_COMPLETE_ERROR);
}
}
CVMX_USB_RETURN(0);
}
/**
* Poll the USB block for status and call all needed callback
* handlers. This function is meant to be called in the interrupt
* handler for the USB controller. It can also be called
* periodically in a loop for non-interrupt based operation.
*
* @param state USB device state populated by
* cvmx_usb_initialize().
*
* @return CVMX_USB_SUCCESS or a negative error code defined in
* cvmx_usb_status_t.
*/
cvmx_usb_status_t cvmx_usb_poll(cvmx_usb_state_t *state)
{
cvmx_usbcx_hfnum_t usbc_hfnum;
cvmx_usbcx_gintsts_t usbc_gintsts;
cvmx_usb_internal_state_t *usb = (cvmx_usb_internal_state_t*)state;
CVMX_PREFETCH(usb, 0);
CVMX_PREFETCH(usb, 1*128);
CVMX_PREFETCH(usb, 2*128);
CVMX_PREFETCH(usb, 3*128);
CVMX_PREFETCH(usb, 4*128);
CVMX_USB_LOG_CALLED();
CVMX_USB_LOG_PARAM("%p", state);
/* Update the frame counter */
usbc_hfnum.u32 = __cvmx_usb_read_csr32(usb, CVMX_USBCX_HFNUM(usb->index));
if ((usb->frame_number&0x3fff) > usbc_hfnum.s.frnum)
usb->frame_number += 0x4000;
usb->frame_number &= ~0x3fffull;
usb->frame_number |= usbc_hfnum.s.frnum;
/* Read the pending interrupts */
usbc_gintsts.u32 = __cvmx_usb_read_csr32(usb, CVMX_USBCX_GINTSTS(usb->index));
/* Clear the interrupts now that we know about them */
__cvmx_usb_write_csr32(usb, CVMX_USBCX_GINTSTS(usb->index), usbc_gintsts.u32);
if (usbc_gintsts.s.rxflvl)
{
/* RxFIFO Non-Empty (RxFLvl)
Indicates that there is at least one packet pending to be read
from the RxFIFO. */
/* In DMA mode this is handled by hardware */
if (usb->init_flags & CVMX_USB_INITIALIZE_FLAGS_NO_DMA)
__cvmx_usb_poll_rx_fifo(usb);
}
if (usbc_gintsts.s.ptxfemp || usbc_gintsts.s.nptxfemp)
{
/* Fill the Tx FIFOs when not in DMA mode */
if (usb->init_flags & CVMX_USB_INITIALIZE_FLAGS_NO_DMA)
__cvmx_usb_poll_tx_fifo(usb);
}
if (usbc_gintsts.s.disconnint || usbc_gintsts.s.prtint)
{
cvmx_usbcx_hprt_t usbc_hprt;
/* Disconnect Detected Interrupt (DisconnInt)
Asserted when a device disconnect is detected. */
/* Host Port Interrupt (PrtInt)
The core sets this bit to indicate a change in port status of one
of the O2P USB core ports in Host mode. The application must
read the Host Port Control and Status (HPRT) register to
determine the exact event that caused this interrupt. The
application must clear the appropriate status bit in the Host Port
Control and Status register to clear this bit. */
/* Call the user's port callback */
__cvmx_usb_perform_callback(usb, NULL, NULL,
CVMX_USB_CALLBACK_PORT_CHANGED,
CVMX_USB_COMPLETE_SUCCESS);
/* Clear the port change bits */
usbc_hprt.u32 = __cvmx_usb_read_csr32(usb, CVMX_USBCX_HPRT(usb->index));
usbc_hprt.s.prtena = 0;
__cvmx_usb_write_csr32(usb, CVMX_USBCX_HPRT(usb->index), usbc_hprt.u32);
}
if (usbc_gintsts.s.hchint)
{
/* Host Channels Interrupt (HChInt)
The core sets this bit to indicate that an interrupt is pending on
one of the channels of the core (in Host mode). The application
must read the Host All Channels Interrupt (HAINT) register to
determine the exact number of the channel on which the
interrupt occurred, and then read the corresponding Host
Channel-n Interrupt (HCINTn) register to determine the exact
cause of the interrupt. The application must clear the
appropriate status bit in the HCINTn register to clear this bit. */
cvmx_usbcx_haint_t usbc_haint;
usbc_haint.u32 = __cvmx_usb_read_csr32(usb, CVMX_USBCX_HAINT(usb->index));
while (usbc_haint.u32)
{
int channel;
CVMX_CLZ(channel, usbc_haint.u32);
channel = 31 - channel;
__cvmx_usb_poll_channel(usb, channel);
usbc_haint.u32 ^= 1<<channel;
}
}
__cvmx_usb_schedule(usb, usbc_gintsts.s.sof);
CVMX_USB_RETURN(CVMX_USB_SUCCESS);
}
/***********************license start***************
* Copyright (c) 2003-2010 Cavium Networks (support@cavium.com). All rights
* reserved.
*
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided
* with the distribution.
* * Neither the name of Cavium Networks nor the names of
* its contributors may be used to endorse or promote products
* derived from this software without specific prior written
* permission.
* This Software, including technical data, may be subject to U.S. export control
* laws, including the U.S. Export Administration Act and its associated
* regulations, and may be subject to export or import regulations in other
* countries.
* TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SOFTWARE IS PROVIDED "AS IS"
* AND WITH ALL FAULTS AND CAVIUM NETWORKS MAKES NO PROMISES, REPRESENTATIONS OR
* WARRANTIES, EITHER EXPRESS, IMPLIED, STATUTORY, OR OTHERWISE, WITH RESPECT TO
* THE SOFTWARE, INCLUDING ITS CONDITION, ITS CONFORMITY TO ANY REPRESENTATION OR
* DESCRIPTION, OR THE EXISTENCE OF ANY LATENT OR PATENT DEFECTS, AND CAVIUM
* SPECIFICALLY DISCLAIMS ALL IMPLIED (IF ANY) WARRANTIES OF TITLE,
* MERCHANTABILITY, NONINFRINGEMENT, FITNESS FOR A PARTICULAR PURPOSE, LACK OF
* VIRUSES, ACCURACY OR COMPLETENESS, QUIET ENJOYMENT, QUIET POSSESSION OR
* CORRESPONDENCE TO DESCRIPTION. THE ENTIRE RISK ARISING OUT OF USE OR
* PERFORMANCE OF THE SOFTWARE LIES WITH YOU.
***********************license end**************************************/
/**
* @file
*
* "cvmx-usb.h" defines a set of low level USB functions to help
* developers create Octeon USB drivers for various operating
* systems. These functions provide a generic API to the Octeon
* USB blocks, hiding the internal hardware specific
* operations.
*
* At a high level the device driver needs to:
*
* -# Call cvmx_usb_get_num_ports() to get the number of
* supported ports.
* -# Call cvmx_usb_initialize() for each Octeon USB port.
* -# Enable the port using cvmx_usb_enable().
* -# Either periodically, or in an interrupt handler, call
* cvmx_usb_poll() to service USB events.
* -# Manage pipes using cvmx_usb_open_pipe() and
* cvmx_usb_close_pipe().
* -# Manage transfers using cvmx_usb_submit_*() and
* cvmx_usb_cancel*().
* -# Shutdown USB on unload using cvmx_usb_shutdown().
*
* To monitor USB status changes, the device driver must use
* cvmx_usb_register_callback() to register for events that it
* is interested in. Below are a few hints on successfully
* implementing a driver on top of this API.
*
* <h2>Initialization</h2>
*
* When a driver is first loaded, it is normally not necessary
* to bring up the USB port completely. Most operating systems
* expect to initialize and enable the port in two independent
* steps. Normally an operating system will probe hardware,
* initialize anything found, and then enable the hardware.
*
* In the probe phase you should:
* -# Use cvmx_usb_get_num_ports() to determine the number of
* USB port to be supported.
* -# Allocate space for a cvmx_usb_state_t structure for each
* port.
* -# Tell the operating system about each port
*
* In the initialization phase you should:
* -# Use cvmx_usb_initialize() on each port.
* -# Do not call cvmx_usb_enable(). This leaves the USB port in
* the disabled state until the operating system is ready.
*
* Finally, in the enable phase you should:
* -# Call cvmx_usb_enable() on the appropriate port.
* -# Note that some operating system use a RESET instead of an
* enable call. To implement RESET, you should call
* cvmx_usb_disable() followed by cvmx_usb_enable().
*
* <h2>Locking</h2>
*
* All of the functions in the cvmx-usb API assume exclusive
* access to the USB hardware and internal data structures. This
* means that the driver must provide locking as necessary.
*
* In the single CPU state it is normally enough to disable
* interrupts before every call to cvmx_usb*() and enable them
* again after the call is complete. Keep in mind that it is
* very common for the callback handlers to make additional
* calls into cvmx-usb, so the disable/enable must be protected
* against recursion. As an example, the Linux kernel
* local_irq_save() and local_irq_restore() are perfect for this
* in the non SMP case.
*
* In the SMP case, locking is more complicated. For SMP you not
* only need to disable interrupts on the local core, but also
* take a lock to make sure that another core cannot call
* cvmx-usb.
*
* <h2>Port callback</h2>
*
* The port callback prototype needs to look as follows:
*
* void port_callback(cvmx_usb_state_t *usb,
* cvmx_usb_callback_t reason,
* cvmx_usb_complete_t status,
* int pipe_handle,
* int submit_handle,
* int bytes_transferred,
* void *user_data);
* - @b usb is the cvmx_usb_state_t for the port.
* - @b reason will always be
* CVMX_USB_CALLBACK_PORT_CHANGED.
* - @b status will always be CVMX_USB_COMPLETE_SUCCESS.
* - @b pipe_handle will always be -1.
* - @b submit_handle will always be -1.
* - @b bytes_transferred will always be 0.
* - @b user_data is the void pointer originally passed along
* with the callback. Use this for any state information you
* need.
*
* The port callback will be called whenever the user plugs /
* unplugs a device from the port. It will not be called when a
* device is plugged / unplugged from a hub connected to the
* root port. Normally all the callback needs to do is tell the
* operating system to poll the root hub for status. Under
* Linux, this is performed by calling usb_hcd_poll_rh_status().
* In the Linux driver we use @b user_data. to pass around the
* Linux "hcd" structure. Once the port callback completes,
* Linux automatically calls octeon_usb_hub_status_data() which
* uses cvmx_usb_get_status() to determine the root port status.
*
* <h2>Complete callback</h2>
*
* The completion callback prototype needs to look as follows:
*
* void complete_callback(cvmx_usb_state_t *usb,
* cvmx_usb_callback_t reason,
* cvmx_usb_complete_t status,
* int pipe_handle,
* int submit_handle,
* int bytes_transferred,
* void *user_data);
* - @b usb is the cvmx_usb_state_t for the port.
* - @b reason will always be
* CVMX_USB_CALLBACK_TRANSFER_COMPLETE.
* - @b status will be one of the cvmx_usb_complete_t
* enumerations.
* - @b pipe_handle is the handle to the pipe the transaction
* was originally submitted on.
* - @b submit_handle is the handle returned by the original
* cvmx_usb_submit_* call.
* - @b bytes_transferred is the number of bytes successfully
* transferred in the transaction. This will be zero on most
* error conditions.
* - @b user_data is the void pointer originally passed along
* with the callback. Use this for any state information you
* need. For example, the Linux "urb" is stored in here in the
* Linux driver.
*
* In general your callback handler should use @b status and @b
* bytes_transferred to tell the operating system the how the
* transaction completed. Normally the pipe is not changed in
* this callback.
*
* <h2>Canceling transactions</h2>
*
* When a transaction is cancelled using cvmx_usb_cancel*(), the
* actual length of time until the complete callback is called
* can vary greatly. It may be called before cvmx_usb_cancel*()
* returns, or it may be called a number of usb frames in the
* future once the hardware frees the transaction. In either of
* these cases, the complete handler will receive
* CVMX_USB_COMPLETE_CANCEL.
*
* <h2>Handling pipes</h2>
*
* USB "pipes" is a software construct created by this API to
* enable the ordering of usb transactions to a device endpoint.
* Octeon's underlying hardware doesn't have any concept
* equivalent to "pipes". The hardware instead has eight
* channels that can be used simultaneously to have up to eight
* transaction in process at the same time. In order to maintain
* ordering in a pipe, the transactions for a pipe will only be
* active in one hardware channel at a time. From an API user's
* perspective, this doesn't matter but it can be helpful to
* keep this in mind when you are probing hardware while
* debugging.
*
* Also keep in mind that usb transactions contain state
* information about the previous transaction to the same
* endpoint. Each transaction has a PID toggle that changes 0/1
* between each sub packet. This is maintained in the pipe data
* structures. For this reason, you generally cannot create and
* destroy a pipe for every transaction. A sequence of
* transaction to the same endpoint must use the same pipe.
*
* <h2>Root Hub</h2>
*
* Some operating systems view the usb root port as a normal usb
* hub. These systems attempt to control the root hub with
* messages similar to the usb 2.0 spec for hub control and
* status. For these systems it may be necessary to write
* function to decode standard usb control messages into
* equivalent cvmx-usb API calls. As an example, the following
* code is used under Linux for some of the basic hub control
* messages.
*
* @code
* static int octeon_usb_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, u16 wIndex, char *buf, u16 wLength)
* {
* cvmx_usb_state_t *usb = (cvmx_usb_state_t *)hcd->hcd_priv;
* cvmx_usb_port_status_t usb_port_status;
* int port_status;
* struct usb_hub_descriptor *desc;
* unsigned long flags;
*
* switch (typeReq)
* {
* case ClearHubFeature:
* DEBUG_ROOT_HUB("OcteonUSB: ClearHubFeature\n");
* switch (wValue)
* {
* case C_HUB_LOCAL_POWER:
* case C_HUB_OVER_CURRENT:
* // Nothing required here
* break;
* default:
* return -EINVAL;
* }
* break;
* case ClearPortFeature:
* DEBUG_ROOT_HUB("OcteonUSB: ClearPortFeature");
* if (wIndex != 1)
* {
* DEBUG_ROOT_HUB(" INVALID\n");
* return -EINVAL;
* }
*
* switch (wValue)
* {
* case USB_PORT_FEAT_ENABLE:
* DEBUG_ROOT_HUB(" ENABLE");
* local_irq_save(flags);
* cvmx_usb_disable(usb);
* local_irq_restore(flags);
* break;
* case USB_PORT_FEAT_SUSPEND:
* DEBUG_ROOT_HUB(" SUSPEND");
* // Not supported on Octeon
* break;
* case USB_PORT_FEAT_POWER:
* DEBUG_ROOT_HUB(" POWER");
* // Not supported on Octeon
* break;
* case USB_PORT_FEAT_INDICATOR:
* DEBUG_ROOT_HUB(" INDICATOR");
* // Port inidicator not supported
* break;
* case USB_PORT_FEAT_C_CONNECTION:
* DEBUG_ROOT_HUB(" C_CONNECTION");
* // Clears drivers internal connect status change flag
* cvmx_usb_set_status(usb, cvmx_usb_get_status(usb));
* break;
* case USB_PORT_FEAT_C_RESET:
* DEBUG_ROOT_HUB(" C_RESET");
* // Clears the driver's internal Port Reset Change flag
* cvmx_usb_set_status(usb, cvmx_usb_get_status(usb));
* break;
* case USB_PORT_FEAT_C_ENABLE:
* DEBUG_ROOT_HUB(" C_ENABLE");
* // Clears the driver's internal Port Enable/Disable Change flag
* cvmx_usb_set_status(usb, cvmx_usb_get_status(usb));
* break;
* case USB_PORT_FEAT_C_SUSPEND:
* DEBUG_ROOT_HUB(" C_SUSPEND");
* // Clears the driver's internal Port Suspend Change flag,
* which is set when resume signaling on the host port is
* complete
* break;
* case USB_PORT_FEAT_C_OVER_CURRENT:
* DEBUG_ROOT_HUB(" C_OVER_CURRENT");
* // Clears the driver's overcurrent Change flag
* cvmx_usb_set_status(usb, cvmx_usb_get_status(usb));
* break;
* default:
* DEBUG_ROOT_HUB(" UNKNOWN\n");
* return -EINVAL;
* }
* DEBUG_ROOT_HUB("\n");
* break;
* case GetHubDescriptor:
* DEBUG_ROOT_HUB("OcteonUSB: GetHubDescriptor\n");
* desc = (struct usb_hub_descriptor *)buf;
* desc->bDescLength = 9;
* desc->bDescriptorType = 0x29;
* desc->bNbrPorts = 1;
* desc->wHubCharacteristics = 0x08;
* desc->bPwrOn2PwrGood = 1;
* desc->bHubContrCurrent = 0;
* desc->bitmap[0] = 0;
* desc->bitmap[1] = 0xff;
* break;
* case GetHubStatus:
* DEBUG_ROOT_HUB("OcteonUSB: GetHubStatus\n");
* *(__le32 *)buf = 0;
* break;
* case GetPortStatus:
* DEBUG_ROOT_HUB("OcteonUSB: GetPortStatus");
* if (wIndex != 1)
* {
* DEBUG_ROOT_HUB(" INVALID\n");
* return -EINVAL;
* }
*
* usb_port_status = cvmx_usb_get_status(usb);
* port_status = 0;
*
* if (usb_port_status.connect_change)
* {
* port_status |= (1 << USB_PORT_FEAT_C_CONNECTION);
* DEBUG_ROOT_HUB(" C_CONNECTION");
* }
*
* if (usb_port_status.port_enabled)
* {
* port_status |= (1 << USB_PORT_FEAT_C_ENABLE);
* DEBUG_ROOT_HUB(" C_ENABLE");
* }
*
* if (usb_port_status.connected)
* {
* port_status |= (1 << USB_PORT_FEAT_CONNECTION);
* DEBUG_ROOT_HUB(" CONNECTION");
* }
*
* if (usb_port_status.port_enabled)
* {
* port_status |= (1 << USB_PORT_FEAT_ENABLE);
* DEBUG_ROOT_HUB(" ENABLE");
* }
*
* if (usb_port_status.port_over_current)
* {
* port_status |= (1 << USB_PORT_FEAT_OVER_CURRENT);
* DEBUG_ROOT_HUB(" OVER_CURRENT");
* }
*
* if (usb_port_status.port_powered)
* {
* port_status |= (1 << USB_PORT_FEAT_POWER);
* DEBUG_ROOT_HUB(" POWER");
* }
*
* if (usb_port_status.port_speed == CVMX_USB_SPEED_HIGH)
* {
* port_status |= (1 << USB_PORT_FEAT_HIGHSPEED);
* DEBUG_ROOT_HUB(" HIGHSPEED");
* }
* else if (usb_port_status.port_speed == CVMX_USB_SPEED_LOW)
* {
* port_status |= (1 << USB_PORT_FEAT_LOWSPEED);
* DEBUG_ROOT_HUB(" LOWSPEED");
* }
*
* *((__le32 *)buf) = cpu_to_le32(port_status);
* DEBUG_ROOT_HUB("\n");
* break;
* case SetHubFeature:
* DEBUG_ROOT_HUB("OcteonUSB: SetHubFeature\n");
* // No HUB features supported
* break;
* case SetPortFeature:
* DEBUG_ROOT_HUB("OcteonUSB: SetPortFeature");
* if (wIndex != 1)
* {
* DEBUG_ROOT_HUB(" INVALID\n");
* return -EINVAL;
* }
*
* switch (wValue)
* {
* case USB_PORT_FEAT_SUSPEND:
* DEBUG_ROOT_HUB(" SUSPEND\n");
* return -EINVAL;
* case USB_PORT_FEAT_POWER:
* DEBUG_ROOT_HUB(" POWER\n");
* return -EINVAL;
* case USB_PORT_FEAT_RESET:
* DEBUG_ROOT_HUB(" RESET\n");
* local_irq_save(flags);
* cvmx_usb_disable(usb);
* if (cvmx_usb_enable(usb))
* DEBUG_ERROR("Failed to enable the port\n");
* local_irq_restore(flags);
* return 0;
* case USB_PORT_FEAT_INDICATOR:
* DEBUG_ROOT_HUB(" INDICATOR\n");
* // Not supported
* break;
* default:
* DEBUG_ROOT_HUB(" UNKNOWN\n");
* return -EINVAL;
* }
* break;
* default:
* DEBUG_ROOT_HUB("OcteonUSB: Unknown root hub request\n");
* return -EINVAL;
* }
* return 0;
* }
* @endcode
*
* <h2>Interrupts</h2>
*
* If you plan on using usb interrupts, cvmx_usb_poll() must be
* called on every usb interrupt. It will read the usb state,
* call any needed callbacks, and schedule transactions as
* needed. Your device driver needs only to hookup an interrupt
* handler and call cvmx_usb_poll(). Octeon's usb port 0 causes
* CIU bit CIU_INT*_SUM0[USB] to be set (bit 56). For port 1,
* CIU bit CIU_INT_SUM1[USB1] is set (bit 17). How these bits
* are turned into interrupt numbers is operating system
* specific. For Linux, there are the convenient defines
* OCTEON_IRQ_USB0 and OCTEON_IRQ_USB1 for the IRQ numbers.
*
* If you aren't using interrupts, simple call cvmx_usb_poll()
* in your main processing loop.
*
* <hr>$Revision: 32636 $<hr>
*/
#ifndef __CVMX_USB_H__
#define __CVMX_USB_H__
#ifdef __cplusplus
extern "C" {
#endif
/**
* Enumerations representing the status of function calls.
*/
typedef enum
{
CVMX_USB_SUCCESS = 0, /**< There were no errors */
CVMX_USB_INVALID_PARAM = -1, /**< A parameter to the function was invalid */
CVMX_USB_NO_MEMORY = -2, /**< Insufficient resources were available for the request */
CVMX_USB_BUSY = -3, /**< The resource is busy and cannot service the request */
CVMX_USB_TIMEOUT = -4, /**< Waiting for an action timed out */
CVMX_USB_INCORRECT_MODE = -5, /**< The function call doesn't work in the current USB
mode. This happens when host only functions are
called in device mode or vice versa */
} cvmx_usb_status_t;
/**
* Enumerations representing the possible USB device speeds
*/
typedef enum
{
CVMX_USB_SPEED_HIGH = 0, /**< Device is operation at 480Mbps */
CVMX_USB_SPEED_FULL = 1, /**< Device is operation at 12Mbps */
CVMX_USB_SPEED_LOW = 2, /**< Device is operation at 1.5Mbps */
} cvmx_usb_speed_t;
/**
* Enumeration representing the possible USB transfer types.
*/
typedef enum
{
CVMX_USB_TRANSFER_CONTROL = 0, /**< USB transfer type control for hub and status transfers */
CVMX_USB_TRANSFER_ISOCHRONOUS = 1, /**< USB transfer type isochronous for low priority periodic transfers */
CVMX_USB_TRANSFER_BULK = 2, /**< USB transfer type bulk for large low priority transfers */
CVMX_USB_TRANSFER_INTERRUPT = 3, /**< USB transfer type interrupt for high priority periodic transfers */
} cvmx_usb_transfer_t;
/**
* Enumeration of the transfer directions
*/
typedef enum
{
CVMX_USB_DIRECTION_OUT, /**< Data is transferring from Octeon to the device/host */
CVMX_USB_DIRECTION_IN, /**< Data is transferring from the device/host to Octeon */
} cvmx_usb_direction_t;
/**
* Enumeration of all possible status codes passed to callback
* functions.
*/
typedef enum
{
CVMX_USB_COMPLETE_SUCCESS, /**< The transaction / operation finished without any errors */
CVMX_USB_COMPLETE_SHORT, /**< FIXME: This is currently not implemented */
CVMX_USB_COMPLETE_CANCEL, /**< The transaction was canceled while in flight by a user call to cvmx_usb_cancel* */
CVMX_USB_COMPLETE_ERROR, /**< The transaction aborted with an unexpected error status */
CVMX_USB_COMPLETE_STALL, /**< The transaction received a USB STALL response from the device */
CVMX_USB_COMPLETE_XACTERR, /**< The transaction failed with an error from the device even after a number of retries */
CVMX_USB_COMPLETE_DATATGLERR, /**< The transaction failed with a data toggle error even after a number of retries */
CVMX_USB_COMPLETE_BABBLEERR, /**< The transaction failed with a babble error */
CVMX_USB_COMPLETE_FRAMEERR, /**< The transaction failed with a frame error even after a number of retries */
} cvmx_usb_complete_t;
/**
* Structure returned containing the USB port status information.
*/
typedef struct
{
uint32_t reserved : 25;
uint32_t port_enabled : 1; /**< 1 = Usb port is enabled, 0 = disabled */
uint32_t port_over_current : 1; /**< 1 = Over current detected, 0 = Over current not detected. Octeon doesn't support over current detection */
uint32_t port_powered : 1; /**< 1 = Port power is being supplied to the device, 0 = power is off. Octeon doesn't support turning port power off */
cvmx_usb_speed_t port_speed : 2; /**< Current port speed */
uint32_t connected : 1; /**< 1 = A device is connected to the port, 0 = No device is connected */
uint32_t connect_change : 1; /**< 1 = Device connected state changed since the last set status call */
} cvmx_usb_port_status_t;
/**
* This is the structure of a Control packet header
*/
typedef union
{
uint64_t u64;
struct
{
uint64_t request_type : 8; /**< Bit 7 tells the direction: 1=IN, 0=OUT */
uint64_t request : 8; /**< The standard usb request to make */
uint64_t value : 16; /**< Value parameter for the request in little endian format */
uint64_t index : 16; /**< Index for the request in little endian format */
uint64_t length : 16; /**< Length of the data associated with this request in little endian format */
} s;
} cvmx_usb_control_header_t;
/**
* Descriptor for Isochronous packets
*/
typedef struct
{
int offset; /**< This is the offset in bytes into the main buffer where this data is stored */
int length; /**< This is the length in bytes of the data */
cvmx_usb_complete_t status; /**< This is the status of this individual packet transfer */
} cvmx_usb_iso_packet_t;
/**
* Possible callback reasons for the USB API.
*/
typedef enum
{
CVMX_USB_CALLBACK_TRANSFER_COMPLETE,
/**< A callback of this type is called when a submitted transfer
completes. The completion callback will be called even if the
transfer fails or is canceled. The status parameter will
contain details of why he callback was called. */
CVMX_USB_CALLBACK_PORT_CHANGED, /**< The status of the port changed. For example, someone may have
plugged a device in. The status parameter contains
CVMX_USB_COMPLETE_SUCCESS. Use cvmx_usb_get_status() to get
the new port status. */
__CVMX_USB_CALLBACK_END /**< Do not use. Used internally for array bounds */
} cvmx_usb_callback_t;
/**
* USB state internal data. The contents of this structure
* may change in future SDKs. No data in it should be referenced
* by user's of this API.
*/
typedef struct
{
char data[65536];
} cvmx_usb_state_t;
/**
* USB callback functions are always of the following type.
* The parameters are as follows:
* - state = USB device state populated by
* cvmx_usb_initialize().
* - reason = The cvmx_usb_callback_t used to register
* the callback.
* - status = The cvmx_usb_complete_t representing the
* status code of a transaction.
* - pipe_handle = The Pipe that caused this callback, or
* -1 if this callback wasn't associated with a pipe.
* - submit_handle = Transfer submit handle causing this
* callback, or -1 if this callback wasn't associated
* with a transfer.
* - Actual number of bytes transfer.
* - user_data = The user pointer supplied to the
* function cvmx_usb_submit() or
* cvmx_usb_register_callback() */
typedef void (*cvmx_usb_callback_func_t)(cvmx_usb_state_t *state,
cvmx_usb_callback_t reason,
cvmx_usb_complete_t status,
int pipe_handle, int submit_handle,
int bytes_transferred, void *user_data);
/**
* Flags to pass the initialization function.
*/
typedef enum
{
CVMX_USB_INITIALIZE_FLAGS_CLOCK_XO_XI = 1<<0, /**< The USB port uses a 12MHz crystal as clock source
at USB_XO and USB_XI. */
CVMX_USB_INITIALIZE_FLAGS_CLOCK_XO_GND = 1<<1, /**< The USB port uses 12/24/48MHz 2.5V board clock
source at USB_XO. USB_XI should be tied to GND.*/
CVMX_USB_INITIALIZE_FLAGS_CLOCK_AUTO = 0, /**< Automatically determine clock type based on function
in cvmx-helper-board.c. */
CVMX_USB_INITIALIZE_FLAGS_CLOCK_MHZ_MASK = 3<<3, /**< Mask for clock speed field */
CVMX_USB_INITIALIZE_FLAGS_CLOCK_12MHZ = 1<<3, /**< Speed of reference clock or crystal */
CVMX_USB_INITIALIZE_FLAGS_CLOCK_24MHZ = 2<<3, /**< Speed of reference clock */
CVMX_USB_INITIALIZE_FLAGS_CLOCK_48MHZ = 3<<3, /**< Speed of reference clock */
/* Bits 3-4 used to encode the clock frequency */
CVMX_USB_INITIALIZE_FLAGS_NO_DMA = 1<<5, /**< Disable DMA and used polled IO for data transfer use for the USB */
CVMX_USB_INITIALIZE_FLAGS_DEBUG_TRANSFERS = 1<<16, /**< Enable extra console output for debugging USB transfers */
CVMX_USB_INITIALIZE_FLAGS_DEBUG_CALLBACKS = 1<<17, /**< Enable extra console output for debugging USB callbacks */
CVMX_USB_INITIALIZE_FLAGS_DEBUG_INFO = 1<<18, /**< Enable extra console output for USB informational data */
CVMX_USB_INITIALIZE_FLAGS_DEBUG_CALLS = 1<<19, /**< Enable extra console output for every function call */
CVMX_USB_INITIALIZE_FLAGS_DEBUG_CSRS = 1<<20, /**< Enable extra console output for every CSR access */
CVMX_USB_INITIALIZE_FLAGS_DEBUG_ALL = ((CVMX_USB_INITIALIZE_FLAGS_DEBUG_CSRS<<1)-1) - (CVMX_USB_INITIALIZE_FLAGS_DEBUG_TRANSFERS-1),
} cvmx_usb_initialize_flags_t;
/**
* Flags for passing when a pipe is created. Currently no flags
* need to be passed.
*/
typedef enum
{
CVMX_USB_PIPE_FLAGS_DEBUG_TRANSFERS = 1<<15,/**< Used to display CVMX_USB_INITIALIZE_FLAGS_DEBUG_TRANSFERS for a specific pipe only */
__CVMX_USB_PIPE_FLAGS_OPEN = 1<<16, /**< Used internally to determine if a pipe is open. Do not use */
__CVMX_USB_PIPE_FLAGS_SCHEDULED = 1<<17, /**< Used internally to determine if a pipe is actively using hardware. Do not use */
__CVMX_USB_PIPE_FLAGS_NEED_PING = 1<<18, /**< Used internally to determine if a high speed pipe is in the ping state. Do not use */
} cvmx_usb_pipe_flags_t;
/**
* Return the number of USB ports supported by this Octeon
* chip. If the chip doesn't support USB, or is not supported
* by this API, a zero will be returned. Most Octeon chips
* support one usb port, but some support two ports.
* cvmx_usb_initialize() must be called on independent
* cvmx_usb_state_t structures.
*
* @return Number of port, zero if usb isn't supported
*/
extern int cvmx_usb_get_num_ports(void);
/**
* Initialize a USB port for use. This must be called before any
* other access to the Octeon USB port is made. The port starts
* off in the disabled state.
*
* @param state Pointer to an empty cvmx_usb_state_t structure
* that will be populated by the initialize call.
* This structure is then passed to all other USB
* functions.
* @param usb_port_number
* Which Octeon USB port to initialize.
* @param flags Flags to control hardware initialization. See
* cvmx_usb_initialize_flags_t for the flag
* definitions. Some flags are mandatory.
*
* @return CVMX_USB_SUCCESS or a negative error code defined in
* cvmx_usb_status_t.
*/
extern cvmx_usb_status_t cvmx_usb_initialize(cvmx_usb_state_t *state,
int usb_port_number,
cvmx_usb_initialize_flags_t flags);
/**
* Shutdown a USB port after a call to cvmx_usb_initialize().
* The port should be disabled with all pipes closed when this
* function is called.
*
* @param state USB device state populated by
* cvmx_usb_initialize().
*
* @return CVMX_USB_SUCCESS or a negative error code defined in
* cvmx_usb_status_t.
*/
extern cvmx_usb_status_t cvmx_usb_shutdown(cvmx_usb_state_t *state);
/**
* Enable a USB port. After this call succeeds, the USB port is
* online and servicing requests.
*
* @param state USB device state populated by
* cvmx_usb_initialize().
*
* @return CVMX_USB_SUCCESS or a negative error code defined in
* cvmx_usb_status_t.
*/
extern cvmx_usb_status_t cvmx_usb_enable(cvmx_usb_state_t *state);
/**
* Disable a USB port. After this call the USB port will not
* generate data transfers and will not generate events.
* Transactions in process will fail and call their
* associated callbacks.
*
* @param state USB device state populated by
* cvmx_usb_initialize().
*
* @return CVMX_USB_SUCCESS or a negative error code defined in
* cvmx_usb_status_t.
*/
extern cvmx_usb_status_t cvmx_usb_disable(cvmx_usb_state_t *state);
/**
* Get the current state of the USB port. Use this call to
* determine if the usb port has anything connected, is enabled,
* or has some sort of error condition. The return value of this
* call has "changed" bits to signal of the value of some fields
* have changed between calls. These "changed" fields are based
* on the last call to cvmx_usb_set_status(). In order to clear
* them, you must update the status through cvmx_usb_set_status().
*
* @param state USB device state populated by
* cvmx_usb_initialize().
*
* @return Port status information
*/
extern cvmx_usb_port_status_t cvmx_usb_get_status(cvmx_usb_state_t *state);
/**
* Set the current state of the USB port. The status is used as
* a reference for the "changed" bits returned by
* cvmx_usb_get_status(). Other than serving as a reference, the
* status passed to this function is not used. No fields can be
* changed through this call.
*
* @param state USB device state populated by
* cvmx_usb_initialize().
* @param port_status
* Port status to set, most like returned by cvmx_usb_get_status()
*/
extern void cvmx_usb_set_status(cvmx_usb_state_t *state, cvmx_usb_port_status_t port_status);
/**
* Open a virtual pipe between the host and a USB device. A pipe
* must be opened before data can be transferred between a device
* and Octeon.
*
* @param state USB device state populated by
* cvmx_usb_initialize().
* @param flags Optional pipe flags defined in
* cvmx_usb_pipe_flags_t.
* @param device_addr
* USB device address to open the pipe to
* (0-127).
* @param endpoint_num
* USB endpoint number to open the pipe to
* (0-15).
* @param device_speed
* The speed of the device the pipe is going
* to. This must match the device's speed,
* which may be different than the port speed.
* @param max_packet The maximum packet length the device can
* transmit/receive (low speed=0-8, full
* speed=0-1023, high speed=0-1024). This value
* comes from the standard endpoint descriptor
* field wMaxPacketSize bits <10:0>.
* @param transfer_type
* The type of transfer this pipe is for.
* @param transfer_dir
* The direction the pipe is in. This is not
* used for control pipes.
* @param interval For ISOCHRONOUS and INTERRUPT transfers,
* this is how often the transfer is scheduled
* for. All other transfers should specify
* zero. The units are in frames (8000/sec at
* high speed, 1000/sec for full speed).
* @param multi_count
* For high speed devices, this is the maximum
* allowed number of packet per microframe.
* Specify zero for non high speed devices. This
* value comes from the standard endpoint descriptor
* field wMaxPacketSize bits <12:11>.
* @param hub_device_addr
* Hub device address this device is connected
* to. Devices connected directly to Octeon
* use zero. This is only used when the device
* is full/low speed behind a high speed hub.
* The address will be of the high speed hub,
* not and full speed hubs after it.
* @param hub_port Which port on the hub the device is
* connected. Use zero for devices connected
* directly to Octeon. Like hub_device_addr,
* this is only used for full/low speed
* devices behind a high speed hub.
*
* @return A non negative value is a pipe handle. Negative
* values are failure codes from cvmx_usb_status_t.
*/
extern int cvmx_usb_open_pipe(cvmx_usb_state_t *state,
cvmx_usb_pipe_flags_t flags,
int device_addr, int endpoint_num,
cvmx_usb_speed_t device_speed, int max_packet,
cvmx_usb_transfer_t transfer_type,
cvmx_usb_direction_t transfer_dir, int interval,
int multi_count, int hub_device_addr,
int hub_port);
/**
* Call to submit a USB Bulk transfer to a pipe.
*
* @param state USB device state populated by
* cvmx_usb_initialize().
* @param pipe_handle
* Handle to the pipe for the transfer.
* @param buffer Physical address of the data buffer in
* memory. Note that this is NOT A POINTER, but
* the full 64bit physical address of the
* buffer. This may be zero if buffer_length is
* zero.
* @param buffer_length
* Length of buffer in bytes.
* @param callback Function to call when this transaction
* completes. If the return value of this
* function isn't an error, then this function
* is guaranteed to be called when the
* transaction completes. If this parameter is
* NULL, then the generic callback registered
* through cvmx_usb_register_callback is
* called. If both are NULL, then there is no
* way to know when a transaction completes.
* @param user_data User supplied data returned when the
* callback is called. This is only used if
* callback in not NULL.
*
* @return A submitted transaction handle or negative on
* failure. Negative values are failure codes from
* cvmx_usb_status_t.
*/
extern int cvmx_usb_submit_bulk(cvmx_usb_state_t *state, int pipe_handle,
uint64_t buffer, int buffer_length,
cvmx_usb_callback_func_t callback,
void *user_data);
/**
* Call to submit a USB Interrupt transfer to a pipe.
*
* @param state USB device state populated by
* cvmx_usb_initialize().
* @param pipe_handle
* Handle to the pipe for the transfer.
* @param buffer Physical address of the data buffer in
* memory. Note that this is NOT A POINTER, but
* the full 64bit physical address of the
* buffer. This may be zero if buffer_length is
* zero.
* @param buffer_length
* Length of buffer in bytes.
* @param callback Function to call when this transaction
* completes. If the return value of this
* function isn't an error, then this function
* is guaranteed to be called when the
* transaction completes. If this parameter is
* NULL, then the generic callback registered
* through cvmx_usb_register_callback is
* called. If both are NULL, then there is no
* way to know when a transaction completes.
* @param user_data User supplied data returned when the
* callback is called. This is only used if
* callback in not NULL.
*
* @return A submitted transaction handle or negative on
* failure. Negative values are failure codes from
* cvmx_usb_status_t.
*/
extern int cvmx_usb_submit_interrupt(cvmx_usb_state_t *state, int pipe_handle,
uint64_t buffer, int buffer_length,
cvmx_usb_callback_func_t callback,
void *user_data);
/**
* Call to submit a USB Control transfer to a pipe.
*
* @param state USB device state populated by
* cvmx_usb_initialize().
* @param pipe_handle
* Handle to the pipe for the transfer.
* @param control_header
* USB 8 byte control header physical address.
* Note that this is NOT A POINTER, but the
* full 64bit physical address of the buffer.
* @param buffer Physical address of the data buffer in
* memory. Note that this is NOT A POINTER, but
* the full 64bit physical address of the
* buffer. This may be zero if buffer_length is
* zero.
* @param buffer_length
* Length of buffer in bytes.
* @param callback Function to call when this transaction
* completes. If the return value of this
* function isn't an error, then this function
* is guaranteed to be called when the
* transaction completes. If this parameter is
* NULL, then the generic callback registered
* through cvmx_usb_register_callback is
* called. If both are NULL, then there is no
* way to know when a transaction completes.
* @param user_data User supplied data returned when the
* callback is called. This is only used if
* callback in not NULL.
*
* @return A submitted transaction handle or negative on
* failure. Negative values are failure codes from
* cvmx_usb_status_t.
*/
extern int cvmx_usb_submit_control(cvmx_usb_state_t *state, int pipe_handle,
uint64_t control_header,
uint64_t buffer, int buffer_length,
cvmx_usb_callback_func_t callback,
void *user_data);
/**
* Flags to pass the cvmx_usb_submit_isochronous() function.
*/
typedef enum
{
CVMX_USB_ISOCHRONOUS_FLAGS_ALLOW_SHORT = 1<<0, /**< Do not return an error if a transfer is less than the maximum packet size of the device */
CVMX_USB_ISOCHRONOUS_FLAGS_ASAP = 1<<1, /**< Schedule the transaction as soon as possible */
} cvmx_usb_isochronous_flags_t;
/**
* Call to submit a USB Isochronous transfer to a pipe.
*
* @param state USB device state populated by
* cvmx_usb_initialize().
* @param pipe_handle
* Handle to the pipe for the transfer.
* @param start_frame
* Number of frames into the future to schedule
* this transaction.
* @param flags Flags to control the transfer. See
* cvmx_usb_isochronous_flags_t for the flag
* definitions.
* @param number_packets
* Number of sequential packets to transfer.
* "packets" is a pointer to an array of this
* many packet structures.
* @param packets Description of each transfer packet as
* defined by cvmx_usb_iso_packet_t. The array
* pointed to here must stay valid until the
* complete callback is called.
* @param buffer Physical address of the data buffer in
* memory. Note that this is NOT A POINTER, but
* the full 64bit physical address of the
* buffer. This may be zero if buffer_length is
* zero.
* @param buffer_length
* Length of buffer in bytes.
* @param callback Function to call when this transaction
* completes. If the return value of this
* function isn't an error, then this function
* is guaranteed to be called when the
* transaction completes. If this parameter is
* NULL, then the generic callback registered
* through cvmx_usb_register_callback is
* called. If both are NULL, then there is no
* way to know when a transaction completes.
* @param user_data User supplied data returned when the
* callback is called. This is only used if
* callback in not NULL.
*
* @return A submitted transaction handle or negative on
* failure. Negative values are failure codes from
* cvmx_usb_status_t.
*/
extern int cvmx_usb_submit_isochronous(cvmx_usb_state_t *state, int pipe_handle,
int start_frame, int flags,
int number_packets,
cvmx_usb_iso_packet_t packets[],
uint64_t buffer, int buffer_length,
cvmx_usb_callback_func_t callback,
void *user_data);
/**
* Cancel one outstanding request in a pipe. Canceling a request
* can fail if the transaction has already completed before cancel
* is called. Even after a successful cancel call, it may take
* a frame or two for the cvmx_usb_poll() function to call the
* associated callback.
*
* @param state USB device state populated by
* cvmx_usb_initialize().
* @param pipe_handle
* Pipe handle to cancel requests in.
* @param submit_handle
* Handle to transaction to cancel, returned by the submit function.
*
* @return CVMX_USB_SUCCESS or a negative error code defined in
* cvmx_usb_status_t.
*/
extern cvmx_usb_status_t cvmx_usb_cancel(cvmx_usb_state_t *state,
int pipe_handle, int submit_handle);
/**
* Cancel all outstanding requests in a pipe. Logically all this
* does is call cvmx_usb_cancel() in a loop.
*
* @param state USB device state populated by
* cvmx_usb_initialize().
* @param pipe_handle
* Pipe handle to cancel requests in.
*
* @return CVMX_USB_SUCCESS or a negative error code defined in
* cvmx_usb_status_t.
*/
extern cvmx_usb_status_t cvmx_usb_cancel_all(cvmx_usb_state_t *state,
int pipe_handle);
/**
* Close a pipe created with cvmx_usb_open_pipe().
*
* @param state USB device state populated by
* cvmx_usb_initialize().
* @param pipe_handle
* Pipe handle to close.
*
* @return CVMX_USB_SUCCESS or a negative error code defined in
* cvmx_usb_status_t. CVMX_USB_BUSY is returned if the
* pipe has outstanding transfers.
*/
extern cvmx_usb_status_t cvmx_usb_close_pipe(cvmx_usb_state_t *state,
int pipe_handle);
/**
* Register a function to be called when various USB events occur.
*
* @param state USB device state populated by
* cvmx_usb_initialize().
* @param reason Which event to register for.
* @param callback Function to call when the event occurs.
* @param user_data User data parameter to the function.
*
* @return CVMX_USB_SUCCESS or a negative error code defined in
* cvmx_usb_status_t.
*/
extern cvmx_usb_status_t cvmx_usb_register_callback(cvmx_usb_state_t *state,
cvmx_usb_callback_t reason,
cvmx_usb_callback_func_t callback,
void *user_data);
/**
* Get the current USB protocol level frame number. The frame
* number is always in the range of 0-0x7ff.
*
* @param state USB device state populated by
* cvmx_usb_initialize().
*
* @return USB frame number
*/
extern int cvmx_usb_get_frame_number(cvmx_usb_state_t *state);
/**
* Poll the USB block for status and call all needed callback
* handlers. This function is meant to be called in the interrupt
* handler for the USB controller. It can also be called
* periodically in a loop for non-interrupt based operation.
*
* @param state USB device state populated by
* cvmx_usb_initialize().
*
* @return CVMX_USB_SUCCESS or a negative error code defined in
* cvmx_usb_status_t.
*/
extern cvmx_usb_status_t cvmx_usb_poll(cvmx_usb_state_t *state);
#ifdef __cplusplus
}
#endif
#endif /* __CVMX_USB_H__ */
因为 它太大了无法显示 source diff 。你可以改为 查看blob
/***********************license start***************
* Copyright (c) 2003-2010 Cavium Networks (support@cavium.com). All rights
* reserved.
*
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided
* with the distribution.
* * Neither the name of Cavium Networks nor the names of
* its contributors may be used to endorse or promote products
* derived from this software without specific prior written
* permission.
* This Software, including technical data, may be subject to U.S. export control
* laws, including the U.S. Export Administration Act and its associated
* regulations, and may be subject to export or import regulations in other
* countries.
* TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SOFTWARE IS PROVIDED "AS IS"
* AND WITH ALL FAULTS AND CAVIUM NETWORKS MAKES NO PROMISES, REPRESENTATIONS OR
* WARRANTIES, EITHER EXPRESS, IMPLIED, STATUTORY, OR OTHERWISE, WITH RESPECT TO
* THE SOFTWARE, INCLUDING ITS CONDITION, ITS CONFORMITY TO ANY REPRESENTATION OR
* DESCRIPTION, OR THE EXISTENCE OF ANY LATENT OR PATENT DEFECTS, AND CAVIUM
* SPECIFICALLY DISCLAIMS ALL IMPLIED (IF ANY) WARRANTIES OF TITLE,
* MERCHANTABILITY, NONINFRINGEMENT, FITNESS FOR A PARTICULAR PURPOSE, LACK OF
* VIRUSES, ACCURACY OR COMPLETENESS, QUIET ENJOYMENT, QUIET POSSESSION OR
* CORRESPONDENCE TO DESCRIPTION. THE ENTIRE RISK ARISING OUT OF USE OR
* PERFORMANCE OF THE SOFTWARE LIES WITH YOU.
***********************license end**************************************/
/**
* cvmx-usbnx-defs.h
*
* Configuration and status register (CSR) type definitions for
* Octeon usbnx.
*
* This file is auto generated. Do not edit.
*
* <hr>$Revision$<hr>
*
*/
#ifndef __CVMX_USBNX_TYPEDEFS_H__
#define __CVMX_USBNX_TYPEDEFS_H__
#define CVMX_USBNX_BIST_STATUS(block_id) (CVMX_ADD_IO_SEG(0x00011800680007F8ull) + ((block_id) & 1) * 0x10000000ull)
#define CVMX_USBNX_CLK_CTL(block_id) (CVMX_ADD_IO_SEG(0x0001180068000010ull) + ((block_id) & 1) * 0x10000000ull)
#define CVMX_USBNX_CTL_STATUS(block_id) (CVMX_ADD_IO_SEG(0x00016F0000000800ull) + ((block_id) & 1) * 0x100000000000ull)
#define CVMX_USBNX_DMA0_INB_CHN0(block_id) (CVMX_ADD_IO_SEG(0x00016F0000000818ull) + ((block_id) & 1) * 0x100000000000ull)
#define CVMX_USBNX_DMA0_INB_CHN1(block_id) (CVMX_ADD_IO_SEG(0x00016F0000000820ull) + ((block_id) & 1) * 0x100000000000ull)
#define CVMX_USBNX_DMA0_INB_CHN2(block_id) (CVMX_ADD_IO_SEG(0x00016F0000000828ull) + ((block_id) & 1) * 0x100000000000ull)
#define CVMX_USBNX_DMA0_INB_CHN3(block_id) (CVMX_ADD_IO_SEG(0x00016F0000000830ull) + ((block_id) & 1) * 0x100000000000ull)
#define CVMX_USBNX_DMA0_INB_CHN4(block_id) (CVMX_ADD_IO_SEG(0x00016F0000000838ull) + ((block_id) & 1) * 0x100000000000ull)
#define CVMX_USBNX_DMA0_INB_CHN5(block_id) (CVMX_ADD_IO_SEG(0x00016F0000000840ull) + ((block_id) & 1) * 0x100000000000ull)
#define CVMX_USBNX_DMA0_INB_CHN6(block_id) (CVMX_ADD_IO_SEG(0x00016F0000000848ull) + ((block_id) & 1) * 0x100000000000ull)
#define CVMX_USBNX_DMA0_INB_CHN7(block_id) (CVMX_ADD_IO_SEG(0x00016F0000000850ull) + ((block_id) & 1) * 0x100000000000ull)
#define CVMX_USBNX_DMA0_OUTB_CHN0(block_id) (CVMX_ADD_IO_SEG(0x00016F0000000858ull) + ((block_id) & 1) * 0x100000000000ull)
#define CVMX_USBNX_DMA0_OUTB_CHN1(block_id) (CVMX_ADD_IO_SEG(0x00016F0000000860ull) + ((block_id) & 1) * 0x100000000000ull)
#define CVMX_USBNX_DMA0_OUTB_CHN2(block_id) (CVMX_ADD_IO_SEG(0x00016F0000000868ull) + ((block_id) & 1) * 0x100000000000ull)
#define CVMX_USBNX_DMA0_OUTB_CHN3(block_id) (CVMX_ADD_IO_SEG(0x00016F0000000870ull) + ((block_id) & 1) * 0x100000000000ull)
#define CVMX_USBNX_DMA0_OUTB_CHN4(block_id) (CVMX_ADD_IO_SEG(0x00016F0000000878ull) + ((block_id) & 1) * 0x100000000000ull)
#define CVMX_USBNX_DMA0_OUTB_CHN5(block_id) (CVMX_ADD_IO_SEG(0x00016F0000000880ull) + ((block_id) & 1) * 0x100000000000ull)
#define CVMX_USBNX_DMA0_OUTB_CHN6(block_id) (CVMX_ADD_IO_SEG(0x00016F0000000888ull) + ((block_id) & 1) * 0x100000000000ull)
#define CVMX_USBNX_DMA0_OUTB_CHN7(block_id) (CVMX_ADD_IO_SEG(0x00016F0000000890ull) + ((block_id) & 1) * 0x100000000000ull)
#define CVMX_USBNX_DMA_TEST(block_id) (CVMX_ADD_IO_SEG(0x00016F0000000808ull) + ((block_id) & 1) * 0x100000000000ull)
#define CVMX_USBNX_INT_ENB(block_id) (CVMX_ADD_IO_SEG(0x0001180068000008ull) + ((block_id) & 1) * 0x10000000ull)
#define CVMX_USBNX_INT_SUM(block_id) (CVMX_ADD_IO_SEG(0x0001180068000000ull) + ((block_id) & 1) * 0x10000000ull)
#define CVMX_USBNX_USBP_CTL_STATUS(block_id) (CVMX_ADD_IO_SEG(0x0001180068000018ull) + ((block_id) & 1) * 0x10000000ull)
/**
* cvmx_usbn#_bist_status
*
* USBN_BIST_STATUS = USBN's Control and Status
*
* Contain general control bits and status information for the USBN.
*/
union cvmx_usbnx_bist_status
{
uint64_t u64;
struct cvmx_usbnx_bist_status_s
{
uint64_t reserved_7_63 : 57;
uint64_t u2nc_bis : 1; /**< Bist status U2N CTL FIFO Memory. */
uint64_t u2nf_bis : 1; /**< Bist status U2N FIFO Memory. */
uint64_t e2hc_bis : 1; /**< Bist status E2H CTL FIFO Memory. */
uint64_t n2uf_bis : 1; /**< Bist status N2U FIFO Memory. */
uint64_t usbc_bis : 1; /**< Bist status USBC FIFO Memory. */
uint64_t nif_bis : 1; /**< Bist status for Inbound Memory. */
uint64_t nof_bis : 1; /**< Bist status for Outbound Memory. */
} s;
struct cvmx_usbnx_bist_status_cn30xx
{
uint64_t reserved_3_63 : 61;
uint64_t usbc_bis : 1; /**< Bist status USBC FIFO Memory. */
uint64_t nif_bis : 1; /**< Bist status for Inbound Memory. */
uint64_t nof_bis : 1; /**< Bist status for Outbound Memory. */
} cn30xx;
struct cvmx_usbnx_bist_status_cn30xx cn31xx;
struct cvmx_usbnx_bist_status_s cn50xx;
struct cvmx_usbnx_bist_status_s cn52xx;
struct cvmx_usbnx_bist_status_s cn52xxp1;
struct cvmx_usbnx_bist_status_s cn56xx;
struct cvmx_usbnx_bist_status_s cn56xxp1;
};
typedef union cvmx_usbnx_bist_status cvmx_usbnx_bist_status_t;
/**
* cvmx_usbn#_clk_ctl
*
* USBN_CLK_CTL = USBN's Clock Control
*
* This register is used to control the frequency of the hclk and the hreset and phy_rst signals.
*/
union cvmx_usbnx_clk_ctl
{
uint64_t u64;
struct cvmx_usbnx_clk_ctl_s
{
uint64_t reserved_20_63 : 44;
uint64_t divide2 : 2; /**< The 'hclk' used by the USB subsystem is derived
from the eclk.
Also see the field DIVIDE. DIVIDE2<1> must currently
be zero because it is not implemented, so the maximum
ratio of eclk/hclk is currently 16.
The actual divide number for hclk is:
(DIVIDE2 + 1) * (DIVIDE + 1) */
uint64_t hclk_rst : 1; /**< When this field is '0' the HCLK-DIVIDER used to
generate the hclk in the USB Subsystem is held
in reset. This bit must be set to '0' before
changing the value os DIVIDE in this register.
The reset to the HCLK_DIVIDERis also asserted
when core reset is asserted. */
uint64_t p_x_on : 1; /**< Force USB-PHY on during suspend.
'1' USB-PHY XO block is powered-down during
suspend.
'0' USB-PHY XO block is powered-up during
suspend.
The value of this field must be set while POR is
active. */
uint64_t reserved_14_15 : 2;
uint64_t p_com_on : 1; /**< '0' Force USB-PHY XO Bias, Bandgap and PLL to
remain powered in Suspend Mode.
'1' The USB-PHY XO Bias, Bandgap and PLL are
powered down in suspend mode.
The value of this field must be set while POR is
active. */
uint64_t p_c_sel : 2; /**< Phy clock speed select.
Selects the reference clock / crystal frequency.
'11': Reserved
'10': 48 MHz (reserved when a crystal is used)
'01': 24 MHz (reserved when a crystal is used)
'00': 12 MHz
The value of this field must be set while POR is
active.
NOTE: if a crystal is used as a reference clock,
this field must be set to 12 MHz. */
uint64_t cdiv_byp : 1; /**< Used to enable the bypass input to the USB_CLK_DIV. */
uint64_t sd_mode : 2; /**< Scaledown mode for the USBC. Control timing events
in the USBC, for normal operation this must be '0'. */
uint64_t s_bist : 1; /**< Starts bist on the hclk memories, during the '0'
to '1' transition. */
uint64_t por : 1; /**< Power On Reset for the PHY.
Resets all the PHYS registers and state machines. */
uint64_t enable : 1; /**< When '1' allows the generation of the hclk. When
'0' the hclk will not be generated. SEE DIVIDE
field of this register. */
uint64_t prst : 1; /**< When this field is '0' the reset associated with
the phy_clk functionality in the USB Subsystem is
help in reset. This bit should not be set to '1'
until the time it takes 6 clocks (hclk or phy_clk,
whichever is slower) has passed. Under normal
operation once this bit is set to '1' it should not
be set to '0'. */
uint64_t hrst : 1; /**< When this field is '0' the reset associated with
the hclk functioanlity in the USB Subsystem is
held in reset.This bit should not be set to '1'
until 12ms after phy_clk is stable. Under normal
operation, once this bit is set to '1' it should
not be set to '0'. */
uint64_t divide : 3; /**< The frequency of 'hclk' used by the USB subsystem
is the eclk frequency divided by the value of
(DIVIDE2 + 1) * (DIVIDE + 1), also see the field
DIVIDE2 of this register.
The hclk frequency should be less than 125Mhz.
After writing a value to this field the SW should
read the field for the value written.
The ENABLE field of this register should not be set
until AFTER this field is set and then read. */
} s;
struct cvmx_usbnx_clk_ctl_cn30xx
{
uint64_t reserved_18_63 : 46;
uint64_t hclk_rst : 1; /**< When this field is '0' the HCLK-DIVIDER used to
generate the hclk in the USB Subsystem is held
in reset. This bit must be set to '0' before
changing the value os DIVIDE in this register.
The reset to the HCLK_DIVIDERis also asserted
when core reset is asserted. */
uint64_t p_x_on : 1; /**< Force USB-PHY on during suspend.
'1' USB-PHY XO block is powered-down during
suspend.
'0' USB-PHY XO block is powered-up during
suspend.
The value of this field must be set while POR is
active. */
uint64_t p_rclk : 1; /**< Phy refrence clock enable.
'1' The PHY PLL uses the XO block output as a
reference.
'0' Reserved. */
uint64_t p_xenbn : 1; /**< Phy external clock enable.
'1' The XO block uses the clock from a crystal.
'0' The XO block uses an external clock supplied
on the XO pin. USB_XI should be tied to
ground for this usage. */
uint64_t p_com_on : 1; /**< '0' Force USB-PHY XO Bias, Bandgap and PLL to
remain powered in Suspend Mode.
'1' The USB-PHY XO Bias, Bandgap and PLL are
powered down in suspend mode.
The value of this field must be set while POR is
active. */
uint64_t p_c_sel : 2; /**< Phy clock speed select.
Selects the reference clock / crystal frequency.
'11': Reserved
'10': 48 MHz
'01': 24 MHz
'00': 12 MHz
The value of this field must be set while POR is
active. */
uint64_t cdiv_byp : 1; /**< Used to enable the bypass input to the USB_CLK_DIV. */
uint64_t sd_mode : 2; /**< Scaledown mode for the USBC. Control timing events
in the USBC, for normal operation this must be '0'. */
uint64_t s_bist : 1; /**< Starts bist on the hclk memories, during the '0'
to '1' transition. */
uint64_t por : 1; /**< Power On Reset for the PHY.
Resets all the PHYS registers and state machines. */
uint64_t enable : 1; /**< When '1' allows the generation of the hclk. When
'0' the hclk will not be generated. */
uint64_t prst : 1; /**< When this field is '0' the reset associated with
the phy_clk functionality in the USB Subsystem is
help in reset. This bit should not be set to '1'
until the time it takes 6 clocks (hclk or phy_clk,
whichever is slower) has passed. Under normal
operation once this bit is set to '1' it should not
be set to '0'. */
uint64_t hrst : 1; /**< When this field is '0' the reset associated with
the hclk functioanlity in the USB Subsystem is
held in reset.This bit should not be set to '1'
until 12ms after phy_clk is stable. Under normal
operation, once this bit is set to '1' it should
not be set to '0'. */
uint64_t divide : 3; /**< The 'hclk' used by the USB subsystem is derived
from the eclk. The eclk will be divided by the
value of this field +1 to determine the hclk
frequency. (Also see HRST of this register).
The hclk frequency must be less than 125 MHz. */
} cn30xx;
struct cvmx_usbnx_clk_ctl_cn30xx cn31xx;
struct cvmx_usbnx_clk_ctl_cn50xx
{
uint64_t reserved_20_63 : 44;
uint64_t divide2 : 2; /**< The 'hclk' used by the USB subsystem is derived
from the eclk.
Also see the field DIVIDE. DIVIDE2<1> must currently
be zero because it is not implemented, so the maximum
ratio of eclk/hclk is currently 16.
The actual divide number for hclk is:
(DIVIDE2 + 1) * (DIVIDE + 1) */
uint64_t hclk_rst : 1; /**< When this field is '0' the HCLK-DIVIDER used to
generate the hclk in the USB Subsystem is held
in reset. This bit must be set to '0' before
changing the value os DIVIDE in this register.
The reset to the HCLK_DIVIDERis also asserted
when core reset is asserted. */
uint64_t reserved_16_16 : 1;
uint64_t p_rtype : 2; /**< PHY reference clock type
'0' The USB-PHY uses a 12MHz crystal as a clock
source at the USB_XO and USB_XI pins
'1' Reserved
'2' The USB_PHY uses 12/24/48MHz 2.5V board clock
at the USB_XO pin. USB_XI should be tied to
ground in this case.
'3' Reserved
(bit 14 was P_XENBN on 3xxx)
(bit 15 was P_RCLK on 3xxx) */
uint64_t p_com_on : 1; /**< '0' Force USB-PHY XO Bias, Bandgap and PLL to
remain powered in Suspend Mode.
'1' The USB-PHY XO Bias, Bandgap and PLL are
powered down in suspend mode.
The value of this field must be set while POR is
active. */
uint64_t p_c_sel : 2; /**< Phy clock speed select.
Selects the reference clock / crystal frequency.
'11': Reserved
'10': 48 MHz (reserved when a crystal is used)
'01': 24 MHz (reserved when a crystal is used)
'00': 12 MHz
The value of this field must be set while POR is
active.
NOTE: if a crystal is used as a reference clock,
this field must be set to 12 MHz. */
uint64_t cdiv_byp : 1; /**< Used to enable the bypass input to the USB_CLK_DIV. */
uint64_t sd_mode : 2; /**< Scaledown mode for the USBC. Control timing events
in the USBC, for normal operation this must be '0'. */
uint64_t s_bist : 1; /**< Starts bist on the hclk memories, during the '0'
to '1' transition. */
uint64_t por : 1; /**< Power On Reset for the PHY.
Resets all the PHYS registers and state machines. */
uint64_t enable : 1; /**< When '1' allows the generation of the hclk. When
'0' the hclk will not be generated. SEE DIVIDE
field of this register. */
uint64_t prst : 1; /**< When this field is '0' the reset associated with
the phy_clk functionality in the USB Subsystem is
help in reset. This bit should not be set to '1'
until the time it takes 6 clocks (hclk or phy_clk,
whichever is slower) has passed. Under normal
operation once this bit is set to '1' it should not
be set to '0'. */
uint64_t hrst : 1; /**< When this field is '0' the reset associated with
the hclk functioanlity in the USB Subsystem is
held in reset.This bit should not be set to '1'
until 12ms after phy_clk is stable. Under normal
operation, once this bit is set to '1' it should
not be set to '0'. */
uint64_t divide : 3; /**< The frequency of 'hclk' used by the USB subsystem
is the eclk frequency divided by the value of
(DIVIDE2 + 1) * (DIVIDE + 1), also see the field
DIVIDE2 of this register.
The hclk frequency should be less than 125Mhz.
After writing a value to this field the SW should
read the field for the value written.
The ENABLE field of this register should not be set
until AFTER this field is set and then read. */
} cn50xx;
struct cvmx_usbnx_clk_ctl_cn50xx cn52xx;
struct cvmx_usbnx_clk_ctl_cn50xx cn52xxp1;
struct cvmx_usbnx_clk_ctl_cn50xx cn56xx;
struct cvmx_usbnx_clk_ctl_cn50xx cn56xxp1;
};
typedef union cvmx_usbnx_clk_ctl cvmx_usbnx_clk_ctl_t;
/**
* cvmx_usbn#_ctl_status
*
* USBN_CTL_STATUS = USBN's Control And Status Register
*
* Contains general control and status information for the USBN block.
*/
union cvmx_usbnx_ctl_status
{
uint64_t u64;
struct cvmx_usbnx_ctl_status_s
{
uint64_t reserved_6_63 : 58;
uint64_t dma_0pag : 1; /**< When '1' sets the DMA engine will set the zero-Page
bit in the L2C store operation to the IOB. */
uint64_t dma_stt : 1; /**< When '1' sets the DMA engine to use STT operations. */
uint64_t dma_test : 1; /**< When '1' sets the DMA engine into Test-Mode.
For normal operation this bit should be '0'. */
uint64_t inv_a2 : 1; /**< When '1' causes the address[2] driven on the AHB
for USB-CORE FIFO access to be inverted. Also data
writen to and read from the AHB will have it byte
order swapped. If the orginal order was A-B-C-D the
new byte order will be D-C-B-A. */
uint64_t l2c_emod : 2; /**< Endian format for data from/to the L2C.
IN: A-B-C-D-E-F-G-H
OUT0: A-B-C-D-E-F-G-H
OUT1: H-G-F-E-D-C-B-A
OUT2: D-C-B-A-H-G-F-E
OUT3: E-F-G-H-A-B-C-D */
} s;
struct cvmx_usbnx_ctl_status_s cn30xx;
struct cvmx_usbnx_ctl_status_s cn31xx;
struct cvmx_usbnx_ctl_status_s cn50xx;
struct cvmx_usbnx_ctl_status_s cn52xx;
struct cvmx_usbnx_ctl_status_s cn52xxp1;
struct cvmx_usbnx_ctl_status_s cn56xx;
struct cvmx_usbnx_ctl_status_s cn56xxp1;
};
typedef union cvmx_usbnx_ctl_status cvmx_usbnx_ctl_status_t;
/**
* cvmx_usbn#_dma0_inb_chn0
*
* USBN_DMA0_INB_CHN0 = USBN's Inbound DMA for USB0 Channel0
*
* Contains the starting address for use when USB0 writes to L2C via Channel0.
* Writing of this register sets the base address.
*/
union cvmx_usbnx_dma0_inb_chn0
{
uint64_t u64;
struct cvmx_usbnx_dma0_inb_chn0_s
{
uint64_t reserved_36_63 : 28;
uint64_t addr : 36; /**< Base address for DMA Write to L2C. */
} s;
struct cvmx_usbnx_dma0_inb_chn0_s cn30xx;
struct cvmx_usbnx_dma0_inb_chn0_s cn31xx;
struct cvmx_usbnx_dma0_inb_chn0_s cn50xx;
struct cvmx_usbnx_dma0_inb_chn0_s cn52xx;
struct cvmx_usbnx_dma0_inb_chn0_s cn52xxp1;
struct cvmx_usbnx_dma0_inb_chn0_s cn56xx;
struct cvmx_usbnx_dma0_inb_chn0_s cn56xxp1;
};
typedef union cvmx_usbnx_dma0_inb_chn0 cvmx_usbnx_dma0_inb_chn0_t;
/**
* cvmx_usbn#_dma0_inb_chn1
*
* USBN_DMA0_INB_CHN1 = USBN's Inbound DMA for USB0 Channel1
*
* Contains the starting address for use when USB0 writes to L2C via Channel1.
* Writing of this register sets the base address.
*/
union cvmx_usbnx_dma0_inb_chn1
{
uint64_t u64;
struct cvmx_usbnx_dma0_inb_chn1_s
{
uint64_t reserved_36_63 : 28;
uint64_t addr : 36; /**< Base address for DMA Write to L2C. */
} s;
struct cvmx_usbnx_dma0_inb_chn1_s cn30xx;
struct cvmx_usbnx_dma0_inb_chn1_s cn31xx;
struct cvmx_usbnx_dma0_inb_chn1_s cn50xx;
struct cvmx_usbnx_dma0_inb_chn1_s cn52xx;
struct cvmx_usbnx_dma0_inb_chn1_s cn52xxp1;
struct cvmx_usbnx_dma0_inb_chn1_s cn56xx;
struct cvmx_usbnx_dma0_inb_chn1_s cn56xxp1;
};
typedef union cvmx_usbnx_dma0_inb_chn1 cvmx_usbnx_dma0_inb_chn1_t;
/**
* cvmx_usbn#_dma0_inb_chn2
*
* USBN_DMA0_INB_CHN2 = USBN's Inbound DMA for USB0 Channel2
*
* Contains the starting address for use when USB0 writes to L2C via Channel2.
* Writing of this register sets the base address.
*/
union cvmx_usbnx_dma0_inb_chn2
{
uint64_t u64;
struct cvmx_usbnx_dma0_inb_chn2_s
{
uint64_t reserved_36_63 : 28;
uint64_t addr : 36; /**< Base address for DMA Write to L2C. */
} s;
struct cvmx_usbnx_dma0_inb_chn2_s cn30xx;
struct cvmx_usbnx_dma0_inb_chn2_s cn31xx;
struct cvmx_usbnx_dma0_inb_chn2_s cn50xx;
struct cvmx_usbnx_dma0_inb_chn2_s cn52xx;
struct cvmx_usbnx_dma0_inb_chn2_s cn52xxp1;
struct cvmx_usbnx_dma0_inb_chn2_s cn56xx;
struct cvmx_usbnx_dma0_inb_chn2_s cn56xxp1;
};
typedef union cvmx_usbnx_dma0_inb_chn2 cvmx_usbnx_dma0_inb_chn2_t;
/**
* cvmx_usbn#_dma0_inb_chn3
*
* USBN_DMA0_INB_CHN3 = USBN's Inbound DMA for USB0 Channel3
*
* Contains the starting address for use when USB0 writes to L2C via Channel3.
* Writing of this register sets the base address.
*/
union cvmx_usbnx_dma0_inb_chn3
{
uint64_t u64;
struct cvmx_usbnx_dma0_inb_chn3_s
{
uint64_t reserved_36_63 : 28;
uint64_t addr : 36; /**< Base address for DMA Write to L2C. */
} s;
struct cvmx_usbnx_dma0_inb_chn3_s cn30xx;
struct cvmx_usbnx_dma0_inb_chn3_s cn31xx;
struct cvmx_usbnx_dma0_inb_chn3_s cn50xx;
struct cvmx_usbnx_dma0_inb_chn3_s cn52xx;
struct cvmx_usbnx_dma0_inb_chn3_s cn52xxp1;
struct cvmx_usbnx_dma0_inb_chn3_s cn56xx;
struct cvmx_usbnx_dma0_inb_chn3_s cn56xxp1;
};
typedef union cvmx_usbnx_dma0_inb_chn3 cvmx_usbnx_dma0_inb_chn3_t;
/**
* cvmx_usbn#_dma0_inb_chn4
*
* USBN_DMA0_INB_CHN4 = USBN's Inbound DMA for USB0 Channel4
*
* Contains the starting address for use when USB0 writes to L2C via Channel4.
* Writing of this register sets the base address.
*/
union cvmx_usbnx_dma0_inb_chn4
{
uint64_t u64;
struct cvmx_usbnx_dma0_inb_chn4_s
{
uint64_t reserved_36_63 : 28;
uint64_t addr : 36; /**< Base address for DMA Write to L2C. */
} s;
struct cvmx_usbnx_dma0_inb_chn4_s cn30xx;
struct cvmx_usbnx_dma0_inb_chn4_s cn31xx;
struct cvmx_usbnx_dma0_inb_chn4_s cn50xx;
struct cvmx_usbnx_dma0_inb_chn4_s cn52xx;
struct cvmx_usbnx_dma0_inb_chn4_s cn52xxp1;
struct cvmx_usbnx_dma0_inb_chn4_s cn56xx;
struct cvmx_usbnx_dma0_inb_chn4_s cn56xxp1;
};
typedef union cvmx_usbnx_dma0_inb_chn4 cvmx_usbnx_dma0_inb_chn4_t;
/**
* cvmx_usbn#_dma0_inb_chn5
*
* USBN_DMA0_INB_CHN5 = USBN's Inbound DMA for USB0 Channel5
*
* Contains the starting address for use when USB0 writes to L2C via Channel5.
* Writing of this register sets the base address.
*/
union cvmx_usbnx_dma0_inb_chn5
{
uint64_t u64;
struct cvmx_usbnx_dma0_inb_chn5_s
{
uint64_t reserved_36_63 : 28;
uint64_t addr : 36; /**< Base address for DMA Write to L2C. */
} s;
struct cvmx_usbnx_dma0_inb_chn5_s cn30xx;
struct cvmx_usbnx_dma0_inb_chn5_s cn31xx;
struct cvmx_usbnx_dma0_inb_chn5_s cn50xx;
struct cvmx_usbnx_dma0_inb_chn5_s cn52xx;
struct cvmx_usbnx_dma0_inb_chn5_s cn52xxp1;
struct cvmx_usbnx_dma0_inb_chn5_s cn56xx;
struct cvmx_usbnx_dma0_inb_chn5_s cn56xxp1;
};
typedef union cvmx_usbnx_dma0_inb_chn5 cvmx_usbnx_dma0_inb_chn5_t;
/**
* cvmx_usbn#_dma0_inb_chn6
*
* USBN_DMA0_INB_CHN6 = USBN's Inbound DMA for USB0 Channel6
*
* Contains the starting address for use when USB0 writes to L2C via Channel6.
* Writing of this register sets the base address.
*/
union cvmx_usbnx_dma0_inb_chn6
{
uint64_t u64;
struct cvmx_usbnx_dma0_inb_chn6_s
{
uint64_t reserved_36_63 : 28;
uint64_t addr : 36; /**< Base address for DMA Write to L2C. */
} s;
struct cvmx_usbnx_dma0_inb_chn6_s cn30xx;
struct cvmx_usbnx_dma0_inb_chn6_s cn31xx;
struct cvmx_usbnx_dma0_inb_chn6_s cn50xx;
struct cvmx_usbnx_dma0_inb_chn6_s cn52xx;
struct cvmx_usbnx_dma0_inb_chn6_s cn52xxp1;
struct cvmx_usbnx_dma0_inb_chn6_s cn56xx;
struct cvmx_usbnx_dma0_inb_chn6_s cn56xxp1;
};
typedef union cvmx_usbnx_dma0_inb_chn6 cvmx_usbnx_dma0_inb_chn6_t;
/**
* cvmx_usbn#_dma0_inb_chn7
*
* USBN_DMA0_INB_CHN7 = USBN's Inbound DMA for USB0 Channel7
*
* Contains the starting address for use when USB0 writes to L2C via Channel7.
* Writing of this register sets the base address.
*/
union cvmx_usbnx_dma0_inb_chn7
{
uint64_t u64;
struct cvmx_usbnx_dma0_inb_chn7_s
{
uint64_t reserved_36_63 : 28;
uint64_t addr : 36; /**< Base address for DMA Write to L2C. */
} s;
struct cvmx_usbnx_dma0_inb_chn7_s cn30xx;
struct cvmx_usbnx_dma0_inb_chn7_s cn31xx;
struct cvmx_usbnx_dma0_inb_chn7_s cn50xx;
struct cvmx_usbnx_dma0_inb_chn7_s cn52xx;
struct cvmx_usbnx_dma0_inb_chn7_s cn52xxp1;
struct cvmx_usbnx_dma0_inb_chn7_s cn56xx;
struct cvmx_usbnx_dma0_inb_chn7_s cn56xxp1;
};
typedef union cvmx_usbnx_dma0_inb_chn7 cvmx_usbnx_dma0_inb_chn7_t;
/**
* cvmx_usbn#_dma0_outb_chn0
*
* USBN_DMA0_OUTB_CHN0 = USBN's Outbound DMA for USB0 Channel0
*
* Contains the starting address for use when USB0 reads from L2C via Channel0.
* Writing of this register sets the base address.
*/
union cvmx_usbnx_dma0_outb_chn0
{
uint64_t u64;
struct cvmx_usbnx_dma0_outb_chn0_s
{
uint64_t reserved_36_63 : 28;
uint64_t addr : 36; /**< Base address for DMA Read from L2C. */
} s;
struct cvmx_usbnx_dma0_outb_chn0_s cn30xx;
struct cvmx_usbnx_dma0_outb_chn0_s cn31xx;
struct cvmx_usbnx_dma0_outb_chn0_s cn50xx;
struct cvmx_usbnx_dma0_outb_chn0_s cn52xx;
struct cvmx_usbnx_dma0_outb_chn0_s cn52xxp1;
struct cvmx_usbnx_dma0_outb_chn0_s cn56xx;
struct cvmx_usbnx_dma0_outb_chn0_s cn56xxp1;
};
typedef union cvmx_usbnx_dma0_outb_chn0 cvmx_usbnx_dma0_outb_chn0_t;
/**
* cvmx_usbn#_dma0_outb_chn1
*
* USBN_DMA0_OUTB_CHN1 = USBN's Outbound DMA for USB0 Channel1
*
* Contains the starting address for use when USB0 reads from L2C via Channel1.
* Writing of this register sets the base address.
*/
union cvmx_usbnx_dma0_outb_chn1
{
uint64_t u64;
struct cvmx_usbnx_dma0_outb_chn1_s
{
uint64_t reserved_36_63 : 28;
uint64_t addr : 36; /**< Base address for DMA Read from L2C. */
} s;
struct cvmx_usbnx_dma0_outb_chn1_s cn30xx;
struct cvmx_usbnx_dma0_outb_chn1_s cn31xx;
struct cvmx_usbnx_dma0_outb_chn1_s cn50xx;
struct cvmx_usbnx_dma0_outb_chn1_s cn52xx;
struct cvmx_usbnx_dma0_outb_chn1_s cn52xxp1;
struct cvmx_usbnx_dma0_outb_chn1_s cn56xx;
struct cvmx_usbnx_dma0_outb_chn1_s cn56xxp1;
};
typedef union cvmx_usbnx_dma0_outb_chn1 cvmx_usbnx_dma0_outb_chn1_t;
/**
* cvmx_usbn#_dma0_outb_chn2
*
* USBN_DMA0_OUTB_CHN2 = USBN's Outbound DMA for USB0 Channel2
*
* Contains the starting address for use when USB0 reads from L2C via Channel2.
* Writing of this register sets the base address.
*/
union cvmx_usbnx_dma0_outb_chn2
{
uint64_t u64;
struct cvmx_usbnx_dma0_outb_chn2_s
{
uint64_t reserved_36_63 : 28;
uint64_t addr : 36; /**< Base address for DMA Read from L2C. */
} s;
struct cvmx_usbnx_dma0_outb_chn2_s cn30xx;
struct cvmx_usbnx_dma0_outb_chn2_s cn31xx;
struct cvmx_usbnx_dma0_outb_chn2_s cn50xx;
struct cvmx_usbnx_dma0_outb_chn2_s cn52xx;
struct cvmx_usbnx_dma0_outb_chn2_s cn52xxp1;
struct cvmx_usbnx_dma0_outb_chn2_s cn56xx;
struct cvmx_usbnx_dma0_outb_chn2_s cn56xxp1;
};
typedef union cvmx_usbnx_dma0_outb_chn2 cvmx_usbnx_dma0_outb_chn2_t;
/**
* cvmx_usbn#_dma0_outb_chn3
*
* USBN_DMA0_OUTB_CHN3 = USBN's Outbound DMA for USB0 Channel3
*
* Contains the starting address for use when USB0 reads from L2C via Channel3.
* Writing of this register sets the base address.
*/
union cvmx_usbnx_dma0_outb_chn3
{
uint64_t u64;
struct cvmx_usbnx_dma0_outb_chn3_s
{
uint64_t reserved_36_63 : 28;
uint64_t addr : 36; /**< Base address for DMA Read from L2C. */
} s;
struct cvmx_usbnx_dma0_outb_chn3_s cn30xx;
struct cvmx_usbnx_dma0_outb_chn3_s cn31xx;
struct cvmx_usbnx_dma0_outb_chn3_s cn50xx;
struct cvmx_usbnx_dma0_outb_chn3_s cn52xx;
struct cvmx_usbnx_dma0_outb_chn3_s cn52xxp1;
struct cvmx_usbnx_dma0_outb_chn3_s cn56xx;
struct cvmx_usbnx_dma0_outb_chn3_s cn56xxp1;
};
typedef union cvmx_usbnx_dma0_outb_chn3 cvmx_usbnx_dma0_outb_chn3_t;
/**
* cvmx_usbn#_dma0_outb_chn4
*
* USBN_DMA0_OUTB_CHN4 = USBN's Outbound DMA for USB0 Channel4
*
* Contains the starting address for use when USB0 reads from L2C via Channel4.
* Writing of this register sets the base address.
*/
union cvmx_usbnx_dma0_outb_chn4
{
uint64_t u64;
struct cvmx_usbnx_dma0_outb_chn4_s
{
uint64_t reserved_36_63 : 28;
uint64_t addr : 36; /**< Base address for DMA Read from L2C. */
} s;
struct cvmx_usbnx_dma0_outb_chn4_s cn30xx;
struct cvmx_usbnx_dma0_outb_chn4_s cn31xx;
struct cvmx_usbnx_dma0_outb_chn4_s cn50xx;
struct cvmx_usbnx_dma0_outb_chn4_s cn52xx;
struct cvmx_usbnx_dma0_outb_chn4_s cn52xxp1;
struct cvmx_usbnx_dma0_outb_chn4_s cn56xx;
struct cvmx_usbnx_dma0_outb_chn4_s cn56xxp1;
};
typedef union cvmx_usbnx_dma0_outb_chn4 cvmx_usbnx_dma0_outb_chn4_t;
/**
* cvmx_usbn#_dma0_outb_chn5
*
* USBN_DMA0_OUTB_CHN5 = USBN's Outbound DMA for USB0 Channel5
*
* Contains the starting address for use when USB0 reads from L2C via Channel5.
* Writing of this register sets the base address.
*/
union cvmx_usbnx_dma0_outb_chn5
{
uint64_t u64;
struct cvmx_usbnx_dma0_outb_chn5_s
{
uint64_t reserved_36_63 : 28;
uint64_t addr : 36; /**< Base address for DMA Read from L2C. */
} s;
struct cvmx_usbnx_dma0_outb_chn5_s cn30xx;
struct cvmx_usbnx_dma0_outb_chn5_s cn31xx;
struct cvmx_usbnx_dma0_outb_chn5_s cn50xx;
struct cvmx_usbnx_dma0_outb_chn5_s cn52xx;
struct cvmx_usbnx_dma0_outb_chn5_s cn52xxp1;
struct cvmx_usbnx_dma0_outb_chn5_s cn56xx;
struct cvmx_usbnx_dma0_outb_chn5_s cn56xxp1;
};
typedef union cvmx_usbnx_dma0_outb_chn5 cvmx_usbnx_dma0_outb_chn5_t;
/**
* cvmx_usbn#_dma0_outb_chn6
*
* USBN_DMA0_OUTB_CHN6 = USBN's Outbound DMA for USB0 Channel6
*
* Contains the starting address for use when USB0 reads from L2C via Channel6.
* Writing of this register sets the base address.
*/
union cvmx_usbnx_dma0_outb_chn6
{
uint64_t u64;
struct cvmx_usbnx_dma0_outb_chn6_s
{
uint64_t reserved_36_63 : 28;
uint64_t addr : 36; /**< Base address for DMA Read from L2C. */
} s;
struct cvmx_usbnx_dma0_outb_chn6_s cn30xx;
struct cvmx_usbnx_dma0_outb_chn6_s cn31xx;
struct cvmx_usbnx_dma0_outb_chn6_s cn50xx;
struct cvmx_usbnx_dma0_outb_chn6_s cn52xx;
struct cvmx_usbnx_dma0_outb_chn6_s cn52xxp1;
struct cvmx_usbnx_dma0_outb_chn6_s cn56xx;
struct cvmx_usbnx_dma0_outb_chn6_s cn56xxp1;
};
typedef union cvmx_usbnx_dma0_outb_chn6 cvmx_usbnx_dma0_outb_chn6_t;
/**
* cvmx_usbn#_dma0_outb_chn7
*
* USBN_DMA0_OUTB_CHN7 = USBN's Outbound DMA for USB0 Channel7
*
* Contains the starting address for use when USB0 reads from L2C via Channel7.
* Writing of this register sets the base address.
*/
union cvmx_usbnx_dma0_outb_chn7
{
uint64_t u64;
struct cvmx_usbnx_dma0_outb_chn7_s
{
uint64_t reserved_36_63 : 28;
uint64_t addr : 36; /**< Base address for DMA Read from L2C. */
} s;
struct cvmx_usbnx_dma0_outb_chn7_s cn30xx;
struct cvmx_usbnx_dma0_outb_chn7_s cn31xx;
struct cvmx_usbnx_dma0_outb_chn7_s cn50xx;
struct cvmx_usbnx_dma0_outb_chn7_s cn52xx;
struct cvmx_usbnx_dma0_outb_chn7_s cn52xxp1;
struct cvmx_usbnx_dma0_outb_chn7_s cn56xx;
struct cvmx_usbnx_dma0_outb_chn7_s cn56xxp1;
};
typedef union cvmx_usbnx_dma0_outb_chn7 cvmx_usbnx_dma0_outb_chn7_t;
/**
* cvmx_usbn#_dma_test
*
* USBN_DMA_TEST = USBN's DMA TestRegister
*
* This register can cause the external DMA engine to the USB-Core to make transfers from/to L2C/USB-FIFOs
*/
union cvmx_usbnx_dma_test
{
uint64_t u64;
struct cvmx_usbnx_dma_test_s
{
uint64_t reserved_40_63 : 24;
uint64_t done : 1; /**< This field is set when a DMA completes. Writing a
'1' to this field clears this bit. */
uint64_t req : 1; /**< DMA Request. Writing a 1 to this register
will cause a DMA request as specified in the other
fields of this register to take place. This field
will always read as '0'. */
uint64_t f_addr : 18; /**< The address to read from in the Data-Fifo. */
uint64_t count : 11; /**< DMA Request Count. */
uint64_t channel : 5; /**< DMA Channel/Enpoint. */
uint64_t burst : 4; /**< DMA Burst Size. */
} s;
struct cvmx_usbnx_dma_test_s cn30xx;
struct cvmx_usbnx_dma_test_s cn31xx;
struct cvmx_usbnx_dma_test_s cn50xx;
struct cvmx_usbnx_dma_test_s cn52xx;
struct cvmx_usbnx_dma_test_s cn52xxp1;
struct cvmx_usbnx_dma_test_s cn56xx;
struct cvmx_usbnx_dma_test_s cn56xxp1;
};
typedef union cvmx_usbnx_dma_test cvmx_usbnx_dma_test_t;
/**
* cvmx_usbn#_int_enb
*
* USBN_INT_ENB = USBN's Interrupt Enable
*
* The USBN's interrupt enable register.
*/
union cvmx_usbnx_int_enb
{
uint64_t u64;
struct cvmx_usbnx_int_enb_s
{
uint64_t reserved_38_63 : 26;
uint64_t nd4o_dpf : 1; /**< When set (1) and bit 37 of the USBN_INT_SUM
register is asserted the USBN will assert an
interrupt. */
uint64_t nd4o_dpe : 1; /**< When set (1) and bit 36 of the USBN_INT_SUM
register is asserted the USBN will assert an
interrupt. */
uint64_t nd4o_rpf : 1; /**< When set (1) and bit 35 of the USBN_INT_SUM
register is asserted the USBN will assert an
interrupt. */
uint64_t nd4o_rpe : 1; /**< When set (1) and bit 34 of the USBN_INT_SUM
register is asserted the USBN will assert an
interrupt. */
uint64_t ltl_f_pf : 1; /**< When set (1) and bit 33 of the USBN_INT_SUM
register is asserted the USBN will assert an
interrupt. */
uint64_t ltl_f_pe : 1; /**< When set (1) and bit 32 of the USBN_INT_SUM
register is asserted the USBN will assert an
interrupt. */
uint64_t u2n_c_pe : 1; /**< When set (1) and bit 31 of the USBN_INT_SUM
register is asserted the USBN will assert an
interrupt. */
uint64_t u2n_c_pf : 1; /**< When set (1) and bit 30 of the USBN_INT_SUM
register is asserted the USBN will assert an
interrupt. */
uint64_t u2n_d_pf : 1; /**< When set (1) and bit 29 of the USBN_INT_SUM
register is asserted the USBN will assert an
interrupt. */
uint64_t u2n_d_pe : 1; /**< When set (1) and bit 28 of the USBN_INT_SUM
register is asserted the USBN will assert an
interrupt. */
uint64_t n2u_pe : 1; /**< When set (1) and bit 27 of the USBN_INT_SUM
register is asserted the USBN will assert an
interrupt. */
uint64_t n2u_pf : 1; /**< When set (1) and bit 26 of the USBN_INT_SUM
register is asserted the USBN will assert an
interrupt. */
uint64_t uod_pf : 1; /**< When set (1) and bit 25 of the USBN_INT_SUM
register is asserted the USBN will assert an
interrupt. */
uint64_t uod_pe : 1; /**< When set (1) and bit 24 of the USBN_INT_SUM
register is asserted the USBN will assert an
interrupt. */
uint64_t rq_q3_e : 1; /**< When set (1) and bit 23 of the USBN_INT_SUM
register is asserted the USBN will assert an
interrupt. */
uint64_t rq_q3_f : 1; /**< When set (1) and bit 22 of the USBN_INT_SUM
register is asserted the USBN will assert an
interrupt. */
uint64_t rq_q2_e : 1; /**< When set (1) and bit 21 of the USBN_INT_SUM
register is asserted the USBN will assert an
interrupt. */
uint64_t rq_q2_f : 1; /**< When set (1) and bit 20 of the USBN_INT_SUM
register is asserted the USBN will assert an
interrupt. */
uint64_t rg_fi_f : 1; /**< When set (1) and bit 19 of the USBN_INT_SUM
register is asserted the USBN will assert an
interrupt. */
uint64_t rg_fi_e : 1; /**< When set (1) and bit 18 of the USBN_INT_SUM
register is asserted the USBN will assert an
interrupt. */
uint64_t l2_fi_f : 1; /**< When set (1) and bit 17 of the USBN_INT_SUM
register is asserted the USBN will assert an
interrupt. */
uint64_t l2_fi_e : 1; /**< When set (1) and bit 16 of the USBN_INT_SUM
register is asserted the USBN will assert an
interrupt. */
uint64_t l2c_a_f : 1; /**< When set (1) and bit 15 of the USBN_INT_SUM
register is asserted the USBN will assert an
interrupt. */
uint64_t l2c_s_e : 1; /**< When set (1) and bit 14 of the USBN_INT_SUM
register is asserted the USBN will assert an
interrupt. */
uint64_t dcred_f : 1; /**< When set (1) and bit 13 of the USBN_INT_SUM
register is asserted the USBN will assert an
interrupt. */
uint64_t dcred_e : 1; /**< When set (1) and bit 12 of the USBN_INT_SUM
register is asserted the USBN will assert an
interrupt. */
uint64_t lt_pu_f : 1; /**< When set (1) and bit 11 of the USBN_INT_SUM
register is asserted the USBN will assert an
interrupt. */
uint64_t lt_po_e : 1; /**< When set (1) and bit 10 of the USBN_INT_SUM
register is asserted the USBN will assert an
interrupt. */
uint64_t nt_pu_f : 1; /**< When set (1) and bit 9 of the USBN_INT_SUM
register is asserted the USBN will assert an
interrupt. */
uint64_t nt_po_e : 1; /**< When set (1) and bit 8 of the USBN_INT_SUM
register is asserted the USBN will assert an
interrupt. */
uint64_t pt_pu_f : 1; /**< When set (1) and bit 7 of the USBN_INT_SUM
register is asserted the USBN will assert an
interrupt. */
uint64_t pt_po_e : 1; /**< When set (1) and bit 6 of the USBN_INT_SUM
register is asserted the USBN will assert an
interrupt. */
uint64_t lr_pu_f : 1; /**< When set (1) and bit 5 of the USBN_INT_SUM
register is asserted the USBN will assert an
interrupt. */
uint64_t lr_po_e : 1; /**< When set (1) and bit 4 of the USBN_INT_SUM
register is asserted the USBN will assert an
interrupt. */
uint64_t nr_pu_f : 1; /**< When set (1) and bit 3 of the USBN_INT_SUM
register is asserted the USBN will assert an
interrupt. */
uint64_t nr_po_e : 1; /**< When set (1) and bit 2 of the USBN_INT_SUM
register is asserted the USBN will assert an
interrupt. */
uint64_t pr_pu_f : 1; /**< When set (1) and bit 1 of the USBN_INT_SUM
register is asserted the USBN will assert an
interrupt. */
uint64_t pr_po_e : 1; /**< When set (1) and bit 0 of the USBN_INT_SUM
register is asserted the USBN will assert an
interrupt. */
} s;
struct cvmx_usbnx_int_enb_s cn30xx;
struct cvmx_usbnx_int_enb_s cn31xx;
struct cvmx_usbnx_int_enb_cn50xx
{
uint64_t reserved_38_63 : 26;
uint64_t nd4o_dpf : 1; /**< When set (1) and bit 37 of the USBN_INT_SUM
register is asserted the USBN will assert an
interrupt. */
uint64_t nd4o_dpe : 1; /**< When set (1) and bit 36 of the USBN_INT_SUM
register is asserted the USBN will assert an
interrupt. */
uint64_t nd4o_rpf : 1; /**< When set (1) and bit 35 of the USBN_INT_SUM
register is asserted the USBN will assert an
interrupt. */
uint64_t nd4o_rpe : 1; /**< When set (1) and bit 34 of the USBN_INT_SUM
register is asserted the USBN will assert an
interrupt. */
uint64_t ltl_f_pf : 1; /**< When set (1) and bit 33 of the USBN_INT_SUM
register is asserted the USBN will assert an
interrupt. */
uint64_t ltl_f_pe : 1; /**< When set (1) and bit 32 of the USBN_INT_SUM
register is asserted the USBN will assert an
interrupt. */
uint64_t reserved_26_31 : 6;
uint64_t uod_pf : 1; /**< When set (1) and bit 25 of the USBN_INT_SUM
register is asserted the USBN will assert an
interrupt. */
uint64_t uod_pe : 1; /**< When set (1) and bit 24 of the USBN_INT_SUM
register is asserted the USBN will assert an
interrupt. */
uint64_t rq_q3_e : 1; /**< When set (1) and bit 23 of the USBN_INT_SUM
register is asserted the USBN will assert an
interrupt. */
uint64_t rq_q3_f : 1; /**< When set (1) and bit 22 of the USBN_INT_SUM
register is asserted the USBN will assert an
interrupt. */
uint64_t rq_q2_e : 1; /**< When set (1) and bit 21 of the USBN_INT_SUM
register is asserted the USBN will assert an
interrupt. */
uint64_t rq_q2_f : 1; /**< When set (1) and bit 20 of the USBN_INT_SUM
register is asserted the USBN will assert an
interrupt. */
uint64_t rg_fi_f : 1; /**< When set (1) and bit 19 of the USBN_INT_SUM
register is asserted the USBN will assert an
interrupt. */
uint64_t rg_fi_e : 1; /**< When set (1) and bit 18 of the USBN_INT_SUM
register is asserted the USBN will assert an
interrupt. */
uint64_t l2_fi_f : 1; /**< When set (1) and bit 17 of the USBN_INT_SUM
register is asserted the USBN will assert an
interrupt. */
uint64_t l2_fi_e : 1; /**< When set (1) and bit 16 of the USBN_INT_SUM
register is asserted the USBN will assert an
interrupt. */
uint64_t l2c_a_f : 1; /**< When set (1) and bit 15 of the USBN_INT_SUM
register is asserted the USBN will assert an
interrupt. */
uint64_t l2c_s_e : 1; /**< When set (1) and bit 14 of the USBN_INT_SUM
register is asserted the USBN will assert an
interrupt. */
uint64_t dcred_f : 1; /**< When set (1) and bit 13 of the USBN_INT_SUM
register is asserted the USBN will assert an
interrupt. */
uint64_t dcred_e : 1; /**< When set (1) and bit 12 of the USBN_INT_SUM
register is asserted the USBN will assert an
interrupt. */
uint64_t lt_pu_f : 1; /**< When set (1) and bit 11 of the USBN_INT_SUM
register is asserted the USBN will assert an
interrupt. */
uint64_t lt_po_e : 1; /**< When set (1) and bit 10 of the USBN_INT_SUM
register is asserted the USBN will assert an
interrupt. */
uint64_t nt_pu_f : 1; /**< When set (1) and bit 9 of the USBN_INT_SUM
register is asserted the USBN will assert an
interrupt. */
uint64_t nt_po_e : 1; /**< When set (1) and bit 8 of the USBN_INT_SUM
register is asserted the USBN will assert an
interrupt. */
uint64_t pt_pu_f : 1; /**< When set (1) and bit 7 of the USBN_INT_SUM
register is asserted the USBN will assert an
interrupt. */
uint64_t pt_po_e : 1; /**< When set (1) and bit 6 of the USBN_INT_SUM
register is asserted the USBN will assert an
interrupt. */
uint64_t lr_pu_f : 1; /**< When set (1) and bit 5 of the USBN_INT_SUM
register is asserted the USBN will assert an
interrupt. */
uint64_t lr_po_e : 1; /**< When set (1) and bit 4 of the USBN_INT_SUM
register is asserted the USBN will assert an
interrupt. */
uint64_t nr_pu_f : 1; /**< When set (1) and bit 3 of the USBN_INT_SUM
register is asserted the USBN will assert an
interrupt. */
uint64_t nr_po_e : 1; /**< When set (1) and bit 2 of the USBN_INT_SUM
register is asserted the USBN will assert an
interrupt. */
uint64_t pr_pu_f : 1; /**< When set (1) and bit 1 of the USBN_INT_SUM
register is asserted the USBN will assert an
interrupt. */
uint64_t pr_po_e : 1; /**< When set (1) and bit 0 of the USBN_INT_SUM
register is asserted the USBN will assert an
interrupt. */
} cn50xx;
struct cvmx_usbnx_int_enb_cn50xx cn52xx;
struct cvmx_usbnx_int_enb_cn50xx cn52xxp1;
struct cvmx_usbnx_int_enb_cn50xx cn56xx;
struct cvmx_usbnx_int_enb_cn50xx cn56xxp1;
};
typedef union cvmx_usbnx_int_enb cvmx_usbnx_int_enb_t;
/**
* cvmx_usbn#_int_sum
*
* USBN_INT_SUM = USBN's Interrupt Summary Register
*
* Contains the diffrent interrupt summary bits of the USBN.
*/
union cvmx_usbnx_int_sum
{
uint64_t u64;
struct cvmx_usbnx_int_sum_s
{
uint64_t reserved_38_63 : 26;
uint64_t nd4o_dpf : 1; /**< NCB DMA Out Data Fifo Push Full. */
uint64_t nd4o_dpe : 1; /**< NCB DMA Out Data Fifo Pop Empty. */
uint64_t nd4o_rpf : 1; /**< NCB DMA Out Request Fifo Push Full. */
uint64_t nd4o_rpe : 1; /**< NCB DMA Out Request Fifo Pop Empty. */
uint64_t ltl_f_pf : 1; /**< L2C Transfer Length Fifo Push Full. */
uint64_t ltl_f_pe : 1; /**< L2C Transfer Length Fifo Pop Empty. */
uint64_t u2n_c_pe : 1; /**< U2N Control Fifo Pop Empty. */
uint64_t u2n_c_pf : 1; /**< U2N Control Fifo Push Full. */
uint64_t u2n_d_pf : 1; /**< U2N Data Fifo Push Full. */
uint64_t u2n_d_pe : 1; /**< U2N Data Fifo Pop Empty. */
uint64_t n2u_pe : 1; /**< N2U Fifo Pop Empty. */
uint64_t n2u_pf : 1; /**< N2U Fifo Push Full. */
uint64_t uod_pf : 1; /**< UOD Fifo Push Full. */
uint64_t uod_pe : 1; /**< UOD Fifo Pop Empty. */
uint64_t rq_q3_e : 1; /**< Request Queue-3 Fifo Pushed When Full. */
uint64_t rq_q3_f : 1; /**< Request Queue-3 Fifo Pushed When Full. */
uint64_t rq_q2_e : 1; /**< Request Queue-2 Fifo Pushed When Full. */
uint64_t rq_q2_f : 1; /**< Request Queue-2 Fifo Pushed When Full. */
uint64_t rg_fi_f : 1; /**< Register Request Fifo Pushed When Full. */
uint64_t rg_fi_e : 1; /**< Register Request Fifo Pushed When Full. */
uint64_t lt_fi_f : 1; /**< L2C Request Fifo Pushed When Full. */
uint64_t lt_fi_e : 1; /**< L2C Request Fifo Pushed When Full. */
uint64_t l2c_a_f : 1; /**< L2C Credit Count Added When Full. */
uint64_t l2c_s_e : 1; /**< L2C Credit Count Subtracted When Empty. */
uint64_t dcred_f : 1; /**< Data CreditFifo Pushed When Full. */
uint64_t dcred_e : 1; /**< Data Credit Fifo Pushed When Full. */
uint64_t lt_pu_f : 1; /**< L2C Trasaction Fifo Pushed When Full. */
uint64_t lt_po_e : 1; /**< L2C Trasaction Fifo Popped When Full. */
uint64_t nt_pu_f : 1; /**< NPI Trasaction Fifo Pushed When Full. */
uint64_t nt_po_e : 1; /**< NPI Trasaction Fifo Popped When Full. */
uint64_t pt_pu_f : 1; /**< PP Trasaction Fifo Pushed When Full. */
uint64_t pt_po_e : 1; /**< PP Trasaction Fifo Popped When Full. */
uint64_t lr_pu_f : 1; /**< L2C Request Fifo Pushed When Full. */
uint64_t lr_po_e : 1; /**< L2C Request Fifo Popped When Empty. */
uint64_t nr_pu_f : 1; /**< NPI Request Fifo Pushed When Full. */
uint64_t nr_po_e : 1; /**< NPI Request Fifo Popped When Empty. */
uint64_t pr_pu_f : 1; /**< PP Request Fifo Pushed When Full. */
uint64_t pr_po_e : 1; /**< PP Request Fifo Popped When Empty. */
} s;
struct cvmx_usbnx_int_sum_s cn30xx;
struct cvmx_usbnx_int_sum_s cn31xx;
struct cvmx_usbnx_int_sum_cn50xx
{
uint64_t reserved_38_63 : 26;
uint64_t nd4o_dpf : 1; /**< NCB DMA Out Data Fifo Push Full. */
uint64_t nd4o_dpe : 1; /**< NCB DMA Out Data Fifo Pop Empty. */
uint64_t nd4o_rpf : 1; /**< NCB DMA Out Request Fifo Push Full. */
uint64_t nd4o_rpe : 1; /**< NCB DMA Out Request Fifo Pop Empty. */
uint64_t ltl_f_pf : 1; /**< L2C Transfer Length Fifo Push Full. */
uint64_t ltl_f_pe : 1; /**< L2C Transfer Length Fifo Pop Empty. */
uint64_t reserved_26_31 : 6;
uint64_t uod_pf : 1; /**< UOD Fifo Push Full. */
uint64_t uod_pe : 1; /**< UOD Fifo Pop Empty. */
uint64_t rq_q3_e : 1; /**< Request Queue-3 Fifo Pushed When Full. */
uint64_t rq_q3_f : 1; /**< Request Queue-3 Fifo Pushed When Full. */
uint64_t rq_q2_e : 1; /**< Request Queue-2 Fifo Pushed When Full. */
uint64_t rq_q2_f : 1; /**< Request Queue-2 Fifo Pushed When Full. */
uint64_t rg_fi_f : 1; /**< Register Request Fifo Pushed When Full. */
uint64_t rg_fi_e : 1; /**< Register Request Fifo Pushed When Full. */
uint64_t lt_fi_f : 1; /**< L2C Request Fifo Pushed When Full. */
uint64_t lt_fi_e : 1; /**< L2C Request Fifo Pushed When Full. */
uint64_t l2c_a_f : 1; /**< L2C Credit Count Added When Full. */
uint64_t l2c_s_e : 1; /**< L2C Credit Count Subtracted When Empty. */
uint64_t dcred_f : 1; /**< Data CreditFifo Pushed When Full. */
uint64_t dcred_e : 1; /**< Data Credit Fifo Pushed When Full. */
uint64_t lt_pu_f : 1; /**< L2C Trasaction Fifo Pushed When Full. */
uint64_t lt_po_e : 1; /**< L2C Trasaction Fifo Popped When Full. */
uint64_t nt_pu_f : 1; /**< NPI Trasaction Fifo Pushed When Full. */
uint64_t nt_po_e : 1; /**< NPI Trasaction Fifo Popped When Full. */
uint64_t pt_pu_f : 1; /**< PP Trasaction Fifo Pushed When Full. */
uint64_t pt_po_e : 1; /**< PP Trasaction Fifo Popped When Full. */
uint64_t lr_pu_f : 1; /**< L2C Request Fifo Pushed When Full. */
uint64_t lr_po_e : 1; /**< L2C Request Fifo Popped When Empty. */
uint64_t nr_pu_f : 1; /**< NPI Request Fifo Pushed When Full. */
uint64_t nr_po_e : 1; /**< NPI Request Fifo Popped When Empty. */
uint64_t pr_pu_f : 1; /**< PP Request Fifo Pushed When Full. */
uint64_t pr_po_e : 1; /**< PP Request Fifo Popped When Empty. */
} cn50xx;
struct cvmx_usbnx_int_sum_cn50xx cn52xx;
struct cvmx_usbnx_int_sum_cn50xx cn52xxp1;
struct cvmx_usbnx_int_sum_cn50xx cn56xx;
struct cvmx_usbnx_int_sum_cn50xx cn56xxp1;
};
typedef union cvmx_usbnx_int_sum cvmx_usbnx_int_sum_t;
/**
* cvmx_usbn#_usbp_ctl_status
*
* USBN_USBP_CTL_STATUS = USBP Control And Status Register
*
* Contains general control and status information for the USBN block.
*/
union cvmx_usbnx_usbp_ctl_status
{
uint64_t u64;
struct cvmx_usbnx_usbp_ctl_status_s
{
uint64_t txrisetune : 1; /**< HS Transmitter Rise/Fall Time Adjustment */
uint64_t txvreftune : 4; /**< HS DC Voltage Level Adjustment */
uint64_t txfslstune : 4; /**< FS/LS Source Impedence Adjustment */
uint64_t txhsxvtune : 2; /**< Transmitter High-Speed Crossover Adjustment */
uint64_t sqrxtune : 3; /**< Squelch Threshold Adjustment */
uint64_t compdistune : 3; /**< Disconnect Threshold Adjustment */
uint64_t otgtune : 3; /**< VBUS Valid Threshold Adjustment */
uint64_t otgdisable : 1; /**< OTG Block Disable */
uint64_t portreset : 1; /**< Per_Port Reset */
uint64_t drvvbus : 1; /**< Drive VBUS */
uint64_t lsbist : 1; /**< Low-Speed BIST Enable. */
uint64_t fsbist : 1; /**< Full-Speed BIST Enable. */
uint64_t hsbist : 1; /**< High-Speed BIST Enable. */
uint64_t bist_done : 1; /**< PHY Bist Done.
Asserted at the end of the PHY BIST sequence. */
uint64_t bist_err : 1; /**< PHY Bist Error.
Indicates an internal error was detected during
the BIST sequence. */
uint64_t tdata_out : 4; /**< PHY Test Data Out.
Presents either internaly generated signals or
test register contents, based upon the value of
test_data_out_sel. */
uint64_t siddq : 1; /**< Drives the USBP (USB-PHY) SIDDQ input.
Normally should be set to zero.
When customers have no intent to use USB PHY
interface, they should:
- still provide 3.3V to USB_VDD33, and
- tie USB_REXT to 3.3V supply, and
- set USBN*_USBP_CTL_STATUS[SIDDQ]=1 */
uint64_t txpreemphasistune : 1; /**< HS Transmitter Pre-Emphasis Enable */
uint64_t dma_bmode : 1; /**< When set to 1 the L2C DMA address will be updated
with byte-counts between packets. When set to 0
the L2C DMA address is incremented to the next
4-byte aligned address after adding byte-count. */
uint64_t usbc_end : 1; /**< Bigendian input to the USB Core. This should be
set to '0' for operation. */
uint64_t usbp_bist : 1; /**< PHY, This is cleared '0' to run BIST on the USBP. */
uint64_t tclk : 1; /**< PHY Test Clock, used to load TDATA_IN to the USBP. */
uint64_t dp_pulld : 1; /**< PHY DP_PULLDOWN input to the USB-PHY.
This signal enables the pull-down resistance on
the D+ line. '1' pull down-resistance is connected
to D+/ '0' pull down resistance is not connected
to D+. When an A/B device is acting as a host
(downstream-facing port), dp_pulldown and
dm_pulldown are enabled. This must not toggle
during normal opeartion. */
uint64_t dm_pulld : 1; /**< PHY DM_PULLDOWN input to the USB-PHY.
This signal enables the pull-down resistance on
the D- line. '1' pull down-resistance is connected
to D-. '0' pull down resistance is not connected
to D-. When an A/B device is acting as a host
(downstream-facing port), dp_pulldown and
dm_pulldown are enabled. This must not toggle
during normal opeartion. */
uint64_t hst_mode : 1; /**< When '0' the USB is acting as HOST, when '1'
USB is acting as device. This field needs to be
set while the USB is in reset. */
uint64_t tuning : 4; /**< Transmitter Tuning for High-Speed Operation.
Tunes the current supply and rise/fall output
times for high-speed operation.
[20:19] == 11: Current supply increased
approximately 9%
[20:19] == 10: Current supply increased
approximately 4.5%
[20:19] == 01: Design default.
[20:19] == 00: Current supply decreased
approximately 4.5%
[22:21] == 11: Rise and fall times are increased.
[22:21] == 10: Design default.
[22:21] == 01: Rise and fall times are decreased.
[22:21] == 00: Rise and fall times are decreased
further as compared to the 01 setting. */
uint64_t tx_bs_enh : 1; /**< Transmit Bit Stuffing on [15:8].
Enables or disables bit stuffing on data[15:8]
when bit-stuffing is enabled. */
uint64_t tx_bs_en : 1; /**< Transmit Bit Stuffing on [7:0].
Enables or disables bit stuffing on data[7:0]
when bit-stuffing is enabled. */
uint64_t loop_enb : 1; /**< PHY Loopback Test Enable.
'1': During data transmission the receive is
enabled.
'0': During data transmission the receive is
disabled.
Must be '0' for normal operation. */
uint64_t vtest_enb : 1; /**< Analog Test Pin Enable.
'1' The PHY's analog_test pin is enabled for the
input and output of applicable analog test signals.
'0' THe analog_test pin is disabled. */
uint64_t bist_enb : 1; /**< Built-In Self Test Enable.
Used to activate BIST in the PHY. */
uint64_t tdata_sel : 1; /**< Test Data Out Select.
'1' test_data_out[3:0] (PHY) register contents
are output. '0' internaly generated signals are
output. */
uint64_t taddr_in : 4; /**< Mode Address for Test Interface.
Specifies the register address for writing to or
reading from the PHY test interface register. */
uint64_t tdata_in : 8; /**< Internal Testing Register Input Data and Select
This is a test bus. Data is present on [3:0],
and its corresponding select (enable) is present
on bits [7:4]. */
uint64_t ate_reset : 1; /**< Reset input from automatic test equipment.
This is a test signal. When the USB Core is
powered up (not in Susned Mode), an automatic
tester can use this to disable phy_clock and
free_clk, then re-eanable them with an aligned
phase.
'1': The phy_clk and free_clk outputs are
disabled. "0": The phy_clock and free_clk outputs
are available within a specific period after the
de-assertion. */
} s;
struct cvmx_usbnx_usbp_ctl_status_cn30xx
{
uint64_t reserved_38_63 : 26;
uint64_t bist_done : 1; /**< PHY Bist Done.
Asserted at the end of the PHY BIST sequence. */
uint64_t bist_err : 1; /**< PHY Bist Error.
Indicates an internal error was detected during
the BIST sequence. */
uint64_t tdata_out : 4; /**< PHY Test Data Out.
Presents either internaly generated signals or
test register contents, based upon the value of
test_data_out_sel. */
uint64_t reserved_30_31 : 2;
uint64_t dma_bmode : 1; /**< When set to 1 the L2C DMA address will be updated
with byte-counts between packets. When set to 0
the L2C DMA address is incremented to the next
4-byte aligned address after adding byte-count. */
uint64_t usbc_end : 1; /**< Bigendian input to the USB Core. This should be
set to '0' for operation. */
uint64_t usbp_bist : 1; /**< PHY, This is cleared '0' to run BIST on the USBP. */
uint64_t tclk : 1; /**< PHY Test Clock, used to load TDATA_IN to the USBP. */
uint64_t dp_pulld : 1; /**< PHY DP_PULLDOWN input to the USB-PHY.
This signal enables the pull-down resistance on
the D+ line. '1' pull down-resistance is connected
to D+/ '0' pull down resistance is not connected
to D+. When an A/B device is acting as a host
(downstream-facing port), dp_pulldown and
dm_pulldown are enabled. This must not toggle
during normal opeartion. */
uint64_t dm_pulld : 1; /**< PHY DM_PULLDOWN input to the USB-PHY.
This signal enables the pull-down resistance on
the D- line. '1' pull down-resistance is connected
to D-. '0' pull down resistance is not connected
to D-. When an A/B device is acting as a host
(downstream-facing port), dp_pulldown and
dm_pulldown are enabled. This must not toggle
during normal opeartion. */
uint64_t hst_mode : 1; /**< When '0' the USB is acting as HOST, when '1'
USB is acting as device. This field needs to be
set while the USB is in reset. */
uint64_t tuning : 4; /**< Transmitter Tuning for High-Speed Operation.
Tunes the current supply and rise/fall output
times for high-speed operation.
[20:19] == 11: Current supply increased
approximately 9%
[20:19] == 10: Current supply increased
approximately 4.5%
[20:19] == 01: Design default.
[20:19] == 00: Current supply decreased
approximately 4.5%
[22:21] == 11: Rise and fall times are increased.
[22:21] == 10: Design default.
[22:21] == 01: Rise and fall times are decreased.
[22:21] == 00: Rise and fall times are decreased
further as compared to the 01 setting. */
uint64_t tx_bs_enh : 1; /**< Transmit Bit Stuffing on [15:8].
Enables or disables bit stuffing on data[15:8]
when bit-stuffing is enabled. */
uint64_t tx_bs_en : 1; /**< Transmit Bit Stuffing on [7:0].
Enables or disables bit stuffing on data[7:0]
when bit-stuffing is enabled. */
uint64_t loop_enb : 1; /**< PHY Loopback Test Enable.
'1': During data transmission the receive is
enabled.
'0': During data transmission the receive is
disabled.
Must be '0' for normal operation. */
uint64_t vtest_enb : 1; /**< Analog Test Pin Enable.
'1' The PHY's analog_test pin is enabled for the
input and output of applicable analog test signals.
'0' THe analog_test pin is disabled. */
uint64_t bist_enb : 1; /**< Built-In Self Test Enable.
Used to activate BIST in the PHY. */
uint64_t tdata_sel : 1; /**< Test Data Out Select.
'1' test_data_out[3:0] (PHY) register contents
are output. '0' internaly generated signals are
output. */
uint64_t taddr_in : 4; /**< Mode Address for Test Interface.
Specifies the register address for writing to or
reading from the PHY test interface register. */
uint64_t tdata_in : 8; /**< Internal Testing Register Input Data and Select
This is a test bus. Data is present on [3:0],
and its corresponding select (enable) is present
on bits [7:4]. */
uint64_t ate_reset : 1; /**< Reset input from automatic test equipment.
This is a test signal. When the USB Core is
powered up (not in Susned Mode), an automatic
tester can use this to disable phy_clock and
free_clk, then re-eanable them with an aligned
phase.
'1': The phy_clk and free_clk outputs are
disabled. "0": The phy_clock and free_clk outputs
are available within a specific period after the
de-assertion. */
} cn30xx;
struct cvmx_usbnx_usbp_ctl_status_cn30xx cn31xx;
struct cvmx_usbnx_usbp_ctl_status_cn50xx
{
uint64_t txrisetune : 1; /**< HS Transmitter Rise/Fall Time Adjustment */
uint64_t txvreftune : 4; /**< HS DC Voltage Level Adjustment */
uint64_t txfslstune : 4; /**< FS/LS Source Impedence Adjustment */
uint64_t txhsxvtune : 2; /**< Transmitter High-Speed Crossover Adjustment */
uint64_t sqrxtune : 3; /**< Squelch Threshold Adjustment */
uint64_t compdistune : 3; /**< Disconnect Threshold Adjustment */
uint64_t otgtune : 3; /**< VBUS Valid Threshold Adjustment */
uint64_t otgdisable : 1; /**< OTG Block Disable */
uint64_t portreset : 1; /**< Per_Port Reset */
uint64_t drvvbus : 1; /**< Drive VBUS */
uint64_t lsbist : 1; /**< Low-Speed BIST Enable. */
uint64_t fsbist : 1; /**< Full-Speed BIST Enable. */
uint64_t hsbist : 1; /**< High-Speed BIST Enable. */
uint64_t bist_done : 1; /**< PHY Bist Done.
Asserted at the end of the PHY BIST sequence. */
uint64_t bist_err : 1; /**< PHY Bist Error.
Indicates an internal error was detected during
the BIST sequence. */
uint64_t tdata_out : 4; /**< PHY Test Data Out.
Presents either internaly generated signals or
test register contents, based upon the value of
test_data_out_sel. */
uint64_t reserved_31_31 : 1;
uint64_t txpreemphasistune : 1; /**< HS Transmitter Pre-Emphasis Enable */
uint64_t dma_bmode : 1; /**< When set to 1 the L2C DMA address will be updated
with byte-counts between packets. When set to 0
the L2C DMA address is incremented to the next
4-byte aligned address after adding byte-count. */
uint64_t usbc_end : 1; /**< Bigendian input to the USB Core. This should be
set to '0' for operation. */
uint64_t usbp_bist : 1; /**< PHY, This is cleared '0' to run BIST on the USBP. */
uint64_t tclk : 1; /**< PHY Test Clock, used to load TDATA_IN to the USBP. */
uint64_t dp_pulld : 1; /**< PHY DP_PULLDOWN input to the USB-PHY.
This signal enables the pull-down resistance on
the D+ line. '1' pull down-resistance is connected
to D+/ '0' pull down resistance is not connected
to D+. When an A/B device is acting as a host
(downstream-facing port), dp_pulldown and
dm_pulldown are enabled. This must not toggle
during normal opeartion. */
uint64_t dm_pulld : 1; /**< PHY DM_PULLDOWN input to the USB-PHY.
This signal enables the pull-down resistance on
the D- line. '1' pull down-resistance is connected
to D-. '0' pull down resistance is not connected
to D-. When an A/B device is acting as a host
(downstream-facing port), dp_pulldown and
dm_pulldown are enabled. This must not toggle
during normal opeartion. */
uint64_t hst_mode : 1; /**< When '0' the USB is acting as HOST, when '1'
USB is acting as device. This field needs to be
set while the USB is in reset. */
uint64_t reserved_19_22 : 4;
uint64_t tx_bs_enh : 1; /**< Transmit Bit Stuffing on [15:8].
Enables or disables bit stuffing on data[15:8]
when bit-stuffing is enabled. */
uint64_t tx_bs_en : 1; /**< Transmit Bit Stuffing on [7:0].
Enables or disables bit stuffing on data[7:0]
when bit-stuffing is enabled. */
uint64_t loop_enb : 1; /**< PHY Loopback Test Enable.
'1': During data transmission the receive is
enabled.
'0': During data transmission the receive is
disabled.
Must be '0' for normal operation. */
uint64_t vtest_enb : 1; /**< Analog Test Pin Enable.
'1' The PHY's analog_test pin is enabled for the
input and output of applicable analog test signals.
'0' THe analog_test pin is disabled. */
uint64_t bist_enb : 1; /**< Built-In Self Test Enable.
Used to activate BIST in the PHY. */
uint64_t tdata_sel : 1; /**< Test Data Out Select.
'1' test_data_out[3:0] (PHY) register contents
are output. '0' internaly generated signals are
output. */
uint64_t taddr_in : 4; /**< Mode Address for Test Interface.
Specifies the register address for writing to or
reading from the PHY test interface register. */
uint64_t tdata_in : 8; /**< Internal Testing Register Input Data and Select
This is a test bus. Data is present on [3:0],
and its corresponding select (enable) is present
on bits [7:4]. */
uint64_t ate_reset : 1; /**< Reset input from automatic test equipment.
This is a test signal. When the USB Core is
powered up (not in Susned Mode), an automatic
tester can use this to disable phy_clock and
free_clk, then re-eanable them with an aligned
phase.
'1': The phy_clk and free_clk outputs are
disabled. "0": The phy_clock and free_clk outputs
are available within a specific period after the
de-assertion. */
} cn50xx;
struct cvmx_usbnx_usbp_ctl_status_cn52xx
{
uint64_t txrisetune : 1; /**< HS Transmitter Rise/Fall Time Adjustment */
uint64_t txvreftune : 4; /**< HS DC Voltage Level Adjustment */
uint64_t txfslstune : 4; /**< FS/LS Source Impedence Adjustment */
uint64_t txhsxvtune : 2; /**< Transmitter High-Speed Crossover Adjustment */
uint64_t sqrxtune : 3; /**< Squelch Threshold Adjustment */
uint64_t compdistune : 3; /**< Disconnect Threshold Adjustment */
uint64_t otgtune : 3; /**< VBUS Valid Threshold Adjustment */
uint64_t otgdisable : 1; /**< OTG Block Disable */
uint64_t portreset : 1; /**< Per_Port Reset */
uint64_t drvvbus : 1; /**< Drive VBUS */
uint64_t lsbist : 1; /**< Low-Speed BIST Enable. */
uint64_t fsbist : 1; /**< Full-Speed BIST Enable. */
uint64_t hsbist : 1; /**< High-Speed BIST Enable. */
uint64_t bist_done : 1; /**< PHY Bist Done.
Asserted at the end of the PHY BIST sequence. */
uint64_t bist_err : 1; /**< PHY Bist Error.
Indicates an internal error was detected during
the BIST sequence. */
uint64_t tdata_out : 4; /**< PHY Test Data Out.
Presents either internaly generated signals or
test register contents, based upon the value of
test_data_out_sel. */
uint64_t siddq : 1; /**< Drives the USBP (USB-PHY) SIDDQ input.
Normally should be set to zero.
When customers have no intent to use USB PHY
interface, they should:
- still provide 3.3V to USB_VDD33, and
- tie USB_REXT to 3.3V supply, and
- set USBN*_USBP_CTL_STATUS[SIDDQ]=1 */
uint64_t txpreemphasistune : 1; /**< HS Transmitter Pre-Emphasis Enable */
uint64_t dma_bmode : 1; /**< When set to 1 the L2C DMA address will be updated
with byte-counts between packets. When set to 0
the L2C DMA address is incremented to the next
4-byte aligned address after adding byte-count. */
uint64_t usbc_end : 1; /**< Bigendian input to the USB Core. This should be
set to '0' for operation. */
uint64_t usbp_bist : 1; /**< PHY, This is cleared '0' to run BIST on the USBP. */
uint64_t tclk : 1; /**< PHY Test Clock, used to load TDATA_IN to the USBP. */
uint64_t dp_pulld : 1; /**< PHY DP_PULLDOWN input to the USB-PHY.
This signal enables the pull-down resistance on
the D+ line. '1' pull down-resistance is connected
to D+/ '0' pull down resistance is not connected
to D+. When an A/B device is acting as a host
(downstream-facing port), dp_pulldown and
dm_pulldown are enabled. This must not toggle
during normal opeartion. */
uint64_t dm_pulld : 1; /**< PHY DM_PULLDOWN input to the USB-PHY.
This signal enables the pull-down resistance on
the D- line. '1' pull down-resistance is connected
to D-. '0' pull down resistance is not connected
to D-. When an A/B device is acting as a host
(downstream-facing port), dp_pulldown and
dm_pulldown are enabled. This must not toggle
during normal opeartion. */
uint64_t hst_mode : 1; /**< When '0' the USB is acting as HOST, when '1'
USB is acting as device. This field needs to be
set while the USB is in reset. */
uint64_t reserved_19_22 : 4;
uint64_t tx_bs_enh : 1; /**< Transmit Bit Stuffing on [15:8].
Enables or disables bit stuffing on data[15:8]
when bit-stuffing is enabled. */
uint64_t tx_bs_en : 1; /**< Transmit Bit Stuffing on [7:0].
Enables or disables bit stuffing on data[7:0]
when bit-stuffing is enabled. */
uint64_t loop_enb : 1; /**< PHY Loopback Test Enable.
'1': During data transmission the receive is
enabled.
'0': During data transmission the receive is
disabled.
Must be '0' for normal operation. */
uint64_t vtest_enb : 1; /**< Analog Test Pin Enable.
'1' The PHY's analog_test pin is enabled for the
input and output of applicable analog test signals.
'0' THe analog_test pin is disabled. */
uint64_t bist_enb : 1; /**< Built-In Self Test Enable.
Used to activate BIST in the PHY. */
uint64_t tdata_sel : 1; /**< Test Data Out Select.
'1' test_data_out[3:0] (PHY) register contents
are output. '0' internaly generated signals are
output. */
uint64_t taddr_in : 4; /**< Mode Address for Test Interface.
Specifies the register address for writing to or
reading from the PHY test interface register. */
uint64_t tdata_in : 8; /**< Internal Testing Register Input Data and Select
This is a test bus. Data is present on [3:0],
and its corresponding select (enable) is present
on bits [7:4]. */
uint64_t ate_reset : 1; /**< Reset input from automatic test equipment.
This is a test signal. When the USB Core is
powered up (not in Susned Mode), an automatic
tester can use this to disable phy_clock and
free_clk, then re-eanable them with an aligned
phase.
'1': The phy_clk and free_clk outputs are
disabled. "0": The phy_clock and free_clk outputs
are available within a specific period after the
de-assertion. */
} cn52xx;
struct cvmx_usbnx_usbp_ctl_status_cn50xx cn52xxp1;
struct cvmx_usbnx_usbp_ctl_status_cn52xx cn56xx;
struct cvmx_usbnx_usbp_ctl_status_cn50xx cn56xxp1;
};
typedef union cvmx_usbnx_usbp_ctl_status cvmx_usbnx_usbp_ctl_status_t;
#endif
/*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file "COPYING" in the main directory of this archive
* for more details.
*
* Copyright (C) 2008 Cavium Networks
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/pci.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <linux/platform_device.h>
#include <linux/usb.h>
#include <asm/time.h>
#include <asm/delay.h>
#include <asm/octeon/cvmx.h>
#include "cvmx-usb.h"
#include <asm/octeon/cvmx-iob-defs.h>
#include <linux/usb/hcd.h>
//#define DEBUG_CALL(format, ...) printk(format, ##__VA_ARGS__)
#define DEBUG_CALL(format, ...) do {} while (0)
//#define DEBUG_SUBMIT(format, ...) printk(format, ##__VA_ARGS__)
#define DEBUG_SUBMIT(format, ...) do {} while (0)
//#define DEBUG_ROOT_HUB(format, ...) printk(format, ##__VA_ARGS__)
#define DEBUG_ROOT_HUB(format, ...) do {} while (0)
//#define DEBUG_ERROR(format, ...) printk(format, ##__VA_ARGS__)
#define DEBUG_ERROR(format, ...) do {} while (0)
#define DEBUG_FATAL(format, ...) printk(format, ##__VA_ARGS__)
struct octeon_hcd {
spinlock_t lock;
cvmx_usb_state_t usb;
struct tasklet_struct dequeue_tasklet;
struct list_head dequeue_list;
};
/* convert between an HCD pointer and the corresponding struct octeon_hcd */
static inline struct octeon_hcd *hcd_to_octeon(struct usb_hcd *hcd)
{
return (struct octeon_hcd *)(hcd->hcd_priv);
}
static inline struct usb_hcd *octeon_to_hcd(struct octeon_hcd *p)
{
return container_of((void *)p, struct usb_hcd, hcd_priv);
}
static inline struct octeon_hcd *cvmx_usb_to_octeon(cvmx_usb_state_t *p)
{
return container_of(p, struct octeon_hcd, usb);
}
static irqreturn_t octeon_usb_irq(struct usb_hcd *hcd)
{
struct octeon_hcd *priv = hcd_to_octeon(hcd);
unsigned long flags;
DEBUG_CALL("OcteonUSB: %s called\n", __FUNCTION__);
spin_lock_irqsave(&priv->lock, flags);
cvmx_usb_poll(&priv->usb);
spin_unlock_irqrestore(&priv->lock, flags);
return IRQ_HANDLED;
}
static void octeon_usb_port_callback(cvmx_usb_state_t *usb,
cvmx_usb_callback_t reason,
cvmx_usb_complete_t status,
int pipe_handle,
int submit_handle,
int bytes_transferred,
void *user_data)
{
struct octeon_hcd *priv = cvmx_usb_to_octeon(usb);
DEBUG_CALL("OcteonUSB: %s called\n", __FUNCTION__);
spin_unlock(&priv->lock);
usb_hcd_poll_rh_status(octeon_to_hcd(priv));
spin_lock(&priv->lock);
}
static int octeon_usb_start(struct usb_hcd *hcd)
{
struct octeon_hcd *priv = hcd_to_octeon(hcd);
unsigned long flags;
DEBUG_CALL("OcteonUSB: %s called\n", __FUNCTION__);
hcd->state = HC_STATE_RUNNING;
spin_lock_irqsave(&priv->lock, flags);
cvmx_usb_register_callback(&priv->usb, CVMX_USB_CALLBACK_PORT_CHANGED,
octeon_usb_port_callback, NULL);
spin_unlock_irqrestore(&priv->lock, flags);
return 0;
}
static void octeon_usb_stop(struct usb_hcd *hcd)
{
struct octeon_hcd *priv = hcd_to_octeon(hcd);
unsigned long flags;
DEBUG_CALL("OcteonUSB: %s called\n", __FUNCTION__);
spin_lock_irqsave(&priv->lock, flags);
cvmx_usb_register_callback(&priv->usb, CVMX_USB_CALLBACK_PORT_CHANGED,
NULL, NULL);
spin_unlock_irqrestore(&priv->lock, flags);
hcd->state = HC_STATE_HALT;
}
static int octeon_usb_get_frame_number(struct usb_hcd *hcd)
{
struct octeon_hcd *priv = hcd_to_octeon(hcd);
DEBUG_CALL("OcteonUSB: %s called\n", __FUNCTION__);
return cvmx_usb_get_frame_number(&priv->usb);
}
static void octeon_usb_urb_complete_callback(cvmx_usb_state_t *usb,
cvmx_usb_callback_t reason,
cvmx_usb_complete_t status,
int pipe_handle,
int submit_handle,
int bytes_transferred,
void *user_data)
{
struct octeon_hcd *priv = cvmx_usb_to_octeon(usb);
struct urb *urb = user_data;
DEBUG_CALL("OcteonUSB: %s called\n", __FUNCTION__);
urb->actual_length = bytes_transferred;
urb->hcpriv = NULL;
if (!list_empty(&urb->urb_list)) {
/*
* It is on the dequeue_list, but we are going to call
* usb_hcd_giveback_urb(), so we must clear it from
* the list. We got to it before the
* octeon_usb_urb_dequeue_work() tasklet did.
*/
list_del(&urb->urb_list);
/* No longer on the dequeue_list. */
INIT_LIST_HEAD(&urb->urb_list);
}
/* For Isochronous transactions we need to update the URB packet status
list from data in our private copy */
if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS)
{
int i;
/* The pointer to the private list is stored in the setup_packet field */
cvmx_usb_iso_packet_t *iso_packet = (cvmx_usb_iso_packet_t *)urb->setup_packet;
/* Recalculate the transfer size by adding up each packet */
urb->actual_length = 0;
for (i=0; i<urb->number_of_packets; i++)
{
if (iso_packet[i].status == CVMX_USB_COMPLETE_SUCCESS)
{
urb->iso_frame_desc[i].status = 0;
urb->iso_frame_desc[i].actual_length = iso_packet[i].length;
urb->actual_length += urb->iso_frame_desc[i].actual_length;
}
else
{
DEBUG_ERROR("%s: ISOCHRONOUS packet=%d of %d status=%d pipe=%d submit=%d size=%d\n",
__FUNCTION__, i, urb->number_of_packets,
iso_packet[i].status, pipe_handle,
submit_handle, iso_packet[i].length);
urb->iso_frame_desc[i].status = -EREMOTEIO;
}
}
/* Free the private list now that we don't need it anymore */
kfree(iso_packet);
urb->setup_packet = NULL;
}
switch (status)
{
case CVMX_USB_COMPLETE_SUCCESS:
urb->status = 0;
break;
case CVMX_USB_COMPLETE_CANCEL:
if (urb->status == 0)
urb->status = -ENOENT;
break;
case CVMX_USB_COMPLETE_STALL:
DEBUG_ERROR("%s: status=stall pipe=%d submit=%d size=%d\n", __FUNCTION__, pipe_handle, submit_handle, bytes_transferred);
urb->status = -EPIPE;
break;
case CVMX_USB_COMPLETE_BABBLEERR:
DEBUG_ERROR("%s: status=babble pipe=%d submit=%d size=%d\n", __FUNCTION__, pipe_handle, submit_handle, bytes_transferred);
urb->status = -EPIPE;
break;
case CVMX_USB_COMPLETE_SHORT:
DEBUG_ERROR("%s: status=short pipe=%d submit=%d size=%d\n", __FUNCTION__, pipe_handle, submit_handle, bytes_transferred);
urb->status = -EREMOTEIO;
break;
case CVMX_USB_COMPLETE_ERROR:
case CVMX_USB_COMPLETE_XACTERR:
case CVMX_USB_COMPLETE_DATATGLERR:
case CVMX_USB_COMPLETE_FRAMEERR:
DEBUG_ERROR("%s: status=%d pipe=%d submit=%d size=%d\n", __FUNCTION__, status, pipe_handle, submit_handle, bytes_transferred);
urb->status = -EPROTO;
break;
}
spin_unlock(&priv->lock);
usb_hcd_giveback_urb(octeon_to_hcd(priv), urb, urb->status);
spin_lock(&priv->lock);
}
static int octeon_usb_urb_enqueue(struct usb_hcd *hcd,
struct urb *urb,
gfp_t mem_flags)
{
struct octeon_hcd *priv = hcd_to_octeon(hcd);
int submit_handle = -1;
int pipe_handle;
unsigned long flags;
cvmx_usb_iso_packet_t *iso_packet;
struct usb_host_endpoint *ep = urb->ep;
DEBUG_CALL("OcteonUSB: %s called\n", __FUNCTION__);
urb->status = 0;
INIT_LIST_HEAD(&urb->urb_list); /* not enqueued on dequeue_list */
spin_lock_irqsave(&priv->lock, flags);
if (!ep->hcpriv)
{
cvmx_usb_transfer_t transfer_type;
cvmx_usb_speed_t speed;
int split_device = 0;
int split_port = 0;
switch (usb_pipetype(urb->pipe))
{
case PIPE_ISOCHRONOUS:
transfer_type = CVMX_USB_TRANSFER_ISOCHRONOUS;
break;
case PIPE_INTERRUPT:
transfer_type = CVMX_USB_TRANSFER_INTERRUPT;
break;
case PIPE_CONTROL:
transfer_type = CVMX_USB_TRANSFER_CONTROL;
break;
default:
transfer_type = CVMX_USB_TRANSFER_BULK;
break;
}
switch (urb->dev->speed)
{
case USB_SPEED_LOW:
speed = CVMX_USB_SPEED_LOW;
break;
case USB_SPEED_FULL:
speed = CVMX_USB_SPEED_FULL;
break;
default:
speed = CVMX_USB_SPEED_HIGH;
break;
}
/* For slow devices on high speed ports we need to find the hub that
does the speed translation so we know where to send the split
transactions */
if (speed != CVMX_USB_SPEED_HIGH)
{
/* Start at this device and work our way up the usb tree */
struct usb_device *dev = urb->dev;
while (dev->parent)
{
/* If our parent is high speed then he'll receive the splits */
if (dev->parent->speed == USB_SPEED_HIGH)
{
split_device = dev->parent->devnum;
split_port = dev->portnum;
break;
}
/* Move up the tree one level. If we make it all the way up the
tree, then the port must not be in high speed mode and we
don't need a split */
dev = dev->parent;
}
}
pipe_handle = cvmx_usb_open_pipe(&priv->usb,
0,
usb_pipedevice(urb->pipe),
usb_pipeendpoint(urb->pipe),
speed,
le16_to_cpu(ep->desc.wMaxPacketSize) & 0x7ff,
transfer_type,
usb_pipein(urb->pipe) ? CVMX_USB_DIRECTION_IN : CVMX_USB_DIRECTION_OUT,
urb->interval,
(le16_to_cpu(ep->desc.wMaxPacketSize)>>11) & 0x3,
split_device,
split_port);
if (pipe_handle < 0)
{
spin_unlock_irqrestore(&priv->lock, flags);
DEBUG_ERROR("OcteonUSB: %s failed to create pipe\n", __FUNCTION__);
return -ENOMEM;
}
ep->hcpriv = (void*)(0x10000L + pipe_handle);
}
else
pipe_handle = 0xffff & (long)ep->hcpriv;
switch (usb_pipetype(urb->pipe))
{
case PIPE_ISOCHRONOUS:
DEBUG_SUBMIT("OcteonUSB: %s submit isochronous to %d.%d\n", __FUNCTION__, usb_pipedevice(urb->pipe), usb_pipeendpoint(urb->pipe));
/* Allocate a structure to use for our private list of isochronous
packets */
iso_packet = kmalloc(urb->number_of_packets * sizeof(cvmx_usb_iso_packet_t), GFP_ATOMIC);
if (iso_packet)
{
int i;
/* Fill the list with the data from the URB */
for (i=0; i<urb->number_of_packets; i++)
{
iso_packet[i].offset = urb->iso_frame_desc[i].offset;
iso_packet[i].length = urb->iso_frame_desc[i].length;
iso_packet[i].status = CVMX_USB_COMPLETE_ERROR;
}
/* Store a pointer to the list in uthe URB setup_pakcet field.
We know this currently isn't being used and this saves us
a bunch of logic */
urb->setup_packet = (char*)iso_packet;
submit_handle = cvmx_usb_submit_isochronous(&priv->usb, pipe_handle,
urb->start_frame,
0 /* flags */,
urb->number_of_packets,
iso_packet,
urb->transfer_dma,
urb->transfer_buffer_length,
octeon_usb_urb_complete_callback,
urb);
/* If submit failed we need to free our private packet list */
if (submit_handle < 0)
{
urb->setup_packet = NULL;
kfree(iso_packet);
}
}
break;
case PIPE_INTERRUPT:
DEBUG_SUBMIT("OcteonUSB: %s submit interrupt to %d.%d\n", __FUNCTION__, usb_pipedevice(urb->pipe), usb_pipeendpoint(urb->pipe));
submit_handle = cvmx_usb_submit_interrupt(&priv->usb, pipe_handle,
urb->transfer_dma,
urb->transfer_buffer_length,
octeon_usb_urb_complete_callback,
urb);
break;
case PIPE_CONTROL:
DEBUG_SUBMIT("OcteonUSB: %s submit control to %d.%d\n", __FUNCTION__, usb_pipedevice(urb->pipe), usb_pipeendpoint(urb->pipe));
submit_handle = cvmx_usb_submit_control(&priv->usb, pipe_handle,
urb->setup_dma,
urb->transfer_dma,
urb->transfer_buffer_length,
octeon_usb_urb_complete_callback,
urb);
break;
case PIPE_BULK:
DEBUG_SUBMIT("OcteonUSB: %s submit bulk to %d.%d\n", __FUNCTION__, usb_pipedevice(urb->pipe), usb_pipeendpoint(urb->pipe));
submit_handle = cvmx_usb_submit_bulk(&priv->usb, pipe_handle,
urb->transfer_dma,
urb->transfer_buffer_length,
octeon_usb_urb_complete_callback,
urb);
break;
}
if (submit_handle < 0)
{
spin_unlock_irqrestore(&priv->lock, flags);
DEBUG_ERROR("OcteonUSB: %s failed to submit\n", __FUNCTION__);
return -ENOMEM;
}
urb->hcpriv = (void*)(long)(((submit_handle & 0xffff) << 16) | pipe_handle);
spin_unlock_irqrestore(&priv->lock, flags);
return 0;
}
static void octeon_usb_urb_dequeue_work(unsigned long arg)
{
unsigned long flags;
struct octeon_hcd *priv = (struct octeon_hcd *)arg;
spin_lock_irqsave(&priv->lock, flags);
while (!list_empty(&priv->dequeue_list)) {
int pipe_handle;
int submit_handle;
struct urb *urb = container_of(priv->dequeue_list.next, struct urb, urb_list);
list_del(&urb->urb_list);
/* not enqueued on dequeue_list */
INIT_LIST_HEAD(&urb->urb_list);
pipe_handle = 0xffff & (long)urb->hcpriv;
submit_handle = ((long)urb->hcpriv) >> 16;
cvmx_usb_cancel(&priv->usb, pipe_handle, submit_handle);
}
spin_unlock_irqrestore(&priv->lock, flags);
}
static int octeon_usb_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
{
struct octeon_hcd *priv = hcd_to_octeon(hcd);
unsigned long flags;
DEBUG_CALL("OcteonUSB: %s called\n", __FUNCTION__);
if (!urb->dev)
return -EINVAL;
spin_lock_irqsave(&priv->lock, flags);
urb->status = status;
list_add_tail(&urb->urb_list, &priv->dequeue_list);
spin_unlock_irqrestore(&priv->lock, flags);
tasklet_schedule(&priv->dequeue_tasklet);
return 0;
}
static void octeon_usb_endpoint_disable(struct usb_hcd *hcd, struct usb_host_endpoint *ep)
{
DEBUG_CALL("OcteonUSB: %s called\n", __FUNCTION__);
if (ep->hcpriv)
{
struct octeon_hcd *priv = hcd_to_octeon(hcd);
int pipe_handle = 0xffff & (long)ep->hcpriv;
unsigned long flags;
spin_lock_irqsave(&priv->lock, flags);
cvmx_usb_cancel_all(&priv->usb, pipe_handle);
if (cvmx_usb_close_pipe(&priv->usb, pipe_handle))
DEBUG_ERROR("OcteonUSB: Closing pipe %d failed\n", pipe_handle);
spin_unlock_irqrestore(&priv->lock, flags);
ep->hcpriv = NULL;
}
}
static int octeon_usb_hub_status_data(struct usb_hcd *hcd, char *buf)
{
struct octeon_hcd *priv = hcd_to_octeon(hcd);
cvmx_usb_port_status_t port_status;
unsigned long flags;
DEBUG_CALL("OcteonUSB: %s called\n", __FUNCTION__);
spin_lock_irqsave(&priv->lock, flags);
port_status = cvmx_usb_get_status(&priv->usb);
spin_unlock_irqrestore(&priv->lock, flags);
buf[0] = 0;
buf[0] = port_status.connect_change << 1;
return(buf[0] != 0);
}
static int octeon_usb_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, u16 wIndex, char *buf, u16 wLength)
{
struct octeon_hcd *priv = hcd_to_octeon(hcd);
cvmx_usb_port_status_t usb_port_status;
int port_status;
struct usb_hub_descriptor *desc;
unsigned long flags;
switch (typeReq)
{
case ClearHubFeature:
DEBUG_ROOT_HUB("OcteonUSB: ClearHubFeature\n");
switch (wValue)
{
case C_HUB_LOCAL_POWER:
case C_HUB_OVER_CURRENT:
/* Nothing required here */
break;
default:
return -EINVAL;
}
break;
case ClearPortFeature:
DEBUG_ROOT_HUB("OcteonUSB: ClearPortFeature");
if (wIndex != 1)
{
DEBUG_ROOT_HUB(" INVALID\n");
return -EINVAL;
}
switch (wValue)
{
case USB_PORT_FEAT_ENABLE:
DEBUG_ROOT_HUB(" ENABLE");
spin_lock_irqsave(&priv->lock, flags);
cvmx_usb_disable(&priv->usb);
spin_unlock_irqrestore(&priv->lock, flags);
break;
case USB_PORT_FEAT_SUSPEND:
DEBUG_ROOT_HUB(" SUSPEND");
/* Not supported on Octeon */
break;
case USB_PORT_FEAT_POWER:
DEBUG_ROOT_HUB(" POWER");
/* Not supported on Octeon */
break;
case USB_PORT_FEAT_INDICATOR:
DEBUG_ROOT_HUB(" INDICATOR");
/* Port inidicator not supported */
break;
case USB_PORT_FEAT_C_CONNECTION:
DEBUG_ROOT_HUB(" C_CONNECTION");
/* Clears drivers internal connect status change flag */
spin_lock_irqsave(&priv->lock, flags);
cvmx_usb_set_status(&priv->usb, cvmx_usb_get_status(&priv->usb));
spin_unlock_irqrestore(&priv->lock, flags);
break;
case USB_PORT_FEAT_C_RESET:
DEBUG_ROOT_HUB(" C_RESET");
/* Clears the driver's internal Port Reset Change flag */
spin_lock_irqsave(&priv->lock, flags);
cvmx_usb_set_status(&priv->usb, cvmx_usb_get_status(&priv->usb));
spin_unlock_irqrestore(&priv->lock, flags);
break;
case USB_PORT_FEAT_C_ENABLE:
DEBUG_ROOT_HUB(" C_ENABLE");
/* Clears the driver's internal Port Enable/Disable Change flag */
spin_lock_irqsave(&priv->lock, flags);
cvmx_usb_set_status(&priv->usb, cvmx_usb_get_status(&priv->usb));
spin_unlock_irqrestore(&priv->lock, flags);
break;
case USB_PORT_FEAT_C_SUSPEND:
DEBUG_ROOT_HUB(" C_SUSPEND");
/* Clears the driver's internal Port Suspend Change flag,
which is set when resume signaling on the host port is
complete */
break;
case USB_PORT_FEAT_C_OVER_CURRENT:
DEBUG_ROOT_HUB(" C_OVER_CURRENT");
/* Clears the driver's overcurrent Change flag */
spin_lock_irqsave(&priv->lock, flags);
cvmx_usb_set_status(&priv->usb, cvmx_usb_get_status(&priv->usb));
spin_unlock_irqrestore(&priv->lock, flags);
break;
default:
DEBUG_ROOT_HUB(" UNKNOWN\n");
return -EINVAL;
}
DEBUG_ROOT_HUB("\n");
break;
case GetHubDescriptor:
DEBUG_ROOT_HUB("OcteonUSB: GetHubDescriptor\n");
desc = (struct usb_hub_descriptor *)buf;
desc->bDescLength = 9;
desc->bDescriptorType = 0x29;
desc->bNbrPorts = 1;
desc->wHubCharacteristics = 0x08;
desc->bPwrOn2PwrGood = 1;
desc->bHubContrCurrent = 0;
desc->u.hs.DeviceRemovable[0] = 0;
desc->u.hs.DeviceRemovable[1] = 0xff;
break;
case GetHubStatus:
DEBUG_ROOT_HUB("OcteonUSB: GetHubStatus\n");
*(__le32 *)buf = 0;
break;
case GetPortStatus:
DEBUG_ROOT_HUB("OcteonUSB: GetPortStatus");
if (wIndex != 1)
{
DEBUG_ROOT_HUB(" INVALID\n");
return -EINVAL;
}
spin_lock_irqsave(&priv->lock, flags);
usb_port_status = cvmx_usb_get_status(&priv->usb);
spin_unlock_irqrestore(&priv->lock, flags);
port_status = 0;
if (usb_port_status.connect_change)
{
port_status |= (1 << USB_PORT_FEAT_C_CONNECTION);
DEBUG_ROOT_HUB(" C_CONNECTION");
}
if (usb_port_status.port_enabled)
{
port_status |= (1 << USB_PORT_FEAT_C_ENABLE);
DEBUG_ROOT_HUB(" C_ENABLE");
}
if (usb_port_status.connected)
{
port_status |= (1 << USB_PORT_FEAT_CONNECTION);
DEBUG_ROOT_HUB(" CONNECTION");
}
if (usb_port_status.port_enabled)
{
port_status |= (1 << USB_PORT_FEAT_ENABLE);
DEBUG_ROOT_HUB(" ENABLE");
}
if (usb_port_status.port_over_current)
{
port_status |= (1 << USB_PORT_FEAT_OVER_CURRENT);
DEBUG_ROOT_HUB(" OVER_CURRENT");
}
if (usb_port_status.port_powered)
{
port_status |= (1 << USB_PORT_FEAT_POWER);
DEBUG_ROOT_HUB(" POWER");
}
if (usb_port_status.port_speed == CVMX_USB_SPEED_HIGH)
{
port_status |= USB_PORT_STAT_HIGH_SPEED;
DEBUG_ROOT_HUB(" HIGHSPEED");
}
else if (usb_port_status.port_speed == CVMX_USB_SPEED_LOW)
{
port_status |= (1 << USB_PORT_FEAT_LOWSPEED);
DEBUG_ROOT_HUB(" LOWSPEED");
}
*((__le32 *)buf) = cpu_to_le32(port_status);
DEBUG_ROOT_HUB("\n");
break;
case SetHubFeature:
DEBUG_ROOT_HUB("OcteonUSB: SetHubFeature\n");
/* No HUB features supported */
break;
case SetPortFeature:
DEBUG_ROOT_HUB("OcteonUSB: SetPortFeature");
if (wIndex != 1)
{
DEBUG_ROOT_HUB(" INVALID\n");
return -EINVAL;
}
switch (wValue)
{
case USB_PORT_FEAT_SUSPEND:
DEBUG_ROOT_HUB(" SUSPEND\n");
return -EINVAL;
case USB_PORT_FEAT_POWER:
DEBUG_ROOT_HUB(" POWER\n");
return -EINVAL;
case USB_PORT_FEAT_RESET:
DEBUG_ROOT_HUB(" RESET\n");
spin_lock_irqsave(&priv->lock, flags);
cvmx_usb_disable(&priv->usb);
if (cvmx_usb_enable(&priv->usb))
DEBUG_ERROR("Failed to enable the port\n");
spin_unlock_irqrestore(&priv->lock, flags);
return 0;
case USB_PORT_FEAT_INDICATOR:
DEBUG_ROOT_HUB(" INDICATOR\n");
/* Not supported */
break;
default:
DEBUG_ROOT_HUB(" UNKNOWN\n");
return -EINVAL;
}
break;
default:
DEBUG_ROOT_HUB("OcteonUSB: Unknown root hub request\n");
return -EINVAL;
}
return 0;
}
static const struct hc_driver octeon_hc_driver = {
.description = "Octeon USB",
.product_desc = "Octeon Host Controller",
.hcd_priv_size = sizeof(struct octeon_hcd),
.irq = octeon_usb_irq,
.flags = HCD_MEMORY | HCD_USB2,
.start = octeon_usb_start,
.stop = octeon_usb_stop,
.urb_enqueue = octeon_usb_urb_enqueue,
.urb_dequeue = octeon_usb_urb_dequeue,
.endpoint_disable = octeon_usb_endpoint_disable,
.get_frame_number = octeon_usb_get_frame_number,
.hub_status_data = octeon_usb_hub_status_data,
.hub_control = octeon_usb_hub_control,
};
static int octeon_usb_driver_probe(struct device *dev)
{
int status;
int usb_num = to_platform_device(dev)->id;
int irq = platform_get_irq(to_platform_device(dev), 0);
struct octeon_hcd *priv;
struct usb_hcd *hcd;
unsigned long flags;
DEBUG_CALL("OcteonUSB: %s called\n", __FUNCTION__);
/* Set the DMA mask to 64bits so we get buffers already translated for
DMA */
dev->coherent_dma_mask = ~0;
dev->dma_mask = &dev->coherent_dma_mask;
hcd = usb_create_hcd(&octeon_hc_driver, dev, dev_name(dev));
if (!hcd)
{
DEBUG_FATAL("OcteonUSB: Failed to allocate memory for HCD\n");
return -1;
}
hcd->uses_new_polling = 1;
priv = (struct octeon_hcd *)hcd->hcd_priv;
spin_lock_init(&priv->lock);
tasklet_init(&priv->dequeue_tasklet, octeon_usb_urb_dequeue_work, (unsigned long)priv);
INIT_LIST_HEAD(&priv->dequeue_list);
//status = cvmx_usb_initialize(&priv->usb, usb_num, CVMX_USB_INITIALIZE_FLAGS_CLOCK_AUTO | CVMX_USB_INITIALIZE_FLAGS_DEBUG_INFO | CVMX_USB_INITIALIZE_FLAGS_DEBUG_TRANSFERS | CVMX_USB_INITIALIZE_FLAGS_DEBUG_CALLBACKS);
status = cvmx_usb_initialize(&priv->usb, usb_num, CVMX_USB_INITIALIZE_FLAGS_CLOCK_AUTO);
if (status)
{
DEBUG_FATAL("OcteonUSB: USB initialization failed with %d\n", status);
kfree(hcd);
return -1;
}
/* This delay is needed for CN3010, but I don't know why... */
mdelay(10);
spin_lock_irqsave(&priv->lock, flags);
cvmx_usb_poll(&priv->usb);
spin_unlock_irqrestore(&priv->lock, flags);
status = usb_add_hcd(hcd, irq, IRQF_SHARED);
if (status)
{
DEBUG_FATAL("OcteonUSB: USB add HCD failed with %d\n", status);
kfree(hcd);
return -1;
}
printk("OcteonUSB: Registered HCD for port %d on irq %d\n", usb_num, irq);
return 0;
}
static int octeon_usb_driver_remove(struct device *dev)
{
int status;
struct usb_hcd *hcd = dev_get_drvdata(dev);
struct octeon_hcd *priv = hcd_to_octeon(hcd);
unsigned long flags;
DEBUG_CALL("OcteonUSB: %s called\n", __FUNCTION__);
usb_remove_hcd(hcd);
tasklet_kill(&priv->dequeue_tasklet);
spin_lock_irqsave(&priv->lock, flags);
status = cvmx_usb_shutdown(&priv->usb);
spin_unlock_irqrestore(&priv->lock, flags);
if (status)
DEBUG_FATAL("OcteonUSB: USB shutdown failed with %d\n", status);
kfree(hcd);
return 0;
}
static struct device_driver octeon_usb_driver = {
.name = "OcteonUSB",
.bus = &platform_bus_type,
.probe = octeon_usb_driver_probe,
.remove = octeon_usb_driver_remove,
};
#define MAX_USB_PORTS 10
struct platform_device *pdev_glob[MAX_USB_PORTS];
static int octeon_usb_registered;
static int __init octeon_usb_module_init(void)
{
int num_devices = cvmx_usb_get_num_ports();
int device;
if (usb_disabled() || num_devices == 0)
return -ENODEV;
if (driver_register(&octeon_usb_driver))
{
DEBUG_FATAL("OcteonUSB: Failed to register driver\n");
return -ENOMEM;
}
octeon_usb_registered = 1;
printk("OcteonUSB: Detected %d ports\n", num_devices);
/*
* Only cn52XX and cn56XX have DWC_OTG USB hardware and the
* IOB priority registers. Under heavy network load USB
* hardware can be starved by the IOB causing a crash. Give
* it a priority boost if it has been waiting more than 400
* cycles to avoid this situation.
*
* Testing indicates that a cnt_val of 8192 is not sufficient,
* but no failures are seen with 4096. We choose a value of
* 400 to give a safety factor of 10.
*/
if (OCTEON_IS_MODEL(OCTEON_CN52XX) || OCTEON_IS_MODEL(OCTEON_CN56XX)) {
union cvmx_iob_n2c_l2c_pri_cnt pri_cnt;
pri_cnt.u64 = 0;
pri_cnt.s.cnt_enb = 1;
pri_cnt.s.cnt_val = 400;
cvmx_write_csr(CVMX_IOB_N2C_L2C_PRI_CNT, pri_cnt.u64);
}
for (device = 0; device < num_devices; device++)
{
struct resource irq_resource;
struct platform_device *pdev;
memset(&irq_resource, 0, sizeof(irq_resource));
irq_resource.start = (device==0) ? OCTEON_IRQ_USB0 : OCTEON_IRQ_USB1;
irq_resource.end = irq_resource.start;
irq_resource.flags = IORESOURCE_IRQ;
pdev = platform_device_register_simple((char*)octeon_usb_driver.name, device, &irq_resource, 1);
if (!pdev)
{
DEBUG_FATAL("OcteonUSB: Failed to allocate platform device for USB%d\n", device);
return -ENOMEM;
}
if (device < MAX_USB_PORTS)
pdev_glob[device] = pdev;
}
return 0;
}
static void __exit octeon_usb_module_cleanup(void)
{
int i;
DEBUG_CALL("OcteonUSB: %s called\n", __FUNCTION__);
for (i = 0; i <MAX_USB_PORTS; i++)
if (pdev_glob[i])
{
platform_device_unregister(pdev_glob[i]);
pdev_glob[i] = NULL;
}
if (octeon_usb_registered)
driver_unregister(&octeon_usb_driver);
}
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Cavium Networks <support@caviumnetworks.com>");
MODULE_DESCRIPTION("Cavium Networks Octeon USB Host driver.");
module_init(octeon_usb_module_init);
module_exit(octeon_usb_module_cleanup);
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册