Initial commit; kernel source import

This commit is contained in:
Nathan
2025-04-06 23:50:55 -05:00
commit 25c6d769f4
45093 changed files with 18199410 additions and 0 deletions

2
arch/mips/cavium-octeon/.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
*.dtb.S
*.dtb

View File

@@ -0,0 +1,106 @@
if CPU_CAVIUM_OCTEON
config CAVIUM_CN63XXP1
bool "Enable CN63XXP1 errata worarounds"
default "n"
help
The CN63XXP1 chip requires build time workarounds to
function reliably, select this option to enable them. These
workarounds will cause a slight decrease in performance on
non-CN63XXP1 hardware, so it is recommended to select "n"
unless it is known the workarounds are needed.
config CAVIUM_OCTEON_2ND_KERNEL
bool "Build the kernel to be used as a 2nd kernel on the same chip"
default "n"
help
This option configures this kernel to be linked at a different
address and use the 2nd uart for output. This allows a kernel built
with this option to be run at the same time as one built without this
option.
config CAVIUM_OCTEON_HW_FIX_UNALIGNED
bool "Enable hardware fixups of unaligned loads and stores"
default "y"
help
Configure the Octeon hardware to automatically fix unaligned loads
and stores. Normally unaligned accesses are fixed using a kernel
exception handler. This option enables the hardware automatic fixups,
which requires only an extra 3 cycles. Disable this option if you
are running code that relies on address exceptions on unaligned
accesses.
config CAVIUM_OCTEON_CVMSEG_SIZE
int "Number of L1 cache lines reserved for CVMSEG memory"
range 0 54
default 1
help
CVMSEG LM is a segment that accesses portions of the dcache as a
local memory; the larger CVMSEG is, the smaller the cache is.
This selects the size of CVMSEG LM, which is in cache blocks. The
legally range is from zero to 54 cache blocks (i.e. CVMSEG LM is
between zero and 6192 bytes).
config CAVIUM_OCTEON_LOCK_L2
bool "Lock often used kernel code in the L2"
default "y"
help
Enable locking parts of the kernel into the L2 cache.
config CAVIUM_OCTEON_LOCK_L2_TLB
bool "Lock the TLB handler in L2"
depends on CAVIUM_OCTEON_LOCK_L2
default "y"
help
Lock the low level TLB fast path into L2.
config CAVIUM_OCTEON_LOCK_L2_EXCEPTION
bool "Lock the exception handler in L2"
depends on CAVIUM_OCTEON_LOCK_L2
default "y"
help
Lock the low level exception handler into L2.
config CAVIUM_OCTEON_LOCK_L2_LOW_LEVEL_INTERRUPT
bool "Lock the interrupt handler in L2"
depends on CAVIUM_OCTEON_LOCK_L2
default "y"
help
Lock the low level interrupt handler into L2.
config CAVIUM_OCTEON_LOCK_L2_INTERRUPT
bool "Lock the 2nd level interrupt handler in L2"
depends on CAVIUM_OCTEON_LOCK_L2
default "y"
help
Lock the 2nd level interrupt handler in L2.
config CAVIUM_OCTEON_LOCK_L2_MEMCPY
bool "Lock memcpy() in L2"
depends on CAVIUM_OCTEON_LOCK_L2
default "y"
help
Lock the kernel's implementation of memcpy() into L2.
config IOMMU_HELPER
bool
config NEED_SG_DMA_LENGTH
bool
config SWIOTLB
def_bool y
select IOMMU_HELPER
select NEED_SG_DMA_LENGTH
config OCTEON_ILM
tristate "Module to measure interrupt latency using Octeon CIU Timer"
help
This driver is a module to measure interrupt latency using the
the CIU Timers on Octeon.
To compile this driver as a module, choose M here. The module
will be called octeon-ilm
endif # CPU_CAVIUM_OCTEON

View File

@@ -0,0 +1,31 @@
#
# 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-2009 Cavium Networks
#
CFLAGS_octeon-platform.o = -I$(src)/../../../scripts/dtc/libfdt
CFLAGS_setup.o = -I$(src)/../../../scripts/dtc/libfdt
obj-y := cpu.o setup.o serial.o octeon-platform.o octeon-irq.o csrc-octeon.o
obj-y += dma-octeon.o flash_setup.o
obj-y += octeon-memcpy.o
obj-y += executive/
obj-$(CONFIG_SMP) += smp.o
obj-$(CONFIG_OCTEON_ILM) += oct_ilm.o
DTS_FILES = octeon_3xxx.dts octeon_68xx.dts
DTB_FILES = $(patsubst %.dts, %.dtb, $(DTS_FILES))
obj-y += $(patsubst %.dts, %.dtb.o, $(DTS_FILES))
# Let's keep the .dtb files around in case we want to look at them.
.SECONDARY: $(addprefix $(obj)/, $(DTB_FILES))
clean-files += $(DTB_FILES) $(patsubst %.dtb, %.dtb.S, $(DTB_FILES))

View File

@@ -0,0 +1,11 @@
#
# Cavium Octeon
#
platform-$(CONFIG_CPU_CAVIUM_OCTEON) += cavium-octeon/
cflags-$(CONFIG_CPU_CAVIUM_OCTEON) += \
-I$(srctree)/arch/mips/include/asm/mach-cavium-octeon
ifdef CONFIG_CAVIUM_OCTEON_2ND_KERNEL
load-$(CONFIG_CPU_CAVIUM_OCTEON) += 0xffffffff84100000
else
load-$(CONFIG_CPU_CAVIUM_OCTEON) += 0xffffffff81100000
endif

View File

@@ -0,0 +1,48 @@
/*
* 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) 2009 Wind River Systems,
* written by Ralf Baechle <ralf@linux-mips.org>
*/
#include <linux/init.h>
#include <linux/irqflags.h>
#include <linux/notifier.h>
#include <linux/prefetch.h>
#include <linux/sched.h>
#include <asm/cop2.h>
#include <asm/current.h>
#include <asm/mipsregs.h>
#include <asm/page.h>
#include <asm/octeon/octeon.h>
static int cnmips_cu2_call(struct notifier_block *nfb, unsigned long action,
void *data)
{
unsigned long flags;
unsigned int status;
switch (action) {
case CU2_EXCEPTION:
prefetch(&current->thread.cp2);
local_irq_save(flags);
KSTK_STATUS(current) |= ST0_CU2;
status = read_c0_status();
write_c0_status(status | ST0_CU2);
octeon_cop2_restore(&(current->thread.cp2));
write_c0_status(status & ~ST0_CU2);
local_irq_restore(flags);
return NOTIFY_BAD; /* Don't call default notifier */
}
return NOTIFY_OK; /* Let default notifier send signals */
}
static int __init cnmips_cu2_setup(void)
{
return cu2_notifier(cnmips_cu2_call, 0);
}
early_initcall(cnmips_cu2_setup);

View File

@@ -0,0 +1,198 @@
/*
* 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) 2007 by Ralf Baechle
* Copyright (C) 2009, 2012 Cavium, Inc.
*/
#include <linux/clocksource.h>
#include <linux/export.h>
#include <linux/init.h>
#include <linux/smp.h>
#include <asm/cpu-info.h>
#include <asm/time.h>
#include <asm/octeon/octeon.h>
#include <asm/octeon/cvmx-ipd-defs.h>
#include <asm/octeon/cvmx-mio-defs.h>
static u64 f;
static u64 rdiv;
static u64 sdiv;
static u64 octeon_udelay_factor;
static u64 octeon_ndelay_factor;
void __init octeon_setup_delays(void)
{
octeon_udelay_factor = octeon_get_clock_rate() / 1000000;
/*
* For __ndelay we divide by 2^16, so the factor is multiplied
* by the same amount.
*/
octeon_ndelay_factor = (octeon_udelay_factor * 0x10000ull) / 1000ull;
preset_lpj = octeon_get_clock_rate() / HZ;
if (current_cpu_type() == CPU_CAVIUM_OCTEON2) {
union cvmx_mio_rst_boot rst_boot;
rst_boot.u64 = cvmx_read_csr(CVMX_MIO_RST_BOOT);
rdiv = rst_boot.s.c_mul; /* CPU clock */
sdiv = rst_boot.s.pnr_mul; /* I/O clock */
f = (0x8000000000000000ull / sdiv) * 2;
}
}
/*
* Set the current core's cvmcount counter to the value of the
* IPD_CLK_COUNT. We do this on all cores as they are brought
* on-line. This allows for a read from a local cpu register to
* access a synchronized counter.
*
* On CPU_CAVIUM_OCTEON2 the IPD_CLK_COUNT is scaled by rdiv/sdiv.
*/
void octeon_init_cvmcount(void)
{
unsigned long flags;
unsigned loops = 2;
/* Clobber loops so GCC will not unroll the following while loop. */
asm("" : "+r" (loops));
local_irq_save(flags);
/*
* Loop several times so we are executing from the cache,
* which should give more deterministic timing.
*/
while (loops--) {
u64 ipd_clk_count = cvmx_read_csr(CVMX_IPD_CLK_COUNT);
if (rdiv != 0) {
ipd_clk_count *= rdiv;
if (f != 0) {
asm("dmultu\t%[cnt],%[f]\n\t"
"mfhi\t%[cnt]"
: [cnt] "+r" (ipd_clk_count)
: [f] "r" (f)
: "hi", "lo");
}
}
write_c0_cvmcount(ipd_clk_count);
}
local_irq_restore(flags);
}
static cycle_t octeon_cvmcount_read(struct clocksource *cs)
{
return read_c0_cvmcount();
}
static struct clocksource clocksource_mips = {
.name = "OCTEON_CVMCOUNT",
.read = octeon_cvmcount_read,
.mask = CLOCKSOURCE_MASK(64),
.flags = CLOCK_SOURCE_IS_CONTINUOUS,
};
unsigned long long notrace sched_clock(void)
{
/* 64-bit arithmatic can overflow, so use 128-bit. */
u64 t1, t2, t3;
unsigned long long rv;
u64 mult = clocksource_mips.mult;
u64 shift = clocksource_mips.shift;
u64 cnt = read_c0_cvmcount();
asm (
"dmultu\t%[cnt],%[mult]\n\t"
"nor\t%[t1],$0,%[shift]\n\t"
"mfhi\t%[t2]\n\t"
"mflo\t%[t3]\n\t"
"dsll\t%[t2],%[t2],1\n\t"
"dsrlv\t%[rv],%[t3],%[shift]\n\t"
"dsllv\t%[t1],%[t2],%[t1]\n\t"
"or\t%[rv],%[t1],%[rv]\n\t"
: [rv] "=&r" (rv), [t1] "=&r" (t1), [t2] "=&r" (t2), [t3] "=&r" (t3)
: [cnt] "r" (cnt), [mult] "r" (mult), [shift] "r" (shift)
: "hi", "lo");
return rv;
}
void __init plat_time_init(void)
{
clocksource_mips.rating = 300;
clocksource_register_hz(&clocksource_mips, octeon_get_clock_rate());
}
void __udelay(unsigned long us)
{
u64 cur, end, inc;
cur = read_c0_cvmcount();
inc = us * octeon_udelay_factor;
end = cur + inc;
while (end > cur)
cur = read_c0_cvmcount();
}
EXPORT_SYMBOL(__udelay);
void __ndelay(unsigned long ns)
{
u64 cur, end, inc;
cur = read_c0_cvmcount();
inc = ((ns * octeon_ndelay_factor) >> 16);
end = cur + inc;
while (end > cur)
cur = read_c0_cvmcount();
}
EXPORT_SYMBOL(__ndelay);
void __delay(unsigned long loops)
{
u64 cur, end;
cur = read_c0_cvmcount();
end = cur + loops;
while (end > cur)
cur = read_c0_cvmcount();
}
EXPORT_SYMBOL(__delay);
/**
* octeon_io_clk_delay - wait for a given number of io clock cycles to pass.
*
* We scale the wait by the clock ratio, and then wait for the
* corresponding number of core clocks.
*
* @count: The number of clocks to wait.
*/
void octeon_io_clk_delay(unsigned long count)
{
u64 cur, end;
cur = read_c0_cvmcount();
if (rdiv != 0) {
end = count * rdiv;
if (f != 0) {
asm("dmultu\t%[cnt],%[f]\n\t"
"mfhi\t%[cnt]"
: [cnt] "+r" (end)
: [f] "r" (f)
: "hi", "lo");
}
end = cur + end;
} else {
end = cur + count;
}
while (end > cur)
cur = read_c0_cvmcount();
}
EXPORT_SYMBOL(octeon_io_clk_delay);

View File

@@ -0,0 +1,370 @@
/*
* 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) 2000 Ani Joshi <ajoshi@unixbox.com>
* Copyright (C) 2000, 2001 Ralf Baechle <ralf@gnu.org>
* Copyright (C) 2005 Ilya A. Volynets-Evenbakh <ilya@total-knowledge.com>
* swiped from i386, and cloned for MIPS by Geert, polished by Ralf.
* IP32 changes by Ilya.
* Copyright (C) 2010 Cavium Networks, Inc.
*/
#include <linux/dma-mapping.h>
#include <linux/scatterlist.h>
#include <linux/bootmem.h>
#include <linux/export.h>
#include <linux/swiotlb.h>
#include <linux/types.h>
#include <linux/init.h>
#include <linux/mm.h>
#include <asm/bootinfo.h>
#include <asm/octeon/octeon.h>
#ifdef CONFIG_PCI
#include <asm/octeon/pci-octeon.h>
#include <asm/octeon/cvmx-npi-defs.h>
#include <asm/octeon/cvmx-pci-defs.h>
static dma_addr_t octeon_hole_phys_to_dma(phys_addr_t paddr)
{
if (paddr >= CVMX_PCIE_BAR1_PHYS_BASE && paddr < (CVMX_PCIE_BAR1_PHYS_BASE + CVMX_PCIE_BAR1_PHYS_SIZE))
return paddr - CVMX_PCIE_BAR1_PHYS_BASE + CVMX_PCIE_BAR1_RC_BASE;
else
return paddr;
}
static phys_addr_t octeon_hole_dma_to_phys(dma_addr_t daddr)
{
if (daddr >= CVMX_PCIE_BAR1_RC_BASE)
return daddr + CVMX_PCIE_BAR1_PHYS_BASE - CVMX_PCIE_BAR1_RC_BASE;
else
return daddr;
}
static dma_addr_t octeon_gen1_phys_to_dma(struct device *dev, phys_addr_t paddr)
{
if (paddr >= 0x410000000ull && paddr < 0x420000000ull)
paddr -= 0x400000000ull;
return octeon_hole_phys_to_dma(paddr);
}
static phys_addr_t octeon_gen1_dma_to_phys(struct device *dev, dma_addr_t daddr)
{
daddr = octeon_hole_dma_to_phys(daddr);
if (daddr >= 0x10000000ull && daddr < 0x20000000ull)
daddr += 0x400000000ull;
return daddr;
}
static dma_addr_t octeon_gen2_phys_to_dma(struct device *dev, phys_addr_t paddr)
{
return octeon_hole_phys_to_dma(paddr);
}
static phys_addr_t octeon_gen2_dma_to_phys(struct device *dev, dma_addr_t daddr)
{
return octeon_hole_dma_to_phys(daddr);
}
static dma_addr_t octeon_big_phys_to_dma(struct device *dev, phys_addr_t paddr)
{
if (paddr >= 0x410000000ull && paddr < 0x420000000ull)
paddr -= 0x400000000ull;
/* Anything in the BAR1 hole or above goes via BAR2 */
if (paddr >= 0xf0000000ull)
paddr = OCTEON_BAR2_PCI_ADDRESS + paddr;
return paddr;
}
static phys_addr_t octeon_big_dma_to_phys(struct device *dev, dma_addr_t daddr)
{
if (daddr >= OCTEON_BAR2_PCI_ADDRESS)
daddr -= OCTEON_BAR2_PCI_ADDRESS;
if (daddr >= 0x10000000ull && daddr < 0x20000000ull)
daddr += 0x400000000ull;
return daddr;
}
static dma_addr_t octeon_small_phys_to_dma(struct device *dev,
phys_addr_t paddr)
{
if (paddr >= 0x410000000ull && paddr < 0x420000000ull)
paddr -= 0x400000000ull;
/* Anything not in the BAR1 range goes via BAR2 */
if (paddr >= octeon_bar1_pci_phys && paddr < octeon_bar1_pci_phys + 0x8000000ull)
paddr = paddr - octeon_bar1_pci_phys;
else
paddr = OCTEON_BAR2_PCI_ADDRESS + paddr;
return paddr;
}
static phys_addr_t octeon_small_dma_to_phys(struct device *dev,
dma_addr_t daddr)
{
if (daddr >= OCTEON_BAR2_PCI_ADDRESS)
daddr -= OCTEON_BAR2_PCI_ADDRESS;
else
daddr += octeon_bar1_pci_phys;
if (daddr >= 0x10000000ull && daddr < 0x20000000ull)
daddr += 0x400000000ull;
return daddr;
}
#endif /* CONFIG_PCI */
static dma_addr_t octeon_dma_map_page(struct device *dev, struct page *page,
unsigned long offset, size_t size, enum dma_data_direction direction,
struct dma_attrs *attrs)
{
dma_addr_t daddr = swiotlb_map_page(dev, page, offset, size,
direction, attrs);
mb();
return daddr;
}
static int octeon_dma_map_sg(struct device *dev, struct scatterlist *sg,
int nents, enum dma_data_direction direction, struct dma_attrs *attrs)
{
int r = swiotlb_map_sg_attrs(dev, sg, nents, direction, attrs);
mb();
return r;
}
static void octeon_dma_sync_single_for_device(struct device *dev,
dma_addr_t dma_handle, size_t size, enum dma_data_direction direction)
{
swiotlb_sync_single_for_device(dev, dma_handle, size, direction);
mb();
}
static void octeon_dma_sync_sg_for_device(struct device *dev,
struct scatterlist *sg, int nelems, enum dma_data_direction direction)
{
swiotlb_sync_sg_for_device(dev, sg, nelems, direction);
mb();
}
static void *octeon_dma_alloc_coherent(struct device *dev, size_t size,
dma_addr_t *dma_handle, gfp_t gfp, struct dma_attrs *attrs)
{
void *ret;
if (dma_alloc_from_coherent(dev, size, dma_handle, &ret))
return ret;
/* ignore region specifiers */
gfp &= ~(__GFP_DMA | __GFP_DMA32 | __GFP_HIGHMEM);
#ifdef CONFIG_ZONE_DMA
if (dev == NULL)
gfp |= __GFP_DMA;
else if (dev->coherent_dma_mask <= DMA_BIT_MASK(24))
gfp |= __GFP_DMA;
else
#endif
#ifdef CONFIG_ZONE_DMA32
if (dev->coherent_dma_mask <= DMA_BIT_MASK(32))
gfp |= __GFP_DMA32;
else
#endif
;
/* Don't invoke OOM killer */
gfp |= __GFP_NORETRY;
ret = swiotlb_alloc_coherent(dev, size, dma_handle, gfp);
mb();
return ret;
}
static void octeon_dma_free_coherent(struct device *dev, size_t size,
void *vaddr, dma_addr_t dma_handle, struct dma_attrs *attrs)
{
int order = get_order(size);
if (dma_release_from_coherent(dev, order, vaddr))
return;
swiotlb_free_coherent(dev, size, vaddr, dma_handle);
}
static dma_addr_t octeon_unity_phys_to_dma(struct device *dev, phys_addr_t paddr)
{
return paddr;
}
static phys_addr_t octeon_unity_dma_to_phys(struct device *dev, dma_addr_t daddr)
{
return daddr;
}
struct octeon_dma_map_ops {
struct dma_map_ops dma_map_ops;
dma_addr_t (*phys_to_dma)(struct device *dev, phys_addr_t paddr);
phys_addr_t (*dma_to_phys)(struct device *dev, dma_addr_t daddr);
};
dma_addr_t phys_to_dma(struct device *dev, phys_addr_t paddr)
{
struct octeon_dma_map_ops *ops = container_of(get_dma_ops(dev),
struct octeon_dma_map_ops,
dma_map_ops);
return ops->phys_to_dma(dev, paddr);
}
EXPORT_SYMBOL(phys_to_dma);
phys_addr_t dma_to_phys(struct device *dev, dma_addr_t daddr)
{
struct octeon_dma_map_ops *ops = container_of(get_dma_ops(dev),
struct octeon_dma_map_ops,
dma_map_ops);
return ops->dma_to_phys(dev, daddr);
}
EXPORT_SYMBOL(dma_to_phys);
static struct octeon_dma_map_ops octeon_linear_dma_map_ops = {
.dma_map_ops = {
.alloc = octeon_dma_alloc_coherent,
.free = octeon_dma_free_coherent,
.map_page = octeon_dma_map_page,
.unmap_page = swiotlb_unmap_page,
.map_sg = octeon_dma_map_sg,
.unmap_sg = swiotlb_unmap_sg_attrs,
.sync_single_for_cpu = swiotlb_sync_single_for_cpu,
.sync_single_for_device = octeon_dma_sync_single_for_device,
.sync_sg_for_cpu = swiotlb_sync_sg_for_cpu,
.sync_sg_for_device = octeon_dma_sync_sg_for_device,
.mapping_error = swiotlb_dma_mapping_error,
.dma_supported = swiotlb_dma_supported
},
.phys_to_dma = octeon_unity_phys_to_dma,
.dma_to_phys = octeon_unity_dma_to_phys
};
char *octeon_swiotlb;
void __init plat_swiotlb_setup(void)
{
int i;
phys_t max_addr;
phys_t addr_size;
size_t swiotlbsize;
unsigned long swiotlb_nslabs;
max_addr = 0;
addr_size = 0;
for (i = 0 ; i < boot_mem_map.nr_map; i++) {
struct boot_mem_map_entry *e = &boot_mem_map.map[i];
if (e->type != BOOT_MEM_RAM && e->type != BOOT_MEM_INIT_RAM)
continue;
/* These addresses map low for PCI. */
if (e->addr > 0x410000000ull && !OCTEON_IS_MODEL(OCTEON_CN6XXX))
continue;
addr_size += e->size;
if (max_addr < e->addr + e->size)
max_addr = e->addr + e->size;
}
swiotlbsize = PAGE_SIZE;
#ifdef CONFIG_PCI
/*
* For OCTEON_DMA_BAR_TYPE_SMALL, size the iotlb at 1/4 memory
* size to a maximum of 64MB
*/
if (OCTEON_IS_MODEL(OCTEON_CN31XX)
|| OCTEON_IS_MODEL(OCTEON_CN38XX_PASS2)) {
swiotlbsize = addr_size / 4;
if (swiotlbsize > 64 * (1<<20))
swiotlbsize = 64 * (1<<20);
} else if (max_addr > 0xf0000000ul) {
/*
* Otherwise only allocate a big iotlb if there is
* memory past the BAR1 hole.
*/
swiotlbsize = 64 * (1<<20);
}
#endif
#ifdef CONFIG_USB_OCTEON_OHCI
/* OCTEON II ohci is only 32-bit. */
if (OCTEON_IS_MODEL(OCTEON_CN6XXX) && max_addr >= 0x100000000ul)
swiotlbsize = 64 * (1<<20);
#endif
swiotlb_nslabs = swiotlbsize >> IO_TLB_SHIFT;
swiotlb_nslabs = ALIGN(swiotlb_nslabs, IO_TLB_SEGSIZE);
swiotlbsize = swiotlb_nslabs << IO_TLB_SHIFT;
octeon_swiotlb = alloc_bootmem_low_pages(swiotlbsize);
if (swiotlb_init_with_tbl(octeon_swiotlb, swiotlb_nslabs, 1) == -ENOMEM)
panic("Cannot allocate SWIOTLB buffer");
mips_dma_map_ops = &octeon_linear_dma_map_ops.dma_map_ops;
}
#ifdef CONFIG_PCI
static struct octeon_dma_map_ops _octeon_pci_dma_map_ops = {
.dma_map_ops = {
.alloc = octeon_dma_alloc_coherent,
.free = octeon_dma_free_coherent,
.map_page = octeon_dma_map_page,
.unmap_page = swiotlb_unmap_page,
.map_sg = octeon_dma_map_sg,
.unmap_sg = swiotlb_unmap_sg_attrs,
.sync_single_for_cpu = swiotlb_sync_single_for_cpu,
.sync_single_for_device = octeon_dma_sync_single_for_device,
.sync_sg_for_cpu = swiotlb_sync_sg_for_cpu,
.sync_sg_for_device = octeon_dma_sync_sg_for_device,
.mapping_error = swiotlb_dma_mapping_error,
.dma_supported = swiotlb_dma_supported
},
};
struct dma_map_ops *octeon_pci_dma_map_ops;
void __init octeon_pci_dma_init(void)
{
switch (octeon_dma_bar_type) {
case OCTEON_DMA_BAR_TYPE_PCIE2:
_octeon_pci_dma_map_ops.phys_to_dma = octeon_gen2_phys_to_dma;
_octeon_pci_dma_map_ops.dma_to_phys = octeon_gen2_dma_to_phys;
break;
case OCTEON_DMA_BAR_TYPE_PCIE:
_octeon_pci_dma_map_ops.phys_to_dma = octeon_gen1_phys_to_dma;
_octeon_pci_dma_map_ops.dma_to_phys = octeon_gen1_dma_to_phys;
break;
case OCTEON_DMA_BAR_TYPE_BIG:
_octeon_pci_dma_map_ops.phys_to_dma = octeon_big_phys_to_dma;
_octeon_pci_dma_map_ops.dma_to_phys = octeon_big_dma_to_phys;
break;
case OCTEON_DMA_BAR_TYPE_SMALL:
_octeon_pci_dma_map_ops.phys_to_dma = octeon_small_phys_to_dma;
_octeon_pci_dma_map_ops.dma_to_phys = octeon_small_dma_to_phys;
break;
default:
BUG();
}
octeon_pci_dma_map_ops = &_octeon_pci_dma_map_ops.dma_map_ops;
}
#endif /* CONFIG_PCI */

View 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

View 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;
}

View File

@@ -0,0 +1,306 @@
/***********************license start***************
* Author: Cavium Networks
*
* Contact: support@caviumnetworks.com
* This file is part of the OCTEON SDK
*
* Copyright (c) 2003-2008 Cavium Networks
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License, Version 2, as
* published by the Free Software Foundation.
*
* This file is distributed in the hope that it will be useful, but
* AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or
* NONINFRINGEMENT. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License
* along with this file; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
* or visit http://www.gnu.org/licenses/.
*
* This file may also be available under a different license from Cavium.
* Contact Cavium Networks for more information
***********************license end**************************************/
/*
* Support functions for managing command queues used for
* various hardware blocks.
*/
#include <linux/kernel.h>
#include <asm/octeon/octeon.h>
#include <asm/octeon/cvmx-config.h>
#include <asm/octeon/cvmx-fpa.h>
#include <asm/octeon/cvmx-cmd-queue.h>
#include <asm/octeon/cvmx-npei-defs.h>
#include <asm/octeon/cvmx-pexp-defs.h>
#include <asm/octeon/cvmx-pko-defs.h>
/**
* This application uses this pointer to access the global queue
* state. It points to a bootmem named block.
*/
__cvmx_cmd_queue_all_state_t *__cvmx_cmd_queue_state_ptr;
/**
* Initialize the Global queue state pointer.
*
* Returns CVMX_CMD_QUEUE_SUCCESS or a failure code
*/
static cvmx_cmd_queue_result_t __cvmx_cmd_queue_init_state_ptr(void)
{
char *alloc_name = "cvmx_cmd_queues";
#if defined(CONFIG_CAVIUM_RESERVE32) && CONFIG_CAVIUM_RESERVE32
extern uint64_t octeon_reserve32_memory;
#endif
if (likely(__cvmx_cmd_queue_state_ptr))
return CVMX_CMD_QUEUE_SUCCESS;
#if defined(CONFIG_CAVIUM_RESERVE32) && CONFIG_CAVIUM_RESERVE32
if (octeon_reserve32_memory)
__cvmx_cmd_queue_state_ptr =
cvmx_bootmem_alloc_named_range(sizeof(*__cvmx_cmd_queue_state_ptr),
octeon_reserve32_memory,
octeon_reserve32_memory +
(CONFIG_CAVIUM_RESERVE32 <<
20) - 1, 128, alloc_name);
else
#endif
__cvmx_cmd_queue_state_ptr =
cvmx_bootmem_alloc_named(sizeof(*__cvmx_cmd_queue_state_ptr),
128,
alloc_name);
if (__cvmx_cmd_queue_state_ptr)
memset(__cvmx_cmd_queue_state_ptr, 0,
sizeof(*__cvmx_cmd_queue_state_ptr));
else {
struct cvmx_bootmem_named_block_desc *block_desc =
cvmx_bootmem_find_named_block(alloc_name);
if (block_desc)
__cvmx_cmd_queue_state_ptr =
cvmx_phys_to_ptr(block_desc->base_addr);
else {
cvmx_dprintf
("ERROR: cvmx_cmd_queue_initialize: Unable to get named block %s.\n",
alloc_name);
return CVMX_CMD_QUEUE_NO_MEMORY;
}
}
return CVMX_CMD_QUEUE_SUCCESS;
}
/**
* Initialize a command queue for use. The initial FPA buffer is
* allocated and the hardware unit is configured to point to the
* new command queue.
*
* @queue_id: Hardware command queue to initialize.
* @max_depth: Maximum outstanding commands that can be queued.
* @fpa_pool: FPA pool the command queues should come from.
* @pool_size: Size of each buffer in the FPA pool (bytes)
*
* Returns CVMX_CMD_QUEUE_SUCCESS or a failure code
*/
cvmx_cmd_queue_result_t cvmx_cmd_queue_initialize(cvmx_cmd_queue_id_t queue_id,
int max_depth, int fpa_pool,
int pool_size)
{
__cvmx_cmd_queue_state_t *qstate;
cvmx_cmd_queue_result_t result = __cvmx_cmd_queue_init_state_ptr();
if (result != CVMX_CMD_QUEUE_SUCCESS)
return result;
qstate = __cvmx_cmd_queue_get_state(queue_id);
if (qstate == NULL)
return CVMX_CMD_QUEUE_INVALID_PARAM;
/*
* We artificially limit max_depth to 1<<20 words. It is an
* arbitrary limit.
*/
if (CVMX_CMD_QUEUE_ENABLE_MAX_DEPTH) {
if ((max_depth < 0) || (max_depth > 1 << 20))
return CVMX_CMD_QUEUE_INVALID_PARAM;
} else if (max_depth != 0)
return CVMX_CMD_QUEUE_INVALID_PARAM;
if ((fpa_pool < 0) || (fpa_pool > 7))
return CVMX_CMD_QUEUE_INVALID_PARAM;
if ((pool_size < 128) || (pool_size > 65536))
return CVMX_CMD_QUEUE_INVALID_PARAM;
/* See if someone else has already initialized the queue */
if (qstate->base_ptr_div128) {
if (max_depth != (int)qstate->max_depth) {
cvmx_dprintf("ERROR: cvmx_cmd_queue_initialize: "
"Queue already initialized with different "
"max_depth (%d).\n",
(int)qstate->max_depth);
return CVMX_CMD_QUEUE_INVALID_PARAM;
}
if (fpa_pool != qstate->fpa_pool) {
cvmx_dprintf("ERROR: cvmx_cmd_queue_initialize: "
"Queue already initialized with different "
"FPA pool (%u).\n",
qstate->fpa_pool);
return CVMX_CMD_QUEUE_INVALID_PARAM;
}
if ((pool_size >> 3) - 1 != qstate->pool_size_m1) {
cvmx_dprintf("ERROR: cvmx_cmd_queue_initialize: "
"Queue already initialized with different "
"FPA pool size (%u).\n",
(qstate->pool_size_m1 + 1) << 3);
return CVMX_CMD_QUEUE_INVALID_PARAM;
}
CVMX_SYNCWS;
return CVMX_CMD_QUEUE_ALREADY_SETUP;
} else {
union cvmx_fpa_ctl_status status;
void *buffer;
status.u64 = cvmx_read_csr(CVMX_FPA_CTL_STATUS);
if (!status.s.enb) {
cvmx_dprintf("ERROR: cvmx_cmd_queue_initialize: "
"FPA is not enabled.\n");
return CVMX_CMD_QUEUE_NO_MEMORY;
}
buffer = cvmx_fpa_alloc(fpa_pool);
if (buffer == NULL) {
cvmx_dprintf("ERROR: cvmx_cmd_queue_initialize: "
"Unable to allocate initial buffer.\n");
return CVMX_CMD_QUEUE_NO_MEMORY;
}
memset(qstate, 0, sizeof(*qstate));
qstate->max_depth = max_depth;
qstate->fpa_pool = fpa_pool;
qstate->pool_size_m1 = (pool_size >> 3) - 1;
qstate->base_ptr_div128 = cvmx_ptr_to_phys(buffer) / 128;
/*
* We zeroed the now serving field so we need to also
* zero the ticket.
*/
__cvmx_cmd_queue_state_ptr->
ticket[__cvmx_cmd_queue_get_index(queue_id)] = 0;
CVMX_SYNCWS;
return CVMX_CMD_QUEUE_SUCCESS;
}
}
/**
* Shutdown a queue a free it's command buffers to the FPA. The
* hardware connected to the queue must be stopped before this
* function is called.
*
* @queue_id: Queue to shutdown
*
* Returns CVMX_CMD_QUEUE_SUCCESS or a failure code
*/
cvmx_cmd_queue_result_t cvmx_cmd_queue_shutdown(cvmx_cmd_queue_id_t queue_id)
{
__cvmx_cmd_queue_state_t *qptr = __cvmx_cmd_queue_get_state(queue_id);
if (qptr == NULL) {
cvmx_dprintf("ERROR: cvmx_cmd_queue_shutdown: Unable to "
"get queue information.\n");
return CVMX_CMD_QUEUE_INVALID_PARAM;
}
if (cvmx_cmd_queue_length(queue_id) > 0) {
cvmx_dprintf("ERROR: cvmx_cmd_queue_shutdown: Queue still "
"has data in it.\n");
return CVMX_CMD_QUEUE_FULL;
}
__cvmx_cmd_queue_lock(queue_id, qptr);
if (qptr->base_ptr_div128) {
cvmx_fpa_free(cvmx_phys_to_ptr
((uint64_t) qptr->base_ptr_div128 << 7),
qptr->fpa_pool, 0);
qptr->base_ptr_div128 = 0;
}
__cvmx_cmd_queue_unlock(qptr);
return CVMX_CMD_QUEUE_SUCCESS;
}
/**
* Return the number of command words pending in the queue. This
* function may be relatively slow for some hardware units.
*
* @queue_id: Hardware command queue to query
*
* Returns Number of outstanding commands
*/
int cvmx_cmd_queue_length(cvmx_cmd_queue_id_t queue_id)
{
if (CVMX_ENABLE_PARAMETER_CHECKING) {
if (__cvmx_cmd_queue_get_state(queue_id) == NULL)
return CVMX_CMD_QUEUE_INVALID_PARAM;
}
/*
* The cast is here so gcc with check that all values in the
* cvmx_cmd_queue_id_t enumeration are here.
*/
switch ((cvmx_cmd_queue_id_t) (queue_id & 0xff0000)) {
case CVMX_CMD_QUEUE_PKO_BASE:
/*
* FIXME: Need atomic lock on
* CVMX_PKO_REG_READ_IDX. Right now we are normally
* called with the queue lock, so that is a SLIGHT
* amount of protection.
*/
cvmx_write_csr(CVMX_PKO_REG_READ_IDX, queue_id & 0xffff);
if (OCTEON_IS_MODEL(OCTEON_CN3XXX)) {
union cvmx_pko_mem_debug9 debug9;
debug9.u64 = cvmx_read_csr(CVMX_PKO_MEM_DEBUG9);
return debug9.cn38xx.doorbell;
} else {
union cvmx_pko_mem_debug8 debug8;
debug8.u64 = cvmx_read_csr(CVMX_PKO_MEM_DEBUG8);
return debug8.cn58xx.doorbell;
}
case CVMX_CMD_QUEUE_ZIP:
case CVMX_CMD_QUEUE_DFA:
case CVMX_CMD_QUEUE_RAID:
/* FIXME: Implement other lengths */
return 0;
case CVMX_CMD_QUEUE_DMA_BASE:
{
union cvmx_npei_dmax_counts dmax_counts;
dmax_counts.u64 =
cvmx_read_csr(CVMX_PEXP_NPEI_DMAX_COUNTS
(queue_id & 0x7));
return dmax_counts.s.dbell;
}
case CVMX_CMD_QUEUE_END:
return CVMX_CMD_QUEUE_INVALID_PARAM;
}
return CVMX_CMD_QUEUE_INVALID_PARAM;
}
/**
* Return the command buffer to be written to. The purpose of this
* function is to allow CVMX routine access t othe low level buffer
* for initial hardware setup. User applications should not call this
* function directly.
*
* @queue_id: Command queue to query
*
* Returns Command buffer or NULL on failure
*/
void *cvmx_cmd_queue_buffer(cvmx_cmd_queue_id_t queue_id)
{
__cvmx_cmd_queue_state_t *qptr = __cvmx_cmd_queue_get_state(queue_id);
if (qptr && qptr->base_ptr_div128)
return cvmx_phys_to_ptr((uint64_t) qptr->base_ptr_div128 << 7);
else
return NULL;
}

View File

@@ -0,0 +1,711 @@
/***********************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;
}
/* Some unknown board. Somebody forgot to update this function... */
cvmx_dprintf
("cvmx_helper_board_get_mii_address: Unknown board type %d\n",
cvmx_sysinfo_get()->board_type);
return -1;
}
/**
* This function is the board specific method of determining an
* ethernet ports link speed. Most Octeon boards have Marvell PHYs
* and are handled by the fall through case. This function must be
* updated for boards that don't have the normal Marvell PHYs.
*
* This function must be modified for every new Octeon board.
* Internally it uses switch statements based on the cvmx_sysinfo
* data to determine board types and revisions. It relies on the
* fact that every Octeon board receives a unique board type
* enumeration from the bootloader.
*
* @ipd_port: IPD input port associated with the port we want to get link
* status for.
*
* Returns The ports link status. If the link isn't fully resolved, this must
* return zero.
*/
cvmx_helper_link_info_t __cvmx_helper_board_link_get(int ipd_port)
{
cvmx_helper_link_info_t result;
int phy_addr;
int is_broadcom_phy = 0;
/* Give the user a chance to override the processing of this function */
if (cvmx_override_board_link_get)
return cvmx_override_board_link_get(ipd_port);
/* Unless we fix it later, all links are defaulted to down */
result.u64 = 0;
/*
* This switch statement should handle all ports that either don't use
* Marvell PHYS, or don't support in-band status.
*/
switch (cvmx_sysinfo_get()->board_type) {
case CVMX_BOARD_TYPE_SIM:
/* The simulator gives you a simulated 1Gbps full duplex link */
result.s.link_up = 1;
result.s.full_duplex = 1;
result.s.speed = 1000;
return result;
case CVMX_BOARD_TYPE_EBH3100:
case CVMX_BOARD_TYPE_CN3010_EVB_HS5:
case CVMX_BOARD_TYPE_CN3005_EVB_HS5:
case CVMX_BOARD_TYPE_CN3020_EVB_HS5:
/* Port 1 on these boards is always Gigabit */
if (ipd_port == 1) {
result.s.link_up = 1;
result.s.full_duplex = 1;
result.s.speed = 1000;
return result;
}
/* Fall through to the generic code below */
break;
case CVMX_BOARD_TYPE_CUST_NB5:
/* Port 1 on these boards is always Gigabit */
if (ipd_port == 1) {
result.s.link_up = 1;
result.s.full_duplex = 1;
result.s.speed = 1000;
return result;
} else /* The other port uses a broadcom PHY */
is_broadcom_phy = 1;
break;
case CVMX_BOARD_TYPE_BBGW_REF:
/* Port 1 on these boards is always Gigabit */
if (ipd_port == 2) {
/* Port 2 is not hooked up */
result.u64 = 0;
return result;
} else {
/* Ports 0 and 1 connect to the switch */
result.s.link_up = 1;
result.s.full_duplex = 1;
result.s.speed = 1000;
return result;
}
break;
}
phy_addr = cvmx_helper_board_get_mii_address(ipd_port);
if (phy_addr != -1) {
if (is_broadcom_phy) {
/*
* Below we are going to read SMI/MDIO
* register 0x19 which works on Broadcom
* parts
*/
int phy_status =
cvmx_mdio_read(phy_addr >> 8, phy_addr & 0xff,
0x19);
switch ((phy_status >> 8) & 0x7) {
case 0:
result.u64 = 0;
break;
case 1:
result.s.link_up = 1;
result.s.full_duplex = 0;
result.s.speed = 10;
break;
case 2:
result.s.link_up = 1;
result.s.full_duplex = 1;
result.s.speed = 10;
break;
case 3:
result.s.link_up = 1;
result.s.full_duplex = 0;
result.s.speed = 100;
break;
case 4:
result.s.link_up = 1;
result.s.full_duplex = 1;
result.s.speed = 100;
break;
case 5:
result.s.link_up = 1;
result.s.full_duplex = 1;
result.s.speed = 100;
break;
case 6:
result.s.link_up = 1;
result.s.full_duplex = 0;
result.s.speed = 1000;
break;
case 7:
result.s.link_up = 1;
result.s.full_duplex = 1;
result.s.speed = 1000;
break;
}
} else {
/*
* This code assumes we are using a Marvell
* Gigabit PHY. All the speed information can
* be read from register 17 in one
* go. Somebody using a different PHY will
* need to handle it above in the board
* specific area.
*/
int phy_status =
cvmx_mdio_read(phy_addr >> 8, phy_addr & 0xff, 17);
/*
* If the resolve bit 11 isn't set, see if
* autoneg is turned off (bit 12, reg 0). The
* resolve bit doesn't get set properly when
* autoneg is off, so force it.
*/
if ((phy_status & (1 << 11)) == 0) {
int auto_status =
cvmx_mdio_read(phy_addr >> 8,
phy_addr & 0xff, 0);
if ((auto_status & (1 << 12)) == 0)
phy_status |= 1 << 11;
}
/*
* Only return a link if the PHY has finished
* auto negotiation and set the resolved bit
* (bit 11)
*/
if (phy_status & (1 << 11)) {
result.s.link_up = 1;
result.s.full_duplex = ((phy_status >> 13) & 1);
switch ((phy_status >> 14) & 3) {
case 0: /* 10 Mbps */
result.s.speed = 10;
break;
case 1: /* 100 Mbps */
result.s.speed = 100;
break;
case 2: /* 1 Gbps */
result.s.speed = 1000;
break;
case 3: /* Illegal */
result.u64 = 0;
break;
}
}
}
} else if (OCTEON_IS_MODEL(OCTEON_CN3XXX)
|| OCTEON_IS_MODEL(OCTEON_CN58XX)
|| OCTEON_IS_MODEL(OCTEON_CN50XX)) {
/*
* We don't have a PHY address, so attempt to use
* in-band status. It is really important that boards
* not supporting in-band status never get
* here. Reading broken in-band status tends to do bad
* things
*/
union cvmx_gmxx_rxx_rx_inbnd inband_status;
int interface = cvmx_helper_get_interface_num(ipd_port);
int index = cvmx_helper_get_interface_index_num(ipd_port);
inband_status.u64 =
cvmx_read_csr(CVMX_GMXX_RXX_RX_INBND(index, interface));
result.s.link_up = inband_status.s.status;
result.s.full_duplex = inband_status.s.duplex;
switch (inband_status.s.speed) {
case 0: /* 10 Mbps */
result.s.speed = 10;
break;
case 1: /* 100 Mbps */
result.s.speed = 100;
break;
case 2: /* 1 Gbps */
result.s.speed = 1000;
break;
case 3: /* Illegal */
result.u64 = 0;
break;
}
} else {
/*
* We don't have a PHY address and we don't have
* in-band status. There is no way to determine the
* link speed. Return down assuming this port isn't
* wired
*/
result.u64 = 0;
}
/* If link is down, return all fields as zero. */
if (!result.s.link_up)
result.u64 = 0;
return result;
}
/**
* This function as a board specific method of changing the PHY
* speed, duplex, and auto-negotiation. This programs the PHY and
* not Octeon. This can be used to force Octeon's links to
* specific settings.
*
* @phy_addr: The address of the PHY to program
* @enable_autoneg:
* Non zero if you want to enable auto-negotiation.
* @link_info: Link speed to program. If the speed is zero and auto-negotiation
* is enabled, all possible negotiation speeds are advertised.
*
* Returns Zero on success, negative on failure
*/
int cvmx_helper_board_link_set_phy(int phy_addr,
cvmx_helper_board_set_phy_link_flags_types_t
link_flags,
cvmx_helper_link_info_t link_info)
{
/* Set the flow control settings based on link_flags */
if ((link_flags & set_phy_link_flags_flow_control_mask) !=
set_phy_link_flags_flow_control_dont_touch) {
cvmx_mdio_phy_reg_autoneg_adver_t reg_autoneg_adver;
reg_autoneg_adver.u16 =
cvmx_mdio_read(phy_addr >> 8, phy_addr & 0xff,
CVMX_MDIO_PHY_REG_AUTONEG_ADVER);
reg_autoneg_adver.s.asymmetric_pause =
(link_flags & set_phy_link_flags_flow_control_mask) ==
set_phy_link_flags_flow_control_enable;
reg_autoneg_adver.s.pause =
(link_flags & set_phy_link_flags_flow_control_mask) ==
set_phy_link_flags_flow_control_enable;
cvmx_mdio_write(phy_addr >> 8, phy_addr & 0xff,
CVMX_MDIO_PHY_REG_AUTONEG_ADVER,
reg_autoneg_adver.u16);
}
/* If speed isn't set and autoneg is on advertise all supported modes */
if ((link_flags & set_phy_link_flags_autoneg)
&& (link_info.s.speed == 0)) {
cvmx_mdio_phy_reg_control_t reg_control;
cvmx_mdio_phy_reg_status_t reg_status;
cvmx_mdio_phy_reg_autoneg_adver_t reg_autoneg_adver;
cvmx_mdio_phy_reg_extended_status_t reg_extended_status;
cvmx_mdio_phy_reg_control_1000_t reg_control_1000;
reg_status.u16 =
cvmx_mdio_read(phy_addr >> 8, phy_addr & 0xff,
CVMX_MDIO_PHY_REG_STATUS);
reg_autoneg_adver.u16 =
cvmx_mdio_read(phy_addr >> 8, phy_addr & 0xff,
CVMX_MDIO_PHY_REG_AUTONEG_ADVER);
reg_autoneg_adver.s.advert_100base_t4 =
reg_status.s.capable_100base_t4;
reg_autoneg_adver.s.advert_10base_tx_full =
reg_status.s.capable_10_full;
reg_autoneg_adver.s.advert_10base_tx_half =
reg_status.s.capable_10_half;
reg_autoneg_adver.s.advert_100base_tx_full =
reg_status.s.capable_100base_x_full;
reg_autoneg_adver.s.advert_100base_tx_half =
reg_status.s.capable_100base_x_half;
cvmx_mdio_write(phy_addr >> 8, phy_addr & 0xff,
CVMX_MDIO_PHY_REG_AUTONEG_ADVER,
reg_autoneg_adver.u16);
if (reg_status.s.capable_extended_status) {
reg_extended_status.u16 =
cvmx_mdio_read(phy_addr >> 8, phy_addr & 0xff,
CVMX_MDIO_PHY_REG_EXTENDED_STATUS);
reg_control_1000.u16 =
cvmx_mdio_read(phy_addr >> 8, phy_addr & 0xff,
CVMX_MDIO_PHY_REG_CONTROL_1000);
reg_control_1000.s.advert_1000base_t_full =
reg_extended_status.s.capable_1000base_t_full;
reg_control_1000.s.advert_1000base_t_half =
reg_extended_status.s.capable_1000base_t_half;
cvmx_mdio_write(phy_addr >> 8, phy_addr & 0xff,
CVMX_MDIO_PHY_REG_CONTROL_1000,
reg_control_1000.u16);
}
reg_control.u16 =
cvmx_mdio_read(phy_addr >> 8, phy_addr & 0xff,
CVMX_MDIO_PHY_REG_CONTROL);
reg_control.s.autoneg_enable = 1;
reg_control.s.restart_autoneg = 1;
cvmx_mdio_write(phy_addr >> 8, phy_addr & 0xff,
CVMX_MDIO_PHY_REG_CONTROL, reg_control.u16);
} else if ((link_flags & set_phy_link_flags_autoneg)) {
cvmx_mdio_phy_reg_control_t reg_control;
cvmx_mdio_phy_reg_status_t reg_status;
cvmx_mdio_phy_reg_autoneg_adver_t reg_autoneg_adver;
cvmx_mdio_phy_reg_control_1000_t reg_control_1000;
reg_status.u16 =
cvmx_mdio_read(phy_addr >> 8, phy_addr & 0xff,
CVMX_MDIO_PHY_REG_STATUS);
reg_autoneg_adver.u16 =
cvmx_mdio_read(phy_addr >> 8, phy_addr & 0xff,
CVMX_MDIO_PHY_REG_AUTONEG_ADVER);
reg_autoneg_adver.s.advert_100base_t4 = 0;
reg_autoneg_adver.s.advert_10base_tx_full = 0;
reg_autoneg_adver.s.advert_10base_tx_half = 0;
reg_autoneg_adver.s.advert_100base_tx_full = 0;
reg_autoneg_adver.s.advert_100base_tx_half = 0;
if (reg_status.s.capable_extended_status) {
reg_control_1000.u16 =
cvmx_mdio_read(phy_addr >> 8, phy_addr & 0xff,
CVMX_MDIO_PHY_REG_CONTROL_1000);
reg_control_1000.s.advert_1000base_t_full = 0;
reg_control_1000.s.advert_1000base_t_half = 0;
}
switch (link_info.s.speed) {
case 10:
reg_autoneg_adver.s.advert_10base_tx_full =
link_info.s.full_duplex;
reg_autoneg_adver.s.advert_10base_tx_half =
!link_info.s.full_duplex;
break;
case 100:
reg_autoneg_adver.s.advert_100base_tx_full =
link_info.s.full_duplex;
reg_autoneg_adver.s.advert_100base_tx_half =
!link_info.s.full_duplex;
break;
case 1000:
reg_control_1000.s.advert_1000base_t_full =
link_info.s.full_duplex;
reg_control_1000.s.advert_1000base_t_half =
!link_info.s.full_duplex;
break;
}
cvmx_mdio_write(phy_addr >> 8, phy_addr & 0xff,
CVMX_MDIO_PHY_REG_AUTONEG_ADVER,
reg_autoneg_adver.u16);
if (reg_status.s.capable_extended_status)
cvmx_mdio_write(phy_addr >> 8, phy_addr & 0xff,
CVMX_MDIO_PHY_REG_CONTROL_1000,
reg_control_1000.u16);
reg_control.u16 =
cvmx_mdio_read(phy_addr >> 8, phy_addr & 0xff,
CVMX_MDIO_PHY_REG_CONTROL);
reg_control.s.autoneg_enable = 1;
reg_control.s.restart_autoneg = 1;
cvmx_mdio_write(phy_addr >> 8, phy_addr & 0xff,
CVMX_MDIO_PHY_REG_CONTROL, reg_control.u16);
} else {
cvmx_mdio_phy_reg_control_t reg_control;
reg_control.u16 =
cvmx_mdio_read(phy_addr >> 8, phy_addr & 0xff,
CVMX_MDIO_PHY_REG_CONTROL);
reg_control.s.autoneg_enable = 0;
reg_control.s.restart_autoneg = 1;
reg_control.s.duplex = link_info.s.full_duplex;
if (link_info.s.speed == 1000) {
reg_control.s.speed_msb = 1;
reg_control.s.speed_lsb = 0;
} else if (link_info.s.speed == 100) {
reg_control.s.speed_msb = 0;
reg_control.s.speed_lsb = 1;
} else if (link_info.s.speed == 10) {
reg_control.s.speed_msb = 0;
reg_control.s.speed_lsb = 0;
}
cvmx_mdio_write(phy_addr >> 8, phy_addr & 0xff,
CVMX_MDIO_PHY_REG_CONTROL, reg_control.u16);
}
return 0;
}
/**
* This function is called by cvmx_helper_interface_probe() after it
* determines the number of ports Octeon can support on a specific
* interface. This function is the per board location to override
* this value. It is called with the number of ports Octeon might
* support and should return the number of actual ports on the
* board.
*
* This function must be modifed for every new Octeon board.
* Internally it uses switch statements based on the cvmx_sysinfo
* data to determine board types and revisions. It relys on the
* fact that every Octeon board receives a unique board type
* enumeration from the bootloader.
*
* @interface: Interface to probe
* @supported_ports:
* Number of ports Octeon supports.
*
* Returns Number of ports the actual board supports. Many times this will
* simple be "support_ports".
*/
int __cvmx_helper_board_interface_probe(int interface, int supported_ports)
{
switch (cvmx_sysinfo_get()->board_type) {
case CVMX_BOARD_TYPE_CN3005_EVB_HS5:
if (interface == 0)
return 2;
break;
case CVMX_BOARD_TYPE_BBGW_REF:
if (interface == 0)
return 2;
break;
case CVMX_BOARD_TYPE_NIC_XLE_4G:
if (interface == 0)
return 0;
break;
/* The 2nd interface on the EBH5600 is connected to the Marvel switch,
which we don't support. Disable ports connected to it */
case CVMX_BOARD_TYPE_EBH5600:
if (interface == 1)
return 0;
break;
}
return supported_ports;
}
/**
* Enable packet input/output from the hardware. This function is
* called after by cvmx_helper_packet_hardware_enable() to
* perform board specific initialization. For most boards
* nothing is needed.
*
* @interface: Interface to enable
*
* Returns Zero on success, negative on failure
*/
int __cvmx_helper_board_hardware_enable(int interface)
{
if (cvmx_sysinfo_get()->board_type == CVMX_BOARD_TYPE_CN3005_EVB_HS5) {
if (interface == 0) {
/* Different config for switch port */
cvmx_write_csr(CVMX_ASXX_TX_CLK_SETX(1, interface), 0);
cvmx_write_csr(CVMX_ASXX_RX_CLK_SETX(1, interface), 0);
/*
* Boards with gigabit WAN ports need a
* different setting that is compatible with
* 100 Mbit settings
*/
cvmx_write_csr(CVMX_ASXX_TX_CLK_SETX(0, interface),
0xc);
cvmx_write_csr(CVMX_ASXX_RX_CLK_SETX(0, interface),
0xc);
}
} else if (cvmx_sysinfo_get()->board_type ==
CVMX_BOARD_TYPE_CN3010_EVB_HS5) {
/*
* Broadcom PHYs require differnet ASX
* clocks. Unfortunately many boards don't define a
* new board Id and simply mangle the
* CN3010_EVB_HS5
*/
if (interface == 0) {
/*
* Some boards use a hacked up bootloader that
* identifies them as CN3010_EVB_HS5
* evaluation boards. This leads to all kinds
* of configuration problems. Detect one
* case, and print warning, while trying to do
* the right thing.
*/
int phy_addr = cvmx_helper_board_get_mii_address(0);
if (phy_addr != -1) {
int phy_identifier =
cvmx_mdio_read(phy_addr >> 8,
phy_addr & 0xff, 0x2);
/* Is it a Broadcom PHY? */
if (phy_identifier == 0x0143) {
cvmx_dprintf("\n");
cvmx_dprintf("ERROR:\n");
cvmx_dprintf
("ERROR: Board type is CVMX_BOARD_TYPE_CN3010_EVB_HS5, but Broadcom PHY found.\n");
cvmx_dprintf
("ERROR: The board type is mis-configured, and software malfunctions are likely.\n");
cvmx_dprintf
("ERROR: All boards require a unique board type to identify them.\n");
cvmx_dprintf("ERROR:\n");
cvmx_dprintf("\n");
cvmx_wait(1000000000);
cvmx_write_csr(CVMX_ASXX_RX_CLK_SETX
(0, interface), 5);
cvmx_write_csr(CVMX_ASXX_TX_CLK_SETX
(0, interface), 5);
}
}
}
}
return 0;
}

View 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);

View 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);
}

View 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;
}

View 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;
}

View 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;
}

View File

@@ -0,0 +1,554 @@
/***********************license start***************
* Author: Cavium Networks
*
* Contact: support@caviumnetworks.com
* This file is part of the OCTEON SDK
*
* Copyright (c) 2003-2008 Cavium Networks
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License, Version 2, as
* published by the Free Software Foundation.
*
* This file is distributed in the hope that it will be useful, but
* AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or
* NONINFRINGEMENT. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License
* along with this file; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
* or visit http://www.gnu.org/licenses/.
*
* This file may also be available under a different license from Cavium.
* Contact Cavium Networks for more information
***********************license end**************************************/
/*
* Functions for SGMII initialization, configuration,
* and monitoring.
*/
#include <asm/octeon/octeon.h>
#include <asm/octeon/cvmx-config.h>
#include <asm/octeon/cvmx-mdio.h>
#include <asm/octeon/cvmx-helper.h>
#include <asm/octeon/cvmx-helper-board.h>
#include <asm/octeon/cvmx-gmxx-defs.h>
#include <asm/octeon/cvmx-pcsx-defs.h>
void __cvmx_interrupt_gmxx_enable(int interface);
void __cvmx_interrupt_pcsx_intx_en_reg_enable(int index, int block);
void __cvmx_interrupt_pcsxx_int_en_reg_enable(int index);
/**
* Perform initialization required only once for an SGMII port.
*
* @interface: Interface to init
* @index: Index of prot on the interface
*
* Returns Zero on success, negative on failure
*/
static int __cvmx_helper_sgmii_hardware_init_one_time(int interface, int index)
{
const uint64_t clock_mhz = cvmx_sysinfo_get()->cpu_clock_hz / 1000000;
union cvmx_pcsx_miscx_ctl_reg pcs_misc_ctl_reg;
union cvmx_pcsx_linkx_timer_count_reg pcsx_linkx_timer_count_reg;
union cvmx_gmxx_prtx_cfg gmxx_prtx_cfg;
/* Disable GMX */
gmxx_prtx_cfg.u64 = cvmx_read_csr(CVMX_GMXX_PRTX_CFG(index, interface));
gmxx_prtx_cfg.s.en = 0;
cvmx_write_csr(CVMX_GMXX_PRTX_CFG(index, interface), gmxx_prtx_cfg.u64);
/*
* Write PCS*_LINK*_TIMER_COUNT_REG[COUNT] with the
* appropriate value. 1000BASE-X specifies a 10ms
* interval. SGMII specifies a 1.6ms interval.
*/
pcs_misc_ctl_reg.u64 =
cvmx_read_csr(CVMX_PCSX_MISCX_CTL_REG(index, interface));
pcsx_linkx_timer_count_reg.u64 =
cvmx_read_csr(CVMX_PCSX_LINKX_TIMER_COUNT_REG(index, interface));
if (pcs_misc_ctl_reg.s.mode) {
/* 1000BASE-X */
pcsx_linkx_timer_count_reg.s.count =
(10000ull * clock_mhz) >> 10;
} else {
/* SGMII */
pcsx_linkx_timer_count_reg.s.count =
(1600ull * clock_mhz) >> 10;
}
cvmx_write_csr(CVMX_PCSX_LINKX_TIMER_COUNT_REG(index, interface),
pcsx_linkx_timer_count_reg.u64);
/*
* Write the advertisement register to be used as the
* tx_Config_Reg<D15:D0> of the autonegotiation. In
* 1000BASE-X mode, tx_Config_Reg<D15:D0> is PCS*_AN*_ADV_REG.
* In SGMII PHY mode, tx_Config_Reg<D15:D0> is
* PCS*_SGM*_AN_ADV_REG. In SGMII MAC mode,
* tx_Config_Reg<D15:D0> is the fixed value 0x4001, so this
* step can be skipped.
*/
if (pcs_misc_ctl_reg.s.mode) {
/* 1000BASE-X */
union cvmx_pcsx_anx_adv_reg pcsx_anx_adv_reg;
pcsx_anx_adv_reg.u64 =
cvmx_read_csr(CVMX_PCSX_ANX_ADV_REG(index, interface));
pcsx_anx_adv_reg.s.rem_flt = 0;
pcsx_anx_adv_reg.s.pause = 3;
pcsx_anx_adv_reg.s.hfd = 1;
pcsx_anx_adv_reg.s.fd = 1;
cvmx_write_csr(CVMX_PCSX_ANX_ADV_REG(index, interface),
pcsx_anx_adv_reg.u64);
} else {
union cvmx_pcsx_miscx_ctl_reg pcsx_miscx_ctl_reg;
pcsx_miscx_ctl_reg.u64 =
cvmx_read_csr(CVMX_PCSX_MISCX_CTL_REG(index, interface));
if (pcsx_miscx_ctl_reg.s.mac_phy) {
/* PHY Mode */
union cvmx_pcsx_sgmx_an_adv_reg pcsx_sgmx_an_adv_reg;
pcsx_sgmx_an_adv_reg.u64 =
cvmx_read_csr(CVMX_PCSX_SGMX_AN_ADV_REG
(index, interface));
pcsx_sgmx_an_adv_reg.s.link = 1;
pcsx_sgmx_an_adv_reg.s.dup = 1;
pcsx_sgmx_an_adv_reg.s.speed = 2;
cvmx_write_csr(CVMX_PCSX_SGMX_AN_ADV_REG
(index, interface),
pcsx_sgmx_an_adv_reg.u64);
} else {
/* MAC Mode - Nothing to do */
}
}
return 0;
}
/**
* Initialize the SERTES link for the first time or after a loss
* of link.
*
* @interface: Interface to init
* @index: Index of prot on the interface
*
* Returns Zero on success, negative on failure
*/
static int __cvmx_helper_sgmii_hardware_init_link(int interface, int index)
{
union cvmx_pcsx_mrx_control_reg control_reg;
/*
* Take PCS through a reset sequence.
* PCS*_MR*_CONTROL_REG[PWR_DN] should be cleared to zero.
* Write PCS*_MR*_CONTROL_REG[RESET]=1 (while not changing the
* value of the other PCS*_MR*_CONTROL_REG bits). Read
* PCS*_MR*_CONTROL_REG[RESET] until it changes value to
* zero.
*/
control_reg.u64 =
cvmx_read_csr(CVMX_PCSX_MRX_CONTROL_REG(index, interface));
if (cvmx_sysinfo_get()->board_type != CVMX_BOARD_TYPE_SIM) {
control_reg.s.reset = 1;
cvmx_write_csr(CVMX_PCSX_MRX_CONTROL_REG(index, interface),
control_reg.u64);
if (CVMX_WAIT_FOR_FIELD64
(CVMX_PCSX_MRX_CONTROL_REG(index, interface),
union cvmx_pcsx_mrx_control_reg, reset, ==, 0, 10000)) {
cvmx_dprintf("SGMII%d: Timeout waiting for port %d "
"to finish reset\n",
interface, index);
return -1;
}
}
/*
* Write PCS*_MR*_CONTROL_REG[RST_AN]=1 to ensure a fresh
* sgmii negotiation starts.
*/
control_reg.s.rst_an = 1;
control_reg.s.an_en = 1;
control_reg.s.pwr_dn = 0;
cvmx_write_csr(CVMX_PCSX_MRX_CONTROL_REG(index, interface),
control_reg.u64);
/*
* Wait for PCS*_MR*_STATUS_REG[AN_CPT] to be set, indicating
* that sgmii autonegotiation is complete. In MAC mode this
* isn't an ethernet link, but a link between Octeon and the
* PHY.
*/
if ((cvmx_sysinfo_get()->board_type != CVMX_BOARD_TYPE_SIM) &&
CVMX_WAIT_FOR_FIELD64(CVMX_PCSX_MRX_STATUS_REG(index, interface),
union cvmx_pcsx_mrx_status_reg, an_cpt, ==, 1,
10000)) {
/* cvmx_dprintf("SGMII%d: Port %d link timeout\n", interface, index); */
return -1;
}
return 0;
}
/**
* Configure an SGMII link to the specified speed after the SERTES
* link is up.
*
* @interface: Interface to init
* @index: Index of prot on the interface
* @link_info: Link state to configure
*
* Returns Zero on success, negative on failure
*/
static int __cvmx_helper_sgmii_hardware_init_link_speed(int interface,
int index,
cvmx_helper_link_info_t
link_info)
{
int is_enabled;
union cvmx_gmxx_prtx_cfg gmxx_prtx_cfg;
union cvmx_pcsx_miscx_ctl_reg pcsx_miscx_ctl_reg;
/* Disable GMX before we make any changes. Remember the enable state */
gmxx_prtx_cfg.u64 = cvmx_read_csr(CVMX_GMXX_PRTX_CFG(index, interface));
is_enabled = gmxx_prtx_cfg.s.en;
gmxx_prtx_cfg.s.en = 0;
cvmx_write_csr(CVMX_GMXX_PRTX_CFG(index, interface), gmxx_prtx_cfg.u64);
/* Wait for GMX to be idle */
if (CVMX_WAIT_FOR_FIELD64
(CVMX_GMXX_PRTX_CFG(index, interface), union cvmx_gmxx_prtx_cfg,
rx_idle, ==, 1, 10000)
|| CVMX_WAIT_FOR_FIELD64(CVMX_GMXX_PRTX_CFG(index, interface),
union cvmx_gmxx_prtx_cfg, tx_idle, ==, 1,
10000)) {
cvmx_dprintf
("SGMII%d: Timeout waiting for port %d to be idle\n",
interface, index);
return -1;
}
/* Read GMX CFG again to make sure the disable completed */
gmxx_prtx_cfg.u64 = cvmx_read_csr(CVMX_GMXX_PRTX_CFG(index, interface));
/*
* Get the misc control for PCS. We will need to set the
* duplication amount.
*/
pcsx_miscx_ctl_reg.u64 =
cvmx_read_csr(CVMX_PCSX_MISCX_CTL_REG(index, interface));
/*
* Use GMXENO to force the link down if the status we get says
* it should be down.
*/
pcsx_miscx_ctl_reg.s.gmxeno = !link_info.s.link_up;
/* Only change the duplex setting if the link is up */
if (link_info.s.link_up)
gmxx_prtx_cfg.s.duplex = link_info.s.full_duplex;
/* Do speed based setting for GMX */
switch (link_info.s.speed) {
case 10:
gmxx_prtx_cfg.s.speed = 0;
gmxx_prtx_cfg.s.speed_msb = 1;
gmxx_prtx_cfg.s.slottime = 0;
/* Setting from GMX-603 */
pcsx_miscx_ctl_reg.s.samp_pt = 25;
cvmx_write_csr(CVMX_GMXX_TXX_SLOT(index, interface), 64);
cvmx_write_csr(CVMX_GMXX_TXX_BURST(index, interface), 0);
break;
case 100:
gmxx_prtx_cfg.s.speed = 0;
gmxx_prtx_cfg.s.speed_msb = 0;
gmxx_prtx_cfg.s.slottime = 0;
pcsx_miscx_ctl_reg.s.samp_pt = 0x5;
cvmx_write_csr(CVMX_GMXX_TXX_SLOT(index, interface), 64);
cvmx_write_csr(CVMX_GMXX_TXX_BURST(index, interface), 0);
break;
case 1000:
gmxx_prtx_cfg.s.speed = 1;
gmxx_prtx_cfg.s.speed_msb = 0;
gmxx_prtx_cfg.s.slottime = 1;
pcsx_miscx_ctl_reg.s.samp_pt = 1;
cvmx_write_csr(CVMX_GMXX_TXX_SLOT(index, interface), 512);
cvmx_write_csr(CVMX_GMXX_TXX_BURST(index, interface), 8192);
break;
default:
break;
}
/* Write the new misc control for PCS */
cvmx_write_csr(CVMX_PCSX_MISCX_CTL_REG(index, interface),
pcsx_miscx_ctl_reg.u64);
/* Write the new GMX settings with the port still disabled */
cvmx_write_csr(CVMX_GMXX_PRTX_CFG(index, interface), gmxx_prtx_cfg.u64);
/* Read GMX CFG again to make sure the config completed */
gmxx_prtx_cfg.u64 = cvmx_read_csr(CVMX_GMXX_PRTX_CFG(index, interface));
/* Restore the enabled / disabled state */
gmxx_prtx_cfg.s.en = is_enabled;
cvmx_write_csr(CVMX_GMXX_PRTX_CFG(index, interface), gmxx_prtx_cfg.u64);
return 0;
}
/**
* Bring up the SGMII interface to be ready for packet I/O but
* leave I/O disabled using the GMX override. This function
* follows the bringup documented in 10.6.3 of the manual.
*
* @interface: Interface to bringup
* @num_ports: Number of ports on the interface
*
* Returns Zero on success, negative on failure
*/
static int __cvmx_helper_sgmii_hardware_init(int interface, int num_ports)
{
int index;
__cvmx_helper_setup_gmx(interface, num_ports);
for (index = 0; index < num_ports; index++) {
int ipd_port = cvmx_helper_get_ipd_port(interface, index);
__cvmx_helper_sgmii_hardware_init_one_time(interface, index);
__cvmx_helper_sgmii_link_set(ipd_port,
__cvmx_helper_sgmii_link_get
(ipd_port));
}
return 0;
}
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;
}

View 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;
}

View File

@@ -0,0 +1,433 @@
/***********************license start***************
* Author: Cavium Networks
*
* Contact: support@caviumnetworks.com
* This file is part of the OCTEON SDK
*
* Copyright (c) 2003-2008 Cavium Networks
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License, Version 2, as
* published by the Free Software Foundation.
*
* This file is distributed in the hope that it will be useful, but
* AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or
* NONINFRINGEMENT. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License
* along with this file; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
* or visit http://www.gnu.org/licenses/.
*
* This file may also be available under a different license from Cavium.
* Contact Cavium Networks for more information
***********************license end**************************************/
/*
* Small helper utilities.
*/
#include <linux/kernel.h>
#include <asm/octeon/octeon.h>
#include <asm/octeon/cvmx-config.h>
#include <asm/octeon/cvmx-fpa.h>
#include <asm/octeon/cvmx-pip.h>
#include <asm/octeon/cvmx-pko.h>
#include <asm/octeon/cvmx-ipd.h>
#include <asm/octeon/cvmx-spi.h>
#include <asm/octeon/cvmx-helper.h>
#include <asm/octeon/cvmx-helper-util.h>
#include <asm/octeon/cvmx-ipd-defs.h>
/**
* Convert a interface mode into a human readable string
*
* @mode: Mode to convert
*
* Returns String
*/
const char *cvmx_helper_interface_mode_to_string(cvmx_helper_interface_mode_t
mode)
{
switch (mode) {
case CVMX_HELPER_INTERFACE_MODE_DISABLED:
return "DISABLED";
case CVMX_HELPER_INTERFACE_MODE_RGMII:
return "RGMII";
case CVMX_HELPER_INTERFACE_MODE_GMII:
return "GMII";
case CVMX_HELPER_INTERFACE_MODE_SPI:
return "SPI";
case CVMX_HELPER_INTERFACE_MODE_PCIE:
return "PCIE";
case CVMX_HELPER_INTERFACE_MODE_XAUI:
return "XAUI";
case CVMX_HELPER_INTERFACE_MODE_SGMII:
return "SGMII";
case CVMX_HELPER_INTERFACE_MODE_PICMG:
return "PICMG";
case CVMX_HELPER_INTERFACE_MODE_NPI:
return "NPI";
case CVMX_HELPER_INTERFACE_MODE_LOOP:
return "LOOP";
}
return "UNKNOWN";
}
/**
* Debug routine to dump the packet structure to the console
*
* @work: Work queue entry containing the packet to dump
* Returns
*/
int cvmx_helper_dump_packet(cvmx_wqe_t *work)
{
uint64_t count;
uint64_t remaining_bytes;
union cvmx_buf_ptr buffer_ptr;
uint64_t start_of_buffer;
uint8_t *data_address;
uint8_t *end_of_data;
cvmx_dprintf("Packet Length: %u\n", work->len);
cvmx_dprintf(" Input Port: %u\n", work->ipprt);
cvmx_dprintf(" QoS: %u\n", work->qos);
cvmx_dprintf(" Buffers: %u\n", work->word2.s.bufs);
if (work->word2.s.bufs == 0) {
union cvmx_ipd_wqe_fpa_queue wqe_pool;
wqe_pool.u64 = cvmx_read_csr(CVMX_IPD_WQE_FPA_QUEUE);
buffer_ptr.u64 = 0;
buffer_ptr.s.pool = wqe_pool.s.wqe_pool;
buffer_ptr.s.size = 128;
buffer_ptr.s.addr = cvmx_ptr_to_phys(work->packet_data);
if (likely(!work->word2.s.not_IP)) {
union cvmx_pip_ip_offset pip_ip_offset;
pip_ip_offset.u64 = cvmx_read_csr(CVMX_PIP_IP_OFFSET);
buffer_ptr.s.addr +=
(pip_ip_offset.s.offset << 3) -
work->word2.s.ip_offset;
buffer_ptr.s.addr += (work->word2.s.is_v6 ^ 1) << 2;
} else {
/*
* WARNING: This code assumes that the packet
* is not RAW. If it was, we would use
* PIP_GBL_CFG[RAW_SHF] instead of
* PIP_GBL_CFG[NIP_SHF].
*/
union cvmx_pip_gbl_cfg pip_gbl_cfg;
pip_gbl_cfg.u64 = cvmx_read_csr(CVMX_PIP_GBL_CFG);
buffer_ptr.s.addr += pip_gbl_cfg.s.nip_shf;
}
} else
buffer_ptr = work->packet_ptr;
remaining_bytes = work->len;
while (remaining_bytes) {
start_of_buffer =
((buffer_ptr.s.addr >> 7) - buffer_ptr.s.back) << 7;
cvmx_dprintf(" Buffer Start:%llx\n",
(unsigned long long)start_of_buffer);
cvmx_dprintf(" Buffer I : %u\n", buffer_ptr.s.i);
cvmx_dprintf(" Buffer Back: %u\n", buffer_ptr.s.back);
cvmx_dprintf(" Buffer Pool: %u\n", buffer_ptr.s.pool);
cvmx_dprintf(" Buffer Data: %llx\n",
(unsigned long long)buffer_ptr.s.addr);
cvmx_dprintf(" Buffer Size: %u\n", buffer_ptr.s.size);
cvmx_dprintf("\t\t");
data_address = (uint8_t *) cvmx_phys_to_ptr(buffer_ptr.s.addr);
end_of_data = data_address + buffer_ptr.s.size;
count = 0;
while (data_address < end_of_data) {
if (remaining_bytes == 0)
break;
else
remaining_bytes--;
cvmx_dprintf("%02x", (unsigned int)*data_address);
data_address++;
if (remaining_bytes && (count == 7)) {
cvmx_dprintf("\n\t\t");
count = 0;
} else
count++;
}
cvmx_dprintf("\n");
if (remaining_bytes)
buffer_ptr = *(union cvmx_buf_ptr *)
cvmx_phys_to_ptr(buffer_ptr.s.addr - 8);
}
return 0;
}
/**
* Setup Random Early Drop on a specific input queue
*
* @queue: Input queue to setup RED on (0-7)
* @pass_thresh:
* Packets will begin slowly dropping when there are less than
* this many packet buffers free in FPA 0.
* @drop_thresh:
* All 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;
}
/**
* Setup the common GMX settings that determine the number of
* ports. These setting apply to almost all configurations of all
* chips.
*
* @interface: Interface to configure
* @num_ports: Number of ports on the interface
*
* Returns Zero on success, negative on failure
*/
int __cvmx_helper_setup_gmx(int interface, int num_ports)
{
union cvmx_gmxx_tx_prts gmx_tx_prts;
union cvmx_gmxx_rx_prts gmx_rx_prts;
union cvmx_pko_reg_gmx_port_mode pko_mode;
union cvmx_gmxx_txx_thresh gmx_tx_thresh;
int index;
/* Tell GMX the number of TX ports on this interface */
gmx_tx_prts.u64 = cvmx_read_csr(CVMX_GMXX_TX_PRTS(interface));
gmx_tx_prts.s.prts = num_ports;
cvmx_write_csr(CVMX_GMXX_TX_PRTS(interface), gmx_tx_prts.u64);
/* Tell GMX the number of RX ports on this interface. This only
** applies to *GMII and XAUI ports */
if (cvmx_helper_interface_get_mode(interface) ==
CVMX_HELPER_INTERFACE_MODE_RGMII
|| cvmx_helper_interface_get_mode(interface) ==
CVMX_HELPER_INTERFACE_MODE_SGMII
|| cvmx_helper_interface_get_mode(interface) ==
CVMX_HELPER_INTERFACE_MODE_GMII
|| cvmx_helper_interface_get_mode(interface) ==
CVMX_HELPER_INTERFACE_MODE_XAUI) {
if (num_ports > 4) {
cvmx_dprintf("__cvmx_helper_setup_gmx: Illegal "
"num_ports\n");
return -1;
}
gmx_rx_prts.u64 = cvmx_read_csr(CVMX_GMXX_RX_PRTS(interface));
gmx_rx_prts.s.prts = num_ports;
cvmx_write_csr(CVMX_GMXX_RX_PRTS(interface), gmx_rx_prts.u64);
}
/* Skip setting CVMX_PKO_REG_GMX_PORT_MODE on 30XX, 31XX, and 50XX */
if (!OCTEON_IS_MODEL(OCTEON_CN30XX) && !OCTEON_IS_MODEL(OCTEON_CN31XX)
&& !OCTEON_IS_MODEL(OCTEON_CN50XX)) {
/* Tell PKO the number of ports on this interface */
pko_mode.u64 = cvmx_read_csr(CVMX_PKO_REG_GMX_PORT_MODE);
if (interface == 0) {
if (num_ports == 1)
pko_mode.s.mode0 = 4;
else if (num_ports == 2)
pko_mode.s.mode0 = 3;
else if (num_ports <= 4)
pko_mode.s.mode0 = 2;
else if (num_ports <= 8)
pko_mode.s.mode0 = 1;
else
pko_mode.s.mode0 = 0;
} else {
if (num_ports == 1)
pko_mode.s.mode1 = 4;
else if (num_ports == 2)
pko_mode.s.mode1 = 3;
else if (num_ports <= 4)
pko_mode.s.mode1 = 2;
else if (num_ports <= 8)
pko_mode.s.mode1 = 1;
else
pko_mode.s.mode1 = 0;
}
cvmx_write_csr(CVMX_PKO_REG_GMX_PORT_MODE, pko_mode.u64);
}
/*
* Set GMX to buffer as much data as possible before starting
* transmit. This reduces the chances that we have a TX under
* run due to memory contention. Any packet that fits entirely
* in the GMX FIFO can never have an under run regardless of
* memory load.
*/
gmx_tx_thresh.u64 = cvmx_read_csr(CVMX_GMXX_TXX_THRESH(0, interface));
if (OCTEON_IS_MODEL(OCTEON_CN30XX) || OCTEON_IS_MODEL(OCTEON_CN31XX)
|| OCTEON_IS_MODEL(OCTEON_CN50XX)) {
/* These chips have a fixed max threshold of 0x40 */
gmx_tx_thresh.s.cnt = 0x40;
} else {
/* Choose the max value for the number of ports */
if (num_ports <= 1)
gmx_tx_thresh.s.cnt = 0x100 / 1;
else if (num_ports == 2)
gmx_tx_thresh.s.cnt = 0x100 / 2;
else
gmx_tx_thresh.s.cnt = 0x100 / 4;
}
/*
* SPI and XAUI can have lots of ports but the GMX hardware
* only ever has a max of 4.
*/
if (num_ports > 4)
num_ports = 4;
for (index = 0; index < num_ports; index++)
cvmx_write_csr(CVMX_GMXX_TXX_THRESH(index, interface),
gmx_tx_thresh.u64);
return 0;
}
/**
* Returns the IPD/PKO port number for a port on the given
* interface.
*
* @interface: Interface to use
* @port: Port on the interface
*
* Returns IPD/PKO port number
*/
int cvmx_helper_get_ipd_port(int interface, int port)
{
switch (interface) {
case 0:
return port;
case 1:
return port + 16;
case 2:
return port + 32;
case 3:
return port + 36;
}
return -1;
}
/**
* Returns the interface number for an IPD/PKO port number.
*
* @ipd_port: IPD/PKO port number
*
* Returns Interface number
*/
int cvmx_helper_get_interface_num(int ipd_port)
{
if (ipd_port < 16)
return 0;
else if (ipd_port < 32)
return 1;
else if (ipd_port < 36)
return 2;
else if (ipd_port < 40)
return 3;
else
cvmx_dprintf("cvmx_helper_get_interface_num: Illegal IPD "
"port number\n");
return -1;
}
/**
* Returns the interface index number for an IPD/PKO port
* number.
*
* @ipd_port: IPD/PKO port number
*
* Returns Interface index number
*/
int cvmx_helper_get_interface_index_num(int ipd_port)
{
if (ipd_port < 32)
return ipd_port & 15;
else if (ipd_port < 36)
return ipd_port & 3;
else if (ipd_port < 40)
return ipd_port & 3;
else
cvmx_dprintf("cvmx_helper_get_interface_index_num: "
"Illegal IPD port number\n");
return -1;
}

View 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);
}

File diff suppressed because it is too large Load Diff

View 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);
}

View 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);
}

View 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);
}
}

View File

@@ -0,0 +1,506 @@
/***********************license start***************
* Author: Cavium Networks
*
* Contact: support@caviumnetworks.com
* This file is part of the OCTEON SDK
*
* Copyright (c) 2003-2008 Cavium Networks
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License, Version 2, as
* published by the Free Software Foundation.
*
* This file is distributed in the hope that it will be useful, but
* AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or
* NONINFRINGEMENT. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License
* along with this file; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
* or visit http://www.gnu.org/licenses/.
*
* This file may also be available under a different license from Cavium.
* Contact Cavium Networks for more information
***********************license end**************************************/
/*
* Support library for the hardware Packet Output unit.
*/
#include <asm/octeon/octeon.h>
#include <asm/octeon/cvmx-config.h>
#include <asm/octeon/cvmx-pko.h>
#include <asm/octeon/cvmx-helper.h>
/**
* Internal state of packet output
*/
/**
* Call before any other calls to initialize the packet
* output system. This does chip global config, and should only be
* done by one core.
*/
void cvmx_pko_initialize_global(void)
{
int i;
uint64_t priority = 8;
union cvmx_pko_reg_cmd_buf config;
/*
* Set the size of the PKO command buffers to an odd number of
* 64bit words. This allows the normal two word send to stay
* aligned and never span a comamnd word buffer.
*/
config.u64 = 0;
config.s.pool = CVMX_FPA_OUTPUT_BUFFER_POOL;
config.s.size = CVMX_FPA_OUTPUT_BUFFER_POOL_SIZE / 8 - 1;
cvmx_write_csr(CVMX_PKO_REG_CMD_BUF, config.u64);
for (i = 0; i < CVMX_PKO_MAX_OUTPUT_QUEUES; i++)
cvmx_pko_config_port(CVMX_PKO_MEM_QUEUE_PTRS_ILLEGAL_PID, i, 1,
&priority);
/*
* If we aren't using all of the queues optimize PKO's
* internal memory.
*/
if (OCTEON_IS_MODEL(OCTEON_CN38XX) || OCTEON_IS_MODEL(OCTEON_CN58XX)
|| OCTEON_IS_MODEL(OCTEON_CN56XX)
|| OCTEON_IS_MODEL(OCTEON_CN52XX)) {
int num_interfaces = cvmx_helper_get_number_of_interfaces();
int last_port =
cvmx_helper_get_last_ipd_port(num_interfaces - 1);
int max_queues =
cvmx_pko_get_base_queue(last_port) +
cvmx_pko_get_num_queues(last_port);
if (OCTEON_IS_MODEL(OCTEON_CN38XX)) {
if (max_queues <= 32)
cvmx_write_csr(CVMX_PKO_REG_QUEUE_MODE, 2);
else if (max_queues <= 64)
cvmx_write_csr(CVMX_PKO_REG_QUEUE_MODE, 1);
} else {
if (max_queues <= 64)
cvmx_write_csr(CVMX_PKO_REG_QUEUE_MODE, 2);
else if (max_queues <= 128)
cvmx_write_csr(CVMX_PKO_REG_QUEUE_MODE, 1);
}
}
}
/**
* This function does per-core initialization required by the PKO routines.
* This must be called on all cores that will do packet output, and must
* be called after the FPA has been initialized and filled with pages.
*
* Returns 0 on success
* !0 on failure
*/
int cvmx_pko_initialize_local(void)
{
/* Nothing to do */
return 0;
}
/**
* Enables the packet output hardware. It must already be
* configured.
*/
void cvmx_pko_enable(void)
{
union cvmx_pko_reg_flags flags;
flags.u64 = cvmx_read_csr(CVMX_PKO_REG_FLAGS);
if (flags.s.ena_pko)
cvmx_dprintf
("Warning: Enabling PKO when PKO already enabled.\n");
flags.s.ena_dwb = 1;
flags.s.ena_pko = 1;
/*
* always enable big endian for 3-word command. Does nothing
* for 2-word.
*/
flags.s.store_be = 1;
cvmx_write_csr(CVMX_PKO_REG_FLAGS, flags.u64);
}
/**
* Disables the packet output. Does not affect any configuration.
*/
void cvmx_pko_disable(void)
{
union cvmx_pko_reg_flags pko_reg_flags;
pko_reg_flags.u64 = cvmx_read_csr(CVMX_PKO_REG_FLAGS);
pko_reg_flags.s.ena_pko = 0;
cvmx_write_csr(CVMX_PKO_REG_FLAGS, pko_reg_flags.u64);
}
/**
* Reset the packet output.
*/
static void __cvmx_pko_reset(void)
{
union cvmx_pko_reg_flags pko_reg_flags;
pko_reg_flags.u64 = cvmx_read_csr(CVMX_PKO_REG_FLAGS);
pko_reg_flags.s.reset = 1;
cvmx_write_csr(CVMX_PKO_REG_FLAGS, pko_reg_flags.u64);
}
/**
* Shutdown and free resources required by packet output.
*/
void cvmx_pko_shutdown(void)
{
union cvmx_pko_mem_queue_ptrs config;
int queue;
cvmx_pko_disable();
for (queue = 0; queue < CVMX_PKO_MAX_OUTPUT_QUEUES; queue++) {
config.u64 = 0;
config.s.tail = 1;
config.s.index = 0;
config.s.port = CVMX_PKO_MEM_QUEUE_PTRS_ILLEGAL_PID;
config.s.queue = queue & 0x7f;
config.s.qos_mask = 0;
config.s.buf_ptr = 0;
if (!OCTEON_IS_MODEL(OCTEON_CN3XXX)) {
union cvmx_pko_reg_queue_ptrs1 config1;
config1.u64 = 0;
config1.s.qid7 = queue >> 7;
cvmx_write_csr(CVMX_PKO_REG_QUEUE_PTRS1, config1.u64);
}
cvmx_write_csr(CVMX_PKO_MEM_QUEUE_PTRS, config.u64);
cvmx_cmd_queue_shutdown(CVMX_CMD_QUEUE_PKO(queue));
}
__cvmx_pko_reset();
}
/**
* Configure a output port and the associated queues for use.
*
* @port: Port to configure.
* @base_queue: First queue number to associate with this port.
* @num_queues: Number of queues to associate with this port
* @priority: Array of priority levels for each queue. Values are
* allowed to be 0-8. A value of 8 get 8 times the traffic
* of a value of 1. A value of 0 indicates that no rounds
* will be participated in. These priorities can be changed
* on the fly while the pko is enabled. A priority of 9
* indicates that static priority should be used. If static
* priority is used all queues with static priority must be
* contiguous starting at the base_queue, and lower numbered
* queues have higher priority than higher numbered queues.
* There must be num_queues elements in the array.
*/
cvmx_pko_status_t cvmx_pko_config_port(uint64_t port, uint64_t base_queue,
uint64_t num_queues,
const uint64_t priority[])
{
cvmx_pko_status_t result_code;
uint64_t queue;
union cvmx_pko_mem_queue_ptrs config;
union cvmx_pko_reg_queue_ptrs1 config1;
int static_priority_base = -1;
int static_priority_end = -1;
if ((port >= CVMX_PKO_NUM_OUTPUT_PORTS)
&& (port != CVMX_PKO_MEM_QUEUE_PTRS_ILLEGAL_PID)) {
cvmx_dprintf("ERROR: cvmx_pko_config_port: Invalid port %llu\n",
(unsigned long long)port);
return CVMX_PKO_INVALID_PORT;
}
if (base_queue + num_queues > CVMX_PKO_MAX_OUTPUT_QUEUES) {
cvmx_dprintf
("ERROR: cvmx_pko_config_port: Invalid queue range %llu\n",
(unsigned long long)(base_queue + num_queues));
return CVMX_PKO_INVALID_QUEUE;
}
if (port != CVMX_PKO_MEM_QUEUE_PTRS_ILLEGAL_PID) {
/*
* Validate the static queue priority setup and set
* static_priority_base and static_priority_end
* accordingly.
*/
for (queue = 0; queue < num_queues; queue++) {
/* Find first queue of static priority */
if (static_priority_base == -1
&& priority[queue] ==
CVMX_PKO_QUEUE_STATIC_PRIORITY)
static_priority_base = queue;
/* Find last queue of static priority */
if (static_priority_base != -1
&& static_priority_end == -1
&& priority[queue] != CVMX_PKO_QUEUE_STATIC_PRIORITY
&& queue)
static_priority_end = queue - 1;
else if (static_priority_base != -1
&& static_priority_end == -1
&& queue == num_queues - 1)
/* all queues are static priority */
static_priority_end = queue;
/*
* Check to make sure all static priority
* queues are contiguous. Also catches some
* cases of static priorites not starting at
* queue 0.
*/
if (static_priority_end != -1
&& (int)queue > static_priority_end
&& priority[queue] ==
CVMX_PKO_QUEUE_STATIC_PRIORITY) {
cvmx_dprintf("ERROR: cvmx_pko_config_port: "
"Static priority queues aren't "
"contiguous or don't start at "
"base queue. q: %d, eq: %d\n",
(int)queue, static_priority_end);
return CVMX_PKO_INVALID_PRIORITY;
}
}
if (static_priority_base > 0) {
cvmx_dprintf("ERROR: cvmx_pko_config_port: Static "
"priority queues don't start at base "
"queue. sq: %d\n",
static_priority_base);
return CVMX_PKO_INVALID_PRIORITY;
}
#if 0
cvmx_dprintf("Port %d: Static priority queue base: %d, "
"end: %d\n", port,
static_priority_base, static_priority_end);
#endif
}
/*
* At this point, static_priority_base and static_priority_end
* are either both -1, or are valid start/end queue
* numbers.
*/
result_code = CVMX_PKO_SUCCESS;
#ifdef PKO_DEBUG
cvmx_dprintf("num queues: %d (%lld,%lld)\n", num_queues,
CVMX_PKO_QUEUES_PER_PORT_INTERFACE0,
CVMX_PKO_QUEUES_PER_PORT_INTERFACE1);
#endif
for (queue = 0; queue < num_queues; queue++) {
uint64_t *buf_ptr = NULL;
config1.u64 = 0;
config1.s.idx3 = queue >> 3;
config1.s.qid7 = (base_queue + queue) >> 7;
config.u64 = 0;
config.s.tail = queue == (num_queues - 1);
config.s.index = queue;
config.s.port = port;
config.s.queue = base_queue + queue;
if (!cvmx_octeon_is_pass1()) {
config.s.static_p = static_priority_base >= 0;
config.s.static_q = (int)queue <= static_priority_end;
config.s.s_tail = (int)queue == static_priority_end;
}
/*
* Convert the priority into an enable bit field. Try
* to space the bits out evenly so the packet don't
* get grouped up
*/
switch ((int)priority[queue]) {
case 0:
config.s.qos_mask = 0x00;
break;
case 1:
config.s.qos_mask = 0x01;
break;
case 2:
config.s.qos_mask = 0x11;
break;
case 3:
config.s.qos_mask = 0x49;
break;
case 4:
config.s.qos_mask = 0x55;
break;
case 5:
config.s.qos_mask = 0x57;
break;
case 6:
config.s.qos_mask = 0x77;
break;
case 7:
config.s.qos_mask = 0x7f;
break;
case 8:
config.s.qos_mask = 0xff;
break;
case CVMX_PKO_QUEUE_STATIC_PRIORITY:
/* Pass 1 will fall through to the error case */
if (!cvmx_octeon_is_pass1()) {
config.s.qos_mask = 0xff;
break;
}
default:
cvmx_dprintf("ERROR: cvmx_pko_config_port: Invalid "
"priority %llu\n",
(unsigned long long)priority[queue]);
config.s.qos_mask = 0xff;
result_code = CVMX_PKO_INVALID_PRIORITY;
break;
}
if (port != CVMX_PKO_MEM_QUEUE_PTRS_ILLEGAL_PID) {
cvmx_cmd_queue_result_t cmd_res =
cvmx_cmd_queue_initialize(CVMX_CMD_QUEUE_PKO
(base_queue + queue),
CVMX_PKO_MAX_QUEUE_DEPTH,
CVMX_FPA_OUTPUT_BUFFER_POOL,
CVMX_FPA_OUTPUT_BUFFER_POOL_SIZE
-
CVMX_PKO_COMMAND_BUFFER_SIZE_ADJUST
* 8);
if (cmd_res != CVMX_CMD_QUEUE_SUCCESS) {
switch (cmd_res) {
case CVMX_CMD_QUEUE_NO_MEMORY:
cvmx_dprintf("ERROR: "
"cvmx_pko_config_port: "
"Unable to allocate "
"output buffer.\n");
return CVMX_PKO_NO_MEMORY;
case CVMX_CMD_QUEUE_ALREADY_SETUP:
cvmx_dprintf
("ERROR: cvmx_pko_config_port: Port already setup.\n");
return CVMX_PKO_PORT_ALREADY_SETUP;
case CVMX_CMD_QUEUE_INVALID_PARAM:
default:
cvmx_dprintf
("ERROR: cvmx_pko_config_port: Command queue initialization failed.\n");
return CVMX_PKO_CMD_QUEUE_INIT_ERROR;
}
}
buf_ptr =
(uint64_t *)
cvmx_cmd_queue_buffer(CVMX_CMD_QUEUE_PKO
(base_queue + queue));
config.s.buf_ptr = cvmx_ptr_to_phys(buf_ptr);
} else
config.s.buf_ptr = 0;
CVMX_SYNCWS;
if (!OCTEON_IS_MODEL(OCTEON_CN3XXX))
cvmx_write_csr(CVMX_PKO_REG_QUEUE_PTRS1, config1.u64);
cvmx_write_csr(CVMX_PKO_MEM_QUEUE_PTRS, config.u64);
}
return result_code;
}
#ifdef PKO_DEBUG
/**
* Show map of ports -> queues for different cores.
*/
void cvmx_pko_show_queue_map()
{
int core, port;
int pko_output_ports = 36;
cvmx_dprintf("port");
for (port = 0; port < pko_output_ports; port++)
cvmx_dprintf("%3d ", port);
cvmx_dprintf("\n");
for (core = 0; core < CVMX_MAX_CORES; core++) {
cvmx_dprintf("\n%2d: ", core);
for (port = 0; port < pko_output_ports; port++) {
cvmx_dprintf("%3d ",
cvmx_pko_get_base_queue_per_core(port,
core));
}
}
cvmx_dprintf("\n");
}
#endif
/**
* Rate limit a PKO port to a max packets/sec. This function is only
* supported on CN51XX and higher, excluding CN58XX.
*
* @port: Port to rate limit
* @packets_s: Maximum packet/sec
* @burst: Maximum number of packets to burst in a row before rate
* limiting cuts in.
*
* Returns Zero on success, negative on failure
*/
int cvmx_pko_rate_limit_packets(int port, int packets_s, int burst)
{
union cvmx_pko_mem_port_rate0 pko_mem_port_rate0;
union cvmx_pko_mem_port_rate1 pko_mem_port_rate1;
pko_mem_port_rate0.u64 = 0;
pko_mem_port_rate0.s.pid = port;
pko_mem_port_rate0.s.rate_pkt =
cvmx_sysinfo_get()->cpu_clock_hz / packets_s / 16;
/* No cost per word since we are limited by packets/sec, not bits/sec */
pko_mem_port_rate0.s.rate_word = 0;
pko_mem_port_rate1.u64 = 0;
pko_mem_port_rate1.s.pid = port;
pko_mem_port_rate1.s.rate_lim =
((uint64_t) pko_mem_port_rate0.s.rate_pkt * burst) >> 8;
cvmx_write_csr(CVMX_PKO_MEM_PORT_RATE0, pko_mem_port_rate0.u64);
cvmx_write_csr(CVMX_PKO_MEM_PORT_RATE1, pko_mem_port_rate1.u64);
return 0;
}
/**
* Rate limit a PKO port to a max bits/sec. This function is only
* supported on CN51XX and higher, excluding CN58XX.
*
* @port: Port to rate limit
* @bits_s: PKO rate limit in bits/sec
* @burst: Maximum number of bits to burst before rate
* limiting cuts in.
*
* Returns Zero on success, negative on failure
*/
int cvmx_pko_rate_limit_bits(int port, uint64_t bits_s, int burst)
{
union cvmx_pko_mem_port_rate0 pko_mem_port_rate0;
union cvmx_pko_mem_port_rate1 pko_mem_port_rate1;
uint64_t clock_rate = cvmx_sysinfo_get()->cpu_clock_hz;
uint64_t tokens_per_bit = clock_rate * 16 / bits_s;
pko_mem_port_rate0.u64 = 0;
pko_mem_port_rate0.s.pid = port;
/*
* Each packet has a 12 bytes of interframe gap, an 8 byte
* preamble, and a 4 byte CRC. These are not included in the
* per word count. Multiply by 8 to covert to bits and divide
* by 256 for limit granularity.
*/
pko_mem_port_rate0.s.rate_pkt = (12 + 8 + 4) * 8 * tokens_per_bit / 256;
/* Each 8 byte word has 64bits */
pko_mem_port_rate0.s.rate_word = 64 * tokens_per_bit;
pko_mem_port_rate1.u64 = 0;
pko_mem_port_rate1.s.pid = port;
pko_mem_port_rate1.s.rate_lim = tokens_per_bit * burst / 256;
cvmx_write_csr(CVMX_PKO_MEM_PORT_RATE0, pko_mem_port_rate0.u64);
cvmx_write_csr(CVMX_PKO_MEM_PORT_RATE1, pko_mem_port_rate1.u64);
return 0;
}

View File

@@ -0,0 +1,667 @@
/***********************license start***************
* Author: Cavium Networks
*
* Contact: support@caviumnetworks.com
* This file is part of the OCTEON SDK
*
* Copyright (c) 2003-2008 Cavium Networks
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License, Version 2, as
* published by the Free Software Foundation.
*
* This file is distributed in the hope that it will be useful, but
* AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or
* NONINFRINGEMENT. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License
* along with this file; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
* or visit http://www.gnu.org/licenses/.
*
* This file may also be available under a different license from Cavium.
* Contact Cavium Networks for more information
***********************license end**************************************/
/*
*
* Support library for the SPI
*/
#include <asm/octeon/octeon.h>
#include <asm/octeon/cvmx-config.h>
#include <asm/octeon/cvmx-pko.h>
#include <asm/octeon/cvmx-spi.h>
#include <asm/octeon/cvmx-spxx-defs.h>
#include <asm/octeon/cvmx-stxx-defs.h>
#include <asm/octeon/cvmx-srxx-defs.h>
#define INVOKE_CB(function_p, args...) \
do { \
if (function_p) { \
res = function_p(args); \
if (res) \
return res; \
} \
} while (0)
#if CVMX_ENABLE_DEBUG_PRINTS
static const char *modes[] =
{ "UNKNOWN", "TX Halfplex", "Rx Halfplex", "Duplex" };
#endif
/* Default callbacks, can be overridden
* using cvmx_spi_get_callbacks/cvmx_spi_set_callbacks
*/
static cvmx_spi_callbacks_t cvmx_spi_callbacks = {
.reset_cb = cvmx_spi_reset_cb,
.calendar_setup_cb = cvmx_spi_calendar_setup_cb,
.clock_detect_cb = cvmx_spi_clock_detect_cb,
.training_cb = cvmx_spi_training_cb,
.calendar_sync_cb = cvmx_spi_calendar_sync_cb,
.interface_up_cb = cvmx_spi_interface_up_cb
};
/**
* Get current SPI4 initialization callbacks
*
* @callbacks: Pointer to the callbacks structure.to fill
*
* Returns Pointer to cvmx_spi_callbacks_t structure.
*/
void cvmx_spi_get_callbacks(cvmx_spi_callbacks_t *callbacks)
{
memcpy(callbacks, &cvmx_spi_callbacks, sizeof(cvmx_spi_callbacks));
}
/**
* Set new SPI4 initialization callbacks
*
* @new_callbacks: Pointer to an updated callbacks structure.
*/
void cvmx_spi_set_callbacks(cvmx_spi_callbacks_t *new_callbacks)
{
memcpy(&cvmx_spi_callbacks, new_callbacks, sizeof(cvmx_spi_callbacks));
}
/**
* Initialize and start the SPI interface.
*
* @interface: The identifier of the packet interface to configure and
* use as a SPI interface.
* @mode: The operating mode for the SPI interface. The interface
* can operate as a full duplex (both Tx and Rx data paths
* active) or as a halfplex (either the Tx data path is
* active or the Rx data path is active, but not both).
* @timeout: Timeout to wait for clock synchronization in seconds
* @num_ports: Number of SPI ports to configure
*
* Returns Zero on success, negative of failure.
*/
int cvmx_spi_start_interface(int interface, cvmx_spi_mode_t mode, int timeout,
int num_ports)
{
int res = -1;
if (!(OCTEON_IS_MODEL(OCTEON_CN38XX) || OCTEON_IS_MODEL(OCTEON_CN58XX)))
return res;
/* Callback to perform SPI4 reset */
INVOKE_CB(cvmx_spi_callbacks.reset_cb, interface, mode);
/* Callback to perform calendar setup */
INVOKE_CB(cvmx_spi_callbacks.calendar_setup_cb, interface, mode,
num_ports);
/* Callback to perform clock detection */
INVOKE_CB(cvmx_spi_callbacks.clock_detect_cb, interface, mode, timeout);
/* Callback to perform SPI4 link training */
INVOKE_CB(cvmx_spi_callbacks.training_cb, interface, mode, timeout);
/* Callback to perform calendar sync */
INVOKE_CB(cvmx_spi_callbacks.calendar_sync_cb, interface, mode,
timeout);
/* Callback to handle interface coming up */
INVOKE_CB(cvmx_spi_callbacks.interface_up_cb, interface, mode);
return res;
}
/**
* This routine restarts the SPI interface after it has lost synchronization
* with its correspondent system.
*
* @interface: The identifier of the packet interface to configure and
* use as a SPI interface.
* @mode: The operating mode for the SPI interface. The interface
* can operate as a full duplex (both Tx and Rx data paths
* active) or as a halfplex (either the Tx data path is
* active or the Rx data path is active, but not both).
* @timeout: Timeout to wait for clock synchronization in seconds
*
* Returns Zero on success, negative of failure.
*/
int cvmx_spi_restart_interface(int interface, cvmx_spi_mode_t mode, int timeout)
{
int res = -1;
if (!(OCTEON_IS_MODEL(OCTEON_CN38XX) || OCTEON_IS_MODEL(OCTEON_CN58XX)))
return res;
cvmx_dprintf("SPI%d: Restart %s\n", interface, modes[mode]);
/* Callback to perform SPI4 reset */
INVOKE_CB(cvmx_spi_callbacks.reset_cb, interface, mode);
/* NOTE: Calendar setup is not performed during restart */
/* Refer to cvmx_spi_start_interface() for the full sequence */
/* Callback to perform clock detection */
INVOKE_CB(cvmx_spi_callbacks.clock_detect_cb, interface, mode, timeout);
/* Callback to perform SPI4 link training */
INVOKE_CB(cvmx_spi_callbacks.training_cb, interface, mode, timeout);
/* Callback to perform calendar sync */
INVOKE_CB(cvmx_spi_callbacks.calendar_sync_cb, interface, mode,
timeout);
/* Callback to handle interface coming up */
INVOKE_CB(cvmx_spi_callbacks.interface_up_cb, interface, mode);
return res;
}
/**
* Callback to perform SPI4 reset
*
* @interface: The identifier of the packet interface to configure and
* use as a SPI interface.
* @mode: The operating mode for the SPI interface. The interface
* can operate as a full duplex (both Tx and Rx data paths
* active) or as a halfplex (either the Tx data path is
* active or the Rx data path is active, but not both).
*
* Returns Zero on success, non-zero error code on failure (will cause
* SPI initialization to abort)
*/
int cvmx_spi_reset_cb(int interface, cvmx_spi_mode_t mode)
{
union cvmx_spxx_dbg_deskew_ctl spxx_dbg_deskew_ctl;
union cvmx_spxx_clk_ctl spxx_clk_ctl;
union cvmx_spxx_bist_stat spxx_bist_stat;
union cvmx_spxx_int_msk spxx_int_msk;
union cvmx_stxx_int_msk stxx_int_msk;
union cvmx_spxx_trn4_ctl spxx_trn4_ctl;
int index;
uint64_t MS = cvmx_sysinfo_get()->cpu_clock_hz / 1000;
/* Disable SPI error events while we run BIST */
spxx_int_msk.u64 = cvmx_read_csr(CVMX_SPXX_INT_MSK(interface));
cvmx_write_csr(CVMX_SPXX_INT_MSK(interface), 0);
stxx_int_msk.u64 = cvmx_read_csr(CVMX_STXX_INT_MSK(interface));
cvmx_write_csr(CVMX_STXX_INT_MSK(interface), 0);
/* Run BIST in the SPI interface */
cvmx_write_csr(CVMX_SRXX_COM_CTL(interface), 0);
cvmx_write_csr(CVMX_STXX_COM_CTL(interface), 0);
spxx_clk_ctl.u64 = 0;
spxx_clk_ctl.s.runbist = 1;
cvmx_write_csr(CVMX_SPXX_CLK_CTL(interface), spxx_clk_ctl.u64);
cvmx_wait(10 * MS);
spxx_bist_stat.u64 = cvmx_read_csr(CVMX_SPXX_BIST_STAT(interface));
if (spxx_bist_stat.s.stat0)
cvmx_dprintf
("ERROR SPI%d: BIST failed on receive datapath FIFO\n",
interface);
if (spxx_bist_stat.s.stat1)
cvmx_dprintf("ERROR SPI%d: BIST failed on RX calendar table\n",
interface);
if (spxx_bist_stat.s.stat2)
cvmx_dprintf("ERROR SPI%d: BIST failed on TX calendar table\n",
interface);
/* Clear the calendar table after BIST to fix parity errors */
for (index = 0; index < 32; index++) {
union cvmx_srxx_spi4_calx srxx_spi4_calx;
union cvmx_stxx_spi4_calx stxx_spi4_calx;
srxx_spi4_calx.u64 = 0;
srxx_spi4_calx.s.oddpar = 1;
cvmx_write_csr(CVMX_SRXX_SPI4_CALX(index, interface),
srxx_spi4_calx.u64);
stxx_spi4_calx.u64 = 0;
stxx_spi4_calx.s.oddpar = 1;
cvmx_write_csr(CVMX_STXX_SPI4_CALX(index, interface),
stxx_spi4_calx.u64);
}
/* Re enable reporting of error interrupts */
cvmx_write_csr(CVMX_SPXX_INT_REG(interface),
cvmx_read_csr(CVMX_SPXX_INT_REG(interface)));
cvmx_write_csr(CVMX_SPXX_INT_MSK(interface), spxx_int_msk.u64);
cvmx_write_csr(CVMX_STXX_INT_REG(interface),
cvmx_read_csr(CVMX_STXX_INT_REG(interface)));
cvmx_write_csr(CVMX_STXX_INT_MSK(interface), stxx_int_msk.u64);
/* Setup the CLKDLY right in the middle */
spxx_clk_ctl.u64 = 0;
spxx_clk_ctl.s.seetrn = 0;
spxx_clk_ctl.s.clkdly = 0x10;
spxx_clk_ctl.s.runbist = 0;
spxx_clk_ctl.s.statdrv = 0;
/* This should always be on the opposite edge as statdrv */
spxx_clk_ctl.s.statrcv = 1;
spxx_clk_ctl.s.sndtrn = 0;
spxx_clk_ctl.s.drptrn = 0;
spxx_clk_ctl.s.rcvtrn = 0;
spxx_clk_ctl.s.srxdlck = 0;
cvmx_write_csr(CVMX_SPXX_CLK_CTL(interface), spxx_clk_ctl.u64);
cvmx_wait(100 * MS);
/* Reset SRX0 DLL */
spxx_clk_ctl.s.srxdlck = 1;
cvmx_write_csr(CVMX_SPXX_CLK_CTL(interface), spxx_clk_ctl.u64);
/* Waiting for Inf0 Spi4 RX DLL to lock */
cvmx_wait(100 * MS);
/* Enable dynamic alignment */
spxx_trn4_ctl.s.trntest = 0;
spxx_trn4_ctl.s.jitter = 1;
spxx_trn4_ctl.s.clr_boot = 1;
spxx_trn4_ctl.s.set_boot = 0;
if (OCTEON_IS_MODEL(OCTEON_CN58XX))
spxx_trn4_ctl.s.maxdist = 3;
else
spxx_trn4_ctl.s.maxdist = 8;
spxx_trn4_ctl.s.macro_en = 1;
spxx_trn4_ctl.s.mux_en = 1;
cvmx_write_csr(CVMX_SPXX_TRN4_CTL(interface), spxx_trn4_ctl.u64);
spxx_dbg_deskew_ctl.u64 = 0;
cvmx_write_csr(CVMX_SPXX_DBG_DESKEW_CTL(interface),
spxx_dbg_deskew_ctl.u64);
return 0;
}
/**
* Callback to setup calendar and miscellaneous settings before clock detection
*
* @interface: The identifier of the packet interface to configure and
* use as a SPI interface.
* @mode: The operating mode for the SPI interface. The interface
* can operate as a full duplex (both Tx and Rx data paths
* active) or as a halfplex (either the Tx data path is
* active or the Rx data path is active, but not both).
* @num_ports: Number of ports to configure on SPI
*
* Returns Zero on success, non-zero error code on failure (will cause
* SPI initialization to abort)
*/
int cvmx_spi_calendar_setup_cb(int interface, cvmx_spi_mode_t mode,
int num_ports)
{
int port;
int index;
if (mode & CVMX_SPI_MODE_RX_HALFPLEX) {
union cvmx_srxx_com_ctl srxx_com_ctl;
union cvmx_srxx_spi4_stat srxx_spi4_stat;
/* SRX0 number of Ports */
srxx_com_ctl.u64 = 0;
srxx_com_ctl.s.prts = num_ports - 1;
srxx_com_ctl.s.st_en = 0;
srxx_com_ctl.s.inf_en = 0;
cvmx_write_csr(CVMX_SRXX_COM_CTL(interface), srxx_com_ctl.u64);
/* SRX0 Calendar Table. This round robbins through all ports */
port = 0;
index = 0;
while (port < num_ports) {
union cvmx_srxx_spi4_calx srxx_spi4_calx;
srxx_spi4_calx.u64 = 0;
srxx_spi4_calx.s.prt0 = port++;
srxx_spi4_calx.s.prt1 = port++;
srxx_spi4_calx.s.prt2 = port++;
srxx_spi4_calx.s.prt3 = port++;
srxx_spi4_calx.s.oddpar =
~(cvmx_dpop(srxx_spi4_calx.u64) & 1);
cvmx_write_csr(CVMX_SRXX_SPI4_CALX(index, interface),
srxx_spi4_calx.u64);
index++;
}
srxx_spi4_stat.u64 = 0;
srxx_spi4_stat.s.len = num_ports;
srxx_spi4_stat.s.m = 1;
cvmx_write_csr(CVMX_SRXX_SPI4_STAT(interface),
srxx_spi4_stat.u64);
}
if (mode & CVMX_SPI_MODE_TX_HALFPLEX) {
union cvmx_stxx_arb_ctl stxx_arb_ctl;
union cvmx_gmxx_tx_spi_max gmxx_tx_spi_max;
union cvmx_gmxx_tx_spi_thresh gmxx_tx_spi_thresh;
union cvmx_gmxx_tx_spi_ctl gmxx_tx_spi_ctl;
union cvmx_stxx_spi4_stat stxx_spi4_stat;
union cvmx_stxx_spi4_dat stxx_spi4_dat;
/* STX0 Config */
stxx_arb_ctl.u64 = 0;
stxx_arb_ctl.s.igntpa = 0;
stxx_arb_ctl.s.mintrn = 0;
cvmx_write_csr(CVMX_STXX_ARB_CTL(interface), stxx_arb_ctl.u64);
gmxx_tx_spi_max.u64 = 0;
gmxx_tx_spi_max.s.max1 = 8;
gmxx_tx_spi_max.s.max2 = 4;
gmxx_tx_spi_max.s.slice = 0;
cvmx_write_csr(CVMX_GMXX_TX_SPI_MAX(interface),
gmxx_tx_spi_max.u64);
gmxx_tx_spi_thresh.u64 = 0;
gmxx_tx_spi_thresh.s.thresh = 4;
cvmx_write_csr(CVMX_GMXX_TX_SPI_THRESH(interface),
gmxx_tx_spi_thresh.u64);
gmxx_tx_spi_ctl.u64 = 0;
gmxx_tx_spi_ctl.s.tpa_clr = 0;
gmxx_tx_spi_ctl.s.cont_pkt = 0;
cvmx_write_csr(CVMX_GMXX_TX_SPI_CTL(interface),
gmxx_tx_spi_ctl.u64);
/* STX0 Training Control */
stxx_spi4_dat.u64 = 0;
/*Minimum needed by dynamic alignment */
stxx_spi4_dat.s.alpha = 32;
stxx_spi4_dat.s.max_t = 0xFFFF; /*Minimum interval is 0x20 */
cvmx_write_csr(CVMX_STXX_SPI4_DAT(interface),
stxx_spi4_dat.u64);
/* STX0 Calendar Table. This round robbins through all ports */
port = 0;
index = 0;
while (port < num_ports) {
union cvmx_stxx_spi4_calx stxx_spi4_calx;
stxx_spi4_calx.u64 = 0;
stxx_spi4_calx.s.prt0 = port++;
stxx_spi4_calx.s.prt1 = port++;
stxx_spi4_calx.s.prt2 = port++;
stxx_spi4_calx.s.prt3 = port++;
stxx_spi4_calx.s.oddpar =
~(cvmx_dpop(stxx_spi4_calx.u64) & 1);
cvmx_write_csr(CVMX_STXX_SPI4_CALX(index, interface),
stxx_spi4_calx.u64);
index++;
}
stxx_spi4_stat.u64 = 0;
stxx_spi4_stat.s.len = num_ports;
stxx_spi4_stat.s.m = 1;
cvmx_write_csr(CVMX_STXX_SPI4_STAT(interface),
stxx_spi4_stat.u64);
}
return 0;
}
/**
* Callback to perform clock detection
*
* @interface: The identifier of the packet interface to configure and
* use as a SPI interface.
* @mode: The operating mode for the SPI interface. The interface
* can operate as a full duplex (both Tx and Rx data paths
* active) or as a halfplex (either the Tx data path is
* active or the Rx data path is active, but not both).
* @timeout: Timeout to wait for clock synchronization in seconds
*
* Returns Zero on success, non-zero error code on failure (will cause
* SPI initialization to abort)
*/
int cvmx_spi_clock_detect_cb(int interface, cvmx_spi_mode_t mode, int timeout)
{
int clock_transitions;
union cvmx_spxx_clk_stat stat;
uint64_t timeout_time;
uint64_t MS = cvmx_sysinfo_get()->cpu_clock_hz / 1000;
/*
* Regardless of operating mode, both Tx and Rx clocks must be
* present for the SPI interface to operate.
*/
cvmx_dprintf("SPI%d: Waiting to see TsClk...\n", interface);
timeout_time = cvmx_get_cycle() + 1000ull * MS * timeout;
/*
* Require 100 clock transitions in order to avoid any noise
* in the beginning.
*/
clock_transitions = 100;
do {
stat.u64 = cvmx_read_csr(CVMX_SPXX_CLK_STAT(interface));
if (stat.s.s4clk0 && stat.s.s4clk1 && clock_transitions) {
/*
* We've seen a clock transition, so decrement
* the number we still need.
*/
clock_transitions--;
cvmx_write_csr(CVMX_SPXX_CLK_STAT(interface), stat.u64);
stat.s.s4clk0 = 0;
stat.s.s4clk1 = 0;
}
if (cvmx_get_cycle() > timeout_time) {
cvmx_dprintf("SPI%d: Timeout\n", interface);
return -1;
}
} while (stat.s.s4clk0 == 0 || stat.s.s4clk1 == 0);
cvmx_dprintf("SPI%d: Waiting to see RsClk...\n", interface);
timeout_time = cvmx_get_cycle() + 1000ull * MS * timeout;
/*
* Require 100 clock transitions in order to avoid any noise in the
* beginning.
*/
clock_transitions = 100;
do {
stat.u64 = cvmx_read_csr(CVMX_SPXX_CLK_STAT(interface));
if (stat.s.d4clk0 && stat.s.d4clk1 && clock_transitions) {
/*
* We've seen a clock transition, so decrement
* the number we still need
*/
clock_transitions--;
cvmx_write_csr(CVMX_SPXX_CLK_STAT(interface), stat.u64);
stat.s.d4clk0 = 0;
stat.s.d4clk1 = 0;
}
if (cvmx_get_cycle() > timeout_time) {
cvmx_dprintf("SPI%d: Timeout\n", interface);
return -1;
}
} while (stat.s.d4clk0 == 0 || stat.s.d4clk1 == 0);
return 0;
}
/**
* Callback to perform link training
*
* @interface: The identifier of the packet interface to configure and
* use as a SPI interface.
* @mode: The operating mode for the SPI interface. The interface
* can operate as a full duplex (both Tx and Rx data paths
* active) or as a halfplex (either the Tx data path is
* active or the Rx data path is active, but not both).
* @timeout: Timeout to wait for link to be trained (in seconds)
*
* Returns Zero on success, non-zero error code on failure (will cause
* SPI initialization to abort)
*/
int cvmx_spi_training_cb(int interface, cvmx_spi_mode_t mode, int timeout)
{
union cvmx_spxx_trn4_ctl spxx_trn4_ctl;
union cvmx_spxx_clk_stat stat;
uint64_t MS = cvmx_sysinfo_get()->cpu_clock_hz / 1000;
uint64_t timeout_time = cvmx_get_cycle() + 1000ull * MS * timeout;
int rx_training_needed;
/* SRX0 & STX0 Inf0 Links are configured - begin training */
union cvmx_spxx_clk_ctl spxx_clk_ctl;
spxx_clk_ctl.u64 = 0;
spxx_clk_ctl.s.seetrn = 0;
spxx_clk_ctl.s.clkdly = 0x10;
spxx_clk_ctl.s.runbist = 0;
spxx_clk_ctl.s.statdrv = 0;
/* This should always be on the opposite edge as statdrv */
spxx_clk_ctl.s.statrcv = 1;
spxx_clk_ctl.s.sndtrn = 1;
spxx_clk_ctl.s.drptrn = 1;
spxx_clk_ctl.s.rcvtrn = 1;
spxx_clk_ctl.s.srxdlck = 1;
cvmx_write_csr(CVMX_SPXX_CLK_CTL(interface), spxx_clk_ctl.u64);
cvmx_wait(1000 * MS);
/* SRX0 clear the boot bit */
spxx_trn4_ctl.u64 = cvmx_read_csr(CVMX_SPXX_TRN4_CTL(interface));
spxx_trn4_ctl.s.clr_boot = 1;
cvmx_write_csr(CVMX_SPXX_TRN4_CTL(interface), spxx_trn4_ctl.u64);
/* Wait for the training sequence to complete */
cvmx_dprintf("SPI%d: Waiting for training\n", interface);
cvmx_wait(1000 * MS);
/* Wait a really long time here */
timeout_time = cvmx_get_cycle() + 1000ull * MS * 600;
/*
* The HRM says we must wait for 34 + 16 * MAXDIST training sequences.
* We'll be pessimistic and wait for a lot more.
*/
rx_training_needed = 500;
do {
stat.u64 = cvmx_read_csr(CVMX_SPXX_CLK_STAT(interface));
if (stat.s.srxtrn && rx_training_needed) {
rx_training_needed--;
cvmx_write_csr(CVMX_SPXX_CLK_STAT(interface), stat.u64);
stat.s.srxtrn = 0;
}
if (cvmx_get_cycle() > timeout_time) {
cvmx_dprintf("SPI%d: Timeout\n", interface);
return -1;
}
} while (stat.s.srxtrn == 0);
return 0;
}
/**
* Callback to perform calendar data synchronization
*
* @interface: The identifier of the packet interface to configure and
* use as a SPI interface.
* @mode: The operating mode for the SPI interface. The interface
* can operate as a full duplex (both Tx and Rx data paths
* active) or as a halfplex (either the Tx data path is
* active or the Rx data path is active, but not both).
* @timeout: Timeout to wait for calendar data in seconds
*
* Returns Zero on success, non-zero error code on failure (will cause
* SPI initialization to abort)
*/
int cvmx_spi_calendar_sync_cb(int interface, cvmx_spi_mode_t mode, int timeout)
{
uint64_t MS = cvmx_sysinfo_get()->cpu_clock_hz / 1000;
if (mode & CVMX_SPI_MODE_RX_HALFPLEX) {
/* SRX0 interface should be good, send calendar data */
union cvmx_srxx_com_ctl srxx_com_ctl;
cvmx_dprintf
("SPI%d: Rx is synchronized, start sending calendar data\n",
interface);
srxx_com_ctl.u64 = cvmx_read_csr(CVMX_SRXX_COM_CTL(interface));
srxx_com_ctl.s.inf_en = 1;
srxx_com_ctl.s.st_en = 1;
cvmx_write_csr(CVMX_SRXX_COM_CTL(interface), srxx_com_ctl.u64);
}
if (mode & CVMX_SPI_MODE_TX_HALFPLEX) {
/* STX0 has achieved sync */
/* The corespondant board should be sending calendar data */
/* Enable the STX0 STAT receiver. */
union cvmx_spxx_clk_stat stat;
uint64_t timeout_time;
union cvmx_stxx_com_ctl stxx_com_ctl;
stxx_com_ctl.u64 = 0;
stxx_com_ctl.s.st_en = 1;
cvmx_write_csr(CVMX_STXX_COM_CTL(interface), stxx_com_ctl.u64);
/* Waiting for calendar sync on STX0 STAT */
cvmx_dprintf("SPI%d: Waiting to sync on STX[%d] STAT\n",
interface, interface);
timeout_time = cvmx_get_cycle() + 1000ull * MS * timeout;
/* SPX0_CLK_STAT - SPX0_CLK_STAT[STXCAL] should be 1 (bit10) */
do {
stat.u64 = cvmx_read_csr(CVMX_SPXX_CLK_STAT(interface));
if (cvmx_get_cycle() > timeout_time) {
cvmx_dprintf("SPI%d: Timeout\n", interface);
return -1;
}
} while (stat.s.stxcal == 0);
}
return 0;
}
/**
* Callback to handle interface up
*
* @interface: The identifier of the packet interface to configure and
* use as a SPI interface.
* @mode: The operating mode for the SPI interface. The interface
* can operate as a full duplex (both Tx and Rx data paths
* active) or as a halfplex (either the Tx data path is
* active or the Rx data path is active, but not both).
*
* Returns Zero on success, non-zero error code on failure (will cause
* SPI initialization to abort)
*/
int cvmx_spi_interface_up_cb(int interface, cvmx_spi_mode_t mode)
{
union cvmx_gmxx_rxx_frm_min gmxx_rxx_frm_min;
union cvmx_gmxx_rxx_frm_max gmxx_rxx_frm_max;
union cvmx_gmxx_rxx_jabber gmxx_rxx_jabber;
if (mode & CVMX_SPI_MODE_RX_HALFPLEX) {
union cvmx_srxx_com_ctl srxx_com_ctl;
srxx_com_ctl.u64 = cvmx_read_csr(CVMX_SRXX_COM_CTL(interface));
srxx_com_ctl.s.inf_en = 1;
cvmx_write_csr(CVMX_SRXX_COM_CTL(interface), srxx_com_ctl.u64);
cvmx_dprintf("SPI%d: Rx is now up\n", interface);
}
if (mode & CVMX_SPI_MODE_TX_HALFPLEX) {
union cvmx_stxx_com_ctl stxx_com_ctl;
stxx_com_ctl.u64 = cvmx_read_csr(CVMX_STXX_COM_CTL(interface));
stxx_com_ctl.s.inf_en = 1;
cvmx_write_csr(CVMX_STXX_COM_CTL(interface), stxx_com_ctl.u64);
cvmx_dprintf("SPI%d: Tx is now up\n", interface);
}
gmxx_rxx_frm_min.u64 = 0;
gmxx_rxx_frm_min.s.len = 64;
cvmx_write_csr(CVMX_GMXX_RXX_FRM_MIN(0, interface),
gmxx_rxx_frm_min.u64);
gmxx_rxx_frm_max.u64 = 0;
gmxx_rxx_frm_max.s.len = 64 * 1024 - 4;
cvmx_write_csr(CVMX_GMXX_RXX_FRM_MAX(0, interface),
gmxx_rxx_frm_max.u64);
gmxx_rxx_jabber.u64 = 0;
gmxx_rxx_jabber.s.cnt = 64 * 1024 - 4;
cvmx_write_csr(CVMX_GMXX_RXX_JABBER(0, interface), gmxx_rxx_jabber.u64);
return 0;
}

View 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;
}

View 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;
}

View File

@@ -0,0 +1,72 @@
/*
* Octeon Bootbus flash setup
*
* 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) 2007, 2008 Cavium Networks
*/
#include <linux/kernel.h>
#include <linux/export.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/map.h>
#include <linux/mtd/partitions.h>
#include <asm/octeon/octeon.h>
static struct map_info flash_map;
static struct mtd_info *mymtd;
static const char *part_probe_types[] = {
"cmdlinepart",
#ifdef CONFIG_MTD_REDBOOT_PARTS
"RedBoot",
#endif
NULL
};
/**
* Module/ driver initialization.
*
* Returns Zero on success
*/
static int __init flash_init(void)
{
/*
* Read the bootbus region 0 setup to determine the base
* address of the flash.
*/
union cvmx_mio_boot_reg_cfgx region_cfg;
region_cfg.u64 = cvmx_read_csr(CVMX_MIO_BOOT_REG_CFGX(0));
if (region_cfg.s.en) {
/*
* The bootloader always takes the flash and sets its
* address so the entire flash fits below
* 0x1fc00000. This way the flash aliases to
* 0x1fc00000 for booting. Software can access the
* full flash at the true address, while core boot can
* access 4MB.
*/
/* Use this name so old part lines work */
flash_map.name = "phys_mapped_flash";
flash_map.phys = region_cfg.s.base << 16;
flash_map.size = 0x1fc00000 - flash_map.phys;
/* 8-bit bus (0 + 1) or 16-bit bus (1 + 1) */
flash_map.bankwidth = region_cfg.s.width + 1;
flash_map.virt = ioremap(flash_map.phys, flash_map.size);
pr_notice("Bootbus flash: Setting flash for %luMB flash at "
"0x%08llx\n", flash_map.size >> 20, flash_map.phys);
simple_map_init(&flash_map);
mymtd = do_map_probe("cfi_probe", &flash_map);
if (mymtd) {
mymtd->owner = THIS_MODULE;
mtd_device_parse_register(mymtd, part_probe_types,
NULL, NULL, 0);
} else {
pr_err("Failed to register MTD device for flash\n");
}
}
return 0;
}
late_initcall(flash_init);

View File

@@ -0,0 +1,206 @@
#include <linux/fs.h>
#include <linux/interrupt.h>
#include <asm/octeon/octeon.h>
#include <asm/octeon/cvmx-ciu-defs.h>
#include <asm/octeon/cvmx.h>
#include <linux/debugfs.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/seq_file.h>
#define TIMER_NUM 3
static bool reset_stats;
struct latency_info {
u64 io_interval;
u64 cpu_interval;
u64 timer_start1;
u64 timer_start2;
u64 max_latency;
u64 min_latency;
u64 latency_sum;
u64 average_latency;
u64 interrupt_cnt;
};
static struct latency_info li;
static struct dentry *dir;
static int show_latency(struct seq_file *m, void *v)
{
u64 cpuclk, avg, max, min;
struct latency_info curr_li = li;
cpuclk = octeon_get_clock_rate();
max = (curr_li.max_latency * 1000000000) / cpuclk;
min = (curr_li.min_latency * 1000000000) / cpuclk;
avg = (curr_li.latency_sum * 1000000000) / (cpuclk * curr_li.interrupt_cnt);
seq_printf(m, "cnt: %10lld, avg: %7lld ns, max: %7lld ns, min: %7lld ns\n",
curr_li.interrupt_cnt, avg, max, min);
return 0;
}
static int oct_ilm_open(struct inode *inode, struct file *file)
{
return single_open(file, show_latency, NULL);
}
static const struct file_operations oct_ilm_ops = {
.open = oct_ilm_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
static int reset_statistics(void *data, u64 value)
{
reset_stats = true;
return 0;
}
DEFINE_SIMPLE_ATTRIBUTE(reset_statistics_ops, NULL, reset_statistics, "%llu\n");
static int init_debufs(void)
{
struct dentry *show_dentry;
dir = debugfs_create_dir("oct_ilm", 0);
if (!dir) {
pr_err("oct_ilm: failed to create debugfs entry oct_ilm\n");
return -1;
}
show_dentry = debugfs_create_file("statistics", 0222, dir, NULL,
&oct_ilm_ops);
if (!show_dentry) {
pr_err("oct_ilm: failed to create debugfs entry oct_ilm/statistics\n");
return -1;
}
show_dentry = debugfs_create_file("reset", 0222, dir, NULL,
&reset_statistics_ops);
if (!show_dentry) {
pr_err("oct_ilm: failed to create debugfs entry oct_ilm/reset\n");
return -1;
}
return 0;
}
static void init_latency_info(struct latency_info *li, int startup)
{
/* interval in milli seconds after which the interrupt will
* be triggered
*/
int interval = 1;
if (startup) {
/* Calculating by the amounts io clock and cpu clock would
* increment in interval amount of ms
*/
li->io_interval = (octeon_get_io_clock_rate() * interval) / 1000;
li->cpu_interval = (octeon_get_clock_rate() * interval) / 1000;
}
li->timer_start1 = 0;
li->timer_start2 = 0;
li->max_latency = 0;
li->min_latency = (u64)-1;
li->latency_sum = 0;
li->interrupt_cnt = 0;
}
static void start_timer(int timer, u64 interval)
{
union cvmx_ciu_timx timx;
unsigned long flags;
timx.u64 = 0;
timx.s.one_shot = 1;
timx.s.len = interval;
raw_local_irq_save(flags);
li.timer_start1 = read_c0_cvmcount();
cvmx_write_csr(CVMX_CIU_TIMX(timer), timx.u64);
/* Read it back to force wait until register is written. */
timx.u64 = cvmx_read_csr(CVMX_CIU_TIMX(timer));
li.timer_start2 = read_c0_cvmcount();
raw_local_irq_restore(flags);
}
static irqreturn_t cvm_oct_ciu_timer_interrupt(int cpl, void *dev_id)
{
u64 last_latency;
u64 last_int_cnt;
if (reset_stats) {
init_latency_info(&li, 0);
reset_stats = false;
} else {
last_int_cnt = read_c0_cvmcount();
last_latency = last_int_cnt - (li.timer_start1 + li.cpu_interval);
li.interrupt_cnt++;
li.latency_sum += last_latency;
if (last_latency > li.max_latency)
li.max_latency = last_latency;
if (last_latency < li.min_latency)
li.min_latency = last_latency;
}
start_timer(TIMER_NUM, li.io_interval);
return IRQ_HANDLED;
}
static void disable_timer(int timer)
{
union cvmx_ciu_timx timx;
timx.s.one_shot = 0;
timx.s.len = 0;
cvmx_write_csr(CVMX_CIU_TIMX(timer), timx.u64);
/* Read it back to force immediate write of timer register*/
timx.u64 = cvmx_read_csr(CVMX_CIU_TIMX(timer));
}
static __init int oct_ilm_module_init(void)
{
int rc;
int irq = OCTEON_IRQ_TIMER0 + TIMER_NUM;
rc = init_debufs();
if (rc) {
WARN(1, "Could not create debugfs entries");
return rc;
}
rc = request_irq(irq, cvm_oct_ciu_timer_interrupt, IRQF_NO_THREAD,
"oct_ilm", 0);
if (rc) {
WARN(1, "Could not acquire IRQ %d", irq);
goto err_irq;
}
init_latency_info(&li, 1);
start_timer(TIMER_NUM, li.io_interval);
return 0;
err_irq:
debugfs_remove_recursive(dir);
return rc;
}
static __exit void oct_ilm_module_exit(void)
{
disable_timer(TIMER_NUM);
if (dir)
debugfs_remove_recursive(dir);
free_irq(OCTEON_IRQ_TIMER0 + TIMER_NUM, 0);
}
module_exit(oct_ilm_module_exit);
module_init(oct_ilm_module_init);
MODULE_AUTHOR("Venkat Subbiah, Cavium");
MODULE_DESCRIPTION("Measures interrupt latency on Octeon chips.");
MODULE_LICENSE("GPL");

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,502 @@
/*
* 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.
*
* Unified implementation of memcpy, memmove and the __copy_user backend.
*
* Copyright (C) 1998, 99, 2000, 01, 2002 Ralf Baechle (ralf@gnu.org)
* Copyright (C) 1999, 2000, 01, 2002 Silicon Graphics, Inc.
* Copyright (C) 2002 Broadcom, Inc.
* memcpy/copy_user author: Mark Vandevoorde
*
* Mnemonic names for arguments to memcpy/__copy_user
*/
#include <asm/asm.h>
#include <asm/asm-offsets.h>
#include <asm/regdef.h>
#define dst a0
#define src a1
#define len a2
/*
* Spec
*
* memcpy copies len bytes from src to dst and sets v0 to dst.
* It assumes that
* - src and dst don't overlap
* - src is readable
* - dst is writable
* memcpy uses the standard calling convention
*
* __copy_user copies up to len bytes from src to dst and sets a2 (len) to
* the number of uncopied bytes due to an exception caused by a read or write.
* __copy_user assumes that src and dst don't overlap, and that the call is
* implementing one of the following:
* copy_to_user
* - src is readable (no exceptions when reading src)
* copy_from_user
* - dst is writable (no exceptions when writing dst)
* __copy_user uses a non-standard calling convention; see
* arch/mips/include/asm/uaccess.h
*
* When an exception happens on a load, the handler must
# ensure that all of the destination buffer is overwritten to prevent
* leaking information to user mode programs.
*/
/*
* Implementation
*/
/*
* The exception handler for loads requires that:
* 1- AT contain the address of the byte just past the end of the source
* of the copy,
* 2- src_entry <= src < AT, and
* 3- (dst - src) == (dst_entry - src_entry),
* The _entry suffix denotes values when __copy_user was called.
*
* (1) is set up up by uaccess.h and maintained by not writing AT in copy_user
* (2) is met by incrementing src by the number of bytes copied
* (3) is met by not doing loads between a pair of increments of dst and src
*
* The exception handlers for stores adjust len (if necessary) and return.
* These handlers do not need to overwrite any data.
*
* For __rmemcpy and memmove an exception is always a kernel bug, therefore
* they're not protected.
*/
#define EXC(inst_reg,addr,handler) \
9: inst_reg, addr; \
.section __ex_table,"a"; \
PTR 9b, handler; \
.previous
/*
* Only on the 64-bit kernel we can made use of 64-bit registers.
*/
#define LOAD ld
#define LOADL ldl
#define LOADR ldr
#define STOREL sdl
#define STORER sdr
#define STORE sd
#define ADD daddu
#define SUB dsubu
#define SRL dsrl
#define SRA dsra
#define SLL dsll
#define SLLV dsllv
#define SRLV dsrlv
#define NBYTES 8
#define LOG_NBYTES 3
/*
* As we are sharing code base with the mips32 tree (which use the o32 ABI
* register definitions). We need to redefine the register definitions from
* the n64 ABI register naming to the o32 ABI register naming.
*/
#undef t0
#undef t1
#undef t2
#undef t3
#define t0 $8
#define t1 $9
#define t2 $10
#define t3 $11
#define t4 $12
#define t5 $13
#define t6 $14
#define t7 $15
#ifdef CONFIG_CPU_LITTLE_ENDIAN
#define LDFIRST LOADR
#define LDREST LOADL
#define STFIRST STORER
#define STREST STOREL
#define SHIFT_DISCARD SLLV
#else
#define LDFIRST LOADL
#define LDREST LOADR
#define STFIRST STOREL
#define STREST STORER
#define SHIFT_DISCARD SRLV
#endif
#define FIRST(unit) ((unit)*NBYTES)
#define REST(unit) (FIRST(unit)+NBYTES-1)
#define UNIT(unit) FIRST(unit)
#define ADDRMASK (NBYTES-1)
.text
.set noreorder
.set noat
/*
* t7 is used as a flag to note inatomic mode.
*/
LEAF(__copy_user_inatomic)
b __copy_user_common
li t7, 1
END(__copy_user_inatomic)
/*
* A combined memcpy/__copy_user
* __copy_user sets len to 0 for success; else to an upper bound of
* the number of uncopied bytes.
* memcpy sets v0 to dst.
*/
.align 5
LEAF(memcpy) /* a0=dst a1=src a2=len */
move v0, dst /* return value */
__memcpy:
FEXPORT(__copy_user)
li t7, 0 /* not inatomic */
__copy_user_common:
/*
* Note: dst & src may be unaligned, len may be 0
* Temps
*/
#
# Octeon doesn't care if the destination is unaligned. The hardware
# can fix it faster than we can special case the assembly.
#
pref 0, 0(src)
sltu t0, len, NBYTES # Check if < 1 word
bnez t0, copy_bytes_checklen
and t0, src, ADDRMASK # Check if src unaligned
bnez t0, src_unaligned
sltu t0, len, 4*NBYTES # Check if < 4 words
bnez t0, less_than_4units
sltu t0, len, 8*NBYTES # Check if < 8 words
bnez t0, less_than_8units
sltu t0, len, 16*NBYTES # Check if < 16 words
bnez t0, cleanup_both_aligned
sltu t0, len, 128+1 # Check if len < 129
bnez t0, 1f # Skip prefetch if len is too short
sltu t0, len, 256+1 # Check if len < 257
bnez t0, 1f # Skip prefetch if len is too short
pref 0, 128(src) # We must not prefetch invalid addresses
#
# This is where we loop if there is more than 128 bytes left
2: pref 0, 256(src) # We must not prefetch invalid addresses
#
# This is where we loop if we can't prefetch anymore
1:
EXC( LOAD t0, UNIT(0)(src), l_exc)
EXC( LOAD t1, UNIT(1)(src), l_exc_copy)
EXC( LOAD t2, UNIT(2)(src), l_exc_copy)
EXC( LOAD t3, UNIT(3)(src), l_exc_copy)
SUB len, len, 16*NBYTES
EXC( STORE t0, UNIT(0)(dst), s_exc_p16u)
EXC( STORE t1, UNIT(1)(dst), s_exc_p15u)
EXC( STORE t2, UNIT(2)(dst), s_exc_p14u)
EXC( STORE t3, UNIT(3)(dst), s_exc_p13u)
EXC( LOAD t0, UNIT(4)(src), l_exc_copy)
EXC( LOAD t1, UNIT(5)(src), l_exc_copy)
EXC( LOAD t2, UNIT(6)(src), l_exc_copy)
EXC( LOAD t3, UNIT(7)(src), l_exc_copy)
EXC( STORE t0, UNIT(4)(dst), s_exc_p12u)
EXC( STORE t1, UNIT(5)(dst), s_exc_p11u)
EXC( STORE t2, UNIT(6)(dst), s_exc_p10u)
ADD src, src, 16*NBYTES
EXC( STORE t3, UNIT(7)(dst), s_exc_p9u)
ADD dst, dst, 16*NBYTES
EXC( LOAD t0, UNIT(-8)(src), l_exc_copy)
EXC( LOAD t1, UNIT(-7)(src), l_exc_copy)
EXC( LOAD t2, UNIT(-6)(src), l_exc_copy)
EXC( LOAD t3, UNIT(-5)(src), l_exc_copy)
EXC( STORE t0, UNIT(-8)(dst), s_exc_p8u)
EXC( STORE t1, UNIT(-7)(dst), s_exc_p7u)
EXC( STORE t2, UNIT(-6)(dst), s_exc_p6u)
EXC( STORE t3, UNIT(-5)(dst), s_exc_p5u)
EXC( LOAD t0, UNIT(-4)(src), l_exc_copy)
EXC( LOAD t1, UNIT(-3)(src), l_exc_copy)
EXC( LOAD t2, UNIT(-2)(src), l_exc_copy)
EXC( LOAD t3, UNIT(-1)(src), l_exc_copy)
EXC( STORE t0, UNIT(-4)(dst), s_exc_p4u)
EXC( STORE t1, UNIT(-3)(dst), s_exc_p3u)
EXC( STORE t2, UNIT(-2)(dst), s_exc_p2u)
EXC( STORE t3, UNIT(-1)(dst), s_exc_p1u)
sltu t0, len, 256+1 # See if we can prefetch more
beqz t0, 2b
sltu t0, len, 128 # See if we can loop more time
beqz t0, 1b
nop
#
# Jump here if there are less than 16*NBYTES left.
#
cleanup_both_aligned:
beqz len, done
sltu t0, len, 8*NBYTES
bnez t0, less_than_8units
nop
EXC( LOAD t0, UNIT(0)(src), l_exc)
EXC( LOAD t1, UNIT(1)(src), l_exc_copy)
EXC( LOAD t2, UNIT(2)(src), l_exc_copy)
EXC( LOAD t3, UNIT(3)(src), l_exc_copy)
SUB len, len, 8*NBYTES
EXC( STORE t0, UNIT(0)(dst), s_exc_p8u)
EXC( STORE t1, UNIT(1)(dst), s_exc_p7u)
EXC( STORE t2, UNIT(2)(dst), s_exc_p6u)
EXC( STORE t3, UNIT(3)(dst), s_exc_p5u)
EXC( LOAD t0, UNIT(4)(src), l_exc_copy)
EXC( LOAD t1, UNIT(5)(src), l_exc_copy)
EXC( LOAD t2, UNIT(6)(src), l_exc_copy)
EXC( LOAD t3, UNIT(7)(src), l_exc_copy)
EXC( STORE t0, UNIT(4)(dst), s_exc_p4u)
EXC( STORE t1, UNIT(5)(dst), s_exc_p3u)
EXC( STORE t2, UNIT(6)(dst), s_exc_p2u)
EXC( STORE t3, UNIT(7)(dst), s_exc_p1u)
ADD src, src, 8*NBYTES
beqz len, done
ADD dst, dst, 8*NBYTES
#
# Jump here if there are less than 8*NBYTES left.
#
less_than_8units:
sltu t0, len, 4*NBYTES
bnez t0, less_than_4units
nop
EXC( LOAD t0, UNIT(0)(src), l_exc)
EXC( LOAD t1, UNIT(1)(src), l_exc_copy)
EXC( LOAD t2, UNIT(2)(src), l_exc_copy)
EXC( LOAD t3, UNIT(3)(src), l_exc_copy)
SUB len, len, 4*NBYTES
EXC( STORE t0, UNIT(0)(dst), s_exc_p4u)
EXC( STORE t1, UNIT(1)(dst), s_exc_p3u)
EXC( STORE t2, UNIT(2)(dst), s_exc_p2u)
EXC( STORE t3, UNIT(3)(dst), s_exc_p1u)
ADD src, src, 4*NBYTES
beqz len, done
ADD dst, dst, 4*NBYTES
#
# Jump here if there are less than 4*NBYTES left. This means
# we may need to copy up to 3 NBYTES words.
#
less_than_4units:
sltu t0, len, 1*NBYTES
bnez t0, copy_bytes_checklen
nop
#
# 1) Copy NBYTES, then check length again
#
EXC( LOAD t0, 0(src), l_exc)
SUB len, len, NBYTES
sltu t1, len, 8
EXC( STORE t0, 0(dst), s_exc_p1u)
ADD src, src, NBYTES
bnez t1, copy_bytes_checklen
ADD dst, dst, NBYTES
#
# 2) Copy NBYTES, then check length again
#
EXC( LOAD t0, 0(src), l_exc)
SUB len, len, NBYTES
sltu t1, len, 8
EXC( STORE t0, 0(dst), s_exc_p1u)
ADD src, src, NBYTES
bnez t1, copy_bytes_checklen
ADD dst, dst, NBYTES
#
# 3) Copy NBYTES, then check length again
#
EXC( LOAD t0, 0(src), l_exc)
SUB len, len, NBYTES
ADD src, src, NBYTES
ADD dst, dst, NBYTES
b copy_bytes_checklen
EXC( STORE t0, -8(dst), s_exc_p1u)
src_unaligned:
#define rem t8
SRL t0, len, LOG_NBYTES+2 # +2 for 4 units/iter
beqz t0, cleanup_src_unaligned
and rem, len, (4*NBYTES-1) # rem = len % 4*NBYTES
1:
/*
* Avoid consecutive LD*'s to the same register since some mips
* implementations can't issue them in the same cycle.
* It's OK to load FIRST(N+1) before REST(N) because the two addresses
* are to the same unit (unless src is aligned, but it's not).
*/
EXC( LDFIRST t0, FIRST(0)(src), l_exc)
EXC( LDFIRST t1, FIRST(1)(src), l_exc_copy)
SUB len, len, 4*NBYTES
EXC( LDREST t0, REST(0)(src), l_exc_copy)
EXC( LDREST t1, REST(1)(src), l_exc_copy)
EXC( LDFIRST t2, FIRST(2)(src), l_exc_copy)
EXC( LDFIRST t3, FIRST(3)(src), l_exc_copy)
EXC( LDREST t2, REST(2)(src), l_exc_copy)
EXC( LDREST t3, REST(3)(src), l_exc_copy)
ADD src, src, 4*NBYTES
EXC( STORE t0, UNIT(0)(dst), s_exc_p4u)
EXC( STORE t1, UNIT(1)(dst), s_exc_p3u)
EXC( STORE t2, UNIT(2)(dst), s_exc_p2u)
EXC( STORE t3, UNIT(3)(dst), s_exc_p1u)
bne len, rem, 1b
ADD dst, dst, 4*NBYTES
cleanup_src_unaligned:
beqz len, done
and rem, len, NBYTES-1 # rem = len % NBYTES
beq rem, len, copy_bytes
nop
1:
EXC( LDFIRST t0, FIRST(0)(src), l_exc)
EXC( LDREST t0, REST(0)(src), l_exc_copy)
SUB len, len, NBYTES
EXC( STORE t0, 0(dst), s_exc_p1u)
ADD src, src, NBYTES
bne len, rem, 1b
ADD dst, dst, NBYTES
copy_bytes_checklen:
beqz len, done
nop
copy_bytes:
/* 0 < len < NBYTES */
#define COPY_BYTE(N) \
EXC( lb t0, N(src), l_exc); \
SUB len, len, 1; \
beqz len, done; \
EXC( sb t0, N(dst), s_exc_p1)
COPY_BYTE(0)
COPY_BYTE(1)
COPY_BYTE(2)
COPY_BYTE(3)
COPY_BYTE(4)
COPY_BYTE(5)
EXC( lb t0, NBYTES-2(src), l_exc)
SUB len, len, 1
jr ra
EXC( sb t0, NBYTES-2(dst), s_exc_p1)
done:
jr ra
nop
END(memcpy)
l_exc_copy:
/*
* Copy bytes from src until faulting load address (or until a
* lb faults)
*
* When reached by a faulting LDFIRST/LDREST, THREAD_BUADDR($28)
* may be more than a byte beyond the last address.
* Hence, the lb below may get an exception.
*
* Assumes src < THREAD_BUADDR($28)
*/
LOAD t0, TI_TASK($28)
LOAD t0, THREAD_BUADDR(t0)
1:
EXC( lb t1, 0(src), l_exc)
ADD src, src, 1
sb t1, 0(dst) # can't fault -- we're copy_from_user
bne src, t0, 1b
ADD dst, dst, 1
l_exc:
LOAD t0, TI_TASK($28)
LOAD t0, THREAD_BUADDR(t0) # t0 is just past last good address
SUB len, AT, t0 # len number of uncopied bytes
bnez t7, 2f /* Skip the zeroing out part if inatomic */
/*
* Here's where we rely on src and dst being incremented in tandem,
* See (3) above.
* dst += (fault addr - src) to put dst at first byte to clear
*/
ADD dst, t0 # compute start address in a1
SUB dst, src
/*
* Clear len bytes starting at dst. Can't call __bzero because it
* might modify len. An inefficient loop for these rare times...
*/
beqz len, done
SUB src, len, 1
1: sb zero, 0(dst)
ADD dst, dst, 1
bnez src, 1b
SUB src, src, 1
2: jr ra
nop
#define SEXC(n) \
s_exc_p ## n ## u: \
jr ra; \
ADD len, len, n*NBYTES
SEXC(16)
SEXC(15)
SEXC(14)
SEXC(13)
SEXC(12)
SEXC(11)
SEXC(10)
SEXC(9)
SEXC(8)
SEXC(7)
SEXC(6)
SEXC(5)
SEXC(4)
SEXC(3)
SEXC(2)
SEXC(1)
s_exc_p1:
jr ra
ADD len, len, 1
s_exc:
jr ra
nop
.align 5
LEAF(memmove)
ADD t0, a0, a2
ADD t1, a1, a2
sltu t0, a1, t0 # dst + len <= src -> memcpy
sltu t1, a0, t1 # dst >= src + len -> memcpy
and t0, t1
beqz t0, __memcpy
move v0, a0 /* return value */
beqz a2, r_out
END(memmove)
/* fall through to __rmemcpy */
LEAF(__rmemcpy) /* a0=dst a1=src a2=len */
sltu t0, a1, a0
beqz t0, r_end_bytes_up # src >= dst
nop
ADD a0, a2 # dst = dst + len
ADD a1, a2 # src = src + len
r_end_bytes:
lb t0, -1(a1)
SUB a2, a2, 0x1
sb t0, -1(a0)
SUB a1, a1, 0x1
bnez a2, r_end_bytes
SUB a0, a0, 0x1
r_out:
jr ra
move a2, zero
r_end_bytes_up:
lb t0, (a1)
SUB a2, a2, 0x1
sb t0, (a0)
ADD a1, a1, 0x1
bnez a2, r_end_bytes_up
ADD a0, a0, 0x1
jr ra
move a2, zero
END(__rmemcpy)

View File

@@ -0,0 +1,688 @@
/*
* 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) 2004-2011 Cavium Networks
* Copyright (C) 2008 Wind River Systems
*/
#include <linux/init.h>
#include <linux/irq.h>
#include <linux/i2c.h>
#include <linux/usb.h>
#include <linux/dma-mapping.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/platform_device.h>
#include <linux/of_platform.h>
#include <linux/of_fdt.h>
#include <linux/libfdt.h>
#include <asm/octeon/octeon.h>
#include <asm/octeon/cvmx-rnm-defs.h>
#include <asm/octeon/cvmx-helper.h>
#include <asm/octeon/cvmx-helper-board.h>
/* Octeon Random Number Generator. */
static int __init octeon_rng_device_init(void)
{
struct platform_device *pd;
int ret = 0;
struct resource rng_resources[] = {
{
.flags = IORESOURCE_MEM,
.start = XKPHYS_TO_PHYS(CVMX_RNM_CTL_STATUS),
.end = XKPHYS_TO_PHYS(CVMX_RNM_CTL_STATUS) + 0xf
}, {
.flags = IORESOURCE_MEM,
.start = cvmx_build_io_address(8, 0),
.end = cvmx_build_io_address(8, 0) + 0x7
}
};
pd = platform_device_alloc("octeon_rng", -1);
if (!pd) {
ret = -ENOMEM;
goto out;
}
ret = platform_device_add_resources(pd, rng_resources,
ARRAY_SIZE(rng_resources));
if (ret)
goto fail;
ret = platform_device_add(pd);
if (ret)
goto fail;
return ret;
fail:
platform_device_put(pd);
out:
return ret;
}
device_initcall(octeon_rng_device_init);
#ifdef CONFIG_USB
static int __init octeon_ehci_device_init(void)
{
struct platform_device *pd;
int ret = 0;
struct resource usb_resources[] = {
{
.flags = IORESOURCE_MEM,
}, {
.flags = IORESOURCE_IRQ,
}
};
/* Only Octeon2 has ehci/ohci */
if (!OCTEON_IS_MODEL(OCTEON_CN63XX))
return 0;
if (octeon_is_simulation() || usb_disabled())
return 0; /* No USB in the simulator. */
pd = platform_device_alloc("octeon-ehci", 0);
if (!pd) {
ret = -ENOMEM;
goto out;
}
usb_resources[0].start = 0x00016F0000000000ULL;
usb_resources[0].end = usb_resources[0].start + 0x100;
usb_resources[1].start = OCTEON_IRQ_USB0;
usb_resources[1].end = OCTEON_IRQ_USB0;
ret = platform_device_add_resources(pd, usb_resources,
ARRAY_SIZE(usb_resources));
if (ret)
goto fail;
ret = platform_device_add(pd);
if (ret)
goto fail;
return ret;
fail:
platform_device_put(pd);
out:
return ret;
}
device_initcall(octeon_ehci_device_init);
static int __init octeon_ohci_device_init(void)
{
struct platform_device *pd;
int ret = 0;
struct resource usb_resources[] = {
{
.flags = IORESOURCE_MEM,
}, {
.flags = IORESOURCE_IRQ,
}
};
/* Only Octeon2 has ehci/ohci */
if (!OCTEON_IS_MODEL(OCTEON_CN63XX))
return 0;
if (octeon_is_simulation() || usb_disabled())
return 0; /* No USB in the simulator. */
pd = platform_device_alloc("octeon-ohci", 0);
if (!pd) {
ret = -ENOMEM;
goto out;
}
usb_resources[0].start = 0x00016F0000000400ULL;
usb_resources[0].end = usb_resources[0].start + 0x100;
usb_resources[1].start = OCTEON_IRQ_USB0;
usb_resources[1].end = OCTEON_IRQ_USB0;
ret = platform_device_add_resources(pd, usb_resources,
ARRAY_SIZE(usb_resources));
if (ret)
goto fail;
ret = platform_device_add(pd);
if (ret)
goto fail;
return ret;
fail:
platform_device_put(pd);
out:
return ret;
}
device_initcall(octeon_ohci_device_init);
#endif /* CONFIG_USB */
static struct of_device_id __initdata octeon_ids[] = {
{ .compatible = "simple-bus", },
{ .compatible = "cavium,octeon-6335-uctl", },
{ .compatible = "cavium,octeon-3860-bootbus", },
{ .compatible = "cavium,mdio-mux", },
{ .compatible = "gpio-leds", },
{},
};
static bool __init octeon_has_88e1145(void)
{
return !OCTEON_IS_MODEL(OCTEON_CN52XX) &&
!OCTEON_IS_MODEL(OCTEON_CN6XXX) &&
!OCTEON_IS_MODEL(OCTEON_CN56XX);
}
static void __init octeon_fdt_set_phy(int eth, int phy_addr)
{
const __be32 *phy_handle;
const __be32 *alt_phy_handle;
const __be32 *reg;
u32 phandle;
int phy;
int alt_phy;
const char *p;
int current_len;
char new_name[20];
phy_handle = fdt_getprop(initial_boot_params, eth, "phy-handle", NULL);
if (!phy_handle)
return;
phandle = be32_to_cpup(phy_handle);
phy = fdt_node_offset_by_phandle(initial_boot_params, phandle);
alt_phy_handle = fdt_getprop(initial_boot_params, eth, "cavium,alt-phy-handle", NULL);
if (alt_phy_handle) {
u32 alt_phandle = be32_to_cpup(alt_phy_handle);
alt_phy = fdt_node_offset_by_phandle(initial_boot_params, alt_phandle);
} else {
alt_phy = -1;
}
if (phy_addr < 0 || phy < 0) {
/* Delete the PHY things */
fdt_nop_property(initial_boot_params, eth, "phy-handle");
/* This one may fail */
fdt_nop_property(initial_boot_params, eth, "cavium,alt-phy-handle");
if (phy >= 0)
fdt_nop_node(initial_boot_params, phy);
if (alt_phy >= 0)
fdt_nop_node(initial_boot_params, alt_phy);
return;
}
if (phy_addr >= 256 && alt_phy > 0) {
const struct fdt_property *phy_prop;
struct fdt_property *alt_prop;
u32 phy_handle_name;
/* Use the alt phy node instead.*/
phy_prop = fdt_get_property(initial_boot_params, eth, "phy-handle", NULL);
phy_handle_name = phy_prop->nameoff;
fdt_nop_node(initial_boot_params, phy);
fdt_nop_property(initial_boot_params, eth, "phy-handle");
alt_prop = fdt_get_property_w(initial_boot_params, eth, "cavium,alt-phy-handle", NULL);
alt_prop->nameoff = phy_handle_name;
phy = alt_phy;
}
phy_addr &= 0xff;
if (octeon_has_88e1145()) {
fdt_nop_property(initial_boot_params, phy, "marvell,reg-init");
memset(new_name, 0, sizeof(new_name));
strcpy(new_name, "marvell,88e1145");
p = fdt_getprop(initial_boot_params, phy, "compatible",
&current_len);
if (p && current_len >= strlen(new_name))
fdt_setprop_inplace(initial_boot_params, phy,
"compatible", new_name, current_len);
}
reg = fdt_getprop(initial_boot_params, phy, "reg", NULL);
if (phy_addr == be32_to_cpup(reg))
return;
fdt_setprop_inplace_cell(initial_boot_params, phy, "reg", phy_addr);
snprintf(new_name, sizeof(new_name), "ethernet-phy@%x", phy_addr);
p = fdt_get_name(initial_boot_params, phy, &current_len);
if (p && current_len == strlen(new_name))
fdt_set_name(initial_boot_params, phy, new_name);
else
pr_err("Error: could not rename ethernet phy: <%s>", p);
}
static void __init octeon_fdt_set_mac_addr(int n, u64 *pmac)
{
u8 new_mac[6];
u64 mac = *pmac;
int r;
new_mac[0] = (mac >> 40) & 0xff;
new_mac[1] = (mac >> 32) & 0xff;
new_mac[2] = (mac >> 24) & 0xff;
new_mac[3] = (mac >> 16) & 0xff;
new_mac[4] = (mac >> 8) & 0xff;
new_mac[5] = mac & 0xff;
r = fdt_setprop_inplace(initial_boot_params, n, "local-mac-address",
new_mac, sizeof(new_mac));
if (r) {
pr_err("Setting \"local-mac-address\" failed %d", r);
return;
}
*pmac = mac + 1;
}
static void __init octeon_fdt_rm_ethernet(int node)
{
const __be32 *phy_handle;
phy_handle = fdt_getprop(initial_boot_params, node, "phy-handle", NULL);
if (phy_handle) {
u32 ph = be32_to_cpup(phy_handle);
int p = fdt_node_offset_by_phandle(initial_boot_params, ph);
if (p >= 0)
fdt_nop_node(initial_boot_params, p);
}
fdt_nop_node(initial_boot_params, node);
}
static void __init octeon_fdt_pip_port(int iface, int i, int p, int max, u64 *pmac)
{
char name_buffer[20];
int eth;
int phy_addr;
int ipd_port;
snprintf(name_buffer, sizeof(name_buffer), "ethernet@%x", p);
eth = fdt_subnode_offset(initial_boot_params, iface, name_buffer);
if (eth < 0)
return;
if (p > max) {
pr_debug("Deleting port %x:%x\n", i, p);
octeon_fdt_rm_ethernet(eth);
return;
}
if (OCTEON_IS_MODEL(OCTEON_CN68XX))
ipd_port = (0x100 * i) + (0x10 * p) + 0x800;
else
ipd_port = 16 * i + p;
phy_addr = cvmx_helper_board_get_mii_address(ipd_port);
octeon_fdt_set_phy(eth, phy_addr);
octeon_fdt_set_mac_addr(eth, pmac);
}
static void __init octeon_fdt_pip_iface(int pip, int idx, u64 *pmac)
{
char name_buffer[20];
int iface;
int p;
int count;
count = cvmx_helper_interface_enumerate(idx);
snprintf(name_buffer, sizeof(name_buffer), "interface@%d", idx);
iface = fdt_subnode_offset(initial_boot_params, pip, name_buffer);
if (iface < 0)
return;
for (p = 0; p < 16; p++)
octeon_fdt_pip_port(iface, idx, p, count - 1, pmac);
}
int __init octeon_prune_device_tree(void)
{
int i, max_port, uart_mask;
const char *pip_path;
const char *alias_prop;
char name_buffer[20];
int aliases;
u64 mac_addr_base;
if (fdt_check_header(initial_boot_params))
panic("Corrupt Device Tree.");
aliases = fdt_path_offset(initial_boot_params, "/aliases");
if (aliases < 0) {
pr_err("Error: No /aliases node in device tree.");
return -EINVAL;
}
mac_addr_base =
((octeon_bootinfo->mac_addr_base[0] & 0xffull)) << 40 |
((octeon_bootinfo->mac_addr_base[1] & 0xffull)) << 32 |
((octeon_bootinfo->mac_addr_base[2] & 0xffull)) << 24 |
((octeon_bootinfo->mac_addr_base[3] & 0xffull)) << 16 |
((octeon_bootinfo->mac_addr_base[4] & 0xffull)) << 8 |
(octeon_bootinfo->mac_addr_base[5] & 0xffull);
if (OCTEON_IS_MODEL(OCTEON_CN52XX) || OCTEON_IS_MODEL(OCTEON_CN63XX))
max_port = 2;
else if (OCTEON_IS_MODEL(OCTEON_CN56XX) || OCTEON_IS_MODEL(OCTEON_CN68XX))
max_port = 1;
else
max_port = 0;
if (octeon_bootinfo->board_type == CVMX_BOARD_TYPE_NIC10E)
max_port = 0;
for (i = 0; i < 2; i++) {
int mgmt;
snprintf(name_buffer, sizeof(name_buffer),
"mix%d", i);
alias_prop = fdt_getprop(initial_boot_params, aliases,
name_buffer, NULL);
if (alias_prop) {
mgmt = fdt_path_offset(initial_boot_params, alias_prop);
if (mgmt < 0)
continue;
if (i >= max_port) {
pr_debug("Deleting mix%d\n", i);
octeon_fdt_rm_ethernet(mgmt);
fdt_nop_property(initial_boot_params, aliases,
name_buffer);
} else {
int phy_addr = cvmx_helper_board_get_mii_address(CVMX_HELPER_BOARD_MGMT_IPD_PORT + i);
octeon_fdt_set_phy(mgmt, phy_addr);
octeon_fdt_set_mac_addr(mgmt, &mac_addr_base);
}
}
}
pip_path = fdt_getprop(initial_boot_params, aliases, "pip", NULL);
if (pip_path) {
int pip = fdt_path_offset(initial_boot_params, pip_path);
if (pip >= 0)
for (i = 0; i <= 4; i++)
octeon_fdt_pip_iface(pip, i, &mac_addr_base);
}
/* I2C */
if (OCTEON_IS_MODEL(OCTEON_CN52XX) ||
OCTEON_IS_MODEL(OCTEON_CN63XX) ||
OCTEON_IS_MODEL(OCTEON_CN68XX) ||
OCTEON_IS_MODEL(OCTEON_CN56XX))
max_port = 2;
else
max_port = 1;
for (i = 0; i < 2; i++) {
int i2c;
snprintf(name_buffer, sizeof(name_buffer),
"twsi%d", i);
alias_prop = fdt_getprop(initial_boot_params, aliases,
name_buffer, NULL);
if (alias_prop) {
i2c = fdt_path_offset(initial_boot_params, alias_prop);
if (i2c < 0)
continue;
if (i >= max_port) {
pr_debug("Deleting twsi%d\n", i);
fdt_nop_node(initial_boot_params, i2c);
fdt_nop_property(initial_boot_params, aliases,
name_buffer);
}
}
}
/* SMI/MDIO */
if (OCTEON_IS_MODEL(OCTEON_CN68XX))
max_port = 4;
else if (OCTEON_IS_MODEL(OCTEON_CN52XX) ||
OCTEON_IS_MODEL(OCTEON_CN63XX) ||
OCTEON_IS_MODEL(OCTEON_CN56XX))
max_port = 2;
else
max_port = 1;
for (i = 0; i < 2; i++) {
int i2c;
snprintf(name_buffer, sizeof(name_buffer),
"smi%d", i);
alias_prop = fdt_getprop(initial_boot_params, aliases,
name_buffer, NULL);
if (alias_prop) {
i2c = fdt_path_offset(initial_boot_params, alias_prop);
if (i2c < 0)
continue;
if (i >= max_port) {
pr_debug("Deleting smi%d\n", i);
fdt_nop_node(initial_boot_params, i2c);
fdt_nop_property(initial_boot_params, aliases,
name_buffer);
}
}
}
/* Serial */
uart_mask = 3;
/* Right now CN52XX is the only chip with a third uart */
if (OCTEON_IS_MODEL(OCTEON_CN52XX))
uart_mask |= 4; /* uart2 */
for (i = 0; i < 3; i++) {
int uart;
snprintf(name_buffer, sizeof(name_buffer),
"uart%d", i);
alias_prop = fdt_getprop(initial_boot_params, aliases,
name_buffer, NULL);
if (alias_prop) {
uart = fdt_path_offset(initial_boot_params, alias_prop);
if (uart_mask & (1 << i))
continue;
pr_debug("Deleting uart%d\n", i);
fdt_nop_node(initial_boot_params, uart);
fdt_nop_property(initial_boot_params, aliases,
name_buffer);
}
}
/* Compact Flash */
alias_prop = fdt_getprop(initial_boot_params, aliases,
"cf0", NULL);
if (alias_prop) {
union cvmx_mio_boot_reg_cfgx mio_boot_reg_cfg;
unsigned long base_ptr, region_base, region_size;
unsigned long region1_base = 0;
unsigned long region1_size = 0;
int cs, bootbus;
bool is_16bit = false;
bool is_true_ide = false;
__be32 new_reg[6];
__be32 *ranges;
int len;
int cf = fdt_path_offset(initial_boot_params, alias_prop);
base_ptr = 0;
if (octeon_bootinfo->major_version == 1
&& octeon_bootinfo->minor_version >= 1) {
if (octeon_bootinfo->compact_flash_common_base_addr)
base_ptr = octeon_bootinfo->compact_flash_common_base_addr;
} else {
base_ptr = 0x1d000800;
}
if (!base_ptr)
goto no_cf;
/* Find CS0 region. */
for (cs = 0; cs < 8; cs++) {
mio_boot_reg_cfg.u64 = cvmx_read_csr(CVMX_MIO_BOOT_REG_CFGX(cs));
region_base = mio_boot_reg_cfg.s.base << 16;
region_size = (mio_boot_reg_cfg.s.size + 1) << 16;
if (mio_boot_reg_cfg.s.en && base_ptr >= region_base
&& base_ptr < region_base + region_size) {
is_16bit = mio_boot_reg_cfg.s.width;
break;
}
}
if (cs >= 7) {
/* cs and cs + 1 are CS0 and CS1, both must be less than 8. */
goto no_cf;
}
if (!(base_ptr & 0xfffful)) {
/*
* Boot loader signals availability of DMA (true_ide
* mode) by setting low order bits of base_ptr to
* zero.
*/
/* Asume that CS1 immediately follows. */
mio_boot_reg_cfg.u64 =
cvmx_read_csr(CVMX_MIO_BOOT_REG_CFGX(cs + 1));
region1_base = mio_boot_reg_cfg.s.base << 16;
region1_size = (mio_boot_reg_cfg.s.size + 1) << 16;
if (!mio_boot_reg_cfg.s.en)
goto no_cf;
is_true_ide = true;
} else {
fdt_nop_property(initial_boot_params, cf, "cavium,true-ide");
fdt_nop_property(initial_boot_params, cf, "cavium,dma-engine-handle");
if (!is_16bit) {
__be32 width = cpu_to_be32(8);
fdt_setprop_inplace(initial_boot_params, cf,
"cavium,bus-width", &width, sizeof(width));
}
}
new_reg[0] = cpu_to_be32(cs);
new_reg[1] = cpu_to_be32(0);
new_reg[2] = cpu_to_be32(0x10000);
new_reg[3] = cpu_to_be32(cs + 1);
new_reg[4] = cpu_to_be32(0);
new_reg[5] = cpu_to_be32(0x10000);
fdt_setprop_inplace(initial_boot_params, cf,
"reg", new_reg, sizeof(new_reg));
bootbus = fdt_parent_offset(initial_boot_params, cf);
if (bootbus < 0)
goto no_cf;
ranges = fdt_getprop_w(initial_boot_params, bootbus, "ranges", &len);
if (!ranges || len < (5 * 8 * sizeof(__be32)))
goto no_cf;
ranges[(cs * 5) + 2] = cpu_to_be32(region_base >> 32);
ranges[(cs * 5) + 3] = cpu_to_be32(region_base & 0xffffffff);
ranges[(cs * 5) + 4] = cpu_to_be32(region_size);
if (is_true_ide) {
cs++;
ranges[(cs * 5) + 2] = cpu_to_be32(region1_base >> 32);
ranges[(cs * 5) + 3] = cpu_to_be32(region1_base & 0xffffffff);
ranges[(cs * 5) + 4] = cpu_to_be32(region1_size);
}
goto end_cf;
no_cf:
fdt_nop_node(initial_boot_params, cf);
end_cf:
;
}
/* 8 char LED */
alias_prop = fdt_getprop(initial_boot_params, aliases,
"led0", NULL);
if (alias_prop) {
union cvmx_mio_boot_reg_cfgx mio_boot_reg_cfg;
unsigned long base_ptr, region_base, region_size;
int cs, bootbus;
__be32 new_reg[6];
__be32 *ranges;
int len;
int led = fdt_path_offset(initial_boot_params, alias_prop);
base_ptr = octeon_bootinfo->led_display_base_addr;
if (base_ptr == 0)
goto no_led;
/* Find CS0 region. */
for (cs = 0; cs < 8; cs++) {
mio_boot_reg_cfg.u64 = cvmx_read_csr(CVMX_MIO_BOOT_REG_CFGX(cs));
region_base = mio_boot_reg_cfg.s.base << 16;
region_size = (mio_boot_reg_cfg.s.size + 1) << 16;
if (mio_boot_reg_cfg.s.en && base_ptr >= region_base
&& base_ptr < region_base + region_size)
break;
}
if (cs > 7)
goto no_led;
new_reg[0] = cpu_to_be32(cs);
new_reg[1] = cpu_to_be32(0x20);
new_reg[2] = cpu_to_be32(0x20);
new_reg[3] = cpu_to_be32(cs);
new_reg[4] = cpu_to_be32(0);
new_reg[5] = cpu_to_be32(0x20);
fdt_setprop_inplace(initial_boot_params, led,
"reg", new_reg, sizeof(new_reg));
bootbus = fdt_parent_offset(initial_boot_params, led);
if (bootbus < 0)
goto no_led;
ranges = fdt_getprop_w(initial_boot_params, bootbus, "ranges", &len);
if (!ranges || len < (5 * 8 * sizeof(__be32)))
goto no_led;
ranges[(cs * 5) + 2] = cpu_to_be32(region_base >> 32);
ranges[(cs * 5) + 3] = cpu_to_be32(region_base & 0xffffffff);
ranges[(cs * 5) + 4] = cpu_to_be32(region_size);
goto end_led;
no_led:
fdt_nop_node(initial_boot_params, led);
end_led:
;
}
/* OHCI/UHCI USB */
alias_prop = fdt_getprop(initial_boot_params, aliases,
"uctl", NULL);
if (alias_prop) {
int uctl = fdt_path_offset(initial_boot_params, alias_prop);
if (uctl >= 0 && (!OCTEON_IS_MODEL(OCTEON_CN6XXX) ||
octeon_bootinfo->board_type == CVMX_BOARD_TYPE_NIC2E)) {
pr_debug("Deleting uctl\n");
fdt_nop_node(initial_boot_params, uctl);
fdt_nop_property(initial_boot_params, aliases, "uctl");
} else if (octeon_bootinfo->board_type == CVMX_BOARD_TYPE_NIC10E ||
octeon_bootinfo->board_type == CVMX_BOARD_TYPE_NIC4E) {
/* Missing "refclk-type" defaults to crystal. */
fdt_nop_property(initial_boot_params, uctl, "refclk-type");
}
}
return 0;
}
static int __init octeon_publish_devices(void)
{
return of_platform_bus_probe(NULL, octeon_ids, NULL);
}
device_initcall(octeon_publish_devices);
MODULE_AUTHOR("David Daney <ddaney@caviumnetworks.com>");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Platform driver for Octeon SOC");

View File

@@ -0,0 +1,571 @@
/dts-v1/;
/*
* OCTEON 3XXX, 5XXX, 63XX device tree skeleton.
*
* This device tree is pruned and patched by early boot code before
* use. Because of this, it contains a super-set of the available
* devices and properties.
*/
/ {
compatible = "cavium,octeon-3860";
#address-cells = <2>;
#size-cells = <2>;
interrupt-parent = <&ciu>;
soc@0 {
compatible = "simple-bus";
#address-cells = <2>;
#size-cells = <2>;
ranges; /* Direct mapping */
ciu: interrupt-controller@1070000000000 {
compatible = "cavium,octeon-3860-ciu";
interrupt-controller;
/* Interrupts are specified by two parts:
* 1) Controller register (0 or 1)
* 2) Bit within the register (0..63)
*/
#interrupt-cells = <2>;
reg = <0x10700 0x00000000 0x0 0x7000>;
};
gpio: gpio-controller@1070000000800 {
#gpio-cells = <2>;
compatible = "cavium,octeon-3860-gpio";
reg = <0x10700 0x00000800 0x0 0x100>;
gpio-controller;
/* Interrupts are specified by two parts:
* 1) GPIO pin number (0..15)
* 2) Triggering (1 - edge rising
* 2 - edge falling
* 4 - level active high
* 8 - level active low)
*/
interrupt-controller;
#interrupt-cells = <2>;
/* The GPIO pin connect to 16 consecutive CUI bits */
interrupts = <0 16>, <0 17>, <0 18>, <0 19>,
<0 20>, <0 21>, <0 22>, <0 23>,
<0 24>, <0 25>, <0 26>, <0 27>,
<0 28>, <0 29>, <0 30>, <0 31>;
};
smi0: mdio@1180000001800 {
compatible = "cavium,octeon-3860-mdio";
#address-cells = <1>;
#size-cells = <0>;
reg = <0x11800 0x00001800 0x0 0x40>;
phy0: ethernet-phy@0 {
compatible = "marvell,88e1118";
marvell,reg-init =
/* Fix rx and tx clock transition timing */
<2 0x15 0xffcf 0>, /* Reg 2,21 Clear bits 4, 5 */
/* Adjust LED drive. */
<3 0x11 0 0x442a>, /* Reg 3,17 <- 0442a */
/* irq, blink-activity, blink-link */
<3 0x10 0 0x0242>; /* Reg 3,16 <- 0x0242 */
reg = <0>;
};
phy1: ethernet-phy@1 {
compatible = "marvell,88e1118";
marvell,reg-init =
/* Fix rx and tx clock transition timing */
<2 0x15 0xffcf 0>, /* Reg 2,21 Clear bits 4, 5 */
/* Adjust LED drive. */
<3 0x11 0 0x442a>, /* Reg 3,17 <- 0442a */
/* irq, blink-activity, blink-link */
<3 0x10 0 0x0242>; /* Reg 3,16 <- 0x0242 */
reg = <1>;
};
phy2: ethernet-phy@2 {
reg = <2>;
compatible = "marvell,88e1149r";
marvell,reg-init = <3 0x10 0 0x5777>,
<3 0x11 0 0x00aa>,
<3 0x12 0 0x4105>,
<3 0x13 0 0x0a60>;
};
phy3: ethernet-phy@3 {
reg = <3>;
compatible = "marvell,88e1149r";
marvell,reg-init = <3 0x10 0 0x5777>,
<3 0x11 0 0x00aa>,
<3 0x12 0 0x4105>,
<3 0x13 0 0x0a60>;
};
phy4: ethernet-phy@4 {
reg = <4>;
compatible = "marvell,88e1149r";
marvell,reg-init = <3 0x10 0 0x5777>,
<3 0x11 0 0x00aa>,
<3 0x12 0 0x4105>,
<3 0x13 0 0x0a60>;
};
phy5: ethernet-phy@5 {
reg = <5>;
compatible = "marvell,88e1149r";
marvell,reg-init = <3 0x10 0 0x5777>,
<3 0x11 0 0x00aa>,
<3 0x12 0 0x4105>,
<3 0x13 0 0x0a60>;
};
phy6: ethernet-phy@6 {
reg = <6>;
compatible = "marvell,88e1149r";
marvell,reg-init = <3 0x10 0 0x5777>,
<3 0x11 0 0x00aa>,
<3 0x12 0 0x4105>,
<3 0x13 0 0x0a60>;
};
phy7: ethernet-phy@7 {
reg = <7>;
compatible = "marvell,88e1149r";
marvell,reg-init = <3 0x10 0 0x5777>,
<3 0x11 0 0x00aa>,
<3 0x12 0 0x4105>,
<3 0x13 0 0x0a60>;
};
phy8: ethernet-phy@8 {
reg = <8>;
compatible = "marvell,88e1149r";
marvell,reg-init = <3 0x10 0 0x5777>,
<3 0x11 0 0x00aa>,
<3 0x12 0 0x4105>,
<3 0x13 0 0x0a60>;
};
phy9: ethernet-phy@9 {
reg = <9>;
compatible = "marvell,88e1149r";
marvell,reg-init = <3 0x10 0 0x5777>,
<3 0x11 0 0x00aa>,
<3 0x12 0 0x4105>,
<3 0x13 0 0x0a60>;
};
};
smi1: mdio@1180000001900 {
compatible = "cavium,octeon-3860-mdio";
#address-cells = <1>;
#size-cells = <0>;
reg = <0x11800 0x00001900 0x0 0x40>;
phy100: ethernet-phy@1 {
reg = <1>;
compatible = "marvell,88e1149r";
marvell,reg-init = <3 0x10 0 0x5777>,
<3 0x11 0 0x00aa>,
<3 0x12 0 0x4105>,
<3 0x13 0 0x0a60>;
interrupt-parent = <&gpio>;
interrupts = <12 8>; /* Pin 12, active low */
};
phy101: ethernet-phy@2 {
reg = <2>;
compatible = "marvell,88e1149r";
marvell,reg-init = <3 0x10 0 0x5777>,
<3 0x11 0 0x00aa>,
<3 0x12 0 0x4105>,
<3 0x13 0 0x0a60>;
interrupt-parent = <&gpio>;
interrupts = <12 8>; /* Pin 12, active low */
};
phy102: ethernet-phy@3 {
reg = <3>;
compatible = "marvell,88e1149r";
marvell,reg-init = <3 0x10 0 0x5777>,
<3 0x11 0 0x00aa>,
<3 0x12 0 0x4105>,
<3 0x13 0 0x0a60>;
interrupt-parent = <&gpio>;
interrupts = <12 8>; /* Pin 12, active low */
};
phy103: ethernet-phy@4 {
reg = <4>;
compatible = "marvell,88e1149r";
marvell,reg-init = <3 0x10 0 0x5777>,
<3 0x11 0 0x00aa>,
<3 0x12 0 0x4105>,
<3 0x13 0 0x0a60>;
interrupt-parent = <&gpio>;
interrupts = <12 8>; /* Pin 12, active low */
};
};
mix0: ethernet@1070000100000 {
compatible = "cavium,octeon-5750-mix";
reg = <0x10700 0x00100000 0x0 0x100>, /* MIX */
<0x11800 0xE0000000 0x0 0x300>, /* AGL */
<0x11800 0xE0000400 0x0 0x400>, /* AGL_SHARED */
<0x11800 0xE0002000 0x0 0x8>; /* AGL_PRT_CTL */
cell-index = <0>;
interrupts = <0 62>, <1 46>;
local-mac-address = [ 00 00 00 00 00 00 ];
phy-handle = <&phy0>;
};
mix1: ethernet@1070000100800 {
compatible = "cavium,octeon-5750-mix";
reg = <0x10700 0x00100800 0x0 0x100>, /* MIX */
<0x11800 0xE0000800 0x0 0x300>, /* AGL */
<0x11800 0xE0000400 0x0 0x400>, /* AGL_SHARED */
<0x11800 0xE0002008 0x0 0x8>; /* AGL_PRT_CTL */
cell-index = <1>;
interrupts = <1 18>, < 1 46>;
local-mac-address = [ 00 00 00 00 00 00 ];
phy-handle = <&phy1>;
};
pip: pip@11800a0000000 {
compatible = "cavium,octeon-3860-pip";
#address-cells = <1>;
#size-cells = <0>;
reg = <0x11800 0xa0000000 0x0 0x2000>;
interface@0 {
compatible = "cavium,octeon-3860-pip-interface";
#address-cells = <1>;
#size-cells = <0>;
reg = <0>; /* interface */
ethernet@0 {
compatible = "cavium,octeon-3860-pip-port";
reg = <0x0>; /* Port */
local-mac-address = [ 00 00 00 00 00 00 ];
phy-handle = <&phy2>;
cavium,alt-phy-handle = <&phy100>;
};
ethernet@1 {
compatible = "cavium,octeon-3860-pip-port";
reg = <0x1>; /* Port */
local-mac-address = [ 00 00 00 00 00 00 ];
phy-handle = <&phy3>;
cavium,alt-phy-handle = <&phy101>;
};
ethernet@2 {
compatible = "cavium,octeon-3860-pip-port";
reg = <0x2>; /* Port */
local-mac-address = [ 00 00 00 00 00 00 ];
phy-handle = <&phy4>;
cavium,alt-phy-handle = <&phy102>;
};
ethernet@3 {
compatible = "cavium,octeon-3860-pip-port";
reg = <0x3>; /* Port */
local-mac-address = [ 00 00 00 00 00 00 ];
phy-handle = <&phy5>;
cavium,alt-phy-handle = <&phy103>;
};
ethernet@4 {
compatible = "cavium,octeon-3860-pip-port";
reg = <0x4>; /* Port */
local-mac-address = [ 00 00 00 00 00 00 ];
};
ethernet@5 {
compatible = "cavium,octeon-3860-pip-port";
reg = <0x5>; /* Port */
local-mac-address = [ 00 00 00 00 00 00 ];
};
ethernet@6 {
compatible = "cavium,octeon-3860-pip-port";
reg = <0x6>; /* Port */
local-mac-address = [ 00 00 00 00 00 00 ];
};
ethernet@7 {
compatible = "cavium,octeon-3860-pip-port";
reg = <0x7>; /* Port */
local-mac-address = [ 00 00 00 00 00 00 ];
};
ethernet@8 {
compatible = "cavium,octeon-3860-pip-port";
reg = <0x8>; /* Port */
local-mac-address = [ 00 00 00 00 00 00 ];
};
ethernet@9 {
compatible = "cavium,octeon-3860-pip-port";
reg = <0x9>; /* Port */
local-mac-address = [ 00 00 00 00 00 00 ];
};
ethernet@a {
compatible = "cavium,octeon-3860-pip-port";
reg = <0xa>; /* Port */
local-mac-address = [ 00 00 00 00 00 00 ];
};
ethernet@b {
compatible = "cavium,octeon-3860-pip-port";
reg = <0xb>; /* Port */
local-mac-address = [ 00 00 00 00 00 00 ];
};
ethernet@c {
compatible = "cavium,octeon-3860-pip-port";
reg = <0xc>; /* Port */
local-mac-address = [ 00 00 00 00 00 00 ];
};
ethernet@d {
compatible = "cavium,octeon-3860-pip-port";
reg = <0xd>; /* Port */
local-mac-address = [ 00 00 00 00 00 00 ];
};
ethernet@e {
compatible = "cavium,octeon-3860-pip-port";
reg = <0xe>; /* Port */
local-mac-address = [ 00 00 00 00 00 00 ];
};
ethernet@f {
compatible = "cavium,octeon-3860-pip-port";
reg = <0xf>; /* Port */
local-mac-address = [ 00 00 00 00 00 00 ];
};
};
interface@1 {
compatible = "cavium,octeon-3860-pip-interface";
#address-cells = <1>;
#size-cells = <0>;
reg = <1>; /* interface */
ethernet@0 {
compatible = "cavium,octeon-3860-pip-port";
reg = <0x0>; /* Port */
local-mac-address = [ 00 00 00 00 00 00 ];
phy-handle = <&phy6>;
};
ethernet@1 {
compatible = "cavium,octeon-3860-pip-port";
reg = <0x1>; /* Port */
local-mac-address = [ 00 00 00 00 00 00 ];
phy-handle = <&phy7>;
};
ethernet@2 {
compatible = "cavium,octeon-3860-pip-port";
reg = <0x2>; /* Port */
local-mac-address = [ 00 00 00 00 00 00 ];
phy-handle = <&phy8>;
};
ethernet@3 {
compatible = "cavium,octeon-3860-pip-port";
reg = <0x3>; /* Port */
local-mac-address = [ 00 00 00 00 00 00 ];
phy-handle = <&phy9>;
};
};
};
twsi0: i2c@1180000001000 {
#address-cells = <1>;
#size-cells = <0>;
compatible = "cavium,octeon-3860-twsi";
reg = <0x11800 0x00001000 0x0 0x200>;
interrupts = <0 45>;
clock-frequency = <100000>;
rtc@68 {
compatible = "dallas,ds1337";
reg = <0x68>;
};
tmp@4c {
compatible = "ti,tmp421";
reg = <0x4c>;
};
};
twsi1: i2c@1180000001200 {
#address-cells = <1>;
#size-cells = <0>;
compatible = "cavium,octeon-3860-twsi";
reg = <0x11800 0x00001200 0x0 0x200>;
interrupts = <0 59>;
clock-frequency = <100000>;
};
uart0: serial@1180000000800 {
compatible = "cavium,octeon-3860-uart","ns16550";
reg = <0x11800 0x00000800 0x0 0x400>;
clock-frequency = <0>;
current-speed = <115200>;
reg-shift = <3>;
interrupts = <0 34>;
};
uart1: serial@1180000000c00 {
compatible = "cavium,octeon-3860-uart","ns16550";
reg = <0x11800 0x00000c00 0x0 0x400>;
clock-frequency = <0>;
current-speed = <115200>;
reg-shift = <3>;
interrupts = <0 35>;
};
uart2: serial@1180000000400 {
compatible = "cavium,octeon-3860-uart","ns16550";
reg = <0x11800 0x00000400 0x0 0x400>;
clock-frequency = <0>;
current-speed = <115200>;
reg-shift = <3>;
interrupts = <1 16>;
};
bootbus: bootbus@1180000000000 {
compatible = "cavium,octeon-3860-bootbus";
reg = <0x11800 0x00000000 0x0 0x200>;
/* The chip select number and offset */
#address-cells = <2>;
/* The size of the chip select region */
#size-cells = <1>;
ranges = <0 0 0x0 0x1f400000 0xc00000>,
<1 0 0x10000 0x30000000 0>,
<2 0 0x10000 0x40000000 0>,
<3 0 0x10000 0x50000000 0>,
<4 0 0x0 0x1d020000 0x10000>,
<5 0 0x0 0x1d040000 0x10000>,
<6 0 0x0 0x1d050000 0x10000>,
<7 0 0x10000 0x90000000 0>;
cavium,cs-config@0 {
compatible = "cavium,octeon-3860-bootbus-config";
cavium,cs-index = <0>;
cavium,t-adr = <20>;
cavium,t-ce = <60>;
cavium,t-oe = <60>;
cavium,t-we = <45>;
cavium,t-rd-hld = <35>;
cavium,t-wr-hld = <45>;
cavium,t-pause = <0>;
cavium,t-wait = <0>;
cavium,t-page = <35>;
cavium,t-rd-dly = <0>;
cavium,pages = <0>;
cavium,bus-width = <8>;
};
cavium,cs-config@4 {
compatible = "cavium,octeon-3860-bootbus-config";
cavium,cs-index = <4>;
cavium,t-adr = <320>;
cavium,t-ce = <320>;
cavium,t-oe = <320>;
cavium,t-we = <320>;
cavium,t-rd-hld = <320>;
cavium,t-wr-hld = <320>;
cavium,t-pause = <320>;
cavium,t-wait = <320>;
cavium,t-page = <320>;
cavium,t-rd-dly = <0>;
cavium,pages = <0>;
cavium,bus-width = <8>;
};
cavium,cs-config@5 {
compatible = "cavium,octeon-3860-bootbus-config";
cavium,cs-index = <5>;
cavium,t-adr = <5>;
cavium,t-ce = <300>;
cavium,t-oe = <125>;
cavium,t-we = <150>;
cavium,t-rd-hld = <100>;
cavium,t-wr-hld = <30>;
cavium,t-pause = <0>;
cavium,t-wait = <30>;
cavium,t-page = <320>;
cavium,t-rd-dly = <0>;
cavium,pages = <0>;
cavium,bus-width = <16>;
};
cavium,cs-config@6 {
compatible = "cavium,octeon-3860-bootbus-config";
cavium,cs-index = <6>;
cavium,t-adr = <5>;
cavium,t-ce = <300>;
cavium,t-oe = <270>;
cavium,t-we = <150>;
cavium,t-rd-hld = <100>;
cavium,t-wr-hld = <70>;
cavium,t-pause = <0>;
cavium,t-wait = <0>;
cavium,t-page = <320>;
cavium,t-rd-dly = <0>;
cavium,pages = <0>;
cavium,wait-mode;
cavium,bus-width = <16>;
};
flash0: nor@0,0 {
compatible = "cfi-flash";
reg = <0 0 0x800000>;
#address-cells = <1>;
#size-cells = <1>;
};
led0: led-display@4,0 {
compatible = "avago,hdsp-253x";
reg = <4 0x20 0x20>, <4 0 0x20>;
};
cf0: compact-flash@5,0 {
compatible = "cavium,ebt3000-compact-flash";
reg = <5 0 0x10000>, <6 0 0x10000>;
cavium,bus-width = <16>;
cavium,true-ide;
cavium,dma-engine-handle = <&dma0>;
};
};
dma0: dma-engine@1180000000100 {
compatible = "cavium,octeon-5750-bootbus-dma";
reg = <0x11800 0x00000100 0x0 0x8>;
interrupts = <0 63>;
};
dma1: dma-engine@1180000000108 {
compatible = "cavium,octeon-5750-bootbus-dma";
reg = <0x11800 0x00000108 0x0 0x8>;
interrupts = <0 63>;
};
uctl: uctl@118006f000000 {
compatible = "cavium,octeon-6335-uctl";
reg = <0x11800 0x6f000000 0x0 0x100>;
ranges; /* Direct mapping */
#address-cells = <2>;
#size-cells = <2>;
/* 12MHz, 24MHz and 48MHz allowed */
refclk-frequency = <12000000>;
/* Either "crystal" or "external" */
refclk-type = "crystal";
ehci@16f0000000000 {
compatible = "cavium,octeon-6335-ehci","usb-ehci";
reg = <0x16f00 0x00000000 0x0 0x100>;
interrupts = <0 56>;
big-endian-regs;
};
ohci@16f0000000400 {
compatible = "cavium,octeon-6335-ohci","usb-ohci";
reg = <0x16f00 0x00000400 0x0 0x100>;
interrupts = <0 56>;
big-endian-regs;
};
};
};
aliases {
mix0 = &mix0;
mix1 = &mix1;
pip = &pip;
smi0 = &smi0;
smi1 = &smi1;
twsi0 = &twsi0;
twsi1 = &twsi1;
uart0 = &uart0;
uart1 = &uart1;
uart2 = &uart2;
flash0 = &flash0;
cf0 = &cf0;
uctl = &uctl;
led0 = &led0;
};
};

View File

@@ -0,0 +1,625 @@
/dts-v1/;
/*
* OCTEON 68XX device tree skeleton.
*
* This device tree is pruned and patched by early boot code before
* use. Because of this, it contains a super-set of the available
* devices and properties.
*/
/ {
compatible = "cavium,octeon-6880";
#address-cells = <2>;
#size-cells = <2>;
interrupt-parent = <&ciu2>;
soc@0 {
compatible = "simple-bus";
#address-cells = <2>;
#size-cells = <2>;
ranges; /* Direct mapping */
ciu2: interrupt-controller@1070100000000 {
compatible = "cavium,octeon-6880-ciu2";
interrupt-controller;
/* Interrupts are specified by two parts:
* 1) Controller register (0 or 7)
* 2) Bit within the register (0..63)
*/
#address-cells = <0>;
#interrupt-cells = <2>;
reg = <0x10701 0x00000000 0x0 0x4000000>;
};
gpio: gpio-controller@1070000000800 {
#gpio-cells = <2>;
compatible = "cavium,octeon-3860-gpio";
reg = <0x10700 0x00000800 0x0 0x100>;
gpio-controller;
/* Interrupts are specified by two parts:
* 1) GPIO pin number (0..15)
* 2) Triggering (1 - edge rising
* 2 - edge falling
* 4 - level active high
* 8 - level active low)
*/
interrupt-controller;
#interrupt-cells = <2>;
/* The GPIO pins connect to 16 consecutive CUI bits */
interrupts = <7 0>, <7 1>, <7 2>, <7 3>,
<7 4>, <7 5>, <7 6>, <7 7>,
<7 8>, <7 9>, <7 10>, <7 11>,
<7 12>, <7 13>, <7 14>, <7 15>;
};
smi0: mdio@1180000003800 {
compatible = "cavium,octeon-3860-mdio";
#address-cells = <1>;
#size-cells = <0>;
reg = <0x11800 0x00003800 0x0 0x40>;
phy0: ethernet-phy@6 {
compatible = "marvell,88e1118";
marvell,reg-init =
/* Fix rx and tx clock transition timing */
<2 0x15 0xffcf 0>, /* Reg 2,21 Clear bits 4, 5 */
/* Adjust LED drive. */
<3 0x11 0 0x442a>, /* Reg 3,17 <- 0442a */
/* irq, blink-activity, blink-link */
<3 0x10 0 0x0242>; /* Reg 3,16 <- 0x0242 */
reg = <6>;
};
phy1: ethernet-phy@1 {
cavium,qlm-trim = "4,sgmii";
reg = <1>;
compatible = "marvell,88e1149r";
marvell,reg-init = <3 0x10 0 0x5777>,
<3 0x11 0 0x00aa>,
<3 0x12 0 0x4105>,
<3 0x13 0 0x0a60>;
};
phy2: ethernet-phy@2 {
cavium,qlm-trim = "4,sgmii";
reg = <2>;
compatible = "marvell,88e1149r";
marvell,reg-init = <3 0x10 0 0x5777>,
<3 0x11 0 0x00aa>,
<3 0x12 0 0x4105>,
<3 0x13 0 0x0a60>;
};
phy3: ethernet-phy@3 {
cavium,qlm-trim = "4,sgmii";
reg = <3>;
compatible = "marvell,88e1149r";
marvell,reg-init = <3 0x10 0 0x5777>,
<3 0x11 0 0x00aa>,
<3 0x12 0 0x4105>,
<3 0x13 0 0x0a60>;
};
phy4: ethernet-phy@4 {
cavium,qlm-trim = "4,sgmii";
reg = <4>;
compatible = "marvell,88e1149r";
marvell,reg-init = <3 0x10 0 0x5777>,
<3 0x11 0 0x00aa>,
<3 0x12 0 0x4105>,
<3 0x13 0 0x0a60>;
};
};
smi1: mdio@1180000003880 {
compatible = "cavium,octeon-3860-mdio";
#address-cells = <1>;
#size-cells = <0>;
reg = <0x11800 0x00003880 0x0 0x40>;
phy41: ethernet-phy@1 {
cavium,qlm-trim = "0,sgmii";
reg = <1>;
compatible = "marvell,88e1149r";
marvell,reg-init = <3 0x10 0 0x5777>,
<3 0x11 0 0x00aa>,
<3 0x12 0 0x4105>,
<3 0x13 0 0x0a60>;
};
phy42: ethernet-phy@2 {
cavium,qlm-trim = "0,sgmii";
reg = <2>;
compatible = "marvell,88e1149r";
marvell,reg-init = <3 0x10 0 0x5777>,
<3 0x11 0 0x00aa>,
<3 0x12 0 0x4105>,
<3 0x13 0 0x0a60>;
};
phy43: ethernet-phy@3 {
cavium,qlm-trim = "0,sgmii";
reg = <3>;
compatible = "marvell,88e1149r";
marvell,reg-init = <3 0x10 0 0x5777>,
<3 0x11 0 0x00aa>,
<3 0x12 0 0x4105>,
<3 0x13 0 0x0a60>;
};
phy44: ethernet-phy@4 {
cavium,qlm-trim = "0,sgmii";
reg = <4>;
compatible = "marvell,88e1149r";
marvell,reg-init = <3 0x10 0 0x5777>,
<3 0x11 0 0x00aa>,
<3 0x12 0 0x4105>,
<3 0x13 0 0x0a60>;
};
};
smi2: mdio@1180000003900 {
compatible = "cavium,octeon-3860-mdio";
#address-cells = <1>;
#size-cells = <0>;
reg = <0x11800 0x00003900 0x0 0x40>;
phy21: ethernet-phy@1 {
cavium,qlm-trim = "2,sgmii";
reg = <1>;
compatible = "marvell,88e1149r";
marvell,reg-init = <3 0x10 0 0x5777>,
<3 0x11 0 0x00aa>,
<3 0x12 0 0x4105>,
<3 0x13 0 0x0a60>;
};
phy22: ethernet-phy@2 {
cavium,qlm-trim = "2,sgmii";
reg = <2>;
compatible = "marvell,88e1149r";
marvell,reg-init = <3 0x10 0 0x5777>,
<3 0x11 0 0x00aa>,
<3 0x12 0 0x4105>,
<3 0x13 0 0x0a60>;
};
phy23: ethernet-phy@3 {
cavium,qlm-trim = "2,sgmii";
reg = <3>;
compatible = "marvell,88e1149r";
marvell,reg-init = <3 0x10 0 0x5777>,
<3 0x11 0 0x00aa>,
<3 0x12 0 0x4105>,
<3 0x13 0 0x0a60>;
};
phy24: ethernet-phy@4 {
cavium,qlm-trim = "2,sgmii";
reg = <4>;
compatible = "marvell,88e1149r";
marvell,reg-init = <3 0x10 0 0x5777>,
<3 0x11 0 0x00aa>,
<3 0x12 0 0x4105>,
<3 0x13 0 0x0a60>;
};
};
smi3: mdio@1180000003980 {
compatible = "cavium,octeon-3860-mdio";
#address-cells = <1>;
#size-cells = <0>;
reg = <0x11800 0x00003980 0x0 0x40>;
phy11: ethernet-phy@1 {
cavium,qlm-trim = "3,sgmii";
reg = <1>;
compatible = "marvell,88e1149r";
marvell,reg-init = <3 0x10 0 0x5777>,
<3 0x11 0 0x00aa>,
<3 0x12 0 0x4105>,
<3 0x13 0 0x0a60>;
};
phy12: ethernet-phy@2 {
cavium,qlm-trim = "3,sgmii";
reg = <2>;
compatible = "marvell,88e1149r";
marvell,reg-init = <3 0x10 0 0x5777>,
<3 0x11 0 0x00aa>,
<3 0x12 0 0x4105>,
<3 0x13 0 0x0a60>;
};
phy13: ethernet-phy@3 {
cavium,qlm-trim = "3,sgmii";
reg = <3>;
compatible = "marvell,88e1149r";
marvell,reg-init = <3 0x10 0 0x5777>,
<3 0x11 0 0x00aa>,
<3 0x12 0 0x4105>,
<3 0x13 0 0x0a60>;
};
phy14: ethernet-phy@4 {
cavium,qlm-trim = "3,sgmii";
reg = <4>;
compatible = "marvell,88e1149r";
marvell,reg-init = <3 0x10 0 0x5777>,
<3 0x11 0 0x00aa>,
<3 0x12 0 0x4105>,
<3 0x13 0 0x0a60>;
};
};
mix0: ethernet@1070000100000 {
compatible = "cavium,octeon-5750-mix";
reg = <0x10700 0x00100000 0x0 0x100>, /* MIX */
<0x11800 0xE0000000 0x0 0x300>, /* AGL */
<0x11800 0xE0000400 0x0 0x400>, /* AGL_SHARED */
<0x11800 0xE0002000 0x0 0x8>; /* AGL_PRT_CTL */
cell-index = <0>;
interrupts = <6 40>, <6 32>;
local-mac-address = [ 00 00 00 00 00 00 ];
phy-handle = <&phy0>;
};
pip: pip@11800a0000000 {
compatible = "cavium,octeon-3860-pip";
#address-cells = <1>;
#size-cells = <0>;
reg = <0x11800 0xa0000000 0x0 0x2000>;
interface@4 {
compatible = "cavium,octeon-3860-pip-interface";
#address-cells = <1>;
#size-cells = <0>;
reg = <0x4>; /* interface */
ethernet@0 {
compatible = "cavium,octeon-3860-pip-port";
reg = <0x0>; /* Port */
local-mac-address = [ 00 00 00 00 00 00 ];
phy-handle = <&phy1>;
};
ethernet@1 {
compatible = "cavium,octeon-3860-pip-port";
reg = <0x1>; /* Port */
local-mac-address = [ 00 00 00 00 00 00 ];
phy-handle = <&phy2>;
};
ethernet@2 {
compatible = "cavium,octeon-3860-pip-port";
reg = <0x2>; /* Port */
local-mac-address = [ 00 00 00 00 00 00 ];
phy-handle = <&phy3>;
};
ethernet@3 {
compatible = "cavium,octeon-3860-pip-port";
reg = <0x3>; /* Port */
local-mac-address = [ 00 00 00 00 00 00 ];
phy-handle = <&phy4>;
};
};
interface@3 {
compatible = "cavium,octeon-3860-pip-interface";
#address-cells = <1>;
#size-cells = <0>;
reg = <0x3>; /* interface */
ethernet@0 {
compatible = "cavium,octeon-3860-pip-port";
reg = <0x0>; /* Port */
local-mac-address = [ 00 00 00 00 00 00 ];
phy-handle = <&phy11>;
};
ethernet@1 {
compatible = "cavium,octeon-3860-pip-port";
reg = <0x1>; /* Port */
local-mac-address = [ 00 00 00 00 00 00 ];
phy-handle = <&phy12>;
};
ethernet@2 {
compatible = "cavium,octeon-3860-pip-port";
reg = <0x2>; /* Port */
local-mac-address = [ 00 00 00 00 00 00 ];
phy-handle = <&phy13>;
};
ethernet@3 {
compatible = "cavium,octeon-3860-pip-port";
reg = <0x3>; /* Port */
local-mac-address = [ 00 00 00 00 00 00 ];
phy-handle = <&phy14>;
};
};
interface@2 {
compatible = "cavium,octeon-3860-pip-interface";
#address-cells = <1>;
#size-cells = <0>;
reg = <0x2>; /* interface */
ethernet@0 {
compatible = "cavium,octeon-3860-pip-port";
reg = <0x0>; /* Port */
local-mac-address = [ 00 00 00 00 00 00 ];
phy-handle = <&phy21>;
};
ethernet@1 {
compatible = "cavium,octeon-3860-pip-port";
reg = <0x1>; /* Port */
local-mac-address = [ 00 00 00 00 00 00 ];
phy-handle = <&phy22>;
};
ethernet@2 {
compatible = "cavium,octeon-3860-pip-port";
reg = <0x2>; /* Port */
local-mac-address = [ 00 00 00 00 00 00 ];
phy-handle = <&phy23>;
};
ethernet@3 {
compatible = "cavium,octeon-3860-pip-port";
reg = <0x3>; /* Port */
local-mac-address = [ 00 00 00 00 00 00 ];
phy-handle = <&phy24>;
};
};
interface@1 {
compatible = "cavium,octeon-3860-pip-interface";
#address-cells = <1>;
#size-cells = <0>;
reg = <0x1>; /* interface */
ethernet@0 {
compatible = "cavium,octeon-3860-pip-port";
reg = <0x0>; /* Port */
local-mac-address = [ 00 00 00 00 00 00 ];
};
};
interface@0 {
compatible = "cavium,octeon-3860-pip-interface";
#address-cells = <1>;
#size-cells = <0>;
reg = <0x0>; /* interface */
ethernet@0 {
compatible = "cavium,octeon-3860-pip-port";
reg = <0x0>; /* Port */
local-mac-address = [ 00 00 00 00 00 00 ];
phy-handle = <&phy41>;
};
ethernet@1 {
compatible = "cavium,octeon-3860-pip-port";
reg = <0x1>; /* Port */
local-mac-address = [ 00 00 00 00 00 00 ];
phy-handle = <&phy42>;
};
ethernet@2 {
compatible = "cavium,octeon-3860-pip-port";
reg = <0x2>; /* Port */
local-mac-address = [ 00 00 00 00 00 00 ];
phy-handle = <&phy43>;
};
ethernet@3 {
compatible = "cavium,octeon-3860-pip-port";
reg = <0x3>; /* Port */
local-mac-address = [ 00 00 00 00 00 00 ];
phy-handle = <&phy44>;
};
};
};
twsi0: i2c@1180000001000 {
#address-cells = <1>;
#size-cells = <0>;
compatible = "cavium,octeon-3860-twsi";
reg = <0x11800 0x00001000 0x0 0x200>;
interrupts = <3 32>;
clock-frequency = <100000>;
rtc@68 {
compatible = "dallas,ds1337";
reg = <0x68>;
};
tmp@4c {
compatible = "ti,tmp421";
reg = <0x4c>;
};
};
twsi1: i2c@1180000001200 {
#address-cells = <1>;
#size-cells = <0>;
compatible = "cavium,octeon-3860-twsi";
reg = <0x11800 0x00001200 0x0 0x200>;
interrupts = <3 33>;
clock-frequency = <100000>;
};
uart0: serial@1180000000800 {
compatible = "cavium,octeon-3860-uart","ns16550";
reg = <0x11800 0x00000800 0x0 0x400>;
clock-frequency = <0>;
current-speed = <115200>;
reg-shift = <3>;
interrupts = <3 36>;
};
uart1: serial@1180000000c00 {
compatible = "cavium,octeon-3860-uart","ns16550";
reg = <0x11800 0x00000c00 0x0 0x400>;
clock-frequency = <0>;
current-speed = <115200>;
reg-shift = <3>;
interrupts = <3 37>;
};
bootbus: bootbus@1180000000000 {
compatible = "cavium,octeon-3860-bootbus";
reg = <0x11800 0x00000000 0x0 0x200>;
/* The chip select number and offset */
#address-cells = <2>;
/* The size of the chip select region */
#size-cells = <1>;
ranges = <0 0 0 0x1f400000 0xc00000>,
<1 0 0x10000 0x30000000 0>,
<2 0 0x10000 0x40000000 0>,
<3 0 0x10000 0x50000000 0>,
<4 0 0 0x1d020000 0x10000>,
<5 0 0 0x1d040000 0x10000>,
<6 0 0 0x1d050000 0x10000>,
<7 0 0x10000 0x90000000 0>;
cavium,cs-config@0 {
compatible = "cavium,octeon-3860-bootbus-config";
cavium,cs-index = <0>;
cavium,t-adr = <10>;
cavium,t-ce = <50>;
cavium,t-oe = <50>;
cavium,t-we = <35>;
cavium,t-rd-hld = <25>;
cavium,t-wr-hld = <35>;
cavium,t-pause = <0>;
cavium,t-wait = <300>;
cavium,t-page = <25>;
cavium,t-rd-dly = <0>;
cavium,pages = <0>;
cavium,bus-width = <8>;
};
cavium,cs-config@4 {
compatible = "cavium,octeon-3860-bootbus-config";
cavium,cs-index = <4>;
cavium,t-adr = <320>;
cavium,t-ce = <320>;
cavium,t-oe = <320>;
cavium,t-we = <320>;
cavium,t-rd-hld = <320>;
cavium,t-wr-hld = <320>;
cavium,t-pause = <320>;
cavium,t-wait = <320>;
cavium,t-page = <320>;
cavium,t-rd-dly = <0>;
cavium,pages = <0>;
cavium,bus-width = <8>;
};
cavium,cs-config@5 {
compatible = "cavium,octeon-3860-bootbus-config";
cavium,cs-index = <5>;
cavium,t-adr = <0>;
cavium,t-ce = <300>;
cavium,t-oe = <125>;
cavium,t-we = <150>;
cavium,t-rd-hld = <100>;
cavium,t-wr-hld = <300>;
cavium,t-pause = <0>;
cavium,t-wait = <300>;
cavium,t-page = <310>;
cavium,t-rd-dly = <0>;
cavium,pages = <0>;
cavium,bus-width = <16>;
};
cavium,cs-config@6 {
compatible = "cavium,octeon-3860-bootbus-config";
cavium,cs-index = <6>;
cavium,t-adr = <0>;
cavium,t-ce = <30>;
cavium,t-oe = <125>;
cavium,t-we = <150>;
cavium,t-rd-hld = <100>;
cavium,t-wr-hld = <30>;
cavium,t-pause = <0>;
cavium,t-wait = <30>;
cavium,t-page = <310>;
cavium,t-rd-dly = <0>;
cavium,pages = <0>;
cavium,wait-mode;
cavium,bus-width = <16>;
};
flash0: nor@0,0 {
compatible = "cfi-flash";
reg = <0 0 0x800000>;
#address-cells = <1>;
#size-cells = <1>;
partition@0 {
label = "bootloader";
reg = <0 0x200000>;
read-only;
};
partition@200000 {
label = "kernel";
reg = <0x200000 0x200000>;
};
partition@400000 {
label = "cramfs";
reg = <0x400000 0x3fe000>;
};
partition@7fe000 {
label = "environment";
reg = <0x7fe000 0x2000>;
read-only;
};
};
led0: led-display@4,0 {
compatible = "avago,hdsp-253x";
reg = <4 0x20 0x20>, <4 0 0x20>;
};
compact-flash@5,0 {
compatible = "cavium,ebt3000-compact-flash";
reg = <5 0 0x10000>, <6 0 0x10000>;
cavium,bus-width = <16>;
cavium,true-ide;
cavium,dma-engine-handle = <&dma0>;
};
};
dma0: dma-engine@1180000000100 {
compatible = "cavium,octeon-5750-bootbus-dma";
reg = <0x11800 0x00000100 0x0 0x8>;
interrupts = <0 63>;
};
dma1: dma-engine@1180000000108 {
compatible = "cavium,octeon-5750-bootbus-dma";
reg = <0x11800 0x00000108 0x0 0x8>;
interrupts = <0 63>;
};
uctl: uctl@118006f000000 {
compatible = "cavium,octeon-6335-uctl";
reg = <0x11800 0x6f000000 0x0 0x100>;
ranges; /* Direct mapping */
#address-cells = <2>;
#size-cells = <2>;
/* 12MHz, 24MHz and 48MHz allowed */
refclk-frequency = <12000000>;
/* Either "crystal" or "external" */
refclk-type = "crystal";
ehci@16f0000000000 {
compatible = "cavium,octeon-6335-ehci","usb-ehci";
reg = <0x16f00 0x00000000 0x0 0x100>;
interrupts = <3 44>;
big-endian-regs;
};
ohci@16f0000000400 {
compatible = "cavium,octeon-6335-ohci","usb-ohci";
reg = <0x16f00 0x00000400 0x0 0x100>;
interrupts = <3 44>;
big-endian-regs;
};
};
};
aliases {
mix0 = &mix0;
pip = &pip;
smi0 = &smi0;
smi1 = &smi1;
smi2 = &smi2;
smi3 = &smi3;
twsi0 = &twsi0;
twsi1 = &twsi1;
uart0 = &uart0;
uart1 = &uart1;
uctl = &uctl;
led0 = &led0;
flash0 = &flash0;
};
};

View File

@@ -0,0 +1,72 @@
/*
* (C) Copyright 2004, 2005 Cavium Networks
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*/
#ifndef __OCTEON_BOOT_H__
#define __OCTEON_BOOT_H__
#include <linux/types.h>
struct boot_init_vector {
/* First stage address - in ram instead of flash */
uint64_t code_addr;
/* Setup code for application, NOT application entry point */
uint32_t app_start_func_addr;
/* k0 is used for global data - needs to be passed to other cores */
uint32_t k0_val;
/* Address of boot info block structure */
uint64_t boot_info_addr;
uint32_t flags; /* flags */
uint32_t pad;
};
/* similar to bootloader's linux_app_boot_info but without global data */
struct linux_app_boot_info {
uint32_t labi_signature;
uint32_t start_core0_addr;
uint32_t avail_coremask;
uint32_t pci_console_active;
uint32_t icache_prefetch_disable;
uint64_t InitTLBStart_addr;
uint32_t start_app_addr;
uint32_t cur_exception_base;
uint32_t no_mark_private_data;
uint32_t compact_flash_common_base_addr;
uint32_t compact_flash_attribute_base_addr;
uint32_t led_display_base_addr;
};
/* If not to copy a lot of bootloader's structures
here is only offset of requested member */
#define AVAIL_COREMASK_OFFSET_IN_LINUX_APP_BOOT_BLOCK 0x765c
/* hardcoded in bootloader */
#define LABI_ADDR_IN_BOOTLOADER 0x700
#define LINUX_APP_BOOT_BLOCK_NAME "linux-app-boot"
#define LABI_SIGNATURE 0xAABBCC01
/* from uboot-headers/octeon_mem_map.h */
#define EXCEPTION_BASE_INCR (4 * 1024)
/* Increment size for exception base addresses (4k minimum) */
#define EXCEPTION_BASE_BASE 0
#define BOOTLOADER_PRIV_DATA_BASE (EXCEPTION_BASE_BASE + 0x800)
#define BOOTLOADER_BOOT_VECTOR (BOOTLOADER_PRIV_DATA_BASE)
#endif /* __OCTEON_BOOT_H__ */

View File

@@ -0,0 +1,109 @@
/*
* 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) 2004-2007 Cavium Networks
*/
#include <linux/console.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/serial.h>
#include <linux/serial_8250.h>
#include <linux/serial_reg.h>
#include <linux/tty.h>
#include <linux/irq.h>
#include <asm/time.h>
#include <asm/octeon/octeon.h>
#define DEBUG_UART 1
unsigned int octeon_serial_in(struct uart_port *up, int offset)
{
int rv = cvmx_read_csr((uint64_t)(up->membase + (offset << 3)));
if (offset == UART_IIR && (rv & 0xf) == 7) {
/* Busy interrupt, read the USR (39) and try again. */
cvmx_read_csr((uint64_t)(up->membase + (39 << 3)));
rv = cvmx_read_csr((uint64_t)(up->membase + (offset << 3)));
}
return rv;
}
void octeon_serial_out(struct uart_port *up, int offset, int value)
{
/*
* If bits 6 or 7 of the OCTEON UART's LCR are set, it quits
* working.
*/
if (offset == UART_LCR)
value &= 0x9f;
cvmx_write_csr((uint64_t)(up->membase + (offset << 3)), (u8)value);
}
static int octeon_serial_probe(struct platform_device *pdev)
{
int irq, res;
struct resource *res_mem;
struct uart_8250_port up;
/* All adaptors have an irq. */
irq = platform_get_irq(pdev, 0);
if (irq < 0)
return irq;
memset(&up, 0, sizeof(up));
up.port.flags = ASYNC_SKIP_TEST | UPF_SHARE_IRQ | UPF_FIXED_TYPE;
up.port.type = PORT_OCTEON;
up.port.iotype = UPIO_MEM;
up.port.regshift = 3;
up.port.dev = &pdev->dev;
if (octeon_is_simulation())
/* Make simulator output fast*/
up.port.uartclk = 115200 * 16;
else
up.port.uartclk = octeon_get_io_clock_rate();
up.port.serial_in = octeon_serial_in;
up.port.serial_out = octeon_serial_out;
up.port.irq = irq;
res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (res_mem == NULL) {
dev_err(&pdev->dev, "found no memory resource\n");
return -ENXIO;
}
up.port.mapbase = res_mem->start;
up.port.membase = ioremap(res_mem->start, resource_size(res_mem));
res = serial8250_register_8250_port(&up);
return res >= 0 ? 0 : res;
}
static struct of_device_id octeon_serial_match[] = {
{
.compatible = "cavium,octeon-3860-uart",
},
{},
};
MODULE_DEVICE_TABLE(of, octeon_serial_match);
static struct platform_driver octeon_serial_driver = {
.probe = octeon_serial_probe,
.driver = {
.owner = THIS_MODULE,
.name = "octeon_serial",
.of_match_table = octeon_serial_match,
},
};
static int __init octeon_serial_init(void)
{
return platform_driver_register(&octeon_serial_driver);
}
late_initcall(octeon_serial_init);

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,419 @@
/*
* 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) 2004-2008, 2009, 2010 Cavium Networks
*/
#include <linux/cpu.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/smp.h>
#include <linux/interrupt.h>
#include <linux/kernel_stat.h>
#include <linux/sched.h>
#include <linux/module.h>
#include <asm/mmu_context.h>
#include <asm/time.h>
#include <asm/setup.h>
#include <asm/octeon/octeon.h>
#include "octeon_boot.h"
volatile unsigned long octeon_processor_boot = 0xff;
volatile unsigned long octeon_processor_sp;
volatile unsigned long octeon_processor_gp;
#ifdef CONFIG_HOTPLUG_CPU
uint64_t octeon_bootloader_entry_addr;
EXPORT_SYMBOL(octeon_bootloader_entry_addr);
#endif
static irqreturn_t mailbox_interrupt(int irq, void *dev_id)
{
const int coreid = cvmx_get_core_num();
uint64_t action;
/* Load the mailbox register to figure out what we're supposed to do */
action = cvmx_read_csr(CVMX_CIU_MBOX_CLRX(coreid)) & 0xffff;
/* Clear the mailbox to clear the interrupt */
cvmx_write_csr(CVMX_CIU_MBOX_CLRX(coreid), action);
if (action & SMP_CALL_FUNCTION)
smp_call_function_interrupt();
if (action & SMP_RESCHEDULE_YOURSELF)
scheduler_ipi();
/* Check if we've been told to flush the icache */
if (action & SMP_ICACHE_FLUSH)
asm volatile ("synci 0($0)\n");
return IRQ_HANDLED;
}
/**
* Cause the function described by call_data to be executed on the passed
* cpu. When the function has finished, increment the finished field of
* call_data.
*/
void octeon_send_ipi_single(int cpu, unsigned int action)
{
int coreid = cpu_logical_map(cpu);
/*
pr_info("SMP: Mailbox send cpu=%d, coreid=%d, action=%u\n", cpu,
coreid, action);
*/
cvmx_write_csr(CVMX_CIU_MBOX_SETX(coreid), action);
}
static inline void octeon_send_ipi_mask(const struct cpumask *mask,
unsigned int action)
{
unsigned int i;
for_each_cpu_mask(i, *mask)
octeon_send_ipi_single(i, action);
}
/**
* Detect available CPUs, populate cpu_possible_mask
*/
static void octeon_smp_hotplug_setup(void)
{
#ifdef CONFIG_HOTPLUG_CPU
struct linux_app_boot_info *labi;
labi = (struct linux_app_boot_info *)PHYS_TO_XKSEG_CACHED(LABI_ADDR_IN_BOOTLOADER);
if (labi->labi_signature != LABI_SIGNATURE)
panic("The bootloader version on this board is incorrect.");
octeon_bootloader_entry_addr = labi->InitTLBStart_addr;
#endif
}
static void octeon_smp_setup(void)
{
const int coreid = cvmx_get_core_num();
int cpus;
int id;
int core_mask = octeon_get_boot_coremask();
#ifdef CONFIG_HOTPLUG_CPU
unsigned int num_cores = cvmx_octeon_num_cores();
#endif
/* The present CPUs are initially just the boot cpu (CPU 0). */
for (id = 0; id < NR_CPUS; id++) {
set_cpu_possible(id, id == 0);
set_cpu_present(id, id == 0);
}
__cpu_number_map[coreid] = 0;
__cpu_logical_map[0] = coreid;
/* The present CPUs get the lowest CPU numbers. */
cpus = 1;
for (id = 0; id < NR_CPUS; id++) {
if ((id != coreid) && (core_mask & (1 << id))) {
set_cpu_possible(cpus, true);
set_cpu_present(cpus, true);
__cpu_number_map[id] = cpus;
__cpu_logical_map[cpus] = id;
cpus++;
}
}
#ifdef CONFIG_HOTPLUG_CPU
/*
* The possible CPUs are all those present on the chip. We
* will assign CPU numbers for possible cores as well. Cores
* are always consecutively numberd from 0.
*/
for (id = 0; id < num_cores && id < NR_CPUS; id++) {
if (!(core_mask & (1 << id))) {
set_cpu_possible(cpus, true);
__cpu_number_map[id] = cpus;
__cpu_logical_map[cpus] = id;
cpus++;
}
}
#endif
octeon_smp_hotplug_setup();
}
/**
* Firmware CPU startup hook
*
*/
static void octeon_boot_secondary(int cpu, struct task_struct *idle)
{
int count;
pr_info("SMP: Booting CPU%02d (CoreId %2d)...\n", cpu,
cpu_logical_map(cpu));
octeon_processor_sp = __KSTK_TOS(idle);
octeon_processor_gp = (unsigned long)(task_thread_info(idle));
octeon_processor_boot = cpu_logical_map(cpu);
mb();
count = 10000;
while (octeon_processor_sp && count) {
/* Waiting for processor to get the SP and GP */
udelay(1);
count--;
}
if (count == 0)
pr_err("Secondary boot timeout\n");
}
/**
* After we've done initial boot, this function is called to allow the
* board code to clean up state, if needed
*/
static void __cpuinit octeon_init_secondary(void)
{
unsigned int sr;
sr = set_c0_status(ST0_BEV);
write_c0_ebase((u32)ebase);
write_c0_status(sr);
octeon_check_cpu_bist();
octeon_init_cvmcount();
octeon_irq_setup_secondary();
}
/**
* Callout to firmware before smp_init
*
*/
void octeon_prepare_cpus(unsigned int max_cpus)
{
#ifdef CONFIG_HOTPLUG_CPU
struct linux_app_boot_info *labi;
labi = (struct linux_app_boot_info *)PHYS_TO_XKSEG_CACHED(LABI_ADDR_IN_BOOTLOADER);
if (labi->labi_signature != LABI_SIGNATURE)
panic("The bootloader version on this board is incorrect.");
#endif
/*
* Only the low order mailbox bits are used for IPIs, leave
* the other bits alone.
*/
cvmx_write_csr(CVMX_CIU_MBOX_CLRX(cvmx_get_core_num()), 0xffff);
if (request_irq(OCTEON_IRQ_MBOX0, mailbox_interrupt,
IRQF_PERCPU | IRQF_NO_THREAD, "SMP-IPI",
mailbox_interrupt)) {
panic("Cannot request_irq(OCTEON_IRQ_MBOX0)");
}
}
/**
* Last chance for the board code to finish SMP initialization before
* the CPU is "online".
*/
static void octeon_smp_finish(void)
{
#ifdef CONFIG_CAVIUM_GDB
unsigned long tmp;
/* Pulse MCD0 signal on Ctrl-C to stop all the cores. Also set the MCD0
to be not masked by this core so we know the signal is received by
someone */
asm volatile ("dmfc0 %0, $22\n"
"ori %0, %0, 0x9100\n" "dmtc0 %0, $22\n" : "=r" (tmp));
#endif
octeon_user_io_init();
/* to generate the first CPU timer interrupt */
write_c0_compare(read_c0_count() + mips_hpt_frequency / HZ);
local_irq_enable();
}
/**
* Hook for after all CPUs are online
*/
static void octeon_cpus_done(void)
{
#ifdef CONFIG_CAVIUM_GDB
unsigned long tmp;
/* Pulse MCD0 signal on Ctrl-C to stop all the cores. Also set the MCD0
to be not masked by this core so we know the signal is received by
someone */
asm volatile ("dmfc0 %0, $22\n"
"ori %0, %0, 0x9100\n" "dmtc0 %0, $22\n" : "=r" (tmp));
#endif
}
#ifdef CONFIG_HOTPLUG_CPU
/* State of each CPU. */
DEFINE_PER_CPU(int, cpu_state);
extern void fixup_irqs(void);
static int octeon_cpu_disable(void)
{
unsigned int cpu = smp_processor_id();
if (cpu == 0)
return -EBUSY;
set_cpu_online(cpu, false);
cpu_clear(cpu, cpu_callin_map);
local_irq_disable();
fixup_irqs();
local_irq_enable();
flush_cache_all();
local_flush_tlb_all();
return 0;
}
static void octeon_cpu_die(unsigned int cpu)
{
int coreid = cpu_logical_map(cpu);
uint32_t mask, new_mask;
const struct cvmx_bootmem_named_block_desc *block_desc;
while (per_cpu(cpu_state, cpu) != CPU_DEAD)
cpu_relax();
/*
* This is a bit complicated strategics of getting/settig available
* cores mask, copied from bootloader
*/
mask = 1 << coreid;
/* LINUX_APP_BOOT_BLOCK is initialized in bootoct binary */
block_desc = cvmx_bootmem_find_named_block(LINUX_APP_BOOT_BLOCK_NAME);
if (!block_desc) {
struct linux_app_boot_info *labi;
labi = (struct linux_app_boot_info *)PHYS_TO_XKSEG_CACHED(LABI_ADDR_IN_BOOTLOADER);
labi->avail_coremask |= mask;
new_mask = labi->avail_coremask;
} else { /* alternative, already initialized */
uint32_t *p = (uint32_t *)PHYS_TO_XKSEG_CACHED(block_desc->base_addr +
AVAIL_COREMASK_OFFSET_IN_LINUX_APP_BOOT_BLOCK);
*p |= mask;
new_mask = *p;
}
pr_info("Reset core %d. Available Coremask = 0x%x \n", coreid, new_mask);
mb();
cvmx_write_csr(CVMX_CIU_PP_RST, 1 << coreid);
cvmx_write_csr(CVMX_CIU_PP_RST, 0);
}
void play_dead(void)
{
int cpu = cpu_number_map(cvmx_get_core_num());
idle_task_exit();
octeon_processor_boot = 0xff;
per_cpu(cpu_state, cpu) = CPU_DEAD;
mb();
while (1) /* core will be reset here */
;
}
extern void kernel_entry(unsigned long arg1, ...);
static void start_after_reset(void)
{
kernel_entry(0, 0, 0); /* set a2 = 0 for secondary core */
}
static int octeon_update_boot_vector(unsigned int cpu)
{
int coreid = cpu_logical_map(cpu);
uint32_t avail_coremask;
const struct cvmx_bootmem_named_block_desc *block_desc;
struct boot_init_vector *boot_vect =
(struct boot_init_vector *)PHYS_TO_XKSEG_CACHED(BOOTLOADER_BOOT_VECTOR);
block_desc = cvmx_bootmem_find_named_block(LINUX_APP_BOOT_BLOCK_NAME);
if (!block_desc) {
struct linux_app_boot_info *labi;
labi = (struct linux_app_boot_info *)PHYS_TO_XKSEG_CACHED(LABI_ADDR_IN_BOOTLOADER);
avail_coremask = labi->avail_coremask;
labi->avail_coremask &= ~(1 << coreid);
} else { /* alternative, already initialized */
avail_coremask = *(uint32_t *)PHYS_TO_XKSEG_CACHED(
block_desc->base_addr + AVAIL_COREMASK_OFFSET_IN_LINUX_APP_BOOT_BLOCK);
}
if (!(avail_coremask & (1 << coreid))) {
/* core not available, assume, that catched by simple-executive */
cvmx_write_csr(CVMX_CIU_PP_RST, 1 << coreid);
cvmx_write_csr(CVMX_CIU_PP_RST, 0);
}
boot_vect[coreid].app_start_func_addr =
(uint32_t) (unsigned long) start_after_reset;
boot_vect[coreid].code_addr = octeon_bootloader_entry_addr;
mb();
cvmx_write_csr(CVMX_CIU_NMI, (1 << coreid) & avail_coremask);
return 0;
}
static int __cpuinit octeon_cpu_callback(struct notifier_block *nfb,
unsigned long action, void *hcpu)
{
unsigned int cpu = (unsigned long)hcpu;
switch (action) {
case CPU_UP_PREPARE:
octeon_update_boot_vector(cpu);
break;
case CPU_ONLINE:
pr_info("Cpu %d online\n", cpu);
break;
case CPU_DEAD:
break;
}
return NOTIFY_OK;
}
static int __cpuinit register_cavium_notifier(void)
{
hotcpu_notifier(octeon_cpu_callback, 0);
return 0;
}
late_initcall(register_cavium_notifier);
#endif /* CONFIG_HOTPLUG_CPU */
struct plat_smp_ops octeon_smp_ops = {
.send_ipi_single = octeon_send_ipi_single,
.send_ipi_mask = octeon_send_ipi_mask,
.init_secondary = octeon_init_secondary,
.smp_finish = octeon_smp_finish,
.cpus_done = octeon_cpus_done,
.boot_secondary = octeon_boot_secondary,
.smp_setup = octeon_smp_setup,
.prepare_cpus = octeon_prepare_cpus,
#ifdef CONFIG_HOTPLUG_CPU
.cpu_disable = octeon_cpu_disable,
.cpu_die = octeon_cpu_die,
#endif
};