Index: head/sys/contrib/octeon-sdk/cvmx-helper.c
===================================================================
--- head/sys/contrib/octeon-sdk/cvmx-helper.c (revision 232815)
+++ head/sys/contrib/octeon-sdk/cvmx-helper.c (revision 232816)
@@ -1,1995 +1,1995 @@
/***********************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
*
* Helper functions for common, but complicated tasks.
*
*
$Revision: 70030 $
*/
#ifdef CVMX_BUILD_FOR_LINUX_KERNEL
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#else
#if !defined(__FreeBSD__) || !defined(_KERNEL)
#include "executive-config.h"
#endif
#include "cvmx.h"
#include "cvmx-sysinfo.h"
#include "cvmx-bootmem.h"
#include "cvmx-version.h"
#include "cvmx-helper-check-defines.h"
#include "cvmx-gmx.h"
-#include "cvmx-error.h"
#if !defined(__FreeBSD__) || !defined(_KERNEL)
+#include "cvmx-error.h"
#include "cvmx-config.h"
#endif
#include "cvmx-fpa.h"
#include "cvmx-pip.h"
#include "cvmx-pko.h"
#include "cvmx-ipd.h"
#include "cvmx-spi.h"
#include "cvmx-helper.h"
#include "cvmx-helper-board.h"
#include "cvmx-helper-errata.h"
#include "cvmx-helper-cfg.h"
#endif
#ifdef CVMX_ENABLE_PKO_FUNCTIONS
/**
* cvmx_override_pko_queue_priority(int pko_port, uint64_t
* priorities[16]) is a function pointer. It is meant to allow
* customization of the PKO queue priorities based on the port
* number. Users should set this pointer to a function before
* calling any cvmx-helper operations.
*/
CVMX_SHARED void (*cvmx_override_pko_queue_priority)(int ipd_port,
uint64_t *priorities) = NULL;
#ifdef CVMX_BUILD_FOR_LINUX_KERNEL
EXPORT_SYMBOL(cvmx_override_pko_queue_priority);
#endif
/**
* cvmx_override_ipd_port_setup(int ipd_port) is a function
* pointer. It is meant to allow customization of the IPD
* port/port kind setup before packet input/output comes online.
* It is called after cvmx-helper does the default IPD configuration,
* but before IPD is enabled. Users should set this pointer to a
* function before calling any cvmx-helper operations.
*/
CVMX_SHARED void (*cvmx_override_ipd_port_setup)(int ipd_port) = NULL;
/**
* Return the number of interfaces the chip has. Each interface
* may have multiple ports. Most chips support two interfaces,
* but the CNX0XX and CNX1XX are exceptions. These only support
* one interface.
*
* @return Number of interfaces on chip
*/
int cvmx_helper_get_number_of_interfaces(void)
{
switch (cvmx_sysinfo_get()->board_type) {
#if defined(OCTEON_VENDOR_LANNER)
case CVMX_BOARD_TYPE_CUST_LANNER_MR955:
return 2;
case CVMX_BOARD_TYPE_CUST_LANNER_MR730:
return 1;
#endif
default:
break;
}
if (OCTEON_IS_MODEL(OCTEON_CN68XX))
return 9;
else if (OCTEON_IS_MODEL(OCTEON_CN66XX))
if (OCTEON_IS_MODEL(OCTEON_CN66XX_PASS1_0))
return 7;
else
return 8;
else if (OCTEON_IS_MODEL(OCTEON_CN63XX))
return 6;
else if (OCTEON_IS_MODEL(OCTEON_CN56XX) || OCTEON_IS_MODEL(OCTEON_CN52XX) || OCTEON_IS_MODEL(OCTEON_CN61XX))
return 4;
else
return 3;
}
#ifdef CVMX_BUILD_FOR_LINUX_KERNEL
EXPORT_SYMBOL(cvmx_helper_get_number_of_interfaces);
#endif
/**
* Return the number of ports on an interface. Depending on the
* chip and configuration, this can be 1-16. A value of 0
* specifies that the interface doesn't exist or isn't usable.
*
* @param interface Interface to get the port count for
*
* @return Number of ports on interface. Can be Zero.
*/
int cvmx_helper_ports_on_interface(int interface)
{
if (octeon_has_feature(OCTEON_FEATURE_PKND))
return cvmx_helper_interface_enumerate(interface);
else
return __cvmx_helper_get_num_ipd_ports(interface);
}
#ifdef CVMX_BUILD_FOR_LINUX_KERNEL
EXPORT_SYMBOL(cvmx_helper_ports_on_interface);
#endif
/**
* Get the operating mode of an interface. Depending on the Octeon
* chip and configuration, this function returns an enumeration
* of the type of packet I/O supported by an interface.
*
* @param interface Interface to probe
*
* @return Mode of the interface. Unknown or unsupported interfaces return
* DISABLED.
*/
cvmx_helper_interface_mode_t cvmx_helper_interface_get_mode(int interface)
{
cvmx_gmxx_inf_mode_t mode;
if (OCTEON_IS_MODEL(OCTEON_CN68XX))
{
cvmx_mio_qlmx_cfg_t qlm_cfg;
switch(interface)
{
case 0:
qlm_cfg.u64 = cvmx_read_csr(CVMX_MIO_QLMX_CFG(0));
/* QLM is disabled when QLM SPD is 15. */
if (qlm_cfg.s.qlm_spd == 15)
return CVMX_HELPER_INTERFACE_MODE_DISABLED;
if (qlm_cfg.s.qlm_cfg == 7)
return CVMX_HELPER_INTERFACE_MODE_RXAUI;
else if (qlm_cfg.s.qlm_cfg == 2)
return CVMX_HELPER_INTERFACE_MODE_SGMII;
else if (qlm_cfg.s.qlm_cfg == 3)
return CVMX_HELPER_INTERFACE_MODE_XAUI;
else
return CVMX_HELPER_INTERFACE_MODE_DISABLED;
break;
case 1:
qlm_cfg.u64 = cvmx_read_csr(CVMX_MIO_QLMX_CFG(0));
/* QLM is disabled when QLM SPD is 15. */
if (qlm_cfg.s.qlm_spd == 15)
return CVMX_HELPER_INTERFACE_MODE_DISABLED;
if (qlm_cfg.s.qlm_cfg == 7)
return CVMX_HELPER_INTERFACE_MODE_RXAUI;
else
return CVMX_HELPER_INTERFACE_MODE_DISABLED;
break;
case 2:
case 3:
case 4:
qlm_cfg.u64 = cvmx_read_csr(CVMX_MIO_QLMX_CFG(interface));
/* QLM is disabled when QLM SPD is 15. */
if (qlm_cfg.s.qlm_spd == 15)
return CVMX_HELPER_INTERFACE_MODE_DISABLED;
if (qlm_cfg.s.qlm_cfg == 2)
return CVMX_HELPER_INTERFACE_MODE_SGMII;
else if (qlm_cfg.s.qlm_cfg == 3)
return CVMX_HELPER_INTERFACE_MODE_XAUI;
else
return CVMX_HELPER_INTERFACE_MODE_DISABLED;
break;
case 5:
case 6:
qlm_cfg.u64 = cvmx_read_csr(CVMX_MIO_QLMX_CFG(interface - 4));
/* QLM is disabled when QLM SPD is 15. */
if (qlm_cfg.s.qlm_spd == 15)
return CVMX_HELPER_INTERFACE_MODE_DISABLED;
if (qlm_cfg.s.qlm_cfg == 1)
{
return CVMX_HELPER_INTERFACE_MODE_ILK;
}
else
return CVMX_HELPER_INTERFACE_MODE_DISABLED;
break;
case 7:
qlm_cfg.u64 = cvmx_read_csr(CVMX_MIO_QLMX_CFG(3));
/* QLM is disabled when QLM SPD is 15. */
if (qlm_cfg.s.qlm_spd == 15)
return CVMX_HELPER_INTERFACE_MODE_DISABLED;
else if (qlm_cfg.s.qlm_cfg != 0)
{
qlm_cfg.u64 = cvmx_read_csr(CVMX_MIO_QLMX_CFG(1));
if (qlm_cfg.s.qlm_cfg != 0)
return CVMX_HELPER_INTERFACE_MODE_DISABLED;
}
return CVMX_HELPER_INTERFACE_MODE_NPI;
break;
case 8:
return CVMX_HELPER_INTERFACE_MODE_LOOP;
break;
default:
return CVMX_HELPER_INTERFACE_MODE_DISABLED;
break;
}
}
if (interface == 2)
return CVMX_HELPER_INTERFACE_MODE_NPI;
if (interface == 3)
{
if (OCTEON_IS_MODEL(OCTEON_CN56XX)
|| OCTEON_IS_MODEL(OCTEON_CN52XX)
|| OCTEON_IS_MODEL(OCTEON_CN6XXX)
|| OCTEON_IS_MODEL(OCTEON_CNF7XXX))
return CVMX_HELPER_INTERFACE_MODE_LOOP;
else
return CVMX_HELPER_INTERFACE_MODE_DISABLED;
}
/* Only present in CN63XX & CN66XX Octeon model */
if ((OCTEON_IS_MODEL(OCTEON_CN63XX) && (interface == 4 || interface == 5))
|| (OCTEON_IS_MODEL(OCTEON_CN66XX) && interface >= 4 && interface <= 7))
{
cvmx_sriox_status_reg_t sriox_status_reg;
/* cn66xx pass1.0 has only 2 SRIO interfaces. */
if ((interface == 5 || interface == 7) && OCTEON_IS_MODEL(OCTEON_CN66XX_PASS1_0))
return CVMX_HELPER_INTERFACE_MODE_DISABLED;
sriox_status_reg.u64 = cvmx_read_csr(CVMX_SRIOX_STATUS_REG(interface-4));
if (sriox_status_reg.s.srio)
return CVMX_HELPER_INTERFACE_MODE_SRIO;
else
return CVMX_HELPER_INTERFACE_MODE_DISABLED;
}
/* Interface 5 always disabled in CN66XX */
if (OCTEON_IS_MODEL(OCTEON_CN66XX))
{
cvmx_mio_qlmx_cfg_t mio_qlm_cfg;
/* QLM2 is SGMII0 and QLM1 is SGMII1 */
if (interface == 0)
mio_qlm_cfg.u64 = cvmx_read_csr(CVMX_MIO_QLMX_CFG(2));
else if (interface == 1)
mio_qlm_cfg.u64 = cvmx_read_csr(CVMX_MIO_QLMX_CFG(1));
else
return CVMX_HELPER_INTERFACE_MODE_DISABLED;
if (mio_qlm_cfg.s.qlm_spd == 15)
return CVMX_HELPER_INTERFACE_MODE_DISABLED;
if (mio_qlm_cfg.s.qlm_cfg == 9)
return CVMX_HELPER_INTERFACE_MODE_SGMII;
else if (mio_qlm_cfg.s.qlm_cfg == 11)
return CVMX_HELPER_INTERFACE_MODE_XAUI;
else
return CVMX_HELPER_INTERFACE_MODE_DISABLED;
}
else if (OCTEON_IS_MODEL(OCTEON_CN61XX))
{
cvmx_mio_qlmx_cfg_t qlm_cfg;
if (interface == 0)
{
qlm_cfg.u64 = cvmx_read_csr(CVMX_MIO_QLMX_CFG(2));
if (qlm_cfg.s.qlm_cfg == 2)
return CVMX_HELPER_INTERFACE_MODE_SGMII;
else if (qlm_cfg.s.qlm_cfg == 3)
return CVMX_HELPER_INTERFACE_MODE_XAUI;
else
return CVMX_HELPER_INTERFACE_MODE_DISABLED;
}
else if (interface == 1)
{
/* If QLM 1 is PEV0/PEM1 mode, them QLM0 cannot be SGMII/XAUI */
qlm_cfg.u64 = cvmx_read_csr(CVMX_MIO_QLMX_CFG(1));
if (qlm_cfg.s.qlm_cfg == 1)
return CVMX_HELPER_INTERFACE_MODE_DISABLED;
qlm_cfg.u64 = cvmx_read_csr(CVMX_MIO_QLMX_CFG(0));
if (qlm_cfg.s.qlm_cfg == 2)
return CVMX_HELPER_INTERFACE_MODE_SGMII;
else if (qlm_cfg.s.qlm_cfg == 3)
return CVMX_HELPER_INTERFACE_MODE_XAUI;
else
return CVMX_HELPER_INTERFACE_MODE_DISABLED;
}
}
if (interface == 0 && cvmx_sysinfo_get()->board_type == CVMX_BOARD_TYPE_CN3005_EVB_HS5 && cvmx_sysinfo_get()->board_rev_major == 1)
{
/* Lie about interface type of CN3005 board. This board has a switch on port 1 like
** the other evaluation boards, but it is connected over RGMII instead of GMII. Report
** GMII mode so that the speed is forced to 1 Gbit full duplex. Other than some initial configuration
** (which does not use the output of this function) there is no difference in setup between GMII and RGMII modes.
*/
return CVMX_HELPER_INTERFACE_MODE_GMII;
}
/* Interface 1 is always disabled on CN31XX and CN30XX */
if ((interface == 1)
&& (OCTEON_IS_MODEL(OCTEON_CN31XX)
|| OCTEON_IS_MODEL(OCTEON_CN30XX)
|| OCTEON_IS_MODEL(OCTEON_CN50XX)
|| OCTEON_IS_MODEL(OCTEON_CN52XX)
|| OCTEON_IS_MODEL(OCTEON_CN63XX)
|| OCTEON_IS_MODEL(OCTEON_CNF71XX)))
return CVMX_HELPER_INTERFACE_MODE_DISABLED;
mode.u64 = cvmx_read_csr(CVMX_GMXX_INF_MODE(interface));
if (OCTEON_IS_MODEL(OCTEON_CN56XX) || OCTEON_IS_MODEL(OCTEON_CN52XX))
{
switch(mode.cn56xx.mode)
{
case 0: return CVMX_HELPER_INTERFACE_MODE_DISABLED;
case 1: return CVMX_HELPER_INTERFACE_MODE_XAUI;
case 2: return CVMX_HELPER_INTERFACE_MODE_SGMII;
case 3: return CVMX_HELPER_INTERFACE_MODE_PICMG;
default:return CVMX_HELPER_INTERFACE_MODE_DISABLED;
}
}
else if (OCTEON_IS_MODEL(OCTEON_CN63XX) || OCTEON_IS_MODEL(OCTEON_CNF71XX))
{
switch(mode.cn63xx.mode)
{
case 0: return CVMX_HELPER_INTERFACE_MODE_SGMII;
case 1: return CVMX_HELPER_INTERFACE_MODE_XAUI;
default: return CVMX_HELPER_INTERFACE_MODE_DISABLED;
}
}
else
{
if (!mode.s.en)
return CVMX_HELPER_INTERFACE_MODE_DISABLED;
if (mode.s.type)
{
if (OCTEON_IS_MODEL(OCTEON_CN38XX) || OCTEON_IS_MODEL(OCTEON_CN58XX))
return CVMX_HELPER_INTERFACE_MODE_SPI;
else
return CVMX_HELPER_INTERFACE_MODE_GMII;
}
else
return CVMX_HELPER_INTERFACE_MODE_RGMII;
}
}
#ifdef CVMX_BUILD_FOR_LINUX_KERNEL
EXPORT_SYMBOL(cvmx_helper_interface_get_mode);
#endif
/**
* @INTERNAL
* Configure the IPD/PIP tagging and QoS options for a specific
* port. This function determines the POW work queue entry
* contents for a port. The setup performed here is controlled by
* the defines in executive-config.h.
*
* @param ipd_port Port/Port kind to configure. This follows the IPD numbering,
* not the per interface numbering
*
* @return Zero on success, negative on failure
*/
static int __cvmx_helper_port_setup_ipd(int ipd_port)
{
cvmx_pip_prt_cfgx_t port_config;
cvmx_pip_prt_tagx_t tag_config;
if (octeon_has_feature(OCTEON_FEATURE_PKND))
{
int interface, index, pknd;
cvmx_pip_prt_cfgbx_t prt_cfgbx;
interface = cvmx_helper_get_interface_num(ipd_port);
index = cvmx_helper_get_interface_index_num(ipd_port);
pknd = cvmx_helper_get_pknd(interface, index);
port_config.u64 = cvmx_read_csr(CVMX_PIP_PRT_CFGX(pknd));
tag_config.u64 = cvmx_read_csr(CVMX_PIP_PRT_TAGX(pknd));
port_config.s.qos = pknd & 0x7;
/* Default BPID to use for packets on this port-kind */
prt_cfgbx.u64 = cvmx_read_csr(CVMX_PIP_PRT_CFGBX(pknd));
prt_cfgbx.s.bpid = pknd;
cvmx_write_csr(CVMX_PIP_PRT_CFGBX(pknd), prt_cfgbx.u64);
}
else
{
port_config.u64 = cvmx_read_csr(CVMX_PIP_PRT_CFGX(ipd_port));
tag_config.u64 = cvmx_read_csr(CVMX_PIP_PRT_TAGX(ipd_port));
/* Have each port go to a different POW queue */
port_config.s.qos = ipd_port & 0x7;
}
/* Process the headers and place the IP header in the work queue */
port_config.s.mode = CVMX_HELPER_INPUT_PORT_SKIP_MODE;
tag_config.s.ip6_src_flag = CVMX_HELPER_INPUT_TAG_IPV6_SRC_IP;
tag_config.s.ip6_dst_flag = CVMX_HELPER_INPUT_TAG_IPV6_DST_IP;
tag_config.s.ip6_sprt_flag = CVMX_HELPER_INPUT_TAG_IPV6_SRC_PORT;
tag_config.s.ip6_dprt_flag = CVMX_HELPER_INPUT_TAG_IPV6_DST_PORT;
tag_config.s.ip6_nxth_flag = CVMX_HELPER_INPUT_TAG_IPV6_NEXT_HEADER;
tag_config.s.ip4_src_flag = CVMX_HELPER_INPUT_TAG_IPV4_SRC_IP;
tag_config.s.ip4_dst_flag = CVMX_HELPER_INPUT_TAG_IPV4_DST_IP;
tag_config.s.ip4_sprt_flag = CVMX_HELPER_INPUT_TAG_IPV4_SRC_PORT;
tag_config.s.ip4_dprt_flag = CVMX_HELPER_INPUT_TAG_IPV4_DST_PORT;
tag_config.s.ip4_pctl_flag = CVMX_HELPER_INPUT_TAG_IPV4_PROTOCOL;
tag_config.s.inc_prt_flag = CVMX_HELPER_INPUT_TAG_INPUT_PORT;
tag_config.s.tcp6_tag_type = CVMX_HELPER_INPUT_TAG_TYPE;
tag_config.s.tcp4_tag_type = CVMX_HELPER_INPUT_TAG_TYPE;
tag_config.s.ip6_tag_type = CVMX_HELPER_INPUT_TAG_TYPE;
tag_config.s.ip4_tag_type = CVMX_HELPER_INPUT_TAG_TYPE;
tag_config.s.non_tag_type = CVMX_HELPER_INPUT_TAG_TYPE;
/* Put all packets in group 0. Other groups can be used by the app */
tag_config.s.grp = 0;
cvmx_pip_config_port(ipd_port, port_config, tag_config);
/* Give the user a chance to override our setting for each port */
if (cvmx_override_ipd_port_setup)
cvmx_override_ipd_port_setup(ipd_port);
return 0;
}
/**
* Enable or disable FCS stripping for all the ports on an interface.
*
* @param interface
* @param nports number of ports
* @param has_fcs 0 for disable and !0 for enable
*/
static int cvmx_helper_fcs_op(int interface, int nports, int has_fcs)
{
uint64_t port_bit;
int index;
int pknd;
cvmx_pip_sub_pkind_fcsx_t pkind_fcsx;
cvmx_pip_prt_cfgx_t port_cfg;
if (!octeon_has_feature(OCTEON_FEATURE_PKND))
return 0;
port_bit = 0;
for (index = 0; index < nports; index++)
port_bit |= ((uint64_t)1 << cvmx_helper_get_pknd(interface, index));
pkind_fcsx.u64 = cvmx_read_csr(CVMX_PIP_SUB_PKIND_FCSX(0));
if (has_fcs)
pkind_fcsx.s.port_bit |= port_bit;
else
pkind_fcsx.s.port_bit &= ~port_bit;
cvmx_write_csr(CVMX_PIP_SUB_PKIND_FCSX(0), pkind_fcsx.u64);
for (pknd = 0; pknd < 64; pknd++)
{
if ((1ull << pknd) & port_bit)
{
port_cfg.u64 = cvmx_read_csr(CVMX_PIP_PRT_CFGX(pknd));
port_cfg.s.crc_en = (has_fcs) ? 1 : 0;
cvmx_write_csr(CVMX_PIP_PRT_CFGX(pknd), port_cfg.u64);
}
}
return 0;
}
/**
* Determine the actual number of hardware ports connected to an
* interface. It doesn't setup the ports or enable them.
*
* @param interface Interface to enumerate
*
* @return The number of ports on the interface, negative on failure
*/
int cvmx_helper_interface_enumerate(int interface)
{
switch (cvmx_helper_interface_get_mode(interface)) {
/* XAUI is a single high speed port */
case CVMX_HELPER_INTERFACE_MODE_XAUI:
case CVMX_HELPER_INTERFACE_MODE_RXAUI:
return __cvmx_helper_xaui_enumerate(interface);
/* RGMII/GMII/MII are all treated about the same. Most functions
refer to these ports as RGMII */
case CVMX_HELPER_INTERFACE_MODE_RGMII:
case CVMX_HELPER_INTERFACE_MODE_GMII:
return __cvmx_helper_rgmii_enumerate(interface);
/* SPI4 can have 1-16 ports depending on the device at the other end */
case CVMX_HELPER_INTERFACE_MODE_SPI:
return __cvmx_helper_spi_enumerate(interface);
/* SGMII can have 1-4 ports depending on how many are hooked up */
case CVMX_HELPER_INTERFACE_MODE_SGMII:
case CVMX_HELPER_INTERFACE_MODE_PICMG:
return __cvmx_helper_sgmii_enumerate(interface);
/* PCI target Network Packet Interface */
case CVMX_HELPER_INTERFACE_MODE_NPI:
return __cvmx_helper_npi_enumerate(interface);
/* Special loopback only ports. These are not the same
* as other ports in loopback mode */
case CVMX_HELPER_INTERFACE_MODE_LOOP:
return __cvmx_helper_loop_enumerate(interface);
/* SRIO has 2^N ports, where N is number of interfaces */
case CVMX_HELPER_INTERFACE_MODE_SRIO:
return __cvmx_helper_srio_enumerate(interface);
case CVMX_HELPER_INTERFACE_MODE_ILK:
return __cvmx_helper_ilk_enumerate(interface);
/* These types don't support ports to IPD/PKO */
case CVMX_HELPER_INTERFACE_MODE_DISABLED:
case CVMX_HELPER_INTERFACE_MODE_PCIE:
default:
return 0;
}
}
/**
* This function probes an interface to determine the actual number of
* hardware ports connected to it. It does some setup the ports but
* doesn't enable them. The main goal here is to set the global
* interface_port_count[interface] correctly. Final hardware setup of
* the ports will be performed later.
*
* @param interface Interface to probe
*
* @return Zero on success, negative on failure
*/
int cvmx_helper_interface_probe(int interface)
{
/* At this stage in the game we don't want packets to be moving yet.
The following probe calls should perform hardware setup
needed to determine port counts. Receive must still be disabled */
int nports;
int has_fcs;
enum cvmx_pko_padding padding = CVMX_PKO_PADDING_NONE;
nports = -1;
has_fcs = 0;
switch (cvmx_helper_interface_get_mode(interface))
{
/* These types don't support ports to IPD/PKO */
case CVMX_HELPER_INTERFACE_MODE_DISABLED:
case CVMX_HELPER_INTERFACE_MODE_PCIE:
nports = 0;
break;
/* XAUI is a single high speed port */
case CVMX_HELPER_INTERFACE_MODE_XAUI:
case CVMX_HELPER_INTERFACE_MODE_RXAUI:
nports = __cvmx_helper_xaui_probe(interface);
has_fcs = 1;
padding = CVMX_PKO_PADDING_60;
break;
/* RGMII/GMII/MII are all treated about the same. Most functions
refer to these ports as RGMII */
case CVMX_HELPER_INTERFACE_MODE_RGMII:
case CVMX_HELPER_INTERFACE_MODE_GMII:
nports = __cvmx_helper_rgmii_probe(interface);
padding = CVMX_PKO_PADDING_60;
break;
/* SPI4 can have 1-16 ports depending on the device at the other end */
case CVMX_HELPER_INTERFACE_MODE_SPI:
nports = __cvmx_helper_spi_probe(interface);
padding = CVMX_PKO_PADDING_60;
break;
/* SGMII can have 1-4 ports depending on how many are hooked up */
case CVMX_HELPER_INTERFACE_MODE_SGMII:
padding = CVMX_PKO_PADDING_60;
case CVMX_HELPER_INTERFACE_MODE_PICMG:
nports = __cvmx_helper_sgmii_probe(interface);
has_fcs = 1;
break;
/* PCI target Network Packet Interface */
case CVMX_HELPER_INTERFACE_MODE_NPI:
nports = __cvmx_helper_npi_probe(interface);
break;
/* Special loopback only ports. These are not the same as other ports
in loopback mode */
case CVMX_HELPER_INTERFACE_MODE_LOOP:
nports = __cvmx_helper_loop_probe(interface);
break;
/* SRIO has 2^N ports, where N is number of interfaces */
case CVMX_HELPER_INTERFACE_MODE_SRIO:
nports = __cvmx_helper_srio_probe(interface);
break;
case CVMX_HELPER_INTERFACE_MODE_ILK:
nports = __cvmx_helper_ilk_probe(interface);
has_fcs = 1;
padding = CVMX_PKO_PADDING_60;
break;
}
if (nports == -1)
return -1;
if (!octeon_has_feature(OCTEON_FEATURE_PKND))
has_fcs = 0;
nports = __cvmx_helper_board_interface_probe(interface, nports);
__cvmx_helper_init_interface(interface, nports, has_fcs, padding);
cvmx_helper_fcs_op(interface, nports, has_fcs);
/* Make sure all global variables propagate to other cores */
CVMX_SYNCWS;
return 0;
}
/**
* @INTERNAL
* Setup the IPD/PIP for the ports on an interface. Packet
* classification and tagging are set for every port on the
* interface. The number of ports on the interface must already
* have been probed.
*
* @param interface Interface to setup IPD/PIP for
*
* @return Zero on success, negative on failure
*/
static int __cvmx_helper_interface_setup_ipd(int interface)
{
cvmx_helper_interface_mode_t mode;
int ipd_port = cvmx_helper_get_ipd_port(interface, 0);
int num_ports = cvmx_helper_ports_on_interface(interface);
int delta;
if (num_ports == CVMX_HELPER_CFG_INVALID_VALUE)
return 0;
mode = cvmx_helper_interface_get_mode(interface);
if (mode == CVMX_HELPER_INTERFACE_MODE_LOOP)
__cvmx_helper_loop_enable(interface);
delta = 1;
if (octeon_has_feature(OCTEON_FEATURE_PKND))
{
if (mode == CVMX_HELPER_INTERFACE_MODE_SGMII)
delta = 16;
}
while (num_ports--)
{
__cvmx_helper_port_setup_ipd(ipd_port);
ipd_port += delta;
}
return 0;
}
/**
* @INTERNAL
* Setup global setting for IPD/PIP not related to a specific
* interface or port. This must be called before IPD is enabled.
*
* @return Zero on success, negative on failure.
*/
static int __cvmx_helper_global_setup_ipd(void)
{
#ifndef CVMX_HELPER_IPD_DRAM_MODE
#define CVMX_HELPER_IPD_DRAM_MODE CVMX_IPD_OPC_MODE_STT
#endif
/* Setup the global packet input options */
cvmx_ipd_config(CVMX_FPA_PACKET_POOL_SIZE/8,
CVMX_HELPER_FIRST_MBUFF_SKIP/8,
CVMX_HELPER_NOT_FIRST_MBUFF_SKIP/8,
(CVMX_HELPER_FIRST_MBUFF_SKIP+8) / 128, /* The +8 is to account for the next ptr */
(CVMX_HELPER_NOT_FIRST_MBUFF_SKIP+8) / 128, /* The +8 is to account for the next ptr */
CVMX_FPA_WQE_POOL,
CVMX_HELPER_IPD_DRAM_MODE,
1);
return 0;
}
/**
* @INTERNAL
* Setup the PKO for the ports on an interface. The number of
* queues per port and the priority of each PKO output queue
* is set here. PKO must be disabled when this function is called.
*
* @param interface Interface to setup PKO for
*
* @return Zero on success, negative on failure
*/
static int __cvmx_helper_interface_setup_pko(int interface)
{
/* Each packet output queue has an associated priority. The higher the
priority, the more often it can send a packet. A priority of 8 means
it can send in all 8 rounds of contention. We're going to make each
queue one less than the last.
The vector of priorities has been extended to support CN5xxx CPUs,
where up to 16 queues can be associated to a port.
To keep backward compatibility we don't change the initial 8
priorities and replicate them in the second half.
With per-core PKO queues (PKO lockless operation) all queues have
the same priority. */
/* uint64_t priorities[16] = {8,7,6,5,4,3,2,1,8,7,6,5,4,3,2,1}; */
uint64_t priorities[16] = {[0 ... 15] = 8};
/* Setup the IPD/PIP and PKO for the ports discovered above. Here packet
classification, tagging and output priorities are set */
int ipd_port = cvmx_helper_get_ipd_port(interface, 0);
int num_ports = cvmx_helper_ports_on_interface(interface);
while (num_ports--)
{
/* Give the user a chance to override the per queue priorities */
if (cvmx_override_pko_queue_priority)
cvmx_override_pko_queue_priority(ipd_port, priorities);
cvmx_pko_config_port(ipd_port, cvmx_pko_get_base_queue_per_core(ipd_port, 0),
cvmx_pko_get_num_queues(ipd_port), priorities);
ipd_port++;
}
return 0;
}
/**
* @INTERNAL
* Setup global setting for PKO not related to a specific
* interface or port. This must be called before PKO is enabled.
*
* @return Zero on success, negative on failure.
*/
static int __cvmx_helper_global_setup_pko(void)
{
/* Disable tagwait FAU timeout. This needs to be done before anyone might
start packet output using tags */
cvmx_iob_fau_timeout_t fau_to;
fau_to.u64 = 0;
fau_to.s.tout_val = 0xfff;
fau_to.s.tout_enb = 0;
cvmx_write_csr(CVMX_IOB_FAU_TIMEOUT, fau_to.u64);
if (OCTEON_IS_MODEL(OCTEON_CN68XX)) {
cvmx_pko_reg_min_pkt_t min_pkt;
min_pkt.u64 = 0;
min_pkt.s.size1 = 59;
min_pkt.s.size2 = 59;
min_pkt.s.size3 = 59;
min_pkt.s.size4 = 59;
min_pkt.s.size5 = 59;
min_pkt.s.size6 = 59;
min_pkt.s.size7 = 59;
cvmx_write_csr(CVMX_PKO_REG_MIN_PKT, min_pkt.u64);
}
return 0;
}
/**
* @INTERNAL
* Setup global backpressure setting.
*
* @return Zero on success, negative on failure
*/
static int __cvmx_helper_global_setup_backpressure(void)
{
#if CVMX_HELPER_DISABLE_RGMII_BACKPRESSURE
/* Disable backpressure if configured to do so */
/* Disable backpressure (pause frame) generation */
int num_interfaces = cvmx_helper_get_number_of_interfaces();
int interface;
for (interface=0; interface 0)
{
//cvmx_dprintf("Enabling packet I/O on interface %d\n", interface);
__cvmx_helper_packet_hardware_enable(interface);
}
}
/* Finally enable PKO now that the entire path is up and running */
cvmx_pko_enable();
if ((OCTEON_IS_MODEL(OCTEON_CN31XX_PASS1) || OCTEON_IS_MODEL(OCTEON_CN30XX_PASS1)) &&
(cvmx_sysinfo_get()->board_type != CVMX_BOARD_TYPE_SIM))
__cvmx_helper_errata_fix_ipd_ptr_alignment();
return 0;
}
#ifdef CVMX_BUILD_FOR_LINUX_KERNEL
EXPORT_SYMBOL(cvmx_helper_ipd_and_packet_input_enable);
#endif
#define __CVMX_SSO_RWQ_SIZE 256
int cvmx_helper_initialize_sso(int wqe_entries)
{
int cvm_oct_sso_number_rwq_bufs;
char *mem;
int i;
cvmx_sso_cfg_t sso_cfg;
cvmx_fpa_fpfx_marks_t fpa_marks;
if (!OCTEON_IS_MODEL(OCTEON_CN68XX))
return 0;
/*
* CN68XX-P1 may reset with the wrong values, put in
* the correct values.
*/
fpa_marks.u64 = 0;
fpa_marks.s.fpf_wr = 0xa4;
fpa_marks.s.fpf_rd = 0x40;
cvmx_write_csr(CVMX_FPA_FPF8_MARKS, fpa_marks.u64);
cvm_oct_sso_number_rwq_bufs = ((wqe_entries - 1) / 26) + 1 + 48 + 8;
mem = cvmx_bootmem_alloc(__CVMX_SSO_RWQ_SIZE * cvm_oct_sso_number_rwq_bufs, CVMX_CACHE_LINE_SIZE);
if (mem == NULL) {
cvmx_dprintf("Out of memory initializing sso pool\n");
return -1;
}
/* Make sure RWI/RWO is disabled. */
sso_cfg.u64 = cvmx_read_csr(CVMX_SSO_CFG);
sso_cfg.s.rwen = 0;
cvmx_write_csr(CVMX_SSO_CFG, sso_cfg.u64);
for (i = cvm_oct_sso_number_rwq_bufs - 8; i > 0; i--) {
cvmx_sso_rwq_psh_fptr_t fptr;
for (;;) {
fptr.u64 = cvmx_read_csr(CVMX_SSO_RWQ_PSH_FPTR);
if (!fptr.s.full)
break;
cvmx_wait(1000);
}
fptr.s.fptr = cvmx_ptr_to_phys(mem) >> 7;
cvmx_write_csr(CVMX_SSO_RWQ_PSH_FPTR, fptr.u64);
mem = mem + __CVMX_SSO_RWQ_SIZE;
}
for (i = 0; i < 8; i++) {
cvmx_sso_rwq_head_ptrx_t head_ptr;
cvmx_sso_rwq_tail_ptrx_t tail_ptr;
head_ptr.u64 = 0;
tail_ptr.u64 = 0;
head_ptr.s.ptr = cvmx_ptr_to_phys(mem) >> 7;
tail_ptr.s.ptr = head_ptr.s.ptr;
cvmx_write_csr(CVMX_SSO_RWQ_HEAD_PTRX(i), head_ptr.u64);
cvmx_write_csr(CVMX_SSO_RWQ_TAIL_PTRX(i), tail_ptr.u64);
mem = mem + __CVMX_SSO_RWQ_SIZE;
}
sso_cfg.u64 = cvmx_read_csr(CVMX_SSO_CFG);
sso_cfg.s.rwen = 1;
sso_cfg.s.dwb = cvmx_helper_cfg_opt_get(CVMX_HELPER_CFG_OPT_USE_DWB);
sso_cfg.s.rwq_byp_dis = 0;
sso_cfg.s.rwio_byp_dis = 0;
cvmx_write_csr(CVMX_SSO_CFG, sso_cfg.u64);
return 0;
}
int cvmx_helper_uninitialize_sso(void)
{
cvmx_fpa_quex_available_t queue_available;
cvmx_sso_cfg_t sso_cfg;
cvmx_sso_rwq_pop_fptr_t pop_fptr;
cvmx_sso_rwq_psh_fptr_t fptr;
cvmx_sso_fpage_cnt_t fpage_cnt;
int num_to_transfer, i;
char *mem;
if (!OCTEON_IS_MODEL(OCTEON_CN68XX))
return 0;
sso_cfg.u64 = cvmx_read_csr(CVMX_SSO_CFG);
sso_cfg.s.rwen = 0;
sso_cfg.s.rwq_byp_dis = 1;
cvmx_write_csr(CVMX_SSO_CFG, sso_cfg.u64);
cvmx_read_csr(CVMX_SSO_CFG);
queue_available.u64 = cvmx_read_csr(CVMX_FPA_QUEX_AVAILABLE(8));
/* Make CVMX_FPA_QUEX_AVAILABLE(8) % 16 == 0*/
for (num_to_transfer = (16 - queue_available.s.que_siz) % 16;
num_to_transfer > 0; num_to_transfer--) {
do {
pop_fptr.u64 = cvmx_read_csr(CVMX_SSO_RWQ_POP_FPTR);
} while (!pop_fptr.s.val);
for (;;) {
fptr.u64 = cvmx_read_csr(CVMX_SSO_RWQ_PSH_FPTR);
if (!fptr.s.full)
break;
cvmx_wait(1000);
}
fptr.s.fptr = pop_fptr.s.fptr;
cvmx_write_csr(CVMX_SSO_RWQ_PSH_FPTR, fptr.u64);
}
cvmx_read_csr(CVMX_SSO_CFG);
do {
queue_available.u64 = cvmx_read_csr(CVMX_FPA_QUEX_AVAILABLE(8));
} while (queue_available.s.que_siz % 16);
sso_cfg.s.rwen = 1;
sso_cfg.s.rwq_byp_dis = 0;
cvmx_write_csr(CVMX_SSO_CFG, sso_cfg.u64);
for (i = 0; i < 8; i++) {
cvmx_sso_rwq_head_ptrx_t head_ptr;
cvmx_sso_rwq_tail_ptrx_t tail_ptr;
head_ptr.u64 = cvmx_read_csr(CVMX_SSO_RWQ_HEAD_PTRX(i));
tail_ptr.u64 = cvmx_read_csr(CVMX_SSO_RWQ_TAIL_PTRX(i));
if (head_ptr.s.ptr != tail_ptr.s.ptr) {
cvmx_dprintf("head_ptr.s.ptr != tail_ptr.s.ptr, idx: %d\n", i);
}
mem = cvmx_phys_to_ptr(((uint64_t)head_ptr.s.ptr) << 7);
/* Leak the memory */
}
do {
do {
pop_fptr.u64 = cvmx_read_csr(CVMX_SSO_RWQ_POP_FPTR);
if (pop_fptr.s.val) {
mem = cvmx_phys_to_ptr(((uint64_t)pop_fptr.s.fptr) << 7);
/* Leak the memory */
}
} while (pop_fptr.s.val);
fpage_cnt.u64 = cvmx_read_csr(CVMX_SSO_FPAGE_CNT);
} while (fpage_cnt.s.fpage_cnt);
sso_cfg.s.rwen = 0;
sso_cfg.s.rwq_byp_dis = 0;
cvmx_write_csr(CVMX_SSO_CFG, sso_cfg.u64);
return 0;
}
/**
* Initialize the PIP, IPD, and PKO hardware to support
* simple priority based queues for the ethernet ports. Each
* port is configured with a number of priority queues based
* on CVMX_PKO_QUEUES_PER_PORT_* where each queue is lower
* priority than the previous.
*
* @return Zero on success, non-zero on failure
*/
int cvmx_helper_initialize_packet_io_global(void)
{
int result = 0;
int interface;
cvmx_l2c_cfg_t l2c_cfg;
cvmx_smix_en_t smix_en;
const int num_interfaces = cvmx_helper_get_number_of_interfaces();
/* CN52XX pass 1: Due to a bug in 2nd order CDR, it needs to be disabled */
if (OCTEON_IS_MODEL(OCTEON_CN52XX_PASS1_0))
__cvmx_helper_errata_qlm_disable_2nd_order_cdr(1);
/* Tell L2 to give the IOB statically higher priority compared to the
cores. This avoids conditions where IO blocks might be starved under
very high L2 loads */
if (OCTEON_IS_MODEL(OCTEON_CN6XXX) || OCTEON_IS_MODEL(OCTEON_CNF7XXX))
{
cvmx_l2c_ctl_t l2c_ctl;
l2c_ctl.u64 = cvmx_read_csr(CVMX_L2C_CTL);
l2c_ctl.s.rsp_arb_mode = 1;
l2c_ctl.s.xmc_arb_mode = 0;
cvmx_write_csr(CVMX_L2C_CTL, l2c_ctl.u64);
}
else
{
l2c_cfg.u64 = cvmx_read_csr(CVMX_L2C_CFG);
l2c_cfg.s.lrf_arb_mode = 0;
l2c_cfg.s.rfb_arb_mode = 0;
cvmx_write_csr(CVMX_L2C_CFG, l2c_cfg.u64);
}
if (cvmx_sysinfo_get()->board_type != CVMX_BOARD_TYPE_SIM)
{
int smi_inf = 1;
int i;
/* Newer chips have more than one SMI/MDIO interface */
if (OCTEON_IS_MODEL(OCTEON_CN68XX))
smi_inf = 4;
else if (!OCTEON_IS_MODEL(OCTEON_CN3XXX)
&& !OCTEON_IS_MODEL(OCTEON_CN58XX)
&& !OCTEON_IS_MODEL(OCTEON_CN50XX))
smi_inf = 2;
for (i = 0; i < smi_inf; i++)
{
/* Make sure SMI/MDIO is enabled so we can query PHYs */
smix_en.u64 = cvmx_read_csr(CVMX_SMIX_EN(i));
if (!smix_en.s.en)
{
smix_en.s.en = 1;
cvmx_write_csr(CVMX_SMIX_EN(i), smix_en.u64);
}
}
}
__cvmx_helper_cfg_init();
for (interface=0; interface 0)
cvmx_dprintf("Interface %d has %d ports (%s)\n",
interface, cvmx_helper_ports_on_interface(interface),
cvmx_helper_interface_mode_to_string(cvmx_helper_interface_get_mode(interface)));
result |= __cvmx_helper_interface_setup_ipd(interface);
if (!OCTEON_IS_MODEL(OCTEON_CN68XX))
result |= __cvmx_helper_interface_setup_pko(interface);
}
result |= __cvmx_helper_global_setup_ipd();
result |= __cvmx_helper_global_setup_pko();
/* Enable any flow control and backpressure */
result |= __cvmx_helper_global_setup_backpressure();
#if CVMX_HELPER_ENABLE_IPD
result |= cvmx_helper_ipd_and_packet_input_enable();
#endif
return result;
}
#ifdef CVMX_BUILD_FOR_LINUX_KERNEL
EXPORT_SYMBOL(cvmx_helper_initialize_packet_io_global);
#endif
/**
* Does core local initialization for packet io
*
* @return Zero on success, non-zero on failure
*/
int cvmx_helper_initialize_packet_io_local(void)
{
return cvmx_pko_initialize_local();
}
/**
* wait for the pko queue to drain
*
* @param queue a valid pko queue
* @return count is the length of the queue after calling this
* function
*/
static int cvmx_helper_wait_pko_queue_drain(int queue)
{
const int timeout = 5; /* Wait up to 5 seconds for timeouts */
int count;
uint64_t start_cycle, stop_cycle;
count = cvmx_cmd_queue_length(CVMX_CMD_QUEUE_PKO(queue));
start_cycle = cvmx_get_cycle();
stop_cycle = start_cycle + cvmx_clock_get_rate(CVMX_CLOCK_CORE) * timeout;
while (count && (cvmx_get_cycle() < stop_cycle))
{
cvmx_wait(10000);
count = cvmx_cmd_queue_length(CVMX_CMD_QUEUE_PKO(queue));
}
return count;
}
struct cvmx_buffer_list {
struct cvmx_buffer_list *next;
};
/**
* Undo the initialization performed in
* cvmx_helper_initialize_packet_io_global(). After calling this routine and the
* local version on each core, packet IO for Octeon will be disabled and placed
* in the initial reset state. It will then be safe to call the initialize
* later on. Note that this routine does not empty the FPA pools. It frees all
* buffers used by the packet IO hardware to the FPA so a function emptying the
* FPA after shutdown should find all packet buffers in the FPA.
*
* @return Zero on success, negative on failure.
*/
int cvmx_helper_shutdown_packet_io_global(void)
{
const int timeout = 5; /* Wait up to 5 seconds for timeouts */
int result = 0;
int num_interfaces;
int interface;
int num_ports;
int index;
struct cvmx_buffer_list *pool0_buffers;
struct cvmx_buffer_list *pool0_buffers_tail;
cvmx_wqe_t *work;
/* Step 1: Disable all backpressure */
for (interface=0; interface 4)
num_ports = 4;
for (index=0; index 4)
num_ports = 4;
for (index=0; index 4)
num_ports = 4;
for (index=0; indexnext = NULL;
if (pool0_buffers == NULL)
pool0_buffers = buffer;
else
pool0_buffers_tail->next = buffer;
pool0_buffers_tail = buffer;
}
else
break;
}
/* Step 10: Reset IPD and PIP */
{
cvmx_ipd_ctl_status_t ipd_ctl_status;
ipd_ctl_status.u64 = cvmx_read_csr(CVMX_IPD_CTL_STATUS);
ipd_ctl_status.s.reset = 1;
cvmx_write_csr(CVMX_IPD_CTL_STATUS, ipd_ctl_status.u64);
if ((cvmx_sysinfo_get()->board_type != CVMX_BOARD_TYPE_SIM) &&
(OCTEON_IS_MODEL(OCTEON_CN3XXX) || OCTEON_IS_MODEL(OCTEON_CN5XXX)))
{
/* only try 1000 times. Normally if this works it will happen in
** the first 50 loops. */
int max_loops = 1000;
int loop = 0;
/* Per port backpressure counters can get misaligned after an
IPD reset. This code realigns them by performing repeated
resets. See IPD-13473 */
cvmx_wait(100);
if (__cvmx_helper_backpressure_is_misaligned())
{
cvmx_dprintf("Starting to align per port backpressure counters.\n");
while (__cvmx_helper_backpressure_is_misaligned() && (loop++ < max_loops))
{
cvmx_write_csr(CVMX_IPD_CTL_STATUS, ipd_ctl_status.u64);
cvmx_wait(123);
}
if (loop < max_loops)
cvmx_dprintf("Completed aligning per port backpressure counters (%d loops).\n", loop);
else
{
cvmx_dprintf("ERROR: unable to align per port backpressure counters.\n");
/* For now, don't hang.... */
}
}
}
/* PIP_SFT_RST not present in CN38XXp{1,2} */
if (!OCTEON_IS_MODEL(OCTEON_CN38XX_PASS2))
{
cvmx_pip_sft_rst_t pip_sft_rst;
pip_sft_rst.u64 = cvmx_read_csr(CVMX_PIP_SFT_RST);
pip_sft_rst.s.rst = 1;
cvmx_write_csr(CVMX_PIP_SFT_RST, pip_sft_rst.u64);
}
/* Make sure IPD has finished reset. */
if (OCTEON_IS_MODEL(OCTEON_CN6XXX))
{
if (CVMX_WAIT_FOR_FIELD64(CVMX_IPD_CTL_STATUS, cvmx_ipd_ctl_status_t, rst_done, ==, 0, 1000))
{
cvmx_dprintf("IPD reset timeout waiting for idle\n");
result = -1;
}
}
}
/* Step 11: Restore the FPA buffers into pool 0 */
while (pool0_buffers) {
struct cvmx_buffer_list *n = pool0_buffers->next;
cvmx_fpa_free(pool0_buffers, 0, 0);
pool0_buffers = n;
}
/* Step 12: Release interface structures */
__cvmx_helper_shutdown_interfaces();
return result;
}
#ifdef CVMX_BUILD_FOR_LINUX_KERNEL
EXPORT_SYMBOL(cvmx_helper_shutdown_packet_io_global);
#endif
/**
* Does core local shutdown of packet io
*
* @return Zero on success, non-zero on failure
*/
int cvmx_helper_shutdown_packet_io_local(void)
{
/* Currently there is nothing to do per core. This may change in
the future */
return 0;
}
/**
* Auto configure an IPD/PKO port link state and speed. This
* function basically does the equivalent of:
* cvmx_helper_link_set(ipd_port, cvmx_helper_link_get(ipd_port));
*
* @param ipd_port IPD/PKO port to auto configure
*
* @return Link state after configure
*/
cvmx_helper_link_info_t cvmx_helper_link_autoconf(int ipd_port)
{
cvmx_helper_link_info_t link_info;
int interface = cvmx_helper_get_interface_num(ipd_port);
int index = cvmx_helper_get_interface_index_num(ipd_port);
if (index >= cvmx_helper_ports_on_interface(interface))
{
link_info.u64 = 0;
return link_info;
}
link_info = cvmx_helper_link_get(ipd_port);
if (link_info.u64 == (__cvmx_helper_get_link_info(interface, index)).u64)
return link_info;
#if !defined(CVMX_BUILD_FOR_LINUX_KERNEL) && !defined(CVMX_BUILD_FOR_FREEBSD_KERNEL)
if (!link_info.s.link_up)
cvmx_error_disable_group(CVMX_ERROR_GROUP_ETHERNET, ipd_port);
#endif
/* If we fail to set the link speed, port_link_info will not change */
cvmx_helper_link_set(ipd_port, link_info);
#if !defined(CVMX_BUILD_FOR_LINUX_KERNEL) && !defined(CVMX_BUILD_FOR_FREEBSD_KERNEL)
if (link_info.s.link_up)
cvmx_error_enable_group(CVMX_ERROR_GROUP_ETHERNET, ipd_port);
#endif
return link_info;
}
#ifdef CVMX_BUILD_FOR_LINUX_KERNEL
EXPORT_SYMBOL(cvmx_helper_link_autoconf);
#endif
/**
* Return the link state of an IPD/PKO port as returned by
* auto negotiation. The result of this function may not match
* Octeon's link config if auto negotiation has changed since
* the last call to cvmx_helper_link_set().
*
* @param ipd_port IPD/PKO port to query
*
* @return Link state
*/
cvmx_helper_link_info_t cvmx_helper_link_get(int ipd_port)
{
cvmx_helper_link_info_t result;
int interface = cvmx_helper_get_interface_num(ipd_port);
int index = cvmx_helper_get_interface_index_num(ipd_port);
/* The default result will be a down link unless the code below
changes it */
result.u64 = 0;
if (index >= cvmx_helper_ports_on_interface(interface))
return result;
switch (cvmx_helper_interface_get_mode(interface))
{
case CVMX_HELPER_INTERFACE_MODE_DISABLED:
case CVMX_HELPER_INTERFACE_MODE_PCIE:
/* Network links are not supported */
break;
case CVMX_HELPER_INTERFACE_MODE_XAUI:
case CVMX_HELPER_INTERFACE_MODE_RXAUI:
result = __cvmx_helper_xaui_link_get(ipd_port);
break;
case CVMX_HELPER_INTERFACE_MODE_GMII:
if (index == 0)
result = __cvmx_helper_rgmii_link_get(ipd_port);
else
{
result.s.full_duplex = 1;
result.s.link_up = 1;
result.s.speed = 1000;
}
break;
case CVMX_HELPER_INTERFACE_MODE_RGMII:
result = __cvmx_helper_rgmii_link_get(ipd_port);
break;
case CVMX_HELPER_INTERFACE_MODE_SPI:
result = __cvmx_helper_spi_link_get(ipd_port);
break;
case CVMX_HELPER_INTERFACE_MODE_SGMII:
case CVMX_HELPER_INTERFACE_MODE_PICMG:
result = __cvmx_helper_sgmii_link_get(ipd_port);
break;
case CVMX_HELPER_INTERFACE_MODE_SRIO:
result = __cvmx_helper_srio_link_get(ipd_port);
break;
case CVMX_HELPER_INTERFACE_MODE_ILK:
result = __cvmx_helper_ilk_link_get(ipd_port);
break;
case CVMX_HELPER_INTERFACE_MODE_NPI:
case CVMX_HELPER_INTERFACE_MODE_LOOP:
/* Network links are not supported */
break;
}
return result;
}
#ifdef CVMX_BUILD_FOR_LINUX_KERNEL
EXPORT_SYMBOL(cvmx_helper_link_get);
#endif
/**
* Configure an IPD/PKO port for the specified link state. This
* function does not influence auto negotiation at the PHY level.
* The passed link state must always match the link state returned
* by cvmx_helper_link_get(). It is normally best to use
* cvmx_helper_link_autoconf() instead.
*
* @param ipd_port IPD/PKO port to configure
* @param link_info The new link state
*
* @return Zero on success, negative on failure
*/
int cvmx_helper_link_set(int ipd_port, cvmx_helper_link_info_t link_info)
{
int result = -1;
int interface = cvmx_helper_get_interface_num(ipd_port);
int index = cvmx_helper_get_interface_index_num(ipd_port);
if (index >= cvmx_helper_ports_on_interface(interface))
return -1;
switch (cvmx_helper_interface_get_mode(interface))
{
case CVMX_HELPER_INTERFACE_MODE_DISABLED:
case CVMX_HELPER_INTERFACE_MODE_PCIE:
break;
case CVMX_HELPER_INTERFACE_MODE_XAUI:
case CVMX_HELPER_INTERFACE_MODE_RXAUI:
result = __cvmx_helper_xaui_link_set(ipd_port, link_info);
break;
/* RGMII/GMII/MII are all treated about the same. Most functions
refer to these ports as RGMII */
case CVMX_HELPER_INTERFACE_MODE_RGMII:
case CVMX_HELPER_INTERFACE_MODE_GMII:
result = __cvmx_helper_rgmii_link_set(ipd_port, link_info);
break;
case CVMX_HELPER_INTERFACE_MODE_SPI:
result = __cvmx_helper_spi_link_set(ipd_port, link_info);
break;
case CVMX_HELPER_INTERFACE_MODE_SGMII:
case CVMX_HELPER_INTERFACE_MODE_PICMG:
result = __cvmx_helper_sgmii_link_set(ipd_port, link_info);
break;
case CVMX_HELPER_INTERFACE_MODE_SRIO:
result = __cvmx_helper_srio_link_set(ipd_port, link_info);
break;
case CVMX_HELPER_INTERFACE_MODE_ILK:
result = __cvmx_helper_ilk_link_set(ipd_port, link_info);
break;
case CVMX_HELPER_INTERFACE_MODE_NPI:
case CVMX_HELPER_INTERFACE_MODE_LOOP:
break;
}
/* Set the port_link_info here so that the link status is updated
no matter how cvmx_helper_link_set is called. We don't change
the value if link_set failed */
if (result == 0)
__cvmx_helper_set_link_info(interface, index, link_info);
return result;
}
#ifdef CVMX_BUILD_FOR_LINUX_KERNEL
EXPORT_SYMBOL(cvmx_helper_link_set);
#endif
/**
* Configure a port for internal and/or external loopback. Internal loopback
* causes packets sent by the port to be received by Octeon. External loopback
* causes packets received from the wire to sent out again.
*
* @param ipd_port IPD/PKO port to loopback.
* @param enable_internal
* Non zero if you want internal loopback
* @param enable_external
* Non zero if you want external loopback
*
* @return Zero on success, negative on failure.
*/
int cvmx_helper_configure_loopback(int ipd_port, int enable_internal, int enable_external)
{
int result = -1;
int interface = cvmx_helper_get_interface_num(ipd_port);
int index = cvmx_helper_get_interface_index_num(ipd_port);
if (index >= cvmx_helper_ports_on_interface(interface))
return -1;
switch (cvmx_helper_interface_get_mode(interface))
{
case CVMX_HELPER_INTERFACE_MODE_DISABLED:
case CVMX_HELPER_INTERFACE_MODE_PCIE:
case CVMX_HELPER_INTERFACE_MODE_SRIO:
case CVMX_HELPER_INTERFACE_MODE_ILK:
case CVMX_HELPER_INTERFACE_MODE_SPI:
case CVMX_HELPER_INTERFACE_MODE_NPI:
case CVMX_HELPER_INTERFACE_MODE_LOOP:
break;
case CVMX_HELPER_INTERFACE_MODE_XAUI:
case CVMX_HELPER_INTERFACE_MODE_RXAUI:
result = __cvmx_helper_xaui_configure_loopback(ipd_port, enable_internal, enable_external);
break;
case CVMX_HELPER_INTERFACE_MODE_RGMII:
case CVMX_HELPER_INTERFACE_MODE_GMII:
result = __cvmx_helper_rgmii_configure_loopback(ipd_port, enable_internal, enable_external);
break;
case CVMX_HELPER_INTERFACE_MODE_SGMII:
case CVMX_HELPER_INTERFACE_MODE_PICMG:
result = __cvmx_helper_sgmii_configure_loopback(ipd_port, enable_internal, enable_external);
break;
}
return result;
}
#endif /* CVMX_ENABLE_PKO_FUNCTIONS */
Index: head/sys/contrib/octeon-sdk/cvmx-ipd.c
===================================================================
--- head/sys/contrib/octeon-sdk/cvmx-ipd.c (revision 232815)
+++ head/sys/contrib/octeon-sdk/cvmx-ipd.c (revision 232816)
@@ -1,318 +1,318 @@
/***********************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
*
* IPD Support.
*
*
$Revision: 58943 $
*/
#ifdef CVMX_BUILD_FOR_LINUX_KERNEL
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#else
#if !defined(__FreeBSD__) || !defined(_KERNEL)
#include "executive-config.h"
#endif
#include "cvmx.h"
#include "cvmx-sysinfo.h"
#include "cvmx-bootmem.h"
#include "cvmx-version.h"
#include "cvmx-helper-check-defines.h"
-#include "cvmx-error.h"
#if !defined(__FreeBSD__) || !defined(_KERNEL)
+#include "cvmx-error.h"
#include "cvmx-config.h"
#endif
#include "cvmx-fpa.h"
#include "cvmx-wqe.h"
#include "cvmx-ipd.h"
#include "cvmx-helper-errata.h"
#include "cvmx-helper-cfg.h"
#endif
#ifdef CVMX_ENABLE_PKO_FUNCTIONS
static void __cvmx_ipd_free_ptr_v1(void)
{
/* Only CN38XXp{1,2} cannot read pointer out of the IPD */
if (!OCTEON_IS_MODEL(OCTEON_CN38XX_PASS2)) {
int no_wptr = 0;
cvmx_ipd_ptr_count_t ipd_ptr_count;
ipd_ptr_count.u64 = cvmx_read_csr(CVMX_IPD_PTR_COUNT);
/* Handle Work Queue Entry in cn56xx and cn52xx */
if (octeon_has_feature(OCTEON_FEATURE_NO_WPTR)) {
cvmx_ipd_ctl_status_t ipd_ctl_status;
ipd_ctl_status.u64 = cvmx_read_csr(CVMX_IPD_CTL_STATUS);
if (ipd_ctl_status.s.no_wptr)
no_wptr = 1;
}
/* Free the prefetched WQE */
if (ipd_ptr_count.s.wqev_cnt) {
cvmx_ipd_wqe_ptr_valid_t ipd_wqe_ptr_valid;
ipd_wqe_ptr_valid.u64 = cvmx_read_csr(CVMX_IPD_WQE_PTR_VALID);
if (no_wptr)
cvmx_fpa_free(cvmx_phys_to_ptr((uint64_t)ipd_wqe_ptr_valid.s.ptr<<7), CVMX_FPA_PACKET_POOL, 0);
else
cvmx_fpa_free(cvmx_phys_to_ptr((uint64_t)ipd_wqe_ptr_valid.s.ptr<<7), CVMX_FPA_WQE_POOL, 0);
}
/* Free all WQE in the fifo */
if (ipd_ptr_count.s.wqe_pcnt) {
int i;
cvmx_ipd_pwp_ptr_fifo_ctl_t ipd_pwp_ptr_fifo_ctl;
ipd_pwp_ptr_fifo_ctl.u64 = cvmx_read_csr(CVMX_IPD_PWP_PTR_FIFO_CTL);
for (i = 0; i < ipd_ptr_count.s.wqe_pcnt; i++) {
ipd_pwp_ptr_fifo_ctl.s.cena = 0;
ipd_pwp_ptr_fifo_ctl.s.raddr = ipd_pwp_ptr_fifo_ctl.s.max_cnts + (ipd_pwp_ptr_fifo_ctl.s.wraddr+i) % ipd_pwp_ptr_fifo_ctl.s.max_cnts;
cvmx_write_csr(CVMX_IPD_PWP_PTR_FIFO_CTL, ipd_pwp_ptr_fifo_ctl.u64);
ipd_pwp_ptr_fifo_ctl.u64 = cvmx_read_csr(CVMX_IPD_PWP_PTR_FIFO_CTL);
if (no_wptr)
cvmx_fpa_free(cvmx_phys_to_ptr((uint64_t)ipd_pwp_ptr_fifo_ctl.s.ptr<<7), CVMX_FPA_PACKET_POOL, 0);
else
cvmx_fpa_free(cvmx_phys_to_ptr((uint64_t)ipd_pwp_ptr_fifo_ctl.s.ptr<<7), CVMX_FPA_WQE_POOL, 0);
}
ipd_pwp_ptr_fifo_ctl.s.cena = 1;
cvmx_write_csr(CVMX_IPD_PWP_PTR_FIFO_CTL, ipd_pwp_ptr_fifo_ctl.u64);
}
/* Free the prefetched packet */
if (ipd_ptr_count.s.pktv_cnt) {
cvmx_ipd_pkt_ptr_valid_t ipd_pkt_ptr_valid;
ipd_pkt_ptr_valid.u64 = cvmx_read_csr(CVMX_IPD_PKT_PTR_VALID);
cvmx_fpa_free(cvmx_phys_to_ptr((uint64_t)ipd_pkt_ptr_valid.s.ptr<<7), CVMX_FPA_PACKET_POOL, 0);
}
/* Free the per port prefetched packets */
if (1) {
int i;
cvmx_ipd_prc_port_ptr_fifo_ctl_t ipd_prc_port_ptr_fifo_ctl;
ipd_prc_port_ptr_fifo_ctl.u64 = cvmx_read_csr(CVMX_IPD_PRC_PORT_PTR_FIFO_CTL);
for (i = 0; i < ipd_prc_port_ptr_fifo_ctl.s.max_pkt; i++) {
ipd_prc_port_ptr_fifo_ctl.s.cena = 0;
ipd_prc_port_ptr_fifo_ctl.s.raddr = i % ipd_prc_port_ptr_fifo_ctl.s.max_pkt;
cvmx_write_csr(CVMX_IPD_PRC_PORT_PTR_FIFO_CTL, ipd_prc_port_ptr_fifo_ctl.u64);
ipd_prc_port_ptr_fifo_ctl.u64 = cvmx_read_csr(CVMX_IPD_PRC_PORT_PTR_FIFO_CTL);
cvmx_fpa_free(cvmx_phys_to_ptr((uint64_t)ipd_prc_port_ptr_fifo_ctl.s.ptr<<7), CVMX_FPA_PACKET_POOL, 0);
}
ipd_prc_port_ptr_fifo_ctl.s.cena = 1;
cvmx_write_csr(CVMX_IPD_PRC_PORT_PTR_FIFO_CTL, ipd_prc_port_ptr_fifo_ctl.u64);
}
/* Free all packets in the holding fifo */
if (ipd_ptr_count.s.pfif_cnt) {
int i;
cvmx_ipd_prc_hold_ptr_fifo_ctl_t ipd_prc_hold_ptr_fifo_ctl;
ipd_prc_hold_ptr_fifo_ctl.u64 = cvmx_read_csr(CVMX_IPD_PRC_HOLD_PTR_FIFO_CTL);
for (i = 0; i < ipd_ptr_count.s.pfif_cnt; i++) {
ipd_prc_hold_ptr_fifo_ctl.s.cena = 0;
ipd_prc_hold_ptr_fifo_ctl.s.raddr = (ipd_prc_hold_ptr_fifo_ctl.s.praddr + i) % ipd_prc_hold_ptr_fifo_ctl.s.max_pkt;
cvmx_write_csr(CVMX_IPD_PRC_HOLD_PTR_FIFO_CTL, ipd_prc_hold_ptr_fifo_ctl.u64);
ipd_prc_hold_ptr_fifo_ctl.u64 = cvmx_read_csr(CVMX_IPD_PRC_HOLD_PTR_FIFO_CTL);
cvmx_fpa_free(cvmx_phys_to_ptr((uint64_t)ipd_prc_hold_ptr_fifo_ctl.s.ptr<<7), CVMX_FPA_PACKET_POOL, 0);
}
ipd_prc_hold_ptr_fifo_ctl.s.cena = 1;
cvmx_write_csr(CVMX_IPD_PRC_HOLD_PTR_FIFO_CTL, ipd_prc_hold_ptr_fifo_ctl.u64);
}
/* Free all packets in the fifo */
if (ipd_ptr_count.s.pkt_pcnt) {
int i;
cvmx_ipd_pwp_ptr_fifo_ctl_t ipd_pwp_ptr_fifo_ctl;
ipd_pwp_ptr_fifo_ctl.u64 = cvmx_read_csr(CVMX_IPD_PWP_PTR_FIFO_CTL);
for (i = 0; i < ipd_ptr_count.s.pkt_pcnt; i++) {
ipd_pwp_ptr_fifo_ctl.s.cena = 0;
ipd_pwp_ptr_fifo_ctl.s.raddr = (ipd_pwp_ptr_fifo_ctl.s.praddr+i) % ipd_pwp_ptr_fifo_ctl.s.max_cnts;
cvmx_write_csr(CVMX_IPD_PWP_PTR_FIFO_CTL, ipd_pwp_ptr_fifo_ctl.u64);
ipd_pwp_ptr_fifo_ctl.u64 = cvmx_read_csr(CVMX_IPD_PWP_PTR_FIFO_CTL);
cvmx_fpa_free(cvmx_phys_to_ptr((uint64_t)ipd_pwp_ptr_fifo_ctl.s.ptr<<7), CVMX_FPA_PACKET_POOL, 0);
}
ipd_pwp_ptr_fifo_ctl.s.cena = 1;
cvmx_write_csr(CVMX_IPD_PWP_PTR_FIFO_CTL, ipd_pwp_ptr_fifo_ctl.u64);
}
}
}
static void __cvmx_ipd_free_ptr_v2(void)
{
int no_wptr = 0;
int i;
cvmx_ipd_port_ptr_fifo_ctl_t ipd_port_ptr_fifo_ctl;
cvmx_ipd_ptr_count_t ipd_ptr_count;
ipd_ptr_count.u64 = cvmx_read_csr(CVMX_IPD_PTR_COUNT);
/* Handle Work Queue Entry in cn68xx */
if (octeon_has_feature(OCTEON_FEATURE_NO_WPTR)) {
cvmx_ipd_ctl_status_t ipd_ctl_status;
ipd_ctl_status.u64 = cvmx_read_csr(CVMX_IPD_CTL_STATUS);
if (ipd_ctl_status.s.no_wptr)
no_wptr = 1;
}
/* Free the prefetched WQE */
if (ipd_ptr_count.s.wqev_cnt) {
cvmx_ipd_next_wqe_ptr_t ipd_next_wqe_ptr;
ipd_next_wqe_ptr.u64 = cvmx_read_csr(CVMX_IPD_NEXT_WQE_PTR);
if (no_wptr)
cvmx_fpa_free(cvmx_phys_to_ptr((uint64_t)ipd_next_wqe_ptr.s.ptr<<7), CVMX_FPA_PACKET_POOL, 0);
else
cvmx_fpa_free(cvmx_phys_to_ptr((uint64_t)ipd_next_wqe_ptr.s.ptr<<7), CVMX_FPA_WQE_POOL, 0);
}
/* Free all WQE in the fifo */
if (ipd_ptr_count.s.wqe_pcnt) {
cvmx_ipd_free_ptr_fifo_ctl_t ipd_free_ptr_fifo_ctl;
cvmx_ipd_free_ptr_value_t ipd_free_ptr_value;
ipd_free_ptr_fifo_ctl.u64 = cvmx_read_csr(CVMX_IPD_FREE_PTR_FIFO_CTL);
for (i = 0; i < ipd_ptr_count.s.wqe_pcnt; i++) {
ipd_free_ptr_fifo_ctl.s.cena = 0;
ipd_free_ptr_fifo_ctl.s.raddr = ipd_free_ptr_fifo_ctl.s.max_cnts + (ipd_free_ptr_fifo_ctl.s.wraddr+i) % ipd_free_ptr_fifo_ctl.s.max_cnts;
cvmx_write_csr(CVMX_IPD_FREE_PTR_FIFO_CTL, ipd_free_ptr_fifo_ctl.u64);
ipd_free_ptr_fifo_ctl.u64 = cvmx_read_csr(CVMX_IPD_FREE_PTR_FIFO_CTL);
ipd_free_ptr_value.u64 = cvmx_read_csr(CVMX_IPD_FREE_PTR_VALUE);
if (no_wptr)
cvmx_fpa_free(cvmx_phys_to_ptr((uint64_t)ipd_free_ptr_value.s.ptr<<7), CVMX_FPA_PACKET_POOL, 0);
else
cvmx_fpa_free(cvmx_phys_to_ptr((uint64_t)ipd_free_ptr_value.s.ptr<<7), CVMX_FPA_WQE_POOL, 0);
}
ipd_free_ptr_fifo_ctl.s.cena = 1;
cvmx_write_csr(CVMX_IPD_FREE_PTR_FIFO_CTL, ipd_free_ptr_fifo_ctl.u64);
}
/* Free the prefetched packet */
if (ipd_ptr_count.s.pktv_cnt) {
cvmx_ipd_next_pkt_ptr_t ipd_next_pkt_ptr;
ipd_next_pkt_ptr.u64 = cvmx_read_csr(CVMX_IPD_NEXT_PKT_PTR);
cvmx_fpa_free(cvmx_phys_to_ptr((uint64_t)ipd_next_pkt_ptr.s.ptr<<7), CVMX_FPA_PACKET_POOL, 0);
}
/* Free the per port prefetched packets */
ipd_port_ptr_fifo_ctl.u64 = cvmx_read_csr(CVMX_IPD_PORT_PTR_FIFO_CTL);
for (i = 0; i < ipd_port_ptr_fifo_ctl.s.max_pkt; i++) {
ipd_port_ptr_fifo_ctl.s.cena = 0;
ipd_port_ptr_fifo_ctl.s.raddr = i % ipd_port_ptr_fifo_ctl.s.max_pkt;
cvmx_write_csr(CVMX_IPD_PORT_PTR_FIFO_CTL, ipd_port_ptr_fifo_ctl.u64);
ipd_port_ptr_fifo_ctl.u64 = cvmx_read_csr(CVMX_IPD_PORT_PTR_FIFO_CTL);
cvmx_fpa_free(cvmx_phys_to_ptr((uint64_t)ipd_port_ptr_fifo_ctl.s.ptr<<7), CVMX_FPA_PACKET_POOL, 0);
}
ipd_port_ptr_fifo_ctl.s.cena = 1;
cvmx_write_csr(CVMX_IPD_PORT_PTR_FIFO_CTL, ipd_port_ptr_fifo_ctl.u64);
/* Free all packets in the holding fifo */
if (ipd_ptr_count.s.pfif_cnt) {
cvmx_ipd_hold_ptr_fifo_ctl_t ipd_hold_ptr_fifo_ctl;
ipd_hold_ptr_fifo_ctl.u64 = cvmx_read_csr(CVMX_IPD_HOLD_PTR_FIFO_CTL);
for (i = 0; i < ipd_ptr_count.s.pfif_cnt; i++) {
ipd_hold_ptr_fifo_ctl.s.cena = 0;
ipd_hold_ptr_fifo_ctl.s.raddr = (ipd_hold_ptr_fifo_ctl.s.praddr + i) % ipd_hold_ptr_fifo_ctl.s.max_pkt;
cvmx_write_csr(CVMX_IPD_HOLD_PTR_FIFO_CTL, ipd_hold_ptr_fifo_ctl.u64);
ipd_hold_ptr_fifo_ctl.u64 = cvmx_read_csr(CVMX_IPD_HOLD_PTR_FIFO_CTL);
cvmx_fpa_free(cvmx_phys_to_ptr((uint64_t)ipd_hold_ptr_fifo_ctl.s.ptr<<7), CVMX_FPA_PACKET_POOL, 0);
}
ipd_hold_ptr_fifo_ctl.s.cena = 1;
cvmx_write_csr(CVMX_IPD_HOLD_PTR_FIFO_CTL, ipd_hold_ptr_fifo_ctl.u64);
}
/* Free all packets in the fifo */
if (ipd_ptr_count.s.pkt_pcnt) {
cvmx_ipd_free_ptr_fifo_ctl_t ipd_free_ptr_fifo_ctl;
cvmx_ipd_free_ptr_value_t ipd_free_ptr_value;
ipd_free_ptr_fifo_ctl.u64 = cvmx_read_csr(CVMX_IPD_FREE_PTR_FIFO_CTL);
for (i = 0; i < ipd_ptr_count.s.pkt_pcnt; i++) {
ipd_free_ptr_fifo_ctl.s.cena = 0;
ipd_free_ptr_fifo_ctl.s.raddr = (ipd_free_ptr_fifo_ctl.s.praddr+i) % ipd_free_ptr_fifo_ctl.s.max_cnts;
cvmx_write_csr(CVMX_IPD_FREE_PTR_FIFO_CTL, ipd_free_ptr_fifo_ctl.u64);
ipd_free_ptr_fifo_ctl.u64 = cvmx_read_csr(CVMX_IPD_FREE_PTR_FIFO_CTL);
ipd_free_ptr_value.u64 = cvmx_read_csr(CVMX_IPD_FREE_PTR_VALUE);
cvmx_fpa_free(cvmx_phys_to_ptr((uint64_t)ipd_free_ptr_value.s.ptr<<7), CVMX_FPA_PACKET_POOL, 0);
}
ipd_free_ptr_fifo_ctl.s.cena = 1;
cvmx_write_csr(CVMX_IPD_FREE_PTR_FIFO_CTL, ipd_free_ptr_fifo_ctl.u64);
}
}
/**
* @INTERNAL
* This function is called by cvmx_helper_shutdown() to extract
* all FPA buffers out of the IPD and PIP. After this function
* completes, all FPA buffers that were prefetched by IPD and PIP
* wil be in the apropriate FPA pool. This functions does not reset
* PIP or IPD as FPA pool zero must be empty before the reset can
* be performed. WARNING: It is very important that IPD and PIP be
* reset soon after a call to this function.
*/
void __cvmx_ipd_free_ptr(void)
{
if (octeon_has_feature(OCTEON_FEATURE_PKND))
__cvmx_ipd_free_ptr_v2();
else
__cvmx_ipd_free_ptr_v1();
}
#endif
Index: head/sys/contrib/octeon-sdk/cvmx-mgmt-port.c
===================================================================
--- head/sys/contrib/octeon-sdk/cvmx-mgmt-port.c (revision 232815)
+++ head/sys/contrib/octeon-sdk/cvmx-mgmt-port.c (revision 232816)
@@ -1,1008 +1,1014 @@
/***********************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
*
* Support functions for managing the MII management port
*
*
$Revision: 70030 $
*/
#include "cvmx.h"
#include "cvmx-bootmem.h"
#include "cvmx-spinlock.h"
#include "cvmx-mdio.h"
#include "cvmx-mgmt-port.h"
#include "cvmx-sysinfo.h"
+#if !defined(CVMX_BUILD_FOR_FREEBSD_KERNEL)
#include "cvmx-error.h"
+#endif
/**
* Enum of MIX interface modes
*/
typedef enum
{
CVMX_MGMT_PORT_NONE = 0,
CVMX_MGMT_PORT_MII_MODE,
CVMX_MGMT_PORT_RGMII_MODE,
} cvmx_mgmt_port_mode_t;
/**
* Format of the TX/RX ring buffer entries
*/
typedef union
{
uint64_t u64;
struct
{
uint64_t reserved_62_63 : 2;
uint64_t len : 14; /* Length of the buffer/packet in bytes */
uint64_t tstamp : 1; /* For TX, signals that the packet should be timestamped */
uint64_t code : 7; /* The RX error code */
uint64_t addr : 40; /* Physical address of the buffer */
} s;
} cvmx_mgmt_port_ring_entry_t;
/**
* Per port state required for each mgmt port
*/
typedef struct
{
cvmx_spinlock_t lock; /* Used for exclusive access to this structure */
int tx_write_index; /* Where the next TX will write in the tx_ring and tx_buffers */
int rx_read_index; /* Where the next RX will be in the rx_ring and rx_buffers */
int port; /* Port to use. (This is the 'fake' IPD port number */
uint64_t mac; /* Our MAC address */
cvmx_mgmt_port_ring_entry_t tx_ring[CVMX_MGMT_PORT_NUM_TX_BUFFERS];
cvmx_mgmt_port_ring_entry_t rx_ring[CVMX_MGMT_PORT_NUM_RX_BUFFERS];
char tx_buffers[CVMX_MGMT_PORT_NUM_TX_BUFFERS][CVMX_MGMT_PORT_TX_BUFFER_SIZE];
char rx_buffers[CVMX_MGMT_PORT_NUM_RX_BUFFERS][CVMX_MGMT_PORT_RX_BUFFER_SIZE];
cvmx_mgmt_port_mode_t mode; /* Mode of the interface */
} cvmx_mgmt_port_state_t;
/**
* Pointers to each mgmt port's state
*/
CVMX_SHARED cvmx_mgmt_port_state_t *cvmx_mgmt_port_state_ptr = NULL;
/**
* Return the number of management ports supported by this chip
*
* @return Number of ports
*/
static int __cvmx_mgmt_port_num_ports(void)
{
if (OCTEON_IS_MODEL(OCTEON_CN56XX) || OCTEON_IS_MODEL(OCTEON_CN68XX))
return 1;
else if (OCTEON_IS_MODEL(OCTEON_CN52XX) || OCTEON_IS_MODEL(OCTEON_CN6XXX))
return 2;
else
return 0;
}
/**
* Called to initialize a management port for use. Multiple calls
* to this function across applications is safe.
*
* @param port Port to initialize
*
* @return CVMX_MGMT_PORT_SUCCESS or an error code
*/
cvmx_mgmt_port_result_t cvmx_mgmt_port_initialize(int port)
{
char *alloc_name = "cvmx_mgmt_port";
cvmx_mixx_oring1_t oring1;
cvmx_mixx_ctl_t mix_ctl;
if ((port < 0) || (port >= __cvmx_mgmt_port_num_ports()))
return CVMX_MGMT_PORT_INVALID_PARAM;
cvmx_mgmt_port_state_ptr = cvmx_bootmem_alloc_named_flags(CVMX_MGMT_PORT_NUM_PORTS * sizeof(cvmx_mgmt_port_state_t), 128, alloc_name, CVMX_BOOTMEM_FLAG_END_ALLOC);
if (cvmx_mgmt_port_state_ptr)
{
memset(cvmx_mgmt_port_state_ptr, 0, CVMX_MGMT_PORT_NUM_PORTS * sizeof(cvmx_mgmt_port_state_t));
}
else
{
const cvmx_bootmem_named_block_desc_t *block_desc = cvmx_bootmem_find_named_block(alloc_name);
if (block_desc)
cvmx_mgmt_port_state_ptr = cvmx_phys_to_ptr(block_desc->base_addr);
else
{
cvmx_dprintf("ERROR: cvmx_mgmt_port_initialize: Unable to get named block %s on MIX%d.\n", alloc_name, port);
return CVMX_MGMT_PORT_NO_MEMORY;
}
}
/* Reset the MIX block if the previous user had a different TX ring size, or if
** we allocated a new (and blank) state structure. */
mix_ctl.u64 = cvmx_read_csr(CVMX_MIXX_CTL(port));
if (!mix_ctl.s.reset)
{
oring1.u64 = cvmx_read_csr(CVMX_MIXX_ORING1(port));
if (oring1.s.osize != CVMX_MGMT_PORT_NUM_TX_BUFFERS || cvmx_mgmt_port_state_ptr[port].tx_ring[0].u64 == 0)
{
mix_ctl.u64 = cvmx_read_csr(CVMX_MIXX_CTL(port));
mix_ctl.s.en = 0;
cvmx_write_csr(CVMX_MIXX_CTL(port), mix_ctl.u64);
do
{
mix_ctl.u64 = cvmx_read_csr(CVMX_MIXX_CTL(port));
} while (mix_ctl.s.busy);
mix_ctl.s.reset = 1;
cvmx_write_csr(CVMX_MIXX_CTL(port), mix_ctl.u64);
cvmx_read_csr(CVMX_MIXX_CTL(port));
memset(cvmx_mgmt_port_state_ptr + port, 0, sizeof(cvmx_mgmt_port_state_t));
}
}
if (cvmx_mgmt_port_state_ptr[port].tx_ring[0].u64 == 0)
{
cvmx_mgmt_port_state_t *state = cvmx_mgmt_port_state_ptr + port;
int i;
cvmx_mixx_bist_t mix_bist;
cvmx_agl_gmx_bist_t agl_gmx_bist;
cvmx_mixx_oring1_t oring1;
cvmx_mixx_iring1_t iring1;
cvmx_mixx_ctl_t mix_ctl;
cvmx_agl_prtx_ctl_t agl_prtx_ctl;
/* Make sure BIST passed */
mix_bist.u64 = cvmx_read_csr(CVMX_MIXX_BIST(port));
if (mix_bist.u64)
cvmx_dprintf("WARNING: cvmx_mgmt_port_initialize: Managment port MIX failed BIST (0x%016llx) on MIX%d\n", CAST64(mix_bist.u64), port);
agl_gmx_bist.u64 = cvmx_read_csr(CVMX_AGL_GMX_BIST);
if (agl_gmx_bist.u64)
cvmx_dprintf("WARNING: cvmx_mgmt_port_initialize: Managment port AGL failed BIST (0x%016llx) on MIX%d\n", CAST64(agl_gmx_bist.u64), port);
/* Clear all state information */
memset(state, 0, sizeof(*state));
/* Take the control logic out of reset */
mix_ctl.u64 = cvmx_read_csr(CVMX_MIXX_CTL(port));
mix_ctl.s.reset = 0;
cvmx_write_csr(CVMX_MIXX_CTL(port), mix_ctl.u64);
/* Read until reset == 0. Timeout should never happen... */
if (CVMX_WAIT_FOR_FIELD64(CVMX_MIXX_CTL(port), cvmx_mixx_ctl_t, reset, ==, 0, 300000000))
{
cvmx_dprintf("ERROR: cvmx_mgmt_port_initialize: Timeout waiting for MIX(%d) reset.\n", port);
return CVMX_MGMT_PORT_INIT_ERROR;
}
/* Set the PHY address and mode of the interface (RGMII/MII mode). */
if (cvmx_sysinfo_get()->board_type == CVMX_BOARD_TYPE_SIM)
{
state->port = -1;
state->mode = CVMX_MGMT_PORT_MII_MODE;
}
else
{
int port_num = CVMX_HELPER_BOARD_MGMT_IPD_PORT + port;
int phy_addr = cvmx_helper_board_get_mii_address(port_num);
if (phy_addr != -1)
{
cvmx_mdio_phy_reg_status_t phy_status;
/* Read PHY status register to find the mode of the interface. */
phy_status.u16 = cvmx_mdio_read(phy_addr >> 8, phy_addr & 0xff, CVMX_MDIO_PHY_REG_STATUS);
if (phy_status.s.capable_extended_status == 0) // MII mode
state->mode = CVMX_MGMT_PORT_MII_MODE;
else if (OCTEON_IS_MODEL(OCTEON_CN6XXX)
&& phy_status.s.capable_extended_status) // RGMII mode
state->mode = CVMX_MGMT_PORT_RGMII_MODE;
else
state->mode = CVMX_MGMT_PORT_NONE;
}
else
{
cvmx_dprintf("ERROR: cvmx_mgmt_port_initialize: Not able to read the PHY on MIX%d\n", port);
return CVMX_MGMT_PORT_INVALID_PARAM;
}
state->port = port_num;
}
/* All interfaces should be configured in same mode */
for (i = 0; i < __cvmx_mgmt_port_num_ports(); i++)
{
if (i != port
&& cvmx_mgmt_port_state_ptr[i].mode != CVMX_MGMT_PORT_NONE
&& cvmx_mgmt_port_state_ptr[i].mode != state->mode)
{
cvmx_dprintf("ERROR: cvmx_mgmt_port_initialize: All ports in MIX interface are not configured in same mode.\n \
Port %d is configured as %d\n \
And Port %d is configured as %d\n", port, state->mode, i, cvmx_mgmt_port_state_ptr[i].mode);
return CVMX_MGMT_PORT_INVALID_PARAM;
}
}
/* Create a default MAC address */
state->mac = 0x000000dead000000ull;
state->mac += 0xffffff & CAST64(state);
/* Setup the TX ring */
for (i=0; itx_ring[i].s.len = CVMX_MGMT_PORT_TX_BUFFER_SIZE;
state->tx_ring[i].s.addr = cvmx_ptr_to_phys(state->tx_buffers[i]);
}
/* Tell the HW where the TX ring is */
oring1.u64 = 0;
oring1.s.obase = cvmx_ptr_to_phys(state->tx_ring)>>3;
oring1.s.osize = CVMX_MGMT_PORT_NUM_TX_BUFFERS;
CVMX_SYNCWS;
cvmx_write_csr(CVMX_MIXX_ORING1(port), oring1.u64);
/* Setup the RX ring */
for (i=0; irx_ring[i].s.len = CVMX_MGMT_PORT_RX_BUFFER_SIZE - 8;
state->rx_ring[i].s.addr = cvmx_ptr_to_phys(state->rx_buffers[i]);
}
/* Tell the HW where the RX ring is */
iring1.u64 = 0;
iring1.s.ibase = cvmx_ptr_to_phys(state->rx_ring)>>3;
iring1.s.isize = CVMX_MGMT_PORT_NUM_RX_BUFFERS;
CVMX_SYNCWS;
cvmx_write_csr(CVMX_MIXX_IRING1(port), iring1.u64);
cvmx_write_csr(CVMX_MIXX_IRING2(port), CVMX_MGMT_PORT_NUM_RX_BUFFERS);
/* Disable the external input/output */
cvmx_mgmt_port_disable(port);
/* Set the MAC address filtering up */
cvmx_mgmt_port_set_mac(port, state->mac);
/* Set the default max size to an MTU of 1500 with L2 and VLAN */
cvmx_mgmt_port_set_max_packet_size(port, 1518);
/* Enable the port HW. Packets are not allowed until cvmx_mgmt_port_enable() is called */
mix_ctl.u64 = 0;
mix_ctl.s.crc_strip = 1; /* Strip the ending CRC */
mix_ctl.s.en = 1; /* Enable the port */
mix_ctl.s.nbtarb = 0; /* Arbitration mode */
mix_ctl.s.mrq_hwm = 1; /* MII CB-request FIFO programmable high watermark */
cvmx_write_csr(CVMX_MIXX_CTL(port), mix_ctl.u64);
/* Select the mode of operation for the interface. */
if (OCTEON_IS_MODEL(OCTEON_CN6XXX))
{
agl_prtx_ctl.u64 = cvmx_read_csr(CVMX_AGL_PRTX_CTL(port));
if (state->mode == CVMX_MGMT_PORT_RGMII_MODE)
agl_prtx_ctl.s.mode = 0;
else if (state->mode == CVMX_MGMT_PORT_MII_MODE)
agl_prtx_ctl.s.mode = 1;
else
{
cvmx_dprintf("ERROR: cvmx_mgmt_port_initialize: Invalid mode for MIX(%d)\n", port);
return CVMX_MGMT_PORT_INVALID_PARAM;
}
cvmx_write_csr(CVMX_AGL_PRTX_CTL(port), agl_prtx_ctl.u64);
}
/* Initialize the physical layer. */
if (OCTEON_IS_MODEL(OCTEON_CN6XXX))
{
/* MII clocks counts are based on the 125Mhz reference, so our
delays need to be scaled to match the core clock rate. The
"+1" is to make sure rounding always waits a little too
long. */
uint64_t clock_scale = cvmx_clock_get_rate(CVMX_CLOCK_CORE) / 125000000 + 1;
/* Take the DLL and clock tree out of reset */
agl_prtx_ctl.u64 = cvmx_read_csr(CVMX_AGL_PRTX_CTL(port));
agl_prtx_ctl.s.clkrst = 0;
if (state->mode == CVMX_MGMT_PORT_RGMII_MODE) // RGMII Initialization
{
agl_prtx_ctl.s.dllrst = 0;
agl_prtx_ctl.s.clktx_byp = 0;
}
cvmx_write_csr(CVMX_AGL_PRTX_CTL(port), agl_prtx_ctl.u64);
cvmx_read_csr(CVMX_AGL_PRTX_CTL(port)); /* Force write out before wait */
/* Wait for the DLL to lock. External 125 MHz reference clock must be stable at this point. */
cvmx_wait(256 * clock_scale);
/* The rest of the config is common between RGMII/MII */
/* Enable the interface */
agl_prtx_ctl.u64 = cvmx_read_csr(CVMX_AGL_PRTX_CTL(port));
agl_prtx_ctl.s.enable = 1;
cvmx_write_csr(CVMX_AGL_PRTX_CTL(port), agl_prtx_ctl.u64);
/* Read the value back to force the previous write */
agl_prtx_ctl.u64 = cvmx_read_csr(CVMX_AGL_PRTX_CTL(port));
/* Enable the componsation controller */
agl_prtx_ctl.s.comp = 1;
agl_prtx_ctl.s.drv_byp = 0;
cvmx_write_csr(CVMX_AGL_PRTX_CTL(port), agl_prtx_ctl.u64);
cvmx_read_csr(CVMX_AGL_PRTX_CTL(port)); /* Force write out before wait */
cvmx_wait(1024 * clock_scale); // for componsation state to lock.
}
else if (OCTEON_IS_MODEL(OCTEON_CN56XX_PASS1_X) || OCTEON_IS_MODEL(OCTEON_CN52XX_PASS1_X))
{
/* Force compensation values, as they are not determined properly by HW */
cvmx_agl_gmx_drv_ctl_t drv_ctl;
drv_ctl.u64 = cvmx_read_csr(CVMX_AGL_GMX_DRV_CTL);
if (port)
{
drv_ctl.s.byp_en1 = 1;
drv_ctl.s.nctl1 = 6;
drv_ctl.s.pctl1 = 6;
}
else
{
drv_ctl.s.byp_en = 1;
drv_ctl.s.nctl = 6;
drv_ctl.s.pctl = 6;
}
cvmx_write_csr(CVMX_AGL_GMX_DRV_CTL, drv_ctl.u64);
}
}
+#if !defined(CVMX_BUILD_FOR_FREEBSD_KERNEL)
cvmx_error_enable_group(CVMX_ERROR_GROUP_MGMT_PORT, port);
+#endif
return CVMX_MGMT_PORT_SUCCESS;
}
/**
* Shutdown a management port. This currently disables packet IO
* but leaves all hardware and buffers. Another application can then
* call initialize() without redoing the hardware setup.
*
* @param port Management port
*
* @return CVMX_MGMT_PORT_SUCCESS or an error code
*/
cvmx_mgmt_port_result_t cvmx_mgmt_port_shutdown(int port)
{
if ((port < 0) || (port >= __cvmx_mgmt_port_num_ports()))
return CVMX_MGMT_PORT_INVALID_PARAM;
+#if !defined(CVMX_BUILD_FOR_FREEBSD_KERNEL)
cvmx_error_disable_group(CVMX_ERROR_GROUP_MGMT_PORT, port);
+#endif
/* Stop packets from comming in */
cvmx_mgmt_port_disable(port);
/* We don't free any memory so the next intialize can reuse the HW setup */
return CVMX_MGMT_PORT_SUCCESS;
}
/**
* Enable packet IO on a management port
*
* @param port Management port
*
* @return CVMX_MGMT_PORT_SUCCESS or an error code
*/
cvmx_mgmt_port_result_t cvmx_mgmt_port_enable(int port)
{
cvmx_mgmt_port_state_t *state;
cvmx_agl_gmx_inf_mode_t agl_gmx_inf_mode;
cvmx_agl_gmx_rxx_frm_ctl_t rxx_frm_ctl;
if ((port < 0) || (port >= __cvmx_mgmt_port_num_ports()))
return CVMX_MGMT_PORT_INVALID_PARAM;
state = cvmx_mgmt_port_state_ptr + port;
cvmx_spinlock_lock(&state->lock);
rxx_frm_ctl.u64 = 0;
rxx_frm_ctl.s.pre_align = 1;
rxx_frm_ctl.s.pad_len = 1; /* When set, disables the length check for non-min sized pkts with padding in the client data */
rxx_frm_ctl.s.vlan_len = 1; /* When set, disables the length check for VLAN pkts */
rxx_frm_ctl.s.pre_free = 1; /* When set, PREAMBLE checking is less strict */
rxx_frm_ctl.s.ctl_smac = 0; /* Control Pause Frames can match station SMAC */
rxx_frm_ctl.s.ctl_mcst = 1; /* Control Pause Frames can match globally assign Multicast address */
rxx_frm_ctl.s.ctl_bck = 1; /* Forward pause information to TX block */
rxx_frm_ctl.s.ctl_drp = 1; /* Drop Control Pause Frames */
rxx_frm_ctl.s.pre_strp = 1; /* Strip off the preamble */
rxx_frm_ctl.s.pre_chk = 1; /* This port is configured to send PREAMBLE+SFD to begin every frame. GMX checks that the PREAMBLE is sent correctly */
cvmx_write_csr(CVMX_AGL_GMX_RXX_FRM_CTL(port), rxx_frm_ctl.u64);
/* Enable the AGL block */
if (OCTEON_IS_MODEL(OCTEON_CN5XXX))
{
agl_gmx_inf_mode.u64 = 0;
agl_gmx_inf_mode.s.en = 1;
cvmx_write_csr(CVMX_AGL_GMX_INF_MODE, agl_gmx_inf_mode.u64);
}
/* Configure the port duplex and enables */
cvmx_mgmt_port_link_set(port, cvmx_mgmt_port_link_get(port));
cvmx_spinlock_unlock(&state->lock);
return CVMX_MGMT_PORT_SUCCESS;
}
/**
* Disable packet IO on a management port
*
* @param port Management port
*
* @return CVMX_MGMT_PORT_SUCCESS or an error code
*/
cvmx_mgmt_port_result_t cvmx_mgmt_port_disable(int port)
{
cvmx_mgmt_port_state_t *state;
cvmx_agl_gmx_prtx_cfg_t agl_gmx_prtx;
if ((port < 0) || (port >= __cvmx_mgmt_port_num_ports()))
return CVMX_MGMT_PORT_INVALID_PARAM;
state = cvmx_mgmt_port_state_ptr + port;
cvmx_spinlock_lock(&state->lock);
agl_gmx_prtx.u64 = cvmx_read_csr(CVMX_AGL_GMX_PRTX_CFG(port));
agl_gmx_prtx.s.en = 0;
cvmx_write_csr(CVMX_AGL_GMX_PRTX_CFG(port), agl_gmx_prtx.u64);
cvmx_spinlock_unlock(&state->lock);
return CVMX_MGMT_PORT_SUCCESS;
}
/**
* Send a packet out the management port. The packet is copied so
* the input buffer isn't used after this call.
*
* @param port Management port
* @param packet_len Length of the packet to send. It does not include the final CRC
* @param buffer Packet data
*
* @return CVMX_MGMT_PORT_SUCCESS or an error code
*/
cvmx_mgmt_port_result_t cvmx_mgmt_port_send(int port, int packet_len, void *buffer)
{
cvmx_mgmt_port_state_t *state;
cvmx_mixx_oring2_t mix_oring2;
if ((port < 0) || (port >= __cvmx_mgmt_port_num_ports()))
return CVMX_MGMT_PORT_INVALID_PARAM;
/* Max sure the packet size is valid */
if ((packet_len < 1) || (packet_len > CVMX_MGMT_PORT_TX_BUFFER_SIZE))
return CVMX_MGMT_PORT_INVALID_PARAM;
if (buffer == NULL)
return CVMX_MGMT_PORT_INVALID_PARAM;
state = cvmx_mgmt_port_state_ptr + port;
cvmx_spinlock_lock(&state->lock);
mix_oring2.u64 = cvmx_read_csr(CVMX_MIXX_ORING2(port));
if (mix_oring2.s.odbell >= CVMX_MGMT_PORT_NUM_TX_BUFFERS - 1)
{
/* No room for another packet */
cvmx_spinlock_unlock(&state->lock);
return CVMX_MGMT_PORT_NO_MEMORY;
}
else
{
/* Copy the packet into the output buffer */
memcpy(state->tx_buffers[state->tx_write_index], buffer, packet_len);
/* Insert the source MAC */
memcpy(state->tx_buffers[state->tx_write_index] + 6, ((char*)&state->mac) + 2, 6);
/* Update the TX ring buffer entry size */
state->tx_ring[state->tx_write_index].s.len = packet_len;
/* This code doesn't support TX timestamps */
state->tx_ring[state->tx_write_index].s.tstamp = 0;
/* Increment our TX index */
state->tx_write_index = (state->tx_write_index + 1) % CVMX_MGMT_PORT_NUM_TX_BUFFERS;
/* Ring the doorbell, sending the packet */
CVMX_SYNCWS;
cvmx_write_csr(CVMX_MIXX_ORING2(port), 1);
if (cvmx_read_csr(CVMX_MIXX_ORCNT(port)))
cvmx_write_csr(CVMX_MIXX_ORCNT(port), cvmx_read_csr(CVMX_MIXX_ORCNT(port)));
cvmx_spinlock_unlock(&state->lock);
return CVMX_MGMT_PORT_SUCCESS;
}
}
#if defined(__FreeBSD__)
/**
* Send a packet out the management port. The packet is copied so
* the input mbuf isn't used after this call.
*
* @param port Management port
* @param m Packet mbuf (with pkthdr)
*
* @return CVMX_MGMT_PORT_SUCCESS or an error code
*/
cvmx_mgmt_port_result_t cvmx_mgmt_port_sendm(int port, const struct mbuf *m)
{
cvmx_mgmt_port_state_t *state;
cvmx_mixx_oring2_t mix_oring2;
if ((port < 0) || (port >= __cvmx_mgmt_port_num_ports()))
return CVMX_MGMT_PORT_INVALID_PARAM;
/* Max sure the packet size is valid */
if ((m->m_pkthdr.len < 1) || (m->m_pkthdr.len > CVMX_MGMT_PORT_TX_BUFFER_SIZE))
return CVMX_MGMT_PORT_INVALID_PARAM;
state = cvmx_mgmt_port_state_ptr + port;
cvmx_spinlock_lock(&state->lock);
mix_oring2.u64 = cvmx_read_csr(CVMX_MIXX_ORING2(port));
if (mix_oring2.s.odbell >= CVMX_MGMT_PORT_NUM_TX_BUFFERS - 1)
{
/* No room for another packet */
cvmx_spinlock_unlock(&state->lock);
return CVMX_MGMT_PORT_NO_MEMORY;
}
else
{
/* Copy the packet into the output buffer */
m_copydata(m, 0, m->m_pkthdr.len, state->tx_buffers[state->tx_write_index]);
/* Update the TX ring buffer entry size */
state->tx_ring[state->tx_write_index].s.len = m->m_pkthdr.len;
/* This code doesn't support TX timestamps */
state->tx_ring[state->tx_write_index].s.tstamp = 0;
/* Increment our TX index */
state->tx_write_index = (state->tx_write_index + 1) % CVMX_MGMT_PORT_NUM_TX_BUFFERS;
/* Ring the doorbell, sending the packet */
CVMX_SYNCWS;
cvmx_write_csr(CVMX_MIXX_ORING2(port), 1);
if (cvmx_read_csr(CVMX_MIXX_ORCNT(port)))
cvmx_write_csr(CVMX_MIXX_ORCNT(port), cvmx_read_csr(CVMX_MIXX_ORCNT(port)));
cvmx_spinlock_unlock(&state->lock);
return CVMX_MGMT_PORT_SUCCESS;
}
}
#endif
/**
* Receive a packet from the management port.
*
* @param port Management port
* @param buffer_len Size of the buffer to receive the packet into
* @param buffer Buffer to receive the packet into
*
* @return The size of the packet, or a negative erorr code on failure. Zero
* means that no packets were available.
*/
int cvmx_mgmt_port_receive(int port, int buffer_len, uint8_t *buffer)
{
cvmx_mixx_ircnt_t mix_ircnt;
cvmx_mgmt_port_state_t *state;
int result;
if ((port < 0) || (port >= __cvmx_mgmt_port_num_ports()))
return CVMX_MGMT_PORT_INVALID_PARAM;
/* Max sure the buffer size is valid */
if (buffer_len < 1)
return CVMX_MGMT_PORT_INVALID_PARAM;
if (buffer == NULL)
return CVMX_MGMT_PORT_INVALID_PARAM;
state = cvmx_mgmt_port_state_ptr + port;
cvmx_spinlock_lock(&state->lock);
/* Find out how many RX packets are pending */
mix_ircnt.u64 = cvmx_read_csr(CVMX_MIXX_IRCNT(port));
if (mix_ircnt.s.ircnt)
{
uint64_t *source = (void *)state->rx_buffers[state->rx_read_index];
uint64_t *zero_check = source;
/* CN56XX pass 1 has an errata where packets might start 8 bytes
into the buffer instead of at their correct lcoation. If the
first 8 bytes is zero we assume this has happened */
if (OCTEON_IS_MODEL(OCTEON_CN56XX_PASS1_X) && (*zero_check == 0))
source++;
/* Start off with zero bytes received */
result = 0;
/* While the completion code signals more data, copy the buffers
into the user's data */
while (state->rx_ring[state->rx_read_index].s.code == 16)
{
/* Only copy what will fit in the user's buffer */
int length = state->rx_ring[state->rx_read_index].s.len;
if (length > buffer_len)
length = buffer_len;
memcpy(buffer, source, length);
/* Reduce the size of the buffer to the remaining space. If we run
out we will signal an error when the code 15 buffer doesn't fit */
buffer += length;
buffer_len -= length;
result += length;
/* Update this buffer for reuse in future receives. This size is
-8 due to an errata for CN56XX pass 1 */
state->rx_ring[state->rx_read_index].s.code = 0;
state->rx_ring[state->rx_read_index].s.len = CVMX_MGMT_PORT_RX_BUFFER_SIZE - 8;
state->rx_read_index = (state->rx_read_index + 1) % CVMX_MGMT_PORT_NUM_RX_BUFFERS;
/* Zero the beginning of the buffer for use by the errata check */
*zero_check = 0;
CVMX_SYNCWS;
/* Increment the number of RX buffers */
cvmx_write_csr(CVMX_MIXX_IRING2(port), 1);
source = (void *)state->rx_buffers[state->rx_read_index];
zero_check = source;
}
/* Check for the final good completion code */
if (state->rx_ring[state->rx_read_index].s.code == 15)
{
if (buffer_len >= state->rx_ring[state->rx_read_index].s.len)
{
int length = state->rx_ring[state->rx_read_index].s.len;
memcpy(buffer, source, length);
result += length;
}
else
{
/* Not enough room for the packet */
cvmx_dprintf("ERROR: cvmx_mgmt_port_receive: Packet (%d) larger than supplied buffer (%d)\n", state->rx_ring[state->rx_read_index].s.len, buffer_len);
result = CVMX_MGMT_PORT_NO_MEMORY;
}
}
else
{
cvmx_dprintf("ERROR: cvmx_mgmt_port_receive: Receive error code %d. Packet dropped(Len %d), \n",
state->rx_ring[state->rx_read_index].s.code, state->rx_ring[state->rx_read_index].s.len + result);
result = -state->rx_ring[state->rx_read_index].s.code;
/* Check to see if we need to change the duplex. */
cvmx_mgmt_port_link_set(port, cvmx_mgmt_port_link_get(port));
}
/* Clean out the ring buffer entry. This size is -8 due to an errata
for CN56XX pass 1 */
state->rx_ring[state->rx_read_index].s.code = 0;
state->rx_ring[state->rx_read_index].s.len = CVMX_MGMT_PORT_RX_BUFFER_SIZE - 8;
state->rx_read_index = (state->rx_read_index + 1) % CVMX_MGMT_PORT_NUM_RX_BUFFERS;
/* Zero the beginning of the buffer for use by the errata check */
*zero_check = 0;
CVMX_SYNCWS;
/* Increment the number of RX buffers */
cvmx_write_csr(CVMX_MIXX_IRING2(port), 1);
/* Decrement the pending RX count */
cvmx_write_csr(CVMX_MIXX_IRCNT(port), 1);
}
else
{
/* No packets available */
result = 0;
}
cvmx_spinlock_unlock(&state->lock);
return result;
}
/**
* Set the MAC address for a management port
*
* @param port Management port
* @param mac New MAC address. The lower 6 bytes are used.
*
* @return CVMX_MGMT_PORT_SUCCESS or an error code
*/
cvmx_mgmt_port_result_t cvmx_mgmt_port_set_mac(int port, uint64_t mac)
{
cvmx_mgmt_port_state_t *state;
cvmx_agl_gmx_rxx_adr_ctl_t agl_gmx_rxx_adr_ctl;
if ((port < 0) || (port >= __cvmx_mgmt_port_num_ports()))
return CVMX_MGMT_PORT_INVALID_PARAM;
state = cvmx_mgmt_port_state_ptr + port;
cvmx_spinlock_lock(&state->lock);
agl_gmx_rxx_adr_ctl.u64 = 0;
agl_gmx_rxx_adr_ctl.s.cam_mode = 1; /* Only accept matching MAC addresses */
agl_gmx_rxx_adr_ctl.s.mcst = 0; /* Drop multicast */
agl_gmx_rxx_adr_ctl.s.bcst = 1; /* Allow broadcast */
cvmx_write_csr(CVMX_AGL_GMX_RXX_ADR_CTL(port), agl_gmx_rxx_adr_ctl.u64);
/* Only using one of the CAMs */
cvmx_write_csr(CVMX_AGL_GMX_RXX_ADR_CAM0(port), (mac >> 40) & 0xff);
cvmx_write_csr(CVMX_AGL_GMX_RXX_ADR_CAM1(port), (mac >> 32) & 0xff);
cvmx_write_csr(CVMX_AGL_GMX_RXX_ADR_CAM2(port), (mac >> 24) & 0xff);
cvmx_write_csr(CVMX_AGL_GMX_RXX_ADR_CAM3(port), (mac >> 16) & 0xff);
cvmx_write_csr(CVMX_AGL_GMX_RXX_ADR_CAM4(port), (mac >> 8) & 0xff);
cvmx_write_csr(CVMX_AGL_GMX_RXX_ADR_CAM5(port), (mac >> 0) & 0xff);
cvmx_write_csr(CVMX_AGL_GMX_RXX_ADR_CAM_EN(port), 1);
state->mac = mac;
cvmx_spinlock_unlock(&state->lock);
return CVMX_MGMT_PORT_SUCCESS;
}
/**
* Get the MAC address for a management port
*
* @param port Management port
*
* @return MAC address
*/
uint64_t cvmx_mgmt_port_get_mac(int port)
{
if ((port < 0) || (port >= __cvmx_mgmt_port_num_ports()))
return CVMX_MGMT_PORT_INVALID_PARAM;
return cvmx_mgmt_port_state_ptr[port].mac;
}
/**
* Set the multicast list.
*
* @param port Management port
* @param flags Interface flags
*
* @return
*/
void cvmx_mgmt_port_set_multicast_list(int port, int flags)
{
cvmx_mgmt_port_state_t *state;
cvmx_agl_gmx_rxx_adr_ctl_t agl_gmx_rxx_adr_ctl;
if ((port < 0) || (port >= __cvmx_mgmt_port_num_ports()))
return;
state = cvmx_mgmt_port_state_ptr + port;
cvmx_spinlock_lock(&state->lock);
agl_gmx_rxx_adr_ctl.u64 = cvmx_read_csr(CVMX_AGL_GMX_RXX_ADR_CTL(port));
/* Allow broadcast MAC addresses */
if (!agl_gmx_rxx_adr_ctl.s.bcst)
agl_gmx_rxx_adr_ctl.s.bcst = 1;
if ((flags & CVMX_IFF_ALLMULTI) || (flags & CVMX_IFF_PROMISC))
agl_gmx_rxx_adr_ctl.s.mcst = 2; /* Force accept multicast packets */
else
agl_gmx_rxx_adr_ctl.s.mcst = 1; /* Force reject multicast packets */
if (flags & CVMX_IFF_PROMISC)
agl_gmx_rxx_adr_ctl.s.cam_mode = 0; /* Reject matches if promisc. Since CAM is shut off, should accept everything */
else
agl_gmx_rxx_adr_ctl.s.cam_mode = 1; /* Filter packets based on the CAM */
cvmx_write_csr(CVMX_AGL_GMX_RXX_ADR_CTL(port), agl_gmx_rxx_adr_ctl.u64);
if (flags & CVMX_IFF_PROMISC)
cvmx_write_csr(CVMX_AGL_GMX_RXX_ADR_CAM_EN(port), 0);
else
cvmx_write_csr(CVMX_AGL_GMX_RXX_ADR_CAM_EN(port), 1);
cvmx_spinlock_unlock(&state->lock);
}
/**
* Set the maximum packet allowed in. Size is specified
* including L2 but without FCS. A normal MTU would corespond
* to 1514 assuming the standard 14 byte L2 header.
*
* @param port Management port
* @param size_without_fcs
* Size in bytes without FCS
*/
void cvmx_mgmt_port_set_max_packet_size(int port, int size_without_fcs)
{
cvmx_mgmt_port_state_t *state;
if ((port < 0) || (port >= __cvmx_mgmt_port_num_ports()))
return;
state = cvmx_mgmt_port_state_ptr + port;
cvmx_spinlock_lock(&state->lock);
cvmx_write_csr(CVMX_AGL_GMX_RXX_FRM_MAX(port), size_without_fcs);
cvmx_write_csr(CVMX_AGL_GMX_RXX_JABBER(port), (size_without_fcs+7) & 0xfff8);
cvmx_spinlock_unlock(&state->lock);
}
/**
* Return the link state of an RGMII/MII port as returned by
* auto negotiation. The result of this function may not match
* Octeon's link config if auto negotiation has changed since
* the last call to cvmx_mgmt_port_link_set().
*
* @param port The RGMII/MII interface port to query
*
* @return Link state
*/
cvmx_helper_link_info_t cvmx_mgmt_port_link_get(int port)
{
cvmx_mgmt_port_state_t *state;
cvmx_helper_link_info_t result;
state = cvmx_mgmt_port_state_ptr + port;
result.u64 = 0;
if (port > __cvmx_mgmt_port_num_ports())
{
cvmx_dprintf("WARNING: Invalid port %d\n", port);
return result;
}
if (state->port != -1)
return __cvmx_helper_board_link_get(state->port);
else // Simulator does not have PHY, use some defaults.
{
result.s.full_duplex = 1;
result.s.link_up = 1;
result.s.speed = 100;
return result;
}
return result;
}
/**
* Configure RGMII/MII port for the specified link state. This
* function does not influence auto negotiation at the PHY level.
*
* @param port RGMII/MII interface port
* @param link_info The new link state
*
* @return Zero on success, negative on failure
*/
int cvmx_mgmt_port_link_set(int port, cvmx_helper_link_info_t link_info)
{
cvmx_agl_gmx_prtx_cfg_t agl_gmx_prtx;
/* Disable GMX before we make any changes. */
agl_gmx_prtx.u64 = cvmx_read_csr(CVMX_AGL_GMX_PRTX_CFG(port));
agl_gmx_prtx.s.en = 0;
agl_gmx_prtx.s.tx_en = 0;
agl_gmx_prtx.s.rx_en = 0;
cvmx_write_csr(CVMX_AGL_GMX_PRTX_CFG(port), agl_gmx_prtx.u64);
if (OCTEON_IS_MODEL(OCTEON_CN6XXX))
{
uint64_t one_second = cvmx_clock_get_rate(CVMX_CLOCK_CORE);
/* Wait for GMX to be idle */
if (CVMX_WAIT_FOR_FIELD64(CVMX_AGL_GMX_PRTX_CFG(port), cvmx_agl_gmx_prtx_cfg_t, rx_idle, ==, 1, one_second)
|| CVMX_WAIT_FOR_FIELD64(CVMX_AGL_GMX_PRTX_CFG(port), cvmx_agl_gmx_prtx_cfg_t, tx_idle, ==, 1, one_second))
{
cvmx_dprintf("MIX%d: Timeout waiting for GMX to be idle\n", port);
return -1;
}
}
agl_gmx_prtx.u64 = cvmx_read_csr(CVMX_AGL_GMX_PRTX_CFG(port));
/* Set duplex mode */
if (!link_info.s.link_up)
agl_gmx_prtx.s.duplex = 1; /* Force full duplex on down links */
else
agl_gmx_prtx.s.duplex = link_info.s.full_duplex;
switch(link_info.s.speed)
{
case 10:
agl_gmx_prtx.s.speed = 0;
agl_gmx_prtx.s.slottime = 0;
if (OCTEON_IS_MODEL(OCTEON_CN6XXX))
{
agl_gmx_prtx.s.speed_msb = 1;
agl_gmx_prtx.s.burst = 1;
}
break;
case 100:
agl_gmx_prtx.s.speed = 0;
agl_gmx_prtx.s.slottime = 0;
if (OCTEON_IS_MODEL(OCTEON_CN6XXX))
{
agl_gmx_prtx.s.speed_msb = 0;
agl_gmx_prtx.s.burst = 1;
}
break;
case 1000:
/* 1000 MBits is only supported on 6XXX chips */
if (OCTEON_IS_MODEL(OCTEON_CN6XXX))
{
agl_gmx_prtx.s.speed_msb = 0;
agl_gmx_prtx.s.speed = 1;
agl_gmx_prtx.s.slottime = 1; /* Only matters for half-duplex */
agl_gmx_prtx.s.burst = agl_gmx_prtx.s.duplex;
}
break;
/* No link */
case 0:
default:
break;
}
/* Write the new GMX setting with the port still disabled. */
cvmx_write_csr(CVMX_AGL_GMX_PRTX_CFG(port), agl_gmx_prtx.u64);
/* Read GMX CFG again to make sure the config is completed. */
agl_gmx_prtx.u64 = cvmx_read_csr(CVMX_AGL_GMX_PRTX_CFG(port));
if (OCTEON_IS_MODEL(OCTEON_CN6XXX))
{
cvmx_mgmt_port_state_t *state = cvmx_mgmt_port_state_ptr + port;
cvmx_agl_gmx_txx_clk_t agl_clk;
agl_clk.u64 = cvmx_read_csr(CVMX_AGL_GMX_TXX_CLK(port));
agl_clk.s.clk_cnt = 1; /* MII (both speeds) and RGMII 1000 setting */
if (state->mode == CVMX_MGMT_PORT_RGMII_MODE)
{
if (link_info.s.speed == 10)
agl_clk.s.clk_cnt = 50;
else if (link_info.s.speed == 100)
agl_clk.s.clk_cnt = 5;
}
cvmx_write_csr(CVMX_AGL_GMX_TXX_CLK(port), agl_clk.u64);
}
/* Enable transmit and receive ports */
agl_gmx_prtx.s.tx_en = 1;
agl_gmx_prtx.s.rx_en = 1;
cvmx_write_csr(CVMX_AGL_GMX_PRTX_CFG(port), agl_gmx_prtx.u64);
/* Enable the link. */
agl_gmx_prtx.s.en = 1;
cvmx_write_csr(CVMX_AGL_GMX_PRTX_CFG(port), agl_gmx_prtx.u64);
return 0;
}
Index: head/sys/contrib/octeon-sdk/cvmx-pcie.c
===================================================================
--- head/sys/contrib/octeon-sdk/cvmx-pcie.c (revision 232815)
+++ head/sys/contrib/octeon-sdk/cvmx-pcie.c (revision 232816)
@@ -1,1696 +1,1698 @@
/***********************license start***************
* Copyright (c) 2003-2011 Cavium, Inc. . 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
*
* Interface to PCIe as a host(RC) or target(EP)
*
*
$Revision: 70030 $
*/
#ifdef CVMX_BUILD_FOR_LINUX_KERNEL
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#ifdef CONFIG_CAVIUM_DECODE_RSL
#include
#endif
#include
#include
#include
#include
#include
#include
#include
#include
#else
#include "cvmx.h"
#include "cvmx-csr-db.h"
#include "cvmx-pcie.h"
#include "cvmx-sysinfo.h"
#include "cvmx-swap.h"
#include "cvmx-wqe.h"
+#if !defined(CVMX_BUILD_FOR_FREEBSD_KERNEL)
#include "cvmx-error.h"
+#endif
#include "cvmx-helper-errata.h"
#include "cvmx-qlm.h"
#endif
#define MRRS_CN5XXX 0 /* 128 byte Max Read Request Size */
#define MPS_CN5XXX 0 /* 128 byte Max Packet Size (Limit of most PCs) */
#define MRRS_CN6XXX 3 /* 1024 byte Max Read Request Size */
#define MPS_CN6XXX 0 /* 128 byte Max Packet Size (Limit of most PCs) */
/**
* Return the Core virtual base address for PCIe IO access. IOs are
* read/written as an offset from this address.
*
* @param pcie_port PCIe port the IO is for
*
* @return 64bit Octeon IO base address for read/write
*/
uint64_t cvmx_pcie_get_io_base_address(int pcie_port)
{
cvmx_pcie_address_t pcie_addr;
pcie_addr.u64 = 0;
pcie_addr.io.upper = 0;
pcie_addr.io.io = 1;
pcie_addr.io.did = 3;
pcie_addr.io.subdid = 2;
pcie_addr.io.es = 1;
pcie_addr.io.port = pcie_port;
return pcie_addr.u64;
}
/**
* Size of the IO address region returned at address
* cvmx_pcie_get_io_base_address()
*
* @param pcie_port PCIe port the IO is for
*
* @return Size of the IO window
*/
uint64_t cvmx_pcie_get_io_size(int pcie_port)
{
return 1ull<<32;
}
/**
* Return the Core virtual base address for PCIe MEM access. Memory is
* read/written as an offset from this address.
*
* @param pcie_port PCIe port the IO is for
*
* @return 64bit Octeon IO base address for read/write
*/
uint64_t cvmx_pcie_get_mem_base_address(int pcie_port)
{
cvmx_pcie_address_t pcie_addr;
pcie_addr.u64 = 0;
pcie_addr.mem.upper = 0;
pcie_addr.mem.io = 1;
pcie_addr.mem.did = 3;
pcie_addr.mem.subdid = 3 + pcie_port;
return pcie_addr.u64;
}
/**
* Size of the Mem address region returned at address
* cvmx_pcie_get_mem_base_address()
*
* @param pcie_port PCIe port the IO is for
*
* @return Size of the Mem window
*/
uint64_t cvmx_pcie_get_mem_size(int pcie_port)
{
return 1ull<<36;
}
/**
* @INTERNAL
* Initialize the RC config space CSRs
*
* @param pcie_port PCIe port to initialize
*/
static void __cvmx_pcie_rc_initialize_config_space(int pcie_port)
{
/* Max Payload Size (PCIE*_CFG030[MPS]) */
/* Max Read Request Size (PCIE*_CFG030[MRRS]) */
/* Relaxed-order, no-snoop enables (PCIE*_CFG030[RO_EN,NS_EN] */
/* Error Message Enables (PCIE*_CFG030[CE_EN,NFE_EN,FE_EN,UR_EN]) */
{
cvmx_pciercx_cfg030_t pciercx_cfg030;
pciercx_cfg030.u32 = cvmx_pcie_cfgx_read(pcie_port, CVMX_PCIERCX_CFG030(pcie_port));
if (OCTEON_IS_MODEL(OCTEON_CN5XXX))
{
pciercx_cfg030.s.mps = MPS_CN5XXX;
pciercx_cfg030.s.mrrs = MRRS_CN5XXX;
}
else
{
pciercx_cfg030.s.mps = MPS_CN6XXX;
pciercx_cfg030.s.mrrs = MRRS_CN6XXX;
}
pciercx_cfg030.s.ro_en = 1; /* Enable relaxed order processing. This will allow devices to affect read response ordering */
pciercx_cfg030.s.ns_en = 1; /* Enable no snoop processing. Not used by Octeon */
pciercx_cfg030.s.ce_en = 1; /* Correctable error reporting enable. */
pciercx_cfg030.s.nfe_en = 1; /* Non-fatal error reporting enable. */
pciercx_cfg030.s.fe_en = 1; /* Fatal error reporting enable. */
pciercx_cfg030.s.ur_en = 1; /* Unsupported request reporting enable. */
cvmx_pcie_cfgx_write(pcie_port, CVMX_PCIERCX_CFG030(pcie_port), pciercx_cfg030.u32);
}
if (octeon_has_feature(OCTEON_FEATURE_NPEI))
{
/* Max Payload Size (NPEI_CTL_STATUS2[MPS]) must match PCIE*_CFG030[MPS] */
/* Max Read Request Size (NPEI_CTL_STATUS2[MRRS]) must not exceed PCIE*_CFG030[MRRS] */
cvmx_npei_ctl_status2_t npei_ctl_status2;
npei_ctl_status2.u64 = cvmx_read_csr(CVMX_PEXP_NPEI_CTL_STATUS2);
npei_ctl_status2.s.mps = MPS_CN5XXX; /* Max payload size = 128 bytes for best Octeon DMA performance */
npei_ctl_status2.s.mrrs = MRRS_CN5XXX; /* Max read request size = 128 bytes for best Octeon DMA performance */
if (pcie_port)
npei_ctl_status2.s.c1_b1_s = 3; /* Port1 BAR1 Size 256MB */
else
npei_ctl_status2.s.c0_b1_s = 3; /* Port0 BAR1 Size 256MB */
cvmx_write_csr(CVMX_PEXP_NPEI_CTL_STATUS2, npei_ctl_status2.u64);
}
else
{
/* Max Payload Size (DPI_SLI_PRTX_CFG[MPS]) must match PCIE*_CFG030[MPS] */
/* Max Read Request Size (DPI_SLI_PRTX_CFG[MRRS]) must not exceed PCIE*_CFG030[MRRS] */
cvmx_dpi_sli_prtx_cfg_t prt_cfg;
cvmx_sli_s2m_portx_ctl_t sli_s2m_portx_ctl;
prt_cfg.u64 = cvmx_read_csr(CVMX_DPI_SLI_PRTX_CFG(pcie_port));
prt_cfg.s.mps = MPS_CN6XXX;
prt_cfg.s.mrrs = MRRS_CN6XXX;
/* Max outstanding load request. */
prt_cfg.s.molr = 32;
cvmx_write_csr(CVMX_DPI_SLI_PRTX_CFG(pcie_port), prt_cfg.u64);
sli_s2m_portx_ctl.u64 = cvmx_read_csr(CVMX_PEXP_SLI_S2M_PORTX_CTL(pcie_port));
sli_s2m_portx_ctl.s.mrrs = MRRS_CN6XXX;
cvmx_write_csr(CVMX_PEXP_SLI_S2M_PORTX_CTL(pcie_port), sli_s2m_portx_ctl.u64);
}
/* ECRC Generation (PCIE*_CFG070[GE,CE]) */
{
cvmx_pciercx_cfg070_t pciercx_cfg070;
pciercx_cfg070.u32 = cvmx_pcie_cfgx_read(pcie_port, CVMX_PCIERCX_CFG070(pcie_port));
pciercx_cfg070.s.ge = 1; /* ECRC generation enable. */
pciercx_cfg070.s.ce = 1; /* ECRC check enable. */
cvmx_pcie_cfgx_write(pcie_port, CVMX_PCIERCX_CFG070(pcie_port), pciercx_cfg070.u32);
}
/* Access Enables (PCIE*_CFG001[MSAE,ME]) */
/* ME and MSAE should always be set. */
/* Interrupt Disable (PCIE*_CFG001[I_DIS]) */
/* System Error Message Enable (PCIE*_CFG001[SEE]) */
{
cvmx_pciercx_cfg001_t pciercx_cfg001;
pciercx_cfg001.u32 = cvmx_pcie_cfgx_read(pcie_port, CVMX_PCIERCX_CFG001(pcie_port));
pciercx_cfg001.s.msae = 1; /* Memory space enable. */
pciercx_cfg001.s.me = 1; /* Bus master enable. */
pciercx_cfg001.s.i_dis = 1; /* INTx assertion disable. */
pciercx_cfg001.s.see = 1; /* SERR# enable */
cvmx_pcie_cfgx_write(pcie_port, CVMX_PCIERCX_CFG001(pcie_port), pciercx_cfg001.u32);
}
/* Advanced Error Recovery Message Enables */
/* (PCIE*_CFG066,PCIE*_CFG067,PCIE*_CFG069) */
cvmx_pcie_cfgx_write(pcie_port, CVMX_PCIERCX_CFG066(pcie_port), 0);
/* Use CVMX_PCIERCX_CFG067 hardware default */
cvmx_pcie_cfgx_write(pcie_port, CVMX_PCIERCX_CFG069(pcie_port), 0);
/* Active State Power Management (PCIE*_CFG032[ASLPC]) */
{
cvmx_pciercx_cfg032_t pciercx_cfg032;
pciercx_cfg032.u32 = cvmx_pcie_cfgx_read(pcie_port, CVMX_PCIERCX_CFG032(pcie_port));
pciercx_cfg032.s.aslpc = 0; /* Active state Link PM control. */
cvmx_pcie_cfgx_write(pcie_port, CVMX_PCIERCX_CFG032(pcie_port), pciercx_cfg032.u32);
}
/* Link Width Mode (PCIERCn_CFG452[LME]) - Set during cvmx_pcie_rc_initialize_link() */
/* Primary Bus Number (PCIERCn_CFG006[PBNUM]) */
{
/* We set the primary bus number to 1 so IDT bridges are happy. They don't like zero */
cvmx_pciercx_cfg006_t pciercx_cfg006;
pciercx_cfg006.u32 = 0;
pciercx_cfg006.s.pbnum = 1;
pciercx_cfg006.s.sbnum = 1;
pciercx_cfg006.s.subbnum = 1;
cvmx_pcie_cfgx_write(pcie_port, CVMX_PCIERCX_CFG006(pcie_port), pciercx_cfg006.u32);
}
/* Memory-mapped I/O BAR (PCIERCn_CFG008) */
/* Most applications should disable the memory-mapped I/O BAR by */
/* setting PCIERCn_CFG008[ML_ADDR] < PCIERCn_CFG008[MB_ADDR] */
{
cvmx_pciercx_cfg008_t pciercx_cfg008;
pciercx_cfg008.u32 = 0;
pciercx_cfg008.s.mb_addr = 0x100;
pciercx_cfg008.s.ml_addr = 0;
cvmx_pcie_cfgx_write(pcie_port, CVMX_PCIERCX_CFG008(pcie_port), pciercx_cfg008.u32);
}
/* Prefetchable BAR (PCIERCn_CFG009,PCIERCn_CFG010,PCIERCn_CFG011) */
/* Most applications should disable the prefetchable BAR by setting */
/* PCIERCn_CFG011[UMEM_LIMIT],PCIERCn_CFG009[LMEM_LIMIT] < */
/* PCIERCn_CFG010[UMEM_BASE],PCIERCn_CFG009[LMEM_BASE] */
{
cvmx_pciercx_cfg009_t pciercx_cfg009;
cvmx_pciercx_cfg010_t pciercx_cfg010;
cvmx_pciercx_cfg011_t pciercx_cfg011;
pciercx_cfg009.u32 = cvmx_pcie_cfgx_read(pcie_port, CVMX_PCIERCX_CFG009(pcie_port));
pciercx_cfg010.u32 = cvmx_pcie_cfgx_read(pcie_port, CVMX_PCIERCX_CFG010(pcie_port));
pciercx_cfg011.u32 = cvmx_pcie_cfgx_read(pcie_port, CVMX_PCIERCX_CFG011(pcie_port));
pciercx_cfg009.s.lmem_base = 0x100;
pciercx_cfg009.s.lmem_limit = 0;
pciercx_cfg010.s.umem_base = 0x100;
pciercx_cfg011.s.umem_limit = 0;
cvmx_pcie_cfgx_write(pcie_port, CVMX_PCIERCX_CFG009(pcie_port), pciercx_cfg009.u32);
cvmx_pcie_cfgx_write(pcie_port, CVMX_PCIERCX_CFG010(pcie_port), pciercx_cfg010.u32);
cvmx_pcie_cfgx_write(pcie_port, CVMX_PCIERCX_CFG011(pcie_port), pciercx_cfg011.u32);
}
/* System Error Interrupt Enables (PCIERCn_CFG035[SECEE,SEFEE,SENFEE]) */
/* PME Interrupt Enables (PCIERCn_CFG035[PMEIE]) */
{
cvmx_pciercx_cfg035_t pciercx_cfg035;
pciercx_cfg035.u32 = cvmx_pcie_cfgx_read(pcie_port, CVMX_PCIERCX_CFG035(pcie_port));
pciercx_cfg035.s.secee = 1; /* System error on correctable error enable. */
pciercx_cfg035.s.sefee = 1; /* System error on fatal error enable. */
pciercx_cfg035.s.senfee = 1; /* System error on non-fatal error enable. */
pciercx_cfg035.s.pmeie = 1; /* PME interrupt enable. */
cvmx_pcie_cfgx_write(pcie_port, CVMX_PCIERCX_CFG035(pcie_port), pciercx_cfg035.u32);
}
/* Advanced Error Recovery Interrupt Enables */
/* (PCIERCn_CFG075[CERE,NFERE,FERE]) */
{
cvmx_pciercx_cfg075_t pciercx_cfg075;
pciercx_cfg075.u32 = cvmx_pcie_cfgx_read(pcie_port, CVMX_PCIERCX_CFG075(pcie_port));
pciercx_cfg075.s.cere = 1; /* Correctable error reporting enable. */
pciercx_cfg075.s.nfere = 1; /* Non-fatal error reporting enable. */
pciercx_cfg075.s.fere = 1; /* Fatal error reporting enable. */
cvmx_pcie_cfgx_write(pcie_port, CVMX_PCIERCX_CFG075(pcie_port), pciercx_cfg075.u32);
}
/* HP Interrupt Enables (PCIERCn_CFG034[HPINT_EN], */
/* PCIERCn_CFG034[DLLS_EN,CCINT_EN]) */
{
cvmx_pciercx_cfg034_t pciercx_cfg034;
pciercx_cfg034.u32 = cvmx_pcie_cfgx_read(pcie_port, CVMX_PCIERCX_CFG034(pcie_port));
pciercx_cfg034.s.hpint_en = 1; /* Hot-plug interrupt enable. */
pciercx_cfg034.s.dlls_en = 1; /* Data Link Layer state changed enable */
pciercx_cfg034.s.ccint_en = 1; /* Command completed interrupt enable. */
cvmx_pcie_cfgx_write(pcie_port, CVMX_PCIERCX_CFG034(pcie_port), pciercx_cfg034.u32);
}
}
/**
* @INTERNAL
* Initialize a host mode PCIe gen 1 link. This function takes a PCIe
* port from reset to a link up state. Software can then begin
* configuring the rest of the link.
*
* @param pcie_port PCIe port to initialize
*
* @return Zero on success
*/
static int __cvmx_pcie_rc_initialize_link_gen1(int pcie_port)
{
uint64_t start_cycle;
cvmx_pescx_ctl_status_t pescx_ctl_status;
cvmx_pciercx_cfg452_t pciercx_cfg452;
cvmx_pciercx_cfg032_t pciercx_cfg032;
cvmx_pciercx_cfg448_t pciercx_cfg448;
/* Set the lane width */
pciercx_cfg452.u32 = cvmx_pcie_cfgx_read(pcie_port, CVMX_PCIERCX_CFG452(pcie_port));
pescx_ctl_status.u64 = cvmx_read_csr(CVMX_PESCX_CTL_STATUS(pcie_port));
if (pescx_ctl_status.s.qlm_cfg == 0)
{
/* We're in 8 lane (56XX) or 4 lane (54XX) mode */
pciercx_cfg452.s.lme = 0xf;
}
else
{
/* We're in 4 lane (56XX) or 2 lane (52XX) mode */
pciercx_cfg452.s.lme = 0x7;
}
cvmx_pcie_cfgx_write(pcie_port, CVMX_PCIERCX_CFG452(pcie_port), pciercx_cfg452.u32);
/* CN52XX pass 1.x has an errata where length mismatches on UR responses can
cause bus errors on 64bit memory reads. Turning off length error
checking fixes this */
if (OCTEON_IS_MODEL(OCTEON_CN52XX_PASS1_X))
{
cvmx_pciercx_cfg455_t pciercx_cfg455;
pciercx_cfg455.u32 = cvmx_pcie_cfgx_read(pcie_port, CVMX_PCIERCX_CFG455(pcie_port));
pciercx_cfg455.s.m_cpl_len_err = 1;
cvmx_pcie_cfgx_write(pcie_port, CVMX_PCIERCX_CFG455(pcie_port), pciercx_cfg455.u32);
}
/* Lane swap needs to be manually enabled for CN52XX */
if (OCTEON_IS_MODEL(OCTEON_CN52XX) && (pcie_port == 1))
{
switch (cvmx_sysinfo_get()->board_type)
{
#if defined(OCTEON_VENDOR_LANNER)
case CVMX_BOARD_TYPE_CUST_LANNER_MR730:
break;
#endif
default:
pescx_ctl_status.s.lane_swp = 1;
break;
}
cvmx_write_csr(CVMX_PESCX_CTL_STATUS(pcie_port),pescx_ctl_status.u64);
}
/* Bring up the link */
pescx_ctl_status.u64 = cvmx_read_csr(CVMX_PESCX_CTL_STATUS(pcie_port));
pescx_ctl_status.s.lnk_enb = 1;
cvmx_write_csr(CVMX_PESCX_CTL_STATUS(pcie_port), pescx_ctl_status.u64);
/* CN52XX pass 1.0: Due to a bug in 2nd order CDR, it needs to be disabled */
if (OCTEON_IS_MODEL(OCTEON_CN52XX_PASS1_0))
__cvmx_helper_errata_qlm_disable_2nd_order_cdr(0);
/* Wait for the link to come up */
start_cycle = cvmx_get_cycle();
do
{
if (cvmx_get_cycle() - start_cycle > 2*cvmx_clock_get_rate(CVMX_CLOCK_CORE))
{
cvmx_dprintf("PCIe: Port %d link timeout\n", pcie_port);
return -1;
}
cvmx_wait(10000);
pciercx_cfg032.u32 = cvmx_pcie_cfgx_read(pcie_port, CVMX_PCIERCX_CFG032(pcie_port));
} while (pciercx_cfg032.s.dlla == 0);
/* Clear all pending errors */
cvmx_write_csr(CVMX_PEXP_NPEI_INT_SUM, cvmx_read_csr(CVMX_PEXP_NPEI_INT_SUM));
/* Update the Replay Time Limit. Empirically, some PCIe devices take a
little longer to respond than expected under load. As a workaround for
this we configure the Replay Time Limit to the value expected for a 512
byte MPS instead of our actual 256 byte MPS. The numbers below are
directly from the PCIe spec table 3-4 */
pciercx_cfg448.u32 = cvmx_pcie_cfgx_read(pcie_port, CVMX_PCIERCX_CFG448(pcie_port));
switch (pciercx_cfg032.s.nlw)
{
case 1: /* 1 lane */
pciercx_cfg448.s.rtl = 1677;
break;
case 2: /* 2 lanes */
pciercx_cfg448.s.rtl = 867;
break;
case 4: /* 4 lanes */
pciercx_cfg448.s.rtl = 462;
break;
case 8: /* 8 lanes */
pciercx_cfg448.s.rtl = 258;
break;
}
cvmx_pcie_cfgx_write(pcie_port, CVMX_PCIERCX_CFG448(pcie_port), pciercx_cfg448.u32);
return 0;
}
static inline void __cvmx_increment_ba(cvmx_sli_mem_access_subidx_t *pmas)
{
if (OCTEON_IS_MODEL(OCTEON_CN68XX))
pmas->cn68xx.ba++;
else
pmas->cn63xx.ba++;
}
/**
* Initialize a PCIe gen 1 port for use in host(RC) mode. It doesn't enumerate
* the bus.
*
* @param pcie_port PCIe port to initialize
*
* @return Zero on success
*/
static int __cvmx_pcie_rc_initialize_gen1(int pcie_port)
{
int i;
int base;
uint64_t addr_swizzle;
cvmx_ciu_soft_prst_t ciu_soft_prst;
cvmx_pescx_bist_status_t pescx_bist_status;
cvmx_pescx_bist_status2_t pescx_bist_status2;
cvmx_npei_ctl_status_t npei_ctl_status;
cvmx_npei_mem_access_ctl_t npei_mem_access_ctl;
cvmx_npei_mem_access_subidx_t mem_access_subid;
cvmx_npei_dbg_data_t npei_dbg_data;
cvmx_pescx_ctl_status2_t pescx_ctl_status2;
cvmx_pciercx_cfg032_t pciercx_cfg032;
cvmx_npei_bar1_indexx_t bar1_index;
retry:
/* Make sure we aren't trying to setup a target mode interface in host mode */
npei_ctl_status.u64 = cvmx_read_csr(CVMX_PEXP_NPEI_CTL_STATUS);
if ((pcie_port==0) && !npei_ctl_status.s.host_mode)
{
cvmx_dprintf("PCIe: Port %d in endpoint mode\n", pcie_port);
return -1;
}
/* Make sure a CN52XX isn't trying to bring up port 1 when it is disabled */
if (OCTEON_IS_MODEL(OCTEON_CN52XX))
{
npei_dbg_data.u64 = cvmx_read_csr(CVMX_PEXP_NPEI_DBG_DATA);
if ((pcie_port==1) && npei_dbg_data.cn52xx.qlm0_link_width)
{
cvmx_dprintf("PCIe: ERROR: cvmx_pcie_rc_initialize() called on port1, but port1 is disabled\n");
return -1;
}
}
/* Make sure a CN56XX pass 1 isn't trying to do anything; errata for PASS 1 */
if (OCTEON_IS_MODEL(OCTEON_CN56XX_PASS1_X)) {
cvmx_dprintf ("PCIe port %d: CN56XX_PASS_1, skipping\n", pcie_port);
return -1;
}
/* PCIe switch arbitration mode. '0' == fixed priority NPEI, PCIe0, then PCIe1. '1' == round robin. */
npei_ctl_status.s.arb = 1;
/* Allow up to 0x20 config retries */
npei_ctl_status.s.cfg_rtry = 0x20;
/* CN52XX pass1.x has an errata where P0_NTAGS and P1_NTAGS don't reset */
if (OCTEON_IS_MODEL(OCTEON_CN52XX_PASS1_X))
{
npei_ctl_status.s.p0_ntags = 0x20;
npei_ctl_status.s.p1_ntags = 0x20;
}
cvmx_write_csr(CVMX_PEXP_NPEI_CTL_STATUS, npei_ctl_status.u64);
/* Bring the PCIe out of reset */
if (cvmx_sysinfo_get()->board_type == CVMX_BOARD_TYPE_EBH5200)
{
/* The EBH5200 board swapped the PCIe reset lines on the board. As a
workaround for this bug, we bring both PCIe ports out of reset at
the same time instead of on separate calls. So for port 0, we bring
both out of reset and do nothing on port 1 */
if (pcie_port == 0)
{
ciu_soft_prst.u64 = cvmx_read_csr(CVMX_CIU_SOFT_PRST);
/* After a chip reset the PCIe will also be in reset. If it isn't,
most likely someone is trying to init it again without a proper
PCIe reset */
if (ciu_soft_prst.s.soft_prst == 0)
{
/* Reset the ports */
ciu_soft_prst.s.soft_prst = 1;
cvmx_write_csr(CVMX_CIU_SOFT_PRST, ciu_soft_prst.u64);
ciu_soft_prst.u64 = cvmx_read_csr(CVMX_CIU_SOFT_PRST1);
ciu_soft_prst.s.soft_prst = 1;
cvmx_write_csr(CVMX_CIU_SOFT_PRST1, ciu_soft_prst.u64);
/* Wait until pcie resets the ports. */
cvmx_wait_usec(2000);
}
ciu_soft_prst.u64 = cvmx_read_csr(CVMX_CIU_SOFT_PRST1);
ciu_soft_prst.s.soft_prst = 0;
cvmx_write_csr(CVMX_CIU_SOFT_PRST1, ciu_soft_prst.u64);
ciu_soft_prst.u64 = cvmx_read_csr(CVMX_CIU_SOFT_PRST);
ciu_soft_prst.s.soft_prst = 0;
cvmx_write_csr(CVMX_CIU_SOFT_PRST, ciu_soft_prst.u64);
}
}
else
{
/* The normal case: The PCIe ports are completely separate and can be
brought out of reset independently */
if (pcie_port)
ciu_soft_prst.u64 = cvmx_read_csr(CVMX_CIU_SOFT_PRST1);
else
ciu_soft_prst.u64 = cvmx_read_csr(CVMX_CIU_SOFT_PRST);
/* After a chip reset the PCIe will also be in reset. If it isn't,
most likely someone is trying to init it again without a proper
PCIe reset */
if (ciu_soft_prst.s.soft_prst == 0)
{
/* Reset the port */
ciu_soft_prst.s.soft_prst = 1;
if (pcie_port)
cvmx_write_csr(CVMX_CIU_SOFT_PRST1, ciu_soft_prst.u64);
else
cvmx_write_csr(CVMX_CIU_SOFT_PRST, ciu_soft_prst.u64);
/* Wait until pcie resets the ports. */
cvmx_wait_usec(2000);
}
if (pcie_port)
{
ciu_soft_prst.u64 = cvmx_read_csr(CVMX_CIU_SOFT_PRST1);
ciu_soft_prst.s.soft_prst = 0;
cvmx_write_csr(CVMX_CIU_SOFT_PRST1, ciu_soft_prst.u64);
}
else
{
ciu_soft_prst.u64 = cvmx_read_csr(CVMX_CIU_SOFT_PRST);
ciu_soft_prst.s.soft_prst = 0;
cvmx_write_csr(CVMX_CIU_SOFT_PRST, ciu_soft_prst.u64);
}
}
/* Wait for PCIe reset to complete. Due to errata PCIE-700, we don't poll
PESCX_CTL_STATUS2[PCIERST], but simply wait a fixed number of cycles */
cvmx_wait(400000);
/* PESCX_BIST_STATUS2[PCLK_RUN] was missing on pass 1 of CN56XX and
CN52XX, so we only probe it on newer chips */
if (!OCTEON_IS_MODEL(OCTEON_CN56XX_PASS1_X) && !OCTEON_IS_MODEL(OCTEON_CN52XX_PASS1_X))
{
/* Clear PCLK_RUN so we can check if the clock is running */
pescx_ctl_status2.u64 = cvmx_read_csr(CVMX_PESCX_CTL_STATUS2(pcie_port));
pescx_ctl_status2.s.pclk_run = 1;
cvmx_write_csr(CVMX_PESCX_CTL_STATUS2(pcie_port), pescx_ctl_status2.u64);
/* Now that we cleared PCLK_RUN, wait for it to be set again telling
us the clock is running */
if (CVMX_WAIT_FOR_FIELD64(CVMX_PESCX_CTL_STATUS2(pcie_port),
cvmx_pescx_ctl_status2_t, pclk_run, ==, 1, 10000))
{
cvmx_dprintf("PCIe: Port %d isn't clocked, skipping.\n", pcie_port);
return -1;
}
}
/* Check and make sure PCIe came out of reset. If it doesn't the board
probably hasn't wired the clocks up and the interface should be
skipped */
pescx_ctl_status2.u64 = cvmx_read_csr(CVMX_PESCX_CTL_STATUS2(pcie_port));
if (pescx_ctl_status2.s.pcierst)
{
cvmx_dprintf("PCIe: Port %d stuck in reset, skipping.\n", pcie_port);
return -1;
}
/* Check BIST2 status. If any bits are set skip this interface. This
is an attempt to catch PCIE-813 on pass 1 parts */
pescx_bist_status2.u64 = cvmx_read_csr(CVMX_PESCX_BIST_STATUS2(pcie_port));
if (pescx_bist_status2.u64)
{
cvmx_dprintf("PCIe: Port %d BIST2 failed. Most likely this port isn't hooked up, skipping.\n", pcie_port);
return -1;
}
/* Check BIST status */
pescx_bist_status.u64 = cvmx_read_csr(CVMX_PESCX_BIST_STATUS(pcie_port));
if (pescx_bist_status.u64)
cvmx_dprintf("PCIe: BIST FAILED for port %d (0x%016llx)\n", pcie_port, CAST64(pescx_bist_status.u64));
/* Initialize the config space CSRs */
__cvmx_pcie_rc_initialize_config_space(pcie_port);
/* Bring the link up */
if (__cvmx_pcie_rc_initialize_link_gen1(pcie_port))
{
cvmx_dprintf("PCIe: Failed to initialize port %d, probably the slot is empty\n", pcie_port);
return -1;
}
/* Store merge control (NPEI_MEM_ACCESS_CTL[TIMER,MAX_WORD]) */
npei_mem_access_ctl.u64 = cvmx_read_csr(CVMX_PEXP_NPEI_MEM_ACCESS_CTL);
npei_mem_access_ctl.s.max_word = 0; /* Allow 16 words to combine */
npei_mem_access_ctl.s.timer = 127; /* Wait up to 127 cycles for more data */
cvmx_write_csr(CVMX_PEXP_NPEI_MEM_ACCESS_CTL, npei_mem_access_ctl.u64);
/* Setup Mem access SubDIDs */
mem_access_subid.u64 = 0;
mem_access_subid.s.port = pcie_port; /* Port the request is sent to. */
mem_access_subid.s.nmerge = 1; /* Due to an errata on pass 1 chips, no merging is allowed. */
mem_access_subid.s.esr = 1; /* Endian-swap for Reads. */
mem_access_subid.s.esw = 1; /* Endian-swap for Writes. */
mem_access_subid.s.nsr = 0; /* Enable Snooping for Reads. Octeon doesn't care, but devices might want this more conservative setting */
mem_access_subid.s.nsw = 0; /* Enable Snoop for Writes. */
mem_access_subid.s.ror = 0; /* Disable Relaxed Ordering for Reads. */
mem_access_subid.s.row = 0; /* Disable Relaxed Ordering for Writes. */
mem_access_subid.s.ba = 0; /* PCIe Adddress Bits <63:34>. */
/* Setup mem access 12-15 for port 0, 16-19 for port 1, supplying 36 bits of address space */
for (i=12 + pcie_port*4; i<16 + pcie_port*4; i++)
{
cvmx_write_csr(CVMX_PEXP_NPEI_MEM_ACCESS_SUBIDX(i), mem_access_subid.u64);
mem_access_subid.s.ba += 1; /* Set each SUBID to extend the addressable range */
}
/* Disable the peer to peer forwarding register. This must be setup
by the OS after it enumerates the bus and assigns addresses to the
PCIe busses */
for (i=0; i<4; i++)
{
cvmx_write_csr(CVMX_PESCX_P2P_BARX_START(i, pcie_port), -1);
cvmx_write_csr(CVMX_PESCX_P2P_BARX_END(i, pcie_port), -1);
}
/* Set Octeon's BAR0 to decode 0-16KB. It overlaps with Bar2 */
cvmx_write_csr(CVMX_PESCX_P2N_BAR0_START(pcie_port), 0);
/* BAR1 follows BAR2 with a gap so it has the same address as for gen2. */
cvmx_write_csr(CVMX_PESCX_P2N_BAR1_START(pcie_port), CVMX_PCIE_BAR1_RC_BASE);
bar1_index.u32 = 0;
bar1_index.s.addr_idx = (CVMX_PCIE_BAR1_PHYS_BASE >> 22);
bar1_index.s.ca = 1; /* Not Cached */
bar1_index.s.end_swp = 1; /* Endian Swap mode */
bar1_index.s.addr_v = 1; /* Valid entry */
base = pcie_port ? 16 : 0;
/* Big endian swizzle for 32-bit PEXP_NCB register. */
#ifdef __MIPSEB__
addr_swizzle = 4;
#else
addr_swizzle = 0;
#endif
for (i = 0; i < 16; i++) {
cvmx_write64_uint32((CVMX_PEXP_NPEI_BAR1_INDEXX(base) ^ addr_swizzle), bar1_index.u32);
base++;
/* 256MB / 16 >> 22 == 4 */
bar1_index.s.addr_idx += (((1ull << 28) / 16ull) >> 22);
}
/* Set Octeon's BAR2 to decode 0-2^39. Bar0 and Bar1 take precedence
where they overlap. It also overlaps with the device addresses, so
make sure the peer to peer forwarding is set right */
cvmx_write_csr(CVMX_PESCX_P2N_BAR2_START(pcie_port), 0);
/* Setup BAR2 attributes */
/* Relaxed Ordering (NPEI_CTL_PORTn[PTLP_RO,CTLP_RO, WAIT_COM]) */
/* PTLP_RO,CTLP_RO should normally be set (except for debug). */
/* WAIT_COM=0 will likely work for all applications. */
/* Load completion relaxed ordering (NPEI_CTL_PORTn[WAITL_COM]) */
if (pcie_port)
{
cvmx_npei_ctl_port1_t npei_ctl_port;
npei_ctl_port.u64 = cvmx_read_csr(CVMX_PEXP_NPEI_CTL_PORT1);
npei_ctl_port.s.bar2_enb = 1;
npei_ctl_port.s.bar2_esx = 1;
npei_ctl_port.s.bar2_cax = 0;
npei_ctl_port.s.ptlp_ro = 1;
npei_ctl_port.s.ctlp_ro = 1;
npei_ctl_port.s.wait_com = 0;
npei_ctl_port.s.waitl_com = 0;
cvmx_write_csr(CVMX_PEXP_NPEI_CTL_PORT1, npei_ctl_port.u64);
}
else
{
cvmx_npei_ctl_port0_t npei_ctl_port;
npei_ctl_port.u64 = cvmx_read_csr(CVMX_PEXP_NPEI_CTL_PORT0);
npei_ctl_port.s.bar2_enb = 1;
npei_ctl_port.s.bar2_esx = 1;
npei_ctl_port.s.bar2_cax = 0;
npei_ctl_port.s.ptlp_ro = 1;
npei_ctl_port.s.ctlp_ro = 1;
npei_ctl_port.s.wait_com = 0;
npei_ctl_port.s.waitl_com = 0;
cvmx_write_csr(CVMX_PEXP_NPEI_CTL_PORT0, npei_ctl_port.u64);
}
/* Both pass 1 and pass 2 of CN52XX and CN56XX have an errata that causes
TLP ordering to not be preserved after multiple PCIe port resets. This
code detects this fault and corrects it by aligning the TLP counters
properly. Another link reset is then performed. See PCIE-13340 */
if (OCTEON_IS_MODEL(OCTEON_CN56XX_PASS2_X) || OCTEON_IS_MODEL(OCTEON_CN52XX_PASS2_X) ||
OCTEON_IS_MODEL(OCTEON_CN56XX_PASS1_X) || OCTEON_IS_MODEL(OCTEON_CN52XX_PASS1_X))
{
cvmx_npei_dbg_data_t dbg_data;
int old_in_fif_p_count;
int in_fif_p_count;
int out_p_count;
int in_p_offset = (OCTEON_IS_MODEL(OCTEON_CN52XX_PASS1_X) || OCTEON_IS_MODEL(OCTEON_CN56XX_PASS1_X)) ? 4 : 1;
int i;
/* Choose a write address of 1MB. It should be harmless as all bars
haven't been setup */
uint64_t write_address = (cvmx_pcie_get_mem_base_address(pcie_port) + 0x100000) | (1ull<<63);
/* Make sure at least in_p_offset have been executed before we try and
read in_fif_p_count */
i = in_p_offset;
while (i--)
{
cvmx_write64_uint32(write_address, 0);
cvmx_wait(10000);
}
/* Read the IN_FIF_P_COUNT from the debug select. IN_FIF_P_COUNT can be
unstable sometimes so read it twice with a write between the reads.
This way we can tell the value is good as it will increment by one
due to the write */
cvmx_write_csr(CVMX_PEXP_NPEI_DBG_SELECT, (pcie_port) ? 0xd7fc : 0xcffc);
cvmx_read_csr(CVMX_PEXP_NPEI_DBG_SELECT);
do
{
dbg_data.u64 = cvmx_read_csr(CVMX_PEXP_NPEI_DBG_DATA);
old_in_fif_p_count = dbg_data.s.data & 0xff;
cvmx_write64_uint32(write_address, 0);
cvmx_wait(10000);
dbg_data.u64 = cvmx_read_csr(CVMX_PEXP_NPEI_DBG_DATA);
in_fif_p_count = dbg_data.s.data & 0xff;
} while (in_fif_p_count != ((old_in_fif_p_count+1) & 0xff));
/* Update in_fif_p_count for it's offset with respect to out_p_count */
in_fif_p_count = (in_fif_p_count + in_p_offset) & 0xff;
/* Read the OUT_P_COUNT from the debug select */
cvmx_write_csr(CVMX_PEXP_NPEI_DBG_SELECT, (pcie_port) ? 0xd00f : 0xc80f);
cvmx_read_csr(CVMX_PEXP_NPEI_DBG_SELECT);
dbg_data.u64 = cvmx_read_csr(CVMX_PEXP_NPEI_DBG_DATA);
out_p_count = (dbg_data.s.data>>1) & 0xff;
/* Check that the two counters are aligned */
if (out_p_count != in_fif_p_count)
{
cvmx_dprintf("PCIe: Port %d aligning TLP counters as workaround to maintain ordering\n", pcie_port);
while (in_fif_p_count != 0)
{
cvmx_write64_uint32(write_address, 0);
cvmx_wait(10000);
in_fif_p_count = (in_fif_p_count + 1) & 0xff;
}
/* The EBH5200 board swapped the PCIe reset lines on the board. This
means we must bring both links down and up, which will cause the
PCIe0 to need alignment again. Lots of messages will be displayed,
but everything should work */
if ((cvmx_sysinfo_get()->board_type == CVMX_BOARD_TYPE_EBH5200) &&
(pcie_port == 1))
cvmx_pcie_rc_initialize(0);
/* Rety bringing this port up */
goto retry;
}
}
/* Display the link status */
pciercx_cfg032.u32 = cvmx_pcie_cfgx_read(pcie_port, CVMX_PCIERCX_CFG032(pcie_port));
cvmx_dprintf("PCIe: Port %d link active, %d lanes\n", pcie_port, pciercx_cfg032.s.nlw);
return 0;
}
/**
* @INTERNAL
* Initialize a host mode PCIe gen 2 link. This function takes a PCIe
* port from reset to a link up state. Software can then begin
* configuring the rest of the link.
*
* @param pcie_port PCIe port to initialize
*
* @return Zero on success
*/
static int __cvmx_pcie_rc_initialize_link_gen2(int pcie_port)
{
uint64_t start_cycle;
cvmx_pemx_ctl_status_t pem_ctl_status;
cvmx_pciercx_cfg032_t pciercx_cfg032;
cvmx_pciercx_cfg448_t pciercx_cfg448;
/* Bring up the link */
pem_ctl_status.u64 = cvmx_read_csr(CVMX_PEMX_CTL_STATUS(pcie_port));
pem_ctl_status.s.lnk_enb = 1;
cvmx_write_csr(CVMX_PEMX_CTL_STATUS(pcie_port), pem_ctl_status.u64);
/* Wait for the link to come up */
start_cycle = cvmx_get_cycle();
do
{
if (cvmx_get_cycle() - start_cycle > cvmx_clock_get_rate(CVMX_CLOCK_CORE))
return -1;
cvmx_wait(10000);
pciercx_cfg032.u32 = cvmx_pcie_cfgx_read(pcie_port, CVMX_PCIERCX_CFG032(pcie_port));
} while ((pciercx_cfg032.s.dlla == 0) || (pciercx_cfg032.s.lt == 1));
/* Update the Replay Time Limit. Empirically, some PCIe devices take a
little longer to respond than expected under load. As a workaround for
this we configure the Replay Time Limit to the value expected for a 512
byte MPS instead of our actual 256 byte MPS. The numbers below are
directly from the PCIe spec table 3-4 */
pciercx_cfg448.u32 = cvmx_pcie_cfgx_read(pcie_port, CVMX_PCIERCX_CFG448(pcie_port));
switch (pciercx_cfg032.s.nlw)
{
case 1: /* 1 lane */
pciercx_cfg448.s.rtl = 1677;
break;
case 2: /* 2 lanes */
pciercx_cfg448.s.rtl = 867;
break;
case 4: /* 4 lanes */
pciercx_cfg448.s.rtl = 462;
break;
case 8: /* 8 lanes */
pciercx_cfg448.s.rtl = 258;
break;
}
cvmx_pcie_cfgx_write(pcie_port, CVMX_PCIERCX_CFG448(pcie_port), pciercx_cfg448.u32);
return 0;
}
/**
* Initialize a PCIe gen 2 port for use in host(RC) mode. It doesn't enumerate
* the bus.
*
* @param pcie_port PCIe port to initialize
*
* @return Zero on success
*/
static int __cvmx_pcie_rc_initialize_gen2(int pcie_port)
{
int i;
cvmx_ciu_soft_prst_t ciu_soft_prst;
cvmx_mio_rst_ctlx_t mio_rst_ctl;
cvmx_pemx_bar_ctl_t pemx_bar_ctl;
cvmx_pemx_ctl_status_t pemx_ctl_status;
cvmx_pemx_bist_status_t pemx_bist_status;
cvmx_pemx_bist_status2_t pemx_bist_status2;
cvmx_pciercx_cfg032_t pciercx_cfg032;
cvmx_pciercx_cfg515_t pciercx_cfg515;
cvmx_sli_ctl_portx_t sli_ctl_portx;
cvmx_sli_mem_access_ctl_t sli_mem_access_ctl;
cvmx_sli_mem_access_subidx_t mem_access_subid;
cvmx_pemx_bar1_indexx_t bar1_index;
int ep_mode;
/* Make sure this interface is PCIe */
if (OCTEON_IS_MODEL(OCTEON_CN6XXX) || OCTEON_IS_MODEL(OCTEON_CNF71XX))
{
/* Requires reading the MIO_QLMX_CFG register to figure
out the port type. */
int qlm = pcie_port;
int status;
if (OCTEON_IS_MODEL(OCTEON_CN68XX))
qlm = 3 - (pcie_port * 2);
else if (OCTEON_IS_MODEL(OCTEON_CN61XX))
{
cvmx_mio_qlmx_cfg_t qlm_cfg;
qlm_cfg.u64 = cvmx_read_csr(CVMX_MIO_QLMX_CFG(1));
if (qlm_cfg.s.qlm_cfg == 1)
qlm = 1;
}
/* PCIe is allowed only in QLM1, 1 PCIe port in x2 or
2 PCIe ports in x1 */
else if (OCTEON_IS_MODEL(OCTEON_CNF71XX))
qlm = 1;
status = cvmx_qlm_get_status(qlm);
if (status == 4 || status == 5)
{
cvmx_dprintf("PCIe: Port %d is SRIO, skipping.\n", pcie_port);
return -1;
}
if (status == 1)
{
cvmx_dprintf("PCIe: Port %d is SGMII, skipping.\n", pcie_port);
return -1;
}
if (status == 2)
{
cvmx_dprintf("PCIe: Port %d is XAUI, skipping.\n", pcie_port);
return -1;
}
if (status == -1)
{
cvmx_dprintf("PCIe: Port %d is unknown, skipping.\n", pcie_port);
return -1;
}
}
#if 0
/* This code is so that the PCIe analyzer is able to see 63XX traffic */
cvmx_dprintf("PCIE : init for pcie analyzer.\n");
cvmx_helper_qlm_jtag_init();
cvmx_helper_qlm_jtag_shift_zeros(pcie_port, 85);
cvmx_helper_qlm_jtag_shift(pcie_port, 1, 1);
cvmx_helper_qlm_jtag_shift_zeros(pcie_port, 300-86);
cvmx_helper_qlm_jtag_shift_zeros(pcie_port, 85);
cvmx_helper_qlm_jtag_shift(pcie_port, 1, 1);
cvmx_helper_qlm_jtag_shift_zeros(pcie_port, 300-86);
cvmx_helper_qlm_jtag_shift_zeros(pcie_port, 85);
cvmx_helper_qlm_jtag_shift(pcie_port, 1, 1);
cvmx_helper_qlm_jtag_shift_zeros(pcie_port, 300-86);
cvmx_helper_qlm_jtag_shift_zeros(pcie_port, 85);
cvmx_helper_qlm_jtag_shift(pcie_port, 1, 1);
cvmx_helper_qlm_jtag_shift_zeros(pcie_port, 300-86);
cvmx_helper_qlm_jtag_update(pcie_port);
#endif
/* Make sure we aren't trying to setup a target mode interface in host mode */
mio_rst_ctl.u64 = cvmx_read_csr(CVMX_MIO_RST_CTLX(pcie_port));
ep_mode = (OCTEON_IS_MODEL(OCTEON_CN61XX || OCTEON_IS_MODEL(OCTEON_CNF71XX)) ? (mio_rst_ctl.s.prtmode != 1) : (!mio_rst_ctl.s.host_mode));
if (ep_mode)
{
cvmx_dprintf("PCIe: Port %d in endpoint mode.\n", pcie_port);
return -1;
}
/* CN63XX Pass 1.0 errata G-14395 requires the QLM De-emphasis be programmed */
if (OCTEON_IS_MODEL(OCTEON_CN63XX_PASS1_0))
{
if (pcie_port)
{
cvmx_ciu_qlm1_t ciu_qlm;
ciu_qlm.u64 = cvmx_read_csr(CVMX_CIU_QLM1);
ciu_qlm.s.txbypass = 1;
ciu_qlm.s.txdeemph = 5;
ciu_qlm.s.txmargin = 0x17;
cvmx_write_csr(CVMX_CIU_QLM1, ciu_qlm.u64);
}
else
{
cvmx_ciu_qlm0_t ciu_qlm;
ciu_qlm.u64 = cvmx_read_csr(CVMX_CIU_QLM0);
ciu_qlm.s.txbypass = 1;
ciu_qlm.s.txdeemph = 5;
ciu_qlm.s.txmargin = 0x17;
cvmx_write_csr(CVMX_CIU_QLM0, ciu_qlm.u64);
}
}
/* Bring the PCIe out of reset */
if (pcie_port)
ciu_soft_prst.u64 = cvmx_read_csr(CVMX_CIU_SOFT_PRST1);
else
ciu_soft_prst.u64 = cvmx_read_csr(CVMX_CIU_SOFT_PRST);
/* After a chip reset the PCIe will also be in reset. If it isn't,
most likely someone is trying to init it again without a proper
PCIe reset */
if (ciu_soft_prst.s.soft_prst == 0)
{
/* Reset the port */
ciu_soft_prst.s.soft_prst = 1;
if (pcie_port)
cvmx_write_csr(CVMX_CIU_SOFT_PRST1, ciu_soft_prst.u64);
else
cvmx_write_csr(CVMX_CIU_SOFT_PRST, ciu_soft_prst.u64);
/* Wait until pcie resets the ports. */
cvmx_wait_usec(2000);
}
if (pcie_port)
{
ciu_soft_prst.u64 = cvmx_read_csr(CVMX_CIU_SOFT_PRST1);
ciu_soft_prst.s.soft_prst = 0;
cvmx_write_csr(CVMX_CIU_SOFT_PRST1, ciu_soft_prst.u64);
}
else
{
ciu_soft_prst.u64 = cvmx_read_csr(CVMX_CIU_SOFT_PRST);
ciu_soft_prst.s.soft_prst = 0;
cvmx_write_csr(CVMX_CIU_SOFT_PRST, ciu_soft_prst.u64);
}
/* Wait for PCIe reset to complete */
cvmx_wait_usec(1000);
/* Check and make sure PCIe came out of reset. If it doesn't the board
probably hasn't wired the clocks up and the interface should be
skipped */
if (CVMX_WAIT_FOR_FIELD64(CVMX_MIO_RST_CTLX(pcie_port), cvmx_mio_rst_ctlx_t, rst_done, ==, 1, 10000))
{
cvmx_dprintf("PCIe: Port %d stuck in reset, skipping.\n", pcie_port);
return -1;
}
/* Check BIST status */
pemx_bist_status.u64 = cvmx_read_csr(CVMX_PEMX_BIST_STATUS(pcie_port));
if (pemx_bist_status.u64)
cvmx_dprintf("PCIe: BIST FAILED for port %d (0x%016llx)\n", pcie_port, CAST64(pemx_bist_status.u64));
pemx_bist_status2.u64 = cvmx_read_csr(CVMX_PEMX_BIST_STATUS2(pcie_port));
/* Errata PCIE-14766 may cause the lower 6 bits to be randomly set on CN63XXp1 */
if (OCTEON_IS_MODEL(OCTEON_CN63XX_PASS1_X))
pemx_bist_status2.u64 &= ~0x3full;
if (pemx_bist_status2.u64)
cvmx_dprintf("PCIe: BIST2 FAILED for port %d (0x%016llx)\n", pcie_port, CAST64(pemx_bist_status2.u64));
/* Initialize the config space CSRs */
__cvmx_pcie_rc_initialize_config_space(pcie_port);
/* Enable gen2 speed selection */
pciercx_cfg515.u32 = cvmx_pcie_cfgx_read(pcie_port, CVMX_PCIERCX_CFG515(pcie_port));
pciercx_cfg515.s.dsc = 1;
cvmx_pcie_cfgx_write(pcie_port, CVMX_PCIERCX_CFG515(pcie_port), pciercx_cfg515.u32);
/* Bring the link up */
if (__cvmx_pcie_rc_initialize_link_gen2(pcie_port))
{
/* Some gen1 devices don't handle the gen 2 training correctly. Disable
gen2 and try again with only gen1 */
cvmx_pciercx_cfg031_t pciercx_cfg031;
pciercx_cfg031.u32 = cvmx_pcie_cfgx_read(pcie_port, CVMX_PCIERCX_CFG031(pcie_port));
pciercx_cfg031.s.mls = 1;
cvmx_pcie_cfgx_write(pcie_port, CVMX_PCIERCX_CFG031(pcie_port), pciercx_cfg031.u32);
if (__cvmx_pcie_rc_initialize_link_gen2(pcie_port))
{
cvmx_dprintf("PCIe: Link timeout on port %d, probably the slot is empty\n", pcie_port);
return -1;
}
}
/* Store merge control (SLI_MEM_ACCESS_CTL[TIMER,MAX_WORD]) */
sli_mem_access_ctl.u64 = cvmx_read_csr(CVMX_PEXP_SLI_MEM_ACCESS_CTL);
sli_mem_access_ctl.s.max_word = 0; /* Allow 16 words to combine */
sli_mem_access_ctl.s.timer = 127; /* Wait up to 127 cycles for more data */
cvmx_write_csr(CVMX_PEXP_SLI_MEM_ACCESS_CTL, sli_mem_access_ctl.u64);
/* Setup Mem access SubDIDs */
mem_access_subid.u64 = 0;
mem_access_subid.s.port = pcie_port; /* Port the request is sent to. */
mem_access_subid.s.nmerge = 0; /* Allow merging as it works on CN6XXX. */
mem_access_subid.s.esr = 1; /* Endian-swap for Reads. */
mem_access_subid.s.esw = 1; /* Endian-swap for Writes. */
mem_access_subid.s.wtype = 0; /* "No snoop" and "Relaxed ordering" are not set */
mem_access_subid.s.rtype = 0; /* "No snoop" and "Relaxed ordering" are not set */
/* PCIe Adddress Bits <63:34>. */
if (OCTEON_IS_MODEL(OCTEON_CN68XX))
mem_access_subid.cn68xx.ba = 0;
else
mem_access_subid.cn63xx.ba = 0;
/* Setup mem access 12-15 for port 0, 16-19 for port 1, supplying 36 bits of address space */
for (i=12 + pcie_port*4; i<16 + pcie_port*4; i++)
{
cvmx_write_csr(CVMX_PEXP_SLI_MEM_ACCESS_SUBIDX(i), mem_access_subid.u64);
/* Set each SUBID to extend the addressable range */
__cvmx_increment_ba(&mem_access_subid);
}
if (!OCTEON_IS_MODEL(OCTEON_CN61XX))
{
/* Disable the peer to peer forwarding register. This must be setup
by the OS after it enumerates the bus and assigns addresses to the
PCIe busses */
for (i=0; i<4; i++)
{
cvmx_write_csr(CVMX_PEMX_P2P_BARX_START(i, pcie_port), -1);
cvmx_write_csr(CVMX_PEMX_P2P_BARX_END(i, pcie_port), -1);
}
}
/* Set Octeon's BAR0 to decode 0-16KB. It overlaps with Bar2 */
cvmx_write_csr(CVMX_PEMX_P2N_BAR0_START(pcie_port), 0);
/* Set Octeon's BAR2 to decode 0-2^41. Bar0 and Bar1 take precedence
where they overlap. It also overlaps with the device addresses, so
make sure the peer to peer forwarding is set right */
cvmx_write_csr(CVMX_PEMX_P2N_BAR2_START(pcie_port), 0);
/* Setup BAR2 attributes */
/* Relaxed Ordering (NPEI_CTL_PORTn[PTLP_RO,CTLP_RO, WAIT_COM]) */
/* PTLP_RO,CTLP_RO should normally be set (except for debug). */
/* WAIT_COM=0 will likely work for all applications. */
/* Load completion relaxed ordering (NPEI_CTL_PORTn[WAITL_COM]) */
pemx_bar_ctl.u64 = cvmx_read_csr(CVMX_PEMX_BAR_CTL(pcie_port));
pemx_bar_ctl.s.bar1_siz = 3; /* 256MB BAR1*/
pemx_bar_ctl.s.bar2_enb = 1;
pemx_bar_ctl.s.bar2_esx = 1;
pemx_bar_ctl.s.bar2_cax = 0;
cvmx_write_csr(CVMX_PEMX_BAR_CTL(pcie_port), pemx_bar_ctl.u64);
sli_ctl_portx.u64 = cvmx_read_csr(CVMX_PEXP_SLI_CTL_PORTX(pcie_port));
sli_ctl_portx.s.ptlp_ro = 1;
sli_ctl_portx.s.ctlp_ro = 1;
sli_ctl_portx.s.wait_com = 0;
sli_ctl_portx.s.waitl_com = 0;
cvmx_write_csr(CVMX_PEXP_SLI_CTL_PORTX(pcie_port), sli_ctl_portx.u64);
/* BAR1 follows BAR2 */
cvmx_write_csr(CVMX_PEMX_P2N_BAR1_START(pcie_port), CVMX_PCIE_BAR1_RC_BASE);
bar1_index.u64 = 0;
bar1_index.s.addr_idx = (CVMX_PCIE_BAR1_PHYS_BASE >> 22);
bar1_index.s.ca = 1; /* Not Cached */
bar1_index.s.end_swp = 1; /* Endian Swap mode */
bar1_index.s.addr_v = 1; /* Valid entry */
for (i = 0; i < 16; i++) {
cvmx_write_csr(CVMX_PEMX_BAR1_INDEXX(i, pcie_port), bar1_index.u64);
/* 256MB / 16 >> 22 == 4 */
bar1_index.s.addr_idx += (((1ull << 28) / 16ull) >> 22);
}
/* Allow config retries for 250ms. Count is based off the 5Ghz SERDES
clock */
pemx_ctl_status.u64 = cvmx_read_csr(CVMX_PEMX_CTL_STATUS(pcie_port));
pemx_ctl_status.s.cfg_rtry = 250 * 5000000 / 0x10000;
cvmx_write_csr(CVMX_PEMX_CTL_STATUS(pcie_port), pemx_ctl_status.u64);
/* Display the link status */
pciercx_cfg032.u32 = cvmx_pcie_cfgx_read(pcie_port, CVMX_PCIERCX_CFG032(pcie_port));
cvmx_dprintf("PCIe: Port %d link active, %d lanes, speed gen%d\n", pcie_port, pciercx_cfg032.s.nlw, pciercx_cfg032.s.ls);
return 0;
}
/**
* Initialize a PCIe port for use in host(RC) mode. It doesn't enumerate the bus.
*
* @param pcie_port PCIe port to initialize
*
* @return Zero on success
*/
int cvmx_pcie_rc_initialize(int pcie_port)
{
int result;
if (octeon_has_feature(OCTEON_FEATURE_NPEI))
result = __cvmx_pcie_rc_initialize_gen1(pcie_port);
else
result = __cvmx_pcie_rc_initialize_gen2(pcie_port);
-#if !defined(CVMX_BUILD_FOR_LINUX_KERNEL) || defined(CONFIG_CAVIUM_DECODE_RSL)
+#if (!defined(CVMX_BUILD_FOR_LINUX_KERNEL) && !defined(CVMX_BUILD_FOR_FREEBSD_KERNEL)) || defined(CONFIG_CAVIUM_DECODE_RSL)
if (result == 0)
cvmx_error_enable_group(CVMX_ERROR_GROUP_PCI, pcie_port);
#endif
return result;
}
/**
* Shutdown a PCIe port and put it in reset
*
* @param pcie_port PCIe port to shutdown
*
* @return Zero on success
*/
int cvmx_pcie_rc_shutdown(int pcie_port)
{
-#if !defined(CVMX_BUILD_FOR_LINUX_KERNEL) || defined(CONFIG_CAVIUM_DECODE_RSL)
+#if (!defined(CVMX_BUILD_FOR_LINUX_KERNEL) && !defined(CVMX_BUILD_FOR_FREEBSD_KERNEL)) || defined(CONFIG_CAVIUM_DECODE_RSL)
cvmx_error_disable_group(CVMX_ERROR_GROUP_PCI, pcie_port);
#endif
/* Wait for all pending operations to complete */
if (octeon_has_feature(OCTEON_FEATURE_NPEI))
{
if (CVMX_WAIT_FOR_FIELD64(CVMX_PESCX_CPL_LUT_VALID(pcie_port), cvmx_pescx_cpl_lut_valid_t, tag, ==, 0, 2000))
cvmx_dprintf("PCIe: Port %d shutdown timeout\n", pcie_port);
}
else
{
if (CVMX_WAIT_FOR_FIELD64(CVMX_PEMX_CPL_LUT_VALID(pcie_port), cvmx_pemx_cpl_lut_valid_t, tag, ==, 0, 2000))
cvmx_dprintf("PCIe: Port %d shutdown timeout\n", pcie_port);
}
/* Force reset */
if (pcie_port)
{
cvmx_ciu_soft_prst_t ciu_soft_prst;
ciu_soft_prst.u64 = cvmx_read_csr(CVMX_CIU_SOFT_PRST1);
ciu_soft_prst.s.soft_prst = 1;
cvmx_write_csr(CVMX_CIU_SOFT_PRST1, ciu_soft_prst.u64);
}
else
{
cvmx_ciu_soft_prst_t ciu_soft_prst;
ciu_soft_prst.u64 = cvmx_read_csr(CVMX_CIU_SOFT_PRST);
ciu_soft_prst.s.soft_prst = 1;
cvmx_write_csr(CVMX_CIU_SOFT_PRST, ciu_soft_prst.u64);
}
return 0;
}
/**
* @INTERNAL
* Build a PCIe config space request address for a device
*
* @param pcie_port PCIe port to access
* @param bus Sub bus
* @param dev Device ID
* @param fn Device sub function
* @param reg Register to access
*
* @return 64bit Octeon IO address
*/
static inline uint64_t __cvmx_pcie_build_config_addr(int pcie_port, int bus, int dev, int fn, int reg)
{
cvmx_pcie_address_t pcie_addr;
cvmx_pciercx_cfg006_t pciercx_cfg006;
pciercx_cfg006.u32 = cvmx_pcie_cfgx_read(pcie_port, CVMX_PCIERCX_CFG006(pcie_port));
if ((bus <= pciercx_cfg006.s.pbnum) && (dev != 0))
return 0;
pcie_addr.u64 = 0;
pcie_addr.config.upper = 2;
pcie_addr.config.io = 1;
pcie_addr.config.did = 3;
pcie_addr.config.subdid = 1;
pcie_addr.config.es = 1;
pcie_addr.config.port = pcie_port;
pcie_addr.config.ty = (bus > pciercx_cfg006.s.pbnum);
pcie_addr.config.bus = bus;
pcie_addr.config.dev = dev;
pcie_addr.config.func = fn;
pcie_addr.config.reg = reg;
return pcie_addr.u64;
}
/**
* Read 8bits from a Device's config space
*
* @param pcie_port PCIe port the device is on
* @param bus Sub bus
* @param dev Device ID
* @param fn Device sub function
* @param reg Register to access
*
* @return Result of the read
*/
uint8_t cvmx_pcie_config_read8(int pcie_port, int bus, int dev, int fn, int reg)
{
uint64_t address = __cvmx_pcie_build_config_addr(pcie_port, bus, dev, fn, reg);
if (address)
return cvmx_read64_uint8(address);
else
return 0xff;
}
/**
* Read 16bits from a Device's config space
*
* @param pcie_port PCIe port the device is on
* @param bus Sub bus
* @param dev Device ID
* @param fn Device sub function
* @param reg Register to access
*
* @return Result of the read
*/
uint16_t cvmx_pcie_config_read16(int pcie_port, int bus, int dev, int fn, int reg)
{
uint64_t address = __cvmx_pcie_build_config_addr(pcie_port, bus, dev, fn, reg);
if (address)
return cvmx_le16_to_cpu(cvmx_read64_uint16(address));
else
return 0xffff;
}
/**
* Read 32bits from a Device's config space
*
* @param pcie_port PCIe port the device is on
* @param bus Sub bus
* @param dev Device ID
* @param fn Device sub function
* @param reg Register to access
*
* @return Result of the read
*/
uint32_t cvmx_pcie_config_read32(int pcie_port, int bus, int dev, int fn, int reg)
{
uint64_t address = __cvmx_pcie_build_config_addr(pcie_port, bus, dev, fn, reg);
if (address)
return cvmx_le32_to_cpu(cvmx_read64_uint32(address));
else
return 0xffffffff;
}
/**
* Write 8bits to a Device's config space
*
* @param pcie_port PCIe port the device is on
* @param bus Sub bus
* @param dev Device ID
* @param fn Device sub function
* @param reg Register to access
* @param val Value to write
*/
void cvmx_pcie_config_write8(int pcie_port, int bus, int dev, int fn, int reg, uint8_t val)
{
uint64_t address = __cvmx_pcie_build_config_addr(pcie_port, bus, dev, fn, reg);
if (address)
cvmx_write64_uint8(address, val);
}
/**
* Write 16bits to a Device's config space
*
* @param pcie_port PCIe port the device is on
* @param bus Sub bus
* @param dev Device ID
* @param fn Device sub function
* @param reg Register to access
* @param val Value to write
*/
void cvmx_pcie_config_write16(int pcie_port, int bus, int dev, int fn, int reg, uint16_t val)
{
uint64_t address = __cvmx_pcie_build_config_addr(pcie_port, bus, dev, fn, reg);
if (address)
cvmx_write64_uint16(address, cvmx_cpu_to_le16(val));
}
/**
* Write 32bits to a Device's config space
*
* @param pcie_port PCIe port the device is on
* @param bus Sub bus
* @param dev Device ID
* @param fn Device sub function
* @param reg Register to access
* @param val Value to write
*/
void cvmx_pcie_config_write32(int pcie_port, int bus, int dev, int fn, int reg, uint32_t val)
{
uint64_t address = __cvmx_pcie_build_config_addr(pcie_port, bus, dev, fn, reg);
if (address)
cvmx_write64_uint32(address, cvmx_cpu_to_le32(val));
}
/**
* Read a PCIe config space register indirectly. This is used for
* registers of the form PCIEEP_CFG??? and PCIERC?_CFG???.
*
* @param pcie_port PCIe port to read from
* @param cfg_offset Address to read
*
* @return Value read
*/
uint32_t cvmx_pcie_cfgx_read(int pcie_port, uint32_t cfg_offset)
{
if (octeon_has_feature(OCTEON_FEATURE_NPEI))
{
cvmx_pescx_cfg_rd_t pescx_cfg_rd;
pescx_cfg_rd.u64 = 0;
pescx_cfg_rd.s.addr = cfg_offset;
cvmx_write_csr(CVMX_PESCX_CFG_RD(pcie_port), pescx_cfg_rd.u64);
pescx_cfg_rd.u64 = cvmx_read_csr(CVMX_PESCX_CFG_RD(pcie_port));
return pescx_cfg_rd.s.data;
}
else
{
cvmx_pemx_cfg_rd_t pemx_cfg_rd;
pemx_cfg_rd.u64 = 0;
pemx_cfg_rd.s.addr = cfg_offset;
cvmx_write_csr(CVMX_PEMX_CFG_RD(pcie_port), pemx_cfg_rd.u64);
pemx_cfg_rd.u64 = cvmx_read_csr(CVMX_PEMX_CFG_RD(pcie_port));
return pemx_cfg_rd.s.data;
}
}
/**
* Write a PCIe config space register indirectly. This is used for
* registers of the form PCIEEP_CFG??? and PCIERC?_CFG???.
*
* @param pcie_port PCIe port to write to
* @param cfg_offset Address to write
* @param val Value to write
*/
void cvmx_pcie_cfgx_write(int pcie_port, uint32_t cfg_offset, uint32_t val)
{
if (octeon_has_feature(OCTEON_FEATURE_NPEI))
{
cvmx_pescx_cfg_wr_t pescx_cfg_wr;
pescx_cfg_wr.u64 = 0;
pescx_cfg_wr.s.addr = cfg_offset;
pescx_cfg_wr.s.data = val;
cvmx_write_csr(CVMX_PESCX_CFG_WR(pcie_port), pescx_cfg_wr.u64);
}
else
{
cvmx_pemx_cfg_wr_t pemx_cfg_wr;
pemx_cfg_wr.u64 = 0;
pemx_cfg_wr.s.addr = cfg_offset;
pemx_cfg_wr.s.data = val;
cvmx_write_csr(CVMX_PEMX_CFG_WR(pcie_port), pemx_cfg_wr.u64);
}
}
/**
* Initialize a PCIe port for use in target(EP) mode.
*
* @param pcie_port PCIe port to initialize
*
* @return Zero on success
*/
int cvmx_pcie_ep_initialize(int pcie_port)
{
if (octeon_has_feature(OCTEON_FEATURE_NPEI))
{
cvmx_npei_ctl_status_t npei_ctl_status;
npei_ctl_status.u64 = cvmx_read_csr(CVMX_PEXP_NPEI_CTL_STATUS);
if (npei_ctl_status.s.host_mode)
return -1;
}
else
{
cvmx_mio_rst_ctlx_t mio_rst_ctl;
int ep_mode;
mio_rst_ctl.u64 = cvmx_read_csr(CVMX_MIO_RST_CTLX(pcie_port));
ep_mode = (OCTEON_IS_MODEL(OCTEON_CN61XX) ? (mio_rst_ctl.s.prtmode != 0) : mio_rst_ctl.s.host_mode);
if (ep_mode)
return -1;
}
/* CN63XX Pass 1.0 errata G-14395 requires the QLM De-emphasis be programmed */
if (OCTEON_IS_MODEL(OCTEON_CN63XX_PASS1_0))
{
if (pcie_port)
{
cvmx_ciu_qlm1_t ciu_qlm;
ciu_qlm.u64 = cvmx_read_csr(CVMX_CIU_QLM1);
ciu_qlm.s.txbypass = 1;
ciu_qlm.s.txdeemph = 5;
ciu_qlm.s.txmargin = 0x17;
cvmx_write_csr(CVMX_CIU_QLM1, ciu_qlm.u64);
}
else
{
cvmx_ciu_qlm0_t ciu_qlm;
ciu_qlm.u64 = cvmx_read_csr(CVMX_CIU_QLM0);
ciu_qlm.s.txbypass = 1;
ciu_qlm.s.txdeemph = 5;
ciu_qlm.s.txmargin = 0x17;
cvmx_write_csr(CVMX_CIU_QLM0, ciu_qlm.u64);
}
}
/* Enable bus master and memory */
cvmx_pcie_cfgx_write(pcie_port, CVMX_PCIEEPX_CFG001(pcie_port), 0x6);
/* Max Payload Size (PCIE*_CFG030[MPS]) */
/* Max Read Request Size (PCIE*_CFG030[MRRS]) */
/* Relaxed-order, no-snoop enables (PCIE*_CFG030[RO_EN,NS_EN] */
/* Error Message Enables (PCIE*_CFG030[CE_EN,NFE_EN,FE_EN,UR_EN]) */
{
cvmx_pcieepx_cfg030_t pcieepx_cfg030;
pcieepx_cfg030.u32 = cvmx_pcie_cfgx_read(pcie_port, CVMX_PCIEEPX_CFG030(pcie_port));
if (OCTEON_IS_MODEL(OCTEON_CN5XXX))
{
pcieepx_cfg030.s.mps = MPS_CN5XXX;
pcieepx_cfg030.s.mrrs = MRRS_CN5XXX;
}
else
{
pcieepx_cfg030.s.mps = MPS_CN6XXX;
pcieepx_cfg030.s.mrrs = MRRS_CN6XXX;
}
pcieepx_cfg030.s.ro_en = 1; /* Enable relaxed ordering. */
pcieepx_cfg030.s.ns_en = 1; /* Enable no snoop. */
pcieepx_cfg030.s.ce_en = 1; /* Correctable error reporting enable. */
pcieepx_cfg030.s.nfe_en = 1; /* Non-fatal error reporting enable. */
pcieepx_cfg030.s.fe_en = 1; /* Fatal error reporting enable. */
pcieepx_cfg030.s.ur_en = 1; /* Unsupported request reporting enable. */
cvmx_pcie_cfgx_write(pcie_port, CVMX_PCIEEPX_CFG030(pcie_port), pcieepx_cfg030.u32);
}
if (octeon_has_feature(OCTEON_FEATURE_NPEI))
{
/* Max Payload Size (NPEI_CTL_STATUS2[MPS]) must match PCIE*_CFG030[MPS] */
/* Max Read Request Size (NPEI_CTL_STATUS2[MRRS]) must not exceed PCIE*_CFG030[MRRS] */
cvmx_npei_ctl_status2_t npei_ctl_status2;
npei_ctl_status2.u64 = cvmx_read_csr(CVMX_PEXP_NPEI_CTL_STATUS2);
npei_ctl_status2.s.mps = MPS_CN5XXX; /* Max payload size = 128 bytes (Limit of most PCs) */
npei_ctl_status2.s.mrrs = MRRS_CN5XXX; /* Max read request size = 128 bytes for best Octeon DMA performance */
cvmx_write_csr(CVMX_PEXP_NPEI_CTL_STATUS2, npei_ctl_status2.u64);
}
else
{
/* Max Payload Size (DPI_SLI_PRTX_CFG[MPS]) must match PCIE*_CFG030[MPS] */
/* Max Read Request Size (DPI_SLI_PRTX_CFG[MRRS]) must not exceed PCIE*_CFG030[MRRS] */
cvmx_dpi_sli_prtx_cfg_t prt_cfg;
cvmx_sli_s2m_portx_ctl_t sli_s2m_portx_ctl;
prt_cfg.u64 = cvmx_read_csr(CVMX_DPI_SLI_PRTX_CFG(pcie_port));
prt_cfg.s.mps = MPS_CN6XXX;
prt_cfg.s.mrrs = MRRS_CN6XXX;
/* Max outstanding load request. */
prt_cfg.s.molr = 32;
cvmx_write_csr(CVMX_DPI_SLI_PRTX_CFG(pcie_port), prt_cfg.u64);
sli_s2m_portx_ctl.u64 = cvmx_read_csr(CVMX_PEXP_SLI_S2M_PORTX_CTL(pcie_port));
sli_s2m_portx_ctl.s.mrrs = MRRS_CN6XXX;
cvmx_write_csr(CVMX_PEXP_SLI_S2M_PORTX_CTL(pcie_port), sli_s2m_portx_ctl.u64);
}
/* Setup Mem access SubDID 12 to access Host memory */
if (octeon_has_feature(OCTEON_FEATURE_NPEI))
{
cvmx_npei_mem_access_subidx_t mem_access_subid;
mem_access_subid.u64 = 0;
mem_access_subid.s.port = pcie_port; /* Port the request is sent to. */
mem_access_subid.s.nmerge = 1; /* Merging is not allowed in this window. */
mem_access_subid.s.esr = 0; /* Endian-swap for Reads. */
mem_access_subid.s.esw = 0; /* Endian-swap for Writes. */
mem_access_subid.s.nsr = 0; /* Enable Snooping for Reads. Octeon doesn't care, but devices might want this more conservative setting */
mem_access_subid.s.nsw = 0; /* Enable Snoop for Writes. */
mem_access_subid.s.ror = 0; /* Disable Relaxed Ordering for Reads. */
mem_access_subid.s.row = 0; /* Disable Relaxed Ordering for Writes. */
mem_access_subid.s.ba = 0; /* PCIe Adddress Bits <63:34>. */
cvmx_write_csr(CVMX_PEXP_NPEI_MEM_ACCESS_SUBIDX(12), mem_access_subid.u64);
}
else
{
cvmx_sli_mem_access_subidx_t mem_access_subid;
mem_access_subid.u64 = 0;
mem_access_subid.s.port = pcie_port; /* Port the request is sent to. */
mem_access_subid.s.nmerge = 0; /* Merging is allowed in this window. */
mem_access_subid.s.esr = 0; /* Endian-swap for Reads. */
mem_access_subid.s.esw = 0; /* Endian-swap for Writes. */
mem_access_subid.s.wtype = 0; /* "No snoop" and "Relaxed ordering" are not set */
mem_access_subid.s.rtype = 0; /* "No snoop" and "Relaxed ordering" are not set */
/* PCIe Adddress Bits <63:34>. */
if (OCTEON_IS_MODEL(OCTEON_CN68XX))
mem_access_subid.cn68xx.ba = 0;
else
mem_access_subid.cn63xx.ba = 0;
cvmx_write_csr(CVMX_PEXP_SLI_MEM_ACCESS_SUBIDX(12 + pcie_port*4), mem_access_subid.u64);
}
return 0;
}
/**
* Wait for posted PCIe read/writes to reach the other side of
* the internal PCIe switch. This will insure that core
* read/writes are posted before anything after this function
* is called. This may be necessary when writing to memory that
* will later be read using the DMA/PKT engines.
*
* @param pcie_port PCIe port to wait for
*/
void cvmx_pcie_wait_for_pending(int pcie_port)
{
if (octeon_has_feature(OCTEON_FEATURE_NPEI))
{
cvmx_npei_data_out_cnt_t npei_data_out_cnt;
int a;
int b;
int c;
/* See section 9.8, PCIe Core-initiated Requests, in the manual for a
description of how this code works */
npei_data_out_cnt.u64 = cvmx_read_csr(CVMX_PEXP_NPEI_DATA_OUT_CNT);
if (pcie_port)
{
if (!npei_data_out_cnt.s.p1_fcnt)
return;
a = npei_data_out_cnt.s.p1_ucnt;
b = (a + npei_data_out_cnt.s.p1_fcnt-1) & 0xffff;
}
else
{
if (!npei_data_out_cnt.s.p0_fcnt)
return;
a = npei_data_out_cnt.s.p0_ucnt;
b = (a + npei_data_out_cnt.s.p0_fcnt-1) & 0xffff;
}
while (1)
{
npei_data_out_cnt.u64 = cvmx_read_csr(CVMX_PEXP_NPEI_DATA_OUT_CNT);
c = (pcie_port) ? npei_data_out_cnt.s.p1_ucnt : npei_data_out_cnt.s.p0_ucnt;
if (a<=b)
{
if ((cb))
return;
}
else
{
if ((c>b) && (cb))
return;
}
else
{
if ((c>b) && (cbase_addr);
assert (buffer_ptr != NULL);
return buffer_ptr;
}
/**
* @INTERNAL
* Initialize the cpu block metadata.
*
* @param cpu core no
* @param size size of per cpu memory in named block
*
*/
static
void cvmx_init_pcpu_block(int cpu, int size)
{
eccb.cfg_blk.pcpu_base_addr[cpu] = (char *)cvmx_get_memory_addr(EVENT_BUFFER_BLOCK) + (size * cpu);
assert (eccb.cfg_blk.pcpu_base_addr[cpu] != NULL);
cvmx_ringbuf_t *cpu_buf = (cvmx_ringbuf_t *) eccb.cfg_blk.pcpu_base_addr[cpu];
cpu_buf->pcpu_blk_info.size = size;
cpu_buf->pcpu_blk_info.max_samples = ((size - sizeof(cvmx_cpu_event_block_t)) / sizeof(cvmx_sample_entry_t));
cpu_buf->pcpu_blk_info.sample_count = 0;
cpu_buf->pcpu_blk_info.sample_read = 0;
cpu_buf->pcpu_blk_info.data = eccb.cfg_blk.pcpu_base_addr[cpu] + sizeof(cvmx_cpu_event_block_t) + PADBYTES;
cpu_buf->pcpu_blk_info.head = cpu_buf->pcpu_blk_info.tail = \
cpu_buf->pcpu_data = cpu_buf->pcpu_blk_info.data;
cpu_buf->pcpu_blk_info.end = eccb.cfg_blk.pcpu_base_addr[cpu] + size;
cvmx_atomic_set32(&read_percpu_block, 0);
/*
* Write per cpu mem base address info in to 'event config' named block,
* This info is needed by oct-remote-profile to get Per cpu memory
* base address of each core of the named block.
*/
pcpu_cfg_blk = (cvmx_config_block_t *) eccb.config_blk_base_addr;
pcpu_cfg_blk->pcpu_base_addr[cpu] = eccb.cfg_blk.pcpu_base_addr[cpu];
}
/**
* @INTERNAL
* Retrieve the info from the 'event_config' named block.
*
* Here events value is read(as passed to oct-remote-profile) to reset perf
* counters on every Perf counter overflow.
*
*/
static
void cvmx_read_config_blk(void)
{
eccb.config_blk_base_addr = (char *)cvmx_get_memory_addr(EVENT_BUFFER_CONFIG_BLOCK);
memcpy(&(eccb.cfg_blk.events), eccb.config_blk_base_addr + \
offsetof(cvmx_config_block_t, events), sizeof(int64_t));
cvmx_atomic_set32(&eccb.read_cfg_blk,1);
PRINTF("cfg_blk.events=%lu, sample_count=%ld\n", eccb.cfg_blk.events, eccb.cfg_blk.sample_count);
}
/**
* @INTERNAL
* Add new sample to the buffer and increment the head pointer and
* global sample count(i.e sum total of samples collected on all cores)
*
*/
static
void cvmx_add_sample_to_buffer(void)
{
uint32_t epc;
int cpu = cvmx_get_core_num();
CVMX_MF_COP0(epc, COP0_EPC);
cvmx_ringbuf_t *cpu_buf = (cvmx_ringbuf_t *) eccb.cfg_blk.pcpu_base_addr[cpu];
/*
* head/tail pointer can be NULL, and this case arises when oct-remote-profile is
* invoked afresh. To keep memory sane for current instance, we clear namedblock off
* previous data and this is accomplished by octeon_remote_write_mem from host.
*/
if (cvmx_unlikely(!cpu_buf->pcpu_blk_info.head && !cpu_buf->pcpu_blk_info.end)) {
/* Reread the event count as a different threshold val could be
* passed with profiler alongside --events flag */
cvmx_read_config_blk();
cvmx_init_pcpu_block(cpu, EVENT_PERCPU_BUFFER_SIZE);
}
/* In case of hitting end of buffer, reset head,data ptr to start */
if (cpu_buf->pcpu_blk_info.head == cpu_buf->pcpu_blk_info.end)
cpu_buf->pcpu_blk_info.head = cpu_buf->pcpu_blk_info.data = cpu_buf->pcpu_data;
/* Store the pc, respective core no.*/
cvmx_sample_entry_t *sample = (cvmx_sample_entry_t *) cpu_buf->pcpu_blk_info.data;
sample->pc = epc;
sample->core = cpu;
/* Update Per CPU stats */
cpu_buf->pcpu_blk_info.sample_count++;
cpu_buf->pcpu_blk_info.data += sizeof(cvmx_sample_entry_t);
cpu_buf->pcpu_blk_info.head = cpu_buf->pcpu_blk_info.data;
/* Increment the global sample count i.e sum total of samples on all cores*/
cvmx_atomic_add64(&(pcpu_cfg_blk->sample_count), 1);
PRINTF("the core%d:pc 0x%016lx, sample_count=%ld\n", cpu, sample->pc, cpu_buf->pcpu_blk_info.sample_count);
}
/**
* @INTERNAL
* Reset performance counters
*
* @param pf The performance counter Number (0, 1)
* @param events The threshold value for which interrupt has to be asserted
*/
static
void cvmx_reset_perf_counter(int pf, uint64_t events)
{
uint64_t pfc;
pfc = (1ull << 63) - events;
if (!pf) {
CVMX_MT_COP0(pfc, COP0_PERFVALUE0);
} else
CVMX_MT_COP0(pfc, COP0_PERFVALUE1);
}
void cvmx_collect_sample(void)
{
if (!eccb.read_cfg_blk)
cvmx_read_config_blk();
if (read_percpu_block)
cvmx_init_pcpu_block(cvmx_get_core_num(), EVENT_PERCPU_BUFFER_SIZE);
cvmx_add_sample_to_buffer();
cvmx_reset_perf_counter(0, eccb.cfg_blk.events);
}
Index: head/sys/contrib/octeon-sdk/cvmx-srio.c
===================================================================
--- head/sys/contrib/octeon-sdk/cvmx-srio.c (revision 232815)
+++ head/sys/contrib/octeon-sdk/cvmx-srio.c (revision 232816)
@@ -1,1620 +1,1622 @@
/***********************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
*
* Interface to SRIO
*
*
$Revision: 41586 $
*/
#ifdef CVMX_BUILD_FOR_LINUX_KERNEL
#include
#include
#include
#include
#ifdef CONFIG_CAVIUM_DECODE_RSL
#include
#endif
#include
#include
#include
#include
#include
#include
#include
#else
#include "cvmx.h"
#include "cvmx-srio.h"
#include "cvmx-clock.h"
#include "cvmx-helper.h"
#ifndef CVMX_BUILD_FOR_LINUX_HOST
#include "cvmx-atomic.h"
+#if !defined(CVMX_BUILD_FOR_FREEBSD_KERNEL)
#include "cvmx-error.h"
+#endif
#include "cvmx-helper-errata.h"
#endif
#include "cvmx-qlm.h"
#include "cvmx-helper.h"
#endif
#define CVMX_SRIO_CONFIG_TIMEOUT 10000 /* 10ms */
#define CVMX_SRIO_DOORBELL_TIMEOUT 10000 /* 10ms */
#define CVMX_SRIO_CONFIG_PRIORITY 0
#define ULL unsigned long long
typedef union
{
uint64_t u64;
struct
{
#ifdef __BIG_ENDIAN_BITFIELD
uint64_t upper : 2; /* Normally 2 for XKPHYS */
uint64_t reserved_49_61 : 13; /* Must be zero */
uint64_t io : 1; /* 1 for IO space access */
uint64_t did : 5; /* DID = 3 */
uint64_t subdid : 3; /* SubDID = 3-6 */
uint64_t reserved_36_39 : 4; /* Must be zero */
uint64_t se : 2; /* SubDID extender */
uint64_t reserved_32_33 : 2; /* Must be zero */
uint64_t hopcount : 8; /* Hopcount */
uint64_t address : 24; /* Mem address */
#else
uint64_t address : 24;
uint64_t hopcount : 8;
uint64_t reserved_32_33 : 2;
uint64_t se : 2;
uint64_t reserved_36_39 : 4;
uint64_t subdid : 3;
uint64_t did : 5;
uint64_t io : 1;
uint64_t reserved_49_61 : 13;
uint64_t upper : 2;
#endif
} config;
struct
{
#ifdef __BIG_ENDIAN_BITFIELD
uint64_t upper : 2; /* Normally 2 for XKPHYS */
uint64_t reserved_49_61 : 13; /* Must be zero */
uint64_t io : 1; /* 1 for IO space access */
uint64_t did : 5; /* DID = 3 */
uint64_t subdid : 3; /* SubDID = 3-6 */
uint64_t reserved_36_39 : 4; /* Must be zero */
uint64_t se : 2; /* SubDID extender */
uint64_t address : 34; /* Mem address */
#else
uint64_t address : 34;
uint64_t se : 2;
uint64_t reserved_36_39 : 4;
uint64_t subdid : 3;
uint64_t did : 5;
uint64_t io : 1;
uint64_t reserved_49_61 : 13;
uint64_t upper : 2;
#endif
} mem;
} cvmx_sli_address_t;
typedef struct
{
cvmx_srio_initialize_flags_t flags;
int32_t subidx_ref_count[16]; /* Reference count for SLI_MEM_ACCESS_SUBID[12-27]. Index=X-12 */
int32_t s2m_ref_count[16]; /* Reference count for SRIOX_S2M_TYPE[0-15]. */
} __cvmx_srio_state_t;
static CVMX_SHARED __cvmx_srio_state_t __cvmx_srio_state[4];
#ifndef CVMX_BUILD_FOR_LINUX_HOST
/**
* @INTERNAL
* Allocate a SRIOX_S2M_TYPEX register for mapping a remote SRIO
* device's address range into Octeons SLI address space. Reference
* counting is used to allow sharing of duplicate setups. The current
* implementation treats reads and writes as paired, but this could be
* changed if we have trouble running out of indexes.
*
* @param srio_port SRIO port device is on
* @param s2m SRIOX_S2M_TYPEX setup required
*
* @return Index of CSR, or negative on failure
*/
static int __cvmx_srio_alloc_s2m(int srio_port, cvmx_sriox_s2m_typex_t s2m)
{
int s2m_index;
/* Search through the S2M_TYPE registers looking for an unsed one or one
setup the way we need it */
for (s2m_index=0; s2m_index<16; s2m_index++)
{
/* Increment ref count by 2 since we count read and write
independently. We might need a more complicated search in the
future */
int ref_count = cvmx_atomic_fetch_and_add32(&__cvmx_srio_state[srio_port].s2m_ref_count[s2m_index], 2);
if (ref_count == 0)
{
/* Unused location. Write our value */
cvmx_write_csr(CVMX_SRIOX_S2M_TYPEX(s2m_index, srio_port), s2m.u64);
/* Read back to make sure the update is complete */
cvmx_read_csr(CVMX_SRIOX_S2M_TYPEX(s2m_index, srio_port));
return s2m_index;
}
else
{
/* In use, see if we can use it */
if (cvmx_read_csr(CVMX_SRIOX_S2M_TYPEX(s2m_index, srio_port)) == s2m.u64)
return s2m_index;
else
cvmx_atomic_add32(&__cvmx_srio_state[srio_port].s2m_ref_count[s2m_index], -2);
}
}
cvmx_dprintf("SRIO%d: Unable to find free SRIOX_S2M_TYPEX\n", srio_port);
return -1;
}
/**
* @INTERNAL
* Free a handle allocated by __cvmx_srio_alloc_s2m
*
* @param srio_port SRIO port
* @param index Index to free
*/
static void __cvmx_srio_free_s2m(int srio_port, int index)
{
/* Read to force pending transactions to complete */
cvmx_read_csr(CVMX_SRIOX_S2M_TYPEX(index, srio_port));
cvmx_atomic_add32(&__cvmx_srio_state[srio_port].s2m_ref_count[index], -2);
}
/**
* @INTERNAL
* Allocate a SLI SubID to map a region of memory. Reference
* counting is used to allow sharing of duplicate setups.
*
* @param subid SLI_MEM_ACCESS_SUBIDX we need an index for
*
* @return Index of CSR, or negative on failure
*/
static int __cvmx_srio_alloc_subid(cvmx_sli_mem_access_subidx_t subid)
{
int mem_index;
/* Search through the mem access subid registers looking for an unsed one
or one setup the way we need it. PCIe uses the low indexes, so search
backwards */
for (mem_index=27; mem_index>=12; mem_index--)
{
int ref_count = cvmx_atomic_fetch_and_add32(&__cvmx_srio_state[0].subidx_ref_count[mem_index-12], 1);
if (ref_count == 0)
{
/* Unused location. Write our value */
cvmx_write_csr(CVMX_PEXP_SLI_MEM_ACCESS_SUBIDX(mem_index), subid.u64);
/* Read back the value to make sure the update is complete */
cvmx_read_csr(CVMX_PEXP_SLI_MEM_ACCESS_SUBIDX(mem_index));
return mem_index;
}
else
{
/* In use, see if we can use it */
if (cvmx_read_csr(CVMX_PEXP_SLI_MEM_ACCESS_SUBIDX(mem_index)) == subid.u64)
return mem_index;
else
cvmx_atomic_add32(&__cvmx_srio_state[0].subidx_ref_count[mem_index-12], -1);
}
}
cvmx_dprintf("SRIO: Unable to find free SLI_MEM_ACCESS_SUBIDX\n");
return -1;
}
/**
* @INTERNAL
* Free a handle allocated by __cvmx_srio_alloc_subid
*
* @param index Index to free
*/
static void __cvmx_srio_free_subid(int index)
{
/* Read to force pending transactions to complete */
cvmx_read_csr(CVMX_PEXP_SLI_MEM_ACCESS_SUBIDX(index));
cvmx_atomic_add32(&__cvmx_srio_state[0].subidx_ref_count[index-12], -1);
}
#endif
/**
* @INTERNAL
* Read 32bits from a local port
*
* @param srio_port SRIO port the device is on
* @param offset Offset in config space. This must be a multiple of 32 bits.
* @param result Result of the read. This will be unmodified on failure.
*
* @return Zero on success, negative on failure.
*/
static int __cvmx_srio_local_read32(int srio_port, uint32_t offset, uint32_t *result)
{
cvmx_sriox_maint_op_t maint_op;
cvmx_sriox_maint_rd_data_t maint_rd_data;
maint_op.u64 = 0;
maint_op.s.op = 0; /* Read */
maint_op.s.addr = offset;
/* Make sure SRIO isn't already busy */
if (CVMX_WAIT_FOR_FIELD64(CVMX_SRIOX_MAINT_OP(srio_port), cvmx_sriox_maint_op_t, pending, ==, 0, CVMX_SRIO_CONFIG_TIMEOUT))
{
cvmx_dprintf("SRIO%d: Pending bit stuck before config read\n", srio_port);
return -1;
}
/* Issue the read to the hardware */
cvmx_write_csr(CVMX_SRIOX_MAINT_OP(srio_port), maint_op.u64);
/* Wait for the hardware to complete the operation */
if (CVMX_WAIT_FOR_FIELD64(CVMX_SRIOX_MAINT_OP(srio_port), cvmx_sriox_maint_op_t, pending, ==, 0, CVMX_SRIO_CONFIG_TIMEOUT))
{
cvmx_dprintf("SRIO%d: Config read timeout\n", srio_port);
return -1;
}
/* Display and error and return if the operation failed to issue */
maint_op.u64 = cvmx_read_csr(CVMX_SRIOX_MAINT_OP(srio_port));
if (maint_op.s.fail)
{
cvmx_dprintf("SRIO%d: Config read addressing error (offset=0x%x)\n", srio_port, (unsigned int)offset);
return -1;
}
/* Wait for the read data to become valid */
if (CVMX_WAIT_FOR_FIELD64(CVMX_SRIOX_MAINT_RD_DATA(srio_port), cvmx_sriox_maint_rd_data_t, valid, ==, 1, CVMX_SRIO_CONFIG_TIMEOUT))
{
cvmx_dprintf("SRIO%d: Config read data timeout\n", srio_port);
return -1;
}
/* Get the read data */
maint_rd_data.u64 = cvmx_read_csr(CVMX_SRIOX_MAINT_RD_DATA(srio_port));
*result = maint_rd_data.s.rd_data;
return 0;
}
/**
* @INTERNAL
* Write 32bits to a local port
* @param srio_port SRIO port the device is on
* @param offset Offset in config space. This must be a multiple of 32 bits.
* @param data Data to write.
*
* @return Zero on success, negative on failure.
*/
static int __cvmx_srio_local_write32(int srio_port, uint32_t offset, uint32_t data)
{
cvmx_sriox_maint_op_t maint_op;
maint_op.u64 = 0;
maint_op.s.wr_data = data;
maint_op.s.op = 1; /* Write */
maint_op.s.addr = offset;
/* Make sure SRIO isn't already busy */
if (CVMX_WAIT_FOR_FIELD64(CVMX_SRIOX_MAINT_OP(srio_port), cvmx_sriox_maint_op_t, pending, ==, 0, CVMX_SRIO_CONFIG_TIMEOUT))
{
cvmx_dprintf("SRIO%d: Pending bit stuck before config write\n", srio_port);
return -1;
}
/* Issue the write to the hardware */
cvmx_write_csr(CVMX_SRIOX_MAINT_OP(srio_port), maint_op.u64);
/* Wait for the hardware to complete the operation */
if (CVMX_WAIT_FOR_FIELD64(CVMX_SRIOX_MAINT_OP(srio_port), cvmx_sriox_maint_op_t, pending, ==, 0, CVMX_SRIO_CONFIG_TIMEOUT))
{
cvmx_dprintf("SRIO%d: Config write timeout\n", srio_port);
return -1;
}
/* Display and error and return if the operation failed to issue */
maint_op.u64 = cvmx_read_csr(CVMX_SRIOX_MAINT_OP(srio_port));
if (maint_op.s.fail)
{
cvmx_dprintf("SRIO%d: Config write addressing error (offset=0x%x)\n", srio_port, (unsigned int)offset);
return -1;
}
return 0;
}
/**
* Reset SRIO to link partner
*
* @param srio_port SRIO port to initialize
*
* @return Zero on success
*/
int cvmx_srio_link_rst(int srio_port)
{
cvmx_sriomaintx_port_0_link_resp_t link_resp;
if (OCTEON_IS_MODEL(OCTEON_CN63XX_PASS1_X))
return -1;
/* Generate a symbol reset to the link partner by writing 0x3. */
if (cvmx_srio_config_write32(srio_port, 0, -1, 0, 0,
CVMX_SRIOMAINTX_PORT_0_LINK_REQ(srio_port), 3))
return -1;
if (cvmx_srio_config_read32(srio_port, 0, -1, 0, 0,
CVMX_SRIOMAINTX_PORT_0_LINK_RESP(srio_port), &link_resp.u32))
return -1;
/* Poll until link partner has received the reset. */
while (link_resp.s.valid == 0)
{
//cvmx_dprintf("Waiting for Link Response\n");
if (cvmx_srio_config_read32(srio_port, 0, -1, 0, 0,
CVMX_SRIOMAINTX_PORT_0_LINK_RESP(srio_port), &link_resp.u32))
return -1;
}
/* Valid response, Asserting MAC reset */
cvmx_write_csr(CVMX_CIU_SOFT_PRST, 0x1);
cvmx_wait(10);
/* De-asserting MAC Reset */
cvmx_write_csr(CVMX_CIU_SOFT_PRST, 0x0);
return 0;
}
/**
* Initialize a SRIO port for use.
*
* @param srio_port SRIO port to initialize
* @param flags Optional flags
*
* @return Zero on success
*/
int cvmx_srio_initialize(int srio_port, cvmx_srio_initialize_flags_t flags)
{
cvmx_sriomaintx_port_lt_ctl_t port_lt_ctl;
cvmx_sriomaintx_port_rt_ctl_t port_rt_ctl;
cvmx_sriomaintx_port_0_ctl_t port_0_ctl;
cvmx_sriomaintx_core_enables_t core_enables;
cvmx_sriomaintx_port_gen_ctl_t port_gen_ctl;
cvmx_sriox_status_reg_t sriox_status_reg;
cvmx_mio_rst_ctlx_t mio_rst_ctl;
cvmx_sriox_imsg_vport_thr_t sriox_imsg_vport_thr;
cvmx_dpi_sli_prtx_cfg_t prt_cfg;
cvmx_sli_s2m_portx_ctl_t sli_s2m_portx_ctl;
cvmx_sli_mem_access_ctl_t sli_mem_access_ctl;
cvmx_sriomaintx_port_0_ctl2_t port_0_ctl2;
sriox_status_reg.u64 = cvmx_read_csr(CVMX_SRIOX_STATUS_REG(srio_port));
if (OCTEON_IS_MODEL(OCTEON_CN66XX))
{
/* All SRIO ports are connected to QLM0 */
int status = cvmx_qlm_get_status(0);
if (status < 4 || status > 6)
{
cvmx_dprintf("SRIO%d: Initialization called on a port not in SRIO mode\n", srio_port);
return -1;
}
}
else if (!sriox_status_reg.s.srio)
{
cvmx_dprintf("SRIO%d: Initialization called on a port not in SRIO mode\n", srio_port);
return -1;
}
__cvmx_srio_state[srio_port].flags = flags;
/* CN63XX Pass 1.0 errata G-14395 requires the QLM De-emphasis be
programmed */
if (OCTEON_IS_MODEL(OCTEON_CN63XX_PASS1_0))
{
if (srio_port)
{
cvmx_ciu_qlm1_t ciu_qlm;
ciu_qlm.u64 = cvmx_read_csr(CVMX_CIU_QLM1);
ciu_qlm.s.txbypass = 1;
ciu_qlm.s.txdeemph = 5;
ciu_qlm.s.txmargin = 0x17;
cvmx_write_csr(CVMX_CIU_QLM1, ciu_qlm.u64);
}
else
{
cvmx_ciu_qlm0_t ciu_qlm;
ciu_qlm.u64 = cvmx_read_csr(CVMX_CIU_QLM0);
ciu_qlm.s.txbypass = 1;
ciu_qlm.s.txdeemph = 5;
ciu_qlm.s.txmargin = 0x17;
cvmx_write_csr(CVMX_CIU_QLM0, ciu_qlm.u64);
}
}
/* Don't receive or drive reset signals for the SRIO QLM */
if (OCTEON_IS_MODEL(OCTEON_CN66XX))
{
/* The reset signals are available only for srio_port == 0. */
if (srio_port == 0 || (OCTEON_IS_MODEL(OCTEON_CN66XX_PASS1_2) && srio_port == 1))
{
cvmx_mio_rst_cntlx_t mio_rst_cntl;
mio_rst_cntl.u64 = cvmx_read_csr(CVMX_MIO_RST_CNTLX(srio_port));
mio_rst_cntl.s.rst_drv = 0;
mio_rst_cntl.s.rst_rcv = 0;
mio_rst_cntl.s.rst_chip = 0;
cvmx_write_csr(CVMX_MIO_RST_CNTLX(srio_port), mio_rst_cntl.u64);
}
/* MIO_RST_CNTL2 is initialized to 0 on cold reset */
mio_rst_ctl.u64 = cvmx_read_csr(CVMX_MIO_RST_CNTLX(srio_port));
}
else
{
mio_rst_ctl.u64 = cvmx_read_csr(CVMX_MIO_RST_CTLX(srio_port));
mio_rst_ctl.s.rst_drv = 0;
mio_rst_ctl.s.rst_rcv = 0;
mio_rst_ctl.s.rst_chip = 0;
cvmx_write_csr(CVMX_MIO_RST_CTLX(srio_port), mio_rst_ctl.u64);
mio_rst_ctl.u64 = cvmx_read_csr(CVMX_MIO_RST_CTLX(srio_port));
}
cvmx_dprintf("SRIO%d: Port in %s mode\n", srio_port,
(mio_rst_ctl.s.prtmode) ? "host" : "endpoint");
/* Bring the port out of reset if necessary */
switch (srio_port)
{
case 0:
{
cvmx_ciu_soft_prst_t prst;
prst.u64 = cvmx_read_csr(CVMX_CIU_SOFT_PRST);
if (prst.s.soft_prst)
{
prst.s.soft_prst = 0;
cvmx_write_csr(CVMX_CIU_SOFT_PRST, prst.u64);
/* Wait up to 250ms for the port to come out of reset */
if (CVMX_WAIT_FOR_FIELD64(CVMX_SRIOX_STATUS_REG(srio_port), cvmx_sriox_status_reg_t, access, ==, 1, 250000))
return -1;
}
break;
}
case 1:
{
cvmx_ciu_soft_prst1_t prst;
prst.u64 = cvmx_read_csr(CVMX_CIU_SOFT_PRST1);
if (prst.s.soft_prst)
{
prst.s.soft_prst = 0;
cvmx_write_csr(CVMX_CIU_SOFT_PRST1, prst.u64);
/* Wait up to 250ms for the port to come out of reset */
if (CVMX_WAIT_FOR_FIELD64(CVMX_SRIOX_STATUS_REG(srio_port), cvmx_sriox_status_reg_t, access, ==, 1, 250000))
return -1;
}
break;
}
case 2:
{
cvmx_ciu_soft_prst2_t prst;
prst.u64 = cvmx_read_csr(CVMX_CIU_SOFT_PRST2);
if (prst.s.soft_prst)
{
prst.s.soft_prst = 0;
cvmx_write_csr(CVMX_CIU_SOFT_PRST2, prst.u64);
/* Wait up to 250ms for the port to come out of reset */
if (CVMX_WAIT_FOR_FIELD64(CVMX_SRIOX_STATUS_REG(srio_port), cvmx_sriox_status_reg_t, access, ==, 1, 250000))
return -1;
}
break;
}
}
/* Disable the link while we make changes */
if (__cvmx_srio_local_read32(srio_port, CVMX_SRIOMAINTX_PORT_0_CTL(srio_port), &port_0_ctl.u32))
return -1;
port_0_ctl.s.o_enable = 0;
port_0_ctl.s.i_enable = 0;
port_0_ctl.s.prt_lock = 1;
port_0_ctl.s.disable = 0;
if (__cvmx_srio_local_write32(srio_port, CVMX_SRIOMAINTX_PORT_0_CTL(srio_port), port_0_ctl.u32))
return -1;
/* CN63XX Pass 2.0 and 2.1 errata G-15273 requires the QLM De-emphasis be
programmed when using a 156.25Mhz ref clock */
if (OCTEON_IS_MODEL(OCTEON_CN63XX_PASS2_0) ||
OCTEON_IS_MODEL(OCTEON_CN63XX_PASS2_1))
{
cvmx_mio_rst_boot_t mio_rst_boot;
cvmx_sriomaintx_lane_x_status_0_t lane_x_status;
/* Read the QLM config and speed pins */
mio_rst_boot.u64 = cvmx_read_csr(CVMX_MIO_RST_BOOT);
if (__cvmx_srio_local_read32(srio_port, CVMX_SRIOMAINTX_LANE_X_STATUS_0(0, srio_port), &lane_x_status.u32))
return -1;
if (srio_port)
{
cvmx_ciu_qlm1_t ciu_qlm;
ciu_qlm.u64 = cvmx_read_csr(CVMX_CIU_QLM1);
switch (mio_rst_boot.cn63xx.qlm1_spd)
{
case 0x4: /* 1.25 Gbaud, 156.25MHz */
ciu_qlm.s.txbypass = 1;
ciu_qlm.s.txdeemph = 0x0;
ciu_qlm.s.txmargin = (lane_x_status.s.rx_type == 0) ? 0x11 : 0x1c; /* short or med/long */
break;
case 0xb: /* 5.0 Gbaud, 156.25MHz */
ciu_qlm.s.txbypass = 1;
ciu_qlm.s.txdeemph = (lane_x_status.s.rx_type == 0) ? 0xa : 0xf; /* short or med/long */
ciu_qlm.s.txmargin = (lane_x_status.s.rx_type == 0) ? 0xf : 0x1a; /* short or med/long */
break;
}
cvmx_write_csr(CVMX_CIU_QLM1, ciu_qlm.u64);
}
else
{
cvmx_ciu_qlm0_t ciu_qlm;
ciu_qlm.u64 = cvmx_read_csr(CVMX_CIU_QLM0);
switch (mio_rst_boot.cn63xx.qlm0_spd)
{
case 0x4: /* 1.25 Gbaud, 156.25MHz */
ciu_qlm.s.txbypass = 1;
ciu_qlm.s.txdeemph = 0x0;
ciu_qlm.s.txmargin = (lane_x_status.s.rx_type == 0) ? 0x11 : 0x1c; /* short or med/long */
break;
case 0xb: /* 5.0 Gbaud, 156.25MHz */
ciu_qlm.s.txbypass = 1;
ciu_qlm.s.txdeemph = (lane_x_status.s.rx_type == 0) ? 0xa : 0xf; /* short or med/long */
ciu_qlm.s.txmargin = (lane_x_status.s.rx_type == 0) ? 0xf : 0x1a; /* short or med/long */
break;
}
cvmx_write_csr(CVMX_CIU_QLM0, ciu_qlm.u64);
}
}
/* Errata SRIO-14485: Link speed is reported incorrectly in CN63XX
pass 1.x */
if (OCTEON_IS_MODEL(OCTEON_CN63XX_PASS1_X))
{
cvmx_sriomaintx_port_0_ctl2_t port_0_ctl2;
if (__cvmx_srio_local_read32(srio_port, CVMX_SRIOMAINTX_PORT_0_CTL2(srio_port), &port_0_ctl2.u32))
return -1;
if (port_0_ctl2.s.enb_500g)
{
port_0_ctl2.u32 = 0;
port_0_ctl2.s.enb_625g = 1;
}
else if (port_0_ctl2.s.enb_312g)
{
port_0_ctl2.u32 = 0;
port_0_ctl2.s.enb_500g = 1;
}
else if (port_0_ctl2.s.enb_250g)
{
port_0_ctl2.u32 = 0;
port_0_ctl2.s.enb_312g = 1;
}
else if (port_0_ctl2.s.enb_125g)
{
port_0_ctl2.u32 = 0;
port_0_ctl2.s.enb_250g = 1;
}
else
{
port_0_ctl2.u32 = 0;
port_0_ctl2.s.enb_125g = 1;
}
if (__cvmx_srio_local_write32(srio_port, CVMX_SRIOMAINTX_PORT_0_CTL2(srio_port), port_0_ctl2.u32))
return -1;
}
/* Errata SRIO-15351: Turn off SRIOMAINTX_MAC_CTRL[TYPE_MRG] as it may
cause packet ACCEPT to be lost */
if (OCTEON_IS_MODEL(OCTEON_CN63XX_PASS2_0) || OCTEON_IS_MODEL(OCTEON_CN63XX_PASS2_1))
{
cvmx_sriomaintx_mac_ctrl_t mac_ctrl;
if (__cvmx_srio_local_read32(srio_port, CVMX_SRIOMAINTX_MAC_CTRL(srio_port), &mac_ctrl.u32))
return -1;
mac_ctrl.s.type_mrg = 0;
if (__cvmx_srio_local_write32(srio_port, CVMX_SRIOMAINTX_MAC_CTRL(srio_port), mac_ctrl.u32))
return -1;
}
/* Set the link layer timeout to 1ms. The default is too high and causes
core bus errors */
if (__cvmx_srio_local_read32(srio_port, CVMX_SRIOMAINTX_PORT_LT_CTL(srio_port), &port_lt_ctl.u32))
return -1;
port_lt_ctl.s.timeout = 1000000 / 200; /* 1ms = 1000000ns / 200ns */
if (__cvmx_srio_local_write32(srio_port, CVMX_SRIOMAINTX_PORT_LT_CTL(srio_port), port_lt_ctl.u32))
return -1;
/* Set the logical layer timeout to 100ms. The default is too high and causes
core bus errors */
if (__cvmx_srio_local_read32(srio_port, CVMX_SRIOMAINTX_PORT_RT_CTL(srio_port), &port_rt_ctl.u32))
return -1;
port_rt_ctl.s.timeout = 100000000 / 200; /* 100ms = 100000000ns / 200ns */
if (__cvmx_srio_local_write32(srio_port, CVMX_SRIOMAINTX_PORT_RT_CTL(srio_port), port_rt_ctl.u32))
return -1;
/* Allow memory and doorbells. Messaging is enabled later */
if (__cvmx_srio_local_read32(srio_port, CVMX_SRIOMAINTX_CORE_ENABLES(srio_port), &core_enables.u32))
return -1;
core_enables.s.doorbell = 1;
core_enables.s.memory = 1;
if (__cvmx_srio_local_write32(srio_port, CVMX_SRIOMAINTX_CORE_ENABLES(srio_port), core_enables.u32))
return -1;
/* Allow us to master transactions */
if (__cvmx_srio_local_read32(srio_port, CVMX_SRIOMAINTX_PORT_GEN_CTL(srio_port), &port_gen_ctl.u32))
return -1;
port_gen_ctl.s.menable = 1;
if (__cvmx_srio_local_write32(srio_port, CVMX_SRIOMAINTX_PORT_GEN_CTL(srio_port), port_gen_ctl.u32))
return -1;
/* Set the MRRS and MPS for optimal SRIO performance */
prt_cfg.u64 = cvmx_read_csr(CVMX_DPI_SLI_PRTX_CFG(srio_port));
prt_cfg.s.mps = 1;
prt_cfg.s.mrrs = 1;
prt_cfg.s.molr = 32;
if (OCTEON_IS_MODEL(OCTEON_CN66XX))
prt_cfg.s.molr = ((prt_cfg.s.qlm_cfg == 1 || prt_cfg.s.qlm_cfg == 3) ? 8
: (prt_cfg.s.qlm_cfg == 4 || prt_cfg.s.qlm_cfg == 6) ? 16
: 32);
cvmx_write_csr(CVMX_DPI_SLI_PRTX_CFG(srio_port), prt_cfg.u64);
sli_s2m_portx_ctl.u64 = cvmx_read_csr(CVMX_PEXP_SLI_S2M_PORTX_CTL(srio_port));
sli_s2m_portx_ctl.s.mrrs = 1;
cvmx_write_csr(CVMX_PEXP_SLI_S2M_PORTX_CTL(srio_port), sli_s2m_portx_ctl.u64);
/* Setup RX messaging thresholds */
sriox_imsg_vport_thr.u64 = cvmx_read_csr(CVMX_SRIOX_IMSG_VPORT_THR(srio_port));
if (OCTEON_IS_MODEL(OCTEON_CN66XX))
sriox_imsg_vport_thr.s.max_tot = ((prt_cfg.s.qlm_cfg == 1 || prt_cfg.s.qlm_cfg == 3) ? 44 : 46);
else
sriox_imsg_vport_thr.s.max_tot = 48;
sriox_imsg_vport_thr.s.max_s1 = 24;
sriox_imsg_vport_thr.s.max_s0 = 24;
sriox_imsg_vport_thr.s.sp_vport = 1;
sriox_imsg_vport_thr.s.buf_thr = 4;
sriox_imsg_vport_thr.s.max_p1 = 12;
sriox_imsg_vport_thr.s.max_p0 = 12;
cvmx_write_csr(CVMX_SRIOX_IMSG_VPORT_THR(srio_port), sriox_imsg_vport_thr.u64);
/* Setup RX messaging thresholds for other virtual ports. */
if (OCTEON_IS_MODEL(OCTEON_CN66XX))
{
cvmx_sriox_imsg_vport_thr2_t sriox_imsg_vport_thr2;
sriox_imsg_vport_thr2.u64 = cvmx_read_csr(CVMX_SRIOX_IMSG_VPORT_THR2(srio_port));
sriox_imsg_vport_thr2.s.max_s2 = 24;
sriox_imsg_vport_thr2.s.max_s3 = 24;
cvmx_write_csr(CVMX_SRIOX_IMSG_VPORT_THR2(srio_port), sriox_imsg_vport_thr2.u64);
}
/* Errata SRIO-X: SRIO error behavior may not be optimal in CN63XX pass 1.x */
if (OCTEON_IS_MODEL(OCTEON_CN63XX_PASS1_X))
{
cvmx_sriox_tx_ctrl_t sriox_tx_ctrl;
sriox_tx_ctrl.u64 = cvmx_read_csr(CVMX_SRIOX_TX_CTRL(srio_port));
sriox_tx_ctrl.s.tag_th2 = 2;
sriox_tx_ctrl.s.tag_th1 = 3;
sriox_tx_ctrl.s.tag_th0 = 4;
cvmx_write_csr(CVMX_SRIOX_TX_CTRL(srio_port), sriox_tx_ctrl.u64);
}
/* Errata SLI-15954: SLI relaxed order issues */
if (OCTEON_IS_MODEL(OCTEON_CN66XX_PASS1_X))
{
cvmx_sli_ctl_portx_t sli_ctl_portx;
sli_ctl_portx.u64 = cvmx_read_csr(CVMX_PEXP_SLI_CTL_PORTX(srio_port));
sli_ctl_portx.s.ptlp_ro = 1; /* Set to same value for all MACs. */
sli_ctl_portx.s.ctlp_ro = 1; /* Set to same value for all MACs. */
sli_ctl_portx.s.wait_com = 0; /* So that no inbound stores wait for a commit */
sli_ctl_portx.s.waitl_com = 0;
cvmx_write_csr(CVMX_PEXP_SLI_CTL_PORTX(srio_port), sli_ctl_portx.u64);
}
if (!OCTEON_IS_MODEL(OCTEON_CN63XX_PASS1_X))
{
/* Clear the ACK state */
if (__cvmx_srio_local_write32(srio_port, CVMX_SRIOMAINTX_PORT_0_LOCAL_ACKID(srio_port), 0))
return -1;
}
/* Bring the link down, then up, by writing to the SRIO port's
PORT_0_CTL2 CSR. */
if (__cvmx_srio_local_read32(srio_port, CVMX_SRIOMAINTX_PORT_0_CTL2(srio_port), &port_0_ctl2.u32))
return -1;
if (__cvmx_srio_local_write32(srio_port, CVMX_SRIOMAINTX_PORT_0_CTL2(srio_port), port_0_ctl2.u32))
return -1;
/* Clear any pending interrupts */
cvmx_write_csr(CVMX_SRIOX_INT_REG(srio_port), cvmx_read_csr(CVMX_SRIOX_INT_REG(srio_port)));
/* Enable error reporting */
-#if (!defined(CVMX_BUILD_FOR_LINUX_HOST) && !defined(CVMX_BUILD_FOR_LINUX_KERNEL)) || defined(CONFIG_CAVIUM_DECODE_RSL)
+#if (!defined(CVMX_BUILD_FOR_LINUX_HOST) && !defined(CVMX_BUILD_FOR_LINUX_KERNEL) && !defined(CVMX_BUILD_FOR_FREEBSD_KERNEL)) || defined(CONFIG_CAVIUM_DECODE_RSL)
cvmx_error_enable_group(CVMX_ERROR_GROUP_SRIO, srio_port);
#endif
/* Finally enable the link */
if (__cvmx_srio_local_read32(srio_port, CVMX_SRIOMAINTX_PORT_0_CTL(srio_port), &port_0_ctl.u32))
return -1;
port_0_ctl.s.o_enable = 1;
port_0_ctl.s.i_enable = 1;
port_0_ctl.s.disable = 0;
port_0_ctl.s.prt_lock = 0;
if (__cvmx_srio_local_write32(srio_port, CVMX_SRIOMAINTX_PORT_0_CTL(srio_port), port_0_ctl.u32))
return -1;
/* Store merge control (SLI_MEM_ACCESS_CTL[TIMER,MAX_WORD]) */
sli_mem_access_ctl.u64 = cvmx_read_csr(CVMX_PEXP_SLI_MEM_ACCESS_CTL);
sli_mem_access_ctl.s.max_word = 0; /* Allow 16 words to combine */
sli_mem_access_ctl.s.timer = 127; /* Wait up to 127 cycles for more data */
cvmx_write_csr(CVMX_PEXP_SLI_MEM_ACCESS_CTL, sli_mem_access_ctl.u64);
/* FIXME: Disable sending a link request when the SRIO link is
brought up. For unknown reasons this code causes issues with some SRIO
devices. As we currently don't support hotplug in software, this code
should never be needed. Without link down/up events, the ACKs should
start off and stay synchronized */
#if 0
/* Ask for a link and align our ACK state. CN63XXp1 didn't support this */
if (!OCTEON_IS_MODEL(OCTEON_CN63XX_PASS1_X))
{
uint64_t stop_cycle;
cvmx_sriomaintx_port_0_err_stat_t sriomaintx_port_0_err_stat;
/* Clear the SLI_CTL_PORTX[DIS_PORT[ bit to re-enable traffic-flow
to the SRIO MACs. */
cvmx_write_csr(CVMX_PEXP_SLI_CTL_PORTX(srio_port), cvmx_read_csr(CVMX_PEXP_SLI_CTL_PORTX(srio_port)));
/* Wait a little to see if the link comes up */
stop_cycle = cvmx_clock_get_rate(CVMX_CLOCK_CORE)/4 + cvmx_clock_get_count(CVMX_CLOCK_CORE);
do
{
/* Read the port link status */
if (__cvmx_srio_local_read32(srio_port, CVMX_SRIOMAINTX_PORT_0_ERR_STAT(srio_port), &sriomaintx_port_0_err_stat.u32))
return -1;
} while (!sriomaintx_port_0_err_stat.s.pt_ok && (cvmx_clock_get_count(CVMX_CLOCK_CORE) < stop_cycle));
/* Send link request if link is up */
if (sriomaintx_port_0_err_stat.s.pt_ok)
{
cvmx_sriomaintx_port_0_link_req_t link_req;
cvmx_sriomaintx_port_0_link_resp_t link_resp;
link_req.u32 = 0;
link_req.s.cmd = 4;
/* Send the request */
if (__cvmx_srio_local_write32(srio_port, CVMX_SRIOMAINTX_PORT_0_LINK_REQ(srio_port), link_req.u32))
return -1;
/* Wait for the response */
stop_cycle = cvmx_clock_get_rate(CVMX_CLOCK_CORE)/8 + cvmx_clock_get_count(CVMX_CLOCK_CORE);
do
{
if (__cvmx_srio_local_read32(srio_port, CVMX_SRIOMAINTX_PORT_0_LINK_RESP(srio_port), &link_resp.u32))
return -1;
} while (!link_resp.s.valid && (cvmx_clock_get_count(CVMX_CLOCK_CORE) < stop_cycle));
/* Set our ACK state if we got a response */
if (link_resp.s.valid)
{
cvmx_sriomaintx_port_0_local_ackid_t local_ackid;
local_ackid.u32 = 0;
local_ackid.s.i_ackid = 0;
local_ackid.s.e_ackid = link_resp.s.ackid;
local_ackid.s.o_ackid = link_resp.s.ackid;
if (__cvmx_srio_local_write32(srio_port, CVMX_SRIOMAINTX_PORT_0_LOCAL_ACKID(srio_port), local_ackid.u32))
return -1;
}
else
return -1;
}
}
#endif
return 0;
}
/**
* Read 32bits from a Device's config space
*
* @param srio_port SRIO port the device is on
* @param srcid_index
* Which SRIO source ID to use. 0 = Primary, 1 = Secondary
* @param destid RapidIO device ID, or -1 for the local Octeon.
* @param is16bit Non zero if the transactions should use 16bit device IDs. Zero
* if transactions should use 8bit device IDs.
* @param hopcount Number of hops to the remote device. Use 0 for the local Octeon.
* @param offset Offset in config space. This must be a multiple of 32 bits.
* @param result Result of the read. This will be unmodified on failure.
*
* @return Zero on success, negative on failure.
*/
int cvmx_srio_config_read32(int srio_port, int srcid_index, int destid,
int is16bit, uint8_t hopcount, uint32_t offset,
uint32_t *result)
{
if (destid == -1)
{
int status = __cvmx_srio_local_read32(srio_port, offset, result);
if ((status == 0) && (__cvmx_srio_state[srio_port].flags & CVMX_SRIO_INITIALIZE_DEBUG))
cvmx_dprintf("SRIO%d: Local read [0x%06x] <= 0x%08x\n", srio_port, (unsigned int)offset, (unsigned int)*result);
return status;
}
else
{
if (OCTEON_IS_MODEL(OCTEON_CN63XX_PASS1_X))
{
int return_code;
uint32_t pkt = 0;
uint32_t sourceid;
uint64_t stop_cycle;
char rx_buffer[64];
/* Tell the user */
if (__cvmx_srio_state[srio_port].flags & CVMX_SRIO_INITIALIZE_DEBUG)
cvmx_dprintf("SRIO%d: Remote read [id=0x%04x hop=%3d offset=0x%06x] <= ", srio_port, destid, hopcount, (unsigned int)offset);
/* Read the proper source ID */
if (srcid_index)
__cvmx_srio_local_read32(srio_port, CVMX_SRIOMAINTX_SEC_DEV_ID(srio_port), &sourceid);
else
__cvmx_srio_local_read32(srio_port, CVMX_SRIOMAINTX_PRI_DEV_ID(srio_port), &sourceid);
if (is16bit)
{
/* Use the 16bit source ID */
sourceid &= 0xffff;
/* MAINT Reads are 11 bytes */
__cvmx_srio_local_write32(srio_port, CVMX_SRIOMAINTX_IR_SP_TX_CTRL(srio_port), 11<<16);
pkt |= CVMX_SRIO_CONFIG_PRIORITY << 30; /* priority [31:30] */
pkt |= 1 << 28; /* tt [29:28] */
pkt |= 0x8 << 24; /* ftype [27:24] */
pkt |= destid << 8; /* destID [23:8] */
pkt |= sourceid >> 8; /* sourceID [7:0] */
__cvmx_srio_local_write32(srio_port, CVMX_SRIOMAINTX_IR_SP_TX_DATA(srio_port), pkt);
pkt = 0;
pkt |= sourceid << 24; /* sourceID [31:24] */
pkt |= 0 << 20; /* transaction [23:20] */
pkt |= 8 << 16; /* rdsize [19:16] */
pkt |= 0xc0 << 8; /* srcTID [15:8] */
pkt |= hopcount; /* hopcount [7:0] */
__cvmx_srio_local_write32(srio_port, CVMX_SRIOMAINTX_IR_SP_TX_DATA(srio_port), pkt);
pkt = 0;
pkt |= offset << 8; /* offset [31:11, wdptr[10], reserved[9:8] */
__cvmx_srio_local_write32(srio_port, CVMX_SRIOMAINTX_IR_SP_TX_DATA(srio_port), pkt);
}
else
{
/* Use the 8bit source ID */
sourceid = (sourceid >> 16) & 0xff;
/* MAINT Reads are 9 bytes */
__cvmx_srio_local_write32(srio_port, CVMX_SRIOMAINTX_IR_SP_TX_CTRL(srio_port), 9<<16);
pkt |= CVMX_SRIO_CONFIG_PRIORITY << 30; /* priority [31:30] */
pkt |= 0 << 28; /* tt [29:28] */
pkt |= 0x8 << 24; /* ftype [27:24] */
pkt |= destid << 16; /* destID [23:16] */
pkt |= sourceid << 8; /* sourceID [15:8] */
pkt |= 0 << 4; /* transaction [7:4] */
pkt |= 8 << 0; /* rdsize [3:0] */
__cvmx_srio_local_write32(srio_port, CVMX_SRIOMAINTX_IR_SP_TX_DATA(srio_port), pkt);
pkt = 0;
pkt |= 0xc0 << 24; /* srcTID [31:24] */
pkt |= hopcount << 16; /* hopcount [23:16] */
pkt |= offset >> 8; /* offset [15:0] */
__cvmx_srio_local_write32(srio_port, CVMX_SRIOMAINTX_IR_SP_TX_DATA(srio_port), pkt);
pkt = 0;
pkt |= offset << 24; /* offset [31:27, wdptr[26], reserved[25:24] */
__cvmx_srio_local_write32(srio_port, CVMX_SRIOMAINTX_IR_SP_TX_DATA(srio_port), pkt);
}
stop_cycle = cvmx_clock_get_rate(CVMX_CLOCK_CORE)/10 + cvmx_clock_get_count(CVMX_CLOCK_CORE);
do
{
return_code = cvmx_srio_receive_spf(srio_port, rx_buffer, sizeof(rx_buffer));
if ((return_code == 0) && (cvmx_clock_get_count(CVMX_CLOCK_CORE) > stop_cycle))
{
if (__cvmx_srio_state[srio_port].flags & CVMX_SRIO_INITIALIZE_DEBUG)
cvmx_dprintf("timeout\n");
return_code = -1;
}
} while (return_code == 0);
if (return_code == ((is16bit) ? 23 : 19))
{
if (is16bit)
{
if (offset & 4)
*result = *(uint32_t*)(rx_buffer + 15);
else
*result = *(uint32_t*)(rx_buffer + 11);
}
else
{
if (offset & 4)
*result = *(uint32_t*)(rx_buffer + 13);
else
*result = *(uint32_t*)(rx_buffer + 9);
}
if (__cvmx_srio_state[srio_port].flags & CVMX_SRIO_INITIALIZE_DEBUG)
cvmx_dprintf("0x%08x\n", (unsigned int)*result);
return_code = 0;
}
else
{
*result = 0xffffffff;
return_code = -1;
}
return return_code;
}
else
{
#if !defined(CVMX_BUILD_FOR_LINUX_HOST)
uint64_t physical;
physical = cvmx_srio_physical_map(srio_port,
CVMX_SRIO_WRITE_MODE_MAINTENANCE, CVMX_SRIO_CONFIG_PRIORITY,
CVMX_SRIO_READ_MODE_MAINTENANCE, CVMX_SRIO_CONFIG_PRIORITY,
srcid_index, destid, is16bit, offset + (hopcount<<24), 4);
if (!physical)
return -1;
if (__cvmx_srio_state[srio_port].flags & CVMX_SRIO_INITIALIZE_DEBUG)
cvmx_dprintf("SRIO%d: Remote read [id=0x%04x hop=%3d offset=0x%06x] <= ", srio_port, destid, hopcount, (unsigned int)offset);
/* Finally do the maintenance read to complete the config request */
*result = cvmx_read64_uint32(CVMX_ADD_IO_SEG(physical));
cvmx_srio_physical_unmap(physical, 4);
if (__cvmx_srio_state[srio_port].flags & CVMX_SRIO_INITIALIZE_DEBUG)
cvmx_dprintf("0x%08x\n", (unsigned int)*result);
return 0;
#else
return -1;
#endif
}
}
}
#ifdef CVMX_BUILD_FOR_LINUX_KERNEL
EXPORT_SYMBOL(cvmx_srio_config_read32);
#endif
/**
* Write 32bits to a Device's config space
*
* @param srio_port SRIO port the device is on
* @param srcid_index
* Which SRIO source ID to use. 0 = Primary, 1 = Secondary
* @param destid RapidIO device ID, or -1 for the local Octeon.
* @param is16bit Non zero if the transactions should use 16bit device IDs. Zero
* if transactions should use 8bit device IDs.
* @param hopcount Number of hops to the remote device. Use 0 for the local Octeon.
* @param offset Offset in config space. This must be a multiple of 32 bits.
* @param data Data to write.
*
* @return Zero on success, negative on failure.
*/
int cvmx_srio_config_write32(int srio_port, int srcid_index, int destid,
int is16bit, uint8_t hopcount, uint32_t offset,
uint32_t data)
{
if (destid == -1)
{
if (__cvmx_srio_state[srio_port].flags & CVMX_SRIO_INITIALIZE_DEBUG)
cvmx_dprintf("SRIO%d: Local write[0x%06x] => 0x%08x\n", srio_port, (unsigned int)offset, (unsigned int)data);
return __cvmx_srio_local_write32(srio_port, offset, data);
}
else
{
if (OCTEON_IS_MODEL(OCTEON_CN63XX_PASS1_X))
{
int return_code;
uint32_t pkt = 0;
uint32_t sourceid;
uint64_t stop_cycle;
char rx_buffer[64];
/* Tell the user */
if (__cvmx_srio_state[srio_port].flags & CVMX_SRIO_INITIALIZE_DEBUG)
cvmx_dprintf("SRIO%d: Remote write[id=0x%04x hop=%3d offset=0x%06x] => 0x%08x\n", srio_port, destid, hopcount, (unsigned int)offset, (unsigned int)data);
/* Read the proper source ID */
if (srcid_index)
__cvmx_srio_local_read32(srio_port, CVMX_SRIOMAINTX_SEC_DEV_ID(srio_port), &sourceid);
else
__cvmx_srio_local_read32(srio_port, CVMX_SRIOMAINTX_PRI_DEV_ID(srio_port), &sourceid);
if (is16bit)
{
/* Use the 16bit source ID */
sourceid &= 0xffff;
/* MAINT Writes are 19 bytes */
__cvmx_srio_local_write32(srio_port, CVMX_SRIOMAINTX_IR_SP_TX_CTRL(srio_port), 19<<16);
pkt |= CVMX_SRIO_CONFIG_PRIORITY << 30; /* priority [31:30] */
pkt |= 1 << 28; /* tt [29:28] */
pkt |= 0x8 << 24; /* ftype [27:24] */
pkt |= destid << 8; /* destID [23:8] */
pkt |= sourceid >> 8; /* sourceID [7:0] */
__cvmx_srio_local_write32(srio_port, CVMX_SRIOMAINTX_IR_SP_TX_DATA(srio_port), pkt);
pkt = 0;
pkt |= sourceid << 24; /* sourceID [31:24] */
pkt |= 1 << 20; /* transaction [23:20] */
pkt |= 8 << 16; /* wrsize [19:16] */
pkt |= 0xc0 << 8; /* srcTID [15:8] */
pkt |= hopcount; /* hopcount [7:0] */
__cvmx_srio_local_write32(srio_port, CVMX_SRIOMAINTX_IR_SP_TX_DATA(srio_port), pkt);
pkt = 0;
pkt |= offset << 8; /* offset [31:11, wdptr[10], reserved[9:8] */
if ((offset & 4) == 0)
pkt |= 0xff & (data >> 24); /* data [7:0] */
__cvmx_srio_local_write32(srio_port, CVMX_SRIOMAINTX_IR_SP_TX_DATA(srio_port), pkt);
if (offset & 4)
{
pkt = 0xff & (data >> 24);
__cvmx_srio_local_write32(srio_port, CVMX_SRIOMAINTX_IR_SP_TX_DATA(srio_port), pkt);
pkt = data << 8;
__cvmx_srio_local_write32(srio_port, CVMX_SRIOMAINTX_IR_SP_TX_DATA(srio_port), pkt);
}
else
{
pkt = data << 8;
__cvmx_srio_local_write32(srio_port, CVMX_SRIOMAINTX_IR_SP_TX_DATA(srio_port), pkt);
__cvmx_srio_local_write32(srio_port, CVMX_SRIOMAINTX_IR_SP_TX_DATA(srio_port), 0);
}
}
else
{
/* Use the 8bit source ID */
sourceid = (sourceid >> 16) & 0xff;
/* MAINT Writes are 17 bytes */
__cvmx_srio_local_write32(srio_port, CVMX_SRIOMAINTX_IR_SP_TX_CTRL(srio_port), 17<<16);
pkt |= CVMX_SRIO_CONFIG_PRIORITY << 30; /* priority [31:30] */
pkt |= 0 << 28; /* tt [29:28] */
pkt |= 0x8 << 24; /* ftype [27:24] */
pkt |= destid << 16; /* destID [23:16] */
pkt |= sourceid << 8; /* sourceID [15:8] */
pkt |= 1 << 4; /* transaction [7:4] */
pkt |= 8 << 0; /* wrsize [3:0] */
__cvmx_srio_local_write32(srio_port, CVMX_SRIOMAINTX_IR_SP_TX_DATA(srio_port), pkt);
pkt = 0;
pkt |= 0xc0 << 24; /* srcTID [31:24] */
pkt |= hopcount << 16; /* hopcount [23:16] */
pkt |= offset >> 8; /* offset [15:0] */
__cvmx_srio_local_write32(srio_port, CVMX_SRIOMAINTX_IR_SP_TX_DATA(srio_port), pkt);
pkt = 0;
pkt |= offset << 24; /* offset [31:27, wdptr[26], reserved[25:24] */
if (offset & 4)
{
__cvmx_srio_local_write32(srio_port, CVMX_SRIOMAINTX_IR_SP_TX_DATA(srio_port), pkt);
pkt = data >> 8;
__cvmx_srio_local_write32(srio_port, CVMX_SRIOMAINTX_IR_SP_TX_DATA(srio_port), pkt);
pkt = data << 24;
__cvmx_srio_local_write32(srio_port, CVMX_SRIOMAINTX_IR_SP_TX_DATA(srio_port), pkt);
}
else
{
pkt |= data >> 8; /* data [23:0] */
__cvmx_srio_local_write32(srio_port, CVMX_SRIOMAINTX_IR_SP_TX_DATA(srio_port), pkt);
pkt = data << 24; /* data [31:24] */
__cvmx_srio_local_write32(srio_port, CVMX_SRIOMAINTX_IR_SP_TX_DATA(srio_port), pkt);
__cvmx_srio_local_write32(srio_port, CVMX_SRIOMAINTX_IR_SP_TX_DATA(srio_port), 0);
}
}
stop_cycle = cvmx_clock_get_rate(CVMX_CLOCK_CORE)/10 + cvmx_clock_get_count(CVMX_CLOCK_CORE);
do
{
return_code = cvmx_srio_receive_spf(srio_port, rx_buffer, sizeof(rx_buffer));
if ((return_code == 0) && (cvmx_clock_get_count(CVMX_CLOCK_CORE) > stop_cycle))
{
if (__cvmx_srio_state[srio_port].flags & CVMX_SRIO_INITIALIZE_DEBUG)
cvmx_dprintf("timeout\n");
return_code = -1;
}
} while (return_code == 0);
if (return_code == ((is16bit) ? 15 : 11))
return_code = 0;
else
{
cvmx_dprintf("SRIO%d: Remote write failed\n", srio_port);
return_code = -1;
}
return return_code;
}
else
{
#if !defined(CVMX_BUILD_FOR_LINUX_HOST)
uint64_t physical = cvmx_srio_physical_map(srio_port,
CVMX_SRIO_WRITE_MODE_MAINTENANCE, CVMX_SRIO_CONFIG_PRIORITY,
CVMX_SRIO_READ_MODE_MAINTENANCE, CVMX_SRIO_CONFIG_PRIORITY,
srcid_index, destid, is16bit, offset + (hopcount<<24), 4);
if (!physical)
return -1;
if (__cvmx_srio_state[srio_port].flags & CVMX_SRIO_INITIALIZE_DEBUG)
cvmx_dprintf("SRIO%d: Remote write[id=0x%04x hop=%3d offset=0x%06x] => 0x%08x\n", srio_port, destid, hopcount, (unsigned int)offset, (unsigned int)data);
/* Finally do the maintenance write to complete the config request */
cvmx_write64_uint32(CVMX_ADD_IO_SEG(physical), data);
return cvmx_srio_physical_unmap(physical, 4);
#else
return -1;
#endif
}
}
}
/**
* Send a RapidIO doorbell to a remote device
*
* @param srio_port SRIO port the device is on
* @param srcid_index
* Which SRIO source ID to use. 0 = Primary, 1 = Secondary
* @param destid RapidIO device ID.
* @param is16bit Non zero if the transactions should use 16bit device IDs. Zero
* if transactions should use 8bit device IDs.
* @param priority Doorbell priority (0-3)
* @param data Data for doorbell.
*
* @return Zero on success, negative on failure.
*/
int cvmx_srio_send_doorbell(int srio_port, int srcid_index, int destid, int is16bit, int priority, uint16_t data)
{
cvmx_sriox_tx_bell_t tx_bell;
tx_bell.u64 = 0;
tx_bell.s.data = data;
tx_bell.s.dest_id = destid;
tx_bell.s.src_id = srcid_index;
tx_bell.s.id16 = !!is16bit;
tx_bell.s.priority = priority;
/* Make sure the previous doorbell has completed */
if (CVMX_WAIT_FOR_FIELD64(CVMX_SRIOX_TX_BELL(srio_port), cvmx_sriox_tx_bell_t, pending, ==, 0, CVMX_SRIO_DOORBELL_TIMEOUT))
{
cvmx_dprintf("SRIO%d: Pending bit stuck before doorbell\n", srio_port);
return -1;
}
if (__cvmx_srio_state[srio_port].flags & CVMX_SRIO_INITIALIZE_DEBUG)
cvmx_dprintf("SRIO%d: Send doorbell destid=0x%x, priority=%d, data=0x%x\n", srio_port, destid, priority, 0xffff & data);
/* Send the doorbell. We don't wait for it to complete. The next doorbell
may delay on the pending bit, but this gives the caller the ability to
do other stuff while the doorbell processes */
cvmx_write_csr(CVMX_SRIOX_TX_BELL(srio_port), tx_bell.u64);
return 0;
}
#ifdef CVMX_BUILD_FOR_LINUX_KERNEL
EXPORT_SYMBOL(cvmx_srio_send_doorbell);
#endif
/**
* Get the status of the last doorbell sent. If the dooorbell
* hardware is done, then the status is cleared to get ready for
* the next doorbell (or retry).
*
* @param srio_port SRIO port to check doorbell on
*
* @return Doorbell status
*/
cvmx_srio_doorbell_status_t cvmx_srio_send_doorbell_status(int srio_port)
{
cvmx_sriox_tx_bell_t tx_bell;
cvmx_sriox_tx_bell_info_t tx_bell_info;
cvmx_sriox_int_reg_t int_reg;
cvmx_sriox_int_reg_t int_reg_clear;
/* Return busy if the doorbell is still processing */
tx_bell.u64 = cvmx_read_csr(CVMX_SRIOX_TX_BELL(srio_port));
if (tx_bell.s.pending)
return CVMX_SRIO_DOORBELL_BUSY;
/* Read and clear the TX doorbell interrupts */
int_reg.u64 = cvmx_read_csr(CVMX_SRIOX_INT_REG(srio_port));
int_reg_clear.u64 = 0;
int_reg_clear.s.bell_err = int_reg.s.bell_err;
int_reg_clear.s.txbell = int_reg.s.txbell;
cvmx_write_csr(CVMX_SRIOX_INT_REG(srio_port), int_reg_clear.u64);
/* Check for errors */
if (int_reg.s.bell_err)
{
if (__cvmx_srio_state[srio_port].flags & CVMX_SRIO_INITIALIZE_DEBUG)
cvmx_dprintf("SRIO%d: Send doorbell failed\n", srio_port);
tx_bell_info.u64 = cvmx_read_csr(CVMX_SRIOX_TX_BELL_INFO(srio_port));
if (tx_bell_info.s.timeout)
return CVMX_SRIO_DOORBELL_TMOUT;
if (tx_bell_info.s.error)
return CVMX_SRIO_DOORBELL_ERROR;
if (tx_bell_info.s.retry)
return CVMX_SRIO_DOORBELL_RETRY;
}
/* Check if we're done */
if (int_reg.s.txbell)
return CVMX_SRIO_DOORBELL_DONE;
/* No doorbell found */
return CVMX_SRIO_DOORBELL_NONE;
}
/**
* Read a received doorbell and report data about it.
*
* @param srio_port SRIO port to check for the received doorbell
* @param destid_index
* Which Octeon destination ID was the doorbell for
* @param sequence_num
* Sequence number of doorbell (32bits)
* @param srcid RapidIO source ID of the doorbell sender
* @param priority Priority of the doorbell (0-3)
* @param is16bit Non zero if the transactions should use 16bit device IDs. Zero
* if transactions should use 8bit device IDs.
* @param data Data in the doorbell (16 bits)
*
* @return Doorbell status. Either DONE, NONE, or ERROR.
*/
cvmx_srio_doorbell_status_t cvmx_srio_receive_doorbell(int srio_port,
int *destid_index, uint32_t *sequence_num, int *srcid, int *priority,
int *is16bit, uint16_t *data)
{
cvmx_sriox_rx_bell_seq_t rx_bell_seq;
cvmx_sriox_rx_bell_t rx_bell;
/* Check if there are any pending doorbells */
rx_bell_seq.u64 = cvmx_read_csr(CVMX_SRIOX_RX_BELL_SEQ(srio_port));
if (!rx_bell_seq.s.count)
return CVMX_SRIO_DOORBELL_NONE;
/* Read the doorbell and write our return parameters */
rx_bell.u64 = cvmx_read_csr(CVMX_SRIOX_RX_BELL(srio_port));
*sequence_num = rx_bell_seq.s.seq;
*srcid = rx_bell.s.src_id;
*priority = rx_bell.s.priority;
*is16bit = rx_bell.s.id16;
*data = rx_bell.s.data;
*destid_index = rx_bell.s.dest_id;
if (__cvmx_srio_state[srio_port].flags & CVMX_SRIO_INITIALIZE_DEBUG)
cvmx_dprintf("SRIO%d: Receive doorbell sequence=0x%x, srcid=0x%x, priority=%d, data=0x%x\n",
srio_port, rx_bell_seq.s.seq, rx_bell.s.src_id, rx_bell.s.priority, rx_bell.s.data);
return CVMX_SRIO_DOORBELL_DONE;
}
/**
* Receive a packet from the Soft Packet FIFO (SPF).
*
* @param srio_port SRIO port to read the packet from.
* @param buffer Buffer to receive the packet.
* @param buffer_length
* Length of the buffer in bytes.
*
* @return Returns the length of the packet read. Negative on failure.
* Zero if no packets are available.
*/
int cvmx_srio_receive_spf(int srio_port, void *buffer, int buffer_length)
{
uint32_t *ptr = (uint32_t *)buffer;
cvmx_sriomaintx_ir_sp_rx_stat_t sriomaintx_ir_sp_rx_stat;
/* Read the SFP status */
if (__cvmx_srio_local_read32(srio_port, CVMX_SRIOMAINTX_IR_SP_RX_STAT(srio_port), &sriomaintx_ir_sp_rx_stat.u32))
return -1;
/* Return zero if there isn't a packet available */
if (sriomaintx_ir_sp_rx_stat.s.buffers < 1)
return 0;
if (__cvmx_srio_state[srio_port].flags & CVMX_SRIO_INITIALIZE_DEBUG)
cvmx_dprintf("SRIO%d: Soft packet FIFO received %d bytes", srio_port, sriomaintx_ir_sp_rx_stat.s.octets);
/* Return error if the packet is larger than our buffer */
if (sriomaintx_ir_sp_rx_stat.s.octets > buffer_length)
return -1;
/* Read out the packet four bytes at a time */
buffer_length = sriomaintx_ir_sp_rx_stat.s.octets;
while (buffer_length > 0)
{
__cvmx_srio_local_read32(srio_port, CVMX_SRIOMAINTX_IR_SP_RX_DATA(srio_port), ptr);
if (__cvmx_srio_state[srio_port].flags & CVMX_SRIO_INITIALIZE_DEBUG)
cvmx_dprintf(" %08x", (unsigned int)*ptr);
ptr++;
buffer_length-=4;
}
if (__cvmx_srio_state[srio_port].flags & CVMX_SRIO_INITIALIZE_DEBUG)
cvmx_dprintf("\n");
/* Return the number of bytes in the buffer */
return sriomaintx_ir_sp_rx_stat.s.octets;
}
#ifndef CVMX_BUILD_FOR_LINUX_HOST
/**
* Map a remote device's memory region into Octeon's physical
* address area. The caller can then map this into a core using
* the TLB or XKPHYS.
*
* @param srio_port SRIO port to map the device on
* @param write_op Type of operation to perform on a write to the device.
* Normally should be CVMX_SRIO_WRITE_MODE_AUTO.
* @param write_priority
* SRIO priority of writes (0-3)
* @param read_op Type of operation to perform on reads to the device.
* Normally should be CVMX_SRIO_READ_MODE_NORMAL.
* @param read_priority
* SRIO priority of reads (0-3)
* @param srcid_index
* Which SRIO source ID to use. 0 = Primary, 1 = Secondary
* @param destid RapidIO device ID.
* @param is16bit Non zero if the transactions should use 16bit device IDs. Zero
* if transactions should use 8bit device IDs.
* @param base Device base address to start the mapping
* @param size Size of the mapping in bytes
*
* @return Octeon 64bit physical address that accesses the remote device,
* or zero on failure.
*/
uint64_t cvmx_srio_physical_map(int srio_port, cvmx_srio_write_mode_t write_op,
int write_priority, cvmx_srio_read_mode_t read_op, int read_priority,
int srcid_index, int destid, int is16bit, uint64_t base, uint64_t size)
{
cvmx_sriox_s2m_typex_t needed_s2m_type;
cvmx_sli_mem_access_subidx_t needed_subid;
int s2m_index;
int subdid;
cvmx_sli_address_t sli_address;
/* We currently don't support mapping regions that span a 34 bit boundary.
Keeping track of multiple regions to span 34 bits is hard and not
likely to be needed */
if (((base+size-1)>>34) != (base>>34))
{
cvmx_dprintf("SRIO%d: Failed to map range 0x%llx-0x%llx spanning a 34bit boundary\n",
srio_port, (ULL)base, (ULL)base+size-1);
return 0;
}
/* Build the S2M_TYPE we are going to need */
needed_s2m_type.u64 = 0;
needed_s2m_type.s.wr_op = write_op;
needed_s2m_type.s.rd_op = read_op;
needed_s2m_type.s.wr_prior = write_priority;
needed_s2m_type.s.rd_prior = read_priority;
needed_s2m_type.s.src_id = srcid_index;
needed_s2m_type.s.id16 = !!is16bit;
/* Build the needed SubID config */
needed_subid.u64 = 0;
needed_subid.s.port = srio_port;
needed_subid.s.nmerge = 0;
/* FIXME: We might want to use the device ID swapping modes so the device
ID is part of the lower address bits. This would allow many more
devices to share S2M_TYPE indexes. This would require "base+size-1"
to fit in bits [17:0] or bits[25:0] for 8 bits of device ID */
if (base < (1ull<<34))
{
needed_subid.cn63xx.ba = destid;
needed_s2m_type.s.iaow_sel = 0;
}
else if (base < (1ull<<42))
{
needed_subid.cn63xx.ba = (base>>34) & 0xff;
needed_subid.cn63xx.ba |= ((uint64_t)destid & 0xff) << (42-34);
needed_subid.cn63xx.ba |= (((uint64_t)destid>>8) & 0xff) << (51-34);
needed_s2m_type.s.iaow_sel = 1;
}
else
{
if (destid>>8)
{
cvmx_dprintf("SRIO%d: Attempt to map 16bit device ID 0x%x using 66bit addressing\n", srio_port, destid);
return 0;
}
if (base>>50)
{
cvmx_dprintf("SRIO%d: Attempt to map address 0x%llx using 66bit addressing\n", srio_port, (ULL)base);
return 0;
}
needed_subid.cn63xx.ba = (base>>34) & 0xffff;
needed_subid.cn63xx.ba |= ((uint64_t)destid & 0xff) << (51-34);
needed_s2m_type.s.iaow_sel = 2;
}
/* Find a S2M_TYPE index to use. If this fails return 0 */
s2m_index = __cvmx_srio_alloc_s2m(srio_port, needed_s2m_type);
if (s2m_index == -1)
return 0;
/* Attach the SubID to the S2M_TYPE index */
needed_subid.s.rtype = s2m_index & 3;
needed_subid.s.wtype = s2m_index & 3;
needed_subid.cn63xx.ba |= (((uint64_t)s2m_index >> 2) & 1) << (50-34);
needed_subid.cn63xx.ba |= (((uint64_t)s2m_index >> 3) & 1) << (59-34);
/* Allocate a SubID for use */
subdid = __cvmx_srio_alloc_subid(needed_subid);
if (subdid == -1)
{
/* Free the s2m_index as we aren't using it */
__cvmx_srio_free_s2m(srio_port, s2m_index);
return 0;
}
/* Build the final core physical address */
sli_address.u64 = 0;
sli_address.mem.io = 1;
sli_address.mem.did = 3;
sli_address.mem.subdid = subdid>>2;
sli_address.mem.se = subdid & 3;
sli_address.mem.address = base; /* Bits[33:0] of full address */
return sli_address.u64;
}
/**
* Unmap a physical address window created by cvmx_srio_phys_map().
*
* @param physical_address
* Physical address returned by cvmx_srio_phys_map().
* @param size Size used on original call.
*
* @return Zero on success, negative on failure.
*/
int cvmx_srio_physical_unmap(uint64_t physical_address, uint64_t size)
{
cvmx_sli_mem_access_subidx_t subid;
int subdid = (physical_address >> 40) & 7;
int extender = (physical_address >> 34) & 3;
int mem_index = subdid * 4 + extender;
int read_s2m_type;
/* Get the subid setup so we can figure out where this mapping was for */
subid.u64 = cvmx_read_csr(CVMX_PEXP_SLI_MEM_ACCESS_SUBIDX(mem_index));
/* Type[0] is mapped to the Relaxed Ordering
Type[1] is mapped to the No Snoop
Type[2] is mapped directly to bit 50 of the SLI address
Type[3] is mapped directly to bit 59 of the SLI address */
read_s2m_type = ((subid.cn63xx.ba>>(50-34))&1<<2) | ((subid.cn63xx.ba>>(59-34))&1<<3);
read_s2m_type |= subid.s.rtype;
__cvmx_srio_free_subid(mem_index);
__cvmx_srio_free_s2m(subid.s.port, read_s2m_type);
return 0;
}
#ifdef CVMX_ENABLE_PKO_FUNCTIONS
/**
* fill out outbound message descriptor
*
* @param port pip/ipd port number
* @param buf_ptr pointer to a buffer pointer. the buffer pointer points
* to a chain of buffers that hold an outbound srio packet.
* the packet can take the format of (1) a pip/ipd inbound
* message or (2) an application-generated outbound message
* @param desc_ptr pointer to an outbound message descriptor. should be null
* if *buf_ptr is in the format (1)
*
* @return 0 on success; negative of failure.
*/
int cvmx_srio_omsg_desc (uint64_t port, cvmx_buf_ptr_t *buf_ptr,
cvmx_srio_tx_message_header_t *desc_ptr)
{
int ret_val = -1;
int intf_num;
cvmx_helper_interface_mode_t imode;
uint64_t *desc_addr, *hdr_addr;
cvmx_srio_rx_message_header_t rx_msg_hdr;
cvmx_srio_tx_message_header_t *tx_msg_hdr_ptr;
if (buf_ptr == NULL)
return ret_val;
/* check if port is an srio port */
intf_num = cvmx_helper_get_interface_num (port);
imode = cvmx_helper_interface_get_mode (intf_num);
if (imode != CVMX_HELPER_INTERFACE_MODE_SRIO)
return ret_val;
/* app-generated outbound message. descriptor space pre-allocated */
if (desc_ptr != NULL)
{
desc_addr = (uint64_t *) cvmx_phys_to_ptr ((*buf_ptr).s.addr);
*desc_addr = *(uint64_t *) desc_ptr;
ret_val = 0;
return ret_val;
}
/* pip/ipd inbound message. 16-byte srio message header is present */
hdr_addr = (uint64_t *) cvmx_phys_to_ptr ((*buf_ptr).s.addr);
rx_msg_hdr.word0.u64 = *hdr_addr;
/* adjust buffer pointer to get rid of srio message header word 0 */
(*buf_ptr).s.addr += 8;
(*buf_ptr).s.size -= 8; /* last buffer or not */
if ((*buf_ptr).s.addr >> 7 > ((*buf_ptr).s.addr - 8) >> 7)
(*buf_ptr).s.back++;
tx_msg_hdr_ptr = (cvmx_srio_tx_message_header_t *)
cvmx_phys_to_ptr ((*buf_ptr).s.addr);
/* transfer values from rx to tx */
tx_msg_hdr_ptr->s.prio = rx_msg_hdr.word0.s.prio;
tx_msg_hdr_ptr->s.tt = rx_msg_hdr.word0.s.tt; /* called id in hrm */
tx_msg_hdr_ptr->s.sis = rx_msg_hdr.word0.s.dis;
tx_msg_hdr_ptr->s.ssize = rx_msg_hdr.word0.s.ssize;
tx_msg_hdr_ptr->s.did = rx_msg_hdr.word0.s.sid;
tx_msg_hdr_ptr->s.mbox = rx_msg_hdr.word0.s.mbox;
/* other values we have to decide */
tx_msg_hdr_ptr->s.xmbox = 0; /* multi-segement in general */
tx_msg_hdr_ptr->s.letter = 0; /* fake like traffic gen */
tx_msg_hdr_ptr->s.lns = 0; /* not use sriox_omsg_ctrly[] */
tx_msg_hdr_ptr->s.intr = 1; /* get status */
ret_val = 0;
return ret_val;
}
#endif
#endif
Index: head/sys/contrib/octeon-sdk/cvmx-usb.c
===================================================================
--- head/sys/contrib/octeon-sdk/cvmx-usb.c (revision 232815)
+++ head/sys/contrib/octeon-sdk/cvmx-usb.c (revision 232816)
@@ -1,3453 +1,3455 @@
/***********************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-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.
*
*
$Revision: 32636 $
*/
#ifdef CVMX_BUILD_FOR_LINUX_KERNEL
#include
#include
#include
#include
#include
#include
#include
#include
#include
#if 0
/* Do not use cvmx-error.h for now. When the cvmx-error.h is properly
* ported, remove the above #if 0, and all #ifdef __CVMX_ERROR_H__ within
* this file */
#include
#endif
#else
#include "cvmx.h"
#include "cvmx-clock.h"
#include "cvmx-sysinfo.h"
#include "cvmx-usb.h"
#include "cvmx-helper.h"
#include "cvmx-helper-board.h"
#include "cvmx-csr-db.h"
#include "cvmx-swap.h"
+#if !defined(CVMX_BUILD_FOR_FREEBSD_KERNEL)
#include "cvmx-error.h"
+#endif
#endif
#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 */
#define ALLOW_CSR_DECODES 0 /* CSR decoding when CVMX_USB_INITIALIZE_FLAGS_DEBUG_CSRS is set
enlarges the code a lot. This define overrides the ability to do CSR
decoding since it isn't necessary 99% of the time. Change this to a
one if you need CSR decoding */
/* 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;
/**
* 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 * \
cvmx_clock_get_rate(CVMX_CLOCK_CORE) / 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)
/**
* @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);
#if ALLOW_CSR_DECODES
if (cvmx_unlikely(usb->init_flags & CVMX_USB_INITIALIZE_FLAGS_DEBUG_CSRS))
{
cvmx_dprintf("Read: ");
cvmx_csr_db_decode(cvmx_get_proc_id(), address, result);
}
#endif
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)
{
#if ALLOW_CSR_DECODES
if (cvmx_unlikely(usb->init_flags & CVMX_USB_INITIALIZE_FLAGS_DEBUG_CSRS))
{
cvmx_dprintf("Write: ");
cvmx_csr_db_decode(cvmx_get_proc_id(), address, value);
}
#endif
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);
#if ALLOW_CSR_DECODES
if (cvmx_unlikely(usb->init_flags & CVMX_USB_INITIALIZE_FLAGS_DEBUG_CSRS))
{
cvmx_dprintf("Read: ");
cvmx_csr_db_decode(cvmx_get_proc_id(), address, result);
}
#endif
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)
{
#if ALLOW_CSR_DECODES
if (cvmx_unlikely(usb->init_flags & CVMX_USB_INITIALIZE_FLAGS_DEBUG_CSRS))
{
cvmx_dprintf("Write: ");
cvmx_csr_db_decode(cvmx_get_proc_id(), address, value);
}
#endif
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.
*
* This utilizes cvmx_helper_board_usb_get_num_ports()
* to get any board specific variations.
*
* @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 __cvmx_helper_board_usb_get_num_ports(arch_ports);
}
#ifdef CVMX_BUILD_FOR_LINUX_KERNEL
EXPORT_SYMBOL(cvmx_usb_get_num_ports);
#endif
/**
* @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 (__cvmx_helper_board_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 (__cvmx_helper_board_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; itransaction + i);
for (i=0; ifree_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 = (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_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 µ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_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 */
}
#ifdef __CVMX_ERROR_H__
cvmx_error_enable_group(CVMX_ERROR_GROUP_USB, usb->index);
#endif
CVMX_USB_RETURN(CVMX_USB_SUCCESS);
}
#ifdef CVMX_BUILD_FOR_LINUX_KERNEL
EXPORT_SYMBOL(cvmx_usb_initialize);
#endif
/**
* 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);
#ifdef __CVMX_ERROR_H__
cvmx_error_disable_group(CVMX_ERROR_GROUP_USB, usb->index);
#endif
/* 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);
}
#ifdef CVMX_BUILD_FOR_LINUX_KERNEL
EXPORT_SYMBOL(cvmx_usb_shutdown);
#endif
/**
* 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);
}
#ifdef CVMX_BUILD_FOR_LINUX_KERNEL
EXPORT_SYMBOL(cvmx_usb_enable);
#endif
/**
* 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);
}
#ifdef CVMX_BUILD_FOR_LINUX_KERNEL
EXPORT_SYMBOL(cvmx_usb_disable);
#endif
/**
* 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;
}
#ifdef CVMX_BUILD_FOR_LINUX_KERNEL
EXPORT_SYMBOL(cvmx_usb_get_status);
#endif
/**
* 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();
}
#ifdef CVMX_BUILD_FOR_LINUX_KERNEL
EXPORT_SYMBOL(cvmx_usb_set_status);
#endif
/**
* @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));
}
#ifdef CVMX_BUILD_FOR_LINUX_KERNEL
EXPORT_SYMBOL(cvmx_usb_open_pipe);
#endif
/**
* @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<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<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);
}
#ifdef CVMX_BUILD_FOR_LINUX_KERNEL
EXPORT_SYMBOL(cvmx_usb_submit_bulk);
#endif
/**
* 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);
}
#ifdef CVMX_BUILD_FOR_LINUX_KERNEL
EXPORT_SYMBOL(cvmx_usb_submit_interrupt);
#endif
/**
* 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);
}
#ifdef CVMX_BUILD_FOR_LINUX_KERNEL
EXPORT_SYMBOL(cvmx_usb_submit_control);
#endif
/**
* 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);
}
#ifdef CVMX_BUILD_FOR_LINUX_KERNEL
EXPORT_SYMBOL(cvmx_usb_submit_isochronous);
#endif
/**
* 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);
}
#ifdef CVMX_BUILD_FOR_LINUX_KERNEL
EXPORT_SYMBOL(cvmx_usb_cancel);
#endif
/**
* 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);
}
#ifdef CVMX_BUILD_FOR_LINUX_KERNEL
EXPORT_SYMBOL(cvmx_usb_cancel_all);
#endif
/**
* 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);
}
#ifdef CVMX_BUILD_FOR_LINUX_KERNEL
EXPORT_SYMBOL(cvmx_usb_close_pipe);
#endif
/**
* 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);
}
#ifdef CVMX_BUILD_FOR_LINUX_KERNEL
EXPORT_SYMBOL(cvmx_usb_register_callback);
#endif
/**
* 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);
}
#ifdef CVMX_BUILD_FOR_LINUX_KERNEL
EXPORT_SYMBOL(cvmx_usb_get_frame_number);
#endif
/**
* @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 0
cvmx_dprintf("Channel %d%s%s%s%s%s%s%s%s%s%s%s\n", channel,
(usbc_hcint.s.datatglerr) ? " DATATGLERR" : "",
(usbc_hcint.s.frmovrun) ? " FRMOVRUN" : "",
(usbc_hcint.s.bblerr) ? " BBLERR" : "",
(usbc_hcint.s.xacterr) ? " XACTERR" : "",
(usbc_hcint.s.nyet) ? " NYET" : "",
(usbc_hcint.s.ack) ? " ACK" : "",
(usbc_hcint.s.nak) ? " NAK" : "",
(usbc_hcint.s.stall) ? " STALL" : "",
(usbc_hcint.s.ahberr) ? " AHBERR" : "",
(usbc_hcint.s.chhltd) ? " CHHLTD" : "",
(usbc_hcint.s.xfercompl) ? " XFERCOMPL" : "");
#endif
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<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<pipe + endpoint_num;
pipe->pid_toggle = !!toggle;
}
extern int cvmx_usb_get_toggle(cvmx_usb_state_t *state, int endpoint_num)
{
cvmx_usb_internal_state_t *usb = (cvmx_usb_internal_state_t*)state;
cvmx_usb_pipe_t *pipe = usb->pipe + endpoint_num;
if (pipe->pid_toggle)
return (1);
return (0);
}
Index: head/sys/mips/cavium/files.octeon1
===================================================================
--- head/sys/mips/cavium/files.octeon1 (revision 232815)
+++ head/sys/mips/cavium/files.octeon1 (revision 232816)
@@ -1,110 +1,90 @@
# $FreeBSD$
# Octeon Support Files
#
mips/cavium/asm_octeon.S optional smp
mips/cavium/ciu.c standard
mips/cavium/obio.c optional uart
mips/cavium/octeon_ds1337.c standard
mips/cavium/octeon_ebt3000_cf.c optional cf
mips/cavium/octeon_machdep.c standard
mips/cavium/octeon_mp.c optional smp
mips/cavium/octeon_rtc.c standard
mips/cavium/uart_bus_octeonusart.c optional uart
mips/cavium/uart_cpu_octeonusart.c optional uart
mips/cavium/uart_dev_oct16550.c optional uart
mips/mips/intr_machdep.c standard
mips/mips/tick.c standard
mips/cavium/octeon_rnd.c optional random
mips/cavium/octeon_wdog.c optional octeon_wdog
mips/cavium/octeon_nmi.S optional octeon_wdog
mips/cavium/cryptocteon/cavium_crypto.c optional cryptocteon
mips/cavium/cryptocteon/cryptocteon.c optional cryptocteon
mips/mips/octeon_cop2_swtch.S standard
mips/mips/octeon_cop2.c standard
mips/cavium/if_octm.c optional octm
contrib/octeon-sdk/cvmx-mgmt-port.c optional octm
mips/cavium/octe/ethernet.c optional octe
mips/cavium/octe/ethernet-mv88e61xx.c optional octe octeon_vendor_lanner
mips/cavium/octe/ethernet-common.c optional octe
mips/cavium/octe/ethernet-mdio.c optional octe
mips/cavium/octe/ethernet-mem.c optional octe
mips/cavium/octe/ethernet-rgmii.c optional octe
mips/cavium/octe/ethernet-rx.c optional octe
mips/cavium/octe/ethernet-sgmii.c optional octe
mips/cavium/octe/ethernet-spi.c optional octe
mips/cavium/octe/ethernet-tx.c optional octe
mips/cavium/octe/ethernet-xaui.c optional octe
mips/cavium/octe/mv88e61xxphy.c optional octe mv88e61xxphy
mips/cavium/octe/octe.c optional octe
mips/cavium/octe/octebus.c optional octe
mips/cavium/octopci.c optional pci
mips/cavium/octopci_bus_space.c optional pci
mips/cavium/usb/octusb.c optional usb octusb
mips/cavium/usb/octusb_octeon.c optional usb octusb
contrib/octeon-sdk/cvmx-usb.c optional octusb
mips/cavium/octeon_gpio.c optional gpio
# XXX Some files could be excluded in some configurations. Making them
# optional but on in the default config would seem reasonable.
contrib/octeon-sdk/cvmx-cmd-queue.c standard
contrib/octeon-sdk/cvmx-bootmem.c standard
contrib/octeon-sdk/cvmx-clock.c standard
-contrib/octeon-sdk/cvmx-error.c standard
-contrib/octeon-sdk/cvmx-error-custom.c standard
-contrib/octeon-sdk/cvmx-error-init-cn30xx.c standard
-contrib/octeon-sdk/cvmx-error-init-cn31xx.c standard
-contrib/octeon-sdk/cvmx-error-init-cn38xx.c standard
-contrib/octeon-sdk/cvmx-error-init-cn38xxp2.c standard
-contrib/octeon-sdk/cvmx-error-init-cn50xx.c standard
-contrib/octeon-sdk/cvmx-error-init-cn52xx.c standard
-contrib/octeon-sdk/cvmx-error-init-cn52xxp1.c standard
-contrib/octeon-sdk/cvmx-error-init-cn56xx.c standard
-contrib/octeon-sdk/cvmx-error-init-cn56xxp1.c standard
-contrib/octeon-sdk/cvmx-error-init-cn58xx.c standard
-contrib/octeon-sdk/cvmx-error-init-cn58xxp1.c standard
-contrib/octeon-sdk/cvmx-error-init-cn61xx.c standard
-contrib/octeon-sdk/cvmx-error-init-cn63xx.c standard
-contrib/octeon-sdk/cvmx-error-init-cn63xxp1.c standard
-contrib/octeon-sdk/cvmx-error-init-cn66xx.c standard
-contrib/octeon-sdk/cvmx-error-init-cn68xx.c standard
-contrib/octeon-sdk/cvmx-error-init-cn68xxp1.c standard
-contrib/octeon-sdk/cvmx-error-init-cnf71xx.c standard
contrib/octeon-sdk/cvmx-fpa.c standard
contrib/octeon-sdk/cvmx-helper.c standard
contrib/octeon-sdk/cvmx-helper-board.c standard
contrib/octeon-sdk/cvmx-helper-cfg.c standard
contrib/octeon-sdk/cvmx-helper-errata.c standard
contrib/octeon-sdk/cvmx-helper-fpa.c standard
contrib/octeon-sdk/cvmx-helper-ilk.c standard
contrib/octeon-sdk/cvmx-helper-jtag.c standard
contrib/octeon-sdk/cvmx-helper-loop.c standard
contrib/octeon-sdk/cvmx-helper-npi.c standard
contrib/octeon-sdk/cvmx-helper-rgmii.c standard
contrib/octeon-sdk/cvmx-helper-sgmii.c standard
contrib/octeon-sdk/cvmx-helper-spi.c standard
contrib/octeon-sdk/cvmx-helper-srio.c standard
contrib/octeon-sdk/cvmx-helper-util.c standard
contrib/octeon-sdk/cvmx-helper-xaui.c standard
contrib/octeon-sdk/cvmx-ilk.c standard
contrib/octeon-sdk/cvmx-ipd.c standard
contrib/octeon-sdk/cvmx-l2c.c standard
contrib/octeon-sdk/cvmx-pcie.c standard
contrib/octeon-sdk/cvmx-pko.c standard
contrib/octeon-sdk/cvmx-qlm.c standard
contrib/octeon-sdk/cvmx-qlm-tables.c standard
contrib/octeon-sdk/cvmx-spi.c standard
contrib/octeon-sdk/cvmx-spi4000.c standard
contrib/octeon-sdk/cvmx-srio.c standard
contrib/octeon-sdk/cvmx-sysinfo.c standard
contrib/octeon-sdk/cvmx-thunder.c standard
contrib/octeon-sdk/cvmx-twsi.c standard
contrib/octeon-sdk/cvmx-warn.c standard
contrib/octeon-sdk/octeon-model.c standard