mirror of
https://github.com/AetherDroid/android_kernel_samsung_on5xelte.git
synced 2025-09-10 01:12:45 -04:00
Fixed MTP to work with TWRP
This commit is contained in:
commit
f6dfaef42e
50820 changed files with 20846062 additions and 0 deletions
19
arch/mips/cavium-octeon/executive/Makefile
Normal file
19
arch/mips/cavium-octeon/executive/Makefile
Normal file
|
@ -0,0 +1,19 @@
|
|||
#
|
||||
# Makefile for the Cavium Octeon specific kernel interface routines
|
||||
# under Linux.
|
||||
#
|
||||
# This file is subject to the terms and conditions of the GNU General Public
|
||||
# License. See the file "COPYING" in the main directory of this archive
|
||||
# for more details.
|
||||
#
|
||||
# Copyright (C) 2005-2008 Cavium Networks
|
||||
#
|
||||
|
||||
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-y += cvmx-helper-errata.o cvmx-helper-jtag.o
|
695
arch/mips/cavium-octeon/executive/cvmx-bootmem.c
Normal file
695
arch/mips/cavium-octeon/executive/cvmx-bootmem.c
Normal file
|
@ -0,0 +1,695 @@
|
|||
/***********************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**************************************/
|
||||
|
||||
/*
|
||||
* Simple allocate only memory allocator. Used to allocate memory at
|
||||
* application start time.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
#include <asm/octeon/cvmx.h>
|
||||
#include <asm/octeon/cvmx-spinlock.h>
|
||||
#include <asm/octeon/cvmx-bootmem.h>
|
||||
|
||||
/*#define DEBUG */
|
||||
|
||||
|
||||
static struct cvmx_bootmem_desc *cvmx_bootmem_desc;
|
||||
|
||||
/* See header file for descriptions of functions */
|
||||
|
||||
/*
|
||||
* Wrapper functions are provided for reading/writing the size and
|
||||
* next block values as these may not be directly addressible (in 32
|
||||
* bit applications, for instance.) Offsets of data elements in
|
||||
* bootmem list, must match cvmx_bootmem_block_header_t.
|
||||
*/
|
||||
#define NEXT_OFFSET 0
|
||||
#define SIZE_OFFSET 8
|
||||
|
||||
static void cvmx_bootmem_phy_set_size(uint64_t addr, uint64_t size)
|
||||
{
|
||||
cvmx_write64_uint64((addr + SIZE_OFFSET) | (1ull << 63), size);
|
||||
}
|
||||
|
||||
static void cvmx_bootmem_phy_set_next(uint64_t addr, uint64_t next)
|
||||
{
|
||||
cvmx_write64_uint64((addr + NEXT_OFFSET) | (1ull << 63), next);
|
||||
}
|
||||
|
||||
static uint64_t cvmx_bootmem_phy_get_size(uint64_t addr)
|
||||
{
|
||||
return cvmx_read64_uint64((addr + SIZE_OFFSET) | (1ull << 63));
|
||||
}
|
||||
|
||||
static uint64_t cvmx_bootmem_phy_get_next(uint64_t addr)
|
||||
{
|
||||
return cvmx_read64_uint64((addr + NEXT_OFFSET) | (1ull << 63));
|
||||
}
|
||||
|
||||
void *cvmx_bootmem_alloc_range(uint64_t size, uint64_t alignment,
|
||||
uint64_t min_addr, uint64_t max_addr)
|
||||
{
|
||||
int64_t address;
|
||||
address =
|
||||
cvmx_bootmem_phy_alloc(size, min_addr, max_addr, alignment, 0);
|
||||
|
||||
if (address > 0)
|
||||
return cvmx_phys_to_ptr(address);
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void *cvmx_bootmem_alloc_address(uint64_t size, uint64_t address,
|
||||
uint64_t alignment)
|
||||
{
|
||||
return cvmx_bootmem_alloc_range(size, alignment, address,
|
||||
address + size);
|
||||
}
|
||||
|
||||
void *cvmx_bootmem_alloc(uint64_t size, uint64_t alignment)
|
||||
{
|
||||
return cvmx_bootmem_alloc_range(size, alignment, 0, 0);
|
||||
}
|
||||
|
||||
void *cvmx_bootmem_alloc_named_range(uint64_t size, uint64_t min_addr,
|
||||
uint64_t max_addr, uint64_t align,
|
||||
char *name)
|
||||
{
|
||||
int64_t addr;
|
||||
|
||||
addr = cvmx_bootmem_phy_named_block_alloc(size, min_addr, max_addr,
|
||||
align, name, 0);
|
||||
if (addr >= 0)
|
||||
return cvmx_phys_to_ptr(addr);
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void *cvmx_bootmem_alloc_named_address(uint64_t size, uint64_t address,
|
||||
char *name)
|
||||
{
|
||||
return cvmx_bootmem_alloc_named_range(size, address, address + size,
|
||||
0, name);
|
||||
}
|
||||
|
||||
void *cvmx_bootmem_alloc_named(uint64_t size, uint64_t alignment, char *name)
|
||||
{
|
||||
return cvmx_bootmem_alloc_named_range(size, 0, 0, alignment, name);
|
||||
}
|
||||
EXPORT_SYMBOL(cvmx_bootmem_alloc_named);
|
||||
|
||||
int cvmx_bootmem_free_named(char *name)
|
||||
{
|
||||
return cvmx_bootmem_phy_named_block_free(name, 0);
|
||||
}
|
||||
|
||||
struct cvmx_bootmem_named_block_desc *cvmx_bootmem_find_named_block(char *name)
|
||||
{
|
||||
return cvmx_bootmem_phy_named_block_find(name, 0);
|
||||
}
|
||||
EXPORT_SYMBOL(cvmx_bootmem_find_named_block);
|
||||
|
||||
void cvmx_bootmem_lock(void)
|
||||
{
|
||||
cvmx_spinlock_lock((cvmx_spinlock_t *) &(cvmx_bootmem_desc->lock));
|
||||
}
|
||||
|
||||
void cvmx_bootmem_unlock(void)
|
||||
{
|
||||
cvmx_spinlock_unlock((cvmx_spinlock_t *) &(cvmx_bootmem_desc->lock));
|
||||
}
|
||||
|
||||
int cvmx_bootmem_init(void *mem_desc_ptr)
|
||||
{
|
||||
/* Here we set the global pointer to the bootmem descriptor
|
||||
* block. This pointer will be used directly, so we will set
|
||||
* it up to be directly usable by the application. It is set
|
||||
* up as follows for the various runtime/ABI combinations:
|
||||
*
|
||||
* Linux 64 bit: Set XKPHYS bit
|
||||
* Linux 32 bit: use mmap to create mapping, use virtual address
|
||||
* CVMX 64 bit: use physical address directly
|
||||
* CVMX 32 bit: use physical address directly
|
||||
*
|
||||
* Note that the CVMX environment assumes the use of 1-1 TLB
|
||||
* mappings so that the physical addresses can be used
|
||||
* directly
|
||||
*/
|
||||
if (!cvmx_bootmem_desc) {
|
||||
#if defined(CVMX_ABI_64)
|
||||
/* Set XKPHYS bit */
|
||||
cvmx_bootmem_desc = cvmx_phys_to_ptr(CAST64(mem_desc_ptr));
|
||||
#else
|
||||
cvmx_bootmem_desc = (struct cvmx_bootmem_desc *) mem_desc_ptr;
|
||||
#endif
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* The cvmx_bootmem_phy* functions below return 64 bit physical
|
||||
* addresses, and expose more features that the cvmx_bootmem_functions
|
||||
* above. These are required for full memory space access in 32 bit
|
||||
* applications, as well as for using some advance features. Most
|
||||
* applications should not need to use these.
|
||||
*/
|
||||
|
||||
int64_t cvmx_bootmem_phy_alloc(uint64_t req_size, uint64_t address_min,
|
||||
uint64_t address_max, uint64_t alignment,
|
||||
uint32_t flags)
|
||||
{
|
||||
|
||||
uint64_t head_addr;
|
||||
uint64_t ent_addr;
|
||||
/* points to previous list entry, NULL current entry is head of list */
|
||||
uint64_t prev_addr = 0;
|
||||
uint64_t new_ent_addr = 0;
|
||||
uint64_t desired_min_addr;
|
||||
|
||||
#ifdef DEBUG
|
||||
cvmx_dprintf("cvmx_bootmem_phy_alloc: req_size: 0x%llx, "
|
||||
"min_addr: 0x%llx, max_addr: 0x%llx, align: 0x%llx\n",
|
||||
(unsigned long long)req_size,
|
||||
(unsigned long long)address_min,
|
||||
(unsigned long long)address_max,
|
||||
(unsigned long long)alignment);
|
||||
#endif
|
||||
|
||||
if (cvmx_bootmem_desc->major_version > 3) {
|
||||
cvmx_dprintf("ERROR: Incompatible bootmem descriptor "
|
||||
"version: %d.%d at addr: %p\n",
|
||||
(int)cvmx_bootmem_desc->major_version,
|
||||
(int)cvmx_bootmem_desc->minor_version,
|
||||
cvmx_bootmem_desc);
|
||||
goto error_out;
|
||||
}
|
||||
|
||||
/*
|
||||
* Do a variety of checks to validate the arguments. The
|
||||
* allocator code will later assume that these checks have
|
||||
* been made. We validate that the requested constraints are
|
||||
* not self-contradictory before we look through the list of
|
||||
* available memory.
|
||||
*/
|
||||
|
||||
/* 0 is not a valid req_size for this allocator */
|
||||
if (!req_size)
|
||||
goto error_out;
|
||||
|
||||
/* Round req_size up to mult of minimum alignment bytes */
|
||||
req_size = (req_size + (CVMX_BOOTMEM_ALIGNMENT_SIZE - 1)) &
|
||||
~(CVMX_BOOTMEM_ALIGNMENT_SIZE - 1);
|
||||
|
||||
/*
|
||||
* Convert !0 address_min and 0 address_max to special case of
|
||||
* range that specifies an exact memory block to allocate. Do
|
||||
* this before other checks and adjustments so that this
|
||||
* tranformation will be validated.
|
||||
*/
|
||||
if (address_min && !address_max)
|
||||
address_max = address_min + req_size;
|
||||
else if (!address_min && !address_max)
|
||||
address_max = ~0ull; /* If no limits given, use max limits */
|
||||
|
||||
|
||||
/*
|
||||
* Enforce minimum alignment (this also keeps the minimum free block
|
||||
* req_size the same as the alignment req_size.
|
||||
*/
|
||||
if (alignment < CVMX_BOOTMEM_ALIGNMENT_SIZE)
|
||||
alignment = CVMX_BOOTMEM_ALIGNMENT_SIZE;
|
||||
|
||||
/*
|
||||
* Adjust address minimum based on requested alignment (round
|
||||
* up to meet alignment). Do this here so we can reject
|
||||
* impossible requests up front. (NOP for address_min == 0)
|
||||
*/
|
||||
if (alignment)
|
||||
address_min = ALIGN(address_min, alignment);
|
||||
|
||||
/*
|
||||
* Reject inconsistent args. We have adjusted these, so this
|
||||
* may fail due to our internal changes even if this check
|
||||
* would pass for the values the user supplied.
|
||||
*/
|
||||
if (req_size > address_max - address_min)
|
||||
goto error_out;
|
||||
|
||||
/* Walk through the list entries - first fit found is returned */
|
||||
|
||||
if (!(flags & CVMX_BOOTMEM_FLAG_NO_LOCKING))
|
||||
cvmx_bootmem_lock();
|
||||
head_addr = cvmx_bootmem_desc->head_addr;
|
||||
ent_addr = head_addr;
|
||||
for (; ent_addr;
|
||||
prev_addr = ent_addr,
|
||||
ent_addr = cvmx_bootmem_phy_get_next(ent_addr)) {
|
||||
uint64_t usable_base, usable_max;
|
||||
uint64_t ent_size = cvmx_bootmem_phy_get_size(ent_addr);
|
||||
|
||||
if (cvmx_bootmem_phy_get_next(ent_addr)
|
||||
&& ent_addr > cvmx_bootmem_phy_get_next(ent_addr)) {
|
||||
cvmx_dprintf("Internal bootmem_alloc() error: ent: "
|
||||
"0x%llx, next: 0x%llx\n",
|
||||
(unsigned long long)ent_addr,
|
||||
(unsigned long long)
|
||||
cvmx_bootmem_phy_get_next(ent_addr));
|
||||
goto error_out;
|
||||
}
|
||||
|
||||
/*
|
||||
* Determine if this is an entry that can satisify the
|
||||
* request Check to make sure entry is large enough to
|
||||
* satisfy request.
|
||||
*/
|
||||
usable_base =
|
||||
ALIGN(max(address_min, ent_addr), alignment);
|
||||
usable_max = min(address_max, ent_addr + ent_size);
|
||||
/*
|
||||
* We should be able to allocate block at address
|
||||
* usable_base.
|
||||
*/
|
||||
|
||||
desired_min_addr = usable_base;
|
||||
/*
|
||||
* Determine if request can be satisfied from the
|
||||
* current entry.
|
||||
*/
|
||||
if (!((ent_addr + ent_size) > usable_base
|
||||
&& ent_addr < address_max
|
||||
&& req_size <= usable_max - usable_base))
|
||||
continue;
|
||||
/*
|
||||
* We have found an entry that has room to satisfy the
|
||||
* request, so allocate it from this entry. If end
|
||||
* CVMX_BOOTMEM_FLAG_END_ALLOC set, then allocate from
|
||||
* the end of this block rather than the beginning.
|
||||
*/
|
||||
if (flags & CVMX_BOOTMEM_FLAG_END_ALLOC) {
|
||||
desired_min_addr = usable_max - req_size;
|
||||
/*
|
||||
* Align desired address down to required
|
||||
* alignment.
|
||||
*/
|
||||
desired_min_addr &= ~(alignment - 1);
|
||||
}
|
||||
|
||||
/* Match at start of entry */
|
||||
if (desired_min_addr == ent_addr) {
|
||||
if (req_size < ent_size) {
|
||||
/*
|
||||
* big enough to create a new block
|
||||
* from top portion of block.
|
||||
*/
|
||||
new_ent_addr = ent_addr + req_size;
|
||||
cvmx_bootmem_phy_set_next(new_ent_addr,
|
||||
cvmx_bootmem_phy_get_next(ent_addr));
|
||||
cvmx_bootmem_phy_set_size(new_ent_addr,
|
||||
ent_size -
|
||||
req_size);
|
||||
|
||||
/*
|
||||
* Adjust next pointer as following
|
||||
* code uses this.
|
||||
*/
|
||||
cvmx_bootmem_phy_set_next(ent_addr,
|
||||
new_ent_addr);
|
||||
}
|
||||
|
||||
/*
|
||||
* adjust prev ptr or head to remove this
|
||||
* entry from list.
|
||||
*/
|
||||
if (prev_addr)
|
||||
cvmx_bootmem_phy_set_next(prev_addr,
|
||||
cvmx_bootmem_phy_get_next(ent_addr));
|
||||
else
|
||||
/*
|
||||
* head of list being returned, so
|
||||
* update head ptr.
|
||||
*/
|
||||
cvmx_bootmem_desc->head_addr =
|
||||
cvmx_bootmem_phy_get_next(ent_addr);
|
||||
|
||||
if (!(flags & CVMX_BOOTMEM_FLAG_NO_LOCKING))
|
||||
cvmx_bootmem_unlock();
|
||||
return desired_min_addr;
|
||||
}
|
||||
/*
|
||||
* block returned doesn't start at beginning of entry,
|
||||
* so we know that we will be splitting a block off
|
||||
* the front of this one. Create a new block from the
|
||||
* beginning, add to list, and go to top of loop
|
||||
* again.
|
||||
*
|
||||
* create new block from high portion of
|
||||
* block, so that top block starts at desired
|
||||
* addr.
|
||||
*/
|
||||
new_ent_addr = desired_min_addr;
|
||||
cvmx_bootmem_phy_set_next(new_ent_addr,
|
||||
cvmx_bootmem_phy_get_next
|
||||
(ent_addr));
|
||||
cvmx_bootmem_phy_set_size(new_ent_addr,
|
||||
cvmx_bootmem_phy_get_size
|
||||
(ent_addr) -
|
||||
(desired_min_addr -
|
||||
ent_addr));
|
||||
cvmx_bootmem_phy_set_size(ent_addr,
|
||||
desired_min_addr - ent_addr);
|
||||
cvmx_bootmem_phy_set_next(ent_addr, new_ent_addr);
|
||||
/* Loop again to handle actual alloc from new block */
|
||||
}
|
||||
error_out:
|
||||
/* We didn't find anything, so return error */
|
||||
if (!(flags & CVMX_BOOTMEM_FLAG_NO_LOCKING))
|
||||
cvmx_bootmem_unlock();
|
||||
return -1;
|
||||
}
|
||||
|
||||
int __cvmx_bootmem_phy_free(uint64_t phy_addr, uint64_t size, uint32_t flags)
|
||||
{
|
||||
uint64_t cur_addr;
|
||||
uint64_t prev_addr = 0; /* zero is invalid */
|
||||
int retval = 0;
|
||||
|
||||
#ifdef DEBUG
|
||||
cvmx_dprintf("__cvmx_bootmem_phy_free addr: 0x%llx, size: 0x%llx\n",
|
||||
(unsigned long long)phy_addr, (unsigned long long)size);
|
||||
#endif
|
||||
if (cvmx_bootmem_desc->major_version > 3) {
|
||||
cvmx_dprintf("ERROR: Incompatible bootmem descriptor "
|
||||
"version: %d.%d at addr: %p\n",
|
||||
(int)cvmx_bootmem_desc->major_version,
|
||||
(int)cvmx_bootmem_desc->minor_version,
|
||||
cvmx_bootmem_desc);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* 0 is not a valid size for this allocator */
|
||||
if (!size)
|
||||
return 0;
|
||||
|
||||
if (!(flags & CVMX_BOOTMEM_FLAG_NO_LOCKING))
|
||||
cvmx_bootmem_lock();
|
||||
cur_addr = cvmx_bootmem_desc->head_addr;
|
||||
if (cur_addr == 0 || phy_addr < cur_addr) {
|
||||
/* add at front of list - special case with changing head ptr */
|
||||
if (cur_addr && phy_addr + size > cur_addr)
|
||||
goto bootmem_free_done; /* error, overlapping section */
|
||||
else if (phy_addr + size == cur_addr) {
|
||||
/* Add to front of existing first block */
|
||||
cvmx_bootmem_phy_set_next(phy_addr,
|
||||
cvmx_bootmem_phy_get_next
|
||||
(cur_addr));
|
||||
cvmx_bootmem_phy_set_size(phy_addr,
|
||||
cvmx_bootmem_phy_get_size
|
||||
(cur_addr) + size);
|
||||
cvmx_bootmem_desc->head_addr = phy_addr;
|
||||
|
||||
} else {
|
||||
/* New block before first block. OK if cur_addr is 0 */
|
||||
cvmx_bootmem_phy_set_next(phy_addr, cur_addr);
|
||||
cvmx_bootmem_phy_set_size(phy_addr, size);
|
||||
cvmx_bootmem_desc->head_addr = phy_addr;
|
||||
}
|
||||
retval = 1;
|
||||
goto bootmem_free_done;
|
||||
}
|
||||
|
||||
/* Find place in list to add block */
|
||||
while (cur_addr && phy_addr > cur_addr) {
|
||||
prev_addr = cur_addr;
|
||||
cur_addr = cvmx_bootmem_phy_get_next(cur_addr);
|
||||
}
|
||||
|
||||
if (!cur_addr) {
|
||||
/*
|
||||
* We have reached the end of the list, add on to end,
|
||||
* checking to see if we need to combine with last
|
||||
* block
|
||||
*/
|
||||
if (prev_addr + cvmx_bootmem_phy_get_size(prev_addr) ==
|
||||
phy_addr) {
|
||||
cvmx_bootmem_phy_set_size(prev_addr,
|
||||
cvmx_bootmem_phy_get_size
|
||||
(prev_addr) + size);
|
||||
} else {
|
||||
cvmx_bootmem_phy_set_next(prev_addr, phy_addr);
|
||||
cvmx_bootmem_phy_set_size(phy_addr, size);
|
||||
cvmx_bootmem_phy_set_next(phy_addr, 0);
|
||||
}
|
||||
retval = 1;
|
||||
goto bootmem_free_done;
|
||||
} else {
|
||||
/*
|
||||
* insert between prev and cur nodes, checking for
|
||||
* merge with either/both.
|
||||
*/
|
||||
if (prev_addr + cvmx_bootmem_phy_get_size(prev_addr) ==
|
||||
phy_addr) {
|
||||
/* Merge with previous */
|
||||
cvmx_bootmem_phy_set_size(prev_addr,
|
||||
cvmx_bootmem_phy_get_size
|
||||
(prev_addr) + size);
|
||||
if (phy_addr + size == cur_addr) {
|
||||
/* Also merge with current */
|
||||
cvmx_bootmem_phy_set_size(prev_addr,
|
||||
cvmx_bootmem_phy_get_size(cur_addr) +
|
||||
cvmx_bootmem_phy_get_size(prev_addr));
|
||||
cvmx_bootmem_phy_set_next(prev_addr,
|
||||
cvmx_bootmem_phy_get_next(cur_addr));
|
||||
}
|
||||
retval = 1;
|
||||
goto bootmem_free_done;
|
||||
} else if (phy_addr + size == cur_addr) {
|
||||
/* Merge with current */
|
||||
cvmx_bootmem_phy_set_size(phy_addr,
|
||||
cvmx_bootmem_phy_get_size
|
||||
(cur_addr) + size);
|
||||
cvmx_bootmem_phy_set_next(phy_addr,
|
||||
cvmx_bootmem_phy_get_next
|
||||
(cur_addr));
|
||||
cvmx_bootmem_phy_set_next(prev_addr, phy_addr);
|
||||
retval = 1;
|
||||
goto bootmem_free_done;
|
||||
}
|
||||
|
||||
/* It is a standalone block, add in between prev and cur */
|
||||
cvmx_bootmem_phy_set_size(phy_addr, size);
|
||||
cvmx_bootmem_phy_set_next(phy_addr, cur_addr);
|
||||
cvmx_bootmem_phy_set_next(prev_addr, phy_addr);
|
||||
|
||||
}
|
||||
retval = 1;
|
||||
|
||||
bootmem_free_done:
|
||||
if (!(flags & CVMX_BOOTMEM_FLAG_NO_LOCKING))
|
||||
cvmx_bootmem_unlock();
|
||||
return retval;
|
||||
|
||||
}
|
||||
|
||||
struct cvmx_bootmem_named_block_desc *
|
||||
cvmx_bootmem_phy_named_block_find(char *name, uint32_t flags)
|
||||
{
|
||||
unsigned int i;
|
||||
struct cvmx_bootmem_named_block_desc *named_block_array_ptr;
|
||||
|
||||
#ifdef DEBUG
|
||||
cvmx_dprintf("cvmx_bootmem_phy_named_block_find: %s\n", name);
|
||||
#endif
|
||||
/*
|
||||
* Lock the structure to make sure that it is not being
|
||||
* changed while we are examining it.
|
||||
*/
|
||||
if (!(flags & CVMX_BOOTMEM_FLAG_NO_LOCKING))
|
||||
cvmx_bootmem_lock();
|
||||
|
||||
/* Use XKPHYS for 64 bit linux */
|
||||
named_block_array_ptr = (struct cvmx_bootmem_named_block_desc *)
|
||||
cvmx_phys_to_ptr(cvmx_bootmem_desc->named_block_array_addr);
|
||||
|
||||
#ifdef DEBUG
|
||||
cvmx_dprintf
|
||||
("cvmx_bootmem_phy_named_block_find: named_block_array_ptr: %p\n",
|
||||
named_block_array_ptr);
|
||||
#endif
|
||||
if (cvmx_bootmem_desc->major_version == 3) {
|
||||
for (i = 0;
|
||||
i < cvmx_bootmem_desc->named_block_num_blocks; i++) {
|
||||
if ((name && named_block_array_ptr[i].size
|
||||
&& !strncmp(name, named_block_array_ptr[i].name,
|
||||
cvmx_bootmem_desc->named_block_name_len
|
||||
- 1))
|
||||
|| (!name && !named_block_array_ptr[i].size)) {
|
||||
if (!(flags & CVMX_BOOTMEM_FLAG_NO_LOCKING))
|
||||
cvmx_bootmem_unlock();
|
||||
|
||||
return &(named_block_array_ptr[i]);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
cvmx_dprintf("ERROR: Incompatible bootmem descriptor "
|
||||
"version: %d.%d at addr: %p\n",
|
||||
(int)cvmx_bootmem_desc->major_version,
|
||||
(int)cvmx_bootmem_desc->minor_version,
|
||||
cvmx_bootmem_desc);
|
||||
}
|
||||
if (!(flags & CVMX_BOOTMEM_FLAG_NO_LOCKING))
|
||||
cvmx_bootmem_unlock();
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int cvmx_bootmem_phy_named_block_free(char *name, uint32_t flags)
|
||||
{
|
||||
struct cvmx_bootmem_named_block_desc *named_block_ptr;
|
||||
|
||||
if (cvmx_bootmem_desc->major_version != 3) {
|
||||
cvmx_dprintf("ERROR: Incompatible bootmem descriptor version: "
|
||||
"%d.%d at addr: %p\n",
|
||||
(int)cvmx_bootmem_desc->major_version,
|
||||
(int)cvmx_bootmem_desc->minor_version,
|
||||
cvmx_bootmem_desc);
|
||||
return 0;
|
||||
}
|
||||
#ifdef DEBUG
|
||||
cvmx_dprintf("cvmx_bootmem_phy_named_block_free: %s\n", name);
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Take lock here, as name lookup/block free/name free need to
|
||||
* be atomic.
|
||||
*/
|
||||
cvmx_bootmem_lock();
|
||||
|
||||
named_block_ptr =
|
||||
cvmx_bootmem_phy_named_block_find(name,
|
||||
CVMX_BOOTMEM_FLAG_NO_LOCKING);
|
||||
if (named_block_ptr) {
|
||||
#ifdef DEBUG
|
||||
cvmx_dprintf("cvmx_bootmem_phy_named_block_free: "
|
||||
"%s, base: 0x%llx, size: 0x%llx\n",
|
||||
name,
|
||||
(unsigned long long)named_block_ptr->base_addr,
|
||||
(unsigned long long)named_block_ptr->size);
|
||||
#endif
|
||||
__cvmx_bootmem_phy_free(named_block_ptr->base_addr,
|
||||
named_block_ptr->size,
|
||||
CVMX_BOOTMEM_FLAG_NO_LOCKING);
|
||||
named_block_ptr->size = 0;
|
||||
/* Set size to zero to indicate block not used. */
|
||||
}
|
||||
|
||||
cvmx_bootmem_unlock();
|
||||
return named_block_ptr != NULL; /* 0 on failure, 1 on success */
|
||||
}
|
||||
|
||||
int64_t cvmx_bootmem_phy_named_block_alloc(uint64_t size, uint64_t min_addr,
|
||||
uint64_t max_addr,
|
||||
uint64_t alignment,
|
||||
char *name,
|
||||
uint32_t flags)
|
||||
{
|
||||
int64_t addr_allocated;
|
||||
struct cvmx_bootmem_named_block_desc *named_block_desc_ptr;
|
||||
|
||||
#ifdef DEBUG
|
||||
cvmx_dprintf("cvmx_bootmem_phy_named_block_alloc: size: 0x%llx, min: "
|
||||
"0x%llx, max: 0x%llx, align: 0x%llx, name: %s\n",
|
||||
(unsigned long long)size,
|
||||
(unsigned long long)min_addr,
|
||||
(unsigned long long)max_addr,
|
||||
(unsigned long long)alignment,
|
||||
name);
|
||||
#endif
|
||||
if (cvmx_bootmem_desc->major_version != 3) {
|
||||
cvmx_dprintf("ERROR: Incompatible bootmem descriptor version: "
|
||||
"%d.%d at addr: %p\n",
|
||||
(int)cvmx_bootmem_desc->major_version,
|
||||
(int)cvmx_bootmem_desc->minor_version,
|
||||
cvmx_bootmem_desc);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Take lock here, as name lookup/block alloc/name add need to
|
||||
* be atomic.
|
||||
*/
|
||||
if (!(flags & CVMX_BOOTMEM_FLAG_NO_LOCKING))
|
||||
cvmx_spinlock_lock((cvmx_spinlock_t *)&(cvmx_bootmem_desc->lock));
|
||||
|
||||
/* Get pointer to first available named block descriptor */
|
||||
named_block_desc_ptr =
|
||||
cvmx_bootmem_phy_named_block_find(NULL,
|
||||
flags | CVMX_BOOTMEM_FLAG_NO_LOCKING);
|
||||
|
||||
/*
|
||||
* Check to see if name already in use, return error if name
|
||||
* not available or no more room for blocks.
|
||||
*/
|
||||
if (cvmx_bootmem_phy_named_block_find(name,
|
||||
flags | CVMX_BOOTMEM_FLAG_NO_LOCKING) || !named_block_desc_ptr) {
|
||||
if (!(flags & CVMX_BOOTMEM_FLAG_NO_LOCKING))
|
||||
cvmx_spinlock_unlock((cvmx_spinlock_t *)&(cvmx_bootmem_desc->lock));
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Round size up to mult of minimum alignment bytes We need
|
||||
* the actual size allocated to allow for blocks to be
|
||||
* coallesced when they are freed. The alloc routine does the
|
||||
* same rounding up on all allocations.
|
||||
*/
|
||||
size = ALIGN(size, CVMX_BOOTMEM_ALIGNMENT_SIZE);
|
||||
|
||||
addr_allocated = cvmx_bootmem_phy_alloc(size, min_addr, max_addr,
|
||||
alignment,
|
||||
flags | CVMX_BOOTMEM_FLAG_NO_LOCKING);
|
||||
if (addr_allocated >= 0) {
|
||||
named_block_desc_ptr->base_addr = addr_allocated;
|
||||
named_block_desc_ptr->size = size;
|
||||
strncpy(named_block_desc_ptr->name, name,
|
||||
cvmx_bootmem_desc->named_block_name_len);
|
||||
named_block_desc_ptr->name[cvmx_bootmem_desc->named_block_name_len - 1] = 0;
|
||||
}
|
||||
|
||||
if (!(flags & CVMX_BOOTMEM_FLAG_NO_LOCKING))
|
||||
cvmx_spinlock_unlock((cvmx_spinlock_t *)&(cvmx_bootmem_desc->lock));
|
||||
return addr_allocated;
|
||||
}
|
||||
|
||||
struct cvmx_bootmem_desc *cvmx_bootmem_get_desc(void)
|
||||
{
|
||||
return cvmx_bootmem_desc;
|
||||
}
|
307
arch/mips/cavium-octeon/executive/cvmx-cmd-queue.c
Normal file
307
arch/mips/cavium-octeon/executive/cvmx-cmd-queue.c
Normal file
|
@ -0,0 +1,307 @@
|
|||
/***********************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;
|
||||
EXPORT_SYMBOL_GPL(__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;
|
||||
}
|
773
arch/mips/cavium-octeon/executive/cvmx-helper-board.c
Normal file
773
arch/mips/cavium-octeon/executive/cvmx-helper-board.c
Normal file
|
@ -0,0 +1,773 @@
|
|||
/***********************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 2 management ports */
|
||||
if ((ipd_port >= CVMX_HELPER_BOARD_MGMT_IPD_PORT) &&
|
||||
(ipd_port < (CVMX_HELPER_BOARD_MGMT_IPD_PORT + 2)))
|
||||
return ipd_port - CVMX_HELPER_BOARD_MGMT_IPD_PORT;
|
||||
/*
|
||||
* 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 1 management port */
|
||||
if (ipd_port == CVMX_HELPER_BOARD_MGMT_IPD_PORT)
|
||||
return 0;
|
||||
/*
|
||||
* 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_NIC_XLE_10G:
|
||||
case CVMX_BOARD_TYPE_NIC10E:
|
||||
return -1;
|
||||
case CVMX_BOARD_TYPE_NIC4E:
|
||||
if (ipd_port >= 0 && ipd_port <= 3)
|
||||
return (ipd_port + 0x1f) & 0x1f;
|
||||
else
|
||||
return -1;
|
||||
case CVMX_BOARD_TYPE_NIC2E:
|
||||
if (ipd_port >= 0 && ipd_port <= 1)
|
||||
return ipd_port + 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;
|
||||
case CVMX_BOARD_TYPE_UBNT_E100:
|
||||
if (ipd_port >= 0 && ipd_port <= 2)
|
||||
return 7 - ipd_port;
|
||||
else
|
||||
return -1;
|
||||
case CVMX_BOARD_TYPE_CUST_DSR1000N:
|
||||
/*
|
||||
* Port 2 connects to Broadcom PHY (B5081). Other ports (0-1)
|
||||
* connect to a switch (BCM53115).
|
||||
*/
|
||||
if (ipd_port == 2)
|
||||
return 8;
|
||||
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;
|
||||
case CVMX_BOARD_TYPE_CUST_DSR1000N:
|
||||
if (ipd_port == 0 || ipd_port == 1) {
|
||||
/* Ports 0 and 1 connect to a switch (BCM53115). */
|
||||
result.s.link_up = 1;
|
||||
result.s.full_duplex = 1;
|
||||
result.s.speed = 1000;
|
||||
return result;
|
||||
} else {
|
||||
/* Port 2 uses a Broadcom PHY (B5081). */
|
||||
is_broadcom_phy = 1;
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (cvmx_sysinfo_get()->board_type ==
|
||||
CVMX_BOARD_TYPE_UBNT_E100) {
|
||||
cvmx_write_csr(CVMX_ASXX_RX_CLK_SETX(0, interface), 0);
|
||||
cvmx_write_csr(CVMX_ASXX_TX_CLK_SETX(0, interface), 0x10);
|
||||
cvmx_write_csr(CVMX_ASXX_RX_CLK_SETX(1, interface), 0);
|
||||
cvmx_write_csr(CVMX_ASXX_TX_CLK_SETX(1, interface), 0x10);
|
||||
cvmx_write_csr(CVMX_ASXX_RX_CLK_SETX(2, interface), 0);
|
||||
cvmx_write_csr(CVMX_ASXX_TX_CLK_SETX(2, interface), 0x10);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the clock type used for the USB block based on board type.
|
||||
* Used by the USB code for auto configuration of clock type.
|
||||
*
|
||||
* Return USB clock type enumeration
|
||||
*/
|
||||
enum cvmx_helper_board_usb_clock_types __cvmx_helper_board_usb_get_clock_type(void)
|
||||
{
|
||||
switch (cvmx_sysinfo_get()->board_type) {
|
||||
case CVMX_BOARD_TYPE_BBGW_REF:
|
||||
case CVMX_BOARD_TYPE_LANAI2_A:
|
||||
case CVMX_BOARD_TYPE_LANAI2_U:
|
||||
case CVMX_BOARD_TYPE_LANAI2_G:
|
||||
case CVMX_BOARD_TYPE_NIC10E_66:
|
||||
case CVMX_BOARD_TYPE_UBNT_E100:
|
||||
case CVMX_BOARD_TYPE_CUST_DSR1000N:
|
||||
return USB_CLOCK_TYPE_CRYSTAL_12;
|
||||
case CVMX_BOARD_TYPE_NIC10E:
|
||||
return USB_CLOCK_TYPE_REF_12;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
/* Most boards except NIC10e use a 12MHz crystal */
|
||||
if (OCTEON_IS_MODEL(OCTEON_FAM_2))
|
||||
return USB_CLOCK_TYPE_CRYSTAL_12;
|
||||
return USB_CLOCK_TYPE_REF_48;
|
||||
}
|
73
arch/mips/cavium-octeon/executive/cvmx-helper-errata.c
Normal file
73
arch/mips/cavium-octeon/executive/cvmx-helper-errata.c
Normal file
|
@ -0,0 +1,73 @@
|
|||
/***********************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**************************************/
|
||||
|
||||
/**
|
||||
*
|
||||
* Fixes and workaround for Octeon chip errata. This file
|
||||
* contains functions called by cvmx-helper to workaround known
|
||||
* chip errata. For the most part, code doesn't need to call
|
||||
* these functions directly.
|
||||
*
|
||||
*/
|
||||
#include <linux/module.h>
|
||||
|
||||
#include <asm/octeon/octeon.h>
|
||||
|
||||
#include <asm/octeon/cvmx-helper-jtag.h>
|
||||
|
||||
/**
|
||||
* Due to errata G-720, the 2nd order CDR circuit on CN52XX pass
|
||||
* 1 doesn't work properly. The following code disables 2nd order
|
||||
* CDR for the specified QLM.
|
||||
*
|
||||
* @qlm: QLM to disable 2nd order CDR for.
|
||||
*/
|
||||
void __cvmx_helper_errata_qlm_disable_2nd_order_cdr(int qlm)
|
||||
{
|
||||
int lane;
|
||||
cvmx_helper_qlm_jtag_init();
|
||||
/* We need to load all four lanes of the QLM, a total of 1072 bits */
|
||||
for (lane = 0; lane < 4; lane++) {
|
||||
/*
|
||||
* Each lane has 268 bits. We need to set
|
||||
* cfg_cdr_incx<67:64> = 3 and cfg_cdr_secord<77> =
|
||||
* 1. All other bits are zero. Bits go in LSB first,
|
||||
* so start off with the zeros for bits <63:0>.
|
||||
*/
|
||||
cvmx_helper_qlm_jtag_shift_zeros(qlm, 63 - 0 + 1);
|
||||
/* cfg_cdr_incx<67:64>=3 */
|
||||
cvmx_helper_qlm_jtag_shift(qlm, 67 - 64 + 1, 3);
|
||||
/* Zeros for bits <76:68> */
|
||||
cvmx_helper_qlm_jtag_shift_zeros(qlm, 76 - 68 + 1);
|
||||
/* cfg_cdr_secord<77>=1 */
|
||||
cvmx_helper_qlm_jtag_shift(qlm, 77 - 77 + 1, 1);
|
||||
/* Zeros for bits <267:78> */
|
||||
cvmx_helper_qlm_jtag_shift_zeros(qlm, 267 - 78 + 1);
|
||||
}
|
||||
cvmx_helper_qlm_jtag_update(qlm);
|
||||
}
|
||||
EXPORT_SYMBOL(__cvmx_helper_errata_qlm_disable_2nd_order_cdr);
|
144
arch/mips/cavium-octeon/executive/cvmx-helper-jtag.c
Normal file
144
arch/mips/cavium-octeon/executive/cvmx-helper-jtag.c
Normal file
|
@ -0,0 +1,144 @@
|
|||
|
||||
/***********************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 utilities for qlm_jtag.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <asm/octeon/octeon.h>
|
||||
#include <asm/octeon/cvmx-helper-jtag.h>
|
||||
|
||||
|
||||
/**
|
||||
* Initialize the internal QLM JTAG logic to allow programming
|
||||
* of the JTAG chain by the cvmx_helper_qlm_jtag_*() functions.
|
||||
* These functions should only be used at the direction of Cavium
|
||||
* Networks. Programming incorrect values into the JTAG chain
|
||||
* can cause chip damage.
|
||||
*/
|
||||
void cvmx_helper_qlm_jtag_init(void)
|
||||
{
|
||||
union cvmx_ciu_qlm_jtgc jtgc;
|
||||
uint32_t clock_div = 0;
|
||||
uint32_t divisor = cvmx_sysinfo_get()->cpu_clock_hz / (25 * 1000000);
|
||||
divisor = (divisor - 1) >> 2;
|
||||
/* Convert the divisor into a power of 2 shift */
|
||||
while (divisor) {
|
||||
clock_div++;
|
||||
divisor = divisor >> 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Clock divider for QLM JTAG operations. eclk is divided by
|
||||
* 2^(CLK_DIV + 2)
|
||||
*/
|
||||
jtgc.u64 = 0;
|
||||
jtgc.s.clk_div = clock_div;
|
||||
jtgc.s.mux_sel = 0;
|
||||
if (OCTEON_IS_MODEL(OCTEON_CN52XX))
|
||||
jtgc.s.bypass = 0x3;
|
||||
else
|
||||
jtgc.s.bypass = 0xf;
|
||||
cvmx_write_csr(CVMX_CIU_QLM_JTGC, jtgc.u64);
|
||||
cvmx_read_csr(CVMX_CIU_QLM_JTGC);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write up to 32bits into the QLM jtag chain. Bits are shifted
|
||||
* into the MSB and out the LSB, so you should shift in the low
|
||||
* order bits followed by the high order bits. The JTAG chain is
|
||||
* 4 * 268 bits long, or 1072.
|
||||
*
|
||||
* @qlm: QLM to shift value into
|
||||
* @bits: Number of bits to shift in (1-32).
|
||||
* @data: Data to shift in. Bit 0 enters the chain first, followed by
|
||||
* bit 1, etc.
|
||||
*
|
||||
* Returns The low order bits of the JTAG chain that shifted out of the
|
||||
* circle.
|
||||
*/
|
||||
uint32_t cvmx_helper_qlm_jtag_shift(int qlm, int bits, uint32_t data)
|
||||
{
|
||||
union cvmx_ciu_qlm_jtgd jtgd;
|
||||
jtgd.u64 = 0;
|
||||
jtgd.s.shift = 1;
|
||||
jtgd.s.shft_cnt = bits - 1;
|
||||
jtgd.s.shft_reg = data;
|
||||
if (!OCTEON_IS_MODEL(OCTEON_CN56XX_PASS1_X))
|
||||
jtgd.s.select = 1 << qlm;
|
||||
cvmx_write_csr(CVMX_CIU_QLM_JTGD, jtgd.u64);
|
||||
do {
|
||||
jtgd.u64 = cvmx_read_csr(CVMX_CIU_QLM_JTGD);
|
||||
} while (jtgd.s.shift);
|
||||
return jtgd.s.shft_reg >> (32 - bits);
|
||||
}
|
||||
|
||||
/**
|
||||
* Shift long sequences of zeros into the QLM JTAG chain. It is
|
||||
* common to need to shift more than 32 bits of zeros into the
|
||||
* chain. This function is a convience wrapper around
|
||||
* cvmx_helper_qlm_jtag_shift() to shift more than 32 bits of
|
||||
* zeros at a time.
|
||||
*
|
||||
* @qlm: QLM to shift zeros into
|
||||
* @bits:
|
||||
*/
|
||||
void cvmx_helper_qlm_jtag_shift_zeros(int qlm, int bits)
|
||||
{
|
||||
while (bits > 0) {
|
||||
int n = bits;
|
||||
if (n > 32)
|
||||
n = 32;
|
||||
cvmx_helper_qlm_jtag_shift(qlm, n, 0);
|
||||
bits -= n;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Program the QLM JTAG chain into all lanes of the QLM. You must
|
||||
* have already shifted in 268*4, or 1072 bits into the JTAG
|
||||
* chain. Updating invalid values can possibly cause chip damage.
|
||||
*
|
||||
* @qlm: QLM to program
|
||||
*/
|
||||
void cvmx_helper_qlm_jtag_update(int qlm)
|
||||
{
|
||||
union cvmx_ciu_qlm_jtgd jtgd;
|
||||
|
||||
/* Update the new data */
|
||||
jtgd.u64 = 0;
|
||||
jtgd.s.update = 1;
|
||||
if (!OCTEON_IS_MODEL(OCTEON_CN56XX_PASS1_X))
|
||||
jtgd.s.select = 1 << qlm;
|
||||
cvmx_write_csr(CVMX_CIU_QLM_JTGD, jtgd.u64);
|
||||
do {
|
||||
jtgd.u64 = cvmx_read_csr(CVMX_CIU_QLM_JTGD);
|
||||
} while (jtgd.s.update);
|
||||
}
|
85
arch/mips/cavium-octeon/executive/cvmx-helper-loop.c
Normal file
85
arch/mips/cavium-octeon/executive/cvmx-helper-loop.c
Normal file
|
@ -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;
|
||||
}
|
113
arch/mips/cavium-octeon/executive/cvmx-helper-npi.c
Normal file
113
arch/mips/cavium-octeon/executive/cvmx-helper-npi.c
Normal file
|
@ -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;
|
||||
}
|
526
arch/mips/cavium-octeon/executive/cvmx-helper-rgmii.c
Normal file
526
arch/mips/cavium-octeon/executive/cvmx-helper-rgmii.c
Normal file
|
@ -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;
|
||||
}
|
558
arch/mips/cavium-octeon/executive/cvmx-helper-sgmii.c
Normal file
558
arch/mips/cavium-octeon/executive/cvmx-helper-sgmii.c
Normal file
|
@ -0,0 +1,558 @@
|
|||
/***********************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);
|
||||
/* Linux kernel driver will call ....link_set with the
|
||||
* proper link state. In the simulator there is no
|
||||
* link state polling and hence it is set from
|
||||
* here.
|
||||
*/
|
||||
if (cvmx_sysinfo_get()->board_type == CVMX_BOARD_TYPE_SIM)
|
||||
__cvmx_helper_sgmii_link_set(ipd_port,
|
||||
__cvmx_helper_sgmii_link_get(ipd_port));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int __cvmx_helper_sgmii_enumerate(int interface)
|
||||
{
|
||||
return 4;
|
||||
}
|
||||
/**
|
||||
* 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 __cvmx_helper_sgmii_enumerate(interface);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
205
arch/mips/cavium-octeon/executive/cvmx-helper-spi.c
Normal file
205
arch/mips/cavium-octeon/executive/cvmx-helper-spi.c
Normal file
|
@ -0,0 +1,205 @@
|
|||
/***********************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
|
||||
|
||||
int __cvmx_helper_spi_enumerate(int interface)
|
||||
{
|
||||
if ((cvmx_sysinfo_get()->board_type != CVMX_BOARD_TYPE_SIM) &&
|
||||
cvmx_spi4000_is_present(interface)) {
|
||||
return 10;
|
||||
} else {
|
||||
return 16;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
437
arch/mips/cavium-octeon/executive/cvmx-helper-util.c
Normal file
437
arch/mips/cavium-octeon/executive/cvmx-helper-util.c
Normal file
|
@ -0,0 +1,437 @@
|
|||
/***********************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 incoming 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 incoming 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;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(cvmx_helper_setup_red);
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(cvmx_helper_get_ipd_port);
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(cvmx_helper_get_interface_num);
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(cvmx_helper_get_interface_index_num);
|
354
arch/mips/cavium-octeon/executive/cvmx-helper-xaui.c
Normal file
354
arch/mips/cavium-octeon/executive/cvmx-helper-xaui.c
Normal file
|
@ -0,0 +1,354 @@
|
|||
/***********************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);
|
||||
|
||||
int __cvmx_helper_xaui_enumerate(int interface)
|
||||
{
|
||||
union cvmx_gmxx_hg2_control gmx_hg2_control;
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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_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);
|
||||
}
|
||||
return __cvmx_helper_xaui_enumerate(interface);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
1290
arch/mips/cavium-octeon/executive/cvmx-helper.c
Normal file
1290
arch/mips/cavium-octeon/executive/cvmx-helper.c
Normal file
File diff suppressed because it is too large
Load diff
371
arch/mips/cavium-octeon/executive/cvmx-interrupt-decodes.c
Normal file
371
arch/mips/cavium-octeon/executive/cvmx-interrupt-decodes.c
Normal file
|
@ -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);
|
||||
}
|
140
arch/mips/cavium-octeon/executive/cvmx-interrupt-rsl.c
Normal file
140
arch/mips/cavium-octeon/executive/cvmx-interrupt-rsl.c
Normal file
|
@ -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.cn38xx.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);
|
||||
}
|
902
arch/mips/cavium-octeon/executive/cvmx-l2c.c
Normal file
902
arch/mips/cavium-octeon/executive/cvmx-l2c.c
Normal file
|
@ -0,0 +1,902 @@
|
|||
/***********************license start***************
|
||||
* Author: Cavium Networks
|
||||
*
|
||||
* Contact: support@caviumnetworks.com
|
||||
* This file is part of the OCTEON SDK
|
||||
*
|
||||
* Copyright (c) 2003-2010 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**************************************/
|
||||
|
||||
/*
|
||||
* Implementation of the Level 2 Cache (L2C) control,
|
||||
* measurement, and debugging facilities.
|
||||
*/
|
||||
|
||||
#include <linux/compiler.h>
|
||||
#include <linux/irqflags.h>
|
||||
#include <asm/octeon/cvmx.h>
|
||||
#include <asm/octeon/cvmx-l2c.h>
|
||||
#include <asm/octeon/cvmx-spinlock.h>
|
||||
|
||||
/*
|
||||
* This spinlock is used internally to ensure that only one core is
|
||||
* performing certain L2 operations at a time.
|
||||
*
|
||||
* NOTE: This only protects calls from within a single application -
|
||||
* if multiple applications or operating systems are running, then it
|
||||
* is up to the user program to coordinate between them.
|
||||
*/
|
||||
cvmx_spinlock_t cvmx_l2c_spinlock;
|
||||
|
||||
int cvmx_l2c_get_core_way_partition(uint32_t core)
|
||||
{
|
||||
uint32_t field;
|
||||
|
||||
/* Validate the core number */
|
||||
if (core >= cvmx_octeon_num_cores())
|
||||
return -1;
|
||||
|
||||
if (OCTEON_IS_MODEL(OCTEON_CN63XX))
|
||||
return cvmx_read_csr(CVMX_L2C_WPAR_PPX(core)) & 0xffff;
|
||||
|
||||
/*
|
||||
* Use the lower two bits of the coreNumber to determine the
|
||||
* bit offset of the UMSK[] field in the L2C_SPAR register.
|
||||
*/
|
||||
field = (core & 0x3) * 8;
|
||||
|
||||
/*
|
||||
* Return the UMSK[] field from the appropriate L2C_SPAR
|
||||
* register based on the coreNumber.
|
||||
*/
|
||||
|
||||
switch (core & 0xC) {
|
||||
case 0x0:
|
||||
return (cvmx_read_csr(CVMX_L2C_SPAR0) & (0xFF << field)) >> field;
|
||||
case 0x4:
|
||||
return (cvmx_read_csr(CVMX_L2C_SPAR1) & (0xFF << field)) >> field;
|
||||
case 0x8:
|
||||
return (cvmx_read_csr(CVMX_L2C_SPAR2) & (0xFF << field)) >> field;
|
||||
case 0xC:
|
||||
return (cvmx_read_csr(CVMX_L2C_SPAR3) & (0xFF << field)) >> field;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cvmx_l2c_set_core_way_partition(uint32_t core, uint32_t mask)
|
||||
{
|
||||
uint32_t field;
|
||||
uint32_t valid_mask;
|
||||
|
||||
valid_mask = (0x1 << cvmx_l2c_get_num_assoc()) - 1;
|
||||
|
||||
mask &= valid_mask;
|
||||
|
||||
/* A UMSK setting which blocks all L2C Ways is an error on some chips */
|
||||
if (mask == valid_mask && !OCTEON_IS_MODEL(OCTEON_CN63XX))
|
||||
return -1;
|
||||
|
||||
/* Validate the core number */
|
||||
if (core >= cvmx_octeon_num_cores())
|
||||
return -1;
|
||||
|
||||
if (OCTEON_IS_MODEL(OCTEON_CN63XX)) {
|
||||
cvmx_write_csr(CVMX_L2C_WPAR_PPX(core), mask);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Use the lower two bits of core to determine the bit offset of the
|
||||
* UMSK[] field in the L2C_SPAR register.
|
||||
*/
|
||||
field = (core & 0x3) * 8;
|
||||
|
||||
/*
|
||||
* Assign the new mask setting to the UMSK[] field in the appropriate
|
||||
* L2C_SPAR register based on the core_num.
|
||||
*
|
||||
*/
|
||||
switch (core & 0xC) {
|
||||
case 0x0:
|
||||
cvmx_write_csr(CVMX_L2C_SPAR0,
|
||||
(cvmx_read_csr(CVMX_L2C_SPAR0) & ~(0xFF << field)) |
|
||||
mask << field);
|
||||
break;
|
||||
case 0x4:
|
||||
cvmx_write_csr(CVMX_L2C_SPAR1,
|
||||
(cvmx_read_csr(CVMX_L2C_SPAR1) & ~(0xFF << field)) |
|
||||
mask << field);
|
||||
break;
|
||||
case 0x8:
|
||||
cvmx_write_csr(CVMX_L2C_SPAR2,
|
||||
(cvmx_read_csr(CVMX_L2C_SPAR2) & ~(0xFF << field)) |
|
||||
mask << field);
|
||||
break;
|
||||
case 0xC:
|
||||
cvmx_write_csr(CVMX_L2C_SPAR3,
|
||||
(cvmx_read_csr(CVMX_L2C_SPAR3) & ~(0xFF << field)) |
|
||||
mask << field);
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cvmx_l2c_set_hw_way_partition(uint32_t mask)
|
||||
{
|
||||
uint32_t valid_mask;
|
||||
|
||||
valid_mask = (0x1 << cvmx_l2c_get_num_assoc()) - 1;
|
||||
mask &= valid_mask;
|
||||
|
||||
/* A UMSK setting which blocks all L2C Ways is an error on some chips */
|
||||
if (mask == valid_mask && !OCTEON_IS_MODEL(OCTEON_CN63XX))
|
||||
return -1;
|
||||
|
||||
if (OCTEON_IS_MODEL(OCTEON_CN63XX))
|
||||
cvmx_write_csr(CVMX_L2C_WPAR_IOBX(0), mask);
|
||||
else
|
||||
cvmx_write_csr(CVMX_L2C_SPAR4,
|
||||
(cvmx_read_csr(CVMX_L2C_SPAR4) & ~0xFF) | mask);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cvmx_l2c_get_hw_way_partition(void)
|
||||
{
|
||||
if (OCTEON_IS_MODEL(OCTEON_CN63XX))
|
||||
return cvmx_read_csr(CVMX_L2C_WPAR_IOBX(0)) & 0xffff;
|
||||
else
|
||||
return cvmx_read_csr(CVMX_L2C_SPAR4) & (0xFF);
|
||||
}
|
||||
|
||||
void cvmx_l2c_config_perf(uint32_t counter, enum cvmx_l2c_event event,
|
||||
uint32_t clear_on_read)
|
||||
{
|
||||
if (OCTEON_IS_MODEL(OCTEON_CN5XXX) || OCTEON_IS_MODEL(OCTEON_CN3XXX)) {
|
||||
union cvmx_l2c_pfctl pfctl;
|
||||
|
||||
pfctl.u64 = cvmx_read_csr(CVMX_L2C_PFCTL);
|
||||
|
||||
switch (counter) {
|
||||
case 0:
|
||||
pfctl.s.cnt0sel = event;
|
||||
pfctl.s.cnt0ena = 1;
|
||||
pfctl.s.cnt0rdclr = clear_on_read;
|
||||
break;
|
||||
case 1:
|
||||
pfctl.s.cnt1sel = event;
|
||||
pfctl.s.cnt1ena = 1;
|
||||
pfctl.s.cnt1rdclr = clear_on_read;
|
||||
break;
|
||||
case 2:
|
||||
pfctl.s.cnt2sel = event;
|
||||
pfctl.s.cnt2ena = 1;
|
||||
pfctl.s.cnt2rdclr = clear_on_read;
|
||||
break;
|
||||
case 3:
|
||||
default:
|
||||
pfctl.s.cnt3sel = event;
|
||||
pfctl.s.cnt3ena = 1;
|
||||
pfctl.s.cnt3rdclr = clear_on_read;
|
||||
break;
|
||||
}
|
||||
|
||||
cvmx_write_csr(CVMX_L2C_PFCTL, pfctl.u64);
|
||||
} else {
|
||||
union cvmx_l2c_tadx_prf l2c_tadx_prf;
|
||||
int tad;
|
||||
|
||||
cvmx_dprintf("L2C performance counter events are different for this chip, mapping 'event' to cvmx_l2c_tad_event_t\n");
|
||||
if (clear_on_read)
|
||||
cvmx_dprintf("L2C counters don't support clear on read for this chip\n");
|
||||
|
||||
l2c_tadx_prf.u64 = cvmx_read_csr(CVMX_L2C_TADX_PRF(0));
|
||||
|
||||
switch (counter) {
|
||||
case 0:
|
||||
l2c_tadx_prf.s.cnt0sel = event;
|
||||
break;
|
||||
case 1:
|
||||
l2c_tadx_prf.s.cnt1sel = event;
|
||||
break;
|
||||
case 2:
|
||||
l2c_tadx_prf.s.cnt2sel = event;
|
||||
break;
|
||||
default:
|
||||
case 3:
|
||||
l2c_tadx_prf.s.cnt3sel = event;
|
||||
break;
|
||||
}
|
||||
for (tad = 0; tad < CVMX_L2C_TADS; tad++)
|
||||
cvmx_write_csr(CVMX_L2C_TADX_PRF(tad),
|
||||
l2c_tadx_prf.u64);
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t cvmx_l2c_read_perf(uint32_t counter)
|
||||
{
|
||||
switch (counter) {
|
||||
case 0:
|
||||
if (OCTEON_IS_MODEL(OCTEON_CN5XXX) || OCTEON_IS_MODEL(OCTEON_CN3XXX))
|
||||
return cvmx_read_csr(CVMX_L2C_PFC0);
|
||||
else {
|
||||
uint64_t counter = 0;
|
||||
int tad;
|
||||
for (tad = 0; tad < CVMX_L2C_TADS; tad++)
|
||||
counter += cvmx_read_csr(CVMX_L2C_TADX_PFC0(tad));
|
||||
return counter;
|
||||
}
|
||||
case 1:
|
||||
if (OCTEON_IS_MODEL(OCTEON_CN5XXX) || OCTEON_IS_MODEL(OCTEON_CN3XXX))
|
||||
return cvmx_read_csr(CVMX_L2C_PFC1);
|
||||
else {
|
||||
uint64_t counter = 0;
|
||||
int tad;
|
||||
for (tad = 0; tad < CVMX_L2C_TADS; tad++)
|
||||
counter += cvmx_read_csr(CVMX_L2C_TADX_PFC1(tad));
|
||||
return counter;
|
||||
}
|
||||
case 2:
|
||||
if (OCTEON_IS_MODEL(OCTEON_CN5XXX) || OCTEON_IS_MODEL(OCTEON_CN3XXX))
|
||||
return cvmx_read_csr(CVMX_L2C_PFC2);
|
||||
else {
|
||||
uint64_t counter = 0;
|
||||
int tad;
|
||||
for (tad = 0; tad < CVMX_L2C_TADS; tad++)
|
||||
counter += cvmx_read_csr(CVMX_L2C_TADX_PFC2(tad));
|
||||
return counter;
|
||||
}
|
||||
case 3:
|
||||
default:
|
||||
if (OCTEON_IS_MODEL(OCTEON_CN5XXX) || OCTEON_IS_MODEL(OCTEON_CN3XXX))
|
||||
return cvmx_read_csr(CVMX_L2C_PFC3);
|
||||
else {
|
||||
uint64_t counter = 0;
|
||||
int tad;
|
||||
for (tad = 0; tad < CVMX_L2C_TADS; tad++)
|
||||
counter += cvmx_read_csr(CVMX_L2C_TADX_PFC3(tad));
|
||||
return counter;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @INTERNAL
|
||||
* Helper function use to fault in cache lines for L2 cache locking
|
||||
*
|
||||
* @addr: Address of base of memory region to read into L2 cache
|
||||
* @len: Length (in bytes) of region to fault in
|
||||
*/
|
||||
static void fault_in(uint64_t addr, int len)
|
||||
{
|
||||
char *ptr;
|
||||
|
||||
/*
|
||||
* Adjust addr and length so we get all cache lines even for
|
||||
* small ranges spanning two cache lines.
|
||||
*/
|
||||
len += addr & CVMX_CACHE_LINE_MASK;
|
||||
addr &= ~CVMX_CACHE_LINE_MASK;
|
||||
ptr = cvmx_phys_to_ptr(addr);
|
||||
/*
|
||||
* Invalidate L1 cache to make sure all loads result in data
|
||||
* being in L2.
|
||||
*/
|
||||
CVMX_DCACHE_INVALIDATE;
|
||||
while (len > 0) {
|
||||
ACCESS_ONCE(*ptr);
|
||||
len -= CVMX_CACHE_LINE_SIZE;
|
||||
ptr += CVMX_CACHE_LINE_SIZE;
|
||||
}
|
||||
}
|
||||
|
||||
int cvmx_l2c_lock_line(uint64_t addr)
|
||||
{
|
||||
if (OCTEON_IS_MODEL(OCTEON_CN63XX)) {
|
||||
int shift = CVMX_L2C_TAG_ADDR_ALIAS_SHIFT;
|
||||
uint64_t assoc = cvmx_l2c_get_num_assoc();
|
||||
uint64_t tag = addr >> shift;
|
||||
uint64_t index = CVMX_ADD_SEG(CVMX_MIPS_SPACE_XKPHYS, cvmx_l2c_address_to_index(addr) << CVMX_L2C_IDX_ADDR_SHIFT);
|
||||
uint64_t way;
|
||||
union cvmx_l2c_tadx_tag l2c_tadx_tag;
|
||||
|
||||
CVMX_CACHE_LCKL2(CVMX_ADD_SEG(CVMX_MIPS_SPACE_XKPHYS, addr), 0);
|
||||
|
||||
/* Make sure we were able to lock the line */
|
||||
for (way = 0; way < assoc; way++) {
|
||||
CVMX_CACHE_LTGL2I(index | (way << shift), 0);
|
||||
/* make sure CVMX_L2C_TADX_TAG is updated */
|
||||
CVMX_SYNC;
|
||||
l2c_tadx_tag.u64 = cvmx_read_csr(CVMX_L2C_TADX_TAG(0));
|
||||
if (l2c_tadx_tag.s.valid && l2c_tadx_tag.s.tag == tag)
|
||||
break;
|
||||
}
|
||||
|
||||
/* Check if a valid line is found */
|
||||
if (way >= assoc) {
|
||||
/* cvmx_dprintf("ERROR: cvmx_l2c_lock_line: line not found for locking at 0x%llx address\n", (unsigned long long)addr); */
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Check if lock bit is not set */
|
||||
if (!l2c_tadx_tag.s.lock) {
|
||||
/* cvmx_dprintf("ERROR: cvmx_l2c_lock_line: Not able to lock at 0x%llx address\n", (unsigned long long)addr); */
|
||||
return -1;
|
||||
}
|
||||
return way;
|
||||
} else {
|
||||
int retval = 0;
|
||||
union cvmx_l2c_dbg l2cdbg;
|
||||
union cvmx_l2c_lckbase lckbase;
|
||||
union cvmx_l2c_lckoff lckoff;
|
||||
union cvmx_l2t_err l2t_err;
|
||||
|
||||
cvmx_spinlock_lock(&cvmx_l2c_spinlock);
|
||||
|
||||
l2cdbg.u64 = 0;
|
||||
lckbase.u64 = 0;
|
||||
lckoff.u64 = 0;
|
||||
|
||||
/* Clear l2t error bits if set */
|
||||
l2t_err.u64 = cvmx_read_csr(CVMX_L2T_ERR);
|
||||
l2t_err.s.lckerr = 1;
|
||||
l2t_err.s.lckerr2 = 1;
|
||||
cvmx_write_csr(CVMX_L2T_ERR, l2t_err.u64);
|
||||
|
||||
addr &= ~CVMX_CACHE_LINE_MASK;
|
||||
|
||||
/* Set this core as debug core */
|
||||
l2cdbg.s.ppnum = cvmx_get_core_num();
|
||||
CVMX_SYNC;
|
||||
cvmx_write_csr(CVMX_L2C_DBG, l2cdbg.u64);
|
||||
cvmx_read_csr(CVMX_L2C_DBG);
|
||||
|
||||
lckoff.s.lck_offset = 0; /* Only lock 1 line at a time */
|
||||
cvmx_write_csr(CVMX_L2C_LCKOFF, lckoff.u64);
|
||||
cvmx_read_csr(CVMX_L2C_LCKOFF);
|
||||
|
||||
if (((union cvmx_l2c_cfg)(cvmx_read_csr(CVMX_L2C_CFG))).s.idxalias) {
|
||||
int alias_shift = CVMX_L2C_IDX_ADDR_SHIFT + 2 * CVMX_L2_SET_BITS - 1;
|
||||
uint64_t addr_tmp = addr ^ (addr & ((1 << alias_shift) - 1)) >> CVMX_L2_SET_BITS;
|
||||
lckbase.s.lck_base = addr_tmp >> 7;
|
||||
} else {
|
||||
lckbase.s.lck_base = addr >> 7;
|
||||
}
|
||||
|
||||
lckbase.s.lck_ena = 1;
|
||||
cvmx_write_csr(CVMX_L2C_LCKBASE, lckbase.u64);
|
||||
/* Make sure it gets there */
|
||||
cvmx_read_csr(CVMX_L2C_LCKBASE);
|
||||
|
||||
fault_in(addr, CVMX_CACHE_LINE_SIZE);
|
||||
|
||||
lckbase.s.lck_ena = 0;
|
||||
cvmx_write_csr(CVMX_L2C_LCKBASE, lckbase.u64);
|
||||
/* Make sure it gets there */
|
||||
cvmx_read_csr(CVMX_L2C_LCKBASE);
|
||||
|
||||
/* Stop being debug core */
|
||||
cvmx_write_csr(CVMX_L2C_DBG, 0);
|
||||
cvmx_read_csr(CVMX_L2C_DBG);
|
||||
|
||||
l2t_err.u64 = cvmx_read_csr(CVMX_L2T_ERR);
|
||||
if (l2t_err.s.lckerr || l2t_err.s.lckerr2)
|
||||
retval = 1; /* We were unable to lock the line */
|
||||
|
||||
cvmx_spinlock_unlock(&cvmx_l2c_spinlock);
|
||||
return retval;
|
||||
}
|
||||
}
|
||||
|
||||
int cvmx_l2c_lock_mem_region(uint64_t start, uint64_t len)
|
||||
{
|
||||
int retval = 0;
|
||||
|
||||
/* Round start/end to cache line boundaries */
|
||||
len += start & CVMX_CACHE_LINE_MASK;
|
||||
start &= ~CVMX_CACHE_LINE_MASK;
|
||||
len = (len + CVMX_CACHE_LINE_MASK) & ~CVMX_CACHE_LINE_MASK;
|
||||
|
||||
while (len) {
|
||||
retval += cvmx_l2c_lock_line(start);
|
||||
start += CVMX_CACHE_LINE_SIZE;
|
||||
len -= CVMX_CACHE_LINE_SIZE;
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
void cvmx_l2c_flush(void)
|
||||
{
|
||||
uint64_t assoc, set;
|
||||
uint64_t n_assoc, n_set;
|
||||
|
||||
n_set = cvmx_l2c_get_num_sets();
|
||||
n_assoc = cvmx_l2c_get_num_assoc();
|
||||
|
||||
if (OCTEON_IS_MODEL(OCTEON_CN6XXX)) {
|
||||
uint64_t address;
|
||||
/* These may look like constants, but they aren't... */
|
||||
int assoc_shift = CVMX_L2C_TAG_ADDR_ALIAS_SHIFT;
|
||||
int set_shift = CVMX_L2C_IDX_ADDR_SHIFT;
|
||||
for (set = 0; set < n_set; set++) {
|
||||
for (assoc = 0; assoc < n_assoc; assoc++) {
|
||||
address = CVMX_ADD_SEG(CVMX_MIPS_SPACE_XKPHYS,
|
||||
(assoc << assoc_shift) | (set << set_shift));
|
||||
CVMX_CACHE_WBIL2I(address, 0);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (set = 0; set < n_set; set++)
|
||||
for (assoc = 0; assoc < n_assoc; assoc++)
|
||||
cvmx_l2c_flush_line(assoc, set);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int cvmx_l2c_unlock_line(uint64_t address)
|
||||
{
|
||||
|
||||
if (OCTEON_IS_MODEL(OCTEON_CN63XX)) {
|
||||
int assoc;
|
||||
union cvmx_l2c_tag tag;
|
||||
uint32_t tag_addr;
|
||||
uint32_t index = cvmx_l2c_address_to_index(address);
|
||||
|
||||
tag_addr = ((address >> CVMX_L2C_TAG_ADDR_ALIAS_SHIFT) & ((1 << CVMX_L2C_TAG_ADDR_ALIAS_SHIFT) - 1));
|
||||
|
||||
/*
|
||||
* For 63XX, we can flush a line by using the physical
|
||||
* address directly, so finding the cache line used by
|
||||
* the address is only required to provide the proper
|
||||
* return value for the function.
|
||||
*/
|
||||
for (assoc = 0; assoc < CVMX_L2_ASSOC; assoc++) {
|
||||
tag = cvmx_l2c_get_tag(assoc, index);
|
||||
|
||||
if (tag.s.V && (tag.s.addr == tag_addr)) {
|
||||
CVMX_CACHE_WBIL2(CVMX_ADD_SEG(CVMX_MIPS_SPACE_XKPHYS, address), 0);
|
||||
return tag.s.L;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
int assoc;
|
||||
union cvmx_l2c_tag tag;
|
||||
uint32_t tag_addr;
|
||||
|
||||
uint32_t index = cvmx_l2c_address_to_index(address);
|
||||
|
||||
/* Compute portion of address that is stored in tag */
|
||||
tag_addr = ((address >> CVMX_L2C_TAG_ADDR_ALIAS_SHIFT) & ((1 << CVMX_L2C_TAG_ADDR_ALIAS_SHIFT) - 1));
|
||||
for (assoc = 0; assoc < CVMX_L2_ASSOC; assoc++) {
|
||||
tag = cvmx_l2c_get_tag(assoc, index);
|
||||
|
||||
if (tag.s.V && (tag.s.addr == tag_addr)) {
|
||||
cvmx_l2c_flush_line(assoc, index);
|
||||
return tag.s.L;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cvmx_l2c_unlock_mem_region(uint64_t start, uint64_t len)
|
||||
{
|
||||
int num_unlocked = 0;
|
||||
/* Round start/end to cache line boundaries */
|
||||
len += start & CVMX_CACHE_LINE_MASK;
|
||||
start &= ~CVMX_CACHE_LINE_MASK;
|
||||
len = (len + CVMX_CACHE_LINE_MASK) & ~CVMX_CACHE_LINE_MASK;
|
||||
while (len > 0) {
|
||||
num_unlocked += cvmx_l2c_unlock_line(start);
|
||||
start += CVMX_CACHE_LINE_SIZE;
|
||||
len -= CVMX_CACHE_LINE_SIZE;
|
||||
}
|
||||
|
||||
return num_unlocked;
|
||||
}
|
||||
|
||||
/*
|
||||
* Internal l2c tag types. These are converted to a generic structure
|
||||
* that can be used on all chips.
|
||||
*/
|
||||
union __cvmx_l2c_tag {
|
||||
uint64_t u64;
|
||||
struct cvmx_l2c_tag_cn50xx {
|
||||
uint64_t reserved:40;
|
||||
uint64_t V:1; /* Line valid */
|
||||
uint64_t D:1; /* Line dirty */
|
||||
uint64_t L:1; /* Line locked */
|
||||
uint64_t U:1; /* Use, LRU eviction */
|
||||
uint64_t addr:20; /* Phys mem addr (33..14) */
|
||||
} cn50xx;
|
||||
struct cvmx_l2c_tag_cn30xx {
|
||||
uint64_t reserved:41;
|
||||
uint64_t V:1; /* Line valid */
|
||||
uint64_t D:1; /* Line dirty */
|
||||
uint64_t L:1; /* Line locked */
|
||||
uint64_t U:1; /* Use, LRU eviction */
|
||||
uint64_t addr:19; /* Phys mem addr (33..15) */
|
||||
} cn30xx;
|
||||
struct cvmx_l2c_tag_cn31xx {
|
||||
uint64_t reserved:42;
|
||||
uint64_t V:1; /* Line valid */
|
||||
uint64_t D:1; /* Line dirty */
|
||||
uint64_t L:1; /* Line locked */
|
||||
uint64_t U:1; /* Use, LRU eviction */
|
||||
uint64_t addr:18; /* Phys mem addr (33..16) */
|
||||
} cn31xx;
|
||||
struct cvmx_l2c_tag_cn38xx {
|
||||
uint64_t reserved:43;
|
||||
uint64_t V:1; /* Line valid */
|
||||
uint64_t D:1; /* Line dirty */
|
||||
uint64_t L:1; /* Line locked */
|
||||
uint64_t U:1; /* Use, LRU eviction */
|
||||
uint64_t addr:17; /* Phys mem addr (33..17) */
|
||||
} cn38xx;
|
||||
struct cvmx_l2c_tag_cn58xx {
|
||||
uint64_t reserved:44;
|
||||
uint64_t V:1; /* Line valid */
|
||||
uint64_t D:1; /* Line dirty */
|
||||
uint64_t L:1; /* Line locked */
|
||||
uint64_t U:1; /* Use, LRU eviction */
|
||||
uint64_t addr:16; /* Phys mem addr (33..18) */
|
||||
} cn58xx;
|
||||
struct cvmx_l2c_tag_cn58xx cn56xx; /* 2048 sets */
|
||||
struct cvmx_l2c_tag_cn31xx cn52xx; /* 512 sets */
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @INTERNAL
|
||||
* Function to read a L2C tag. This code make the current core
|
||||
* the 'debug core' for the L2. This code must only be executed by
|
||||
* 1 core at a time.
|
||||
*
|
||||
* @assoc: Association (way) of the tag to dump
|
||||
* @index: Index of the cacheline
|
||||
*
|
||||
* Returns The Octeon model specific tag structure. This is
|
||||
* translated by a wrapper function to a generic form that is
|
||||
* easier for applications to use.
|
||||
*/
|
||||
static union __cvmx_l2c_tag __read_l2_tag(uint64_t assoc, uint64_t index)
|
||||
{
|
||||
|
||||
uint64_t debug_tag_addr = CVMX_ADD_SEG(CVMX_MIPS_SPACE_XKPHYS, (index << 7) + 96);
|
||||
uint64_t core = cvmx_get_core_num();
|
||||
union __cvmx_l2c_tag tag_val;
|
||||
uint64_t dbg_addr = CVMX_L2C_DBG;
|
||||
unsigned long flags;
|
||||
|
||||
union cvmx_l2c_dbg debug_val;
|
||||
debug_val.u64 = 0;
|
||||
/*
|
||||
* For low core count parts, the core number is always small
|
||||
* enough to stay in the correct field and not set any
|
||||
* reserved bits.
|
||||
*/
|
||||
debug_val.s.ppnum = core;
|
||||
debug_val.s.l2t = 1;
|
||||
debug_val.s.set = assoc;
|
||||
|
||||
local_irq_save(flags);
|
||||
/*
|
||||
* Make sure core is quiet (no prefetches, etc.) before
|
||||
* entering debug mode.
|
||||
*/
|
||||
CVMX_SYNC;
|
||||
/* Flush L1 to make sure debug load misses L1 */
|
||||
CVMX_DCACHE_INVALIDATE;
|
||||
|
||||
/*
|
||||
* The following must be done in assembly as when in debug
|
||||
* mode all data loads from L2 return special debug data, not
|
||||
* normal memory contents. Also, interrupts must be disabled,
|
||||
* since if an interrupt occurs while in debug mode the ISR
|
||||
* will get debug data from all its memory * reads instead of
|
||||
* the contents of memory.
|
||||
*/
|
||||
|
||||
asm volatile (
|
||||
".set push\n\t"
|
||||
".set mips64\n\t"
|
||||
".set noreorder\n\t"
|
||||
"sd %[dbg_val], 0(%[dbg_addr])\n\t" /* Enter debug mode, wait for store */
|
||||
"ld $0, 0(%[dbg_addr])\n\t"
|
||||
"ld %[tag_val], 0(%[tag_addr])\n\t" /* Read L2C tag data */
|
||||
"sd $0, 0(%[dbg_addr])\n\t" /* Exit debug mode, wait for store */
|
||||
"ld $0, 0(%[dbg_addr])\n\t"
|
||||
"cache 9, 0($0)\n\t" /* Invalidate dcache to discard debug data */
|
||||
".set pop"
|
||||
: [tag_val] "=r" (tag_val)
|
||||
: [dbg_addr] "r" (dbg_addr), [dbg_val] "r" (debug_val), [tag_addr] "r" (debug_tag_addr)
|
||||
: "memory");
|
||||
|
||||
local_irq_restore(flags);
|
||||
|
||||
return tag_val;
|
||||
}
|
||||
|
||||
|
||||
union cvmx_l2c_tag cvmx_l2c_get_tag(uint32_t association, uint32_t index)
|
||||
{
|
||||
union cvmx_l2c_tag tag;
|
||||
tag.u64 = 0;
|
||||
|
||||
if ((int)association >= cvmx_l2c_get_num_assoc()) {
|
||||
cvmx_dprintf("ERROR: cvmx_l2c_get_tag association out of range\n");
|
||||
return tag;
|
||||
}
|
||||
if ((int)index >= cvmx_l2c_get_num_sets()) {
|
||||
cvmx_dprintf("ERROR: cvmx_l2c_get_tag index out of range (arg: %d, max: %d)\n",
|
||||
(int)index, cvmx_l2c_get_num_sets());
|
||||
return tag;
|
||||
}
|
||||
if (OCTEON_IS_MODEL(OCTEON_CN63XX)) {
|
||||
union cvmx_l2c_tadx_tag l2c_tadx_tag;
|
||||
uint64_t address = CVMX_ADD_SEG(CVMX_MIPS_SPACE_XKPHYS,
|
||||
(association << CVMX_L2C_TAG_ADDR_ALIAS_SHIFT) |
|
||||
(index << CVMX_L2C_IDX_ADDR_SHIFT));
|
||||
/*
|
||||
* Use L2 cache Index load tag cache instruction, as
|
||||
* hardware loads the virtual tag for the L2 cache
|
||||
* block with the contents of L2C_TAD0_TAG
|
||||
* register.
|
||||
*/
|
||||
CVMX_CACHE_LTGL2I(address, 0);
|
||||
CVMX_SYNC; /* make sure CVMX_L2C_TADX_TAG is updated */
|
||||
l2c_tadx_tag.u64 = cvmx_read_csr(CVMX_L2C_TADX_TAG(0));
|
||||
|
||||
tag.s.V = l2c_tadx_tag.s.valid;
|
||||
tag.s.D = l2c_tadx_tag.s.dirty;
|
||||
tag.s.L = l2c_tadx_tag.s.lock;
|
||||
tag.s.U = l2c_tadx_tag.s.use;
|
||||
tag.s.addr = l2c_tadx_tag.s.tag;
|
||||
} else {
|
||||
union __cvmx_l2c_tag tmp_tag;
|
||||
/* __read_l2_tag is intended for internal use only */
|
||||
tmp_tag = __read_l2_tag(association, index);
|
||||
|
||||
/*
|
||||
* Convert all tag structure types to generic version,
|
||||
* as it can represent all models.
|
||||
*/
|
||||
if (OCTEON_IS_MODEL(OCTEON_CN58XX) || OCTEON_IS_MODEL(OCTEON_CN56XX)) {
|
||||
tag.s.V = tmp_tag.cn58xx.V;
|
||||
tag.s.D = tmp_tag.cn58xx.D;
|
||||
tag.s.L = tmp_tag.cn58xx.L;
|
||||
tag.s.U = tmp_tag.cn58xx.U;
|
||||
tag.s.addr = tmp_tag.cn58xx.addr;
|
||||
} else if (OCTEON_IS_MODEL(OCTEON_CN38XX)) {
|
||||
tag.s.V = tmp_tag.cn38xx.V;
|
||||
tag.s.D = tmp_tag.cn38xx.D;
|
||||
tag.s.L = tmp_tag.cn38xx.L;
|
||||
tag.s.U = tmp_tag.cn38xx.U;
|
||||
tag.s.addr = tmp_tag.cn38xx.addr;
|
||||
} else if (OCTEON_IS_MODEL(OCTEON_CN31XX) || OCTEON_IS_MODEL(OCTEON_CN52XX)) {
|
||||
tag.s.V = tmp_tag.cn31xx.V;
|
||||
tag.s.D = tmp_tag.cn31xx.D;
|
||||
tag.s.L = tmp_tag.cn31xx.L;
|
||||
tag.s.U = tmp_tag.cn31xx.U;
|
||||
tag.s.addr = tmp_tag.cn31xx.addr;
|
||||
} else if (OCTEON_IS_MODEL(OCTEON_CN30XX)) {
|
||||
tag.s.V = tmp_tag.cn30xx.V;
|
||||
tag.s.D = tmp_tag.cn30xx.D;
|
||||
tag.s.L = tmp_tag.cn30xx.L;
|
||||
tag.s.U = tmp_tag.cn30xx.U;
|
||||
tag.s.addr = tmp_tag.cn30xx.addr;
|
||||
} else if (OCTEON_IS_MODEL(OCTEON_CN50XX)) {
|
||||
tag.s.V = tmp_tag.cn50xx.V;
|
||||
tag.s.D = tmp_tag.cn50xx.D;
|
||||
tag.s.L = tmp_tag.cn50xx.L;
|
||||
tag.s.U = tmp_tag.cn50xx.U;
|
||||
tag.s.addr = tmp_tag.cn50xx.addr;
|
||||
} else {
|
||||
cvmx_dprintf("Unsupported OCTEON Model in %s\n", __func__);
|
||||
}
|
||||
}
|
||||
return tag;
|
||||
}
|
||||
|
||||
uint32_t cvmx_l2c_address_to_index(uint64_t addr)
|
||||
{
|
||||
uint64_t idx = addr >> CVMX_L2C_IDX_ADDR_SHIFT;
|
||||
int indxalias = 0;
|
||||
|
||||
if (OCTEON_IS_MODEL(OCTEON_CN6XXX)) {
|
||||
union cvmx_l2c_ctl l2c_ctl;
|
||||
l2c_ctl.u64 = cvmx_read_csr(CVMX_L2C_CTL);
|
||||
indxalias = !l2c_ctl.s.disidxalias;
|
||||
} else {
|
||||
union cvmx_l2c_cfg l2c_cfg;
|
||||
l2c_cfg.u64 = cvmx_read_csr(CVMX_L2C_CFG);
|
||||
indxalias = l2c_cfg.s.idxalias;
|
||||
}
|
||||
|
||||
if (indxalias) {
|
||||
if (OCTEON_IS_MODEL(OCTEON_CN63XX)) {
|
||||
uint32_t a_14_12 = (idx / (CVMX_L2C_MEMBANK_SELECT_SIZE/(1<<CVMX_L2C_IDX_ADDR_SHIFT))) & 0x7;
|
||||
idx ^= idx / cvmx_l2c_get_num_sets();
|
||||
idx ^= a_14_12;
|
||||
} else {
|
||||
idx ^= ((addr & CVMX_L2C_ALIAS_MASK) >> CVMX_L2C_TAG_ADDR_ALIAS_SHIFT);
|
||||
}
|
||||
}
|
||||
idx &= CVMX_L2C_IDX_MASK;
|
||||
return idx;
|
||||
}
|
||||
|
||||
int cvmx_l2c_get_cache_size_bytes(void)
|
||||
{
|
||||
return cvmx_l2c_get_num_sets() * cvmx_l2c_get_num_assoc() *
|
||||
CVMX_CACHE_LINE_SIZE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return log base 2 of the number of sets in the L2 cache
|
||||
* Returns
|
||||
*/
|
||||
int cvmx_l2c_get_set_bits(void)
|
||||
{
|
||||
int l2_set_bits;
|
||||
if (OCTEON_IS_MODEL(OCTEON_CN56XX) || OCTEON_IS_MODEL(OCTEON_CN58XX))
|
||||
l2_set_bits = 11; /* 2048 sets */
|
||||
else if (OCTEON_IS_MODEL(OCTEON_CN38XX) || OCTEON_IS_MODEL(OCTEON_CN63XX))
|
||||
l2_set_bits = 10; /* 1024 sets */
|
||||
else if (OCTEON_IS_MODEL(OCTEON_CN31XX) || OCTEON_IS_MODEL(OCTEON_CN52XX))
|
||||
l2_set_bits = 9; /* 512 sets */
|
||||
else if (OCTEON_IS_MODEL(OCTEON_CN30XX))
|
||||
l2_set_bits = 8; /* 256 sets */
|
||||
else if (OCTEON_IS_MODEL(OCTEON_CN50XX))
|
||||
l2_set_bits = 7; /* 128 sets */
|
||||
else {
|
||||
cvmx_dprintf("Unsupported OCTEON Model in %s\n", __func__);
|
||||
l2_set_bits = 11; /* 2048 sets */
|
||||
}
|
||||
return l2_set_bits;
|
||||
}
|
||||
|
||||
/* Return the number of sets in the L2 Cache */
|
||||
int cvmx_l2c_get_num_sets(void)
|
||||
{
|
||||
return 1 << cvmx_l2c_get_set_bits();
|
||||
}
|
||||
|
||||
/* Return the number of associations in the L2 Cache */
|
||||
int cvmx_l2c_get_num_assoc(void)
|
||||
{
|
||||
int l2_assoc;
|
||||
if (OCTEON_IS_MODEL(OCTEON_CN56XX) ||
|
||||
OCTEON_IS_MODEL(OCTEON_CN52XX) ||
|
||||
OCTEON_IS_MODEL(OCTEON_CN58XX) ||
|
||||
OCTEON_IS_MODEL(OCTEON_CN50XX) ||
|
||||
OCTEON_IS_MODEL(OCTEON_CN38XX))
|
||||
l2_assoc = 8;
|
||||
else if (OCTEON_IS_MODEL(OCTEON_CN63XX))
|
||||
l2_assoc = 16;
|
||||
else if (OCTEON_IS_MODEL(OCTEON_CN31XX) ||
|
||||
OCTEON_IS_MODEL(OCTEON_CN30XX))
|
||||
l2_assoc = 4;
|
||||
else {
|
||||
cvmx_dprintf("Unsupported OCTEON Model in %s\n", __func__);
|
||||
l2_assoc = 8;
|
||||
}
|
||||
|
||||
/* Check to see if part of the cache is disabled */
|
||||
if (OCTEON_IS_MODEL(OCTEON_CN63XX)) {
|
||||
union cvmx_mio_fus_dat3 mio_fus_dat3;
|
||||
|
||||
mio_fus_dat3.u64 = cvmx_read_csr(CVMX_MIO_FUS_DAT3);
|
||||
/*
|
||||
* cvmx_mio_fus_dat3.s.l2c_crip fuses map as follows
|
||||
* <2> will be not used for 63xx
|
||||
* <1> disables 1/2 ways
|
||||
* <0> disables 1/4 ways
|
||||
* They are cumulative, so for 63xx:
|
||||
* <1> <0>
|
||||
* 0 0 16-way 2MB cache
|
||||
* 0 1 12-way 1.5MB cache
|
||||
* 1 0 8-way 1MB cache
|
||||
* 1 1 4-way 512KB cache
|
||||
*/
|
||||
|
||||
if (mio_fus_dat3.s.l2c_crip == 3)
|
||||
l2_assoc = 4;
|
||||
else if (mio_fus_dat3.s.l2c_crip == 2)
|
||||
l2_assoc = 8;
|
||||
else if (mio_fus_dat3.s.l2c_crip == 1)
|
||||
l2_assoc = 12;
|
||||
} else {
|
||||
union cvmx_l2d_fus3 val;
|
||||
val.u64 = cvmx_read_csr(CVMX_L2D_FUS3);
|
||||
/*
|
||||
* Using shifts here, as bit position names are
|
||||
* different for each model but they all mean the
|
||||
* same.
|
||||
*/
|
||||
if ((val.u64 >> 35) & 0x1)
|
||||
l2_assoc = l2_assoc >> 2;
|
||||
else if ((val.u64 >> 34) & 0x1)
|
||||
l2_assoc = l2_assoc >> 1;
|
||||
}
|
||||
return l2_assoc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Flush a line from the L2 cache
|
||||
* This should only be called from one core at a time, as this routine
|
||||
* sets the core to the 'debug' core in order to flush the line.
|
||||
*
|
||||
* @assoc: Association (or way) to flush
|
||||
* @index: Index to flush
|
||||
*/
|
||||
void cvmx_l2c_flush_line(uint32_t assoc, uint32_t index)
|
||||
{
|
||||
/* Check the range of the index. */
|
||||
if (index > (uint32_t)cvmx_l2c_get_num_sets()) {
|
||||
cvmx_dprintf("ERROR: cvmx_l2c_flush_line index out of range.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
/* Check the range of association. */
|
||||
if (assoc > (uint32_t)cvmx_l2c_get_num_assoc()) {
|
||||
cvmx_dprintf("ERROR: cvmx_l2c_flush_line association out of range.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (OCTEON_IS_MODEL(OCTEON_CN63XX)) {
|
||||
uint64_t address;
|
||||
/* Create the address based on index and association.
|
||||
* Bits<20:17> select the way of the cache block involved in
|
||||
* the operation
|
||||
* Bits<16:7> of the effect address select the index
|
||||
*/
|
||||
address = CVMX_ADD_SEG(CVMX_MIPS_SPACE_XKPHYS,
|
||||
(assoc << CVMX_L2C_TAG_ADDR_ALIAS_SHIFT) |
|
||||
(index << CVMX_L2C_IDX_ADDR_SHIFT));
|
||||
CVMX_CACHE_WBIL2I(address, 0);
|
||||
} else {
|
||||
union cvmx_l2c_dbg l2cdbg;
|
||||
|
||||
l2cdbg.u64 = 0;
|
||||
if (!OCTEON_IS_MODEL(OCTEON_CN30XX))
|
||||
l2cdbg.s.ppnum = cvmx_get_core_num();
|
||||
l2cdbg.s.finv = 1;
|
||||
|
||||
l2cdbg.s.set = assoc;
|
||||
cvmx_spinlock_lock(&cvmx_l2c_spinlock);
|
||||
/*
|
||||
* Enter debug mode, and make sure all other writes
|
||||
* complete before we enter debug mode
|
||||
*/
|
||||
CVMX_SYNC;
|
||||
cvmx_write_csr(CVMX_L2C_DBG, l2cdbg.u64);
|
||||
cvmx_read_csr(CVMX_L2C_DBG);
|
||||
|
||||
CVMX_PREPARE_FOR_STORE(CVMX_ADD_SEG(CVMX_MIPS_SPACE_XKPHYS,
|
||||
index * CVMX_CACHE_LINE_SIZE),
|
||||
0);
|
||||
/* Exit debug mode */
|
||||
CVMX_SYNC;
|
||||
cvmx_write_csr(CVMX_L2C_DBG, 0);
|
||||
cvmx_read_csr(CVMX_L2C_DBG);
|
||||
cvmx_spinlock_unlock(&cvmx_l2c_spinlock);
|
||||
}
|
||||
}
|
507
arch/mips/cavium-octeon/executive/cvmx-pko.c
Normal file
507
arch/mips/cavium-octeon/executive/cvmx-pko.c
Normal file
|
@ -0,0 +1,507 @@
|
|||
/***********************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);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(cvmx_pko_disable);
|
||||
|
||||
/**
|
||||
* 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();
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(cvmx_pko_shutdown);
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
668
arch/mips/cavium-octeon/executive/cvmx-spi.c
Normal file
668
arch/mips/cavium-octeon/executive/cvmx-spi.c
Normal file
|
@ -0,0 +1,668 @@
|
|||
/***********************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;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(cvmx_spi_restart_interface);
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
117
arch/mips/cavium-octeon/executive/cvmx-sysinfo.c
Normal file
117
arch/mips/cavium-octeon/executive/cvmx-sysinfo.c
Normal file
|
@ -0,0 +1,117 @@
|
|||
/***********************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**************************************/
|
||||
|
||||
/*
|
||||
* This module provides system/board/application information obtained
|
||||
* by the bootloader.
|
||||
*/
|
||||
#include <linux/module.h>
|
||||
|
||||
#include <asm/octeon/cvmx.h>
|
||||
#include <asm/octeon/cvmx-spinlock.h>
|
||||
#include <asm/octeon/cvmx-sysinfo.h>
|
||||
|
||||
/**
|
||||
* This structure defines the private state maintained by sysinfo module.
|
||||
*
|
||||
*/
|
||||
static struct {
|
||||
struct cvmx_sysinfo sysinfo; /* system information */
|
||||
cvmx_spinlock_t lock; /* mutex spinlock */
|
||||
|
||||
} state = {
|
||||
.lock = CVMX_SPINLOCK_UNLOCKED_INITIALIZER
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* Global variables that define the min/max of the memory region set
|
||||
* up for 32 bit userspace access.
|
||||
*/
|
||||
uint64_t linux_mem32_min;
|
||||
uint64_t linux_mem32_max;
|
||||
uint64_t linux_mem32_wired;
|
||||
uint64_t linux_mem32_offset;
|
||||
|
||||
/**
|
||||
* This function returns the application information as obtained
|
||||
* by the bootloader. This provides the core mask of the cores
|
||||
* running the same application image, as well as the physical
|
||||
* memory regions available to the core.
|
||||
*
|
||||
* Returns Pointer to the boot information structure
|
||||
*
|
||||
*/
|
||||
struct cvmx_sysinfo *cvmx_sysinfo_get(void)
|
||||
{
|
||||
return &(state.sysinfo);
|
||||
}
|
||||
EXPORT_SYMBOL(cvmx_sysinfo_get);
|
||||
|
||||
/**
|
||||
* This function is used in non-simple executive environments (such as
|
||||
* Linux kernel, u-boot, etc.) to configure the minimal fields that
|
||||
* are required to use simple executive files directly.
|
||||
*
|
||||
* Locking (if required) must be handled outside of this
|
||||
* function
|
||||
*
|
||||
* @phy_mem_desc_ptr:
|
||||
* Pointer to global physical memory descriptor
|
||||
* (bootmem descriptor) @board_type: Octeon board
|
||||
* type enumeration
|
||||
*
|
||||
* @board_rev_major:
|
||||
* Board major revision
|
||||
* @board_rev_minor:
|
||||
* Board minor revision
|
||||
* @cpu_clock_hz:
|
||||
* CPU clock freqency in hertz
|
||||
*
|
||||
* Returns 0: Failure
|
||||
* 1: success
|
||||
*/
|
||||
int cvmx_sysinfo_minimal_initialize(void *phy_mem_desc_ptr,
|
||||
uint16_t board_type,
|
||||
uint8_t board_rev_major,
|
||||
uint8_t board_rev_minor,
|
||||
uint32_t cpu_clock_hz)
|
||||
{
|
||||
|
||||
/* The sysinfo structure was already initialized */
|
||||
if (state.sysinfo.board_type)
|
||||
return 0;
|
||||
|
||||
memset(&(state.sysinfo), 0x0, sizeof(state.sysinfo));
|
||||
state.sysinfo.phy_mem_desc_ptr = phy_mem_desc_ptr;
|
||||
state.sysinfo.board_type = board_type;
|
||||
state.sysinfo.board_rev_major = board_rev_major;
|
||||
state.sysinfo.board_rev_minor = board_rev_minor;
|
||||
state.sysinfo.cpu_clock_hz = cpu_clock_hz;
|
||||
|
||||
return 1;
|
||||
}
|
409
arch/mips/cavium-octeon/executive/octeon-model.c
Normal file
409
arch/mips/cavium-octeon/executive/octeon-model.c
Normal file
|
@ -0,0 +1,409 @@
|
|||
/***********************license start***************
|
||||
* Author: Cavium Networks
|
||||
*
|
||||
* Contact: support@caviumnetworks.com
|
||||
* This file is part of the OCTEON SDK
|
||||
*
|
||||
* Copyright (c) 2003-2010 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**************************************/
|
||||
|
||||
#include <asm/octeon/octeon.h>
|
||||
|
||||
/**
|
||||
* Given the chip processor ID from COP0, this function returns a
|
||||
* string representing the chip model number. The string is of the
|
||||
* form CNXXXXpX.X-FREQ-SUFFIX.
|
||||
* - XXXX = The chip model number
|
||||
* - X.X = Chip pass number
|
||||
* - FREQ = Current frequency in Mhz
|
||||
* - SUFFIX = NSP, EXP, SCP, SSP, or CP
|
||||
*
|
||||
* @chip_id: Chip ID
|
||||
*
|
||||
* Returns Model string
|
||||
*/
|
||||
const char *octeon_model_get_string(uint32_t chip_id)
|
||||
{
|
||||
static char buffer[32];
|
||||
return octeon_model_get_string_buffer(chip_id, buffer);
|
||||
}
|
||||
|
||||
/*
|
||||
* Version of octeon_model_get_string() that takes buffer as argument,
|
||||
* as running early in u-boot static/global variables don't work when
|
||||
* running from flash.
|
||||
*/
|
||||
const char *octeon_model_get_string_buffer(uint32_t chip_id, char *buffer)
|
||||
{
|
||||
const char *family;
|
||||
const char *core_model;
|
||||
char pass[4];
|
||||
int clock_mhz;
|
||||
const char *suffix;
|
||||
union cvmx_l2d_fus3 fus3;
|
||||
int num_cores;
|
||||
union cvmx_mio_fus_dat2 fus_dat2;
|
||||
union cvmx_mio_fus_dat3 fus_dat3;
|
||||
char fuse_model[10];
|
||||
uint32_t fuse_data = 0;
|
||||
|
||||
fus3.u64 = 0;
|
||||
if (!OCTEON_IS_MODEL(OCTEON_CN6XXX))
|
||||
fus3.u64 = cvmx_read_csr(CVMX_L2D_FUS3);
|
||||
fus_dat2.u64 = cvmx_read_csr(CVMX_MIO_FUS_DAT2);
|
||||
fus_dat3.u64 = cvmx_read_csr(CVMX_MIO_FUS_DAT3);
|
||||
num_cores = cvmx_pop(cvmx_read_csr(CVMX_CIU_FUSE));
|
||||
|
||||
/* Make sure the non existent devices look disabled */
|
||||
switch ((chip_id >> 8) & 0xff) {
|
||||
case 6: /* CN50XX */
|
||||
case 2: /* CN30XX */
|
||||
fus_dat3.s.nodfa_dte = 1;
|
||||
fus_dat3.s.nozip = 1;
|
||||
break;
|
||||
case 4: /* CN57XX or CN56XX */
|
||||
fus_dat3.s.nodfa_dte = 1;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
/* Make a guess at the suffix */
|
||||
/* NSP = everything */
|
||||
/* EXP = No crypto */
|
||||
/* SCP = No DFA, No zip */
|
||||
/* CP = No DFA, No crypto, No zip */
|
||||
if (fus_dat3.s.nodfa_dte) {
|
||||
if (fus_dat2.s.nocrypto)
|
||||
suffix = "CP";
|
||||
else
|
||||
suffix = "SCP";
|
||||
} else if (fus_dat2.s.nocrypto)
|
||||
suffix = "EXP";
|
||||
else
|
||||
suffix = "NSP";
|
||||
|
||||
/*
|
||||
* Assume pass number is encoded using <5:3><2:0>. Exceptions
|
||||
* will be fixed later.
|
||||
*/
|
||||
sprintf(pass, "%d.%d", (int)((chip_id >> 3) & 7) + 1, (int)chip_id & 7);
|
||||
|
||||
/*
|
||||
* Use the number of cores to determine the last 2 digits of
|
||||
* the model number. There are some exceptions that are fixed
|
||||
* later.
|
||||
*/
|
||||
switch (num_cores) {
|
||||
case 32:
|
||||
core_model = "80";
|
||||
break;
|
||||
case 24:
|
||||
core_model = "70";
|
||||
break;
|
||||
case 16:
|
||||
core_model = "60";
|
||||
break;
|
||||
case 15:
|
||||
core_model = "58";
|
||||
break;
|
||||
case 14:
|
||||
core_model = "55";
|
||||
break;
|
||||
case 13:
|
||||
core_model = "52";
|
||||
break;
|
||||
case 12:
|
||||
core_model = "50";
|
||||
break;
|
||||
case 11:
|
||||
core_model = "48";
|
||||
break;
|
||||
case 10:
|
||||
core_model = "45";
|
||||
break;
|
||||
case 9:
|
||||
core_model = "42";
|
||||
break;
|
||||
case 8:
|
||||
core_model = "40";
|
||||
break;
|
||||
case 7:
|
||||
core_model = "38";
|
||||
break;
|
||||
case 6:
|
||||
core_model = "34";
|
||||
break;
|
||||
case 5:
|
||||
core_model = "32";
|
||||
break;
|
||||
case 4:
|
||||
core_model = "30";
|
||||
break;
|
||||
case 3:
|
||||
core_model = "25";
|
||||
break;
|
||||
case 2:
|
||||
core_model = "20";
|
||||
break;
|
||||
case 1:
|
||||
core_model = "10";
|
||||
break;
|
||||
default:
|
||||
core_model = "XX";
|
||||
break;
|
||||
}
|
||||
|
||||
/* Now figure out the family, the first two digits */
|
||||
switch ((chip_id >> 8) & 0xff) {
|
||||
case 0: /* CN38XX, CN37XX or CN36XX */
|
||||
if (fus3.cn38xx.crip_512k) {
|
||||
/*
|
||||
* For some unknown reason, the 16 core one is
|
||||
* called 37 instead of 36.
|
||||
*/
|
||||
if (num_cores >= 16)
|
||||
family = "37";
|
||||
else
|
||||
family = "36";
|
||||
} else
|
||||
family = "38";
|
||||
/*
|
||||
* This series of chips didn't follow the standard
|
||||
* pass numbering.
|
||||
*/
|
||||
switch (chip_id & 0xf) {
|
||||
case 0:
|
||||
strcpy(pass, "1.X");
|
||||
break;
|
||||
case 1:
|
||||
strcpy(pass, "2.X");
|
||||
break;
|
||||
case 3:
|
||||
strcpy(pass, "3.X");
|
||||
break;
|
||||
default:
|
||||
strcpy(pass, "X.X");
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 1: /* CN31XX or CN3020 */
|
||||
if ((chip_id & 0x10) || fus3.cn31xx.crip_128k)
|
||||
family = "30";
|
||||
else
|
||||
family = "31";
|
||||
/*
|
||||
* This series of chips didn't follow the standard
|
||||
* pass numbering.
|
||||
*/
|
||||
switch (chip_id & 0xf) {
|
||||
case 0:
|
||||
strcpy(pass, "1.0");
|
||||
break;
|
||||
case 2:
|
||||
strcpy(pass, "1.1");
|
||||
break;
|
||||
default:
|
||||
strcpy(pass, "X.X");
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 2: /* CN3010 or CN3005 */
|
||||
family = "30";
|
||||
/* A chip with half cache is an 05 */
|
||||
if (fus3.cn30xx.crip_64k)
|
||||
core_model = "05";
|
||||
/*
|
||||
* This series of chips didn't follow the standard
|
||||
* pass numbering.
|
||||
*/
|
||||
switch (chip_id & 0xf) {
|
||||
case 0:
|
||||
strcpy(pass, "1.0");
|
||||
break;
|
||||
case 2:
|
||||
strcpy(pass, "1.1");
|
||||
break;
|
||||
default:
|
||||
strcpy(pass, "X.X");
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 3: /* CN58XX */
|
||||
family = "58";
|
||||
/* Special case. 4 core, half cache (CP with half cache) */
|
||||
if ((num_cores == 4) && fus3.cn58xx.crip_1024k && !strncmp(suffix, "CP", 2))
|
||||
core_model = "29";
|
||||
|
||||
/* Pass 1 uses different encodings for pass numbers */
|
||||
if ((chip_id & 0xFF) < 0x8) {
|
||||
switch (chip_id & 0x3) {
|
||||
case 0:
|
||||
strcpy(pass, "1.0");
|
||||
break;
|
||||
case 1:
|
||||
strcpy(pass, "1.1");
|
||||
break;
|
||||
case 3:
|
||||
strcpy(pass, "1.2");
|
||||
break;
|
||||
default:
|
||||
strcpy(pass, "1.X");
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 4: /* CN57XX, CN56XX, CN55XX, CN54XX */
|
||||
if (fus_dat2.cn56xx.raid_en) {
|
||||
if (fus3.cn56xx.crip_1024k)
|
||||
family = "55";
|
||||
else
|
||||
family = "57";
|
||||
if (fus_dat2.cn56xx.nocrypto)
|
||||
suffix = "SP";
|
||||
else
|
||||
suffix = "SSP";
|
||||
} else {
|
||||
if (fus_dat2.cn56xx.nocrypto)
|
||||
suffix = "CP";
|
||||
else {
|
||||
suffix = "NSP";
|
||||
if (fus_dat3.s.nozip)
|
||||
suffix = "SCP";
|
||||
|
||||
if (fus_dat3.s.bar2_en)
|
||||
suffix = "NSPB2";
|
||||
}
|
||||
if (fus3.cn56xx.crip_1024k)
|
||||
family = "54";
|
||||
else
|
||||
family = "56";
|
||||
}
|
||||
break;
|
||||
case 6: /* CN50XX */
|
||||
family = "50";
|
||||
break;
|
||||
case 7: /* CN52XX */
|
||||
if (fus3.cn52xx.crip_256k)
|
||||
family = "51";
|
||||
else
|
||||
family = "52";
|
||||
break;
|
||||
case 0x93: /* CN61XX */
|
||||
family = "61";
|
||||
if (fus_dat2.cn61xx.nocrypto && fus_dat2.cn61xx.dorm_crypto)
|
||||
suffix = "AP";
|
||||
if (fus_dat2.cn61xx.nocrypto)
|
||||
suffix = "CP";
|
||||
else if (fus_dat2.cn61xx.dorm_crypto)
|
||||
suffix = "DAP";
|
||||
else if (fus_dat3.cn61xx.nozip)
|
||||
suffix = "SCP";
|
||||
break;
|
||||
case 0x90: /* CN63XX */
|
||||
family = "63";
|
||||
if (fus_dat3.s.l2c_crip == 2)
|
||||
family = "62";
|
||||
if (num_cores == 6) /* Other core counts match generic */
|
||||
core_model = "35";
|
||||
if (fus_dat2.cn63xx.nocrypto)
|
||||
suffix = "CP";
|
||||
else if (fus_dat2.cn63xx.dorm_crypto)
|
||||
suffix = "DAP";
|
||||
else if (fus_dat3.cn63xx.nozip)
|
||||
suffix = "SCP";
|
||||
else
|
||||
suffix = "AAP";
|
||||
break;
|
||||
case 0x92: /* CN66XX */
|
||||
family = "66";
|
||||
if (num_cores == 6) /* Other core counts match generic */
|
||||
core_model = "35";
|
||||
if (fus_dat2.cn66xx.nocrypto && fus_dat2.cn66xx.dorm_crypto)
|
||||
suffix = "AP";
|
||||
if (fus_dat2.cn66xx.nocrypto)
|
||||
suffix = "CP";
|
||||
else if (fus_dat2.cn66xx.dorm_crypto)
|
||||
suffix = "DAP";
|
||||
else if (fus_dat3.cn66xx.nozip)
|
||||
suffix = "SCP";
|
||||
else
|
||||
suffix = "AAP";
|
||||
break;
|
||||
case 0x91: /* CN68XX */
|
||||
family = "68";
|
||||
if (fus_dat2.cn68xx.nocrypto && fus_dat3.cn68xx.nozip)
|
||||
suffix = "CP";
|
||||
else if (fus_dat2.cn68xx.dorm_crypto)
|
||||
suffix = "DAP";
|
||||
else if (fus_dat3.cn68xx.nozip)
|
||||
suffix = "SCP";
|
||||
else if (fus_dat2.cn68xx.nocrypto)
|
||||
suffix = "SP";
|
||||
else
|
||||
suffix = "AAP";
|
||||
break;
|
||||
default:
|
||||
family = "XX";
|
||||
core_model = "XX";
|
||||
strcpy(pass, "X.X");
|
||||
suffix = "XXX";
|
||||
break;
|
||||
}
|
||||
|
||||
clock_mhz = octeon_get_clock_rate() / 1000000;
|
||||
if (family[0] != '3') {
|
||||
int fuse_base = 384 / 8;
|
||||
if (family[0] == '6')
|
||||
fuse_base = 832 / 8;
|
||||
|
||||
/* Check for model in fuses, overrides normal decode */
|
||||
/* This is _not_ valid for Octeon CN3XXX models */
|
||||
fuse_data |= cvmx_fuse_read_byte(fuse_base + 3);
|
||||
fuse_data = fuse_data << 8;
|
||||
fuse_data |= cvmx_fuse_read_byte(fuse_base + 2);
|
||||
fuse_data = fuse_data << 8;
|
||||
fuse_data |= cvmx_fuse_read_byte(fuse_base + 1);
|
||||
fuse_data = fuse_data << 8;
|
||||
fuse_data |= cvmx_fuse_read_byte(fuse_base);
|
||||
if (fuse_data & 0x7ffff) {
|
||||
int model = fuse_data & 0x3fff;
|
||||
int suffix = (fuse_data >> 14) & 0x1f;
|
||||
if (suffix && model) {
|
||||
/* Have both number and suffix in fuses, so both */
|
||||
sprintf(fuse_model, "%d%c", model, 'A' + suffix - 1);
|
||||
core_model = "";
|
||||
family = fuse_model;
|
||||
} else if (suffix && !model) {
|
||||
/* Only have suffix, so add suffix to 'normal' model number */
|
||||
sprintf(fuse_model, "%s%c", core_model, 'A' + suffix - 1);
|
||||
core_model = fuse_model;
|
||||
} else {
|
||||
/* Don't have suffix, so just use model from fuses */
|
||||
sprintf(fuse_model, "%d", model);
|
||||
core_model = "";
|
||||
family = fuse_model;
|
||||
}
|
||||
}
|
||||
}
|
||||
sprintf(buffer, "CN%s%sp%s-%d-%s", family, core_model, pass, clock_mhz, suffix);
|
||||
return buffer;
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue