dc4ee6ca91
make use of it where possible. This primarily brings in support for newer hardware, and FreeBSD is not yet able to support the abundance of IRQs on new hardware and many features in the Ethernet driver. Because of the changes to IRQs in the Simple Executive, we have to maintain our own list of Octeon IRQs now, which probably can be pared-down and be specific to the CIU interrupt unit soon, and when other interrupt mechanisms are added they can maintain their own definitions. Remove unmasking of interrupts from within the UART device now that the function used is no longer present in the Simple Executive. The unmasking seems to have been gratuitous as this is more properly handled by the buses above the UART device, and seems to work on that basis.
1042 lines
36 KiB
C
1042 lines
36 KiB
C
/***********************license start***************
|
||
* Copyright (c) 2003-2010 Cavium Inc. (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 Inc. 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 INC. 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-usbd.c" defines a set of low level USB functions to help
|
||
* developers create Octeon USB devices 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>
|
||
*/
|
||
|
||
#ifdef CVMX_BUILD_FOR_LINUX_KERNEL
|
||
#include <asm/octeon/cvmx.h>
|
||
#include <asm/octeon/cvmx-clock.h>
|
||
#include <asm/octeon/cvmx-sysinfo.h>
|
||
#include <asm/octeon/cvmx-usbnx-defs.h>
|
||
#include <asm/octeon/cvmx-usbcx-defs.h>
|
||
#include <asm/octeon/cvmx-usbd.h>
|
||
#include <asm/octeon/cvmx-swap.h>
|
||
#include <asm/octeon/cvmx-helper.h>
|
||
#include <asm/octeon/cvmx-helper-board.h>
|
||
#else
|
||
#include "cvmx.h"
|
||
#include "cvmx-clock.h"
|
||
#include "cvmx-sysinfo.h"
|
||
#include "cvmx-usbd.h"
|
||
#include "cvmx-swap.h"
|
||
#include "cvmx-helper.h"
|
||
#include "cvmx-helper-board.h"
|
||
#endif
|
||
|
||
#define ULL unsigned long long
|
||
|
||
/**
|
||
* @INTERNAL
|
||
* Read a USB 32bit CSR. It performs the necessary address swizzle for 32bit
|
||
* CSRs.
|
||
*
|
||
* @param usb USB device state populated by
|
||
* cvmx_usbd_initialize().
|
||
* @param address 64bit address to read
|
||
*
|
||
* @return Result of the read
|
||
*/
|
||
static inline uint32_t __cvmx_usbd_read_csr32(cvmx_usbd_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.
|
||
*
|
||
* @param usb USB device state populated by
|
||
* cvmx_usbd_initialize().
|
||
* @param address 64bit address to write
|
||
* @param value Value to write
|
||
*/
|
||
static inline void __cvmx_usbd_write_csr32(cvmx_usbd_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
|
||
* Calls the user supplied callback when an event happens.
|
||
*
|
||
* @param usb USB device state populated by
|
||
* cvmx_usbd_initialize().
|
||
* @param reason Reason for the callback
|
||
* @param endpoint_num
|
||
* Endpoint number
|
||
* @param bytes_transferred
|
||
* Bytes transferred
|
||
*/
|
||
static void __cvmx_usbd_callback(cvmx_usbd_state_t *usb, cvmx_usbd_callback_t reason, int endpoint_num, int bytes_transferred)
|
||
{
|
||
if (usb->callback[reason])
|
||
{
|
||
if (cvmx_unlikely(usb->init_flags & CVMX_USBD_INITIALIZE_FLAGS_DEBUG))
|
||
cvmx_dprintf("%s: Calling callback reason=%d endpoint=%d bytes=%d func=%p data=%p\n",
|
||
__FUNCTION__, reason, endpoint_num, bytes_transferred, usb->callback[reason], usb->callback_data[reason]);
|
||
usb->callback[reason](reason, endpoint_num, bytes_transferred, usb->callback_data[reason]);
|
||
}
|
||
else
|
||
{
|
||
if (cvmx_unlikely(usb->init_flags & CVMX_USBD_INITIALIZE_FLAGS_DEBUG))
|
||
cvmx_dprintf("%s: No callback for reason=%d endpoint=%d bytes=%d\n",
|
||
__FUNCTION__, reason, endpoint_num, bytes_transferred);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* @INTERNAL
|
||
* Perform USB device mode initialization after a reset completes.
|
||
* This should be called after USBC0/1_GINTSTS[USBRESET] and
|
||
* corresponds to section 22.6.1.1, "Initialization on USB Reset",
|
||
* in the manual.
|
||
*
|
||
* @param usb USB device state populated by
|
||
* cvmx_usbd_initialize().
|
||
*
|
||
* @return Zero or negative on error.
|
||
*/
|
||
static int __cvmx_usbd_device_reset_complete(cvmx_usbd_state_t *usb)
|
||
{
|
||
cvmx_usbcx_ghwcfg2_t usbcx_ghwcfg2;
|
||
cvmx_usbcx_ghwcfg3_t usbcx_ghwcfg3;
|
||
cvmx_usbcx_doepmsk_t usbcx_doepmsk;
|
||
cvmx_usbcx_diepmsk_t usbcx_diepmsk;
|
||
cvmx_usbcx_daintmsk_t usbc_daintmsk;
|
||
cvmx_usbcx_gnptxfsiz_t gnptxfsiz;
|
||
int fifo_space;
|
||
int i;
|
||
|
||
if (cvmx_unlikely(usb->init_flags & CVMX_USBD_INITIALIZE_FLAGS_DEBUG))
|
||
cvmx_dprintf("%s: Processing reset\n", __FUNCTION__);
|
||
|
||
usbcx_ghwcfg2.u32 = __cvmx_usbd_read_csr32(usb, CVMX_USBCX_GHWCFG2(usb->index));
|
||
usbcx_ghwcfg3.u32 = __cvmx_usbd_read_csr32(usb, CVMX_USBCX_GHWCFG3(usb->index));
|
||
|
||
/* Set up the data FIFO RAM for each of the FIFOs */
|
||
fifo_space = usbcx_ghwcfg3.s.dfifodepth;
|
||
|
||
/* Start at the top of the FIFO and assign space for each periodic fifo */
|
||
for (i=usbcx_ghwcfg2.s.numdeveps; i>0; i--)
|
||
{
|
||
cvmx_usbcx_dptxfsizx_t siz;
|
||
siz.u32 = __cvmx_usbd_read_csr32(usb, CVMX_USBCX_DPTXFSIZX(i, usb->index));
|
||
fifo_space -= siz.s.dptxfsize;
|
||
siz.s.dptxfstaddr = fifo_space;
|
||
__cvmx_usbd_write_csr32(usb, CVMX_USBCX_DPTXFSIZX(i, usb->index), siz.u32);
|
||
}
|
||
|
||
/* Assign half the leftover space to the non periodic tx fifo */
|
||
gnptxfsiz.u32 = __cvmx_usbd_read_csr32(usb, CVMX_USBCX_GNPTXFSIZ(usb->index));
|
||
gnptxfsiz.s.nptxfdep = fifo_space / 2;
|
||
fifo_space -= gnptxfsiz.s.nptxfdep;
|
||
gnptxfsiz.s.nptxfstaddr = fifo_space;
|
||
__cvmx_usbd_write_csr32(usb, CVMX_USBCX_GNPTXFSIZ(usb->index), gnptxfsiz.u32);
|
||
|
||
/* Assign the remain space to the RX fifo */
|
||
__cvmx_usbd_write_csr32(usb, CVMX_USBCX_GRXFSIZ(usb->index), fifo_space);
|
||
|
||
/* Unmask the common endpoint interrupts */
|
||
usbcx_doepmsk.u32 = 0;
|
||
usbcx_doepmsk.s.setupmsk = 1;
|
||
usbcx_doepmsk.s.epdisbldmsk = 1;
|
||
usbcx_doepmsk.s.xfercomplmsk = 1;
|
||
__cvmx_usbd_write_csr32(usb, CVMX_USBCX_DOEPMSK(usb->index), usbcx_doepmsk.u32);
|
||
usbcx_diepmsk.u32 = 0;
|
||
usbcx_diepmsk.s.epdisbldmsk = 1;
|
||
usbcx_diepmsk.s.xfercomplmsk = 1;
|
||
__cvmx_usbd_write_csr32(usb, CVMX_USBCX_DIEPMSK(usb->index), usbcx_diepmsk.u32);
|
||
|
||
usbc_daintmsk.u32 = 0;
|
||
usbc_daintmsk.s.inepmsk = -1;
|
||
usbc_daintmsk.s.outepmsk = -1;
|
||
__cvmx_usbd_write_csr32(usb, CVMX_USBCX_DAINTMSK(usb->index), usbc_daintmsk.u32);
|
||
|
||
/* Set all endpoints to NAK */
|
||
for (i=0; i<usbcx_ghwcfg2.s.numdeveps+1; i++)
|
||
{
|
||
cvmx_usbcx_doepctlx_t usbc_doepctl;
|
||
usbc_doepctl.u32 = 0;
|
||
usbc_doepctl.s.snak = 1;
|
||
usbc_doepctl.s.usbactep = 1;
|
||
usbc_doepctl.s.mps = (i==0) ? 0 : 64;
|
||
__cvmx_usbd_write_csr32(usb, CVMX_USBCX_DOEPCTLX(i, usb->index), usbc_doepctl.u32);
|
||
}
|
||
|
||
return 0;
|
||
}
|
||
|
||
|
||
/**
|
||
* 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 usb Pointer to an empty cvmx_usbd_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_usbd_initialize_flags_t for the flag
|
||
* definitions. Some flags are mandatory.
|
||
*
|
||
* @return Zero or a negative on error.
|
||
*/
|
||
int cvmx_usbd_initialize(cvmx_usbd_state_t *usb,
|
||
int usb_port_number,
|
||
cvmx_usbd_initialize_flags_t flags)
|
||
{
|
||
cvmx_usbnx_clk_ctl_t usbn_clk_ctl;
|
||
cvmx_usbnx_usbp_ctl_status_t usbn_usbp_ctl_status;
|
||
|
||
if (cvmx_unlikely(flags & CVMX_USBD_INITIALIZE_FLAGS_DEBUG))
|
||
cvmx_dprintf("%s: Called\n", __FUNCTION__);
|
||
|
||
memset(usb, 0, sizeof(usb));
|
||
usb->init_flags = flags;
|
||
usb->index = usb_port_number;
|
||
|
||
/* Try to determine clock type automatically */
|
||
if ((usb->init_flags & (CVMX_USBD_INITIALIZE_FLAGS_CLOCK_XO_XI |
|
||
CVMX_USBD_INITIALIZE_FLAGS_CLOCK_XO_GND)) == 0)
|
||
{
|
||
if (__cvmx_helper_board_usb_get_clock_type() == USB_CLOCK_TYPE_CRYSTAL_12)
|
||
usb->init_flags |= CVMX_USBD_INITIALIZE_FLAGS_CLOCK_XO_XI; /* Only 12 MHZ crystals are supported */
|
||
else
|
||
usb->init_flags |= CVMX_USBD_INITIALIZE_FLAGS_CLOCK_XO_GND;
|
||
}
|
||
|
||
if (usb->init_flags & CVMX_USBD_INITIALIZE_FLAGS_CLOCK_XO_GND)
|
||
{
|
||
/* Check for auto ref clock frequency */
|
||
if (!(usb->init_flags & CVMX_USBD_INITIALIZE_FLAGS_CLOCK_MHZ_MASK))
|
||
switch (__cvmx_helper_board_usb_get_clock_type())
|
||
{
|
||
case USB_CLOCK_TYPE_REF_12:
|
||
usb->init_flags |= CVMX_USBD_INITIALIZE_FLAGS_CLOCK_12MHZ;
|
||
break;
|
||
case USB_CLOCK_TYPE_REF_24:
|
||
usb->init_flags |= CVMX_USBD_INITIALIZE_FLAGS_CLOCK_24MHZ;
|
||
break;
|
||
case USB_CLOCK_TYPE_REF_48:
|
||
default:
|
||
usb->init_flags |= CVMX_USBD_INITIALIZE_FLAGS_CLOCK_48MHZ;
|
||
break;
|
||
}
|
||
}
|
||
|
||
/* 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_read_csr(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_USBD_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 (usb->init_flags & CVMX_USBD_INITIALIZE_FLAGS_CLOCK_MHZ_MASK)
|
||
{
|
||
case CVMX_USBD_INITIALIZE_FLAGS_CLOCK_12MHZ:
|
||
usbn_clk_ctl.s.p_c_sel = 0;
|
||
break;
|
||
case CVMX_USBD_INITIALIZE_FLAGS_CLOCK_24MHZ:
|
||
usbn_clk_ctl.s.p_c_sel = 1;
|
||
break;
|
||
case CVMX_USBD_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 = (cvmx_clock_get_rate(CVMX_CLOCK_CORE)+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_write_csr(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_write_csr(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_write_csr(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_read_csr(CVMX_USBNX_USBP_CTL_STATUS(usb->index));
|
||
usbn_usbp_ctl_status.s.ate_reset = 1;
|
||
cvmx_write_csr(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_write_csr(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_write_csr(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 = 1;
|
||
usbn_usbp_ctl_status.s.dm_pulld = 0;
|
||
usbn_usbp_ctl_status.s.dp_pulld = 0;
|
||
cvmx_write_csr(CVMX_USBNX_USBP_CTL_STATUS(usb->index), usbn_usbp_ctl_status.u64);
|
||
/* 10. Wait 1 <20>s */
|
||
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_write_csr(CVMX_USBNX_CLK_CTL(usb->index), usbn_clk_ctl.u64);
|
||
/* 12. Proceed to USB core initialization */
|
||
usbn_clk_ctl.s.enable = 1;
|
||
cvmx_write_csr(CVMX_USBNX_CLK_CTL(usb->index), usbn_clk_ctl.u64);
|
||
cvmx_wait_usec(1);
|
||
|
||
/* 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;
|
||
usbcx_gahbcfg.u32 = 0;
|
||
usbcx_gahbcfg.s.dmaen = 1;
|
||
usbcx_gahbcfg.s.hbstlen = 0;
|
||
usbcx_gahbcfg.s.nptxfemplvl = 1;
|
||
usbcx_gahbcfg.s.ptxfemplvl = 1;
|
||
usbcx_gahbcfg.s.glblintrmsk = 1;
|
||
__cvmx_usbd_write_csr32(usb, CVMX_USBCX_GAHBCFG(usb->index), usbcx_gahbcfg.u32);
|
||
}
|
||
|
||
/* 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_usbd_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_usbd_write_csr32(usb, CVMX_USBCX_GUSBCFG(usb->index), usbcx_gusbcfg.u32);
|
||
}
|
||
|
||
/* Program the following fields in the USBC0/1_DCFG register:
|
||
Device speed, USBC0/1_DCFG[DEVSPD] = 0 (high speed)
|
||
Non-zero-length status OUT handshake, USBC0/1_DCFG[NZSTSOUTHSHK]=0
|
||
Periodic frame interval (if periodic endpoints are supported),
|
||
USBC0/1_DCFG[PERFRINT] = 1 */
|
||
{
|
||
cvmx_usbcx_dcfg_t usbcx_dcfg;
|
||
usbcx_dcfg.u32 = __cvmx_usbd_read_csr32(usb, CVMX_USBCX_DCFG(usb->index));
|
||
usbcx_dcfg.s.devspd = 0;
|
||
usbcx_dcfg.s.nzstsouthshk = 0;
|
||
usbcx_dcfg.s.perfrint = 1;
|
||
__cvmx_usbd_write_csr32(usb, CVMX_USBCX_DCFG(usb->index), usbcx_dcfg.u32);
|
||
}
|
||
|
||
/* Program the USBC0/1_GINTMSK register */
|
||
{
|
||
cvmx_usbcx_gintmsk_t usbcx_gintmsk;
|
||
usbcx_gintmsk.u32 = __cvmx_usbd_read_csr32(usb, CVMX_USBCX_GINTMSK(usb->index));
|
||
usbcx_gintmsk.s.oepintmsk = 1;
|
||
usbcx_gintmsk.s.inepintmsk = 1;
|
||
usbcx_gintmsk.s.enumdonemsk = 1;
|
||
usbcx_gintmsk.s.usbrstmsk = 1;
|
||
usbcx_gintmsk.s.usbsuspmsk = 1;
|
||
__cvmx_usbd_write_csr32(usb, CVMX_USBCX_GINTMSK(usb->index), usbcx_gintmsk.u32);
|
||
}
|
||
|
||
cvmx_usbd_disable(usb);
|
||
return 0;
|
||
}
|
||
#ifdef CVMX_BUILD_FOR_LINUX_KERNEL
|
||
EXPORT_SYMBOL(cvmx_usbd_initialize);
|
||
#endif
|
||
|
||
|
||
/**
|
||
* Shutdown a USB port after a call to cvmx_usbd_initialize().
|
||
*
|
||
* @param usb USB device state populated by
|
||
* cvmx_usbd_initialize().
|
||
*
|
||
* @return Zero or a negative on error.
|
||
*/
|
||
int cvmx_usbd_shutdown(cvmx_usbd_state_t *usb)
|
||
{
|
||
cvmx_usbnx_clk_ctl_t usbn_clk_ctl;
|
||
|
||
if (cvmx_unlikely(usb->init_flags & CVMX_USBD_INITIALIZE_FLAGS_DEBUG))
|
||
cvmx_dprintf("%s: Called\n", __FUNCTION__);
|
||
|
||
/* Disable the clocks and put them in power on reset */
|
||
usbn_clk_ctl.u64 = cvmx_read_csr(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_write_csr(CVMX_USBNX_CLK_CTL(usb->index), usbn_clk_ctl.u64);
|
||
return 0;
|
||
}
|
||
#ifdef CVMX_BUILD_FOR_LINUX_KERNEL
|
||
EXPORT_SYMBOL(cvmx_usbd_shutdown);
|
||
#endif
|
||
|
||
|
||
/**
|
||
* Enable a USB port. After this call succeeds, the USB port is
|
||
* online and servicing requests.
|
||
*
|
||
* @param usb USB device state populated by
|
||
* cvmx_usb_initialize().
|
||
*
|
||
* @return Zero or negative on error.
|
||
*/
|
||
int cvmx_usbd_enable(cvmx_usbd_state_t *usb)
|
||
{
|
||
cvmx_usbcx_dctl_t usbcx_dctl;
|
||
usbcx_dctl.u32 = __cvmx_usbd_read_csr32(usb, CVMX_USBCX_DCTL(usb->index));
|
||
usbcx_dctl.s.cgoutnak = 1;
|
||
usbcx_dctl.s.sftdiscon = 0;
|
||
__cvmx_usbd_write_csr32(usb, CVMX_USBCX_DCTL(usb->index), usbcx_dctl.u32);
|
||
return 0;
|
||
}
|
||
#ifdef CVMX_BUILD_FOR_LINUX_KERNEL
|
||
EXPORT_SYMBOL(cvmx_usbd_enable);
|
||
#endif
|
||
|
||
|
||
/**
|
||
* Disable a USB port. After this call the USB port will not
|
||
* generate data transfers and will not generate events.
|
||
*
|
||
* @param usb USB device state populated by
|
||
* cvmx_usb_initialize().
|
||
*
|
||
* @return Zero or negative on error.
|
||
*/
|
||
int cvmx_usbd_disable(cvmx_usbd_state_t *usb)
|
||
{
|
||
cvmx_usbcx_dctl_t usbcx_dctl;
|
||
usbcx_dctl.u32 = __cvmx_usbd_read_csr32(usb, CVMX_USBCX_DCTL(usb->index));
|
||
usbcx_dctl.s.sgoutnak = 1;
|
||
usbcx_dctl.s.sftdiscon = 1;
|
||
__cvmx_usbd_write_csr32(usb, CVMX_USBCX_DCTL(usb->index), usbcx_dctl.u32);
|
||
return 0;
|
||
}
|
||
#ifdef CVMX_BUILD_FOR_LINUX_KERNEL
|
||
EXPORT_SYMBOL(cvmx_usbd_disable);
|
||
#endif
|
||
|
||
|
||
/**
|
||
* Register a callback function to process USB events
|
||
*
|
||
* @param usb USB device state populated by
|
||
* cvmx_usbd_initialize().
|
||
* @param reason The reason this callback should be called
|
||
* @param func Function to call
|
||
* @param user_data User supplied data for the callback
|
||
*
|
||
* @return Zero on succes, negative on failure
|
||
*/
|
||
int cvmx_usbd_register(cvmx_usbd_state_t *usb, cvmx_usbd_callback_t reason, cvmx_usbd_callback_func_t func, void *user_data)
|
||
{
|
||
if (cvmx_unlikely(usb->init_flags & CVMX_USBD_INITIALIZE_FLAGS_DEBUG))
|
||
cvmx_dprintf("%s: Register reason=%d func=%p data=%p\n",
|
||
__FUNCTION__, reason, func, user_data);
|
||
usb->callback[reason] = func;
|
||
usb->callback_data[reason] = user_data;
|
||
return 0;
|
||
}
|
||
#ifdef CVMX_BUILD_FOR_LINUX_KERNEL
|
||
EXPORT_SYMBOL(cvmx_usbd_register);
|
||
#endif
|
||
|
||
/**
|
||
* @INTERNAL
|
||
* Poll a device mode endpoint for status
|
||
*
|
||
* @param usb USB device state populated by
|
||
* cvmx_usbd_initialize().
|
||
* @param endpoint_num
|
||
* Endpoint to poll
|
||
*
|
||
* @return Zero on success
|
||
*/
|
||
static int __cvmx_usbd_poll_in_endpoint(cvmx_usbd_state_t *usb, int endpoint_num)
|
||
{
|
||
cvmx_usbcx_diepintx_t usbc_diepint;
|
||
|
||
if (cvmx_unlikely(usb->init_flags & CVMX_USBD_INITIALIZE_FLAGS_DEBUG))
|
||
cvmx_dprintf("%s: endpoint=%d\n", __FUNCTION__, endpoint_num);
|
||
|
||
usbc_diepint.u32 = __cvmx_usbd_read_csr32(usb, CVMX_USBCX_DIEPINTX(endpoint_num, usb->index));
|
||
__cvmx_usbd_write_csr32(usb, CVMX_USBCX_DIEPINTX(endpoint_num, usb->index), usbc_diepint.u32);
|
||
|
||
if (usbc_diepint.s.epdisbld)
|
||
{
|
||
/* Endpoint Disabled Interrupt (EPDisbld)
|
||
This bit indicates that the endpoint is disabled per the
|
||
application's request. */
|
||
/* Nothing to do */
|
||
}
|
||
if (usbc_diepint.s.xfercompl)
|
||
{
|
||
cvmx_usbcx_dieptsizx_t usbc_dieptsiz;
|
||
int bytes_transferred;
|
||
/* Transfer Completed Interrupt (XferCompl)
|
||
Indicates that the programmed transfer is complete on the AHB
|
||
as well as on the USB, for this endpoint. */
|
||
usbc_dieptsiz.u32 = __cvmx_usbd_read_csr32(usb, CVMX_USBCX_DIEPTSIZX(endpoint_num, usb->index));
|
||
bytes_transferred = usb->endpoint[endpoint_num].buffer_length - usbc_dieptsiz.s.xfersize;
|
||
__cvmx_usbd_callback(usb, CVMX_USBD_CALLBACK_IN_COMPLETE, endpoint_num, bytes_transferred);
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
|
||
/**
|
||
* @INTERNAL
|
||
* Poll a device mode endpoint for status
|
||
*
|
||
* @param usb USB device state populated by
|
||
* cvmx_usbd_initialize().
|
||
* @param endpoint_num
|
||
* Endpoint to poll
|
||
*
|
||
* @return Zero on success
|
||
*/
|
||
static int __cvmx_usbd_poll_out_endpoint(cvmx_usbd_state_t *usb, int endpoint_num)
|
||
{
|
||
cvmx_usbcx_doepintx_t usbc_doepint;
|
||
|
||
if (cvmx_unlikely(usb->init_flags & CVMX_USBD_INITIALIZE_FLAGS_DEBUG))
|
||
cvmx_dprintf("%s: endpoint=%d\n", __FUNCTION__, endpoint_num);
|
||
|
||
usbc_doepint.u32 = __cvmx_usbd_read_csr32(usb, CVMX_USBCX_DOEPINTX(endpoint_num, usb->index));
|
||
__cvmx_usbd_write_csr32(usb, CVMX_USBCX_DOEPINTX(endpoint_num, usb->index), usbc_doepint.u32);
|
||
|
||
if (usbc_doepint.s.setup)
|
||
{
|
||
/* SETUP Phase Done (SetUp)
|
||
Applies to control OUT endpoints only.
|
||
Indicates that the SETUP phase for the control endpoint is
|
||
complete and no more back-to-back SETUP packets were
|
||
received for the current control transfer. On this interrupt, the
|
||
application can decode the received SETUP data packet. */
|
||
__cvmx_usbd_callback(usb, CVMX_USBD_CALLBACK_DEVICE_SETUP, endpoint_num, 0);
|
||
}
|
||
if (usbc_doepint.s.epdisbld)
|
||
{
|
||
/* Endpoint Disabled Interrupt (EPDisbld)
|
||
This bit indicates that the endpoint is disabled per the
|
||
application's request. */
|
||
/* Nothing to do */
|
||
}
|
||
if (usbc_doepint.s.xfercompl)
|
||
{
|
||
cvmx_usbcx_doeptsizx_t usbc_doeptsiz;
|
||
int bytes_transferred;
|
||
/* Transfer Completed Interrupt (XferCompl)
|
||
Indicates that the programmed transfer is complete on the AHB
|
||
as well as on the USB, for this endpoint. */
|
||
usbc_doeptsiz.u32 = __cvmx_usbd_read_csr32(usb, CVMX_USBCX_DOEPTSIZX(endpoint_num, usb->index));
|
||
bytes_transferred = usb->endpoint[endpoint_num].buffer_length - usbc_doeptsiz.s.xfersize;
|
||
__cvmx_usbd_callback(usb, CVMX_USBD_CALLBACK_OUT_COMPLETE, endpoint_num, bytes_transferred);
|
||
}
|
||
|
||
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 usb USB device state populated by
|
||
* cvmx_usbd_initialize().
|
||
*
|
||
* @return Zero or negative on error.
|
||
*/
|
||
int cvmx_usbd_poll(cvmx_usbd_state_t *usb)
|
||
{
|
||
cvmx_usbcx_gintsts_t usbc_gintsts;
|
||
|
||
if (cvmx_unlikely(usb->init_flags & CVMX_USBD_INITIALIZE_FLAGS_DEBUG))
|
||
cvmx_dprintf("%s: Called\n", __FUNCTION__);
|
||
|
||
/* Read the pending interrupts */
|
||
usbc_gintsts.u32 = __cvmx_usbd_read_csr32(usb, CVMX_USBCX_GINTSTS(usb->index));
|
||
usbc_gintsts.u32 &= __cvmx_usbd_read_csr32(usb, CVMX_USBCX_GINTMSK(usb->index));
|
||
|
||
/* Clear the interrupts now that we know about them */
|
||
__cvmx_usbd_write_csr32(usb, CVMX_USBCX_GINTSTS(usb->index), usbc_gintsts.u32);
|
||
|
||
if (usbc_gintsts.s.usbsusp)
|
||
__cvmx_usbd_callback(usb, CVMX_USBD_CALLBACK_SUSPEND, 0, 0);
|
||
|
||
if (usbc_gintsts.s.enumdone)
|
||
__cvmx_usbd_callback(usb, CVMX_USBD_CALLBACK_ENUM_COMPLETE, 0, 0);
|
||
|
||
if (usbc_gintsts.s.usbrst)
|
||
{
|
||
/* USB Reset (USBRst)
|
||
The core sets this bit to indicate that a reset is
|
||
detected on the USB. */
|
||
__cvmx_usbd_device_reset_complete(usb);
|
||
__cvmx_usbd_callback(usb, CVMX_USBD_CALLBACK_RESET, 0, 0);
|
||
}
|
||
|
||
if (usbc_gintsts.s.oepint || usbc_gintsts.s.iepint)
|
||
{
|
||
cvmx_usbcx_daint_t usbc_daint;
|
||
usbc_daint.u32 = __cvmx_usbd_read_csr32(usb, CVMX_USBCX_DAINT(usb->index));
|
||
if (usbc_daint.s.inepint)
|
||
{
|
||
int active_endpoints = usbc_daint.s.inepint;
|
||
|
||
while (active_endpoints)
|
||
{
|
||
int endpoint;
|
||
CVMX_CLZ(endpoint, active_endpoints);
|
||
endpoint = 31 - endpoint;
|
||
__cvmx_usbd_poll_in_endpoint(usb, endpoint);
|
||
active_endpoints ^= 1<<endpoint;
|
||
}
|
||
}
|
||
if (usbc_daint.s.outepint)
|
||
{
|
||
int active_endpoints = usbc_daint.s.outepint;
|
||
|
||
while (active_endpoints)
|
||
{
|
||
int endpoint;
|
||
CVMX_CLZ(endpoint, active_endpoints);
|
||
endpoint = 31 - endpoint;
|
||
__cvmx_usbd_poll_out_endpoint(usb, endpoint);
|
||
active_endpoints ^= 1<<endpoint;
|
||
}
|
||
}
|
||
}
|
||
|
||
return 0;
|
||
}
|
||
#ifdef CVMX_BUILD_FOR_LINUX_KERNEL
|
||
EXPORT_SYMBOL(cvmx_usbd_poll);
|
||
#endif
|
||
|
||
/**
|
||
* Get the current USB address
|
||
*
|
||
* @param usb USB device state populated by
|
||
* cvmx_usbd_initialize().
|
||
*
|
||
* @return The USB address
|
||
*/
|
||
int cvmx_usbd_get_address(cvmx_usbd_state_t *usb)
|
||
{
|
||
cvmx_usbcx_dcfg_t usbc_dcfg;
|
||
usbc_dcfg.u32 = __cvmx_usbd_read_csr32(usb, CVMX_USBCX_DCFG(usb->index));
|
||
return usbc_dcfg.s.devaddr;
|
||
}
|
||
#ifdef CVMX_BUILD_FOR_LINUX_KERNEL
|
||
EXPORT_SYMBOL(cvmx_usbd_get_address);
|
||
#endif
|
||
|
||
/**
|
||
* Set the current USB address
|
||
*
|
||
* @param usb USB device state populated by
|
||
* cvmx_usbd_initialize().
|
||
* @param address Address to set
|
||
*/
|
||
void cvmx_usbd_set_address(cvmx_usbd_state_t *usb, int address)
|
||
{
|
||
cvmx_usbcx_dcfg_t usbc_dcfg;
|
||
usbc_dcfg.u32 = __cvmx_usbd_read_csr32(usb, CVMX_USBCX_DCFG(usb->index));
|
||
usbc_dcfg.s.devaddr = address;
|
||
__cvmx_usbd_write_csr32(usb, CVMX_USBCX_DCFG(usb->index), usbc_dcfg.u32);
|
||
}
|
||
#ifdef CVMX_BUILD_FOR_LINUX_KERNEL
|
||
EXPORT_SYMBOL(cvmx_usbd_set_address);
|
||
#endif
|
||
|
||
/**
|
||
* Get the current USB speed
|
||
*
|
||
* @param usb USB device state populated by
|
||
* cvmx_usbd_initialize().
|
||
*
|
||
* @return The USB speed
|
||
*/
|
||
cvmx_usbd_speed_t cvmx_usbd_get_speed(cvmx_usbd_state_t *usb)
|
||
{
|
||
cvmx_usbcx_dsts_t usbcx_dsts;
|
||
usbcx_dsts.u32 = __cvmx_usbd_read_csr32(usb, CVMX_USBCX_DSTS(usb->index));
|
||
return usbcx_dsts.s.enumspd;
|
||
}
|
||
#ifdef CVMX_BUILD_FOR_LINUX_KERNEL
|
||
EXPORT_SYMBOL(cvmx_usbd_get_speed);
|
||
#endif
|
||
|
||
/**
|
||
* Set the current USB speed
|
||
*
|
||
* @param usb USB device state populated by
|
||
* cvmx_usbd_initialize().
|
||
* @param speed The requested speed
|
||
*/
|
||
void cvmx_usbd_set_speed(cvmx_usbd_state_t *usb, cvmx_usbd_speed_t speed)
|
||
{
|
||
cvmx_usbcx_dcfg_t usbcx_dcfg;
|
||
usbcx_dcfg.u32 = __cvmx_usbd_read_csr32(usb, CVMX_USBCX_DCFG(usb->index));
|
||
usbcx_dcfg.s.devspd = speed;
|
||
__cvmx_usbd_write_csr32(usb, CVMX_USBCX_DCFG(usb->index), usbcx_dcfg.u32);
|
||
}
|
||
#ifdef CVMX_BUILD_FOR_LINUX_KERNEL
|
||
EXPORT_SYMBOL(cvmx_usbd_set_speed);
|
||
#endif
|
||
|
||
/**
|
||
* Enable an endpoint to respond to an OUT transaction
|
||
*
|
||
* @param usb USB device state populated by
|
||
* cvmx_usbd_initialize().
|
||
* @param endpoint_num
|
||
* Endpoint number to enable
|
||
* @param transfer_type
|
||
* Transfer type for the endpoint
|
||
* @param max_packet_size
|
||
* Maximum packet size for the endpoint
|
||
* @param buffer Buffer to receive the data
|
||
* @param buffer_length
|
||
* Length of the buffer in bytes
|
||
*
|
||
* @return Zero on success, negative on failure
|
||
*/
|
||
int cvmx_usbd_out_endpoint_enable(cvmx_usbd_state_t *usb,
|
||
int endpoint_num, cvmx_usbd_transfer_t transfer_type,
|
||
int max_packet_size, uint64_t buffer, int buffer_length)
|
||
{
|
||
cvmx_usbcx_doepctlx_t usbc_doepctl;
|
||
cvmx_usbcx_doeptsizx_t usbc_doeptsiz;
|
||
|
||
if (cvmx_unlikely(usb->init_flags & CVMX_USBD_INITIALIZE_FLAGS_DEBUG))
|
||
cvmx_dprintf("%s: endpoint=%d buffer=0x%llx length=%d\n",
|
||
__FUNCTION__, endpoint_num, (ULL)buffer, buffer_length);
|
||
|
||
usb->endpoint[endpoint_num].buffer_length = buffer_length;
|
||
|
||
CVMX_SYNCW; /* Flush out pending writes before enable */
|
||
|
||
/* Clear any pending interrupts */
|
||
__cvmx_usbd_write_csr32(usb, CVMX_USBCX_DOEPINTX(endpoint_num, usb->index),
|
||
__cvmx_usbd_read_csr32(usb, CVMX_USBCX_DOEPINTX(endpoint_num, usb->index)));
|
||
|
||
/* Setup the locations the DMA engines use */
|
||
cvmx_write_csr(CVMX_USBNX_DMA0_INB_CHN0(usb->index) + endpoint_num*8, buffer);
|
||
|
||
usbc_doeptsiz.u32 = 0;
|
||
usbc_doeptsiz.s.mc = 1;
|
||
usbc_doeptsiz.s.pktcnt = (buffer_length + max_packet_size - 1) / max_packet_size;
|
||
if (usbc_doeptsiz.s.pktcnt == 0)
|
||
usbc_doeptsiz.s.pktcnt = 1;
|
||
usbc_doeptsiz.s.xfersize = buffer_length;
|
||
__cvmx_usbd_write_csr32(usb, CVMX_USBCX_DOEPTSIZX(endpoint_num, usb->index), usbc_doeptsiz.u32);
|
||
|
||
usbc_doepctl.u32 = 0;
|
||
usbc_doepctl.s.epena = 1;
|
||
usbc_doepctl.s.setd1pid = 0;
|
||
usbc_doepctl.s.setd0pid = 0;
|
||
usbc_doepctl.s.cnak = 1;
|
||
usbc_doepctl.s.eptype = transfer_type;
|
||
usbc_doepctl.s.usbactep = 1;
|
||
if (endpoint_num == 0)
|
||
{
|
||
switch (max_packet_size)
|
||
{
|
||
case 8:
|
||
usbc_doepctl.s.mps = 3;
|
||
break;
|
||
case 16:
|
||
usbc_doepctl.s.mps = 2;
|
||
break;
|
||
case 32:
|
||
usbc_doepctl.s.mps = 1;
|
||
break;
|
||
default:
|
||
usbc_doepctl.s.mps = 0;
|
||
break;
|
||
}
|
||
}
|
||
else
|
||
usbc_doepctl.s.mps = max_packet_size;
|
||
__cvmx_usbd_write_csr32(usb, CVMX_USBCX_DOEPCTLX(endpoint_num, usb->index), usbc_doepctl.u32);
|
||
|
||
return 0;
|
||
}
|
||
#ifdef CVMX_BUILD_FOR_LINUX_KERNEL
|
||
EXPORT_SYMBOL(cvmx_usbd_out_endpoint_enable);
|
||
#endif
|
||
|
||
|
||
/**
|
||
* Disable an OUT endpoint
|
||
*
|
||
* @param usb USB device state populated by
|
||
* cvmx_usbd_initialize().
|
||
* @param endpoint_num
|
||
* Endpoint number to disable
|
||
*
|
||
* @return Zero on success, negative on failure
|
||
*/
|
||
int cvmx_usbd_out_endpoint_disable(cvmx_usbd_state_t *usb, int endpoint_num)
|
||
{
|
||
cvmx_usbcx_doepctlx_t usbc_doepctl;
|
||
|
||
if (cvmx_unlikely(usb->init_flags & CVMX_USBD_INITIALIZE_FLAGS_DEBUG))
|
||
cvmx_dprintf("%s: endpoint=%d\n", __FUNCTION__, endpoint_num);
|
||
|
||
usbc_doepctl.u32 = __cvmx_usbd_read_csr32(usb, CVMX_USBCX_DOEPCTLX(endpoint_num, usb->index));
|
||
if (usbc_doepctl.s.epena && !usbc_doepctl.s.epdis)
|
||
{
|
||
usbc_doepctl.s.epdis = 1;
|
||
__cvmx_usbd_write_csr32(usb, CVMX_USBCX_DOEPCTLX(endpoint_num, usb->index), usbc_doepctl.u32);
|
||
}
|
||
return 0;
|
||
}
|
||
#ifdef CVMX_BUILD_FOR_LINUX_KERNEL
|
||
EXPORT_SYMBOL(cvmx_usbd_out_endpoint_disable);
|
||
#endif
|
||
|
||
|
||
/**
|
||
* Enable an endpoint to respond to an IN transaction
|
||
*
|
||
* @param usb USB device state populated by
|
||
* cvmx_usbd_initialize().
|
||
* @param endpoint_num
|
||
* Endpoint number to enable
|
||
* @param transfer_type
|
||
* Transfer type for the endpoint
|
||
* @param max_packet_size
|
||
* Maximum packet size for the endpoint
|
||
* @param buffer Buffer to send
|
||
* @param buffer_length
|
||
* Length of the buffer in bytes
|
||
*
|
||
* @return Zero on success, negative on failure
|
||
*/
|
||
int cvmx_usbd_in_endpoint_enable(cvmx_usbd_state_t *usb,
|
||
int endpoint_num, cvmx_usbd_transfer_t transfer_type,
|
||
int max_packet_size, uint64_t buffer, int buffer_length)
|
||
{
|
||
cvmx_usbcx_diepctlx_t usbc_diepctl;
|
||
cvmx_usbcx_dieptsizx_t usbc_dieptsiz;
|
||
|
||
if (cvmx_unlikely(usb->init_flags & CVMX_USBD_INITIALIZE_FLAGS_DEBUG))
|
||
cvmx_dprintf("%s: endpoint=%d buffer=0x%llx length=%d\n",
|
||
__FUNCTION__, endpoint_num, (ULL)buffer, buffer_length);
|
||
|
||
usb->endpoint[endpoint_num].buffer_length = buffer_length;
|
||
|
||
CVMX_SYNCW; /* Flush out pending writes before enable */
|
||
|
||
/* Clear any pending interrupts */
|
||
__cvmx_usbd_write_csr32(usb, CVMX_USBCX_DIEPINTX(endpoint_num, usb->index),
|
||
__cvmx_usbd_read_csr32(usb, CVMX_USBCX_DIEPINTX(endpoint_num, usb->index)));
|
||
|
||
usbc_dieptsiz.u32 = 0;
|
||
usbc_dieptsiz.s.mc = 1;
|
||
if (buffer)
|
||
{
|
||
cvmx_write_csr(CVMX_USBNX_DMA0_OUTB_CHN0(usb->index) + endpoint_num*8, buffer);
|
||
usbc_dieptsiz.s.pktcnt = (buffer_length + max_packet_size - 1) / max_packet_size;
|
||
if (usbc_dieptsiz.s.pktcnt == 0)
|
||
usbc_dieptsiz.s.pktcnt = 1;
|
||
usbc_dieptsiz.s.xfersize = buffer_length;
|
||
}
|
||
else
|
||
{
|
||
usbc_dieptsiz.s.pktcnt = 0;
|
||
usbc_dieptsiz.s.xfersize = 0;
|
||
}
|
||
__cvmx_usbd_write_csr32(usb, CVMX_USBCX_DIEPTSIZX(endpoint_num, usb->index), usbc_dieptsiz.u32);
|
||
|
||
usbc_diepctl.u32 = 0;
|
||
usbc_diepctl.s.epena = (buffer != 0);
|
||
usbc_diepctl.s.setd1pid = 0;
|
||
usbc_diepctl.s.setd0pid = (buffer == 0);
|
||
usbc_diepctl.s.cnak = 1;
|
||
usbc_diepctl.s.txfnum = endpoint_num;
|
||
usbc_diepctl.s.eptype = transfer_type;
|
||
usbc_diepctl.s.usbactep = 1;
|
||
usbc_diepctl.s.nextep = endpoint_num;
|
||
if (endpoint_num == 0)
|
||
{
|
||
switch (max_packet_size)
|
||
{
|
||
case 8:
|
||
usbc_diepctl.s.mps = 3;
|
||
break;
|
||
case 16:
|
||
usbc_diepctl.s.mps = 2;
|
||
break;
|
||
case 32:
|
||
usbc_diepctl.s.mps = 1;
|
||
break;
|
||
default:
|
||
usbc_diepctl.s.mps = 0;
|
||
break;
|
||
}
|
||
}
|
||
else
|
||
usbc_diepctl.s.mps = max_packet_size;
|
||
__cvmx_usbd_write_csr32(usb, CVMX_USBCX_DIEPCTLX(endpoint_num, usb->index), usbc_diepctl.u32);
|
||
|
||
return 0;
|
||
}
|
||
#ifdef CVMX_BUILD_FOR_LINUX_KERNEL
|
||
EXPORT_SYMBOL(cvmx_usbd_in_endpoint_enable);
|
||
#endif
|
||
|
||
|
||
/**
|
||
* Disable an IN endpoint
|
||
*
|
||
* @param usb USB device state populated by
|
||
* cvmx_usbd_initialize().
|
||
* @param endpoint_num
|
||
* Endpoint number to disable
|
||
*
|
||
* @return Zero on success, negative on failure
|
||
*/
|
||
int cvmx_usbd_in_endpoint_disable(cvmx_usbd_state_t *usb, int endpoint_num)
|
||
{
|
||
cvmx_usbcx_diepctlx_t usbc_diepctl;
|
||
|
||
if (cvmx_unlikely(usb->init_flags & CVMX_USBD_INITIALIZE_FLAGS_DEBUG))
|
||
cvmx_dprintf("%s: endpoint=%d\n", __FUNCTION__, endpoint_num);
|
||
|
||
usbc_diepctl.u32 = __cvmx_usbd_read_csr32(usb, CVMX_USBCX_DIEPCTLX(endpoint_num, usb->index));
|
||
if (usbc_diepctl.s.epena && !usbc_diepctl.s.epdis)
|
||
{
|
||
usbc_diepctl.s.epdis = 1;
|
||
__cvmx_usbd_write_csr32(usb, CVMX_USBCX_DIEPCTLX(endpoint_num, usb->index), usbc_diepctl.u32);
|
||
}
|
||
return 0;
|
||
}
|
||
#ifdef CVMX_BUILD_FOR_LINUX_KERNEL
|
||
EXPORT_SYMBOL(cvmx_usbd_in_endpoint_disable);
|
||
#endif
|
||
|