diff options
author | David Daney <david.daney@cavium.com> | 2011-11-22 15:47:00 +0100 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 2011-12-07 23:03:28 +0100 |
commit | af866496c7752d2c0bd97fcbb4627cac72aa9a64 (patch) | |
tree | 21611d769d7b84933e1d606cd26fccd3b5659f35 /arch/mips/cavium-octeon | |
parent | MIPS: Oceton: Update model detection code for new chips. (diff) | |
download | linux-af866496c7752d2c0bd97fcbb4627cac72aa9a64.tar.xz linux-af866496c7752d2c0bd97fcbb4627cac72aa9a64.zip |
MIPS: Octeon: Move some Ethernet support files out of staging.
Signed-off-by: David Daney <david.daney@cavium.com>
Cc: linux-mips@linux-mips.org
Cc: netdev@vger.kernel.org
Cc: devel@driverdev.osuosl.org
Acked-by: Greg Kroah-Hartman <gregkh@suse.de>
Patchwork: https://patchwork.linux-mips.org/patch/2942/
Patchwork: https://patchwork.linux-mips.org/patch/3012/
Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
Diffstat (limited to 'arch/mips/cavium-octeon')
18 files changed, 6416 insertions, 5 deletions
diff --git a/arch/mips/cavium-octeon/Kconfig b/arch/mips/cavium-octeon/Kconfig index cad555ebeca3..f9e275a50d98 100644 --- a/arch/mips/cavium-octeon/Kconfig +++ b/arch/mips/cavium-octeon/Kconfig @@ -86,10 +86,6 @@ config ARCH_SPARSEMEM_ENABLE def_bool y select SPARSEMEM_STATIC -config CAVIUM_OCTEON_HELPER - def_bool y - depends on OCTEON_ETHERNET || PCI - config IOMMU_HELPER bool diff --git a/arch/mips/cavium-octeon/executive/Makefile b/arch/mips/cavium-octeon/executive/Makefile index 7f41c5be2190..b6d6e841a984 100644 --- a/arch/mips/cavium-octeon/executive/Makefile +++ b/arch/mips/cavium-octeon/executive/Makefile @@ -10,5 +10,10 @@ # obj-y += cvmx-bootmem.o cvmx-l2c.o cvmx-sysinfo.o octeon-model.o +obj-y += cvmx-pko.o cvmx-spi.o cvmx-cmd-queue.o \ + cvmx-helper-board.o cvmx-helper.o cvmx-helper-xaui.o \ + cvmx-helper-rgmii.o cvmx-helper-sgmii.o cvmx-helper-npi.o \ + cvmx-helper-loop.o cvmx-helper-spi.o cvmx-helper-util.o \ + cvmx-interrupt-decodes.o cvmx-interrupt-rsl.o -obj-$(CONFIG_CAVIUM_OCTEON_HELPER) += cvmx-helper-errata.o cvmx-helper-jtag.o +obj-y += cvmx-helper-errata.o cvmx-helper-jtag.o diff --git a/arch/mips/cavium-octeon/executive/cvmx-cmd-queue.c b/arch/mips/cavium-octeon/executive/cvmx-cmd-queue.c new file mode 100644 index 000000000000..132bccc66a93 --- /dev/null +++ b/arch/mips/cavium-octeon/executive/cvmx-cmd-queue.c @@ -0,0 +1,306 @@ +/***********************license start*************** + * Author: Cavium Networks + * + * Contact: support@caviumnetworks.com + * This file is part of the OCTEON SDK + * + * Copyright (c) 2003-2008 Cavium Networks + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, Version 2, as + * published by the Free Software Foundation. + * + * This file is distributed in the hope that it will be useful, but + * AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or + * NONINFRINGEMENT. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License + * along with this file; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * or visit http://www.gnu.org/licenses/. + * + * This file may also be available under a different license from Cavium. + * Contact Cavium Networks for more information + ***********************license end**************************************/ + +/* + * Support functions for managing command queues used for + * various hardware blocks. + */ + +#include <linux/kernel.h> + +#include <asm/octeon/octeon.h> + +#include <asm/octeon/cvmx-config.h> +#include <asm/octeon/cvmx-fpa.h> +#include <asm/octeon/cvmx-cmd-queue.h> + +#include <asm/octeon/cvmx-npei-defs.h> +#include <asm/octeon/cvmx-pexp-defs.h> +#include <asm/octeon/cvmx-pko-defs.h> + +/** + * This application uses this pointer to access the global queue + * state. It points to a bootmem named block. + */ +__cvmx_cmd_queue_all_state_t *__cvmx_cmd_queue_state_ptr; + +/** + * Initialize the Global queue state pointer. + * + * Returns CVMX_CMD_QUEUE_SUCCESS or a failure code + */ +static cvmx_cmd_queue_result_t __cvmx_cmd_queue_init_state_ptr(void) +{ + char *alloc_name = "cvmx_cmd_queues"; +#if defined(CONFIG_CAVIUM_RESERVE32) && CONFIG_CAVIUM_RESERVE32 + extern uint64_t octeon_reserve32_memory; +#endif + + if (likely(__cvmx_cmd_queue_state_ptr)) + return CVMX_CMD_QUEUE_SUCCESS; + +#if defined(CONFIG_CAVIUM_RESERVE32) && CONFIG_CAVIUM_RESERVE32 + if (octeon_reserve32_memory) + __cvmx_cmd_queue_state_ptr = + cvmx_bootmem_alloc_named_range(sizeof(*__cvmx_cmd_queue_state_ptr), + octeon_reserve32_memory, + octeon_reserve32_memory + + (CONFIG_CAVIUM_RESERVE32 << + 20) - 1, 128, alloc_name); + else +#endif + __cvmx_cmd_queue_state_ptr = + cvmx_bootmem_alloc_named(sizeof(*__cvmx_cmd_queue_state_ptr), + 128, + alloc_name); + if (__cvmx_cmd_queue_state_ptr) + memset(__cvmx_cmd_queue_state_ptr, 0, + sizeof(*__cvmx_cmd_queue_state_ptr)); + else { + struct cvmx_bootmem_named_block_desc *block_desc = + cvmx_bootmem_find_named_block(alloc_name); + if (block_desc) + __cvmx_cmd_queue_state_ptr = + cvmx_phys_to_ptr(block_desc->base_addr); + else { + cvmx_dprintf + ("ERROR: cvmx_cmd_queue_initialize: Unable to get named block %s.\n", + alloc_name); + return CVMX_CMD_QUEUE_NO_MEMORY; + } + } + return CVMX_CMD_QUEUE_SUCCESS; +} + +/** + * Initialize a command queue for use. The initial FPA buffer is + * allocated and the hardware unit is configured to point to the + * new command queue. + * + * @queue_id: Hardware command queue to initialize. + * @max_depth: Maximum outstanding commands that can be queued. + * @fpa_pool: FPA pool the command queues should come from. + * @pool_size: Size of each buffer in the FPA pool (bytes) + * + * Returns CVMX_CMD_QUEUE_SUCCESS or a failure code + */ +cvmx_cmd_queue_result_t cvmx_cmd_queue_initialize(cvmx_cmd_queue_id_t queue_id, + int max_depth, int fpa_pool, + int pool_size) +{ + __cvmx_cmd_queue_state_t *qstate; + cvmx_cmd_queue_result_t result = __cvmx_cmd_queue_init_state_ptr(); + if (result != CVMX_CMD_QUEUE_SUCCESS) + return result; + + qstate = __cvmx_cmd_queue_get_state(queue_id); + if (qstate == NULL) + return CVMX_CMD_QUEUE_INVALID_PARAM; + + /* + * We artificially limit max_depth to 1<<20 words. It is an + * arbitrary limit. + */ + if (CVMX_CMD_QUEUE_ENABLE_MAX_DEPTH) { + if ((max_depth < 0) || (max_depth > 1 << 20)) + return CVMX_CMD_QUEUE_INVALID_PARAM; + } else if (max_depth != 0) + return CVMX_CMD_QUEUE_INVALID_PARAM; + + if ((fpa_pool < 0) || (fpa_pool > 7)) + return CVMX_CMD_QUEUE_INVALID_PARAM; + if ((pool_size < 128) || (pool_size > 65536)) + return CVMX_CMD_QUEUE_INVALID_PARAM; + + /* See if someone else has already initialized the queue */ + if (qstate->base_ptr_div128) { + if (max_depth != (int)qstate->max_depth) { + cvmx_dprintf("ERROR: cvmx_cmd_queue_initialize: " + "Queue already initialized with different " + "max_depth (%d).\n", + (int)qstate->max_depth); + return CVMX_CMD_QUEUE_INVALID_PARAM; + } + if (fpa_pool != qstate->fpa_pool) { + cvmx_dprintf("ERROR: cvmx_cmd_queue_initialize: " + "Queue already initialized with different " + "FPA pool (%u).\n", + qstate->fpa_pool); + return CVMX_CMD_QUEUE_INVALID_PARAM; + } + if ((pool_size >> 3) - 1 != qstate->pool_size_m1) { + cvmx_dprintf("ERROR: cvmx_cmd_queue_initialize: " + "Queue already initialized with different " + "FPA pool size (%u).\n", + (qstate->pool_size_m1 + 1) << 3); + return CVMX_CMD_QUEUE_INVALID_PARAM; + } + CVMX_SYNCWS; + return CVMX_CMD_QUEUE_ALREADY_SETUP; + } else { + union cvmx_fpa_ctl_status status; + void *buffer; + + status.u64 = cvmx_read_csr(CVMX_FPA_CTL_STATUS); + if (!status.s.enb) { + cvmx_dprintf("ERROR: cvmx_cmd_queue_initialize: " + "FPA is not enabled.\n"); + return CVMX_CMD_QUEUE_NO_MEMORY; + } + buffer = cvmx_fpa_alloc(fpa_pool); + if (buffer == NULL) { + cvmx_dprintf("ERROR: cvmx_cmd_queue_initialize: " + "Unable to allocate initial buffer.\n"); + return CVMX_CMD_QUEUE_NO_MEMORY; + } + + memset(qstate, 0, sizeof(*qstate)); + qstate->max_depth = max_depth; + qstate->fpa_pool = fpa_pool; + qstate->pool_size_m1 = (pool_size >> 3) - 1; + qstate->base_ptr_div128 = cvmx_ptr_to_phys(buffer) / 128; + /* + * We zeroed the now serving field so we need to also + * zero the ticket. + */ + __cvmx_cmd_queue_state_ptr-> + ticket[__cvmx_cmd_queue_get_index(queue_id)] = 0; + CVMX_SYNCWS; + return CVMX_CMD_QUEUE_SUCCESS; + } +} + +/** + * Shutdown a queue a free it's command buffers to the FPA. The + * hardware connected to the queue must be stopped before this + * function is called. + * + * @queue_id: Queue to shutdown + * + * Returns CVMX_CMD_QUEUE_SUCCESS or a failure code + */ +cvmx_cmd_queue_result_t cvmx_cmd_queue_shutdown(cvmx_cmd_queue_id_t queue_id) +{ + __cvmx_cmd_queue_state_t *qptr = __cvmx_cmd_queue_get_state(queue_id); + if (qptr == NULL) { + cvmx_dprintf("ERROR: cvmx_cmd_queue_shutdown: Unable to " + "get queue information.\n"); + return CVMX_CMD_QUEUE_INVALID_PARAM; + } + + if (cvmx_cmd_queue_length(queue_id) > 0) { + cvmx_dprintf("ERROR: cvmx_cmd_queue_shutdown: Queue still " + "has data in it.\n"); + return CVMX_CMD_QUEUE_FULL; + } + + __cvmx_cmd_queue_lock(queue_id, qptr); + if (qptr->base_ptr_div128) { + cvmx_fpa_free(cvmx_phys_to_ptr + ((uint64_t) qptr->base_ptr_div128 << 7), + qptr->fpa_pool, 0); + qptr->base_ptr_div128 = 0; + } + __cvmx_cmd_queue_unlock(qptr); + + return CVMX_CMD_QUEUE_SUCCESS; +} + +/** + * Return the number of command words pending in the queue. This + * function may be relatively slow for some hardware units. + * + * @queue_id: Hardware command queue to query + * + * Returns Number of outstanding commands + */ +int cvmx_cmd_queue_length(cvmx_cmd_queue_id_t queue_id) +{ + if (CVMX_ENABLE_PARAMETER_CHECKING) { + if (__cvmx_cmd_queue_get_state(queue_id) == NULL) + return CVMX_CMD_QUEUE_INVALID_PARAM; + } + + /* + * The cast is here so gcc with check that all values in the + * cvmx_cmd_queue_id_t enumeration are here. + */ + switch ((cvmx_cmd_queue_id_t) (queue_id & 0xff0000)) { + case CVMX_CMD_QUEUE_PKO_BASE: + /* + * FIXME: Need atomic lock on + * CVMX_PKO_REG_READ_IDX. Right now we are normally + * called with the queue lock, so that is a SLIGHT + * amount of protection. + */ + cvmx_write_csr(CVMX_PKO_REG_READ_IDX, queue_id & 0xffff); + if (OCTEON_IS_MODEL(OCTEON_CN3XXX)) { + union cvmx_pko_mem_debug9 debug9; + debug9.u64 = cvmx_read_csr(CVMX_PKO_MEM_DEBUG9); + return debug9.cn38xx.doorbell; + } else { + union cvmx_pko_mem_debug8 debug8; + debug8.u64 = cvmx_read_csr(CVMX_PKO_MEM_DEBUG8); + return debug8.cn58xx.doorbell; + } + case CVMX_CMD_QUEUE_ZIP: + case CVMX_CMD_QUEUE_DFA: + case CVMX_CMD_QUEUE_RAID: + /* FIXME: Implement other lengths */ + return 0; + case CVMX_CMD_QUEUE_DMA_BASE: + { + union cvmx_npei_dmax_counts dmax_counts; + dmax_counts.u64 = + cvmx_read_csr(CVMX_PEXP_NPEI_DMAX_COUNTS + (queue_id & 0x7)); + return dmax_counts.s.dbell; + } + case CVMX_CMD_QUEUE_END: + return CVMX_CMD_QUEUE_INVALID_PARAM; + } + return CVMX_CMD_QUEUE_INVALID_PARAM; +} + +/** + * Return the command buffer to be written to. The purpose of this + * function is to allow CVMX routine access t othe low level buffer + * for initial hardware setup. User applications should not call this + * function directly. + * + * @queue_id: Command queue to query + * + * Returns Command buffer or NULL on failure + */ +void *cvmx_cmd_queue_buffer(cvmx_cmd_queue_id_t queue_id) +{ + __cvmx_cmd_queue_state_t *qptr = __cvmx_cmd_queue_get_state(queue_id); + if (qptr && qptr->base_ptr_div128) + return cvmx_phys_to_ptr((uint64_t) qptr->base_ptr_div128 << 7); + else + return NULL; +} diff --git a/arch/mips/cavium-octeon/executive/cvmx-fpa.c b/arch/mips/cavium-octeon/executive/cvmx-fpa.c new file mode 100644 index 000000000000..ad44b8bd8057 --- /dev/null +++ b/arch/mips/cavium-octeon/executive/cvmx-fpa.c @@ -0,0 +1,183 @@ +/***********************license start*************** + * Author: Cavium Networks + * + * Contact: support@caviumnetworks.com + * This file is part of the OCTEON SDK + * + * Copyright (c) 2003-2008 Cavium Networks + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, Version 2, as + * published by the Free Software Foundation. + * + * This file is distributed in the hope that it will be useful, but + * AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or + * NONINFRINGEMENT. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License + * along with this file; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * or visit http://www.gnu.org/licenses/. + * + * This file may also be available under a different license from Cavium. + * Contact Cavium Networks for more information + ***********************license end**************************************/ + +/** + * @file + * + * Support library for the hardware Free Pool Allocator. + * + * + */ + +#include "cvmx-config.h" +#include "cvmx.h" +#include "cvmx-fpa.h" +#include "cvmx-ipd.h" + +/** + * Current state of all the pools. Use access functions + * instead of using it directly. + */ +CVMX_SHARED cvmx_fpa_pool_info_t cvmx_fpa_pool_info[CVMX_FPA_NUM_POOLS]; + +/** + * Setup a FPA pool to control a new block of memory. The + * buffer pointer must be a physical address. + * + * @pool: Pool to initialize + * 0 <= pool < 8 + * @name: Constant character string to name this pool. + * String is not copied. + * @buffer: Pointer to the block of memory to use. This must be + * accessible by all processors and external hardware. + * @block_size: Size for each block controlled by the FPA + * @num_blocks: Number of blocks + * + * Returns 0 on Success, + * -1 on failure + */ +int cvmx_fpa_setup_pool(uint64_t pool, const char *name, void *buffer, + uint64_t block_size, uint64_t num_blocks) +{ + char *ptr; + if (!buffer) { + cvmx_dprintf + ("ERROR: cvmx_fpa_setup_pool: NULL buffer pointer!\n"); + return -1; + } + if (pool >= CVMX_FPA_NUM_POOLS) { + cvmx_dprintf("ERROR: cvmx_fpa_setup_pool: Illegal pool!\n"); + return -1; + } + + if (block_size < CVMX_FPA_MIN_BLOCK_SIZE) { + cvmx_dprintf + ("ERROR: cvmx_fpa_setup_pool: Block size too small.\n"); + return -1; + } + + if (((unsigned long)buffer & (CVMX_FPA_ALIGNMENT - 1)) != 0) { + cvmx_dprintf + ("ERROR: cvmx_fpa_setup_pool: Buffer not aligned properly.\n"); + return -1; + } + + cvmx_fpa_pool_info[pool].name = name; + cvmx_fpa_pool_info[pool].size = block_size; + cvmx_fpa_pool_info[pool].starting_element_count = num_blocks; + cvmx_fpa_pool_info[pool].base = buffer; + + ptr = (char *)buffer; + while (num_blocks--) { + cvmx_fpa_free(ptr, pool, 0); + ptr += block_size; + } + return 0; +} + +/** + * Shutdown a Memory pool and validate that it had all of + * the buffers originally placed in it. + * + * @pool: Pool to shutdown + * Returns Zero on success + * - Positive is count of missing buffers + * - Negative is too many buffers or corrupted pointers + */ +uint64_t cvmx_fpa_shutdown_pool(uint64_t pool) +{ + uint64_t errors = 0; + uint64_t count = 0; + uint64_t base = cvmx_ptr_to_phys(cvmx_fpa_pool_info[pool].base); + uint64_t finish = + base + + cvmx_fpa_pool_info[pool].size * + cvmx_fpa_pool_info[pool].starting_element_count; + void *ptr; + uint64_t address; + + count = 0; + do { + ptr = cvmx_fpa_alloc(pool); + if (ptr) + address = cvmx_ptr_to_phys(ptr); + else + address = 0; + if (address) { + if ((address >= base) && (address < finish) && + (((address - + base) % cvmx_fpa_pool_info[pool].size) == 0)) { + count++; + } else { + cvmx_dprintf + ("ERROR: cvmx_fpa_shutdown_pool: Illegal address 0x%llx in pool %s(%d)\n", + (unsigned long long)address, + cvmx_fpa_pool_info[pool].name, (int)pool); + errors++; + } + } + } while (address); + +#ifdef CVMX_ENABLE_PKO_FUNCTIONS + if (pool == 0) + cvmx_ipd_free_ptr(); +#endif + + if (errors) { + cvmx_dprintf + ("ERROR: cvmx_fpa_shutdown_pool: Pool %s(%d) started at 0x%llx, ended at 0x%llx, with a step of 0x%llx\n", + cvmx_fpa_pool_info[pool].name, (int)pool, + (unsigned long long)base, (unsigned long long)finish, + (unsigned long long)cvmx_fpa_pool_info[pool].size); + return -errors; + } else + return 0; +} + +uint64_t cvmx_fpa_get_block_size(uint64_t pool) +{ + switch (pool) { + case 0: + return CVMX_FPA_POOL_0_SIZE; + case 1: + return CVMX_FPA_POOL_1_SIZE; + case 2: + return CVMX_FPA_POOL_2_SIZE; + case 3: + return CVMX_FPA_POOL_3_SIZE; + case 4: + return CVMX_FPA_POOL_4_SIZE; + case 5: + return CVMX_FPA_POOL_5_SIZE; + case 6: + return CVMX_FPA_POOL_6_SIZE; + case 7: + return CVMX_FPA_POOL_7_SIZE; + default: + return 0; + } +} diff --git a/arch/mips/cavium-octeon/executive/cvmx-helper-board.c b/arch/mips/cavium-octeon/executive/cvmx-helper-board.c new file mode 100644 index 000000000000..71590a35163b --- /dev/null +++ b/arch/mips/cavium-octeon/executive/cvmx-helper-board.c @@ -0,0 +1,691 @@ +/***********************license start*************** + * Author: Cavium Networks + * + * Contact: support@caviumnetworks.com + * This file is part of the OCTEON SDK + * + * Copyright (c) 2003-2008 Cavium Networks + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, Version 2, as + * published by the Free Software Foundation. + * + * This file is distributed in the hope that it will be useful, but + * AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or + * NONINFRINGEMENT. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License + * along with this file; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * or visit http://www.gnu.org/licenses/. + * + * This file may also be available under a different license from Cavium. + * Contact Cavium Networks for more information + ***********************license end**************************************/ + +/* + * + * Helper functions to abstract board specific data about + * network ports from the rest of the cvmx-helper files. + */ + +#include <asm/octeon/octeon.h> +#include <asm/octeon/cvmx-bootinfo.h> + +#include <asm/octeon/cvmx-config.h> + +#include <asm/octeon/cvmx-mdio.h> + +#include <asm/octeon/cvmx-helper.h> +#include <asm/octeon/cvmx-helper-util.h> +#include <asm/octeon/cvmx-helper-board.h> + +#include <asm/octeon/cvmx-gmxx-defs.h> +#include <asm/octeon/cvmx-asxx-defs.h> + +/** + * cvmx_override_board_link_get(int ipd_port) is a function + * pointer. It is meant to allow customization of the process of + * talking to a PHY to determine link speed. It is called every + * time a PHY must be polled for link status. Users should set + * this pointer to a function before calling any cvmx-helper + * operations. + */ +cvmx_helper_link_info_t(*cvmx_override_board_link_get) (int ipd_port) = + NULL; + +/** + * Return the MII PHY address associated with the given IPD + * port. A result of -1 means there isn't a MII capable PHY + * connected to this port. On chips supporting multiple MII + * busses the bus number is encoded in bits <15:8>. + * + * This function must be modified for every new Octeon board. + * Internally it uses switch statements based on the cvmx_sysinfo + * data to determine board types and revisions. It replies on the + * fact that every Octeon board receives a unique board type + * enumeration from the bootloader. + * + * @ipd_port: Octeon IPD port to get the MII address for. + * + * Returns MII PHY address and bus number or -1. + */ +int cvmx_helper_board_get_mii_address(int ipd_port) +{ + switch (cvmx_sysinfo_get()->board_type) { + case CVMX_BOARD_TYPE_SIM: + /* Simulator doesn't have MII */ + return -1; + case CVMX_BOARD_TYPE_EBT3000: + case CVMX_BOARD_TYPE_EBT5800: + case CVMX_BOARD_TYPE_THUNDER: + case CVMX_BOARD_TYPE_NICPRO2: + /* Interface 0 is SPI4, interface 1 is RGMII */ + if ((ipd_port >= 16) && (ipd_port < 20)) + return ipd_port - 16; + else + return -1; + case CVMX_BOARD_TYPE_KODAMA: + case CVMX_BOARD_TYPE_EBH3100: + case CVMX_BOARD_TYPE_HIKARI: + case CVMX_BOARD_TYPE_CN3010_EVB_HS5: + case CVMX_BOARD_TYPE_CN3005_EVB_HS5: + case CVMX_BOARD_TYPE_CN3020_EVB_HS5: + /* + * Port 0 is WAN connected to a PHY, Port 1 is GMII + * connected to a switch + */ + if (ipd_port == 0) + return 4; + else if (ipd_port == 1) + return 9; + else + return -1; + case CVMX_BOARD_TYPE_NAC38: + /* Board has 8 RGMII ports PHYs are 0-7 */ + if ((ipd_port >= 0) && (ipd_port < 4)) + return ipd_port; + else if ((ipd_port >= 16) && (ipd_port < 20)) + return ipd_port - 16 + 4; + else + return -1; + case CVMX_BOARD_TYPE_EBH3000: + /* Board has dual SPI4 and no PHYs */ + return -1; + case CVMX_BOARD_TYPE_EBH5200: + case CVMX_BOARD_TYPE_EBH5201: + case CVMX_BOARD_TYPE_EBT5200: + /* + * Board has 4 SGMII ports. The PHYs start right after the MII + * ports MII0 = 0, MII1 = 1, SGMII = 2-5. + */ + if ((ipd_port >= 0) && (ipd_port < 4)) + return ipd_port + 2; + else + return -1; + case CVMX_BOARD_TYPE_EBH5600: + case CVMX_BOARD_TYPE_EBH5601: + case CVMX_BOARD_TYPE_EBH5610: + /* + * Board has 8 SGMII ports. 4 connect out, two connect + * to a switch, and 2 loop to each other + */ + if ((ipd_port >= 0) && (ipd_port < 4)) + return ipd_port + 1; + else + return -1; + case CVMX_BOARD_TYPE_CUST_NB5: + if (ipd_port == 2) + return 4; + else + return -1; + case CVMX_BOARD_TYPE_NIC_XLE_4G: + /* Board has 4 SGMII ports. connected QLM3(interface 1) */ + if ((ipd_port >= 16) && (ipd_port < 20)) + return ipd_port - 16 + 1; + else + return -1; + case CVMX_BOARD_TYPE_BBGW_REF: + /* + * No PHYs are connected to Octeon, everything is + * through switch. + */ + return -1; + + case CVMX_BOARD_TYPE_CUST_WSX16: + if (ipd_port >= 0 && ipd_port <= 3) + return ipd_port; + else if (ipd_port >= 16 && ipd_port <= 19) + return ipd_port - 16 + 4; + else + return -1; + } + + /* Some unknown board. Somebody forgot to update this function... */ + cvmx_dprintf + ("cvmx_helper_board_get_mii_address: Unknown board type %d\n", + cvmx_sysinfo_get()->board_type); + return -1; +} + +/** + * This function is the board specific method of determining an + * ethernet ports link speed. Most Octeon boards have Marvell PHYs + * and are handled by the fall through case. This function must be + * updated for boards that don't have the normal Marvell PHYs. + * + * This function must be modified for every new Octeon board. + * Internally it uses switch statements based on the cvmx_sysinfo + * data to determine board types and revisions. It relies on the + * fact that every Octeon board receives a unique board type + * enumeration from the bootloader. + * + * @ipd_port: IPD input port associated with the port we want to get link + * status for. + * + * Returns The ports link status. If the link isn't fully resolved, this must + * return zero. + */ +cvmx_helper_link_info_t __cvmx_helper_board_link_get(int ipd_port) +{ + cvmx_helper_link_info_t result; + int phy_addr; + int is_broadcom_phy = 0; + + /* Give the user a chance to override the processing of this function */ + if (cvmx_override_board_link_get) + return cvmx_override_board_link_get(ipd_port); + + /* Unless we fix it later, all links are defaulted to down */ + result.u64 = 0; + + /* + * This switch statement should handle all ports that either don't use + * Marvell PHYS, or don't support in-band status. + */ + switch (cvmx_sysinfo_get()->board_type) { + case CVMX_BOARD_TYPE_SIM: + /* The simulator gives you a simulated 1Gbps full duplex link */ + result.s.link_up = 1; + result.s.full_duplex = 1; + result.s.speed = 1000; + return result; + case CVMX_BOARD_TYPE_EBH3100: + case CVMX_BOARD_TYPE_CN3010_EVB_HS5: + case CVMX_BOARD_TYPE_CN3005_EVB_HS5: + case CVMX_BOARD_TYPE_CN3020_EVB_HS5: + /* Port 1 on these boards is always Gigabit */ + if (ipd_port == 1) { + result.s.link_up = 1; + result.s.full_duplex = 1; + result.s.speed = 1000; + return result; + } + /* Fall through to the generic code below */ + break; + case CVMX_BOARD_TYPE_CUST_NB5: + /* Port 1 on these boards is always Gigabit */ + if (ipd_port == 1) { + result.s.link_up = 1; + result.s.full_duplex = 1; + result.s.speed = 1000; + return result; + } else /* The other port uses a broadcom PHY */ + is_broadcom_phy = 1; + break; + case CVMX_BOARD_TYPE_BBGW_REF: + /* Port 1 on these boards is always Gigabit */ + if (ipd_port == 2) { + /* Port 2 is not hooked up */ + result.u64 = 0; + return result; + } else { + /* Ports 0 and 1 connect to the switch */ + result.s.link_up = 1; + result.s.full_duplex = 1; + result.s.speed = 1000; + return result; + } + break; + } + + phy_addr = cvmx_helper_board_get_mii_address(ipd_port); + if (phy_addr != -1) { + if (is_broadcom_phy) { + /* + * Below we are going to read SMI/MDIO + * register 0x19 which works on Broadcom + * parts + */ + int phy_status = + cvmx_mdio_read(phy_addr >> 8, phy_addr & 0xff, + 0x19); + switch ((phy_status >> 8) & 0x7) { + case 0: + result.u64 = 0; + break; + case 1: + result.s.link_up = 1; + result.s.full_duplex = 0; + result.s.speed = 10; + break; + case 2: + result.s.link_up = 1; + result.s.full_duplex = 1; + result.s.speed = 10; + break; + case 3: + result.s.link_up = 1; + result.s.full_duplex = 0; + result.s.speed = 100; + break; + case 4: + result.s.link_up = 1; + result.s.full_duplex = 1; + result.s.speed = 100; + break; + case 5: + result.s.link_up = 1; + result.s.full_duplex = 1; + result.s.speed = 100; + break; + case 6: + result.s.link_up = 1; + result.s.full_duplex = 0; + result.s.speed = 1000; + break; + case 7: + result.s.link_up = 1; + result.s.full_duplex = 1; + result.s.speed = 1000; + break; + } + } else { + /* + * This code assumes we are using a Marvell + * Gigabit PHY. All the speed information can + * be read from register 17 in one + * go. Somebody using a different PHY will + * need to handle it above in the board + * specific area. + */ + int phy_status = + cvmx_mdio_read(phy_addr >> 8, phy_addr & 0xff, 17); + + /* + * If the resolve bit 11 isn't set, see if + * autoneg is turned off (bit 12, reg 0). The + * resolve bit doesn't get set properly when + * autoneg is off, so force it. + */ + if ((phy_status & (1 << 11)) == 0) { + int auto_status = + cvmx_mdio_read(phy_addr >> 8, + phy_addr & 0xff, 0); + if ((auto_status & (1 << 12)) == 0) + phy_status |= 1 << 11; + } + + /* + * Only return a link if the PHY has finished + * auto negotiation and set the resolved bit + * (bit 11) + */ + if (phy_status & (1 << 11)) { + result.s.link_up = 1; + result.s.full_duplex = ((phy_status >> 13) & 1); + switch ((phy_status >> 14) & 3) { + case 0: /* 10 Mbps */ + result.s.speed = 10; + break; + case 1: /* 100 Mbps */ + result.s.speed = 100; + break; + case 2: /* 1 Gbps */ + result.s.speed = 1000; + break; + case 3: /* Illegal */ + result.u64 = 0; + break; + } + } + } + } else if (OCTEON_IS_MODEL(OCTEON_CN3XXX) + || OCTEON_IS_MODEL(OCTEON_CN58XX) + || OCTEON_IS_MODEL(OCTEON_CN50XX)) { + /* + * We don't have a PHY address, so attempt to use + * in-band status. It is really important that boards + * not supporting in-band status never get + * here. Reading broken in-band status tends to do bad + * things + */ + union cvmx_gmxx_rxx_rx_inbnd inband_status; + int interface = cvmx_helper_get_interface_num(ipd_port); + int index = cvmx_helper_get_interface_index_num(ipd_port); + inband_status.u64 = + cvmx_read_csr(CVMX_GMXX_RXX_RX_INBND(index, interface)); + + result.s.link_up = inband_status.s.status; + result.s.full_duplex = inband_status.s.duplex; + switch (inband_status.s.speed) { + case 0: /* 10 Mbps */ + result.s.speed = 10; + break; + case 1: /* 100 Mbps */ + result.s.speed = 100; + break; + case 2: /* 1 Gbps */ + result.s.speed = 1000; + break; + case 3: /* Illegal */ + result.u64 = 0; + break; + } + } else { + /* + * We don't have a PHY address and we don't have + * in-band status. There is no way to determine the + * link speed. Return down assuming this port isn't + * wired + */ + result.u64 = 0; + } + + /* If link is down, return all fields as zero. */ + if (!result.s.link_up) + result.u64 = 0; + + return result; +} + +/** + * This function as a board specific method of changing the PHY + * speed, duplex, and auto-negotiation. This programs the PHY and + * not Octeon. This can be used to force Octeon's links to + * specific settings. + * + * @phy_addr: The address of the PHY to program + * @enable_autoneg: + * Non zero if you want to enable auto-negotiation. + * @link_info: Link speed to program. If the speed is zero and auto-negotiation + * is enabled, all possible negotiation speeds are advertised. + * + * Returns Zero on success, negative on failure + */ +int cvmx_helper_board_link_set_phy(int phy_addr, + cvmx_helper_board_set_phy_link_flags_types_t + link_flags, + cvmx_helper_link_info_t link_info) +{ + + /* Set the flow control settings based on link_flags */ + if ((link_flags & set_phy_link_flags_flow_control_mask) != + set_phy_link_flags_flow_control_dont_touch) { + cvmx_mdio_phy_reg_autoneg_adver_t reg_autoneg_adver; + reg_autoneg_adver.u16 = + cvmx_mdio_read(phy_addr >> 8, phy_addr & 0xff, + CVMX_MDIO_PHY_REG_AUTONEG_ADVER); + reg_autoneg_adver.s.asymmetric_pause = + (link_flags & set_phy_link_flags_flow_control_mask) == + set_phy_link_flags_flow_control_enable; + reg_autoneg_adver.s.pause = + (link_flags & set_phy_link_flags_flow_control_mask) == + set_phy_link_flags_flow_control_enable; + cvmx_mdio_write(phy_addr >> 8, phy_addr & 0xff, + CVMX_MDIO_PHY_REG_AUTONEG_ADVER, + reg_autoneg_adver.u16); + } + + /* If speed isn't set and autoneg is on advertise all supported modes */ + if ((link_flags & set_phy_link_flags_autoneg) + && (link_info.s.speed == 0)) { + cvmx_mdio_phy_reg_control_t reg_control; + cvmx_mdio_phy_reg_status_t reg_status; + cvmx_mdio_phy_reg_autoneg_adver_t reg_autoneg_adver; + cvmx_mdio_phy_reg_extended_status_t reg_extended_status; + cvmx_mdio_phy_reg_control_1000_t reg_control_1000; + + reg_status.u16 = + cvmx_mdio_read(phy_addr >> 8, phy_addr & 0xff, + CVMX_MDIO_PHY_REG_STATUS); + reg_autoneg_adver.u16 = + cvmx_mdio_read(phy_addr >> 8, phy_addr & 0xff, + CVMX_MDIO_PHY_REG_AUTONEG_ADVER); + reg_autoneg_adver.s.advert_100base_t4 = + reg_status.s.capable_100base_t4; + reg_autoneg_adver.s.advert_10base_tx_full = + reg_status.s.capable_10_full; + reg_autoneg_adver.s.advert_10base_tx_half = + reg_status.s.capable_10_half; + reg_autoneg_adver.s.advert_100base_tx_full = + reg_status.s.capable_100base_x_full; + reg_autoneg_adver.s.advert_100base_tx_half = + reg_status.s.capable_100base_x_half; + cvmx_mdio_write(phy_addr >> 8, phy_addr & 0xff, + CVMX_MDIO_PHY_REG_AUTONEG_ADVER, + reg_autoneg_adver.u16); + if (reg_status.s.capable_extended_status) { + reg_extended_status.u16 = + cvmx_mdio_read(phy_addr >> 8, phy_addr & 0xff, + CVMX_MDIO_PHY_REG_EXTENDED_STATUS); + reg_control_1000.u16 = + cvmx_mdio_read(phy_addr >> 8, phy_addr & 0xff, + CVMX_MDIO_PHY_REG_CONTROL_1000); + reg_control_1000.s.advert_1000base_t_full = + reg_extended_status.s.capable_1000base_t_full; + reg_control_1000.s.advert_1000base_t_half = + reg_extended_status.s.capable_1000base_t_half; + cvmx_mdio_write(phy_addr >> 8, phy_addr & 0xff, + CVMX_MDIO_PHY_REG_CONTROL_1000, + reg_control_1000.u16); + } + reg_control.u16 = + cvmx_mdio_read(phy_addr >> 8, phy_addr & 0xff, + CVMX_MDIO_PHY_REG_CONTROL); + reg_control.s.autoneg_enable = 1; + reg_control.s.restart_autoneg = 1; + cvmx_mdio_write(phy_addr >> 8, phy_addr & 0xff, + CVMX_MDIO_PHY_REG_CONTROL, reg_control.u16); + } else if ((link_flags & set_phy_link_flags_autoneg)) { + cvmx_mdio_phy_reg_control_t reg_control; + cvmx_mdio_phy_reg_status_t reg_status; + cvmx_mdio_phy_reg_autoneg_adver_t reg_autoneg_adver; + cvmx_mdio_phy_reg_control_1000_t reg_control_1000; + + reg_status.u16 = + cvmx_mdio_read(phy_addr >> 8, phy_addr & 0xff, + CVMX_MDIO_PHY_REG_STATUS); + reg_autoneg_adver.u16 = + cvmx_mdio_read(phy_addr >> 8, phy_addr & 0xff, + CVMX_MDIO_PHY_REG_AUTONEG_ADVER); + reg_autoneg_adver.s.advert_100base_t4 = 0; + reg_autoneg_adver.s.advert_10base_tx_full = 0; + reg_autoneg_adver.s.advert_10base_tx_half = 0; + reg_autoneg_adver.s.advert_100base_tx_full = 0; + reg_autoneg_adver.s.advert_100base_tx_half = 0; + if (reg_status.s.capable_extended_status) { + reg_control_1000.u16 = + cvmx_mdio_read(phy_addr >> 8, phy_addr & 0xff, + CVMX_MDIO_PHY_REG_CONTROL_1000); + reg_control_1000.s.advert_1000base_t_full = 0; + reg_control_1000.s.advert_1000base_t_half = 0; + } + switch (link_info.s.speed) { + case 10: + reg_autoneg_adver.s.advert_10base_tx_full = + link_info.s.full_duplex; + reg_autoneg_adver.s.advert_10base_tx_half = + !link_info.s.full_duplex; + break; + case 100: + reg_autoneg_adver.s.advert_100base_tx_full = + link_info.s.full_duplex; + reg_autoneg_adver.s.advert_100base_tx_half = + !link_info.s.full_duplex; + break; + case 1000: + reg_control_1000.s.advert_1000base_t_full = + link_info.s.full_duplex; + reg_control_1000.s.advert_1000base_t_half = + !link_info.s.full_duplex; + break; + } + cvmx_mdio_write(phy_addr >> 8, phy_addr & 0xff, + CVMX_MDIO_PHY_REG_AUTONEG_ADVER, + reg_autoneg_adver.u16); + if (reg_status.s.capable_extended_status) + cvmx_mdio_write(phy_addr >> 8, phy_addr & 0xff, + CVMX_MDIO_PHY_REG_CONTROL_1000, + reg_control_1000.u16); + reg_control.u16 = + cvmx_mdio_read(phy_addr >> 8, phy_addr & 0xff, + CVMX_MDIO_PHY_REG_CONTROL); + reg_control.s.autoneg_enable = 1; + reg_control.s.restart_autoneg = 1; + cvmx_mdio_write(phy_addr >> 8, phy_addr & 0xff, + CVMX_MDIO_PHY_REG_CONTROL, reg_control.u16); + } else { + cvmx_mdio_phy_reg_control_t reg_control; + reg_control.u16 = + cvmx_mdio_read(phy_addr >> 8, phy_addr & 0xff, + CVMX_MDIO_PHY_REG_CONTROL); + reg_control.s.autoneg_enable = 0; + reg_control.s.restart_autoneg = 1; + reg_control.s.duplex = link_info.s.full_duplex; + if (link_info.s.speed == 1000) { + reg_control.s.speed_msb = 1; + reg_control.s.speed_lsb = 0; + } else if (link_info.s.speed == 100) { + reg_control.s.speed_msb = 0; + reg_control.s.speed_lsb = 1; + } else if (link_info.s.speed == 10) { + reg_control.s.speed_msb = 0; + reg_control.s.speed_lsb = 0; + } + cvmx_mdio_write(phy_addr >> 8, phy_addr & 0xff, + CVMX_MDIO_PHY_REG_CONTROL, reg_control.u16); + } + return 0; +} + +/** + * This function is called by cvmx_helper_interface_probe() after it + * determines the number of ports Octeon can support on a specific + * interface. This function is the per board location to override + * this value. It is called with the number of ports Octeon might + * support and should return the number of actual ports on the + * board. + * + * This function must be modifed for every new Octeon board. + * Internally it uses switch statements based on the cvmx_sysinfo + * data to determine board types and revisions. It relys on the + * fact that every Octeon board receives a unique board type + * enumeration from the bootloader. + * + * @interface: Interface to probe + * @supported_ports: + * Number of ports Octeon supports. + * + * Returns Number of ports the actual board supports. Many times this will + * simple be "support_ports". + */ +int __cvmx_helper_board_interface_probe(int interface, int supported_ports) +{ + switch (cvmx_sysinfo_get()->board_type) { + case CVMX_BOARD_TYPE_CN3005_EVB_HS5: + if (interface == 0) + return 2; + break; + case CVMX_BOARD_TYPE_BBGW_REF: + if (interface == 0) + return 2; + break; + case CVMX_BOARD_TYPE_NIC_XLE_4G: + if (interface == 0) + return 0; + break; + /* The 2nd interface on the EBH5600 is connected to the Marvel switch, + which we don't support. Disable ports connected to it */ + case CVMX_BOARD_TYPE_EBH5600: + if (interface == 1) + return 0; + break; + } + return supported_ports; +} + +/** + * Enable packet input/output from the hardware. This function is + * called after by cvmx_helper_packet_hardware_enable() to + * perform board specific initialization. For most boards + * nothing is needed. + * + * @interface: Interface to enable + * + * Returns Zero on success, negative on failure + */ +int __cvmx_helper_board_hardware_enable(int interface) +{ + if (cvmx_sysinfo_get()->board_type == CVMX_BOARD_TYPE_CN3005_EVB_HS5) { + if (interface == 0) { + /* Different config for switch port */ + cvmx_write_csr(CVMX_ASXX_TX_CLK_SETX(1, interface), 0); + cvmx_write_csr(CVMX_ASXX_RX_CLK_SETX(1, interface), 0); + /* + * Boards with gigabit WAN ports need a + * different setting that is compatible with + * 100 Mbit settings + */ + cvmx_write_csr(CVMX_ASXX_TX_CLK_SETX(0, interface), + 0xc); + cvmx_write_csr(CVMX_ASXX_RX_CLK_SETX(0, interface), + 0xc); + } + } else if (cvmx_sysinfo_get()->board_type == + CVMX_BOARD_TYPE_CN3010_EVB_HS5) { + /* + * Broadcom PHYs require differnet ASX + * clocks. Unfortunately many boards don't define a + * new board Id and simply mangle the + * CN3010_EVB_HS5 + */ + if (interface == 0) { + /* + * Some boards use a hacked up bootloader that + * identifies them as CN3010_EVB_HS5 + * evaluation boards. This leads to all kinds + * of configuration problems. Detect one + * case, and print warning, while trying to do + * the right thing. + */ + int phy_addr = cvmx_helper_board_get_mii_address(0); + if (phy_addr != -1) { + int phy_identifier = + cvmx_mdio_read(phy_addr >> 8, + phy_addr & 0xff, 0x2); + /* Is it a Broadcom PHY? */ + if (phy_identifier == 0x0143) { + cvmx_dprintf("\n"); + cvmx_dprintf("ERROR:\n"); + cvmx_dprintf + ("ERROR: Board type is CVMX_BOARD_TYPE_CN3010_EVB_HS5, but Broadcom PHY found.\n"); + cvmx_dprintf + ("ERROR: The board type is mis-configured, and software malfunctions are likely.\n"); + cvmx_dprintf + ("ERROR: All boards require a unique board type to identify them.\n"); + cvmx_dprintf("ERROR:\n"); + cvmx_dprintf("\n"); + cvmx_wait(1000000000); + cvmx_write_csr(CVMX_ASXX_RX_CLK_SETX + (0, interface), 5); + cvmx_write_csr(CVMX_ASXX_TX_CLK_SETX + (0, interface), 5); + } + } + } + } + return 0; +} diff --git a/arch/mips/cavium-octeon/executive/cvmx-helper-fpa.c b/arch/mips/cavium-octeon/executive/cvmx-helper-fpa.c new file mode 100644 index 000000000000..c239e5f4ab9a --- /dev/null +++ b/arch/mips/cavium-octeon/executive/cvmx-helper-fpa.c @@ -0,0 +1,243 @@ +/***********************license start*************** + * Author: Cavium Networks + * + * Contact: support@caviumnetworks.com + * This file is part of the OCTEON SDK + * + * Copyright (c) 2003-2008 Cavium Networks + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, Version 2, as + * published by the Free Software Foundation. + * + * This file is distributed in the hope that it will be useful, but + * AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or + * NONINFRINGEMENT. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License + * along with this file; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * or visit http://www.gnu.org/licenses/. + * + * This file may also be available under a different license from Cavium. + * Contact Cavium Networks for more information + ***********************license end**************************************/ + +/** + * @file + * + * Helper functions for FPA setup. + * + */ +#include "executive-config.h" +#include "cvmx-config.h" +#include "cvmx.h" +#include "cvmx-bootmem.h" +#include "cvmx-fpa.h" +#include "cvmx-helper-fpa.h" + +/** + * Allocate memory for and initialize a single FPA pool. + * + * @pool: Pool to initialize + * @buffer_size: Size of buffers to allocate in bytes + * @buffers: Number of buffers to put in the pool. Zero is allowed + * @name: String name of the pool for debugging purposes + * Returns Zero on success, non-zero on failure + */ +static int __cvmx_helper_initialize_fpa_pool(int pool, uint64_t buffer_size, + uint64_t buffers, const char *name) +{ + uint64_t current_num; + void *memory; + uint64_t align = CVMX_CACHE_LINE_SIZE; + + /* + * Align the allocation so that power of 2 size buffers are + * naturally aligned. + */ + while (align < buffer_size) + align = align << 1; + + if (buffers == 0) + return 0; + + current_num = cvmx_read_csr(CVMX_FPA_QUEX_AVAILABLE(pool)); + if (current_num) { + cvmx_dprintf("Fpa pool %d(%s) already has %llu buffers. " + "Skipping setup.\n", + pool, name, (unsigned long long)current_num); + return 0; + } + + memory = cvmx_bootmem_alloc(buffer_size * buffers, align); + if (memory == NULL) { + cvmx_dprintf("Out of memory initializing fpa pool %d(%s).\n", + pool, name); + return -1; + } + cvmx_fpa_setup_pool(pool, name, memory, buffer_size, buffers); + return 0; +} + +/** + * Allocate memory and initialize the FPA pools using memory + * from cvmx-bootmem. Specifying zero for the number of + * buffers will cause that FPA pool to not be setup. This is + * useful if you aren't using some of the hardware and want + * to save memory. Use cvmx_helper_initialize_fpa instead of + * this function directly. + * + * @pip_pool: Should always be CVMX_FPA_PACKET_POOL + * @pip_size: Should always be CVMX_FPA_PACKET_POOL_SIZE + * @pip_buffers: + * Number of packet buffers. + * @wqe_pool: Should always be CVMX_FPA_WQE_POOL + * @wqe_size: Should always be CVMX_FPA_WQE_POOL_SIZE + * @wqe_entries: + * Number of work queue entries + * @pko_pool: Should always be CVMX_FPA_OUTPUT_BUFFER_POOL + * @pko_size: Should always be CVMX_FPA_OUTPUT_BUFFER_POOL_SIZE + * @pko_buffers: + * PKO Command buffers. You should at minimum have two per + * each PKO queue. + * @tim_pool: Should always be CVMX_FPA_TIMER_POOL + * @tim_size: Should always be CVMX_FPA_TIMER_POOL_SIZE + * @tim_buffers: + * TIM ring buffer command queues. At least two per timer bucket + * is recommened. + * @dfa_pool: Should always be CVMX_FPA_DFA_POOL + * @dfa_size: Should always be CVMX_FPA_DFA_POOL_SIZE + * @dfa_buffers: + * DFA command buffer. A relatively small (32 for example) + * number should work. + * Returns Zero on success, non-zero if out of memory + */ +static int __cvmx_helper_initialize_fpa(int pip_pool, int pip_size, + int pip_buffers, int wqe_pool, + int wqe_size, int wqe_entries, + int pko_pool, int pko_size, + int pko_buffers, int tim_pool, + int tim_size, int tim_buffers, + int dfa_pool, int dfa_size, + int dfa_buffers) +{ + int status; + + cvmx_fpa_enable(); + + if ((pip_buffers > 0) && (pip_buffers <= 64)) + cvmx_dprintf + ("Warning: %d packet buffers may not be enough for hardware" + " prefetch. 65 or more is recommended.\n", pip_buffers); + + if (pip_pool >= 0) { + status = + __cvmx_helper_initialize_fpa_pool(pip_pool, pip_size, + pip_buffers, + "Packet Buffers"); + if (status) + return status; + } + + if (wqe_pool >= 0) { + status = + __cvmx_helper_initialize_fpa_pool(wqe_pool, wqe_size, + wqe_entries, + "Work Queue Entries"); + if (status) + return status; + } + + if (pko_pool >= 0) { + status = + __cvmx_helper_initialize_fpa_pool(pko_pool, pko_size, + pko_buffers, + "PKO Command Buffers"); + if (status) + return status; + } + + if (tim_pool >= 0) { + status = + __cvmx_helper_initialize_fpa_pool(tim_pool, tim_size, + tim_buffers, + "TIM Command Buffers"); + if (status) + return status; + } + + if (dfa_pool >= 0) { + status = + __cvmx_helper_initialize_fpa_pool(dfa_pool, dfa_size, + dfa_buffers, + "DFA Command Buffers"); + if (status) + return status; + } + + return 0; +} + +/** + * Allocate memory and initialize the FPA pools using memory + * from cvmx-bootmem. Sizes of each element in the pools is + * controlled by the cvmx-config.h header file. Specifying + * zero for any parameter will cause that FPA pool to not be + * setup. This is useful if you aren't using some of the + * hardware and want to save memory. + * + * @packet_buffers: + * Number of packet buffers to allocate + * @work_queue_entries: + * Number of work queue entries + * @pko_buffers: + * PKO Command buffers. You should at minimum have two per + * each PKO queue. + * @tim_buffers: + * TIM ring buffer command queues. At least two per timer bucket + * is recommened. + * @dfa_buffers: + * DFA command buffer. A relatively small (32 for example) + * number should work. + * Returns Zero on success, non-zero if out of memory + */ +int cvmx_helper_initialize_fpa(int packet_buffers, int work_queue_entries, + int pko_buffers, int tim_buffers, + int dfa_buffers) +{ +#ifndef CVMX_FPA_PACKET_POOL +#define CVMX_FPA_PACKET_POOL -1 +#define CVMX_FPA_PACKET_POOL_SIZE 0 +#endif +#ifndef CVMX_FPA_WQE_POOL +#define CVMX_FPA_WQE_POOL -1 +#define CVMX_FPA_WQE_POOL_SIZE 0 +#endif +#ifndef CVMX_FPA_OUTPUT_BUFFER_POOL +#define CVMX_FPA_OUTPUT_BUFFER_POOL -1 +#define CVMX_FPA_OUTPUT_BUFFER_POOL_SIZE 0 +#endif +#ifndef CVMX_FPA_TIMER_POOL +#define CVMX_FPA_TIMER_POOL -1 +#define CVMX_FPA_TIMER_POOL_SIZE 0 +#endif +#ifndef CVMX_FPA_DFA_POOL +#define CVMX_FPA_DFA_POOL -1 +#define CVMX_FPA_DFA_POOL_SIZE 0 +#endif + return __cvmx_helper_initialize_fpa(CVMX_FPA_PACKET_POOL, + CVMX_FPA_PACKET_POOL_SIZE, + packet_buffers, CVMX_FPA_WQE_POOL, + CVMX_FPA_WQE_POOL_SIZE, + work_queue_entries, + CVMX_FPA_OUTPUT_BUFFER_POOL, + CVMX_FPA_OUTPUT_BUFFER_POOL_SIZE, + pko_buffers, CVMX_FPA_TIMER_POOL, + CVMX_FPA_TIMER_POOL_SIZE, + tim_buffers, CVMX_FPA_DFA_POOL, + CVMX_FPA_DFA_POOL_SIZE, + dfa_buffers); +} diff --git a/arch/mips/cavium-octeon/executive/cvmx-helper-loop.c b/arch/mips/cavium-octeon/executive/cvmx-helper-loop.c new file mode 100644 index 000000000000..bfbd46115e71 --- /dev/null +++ b/arch/mips/cavium-octeon/executive/cvmx-helper-loop.c @@ -0,0 +1,85 @@ +/***********************license start*************** + * Author: Cavium Networks + * + * Contact: support@caviumnetworks.com + * This file is part of the OCTEON SDK + * + * Copyright (c) 2003-2008 Cavium Networks + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, Version 2, as + * published by the Free Software Foundation. + * + * This file is distributed in the hope that it will be useful, but + * AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or + * NONINFRINGEMENT. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License + * along with this file; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * or visit http://www.gnu.org/licenses/. + * + * This file may also be available under a different license from Cavium. + * Contact Cavium Networks for more information + ***********************license end**************************************/ + +/* + * Functions for LOOP initialization, configuration, + * and monitoring. + */ +#include <asm/octeon/octeon.h> + +#include <asm/octeon/cvmx-config.h> + +#include <asm/octeon/cvmx-helper.h> +#include <asm/octeon/cvmx-pip-defs.h> + +/** + * Probe a LOOP interface and determine the number of ports + * connected to it. The LOOP interface should still be down + * after this call. + * + * @interface: Interface to probe + * + * Returns Number of ports on the interface. Zero to disable. + */ +int __cvmx_helper_loop_probe(int interface) +{ + union cvmx_ipd_sub_port_fcs ipd_sub_port_fcs; + int num_ports = 4; + int port; + + /* We need to disable length checking so packet < 64 bytes and jumbo + frames don't get errors */ + for (port = 0; port < num_ports; port++) { + union cvmx_pip_prt_cfgx port_cfg; + int ipd_port = cvmx_helper_get_ipd_port(interface, port); + port_cfg.u64 = cvmx_read_csr(CVMX_PIP_PRT_CFGX(ipd_port)); + port_cfg.s.maxerr_en = 0; + port_cfg.s.minerr_en = 0; + cvmx_write_csr(CVMX_PIP_PRT_CFGX(ipd_port), port_cfg.u64); + } + + /* Disable FCS stripping for loopback ports */ + ipd_sub_port_fcs.u64 = cvmx_read_csr(CVMX_IPD_SUB_PORT_FCS); + ipd_sub_port_fcs.s.port_bit2 = 0; + cvmx_write_csr(CVMX_IPD_SUB_PORT_FCS, ipd_sub_port_fcs.u64); + return num_ports; +} + +/** + * Bringup and enable a LOOP interface. After this call packet + * I/O should be fully functional. This is called with IPD + * enabled but PKO disabled. + * + * @interface: Interface to bring up + * + * Returns Zero on success, negative on failure + */ +int __cvmx_helper_loop_enable(int interface) +{ + /* Do nothing. */ + return 0; +} diff --git a/arch/mips/cavium-octeon/executive/cvmx-helper-npi.c b/arch/mips/cavium-octeon/executive/cvmx-helper-npi.c new file mode 100644 index 000000000000..cc94cfa545b4 --- /dev/null +++ b/arch/mips/cavium-octeon/executive/cvmx-helper-npi.c @@ -0,0 +1,113 @@ +/***********************license start*************** + * Author: Cavium Networks + * + * Contact: support@caviumnetworks.com + * This file is part of the OCTEON SDK + * + * Copyright (c) 2003-2008 Cavium Networks + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, Version 2, as + * published by the Free Software Foundation. + * + * This file is distributed in the hope that it will be useful, but + * AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or + * NONINFRINGEMENT. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License + * along with this file; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * or visit http://www.gnu.org/licenses/. + * + * This file may also be available under a different license from Cavium. + * Contact Cavium Networks for more information + ***********************license end**************************************/ + +/* + * Functions for NPI initialization, configuration, + * and monitoring. + */ +#include <asm/octeon/octeon.h> + +#include <asm/octeon/cvmx-config.h> + +#include <asm/octeon/cvmx-helper.h> + +#include <asm/octeon/cvmx-pip-defs.h> + +/** + * Probe a NPI interface and determine the number of ports + * connected to it. The NPI interface should still be down + * after this call. + * + * @interface: Interface to probe + * + * Returns Number of ports on the interface. Zero to disable. + */ +int __cvmx_helper_npi_probe(int interface) +{ +#if CVMX_PKO_QUEUES_PER_PORT_PCI > 0 + if (OCTEON_IS_MODEL(OCTEON_CN38XX) || OCTEON_IS_MODEL(OCTEON_CN58XX)) + return 4; + else if (OCTEON_IS_MODEL(OCTEON_CN56XX) + && !OCTEON_IS_MODEL(OCTEON_CN56XX_PASS1_X)) + /* The packet engines didn't exist before pass 2 */ + return 4; + else if (OCTEON_IS_MODEL(OCTEON_CN52XX) + && !OCTEON_IS_MODEL(OCTEON_CN52XX_PASS1_X)) + /* The packet engines didn't exist before pass 2 */ + return 4; +#if 0 + /* + * Technically CN30XX, CN31XX, and CN50XX contain packet + * engines, but nobody ever uses them. Since this is the case, + * we disable them here. + */ + else if (OCTEON_IS_MODEL(OCTEON_CN31XX) + || OCTEON_IS_MODEL(OCTEON_CN50XX)) + return 2; + else if (OCTEON_IS_MODEL(OCTEON_CN30XX)) + return 1; +#endif +#endif + return 0; +} + +/** + * Bringup and enable a NPI interface. After this call packet + * I/O should be fully functional. This is called with IPD + * enabled but PKO disabled. + * + * @interface: Interface to bring up + * + * Returns Zero on success, negative on failure + */ +int __cvmx_helper_npi_enable(int interface) +{ + /* + * On CN50XX, CN52XX, and CN56XX we need to disable length + * checking so packet < 64 bytes and jumbo frames don't get + * errors. + */ + if (!OCTEON_IS_MODEL(OCTEON_CN3XXX) && + !OCTEON_IS_MODEL(OCTEON_CN58XX)) { + int num_ports = cvmx_helper_ports_on_interface(interface); + int port; + for (port = 0; port < num_ports; port++) { + union cvmx_pip_prt_cfgx port_cfg; + int ipd_port = + cvmx_helper_get_ipd_port(interface, port); + port_cfg.u64 = + cvmx_read_csr(CVMX_PIP_PRT_CFGX(ipd_port)); + port_cfg.s.maxerr_en = 0; + port_cfg.s.minerr_en = 0; + cvmx_write_csr(CVMX_PIP_PRT_CFGX(ipd_port), + port_cfg.u64); + } + } + + /* Enables are controlled by the remote host, so nothing to do here */ + return 0; +} diff --git a/arch/mips/cavium-octeon/executive/cvmx-helper-rgmii.c b/arch/mips/cavium-octeon/executive/cvmx-helper-rgmii.c new file mode 100644 index 000000000000..82b21843421c --- /dev/null +++ b/arch/mips/cavium-octeon/executive/cvmx-helper-rgmii.c @@ -0,0 +1,526 @@ +/***********************license start*************** + * Author: Cavium Networks + * + * Contact: support@caviumnetworks.com + * This file is part of the OCTEON SDK + * + * Copyright (c) 2003-2008 Cavium Networks + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, Version 2, as + * published by the Free Software Foundation. + * + * This file is distributed in the hope that it will be useful, but + * AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or + * NONINFRINGEMENT. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License + * along with this file; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * or visit http://www.gnu.org/licenses/. + * + * This file may also be available under a different license from Cavium. + * Contact Cavium Networks for more information + ***********************license end**************************************/ + +/* + * Functions for RGMII/GMII/MII initialization, configuration, + * and monitoring. + */ +#include <asm/octeon/octeon.h> + +#include <asm/octeon/cvmx-config.h> + + +#include <asm/octeon/cvmx-mdio.h> +#include <asm/octeon/cvmx-pko.h> +#include <asm/octeon/cvmx-helper.h> +#include <asm/octeon/cvmx-helper-board.h> + +#include <asm/octeon/cvmx-npi-defs.h> +#include <asm/octeon/cvmx-gmxx-defs.h> +#include <asm/octeon/cvmx-asxx-defs.h> +#include <asm/octeon/cvmx-dbg-defs.h> + +void __cvmx_interrupt_gmxx_enable(int interface); +void __cvmx_interrupt_asxx_enable(int block); + +/** + * Probe RGMII ports and determine the number present + * + * @interface: Interface to probe + * + * Returns Number of RGMII/GMII/MII ports (0-4). + */ +int __cvmx_helper_rgmii_probe(int interface) +{ + int num_ports = 0; + union cvmx_gmxx_inf_mode mode; + mode.u64 = cvmx_read_csr(CVMX_GMXX_INF_MODE(interface)); + + if (mode.s.type) { + if (OCTEON_IS_MODEL(OCTEON_CN38XX) + || OCTEON_IS_MODEL(OCTEON_CN58XX)) { + cvmx_dprintf("ERROR: RGMII initialize called in " + "SPI interface\n"); + } else if (OCTEON_IS_MODEL(OCTEON_CN31XX) + || OCTEON_IS_MODEL(OCTEON_CN30XX) + || OCTEON_IS_MODEL(OCTEON_CN50XX)) { + /* + * On these chips "type" says we're in + * GMII/MII mode. This limits us to 2 ports + */ + num_ports = 2; + } else { + cvmx_dprintf("ERROR: Unsupported Octeon model in %s\n", + __func__); + } + } else { + if (OCTEON_IS_MODEL(OCTEON_CN38XX) + || OCTEON_IS_MODEL(OCTEON_CN58XX)) { + num_ports = 4; + } else if (OCTEON_IS_MODEL(OCTEON_CN31XX) + || OCTEON_IS_MODEL(OCTEON_CN30XX) + || OCTEON_IS_MODEL(OCTEON_CN50XX)) { + num_ports = 3; + } else { + cvmx_dprintf("ERROR: Unsupported Octeon model in %s\n", + __func__); + } + } + return num_ports; +} + +/** + * Put an RGMII interface in loopback mode. Internal packets sent + * out will be received back again on the same port. Externally + * received packets will echo back out. + * + * @port: IPD port number to loop. + */ +void cvmx_helper_rgmii_internal_loopback(int port) +{ + int interface = (port >> 4) & 1; + int index = port & 0xf; + uint64_t tmp; + + union cvmx_gmxx_prtx_cfg gmx_cfg; + gmx_cfg.u64 = 0; + gmx_cfg.s.duplex = 1; + gmx_cfg.s.slottime = 1; + gmx_cfg.s.speed = 1; + cvmx_write_csr(CVMX_GMXX_TXX_CLK(index, interface), 1); + cvmx_write_csr(CVMX_GMXX_TXX_SLOT(index, interface), 0x200); + cvmx_write_csr(CVMX_GMXX_TXX_BURST(index, interface), 0x2000); + cvmx_write_csr(CVMX_GMXX_PRTX_CFG(index, interface), gmx_cfg.u64); + tmp = cvmx_read_csr(CVMX_ASXX_PRT_LOOP(interface)); + cvmx_write_csr(CVMX_ASXX_PRT_LOOP(interface), (1 << index) | tmp); + tmp = cvmx_read_csr(CVMX_ASXX_TX_PRT_EN(interface)); + cvmx_write_csr(CVMX_ASXX_TX_PRT_EN(interface), (1 << index) | tmp); + tmp = cvmx_read_csr(CVMX_ASXX_RX_PRT_EN(interface)); + cvmx_write_csr(CVMX_ASXX_RX_PRT_EN(interface), (1 << index) | tmp); + gmx_cfg.s.en = 1; + cvmx_write_csr(CVMX_GMXX_PRTX_CFG(index, interface), gmx_cfg.u64); +} + +/** + * Workaround ASX setup errata with CN38XX pass1 + * + * @interface: Interface to setup + * @port: Port to setup (0..3) + * @cpu_clock_hz: + * Chip frequency in Hertz + * + * Returns Zero on success, negative on failure + */ +static int __cvmx_helper_errata_asx_pass1(int interface, int port, + int cpu_clock_hz) +{ + /* Set hi water mark as per errata GMX-4 */ + if (cpu_clock_hz >= 325000000 && cpu_clock_hz < 375000000) + cvmx_write_csr(CVMX_ASXX_TX_HI_WATERX(port, interface), 12); + else if (cpu_clock_hz >= 375000000 && cpu_clock_hz < 437000000) + cvmx_write_csr(CVMX_ASXX_TX_HI_WATERX(port, interface), 11); + else if (cpu_clock_hz >= 437000000 && cpu_clock_hz < 550000000) + cvmx_write_csr(CVMX_ASXX_TX_HI_WATERX(port, interface), 10); + else if (cpu_clock_hz >= 550000000 && cpu_clock_hz < 687000000) + cvmx_write_csr(CVMX_ASXX_TX_HI_WATERX(port, interface), 9); + else + cvmx_dprintf("Illegal clock frequency (%d). " + "CVMX_ASXX_TX_HI_WATERX not set\n", cpu_clock_hz); + return 0; +} + +/** + * Configure all of the ASX, GMX, and PKO regsiters required + * to get RGMII to function on the supplied interface. + * + * @interface: PKO Interface to configure (0 or 1) + * + * Returns Zero on success + */ +int __cvmx_helper_rgmii_enable(int interface) +{ + int num_ports = cvmx_helper_ports_on_interface(interface); + int port; + struct cvmx_sysinfo *sys_info_ptr = cvmx_sysinfo_get(); + union cvmx_gmxx_inf_mode mode; + union cvmx_asxx_tx_prt_en asx_tx; + union cvmx_asxx_rx_prt_en asx_rx; + + mode.u64 = cvmx_read_csr(CVMX_GMXX_INF_MODE(interface)); + + if (mode.s.en == 0) + return -1; + if ((OCTEON_IS_MODEL(OCTEON_CN38XX) || + OCTEON_IS_MODEL(OCTEON_CN58XX)) && mode.s.type == 1) + /* Ignore SPI interfaces */ + return -1; + + /* Configure the ASX registers needed to use the RGMII ports */ + asx_tx.u64 = 0; + asx_tx.s.prt_en = cvmx_build_mask(num_ports); + cvmx_write_csr(CVMX_ASXX_TX_PRT_EN(interface), asx_tx.u64); + + asx_rx.u64 = 0; + asx_rx.s.prt_en = cvmx_build_mask(num_ports); + cvmx_write_csr(CVMX_ASXX_RX_PRT_EN(interface), asx_rx.u64); + + /* Configure the GMX registers needed to use the RGMII ports */ + for (port = 0; port < num_ports; port++) { + /* Setting of CVMX_GMXX_TXX_THRESH has been moved to + __cvmx_helper_setup_gmx() */ + + if (cvmx_octeon_is_pass1()) + __cvmx_helper_errata_asx_pass1(interface, port, + sys_info_ptr-> + cpu_clock_hz); + else { + /* + * Configure more flexible RGMII preamble + * checking. Pass 1 doesn't support this + * feature. + */ + union cvmx_gmxx_rxx_frm_ctl frm_ctl; + frm_ctl.u64 = + cvmx_read_csr(CVMX_GMXX_RXX_FRM_CTL + (port, interface)); + /* New field, so must be compile time */ + frm_ctl.s.pre_free = 1; + cvmx_write_csr(CVMX_GMXX_RXX_FRM_CTL(port, interface), + frm_ctl.u64); + } + + /* + * Each pause frame transmitted will ask for about 10M + * bit times before resume. If buffer space comes + * available before that time has expired, an XON + * pause frame (0 time) will be transmitted to restart + * the flow. + */ + cvmx_write_csr(CVMX_GMXX_TXX_PAUSE_PKT_TIME(port, interface), + 20000); + cvmx_write_csr(CVMX_GMXX_TXX_PAUSE_PKT_INTERVAL + (port, interface), 19000); + + if (OCTEON_IS_MODEL(OCTEON_CN50XX)) { + cvmx_write_csr(CVMX_ASXX_TX_CLK_SETX(port, interface), + 16); + cvmx_write_csr(CVMX_ASXX_RX_CLK_SETX(port, interface), + 16); + } else { + cvmx_write_csr(CVMX_ASXX_TX_CLK_SETX(port, interface), + 24); + cvmx_write_csr(CVMX_ASXX_RX_CLK_SETX(port, interface), + 24); + } + } + + __cvmx_helper_setup_gmx(interface, num_ports); + + /* enable the ports now */ + for (port = 0; port < num_ports; port++) { + union cvmx_gmxx_prtx_cfg gmx_cfg; + cvmx_helper_link_autoconf(cvmx_helper_get_ipd_port + (interface, port)); + gmx_cfg.u64 = + cvmx_read_csr(CVMX_GMXX_PRTX_CFG(port, interface)); + gmx_cfg.s.en = 1; + cvmx_write_csr(CVMX_GMXX_PRTX_CFG(port, interface), + gmx_cfg.u64); + } + __cvmx_interrupt_asxx_enable(interface); + __cvmx_interrupt_gmxx_enable(interface); + + return 0; +} + +/** + * 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(). + * + * @ipd_port: IPD/PKO port to query + * + * Returns Link state + */ +cvmx_helper_link_info_t __cvmx_helper_rgmii_link_get(int ipd_port) +{ + int interface = cvmx_helper_get_interface_num(ipd_port); + int index = cvmx_helper_get_interface_index_num(ipd_port); + union cvmx_asxx_prt_loop asxx_prt_loop; + + asxx_prt_loop.u64 = cvmx_read_csr(CVMX_ASXX_PRT_LOOP(interface)); + if (asxx_prt_loop.s.int_loop & (1 << index)) { + /* Force 1Gbps full duplex on internal loopback */ + cvmx_helper_link_info_t result; + result.u64 = 0; + result.s.full_duplex = 1; + result.s.link_up = 1; + result.s.speed = 1000; + return result; + } else + return __cvmx_helper_board_link_get(ipd_port); +} + +/** + * 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. + * + * @ipd_port: IPD/PKO port to configure + * @link_info: The new link state + * + * Returns Zero on success, negative on failure + */ +int __cvmx_helper_rgmii_link_set(int ipd_port, + cvmx_helper_link_info_t link_info) +{ + int result = 0; + int interface = cvmx_helper_get_interface_num(ipd_port); + int index = cvmx_helper_get_interface_index_num(ipd_port); + union cvmx_gmxx_prtx_cfg original_gmx_cfg; + union cvmx_gmxx_prtx_cfg new_gmx_cfg; + union cvmx_pko_mem_queue_qos pko_mem_queue_qos; + union cvmx_pko_mem_queue_qos pko_mem_queue_qos_save[16]; + union cvmx_gmxx_tx_ovr_bp gmx_tx_ovr_bp; + union cvmx_gmxx_tx_ovr_bp gmx_tx_ovr_bp_save; + int i; + + /* Ignore speed sets in the simulator */ + if (cvmx_sysinfo_get()->board_type == CVMX_BOARD_TYPE_SIM) + return 0; + + /* Read the current settings so we know the current enable state */ + original_gmx_cfg.u64 = + cvmx_read_csr(CVMX_GMXX_PRTX_CFG(index, interface)); + new_gmx_cfg = original_gmx_cfg; + + /* Disable the lowest level RX */ + cvmx_write_csr(CVMX_ASXX_RX_PRT_EN(interface), + cvmx_read_csr(CVMX_ASXX_RX_PRT_EN(interface)) & + ~(1 << index)); + + memset(pko_mem_queue_qos_save, 0, sizeof(pko_mem_queue_qos_save)); + /* Disable all queues so that TX should become idle */ + for (i = 0; i < cvmx_pko_get_num_queues(ipd_port); i++) { + int queue = cvmx_pko_get_base_queue(ipd_port) + i; + cvmx_write_csr(CVMX_PKO_REG_READ_IDX, queue); + pko_mem_queue_qos.u64 = cvmx_read_csr(CVMX_PKO_MEM_QUEUE_QOS); + pko_mem_queue_qos.s.pid = ipd_port; + pko_mem_queue_qos.s.qid = queue; + pko_mem_queue_qos_save[i] = pko_mem_queue_qos; + pko_mem_queue_qos.s.qos_mask = 0; + cvmx_write_csr(CVMX_PKO_MEM_QUEUE_QOS, pko_mem_queue_qos.u64); + } + + /* Disable backpressure */ + gmx_tx_ovr_bp.u64 = cvmx_read_csr(CVMX_GMXX_TX_OVR_BP(interface)); + gmx_tx_ovr_bp_save = gmx_tx_ovr_bp; + gmx_tx_ovr_bp.s.bp &= ~(1 << index); + gmx_tx_ovr_bp.s.en |= 1 << index; + cvmx_write_csr(CVMX_GMXX_TX_OVR_BP(interface), gmx_tx_ovr_bp.u64); + cvmx_read_csr(CVMX_GMXX_TX_OVR_BP(interface)); + + /* + * Poll the GMX state machine waiting for it to become + * idle. Preferably we should only change speed when it is + * idle. If it doesn't become idle we will still do the speed + * change, but there is a slight chance that GMX will + * lockup. + */ + cvmx_write_csr(CVMX_NPI_DBG_SELECT, + interface * 0x800 + index * 0x100 + 0x880); + CVMX_WAIT_FOR_FIELD64(CVMX_DBG_DATA, union cvmx_dbg_data, data & 7, + ==, 0, 10000); + CVMX_WAIT_FOR_FIELD64(CVMX_DBG_DATA, union cvmx_dbg_data, data & 0xf, + ==, 0, 10000); + + /* Disable the port before we make any changes */ + new_gmx_cfg.s.en = 0; + cvmx_write_csr(CVMX_GMXX_PRTX_CFG(index, interface), new_gmx_cfg.u64); + cvmx_read_csr(CVMX_GMXX_PRTX_CFG(index, interface)); + + /* Set full/half duplex */ + if (cvmx_octeon_is_pass1()) + /* Half duplex is broken for 38XX Pass 1 */ + new_gmx_cfg.s.duplex = 1; + else if (!link_info.s.link_up) + /* Force full duplex on down links */ + new_gmx_cfg.s.duplex = 1; + else + new_gmx_cfg.s.duplex = link_info.s.full_duplex; + + /* Set the link speed. Anything unknown is set to 1Gbps */ + if (link_info.s.speed == 10) { + new_gmx_cfg.s.slottime = 0; + new_gmx_cfg.s.speed = 0; + } else if (link_info.s.speed == 100) { + new_gmx_cfg.s.slottime = 0; + new_gmx_cfg.s.speed = 0; + } else { + new_gmx_cfg.s.slottime = 1; + new_gmx_cfg.s.speed = 1; + } + + /* Adjust the clocks */ + if (link_info.s.speed == 10) { + cvmx_write_csr(CVMX_GMXX_TXX_CLK(index, interface), 50); + cvmx_write_csr(CVMX_GMXX_TXX_SLOT(index, interface), 0x40); + cvmx_write_csr(CVMX_GMXX_TXX_BURST(index, interface), 0); + } else if (link_info.s.speed == 100) { + cvmx_write_csr(CVMX_GMXX_TXX_CLK(index, interface), 5); + cvmx_write_csr(CVMX_GMXX_TXX_SLOT(index, interface), 0x40); + cvmx_write_csr(CVMX_GMXX_TXX_BURST(index, interface), 0); + } else { + cvmx_write_csr(CVMX_GMXX_TXX_CLK(index, interface), 1); + cvmx_write_csr(CVMX_GMXX_TXX_SLOT(index, interface), 0x200); + cvmx_write_csr(CVMX_GMXX_TXX_BURST(index, interface), 0x2000); + } + + if (OCTEON_IS_MODEL(OCTEON_CN30XX) || OCTEON_IS_MODEL(OCTEON_CN50XX)) { + if ((link_info.s.speed == 10) || (link_info.s.speed == 100)) { + union cvmx_gmxx_inf_mode mode; + mode.u64 = cvmx_read_csr(CVMX_GMXX_INF_MODE(interface)); + + /* + * Port .en .type .p0mii Configuration + * ---- --- ----- ------ ----------------------------------------- + * X 0 X X All links are disabled. + * 0 1 X 0 Port 0 is RGMII + * 0 1 X 1 Port 0 is MII + * 1 1 0 X Ports 1 and 2 are configured as RGMII ports. + * 1 1 1 X Port 1: GMII/MII; Port 2: disabled. GMII or + * MII port is selected by GMX_PRT1_CFG[SPEED]. + */ + + /* In MII mode, CLK_CNT = 1. */ + if (((index == 0) && (mode.s.p0mii == 1)) + || ((index != 0) && (mode.s.type == 1))) { + cvmx_write_csr(CVMX_GMXX_TXX_CLK + (index, interface), 1); + } + } + } + + /* Do a read to make sure all setup stuff is complete */ + cvmx_read_csr(CVMX_GMXX_PRTX_CFG(index, interface)); + + /* Save the new GMX setting without enabling the port */ + cvmx_write_csr(CVMX_GMXX_PRTX_CFG(index, interface), new_gmx_cfg.u64); + + /* Enable the lowest level RX */ + cvmx_write_csr(CVMX_ASXX_RX_PRT_EN(interface), + cvmx_read_csr(CVMX_ASXX_RX_PRT_EN(interface)) | (1 << + index)); + + /* Re-enable the TX path */ + for (i = 0; i < cvmx_pko_get_num_queues(ipd_port); i++) { + int queue = cvmx_pko_get_base_queue(ipd_port) + i; + cvmx_write_csr(CVMX_PKO_REG_READ_IDX, queue); + cvmx_write_csr(CVMX_PKO_MEM_QUEUE_QOS, + pko_mem_queue_qos_save[i].u64); + } + + /* Restore backpressure */ + cvmx_write_csr(CVMX_GMXX_TX_OVR_BP(interface), gmx_tx_ovr_bp_save.u64); + + /* Restore the GMX enable state. Port config is complete */ + new_gmx_cfg.s.en = original_gmx_cfg.s.en; + cvmx_write_csr(CVMX_GMXX_PRTX_CFG(index, interface), new_gmx_cfg.u64); + + return result; +} + +/** + * 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. + * + * @ipd_port: IPD/PKO port to loopback. + * @enable_internal: + * Non zero if you want internal loopback + * @enable_external: + * Non zero if you want external loopback + * + * Returns Zero on success, negative on failure. + */ +int __cvmx_helper_rgmii_configure_loopback(int ipd_port, int enable_internal, + int enable_external) +{ + int interface = cvmx_helper_get_interface_num(ipd_port); + int index = cvmx_helper_get_interface_index_num(ipd_port); + int original_enable; + union cvmx_gmxx_prtx_cfg gmx_cfg; + union cvmx_asxx_prt_loop asxx_prt_loop; + + /* Read the current enable state and save it */ + gmx_cfg.u64 = cvmx_read_csr(CVMX_GMXX_PRTX_CFG(index, interface)); + original_enable = gmx_cfg.s.en; + /* Force port to be disabled */ + gmx_cfg.s.en = 0; + if (enable_internal) { + /* Force speed if we're doing internal loopback */ + gmx_cfg.s.duplex = 1; + gmx_cfg.s.slottime = 1; + gmx_cfg.s.speed = 1; + cvmx_write_csr(CVMX_GMXX_TXX_CLK(index, interface), 1); + cvmx_write_csr(CVMX_GMXX_TXX_SLOT(index, interface), 0x200); + cvmx_write_csr(CVMX_GMXX_TXX_BURST(index, interface), 0x2000); + } + cvmx_write_csr(CVMX_GMXX_PRTX_CFG(index, interface), gmx_cfg.u64); + + /* Set the loopback bits */ + asxx_prt_loop.u64 = cvmx_read_csr(CVMX_ASXX_PRT_LOOP(interface)); + if (enable_internal) + asxx_prt_loop.s.int_loop |= 1 << index; + else + asxx_prt_loop.s.int_loop &= ~(1 << index); + if (enable_external) + asxx_prt_loop.s.ext_loop |= 1 << index; + else + asxx_prt_loop.s.ext_loop &= ~(1 << index); + cvmx_write_csr(CVMX_ASXX_PRT_LOOP(interface), asxx_prt_loop.u64); + + /* Force enables in internal loopback */ + if (enable_internal) { + uint64_t tmp; + tmp = cvmx_read_csr(CVMX_ASXX_TX_PRT_EN(interface)); + cvmx_write_csr(CVMX_ASXX_TX_PRT_EN(interface), + (1 << index) | tmp); + tmp = cvmx_read_csr(CVMX_ASXX_RX_PRT_EN(interface)); + cvmx_write_csr(CVMX_ASXX_RX_PRT_EN(interface), + (1 << index) | tmp); + original_enable = 1; + } + + /* Restore the enable state */ + gmx_cfg.s.en = original_enable; + cvmx_write_csr(CVMX_GMXX_PRTX_CFG(index, interface), gmx_cfg.u64); + return 0; +} diff --git a/arch/mips/cavium-octeon/executive/cvmx-helper-sgmii.c b/arch/mips/cavium-octeon/executive/cvmx-helper-sgmii.c new file mode 100644 index 000000000000..464347ffd362 --- /dev/null +++ b/arch/mips/cavium-octeon/executive/cvmx-helper-sgmii.c @@ -0,0 +1,550 @@ +/***********************license start*************** + * Author: Cavium Networks + * + * Contact: support@caviumnetworks.com + * This file is part of the OCTEON SDK + * + * Copyright (c) 2003-2008 Cavium Networks + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, Version 2, as + * published by the Free Software Foundation. + * + * This file is distributed in the hope that it will be useful, but + * AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or + * NONINFRINGEMENT. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License + * along with this file; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * or visit http://www.gnu.org/licenses/. + * + * This file may also be available under a different license from Cavium. + * Contact Cavium Networks for more information + ***********************license end**************************************/ + +/* + * Functions for SGMII initialization, configuration, + * and monitoring. + */ + +#include <asm/octeon/octeon.h> + +#include <asm/octeon/cvmx-config.h> + +#include <asm/octeon/cvmx-mdio.h> +#include <asm/octeon/cvmx-helper.h> +#include <asm/octeon/cvmx-helper-board.h> + +#include <asm/octeon/cvmx-gmxx-defs.h> +#include <asm/octeon/cvmx-pcsx-defs.h> + +void __cvmx_interrupt_gmxx_enable(int interface); +void __cvmx_interrupt_pcsx_intx_en_reg_enable(int index, int block); +void __cvmx_interrupt_pcsxx_int_en_reg_enable(int index); + +/** + * Perform initialization required only once for an SGMII port. + * + * @interface: Interface to init + * @index: Index of prot on the interface + * + * Returns Zero on success, negative on failure + */ +static int __cvmx_helper_sgmii_hardware_init_one_time(int interface, int index) +{ + const uint64_t clock_mhz = cvmx_sysinfo_get()->cpu_clock_hz / 1000000; + union cvmx_pcsx_miscx_ctl_reg pcs_misc_ctl_reg; + union cvmx_pcsx_linkx_timer_count_reg pcsx_linkx_timer_count_reg; + union cvmx_gmxx_prtx_cfg gmxx_prtx_cfg; + + /* Disable GMX */ + gmxx_prtx_cfg.u64 = cvmx_read_csr(CVMX_GMXX_PRTX_CFG(index, interface)); + gmxx_prtx_cfg.s.en = 0; + cvmx_write_csr(CVMX_GMXX_PRTX_CFG(index, interface), gmxx_prtx_cfg.u64); + + /* + * Write PCS*_LINK*_TIMER_COUNT_REG[COUNT] with the + * appropriate value. 1000BASE-X specifies a 10ms + * interval. SGMII specifies a 1.6ms interval. + */ + pcs_misc_ctl_reg.u64 = + cvmx_read_csr(CVMX_PCSX_MISCX_CTL_REG(index, interface)); + pcsx_linkx_timer_count_reg.u64 = + cvmx_read_csr(CVMX_PCSX_LINKX_TIMER_COUNT_REG(index, interface)); + if (pcs_misc_ctl_reg.s.mode) { + /* 1000BASE-X */ + pcsx_linkx_timer_count_reg.s.count = + (10000ull * clock_mhz) >> 10; + } else { + /* SGMII */ + pcsx_linkx_timer_count_reg.s.count = + (1600ull * clock_mhz) >> 10; + } + cvmx_write_csr(CVMX_PCSX_LINKX_TIMER_COUNT_REG(index, interface), + pcsx_linkx_timer_count_reg.u64); + + /* + * Write the advertisement register to be used as the + * tx_Config_Reg<D15:D0> of the autonegotiation. In + * 1000BASE-X mode, tx_Config_Reg<D15:D0> is PCS*_AN*_ADV_REG. + * In SGMII PHY mode, tx_Config_Reg<D15:D0> is + * PCS*_SGM*_AN_ADV_REG. In SGMII MAC mode, + * tx_Config_Reg<D15:D0> is the fixed value 0x4001, so this + * step can be skipped. + */ + if (pcs_misc_ctl_reg.s.mode) { + /* 1000BASE-X */ + union cvmx_pcsx_anx_adv_reg pcsx_anx_adv_reg; + pcsx_anx_adv_reg.u64 = + cvmx_read_csr(CVMX_PCSX_ANX_ADV_REG(index, interface)); + pcsx_anx_adv_reg.s.rem_flt = 0; + pcsx_anx_adv_reg.s.pause = 3; + pcsx_anx_adv_reg.s.hfd = 1; + pcsx_anx_adv_reg.s.fd = 1; + cvmx_write_csr(CVMX_PCSX_ANX_ADV_REG(index, interface), + pcsx_anx_adv_reg.u64); + } else { + union cvmx_pcsx_miscx_ctl_reg pcsx_miscx_ctl_reg; + pcsx_miscx_ctl_reg.u64 = + cvmx_read_csr(CVMX_PCSX_MISCX_CTL_REG(index, interface)); + if (pcsx_miscx_ctl_reg.s.mac_phy) { + /* PHY Mode */ + union cvmx_pcsx_sgmx_an_adv_reg pcsx_sgmx_an_adv_reg; + pcsx_sgmx_an_adv_reg.u64 = + cvmx_read_csr(CVMX_PCSX_SGMX_AN_ADV_REG + (index, interface)); + pcsx_sgmx_an_adv_reg.s.link = 1; + pcsx_sgmx_an_adv_reg.s.dup = 1; + pcsx_sgmx_an_adv_reg.s.speed = 2; + cvmx_write_csr(CVMX_PCSX_SGMX_AN_ADV_REG + (index, interface), + pcsx_sgmx_an_adv_reg.u64); + } else { + /* MAC Mode - Nothing to do */ + } + } + return 0; +} + +/** + * Initialize the SERTES link for the first time or after a loss + * of link. + * + * @interface: Interface to init + * @index: Index of prot on the interface + * + * Returns Zero on success, negative on failure + */ +static int __cvmx_helper_sgmii_hardware_init_link(int interface, int index) +{ + union cvmx_pcsx_mrx_control_reg control_reg; + + /* + * Take PCS through a reset sequence. + * PCS*_MR*_CONTROL_REG[PWR_DN] should be cleared to zero. + * Write PCS*_MR*_CONTROL_REG[RESET]=1 (while not changing the + * value of the other PCS*_MR*_CONTROL_REG bits). Read + * PCS*_MR*_CONTROL_REG[RESET] until it changes value to + * zero. + */ + control_reg.u64 = + cvmx_read_csr(CVMX_PCSX_MRX_CONTROL_REG(index, interface)); + if (cvmx_sysinfo_get()->board_type != CVMX_BOARD_TYPE_SIM) { + control_reg.s.reset = 1; + cvmx_write_csr(CVMX_PCSX_MRX_CONTROL_REG(index, interface), + control_reg.u64); + if (CVMX_WAIT_FOR_FIELD64 + (CVMX_PCSX_MRX_CONTROL_REG(index, interface), + union cvmx_pcsx_mrx_control_reg, reset, ==, 0, 10000)) { + cvmx_dprintf("SGMII%d: Timeout waiting for port %d " + "to finish reset\n", + interface, index); + return -1; + } + } + + /* + * Write PCS*_MR*_CONTROL_REG[RST_AN]=1 to ensure a fresh + * sgmii negotiation starts. + */ + control_reg.s.rst_an = 1; + control_reg.s.an_en = 1; + control_reg.s.pwr_dn = 0; + cvmx_write_csr(CVMX_PCSX_MRX_CONTROL_REG(index, interface), + control_reg.u64); + + /* + * Wait for PCS*_MR*_STATUS_REG[AN_CPT] to be set, indicating + * that sgmii autonegotiation is complete. In MAC mode this + * isn't an ethernet link, but a link between Octeon and the + * PHY. + */ + if ((cvmx_sysinfo_get()->board_type != CVMX_BOARD_TYPE_SIM) && + CVMX_WAIT_FOR_FIELD64(CVMX_PCSX_MRX_STATUS_REG(index, interface), + union cvmx_pcsx_mrx_status_reg, an_cpt, ==, 1, + 10000)) { + /* cvmx_dprintf("SGMII%d: Port %d link timeout\n", interface, index); */ + return -1; + } + return 0; +} + +/** + * Configure an SGMII link to the specified speed after the SERTES + * link is up. + * + * @interface: Interface to init + * @index: Index of prot on the interface + * @link_info: Link state to configure + * + * Returns Zero on success, negative on failure + */ +static int __cvmx_helper_sgmii_hardware_init_link_speed(int interface, + int index, + cvmx_helper_link_info_t + link_info) +{ + int is_enabled; + union cvmx_gmxx_prtx_cfg gmxx_prtx_cfg; + union cvmx_pcsx_miscx_ctl_reg pcsx_miscx_ctl_reg; + + /* Disable GMX before we make any changes. Remember the enable state */ + gmxx_prtx_cfg.u64 = cvmx_read_csr(CVMX_GMXX_PRTX_CFG(index, interface)); + is_enabled = gmxx_prtx_cfg.s.en; + gmxx_prtx_cfg.s.en = 0; + cvmx_write_csr(CVMX_GMXX_PRTX_CFG(index, interface), gmxx_prtx_cfg.u64); + + /* Wait for GMX to be idle */ + if (CVMX_WAIT_FOR_FIELD64 + (CVMX_GMXX_PRTX_CFG(index, interface), union cvmx_gmxx_prtx_cfg, + rx_idle, ==, 1, 10000) + || CVMX_WAIT_FOR_FIELD64(CVMX_GMXX_PRTX_CFG(index, interface), + union cvmx_gmxx_prtx_cfg, tx_idle, ==, 1, + 10000)) { + cvmx_dprintf + ("SGMII%d: Timeout waiting for port %d to be idle\n", + interface, index); + return -1; + } + + /* Read GMX CFG again to make sure the disable completed */ + gmxx_prtx_cfg.u64 = cvmx_read_csr(CVMX_GMXX_PRTX_CFG(index, interface)); + + /* + * Get the misc control for PCS. We will need to set the + * duplication amount. + */ + pcsx_miscx_ctl_reg.u64 = + cvmx_read_csr(CVMX_PCSX_MISCX_CTL_REG(index, interface)); + + /* + * Use GMXENO to force the link down if the status we get says + * it should be down. + */ + pcsx_miscx_ctl_reg.s.gmxeno = !link_info.s.link_up; + + /* Only change the duplex setting if the link is up */ + if (link_info.s.link_up) + gmxx_prtx_cfg.s.duplex = link_info.s.full_duplex; + + /* Do speed based setting for GMX */ + switch (link_info.s.speed) { + case 10: + gmxx_prtx_cfg.s.speed = 0; + gmxx_prtx_cfg.s.speed_msb = 1; + gmxx_prtx_cfg.s.slottime = 0; + /* Setting from GMX-603 */ + pcsx_miscx_ctl_reg.s.samp_pt = 25; + cvmx_write_csr(CVMX_GMXX_TXX_SLOT(index, interface), 64); + cvmx_write_csr(CVMX_GMXX_TXX_BURST(index, interface), 0); + break; + case 100: + gmxx_prtx_cfg.s.speed = 0; + gmxx_prtx_cfg.s.speed_msb = 0; + gmxx_prtx_cfg.s.slottime = 0; + pcsx_miscx_ctl_reg.s.samp_pt = 0x5; + cvmx_write_csr(CVMX_GMXX_TXX_SLOT(index, interface), 64); + cvmx_write_csr(CVMX_GMXX_TXX_BURST(index, interface), 0); + break; + case 1000: + gmxx_prtx_cfg.s.speed = 1; + gmxx_prtx_cfg.s.speed_msb = 0; + gmxx_prtx_cfg.s.slottime = 1; + pcsx_miscx_ctl_reg.s.samp_pt = 1; + cvmx_write_csr(CVMX_GMXX_TXX_SLOT(index, interface), 512); + cvmx_write_csr(CVMX_GMXX_TXX_BURST(index, interface), 8192); + break; + default: + break; + } + + /* Write the new misc control for PCS */ + cvmx_write_csr(CVMX_PCSX_MISCX_CTL_REG(index, interface), + pcsx_miscx_ctl_reg.u64); + + /* Write the new GMX settings with the port still disabled */ + cvmx_write_csr(CVMX_GMXX_PRTX_CFG(index, interface), gmxx_prtx_cfg.u64); + + /* Read GMX CFG again to make sure the config completed */ + gmxx_prtx_cfg.u64 = cvmx_read_csr(CVMX_GMXX_PRTX_CFG(index, interface)); + + /* Restore the enabled / disabled state */ + gmxx_prtx_cfg.s.en = is_enabled; + cvmx_write_csr(CVMX_GMXX_PRTX_CFG(index, interface), gmxx_prtx_cfg.u64); + + return 0; +} + +/** + * Bring up the SGMII interface to be ready for packet I/O but + * leave I/O disabled using the GMX override. This function + * follows the bringup documented in 10.6.3 of the manual. + * + * @interface: Interface to bringup + * @num_ports: Number of ports on the interface + * + * Returns Zero on success, negative on failure + */ +static int __cvmx_helper_sgmii_hardware_init(int interface, int num_ports) +{ + int index; + + __cvmx_helper_setup_gmx(interface, num_ports); + + for (index = 0; index < num_ports; index++) { + int ipd_port = cvmx_helper_get_ipd_port(interface, index); + __cvmx_helper_sgmii_hardware_init_one_time(interface, index); + __cvmx_helper_sgmii_link_set(ipd_port, + __cvmx_helper_sgmii_link_get + (ipd_port)); + + } + + return 0; +} + +/** + * Probe a SGMII interface and determine the number of ports + * connected to it. The SGMII interface should still be down after + * this call. + * + * @interface: Interface to probe + * + * Returns Number of ports on the interface. Zero to disable. + */ +int __cvmx_helper_sgmii_probe(int interface) +{ + union cvmx_gmxx_inf_mode mode; + + /* + * Due to errata GMX-700 on CN56XXp1.x and CN52XXp1.x, the + * interface needs to be enabled before IPD otherwise per port + * backpressure may not work properly + */ + mode.u64 = cvmx_read_csr(CVMX_GMXX_INF_MODE(interface)); + mode.s.en = 1; + cvmx_write_csr(CVMX_GMXX_INF_MODE(interface), mode.u64); + return 4; +} + +/** + * Bringup and enable a SGMII interface. After this call packet + * I/O should be fully functional. This is called with IPD + * enabled but PKO disabled. + * + * @interface: Interface to bring up + * + * Returns Zero on success, negative on failure + */ +int __cvmx_helper_sgmii_enable(int interface) +{ + int num_ports = cvmx_helper_ports_on_interface(interface); + int index; + + __cvmx_helper_sgmii_hardware_init(interface, num_ports); + + for (index = 0; index < num_ports; index++) { + union cvmx_gmxx_prtx_cfg gmxx_prtx_cfg; + gmxx_prtx_cfg.u64 = + cvmx_read_csr(CVMX_GMXX_PRTX_CFG(index, interface)); + gmxx_prtx_cfg.s.en = 1; + cvmx_write_csr(CVMX_GMXX_PRTX_CFG(index, interface), + gmxx_prtx_cfg.u64); + __cvmx_interrupt_pcsx_intx_en_reg_enable(index, interface); + } + __cvmx_interrupt_pcsxx_int_en_reg_enable(interface); + __cvmx_interrupt_gmxx_enable(interface); + return 0; +} + +/** + * 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(). + * + * @ipd_port: IPD/PKO port to query + * + * Returns Link state + */ +cvmx_helper_link_info_t __cvmx_helper_sgmii_link_get(int ipd_port) +{ + cvmx_helper_link_info_t result; + union cvmx_pcsx_miscx_ctl_reg pcs_misc_ctl_reg; + int interface = cvmx_helper_get_interface_num(ipd_port); + int index = cvmx_helper_get_interface_index_num(ipd_port); + union cvmx_pcsx_mrx_control_reg pcsx_mrx_control_reg; + + result.u64 = 0; + + if (cvmx_sysinfo_get()->board_type == CVMX_BOARD_TYPE_SIM) { + /* The simulator gives you a simulated 1Gbps full duplex link */ + result.s.link_up = 1; + result.s.full_duplex = 1; + result.s.speed = 1000; + return result; + } + + pcsx_mrx_control_reg.u64 = + cvmx_read_csr(CVMX_PCSX_MRX_CONTROL_REG(index, interface)); + if (pcsx_mrx_control_reg.s.loopbck1) { + /* Force 1Gbps full duplex link for internal loopback */ + result.s.link_up = 1; + result.s.full_duplex = 1; + result.s.speed = 1000; + return result; + } + + pcs_misc_ctl_reg.u64 = + cvmx_read_csr(CVMX_PCSX_MISCX_CTL_REG(index, interface)); + if (pcs_misc_ctl_reg.s.mode) { + /* 1000BASE-X */ + /* FIXME */ + } else { + union cvmx_pcsx_miscx_ctl_reg pcsx_miscx_ctl_reg; + pcsx_miscx_ctl_reg.u64 = + cvmx_read_csr(CVMX_PCSX_MISCX_CTL_REG(index, interface)); + if (pcsx_miscx_ctl_reg.s.mac_phy) { + /* PHY Mode */ + union cvmx_pcsx_mrx_status_reg pcsx_mrx_status_reg; + union cvmx_pcsx_anx_results_reg pcsx_anx_results_reg; + + /* + * Don't bother continuing if the SERTES low + * level link is down + */ + pcsx_mrx_status_reg.u64 = + cvmx_read_csr(CVMX_PCSX_MRX_STATUS_REG + (index, interface)); + if (pcsx_mrx_status_reg.s.lnk_st == 0) { + if (__cvmx_helper_sgmii_hardware_init_link + (interface, index) != 0) + return result; + } + + /* Read the autoneg results */ + pcsx_anx_results_reg.u64 = + cvmx_read_csr(CVMX_PCSX_ANX_RESULTS_REG + (index, interface)); + if (pcsx_anx_results_reg.s.an_cpt) { + /* + * Auto negotiation is complete. Set + * status accordingly. + */ + result.s.full_duplex = + pcsx_anx_results_reg.s.dup; + result.s.link_up = + pcsx_anx_results_reg.s.link_ok; + switch (pcsx_anx_results_reg.s.spd) { + case 0: + result.s.speed = 10; + break; + case 1: + result.s.speed = 100; + break; + case 2: + result.s.speed = 1000; + break; + default: + result.s.speed = 0; + result.s.link_up = 0; + break; + } + } else { + /* + * Auto negotiation isn't + * complete. Return link down. + */ + result.s.speed = 0; + result.s.link_up = 0; + } + } else { /* MAC Mode */ + + result = __cvmx_helper_board_link_get(ipd_port); + } + } + return result; +} + +/** + * 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. + * + * @ipd_port: IPD/PKO port to configure + * @link_info: The new link state + * + * Returns Zero on success, negative on failure + */ +int __cvmx_helper_sgmii_link_set(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); + __cvmx_helper_sgmii_hardware_init_link(interface, index); + return __cvmx_helper_sgmii_hardware_init_link_speed(interface, index, + link_info); +} + +/** + * 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. + * + * @ipd_port: IPD/PKO port to loopback. + * @enable_internal: + * Non zero if you want internal loopback + * @enable_external: + * Non zero if you want external loopback + * + * Returns Zero on success, negative on failure. + */ +int __cvmx_helper_sgmii_configure_loopback(int ipd_port, int enable_internal, + int enable_external) +{ + int interface = cvmx_helper_get_interface_num(ipd_port); + int index = cvmx_helper_get_interface_index_num(ipd_port); + union cvmx_pcsx_mrx_control_reg pcsx_mrx_control_reg; + union cvmx_pcsx_miscx_ctl_reg pcsx_miscx_ctl_reg; + + pcsx_mrx_control_reg.u64 = + cvmx_read_csr(CVMX_PCSX_MRX_CONTROL_REG(index, interface)); + pcsx_mrx_control_reg.s.loopbck1 = enable_internal; + cvmx_write_csr(CVMX_PCSX_MRX_CONTROL_REG(index, interface), + pcsx_mrx_control_reg.u64); + + pcsx_miscx_ctl_reg.u64 = + cvmx_read_csr(CVMX_PCSX_MISCX_CTL_REG(index, interface)); + pcsx_miscx_ctl_reg.s.loopbck2 = enable_external; + cvmx_write_csr(CVMX_PCSX_MISCX_CTL_REG(index, interface), + pcsx_miscx_ctl_reg.u64); + + __cvmx_helper_sgmii_hardware_init_link(interface, index); + return 0; +} diff --git a/arch/mips/cavium-octeon/executive/cvmx-helper-spi.c b/arch/mips/cavium-octeon/executive/cvmx-helper-spi.c new file mode 100644 index 000000000000..02a444230ef7 --- /dev/null +++ b/arch/mips/cavium-octeon/executive/cvmx-helper-spi.c @@ -0,0 +1,195 @@ +/***********************license start*************** + * Author: Cavium Networks + * + * Contact: support@caviumnetworks.com + * This file is part of the OCTEON SDK + * + * Copyright (c) 2003-2008 Cavium Networks + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, Version 2, as + * published by the Free Software Foundation. + * + * This file is distributed in the hope that it will be useful, but + * AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or + * NONINFRINGEMENT. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License + * along with this file; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * or visit http://www.gnu.org/licenses/. + * + * This file may also be available under a different license from Cavium. + * Contact Cavium Networks for more information + ***********************license end**************************************/ + +void __cvmx_interrupt_gmxx_enable(int interface); +void __cvmx_interrupt_spxx_int_msk_enable(int index); +void __cvmx_interrupt_stxx_int_msk_enable(int index); + +/* + * Functions for SPI initialization, configuration, + * and monitoring. + */ +#include <asm/octeon/octeon.h> + +#include <asm/octeon/cvmx-config.h> +#include <asm/octeon/cvmx-spi.h> +#include <asm/octeon/cvmx-helper.h> + +#include <asm/octeon/cvmx-pip-defs.h> +#include <asm/octeon/cvmx-pko-defs.h> + +/* + * CVMX_HELPER_SPI_TIMEOUT is used to determine how long the SPI + * initialization routines wait for SPI training. You can override the + * value using executive-config.h if necessary. + */ +#ifndef CVMX_HELPER_SPI_TIMEOUT +#define CVMX_HELPER_SPI_TIMEOUT 10 +#endif + +/** + * Probe a SPI interface and determine the number of ports + * connected to it. The SPI interface should still be down after + * this call. + * + * @interface: Interface to probe + * + * Returns Number of ports on the interface. Zero to disable. + */ +int __cvmx_helper_spi_probe(int interface) +{ + int num_ports = 0; + + if ((cvmx_sysinfo_get()->board_type != CVMX_BOARD_TYPE_SIM) && + cvmx_spi4000_is_present(interface)) { + num_ports = 10; + } else { + union cvmx_pko_reg_crc_enable enable; + num_ports = 16; + /* + * Unlike the SPI4000, most SPI devices don't + * automatically put on the L2 CRC. For everything + * except for the SPI4000 have PKO append the L2 CRC + * to the packet. + */ + enable.u64 = cvmx_read_csr(CVMX_PKO_REG_CRC_ENABLE); + enable.s.enable |= 0xffff << (interface * 16); + cvmx_write_csr(CVMX_PKO_REG_CRC_ENABLE, enable.u64); + } + __cvmx_helper_setup_gmx(interface, num_ports); + return num_ports; +} + +/** + * Bringup and enable a SPI interface. After this call packet I/O + * should be fully functional. This is called with IPD enabled but + * PKO disabled. + * + * @interface: Interface to bring up + * + * Returns Zero on success, negative on failure + */ +int __cvmx_helper_spi_enable(int interface) +{ + /* + * Normally the ethernet L2 CRC is checked and stripped in the + * GMX block. When you are using SPI, this isn' the case and + * IPD needs to check the L2 CRC. + */ + int num_ports = cvmx_helper_ports_on_interface(interface); + int ipd_port; + for (ipd_port = interface * 16; ipd_port < interface * 16 + num_ports; + ipd_port++) { + union cvmx_pip_prt_cfgx port_config; + port_config.u64 = cvmx_read_csr(CVMX_PIP_PRT_CFGX(ipd_port)); + port_config.s.crc_en = 1; + cvmx_write_csr(CVMX_PIP_PRT_CFGX(ipd_port), port_config.u64); + } + + if (cvmx_sysinfo_get()->board_type != CVMX_BOARD_TYPE_SIM) { + cvmx_spi_start_interface(interface, CVMX_SPI_MODE_DUPLEX, + CVMX_HELPER_SPI_TIMEOUT, num_ports); + if (cvmx_spi4000_is_present(interface)) + cvmx_spi4000_initialize(interface); + } + __cvmx_interrupt_spxx_int_msk_enable(interface); + __cvmx_interrupt_stxx_int_msk_enable(interface); + __cvmx_interrupt_gmxx_enable(interface); + return 0; +} + +/** + * 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(). + * + * @ipd_port: IPD/PKO port to query + * + * Returns Link state + */ +cvmx_helper_link_info_t __cvmx_helper_spi_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); + result.u64 = 0; + + if (cvmx_sysinfo_get()->board_type == CVMX_BOARD_TYPE_SIM) { + /* The simulator gives you a simulated full duplex link */ + result.s.link_up = 1; + result.s.full_duplex = 1; + result.s.speed = 10000; + } else if (cvmx_spi4000_is_present(interface)) { + union cvmx_gmxx_rxx_rx_inbnd inband = + cvmx_spi4000_check_speed(interface, index); + result.s.link_up = inband.s.status; + result.s.full_duplex = inband.s.duplex; + switch (inband.s.speed) { + case 0: /* 10 Mbps */ + result.s.speed = 10; + break; + case 1: /* 100 Mbps */ + result.s.speed = 100; + break; + case 2: /* 1 Gbps */ + result.s.speed = 1000; + break; + case 3: /* Illegal */ + result.s.speed = 0; + result.s.link_up = 0; + break; + } + } else { + /* For generic SPI we can't determine the link, just return some + sane results */ + result.s.link_up = 1; + result.s.full_duplex = 1; + result.s.speed = 10000; + } + return result; +} + +/** + * 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. + * + * @ipd_port: IPD/PKO port to configure + * @link_info: The new link state + * + * Returns Zero on success, negative on failure + */ +int __cvmx_helper_spi_link_set(int ipd_port, cvmx_helper_link_info_t link_info) +{ + /* Nothing to do. If we have a SPI4000 then the setup was already performed + by cvmx_spi4000_check_speed(). If not then there isn't any link + info */ + return 0; +} diff --git a/arch/mips/cavium-octeon/executive/cvmx-helper-util.c b/arch/mips/cavium-octeon/executive/cvmx-helper-util.c new file mode 100644 index 000000000000..116dea17acf5 --- /dev/null +++ b/arch/mips/cavium-octeon/executive/cvmx-helper-util.c @@ -0,0 +1,433 @@ +/***********************license start*************** + * Author: Cavium Networks + * + * Contact: support@caviumnetworks.com + * This file is part of the OCTEON SDK + * + * Copyright (c) 2003-2008 Cavium Networks + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, Version 2, as + * published by the Free Software Foundation. + * + * This file is distributed in the hope that it will be useful, but + * AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or + * NONINFRINGEMENT. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License + * along with this file; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * or visit http://www.gnu.org/licenses/. + * + * This file may also be available under a different license from Cavium. + * Contact Cavium Networks for more information + ***********************license end**************************************/ + +/* + * Small helper utilities. + */ +#include <linux/kernel.h> + +#include <asm/octeon/octeon.h> + +#include <asm/octeon/cvmx-config.h> + +#include <asm/octeon/cvmx-fpa.h> +#include <asm/octeon/cvmx-pip.h> +#include <asm/octeon/cvmx-pko.h> +#include <asm/octeon/cvmx-ipd.h> +#include <asm/octeon/cvmx-spi.h> + +#include <asm/octeon/cvmx-helper.h> +#include <asm/octeon/cvmx-helper-util.h> + +#include <asm/octeon/cvmx-ipd-defs.h> + +/** + * Convert a interface mode into a human readable string + * + * @mode: Mode to convert + * + * Returns String + */ +const char *cvmx_helper_interface_mode_to_string(cvmx_helper_interface_mode_t + mode) +{ + switch (mode) { + case CVMX_HELPER_INTERFACE_MODE_DISABLED: + return "DISABLED"; + case CVMX_HELPER_INTERFACE_MODE_RGMII: + return "RGMII"; + case CVMX_HELPER_INTERFACE_MODE_GMII: + return "GMII"; + case CVMX_HELPER_INTERFACE_MODE_SPI: + return "SPI"; + case CVMX_HELPER_INTERFACE_MODE_PCIE: + return "PCIE"; + case CVMX_HELPER_INTERFACE_MODE_XAUI: + return "XAUI"; + case CVMX_HELPER_INTERFACE_MODE_SGMII: + return "SGMII"; + case CVMX_HELPER_INTERFACE_MODE_PICMG: + return "PICMG"; + case CVMX_HELPER_INTERFACE_MODE_NPI: + return "NPI"; + case CVMX_HELPER_INTERFACE_MODE_LOOP: + return "LOOP"; + } + return "UNKNOWN"; +} + +/** + * Debug routine to dump the packet structure to the console + * + * @work: Work queue entry containing the packet to dump + * Returns + */ +int cvmx_helper_dump_packet(cvmx_wqe_t *work) +{ + uint64_t count; + uint64_t remaining_bytes; + union cvmx_buf_ptr buffer_ptr; + uint64_t start_of_buffer; + uint8_t *data_address; + uint8_t *end_of_data; + + cvmx_dprintf("Packet Length: %u\n", work->len); + cvmx_dprintf(" Input Port: %u\n", work->ipprt); + cvmx_dprintf(" QoS: %u\n", work->qos); + cvmx_dprintf(" Buffers: %u\n", work->word2.s.bufs); + + if (work->word2.s.bufs == 0) { + union cvmx_ipd_wqe_fpa_queue wqe_pool; + wqe_pool.u64 = cvmx_read_csr(CVMX_IPD_WQE_FPA_QUEUE); + buffer_ptr.u64 = 0; + buffer_ptr.s.pool = wqe_pool.s.wqe_pool; + buffer_ptr.s.size = 128; + buffer_ptr.s.addr = cvmx_ptr_to_phys(work->packet_data); + if (likely(!work->word2.s.not_IP)) { + union cvmx_pip_ip_offset pip_ip_offset; + pip_ip_offset.u64 = cvmx_read_csr(CVMX_PIP_IP_OFFSET); + buffer_ptr.s.addr += + (pip_ip_offset.s.offset << 3) - + work->word2.s.ip_offset; + buffer_ptr.s.addr += (work->word2.s.is_v6 ^ 1) << 2; + } else { + /* + * WARNING: This code assumes that the packet + * is not RAW. If it was, we would use + * PIP_GBL_CFG[RAW_SHF] instead of + * PIP_GBL_CFG[NIP_SHF]. + */ + union cvmx_pip_gbl_cfg pip_gbl_cfg; + pip_gbl_cfg.u64 = cvmx_read_csr(CVMX_PIP_GBL_CFG); + buffer_ptr.s.addr += pip_gbl_cfg.s.nip_shf; + } + } else + buffer_ptr = work->packet_ptr; + remaining_bytes = work->len; + + while (remaining_bytes) { + start_of_buffer = + ((buffer_ptr.s.addr >> 7) - buffer_ptr.s.back) << 7; + cvmx_dprintf(" Buffer Start:%llx\n", + (unsigned long long)start_of_buffer); + cvmx_dprintf(" Buffer I : %u\n", buffer_ptr.s.i); + cvmx_dprintf(" Buffer Back: %u\n", buffer_ptr.s.back); + cvmx_dprintf(" Buffer Pool: %u\n", buffer_ptr.s.pool); + cvmx_dprintf(" Buffer Data: %llx\n", + (unsigned long long)buffer_ptr.s.addr); + cvmx_dprintf(" Buffer Size: %u\n", buffer_ptr.s.size); + + cvmx_dprintf("\t\t"); + data_address = (uint8_t *) cvmx_phys_to_ptr(buffer_ptr.s.addr); + end_of_data = data_address + buffer_ptr.s.size; + count = 0; + while (data_address < end_of_data) { + if (remaining_bytes == 0) + break; + else + remaining_bytes--; + cvmx_dprintf("%02x", (unsigned int)*data_address); + data_address++; + if (remaining_bytes && (count == 7)) { + cvmx_dprintf("\n\t\t"); + count = 0; + } else + count++; + } + cvmx_dprintf("\n"); + + if (remaining_bytes) + buffer_ptr = *(union cvmx_buf_ptr *) + cvmx_phys_to_ptr(buffer_ptr.s.addr - 8); + } + return 0; +} + +/** + * Setup Random Early Drop on a specific input queue + * + * @queue: Input queue to setup RED on (0-7) + * @pass_thresh: + * Packets will begin slowly dropping when there are less than + * this many packet buffers free in FPA 0. + * @drop_thresh: + * All incomming packets will be dropped when there are less + * than this many free packet buffers in FPA 0. + * Returns Zero on success. Negative on failure + */ +int cvmx_helper_setup_red_queue(int queue, int pass_thresh, int drop_thresh) +{ + union cvmx_ipd_qosx_red_marks red_marks; + union cvmx_ipd_red_quex_param red_param; + + /* Set RED to begin dropping packets when there are pass_thresh buffers + left. It will linearly drop more packets until reaching drop_thresh + buffers */ + red_marks.u64 = 0; + red_marks.s.drop = drop_thresh; + red_marks.s.pass = pass_thresh; + cvmx_write_csr(CVMX_IPD_QOSX_RED_MARKS(queue), red_marks.u64); + + /* Use the actual queue 0 counter, not the average */ + red_param.u64 = 0; + red_param.s.prb_con = + (255ul << 24) / (red_marks.s.pass - red_marks.s.drop); + red_param.s.avg_con = 1; + red_param.s.new_con = 255; + red_param.s.use_pcnt = 1; + cvmx_write_csr(CVMX_IPD_RED_QUEX_PARAM(queue), red_param.u64); + return 0; +} + +/** + * Setup Random Early Drop to automatically begin dropping packets. + * + * @pass_thresh: + * Packets will begin slowly dropping when there are less than + * this many packet buffers free in FPA 0. + * @drop_thresh: + * All incomming packets will be dropped when there are less + * than this many free packet buffers in FPA 0. + * Returns Zero on success. Negative on failure + */ +int cvmx_helper_setup_red(int pass_thresh, int drop_thresh) +{ + union cvmx_ipd_portx_bp_page_cnt page_cnt; + union cvmx_ipd_bp_prt_red_end ipd_bp_prt_red_end; + union cvmx_ipd_red_port_enable red_port_enable; + int queue; + int interface; + int port; + + /* Disable backpressure based on queued buffers. It needs SW support */ + page_cnt.u64 = 0; + page_cnt.s.bp_enb = 0; + page_cnt.s.page_cnt = 100; + for (interface = 0; interface < 2; interface++) { + for (port = cvmx_helper_get_first_ipd_port(interface); + port < cvmx_helper_get_last_ipd_port(interface); port++) + cvmx_write_csr(CVMX_IPD_PORTX_BP_PAGE_CNT(port), + page_cnt.u64); + } + + for (queue = 0; queue < 8; queue++) + cvmx_helper_setup_red_queue(queue, pass_thresh, drop_thresh); + + /* Shutoff the dropping based on the per port page count. SW isn't + decrementing it right now */ + ipd_bp_prt_red_end.u64 = 0; + ipd_bp_prt_red_end.s.prt_enb = 0; + cvmx_write_csr(CVMX_IPD_BP_PRT_RED_END, ipd_bp_prt_red_end.u64); + + red_port_enable.u64 = 0; + red_port_enable.s.prt_enb = 0xfffffffffull; + red_port_enable.s.avg_dly = 10000; + red_port_enable.s.prb_dly = 10000; + cvmx_write_csr(CVMX_IPD_RED_PORT_ENABLE, red_port_enable.u64); + + return 0; +} + +/** + * Setup the common GMX settings that determine the number of + * ports. These setting apply to almost all configurations of all + * chips. + * + * @interface: Interface to configure + * @num_ports: Number of ports on the interface + * + * Returns Zero on success, negative on failure + */ +int __cvmx_helper_setup_gmx(int interface, int num_ports) +{ + union cvmx_gmxx_tx_prts gmx_tx_prts; + union cvmx_gmxx_rx_prts gmx_rx_prts; + union cvmx_pko_reg_gmx_port_mode pko_mode; + union cvmx_gmxx_txx_thresh gmx_tx_thresh; + int index; + + /* Tell GMX the number of TX ports on this interface */ + gmx_tx_prts.u64 = cvmx_read_csr(CVMX_GMXX_TX_PRTS(interface)); + gmx_tx_prts.s.prts = num_ports; + cvmx_write_csr(CVMX_GMXX_TX_PRTS(interface), gmx_tx_prts.u64); + + /* Tell GMX the number of RX ports on this interface. This only + ** applies to *GMII and XAUI ports */ + if (cvmx_helper_interface_get_mode(interface) == + CVMX_HELPER_INTERFACE_MODE_RGMII + || cvmx_helper_interface_get_mode(interface) == + CVMX_HELPER_INTERFACE_MODE_SGMII + || cvmx_helper_interface_get_mode(interface) == + CVMX_HELPER_INTERFACE_MODE_GMII + || cvmx_helper_interface_get_mode(interface) == + CVMX_HELPER_INTERFACE_MODE_XAUI) { + if (num_ports > 4) { + cvmx_dprintf("__cvmx_helper_setup_gmx: Illegal " + "num_ports\n"); + return -1; + } + + gmx_rx_prts.u64 = cvmx_read_csr(CVMX_GMXX_RX_PRTS(interface)); + gmx_rx_prts.s.prts = num_ports; + cvmx_write_csr(CVMX_GMXX_RX_PRTS(interface), gmx_rx_prts.u64); + } + + /* Skip setting CVMX_PKO_REG_GMX_PORT_MODE on 30XX, 31XX, and 50XX */ + if (!OCTEON_IS_MODEL(OCTEON_CN30XX) && !OCTEON_IS_MODEL(OCTEON_CN31XX) + && !OCTEON_IS_MODEL(OCTEON_CN50XX)) { + /* Tell PKO the number of ports on this interface */ + pko_mode.u64 = cvmx_read_csr(CVMX_PKO_REG_GMX_PORT_MODE); + if (interface == 0) { + if (num_ports == 1) + pko_mode.s.mode0 = 4; + else if (num_ports == 2) + pko_mode.s.mode0 = 3; + else if (num_ports <= 4) + pko_mode.s.mode0 = 2; + else if (num_ports <= 8) + pko_mode.s.mode0 = 1; + else + pko_mode.s.mode0 = 0; + } else { + if (num_ports == 1) + pko_mode.s.mode1 = 4; + else if (num_ports == 2) + pko_mode.s.mode1 = 3; + else if (num_ports <= 4) + pko_mode.s.mode1 = 2; + else if (num_ports <= 8) + pko_mode.s.mode1 = 1; + else + pko_mode.s.mode1 = 0; + } + cvmx_write_csr(CVMX_PKO_REG_GMX_PORT_MODE, pko_mode.u64); + } + + /* + * Set GMX to buffer as much data as possible before starting + * transmit. This reduces the chances that we have a TX under + * run due to memory contention. Any packet that fits entirely + * in the GMX FIFO can never have an under run regardless of + * memory load. + */ + gmx_tx_thresh.u64 = cvmx_read_csr(CVMX_GMXX_TXX_THRESH(0, interface)); + if (OCTEON_IS_MODEL(OCTEON_CN30XX) || OCTEON_IS_MODEL(OCTEON_CN31XX) + || OCTEON_IS_MODEL(OCTEON_CN50XX)) { + /* These chips have a fixed max threshold of 0x40 */ + gmx_tx_thresh.s.cnt = 0x40; + } else { + /* Choose the max value for the number of ports */ + if (num_ports <= 1) + gmx_tx_thresh.s.cnt = 0x100 / 1; + else if (num_ports == 2) + gmx_tx_thresh.s.cnt = 0x100 / 2; + else + gmx_tx_thresh.s.cnt = 0x100 / 4; + } + /* + * SPI and XAUI can have lots of ports but the GMX hardware + * only ever has a max of 4. + */ + if (num_ports > 4) + num_ports = 4; + for (index = 0; index < num_ports; index++) + cvmx_write_csr(CVMX_GMXX_TXX_THRESH(index, interface), + gmx_tx_thresh.u64); + + return 0; +} + +/** + * Returns the IPD/PKO port number for a port on the given + * interface. + * + * @interface: Interface to use + * @port: Port on the interface + * + * Returns IPD/PKO port number + */ +int cvmx_helper_get_ipd_port(int interface, int port) +{ + switch (interface) { + case 0: + return port; + case 1: + return port + 16; + case 2: + return port + 32; + case 3: + return port + 36; + } + return -1; +} + +/** + * Returns the interface number for an IPD/PKO port number. + * + * @ipd_port: IPD/PKO port number + * + * Returns Interface number + */ +int cvmx_helper_get_interface_num(int ipd_port) +{ + if (ipd_port < 16) + return 0; + else if (ipd_port < 32) + return 1; + else if (ipd_port < 36) + return 2; + else if (ipd_port < 40) + return 3; + else + cvmx_dprintf("cvmx_helper_get_interface_num: Illegal IPD " + "port number\n"); + + return -1; +} + +/** + * Returns the interface index number for an IPD/PKO port + * number. + * + * @ipd_port: IPD/PKO port number + * + * Returns Interface index number + */ +int cvmx_helper_get_interface_index_num(int ipd_port) +{ + if (ipd_port < 32) + return ipd_port & 15; + else if (ipd_port < 36) + return ipd_port & 3; + else if (ipd_port < 40) + return ipd_port & 3; + else + cvmx_dprintf("cvmx_helper_get_interface_index_num: " + "Illegal IPD port number\n"); + + return -1; +} diff --git a/arch/mips/cavium-octeon/executive/cvmx-helper-xaui.c b/arch/mips/cavium-octeon/executive/cvmx-helper-xaui.c new file mode 100644 index 000000000000..667a8e3cb142 --- /dev/null +++ b/arch/mips/cavium-octeon/executive/cvmx-helper-xaui.c @@ -0,0 +1,348 @@ +/***********************license start*************** + * Author: Cavium Networks + * + * Contact: support@caviumnetworks.com + * This file is part of the OCTEON SDK + * + * Copyright (c) 2003-2008 Cavium Networks + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, Version 2, as + * published by the Free Software Foundation. + * + * This file is distributed in the hope that it will be useful, but + * AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or + * NONINFRINGEMENT. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License + * along with this file; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * or visit http://www.gnu.org/licenses/. + * + * This file may also be available under a different license from Cavium. + * Contact Cavium Networks for more information + ***********************license end**************************************/ + +/* + * Functions for XAUI initialization, configuration, + * and monitoring. + * + */ + +#include <asm/octeon/octeon.h> + +#include <asm/octeon/cvmx-config.h> + +#include <asm/octeon/cvmx-helper.h> + +#include <asm/octeon/cvmx-pko-defs.h> +#include <asm/octeon/cvmx-gmxx-defs.h> +#include <asm/octeon/cvmx-pcsxx-defs.h> + +void __cvmx_interrupt_gmxx_enable(int interface); +void __cvmx_interrupt_pcsx_intx_en_reg_enable(int index, int block); +void __cvmx_interrupt_pcsxx_int_en_reg_enable(int index); +/** + * Probe a XAUI interface and determine the number of ports + * connected to it. The XAUI interface should still be down + * after this call. + * + * @interface: Interface to probe + * + * Returns Number of ports on the interface. Zero to disable. + */ +int __cvmx_helper_xaui_probe(int interface) +{ + int i; + union cvmx_gmxx_hg2_control gmx_hg2_control; + union cvmx_gmxx_inf_mode mode; + + /* + * Due to errata GMX-700 on CN56XXp1.x and CN52XXp1.x, the + * interface needs to be enabled before IPD otherwise per port + * backpressure may not work properly. + */ + mode.u64 = cvmx_read_csr(CVMX_GMXX_INF_MODE(interface)); + mode.s.en = 1; + cvmx_write_csr(CVMX_GMXX_INF_MODE(interface), mode.u64); + + __cvmx_helper_setup_gmx(interface, 1); + + /* + * Setup PKO to support 16 ports for HiGig2 virtual + * ports. We're pointing all of the PKO packet ports for this + * interface to the XAUI. This allows us to use HiGig2 + * backpressure per port. + */ + for (i = 0; i < 16; i++) { + union cvmx_pko_mem_port_ptrs pko_mem_port_ptrs; + pko_mem_port_ptrs.u64 = 0; + /* + * We set each PKO port to have equal priority in a + * round robin fashion. + */ + pko_mem_port_ptrs.s.static_p = 0; + pko_mem_port_ptrs.s.qos_mask = 0xff; + /* All PKO ports map to the same XAUI hardware port */ + pko_mem_port_ptrs.s.eid = interface * 4; + pko_mem_port_ptrs.s.pid = interface * 16 + i; + cvmx_write_csr(CVMX_PKO_MEM_PORT_PTRS, pko_mem_port_ptrs.u64); + } + + /* If HiGig2 is enabled return 16 ports, otherwise return 1 port */ + gmx_hg2_control.u64 = cvmx_read_csr(CVMX_GMXX_HG2_CONTROL(interface)); + if (gmx_hg2_control.s.hg2tx_en) + return 16; + else + return 1; +} + +/** + * Bringup and enable a XAUI interface. After this call packet + * I/O should be fully functional. This is called with IPD + * enabled but PKO disabled. + * + * @interface: Interface to bring up + * + * Returns Zero on success, negative on failure + */ +int __cvmx_helper_xaui_enable(int interface) +{ + union cvmx_gmxx_prtx_cfg gmx_cfg; + union cvmx_pcsxx_control1_reg xauiCtl; + union cvmx_pcsxx_misc_ctl_reg xauiMiscCtl; + union cvmx_gmxx_tx_xaui_ctl gmxXauiTxCtl; + union cvmx_gmxx_rxx_int_en gmx_rx_int_en; + union cvmx_gmxx_tx_int_en gmx_tx_int_en; + union cvmx_pcsxx_int_en_reg pcsx_int_en_reg; + + /* (1) Interface has already been enabled. */ + + /* (2) Disable GMX. */ + xauiMiscCtl.u64 = cvmx_read_csr(CVMX_PCSXX_MISC_CTL_REG(interface)); + xauiMiscCtl.s.gmxeno = 1; + cvmx_write_csr(CVMX_PCSXX_MISC_CTL_REG(interface), xauiMiscCtl.u64); + + /* (3) Disable GMX and PCSX interrupts. */ + gmx_rx_int_en.u64 = cvmx_read_csr(CVMX_GMXX_RXX_INT_EN(0, interface)); + cvmx_write_csr(CVMX_GMXX_RXX_INT_EN(0, interface), 0x0); + gmx_tx_int_en.u64 = cvmx_read_csr(CVMX_GMXX_TX_INT_EN(interface)); + cvmx_write_csr(CVMX_GMXX_TX_INT_EN(interface), 0x0); + pcsx_int_en_reg.u64 = cvmx_read_csr(CVMX_PCSXX_INT_EN_REG(interface)); + cvmx_write_csr(CVMX_PCSXX_INT_EN_REG(interface), 0x0); + + /* (4) Bring up the PCSX and GMX reconciliation layer. */ + /* (4)a Set polarity and lane swapping. */ + /* (4)b */ + gmxXauiTxCtl.u64 = cvmx_read_csr(CVMX_GMXX_TX_XAUI_CTL(interface)); + /* Enable better IFG packing and improves performance */ + gmxXauiTxCtl.s.dic_en = 1; + gmxXauiTxCtl.s.uni_en = 0; + cvmx_write_csr(CVMX_GMXX_TX_XAUI_CTL(interface), gmxXauiTxCtl.u64); + + /* (4)c Aply reset sequence */ + xauiCtl.u64 = cvmx_read_csr(CVMX_PCSXX_CONTROL1_REG(interface)); + xauiCtl.s.lo_pwr = 0; + xauiCtl.s.reset = 1; + cvmx_write_csr(CVMX_PCSXX_CONTROL1_REG(interface), xauiCtl.u64); + + /* Wait for PCS to come out of reset */ + if (CVMX_WAIT_FOR_FIELD64 + (CVMX_PCSXX_CONTROL1_REG(interface), union cvmx_pcsxx_control1_reg, + reset, ==, 0, 10000)) + return -1; + /* Wait for PCS to be aligned */ + if (CVMX_WAIT_FOR_FIELD64 + (CVMX_PCSXX_10GBX_STATUS_REG(interface), + union cvmx_pcsxx_10gbx_status_reg, alignd, ==, 1, 10000)) + return -1; + /* Wait for RX to be ready */ + if (CVMX_WAIT_FOR_FIELD64 + (CVMX_GMXX_RX_XAUI_CTL(interface), union cvmx_gmxx_rx_xaui_ctl, + status, ==, 0, 10000)) + return -1; + + /* (6) Configure GMX */ + gmx_cfg.u64 = cvmx_read_csr(CVMX_GMXX_PRTX_CFG(0, interface)); + gmx_cfg.s.en = 0; + cvmx_write_csr(CVMX_GMXX_PRTX_CFG(0, interface), gmx_cfg.u64); + + /* Wait for GMX RX to be idle */ + if (CVMX_WAIT_FOR_FIELD64 + (CVMX_GMXX_PRTX_CFG(0, interface), union cvmx_gmxx_prtx_cfg, + rx_idle, ==, 1, 10000)) + return -1; + /* Wait for GMX TX to be idle */ + if (CVMX_WAIT_FOR_FIELD64 + (CVMX_GMXX_PRTX_CFG(0, interface), union cvmx_gmxx_prtx_cfg, + tx_idle, ==, 1, 10000)) + return -1; + + /* GMX configure */ + gmx_cfg.u64 = cvmx_read_csr(CVMX_GMXX_PRTX_CFG(0, interface)); + gmx_cfg.s.speed = 1; + gmx_cfg.s.speed_msb = 0; + gmx_cfg.s.slottime = 1; + cvmx_write_csr(CVMX_GMXX_TX_PRTS(interface), 1); + cvmx_write_csr(CVMX_GMXX_TXX_SLOT(0, interface), 512); + cvmx_write_csr(CVMX_GMXX_TXX_BURST(0, interface), 8192); + cvmx_write_csr(CVMX_GMXX_PRTX_CFG(0, interface), gmx_cfg.u64); + + /* (7) Clear out any error state */ + cvmx_write_csr(CVMX_GMXX_RXX_INT_REG(0, interface), + cvmx_read_csr(CVMX_GMXX_RXX_INT_REG(0, interface))); + cvmx_write_csr(CVMX_GMXX_TX_INT_REG(interface), + cvmx_read_csr(CVMX_GMXX_TX_INT_REG(interface))); + cvmx_write_csr(CVMX_PCSXX_INT_REG(interface), + cvmx_read_csr(CVMX_PCSXX_INT_REG(interface))); + + /* Wait for receive link */ + if (CVMX_WAIT_FOR_FIELD64 + (CVMX_PCSXX_STATUS1_REG(interface), union cvmx_pcsxx_status1_reg, + rcv_lnk, ==, 1, 10000)) + return -1; + if (CVMX_WAIT_FOR_FIELD64 + (CVMX_PCSXX_STATUS2_REG(interface), union cvmx_pcsxx_status2_reg, + xmtflt, ==, 0, 10000)) + return -1; + if (CVMX_WAIT_FOR_FIELD64 + (CVMX_PCSXX_STATUS2_REG(interface), union cvmx_pcsxx_status2_reg, + rcvflt, ==, 0, 10000)) + return -1; + + cvmx_write_csr(CVMX_GMXX_RXX_INT_EN(0, interface), gmx_rx_int_en.u64); + cvmx_write_csr(CVMX_GMXX_TX_INT_EN(interface), gmx_tx_int_en.u64); + cvmx_write_csr(CVMX_PCSXX_INT_EN_REG(interface), pcsx_int_en_reg.u64); + + cvmx_helper_link_autoconf(cvmx_helper_get_ipd_port(interface, 0)); + + /* (8) Enable packet reception */ + xauiMiscCtl.s.gmxeno = 0; + cvmx_write_csr(CVMX_PCSXX_MISC_CTL_REG(interface), xauiMiscCtl.u64); + + gmx_cfg.u64 = cvmx_read_csr(CVMX_GMXX_PRTX_CFG(0, interface)); + gmx_cfg.s.en = 1; + cvmx_write_csr(CVMX_GMXX_PRTX_CFG(0, interface), gmx_cfg.u64); + + __cvmx_interrupt_pcsx_intx_en_reg_enable(0, interface); + __cvmx_interrupt_pcsx_intx_en_reg_enable(1, interface); + __cvmx_interrupt_pcsx_intx_en_reg_enable(2, interface); + __cvmx_interrupt_pcsx_intx_en_reg_enable(3, interface); + __cvmx_interrupt_pcsxx_int_en_reg_enable(interface); + __cvmx_interrupt_gmxx_enable(interface); + + return 0; +} + +/** + * 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(). + * + * @ipd_port: IPD/PKO port to query + * + * Returns Link state + */ +cvmx_helper_link_info_t __cvmx_helper_xaui_link_get(int ipd_port) +{ + int interface = cvmx_helper_get_interface_num(ipd_port); + union cvmx_gmxx_tx_xaui_ctl gmxx_tx_xaui_ctl; + union cvmx_gmxx_rx_xaui_ctl gmxx_rx_xaui_ctl; + union cvmx_pcsxx_status1_reg pcsxx_status1_reg; + cvmx_helper_link_info_t result; + + gmxx_tx_xaui_ctl.u64 = cvmx_read_csr(CVMX_GMXX_TX_XAUI_CTL(interface)); + gmxx_rx_xaui_ctl.u64 = cvmx_read_csr(CVMX_GMXX_RX_XAUI_CTL(interface)); + pcsxx_status1_reg.u64 = + cvmx_read_csr(CVMX_PCSXX_STATUS1_REG(interface)); + result.u64 = 0; + + /* Only return a link if both RX and TX are happy */ + if ((gmxx_tx_xaui_ctl.s.ls == 0) && (gmxx_rx_xaui_ctl.s.status == 0) && + (pcsxx_status1_reg.s.rcv_lnk == 1)) { + result.s.link_up = 1; + result.s.full_duplex = 1; + result.s.speed = 10000; + } else { + /* Disable GMX and PCSX interrupts. */ + cvmx_write_csr(CVMX_GMXX_RXX_INT_EN(0, interface), 0x0); + cvmx_write_csr(CVMX_GMXX_TX_INT_EN(interface), 0x0); + cvmx_write_csr(CVMX_PCSXX_INT_EN_REG(interface), 0x0); + } + return result; +} + +/** + * 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. + * + * @ipd_port: IPD/PKO port to configure + * @link_info: The new link state + * + * Returns Zero on success, negative on failure + */ +int __cvmx_helper_xaui_link_set(int ipd_port, cvmx_helper_link_info_t link_info) +{ + int interface = cvmx_helper_get_interface_num(ipd_port); + union cvmx_gmxx_tx_xaui_ctl gmxx_tx_xaui_ctl; + union cvmx_gmxx_rx_xaui_ctl gmxx_rx_xaui_ctl; + + gmxx_tx_xaui_ctl.u64 = cvmx_read_csr(CVMX_GMXX_TX_XAUI_CTL(interface)); + gmxx_rx_xaui_ctl.u64 = cvmx_read_csr(CVMX_GMXX_RX_XAUI_CTL(interface)); + + /* If the link shouldn't be up, then just return */ + if (!link_info.s.link_up) + return 0; + + /* Do nothing if both RX and TX are happy */ + if ((gmxx_tx_xaui_ctl.s.ls == 0) && (gmxx_rx_xaui_ctl.s.status == 0)) + return 0; + + /* Bring the link up */ + return __cvmx_helper_xaui_enable(interface); +} + +/** + * 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. + * + * @ipd_port: IPD/PKO port to loopback. + * @enable_internal: + * Non zero if you want internal loopback + * @enable_external: + * Non zero if you want external loopback + * + * Returns Zero on success, negative on failure. + */ +extern int __cvmx_helper_xaui_configure_loopback(int ipd_port, + int enable_internal, + int enable_external) +{ + int interface = cvmx_helper_get_interface_num(ipd_port); + union cvmx_pcsxx_control1_reg pcsxx_control1_reg; + union cvmx_gmxx_xaui_ext_loopback gmxx_xaui_ext_loopback; + + /* Set the internal loop */ + pcsxx_control1_reg.u64 = + cvmx_read_csr(CVMX_PCSXX_CONTROL1_REG(interface)); + pcsxx_control1_reg.s.loopbck1 = enable_internal; + cvmx_write_csr(CVMX_PCSXX_CONTROL1_REG(interface), + pcsxx_control1_reg.u64); + + /* Set the external loop */ + gmxx_xaui_ext_loopback.u64 = + cvmx_read_csr(CVMX_GMXX_XAUI_EXT_LOOPBACK(interface)); + gmxx_xaui_ext_loopback.s.en = enable_external; + cvmx_write_csr(CVMX_GMXX_XAUI_EXT_LOOPBACK(interface), + gmxx_xaui_ext_loopback.u64); + + /* Take the link through a reset */ + return __cvmx_helper_xaui_enable(interface); +} diff --git a/arch/mips/cavium-octeon/executive/cvmx-helper.c b/arch/mips/cavium-octeon/executive/cvmx-helper.c new file mode 100644 index 000000000000..daa8c8bbcfe4 --- /dev/null +++ b/arch/mips/cavium-octeon/executive/cvmx-helper.c @@ -0,0 +1,1053 @@ +/***********************license start*************** + * Author: Cavium Networks + * + * Contact: support@caviumnetworks.com + * This file is part of the OCTEON SDK + * + * Copyright (c) 2003-2008 Cavium Networks + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, Version 2, as + * published by the Free Software Foundation. + * + * This file is distributed in the hope that it will be useful, but + * AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or + * NONINFRINGEMENT. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License + * along with this file; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * or visit http://www.gnu.org/licenses/. + * + * This file may also be available under a different license from Cavium. + * Contact Cavium Networks for more information + ***********************license end**************************************/ + +/* + * + * Helper functions for common, but complicated tasks. + * + */ +#include <asm/octeon/octeon.h> + +#include <asm/octeon/cvmx-config.h> + +#include <asm/octeon/cvmx-fpa.h> +#include <asm/octeon/cvmx-pip.h> +#include <asm/octeon/cvmx-pko.h> +#include <asm/octeon/cvmx-ipd.h> +#include <asm/octeon/cvmx-spi.h> +#include <asm/octeon/cvmx-helper.h> +#include <asm/octeon/cvmx-helper-board.h> + +#include <asm/octeon/cvmx-pip-defs.h> +#include <asm/octeon/cvmx-smix-defs.h> +#include <asm/octeon/cvmx-asxx-defs.h> + +/** + * cvmx_override_pko_queue_priority(int ipd_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. + */ +void (*cvmx_override_pko_queue_priority) (int pko_port, + uint64_t priorities[16]); + +/** + * cvmx_override_ipd_port_setup(int ipd_port) is a function + * pointer. It is meant to allow customization of the IPD port + * 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. + */ +void (*cvmx_override_ipd_port_setup) (int ipd_port); + +/* Port count per interface */ +static int interface_port_count[4] = { 0, 0, 0, 0 }; + +/* Port last configured link info index by IPD/PKO port */ +static cvmx_helper_link_info_t + port_link_info[CVMX_PIP_NUM_INPUT_PORTS]; + +/** + * 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. + * + * Returns Number of interfaces on chip + */ +int cvmx_helper_get_number_of_interfaces(void) +{ + if (OCTEON_IS_MODEL(OCTEON_CN56XX) || OCTEON_IS_MODEL(OCTEON_CN52XX)) + return 4; + else + return 3; +} + +/** + * 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. + * + * @interface: Interface to get the port count for + * + * Returns Number of ports on interface. Can be Zero. + */ +int cvmx_helper_ports_on_interface(int interface) +{ + return interface_port_count[interface]; +} + +/** + * 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. + * + * @interface: Interface to probe + * + * Returns Mode of the interface. Unknown or unsupported interfaces return + * DISABLED. + */ +cvmx_helper_interface_mode_t cvmx_helper_interface_get_mode(int interface) +{ + union cvmx_gmxx_inf_mode mode; + if (interface == 2) + return CVMX_HELPER_INTERFACE_MODE_NPI; + + if (interface == 3) { + if (OCTEON_IS_MODEL(OCTEON_CN56XX) + || OCTEON_IS_MODEL(OCTEON_CN52XX)) + return CVMX_HELPER_INTERFACE_MODE_LOOP; + 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))) + 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 (!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; + } +} + +/** + * 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. + * + * @ipd_port: Port to configure. This follows the IPD numbering, not the + * per interface numbering + * + * Returns Zero on success, negative on failure + */ +static int __cvmx_helper_port_setup_ipd(int ipd_port) +{ + union cvmx_pip_prt_cfgx port_config; + union cvmx_pip_prt_tagx tag_config; + + 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; +} + +/** + * This function probes an interface to determine the actual + * number of hardware ports connected to it. It doesn't setup the + * ports or enable them. The main goal here is to set the global + * interface_port_count[interface] correctly. Hardware setup of the + * ports will be performed later. + * + * @interface: Interface to probe + * + * Returns 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 */ + 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: + interface_port_count[interface] = 0; + break; + /* XAUI is a single high speed port */ + case CVMX_HELPER_INTERFACE_MODE_XAUI: + interface_port_count[interface] = + __cvmx_helper_xaui_probe(interface); + 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: + interface_port_count[interface] = + __cvmx_helper_rgmii_probe(interface); + break; + /* + * SPI4 can have 1-16 ports depending on the device at + * the other end. + */ + case CVMX_HELPER_INTERFACE_MODE_SPI: + interface_port_count[interface] = + __cvmx_helper_spi_probe(interface); + break; + /* + * 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: + interface_port_count[interface] = + __cvmx_helper_sgmii_probe(interface); + break; + /* PCI target Network Packet Interface */ + case CVMX_HELPER_INTERFACE_MODE_NPI: + interface_port_count[interface] = + __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: + interface_port_count[interface] = + __cvmx_helper_loop_probe(interface); + break; + } + + interface_port_count[interface] = + __cvmx_helper_board_interface_probe(interface, + interface_port_count + [interface]); + + /* Make sure all global variables propagate to other cores */ + CVMX_SYNCWS; + + return 0; +} + +/** + * 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. + * + * @interface: Interface to setup IPD/PIP for + * + * Returns Zero on success, negative on failure + */ +static int __cvmx_helper_interface_setup_ipd(int interface) +{ + int ipd_port = cvmx_helper_get_ipd_port(interface, 0); + int num_ports = interface_port_count[interface]; + + while (num_ports--) { + __cvmx_helper_port_setup_ipd(ipd_port); + ipd_port++; + } + return 0; +} + +/** + * Setup global setting for IPD/PIP not related to a specific + * interface or port. This must be called before IPD is enabled. + * + * Returns Zero on success, negative on failure. + */ +static int __cvmx_helper_global_setup_ipd(void) +{ + /* 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, + /* The +8 is to account for the next ptr */ + (CVMX_HELPER_FIRST_MBUFF_SKIP + 8) / 128, + /* The +8 is to account for the next ptr */ + (CVMX_HELPER_NOT_FIRST_MBUFF_SKIP + 8) / 128, + CVMX_FPA_WQE_POOL, + CVMX_IPD_OPC_MODE_STT, + CVMX_HELPER_ENABLE_BACK_PRESSURE); + return 0; +} + +/** + * 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. + * + * @interface: Interface to setup PKO for + * + * Returns 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 }; + + /* + * 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 = interface_port_count[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; +} + +/** + * Setup global setting for PKO not related to a specific + * interface or port. This must be called before PKO is enabled. + * + * Returns 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. + */ + union cvmx_iob_fau_timeout 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); + return 0; +} + +/** + * Setup global backpressure setting. + * + * Returns 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 < num_interfaces; interface++) { + switch (cvmx_helper_interface_get_mode(interface)) { + case CVMX_HELPER_INTERFACE_MODE_DISABLED: + case CVMX_HELPER_INTERFACE_MODE_PCIE: + case CVMX_HELPER_INTERFACE_MODE_NPI: + case CVMX_HELPER_INTERFACE_MODE_LOOP: + case CVMX_HELPER_INTERFACE_MODE_XAUI: + break; + case CVMX_HELPER_INTERFACE_MODE_RGMII: + case CVMX_HELPER_INTERFACE_MODE_GMII: + case CVMX_HELPER_INTERFACE_MODE_SPI: + case CVMX_HELPER_INTERFACE_MODE_SGMII: + case CVMX_HELPER_INTERFACE_MODE_PICMG: + cvmx_gmx_set_backpressure_override(interface, 0xf); + break; + } + } +#endif + + return 0; +} + +/** + * Enable packet input/output from the hardware. This function is + * called after all internal setup is complete and IPD is enabled. + * After this function completes, packets will be accepted from the + * hardware ports. PKO should still be disabled to make sure packets + * aren't sent out partially setup hardware. + * + * @interface: Interface to enable + * + * Returns Zero on success, negative on failure + */ +static int __cvmx_helper_packet_hardware_enable(int interface) +{ + int result = 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: + /* Nothing to do */ + break; + /* XAUI is a single high speed port */ + case CVMX_HELPER_INTERFACE_MODE_XAUI: + result = __cvmx_helper_xaui_enable(interface); + 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_enable(interface); + break; + /* + * SPI4 can have 1-16 ports depending on the device at + * the other end + */ + case CVMX_HELPER_INTERFACE_MODE_SPI: + result = __cvmx_helper_spi_enable(interface); + break; + /* + * 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: + result = __cvmx_helper_sgmii_enable(interface); + break; + /* PCI target Network Packet Interface */ + case CVMX_HELPER_INTERFACE_MODE_NPI: + result = __cvmx_helper_npi_enable(interface); + break; + /* + * Special loopback only ports. These are not the same + * as other ports in loopback mode + */ + case CVMX_HELPER_INTERFACE_MODE_LOOP: + result = __cvmx_helper_loop_enable(interface); + break; + } + result |= __cvmx_helper_board_hardware_enable(interface); + return result; +} + +/** + * Function to adjust internal IPD pointer alignments + * + * Returns 0 on success + * !0 on failure + */ +int __cvmx_helper_errata_fix_ipd_ptr_alignment(void) +{ +#define FIX_IPD_FIRST_BUFF_PAYLOAD_BYTES \ + (CVMX_FPA_PACKET_POOL_SIZE-8-CVMX_HELPER_FIRST_MBUFF_SKIP) +#define FIX_IPD_NON_FIRST_BUFF_PAYLOAD_BYTES \ + (CVMX_FPA_PACKET_POOL_SIZE-8-CVMX_HELPER_NOT_FIRST_MBUFF_SKIP) +#define FIX_IPD_OUTPORT 0 + /* Ports 0-15 are interface 0, 16-31 are interface 1 */ +#define INTERFACE(port) (port >> 4) +#define INDEX(port) (port & 0xf) + uint64_t *p64; + cvmx_pko_command_word0_t pko_command; + union cvmx_buf_ptr g_buffer, pkt_buffer; + cvmx_wqe_t *work; + int size, num_segs = 0, wqe_pcnt, pkt_pcnt; + union cvmx_gmxx_prtx_cfg gmx_cfg; + int retry_cnt; + int retry_loop_cnt; + int i; + cvmx_helper_link_info_t link_info; + + /* Save values for restore at end */ + uint64_t prtx_cfg = + cvmx_read_csr(CVMX_GMXX_PRTX_CFG + (INDEX(FIX_IPD_OUTPORT), INTERFACE(FIX_IPD_OUTPORT))); + uint64_t tx_ptr_en = + cvmx_read_csr(CVMX_ASXX_TX_PRT_EN(INTERFACE(FIX_IPD_OUTPORT))); + uint64_t rx_ptr_en = + cvmx_read_csr(CVMX_ASXX_RX_PRT_EN(INTERFACE(FIX_IPD_OUTPORT))); + uint64_t rxx_jabber = + cvmx_read_csr(CVMX_GMXX_RXX_JABBER + (INDEX(FIX_IPD_OUTPORT), INTERFACE(FIX_IPD_OUTPORT))); + uint64_t frame_max = + cvmx_read_csr(CVMX_GMXX_RXX_FRM_MAX + (INDEX(FIX_IPD_OUTPORT), INTERFACE(FIX_IPD_OUTPORT))); + + /* Configure port to gig FDX as required for loopback mode */ + cvmx_helper_rgmii_internal_loopback(FIX_IPD_OUTPORT); + + /* + * Disable reception on all ports so if traffic is present it + * will not interfere. + */ + cvmx_write_csr(CVMX_ASXX_RX_PRT_EN(INTERFACE(FIX_IPD_OUTPORT)), 0); + + cvmx_wait(100000000ull); + + for (retry_loop_cnt = 0; retry_loop_cnt < 10; retry_loop_cnt++) { + retry_cnt = 100000; + wqe_pcnt = cvmx_read_csr(CVMX_IPD_PTR_COUNT); + pkt_pcnt = (wqe_pcnt >> 7) & 0x7f; + wqe_pcnt &= 0x7f; + + num_segs = (2 + pkt_pcnt - wqe_pcnt) & 3; + + if (num_segs == 0) + goto fix_ipd_exit; + + num_segs += 1; + + size = + FIX_IPD_FIRST_BUFF_PAYLOAD_BYTES + + ((num_segs - 1) * FIX_IPD_NON_FIRST_BUFF_PAYLOAD_BYTES) - + (FIX_IPD_NON_FIRST_BUFF_PAYLOAD_BYTES / 2); + + cvmx_write_csr(CVMX_ASXX_PRT_LOOP(INTERFACE(FIX_IPD_OUTPORT)), + 1 << INDEX(FIX_IPD_OUTPORT)); + CVMX_SYNC; + + g_buffer.u64 = 0; + g_buffer.s.addr = + cvmx_ptr_to_phys(cvmx_fpa_alloc(CVMX_FPA_WQE_POOL)); + if (g_buffer.s.addr == 0) { + cvmx_dprintf("WARNING: FIX_IPD_PTR_ALIGNMENT " + "buffer allocation failure.\n"); + goto fix_ipd_exit; + } + + g_buffer.s.pool = CVMX_FPA_WQE_POOL; + g_buffer.s.size = num_segs; + + pkt_buffer.u64 = 0; + pkt_buffer.s.addr = + cvmx_ptr_to_phys(cvmx_fpa_alloc(CVMX_FPA_PACKET_POOL)); + if (pkt_buffer.s.addr == 0) { + cvmx_dprintf("WARNING: FIX_IPD_PTR_ALIGNMENT " + "buffer allocation failure.\n"); + goto fix_ipd_exit; + } + pkt_buffer.s.i = 1; + pkt_buffer.s.pool = CVMX_FPA_PACKET_POOL; + pkt_buffer.s.size = FIX_IPD_FIRST_BUFF_PAYLOAD_BYTES; + + p64 = (uint64_t *) cvmx_phys_to_ptr(pkt_buffer.s.addr); + p64[0] = 0xffffffffffff0000ull; + p64[1] = 0x08004510ull; + p64[2] = ((uint64_t) (size - 14) << 48) | 0x5ae740004000ull; + p64[3] = 0x3a5fc0a81073c0a8ull; + + for (i = 0; i < num_segs; i++) { + if (i > 0) + pkt_buffer.s.size = + FIX_IPD_NON_FIRST_BUFF_PAYLOAD_BYTES; + + if (i == (num_segs - 1)) + pkt_buffer.s.i = 0; + + *(uint64_t *) cvmx_phys_to_ptr(g_buffer.s.addr + + 8 * i) = pkt_buffer.u64; + } + + /* Build the PKO command */ + pko_command.u64 = 0; + pko_command.s.segs = num_segs; + pko_command.s.total_bytes = size; + pko_command.s.dontfree = 0; + pko_command.s.gather = 1; + + gmx_cfg.u64 = + cvmx_read_csr(CVMX_GMXX_PRTX_CFG + (INDEX(FIX_IPD_OUTPORT), + INTERFACE(FIX_IPD_OUTPORT))); + gmx_cfg.s.en = 1; + cvmx_write_csr(CVMX_GMXX_PRTX_CFG + (INDEX(FIX_IPD_OUTPORT), + INTERFACE(FIX_IPD_OUTPORT)), gmx_cfg.u64); + cvmx_write_csr(CVMX_ASXX_TX_PRT_EN(INTERFACE(FIX_IPD_OUTPORT)), + 1 << INDEX(FIX_IPD_OUTPORT)); + cvmx_write_csr(CVMX_ASXX_RX_PRT_EN(INTERFACE(FIX_IPD_OUTPORT)), + 1 << INDEX(FIX_IPD_OUTPORT)); + + cvmx_write_csr(CVMX_GMXX_RXX_JABBER + (INDEX(FIX_IPD_OUTPORT), + INTERFACE(FIX_IPD_OUTPORT)), 65392 - 14 - 4); + cvmx_write_csr(CVMX_GMXX_RXX_FRM_MAX + (INDEX(FIX_IPD_OUTPORT), + INTERFACE(FIX_IPD_OUTPORT)), 65392 - 14 - 4); + + cvmx_pko_send_packet_prepare(FIX_IPD_OUTPORT, + cvmx_pko_get_base_queue + (FIX_IPD_OUTPORT), + CVMX_PKO_LOCK_CMD_QUEUE); + cvmx_pko_send_packet_finish(FIX_IPD_OUTPORT, + cvmx_pko_get_base_queue + (FIX_IPD_OUTPORT), pko_command, + g_buffer, CVMX_PKO_LOCK_CMD_QUEUE); + + CVMX_SYNC; + + do { + work = cvmx_pow_work_request_sync(CVMX_POW_WAIT); + retry_cnt--; + } while ((work == NULL) && (retry_cnt > 0)); + + if (!retry_cnt) + cvmx_dprintf("WARNING: FIX_IPD_PTR_ALIGNMENT " + "get_work() timeout occurred.\n"); + + /* Free packet */ + if (work) + cvmx_helper_free_packet_data(work); + } + +fix_ipd_exit: + + /* Return CSR configs to saved values */ + cvmx_write_csr(CVMX_GMXX_PRTX_CFG + (INDEX(FIX_IPD_OUTPORT), INTERFACE(FIX_IPD_OUTPORT)), + prtx_cfg); + cvmx_write_csr(CVMX_ASXX_TX_PRT_EN(INTERFACE(FIX_IPD_OUTPORT)), + tx_ptr_en); + cvmx_write_csr(CVMX_ASXX_RX_PRT_EN(INTERFACE(FIX_IPD_OUTPORT)), + rx_ptr_en); + cvmx_write_csr(CVMX_GMXX_RXX_JABBER + (INDEX(FIX_IPD_OUTPORT), INTERFACE(FIX_IPD_OUTPORT)), + rxx_jabber); + cvmx_write_csr(CVMX_GMXX_RXX_FRM_MAX + (INDEX(FIX_IPD_OUTPORT), INTERFACE(FIX_IPD_OUTPORT)), + frame_max); + cvmx_write_csr(CVMX_ASXX_PRT_LOOP(INTERFACE(FIX_IPD_OUTPORT)), 0); + /* Set link to down so autonegotiation will set it up again */ + link_info.u64 = 0; + cvmx_helper_link_set(FIX_IPD_OUTPORT, link_info); + + /* + * Bring the link back up as autonegotiation is not done in + * user applications. + */ + cvmx_helper_link_autoconf(FIX_IPD_OUTPORT); + + CVMX_SYNC; + if (num_segs) + cvmx_dprintf("WARNING: FIX_IPD_PTR_ALIGNMENT failed.\n"); + + return !!num_segs; + +} + +/** + * Called after all internal packet IO paths are setup. This + * function enables IPD/PIP and begins packet input and output. + * + * Returns Zero on success, negative on failure + */ +int cvmx_helper_ipd_and_packet_input_enable(void) +{ + int num_interfaces; + int interface; + + /* Enable IPD */ + cvmx_ipd_enable(); + + /* + * Time to enable hardware ports packet input and output. Note + * that at this point IPD/PIP must be fully functional and PKO + * must be disabled + */ + num_interfaces = cvmx_helper_get_number_of_interfaces(); + for (interface = 0; interface < num_interfaces; interface++) { + if (cvmx_helper_ports_on_interface(interface) > 0) + __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; +} + +/** + * 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. + * + * Returns Zero on success, non-zero on failure + */ +int cvmx_helper_initialize_packet_io_global(void) +{ + int result = 0; + int interface; + union cvmx_l2c_cfg l2c_cfg; + union cvmx_smix_en 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. + */ + 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); + + /* Make sure SMI/MDIO is enabled so we can query PHYs */ + smix_en.u64 = cvmx_read_csr(CVMX_SMIX_EN(0)); + if (!smix_en.s.en) { + smix_en.s.en = 1; + cvmx_write_csr(CVMX_SMIX_EN(0), smix_en.u64); + } + + /* Newer chips actually have two SMI/MDIO interfaces */ + if (!OCTEON_IS_MODEL(OCTEON_CN3XXX) && + !OCTEON_IS_MODEL(OCTEON_CN58XX) && + !OCTEON_IS_MODEL(OCTEON_CN50XX)) { + smix_en.u64 = cvmx_read_csr(CVMX_SMIX_EN(1)); + if (!smix_en.s.en) { + smix_en.s.en = 1; + cvmx_write_csr(CVMX_SMIX_EN(1), smix_en.u64); + } + } + + cvmx_pko_initialize_global(); + for (interface = 0; interface < num_interfaces; interface++) { + result |= cvmx_helper_interface_probe(interface); + if (cvmx_helper_ports_on_interface(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); + 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; +} + +/** + * Does core local initialization for packet io + * + * Returns Zero on success, non-zero on failure + */ +int cvmx_helper_initialize_packet_io_local(void) +{ + return cvmx_pko_initialize_local(); +} + +/** + * 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)); + * + * @ipd_port: IPD/PKO port to auto configure + * + * Returns 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 == port_link_info[ipd_port].u64) + return link_info; + + /* If we fail to set the link speed, port_link_info will not change */ + cvmx_helper_link_set(ipd_port, link_info); + + /* + * port_link_info should be the current value, which will be + * different than expect if cvmx_helper_link_set() failed. + */ + return port_link_info[ipd_port]; +} + +/** + * 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(). + * + * @ipd_port: IPD/PKO port to query + * + * Returns 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: + 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_NPI: + case CVMX_HELPER_INTERFACE_MODE_LOOP: + /* Network links are not supported */ + break; + } + return result; +} + +/** + * 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. + * + * @ipd_port: IPD/PKO port to configure + * @link_info: The new link state + * + * Returns 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: + 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_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) + port_link_info[ipd_port].u64 = link_info.u64; + return result; +} + +/** + * 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. + * + * @ipd_port: IPD/PKO port to loopback. + * @enable_internal: + * Non zero if you want internal loopback + * @enable_external: + * Non zero if you want external loopback + * + * Returns 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_SPI: + case CVMX_HELPER_INTERFACE_MODE_NPI: + case CVMX_HELPER_INTERFACE_MODE_LOOP: + break; + case CVMX_HELPER_INTERFACE_MODE_XAUI: + 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; +} diff --git a/arch/mips/cavium-octeon/executive/cvmx-interrupt-decodes.c b/arch/mips/cavium-octeon/executive/cvmx-interrupt-decodes.c new file mode 100644 index 000000000000..e59d1b79f24c --- /dev/null +++ b/arch/mips/cavium-octeon/executive/cvmx-interrupt-decodes.c @@ -0,0 +1,371 @@ +/***********************license start*************** + * Author: Cavium Networks + * + * Contact: support@caviumnetworks.com + * This file is part of the OCTEON SDK + * + * Copyright (c) 2003-2009 Cavium Networks + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, Version 2, as + * published by the Free Software Foundation. + * + * This file is distributed in the hope that it will be useful, but + * AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or + * NONINFRINGEMENT. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License + * along with this file; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * or visit http://www.gnu.org/licenses/. + * + * This file may also be available under a different license from Cavium. + * Contact Cavium Networks for more information + ***********************license end**************************************/ + +/* + * + * Automatically generated functions useful for enabling + * and decoding RSL_INT_BLOCKS interrupts. + * + */ + +#include <asm/octeon/octeon.h> + +#include <asm/octeon/cvmx-gmxx-defs.h> +#include <asm/octeon/cvmx-pcsx-defs.h> +#include <asm/octeon/cvmx-pcsxx-defs.h> +#include <asm/octeon/cvmx-spxx-defs.h> +#include <asm/octeon/cvmx-stxx-defs.h> + +#ifndef PRINT_ERROR +#define PRINT_ERROR(format, ...) +#endif + + +/** + * __cvmx_interrupt_gmxx_rxx_int_en_enable enables all interrupt bits in cvmx_gmxx_rxx_int_en_t + */ +void __cvmx_interrupt_gmxx_rxx_int_en_enable(int index, int block) +{ + union cvmx_gmxx_rxx_int_en gmx_rx_int_en; + cvmx_write_csr(CVMX_GMXX_RXX_INT_REG(index, block), + cvmx_read_csr(CVMX_GMXX_RXX_INT_REG(index, block))); + gmx_rx_int_en.u64 = 0; + if (OCTEON_IS_MODEL(OCTEON_CN56XX)) { + /* Skipping gmx_rx_int_en.s.reserved_29_63 */ + gmx_rx_int_en.s.hg2cc = 1; + gmx_rx_int_en.s.hg2fld = 1; + gmx_rx_int_en.s.undat = 1; + gmx_rx_int_en.s.uneop = 1; + gmx_rx_int_en.s.unsop = 1; + gmx_rx_int_en.s.bad_term = 1; + gmx_rx_int_en.s.bad_seq = 1; + gmx_rx_int_en.s.rem_fault = 1; + gmx_rx_int_en.s.loc_fault = 1; + gmx_rx_int_en.s.pause_drp = 1; + /* Skipping gmx_rx_int_en.s.reserved_16_18 */ + /*gmx_rx_int_en.s.ifgerr = 1; */ + /*gmx_rx_int_en.s.coldet = 1; // Collsion detect */ + /*gmx_rx_int_en.s.falerr = 1; // False carrier error or extend error after slottime */ + /*gmx_rx_int_en.s.rsverr = 1; // RGMII reserved opcodes */ + /*gmx_rx_int_en.s.pcterr = 1; // Bad Preamble / Protocol */ + gmx_rx_int_en.s.ovrerr = 1; + /* Skipping gmx_rx_int_en.s.reserved_9_9 */ + gmx_rx_int_en.s.skperr = 1; + gmx_rx_int_en.s.rcverr = 1; + /* Skipping gmx_rx_int_en.s.reserved_5_6 */ + /*gmx_rx_int_en.s.fcserr = 1; // FCS errors are handled when we get work */ + gmx_rx_int_en.s.jabber = 1; + /* Skipping gmx_rx_int_en.s.reserved_2_2 */ + gmx_rx_int_en.s.carext = 1; + /* Skipping gmx_rx_int_en.s.reserved_0_0 */ + } + if (OCTEON_IS_MODEL(OCTEON_CN30XX)) { + /* Skipping gmx_rx_int_en.s.reserved_19_63 */ + /*gmx_rx_int_en.s.phy_dupx = 1; */ + /*gmx_rx_int_en.s.phy_spd = 1; */ + /*gmx_rx_int_en.s.phy_link = 1; */ + /*gmx_rx_int_en.s.ifgerr = 1; */ + /*gmx_rx_int_en.s.coldet = 1; // Collsion detect */ + /*gmx_rx_int_en.s.falerr = 1; // False carrier error or extend error after slottime */ + /*gmx_rx_int_en.s.rsverr = 1; // RGMII reserved opcodes */ + /*gmx_rx_int_en.s.pcterr = 1; // Bad Preamble / Protocol */ + gmx_rx_int_en.s.ovrerr = 1; + gmx_rx_int_en.s.niberr = 1; + gmx_rx_int_en.s.skperr = 1; + gmx_rx_int_en.s.rcverr = 1; + /*gmx_rx_int_en.s.lenerr = 1; // Length errors are handled when we get work */ + gmx_rx_int_en.s.alnerr = 1; + /*gmx_rx_int_en.s.fcserr = 1; // FCS errors are handled when we get work */ + gmx_rx_int_en.s.jabber = 1; + gmx_rx_int_en.s.maxerr = 1; + gmx_rx_int_en.s.carext = 1; + gmx_rx_int_en.s.minerr = 1; + } + if (OCTEON_IS_MODEL(OCTEON_CN50XX)) { + /* Skipping gmx_rx_int_en.s.reserved_20_63 */ + gmx_rx_int_en.s.pause_drp = 1; + /*gmx_rx_int_en.s.phy_dupx = 1; */ + /*gmx_rx_int_en.s.phy_spd = 1; */ + /*gmx_rx_int_en.s.phy_link = 1; */ + /*gmx_rx_int_en.s.ifgerr = 1; */ + /*gmx_rx_int_en.s.coldet = 1; // Collsion detect */ + /*gmx_rx_int_en.s.falerr = 1; // False carrier error or extend error after slottime */ + /*gmx_rx_int_en.s.rsverr = 1; // RGMII reserved opcodes */ + /*gmx_rx_int_en.s.pcterr = 1; // Bad Preamble / Protocol */ + gmx_rx_int_en.s.ovrerr = 1; + gmx_rx_int_en.s.niberr = 1; + gmx_rx_int_en.s.skperr = 1; + gmx_rx_int_en.s.rcverr = 1; + /* Skipping gmx_rx_int_en.s.reserved_6_6 */ + gmx_rx_int_en.s.alnerr = 1; + /*gmx_rx_int_en.s.fcserr = 1; // FCS errors are handled when we get work */ + gmx_rx_int_en.s.jabber = 1; + /* Skipping gmx_rx_int_en.s.reserved_2_2 */ + gmx_rx_int_en.s.carext = 1; + /* Skipping gmx_rx_int_en.s.reserved_0_0 */ + } + if (OCTEON_IS_MODEL(OCTEON_CN38XX)) { + /* Skipping gmx_rx_int_en.s.reserved_19_63 */ + /*gmx_rx_int_en.s.phy_dupx = 1; */ + /*gmx_rx_int_en.s.phy_spd = 1; */ + /*gmx_rx_int_en.s.phy_link = 1; */ + /*gmx_rx_int_en.s.ifgerr = 1; */ + /*gmx_rx_int_en.s.coldet = 1; // Collsion detect */ + /*gmx_rx_int_en.s.falerr = 1; // False carrier error or extend error after slottime */ + /*gmx_rx_int_en.s.rsverr = 1; // RGMII reserved opcodes */ + /*gmx_rx_int_en.s.pcterr = 1; // Bad Preamble / Protocol */ + gmx_rx_int_en.s.ovrerr = 1; + gmx_rx_int_en.s.niberr = 1; + gmx_rx_int_en.s.skperr = 1; + gmx_rx_int_en.s.rcverr = 1; + /*gmx_rx_int_en.s.lenerr = 1; // Length errors are handled when we get work */ + gmx_rx_int_en.s.alnerr = 1; + /*gmx_rx_int_en.s.fcserr = 1; // FCS errors are handled when we get work */ + gmx_rx_int_en.s.jabber = 1; + gmx_rx_int_en.s.maxerr = 1; + gmx_rx_int_en.s.carext = 1; + gmx_rx_int_en.s.minerr = 1; + } + if (OCTEON_IS_MODEL(OCTEON_CN31XX)) { + /* Skipping gmx_rx_int_en.s.reserved_19_63 */ + /*gmx_rx_int_en.s.phy_dupx = 1; */ + /*gmx_rx_int_en.s.phy_spd = 1; */ + /*gmx_rx_int_en.s.phy_link = 1; */ + /*gmx_rx_int_en.s.ifgerr = 1; */ + /*gmx_rx_int_en.s.coldet = 1; // Collsion detect */ + /*gmx_rx_int_en.s.falerr = 1; // False carrier error or extend error after slottime */ + /*gmx_rx_int_en.s.rsverr = 1; // RGMII reserved opcodes */ + /*gmx_rx_int_en.s.pcterr = 1; // Bad Preamble / Protocol */ + gmx_rx_int_en.s.ovrerr = 1; + gmx_rx_int_en.s.niberr = 1; + gmx_rx_int_en.s.skperr = 1; + gmx_rx_int_en.s.rcverr = 1; + /*gmx_rx_int_en.s.lenerr = 1; // Length errors are handled when we get work */ + gmx_rx_int_en.s.alnerr = 1; + /*gmx_rx_int_en.s.fcserr = 1; // FCS errors are handled when we get work */ + gmx_rx_int_en.s.jabber = 1; + gmx_rx_int_en.s.maxerr = 1; + gmx_rx_int_en.s.carext = 1; + gmx_rx_int_en.s.minerr = 1; + } + if (OCTEON_IS_MODEL(OCTEON_CN58XX)) { + /* Skipping gmx_rx_int_en.s.reserved_20_63 */ + gmx_rx_int_en.s.pause_drp = 1; + /*gmx_rx_int_en.s.phy_dupx = 1; */ + /*gmx_rx_int_en.s.phy_spd = 1; */ + /*gmx_rx_int_en.s.phy_link = 1; */ + /*gmx_rx_int_en.s.ifgerr = 1; */ + /*gmx_rx_int_en.s.coldet = 1; // Collsion detect */ + /*gmx_rx_int_en.s.falerr = 1; // False carrier error or extend error after slottime */ + /*gmx_rx_int_en.s.rsverr = 1; // RGMII reserved opcodes */ + /*gmx_rx_int_en.s.pcterr = 1; // Bad Preamble / Protocol */ + gmx_rx_int_en.s.ovrerr = 1; + gmx_rx_int_en.s.niberr = 1; + gmx_rx_int_en.s.skperr = 1; + gmx_rx_int_en.s.rcverr = 1; + /*gmx_rx_int_en.s.lenerr = 1; // Length errors are handled when we get work */ + gmx_rx_int_en.s.alnerr = 1; + /*gmx_rx_int_en.s.fcserr = 1; // FCS errors are handled when we get work */ + gmx_rx_int_en.s.jabber = 1; + gmx_rx_int_en.s.maxerr = 1; + gmx_rx_int_en.s.carext = 1; + gmx_rx_int_en.s.minerr = 1; + } + if (OCTEON_IS_MODEL(OCTEON_CN52XX)) { + /* Skipping gmx_rx_int_en.s.reserved_29_63 */ + gmx_rx_int_en.s.hg2cc = 1; + gmx_rx_int_en.s.hg2fld = 1; + gmx_rx_int_en.s.undat = 1; + gmx_rx_int_en.s.uneop = 1; + gmx_rx_int_en.s.unsop = 1; + gmx_rx_int_en.s.bad_term = 1; + gmx_rx_int_en.s.bad_seq = 0; + gmx_rx_int_en.s.rem_fault = 1; + gmx_rx_int_en.s.loc_fault = 0; + gmx_rx_int_en.s.pause_drp = 1; + /* Skipping gmx_rx_int_en.s.reserved_16_18 */ + /*gmx_rx_int_en.s.ifgerr = 1; */ + /*gmx_rx_int_en.s.coldet = 1; // Collsion detect */ + /*gmx_rx_int_en.s.falerr = 1; // False carrier error or extend error after slottime */ + /*gmx_rx_int_en.s.rsverr = 1; // RGMII reserved opcodes */ + /*gmx_rx_int_en.s.pcterr = 1; // Bad Preamble / Protocol */ + gmx_rx_int_en.s.ovrerr = 1; + /* Skipping gmx_rx_int_en.s.reserved_9_9 */ + gmx_rx_int_en.s.skperr = 1; + gmx_rx_int_en.s.rcverr = 1; + /* Skipping gmx_rx_int_en.s.reserved_5_6 */ + /*gmx_rx_int_en.s.fcserr = 1; // FCS errors are handled when we get work */ + gmx_rx_int_en.s.jabber = 1; + /* Skipping gmx_rx_int_en.s.reserved_2_2 */ + gmx_rx_int_en.s.carext = 1; + /* Skipping gmx_rx_int_en.s.reserved_0_0 */ + } + cvmx_write_csr(CVMX_GMXX_RXX_INT_EN(index, block), gmx_rx_int_en.u64); +} +/** + * __cvmx_interrupt_pcsx_intx_en_reg_enable enables all interrupt bits in cvmx_pcsx_intx_en_reg_t + */ +void __cvmx_interrupt_pcsx_intx_en_reg_enable(int index, int block) +{ + union cvmx_pcsx_intx_en_reg pcs_int_en_reg; + cvmx_write_csr(CVMX_PCSX_INTX_REG(index, block), + cvmx_read_csr(CVMX_PCSX_INTX_REG(index, block))); + pcs_int_en_reg.u64 = 0; + if (OCTEON_IS_MODEL(OCTEON_CN56XX)) { + /* Skipping pcs_int_en_reg.s.reserved_12_63 */ + /*pcs_int_en_reg.s.dup = 1; // This happens during normal operation */ + pcs_int_en_reg.s.sync_bad_en = 1; + pcs_int_en_reg.s.an_bad_en = 1; + pcs_int_en_reg.s.rxlock_en = 1; + pcs_int_en_reg.s.rxbad_en = 1; + /*pcs_int_en_reg.s.rxerr_en = 1; // This happens during normal operation */ + pcs_int_en_reg.s.txbad_en = 1; + pcs_int_en_reg.s.txfifo_en = 1; + pcs_int_en_reg.s.txfifu_en = 1; + pcs_int_en_reg.s.an_err_en = 1; + /*pcs_int_en_reg.s.xmit_en = 1; // This happens during normal operation */ + /*pcs_int_en_reg.s.lnkspd_en = 1; // This happens during normal operation */ + } + if (OCTEON_IS_MODEL(OCTEON_CN52XX)) { + /* Skipping pcs_int_en_reg.s.reserved_12_63 */ + /*pcs_int_en_reg.s.dup = 1; // This happens during normal operation */ + pcs_int_en_reg.s.sync_bad_en = 1; + pcs_int_en_reg.s.an_bad_en = 1; + pcs_int_en_reg.s.rxlock_en = 1; + pcs_int_en_reg.s.rxbad_en = 1; + /*pcs_int_en_reg.s.rxerr_en = 1; // This happens during normal operation */ + pcs_int_en_reg.s.txbad_en = 1; + pcs_int_en_reg.s.txfifo_en = 1; + pcs_int_en_reg.s.txfifu_en = 1; + pcs_int_en_reg.s.an_err_en = 1; + /*pcs_int_en_reg.s.xmit_en = 1; // This happens during normal operation */ + /*pcs_int_en_reg.s.lnkspd_en = 1; // This happens during normal operation */ + } + cvmx_write_csr(CVMX_PCSX_INTX_EN_REG(index, block), pcs_int_en_reg.u64); +} +/** + * __cvmx_interrupt_pcsxx_int_en_reg_enable enables all interrupt bits in cvmx_pcsxx_int_en_reg_t + */ +void __cvmx_interrupt_pcsxx_int_en_reg_enable(int index) +{ + union cvmx_pcsxx_int_en_reg pcsx_int_en_reg; + cvmx_write_csr(CVMX_PCSXX_INT_REG(index), + cvmx_read_csr(CVMX_PCSXX_INT_REG(index))); + pcsx_int_en_reg.u64 = 0; + if (OCTEON_IS_MODEL(OCTEON_CN56XX)) { + /* Skipping pcsx_int_en_reg.s.reserved_6_63 */ + pcsx_int_en_reg.s.algnlos_en = 1; + pcsx_int_en_reg.s.synlos_en = 1; + pcsx_int_en_reg.s.bitlckls_en = 1; + pcsx_int_en_reg.s.rxsynbad_en = 1; + pcsx_int_en_reg.s.rxbad_en = 1; + pcsx_int_en_reg.s.txflt_en = 1; + } + if (OCTEON_IS_MODEL(OCTEON_CN52XX)) { + /* Skipping pcsx_int_en_reg.s.reserved_6_63 */ + pcsx_int_en_reg.s.algnlos_en = 1; + pcsx_int_en_reg.s.synlos_en = 1; + pcsx_int_en_reg.s.bitlckls_en = 0; /* Happens if XAUI module is not installed */ + pcsx_int_en_reg.s.rxsynbad_en = 1; + pcsx_int_en_reg.s.rxbad_en = 1; + pcsx_int_en_reg.s.txflt_en = 1; + } + cvmx_write_csr(CVMX_PCSXX_INT_EN_REG(index), pcsx_int_en_reg.u64); +} + +/** + * __cvmx_interrupt_spxx_int_msk_enable enables all interrupt bits in cvmx_spxx_int_msk_t + */ +void __cvmx_interrupt_spxx_int_msk_enable(int index) +{ + union cvmx_spxx_int_msk spx_int_msk; + cvmx_write_csr(CVMX_SPXX_INT_REG(index), + cvmx_read_csr(CVMX_SPXX_INT_REG(index))); + spx_int_msk.u64 = 0; + if (OCTEON_IS_MODEL(OCTEON_CN38XX)) { + /* Skipping spx_int_msk.s.reserved_12_63 */ + spx_int_msk.s.calerr = 1; + spx_int_msk.s.syncerr = 1; + spx_int_msk.s.diperr = 1; + spx_int_msk.s.tpaovr = 1; + spx_int_msk.s.rsverr = 1; + spx_int_msk.s.drwnng = 1; + spx_int_msk.s.clserr = 1; + spx_int_msk.s.spiovr = 1; + /* Skipping spx_int_msk.s.reserved_2_3 */ + spx_int_msk.s.abnorm = 1; + spx_int_msk.s.prtnxa = 1; + } + if (OCTEON_IS_MODEL(OCTEON_CN58XX)) { + /* Skipping spx_int_msk.s.reserved_12_63 */ + spx_int_msk.s.calerr = 1; + spx_int_msk.s.syncerr = 1; + spx_int_msk.s.diperr = 1; + spx_int_msk.s.tpaovr = 1; + spx_int_msk.s.rsverr = 1; + spx_int_msk.s.drwnng = 1; + spx_int_msk.s.clserr = 1; + spx_int_msk.s.spiovr = 1; + /* Skipping spx_int_msk.s.reserved_2_3 */ + spx_int_msk.s.abnorm = 1; + spx_int_msk.s.prtnxa = 1; + } + cvmx_write_csr(CVMX_SPXX_INT_MSK(index), spx_int_msk.u64); +} +/** + * __cvmx_interrupt_stxx_int_msk_enable enables all interrupt bits in cvmx_stxx_int_msk_t + */ +void __cvmx_interrupt_stxx_int_msk_enable(int index) +{ + union cvmx_stxx_int_msk stx_int_msk; + cvmx_write_csr(CVMX_STXX_INT_REG(index), + cvmx_read_csr(CVMX_STXX_INT_REG(index))); + stx_int_msk.u64 = 0; + if (OCTEON_IS_MODEL(OCTEON_CN38XX)) { + /* Skipping stx_int_msk.s.reserved_8_63 */ + stx_int_msk.s.frmerr = 1; + stx_int_msk.s.unxfrm = 1; + stx_int_msk.s.nosync = 1; + stx_int_msk.s.diperr = 1; + stx_int_msk.s.datovr = 1; + stx_int_msk.s.ovrbst = 1; + stx_int_msk.s.calpar1 = 1; + stx_int_msk.s.calpar0 = 1; + } + if (OCTEON_IS_MODEL(OCTEON_CN58XX)) { + /* Skipping stx_int_msk.s.reserved_8_63 */ + stx_int_msk.s.frmerr = 1; + stx_int_msk.s.unxfrm = 1; + stx_int_msk.s.nosync = 1; + stx_int_msk.s.diperr = 1; + stx_int_msk.s.datovr = 1; + stx_int_msk.s.ovrbst = 1; + stx_int_msk.s.calpar1 = 1; + stx_int_msk.s.calpar0 = 1; + } + cvmx_write_csr(CVMX_STXX_INT_MSK(index), stx_int_msk.u64); +} diff --git a/arch/mips/cavium-octeon/executive/cvmx-interrupt-rsl.c b/arch/mips/cavium-octeon/executive/cvmx-interrupt-rsl.c new file mode 100644 index 000000000000..bea7538ea4e9 --- /dev/null +++ b/arch/mips/cavium-octeon/executive/cvmx-interrupt-rsl.c @@ -0,0 +1,140 @@ +/***********************license start*************** + * Author: Cavium Networks + * + * Contact: support@caviumnetworks.com + * This file is part of the OCTEON SDK + * + * Copyright (c) 2003-2008 Cavium Networks + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, Version 2, as + * published by the Free Software Foundation. + * + * This file is distributed in the hope that it will be useful, but + * AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or + * NONINFRINGEMENT. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License + * along with this file; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * or visit http://www.gnu.org/licenses/. + * + * This file may also be available under a different license from Cavium. + * Contact Cavium Networks for more information + ***********************license end**************************************/ + +/* + * Utility functions to decode Octeon's RSL_INT_BLOCKS + * interrupts into error messages. + */ + +#include <asm/octeon/octeon.h> + +#include <asm/octeon/cvmx-asxx-defs.h> +#include <asm/octeon/cvmx-gmxx-defs.h> + +#ifndef PRINT_ERROR +#define PRINT_ERROR(format, ...) +#endif + +void __cvmx_interrupt_gmxx_rxx_int_en_enable(int index, int block); + +/** + * Enable ASX error interrupts that exist on CN3XXX, CN50XX, and + * CN58XX. + * + * @block: Interface to enable 0-1 + */ +void __cvmx_interrupt_asxx_enable(int block) +{ + int mask; + union cvmx_asxx_int_en csr; + /* + * CN38XX and CN58XX have two interfaces with 4 ports per + * interface. All other chips have a max of 3 ports on + * interface 0 + */ + if (OCTEON_IS_MODEL(OCTEON_CN38XX) || OCTEON_IS_MODEL(OCTEON_CN58XX)) + mask = 0xf; /* Set enables for 4 ports */ + else + mask = 0x7; /* Set enables for 3 ports */ + + /* Enable interface interrupts */ + csr.u64 = cvmx_read_csr(CVMX_ASXX_INT_EN(block)); + csr.s.txpsh = mask; + csr.s.txpop = mask; + csr.s.ovrflw = mask; + cvmx_write_csr(CVMX_ASXX_INT_EN(block), csr.u64); +} +/** + * Enable GMX error reporting for the supplied interface + * + * @interface: Interface to enable + */ +void __cvmx_interrupt_gmxx_enable(int interface) +{ + union cvmx_gmxx_inf_mode mode; + union cvmx_gmxx_tx_int_en gmx_tx_int_en; + int num_ports; + int index; + + mode.u64 = cvmx_read_csr(CVMX_GMXX_INF_MODE(interface)); + + if (OCTEON_IS_MODEL(OCTEON_CN56XX) || OCTEON_IS_MODEL(OCTEON_CN52XX)) { + if (mode.s.en) { + switch (mode.cn56xx.mode) { + case 1: /* XAUI */ + num_ports = 1; + break; + case 2: /* SGMII */ + case 3: /* PICMG */ + num_ports = 4; + break; + default: /* Disabled */ + num_ports = 0; + break; + } + } else + num_ports = 0; + } else { + if (mode.s.en) { + if (OCTEON_IS_MODEL(OCTEON_CN38XX) + || OCTEON_IS_MODEL(OCTEON_CN58XX)) { + /* + * SPI on CN38XX and CN58XX report all + * errors through port 0. RGMII needs + * to check all 4 ports + */ + if (mode.s.type) + num_ports = 1; + else + num_ports = 4; + } else { + /* + * CN30XX, CN31XX, and CN50XX have two + * or three ports. GMII and MII has 2, + * RGMII has three + */ + if (mode.s.type) + num_ports = 2; + else + num_ports = 3; + } + } else + num_ports = 0; + } + + gmx_tx_int_en.u64 = 0; + if (num_ports) { + if (OCTEON_IS_MODEL(OCTEON_CN38XX) + || OCTEON_IS_MODEL(OCTEON_CN58XX)) + gmx_tx_int_en.s.ncb_nxa = 1; + gmx_tx_int_en.s.pko_nxa = 1; + } + gmx_tx_int_en.s.undflw = (1 << num_ports) - 1; + cvmx_write_csr(CVMX_GMXX_TX_INT_EN(interface), gmx_tx_int_en.u64); + for (index = 0; index < num_ports; index++) + __cvmx_interrupt_gmxx_rxx_int_en_enable(index, interface); +} diff --git a/arch/mips/cavium-octeon/executive/cvmx-pko.c b/arch/mips/cavium-octeon/executive/cvmx-pko.c new file mode 100644 index 000000000000..f557084b1092 --- /dev/null +++ b/arch/mips/cavium-octeon/executive/cvmx-pko.c @@ -0,0 +1,506 @@ +/***********************license start*************** + * Author: Cavium Networks + * + * Contact: support@caviumnetworks.com + * This file is part of the OCTEON SDK + * + * Copyright (c) 2003-2008 Cavium Networks + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, Version 2, as + * published by the Free Software Foundation. + * + * This file is distributed in the hope that it will be useful, but + * AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or + * NONINFRINGEMENT. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License + * along with this file; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * or visit http://www.gnu.org/licenses/. + * + * This file may also be available under a different license from Cavium. + * Contact Cavium Networks for more information + ***********************license end**************************************/ + +/* + * Support library for the hardware Packet Output unit. + */ + +#include <asm/octeon/octeon.h> + +#include <asm/octeon/cvmx-config.h> +#include <asm/octeon/cvmx-pko.h> +#include <asm/octeon/cvmx-helper.h> + +/** + * Internal state of packet output + */ + +/** + * Call before any other calls to initialize the packet + * output system. This does chip global config, and should only be + * done by one core. + */ + +void cvmx_pko_initialize_global(void) +{ + int i; + uint64_t priority = 8; + union cvmx_pko_reg_cmd_buf config; + + /* + * Set the size of the PKO command buffers to an odd number of + * 64bit words. This allows the normal two word send to stay + * aligned and never span a comamnd word buffer. + */ + config.u64 = 0; + config.s.pool = CVMX_FPA_OUTPUT_BUFFER_POOL; + config.s.size = CVMX_FPA_OUTPUT_BUFFER_POOL_SIZE / 8 - 1; + + cvmx_write_csr(CVMX_PKO_REG_CMD_BUF, config.u64); + + for (i = 0; i < CVMX_PKO_MAX_OUTPUT_QUEUES; i++) + cvmx_pko_config_port(CVMX_PKO_MEM_QUEUE_PTRS_ILLEGAL_PID, i, 1, + &priority); + + /* + * If we aren't using all of the queues optimize PKO's + * internal memory. + */ + if (OCTEON_IS_MODEL(OCTEON_CN38XX) || OCTEON_IS_MODEL(OCTEON_CN58XX) + || OCTEON_IS_MODEL(OCTEON_CN56XX) + || OCTEON_IS_MODEL(OCTEON_CN52XX)) { + int num_interfaces = cvmx_helper_get_number_of_interfaces(); + int last_port = + cvmx_helper_get_last_ipd_port(num_interfaces - 1); + int max_queues = + cvmx_pko_get_base_queue(last_port) + + cvmx_pko_get_num_queues(last_port); + if (OCTEON_IS_MODEL(OCTEON_CN38XX)) { + if (max_queues <= 32) + cvmx_write_csr(CVMX_PKO_REG_QUEUE_MODE, 2); + else if (max_queues <= 64) + cvmx_write_csr(CVMX_PKO_REG_QUEUE_MODE, 1); + } else { + if (max_queues <= 64) + cvmx_write_csr(CVMX_PKO_REG_QUEUE_MODE, 2); + else if (max_queues <= 128) + cvmx_write_csr(CVMX_PKO_REG_QUEUE_MODE, 1); + } + } +} + +/** + * This function does per-core initialization required by the PKO routines. + * This must be called on all cores that will do packet output, and must + * be called after the FPA has been initialized and filled with pages. + * + * Returns 0 on success + * !0 on failure + */ +int cvmx_pko_initialize_local(void) +{ + /* Nothing to do */ + return 0; +} + +/** + * Enables the packet output hardware. It must already be + * configured. + */ +void cvmx_pko_enable(void) +{ + union cvmx_pko_reg_flags flags; + + flags.u64 = cvmx_read_csr(CVMX_PKO_REG_FLAGS); + if (flags.s.ena_pko) + cvmx_dprintf + ("Warning: Enabling PKO when PKO already enabled.\n"); + + flags.s.ena_dwb = 1; + flags.s.ena_pko = 1; + /* + * always enable big endian for 3-word command. Does nothing + * for 2-word. + */ + flags.s.store_be = 1; + cvmx_write_csr(CVMX_PKO_REG_FLAGS, flags.u64); +} + +/** + * Disables the packet output. Does not affect any configuration. + */ +void cvmx_pko_disable(void) +{ + union cvmx_pko_reg_flags pko_reg_flags; + pko_reg_flags.u64 = cvmx_read_csr(CVMX_PKO_REG_FLAGS); + pko_reg_flags.s.ena_pko = 0; + cvmx_write_csr(CVMX_PKO_REG_FLAGS, pko_reg_flags.u64); +} + + +/** + * Reset the packet output. + */ +static void __cvmx_pko_reset(void) +{ + union cvmx_pko_reg_flags pko_reg_flags; + pko_reg_flags.u64 = cvmx_read_csr(CVMX_PKO_REG_FLAGS); + pko_reg_flags.s.reset = 1; + cvmx_write_csr(CVMX_PKO_REG_FLAGS, pko_reg_flags.u64); +} + +/** + * Shutdown and free resources required by packet output. + */ +void cvmx_pko_shutdown(void) +{ + union cvmx_pko_mem_queue_ptrs config; + int queue; + + cvmx_pko_disable(); + + for (queue = 0; queue < CVMX_PKO_MAX_OUTPUT_QUEUES; queue++) { + config.u64 = 0; + config.s.tail = 1; + config.s.index = 0; + config.s.port = CVMX_PKO_MEM_QUEUE_PTRS_ILLEGAL_PID; + config.s.queue = queue & 0x7f; + config.s.qos_mask = 0; + config.s.buf_ptr = 0; + if (!OCTEON_IS_MODEL(OCTEON_CN3XXX)) { + union cvmx_pko_reg_queue_ptrs1 config1; + config1.u64 = 0; + config1.s.qid7 = queue >> 7; + cvmx_write_csr(CVMX_PKO_REG_QUEUE_PTRS1, config1.u64); + } + cvmx_write_csr(CVMX_PKO_MEM_QUEUE_PTRS, config.u64); + cvmx_cmd_queue_shutdown(CVMX_CMD_QUEUE_PKO(queue)); + } + __cvmx_pko_reset(); +} + +/** + * Configure a output port and the associated queues for use. + * + * @port: Port to configure. + * @base_queue: First queue number to associate with this port. + * @num_queues: Number of queues to associate with this port + * @priority: Array of priority levels for each queue. Values are + * allowed to be 0-8. A value of 8 get 8 times the traffic + * of a value of 1. A value of 0 indicates that no rounds + * will be participated in. These priorities can be changed + * on the fly while the pko is enabled. A priority of 9 + * indicates that static priority should be used. If static + * priority is used all queues with static priority must be + * contiguous starting at the base_queue, and lower numbered + * queues have higher priority than higher numbered queues. + * There must be num_queues elements in the array. + */ +cvmx_pko_status_t cvmx_pko_config_port(uint64_t port, uint64_t base_queue, + uint64_t num_queues, + const uint64_t priority[]) +{ + cvmx_pko_status_t result_code; + uint64_t queue; + union cvmx_pko_mem_queue_ptrs config; + union cvmx_pko_reg_queue_ptrs1 config1; + int static_priority_base = -1; + int static_priority_end = -1; + + if ((port >= CVMX_PKO_NUM_OUTPUT_PORTS) + && (port != CVMX_PKO_MEM_QUEUE_PTRS_ILLEGAL_PID)) { + cvmx_dprintf("ERROR: cvmx_pko_config_port: Invalid port %llu\n", + (unsigned long long)port); + return CVMX_PKO_INVALID_PORT; + } + + if (base_queue + num_queues > CVMX_PKO_MAX_OUTPUT_QUEUES) { + cvmx_dprintf + ("ERROR: cvmx_pko_config_port: Invalid queue range %llu\n", + (unsigned long long)(base_queue + num_queues)); + return CVMX_PKO_INVALID_QUEUE; + } + + if (port != CVMX_PKO_MEM_QUEUE_PTRS_ILLEGAL_PID) { + /* + * Validate the static queue priority setup and set + * static_priority_base and static_priority_end + * accordingly. + */ + for (queue = 0; queue < num_queues; queue++) { + /* Find first queue of static priority */ + if (static_priority_base == -1 + && priority[queue] == + CVMX_PKO_QUEUE_STATIC_PRIORITY) + static_priority_base = queue; + /* Find last queue of static priority */ + if (static_priority_base != -1 + && static_priority_end == -1 + && priority[queue] != CVMX_PKO_QUEUE_STATIC_PRIORITY + && queue) + static_priority_end = queue - 1; + else if (static_priority_base != -1 + && static_priority_end == -1 + && queue == num_queues - 1) + /* all queues are static priority */ + static_priority_end = queue; + /* + * Check to make sure all static priority + * queues are contiguous. Also catches some + * cases of static priorites not starting at + * queue 0. + */ + if (static_priority_end != -1 + && (int)queue > static_priority_end + && priority[queue] == + CVMX_PKO_QUEUE_STATIC_PRIORITY) { + cvmx_dprintf("ERROR: cvmx_pko_config_port: " + "Static priority queues aren't " + "contiguous or don't start at " + "base queue. q: %d, eq: %d\n", + (int)queue, static_priority_end); + return CVMX_PKO_INVALID_PRIORITY; + } + } + if (static_priority_base > 0) { + cvmx_dprintf("ERROR: cvmx_pko_config_port: Static " + "priority queues don't start at base " + "queue. sq: %d\n", + static_priority_base); + return CVMX_PKO_INVALID_PRIORITY; + } +#if 0 + cvmx_dprintf("Port %d: Static priority queue base: %d, " + "end: %d\n", port, + static_priority_base, static_priority_end); +#endif + } + /* + * At this point, static_priority_base and static_priority_end + * are either both -1, or are valid start/end queue + * numbers. + */ + + result_code = CVMX_PKO_SUCCESS; + +#ifdef PKO_DEBUG + cvmx_dprintf("num queues: %d (%lld,%lld)\n", num_queues, + CVMX_PKO_QUEUES_PER_PORT_INTERFACE0, + CVMX_PKO_QUEUES_PER_PORT_INTERFACE1); +#endif + + for (queue = 0; queue < num_queues; queue++) { + uint64_t *buf_ptr = NULL; + + config1.u64 = 0; + config1.s.idx3 = queue >> 3; + config1.s.qid7 = (base_queue + queue) >> 7; + + config.u64 = 0; + config.s.tail = queue == (num_queues - 1); + config.s.index = queue; + config.s.port = port; + config.s.queue = base_queue + queue; + + if (!cvmx_octeon_is_pass1()) { + config.s.static_p = static_priority_base >= 0; + config.s.static_q = (int)queue <= static_priority_end; + config.s.s_tail = (int)queue == static_priority_end; + } + /* + * Convert the priority into an enable bit field. Try + * to space the bits out evenly so the packet don't + * get grouped up + */ + switch ((int)priority[queue]) { + case 0: + config.s.qos_mask = 0x00; + break; + case 1: + config.s.qos_mask = 0x01; + break; + case 2: + config.s.qos_mask = 0x11; + break; + case 3: + config.s.qos_mask = 0x49; + break; + case 4: + config.s.qos_mask = 0x55; + break; + case 5: + config.s.qos_mask = 0x57; + break; + case 6: + config.s.qos_mask = 0x77; + break; + case 7: + config.s.qos_mask = 0x7f; + break; + case 8: + config.s.qos_mask = 0xff; + break; + case CVMX_PKO_QUEUE_STATIC_PRIORITY: + /* Pass 1 will fall through to the error case */ + if (!cvmx_octeon_is_pass1()) { + config.s.qos_mask = 0xff; + break; + } + default: + cvmx_dprintf("ERROR: cvmx_pko_config_port: Invalid " + "priority %llu\n", + (unsigned long long)priority[queue]); + config.s.qos_mask = 0xff; + result_code = CVMX_PKO_INVALID_PRIORITY; + break; + } + + if (port != CVMX_PKO_MEM_QUEUE_PTRS_ILLEGAL_PID) { + cvmx_cmd_queue_result_t cmd_res = + cvmx_cmd_queue_initialize(CVMX_CMD_QUEUE_PKO + (base_queue + queue), + CVMX_PKO_MAX_QUEUE_DEPTH, + CVMX_FPA_OUTPUT_BUFFER_POOL, + CVMX_FPA_OUTPUT_BUFFER_POOL_SIZE + - + CVMX_PKO_COMMAND_BUFFER_SIZE_ADJUST + * 8); + if (cmd_res != CVMX_CMD_QUEUE_SUCCESS) { + switch (cmd_res) { + case CVMX_CMD_QUEUE_NO_MEMORY: + cvmx_dprintf("ERROR: " + "cvmx_pko_config_port: " + "Unable to allocate " + "output buffer.\n"); + return CVMX_PKO_NO_MEMORY; + case CVMX_CMD_QUEUE_ALREADY_SETUP: + cvmx_dprintf + ("ERROR: cvmx_pko_config_port: Port already setup.\n"); + return CVMX_PKO_PORT_ALREADY_SETUP; + case CVMX_CMD_QUEUE_INVALID_PARAM: + default: + cvmx_dprintf + ("ERROR: cvmx_pko_config_port: Command queue initialization failed.\n"); + return CVMX_PKO_CMD_QUEUE_INIT_ERROR; + } + } + + buf_ptr = + (uint64_t *) + cvmx_cmd_queue_buffer(CVMX_CMD_QUEUE_PKO + (base_queue + queue)); + config.s.buf_ptr = cvmx_ptr_to_phys(buf_ptr); + } else + config.s.buf_ptr = 0; + + CVMX_SYNCWS; + + if (!OCTEON_IS_MODEL(OCTEON_CN3XXX)) + cvmx_write_csr(CVMX_PKO_REG_QUEUE_PTRS1, config1.u64); + cvmx_write_csr(CVMX_PKO_MEM_QUEUE_PTRS, config.u64); + } + + return result_code; +} + +#ifdef PKO_DEBUG +/** + * Show map of ports -> queues for different cores. + */ +void cvmx_pko_show_queue_map() +{ + int core, port; + int pko_output_ports = 36; + + cvmx_dprintf("port"); + for (port = 0; port < pko_output_ports; port++) + cvmx_dprintf("%3d ", port); + cvmx_dprintf("\n"); + + for (core = 0; core < CVMX_MAX_CORES; core++) { + cvmx_dprintf("\n%2d: ", core); + for (port = 0; port < pko_output_ports; port++) { + cvmx_dprintf("%3d ", + cvmx_pko_get_base_queue_per_core(port, + core)); + } + } + cvmx_dprintf("\n"); +} +#endif + +/** + * Rate limit a PKO port to a max packets/sec. This function is only + * supported on CN51XX and higher, excluding CN58XX. + * + * @port: Port to rate limit + * @packets_s: Maximum packet/sec + * @burst: Maximum number of packets to burst in a row before rate + * limiting cuts in. + * + * Returns Zero on success, negative on failure + */ +int cvmx_pko_rate_limit_packets(int port, int packets_s, int burst) +{ + union cvmx_pko_mem_port_rate0 pko_mem_port_rate0; + union cvmx_pko_mem_port_rate1 pko_mem_port_rate1; + + pko_mem_port_rate0.u64 = 0; + pko_mem_port_rate0.s.pid = port; + pko_mem_port_rate0.s.rate_pkt = + cvmx_sysinfo_get()->cpu_clock_hz / packets_s / 16; + /* No cost per word since we are limited by packets/sec, not bits/sec */ + pko_mem_port_rate0.s.rate_word = 0; + + pko_mem_port_rate1.u64 = 0; + pko_mem_port_rate1.s.pid = port; + pko_mem_port_rate1.s.rate_lim = + ((uint64_t) pko_mem_port_rate0.s.rate_pkt * burst) >> 8; + + cvmx_write_csr(CVMX_PKO_MEM_PORT_RATE0, pko_mem_port_rate0.u64); + cvmx_write_csr(CVMX_PKO_MEM_PORT_RATE1, pko_mem_port_rate1.u64); + return 0; +} + +/** + * Rate limit a PKO port to a max bits/sec. This function is only + * supported on CN51XX and higher, excluding CN58XX. + * + * @port: Port to rate limit + * @bits_s: PKO rate limit in bits/sec + * @burst: Maximum number of bits to burst before rate + * limiting cuts in. + * + * Returns Zero on success, negative on failure + */ +int cvmx_pko_rate_limit_bits(int port, uint64_t bits_s, int burst) +{ + union cvmx_pko_mem_port_rate0 pko_mem_port_rate0; + union cvmx_pko_mem_port_rate1 pko_mem_port_rate1; + uint64_t clock_rate = cvmx_sysinfo_get()->cpu_clock_hz; + uint64_t tokens_per_bit = clock_rate * 16 / bits_s; + + pko_mem_port_rate0.u64 = 0; + pko_mem_port_rate0.s.pid = port; + /* + * Each packet has a 12 bytes of interframe gap, an 8 byte + * preamble, and a 4 byte CRC. These are not included in the + * per word count. Multiply by 8 to covert to bits and divide + * by 256 for limit granularity. + */ + pko_mem_port_rate0.s.rate_pkt = (12 + 8 + 4) * 8 * tokens_per_bit / 256; + /* Each 8 byte word has 64bits */ + pko_mem_port_rate0.s.rate_word = 64 * tokens_per_bit; + + pko_mem_port_rate1.u64 = 0; + pko_mem_port_rate1.s.pid = port; + pko_mem_port_rate1.s.rate_lim = tokens_per_bit * burst / 256; + + cvmx_write_csr(CVMX_PKO_MEM_PORT_RATE0, pko_mem_port_rate0.u64); + cvmx_write_csr(CVMX_PKO_MEM_PORT_RATE1, pko_mem_port_rate1.u64); + return 0; +} diff --git a/arch/mips/cavium-octeon/executive/cvmx-spi.c b/arch/mips/cavium-octeon/executive/cvmx-spi.c new file mode 100644 index 000000000000..74afb1710cd9 --- /dev/null +++ b/arch/mips/cavium-octeon/executive/cvmx-spi.c @@ -0,0 +1,667 @@ +/***********************license start*************** + * Author: Cavium Networks + * + * Contact: support@caviumnetworks.com + * This file is part of the OCTEON SDK + * + * Copyright (c) 2003-2008 Cavium Networks + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, Version 2, as + * published by the Free Software Foundation. + * + * This file is distributed in the hope that it will be useful, but + * AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or + * NONINFRINGEMENT. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License + * along with this file; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * or visit http://www.gnu.org/licenses/. + * + * This file may also be available under a different license from Cavium. + * Contact Cavium Networks for more information + ***********************license end**************************************/ + +/* + * + * Support library for the SPI + */ +#include <asm/octeon/octeon.h> + +#include <asm/octeon/cvmx-config.h> + +#include <asm/octeon/cvmx-pko.h> +#include <asm/octeon/cvmx-spi.h> + +#include <asm/octeon/cvmx-spxx-defs.h> +#include <asm/octeon/cvmx-stxx-defs.h> +#include <asm/octeon/cvmx-srxx-defs.h> + +#define INVOKE_CB(function_p, args...) \ + do { \ + if (function_p) { \ + res = function_p(args); \ + if (res) \ + return res; \ + } \ + } while (0) + +#if CVMX_ENABLE_DEBUG_PRINTS +static const char *modes[] = + { "UNKNOWN", "TX Halfplex", "Rx Halfplex", "Duplex" }; +#endif + +/* Default callbacks, can be overridden + * using cvmx_spi_get_callbacks/cvmx_spi_set_callbacks + */ +static cvmx_spi_callbacks_t cvmx_spi_callbacks = { + .reset_cb = cvmx_spi_reset_cb, + .calendar_setup_cb = cvmx_spi_calendar_setup_cb, + .clock_detect_cb = cvmx_spi_clock_detect_cb, + .training_cb = cvmx_spi_training_cb, + .calendar_sync_cb = cvmx_spi_calendar_sync_cb, + .interface_up_cb = cvmx_spi_interface_up_cb +}; + +/** + * Get current SPI4 initialization callbacks + * + * @callbacks: Pointer to the callbacks structure.to fill + * + * Returns Pointer to cvmx_spi_callbacks_t structure. + */ +void cvmx_spi_get_callbacks(cvmx_spi_callbacks_t *callbacks) +{ + memcpy(callbacks, &cvmx_spi_callbacks, sizeof(cvmx_spi_callbacks)); +} + +/** + * Set new SPI4 initialization callbacks + * + * @new_callbacks: Pointer to an updated callbacks structure. + */ +void cvmx_spi_set_callbacks(cvmx_spi_callbacks_t *new_callbacks) +{ + memcpy(&cvmx_spi_callbacks, new_callbacks, sizeof(cvmx_spi_callbacks)); +} + +/** + * Initialize and start the SPI interface. + * + * @interface: The identifier of the packet interface to configure and + * use as a SPI interface. + * @mode: The operating mode for the SPI interface. The interface + * can operate as a full duplex (both Tx and Rx data paths + * active) or as a halfplex (either the Tx data path is + * active or the Rx data path is active, but not both). + * @timeout: Timeout to wait for clock synchronization in seconds + * @num_ports: Number of SPI ports to configure + * + * Returns Zero on success, negative of failure. + */ +int cvmx_spi_start_interface(int interface, cvmx_spi_mode_t mode, int timeout, + int num_ports) +{ + int res = -1; + + if (!(OCTEON_IS_MODEL(OCTEON_CN38XX) || OCTEON_IS_MODEL(OCTEON_CN58XX))) + return res; + + /* Callback to perform SPI4 reset */ + INVOKE_CB(cvmx_spi_callbacks.reset_cb, interface, mode); + + /* Callback to perform calendar setup */ + INVOKE_CB(cvmx_spi_callbacks.calendar_setup_cb, interface, mode, + num_ports); + + /* Callback to perform clock detection */ + INVOKE_CB(cvmx_spi_callbacks.clock_detect_cb, interface, mode, timeout); + + /* Callback to perform SPI4 link training */ + INVOKE_CB(cvmx_spi_callbacks.training_cb, interface, mode, timeout); + + /* Callback to perform calendar sync */ + INVOKE_CB(cvmx_spi_callbacks.calendar_sync_cb, interface, mode, + timeout); + + /* Callback to handle interface coming up */ + INVOKE_CB(cvmx_spi_callbacks.interface_up_cb, interface, mode); + + return res; +} + +/** + * This routine restarts the SPI interface after it has lost synchronization + * with its correspondent system. + * + * @interface: The identifier of the packet interface to configure and + * use as a SPI interface. + * @mode: The operating mode for the SPI interface. The interface + * can operate as a full duplex (both Tx and Rx data paths + * active) or as a halfplex (either the Tx data path is + * active or the Rx data path is active, but not both). + * @timeout: Timeout to wait for clock synchronization in seconds + * + * Returns Zero on success, negative of failure. + */ +int cvmx_spi_restart_interface(int interface, cvmx_spi_mode_t mode, int timeout) +{ + int res = -1; + + if (!(OCTEON_IS_MODEL(OCTEON_CN38XX) || OCTEON_IS_MODEL(OCTEON_CN58XX))) + return res; + + cvmx_dprintf("SPI%d: Restart %s\n", interface, modes[mode]); + + /* Callback to perform SPI4 reset */ + INVOKE_CB(cvmx_spi_callbacks.reset_cb, interface, mode); + + /* NOTE: Calendar setup is not performed during restart */ + /* Refer to cvmx_spi_start_interface() for the full sequence */ + + /* Callback to perform clock detection */ + INVOKE_CB(cvmx_spi_callbacks.clock_detect_cb, interface, mode, timeout); + + /* Callback to perform SPI4 link training */ + INVOKE_CB(cvmx_spi_callbacks.training_cb, interface, mode, timeout); + + /* Callback to perform calendar sync */ + INVOKE_CB(cvmx_spi_callbacks.calendar_sync_cb, interface, mode, + timeout); + + /* Callback to handle interface coming up */ + INVOKE_CB(cvmx_spi_callbacks.interface_up_cb, interface, mode); + + return res; +} + +/** + * Callback to perform SPI4 reset + * + * @interface: The identifier of the packet interface to configure and + * use as a SPI interface. + * @mode: The operating mode for the SPI interface. The interface + * can operate as a full duplex (both Tx and Rx data paths + * active) or as a halfplex (either the Tx data path is + * active or the Rx data path is active, but not both). + * + * Returns Zero on success, non-zero error code on failure (will cause + * SPI initialization to abort) + */ +int cvmx_spi_reset_cb(int interface, cvmx_spi_mode_t mode) +{ + union cvmx_spxx_dbg_deskew_ctl spxx_dbg_deskew_ctl; + union cvmx_spxx_clk_ctl spxx_clk_ctl; + union cvmx_spxx_bist_stat spxx_bist_stat; + union cvmx_spxx_int_msk spxx_int_msk; + union cvmx_stxx_int_msk stxx_int_msk; + union cvmx_spxx_trn4_ctl spxx_trn4_ctl; + int index; + uint64_t MS = cvmx_sysinfo_get()->cpu_clock_hz / 1000; + + /* Disable SPI error events while we run BIST */ + spxx_int_msk.u64 = cvmx_read_csr(CVMX_SPXX_INT_MSK(interface)); + cvmx_write_csr(CVMX_SPXX_INT_MSK(interface), 0); + stxx_int_msk.u64 = cvmx_read_csr(CVMX_STXX_INT_MSK(interface)); + cvmx_write_csr(CVMX_STXX_INT_MSK(interface), 0); + + /* Run BIST in the SPI interface */ + cvmx_write_csr(CVMX_SRXX_COM_CTL(interface), 0); + cvmx_write_csr(CVMX_STXX_COM_CTL(interface), 0); + spxx_clk_ctl.u64 = 0; + spxx_clk_ctl.s.runbist = 1; + cvmx_write_csr(CVMX_SPXX_CLK_CTL(interface), spxx_clk_ctl.u64); + cvmx_wait(10 * MS); + spxx_bist_stat.u64 = cvmx_read_csr(CVMX_SPXX_BIST_STAT(interface)); + if (spxx_bist_stat.s.stat0) + cvmx_dprintf + ("ERROR SPI%d: BIST failed on receive datapath FIFO\n", + interface); + if (spxx_bist_stat.s.stat1) + cvmx_dprintf("ERROR SPI%d: BIST failed on RX calendar table\n", + interface); + if (spxx_bist_stat.s.stat2) + cvmx_dprintf("ERROR SPI%d: BIST failed on TX calendar table\n", + interface); + + /* Clear the calendar table after BIST to fix parity errors */ + for (index = 0; index < 32; index++) { + union cvmx_srxx_spi4_calx srxx_spi4_calx; + union cvmx_stxx_spi4_calx stxx_spi4_calx; + + srxx_spi4_calx.u64 = 0; + srxx_spi4_calx.s.oddpar = 1; + cvmx_write_csr(CVMX_SRXX_SPI4_CALX(index, interface), + srxx_spi4_calx.u64); + + stxx_spi4_calx.u64 = 0; + stxx_spi4_calx.s.oddpar = 1; + cvmx_write_csr(CVMX_STXX_SPI4_CALX(index, interface), + stxx_spi4_calx.u64); + } + + /* Re enable reporting of error interrupts */ + cvmx_write_csr(CVMX_SPXX_INT_REG(interface), + cvmx_read_csr(CVMX_SPXX_INT_REG(interface))); + cvmx_write_csr(CVMX_SPXX_INT_MSK(interface), spxx_int_msk.u64); + cvmx_write_csr(CVMX_STXX_INT_REG(interface), + cvmx_read_csr(CVMX_STXX_INT_REG(interface))); + cvmx_write_csr(CVMX_STXX_INT_MSK(interface), stxx_int_msk.u64); + + /* Setup the CLKDLY right in the middle */ + spxx_clk_ctl.u64 = 0; + spxx_clk_ctl.s.seetrn = 0; + spxx_clk_ctl.s.clkdly = 0x10; + spxx_clk_ctl.s.runbist = 0; + spxx_clk_ctl.s.statdrv = 0; + /* This should always be on the opposite edge as statdrv */ + spxx_clk_ctl.s.statrcv = 1; + spxx_clk_ctl.s.sndtrn = 0; + spxx_clk_ctl.s.drptrn = 0; + spxx_clk_ctl.s.rcvtrn = 0; + spxx_clk_ctl.s.srxdlck = 0; + cvmx_write_csr(CVMX_SPXX_CLK_CTL(interface), spxx_clk_ctl.u64); + cvmx_wait(100 * MS); + + /* Reset SRX0 DLL */ + spxx_clk_ctl.s.srxdlck = 1; + cvmx_write_csr(CVMX_SPXX_CLK_CTL(interface), spxx_clk_ctl.u64); + + /* Waiting for Inf0 Spi4 RX DLL to lock */ + cvmx_wait(100 * MS); + + /* Enable dynamic alignment */ + spxx_trn4_ctl.s.trntest = 0; + spxx_trn4_ctl.s.jitter = 1; + spxx_trn4_ctl.s.clr_boot = 1; + spxx_trn4_ctl.s.set_boot = 0; + if (OCTEON_IS_MODEL(OCTEON_CN58XX)) + spxx_trn4_ctl.s.maxdist = 3; + else + spxx_trn4_ctl.s.maxdist = 8; + spxx_trn4_ctl.s.macro_en = 1; + spxx_trn4_ctl.s.mux_en = 1; + cvmx_write_csr(CVMX_SPXX_TRN4_CTL(interface), spxx_trn4_ctl.u64); + + spxx_dbg_deskew_ctl.u64 = 0; + cvmx_write_csr(CVMX_SPXX_DBG_DESKEW_CTL(interface), + spxx_dbg_deskew_ctl.u64); + + return 0; +} + +/** + * Callback to setup calendar and miscellaneous settings before clock detection + * + * @interface: The identifier of the packet interface to configure and + * use as a SPI interface. + * @mode: The operating mode for the SPI interface. The interface + * can operate as a full duplex (both Tx and Rx data paths + * active) or as a halfplex (either the Tx data path is + * active or the Rx data path is active, but not both). + * @num_ports: Number of ports to configure on SPI + * + * Returns Zero on success, non-zero error code on failure (will cause + * SPI initialization to abort) + */ +int cvmx_spi_calendar_setup_cb(int interface, cvmx_spi_mode_t mode, + int num_ports) +{ + int port; + int index; + if (mode & CVMX_SPI_MODE_RX_HALFPLEX) { + union cvmx_srxx_com_ctl srxx_com_ctl; + union cvmx_srxx_spi4_stat srxx_spi4_stat; + + /* SRX0 number of Ports */ + srxx_com_ctl.u64 = 0; + srxx_com_ctl.s.prts = num_ports - 1; + srxx_com_ctl.s.st_en = 0; + srxx_com_ctl.s.inf_en = 0; + cvmx_write_csr(CVMX_SRXX_COM_CTL(interface), srxx_com_ctl.u64); + + /* SRX0 Calendar Table. This round robbins through all ports */ + port = 0; + index = 0; + while (port < num_ports) { + union cvmx_srxx_spi4_calx srxx_spi4_calx; + srxx_spi4_calx.u64 = 0; + srxx_spi4_calx.s.prt0 = port++; + srxx_spi4_calx.s.prt1 = port++; + srxx_spi4_calx.s.prt2 = port++; + srxx_spi4_calx.s.prt3 = port++; + srxx_spi4_calx.s.oddpar = + ~(cvmx_dpop(srxx_spi4_calx.u64) & 1); + cvmx_write_csr(CVMX_SRXX_SPI4_CALX(index, interface), + srxx_spi4_calx.u64); + index++; + } + srxx_spi4_stat.u64 = 0; + srxx_spi4_stat.s.len = num_ports; + srxx_spi4_stat.s.m = 1; + cvmx_write_csr(CVMX_SRXX_SPI4_STAT(interface), + srxx_spi4_stat.u64); + } + + if (mode & CVMX_SPI_MODE_TX_HALFPLEX) { + union cvmx_stxx_arb_ctl stxx_arb_ctl; + union cvmx_gmxx_tx_spi_max gmxx_tx_spi_max; + union cvmx_gmxx_tx_spi_thresh gmxx_tx_spi_thresh; + union cvmx_gmxx_tx_spi_ctl gmxx_tx_spi_ctl; + union cvmx_stxx_spi4_stat stxx_spi4_stat; + union cvmx_stxx_spi4_dat stxx_spi4_dat; + + /* STX0 Config */ + stxx_arb_ctl.u64 = 0; + stxx_arb_ctl.s.igntpa = 0; + stxx_arb_ctl.s.mintrn = 0; + cvmx_write_csr(CVMX_STXX_ARB_CTL(interface), stxx_arb_ctl.u64); + + gmxx_tx_spi_max.u64 = 0; + gmxx_tx_spi_max.s.max1 = 8; + gmxx_tx_spi_max.s.max2 = 4; + gmxx_tx_spi_max.s.slice = 0; + cvmx_write_csr(CVMX_GMXX_TX_SPI_MAX(interface), + gmxx_tx_spi_max.u64); + + gmxx_tx_spi_thresh.u64 = 0; + gmxx_tx_spi_thresh.s.thresh = 4; + cvmx_write_csr(CVMX_GMXX_TX_SPI_THRESH(interface), + gmxx_tx_spi_thresh.u64); + + gmxx_tx_spi_ctl.u64 = 0; + gmxx_tx_spi_ctl.s.tpa_clr = 0; + gmxx_tx_spi_ctl.s.cont_pkt = 0; + cvmx_write_csr(CVMX_GMXX_TX_SPI_CTL(interface), + gmxx_tx_spi_ctl.u64); + + /* STX0 Training Control */ + stxx_spi4_dat.u64 = 0; + /*Minimum needed by dynamic alignment */ + stxx_spi4_dat.s.alpha = 32; + stxx_spi4_dat.s.max_t = 0xFFFF; /*Minimum interval is 0x20 */ + cvmx_write_csr(CVMX_STXX_SPI4_DAT(interface), + stxx_spi4_dat.u64); + + /* STX0 Calendar Table. This round robbins through all ports */ + port = 0; + index = 0; + while (port < num_ports) { + union cvmx_stxx_spi4_calx stxx_spi4_calx; + stxx_spi4_calx.u64 = 0; + stxx_spi4_calx.s.prt0 = port++; + stxx_spi4_calx.s.prt1 = port++; + stxx_spi4_calx.s.prt2 = port++; + stxx_spi4_calx.s.prt3 = port++; + stxx_spi4_calx.s.oddpar = + ~(cvmx_dpop(stxx_spi4_calx.u64) & 1); + cvmx_write_csr(CVMX_STXX_SPI4_CALX(index, interface), + stxx_spi4_calx.u64); + index++; + } + stxx_spi4_stat.u64 = 0; + stxx_spi4_stat.s.len = num_ports; + stxx_spi4_stat.s.m = 1; + cvmx_write_csr(CVMX_STXX_SPI4_STAT(interface), + stxx_spi4_stat.u64); + } + + return 0; +} + +/** + * Callback to perform clock detection + * + * @interface: The identifier of the packet interface to configure and + * use as a SPI interface. + * @mode: The operating mode for the SPI interface. The interface + * can operate as a full duplex (both Tx and Rx data paths + * active) or as a halfplex (either the Tx data path is + * active or the Rx data path is active, but not both). + * @timeout: Timeout to wait for clock synchronization in seconds + * + * Returns Zero on success, non-zero error code on failure (will cause + * SPI initialization to abort) + */ +int cvmx_spi_clock_detect_cb(int interface, cvmx_spi_mode_t mode, int timeout) +{ + int clock_transitions; + union cvmx_spxx_clk_stat stat; + uint64_t timeout_time; + uint64_t MS = cvmx_sysinfo_get()->cpu_clock_hz / 1000; + + /* + * Regardless of operating mode, both Tx and Rx clocks must be + * present for the SPI interface to operate. + */ + cvmx_dprintf("SPI%d: Waiting to see TsClk...\n", interface); + timeout_time = cvmx_get_cycle() + 1000ull * MS * timeout; + /* + * Require 100 clock transitions in order to avoid any noise + * in the beginning. + */ + clock_transitions = 100; + do { + stat.u64 = cvmx_read_csr(CVMX_SPXX_CLK_STAT(interface)); + if (stat.s.s4clk0 && stat.s.s4clk1 && clock_transitions) { + /* + * We've seen a clock transition, so decrement + * the number we still need. + */ + clock_transitions--; + cvmx_write_csr(CVMX_SPXX_CLK_STAT(interface), stat.u64); + stat.s.s4clk0 = 0; + stat.s.s4clk1 = 0; + } + if (cvmx_get_cycle() > timeout_time) { + cvmx_dprintf("SPI%d: Timeout\n", interface); + return -1; + } + } while (stat.s.s4clk0 == 0 || stat.s.s4clk1 == 0); + + cvmx_dprintf("SPI%d: Waiting to see RsClk...\n", interface); + timeout_time = cvmx_get_cycle() + 1000ull * MS * timeout; + /* + * Require 100 clock transitions in order to avoid any noise in the + * beginning. + */ + clock_transitions = 100; + do { + stat.u64 = cvmx_read_csr(CVMX_SPXX_CLK_STAT(interface)); + if (stat.s.d4clk0 && stat.s.d4clk1 && clock_transitions) { + /* + * We've seen a clock transition, so decrement + * the number we still need + */ + clock_transitions--; + cvmx_write_csr(CVMX_SPXX_CLK_STAT(interface), stat.u64); + stat.s.d4clk0 = 0; + stat.s.d4clk1 = 0; + } + if (cvmx_get_cycle() > timeout_time) { + cvmx_dprintf("SPI%d: Timeout\n", interface); + return -1; + } + } while (stat.s.d4clk0 == 0 || stat.s.d4clk1 == 0); + + return 0; +} + +/** + * Callback to perform link training + * + * @interface: The identifier of the packet interface to configure and + * use as a SPI interface. + * @mode: The operating mode for the SPI interface. The interface + * can operate as a full duplex (both Tx and Rx data paths + * active) or as a halfplex (either the Tx data path is + * active or the Rx data path is active, but not both). + * @timeout: Timeout to wait for link to be trained (in seconds) + * + * Returns Zero on success, non-zero error code on failure (will cause + * SPI initialization to abort) + */ +int cvmx_spi_training_cb(int interface, cvmx_spi_mode_t mode, int timeout) +{ + union cvmx_spxx_trn4_ctl spxx_trn4_ctl; + union cvmx_spxx_clk_stat stat; + uint64_t MS = cvmx_sysinfo_get()->cpu_clock_hz / 1000; + uint64_t timeout_time = cvmx_get_cycle() + 1000ull * MS * timeout; + int rx_training_needed; + + /* SRX0 & STX0 Inf0 Links are configured - begin training */ + union cvmx_spxx_clk_ctl spxx_clk_ctl; + spxx_clk_ctl.u64 = 0; + spxx_clk_ctl.s.seetrn = 0; + spxx_clk_ctl.s.clkdly = 0x10; + spxx_clk_ctl.s.runbist = 0; + spxx_clk_ctl.s.statdrv = 0; + /* This should always be on the opposite edge as statdrv */ + spxx_clk_ctl.s.statrcv = 1; + spxx_clk_ctl.s.sndtrn = 1; + spxx_clk_ctl.s.drptrn = 1; + spxx_clk_ctl.s.rcvtrn = 1; + spxx_clk_ctl.s.srxdlck = 1; + cvmx_write_csr(CVMX_SPXX_CLK_CTL(interface), spxx_clk_ctl.u64); + cvmx_wait(1000 * MS); + + /* SRX0 clear the boot bit */ + spxx_trn4_ctl.u64 = cvmx_read_csr(CVMX_SPXX_TRN4_CTL(interface)); + spxx_trn4_ctl.s.clr_boot = 1; + cvmx_write_csr(CVMX_SPXX_TRN4_CTL(interface), spxx_trn4_ctl.u64); + + /* Wait for the training sequence to complete */ + cvmx_dprintf("SPI%d: Waiting for training\n", interface); + cvmx_wait(1000 * MS); + /* Wait a really long time here */ + timeout_time = cvmx_get_cycle() + 1000ull * MS * 600; + /* + * The HRM says we must wait for 34 + 16 * MAXDIST training sequences. + * We'll be pessimistic and wait for a lot more. + */ + rx_training_needed = 500; + do { + stat.u64 = cvmx_read_csr(CVMX_SPXX_CLK_STAT(interface)); + if (stat.s.srxtrn && rx_training_needed) { + rx_training_needed--; + cvmx_write_csr(CVMX_SPXX_CLK_STAT(interface), stat.u64); + stat.s.srxtrn = 0; + } + if (cvmx_get_cycle() > timeout_time) { + cvmx_dprintf("SPI%d: Timeout\n", interface); + return -1; + } + } while (stat.s.srxtrn == 0); + + return 0; +} + +/** + * Callback to perform calendar data synchronization + * + * @interface: The identifier of the packet interface to configure and + * use as a SPI interface. + * @mode: The operating mode for the SPI interface. The interface + * can operate as a full duplex (both Tx and Rx data paths + * active) or as a halfplex (either the Tx data path is + * active or the Rx data path is active, but not both). + * @timeout: Timeout to wait for calendar data in seconds + * + * Returns Zero on success, non-zero error code on failure (will cause + * SPI initialization to abort) + */ +int cvmx_spi_calendar_sync_cb(int interface, cvmx_spi_mode_t mode, int timeout) +{ + uint64_t MS = cvmx_sysinfo_get()->cpu_clock_hz / 1000; + if (mode & CVMX_SPI_MODE_RX_HALFPLEX) { + /* SRX0 interface should be good, send calendar data */ + union cvmx_srxx_com_ctl srxx_com_ctl; + cvmx_dprintf + ("SPI%d: Rx is synchronized, start sending calendar data\n", + interface); + srxx_com_ctl.u64 = cvmx_read_csr(CVMX_SRXX_COM_CTL(interface)); + srxx_com_ctl.s.inf_en = 1; + srxx_com_ctl.s.st_en = 1; + cvmx_write_csr(CVMX_SRXX_COM_CTL(interface), srxx_com_ctl.u64); + } + + if (mode & CVMX_SPI_MODE_TX_HALFPLEX) { + /* STX0 has achieved sync */ + /* The corespondant board should be sending calendar data */ + /* Enable the STX0 STAT receiver. */ + union cvmx_spxx_clk_stat stat; + uint64_t timeout_time; + union cvmx_stxx_com_ctl stxx_com_ctl; + stxx_com_ctl.u64 = 0; + stxx_com_ctl.s.st_en = 1; + cvmx_write_csr(CVMX_STXX_COM_CTL(interface), stxx_com_ctl.u64); + + /* Waiting for calendar sync on STX0 STAT */ + cvmx_dprintf("SPI%d: Waiting to sync on STX[%d] STAT\n", + interface, interface); + timeout_time = cvmx_get_cycle() + 1000ull * MS * timeout; + /* SPX0_CLK_STAT - SPX0_CLK_STAT[STXCAL] should be 1 (bit10) */ + do { + stat.u64 = cvmx_read_csr(CVMX_SPXX_CLK_STAT(interface)); + if (cvmx_get_cycle() > timeout_time) { + cvmx_dprintf("SPI%d: Timeout\n", interface); + return -1; + } + } while (stat.s.stxcal == 0); + } + + return 0; +} + +/** + * Callback to handle interface up + * + * @interface: The identifier of the packet interface to configure and + * use as a SPI interface. + * @mode: The operating mode for the SPI interface. The interface + * can operate as a full duplex (both Tx and Rx data paths + * active) or as a halfplex (either the Tx data path is + * active or the Rx data path is active, but not both). + * + * Returns Zero on success, non-zero error code on failure (will cause + * SPI initialization to abort) + */ +int cvmx_spi_interface_up_cb(int interface, cvmx_spi_mode_t mode) +{ + union cvmx_gmxx_rxx_frm_min gmxx_rxx_frm_min; + union cvmx_gmxx_rxx_frm_max gmxx_rxx_frm_max; + union cvmx_gmxx_rxx_jabber gmxx_rxx_jabber; + + if (mode & CVMX_SPI_MODE_RX_HALFPLEX) { + union cvmx_srxx_com_ctl srxx_com_ctl; + srxx_com_ctl.u64 = cvmx_read_csr(CVMX_SRXX_COM_CTL(interface)); + srxx_com_ctl.s.inf_en = 1; + cvmx_write_csr(CVMX_SRXX_COM_CTL(interface), srxx_com_ctl.u64); + cvmx_dprintf("SPI%d: Rx is now up\n", interface); + } + + if (mode & CVMX_SPI_MODE_TX_HALFPLEX) { + union cvmx_stxx_com_ctl stxx_com_ctl; + stxx_com_ctl.u64 = cvmx_read_csr(CVMX_STXX_COM_CTL(interface)); + stxx_com_ctl.s.inf_en = 1; + cvmx_write_csr(CVMX_STXX_COM_CTL(interface), stxx_com_ctl.u64); + cvmx_dprintf("SPI%d: Tx is now up\n", interface); + } + + gmxx_rxx_frm_min.u64 = 0; + gmxx_rxx_frm_min.s.len = 64; + cvmx_write_csr(CVMX_GMXX_RXX_FRM_MIN(0, interface), + gmxx_rxx_frm_min.u64); + gmxx_rxx_frm_max.u64 = 0; + gmxx_rxx_frm_max.s.len = 64 * 1024 - 4; + cvmx_write_csr(CVMX_GMXX_RXX_FRM_MAX(0, interface), + gmxx_rxx_frm_max.u64); + gmxx_rxx_jabber.u64 = 0; + gmxx_rxx_jabber.s.cnt = 64 * 1024 - 4; + cvmx_write_csr(CVMX_GMXX_RXX_JABBER(0, interface), gmxx_rxx_jabber.u64); + + return 0; +} |