aboutsummaryrefslogtreecommitdiff
path: root/arch/mips/cavium-octeon
diff options
context:
space:
mode:
Diffstat (limited to 'arch/mips/cavium-octeon')
-rw-r--r--arch/mips/cavium-octeon/.gitignore2
-rw-r--r--arch/mips/cavium-octeon/Kconfig69
-rw-r--r--arch/mips/cavium-octeon/Makefile24
-rw-r--r--arch/mips/cavium-octeon/Platform11
-rw-r--r--arch/mips/cavium-octeon/cpu.c48
-rw-r--r--arch/mips/cavium-octeon/csrc-octeon.c151
-rw-r--r--arch/mips/cavium-octeon/dma-octeon.c360
-rw-r--r--arch/mips/cavium-octeon/executive/Makefile6
-rw-r--r--arch/mips/cavium-octeon/executive/cvmx-bootmem.c123
-rw-r--r--arch/mips/cavium-octeon/executive/cvmx-cmd-queue.c307
-rw-r--r--arch/mips/cavium-octeon/executive/cvmx-helper-board.c751
-rw-r--r--arch/mips/cavium-octeon/executive/cvmx-helper-errata.c73
-rw-r--r--arch/mips/cavium-octeon/executive/cvmx-helper-jtag.c144
-rw-r--r--arch/mips/cavium-octeon/executive/cvmx-helper-loop.c85
-rw-r--r--arch/mips/cavium-octeon/executive/cvmx-helper-npi.c113
-rw-r--r--arch/mips/cavium-octeon/executive/cvmx-helper-rgmii.c526
-rw-r--r--arch/mips/cavium-octeon/executive/cvmx-helper-sgmii.c554
-rw-r--r--arch/mips/cavium-octeon/executive/cvmx-helper-spi.c205
-rw-r--r--arch/mips/cavium-octeon/executive/cvmx-helper-util.c437
-rw-r--r--arch/mips/cavium-octeon/executive/cvmx-helper-xaui.c354
-rw-r--r--arch/mips/cavium-octeon/executive/cvmx-helper.c1290
-rw-r--r--arch/mips/cavium-octeon/executive/cvmx-interrupt-decodes.c371
-rw-r--r--arch/mips/cavium-octeon/executive/cvmx-interrupt-rsl.c140
-rw-r--r--arch/mips/cavium-octeon/executive/cvmx-l2c.c824
-rw-r--r--arch/mips/cavium-octeon/executive/cvmx-pko.c507
-rw-r--r--arch/mips/cavium-octeon/executive/cvmx-spi.c668
-rw-r--r--arch/mips/cavium-octeon/executive/cvmx-sysinfo.c19
-rw-r--r--arch/mips/cavium-octeon/executive/octeon-model.c121
-rw-r--r--arch/mips/cavium-octeon/flash_setup.c24
-rw-r--r--arch/mips/cavium-octeon/oct_ilm.c206
-rw-r--r--arch/mips/cavium-octeon/octeon-irq.c1961
-rw-r--r--arch/mips/cavium-octeon/octeon-memcpy.S65
-rw-r--r--arch/mips/cavium-octeon/octeon-platform.c728
-rw-r--r--arch/mips/cavium-octeon/octeon_3xxx.dts590
-rw-r--r--arch/mips/cavium-octeon/octeon_68xx.dts625
-rw-r--r--arch/mips/cavium-octeon/octeon_boot.h72
-rw-r--r--arch/mips/cavium-octeon/serial.c136
-rw-r--r--arch/mips/cavium-octeon/setup.c873
-rw-r--r--arch/mips/cavium-octeon/smp.c300
39 files changed, 12516 insertions, 1347 deletions
diff --git a/arch/mips/cavium-octeon/.gitignore b/arch/mips/cavium-octeon/.gitignore
new file mode 100644
index 00000000000..39c968605ff
--- /dev/null
+++ b/arch/mips/cavium-octeon/.gitignore
@@ -0,0 +1,2 @@
+*.dtb.S
+*.dtb
diff --git a/arch/mips/cavium-octeon/Kconfig b/arch/mips/cavium-octeon/Kconfig
index 094c17e38e1..60286665793 100644
--- a/arch/mips/cavium-octeon/Kconfig
+++ b/arch/mips/cavium-octeon/Kconfig
@@ -1,33 +1,17 @@
-config CAVIUM_OCTEON_SPECIFIC_OPTIONS
- bool "Enable Octeon specific options"
- depends on CPU_CAVIUM_OCTEON
- default "y"
+if CPU_CAVIUM_OCTEON
-config CAVIUM_OCTEON_2ND_KERNEL
- bool "Build the kernel to be used as a 2nd kernel on the same chip"
- depends on CAVIUM_OCTEON_SPECIFIC_OPTIONS
+config CAVIUM_CN63XXP1
+ bool "Enable CN63XXP1 errata worarounds"
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"
- depends on CAVIUM_OCTEON_SPECIFIC_OPTIONS
- 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.
+ 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_CVMSEG_SIZE
int "Number of L1 cache lines reserved for CVMSEG memory"
- depends on CAVIUM_OCTEON_SPECIFIC_OPTIONS
range 0 54
default 1
help
@@ -37,9 +21,21 @@ config CAVIUM_OCTEON_CVMSEG_SIZE
legally range is from zero to 54 cache blocks (i.e. CVMSEG LM is
between zero and 6192 bytes).
+endif # CPU_CAVIUM_OCTEON
+
+if CAVIUM_OCTEON_SOC
+
+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_LOCK_L2
bool "Lock often used kernel code in the L2"
- depends on CAVIUM_OCTEON_SPECIFIC_OPTIONS
default "y"
help
Enable locking parts of the kernel into the L2 cache.
@@ -79,7 +75,24 @@ config CAVIUM_OCTEON_LOCK_L2_MEMCPY
help
Lock the kernel's implementation of memcpy() into L2.
-config ARCH_SPARSEMEM_ENABLE
+config IOMMU_HELPER
+ bool
+
+config NEED_SG_DMA_LENGTH
+ bool
+
+config SWIOTLB
def_bool y
- select SPARSEMEM_STATIC
- depends on CPU_CAVIUM_OCTEON
+ 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 # CAVIUM_OCTEON_SOC
diff --git a/arch/mips/cavium-octeon/Makefile b/arch/mips/cavium-octeon/Makefile
index 1c2a7faf588..4e952043c92 100644
--- a/arch/mips/cavium-octeon/Makefile
+++ b/arch/mips/cavium-octeon/Makefile
@@ -6,11 +6,27 @@
# License. See the file "COPYING" in the main directory of this archive
# for more details.
#
-# Copyright (C) 2005-2008 Cavium Networks
+# Copyright (C) 2005-2009 Cavium Networks
#
-obj-y := setup.o serial.o octeon-irq.o csrc-octeon.o
-obj-y += dma-octeon.o flash_setup.o
+CFLAGS_octeon-platform.o = -I$(src)/../../../scripts/dtc/libfdt
+CFLAGS_setup.o = -I$(src)/../../../scripts/dtc/libfdt
+
+obj-y := cpu.o setup.o octeon-platform.o octeon-irq.o csrc-octeon.o
+obj-y += dma-octeon.o
obj-y += octeon-memcpy.o
+obj-y += executive/
+
+obj-$(CONFIG_MTD) += flash_setup.o
+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))
-obj-$(CONFIG_SMP) += smp.o
+clean-files += $(DTB_FILES) $(patsubst %.dtb, %.dtb.S, $(DTB_FILES))
diff --git a/arch/mips/cavium-octeon/Platform b/arch/mips/cavium-octeon/Platform
new file mode 100644
index 00000000000..8a301cb12d6
--- /dev/null
+++ b/arch/mips/cavium-octeon/Platform
@@ -0,0 +1,11 @@
+#
+# Cavium Octeon
+#
+platform-$(CONFIG_CAVIUM_OCTEON_SOC) += cavium-octeon/
+cflags-$(CONFIG_CAVIUM_OCTEON_SOC) += \
+ -I$(srctree)/arch/mips/include/asm/mach-cavium-octeon
+ifdef CONFIG_CAVIUM_OCTEON_2ND_KERNEL
+load-$(CONFIG_CAVIUM_OCTEON_SOC) += 0xffffffff84100000
+else
+load-$(CONFIG_CAVIUM_OCTEON_SOC) += 0xffffffff81100000
+endif
diff --git a/arch/mips/cavium-octeon/cpu.c b/arch/mips/cavium-octeon/cpu.c
new file mode 100644
index 00000000000..a5b427909b5
--- /dev/null
+++ b/arch/mips/cavium-octeon/cpu.c
@@ -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);
diff --git a/arch/mips/cavium-octeon/csrc-octeon.c b/arch/mips/cavium-octeon/csrc-octeon.c
index 70fd92c3165..b752c4ed0b7 100644
--- a/arch/mips/cavium-octeon/csrc-octeon.c
+++ b/arch/mips/cavium-octeon/csrc-octeon.c
@@ -4,14 +4,47 @@
* 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/cpu-type.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
@@ -19,6 +52,7 @@
* 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)
{
@@ -33,12 +67,24 @@ void octeon_init_cvmcount(void)
* Loop several times so we are executing from the cache,
* which should give more deterministic timing.
*/
- while (loops--)
- write_c0_cvmcount(cvmx_read_csr(CVMX_IPD_CLK_COUNT));
+ 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(void)
+static cycle_t octeon_cvmcount_read(struct clocksource *cs)
{
return read_c0_cvmcount();
}
@@ -50,9 +96,104 @@ static struct clocksource clocksource_mips = {
.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_set_clock(&clocksource_mips, mips_hpt_frequency);
- clocksource_register(&clocksource_mips);
+ 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);
diff --git a/arch/mips/cavium-octeon/dma-octeon.c b/arch/mips/cavium-octeon/dma-octeon.c
index 01b1ef94b36..02f24447520 100644
--- a/arch/mips/cavium-octeon/dma-octeon.c
+++ b/arch/mips/cavium-octeon/dma-octeon.c
@@ -8,25 +8,363 @@
* 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.
- * Cavium Networks: Create new dma setup for Cavium Networks Octeon based on
- * the kernels original.
+ * 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 <dma-coherence.h>
+#include <asm/bootinfo.h>
-dma_addr_t octeon_map_dma_mem(struct device *dev, void *ptr, size_t size)
+#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)
{
- /* Without PCI/PCIe this function can be called for Octeon internal
- devices such as USB. These devices all support 64bit addressing */
+ int r = swiotlb_map_sg_attrs(dev, sg, nents, direction, attrs);
mb();
- return virt_to_phys(ptr);
+ return r;
}
-void octeon_unmap_dma_mem(struct device *dev, dma_addr_t dma_addr)
+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)
{
- /* Without PCI/PCIe this function can be called for Octeon internal
- * devices such as USB. These devices all support 64bit addressing */
- return;
+ 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 */
diff --git a/arch/mips/cavium-octeon/executive/Makefile b/arch/mips/cavium-octeon/executive/Makefile
index 80d6cb26766..b6d6e841a98 100644
--- a/arch/mips/cavium-octeon/executive/Makefile
+++ b/arch/mips/cavium-octeon/executive/Makefile
@@ -10,4 +10,10 @@
#
obj-y += cvmx-bootmem.o cvmx-l2c.o cvmx-sysinfo.o octeon-model.o
+obj-y += cvmx-pko.o cvmx-spi.o cvmx-cmd-queue.o \
+ cvmx-helper-board.o cvmx-helper.o cvmx-helper-xaui.o \
+ cvmx-helper-rgmii.o cvmx-helper-sgmii.o cvmx-helper-npi.o \
+ cvmx-helper-loop.o cvmx-helper-spi.o cvmx-helper-util.o \
+ cvmx-interrupt-decodes.o cvmx-interrupt-rsl.o
+obj-y += cvmx-helper-errata.o cvmx-helper-jtag.o
diff --git a/arch/mips/cavium-octeon/executive/cvmx-bootmem.c b/arch/mips/cavium-octeon/executive/cvmx-bootmem.c
index 4f5a08b37cc..504ed61a47c 100644
--- a/arch/mips/cavium-octeon/executive/cvmx-bootmem.c
+++ b/arch/mips/cavium-octeon/executive/cvmx-bootmem.c
@@ -31,6 +31,7 @@
*/
#include <linux/kernel.h>
+#include <linux/module.h>
#include <asm/octeon/cvmx.h>
#include <asm/octeon/cvmx-spinlock.h>
@@ -97,6 +98,33 @@ 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);
@@ -106,6 +134,7 @@ 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)
{
@@ -126,8 +155,8 @@ int cvmx_bootmem_init(void *mem_desc_ptr)
*
* 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
+ * 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
@@ -224,7 +253,7 @@ int64_t cvmx_bootmem_phy_alloc(uint64_t req_size, uint64_t address_min,
* impossible requests up front. (NOP for address_min == 0)
*/
if (alignment)
- address_min = __ALIGN_MASK(address_min, (alignment - 1));
+ address_min = ALIGN(address_min, alignment);
/*
* Reject inconsistent args. We have adjusted these, so this
@@ -262,7 +291,7 @@ int64_t cvmx_bootmem_phy_alloc(uint64_t req_size, uint64_t address_min,
* satisfy request.
*/
usable_base =
- __ALIGN_MASK(max(address_min, ent_addr), alignment - 1);
+ 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
@@ -369,7 +398,7 @@ error_out:
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 */
+ uint64_t prev_addr = 0; /* zero is invalid */
int retval = 0;
#ifdef DEBUG
@@ -395,7 +424,7 @@ int __cvmx_bootmem_phy_free(uint64_t phy_addr, uint64_t size, uint32_t flags)
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 */
+ 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,
@@ -582,5 +611,85 @@ int cvmx_bootmem_phy_named_block_free(char *name, uint32_t flags)
}
cvmx_bootmem_unlock();
- return named_block_ptr != NULL; /* 0 on failure, 1 on success */
+ 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;
}
diff --git a/arch/mips/cavium-octeon/executive/cvmx-cmd-queue.c b/arch/mips/cavium-octeon/executive/cvmx-cmd-queue.c
new file mode 100644
index 00000000000..8241fc6aa17
--- /dev/null
+++ b/arch/mips/cavium-octeon/executive/cvmx-cmd-queue.c
@@ -0,0 +1,307 @@
+/***********************license start***************
+ * Author: Cavium Networks
+ *
+ * Contact: support@caviumnetworks.com
+ * This file is part of the OCTEON SDK
+ *
+ * Copyright (c) 2003-2008 Cavium Networks
+ *
+ * This file is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, Version 2, as
+ * published by the Free Software Foundation.
+ *
+ * This file is distributed in the hope that it will be useful, but
+ * AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or
+ * NONINFRINGEMENT. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this file; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ * or visit http://www.gnu.org/licenses/.
+ *
+ * This file may also be available under a different license from Cavium.
+ * Contact Cavium Networks for more information
+ ***********************license end**************************************/
+
+/*
+ * Support functions for managing command queues used for
+ * various hardware blocks.
+ */
+
+#include <linux/kernel.h>
+
+#include <asm/octeon/octeon.h>
+
+#include <asm/octeon/cvmx-config.h>
+#include <asm/octeon/cvmx-fpa.h>
+#include <asm/octeon/cvmx-cmd-queue.h>
+
+#include <asm/octeon/cvmx-npei-defs.h>
+#include <asm/octeon/cvmx-pexp-defs.h>
+#include <asm/octeon/cvmx-pko-defs.h>
+
+/**
+ * This application uses this pointer to access the global queue
+ * state. It points to a bootmem named block.
+ */
+__cvmx_cmd_queue_all_state_t *__cvmx_cmd_queue_state_ptr;
+EXPORT_SYMBOL_GPL(__cvmx_cmd_queue_state_ptr);
+
+/**
+ * Initialize the Global queue state pointer.
+ *
+ * Returns CVMX_CMD_QUEUE_SUCCESS or a failure code
+ */
+static cvmx_cmd_queue_result_t __cvmx_cmd_queue_init_state_ptr(void)
+{
+ char *alloc_name = "cvmx_cmd_queues";
+#if defined(CONFIG_CAVIUM_RESERVE32) && CONFIG_CAVIUM_RESERVE32
+ extern uint64_t octeon_reserve32_memory;
+#endif
+
+ if (likely(__cvmx_cmd_queue_state_ptr))
+ return CVMX_CMD_QUEUE_SUCCESS;
+
+#if defined(CONFIG_CAVIUM_RESERVE32) && CONFIG_CAVIUM_RESERVE32
+ if (octeon_reserve32_memory)
+ __cvmx_cmd_queue_state_ptr =
+ cvmx_bootmem_alloc_named_range(sizeof(*__cvmx_cmd_queue_state_ptr),
+ octeon_reserve32_memory,
+ octeon_reserve32_memory +
+ (CONFIG_CAVIUM_RESERVE32 <<
+ 20) - 1, 128, alloc_name);
+ else
+#endif
+ __cvmx_cmd_queue_state_ptr =
+ cvmx_bootmem_alloc_named(sizeof(*__cvmx_cmd_queue_state_ptr),
+ 128,
+ alloc_name);
+ if (__cvmx_cmd_queue_state_ptr)
+ memset(__cvmx_cmd_queue_state_ptr, 0,
+ sizeof(*__cvmx_cmd_queue_state_ptr));
+ else {
+ struct cvmx_bootmem_named_block_desc *block_desc =
+ cvmx_bootmem_find_named_block(alloc_name);
+ if (block_desc)
+ __cvmx_cmd_queue_state_ptr =
+ cvmx_phys_to_ptr(block_desc->base_addr);
+ else {
+ cvmx_dprintf
+ ("ERROR: cvmx_cmd_queue_initialize: Unable to get named block %s.\n",
+ alloc_name);
+ return CVMX_CMD_QUEUE_NO_MEMORY;
+ }
+ }
+ return CVMX_CMD_QUEUE_SUCCESS;
+}
+
+/**
+ * Initialize a command queue for use. The initial FPA buffer is
+ * allocated and the hardware unit is configured to point to the
+ * new command queue.
+ *
+ * @queue_id: Hardware command queue to initialize.
+ * @max_depth: Maximum outstanding commands that can be queued.
+ * @fpa_pool: FPA pool the command queues should come from.
+ * @pool_size: Size of each buffer in the FPA pool (bytes)
+ *
+ * Returns CVMX_CMD_QUEUE_SUCCESS or a failure code
+ */
+cvmx_cmd_queue_result_t cvmx_cmd_queue_initialize(cvmx_cmd_queue_id_t queue_id,
+ int max_depth, int fpa_pool,
+ int pool_size)
+{
+ __cvmx_cmd_queue_state_t *qstate;
+ cvmx_cmd_queue_result_t result = __cvmx_cmd_queue_init_state_ptr();
+ if (result != CVMX_CMD_QUEUE_SUCCESS)
+ return result;
+
+ qstate = __cvmx_cmd_queue_get_state(queue_id);
+ if (qstate == NULL)
+ return CVMX_CMD_QUEUE_INVALID_PARAM;
+
+ /*
+ * We artificially limit max_depth to 1<<20 words. It is an
+ * arbitrary limit.
+ */
+ if (CVMX_CMD_QUEUE_ENABLE_MAX_DEPTH) {
+ if ((max_depth < 0) || (max_depth > 1 << 20))
+ return CVMX_CMD_QUEUE_INVALID_PARAM;
+ } else if (max_depth != 0)
+ return CVMX_CMD_QUEUE_INVALID_PARAM;
+
+ if ((fpa_pool < 0) || (fpa_pool > 7))
+ return CVMX_CMD_QUEUE_INVALID_PARAM;
+ if ((pool_size < 128) || (pool_size > 65536))
+ return CVMX_CMD_QUEUE_INVALID_PARAM;
+
+ /* See if someone else has already initialized the queue */
+ if (qstate->base_ptr_div128) {
+ if (max_depth != (int)qstate->max_depth) {
+ cvmx_dprintf("ERROR: cvmx_cmd_queue_initialize: "
+ "Queue already initialized with different "
+ "max_depth (%d).\n",
+ (int)qstate->max_depth);
+ return CVMX_CMD_QUEUE_INVALID_PARAM;
+ }
+ if (fpa_pool != qstate->fpa_pool) {
+ cvmx_dprintf("ERROR: cvmx_cmd_queue_initialize: "
+ "Queue already initialized with different "
+ "FPA pool (%u).\n",
+ qstate->fpa_pool);
+ return CVMX_CMD_QUEUE_INVALID_PARAM;
+ }
+ if ((pool_size >> 3) - 1 != qstate->pool_size_m1) {
+ cvmx_dprintf("ERROR: cvmx_cmd_queue_initialize: "
+ "Queue already initialized with different "
+ "FPA pool size (%u).\n",
+ (qstate->pool_size_m1 + 1) << 3);
+ return CVMX_CMD_QUEUE_INVALID_PARAM;
+ }
+ CVMX_SYNCWS;
+ return CVMX_CMD_QUEUE_ALREADY_SETUP;
+ } else {
+ union cvmx_fpa_ctl_status status;
+ void *buffer;
+
+ status.u64 = cvmx_read_csr(CVMX_FPA_CTL_STATUS);
+ if (!status.s.enb) {
+ cvmx_dprintf("ERROR: cvmx_cmd_queue_initialize: "
+ "FPA is not enabled.\n");
+ return CVMX_CMD_QUEUE_NO_MEMORY;
+ }
+ buffer = cvmx_fpa_alloc(fpa_pool);
+ if (buffer == NULL) {
+ cvmx_dprintf("ERROR: cvmx_cmd_queue_initialize: "
+ "Unable to allocate initial buffer.\n");
+ return CVMX_CMD_QUEUE_NO_MEMORY;
+ }
+
+ memset(qstate, 0, sizeof(*qstate));
+ qstate->max_depth = max_depth;
+ qstate->fpa_pool = fpa_pool;
+ qstate->pool_size_m1 = (pool_size >> 3) - 1;
+ qstate->base_ptr_div128 = cvmx_ptr_to_phys(buffer) / 128;
+ /*
+ * We zeroed the now serving field so we need to also
+ * zero the ticket.
+ */
+ __cvmx_cmd_queue_state_ptr->
+ ticket[__cvmx_cmd_queue_get_index(queue_id)] = 0;
+ CVMX_SYNCWS;
+ return CVMX_CMD_QUEUE_SUCCESS;
+ }
+}
+
+/**
+ * Shutdown a queue a free it's command buffers to the FPA. The
+ * hardware connected to the queue must be stopped before this
+ * function is called.
+ *
+ * @queue_id: Queue to shutdown
+ *
+ * Returns CVMX_CMD_QUEUE_SUCCESS or a failure code
+ */
+cvmx_cmd_queue_result_t cvmx_cmd_queue_shutdown(cvmx_cmd_queue_id_t queue_id)
+{
+ __cvmx_cmd_queue_state_t *qptr = __cvmx_cmd_queue_get_state(queue_id);
+ if (qptr == NULL) {
+ cvmx_dprintf("ERROR: cvmx_cmd_queue_shutdown: Unable to "
+ "get queue information.\n");
+ return CVMX_CMD_QUEUE_INVALID_PARAM;
+ }
+
+ if (cvmx_cmd_queue_length(queue_id) > 0) {
+ cvmx_dprintf("ERROR: cvmx_cmd_queue_shutdown: Queue still "
+ "has data in it.\n");
+ return CVMX_CMD_QUEUE_FULL;
+ }
+
+ __cvmx_cmd_queue_lock(queue_id, qptr);
+ if (qptr->base_ptr_div128) {
+ cvmx_fpa_free(cvmx_phys_to_ptr
+ ((uint64_t) qptr->base_ptr_div128 << 7),
+ qptr->fpa_pool, 0);
+ qptr->base_ptr_div128 = 0;
+ }
+ __cvmx_cmd_queue_unlock(qptr);
+
+ return CVMX_CMD_QUEUE_SUCCESS;
+}
+
+/**
+ * Return the number of command words pending in the queue. This
+ * function may be relatively slow for some hardware units.
+ *
+ * @queue_id: Hardware command queue to query
+ *
+ * Returns Number of outstanding commands
+ */
+int cvmx_cmd_queue_length(cvmx_cmd_queue_id_t queue_id)
+{
+ if (CVMX_ENABLE_PARAMETER_CHECKING) {
+ if (__cvmx_cmd_queue_get_state(queue_id) == NULL)
+ return CVMX_CMD_QUEUE_INVALID_PARAM;
+ }
+
+ /*
+ * The cast is here so gcc with check that all values in the
+ * cvmx_cmd_queue_id_t enumeration are here.
+ */
+ switch ((cvmx_cmd_queue_id_t) (queue_id & 0xff0000)) {
+ case CVMX_CMD_QUEUE_PKO_BASE:
+ /*
+ * FIXME: Need atomic lock on
+ * CVMX_PKO_REG_READ_IDX. Right now we are normally
+ * called with the queue lock, so that is a SLIGHT
+ * amount of protection.
+ */
+ cvmx_write_csr(CVMX_PKO_REG_READ_IDX, queue_id & 0xffff);
+ if (OCTEON_IS_MODEL(OCTEON_CN3XXX)) {
+ union cvmx_pko_mem_debug9 debug9;
+ debug9.u64 = cvmx_read_csr(CVMX_PKO_MEM_DEBUG9);
+ return debug9.cn38xx.doorbell;
+ } else {
+ union cvmx_pko_mem_debug8 debug8;
+ debug8.u64 = cvmx_read_csr(CVMX_PKO_MEM_DEBUG8);
+ return debug8.cn58xx.doorbell;
+ }
+ case CVMX_CMD_QUEUE_ZIP:
+ case CVMX_CMD_QUEUE_DFA:
+ case CVMX_CMD_QUEUE_RAID:
+ /* FIXME: Implement other lengths */
+ return 0;
+ case CVMX_CMD_QUEUE_DMA_BASE:
+ {
+ union cvmx_npei_dmax_counts dmax_counts;
+ dmax_counts.u64 =
+ cvmx_read_csr(CVMX_PEXP_NPEI_DMAX_COUNTS
+ (queue_id & 0x7));
+ return dmax_counts.s.dbell;
+ }
+ case CVMX_CMD_QUEUE_END:
+ return CVMX_CMD_QUEUE_INVALID_PARAM;
+ }
+ return CVMX_CMD_QUEUE_INVALID_PARAM;
+}
+
+/**
+ * Return the command buffer to be written to. The purpose of this
+ * function is to allow CVMX routine access t othe low level buffer
+ * for initial hardware setup. User applications should not call this
+ * function directly.
+ *
+ * @queue_id: Command queue to query
+ *
+ * Returns Command buffer or NULL on failure
+ */
+void *cvmx_cmd_queue_buffer(cvmx_cmd_queue_id_t queue_id)
+{
+ __cvmx_cmd_queue_state_t *qptr = __cvmx_cmd_queue_get_state(queue_id);
+ if (qptr && qptr->base_ptr_div128)
+ return cvmx_phys_to_ptr((uint64_t) qptr->base_ptr_div128 << 7);
+ else
+ return NULL;
+}
diff --git a/arch/mips/cavium-octeon/executive/cvmx-helper-board.c b/arch/mips/cavium-octeon/executive/cvmx-helper-board.c
new file mode 100644
index 00000000000..b764df64be4
--- /dev/null
+++ b/arch/mips/cavium-octeon/executive/cvmx-helper-board.c
@@ -0,0 +1,751 @@
+/***********************license start***************
+ * Author: Cavium Networks
+ *
+ * Contact: support@caviumnetworks.com
+ * This file is part of the OCTEON SDK
+ *
+ * Copyright (c) 2003-2008 Cavium Networks
+ *
+ * This file is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, Version 2, as
+ * published by the Free Software Foundation.
+ *
+ * This file is distributed in the hope that it will be useful, but
+ * AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or
+ * NONINFRINGEMENT. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this file; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ * or visit http://www.gnu.org/licenses/.
+ *
+ * This file may also be available under a different license from Cavium.
+ * Contact Cavium Networks for more information
+ ***********************license end**************************************/
+
+/*
+ *
+ * Helper functions to abstract board specific data about
+ * network ports from the rest of the cvmx-helper files.
+ */
+
+#include <asm/octeon/octeon.h>
+#include <asm/octeon/cvmx-bootinfo.h>
+
+#include <asm/octeon/cvmx-config.h>
+
+#include <asm/octeon/cvmx-mdio.h>
+
+#include <asm/octeon/cvmx-helper.h>
+#include <asm/octeon/cvmx-helper-util.h>
+#include <asm/octeon/cvmx-helper-board.h>
+
+#include <asm/octeon/cvmx-gmxx-defs.h>
+#include <asm/octeon/cvmx-asxx-defs.h>
+
+/**
+ * cvmx_override_board_link_get(int ipd_port) is a function
+ * pointer. It is meant to allow customization of the process of
+ * talking to a PHY to determine link speed. It is called every
+ * time a PHY must be polled for link status. Users should set
+ * this pointer to a function before calling any cvmx-helper
+ * operations.
+ */
+cvmx_helper_link_info_t(*cvmx_override_board_link_get) (int ipd_port) =
+ NULL;
+
+/**
+ * Return the MII PHY address associated with the given IPD
+ * port. A result of -1 means there isn't a MII capable PHY
+ * connected to this port. On chips supporting multiple MII
+ * busses the bus number is encoded in bits <15:8>.
+ *
+ * This function must be modified for every new Octeon board.
+ * Internally it uses switch statements based on the cvmx_sysinfo
+ * data to determine board types and revisions. It replies on the
+ * fact that every Octeon board receives a unique board type
+ * enumeration from the bootloader.
+ *
+ * @ipd_port: Octeon IPD port to get the MII address for.
+ *
+ * Returns MII PHY address and bus number or -1.
+ */
+int cvmx_helper_board_get_mii_address(int ipd_port)
+{
+ switch (cvmx_sysinfo_get()->board_type) {
+ case CVMX_BOARD_TYPE_SIM:
+ /* Simulator doesn't have MII */
+ return -1;
+ case CVMX_BOARD_TYPE_EBT3000:
+ case CVMX_BOARD_TYPE_EBT5800:
+ case CVMX_BOARD_TYPE_THUNDER:
+ case CVMX_BOARD_TYPE_NICPRO2:
+ /* Interface 0 is SPI4, interface 1 is RGMII */
+ if ((ipd_port >= 16) && (ipd_port < 20))
+ return ipd_port - 16;
+ else
+ return -1;
+ case CVMX_BOARD_TYPE_KODAMA:
+ case CVMX_BOARD_TYPE_EBH3100:
+ case CVMX_BOARD_TYPE_HIKARI:
+ case CVMX_BOARD_TYPE_CN3010_EVB_HS5:
+ case CVMX_BOARD_TYPE_CN3005_EVB_HS5:
+ case CVMX_BOARD_TYPE_CN3020_EVB_HS5:
+ /*
+ * Port 0 is WAN connected to a PHY, Port 1 is GMII
+ * connected to a switch
+ */
+ if (ipd_port == 0)
+ return 4;
+ else if (ipd_port == 1)
+ return 9;
+ else
+ return -1;
+ case CVMX_BOARD_TYPE_NAC38:
+ /* Board has 8 RGMII ports PHYs are 0-7 */
+ if ((ipd_port >= 0) && (ipd_port < 4))
+ return ipd_port;
+ else if ((ipd_port >= 16) && (ipd_port < 20))
+ return ipd_port - 16 + 4;
+ else
+ return -1;
+ case CVMX_BOARD_TYPE_EBH3000:
+ /* Board has dual SPI4 and no PHYs */
+ return -1;
+ case CVMX_BOARD_TYPE_EBH5200:
+ case CVMX_BOARD_TYPE_EBH5201:
+ case CVMX_BOARD_TYPE_EBT5200:
+ /* Board has 2 management ports */
+ if ((ipd_port >= CVMX_HELPER_BOARD_MGMT_IPD_PORT) &&
+ (ipd_port < (CVMX_HELPER_BOARD_MGMT_IPD_PORT + 2)))
+ return ipd_port - CVMX_HELPER_BOARD_MGMT_IPD_PORT;
+ /*
+ * Board has 4 SGMII ports. The PHYs start right after the MII
+ * ports MII0 = 0, MII1 = 1, SGMII = 2-5.
+ */
+ if ((ipd_port >= 0) && (ipd_port < 4))
+ return ipd_port + 2;
+ else
+ return -1;
+ case CVMX_BOARD_TYPE_EBH5600:
+ case CVMX_BOARD_TYPE_EBH5601:
+ case CVMX_BOARD_TYPE_EBH5610:
+ /* Board has 1 management port */
+ if (ipd_port == CVMX_HELPER_BOARD_MGMT_IPD_PORT)
+ return 0;
+ /*
+ * Board has 8 SGMII ports. 4 connect out, two connect
+ * to a switch, and 2 loop to each other
+ */
+ if ((ipd_port >= 0) && (ipd_port < 4))
+ return ipd_port + 1;
+ else
+ return -1;
+ case CVMX_BOARD_TYPE_CUST_NB5:
+ if (ipd_port == 2)
+ return 4;
+ else
+ return -1;
+ case CVMX_BOARD_TYPE_NIC_XLE_4G:
+ /* Board has 4 SGMII ports. connected QLM3(interface 1) */
+ if ((ipd_port >= 16) && (ipd_port < 20))
+ return ipd_port - 16 + 1;
+ else
+ return -1;
+ case CVMX_BOARD_TYPE_NIC_XLE_10G:
+ case CVMX_BOARD_TYPE_NIC10E:
+ return -1;
+ case CVMX_BOARD_TYPE_NIC4E:
+ if (ipd_port >= 0 && ipd_port <= 3)
+ return (ipd_port + 0x1f) & 0x1f;
+ else
+ return -1;
+ case CVMX_BOARD_TYPE_NIC2E:
+ if (ipd_port >= 0 && ipd_port <= 1)
+ return ipd_port + 1;
+ else
+ return -1;
+ case CVMX_BOARD_TYPE_BBGW_REF:
+ /*
+ * No PHYs are connected to Octeon, everything is
+ * through switch.
+ */
+ return -1;
+
+ case CVMX_BOARD_TYPE_CUST_WSX16:
+ if (ipd_port >= 0 && ipd_port <= 3)
+ return ipd_port;
+ else if (ipd_port >= 16 && ipd_port <= 19)
+ return ipd_port - 16 + 4;
+ else
+ return -1;
+ case CVMX_BOARD_TYPE_UBNT_E100:
+ if (ipd_port >= 0 && ipd_port <= 2)
+ return 7 - ipd_port;
+ else
+ return -1;
+ }
+
+ /* 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);
+ }
+ }
+ }
+ } else if (cvmx_sysinfo_get()->board_type ==
+ CVMX_BOARD_TYPE_UBNT_E100) {
+ cvmx_write_csr(CVMX_ASXX_RX_CLK_SETX(0, interface), 0);
+ cvmx_write_csr(CVMX_ASXX_TX_CLK_SETX(0, interface), 0x10);
+ cvmx_write_csr(CVMX_ASXX_RX_CLK_SETX(1, interface), 0);
+ cvmx_write_csr(CVMX_ASXX_TX_CLK_SETX(1, interface), 0x10);
+ cvmx_write_csr(CVMX_ASXX_RX_CLK_SETX(2, interface), 0);
+ cvmx_write_csr(CVMX_ASXX_TX_CLK_SETX(2, interface), 0x10);
+ }
+ return 0;
+}
+
+/**
+ * Get the clock type used for the USB block based on board type.
+ * Used by the USB code for auto configuration of clock type.
+ *
+ * Return USB clock type enumeration
+ */
+enum cvmx_helper_board_usb_clock_types __cvmx_helper_board_usb_get_clock_type(void)
+{
+ switch (cvmx_sysinfo_get()->board_type) {
+ case CVMX_BOARD_TYPE_BBGW_REF:
+ case CVMX_BOARD_TYPE_LANAI2_A:
+ case CVMX_BOARD_TYPE_LANAI2_U:
+ case CVMX_BOARD_TYPE_LANAI2_G:
+ case CVMX_BOARD_TYPE_NIC10E_66:
+ case CVMX_BOARD_TYPE_UBNT_E100:
+ return USB_CLOCK_TYPE_CRYSTAL_12;
+ case CVMX_BOARD_TYPE_NIC10E:
+ return USB_CLOCK_TYPE_REF_12;
+ default:
+ break;
+ }
+ /* Most boards except NIC10e use a 12MHz crystal */
+ if (OCTEON_IS_MODEL(OCTEON_FAM_2))
+ return USB_CLOCK_TYPE_CRYSTAL_12;
+ return USB_CLOCK_TYPE_REF_48;
+}
diff --git a/arch/mips/cavium-octeon/executive/cvmx-helper-errata.c b/arch/mips/cavium-octeon/executive/cvmx-helper-errata.c
new file mode 100644
index 00000000000..868659e64d4
--- /dev/null
+++ b/arch/mips/cavium-octeon/executive/cvmx-helper-errata.c
@@ -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);
diff --git a/arch/mips/cavium-octeon/executive/cvmx-helper-jtag.c b/arch/mips/cavium-octeon/executive/cvmx-helper-jtag.c
new file mode 100644
index 00000000000..607b4e65957
--- /dev/null
+++ b/arch/mips/cavium-octeon/executive/cvmx-helper-jtag.c
@@ -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);
+}
diff --git a/arch/mips/cavium-octeon/executive/cvmx-helper-loop.c b/arch/mips/cavium-octeon/executive/cvmx-helper-loop.c
new file mode 100644
index 00000000000..bfbd46115e7
--- /dev/null
+++ b/arch/mips/cavium-octeon/executive/cvmx-helper-loop.c
@@ -0,0 +1,85 @@
+/***********************license start***************
+ * Author: Cavium Networks
+ *
+ * Contact: support@caviumnetworks.com
+ * This file is part of the OCTEON SDK
+ *
+ * Copyright (c) 2003-2008 Cavium Networks
+ *
+ * This file is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, Version 2, as
+ * published by the Free Software Foundation.
+ *
+ * This file is distributed in the hope that it will be useful, but
+ * AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or
+ * NONINFRINGEMENT. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this file; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ * or visit http://www.gnu.org/licenses/.
+ *
+ * This file may also be available under a different license from Cavium.
+ * Contact Cavium Networks for more information
+ ***********************license end**************************************/
+
+/*
+ * Functions for LOOP initialization, configuration,
+ * and monitoring.
+ */
+#include <asm/octeon/octeon.h>
+
+#include <asm/octeon/cvmx-config.h>
+
+#include <asm/octeon/cvmx-helper.h>
+#include <asm/octeon/cvmx-pip-defs.h>
+
+/**
+ * Probe a LOOP interface and determine the number of ports
+ * connected to it. The LOOP interface should still be down
+ * after this call.
+ *
+ * @interface: Interface to probe
+ *
+ * Returns Number of ports on the interface. Zero to disable.
+ */
+int __cvmx_helper_loop_probe(int interface)
+{
+ union cvmx_ipd_sub_port_fcs ipd_sub_port_fcs;
+ int num_ports = 4;
+ int port;
+
+ /* We need to disable length checking so packet < 64 bytes and jumbo
+ frames don't get errors */
+ for (port = 0; port < num_ports; port++) {
+ union cvmx_pip_prt_cfgx port_cfg;
+ int ipd_port = cvmx_helper_get_ipd_port(interface, port);
+ port_cfg.u64 = cvmx_read_csr(CVMX_PIP_PRT_CFGX(ipd_port));
+ port_cfg.s.maxerr_en = 0;
+ port_cfg.s.minerr_en = 0;
+ cvmx_write_csr(CVMX_PIP_PRT_CFGX(ipd_port), port_cfg.u64);
+ }
+
+ /* Disable FCS stripping for loopback ports */
+ ipd_sub_port_fcs.u64 = cvmx_read_csr(CVMX_IPD_SUB_PORT_FCS);
+ ipd_sub_port_fcs.s.port_bit2 = 0;
+ cvmx_write_csr(CVMX_IPD_SUB_PORT_FCS, ipd_sub_port_fcs.u64);
+ return num_ports;
+}
+
+/**
+ * Bringup and enable a LOOP interface. After this call packet
+ * I/O should be fully functional. This is called with IPD
+ * enabled but PKO disabled.
+ *
+ * @interface: Interface to bring up
+ *
+ * Returns Zero on success, negative on failure
+ */
+int __cvmx_helper_loop_enable(int interface)
+{
+ /* Do nothing. */
+ return 0;
+}
diff --git a/arch/mips/cavium-octeon/executive/cvmx-helper-npi.c b/arch/mips/cavium-octeon/executive/cvmx-helper-npi.c
new file mode 100644
index 00000000000..cc94cfa545b
--- /dev/null
+++ b/arch/mips/cavium-octeon/executive/cvmx-helper-npi.c
@@ -0,0 +1,113 @@
+/***********************license start***************
+ * Author: Cavium Networks
+ *
+ * Contact: support@caviumnetworks.com
+ * This file is part of the OCTEON SDK
+ *
+ * Copyright (c) 2003-2008 Cavium Networks
+ *
+ * This file is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, Version 2, as
+ * published by the Free Software Foundation.
+ *
+ * This file is distributed in the hope that it will be useful, but
+ * AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or
+ * NONINFRINGEMENT. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this file; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ * or visit http://www.gnu.org/licenses/.
+ *
+ * This file may also be available under a different license from Cavium.
+ * Contact Cavium Networks for more information
+ ***********************license end**************************************/
+
+/*
+ * Functions for NPI initialization, configuration,
+ * and monitoring.
+ */
+#include <asm/octeon/octeon.h>
+
+#include <asm/octeon/cvmx-config.h>
+
+#include <asm/octeon/cvmx-helper.h>
+
+#include <asm/octeon/cvmx-pip-defs.h>
+
+/**
+ * Probe a NPI interface and determine the number of ports
+ * connected to it. The NPI interface should still be down
+ * after this call.
+ *
+ * @interface: Interface to probe
+ *
+ * Returns Number of ports on the interface. Zero to disable.
+ */
+int __cvmx_helper_npi_probe(int interface)
+{
+#if CVMX_PKO_QUEUES_PER_PORT_PCI > 0
+ if (OCTEON_IS_MODEL(OCTEON_CN38XX) || OCTEON_IS_MODEL(OCTEON_CN58XX))
+ return 4;
+ else if (OCTEON_IS_MODEL(OCTEON_CN56XX)
+ && !OCTEON_IS_MODEL(OCTEON_CN56XX_PASS1_X))
+ /* The packet engines didn't exist before pass 2 */
+ return 4;
+ else if (OCTEON_IS_MODEL(OCTEON_CN52XX)
+ && !OCTEON_IS_MODEL(OCTEON_CN52XX_PASS1_X))
+ /* The packet engines didn't exist before pass 2 */
+ return 4;
+#if 0
+ /*
+ * Technically CN30XX, CN31XX, and CN50XX contain packet
+ * engines, but nobody ever uses them. Since this is the case,
+ * we disable them here.
+ */
+ else if (OCTEON_IS_MODEL(OCTEON_CN31XX)
+ || OCTEON_IS_MODEL(OCTEON_CN50XX))
+ return 2;
+ else if (OCTEON_IS_MODEL(OCTEON_CN30XX))
+ return 1;
+#endif
+#endif
+ return 0;
+}
+
+/**
+ * Bringup and enable a NPI interface. After this call packet
+ * I/O should be fully functional. This is called with IPD
+ * enabled but PKO disabled.
+ *
+ * @interface: Interface to bring up
+ *
+ * Returns Zero on success, negative on failure
+ */
+int __cvmx_helper_npi_enable(int interface)
+{
+ /*
+ * On CN50XX, CN52XX, and CN56XX we need to disable length
+ * checking so packet < 64 bytes and jumbo frames don't get
+ * errors.
+ */
+ if (!OCTEON_IS_MODEL(OCTEON_CN3XXX) &&
+ !OCTEON_IS_MODEL(OCTEON_CN58XX)) {
+ int num_ports = cvmx_helper_ports_on_interface(interface);
+ int port;
+ for (port = 0; port < num_ports; port++) {
+ union cvmx_pip_prt_cfgx port_cfg;
+ int ipd_port =
+ cvmx_helper_get_ipd_port(interface, port);
+ port_cfg.u64 =
+ cvmx_read_csr(CVMX_PIP_PRT_CFGX(ipd_port));
+ port_cfg.s.maxerr_en = 0;
+ port_cfg.s.minerr_en = 0;
+ cvmx_write_csr(CVMX_PIP_PRT_CFGX(ipd_port),
+ port_cfg.u64);
+ }
+ }
+
+ /* Enables are controlled by the remote host, so nothing to do here */
+ return 0;
+}
diff --git a/arch/mips/cavium-octeon/executive/cvmx-helper-rgmii.c b/arch/mips/cavium-octeon/executive/cvmx-helper-rgmii.c
new file mode 100644
index 00000000000..f59c88ee9b3
--- /dev/null
+++ b/arch/mips/cavium-octeon/executive/cvmx-helper-rgmii.c
@@ -0,0 +1,526 @@
+/***********************license start***************
+ * Author: Cavium Networks
+ *
+ * Contact: support@caviumnetworks.com
+ * This file is part of the OCTEON SDK
+ *
+ * Copyright (c) 2003-2008 Cavium Networks
+ *
+ * This file is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, Version 2, as
+ * published by the Free Software Foundation.
+ *
+ * This file is distributed in the hope that it will be useful, but
+ * AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or
+ * NONINFRINGEMENT. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this file; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ * or visit http://www.gnu.org/licenses/.
+ *
+ * This file may also be available under a different license from Cavium.
+ * Contact Cavium Networks for more information
+ ***********************license end**************************************/
+
+/*
+ * Functions for RGMII/GMII/MII initialization, configuration,
+ * and monitoring.
+ */
+#include <asm/octeon/octeon.h>
+
+#include <asm/octeon/cvmx-config.h>
+
+
+#include <asm/octeon/cvmx-mdio.h>
+#include <asm/octeon/cvmx-pko.h>
+#include <asm/octeon/cvmx-helper.h>
+#include <asm/octeon/cvmx-helper-board.h>
+
+#include <asm/octeon/cvmx-npi-defs.h>
+#include <asm/octeon/cvmx-gmxx-defs.h>
+#include <asm/octeon/cvmx-asxx-defs.h>
+#include <asm/octeon/cvmx-dbg-defs.h>
+
+void __cvmx_interrupt_gmxx_enable(int interface);
+void __cvmx_interrupt_asxx_enable(int block);
+
+/**
+ * Probe RGMII ports and determine the number present
+ *
+ * @interface: Interface to probe
+ *
+ * Returns Number of RGMII/GMII/MII ports (0-4).
+ */
+int __cvmx_helper_rgmii_probe(int interface)
+{
+ int num_ports = 0;
+ union cvmx_gmxx_inf_mode mode;
+ mode.u64 = cvmx_read_csr(CVMX_GMXX_INF_MODE(interface));
+
+ if (mode.s.type) {
+ if (OCTEON_IS_MODEL(OCTEON_CN38XX)
+ || OCTEON_IS_MODEL(OCTEON_CN58XX)) {
+ cvmx_dprintf("ERROR: RGMII initialize called in "
+ "SPI interface\n");
+ } else if (OCTEON_IS_MODEL(OCTEON_CN31XX)
+ || OCTEON_IS_MODEL(OCTEON_CN30XX)
+ || OCTEON_IS_MODEL(OCTEON_CN50XX)) {
+ /*
+ * On these chips "type" says we're in
+ * GMII/MII mode. This limits us to 2 ports
+ */
+ num_ports = 2;
+ } else {
+ cvmx_dprintf("ERROR: Unsupported Octeon model in %s\n",
+ __func__);
+ }
+ } else {
+ if (OCTEON_IS_MODEL(OCTEON_CN38XX)
+ || OCTEON_IS_MODEL(OCTEON_CN58XX)) {
+ num_ports = 4;
+ } else if (OCTEON_IS_MODEL(OCTEON_CN31XX)
+ || OCTEON_IS_MODEL(OCTEON_CN30XX)
+ || OCTEON_IS_MODEL(OCTEON_CN50XX)) {
+ num_ports = 3;
+ } else {
+ cvmx_dprintf("ERROR: Unsupported Octeon model in %s\n",
+ __func__);
+ }
+ }
+ return num_ports;
+}
+
+/**
+ * Put an RGMII interface in loopback mode. Internal packets sent
+ * out will be received back again on the same port. Externally
+ * received packets will echo back out.
+ *
+ * @port: IPD port number to loop.
+ */
+void cvmx_helper_rgmii_internal_loopback(int port)
+{
+ int interface = (port >> 4) & 1;
+ int index = port & 0xf;
+ uint64_t tmp;
+
+ union cvmx_gmxx_prtx_cfg gmx_cfg;
+ gmx_cfg.u64 = 0;
+ gmx_cfg.s.duplex = 1;
+ gmx_cfg.s.slottime = 1;
+ gmx_cfg.s.speed = 1;
+ cvmx_write_csr(CVMX_GMXX_TXX_CLK(index, interface), 1);
+ cvmx_write_csr(CVMX_GMXX_TXX_SLOT(index, interface), 0x200);
+ cvmx_write_csr(CVMX_GMXX_TXX_BURST(index, interface), 0x2000);
+ cvmx_write_csr(CVMX_GMXX_PRTX_CFG(index, interface), gmx_cfg.u64);
+ tmp = cvmx_read_csr(CVMX_ASXX_PRT_LOOP(interface));
+ cvmx_write_csr(CVMX_ASXX_PRT_LOOP(interface), (1 << index) | tmp);
+ tmp = cvmx_read_csr(CVMX_ASXX_TX_PRT_EN(interface));
+ cvmx_write_csr(CVMX_ASXX_TX_PRT_EN(interface), (1 << index) | tmp);
+ tmp = cvmx_read_csr(CVMX_ASXX_RX_PRT_EN(interface));
+ cvmx_write_csr(CVMX_ASXX_RX_PRT_EN(interface), (1 << index) | tmp);
+ gmx_cfg.s.en = 1;
+ cvmx_write_csr(CVMX_GMXX_PRTX_CFG(index, interface), gmx_cfg.u64);
+}
+
+/**
+ * Workaround ASX setup errata with CN38XX pass1
+ *
+ * @interface: Interface to setup
+ * @port: Port to setup (0..3)
+ * @cpu_clock_hz:
+ * Chip frequency in Hertz
+ *
+ * Returns Zero on success, negative on failure
+ */
+static int __cvmx_helper_errata_asx_pass1(int interface, int port,
+ int cpu_clock_hz)
+{
+ /* Set hi water mark as per errata GMX-4 */
+ if (cpu_clock_hz >= 325000000 && cpu_clock_hz < 375000000)
+ cvmx_write_csr(CVMX_ASXX_TX_HI_WATERX(port, interface), 12);
+ else if (cpu_clock_hz >= 375000000 && cpu_clock_hz < 437000000)
+ cvmx_write_csr(CVMX_ASXX_TX_HI_WATERX(port, interface), 11);
+ else if (cpu_clock_hz >= 437000000 && cpu_clock_hz < 550000000)
+ cvmx_write_csr(CVMX_ASXX_TX_HI_WATERX(port, interface), 10);
+ else if (cpu_clock_hz >= 550000000 && cpu_clock_hz < 687000000)
+ cvmx_write_csr(CVMX_ASXX_TX_HI_WATERX(port, interface), 9);
+ else
+ cvmx_dprintf("Illegal clock frequency (%d). "
+ "CVMX_ASXX_TX_HI_WATERX not set\n", cpu_clock_hz);
+ return 0;
+}
+
+/**
+ * Configure all of the ASX, GMX, and PKO regsiters required
+ * to get RGMII to function on the supplied interface.
+ *
+ * @interface: PKO Interface to configure (0 or 1)
+ *
+ * Returns Zero on success
+ */
+int __cvmx_helper_rgmii_enable(int interface)
+{
+ int num_ports = cvmx_helper_ports_on_interface(interface);
+ int port;
+ struct cvmx_sysinfo *sys_info_ptr = cvmx_sysinfo_get();
+ union cvmx_gmxx_inf_mode mode;
+ union cvmx_asxx_tx_prt_en asx_tx;
+ union cvmx_asxx_rx_prt_en asx_rx;
+
+ mode.u64 = cvmx_read_csr(CVMX_GMXX_INF_MODE(interface));
+
+ if (mode.s.en == 0)
+ return -1;
+ if ((OCTEON_IS_MODEL(OCTEON_CN38XX) ||
+ OCTEON_IS_MODEL(OCTEON_CN58XX)) && mode.s.type == 1)
+ /* Ignore SPI interfaces */
+ return -1;
+
+ /* Configure the ASX registers needed to use the RGMII ports */
+ asx_tx.u64 = 0;
+ asx_tx.s.prt_en = cvmx_build_mask(num_ports);
+ cvmx_write_csr(CVMX_ASXX_TX_PRT_EN(interface), asx_tx.u64);
+
+ asx_rx.u64 = 0;
+ asx_rx.s.prt_en = cvmx_build_mask(num_ports);
+ cvmx_write_csr(CVMX_ASXX_RX_PRT_EN(interface), asx_rx.u64);
+
+ /* Configure the GMX registers needed to use the RGMII ports */
+ for (port = 0; port < num_ports; port++) {
+ /* Setting of CVMX_GMXX_TXX_THRESH has been moved to
+ __cvmx_helper_setup_gmx() */
+
+ if (cvmx_octeon_is_pass1())
+ __cvmx_helper_errata_asx_pass1(interface, port,
+ sys_info_ptr->
+ cpu_clock_hz);
+ else {
+ /*
+ * Configure more flexible RGMII preamble
+ * checking. Pass 1 doesn't support this
+ * feature.
+ */
+ union cvmx_gmxx_rxx_frm_ctl frm_ctl;
+ frm_ctl.u64 =
+ cvmx_read_csr(CVMX_GMXX_RXX_FRM_CTL
+ (port, interface));
+ /* New field, so must be compile time */
+ frm_ctl.s.pre_free = 1;
+ cvmx_write_csr(CVMX_GMXX_RXX_FRM_CTL(port, interface),
+ frm_ctl.u64);
+ }
+
+ /*
+ * Each pause frame transmitted will ask for about 10M
+ * bit times before resume. If buffer space comes
+ * available before that time has expired, an XON
+ * pause frame (0 time) will be transmitted to restart
+ * the flow.
+ */
+ cvmx_write_csr(CVMX_GMXX_TXX_PAUSE_PKT_TIME(port, interface),
+ 20000);
+ cvmx_write_csr(CVMX_GMXX_TXX_PAUSE_PKT_INTERVAL
+ (port, interface), 19000);
+
+ if (OCTEON_IS_MODEL(OCTEON_CN50XX)) {
+ cvmx_write_csr(CVMX_ASXX_TX_CLK_SETX(port, interface),
+ 16);
+ cvmx_write_csr(CVMX_ASXX_RX_CLK_SETX(port, interface),
+ 16);
+ } else {
+ cvmx_write_csr(CVMX_ASXX_TX_CLK_SETX(port, interface),
+ 24);
+ cvmx_write_csr(CVMX_ASXX_RX_CLK_SETX(port, interface),
+ 24);
+ }
+ }
+
+ __cvmx_helper_setup_gmx(interface, num_ports);
+
+ /* enable the ports now */
+ for (port = 0; port < num_ports; port++) {
+ union cvmx_gmxx_prtx_cfg gmx_cfg;
+ cvmx_helper_link_autoconf(cvmx_helper_get_ipd_port
+ (interface, port));
+ gmx_cfg.u64 =
+ cvmx_read_csr(CVMX_GMXX_PRTX_CFG(port, interface));
+ gmx_cfg.s.en = 1;
+ cvmx_write_csr(CVMX_GMXX_PRTX_CFG(port, interface),
+ gmx_cfg.u64);
+ }
+ __cvmx_interrupt_asxx_enable(interface);
+ __cvmx_interrupt_gmxx_enable(interface);
+
+ return 0;
+}
+
+/**
+ * Return the link state of an IPD/PKO port as returned by
+ * auto negotiation. The result of this function may not match
+ * Octeon's link config if auto negotiation has changed since
+ * the last call to cvmx_helper_link_set().
+ *
+ * @ipd_port: IPD/PKO port to query
+ *
+ * Returns Link state
+ */
+cvmx_helper_link_info_t __cvmx_helper_rgmii_link_get(int ipd_port)
+{
+ int interface = cvmx_helper_get_interface_num(ipd_port);
+ int index = cvmx_helper_get_interface_index_num(ipd_port);
+ union cvmx_asxx_prt_loop asxx_prt_loop;
+
+ asxx_prt_loop.u64 = cvmx_read_csr(CVMX_ASXX_PRT_LOOP(interface));
+ if (asxx_prt_loop.s.int_loop & (1 << index)) {
+ /* Force 1Gbps full duplex on internal loopback */
+ cvmx_helper_link_info_t result;
+ result.u64 = 0;
+ result.s.full_duplex = 1;
+ result.s.link_up = 1;
+ result.s.speed = 1000;
+ return result;
+ } else
+ return __cvmx_helper_board_link_get(ipd_port);
+}
+
+/**
+ * Configure an IPD/PKO port for the specified link state. This
+ * function does not influence auto negotiation at the PHY level.
+ * The passed link state must always match the link state returned
+ * by cvmx_helper_link_get(). It is normally best to use
+ * cvmx_helper_link_autoconf() instead.
+ *
+ * @ipd_port: IPD/PKO port to configure
+ * @link_info: The new link state
+ *
+ * Returns Zero on success, negative on failure
+ */
+int __cvmx_helper_rgmii_link_set(int ipd_port,
+ cvmx_helper_link_info_t link_info)
+{
+ int result = 0;
+ int interface = cvmx_helper_get_interface_num(ipd_port);
+ int index = cvmx_helper_get_interface_index_num(ipd_port);
+ union cvmx_gmxx_prtx_cfg original_gmx_cfg;
+ union cvmx_gmxx_prtx_cfg new_gmx_cfg;
+ union cvmx_pko_mem_queue_qos pko_mem_queue_qos;
+ union cvmx_pko_mem_queue_qos pko_mem_queue_qos_save[16];
+ union cvmx_gmxx_tx_ovr_bp gmx_tx_ovr_bp;
+ union cvmx_gmxx_tx_ovr_bp gmx_tx_ovr_bp_save;
+ int i;
+
+ /* Ignore speed sets in the simulator */
+ if (cvmx_sysinfo_get()->board_type == CVMX_BOARD_TYPE_SIM)
+ return 0;
+
+ /* Read the current settings so we know the current enable state */
+ original_gmx_cfg.u64 =
+ cvmx_read_csr(CVMX_GMXX_PRTX_CFG(index, interface));
+ new_gmx_cfg = original_gmx_cfg;
+
+ /* Disable the lowest level RX */
+ cvmx_write_csr(CVMX_ASXX_RX_PRT_EN(interface),
+ cvmx_read_csr(CVMX_ASXX_RX_PRT_EN(interface)) &
+ ~(1 << index));
+
+ memset(pko_mem_queue_qos_save, 0, sizeof(pko_mem_queue_qos_save));
+ /* Disable all queues so that TX should become idle */
+ for (i = 0; i < cvmx_pko_get_num_queues(ipd_port); i++) {
+ int queue = cvmx_pko_get_base_queue(ipd_port) + i;
+ cvmx_write_csr(CVMX_PKO_REG_READ_IDX, queue);
+ pko_mem_queue_qos.u64 = cvmx_read_csr(CVMX_PKO_MEM_QUEUE_QOS);
+ pko_mem_queue_qos.s.pid = ipd_port;
+ pko_mem_queue_qos.s.qid = queue;
+ pko_mem_queue_qos_save[i] = pko_mem_queue_qos;
+ pko_mem_queue_qos.s.qos_mask = 0;
+ cvmx_write_csr(CVMX_PKO_MEM_QUEUE_QOS, pko_mem_queue_qos.u64);
+ }
+
+ /* Disable backpressure */
+ gmx_tx_ovr_bp.u64 = cvmx_read_csr(CVMX_GMXX_TX_OVR_BP(interface));
+ gmx_tx_ovr_bp_save = gmx_tx_ovr_bp;
+ gmx_tx_ovr_bp.s.bp &= ~(1 << index);
+ gmx_tx_ovr_bp.s.en |= 1 << index;
+ cvmx_write_csr(CVMX_GMXX_TX_OVR_BP(interface), gmx_tx_ovr_bp.u64);
+ cvmx_read_csr(CVMX_GMXX_TX_OVR_BP(interface));
+
+ /*
+ * Poll the GMX state machine waiting for it to become
+ * idle. Preferably we should only change speed when it is
+ * idle. If it doesn't become idle we will still do the speed
+ * change, but there is a slight chance that GMX will
+ * lockup.
+ */
+ cvmx_write_csr(CVMX_NPI_DBG_SELECT,
+ interface * 0x800 + index * 0x100 + 0x880);
+ CVMX_WAIT_FOR_FIELD64(CVMX_DBG_DATA, union cvmx_dbg_data, data & 7,
+ ==, 0, 10000);
+ CVMX_WAIT_FOR_FIELD64(CVMX_DBG_DATA, union cvmx_dbg_data, data & 0xf,
+ ==, 0, 10000);
+
+ /* Disable the port before we make any changes */
+ new_gmx_cfg.s.en = 0;
+ cvmx_write_csr(CVMX_GMXX_PRTX_CFG(index, interface), new_gmx_cfg.u64);
+ cvmx_read_csr(CVMX_GMXX_PRTX_CFG(index, interface));
+
+ /* Set full/half duplex */
+ if (cvmx_octeon_is_pass1())
+ /* Half duplex is broken for 38XX Pass 1 */
+ new_gmx_cfg.s.duplex = 1;
+ else if (!link_info.s.link_up)
+ /* Force full duplex on down links */
+ new_gmx_cfg.s.duplex = 1;
+ else
+ new_gmx_cfg.s.duplex = link_info.s.full_duplex;
+
+ /* Set the link speed. Anything unknown is set to 1Gbps */
+ if (link_info.s.speed == 10) {
+ new_gmx_cfg.s.slottime = 0;
+ new_gmx_cfg.s.speed = 0;
+ } else if (link_info.s.speed == 100) {
+ new_gmx_cfg.s.slottime = 0;
+ new_gmx_cfg.s.speed = 0;
+ } else {
+ new_gmx_cfg.s.slottime = 1;
+ new_gmx_cfg.s.speed = 1;
+ }
+
+ /* Adjust the clocks */
+ if (link_info.s.speed == 10) {
+ cvmx_write_csr(CVMX_GMXX_TXX_CLK(index, interface), 50);
+ cvmx_write_csr(CVMX_GMXX_TXX_SLOT(index, interface), 0x40);
+ cvmx_write_csr(CVMX_GMXX_TXX_BURST(index, interface), 0);
+ } else if (link_info.s.speed == 100) {
+ cvmx_write_csr(CVMX_GMXX_TXX_CLK(index, interface), 5);
+ cvmx_write_csr(CVMX_GMXX_TXX_SLOT(index, interface), 0x40);
+ cvmx_write_csr(CVMX_GMXX_TXX_BURST(index, interface), 0);
+ } else {
+ cvmx_write_csr(CVMX_GMXX_TXX_CLK(index, interface), 1);
+ cvmx_write_csr(CVMX_GMXX_TXX_SLOT(index, interface), 0x200);
+ cvmx_write_csr(CVMX_GMXX_TXX_BURST(index, interface), 0x2000);
+ }
+
+ if (OCTEON_IS_MODEL(OCTEON_CN30XX) || OCTEON_IS_MODEL(OCTEON_CN50XX)) {
+ if ((link_info.s.speed == 10) || (link_info.s.speed == 100)) {
+ union cvmx_gmxx_inf_mode mode;
+ mode.u64 = cvmx_read_csr(CVMX_GMXX_INF_MODE(interface));
+
+ /*
+ * Port .en .type .p0mii Configuration
+ * ---- --- ----- ------ -----------------------------------------
+ * X 0 X X All links are disabled.
+ * 0 1 X 0 Port 0 is RGMII
+ * 0 1 X 1 Port 0 is MII
+ * 1 1 0 X Ports 1 and 2 are configured as RGMII ports.
+ * 1 1 1 X Port 1: GMII/MII; Port 2: disabled. GMII or
+ * MII port is selected by GMX_PRT1_CFG[SPEED].
+ */
+
+ /* In MII mode, CLK_CNT = 1. */
+ if (((index == 0) && (mode.s.p0mii == 1))
+ || ((index != 0) && (mode.s.type == 1))) {
+ cvmx_write_csr(CVMX_GMXX_TXX_CLK
+ (index, interface), 1);
+ }
+ }
+ }
+
+ /* Do a read to make sure all setup stuff is complete */
+ cvmx_read_csr(CVMX_GMXX_PRTX_CFG(index, interface));
+
+ /* Save the new GMX setting without enabling the port */
+ cvmx_write_csr(CVMX_GMXX_PRTX_CFG(index, interface), new_gmx_cfg.u64);
+
+ /* Enable the lowest level RX */
+ cvmx_write_csr(CVMX_ASXX_RX_PRT_EN(interface),
+ cvmx_read_csr(CVMX_ASXX_RX_PRT_EN(interface)) | (1 <<
+ index));
+
+ /* Re-enable the TX path */
+ for (i = 0; i < cvmx_pko_get_num_queues(ipd_port); i++) {
+ int queue = cvmx_pko_get_base_queue(ipd_port) + i;
+ cvmx_write_csr(CVMX_PKO_REG_READ_IDX, queue);
+ cvmx_write_csr(CVMX_PKO_MEM_QUEUE_QOS,
+ pko_mem_queue_qos_save[i].u64);
+ }
+
+ /* Restore backpressure */
+ cvmx_write_csr(CVMX_GMXX_TX_OVR_BP(interface), gmx_tx_ovr_bp_save.u64);
+
+ /* Restore the GMX enable state. Port config is complete */
+ new_gmx_cfg.s.en = original_gmx_cfg.s.en;
+ cvmx_write_csr(CVMX_GMXX_PRTX_CFG(index, interface), new_gmx_cfg.u64);
+
+ return result;
+}
+
+/**
+ * Configure a port for internal and/or external loopback. Internal loopback
+ * causes packets sent by the port to be received by Octeon. External loopback
+ * causes packets received from the wire to sent out again.
+ *
+ * @ipd_port: IPD/PKO port to loopback.
+ * @enable_internal:
+ * Non zero if you want internal loopback
+ * @enable_external:
+ * Non zero if you want external loopback
+ *
+ * Returns Zero on success, negative on failure.
+ */
+int __cvmx_helper_rgmii_configure_loopback(int ipd_port, int enable_internal,
+ int enable_external)
+{
+ int interface = cvmx_helper_get_interface_num(ipd_port);
+ int index = cvmx_helper_get_interface_index_num(ipd_port);
+ int original_enable;
+ union cvmx_gmxx_prtx_cfg gmx_cfg;
+ union cvmx_asxx_prt_loop asxx_prt_loop;
+
+ /* Read the current enable state and save it */
+ gmx_cfg.u64 = cvmx_read_csr(CVMX_GMXX_PRTX_CFG(index, interface));
+ original_enable = gmx_cfg.s.en;
+ /* Force port to be disabled */
+ gmx_cfg.s.en = 0;
+ if (enable_internal) {
+ /* Force speed if we're doing internal loopback */
+ gmx_cfg.s.duplex = 1;
+ gmx_cfg.s.slottime = 1;
+ gmx_cfg.s.speed = 1;
+ cvmx_write_csr(CVMX_GMXX_TXX_CLK(index, interface), 1);
+ cvmx_write_csr(CVMX_GMXX_TXX_SLOT(index, interface), 0x200);
+ cvmx_write_csr(CVMX_GMXX_TXX_BURST(index, interface), 0x2000);
+ }
+ cvmx_write_csr(CVMX_GMXX_PRTX_CFG(index, interface), gmx_cfg.u64);
+
+ /* Set the loopback bits */
+ asxx_prt_loop.u64 = cvmx_read_csr(CVMX_ASXX_PRT_LOOP(interface));
+ if (enable_internal)
+ asxx_prt_loop.s.int_loop |= 1 << index;
+ else
+ asxx_prt_loop.s.int_loop &= ~(1 << index);
+ if (enable_external)
+ asxx_prt_loop.s.ext_loop |= 1 << index;
+ else
+ asxx_prt_loop.s.ext_loop &= ~(1 << index);
+ cvmx_write_csr(CVMX_ASXX_PRT_LOOP(interface), asxx_prt_loop.u64);
+
+ /* Force enables in internal loopback */
+ if (enable_internal) {
+ uint64_t tmp;
+ tmp = cvmx_read_csr(CVMX_ASXX_TX_PRT_EN(interface));
+ cvmx_write_csr(CVMX_ASXX_TX_PRT_EN(interface),
+ (1 << index) | tmp);
+ tmp = cvmx_read_csr(CVMX_ASXX_RX_PRT_EN(interface));
+ cvmx_write_csr(CVMX_ASXX_RX_PRT_EN(interface),
+ (1 << index) | tmp);
+ original_enable = 1;
+ }
+
+ /* Restore the enable state */
+ gmx_cfg.s.en = original_enable;
+ cvmx_write_csr(CVMX_GMXX_PRTX_CFG(index, interface), gmx_cfg.u64);
+ return 0;
+}
diff --git a/arch/mips/cavium-octeon/executive/cvmx-helper-sgmii.c b/arch/mips/cavium-octeon/executive/cvmx-helper-sgmii.c
new file mode 100644
index 00000000000..45f18cce31a
--- /dev/null
+++ b/arch/mips/cavium-octeon/executive/cvmx-helper-sgmii.c
@@ -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;
+}
diff --git a/arch/mips/cavium-octeon/executive/cvmx-helper-spi.c b/arch/mips/cavium-octeon/executive/cvmx-helper-spi.c
new file mode 100644
index 00000000000..1f3030c72d8
--- /dev/null
+++ b/arch/mips/cavium-octeon/executive/cvmx-helper-spi.c
@@ -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;
+}
diff --git a/arch/mips/cavium-octeon/executive/cvmx-helper-util.c b/arch/mips/cavium-octeon/executive/cvmx-helper-util.c
new file mode 100644
index 00000000000..453d7f66459
--- /dev/null
+++ b/arch/mips/cavium-octeon/executive/cvmx-helper-util.c
@@ -0,0 +1,437 @@
+/***********************license start***************
+ * Author: Cavium Networks
+ *
+ * Contact: support@caviumnetworks.com
+ * This file is part of the OCTEON SDK
+ *
+ * Copyright (c) 2003-2008 Cavium Networks
+ *
+ * This file is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, Version 2, as
+ * published by the Free Software Foundation.
+ *
+ * This file is distributed in the hope that it will be useful, but
+ * AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or
+ * NONINFRINGEMENT. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this file; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ * or visit http://www.gnu.org/licenses/.
+ *
+ * This file may also be available under a different license from Cavium.
+ * Contact Cavium Networks for more information
+ ***********************license end**************************************/
+
+/*
+ * Small helper utilities.
+ */
+#include <linux/kernel.h>
+
+#include <asm/octeon/octeon.h>
+
+#include <asm/octeon/cvmx-config.h>
+
+#include <asm/octeon/cvmx-fpa.h>
+#include <asm/octeon/cvmx-pip.h>
+#include <asm/octeon/cvmx-pko.h>
+#include <asm/octeon/cvmx-ipd.h>
+#include <asm/octeon/cvmx-spi.h>
+
+#include <asm/octeon/cvmx-helper.h>
+#include <asm/octeon/cvmx-helper-util.h>
+
+#include <asm/octeon/cvmx-ipd-defs.h>
+
+/**
+ * Convert a interface mode into a human readable string
+ *
+ * @mode: Mode to convert
+ *
+ * Returns String
+ */
+const char *cvmx_helper_interface_mode_to_string(cvmx_helper_interface_mode_t
+ mode)
+{
+ switch (mode) {
+ case CVMX_HELPER_INTERFACE_MODE_DISABLED:
+ return "DISABLED";
+ case CVMX_HELPER_INTERFACE_MODE_RGMII:
+ return "RGMII";
+ case CVMX_HELPER_INTERFACE_MODE_GMII:
+ return "GMII";
+ case CVMX_HELPER_INTERFACE_MODE_SPI:
+ return "SPI";
+ case CVMX_HELPER_INTERFACE_MODE_PCIE:
+ return "PCIE";
+ case CVMX_HELPER_INTERFACE_MODE_XAUI:
+ return "XAUI";
+ case CVMX_HELPER_INTERFACE_MODE_SGMII:
+ return "SGMII";
+ case CVMX_HELPER_INTERFACE_MODE_PICMG:
+ return "PICMG";
+ case CVMX_HELPER_INTERFACE_MODE_NPI:
+ return "NPI";
+ case CVMX_HELPER_INTERFACE_MODE_LOOP:
+ return "LOOP";
+ }
+ return "UNKNOWN";
+}
+
+/**
+ * Debug routine to dump the packet structure to the console
+ *
+ * @work: Work queue entry containing the packet to dump
+ * Returns
+ */
+int cvmx_helper_dump_packet(cvmx_wqe_t *work)
+{
+ uint64_t count;
+ uint64_t remaining_bytes;
+ union cvmx_buf_ptr buffer_ptr;
+ uint64_t start_of_buffer;
+ uint8_t *data_address;
+ uint8_t *end_of_data;
+
+ cvmx_dprintf("Packet Length: %u\n", work->len);
+ cvmx_dprintf(" Input Port: %u\n", work->ipprt);
+ cvmx_dprintf(" QoS: %u\n", work->qos);
+ cvmx_dprintf(" Buffers: %u\n", work->word2.s.bufs);
+
+ if (work->word2.s.bufs == 0) {
+ union cvmx_ipd_wqe_fpa_queue wqe_pool;
+ wqe_pool.u64 = cvmx_read_csr(CVMX_IPD_WQE_FPA_QUEUE);
+ buffer_ptr.u64 = 0;
+ buffer_ptr.s.pool = wqe_pool.s.wqe_pool;
+ buffer_ptr.s.size = 128;
+ buffer_ptr.s.addr = cvmx_ptr_to_phys(work->packet_data);
+ if (likely(!work->word2.s.not_IP)) {
+ union cvmx_pip_ip_offset pip_ip_offset;
+ pip_ip_offset.u64 = cvmx_read_csr(CVMX_PIP_IP_OFFSET);
+ buffer_ptr.s.addr +=
+ (pip_ip_offset.s.offset << 3) -
+ work->word2.s.ip_offset;
+ buffer_ptr.s.addr += (work->word2.s.is_v6 ^ 1) << 2;
+ } else {
+ /*
+ * WARNING: This code assumes that the packet
+ * is not RAW. If it was, we would use
+ * PIP_GBL_CFG[RAW_SHF] instead of
+ * PIP_GBL_CFG[NIP_SHF].
+ */
+ union cvmx_pip_gbl_cfg pip_gbl_cfg;
+ pip_gbl_cfg.u64 = cvmx_read_csr(CVMX_PIP_GBL_CFG);
+ buffer_ptr.s.addr += pip_gbl_cfg.s.nip_shf;
+ }
+ } else
+ buffer_ptr = work->packet_ptr;
+ remaining_bytes = work->len;
+
+ while (remaining_bytes) {
+ start_of_buffer =
+ ((buffer_ptr.s.addr >> 7) - buffer_ptr.s.back) << 7;
+ cvmx_dprintf(" Buffer Start:%llx\n",
+ (unsigned long long)start_of_buffer);
+ cvmx_dprintf(" Buffer I : %u\n", buffer_ptr.s.i);
+ cvmx_dprintf(" Buffer Back: %u\n", buffer_ptr.s.back);
+ cvmx_dprintf(" Buffer Pool: %u\n", buffer_ptr.s.pool);
+ cvmx_dprintf(" Buffer Data: %llx\n",
+ (unsigned long long)buffer_ptr.s.addr);
+ cvmx_dprintf(" Buffer Size: %u\n", buffer_ptr.s.size);
+
+ cvmx_dprintf("\t\t");
+ data_address = (uint8_t *) cvmx_phys_to_ptr(buffer_ptr.s.addr);
+ end_of_data = data_address + buffer_ptr.s.size;
+ count = 0;
+ while (data_address < end_of_data) {
+ if (remaining_bytes == 0)
+ break;
+ else
+ remaining_bytes--;
+ cvmx_dprintf("%02x", (unsigned int)*data_address);
+ data_address++;
+ if (remaining_bytes && (count == 7)) {
+ cvmx_dprintf("\n\t\t");
+ count = 0;
+ } else
+ count++;
+ }
+ cvmx_dprintf("\n");
+
+ if (remaining_bytes)
+ buffer_ptr = *(union cvmx_buf_ptr *)
+ cvmx_phys_to_ptr(buffer_ptr.s.addr - 8);
+ }
+ return 0;
+}
+
+/**
+ * Setup Random Early Drop on a specific input queue
+ *
+ * @queue: Input queue to setup RED on (0-7)
+ * @pass_thresh:
+ * Packets will begin slowly dropping when there are less than
+ * this many packet buffers free in FPA 0.
+ * @drop_thresh:
+ * All incoming packets will be dropped when there are less
+ * than this many free packet buffers in FPA 0.
+ * Returns Zero on success. Negative on failure
+ */
+int cvmx_helper_setup_red_queue(int queue, int pass_thresh, int drop_thresh)
+{
+ union cvmx_ipd_qosx_red_marks red_marks;
+ union cvmx_ipd_red_quex_param red_param;
+
+ /* Set RED to begin dropping packets when there are pass_thresh buffers
+ left. It will linearly drop more packets until reaching drop_thresh
+ buffers */
+ red_marks.u64 = 0;
+ red_marks.s.drop = drop_thresh;
+ red_marks.s.pass = pass_thresh;
+ cvmx_write_csr(CVMX_IPD_QOSX_RED_MARKS(queue), red_marks.u64);
+
+ /* Use the actual queue 0 counter, not the average */
+ red_param.u64 = 0;
+ red_param.s.prb_con =
+ (255ul << 24) / (red_marks.s.pass - red_marks.s.drop);
+ red_param.s.avg_con = 1;
+ red_param.s.new_con = 255;
+ red_param.s.use_pcnt = 1;
+ cvmx_write_csr(CVMX_IPD_RED_QUEX_PARAM(queue), red_param.u64);
+ return 0;
+}
+
+/**
+ * Setup Random Early Drop to automatically begin dropping packets.
+ *
+ * @pass_thresh:
+ * Packets will begin slowly dropping when there are less than
+ * this many packet buffers free in FPA 0.
+ * @drop_thresh:
+ * All incoming packets will be dropped when there are less
+ * than this many free packet buffers in FPA 0.
+ * Returns Zero on success. Negative on failure
+ */
+int cvmx_helper_setup_red(int pass_thresh, int drop_thresh)
+{
+ union cvmx_ipd_portx_bp_page_cnt page_cnt;
+ union cvmx_ipd_bp_prt_red_end ipd_bp_prt_red_end;
+ union cvmx_ipd_red_port_enable red_port_enable;
+ int queue;
+ int interface;
+ int port;
+
+ /* Disable backpressure based on queued buffers. It needs SW support */
+ page_cnt.u64 = 0;
+ page_cnt.s.bp_enb = 0;
+ page_cnt.s.page_cnt = 100;
+ for (interface = 0; interface < 2; interface++) {
+ for (port = cvmx_helper_get_first_ipd_port(interface);
+ port < cvmx_helper_get_last_ipd_port(interface); port++)
+ cvmx_write_csr(CVMX_IPD_PORTX_BP_PAGE_CNT(port),
+ page_cnt.u64);
+ }
+
+ for (queue = 0; queue < 8; queue++)
+ cvmx_helper_setup_red_queue(queue, pass_thresh, drop_thresh);
+
+ /* Shutoff the dropping based on the per port page count. SW isn't
+ decrementing it right now */
+ ipd_bp_prt_red_end.u64 = 0;
+ ipd_bp_prt_red_end.s.prt_enb = 0;
+ cvmx_write_csr(CVMX_IPD_BP_PRT_RED_END, ipd_bp_prt_red_end.u64);
+
+ red_port_enable.u64 = 0;
+ red_port_enable.s.prt_enb = 0xfffffffffull;
+ red_port_enable.s.avg_dly = 10000;
+ red_port_enable.s.prb_dly = 10000;
+ cvmx_write_csr(CVMX_IPD_RED_PORT_ENABLE, red_port_enable.u64);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(cvmx_helper_setup_red);
+
+/**
+ * Setup the common GMX settings that determine the number of
+ * ports. These setting apply to almost all configurations of all
+ * chips.
+ *
+ * @interface: Interface to configure
+ * @num_ports: Number of ports on the interface
+ *
+ * Returns Zero on success, negative on failure
+ */
+int __cvmx_helper_setup_gmx(int interface, int num_ports)
+{
+ union cvmx_gmxx_tx_prts gmx_tx_prts;
+ union cvmx_gmxx_rx_prts gmx_rx_prts;
+ union cvmx_pko_reg_gmx_port_mode pko_mode;
+ union cvmx_gmxx_txx_thresh gmx_tx_thresh;
+ int index;
+
+ /* Tell GMX the number of TX ports on this interface */
+ gmx_tx_prts.u64 = cvmx_read_csr(CVMX_GMXX_TX_PRTS(interface));
+ gmx_tx_prts.s.prts = num_ports;
+ cvmx_write_csr(CVMX_GMXX_TX_PRTS(interface), gmx_tx_prts.u64);
+
+ /* Tell GMX the number of RX ports on this interface. This only
+ ** applies to *GMII and XAUI ports */
+ if (cvmx_helper_interface_get_mode(interface) ==
+ CVMX_HELPER_INTERFACE_MODE_RGMII
+ || cvmx_helper_interface_get_mode(interface) ==
+ CVMX_HELPER_INTERFACE_MODE_SGMII
+ || cvmx_helper_interface_get_mode(interface) ==
+ CVMX_HELPER_INTERFACE_MODE_GMII
+ || cvmx_helper_interface_get_mode(interface) ==
+ CVMX_HELPER_INTERFACE_MODE_XAUI) {
+ if (num_ports > 4) {
+ cvmx_dprintf("__cvmx_helper_setup_gmx: Illegal "
+ "num_ports\n");
+ return -1;
+ }
+
+ gmx_rx_prts.u64 = cvmx_read_csr(CVMX_GMXX_RX_PRTS(interface));
+ gmx_rx_prts.s.prts = num_ports;
+ cvmx_write_csr(CVMX_GMXX_RX_PRTS(interface), gmx_rx_prts.u64);
+ }
+
+ /* Skip setting CVMX_PKO_REG_GMX_PORT_MODE on 30XX, 31XX, and 50XX */
+ if (!OCTEON_IS_MODEL(OCTEON_CN30XX) && !OCTEON_IS_MODEL(OCTEON_CN31XX)
+ && !OCTEON_IS_MODEL(OCTEON_CN50XX)) {
+ /* Tell PKO the number of ports on this interface */
+ pko_mode.u64 = cvmx_read_csr(CVMX_PKO_REG_GMX_PORT_MODE);
+ if (interface == 0) {
+ if (num_ports == 1)
+ pko_mode.s.mode0 = 4;
+ else if (num_ports == 2)
+ pko_mode.s.mode0 = 3;
+ else if (num_ports <= 4)
+ pko_mode.s.mode0 = 2;
+ else if (num_ports <= 8)
+ pko_mode.s.mode0 = 1;
+ else
+ pko_mode.s.mode0 = 0;
+ } else {
+ if (num_ports == 1)
+ pko_mode.s.mode1 = 4;
+ else if (num_ports == 2)
+ pko_mode.s.mode1 = 3;
+ else if (num_ports <= 4)
+ pko_mode.s.mode1 = 2;
+ else if (num_ports <= 8)
+ pko_mode.s.mode1 = 1;
+ else
+ pko_mode.s.mode1 = 0;
+ }
+ cvmx_write_csr(CVMX_PKO_REG_GMX_PORT_MODE, pko_mode.u64);
+ }
+
+ /*
+ * Set GMX to buffer as much data as possible before starting
+ * transmit. This reduces the chances that we have a TX under
+ * run due to memory contention. Any packet that fits entirely
+ * in the GMX FIFO can never have an under run regardless of
+ * memory load.
+ */
+ gmx_tx_thresh.u64 = cvmx_read_csr(CVMX_GMXX_TXX_THRESH(0, interface));
+ if (OCTEON_IS_MODEL(OCTEON_CN30XX) || OCTEON_IS_MODEL(OCTEON_CN31XX)
+ || OCTEON_IS_MODEL(OCTEON_CN50XX)) {
+ /* These chips have a fixed max threshold of 0x40 */
+ gmx_tx_thresh.s.cnt = 0x40;
+ } else {
+ /* Choose the max value for the number of ports */
+ if (num_ports <= 1)
+ gmx_tx_thresh.s.cnt = 0x100 / 1;
+ else if (num_ports == 2)
+ gmx_tx_thresh.s.cnt = 0x100 / 2;
+ else
+ gmx_tx_thresh.s.cnt = 0x100 / 4;
+ }
+ /*
+ * SPI and XAUI can have lots of ports but the GMX hardware
+ * only ever has a max of 4.
+ */
+ if (num_ports > 4)
+ num_ports = 4;
+ for (index = 0; index < num_ports; index++)
+ cvmx_write_csr(CVMX_GMXX_TXX_THRESH(index, interface),
+ gmx_tx_thresh.u64);
+
+ return 0;
+}
+
+/**
+ * Returns the IPD/PKO port number for a port on the given
+ * interface.
+ *
+ * @interface: Interface to use
+ * @port: Port on the interface
+ *
+ * Returns IPD/PKO port number
+ */
+int cvmx_helper_get_ipd_port(int interface, int port)
+{
+ switch (interface) {
+ case 0:
+ return port;
+ case 1:
+ return port + 16;
+ case 2:
+ return port + 32;
+ case 3:
+ return port + 36;
+ }
+ return -1;
+}
+EXPORT_SYMBOL_GPL(cvmx_helper_get_ipd_port);
+
+/**
+ * Returns the interface number for an IPD/PKO port number.
+ *
+ * @ipd_port: IPD/PKO port number
+ *
+ * Returns Interface number
+ */
+int cvmx_helper_get_interface_num(int ipd_port)
+{
+ if (ipd_port < 16)
+ return 0;
+ else if (ipd_port < 32)
+ return 1;
+ else if (ipd_port < 36)
+ return 2;
+ else if (ipd_port < 40)
+ return 3;
+ else
+ cvmx_dprintf("cvmx_helper_get_interface_num: Illegal IPD "
+ "port number\n");
+
+ return -1;
+}
+EXPORT_SYMBOL_GPL(cvmx_helper_get_interface_num);
+
+/**
+ * Returns the interface index number for an IPD/PKO port
+ * number.
+ *
+ * @ipd_port: IPD/PKO port number
+ *
+ * Returns Interface index number
+ */
+int cvmx_helper_get_interface_index_num(int ipd_port)
+{
+ if (ipd_port < 32)
+ return ipd_port & 15;
+ else if (ipd_port < 36)
+ return ipd_port & 3;
+ else if (ipd_port < 40)
+ return ipd_port & 3;
+ else
+ cvmx_dprintf("cvmx_helper_get_interface_index_num: "
+ "Illegal IPD port number\n");
+
+ return -1;
+}
+EXPORT_SYMBOL_GPL(cvmx_helper_get_interface_index_num);
diff --git a/arch/mips/cavium-octeon/executive/cvmx-helper-xaui.c b/arch/mips/cavium-octeon/executive/cvmx-helper-xaui.c
new file mode 100644
index 00000000000..7653b7e9219
--- /dev/null
+++ b/arch/mips/cavium-octeon/executive/cvmx-helper-xaui.c
@@ -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);
+}
diff --git a/arch/mips/cavium-octeon/executive/cvmx-helper.c b/arch/mips/cavium-octeon/executive/cvmx-helper.c
new file mode 100644
index 00000000000..7e5cf7a5e2f
--- /dev/null
+++ b/arch/mips/cavium-octeon/executive/cvmx-helper.c
@@ -0,0 +1,1290 @@
+/***********************license start***************
+ * Author: Cavium Networks
+ *
+ * Contact: support@caviumnetworks.com
+ * This file is part of the OCTEON SDK
+ *
+ * Copyright (c) 2003-2008 Cavium Networks
+ *
+ * This file is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, Version 2, as
+ * published by the Free Software Foundation.
+ *
+ * This file is distributed in the hope that it will be useful, but
+ * AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or
+ * NONINFRINGEMENT. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this file; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ * or visit http://www.gnu.org/licenses/.
+ *
+ * This file may also be available under a different license from Cavium.
+ * Contact Cavium Networks for more information
+ ***********************license end**************************************/
+
+/*
+ *
+ * Helper functions for common, but complicated tasks.
+ *
+ */
+#include <asm/octeon/octeon.h>
+
+#include <asm/octeon/cvmx-config.h>
+
+#include <asm/octeon/cvmx-fpa.h>
+#include <asm/octeon/cvmx-pip.h>
+#include <asm/octeon/cvmx-pko.h>
+#include <asm/octeon/cvmx-ipd.h>
+#include <asm/octeon/cvmx-spi.h>
+#include <asm/octeon/cvmx-helper.h>
+#include <asm/octeon/cvmx-helper-board.h>
+
+#include <asm/octeon/cvmx-pip-defs.h>
+#include <asm/octeon/cvmx-smix-defs.h>
+#include <asm/octeon/cvmx-asxx-defs.h>
+
+/**
+ * cvmx_override_pko_queue_priority(int ipd_port, uint64_t
+ * priorities[16]) is a function pointer. It is meant to allow
+ * customization of the PKO queue priorities based on the port
+ * number. Users should set this pointer to a function before
+ * calling any cvmx-helper operations.
+ */
+void (*cvmx_override_pko_queue_priority) (int pko_port,
+ uint64_t priorities[16]);
+
+/**
+ * cvmx_override_ipd_port_setup(int ipd_port) is a function
+ * pointer. It is meant to allow customization of the IPD port
+ * setup before packet input/output comes online. It is called
+ * after cvmx-helper does the default IPD configuration, but
+ * before IPD is enabled. Users should set this pointer to a
+ * function before calling any cvmx-helper operations.
+ */
+void (*cvmx_override_ipd_port_setup) (int ipd_port);
+
+/* Port count per interface */
+static int interface_port_count[5];
+
+/* Port last configured link info index by IPD/PKO port */
+static cvmx_helper_link_info_t
+ port_link_info[CVMX_PIP_NUM_INPUT_PORTS];
+
+/**
+ * Return the number of interfaces the chip has. Each interface
+ * may have multiple ports. Most chips support two interfaces,
+ * but the CNX0XX and CNX1XX are exceptions. These only support
+ * one interface.
+ *
+ * Returns Number of interfaces on chip
+ */
+int cvmx_helper_get_number_of_interfaces(void)
+{
+ if (OCTEON_IS_MODEL(OCTEON_CN56XX) || OCTEON_IS_MODEL(OCTEON_CN52XX))
+ return 4;
+ else
+ return 3;
+}
+EXPORT_SYMBOL_GPL(cvmx_helper_get_number_of_interfaces);
+
+/**
+ * Return the number of ports on an interface. Depending on the
+ * chip and configuration, this can be 1-16. A value of 0
+ * specifies that the interface doesn't exist or isn't usable.
+ *
+ * @interface: Interface to get the port count for
+ *
+ * Returns Number of ports on interface. Can be Zero.
+ */
+int cvmx_helper_ports_on_interface(int interface)
+{
+ return interface_port_count[interface];
+}
+EXPORT_SYMBOL_GPL(cvmx_helper_ports_on_interface);
+
+/**
+ * @INTERNAL
+ * Return interface mode for CN68xx.
+ */
+static cvmx_helper_interface_mode_t __cvmx_get_mode_cn68xx(int interface)
+{
+ union cvmx_mio_qlmx_cfg qlm_cfg;
+ switch (interface) {
+ case 0:
+ qlm_cfg.u64 = cvmx_read_csr(CVMX_MIO_QLMX_CFG(0));
+ /* QLM is disabled when QLM SPD is 15. */
+ if (qlm_cfg.s.qlm_spd == 15)
+ return CVMX_HELPER_INTERFACE_MODE_DISABLED;
+
+ if (qlm_cfg.s.qlm_cfg == 2)
+ return CVMX_HELPER_INTERFACE_MODE_SGMII;
+ else if (qlm_cfg.s.qlm_cfg == 3)
+ return CVMX_HELPER_INTERFACE_MODE_XAUI;
+ else
+ return CVMX_HELPER_INTERFACE_MODE_DISABLED;
+ case 2:
+ case 3:
+ case 4:
+ qlm_cfg.u64 = cvmx_read_csr(CVMX_MIO_QLMX_CFG(interface));
+ /* QLM is disabled when QLM SPD is 15. */
+ if (qlm_cfg.s.qlm_spd == 15)
+ return CVMX_HELPER_INTERFACE_MODE_DISABLED;
+
+ if (qlm_cfg.s.qlm_cfg == 2)
+ return CVMX_HELPER_INTERFACE_MODE_SGMII;
+ else if (qlm_cfg.s.qlm_cfg == 3)
+ return CVMX_HELPER_INTERFACE_MODE_XAUI;
+ else
+ return CVMX_HELPER_INTERFACE_MODE_DISABLED;
+ case 7:
+ qlm_cfg.u64 = cvmx_read_csr(CVMX_MIO_QLMX_CFG(3));
+ /* QLM is disabled when QLM SPD is 15. */
+ if (qlm_cfg.s.qlm_spd == 15) {
+ return CVMX_HELPER_INTERFACE_MODE_DISABLED;
+ } else if (qlm_cfg.s.qlm_cfg != 0) {
+ qlm_cfg.u64 = cvmx_read_csr(CVMX_MIO_QLMX_CFG(1));
+ if (qlm_cfg.s.qlm_cfg != 0)
+ return CVMX_HELPER_INTERFACE_MODE_DISABLED;
+ }
+ return CVMX_HELPER_INTERFACE_MODE_NPI;
+ case 8:
+ return CVMX_HELPER_INTERFACE_MODE_LOOP;
+ default:
+ return CVMX_HELPER_INTERFACE_MODE_DISABLED;
+ }
+}
+
+/**
+ * @INTERNAL
+ * Return interface mode for an Octeon II
+ */
+static cvmx_helper_interface_mode_t __cvmx_get_mode_octeon2(int interface)
+{
+ union cvmx_gmxx_inf_mode mode;
+
+ if (OCTEON_IS_MODEL(OCTEON_CN68XX))
+ return __cvmx_get_mode_cn68xx(interface);
+
+ if (interface == 2)
+ return CVMX_HELPER_INTERFACE_MODE_NPI;
+
+ if (interface == 3)
+ return CVMX_HELPER_INTERFACE_MODE_LOOP;
+
+ /* Only present in CN63XX & CN66XX Octeon model */
+ if ((OCTEON_IS_MODEL(OCTEON_CN63XX) &&
+ (interface == 4 || interface == 5)) ||
+ (OCTEON_IS_MODEL(OCTEON_CN66XX) &&
+ interface >= 4 && interface <= 7)) {
+ return CVMX_HELPER_INTERFACE_MODE_DISABLED;
+ }
+
+ if (OCTEON_IS_MODEL(OCTEON_CN66XX)) {
+ union cvmx_mio_qlmx_cfg mio_qlm_cfg;
+
+ /* QLM2 is SGMII0 and QLM1 is SGMII1 */
+ if (interface == 0)
+ mio_qlm_cfg.u64 = cvmx_read_csr(CVMX_MIO_QLMX_CFG(2));
+ else if (interface == 1)
+ mio_qlm_cfg.u64 = cvmx_read_csr(CVMX_MIO_QLMX_CFG(1));
+ else
+ return CVMX_HELPER_INTERFACE_MODE_DISABLED;
+
+ if (mio_qlm_cfg.s.qlm_spd == 15)
+ return CVMX_HELPER_INTERFACE_MODE_DISABLED;
+
+ if (mio_qlm_cfg.s.qlm_cfg == 9)
+ return CVMX_HELPER_INTERFACE_MODE_SGMII;
+ else if (mio_qlm_cfg.s.qlm_cfg == 11)
+ return CVMX_HELPER_INTERFACE_MODE_XAUI;
+ else
+ return CVMX_HELPER_INTERFACE_MODE_DISABLED;
+ } else if (OCTEON_IS_MODEL(OCTEON_CN61XX)) {
+ union cvmx_mio_qlmx_cfg qlm_cfg;
+
+ if (interface == 0) {
+ qlm_cfg.u64 = cvmx_read_csr(CVMX_MIO_QLMX_CFG(2));
+ if (qlm_cfg.s.qlm_cfg == 2)
+ return CVMX_HELPER_INTERFACE_MODE_SGMII;
+ else if (qlm_cfg.s.qlm_cfg == 3)
+ return CVMX_HELPER_INTERFACE_MODE_XAUI;
+ else
+ return CVMX_HELPER_INTERFACE_MODE_DISABLED;
+ } else if (interface == 1) {
+ qlm_cfg.u64 = cvmx_read_csr(CVMX_MIO_QLMX_CFG(0));
+ if (qlm_cfg.s.qlm_cfg == 2)
+ return CVMX_HELPER_INTERFACE_MODE_SGMII;
+ else if (qlm_cfg.s.qlm_cfg == 3)
+ return CVMX_HELPER_INTERFACE_MODE_XAUI;
+ else
+ return CVMX_HELPER_INTERFACE_MODE_DISABLED;
+ }
+ } else if (OCTEON_IS_MODEL(OCTEON_CNF71XX)) {
+ if (interface == 0) {
+ union cvmx_mio_qlmx_cfg qlm_cfg;
+ qlm_cfg.u64 = cvmx_read_csr(CVMX_MIO_QLMX_CFG(0));
+ if (qlm_cfg.s.qlm_cfg == 2)
+ return CVMX_HELPER_INTERFACE_MODE_SGMII;
+ }
+ return CVMX_HELPER_INTERFACE_MODE_DISABLED;
+ }
+
+ if (interface == 1 && OCTEON_IS_MODEL(OCTEON_CN63XX))
+ return CVMX_HELPER_INTERFACE_MODE_DISABLED;
+
+ mode.u64 = cvmx_read_csr(CVMX_GMXX_INF_MODE(interface));
+
+ if (OCTEON_IS_MODEL(OCTEON_CN63XX)) {
+ switch (mode.cn63xx.mode) {
+ case 0:
+ return CVMX_HELPER_INTERFACE_MODE_SGMII;
+ case 1:
+ return CVMX_HELPER_INTERFACE_MODE_XAUI;
+ default:
+ return CVMX_HELPER_INTERFACE_MODE_DISABLED;
+ }
+ } else {
+ if (!mode.s.en)
+ return CVMX_HELPER_INTERFACE_MODE_DISABLED;
+
+ if (mode.s.type)
+ return CVMX_HELPER_INTERFACE_MODE_GMII;
+ else
+ return CVMX_HELPER_INTERFACE_MODE_RGMII;
+ }
+}
+
+/**
+ * Get the operating mode of an interface. Depending on the Octeon
+ * chip and configuration, this function returns an enumeration
+ * of the type of packet I/O supported by an interface.
+ *
+ * @interface: Interface to probe
+ *
+ * Returns Mode of the interface. Unknown or unsupported interfaces return
+ * DISABLED.
+ */
+cvmx_helper_interface_mode_t cvmx_helper_interface_get_mode(int interface)
+{
+ union cvmx_gmxx_inf_mode mode;
+
+ if (interface < 0 ||
+ interface >= cvmx_helper_get_number_of_interfaces())
+ return CVMX_HELPER_INTERFACE_MODE_DISABLED;
+
+ /*
+ * Octeon II models
+ */
+ if (OCTEON_IS_MODEL(OCTEON_CN6XXX) || OCTEON_IS_MODEL(OCTEON_CNF71XX))
+ return __cvmx_get_mode_octeon2(interface);
+
+ /*
+ * Octeon and Octeon Plus models
+ */
+ if (interface == 2)
+ return CVMX_HELPER_INTERFACE_MODE_NPI;
+
+ if (interface == 3) {
+ if (OCTEON_IS_MODEL(OCTEON_CN56XX)
+ || OCTEON_IS_MODEL(OCTEON_CN52XX))
+ return CVMX_HELPER_INTERFACE_MODE_LOOP;
+ else
+ return CVMX_HELPER_INTERFACE_MODE_DISABLED;
+ }
+
+ if (interface == 0
+ && cvmx_sysinfo_get()->board_type == CVMX_BOARD_TYPE_CN3005_EVB_HS5
+ && cvmx_sysinfo_get()->board_rev_major == 1) {
+ /*
+ * Lie about interface type of CN3005 board. This
+ * board has a switch on port 1 like the other
+ * evaluation boards, but it is connected over RGMII
+ * instead of GMII. Report GMII mode so that the
+ * speed is forced to 1 Gbit full duplex. Other than
+ * some initial configuration (which does not use the
+ * output of this function) there is no difference in
+ * setup between GMII and RGMII modes.
+ */
+ return CVMX_HELPER_INTERFACE_MODE_GMII;
+ }
+
+ /* Interface 1 is always disabled on CN31XX and CN30XX */
+ if ((interface == 1)
+ && (OCTEON_IS_MODEL(OCTEON_CN31XX) || OCTEON_IS_MODEL(OCTEON_CN30XX)
+ || OCTEON_IS_MODEL(OCTEON_CN50XX)
+ || OCTEON_IS_MODEL(OCTEON_CN52XX)))
+ return CVMX_HELPER_INTERFACE_MODE_DISABLED;
+
+ mode.u64 = cvmx_read_csr(CVMX_GMXX_INF_MODE(interface));
+
+ if (OCTEON_IS_MODEL(OCTEON_CN56XX) || OCTEON_IS_MODEL(OCTEON_CN52XX)) {
+ switch (mode.cn56xx.mode) {
+ case 0:
+ return CVMX_HELPER_INTERFACE_MODE_DISABLED;
+ case 1:
+ return CVMX_HELPER_INTERFACE_MODE_XAUI;
+ case 2:
+ return CVMX_HELPER_INTERFACE_MODE_SGMII;
+ case 3:
+ return CVMX_HELPER_INTERFACE_MODE_PICMG;
+ default:
+ return CVMX_HELPER_INTERFACE_MODE_DISABLED;
+ }
+ } else {
+ if (!mode.s.en)
+ return CVMX_HELPER_INTERFACE_MODE_DISABLED;
+
+ if (mode.s.type) {
+ if (OCTEON_IS_MODEL(OCTEON_CN38XX)
+ || OCTEON_IS_MODEL(OCTEON_CN58XX))
+ return CVMX_HELPER_INTERFACE_MODE_SPI;
+ else
+ return CVMX_HELPER_INTERFACE_MODE_GMII;
+ } else
+ return CVMX_HELPER_INTERFACE_MODE_RGMII;
+ }
+}
+EXPORT_SYMBOL_GPL(cvmx_helper_interface_get_mode);
+
+/**
+ * Configure the IPD/PIP tagging and QoS options for a specific
+ * port. This function determines the POW work queue entry
+ * contents for a port. The setup performed here is controlled by
+ * the defines in executive-config.h.
+ *
+ * @ipd_port: Port to configure. This follows the IPD numbering, not the
+ * per interface numbering
+ *
+ * Returns Zero on success, negative on failure
+ */
+static int __cvmx_helper_port_setup_ipd(int ipd_port)
+{
+ union cvmx_pip_prt_cfgx port_config;
+ union cvmx_pip_prt_tagx tag_config;
+
+ port_config.u64 = cvmx_read_csr(CVMX_PIP_PRT_CFGX(ipd_port));
+ tag_config.u64 = cvmx_read_csr(CVMX_PIP_PRT_TAGX(ipd_port));
+
+ /* Have each port go to a different POW queue */
+ port_config.s.qos = ipd_port & 0x7;
+
+ /* Process the headers and place the IP header in the work queue */
+ port_config.s.mode = CVMX_HELPER_INPUT_PORT_SKIP_MODE;
+
+ tag_config.s.ip6_src_flag = CVMX_HELPER_INPUT_TAG_IPV6_SRC_IP;
+ tag_config.s.ip6_dst_flag = CVMX_HELPER_INPUT_TAG_IPV6_DST_IP;
+ tag_config.s.ip6_sprt_flag = CVMX_HELPER_INPUT_TAG_IPV6_SRC_PORT;
+ tag_config.s.ip6_dprt_flag = CVMX_HELPER_INPUT_TAG_IPV6_DST_PORT;
+ tag_config.s.ip6_nxth_flag = CVMX_HELPER_INPUT_TAG_IPV6_NEXT_HEADER;
+ tag_config.s.ip4_src_flag = CVMX_HELPER_INPUT_TAG_IPV4_SRC_IP;
+ tag_config.s.ip4_dst_flag = CVMX_HELPER_INPUT_TAG_IPV4_DST_IP;
+ tag_config.s.ip4_sprt_flag = CVMX_HELPER_INPUT_TAG_IPV4_SRC_PORT;
+ tag_config.s.ip4_dprt_flag = CVMX_HELPER_INPUT_TAG_IPV4_DST_PORT;
+ tag_config.s.ip4_pctl_flag = CVMX_HELPER_INPUT_TAG_IPV4_PROTOCOL;
+ tag_config.s.inc_prt_flag = CVMX_HELPER_INPUT_TAG_INPUT_PORT;
+ tag_config.s.tcp6_tag_type = CVMX_HELPER_INPUT_TAG_TYPE;
+ tag_config.s.tcp4_tag_type = CVMX_HELPER_INPUT_TAG_TYPE;
+ tag_config.s.ip6_tag_type = CVMX_HELPER_INPUT_TAG_TYPE;
+ tag_config.s.ip4_tag_type = CVMX_HELPER_INPUT_TAG_TYPE;
+ tag_config.s.non_tag_type = CVMX_HELPER_INPUT_TAG_TYPE;
+ /* Put all packets in group 0. Other groups can be used by the app */
+ tag_config.s.grp = 0;
+
+ cvmx_pip_config_port(ipd_port, port_config, tag_config);
+
+ /* Give the user a chance to override our setting for each port */
+ if (cvmx_override_ipd_port_setup)
+ cvmx_override_ipd_port_setup(ipd_port);
+
+ return 0;
+}
+
+/**
+ * This function sets the interface_port_count[interface] correctly,
+ * without modifying any hardware configuration. Hardware setup of
+ * the ports will be performed later.
+ *
+ * @interface: Interface to probe
+ *
+ * Returns Zero on success, negative on failure
+ */
+int cvmx_helper_interface_enumerate(int interface)
+{
+ switch (cvmx_helper_interface_get_mode(interface)) {
+ /* These types don't support ports to IPD/PKO */
+ case CVMX_HELPER_INTERFACE_MODE_DISABLED:
+ case CVMX_HELPER_INTERFACE_MODE_PCIE:
+ interface_port_count[interface] = 0;
+ break;
+ /* XAUI is a single high speed port */
+ case CVMX_HELPER_INTERFACE_MODE_XAUI:
+ interface_port_count[interface] =
+ __cvmx_helper_xaui_enumerate(interface);
+ break;
+ /*
+ * RGMII/GMII/MII are all treated about the same. Most
+ * functions refer to these ports as RGMII.
+ */
+ case CVMX_HELPER_INTERFACE_MODE_RGMII:
+ case CVMX_HELPER_INTERFACE_MODE_GMII:
+ interface_port_count[interface] =
+ __cvmx_helper_rgmii_enumerate(interface);
+ break;
+ /*
+ * SPI4 can have 1-16 ports depending on the device at
+ * the other end.
+ */
+ case CVMX_HELPER_INTERFACE_MODE_SPI:
+ interface_port_count[interface] =
+ __cvmx_helper_spi_enumerate(interface);
+ break;
+ /*
+ * SGMII can have 1-4 ports depending on how many are
+ * hooked up.
+ */
+ case CVMX_HELPER_INTERFACE_MODE_SGMII:
+ case CVMX_HELPER_INTERFACE_MODE_PICMG:
+ interface_port_count[interface] =
+ __cvmx_helper_sgmii_enumerate(interface);
+ break;
+ /* PCI target Network Packet Interface */
+ case CVMX_HELPER_INTERFACE_MODE_NPI:
+ interface_port_count[interface] =
+ __cvmx_helper_npi_enumerate(interface);
+ break;
+ /*
+ * Special loopback only ports. These are not the same
+ * as other ports in loopback mode.
+ */
+ case CVMX_HELPER_INTERFACE_MODE_LOOP:
+ interface_port_count[interface] =
+ __cvmx_helper_loop_enumerate(interface);
+ break;
+ }
+
+ interface_port_count[interface] =
+ __cvmx_helper_board_interface_probe(interface,
+ interface_port_count
+ [interface]);
+
+ /* Make sure all global variables propagate to other cores */
+ CVMX_SYNCWS;
+
+ return 0;
+}
+
+/**
+ * This function probes an interface to determine the actual
+ * number of hardware ports connected to it. It doesn't setup the
+ * ports or enable them. The main goal here is to set the global
+ * interface_port_count[interface] correctly. Hardware setup of the
+ * ports will be performed later.
+ *
+ * @interface: Interface to probe
+ *
+ * Returns Zero on success, negative on failure
+ */
+int cvmx_helper_interface_probe(int interface)
+{
+ cvmx_helper_interface_enumerate(interface);
+ /* At this stage in the game we don't want packets to be moving yet.
+ The following probe calls should perform hardware setup
+ needed to determine port counts. Receive must still be disabled */
+ switch (cvmx_helper_interface_get_mode(interface)) {
+ /* These types don't support ports to IPD/PKO */
+ case CVMX_HELPER_INTERFACE_MODE_DISABLED:
+ case CVMX_HELPER_INTERFACE_MODE_PCIE:
+ break;
+ /* XAUI is a single high speed port */
+ case CVMX_HELPER_INTERFACE_MODE_XAUI:
+ __cvmx_helper_xaui_probe(interface);
+ break;
+ /*
+ * RGMII/GMII/MII are all treated about the same. Most
+ * functions refer to these ports as RGMII.
+ */
+ case CVMX_HELPER_INTERFACE_MODE_RGMII:
+ case CVMX_HELPER_INTERFACE_MODE_GMII:
+ __cvmx_helper_rgmii_probe(interface);
+ break;
+ /*
+ * SPI4 can have 1-16 ports depending on the device at
+ * the other end.
+ */
+ case CVMX_HELPER_INTERFACE_MODE_SPI:
+ __cvmx_helper_spi_probe(interface);
+ break;
+ /*
+ * SGMII can have 1-4 ports depending on how many are
+ * hooked up.
+ */
+ case CVMX_HELPER_INTERFACE_MODE_SGMII:
+ case CVMX_HELPER_INTERFACE_MODE_PICMG:
+ __cvmx_helper_sgmii_probe(interface);
+ break;
+ /* PCI target Network Packet Interface */
+ case CVMX_HELPER_INTERFACE_MODE_NPI:
+ __cvmx_helper_npi_probe(interface);
+ break;
+ /*
+ * Special loopback only ports. These are not the same
+ * as other ports in loopback mode.
+ */
+ case CVMX_HELPER_INTERFACE_MODE_LOOP:
+ __cvmx_helper_loop_probe(interface);
+ break;
+ }
+
+ /* Make sure all global variables propagate to other cores */
+ CVMX_SYNCWS;
+
+ return 0;
+}
+
+/**
+ * Setup the IPD/PIP for the ports on an interface. Packet
+ * classification and tagging are set for every port on the
+ * interface. The number of ports on the interface must already
+ * have been probed.
+ *
+ * @interface: Interface to setup IPD/PIP for
+ *
+ * Returns Zero on success, negative on failure
+ */
+static int __cvmx_helper_interface_setup_ipd(int interface)
+{
+ int ipd_port = cvmx_helper_get_ipd_port(interface, 0);
+ int num_ports = interface_port_count[interface];
+
+ while (num_ports--) {
+ __cvmx_helper_port_setup_ipd(ipd_port);
+ ipd_port++;
+ }
+ return 0;
+}
+
+/**
+ * Setup global setting for IPD/PIP not related to a specific
+ * interface or port. This must be called before IPD is enabled.
+ *
+ * Returns Zero on success, negative on failure.
+ */
+static int __cvmx_helper_global_setup_ipd(void)
+{
+ /* Setup the global packet input options */
+ cvmx_ipd_config(CVMX_FPA_PACKET_POOL_SIZE / 8,
+ CVMX_HELPER_FIRST_MBUFF_SKIP / 8,
+ CVMX_HELPER_NOT_FIRST_MBUFF_SKIP / 8,
+ /* The +8 is to account for the next ptr */
+ (CVMX_HELPER_FIRST_MBUFF_SKIP + 8) / 128,
+ /* The +8 is to account for the next ptr */
+ (CVMX_HELPER_NOT_FIRST_MBUFF_SKIP + 8) / 128,
+ CVMX_FPA_WQE_POOL,
+ CVMX_IPD_OPC_MODE_STT,
+ CVMX_HELPER_ENABLE_BACK_PRESSURE);
+ return 0;
+}
+
+/**
+ * Setup the PKO for the ports on an interface. The number of
+ * queues per port and the priority of each PKO output queue
+ * is set here. PKO must be disabled when this function is called.
+ *
+ * @interface: Interface to setup PKO for
+ *
+ * Returns Zero on success, negative on failure
+ */
+static int __cvmx_helper_interface_setup_pko(int interface)
+{
+ /*
+ * Each packet output queue has an associated priority. The
+ * higher the priority, the more often it can send a packet. A
+ * priority of 8 means it can send in all 8 rounds of
+ * contention. We're going to make each queue one less than
+ * the last. The vector of priorities has been extended to
+ * support CN5xxx CPUs, where up to 16 queues can be
+ * associated to a port. To keep backward compatibility we
+ * don't change the initial 8 priorities and replicate them in
+ * the second half. With per-core PKO queues (PKO lockless
+ * operation) all queues have the same priority.
+ */
+ uint64_t priorities[16] =
+ { 8, 7, 6, 5, 4, 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, 1 };
+
+ /*
+ * Setup the IPD/PIP and PKO for the ports discovered
+ * above. Here packet classification, tagging and output
+ * priorities are set.
+ */
+ int ipd_port = cvmx_helper_get_ipd_port(interface, 0);
+ int num_ports = interface_port_count[interface];
+ while (num_ports--) {
+ /*
+ * Give the user a chance to override the per queue
+ * priorities.
+ */
+ if (cvmx_override_pko_queue_priority)
+ cvmx_override_pko_queue_priority(ipd_port, priorities);
+
+ cvmx_pko_config_port(ipd_port,
+ cvmx_pko_get_base_queue_per_core(ipd_port,
+ 0),
+ cvmx_pko_get_num_queues(ipd_port),
+ priorities);
+ ipd_port++;
+ }
+ return 0;
+}
+
+/**
+ * Setup global setting for PKO not related to a specific
+ * interface or port. This must be called before PKO is enabled.
+ *
+ * Returns Zero on success, negative on failure.
+ */
+static int __cvmx_helper_global_setup_pko(void)
+{
+ /*
+ * Disable tagwait FAU timeout. This needs to be done before
+ * anyone might start packet output using tags.
+ */
+ union cvmx_iob_fau_timeout fau_to;
+ fau_to.u64 = 0;
+ fau_to.s.tout_val = 0xfff;
+ fau_to.s.tout_enb = 0;
+ cvmx_write_csr(CVMX_IOB_FAU_TIMEOUT, fau_to.u64);
+ return 0;
+}
+
+/**
+ * Setup global backpressure setting.
+ *
+ * Returns Zero on success, negative on failure
+ */
+static int __cvmx_helper_global_setup_backpressure(void)
+{
+#if CVMX_HELPER_DISABLE_RGMII_BACKPRESSURE
+ /* Disable backpressure if configured to do so */
+ /* Disable backpressure (pause frame) generation */
+ int num_interfaces = cvmx_helper_get_number_of_interfaces();
+ int interface;
+ for (interface = 0; interface < num_interfaces; interface++) {
+ switch (cvmx_helper_interface_get_mode(interface)) {
+ case CVMX_HELPER_INTERFACE_MODE_DISABLED:
+ case CVMX_HELPER_INTERFACE_MODE_PCIE:
+ case CVMX_HELPER_INTERFACE_MODE_NPI:
+ case CVMX_HELPER_INTERFACE_MODE_LOOP:
+ case CVMX_HELPER_INTERFACE_MODE_XAUI:
+ break;
+ case CVMX_HELPER_INTERFACE_MODE_RGMII:
+ case CVMX_HELPER_INTERFACE_MODE_GMII:
+ case CVMX_HELPER_INTERFACE_MODE_SPI:
+ case CVMX_HELPER_INTERFACE_MODE_SGMII:
+ case CVMX_HELPER_INTERFACE_MODE_PICMG:
+ cvmx_gmx_set_backpressure_override(interface, 0xf);
+ break;
+ }
+ }
+#endif
+
+ return 0;
+}
+
+/**
+ * Enable packet input/output from the hardware. This function is
+ * called after all internal setup is complete and IPD is enabled.
+ * After this function completes, packets will be accepted from the
+ * hardware ports. PKO should still be disabled to make sure packets
+ * aren't sent out partially setup hardware.
+ *
+ * @interface: Interface to enable
+ *
+ * Returns Zero on success, negative on failure
+ */
+static int __cvmx_helper_packet_hardware_enable(int interface)
+{
+ int result = 0;
+ switch (cvmx_helper_interface_get_mode(interface)) {
+ /* These types don't support ports to IPD/PKO */
+ case CVMX_HELPER_INTERFACE_MODE_DISABLED:
+ case CVMX_HELPER_INTERFACE_MODE_PCIE:
+ /* Nothing to do */
+ break;
+ /* XAUI is a single high speed port */
+ case CVMX_HELPER_INTERFACE_MODE_XAUI:
+ result = __cvmx_helper_xaui_enable(interface);
+ break;
+ /*
+ * RGMII/GMII/MII are all treated about the same. Most
+ * functions refer to these ports as RGMII
+ */
+ case CVMX_HELPER_INTERFACE_MODE_RGMII:
+ case CVMX_HELPER_INTERFACE_MODE_GMII:
+ result = __cvmx_helper_rgmii_enable(interface);
+ break;
+ /*
+ * SPI4 can have 1-16 ports depending on the device at
+ * the other end
+ */
+ case CVMX_HELPER_INTERFACE_MODE_SPI:
+ result = __cvmx_helper_spi_enable(interface);
+ break;
+ /*
+ * SGMII can have 1-4 ports depending on how many are
+ * hooked up
+ */
+ case CVMX_HELPER_INTERFACE_MODE_SGMII:
+ case CVMX_HELPER_INTERFACE_MODE_PICMG:
+ result = __cvmx_helper_sgmii_enable(interface);
+ break;
+ /* PCI target Network Packet Interface */
+ case CVMX_HELPER_INTERFACE_MODE_NPI:
+ result = __cvmx_helper_npi_enable(interface);
+ break;
+ /*
+ * Special loopback only ports. These are not the same
+ * as other ports in loopback mode
+ */
+ case CVMX_HELPER_INTERFACE_MODE_LOOP:
+ result = __cvmx_helper_loop_enable(interface);
+ break;
+ }
+ result |= __cvmx_helper_board_hardware_enable(interface);
+ return result;
+}
+
+/**
+ * Function to adjust internal IPD pointer alignments
+ *
+ * Returns 0 on success
+ * !0 on failure
+ */
+int __cvmx_helper_errata_fix_ipd_ptr_alignment(void)
+{
+#define FIX_IPD_FIRST_BUFF_PAYLOAD_BYTES \
+ (CVMX_FPA_PACKET_POOL_SIZE-8-CVMX_HELPER_FIRST_MBUFF_SKIP)
+#define FIX_IPD_NON_FIRST_BUFF_PAYLOAD_BYTES \
+ (CVMX_FPA_PACKET_POOL_SIZE-8-CVMX_HELPER_NOT_FIRST_MBUFF_SKIP)
+#define FIX_IPD_OUTPORT 0
+ /* Ports 0-15 are interface 0, 16-31 are interface 1 */
+#define INTERFACE(port) (port >> 4)
+#define INDEX(port) (port & 0xf)
+ uint64_t *p64;
+ cvmx_pko_command_word0_t pko_command;
+ union cvmx_buf_ptr g_buffer, pkt_buffer;
+ cvmx_wqe_t *work;
+ int size, num_segs = 0, wqe_pcnt, pkt_pcnt;
+ union cvmx_gmxx_prtx_cfg gmx_cfg;
+ int retry_cnt;
+ int retry_loop_cnt;
+ int i;
+ cvmx_helper_link_info_t link_info;
+
+ /* Save values for restore at end */
+ uint64_t prtx_cfg =
+ cvmx_read_csr(CVMX_GMXX_PRTX_CFG
+ (INDEX(FIX_IPD_OUTPORT), INTERFACE(FIX_IPD_OUTPORT)));
+ uint64_t tx_ptr_en =
+ cvmx_read_csr(CVMX_ASXX_TX_PRT_EN(INTERFACE(FIX_IPD_OUTPORT)));
+ uint64_t rx_ptr_en =
+ cvmx_read_csr(CVMX_ASXX_RX_PRT_EN(INTERFACE(FIX_IPD_OUTPORT)));
+ uint64_t rxx_jabber =
+ cvmx_read_csr(CVMX_GMXX_RXX_JABBER
+ (INDEX(FIX_IPD_OUTPORT), INTERFACE(FIX_IPD_OUTPORT)));
+ uint64_t frame_max =
+ cvmx_read_csr(CVMX_GMXX_RXX_FRM_MAX
+ (INDEX(FIX_IPD_OUTPORT), INTERFACE(FIX_IPD_OUTPORT)));
+
+ /* Configure port to gig FDX as required for loopback mode */
+ cvmx_helper_rgmii_internal_loopback(FIX_IPD_OUTPORT);
+
+ /*
+ * Disable reception on all ports so if traffic is present it
+ * will not interfere.
+ */
+ cvmx_write_csr(CVMX_ASXX_RX_PRT_EN(INTERFACE(FIX_IPD_OUTPORT)), 0);
+
+ cvmx_wait(100000000ull);
+
+ for (retry_loop_cnt = 0; retry_loop_cnt < 10; retry_loop_cnt++) {
+ retry_cnt = 100000;
+ wqe_pcnt = cvmx_read_csr(CVMX_IPD_PTR_COUNT);
+ pkt_pcnt = (wqe_pcnt >> 7) & 0x7f;
+ wqe_pcnt &= 0x7f;
+
+ num_segs = (2 + pkt_pcnt - wqe_pcnt) & 3;
+
+ if (num_segs == 0)
+ goto fix_ipd_exit;
+
+ num_segs += 1;
+
+ size =
+ FIX_IPD_FIRST_BUFF_PAYLOAD_BYTES +
+ ((num_segs - 1) * FIX_IPD_NON_FIRST_BUFF_PAYLOAD_BYTES) -
+ (FIX_IPD_NON_FIRST_BUFF_PAYLOAD_BYTES / 2);
+
+ cvmx_write_csr(CVMX_ASXX_PRT_LOOP(INTERFACE(FIX_IPD_OUTPORT)),
+ 1 << INDEX(FIX_IPD_OUTPORT));
+ CVMX_SYNC;
+
+ g_buffer.u64 = 0;
+ g_buffer.s.addr =
+ cvmx_ptr_to_phys(cvmx_fpa_alloc(CVMX_FPA_WQE_POOL));
+ if (g_buffer.s.addr == 0) {
+ cvmx_dprintf("WARNING: FIX_IPD_PTR_ALIGNMENT "
+ "buffer allocation failure.\n");
+ goto fix_ipd_exit;
+ }
+
+ g_buffer.s.pool = CVMX_FPA_WQE_POOL;
+ g_buffer.s.size = num_segs;
+
+ pkt_buffer.u64 = 0;
+ pkt_buffer.s.addr =
+ cvmx_ptr_to_phys(cvmx_fpa_alloc(CVMX_FPA_PACKET_POOL));
+ if (pkt_buffer.s.addr == 0) {
+ cvmx_dprintf("WARNING: FIX_IPD_PTR_ALIGNMENT "
+ "buffer allocation failure.\n");
+ goto fix_ipd_exit;
+ }
+ pkt_buffer.s.i = 1;
+ pkt_buffer.s.pool = CVMX_FPA_PACKET_POOL;
+ pkt_buffer.s.size = FIX_IPD_FIRST_BUFF_PAYLOAD_BYTES;
+
+ p64 = (uint64_t *) cvmx_phys_to_ptr(pkt_buffer.s.addr);
+ p64[0] = 0xffffffffffff0000ull;
+ p64[1] = 0x08004510ull;
+ p64[2] = ((uint64_t) (size - 14) << 48) | 0x5ae740004000ull;
+ p64[3] = 0x3a5fc0a81073c0a8ull;
+
+ for (i = 0; i < num_segs; i++) {
+ if (i > 0)
+ pkt_buffer.s.size =
+ FIX_IPD_NON_FIRST_BUFF_PAYLOAD_BYTES;
+
+ if (i == (num_segs - 1))
+ pkt_buffer.s.i = 0;
+
+ *(uint64_t *) cvmx_phys_to_ptr(g_buffer.s.addr +
+ 8 * i) = pkt_buffer.u64;
+ }
+
+ /* Build the PKO command */
+ pko_command.u64 = 0;
+ pko_command.s.segs = num_segs;
+ pko_command.s.total_bytes = size;
+ pko_command.s.dontfree = 0;
+ pko_command.s.gather = 1;
+
+ gmx_cfg.u64 =
+ cvmx_read_csr(CVMX_GMXX_PRTX_CFG
+ (INDEX(FIX_IPD_OUTPORT),
+ INTERFACE(FIX_IPD_OUTPORT)));
+ gmx_cfg.s.en = 1;
+ cvmx_write_csr(CVMX_GMXX_PRTX_CFG
+ (INDEX(FIX_IPD_OUTPORT),
+ INTERFACE(FIX_IPD_OUTPORT)), gmx_cfg.u64);
+ cvmx_write_csr(CVMX_ASXX_TX_PRT_EN(INTERFACE(FIX_IPD_OUTPORT)),
+ 1 << INDEX(FIX_IPD_OUTPORT));
+ cvmx_write_csr(CVMX_ASXX_RX_PRT_EN(INTERFACE(FIX_IPD_OUTPORT)),
+ 1 << INDEX(FIX_IPD_OUTPORT));
+
+ cvmx_write_csr(CVMX_GMXX_RXX_JABBER
+ (INDEX(FIX_IPD_OUTPORT),
+ INTERFACE(FIX_IPD_OUTPORT)), 65392 - 14 - 4);
+ cvmx_write_csr(CVMX_GMXX_RXX_FRM_MAX
+ (INDEX(FIX_IPD_OUTPORT),
+ INTERFACE(FIX_IPD_OUTPORT)), 65392 - 14 - 4);
+
+ cvmx_pko_send_packet_prepare(FIX_IPD_OUTPORT,
+ cvmx_pko_get_base_queue
+ (FIX_IPD_OUTPORT),
+ CVMX_PKO_LOCK_CMD_QUEUE);
+ cvmx_pko_send_packet_finish(FIX_IPD_OUTPORT,
+ cvmx_pko_get_base_queue
+ (FIX_IPD_OUTPORT), pko_command,
+ g_buffer, CVMX_PKO_LOCK_CMD_QUEUE);
+
+ CVMX_SYNC;
+
+ do {
+ work = cvmx_pow_work_request_sync(CVMX_POW_WAIT);
+ retry_cnt--;
+ } while ((work == NULL) && (retry_cnt > 0));
+
+ if (!retry_cnt)
+ cvmx_dprintf("WARNING: FIX_IPD_PTR_ALIGNMENT "
+ "get_work() timeout occurred.\n");
+
+ /* Free packet */
+ if (work)
+ cvmx_helper_free_packet_data(work);
+ }
+
+fix_ipd_exit:
+
+ /* Return CSR configs to saved values */
+ cvmx_write_csr(CVMX_GMXX_PRTX_CFG
+ (INDEX(FIX_IPD_OUTPORT), INTERFACE(FIX_IPD_OUTPORT)),
+ prtx_cfg);
+ cvmx_write_csr(CVMX_ASXX_TX_PRT_EN(INTERFACE(FIX_IPD_OUTPORT)),
+ tx_ptr_en);
+ cvmx_write_csr(CVMX_ASXX_RX_PRT_EN(INTERFACE(FIX_IPD_OUTPORT)),
+ rx_ptr_en);
+ cvmx_write_csr(CVMX_GMXX_RXX_JABBER
+ (INDEX(FIX_IPD_OUTPORT), INTERFACE(FIX_IPD_OUTPORT)),
+ rxx_jabber);
+ cvmx_write_csr(CVMX_GMXX_RXX_FRM_MAX
+ (INDEX(FIX_IPD_OUTPORT), INTERFACE(FIX_IPD_OUTPORT)),
+ frame_max);
+ cvmx_write_csr(CVMX_ASXX_PRT_LOOP(INTERFACE(FIX_IPD_OUTPORT)), 0);
+ /* Set link to down so autonegotiation will set it up again */
+ link_info.u64 = 0;
+ cvmx_helper_link_set(FIX_IPD_OUTPORT, link_info);
+
+ /*
+ * Bring the link back up as autonegotiation is not done in
+ * user applications.
+ */
+ cvmx_helper_link_autoconf(FIX_IPD_OUTPORT);
+
+ CVMX_SYNC;
+ if (num_segs)
+ cvmx_dprintf("WARNING: FIX_IPD_PTR_ALIGNMENT failed.\n");
+
+ return !!num_segs;
+
+}
+
+/**
+ * Called after all internal packet IO paths are setup. This
+ * function enables IPD/PIP and begins packet input and output.
+ *
+ * Returns Zero on success, negative on failure
+ */
+int cvmx_helper_ipd_and_packet_input_enable(void)
+{
+ int num_interfaces;
+ int interface;
+
+ /* Enable IPD */
+ cvmx_ipd_enable();
+
+ /*
+ * Time to enable hardware ports packet input and output. Note
+ * that at this point IPD/PIP must be fully functional and PKO
+ * must be disabled
+ */
+ num_interfaces = cvmx_helper_get_number_of_interfaces();
+ for (interface = 0; interface < num_interfaces; interface++) {
+ if (cvmx_helper_ports_on_interface(interface) > 0)
+ __cvmx_helper_packet_hardware_enable(interface);
+ }
+
+ /* Finally enable PKO now that the entire path is up and running */
+ cvmx_pko_enable();
+
+ if ((OCTEON_IS_MODEL(OCTEON_CN31XX_PASS1)
+ || OCTEON_IS_MODEL(OCTEON_CN30XX_PASS1))
+ && (cvmx_sysinfo_get()->board_type != CVMX_BOARD_TYPE_SIM))
+ __cvmx_helper_errata_fix_ipd_ptr_alignment();
+ return 0;
+}
+EXPORT_SYMBOL_GPL(cvmx_helper_ipd_and_packet_input_enable);
+
+/**
+ * Initialize the PIP, IPD, and PKO hardware to support
+ * simple priority based queues for the ethernet ports. Each
+ * port is configured with a number of priority queues based
+ * on CVMX_PKO_QUEUES_PER_PORT_* where each queue is lower
+ * priority than the previous.
+ *
+ * Returns Zero on success, non-zero on failure
+ */
+int cvmx_helper_initialize_packet_io_global(void)
+{
+ int result = 0;
+ int interface;
+ union cvmx_l2c_cfg l2c_cfg;
+ union cvmx_smix_en smix_en;
+ const int num_interfaces = cvmx_helper_get_number_of_interfaces();
+
+ /*
+ * CN52XX pass 1: Due to a bug in 2nd order CDR, it needs to
+ * be disabled.
+ */
+ if (OCTEON_IS_MODEL(OCTEON_CN52XX_PASS1_0))
+ __cvmx_helper_errata_qlm_disable_2nd_order_cdr(1);
+
+ /*
+ * Tell L2 to give the IOB statically higher priority compared
+ * to the cores. This avoids conditions where IO blocks might
+ * be starved under very high L2 loads.
+ */
+ l2c_cfg.u64 = cvmx_read_csr(CVMX_L2C_CFG);
+ l2c_cfg.s.lrf_arb_mode = 0;
+ l2c_cfg.s.rfb_arb_mode = 0;
+ cvmx_write_csr(CVMX_L2C_CFG, l2c_cfg.u64);
+
+ /* Make sure SMI/MDIO is enabled so we can query PHYs */
+ smix_en.u64 = cvmx_read_csr(CVMX_SMIX_EN(0));
+ if (!smix_en.s.en) {
+ smix_en.s.en = 1;
+ cvmx_write_csr(CVMX_SMIX_EN(0), smix_en.u64);
+ }
+
+ /* Newer chips actually have two SMI/MDIO interfaces */
+ if (!OCTEON_IS_MODEL(OCTEON_CN3XXX) &&
+ !OCTEON_IS_MODEL(OCTEON_CN58XX) &&
+ !OCTEON_IS_MODEL(OCTEON_CN50XX)) {
+ smix_en.u64 = cvmx_read_csr(CVMX_SMIX_EN(1));
+ if (!smix_en.s.en) {
+ smix_en.s.en = 1;
+ cvmx_write_csr(CVMX_SMIX_EN(1), smix_en.u64);
+ }
+ }
+
+ cvmx_pko_initialize_global();
+ for (interface = 0; interface < num_interfaces; interface++) {
+ result |= cvmx_helper_interface_probe(interface);
+ if (cvmx_helper_ports_on_interface(interface) > 0)
+ cvmx_dprintf("Interface %d has %d ports (%s)\n",
+ interface,
+ cvmx_helper_ports_on_interface(interface),
+ cvmx_helper_interface_mode_to_string
+ (cvmx_helper_interface_get_mode
+ (interface)));
+ result |= __cvmx_helper_interface_setup_ipd(interface);
+ result |= __cvmx_helper_interface_setup_pko(interface);
+ }
+
+ result |= __cvmx_helper_global_setup_ipd();
+ result |= __cvmx_helper_global_setup_pko();
+
+ /* Enable any flow control and backpressure */
+ result |= __cvmx_helper_global_setup_backpressure();
+
+#if CVMX_HELPER_ENABLE_IPD
+ result |= cvmx_helper_ipd_and_packet_input_enable();
+#endif
+ return result;
+}
+EXPORT_SYMBOL_GPL(cvmx_helper_initialize_packet_io_global);
+
+/**
+ * Does core local initialization for packet io
+ *
+ * Returns Zero on success, non-zero on failure
+ */
+int cvmx_helper_initialize_packet_io_local(void)
+{
+ return cvmx_pko_initialize_local();
+}
+
+/**
+ * Auto configure an IPD/PKO port link state and speed. This
+ * function basically does the equivalent of:
+ * cvmx_helper_link_set(ipd_port, cvmx_helper_link_get(ipd_port));
+ *
+ * @ipd_port: IPD/PKO port to auto configure
+ *
+ * Returns Link state after configure
+ */
+cvmx_helper_link_info_t cvmx_helper_link_autoconf(int ipd_port)
+{
+ cvmx_helper_link_info_t link_info;
+ int interface = cvmx_helper_get_interface_num(ipd_port);
+ int index = cvmx_helper_get_interface_index_num(ipd_port);
+
+ if (index >= cvmx_helper_ports_on_interface(interface)) {
+ link_info.u64 = 0;
+ return link_info;
+ }
+
+ link_info = cvmx_helper_link_get(ipd_port);
+ if (link_info.u64 == port_link_info[ipd_port].u64)
+ return link_info;
+
+ /* If we fail to set the link speed, port_link_info will not change */
+ cvmx_helper_link_set(ipd_port, link_info);
+
+ /*
+ * port_link_info should be the current value, which will be
+ * different than expect if cvmx_helper_link_set() failed.
+ */
+ return port_link_info[ipd_port];
+}
+EXPORT_SYMBOL_GPL(cvmx_helper_link_autoconf);
+
+/**
+ * Return the link state of an IPD/PKO port as returned by
+ * auto negotiation. The result of this function may not match
+ * Octeon's link config if auto negotiation has changed since
+ * the last call to cvmx_helper_link_set().
+ *
+ * @ipd_port: IPD/PKO port to query
+ *
+ * Returns Link state
+ */
+cvmx_helper_link_info_t cvmx_helper_link_get(int ipd_port)
+{
+ cvmx_helper_link_info_t result;
+ int interface = cvmx_helper_get_interface_num(ipd_port);
+ int index = cvmx_helper_get_interface_index_num(ipd_port);
+
+ /* The default result will be a down link unless the code below
+ changes it */
+ result.u64 = 0;
+
+ if (index >= cvmx_helper_ports_on_interface(interface))
+ return result;
+
+ switch (cvmx_helper_interface_get_mode(interface)) {
+ case CVMX_HELPER_INTERFACE_MODE_DISABLED:
+ case CVMX_HELPER_INTERFACE_MODE_PCIE:
+ /* Network links are not supported */
+ break;
+ case CVMX_HELPER_INTERFACE_MODE_XAUI:
+ result = __cvmx_helper_xaui_link_get(ipd_port);
+ break;
+ case CVMX_HELPER_INTERFACE_MODE_GMII:
+ if (index == 0)
+ result = __cvmx_helper_rgmii_link_get(ipd_port);
+ else {
+ result.s.full_duplex = 1;
+ result.s.link_up = 1;
+ result.s.speed = 1000;
+ }
+ break;
+ case CVMX_HELPER_INTERFACE_MODE_RGMII:
+ result = __cvmx_helper_rgmii_link_get(ipd_port);
+ break;
+ case CVMX_HELPER_INTERFACE_MODE_SPI:
+ result = __cvmx_helper_spi_link_get(ipd_port);
+ break;
+ case CVMX_HELPER_INTERFACE_MODE_SGMII:
+ case CVMX_HELPER_INTERFACE_MODE_PICMG:
+ result = __cvmx_helper_sgmii_link_get(ipd_port);
+ break;
+ case CVMX_HELPER_INTERFACE_MODE_NPI:
+ case CVMX_HELPER_INTERFACE_MODE_LOOP:
+ /* Network links are not supported */
+ break;
+ }
+ return result;
+}
+EXPORT_SYMBOL_GPL(cvmx_helper_link_get);
+
+/**
+ * Configure an IPD/PKO port for the specified link state. This
+ * function does not influence auto negotiation at the PHY level.
+ * The passed link state must always match the link state returned
+ * by cvmx_helper_link_get(). It is normally best to use
+ * cvmx_helper_link_autoconf() instead.
+ *
+ * @ipd_port: IPD/PKO port to configure
+ * @link_info: The new link state
+ *
+ * Returns Zero on success, negative on failure
+ */
+int cvmx_helper_link_set(int ipd_port, cvmx_helper_link_info_t link_info)
+{
+ int result = -1;
+ int interface = cvmx_helper_get_interface_num(ipd_port);
+ int index = cvmx_helper_get_interface_index_num(ipd_port);
+
+ if (index >= cvmx_helper_ports_on_interface(interface))
+ return -1;
+
+ switch (cvmx_helper_interface_get_mode(interface)) {
+ case CVMX_HELPER_INTERFACE_MODE_DISABLED:
+ case CVMX_HELPER_INTERFACE_MODE_PCIE:
+ break;
+ case CVMX_HELPER_INTERFACE_MODE_XAUI:
+ result = __cvmx_helper_xaui_link_set(ipd_port, link_info);
+ break;
+ /*
+ * RGMII/GMII/MII are all treated about the same. Most
+ * functions refer to these ports as RGMII.
+ */
+ case CVMX_HELPER_INTERFACE_MODE_RGMII:
+ case CVMX_HELPER_INTERFACE_MODE_GMII:
+ result = __cvmx_helper_rgmii_link_set(ipd_port, link_info);
+ break;
+ case CVMX_HELPER_INTERFACE_MODE_SPI:
+ result = __cvmx_helper_spi_link_set(ipd_port, link_info);
+ break;
+ case CVMX_HELPER_INTERFACE_MODE_SGMII:
+ case CVMX_HELPER_INTERFACE_MODE_PICMG:
+ result = __cvmx_helper_sgmii_link_set(ipd_port, link_info);
+ break;
+ case CVMX_HELPER_INTERFACE_MODE_NPI:
+ case CVMX_HELPER_INTERFACE_MODE_LOOP:
+ break;
+ }
+ /* Set the port_link_info here so that the link status is updated
+ no matter how cvmx_helper_link_set is called. We don't change
+ the value if link_set failed */
+ if (result == 0)
+ port_link_info[ipd_port].u64 = link_info.u64;
+ return result;
+}
+EXPORT_SYMBOL_GPL(cvmx_helper_link_set);
+
+/**
+ * Configure a port for internal and/or external loopback. Internal loopback
+ * causes packets sent by the port to be received by Octeon. External loopback
+ * causes packets received from the wire to sent out again.
+ *
+ * @ipd_port: IPD/PKO port to loopback.
+ * @enable_internal:
+ * Non zero if you want internal loopback
+ * @enable_external:
+ * Non zero if you want external loopback
+ *
+ * Returns Zero on success, negative on failure.
+ */
+int cvmx_helper_configure_loopback(int ipd_port, int enable_internal,
+ int enable_external)
+{
+ int result = -1;
+ int interface = cvmx_helper_get_interface_num(ipd_port);
+ int index = cvmx_helper_get_interface_index_num(ipd_port);
+
+ if (index >= cvmx_helper_ports_on_interface(interface))
+ return -1;
+
+ switch (cvmx_helper_interface_get_mode(interface)) {
+ case CVMX_HELPER_INTERFACE_MODE_DISABLED:
+ case CVMX_HELPER_INTERFACE_MODE_PCIE:
+ case CVMX_HELPER_INTERFACE_MODE_SPI:
+ case CVMX_HELPER_INTERFACE_MODE_NPI:
+ case CVMX_HELPER_INTERFACE_MODE_LOOP:
+ break;
+ case CVMX_HELPER_INTERFACE_MODE_XAUI:
+ result =
+ __cvmx_helper_xaui_configure_loopback(ipd_port,
+ enable_internal,
+ enable_external);
+ break;
+ case CVMX_HELPER_INTERFACE_MODE_RGMII:
+ case CVMX_HELPER_INTERFACE_MODE_GMII:
+ result =
+ __cvmx_helper_rgmii_configure_loopback(ipd_port,
+ enable_internal,
+ enable_external);
+ break;
+ case CVMX_HELPER_INTERFACE_MODE_SGMII:
+ case CVMX_HELPER_INTERFACE_MODE_PICMG:
+ result =
+ __cvmx_helper_sgmii_configure_loopback(ipd_port,
+ enable_internal,
+ enable_external);
+ break;
+ }
+ return result;
+}
diff --git a/arch/mips/cavium-octeon/executive/cvmx-interrupt-decodes.c b/arch/mips/cavium-octeon/executive/cvmx-interrupt-decodes.c
new file mode 100644
index 00000000000..e59d1b79f24
--- /dev/null
+++ b/arch/mips/cavium-octeon/executive/cvmx-interrupt-decodes.c
@@ -0,0 +1,371 @@
+/***********************license start***************
+ * Author: Cavium Networks
+ *
+ * Contact: support@caviumnetworks.com
+ * This file is part of the OCTEON SDK
+ *
+ * Copyright (c) 2003-2009 Cavium Networks
+ *
+ * This file is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, Version 2, as
+ * published by the Free Software Foundation.
+ *
+ * This file is distributed in the hope that it will be useful, but
+ * AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or
+ * NONINFRINGEMENT. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this file; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ * or visit http://www.gnu.org/licenses/.
+ *
+ * This file may also be available under a different license from Cavium.
+ * Contact Cavium Networks for more information
+ ***********************license end**************************************/
+
+/*
+ *
+ * Automatically generated functions useful for enabling
+ * and decoding RSL_INT_BLOCKS interrupts.
+ *
+ */
+
+#include <asm/octeon/octeon.h>
+
+#include <asm/octeon/cvmx-gmxx-defs.h>
+#include <asm/octeon/cvmx-pcsx-defs.h>
+#include <asm/octeon/cvmx-pcsxx-defs.h>
+#include <asm/octeon/cvmx-spxx-defs.h>
+#include <asm/octeon/cvmx-stxx-defs.h>
+
+#ifndef PRINT_ERROR
+#define PRINT_ERROR(format, ...)
+#endif
+
+
+/**
+ * __cvmx_interrupt_gmxx_rxx_int_en_enable enables all interrupt bits in cvmx_gmxx_rxx_int_en_t
+ */
+void __cvmx_interrupt_gmxx_rxx_int_en_enable(int index, int block)
+{
+ union cvmx_gmxx_rxx_int_en gmx_rx_int_en;
+ cvmx_write_csr(CVMX_GMXX_RXX_INT_REG(index, block),
+ cvmx_read_csr(CVMX_GMXX_RXX_INT_REG(index, block)));
+ gmx_rx_int_en.u64 = 0;
+ if (OCTEON_IS_MODEL(OCTEON_CN56XX)) {
+ /* Skipping gmx_rx_int_en.s.reserved_29_63 */
+ gmx_rx_int_en.s.hg2cc = 1;
+ gmx_rx_int_en.s.hg2fld = 1;
+ gmx_rx_int_en.s.undat = 1;
+ gmx_rx_int_en.s.uneop = 1;
+ gmx_rx_int_en.s.unsop = 1;
+ gmx_rx_int_en.s.bad_term = 1;
+ gmx_rx_int_en.s.bad_seq = 1;
+ gmx_rx_int_en.s.rem_fault = 1;
+ gmx_rx_int_en.s.loc_fault = 1;
+ gmx_rx_int_en.s.pause_drp = 1;
+ /* Skipping gmx_rx_int_en.s.reserved_16_18 */
+ /*gmx_rx_int_en.s.ifgerr = 1; */
+ /*gmx_rx_int_en.s.coldet = 1; // Collsion detect */
+ /*gmx_rx_int_en.s.falerr = 1; // False carrier error or extend error after slottime */
+ /*gmx_rx_int_en.s.rsverr = 1; // RGMII reserved opcodes */
+ /*gmx_rx_int_en.s.pcterr = 1; // Bad Preamble / Protocol */
+ gmx_rx_int_en.s.ovrerr = 1;
+ /* Skipping gmx_rx_int_en.s.reserved_9_9 */
+ gmx_rx_int_en.s.skperr = 1;
+ gmx_rx_int_en.s.rcverr = 1;
+ /* Skipping gmx_rx_int_en.s.reserved_5_6 */
+ /*gmx_rx_int_en.s.fcserr = 1; // FCS errors are handled when we get work */
+ gmx_rx_int_en.s.jabber = 1;
+ /* Skipping gmx_rx_int_en.s.reserved_2_2 */
+ gmx_rx_int_en.s.carext = 1;
+ /* Skipping gmx_rx_int_en.s.reserved_0_0 */
+ }
+ if (OCTEON_IS_MODEL(OCTEON_CN30XX)) {
+ /* Skipping gmx_rx_int_en.s.reserved_19_63 */
+ /*gmx_rx_int_en.s.phy_dupx = 1; */
+ /*gmx_rx_int_en.s.phy_spd = 1; */
+ /*gmx_rx_int_en.s.phy_link = 1; */
+ /*gmx_rx_int_en.s.ifgerr = 1; */
+ /*gmx_rx_int_en.s.coldet = 1; // Collsion detect */
+ /*gmx_rx_int_en.s.falerr = 1; // False carrier error or extend error after slottime */
+ /*gmx_rx_int_en.s.rsverr = 1; // RGMII reserved opcodes */
+ /*gmx_rx_int_en.s.pcterr = 1; // Bad Preamble / Protocol */
+ gmx_rx_int_en.s.ovrerr = 1;
+ gmx_rx_int_en.s.niberr = 1;
+ gmx_rx_int_en.s.skperr = 1;
+ gmx_rx_int_en.s.rcverr = 1;
+ /*gmx_rx_int_en.s.lenerr = 1; // Length errors are handled when we get work */
+ gmx_rx_int_en.s.alnerr = 1;
+ /*gmx_rx_int_en.s.fcserr = 1; // FCS errors are handled when we get work */
+ gmx_rx_int_en.s.jabber = 1;
+ gmx_rx_int_en.s.maxerr = 1;
+ gmx_rx_int_en.s.carext = 1;
+ gmx_rx_int_en.s.minerr = 1;
+ }
+ if (OCTEON_IS_MODEL(OCTEON_CN50XX)) {
+ /* Skipping gmx_rx_int_en.s.reserved_20_63 */
+ gmx_rx_int_en.s.pause_drp = 1;
+ /*gmx_rx_int_en.s.phy_dupx = 1; */
+ /*gmx_rx_int_en.s.phy_spd = 1; */
+ /*gmx_rx_int_en.s.phy_link = 1; */
+ /*gmx_rx_int_en.s.ifgerr = 1; */
+ /*gmx_rx_int_en.s.coldet = 1; // Collsion detect */
+ /*gmx_rx_int_en.s.falerr = 1; // False carrier error or extend error after slottime */
+ /*gmx_rx_int_en.s.rsverr = 1; // RGMII reserved opcodes */
+ /*gmx_rx_int_en.s.pcterr = 1; // Bad Preamble / Protocol */
+ gmx_rx_int_en.s.ovrerr = 1;
+ gmx_rx_int_en.s.niberr = 1;
+ gmx_rx_int_en.s.skperr = 1;
+ gmx_rx_int_en.s.rcverr = 1;
+ /* Skipping gmx_rx_int_en.s.reserved_6_6 */
+ gmx_rx_int_en.s.alnerr = 1;
+ /*gmx_rx_int_en.s.fcserr = 1; // FCS errors are handled when we get work */
+ gmx_rx_int_en.s.jabber = 1;
+ /* Skipping gmx_rx_int_en.s.reserved_2_2 */
+ gmx_rx_int_en.s.carext = 1;
+ /* Skipping gmx_rx_int_en.s.reserved_0_0 */
+ }
+ if (OCTEON_IS_MODEL(OCTEON_CN38XX)) {
+ /* Skipping gmx_rx_int_en.s.reserved_19_63 */
+ /*gmx_rx_int_en.s.phy_dupx = 1; */
+ /*gmx_rx_int_en.s.phy_spd = 1; */
+ /*gmx_rx_int_en.s.phy_link = 1; */
+ /*gmx_rx_int_en.s.ifgerr = 1; */
+ /*gmx_rx_int_en.s.coldet = 1; // Collsion detect */
+ /*gmx_rx_int_en.s.falerr = 1; // False carrier error or extend error after slottime */
+ /*gmx_rx_int_en.s.rsverr = 1; // RGMII reserved opcodes */
+ /*gmx_rx_int_en.s.pcterr = 1; // Bad Preamble / Protocol */
+ gmx_rx_int_en.s.ovrerr = 1;
+ gmx_rx_int_en.s.niberr = 1;
+ gmx_rx_int_en.s.skperr = 1;
+ gmx_rx_int_en.s.rcverr = 1;
+ /*gmx_rx_int_en.s.lenerr = 1; // Length errors are handled when we get work */
+ gmx_rx_int_en.s.alnerr = 1;
+ /*gmx_rx_int_en.s.fcserr = 1; // FCS errors are handled when we get work */
+ gmx_rx_int_en.s.jabber = 1;
+ gmx_rx_int_en.s.maxerr = 1;
+ gmx_rx_int_en.s.carext = 1;
+ gmx_rx_int_en.s.minerr = 1;
+ }
+ if (OCTEON_IS_MODEL(OCTEON_CN31XX)) {
+ /* Skipping gmx_rx_int_en.s.reserved_19_63 */
+ /*gmx_rx_int_en.s.phy_dupx = 1; */
+ /*gmx_rx_int_en.s.phy_spd = 1; */
+ /*gmx_rx_int_en.s.phy_link = 1; */
+ /*gmx_rx_int_en.s.ifgerr = 1; */
+ /*gmx_rx_int_en.s.coldet = 1; // Collsion detect */
+ /*gmx_rx_int_en.s.falerr = 1; // False carrier error or extend error after slottime */
+ /*gmx_rx_int_en.s.rsverr = 1; // RGMII reserved opcodes */
+ /*gmx_rx_int_en.s.pcterr = 1; // Bad Preamble / Protocol */
+ gmx_rx_int_en.s.ovrerr = 1;
+ gmx_rx_int_en.s.niberr = 1;
+ gmx_rx_int_en.s.skperr = 1;
+ gmx_rx_int_en.s.rcverr = 1;
+ /*gmx_rx_int_en.s.lenerr = 1; // Length errors are handled when we get work */
+ gmx_rx_int_en.s.alnerr = 1;
+ /*gmx_rx_int_en.s.fcserr = 1; // FCS errors are handled when we get work */
+ gmx_rx_int_en.s.jabber = 1;
+ gmx_rx_int_en.s.maxerr = 1;
+ gmx_rx_int_en.s.carext = 1;
+ gmx_rx_int_en.s.minerr = 1;
+ }
+ if (OCTEON_IS_MODEL(OCTEON_CN58XX)) {
+ /* Skipping gmx_rx_int_en.s.reserved_20_63 */
+ gmx_rx_int_en.s.pause_drp = 1;
+ /*gmx_rx_int_en.s.phy_dupx = 1; */
+ /*gmx_rx_int_en.s.phy_spd = 1; */
+ /*gmx_rx_int_en.s.phy_link = 1; */
+ /*gmx_rx_int_en.s.ifgerr = 1; */
+ /*gmx_rx_int_en.s.coldet = 1; // Collsion detect */
+ /*gmx_rx_int_en.s.falerr = 1; // False carrier error or extend error after slottime */
+ /*gmx_rx_int_en.s.rsverr = 1; // RGMII reserved opcodes */
+ /*gmx_rx_int_en.s.pcterr = 1; // Bad Preamble / Protocol */
+ gmx_rx_int_en.s.ovrerr = 1;
+ gmx_rx_int_en.s.niberr = 1;
+ gmx_rx_int_en.s.skperr = 1;
+ gmx_rx_int_en.s.rcverr = 1;
+ /*gmx_rx_int_en.s.lenerr = 1; // Length errors are handled when we get work */
+ gmx_rx_int_en.s.alnerr = 1;
+ /*gmx_rx_int_en.s.fcserr = 1; // FCS errors are handled when we get work */
+ gmx_rx_int_en.s.jabber = 1;
+ gmx_rx_int_en.s.maxerr = 1;
+ gmx_rx_int_en.s.carext = 1;
+ gmx_rx_int_en.s.minerr = 1;
+ }
+ if (OCTEON_IS_MODEL(OCTEON_CN52XX)) {
+ /* Skipping gmx_rx_int_en.s.reserved_29_63 */
+ gmx_rx_int_en.s.hg2cc = 1;
+ gmx_rx_int_en.s.hg2fld = 1;
+ gmx_rx_int_en.s.undat = 1;
+ gmx_rx_int_en.s.uneop = 1;
+ gmx_rx_int_en.s.unsop = 1;
+ gmx_rx_int_en.s.bad_term = 1;
+ gmx_rx_int_en.s.bad_seq = 0;
+ gmx_rx_int_en.s.rem_fault = 1;
+ gmx_rx_int_en.s.loc_fault = 0;
+ gmx_rx_int_en.s.pause_drp = 1;
+ /* Skipping gmx_rx_int_en.s.reserved_16_18 */
+ /*gmx_rx_int_en.s.ifgerr = 1; */
+ /*gmx_rx_int_en.s.coldet = 1; // Collsion detect */
+ /*gmx_rx_int_en.s.falerr = 1; // False carrier error or extend error after slottime */
+ /*gmx_rx_int_en.s.rsverr = 1; // RGMII reserved opcodes */
+ /*gmx_rx_int_en.s.pcterr = 1; // Bad Preamble / Protocol */
+ gmx_rx_int_en.s.ovrerr = 1;
+ /* Skipping gmx_rx_int_en.s.reserved_9_9 */
+ gmx_rx_int_en.s.skperr = 1;
+ gmx_rx_int_en.s.rcverr = 1;
+ /* Skipping gmx_rx_int_en.s.reserved_5_6 */
+ /*gmx_rx_int_en.s.fcserr = 1; // FCS errors are handled when we get work */
+ gmx_rx_int_en.s.jabber = 1;
+ /* Skipping gmx_rx_int_en.s.reserved_2_2 */
+ gmx_rx_int_en.s.carext = 1;
+ /* Skipping gmx_rx_int_en.s.reserved_0_0 */
+ }
+ cvmx_write_csr(CVMX_GMXX_RXX_INT_EN(index, block), gmx_rx_int_en.u64);
+}
+/**
+ * __cvmx_interrupt_pcsx_intx_en_reg_enable enables all interrupt bits in cvmx_pcsx_intx_en_reg_t
+ */
+void __cvmx_interrupt_pcsx_intx_en_reg_enable(int index, int block)
+{
+ union cvmx_pcsx_intx_en_reg pcs_int_en_reg;
+ cvmx_write_csr(CVMX_PCSX_INTX_REG(index, block),
+ cvmx_read_csr(CVMX_PCSX_INTX_REG(index, block)));
+ pcs_int_en_reg.u64 = 0;
+ if (OCTEON_IS_MODEL(OCTEON_CN56XX)) {
+ /* Skipping pcs_int_en_reg.s.reserved_12_63 */
+ /*pcs_int_en_reg.s.dup = 1; // This happens during normal operation */
+ pcs_int_en_reg.s.sync_bad_en = 1;
+ pcs_int_en_reg.s.an_bad_en = 1;
+ pcs_int_en_reg.s.rxlock_en = 1;
+ pcs_int_en_reg.s.rxbad_en = 1;
+ /*pcs_int_en_reg.s.rxerr_en = 1; // This happens during normal operation */
+ pcs_int_en_reg.s.txbad_en = 1;
+ pcs_int_en_reg.s.txfifo_en = 1;
+ pcs_int_en_reg.s.txfifu_en = 1;
+ pcs_int_en_reg.s.an_err_en = 1;
+ /*pcs_int_en_reg.s.xmit_en = 1; // This happens during normal operation */
+ /*pcs_int_en_reg.s.lnkspd_en = 1; // This happens during normal operation */
+ }
+ if (OCTEON_IS_MODEL(OCTEON_CN52XX)) {
+ /* Skipping pcs_int_en_reg.s.reserved_12_63 */
+ /*pcs_int_en_reg.s.dup = 1; // This happens during normal operation */
+ pcs_int_en_reg.s.sync_bad_en = 1;
+ pcs_int_en_reg.s.an_bad_en = 1;
+ pcs_int_en_reg.s.rxlock_en = 1;
+ pcs_int_en_reg.s.rxbad_en = 1;
+ /*pcs_int_en_reg.s.rxerr_en = 1; // This happens during normal operation */
+ pcs_int_en_reg.s.txbad_en = 1;
+ pcs_int_en_reg.s.txfifo_en = 1;
+ pcs_int_en_reg.s.txfifu_en = 1;
+ pcs_int_en_reg.s.an_err_en = 1;
+ /*pcs_int_en_reg.s.xmit_en = 1; // This happens during normal operation */
+ /*pcs_int_en_reg.s.lnkspd_en = 1; // This happens during normal operation */
+ }
+ cvmx_write_csr(CVMX_PCSX_INTX_EN_REG(index, block), pcs_int_en_reg.u64);
+}
+/**
+ * __cvmx_interrupt_pcsxx_int_en_reg_enable enables all interrupt bits in cvmx_pcsxx_int_en_reg_t
+ */
+void __cvmx_interrupt_pcsxx_int_en_reg_enable(int index)
+{
+ union cvmx_pcsxx_int_en_reg pcsx_int_en_reg;
+ cvmx_write_csr(CVMX_PCSXX_INT_REG(index),
+ cvmx_read_csr(CVMX_PCSXX_INT_REG(index)));
+ pcsx_int_en_reg.u64 = 0;
+ if (OCTEON_IS_MODEL(OCTEON_CN56XX)) {
+ /* Skipping pcsx_int_en_reg.s.reserved_6_63 */
+ pcsx_int_en_reg.s.algnlos_en = 1;
+ pcsx_int_en_reg.s.synlos_en = 1;
+ pcsx_int_en_reg.s.bitlckls_en = 1;
+ pcsx_int_en_reg.s.rxsynbad_en = 1;
+ pcsx_int_en_reg.s.rxbad_en = 1;
+ pcsx_int_en_reg.s.txflt_en = 1;
+ }
+ if (OCTEON_IS_MODEL(OCTEON_CN52XX)) {
+ /* Skipping pcsx_int_en_reg.s.reserved_6_63 */
+ pcsx_int_en_reg.s.algnlos_en = 1;
+ pcsx_int_en_reg.s.synlos_en = 1;
+ pcsx_int_en_reg.s.bitlckls_en = 0; /* Happens if XAUI module is not installed */
+ pcsx_int_en_reg.s.rxsynbad_en = 1;
+ pcsx_int_en_reg.s.rxbad_en = 1;
+ pcsx_int_en_reg.s.txflt_en = 1;
+ }
+ cvmx_write_csr(CVMX_PCSXX_INT_EN_REG(index), pcsx_int_en_reg.u64);
+}
+
+/**
+ * __cvmx_interrupt_spxx_int_msk_enable enables all interrupt bits in cvmx_spxx_int_msk_t
+ */
+void __cvmx_interrupt_spxx_int_msk_enable(int index)
+{
+ union cvmx_spxx_int_msk spx_int_msk;
+ cvmx_write_csr(CVMX_SPXX_INT_REG(index),
+ cvmx_read_csr(CVMX_SPXX_INT_REG(index)));
+ spx_int_msk.u64 = 0;
+ if (OCTEON_IS_MODEL(OCTEON_CN38XX)) {
+ /* Skipping spx_int_msk.s.reserved_12_63 */
+ spx_int_msk.s.calerr = 1;
+ spx_int_msk.s.syncerr = 1;
+ spx_int_msk.s.diperr = 1;
+ spx_int_msk.s.tpaovr = 1;
+ spx_int_msk.s.rsverr = 1;
+ spx_int_msk.s.drwnng = 1;
+ spx_int_msk.s.clserr = 1;
+ spx_int_msk.s.spiovr = 1;
+ /* Skipping spx_int_msk.s.reserved_2_3 */
+ spx_int_msk.s.abnorm = 1;
+ spx_int_msk.s.prtnxa = 1;
+ }
+ if (OCTEON_IS_MODEL(OCTEON_CN58XX)) {
+ /* Skipping spx_int_msk.s.reserved_12_63 */
+ spx_int_msk.s.calerr = 1;
+ spx_int_msk.s.syncerr = 1;
+ spx_int_msk.s.diperr = 1;
+ spx_int_msk.s.tpaovr = 1;
+ spx_int_msk.s.rsverr = 1;
+ spx_int_msk.s.drwnng = 1;
+ spx_int_msk.s.clserr = 1;
+ spx_int_msk.s.spiovr = 1;
+ /* Skipping spx_int_msk.s.reserved_2_3 */
+ spx_int_msk.s.abnorm = 1;
+ spx_int_msk.s.prtnxa = 1;
+ }
+ cvmx_write_csr(CVMX_SPXX_INT_MSK(index), spx_int_msk.u64);
+}
+/**
+ * __cvmx_interrupt_stxx_int_msk_enable enables all interrupt bits in cvmx_stxx_int_msk_t
+ */
+void __cvmx_interrupt_stxx_int_msk_enable(int index)
+{
+ union cvmx_stxx_int_msk stx_int_msk;
+ cvmx_write_csr(CVMX_STXX_INT_REG(index),
+ cvmx_read_csr(CVMX_STXX_INT_REG(index)));
+ stx_int_msk.u64 = 0;
+ if (OCTEON_IS_MODEL(OCTEON_CN38XX)) {
+ /* Skipping stx_int_msk.s.reserved_8_63 */
+ stx_int_msk.s.frmerr = 1;
+ stx_int_msk.s.unxfrm = 1;
+ stx_int_msk.s.nosync = 1;
+ stx_int_msk.s.diperr = 1;
+ stx_int_msk.s.datovr = 1;
+ stx_int_msk.s.ovrbst = 1;
+ stx_int_msk.s.calpar1 = 1;
+ stx_int_msk.s.calpar0 = 1;
+ }
+ if (OCTEON_IS_MODEL(OCTEON_CN58XX)) {
+ /* Skipping stx_int_msk.s.reserved_8_63 */
+ stx_int_msk.s.frmerr = 1;
+ stx_int_msk.s.unxfrm = 1;
+ stx_int_msk.s.nosync = 1;
+ stx_int_msk.s.diperr = 1;
+ stx_int_msk.s.datovr = 1;
+ stx_int_msk.s.ovrbst = 1;
+ stx_int_msk.s.calpar1 = 1;
+ stx_int_msk.s.calpar0 = 1;
+ }
+ cvmx_write_csr(CVMX_STXX_INT_MSK(index), stx_int_msk.u64);
+}
diff --git a/arch/mips/cavium-octeon/executive/cvmx-interrupt-rsl.c b/arch/mips/cavium-octeon/executive/cvmx-interrupt-rsl.c
new file mode 100644
index 00000000000..fa327ec891c
--- /dev/null
+++ b/arch/mips/cavium-octeon/executive/cvmx-interrupt-rsl.c
@@ -0,0 +1,140 @@
+/***********************license start***************
+ * Author: Cavium Networks
+ *
+ * Contact: support@caviumnetworks.com
+ * This file is part of the OCTEON SDK
+ *
+ * Copyright (c) 2003-2008 Cavium Networks
+ *
+ * This file is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, Version 2, as
+ * published by the Free Software Foundation.
+ *
+ * This file is distributed in the hope that it will be useful, but
+ * AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or
+ * NONINFRINGEMENT. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this file; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ * or visit http://www.gnu.org/licenses/.
+ *
+ * This file may also be available under a different license from Cavium.
+ * Contact Cavium Networks for more information
+ ***********************license end**************************************/
+
+/*
+ * Utility functions to decode Octeon's RSL_INT_BLOCKS
+ * interrupts into error messages.
+ */
+
+#include <asm/octeon/octeon.h>
+
+#include <asm/octeon/cvmx-asxx-defs.h>
+#include <asm/octeon/cvmx-gmxx-defs.h>
+
+#ifndef PRINT_ERROR
+#define PRINT_ERROR(format, ...)
+#endif
+
+void __cvmx_interrupt_gmxx_rxx_int_en_enable(int index, int block);
+
+/**
+ * Enable ASX error interrupts that exist on CN3XXX, CN50XX, and
+ * CN58XX.
+ *
+ * @block: Interface to enable 0-1
+ */
+void __cvmx_interrupt_asxx_enable(int block)
+{
+ int mask;
+ union cvmx_asxx_int_en csr;
+ /*
+ * CN38XX and CN58XX have two interfaces with 4 ports per
+ * interface. All other chips have a max of 3 ports on
+ * interface 0
+ */
+ if (OCTEON_IS_MODEL(OCTEON_CN38XX) || OCTEON_IS_MODEL(OCTEON_CN58XX))
+ mask = 0xf; /* Set enables for 4 ports */
+ else
+ mask = 0x7; /* Set enables for 3 ports */
+
+ /* Enable interface interrupts */
+ csr.u64 = cvmx_read_csr(CVMX_ASXX_INT_EN(block));
+ csr.s.txpsh = mask;
+ csr.s.txpop = mask;
+ csr.s.ovrflw = mask;
+ cvmx_write_csr(CVMX_ASXX_INT_EN(block), csr.u64);
+}
+/**
+ * Enable GMX error reporting for the supplied interface
+ *
+ * @interface: Interface to enable
+ */
+void __cvmx_interrupt_gmxx_enable(int interface)
+{
+ union cvmx_gmxx_inf_mode mode;
+ union cvmx_gmxx_tx_int_en gmx_tx_int_en;
+ int num_ports;
+ int index;
+
+ mode.u64 = cvmx_read_csr(CVMX_GMXX_INF_MODE(interface));
+
+ if (OCTEON_IS_MODEL(OCTEON_CN56XX) || OCTEON_IS_MODEL(OCTEON_CN52XX)) {
+ if (mode.s.en) {
+ switch (mode.cn56xx.mode) {
+ case 1: /* XAUI */
+ num_ports = 1;
+ break;
+ case 2: /* SGMII */
+ case 3: /* PICMG */
+ num_ports = 4;
+ break;
+ default: /* Disabled */
+ num_ports = 0;
+ break;
+ }
+ } else
+ num_ports = 0;
+ } else {
+ if (mode.s.en) {
+ if (OCTEON_IS_MODEL(OCTEON_CN38XX)
+ || OCTEON_IS_MODEL(OCTEON_CN58XX)) {
+ /*
+ * SPI on CN38XX and CN58XX report all
+ * errors through port 0. RGMII needs
+ * to check all 4 ports
+ */
+ if (mode.s.type)
+ num_ports = 1;
+ else
+ num_ports = 4;
+ } else {
+ /*
+ * CN30XX, CN31XX, and CN50XX have two
+ * or three ports. GMII and MII has 2,
+ * RGMII has three
+ */
+ if (mode.s.type)
+ num_ports = 2;
+ else
+ num_ports = 3;
+ }
+ } else
+ num_ports = 0;
+ }
+
+ gmx_tx_int_en.u64 = 0;
+ if (num_ports) {
+ if (OCTEON_IS_MODEL(OCTEON_CN38XX)
+ || OCTEON_IS_MODEL(OCTEON_CN58XX))
+ gmx_tx_int_en.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);
+}
diff --git a/arch/mips/cavium-octeon/executive/cvmx-l2c.c b/arch/mips/cavium-octeon/executive/cvmx-l2c.c
index 6abe56f1e09..42e38c30b54 100644
--- a/arch/mips/cavium-octeon/executive/cvmx-l2c.c
+++ b/arch/mips/cavium-octeon/executive/cvmx-l2c.c
@@ -4,7 +4,7 @@
* Contact: support@caviumnetworks.com
* This file is part of the OCTEON SDK
*
- * Copyright (c) 2003-2008 Cavium Networks
+ * 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
@@ -26,10 +26,12 @@
***********************license end**************************************/
/*
- * Implementation of the Level 2 Cache (L2C) control, measurement, and
- * debugging facilities.
+ * 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>
@@ -42,13 +44,7 @@
* if multiple applications or operating systems are running, then it
* is up to the user program to coordinate between them.
*/
-static cvmx_spinlock_t cvmx_l2c_spinlock;
-
-static inline int l2_size_half(void)
-{
- uint64_t val = cvmx_read_csr(CVMX_L2D_FUS3);
- return !!(val & (1ull << 34));
-}
+cvmx_spinlock_t cvmx_l2c_spinlock;
int cvmx_l2c_get_core_way_partition(uint32_t core)
{
@@ -58,6 +54,9 @@ int cvmx_l2c_get_core_way_partition(uint32_t core)
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.
@@ -71,17 +70,13 @@ int cvmx_l2c_get_core_way_partition(uint32_t core)
switch (core & 0xC) {
case 0x0:
- return (cvmx_read_csr(CVMX_L2C_SPAR0) & (0xFF << field)) >>
- field;
+ return (cvmx_read_csr(CVMX_L2C_SPAR0) & (0xFF << field)) >> field;
case 0x4:
- return (cvmx_read_csr(CVMX_L2C_SPAR1) & (0xFF << field)) >>
- field;
+ return (cvmx_read_csr(CVMX_L2C_SPAR1) & (0xFF << field)) >> field;
case 0x8:
- return (cvmx_read_csr(CVMX_L2C_SPAR2) & (0xFF << field)) >>
- field;
+ return (cvmx_read_csr(CVMX_L2C_SPAR2) & (0xFF << field)) >> field;
case 0xC:
- return (cvmx_read_csr(CVMX_L2C_SPAR3) & (0xFF << field)) >>
- field;
+ return (cvmx_read_csr(CVMX_L2C_SPAR3) & (0xFF << field)) >> field;
}
return 0;
}
@@ -95,48 +90,50 @@ int cvmx_l2c_set_core_way_partition(uint32_t core, uint32_t mask)
mask &= valid_mask;
- /* A UMSK setting which blocks all L2C Ways is an error. */
- if (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;
- /* Check to make sure current mask & new mask don't block all ways */
- if (((mask | cvmx_l2c_get_core_way_partition(core)) & valid_mask) ==
- valid_mask)
- 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
+ /*
+ * 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
+ /*
+ * 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);
+ (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);
+ (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);
+ (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);
+ (cvmx_read_csr(CVMX_L2C_SPAR3) & ~(0xFF << field)) |
+ mask << field);
break;
}
return 0;
@@ -146,84 +143,137 @@ int cvmx_l2c_set_hw_way_partition(uint32_t mask)
{
uint32_t valid_mask;
- valid_mask = 0xff;
-
- if (OCTEON_IS_MODEL(OCTEON_CN58XX) || OCTEON_IS_MODEL(OCTEON_CN38XX)) {
- if (l2_size_half())
- valid_mask = 0xf;
- } else if (l2_size_half())
- valid_mask = 0x3;
-
+ valid_mask = (0x1 << cvmx_l2c_get_num_assoc()) - 1;
mask &= valid_mask;
- /* A UMSK setting which blocks all L2C Ways is an error. */
- if (mask == valid_mask)
- return -1;
- /* Check to make sure current mask & new mask don't block all ways */
- if (((mask | cvmx_l2c_get_hw_way_partition()) & valid_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;
- cvmx_write_csr(CVMX_L2C_SPAR4,
- (cvmx_read_csr(CVMX_L2C_SPAR4) & ~0xFF) | mask);
+ 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)
{
- return cvmx_read_csr(CVMX_L2C_SPAR4) & (0xFF);
+ 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)
{
- union cvmx_l2c_pfctl pfctl;
+ if (OCTEON_IS_MODEL(OCTEON_CN5XXX) || OCTEON_IS_MODEL(OCTEON_CN3XXX)) {
+ union cvmx_l2c_pfctl pfctl;
- pfctl.u64 = cvmx_read_csr(CVMX_L2C_PFCTL);
+ pfctl.u64 = cvmx_read_csr(CVMX_L2C_PFCTL);
- switch (counter) {
- case 0:
- pfctl.s.cnt0sel = event;
- pfctl.s.cnt0ena = 1;
- if (!cvmx_octeon_is_pass1())
+ 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;
- if (!cvmx_octeon_is_pass1())
+ 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;
- if (!cvmx_octeon_is_pass1())
+ 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;
- if (!cvmx_octeon_is_pass1())
+ break;
+ case 3:
+ default:
+ pfctl.s.cnt3sel = event;
+ pfctl.s.cnt3ena = 1;
pfctl.s.cnt3rdclr = clear_on_read;
- break;
- }
+ break;
+ }
- cvmx_write_csr(CVMX_L2C_PFCTL, pfctl.u64);
+ 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:
- return cvmx_read_csr(CVMX_L2C_PFC0);
+ 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:
- return cvmx_read_csr(CVMX_L2C_PFC1);
+ 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:
- return cvmx_read_csr(CVMX_L2C_PFC2);
+ 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:
- return cvmx_read_csr(CVMX_L2C_PFC3);
+ 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;
+ }
}
}
@@ -236,22 +286,22 @@ uint64_t cvmx_l2c_read_perf(uint32_t counter)
*/
static void fault_in(uint64_t addr, int len)
{
- volatile char *ptr;
- volatile char dummy;
+ char *ptr;
+
/*
* Adjust addr and length so we get all cache lines even for
- * small ranges spanning two cache lines
+ * small ranges spanning two cache lines.
*/
len += addr & CVMX_CACHE_LINE_MASK;
addr &= ~CVMX_CACHE_LINE_MASK;
- ptr = (volatile char *)cvmx_phys_to_ptr(addr);
+ 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) {
- dummy += *ptr;
+ ACCESS_ONCE(*ptr);
len -= CVMX_CACHE_LINE_SIZE;
ptr += CVMX_CACHE_LINE_SIZE;
}
@@ -259,67 +309,100 @@ static void fault_in(uint64_t addr, int len)
int cvmx_l2c_lock_line(uint64_t addr)
{
- int retval = 0;
- union cvmx_l2c_dbg l2cdbg;
- union cvmx_l2c_lckbase lckbase;
- union cvmx_l2c_lckoff lckoff;
- union cvmx_l2t_err l2t_err;
- l2cdbg.u64 = 0;
- lckbase.u64 = 0;
- lckoff.u64 = 0;
-
- cvmx_spinlock_lock(&cvmx_l2c_spinlock);
-
- /* 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);
+ 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;
+ }
- addr &= ~CVMX_CACHE_LINE_MASK;
+ /* 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;
+ }
- /* 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;
+ /* 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 {
- lckbase.s.lck_base = addr >> 7;
- }
+ int retval = 0;
+ union cvmx_l2c_dbg l2cdbg;
+ union cvmx_l2c_lckbase lckbase;
+ union cvmx_l2c_lckoff lckoff;
+ union cvmx_l2t_err l2t_err;
- lckbase.s.lck_ena = 1;
- cvmx_write_csr(CVMX_L2C_LCKBASE, lckbase.u64);
- cvmx_read_csr(CVMX_L2C_LCKBASE); /* Make sure it gets there */
+ cvmx_spinlock_lock(&cvmx_l2c_spinlock);
- fault_in(addr, CVMX_CACHE_LINE_SIZE);
+ l2cdbg.u64 = 0;
+ lckbase.u64 = 0;
+ lckoff.u64 = 0;
- lckbase.s.lck_ena = 0;
- cvmx_write_csr(CVMX_L2C_LCKBASE, lckbase.u64);
- cvmx_read_csr(CVMX_L2C_LCKBASE); /* Make sure it gets there */
+ /* 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);
- /* Stop being debug core */
- cvmx_write_csr(CVMX_L2C_DBG, 0);
- cvmx_read_csr(CVMX_L2C_DBG);
+ addr &= ~CVMX_CACHE_LINE_MASK;
- 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 */
+ /* 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;
+ }
- cvmx_spinlock_unlock(&cvmx_l2c_spinlock);
+ lckbase.s.lck_ena = 1;
+ cvmx_write_csr(CVMX_L2C_LCKBASE, lckbase.u64);
+ /* Make sure it gets there */
+ cvmx_read_csr(CVMX_L2C_LCKBASE);
- return retval;
+ 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)
@@ -336,7 +419,6 @@ int cvmx_l2c_lock_mem_region(uint64_t start, uint64_t len)
start += CVMX_CACHE_LINE_SIZE;
len -= CVMX_CACHE_LINE_SIZE;
}
-
return retval;
}
@@ -344,80 +426,73 @@ void cvmx_l2c_flush(void)
{
uint64_t assoc, set;
uint64_t n_assoc, n_set;
- union cvmx_l2c_dbg l2cdbg;
- cvmx_spinlock_lock(&cvmx_l2c_spinlock);
-
- l2cdbg.u64 = 0;
- if (!OCTEON_IS_MODEL(OCTEON_CN30XX))
- l2cdbg.s.ppnum = cvmx_get_core_num();
- l2cdbg.s.finv = 1;
- n_set = CVMX_L2_SETS;
- n_assoc = l2_size_half() ? (CVMX_L2_ASSOC / 2) : CVMX_L2_ASSOC;
- for (set = 0; set < n_set; set++) {
- for (assoc = 0; assoc < n_assoc; assoc++) {
- l2cdbg.s.set = assoc;
- /* Enter debug mode, and make sure all other
- ** writes complete before we enter debug
- ** mode */
- CVMX_SYNCW;
- 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,
- set * CVMX_CACHE_LINE_SIZE), 0);
- CVMX_SYNCW; /* Push STF out to L2 */
- /* Exit debug mode */
- CVMX_SYNC;
- cvmx_write_csr(CVMX_L2C_DBG, 0);
- cvmx_read_csr(CVMX_L2C_DBG);
+ 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);
}
-
- cvmx_spinlock_unlock(&cvmx_l2c_spinlock);
}
+
int cvmx_l2c_unlock_line(uint64_t address)
{
- int assoc;
- union cvmx_l2c_tag tag;
- union cvmx_l2c_dbg l2cdbg;
- uint32_t tag_addr;
-
- uint32_t index = cvmx_l2c_address_to_index(address);
- cvmx_spinlock_lock(&cvmx_l2c_spinlock);
- /* 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_get_l2c_tag(assoc, index);
+ 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;
- if (tag.s.V && (tag.s.addr == tag_addr)) {
- l2cdbg.u64 = 0;
- l2cdbg.s.ppnum = cvmx_get_core_num();
- l2cdbg.s.set = assoc;
- l2cdbg.s.finv = 1;
+ uint32_t index = cvmx_l2c_address_to_index(address);
- CVMX_SYNC;
- /* Enter debug mode */
- cvmx_write_csr(CVMX_L2C_DBG, l2cdbg.u64);
- cvmx_read_csr(CVMX_L2C_DBG);
+ /* 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);
- CVMX_PREPARE_FOR_STORE(CVMX_ADD_SEG
- (CVMX_MIPS_SPACE_XKPHYS,
- address), 0);
- CVMX_SYNC;
- /* Exit debug mode */
- cvmx_write_csr(CVMX_L2C_DBG, 0);
- cvmx_read_csr(CVMX_L2C_DBG);
- cvmx_spinlock_unlock(&cvmx_l2c_spinlock);
- return tag.s.L;
+ if (tag.s.V && (tag.s.addr == tag_addr)) {
+ cvmx_l2c_flush_line(assoc, index);
+ return tag.s.L;
+ }
}
}
- cvmx_spinlock_unlock(&cvmx_l2c_spinlock);
return 0;
}
@@ -445,48 +520,49 @@ 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 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 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 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 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 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
@@ -497,13 +573,13 @@ union __cvmx_l2c_tag {
* @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.
+ * 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 = (((1ULL << 63) | (index << 7)) + 96);
+ 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;
@@ -512,12 +588,15 @@ static union __cvmx_l2c_tag __read_l2_tag(uint64_t assoc, uint64_t index)
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.
+ * 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.
@@ -526,112 +605,139 @@ static union __cvmx_l2c_tag __read_l2_tag(uint64_t assoc, uint64_t index)
/* Flush L1 to make sure debug load misses L1 */
CVMX_DCACHE_INVALIDATE;
- local_irq_save(flags);
-
/*
* 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
+ * 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"
- " .set mips64 \n"
- " .set noreorder \n"
- /* Enter debug mode, wait for store */
- " sd %[dbg_val], 0(%[dbg_addr]) \n"
- " ld $0, 0(%[dbg_addr]) \n"
- /* Read L2C tag data */
- " ld %[tag_val], 0(%[tag_addr]) \n"
- /* Exit debug mode, wait for store */
- " sd $0, 0(%[dbg_addr]) \n"
- " ld $0, 0(%[dbg_addr]) \n"
- /* Invalidate dcache to discard debug data */
- " cache 9, 0($0) \n"
- " .set pop" :
- [tag_val] "=r"(tag_val.u64) : [dbg_addr] "r"(dbg_addr),
- [dbg_val] "r"(debug_val.u64),
- [tag_addr] "r"(debug_tag_addr) : "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;
+ return tag_val;
}
+
union cvmx_l2c_tag cvmx_l2c_get_tag(uint32_t association, uint32_t index)
{
- union __cvmx_l2c_tag tmp_tag;
union cvmx_l2c_tag tag;
tag.u64 = 0;
if ((int)association >= cvmx_l2c_get_num_assoc()) {
- cvmx_dprintf
- ("ERROR: cvmx_get_l2c_tag association out of range\n");
+ 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_get_l2c_tag "
- "index out of range (arg: %d, max: %d\n",
- 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;
}
- /* __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;
+ 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 {
- cvmx_dprintf("Unsupported OCTEON Model in %s\n", __func__);
+ 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;
- union cvmx_l2c_cfg l2c_cfg;
- l2c_cfg.u64 = cvmx_read_csr(CVMX_L2C_CFG);
+ 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 (l2c_cfg.s.idxalias) {
- idx ^=
- ((addr & CVMX_L2C_ALIAS_MASK) >>
- CVMX_L2C_TAG_ADDR_ALIAS_SHIFT);
+ 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;
@@ -652,10 +758,9 @@ 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))
+ 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))
+ 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 */
@@ -666,7 +771,6 @@ int cvmx_l2c_get_set_bits(void)
l2_set_bits = 11; /* 2048 sets */
}
return l2_set_bits;
-
}
/* Return the number of sets in the L2 Cache */
@@ -682,8 +786,11 @@ int cvmx_l2c_get_num_assoc(void)
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))
+ 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;
@@ -693,11 +800,42 @@ int cvmx_l2c_get_num_assoc(void)
}
/* Check to see if part of the cache is disabled */
- if (cvmx_fuse_read(265))
- l2_assoc = l2_assoc >> 2;
- else if (cvmx_fuse_read(264))
- l2_assoc = l2_assoc >> 1;
-
+ 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;
}
@@ -711,24 +849,54 @@ int cvmx_l2c_get_num_assoc(void)
*/
void cvmx_l2c_flush_line(uint32_t assoc, uint32_t index)
{
- union cvmx_l2c_dbg l2cdbg;
+ /* 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;
+ }
- l2cdbg.u64 = 0;
- l2cdbg.s.ppnum = cvmx_get_core_num();
- l2cdbg.s.finv = 1;
+ /* 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;
+ }
- l2cdbg.s.set = assoc;
- /*
- * Enter debug mode, and make sure all other writes complete
- * before we enter debug mode.
- */
- asm volatile ("sync" : : : "memory");
- cvmx_write_csr(CVMX_L2C_DBG, l2cdbg.u64);
- cvmx_read_csr(CVMX_L2C_DBG);
-
- CVMX_PREPARE_FOR_STORE(((1ULL << 63) + (index) * 128), 0);
- /* Exit debug mode */
- asm volatile ("sync" : : : "memory");
- cvmx_write_csr(CVMX_L2C_DBG, 0);
- cvmx_read_csr(CVMX_L2C_DBG);
+ 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);
+ }
}
diff --git a/arch/mips/cavium-octeon/executive/cvmx-pko.c b/arch/mips/cavium-octeon/executive/cvmx-pko.c
new file mode 100644
index 00000000000..008b881cdf6
--- /dev/null
+++ b/arch/mips/cavium-octeon/executive/cvmx-pko.c
@@ -0,0 +1,507 @@
+/***********************license start***************
+ * Author: Cavium Networks
+ *
+ * Contact: support@caviumnetworks.com
+ * This file is part of the OCTEON SDK
+ *
+ * Copyright (c) 2003-2008 Cavium Networks
+ *
+ * This file is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, Version 2, as
+ * published by the Free Software Foundation.
+ *
+ * This file is distributed in the hope that it will be useful, but
+ * AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or
+ * NONINFRINGEMENT. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this file; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ * or visit http://www.gnu.org/licenses/.
+ *
+ * This file may also be available under a different license from Cavium.
+ * Contact Cavium Networks for more information
+ ***********************license end**************************************/
+
+/*
+ * Support library for the hardware Packet Output unit.
+ */
+
+#include <asm/octeon/octeon.h>
+
+#include <asm/octeon/cvmx-config.h>
+#include <asm/octeon/cvmx-pko.h>
+#include <asm/octeon/cvmx-helper.h>
+
+/**
+ * Internal state of packet output
+ */
+
+/**
+ * Call before any other calls to initialize the packet
+ * output system. This does chip global config, and should only be
+ * done by one core.
+ */
+
+void cvmx_pko_initialize_global(void)
+{
+ int i;
+ uint64_t priority = 8;
+ union cvmx_pko_reg_cmd_buf config;
+
+ /*
+ * Set the size of the PKO command buffers to an odd number of
+ * 64bit words. This allows the normal two word send to stay
+ * aligned and never span a comamnd word buffer.
+ */
+ config.u64 = 0;
+ config.s.pool = CVMX_FPA_OUTPUT_BUFFER_POOL;
+ config.s.size = CVMX_FPA_OUTPUT_BUFFER_POOL_SIZE / 8 - 1;
+
+ cvmx_write_csr(CVMX_PKO_REG_CMD_BUF, config.u64);
+
+ for (i = 0; i < CVMX_PKO_MAX_OUTPUT_QUEUES; i++)
+ cvmx_pko_config_port(CVMX_PKO_MEM_QUEUE_PTRS_ILLEGAL_PID, i, 1,
+ &priority);
+
+ /*
+ * If we aren't using all of the queues optimize PKO's
+ * internal memory.
+ */
+ if (OCTEON_IS_MODEL(OCTEON_CN38XX) || OCTEON_IS_MODEL(OCTEON_CN58XX)
+ || OCTEON_IS_MODEL(OCTEON_CN56XX)
+ || OCTEON_IS_MODEL(OCTEON_CN52XX)) {
+ int num_interfaces = cvmx_helper_get_number_of_interfaces();
+ int last_port =
+ cvmx_helper_get_last_ipd_port(num_interfaces - 1);
+ int max_queues =
+ cvmx_pko_get_base_queue(last_port) +
+ cvmx_pko_get_num_queues(last_port);
+ if (OCTEON_IS_MODEL(OCTEON_CN38XX)) {
+ if (max_queues <= 32)
+ cvmx_write_csr(CVMX_PKO_REG_QUEUE_MODE, 2);
+ else if (max_queues <= 64)
+ cvmx_write_csr(CVMX_PKO_REG_QUEUE_MODE, 1);
+ } else {
+ if (max_queues <= 64)
+ cvmx_write_csr(CVMX_PKO_REG_QUEUE_MODE, 2);
+ else if (max_queues <= 128)
+ cvmx_write_csr(CVMX_PKO_REG_QUEUE_MODE, 1);
+ }
+ }
+}
+
+/**
+ * This function does per-core initialization required by the PKO routines.
+ * This must be called on all cores that will do packet output, and must
+ * be called after the FPA has been initialized and filled with pages.
+ *
+ * Returns 0 on success
+ * !0 on failure
+ */
+int cvmx_pko_initialize_local(void)
+{
+ /* Nothing to do */
+ return 0;
+}
+
+/**
+ * Enables the packet output hardware. It must already be
+ * configured.
+ */
+void cvmx_pko_enable(void)
+{
+ union cvmx_pko_reg_flags flags;
+
+ flags.u64 = cvmx_read_csr(CVMX_PKO_REG_FLAGS);
+ if (flags.s.ena_pko)
+ cvmx_dprintf
+ ("Warning: Enabling PKO when PKO already enabled.\n");
+
+ flags.s.ena_dwb = 1;
+ flags.s.ena_pko = 1;
+ /*
+ * always enable big endian for 3-word command. Does nothing
+ * for 2-word.
+ */
+ flags.s.store_be = 1;
+ cvmx_write_csr(CVMX_PKO_REG_FLAGS, flags.u64);
+}
+
+/**
+ * Disables the packet output. Does not affect any configuration.
+ */
+void cvmx_pko_disable(void)
+{
+ union cvmx_pko_reg_flags pko_reg_flags;
+ pko_reg_flags.u64 = cvmx_read_csr(CVMX_PKO_REG_FLAGS);
+ pko_reg_flags.s.ena_pko = 0;
+ cvmx_write_csr(CVMX_PKO_REG_FLAGS, pko_reg_flags.u64);
+}
+EXPORT_SYMBOL_GPL(cvmx_pko_disable);
+
+/**
+ * Reset the packet output.
+ */
+static void __cvmx_pko_reset(void)
+{
+ union cvmx_pko_reg_flags pko_reg_flags;
+ pko_reg_flags.u64 = cvmx_read_csr(CVMX_PKO_REG_FLAGS);
+ pko_reg_flags.s.reset = 1;
+ cvmx_write_csr(CVMX_PKO_REG_FLAGS, pko_reg_flags.u64);
+}
+
+/**
+ * Shutdown and free resources required by packet output.
+ */
+void cvmx_pko_shutdown(void)
+{
+ union cvmx_pko_mem_queue_ptrs config;
+ int queue;
+
+ cvmx_pko_disable();
+
+ for (queue = 0; queue < CVMX_PKO_MAX_OUTPUT_QUEUES; queue++) {
+ config.u64 = 0;
+ config.s.tail = 1;
+ config.s.index = 0;
+ config.s.port = CVMX_PKO_MEM_QUEUE_PTRS_ILLEGAL_PID;
+ config.s.queue = queue & 0x7f;
+ config.s.qos_mask = 0;
+ config.s.buf_ptr = 0;
+ if (!OCTEON_IS_MODEL(OCTEON_CN3XXX)) {
+ union cvmx_pko_reg_queue_ptrs1 config1;
+ config1.u64 = 0;
+ config1.s.qid7 = queue >> 7;
+ cvmx_write_csr(CVMX_PKO_REG_QUEUE_PTRS1, config1.u64);
+ }
+ cvmx_write_csr(CVMX_PKO_MEM_QUEUE_PTRS, config.u64);
+ cvmx_cmd_queue_shutdown(CVMX_CMD_QUEUE_PKO(queue));
+ }
+ __cvmx_pko_reset();
+}
+EXPORT_SYMBOL_GPL(cvmx_pko_shutdown);
+
+/**
+ * Configure a output port and the associated queues for use.
+ *
+ * @port: Port to configure.
+ * @base_queue: First queue number to associate with this port.
+ * @num_queues: Number of queues to associate with this port
+ * @priority: Array of priority levels for each queue. Values are
+ * allowed to be 0-8. A value of 8 get 8 times the traffic
+ * of a value of 1. A value of 0 indicates that no rounds
+ * will be participated in. These priorities can be changed
+ * on the fly while the pko is enabled. A priority of 9
+ * indicates that static priority should be used. If static
+ * priority is used all queues with static priority must be
+ * contiguous starting at the base_queue, and lower numbered
+ * queues have higher priority than higher numbered queues.
+ * There must be num_queues elements in the array.
+ */
+cvmx_pko_status_t cvmx_pko_config_port(uint64_t port, uint64_t base_queue,
+ uint64_t num_queues,
+ const uint64_t priority[])
+{
+ cvmx_pko_status_t result_code;
+ uint64_t queue;
+ union cvmx_pko_mem_queue_ptrs config;
+ union cvmx_pko_reg_queue_ptrs1 config1;
+ int static_priority_base = -1;
+ int static_priority_end = -1;
+
+ if ((port >= CVMX_PKO_NUM_OUTPUT_PORTS)
+ && (port != CVMX_PKO_MEM_QUEUE_PTRS_ILLEGAL_PID)) {
+ cvmx_dprintf("ERROR: cvmx_pko_config_port: Invalid port %llu\n",
+ (unsigned long long)port);
+ return CVMX_PKO_INVALID_PORT;
+ }
+
+ if (base_queue + num_queues > CVMX_PKO_MAX_OUTPUT_QUEUES) {
+ cvmx_dprintf
+ ("ERROR: cvmx_pko_config_port: Invalid queue range %llu\n",
+ (unsigned long long)(base_queue + num_queues));
+ return CVMX_PKO_INVALID_QUEUE;
+ }
+
+ if (port != CVMX_PKO_MEM_QUEUE_PTRS_ILLEGAL_PID) {
+ /*
+ * Validate the static queue priority setup and set
+ * static_priority_base and static_priority_end
+ * accordingly.
+ */
+ for (queue = 0; queue < num_queues; queue++) {
+ /* Find first queue of static priority */
+ if (static_priority_base == -1
+ && priority[queue] ==
+ CVMX_PKO_QUEUE_STATIC_PRIORITY)
+ static_priority_base = queue;
+ /* Find last queue of static priority */
+ if (static_priority_base != -1
+ && static_priority_end == -1
+ && priority[queue] != CVMX_PKO_QUEUE_STATIC_PRIORITY
+ && queue)
+ static_priority_end = queue - 1;
+ else if (static_priority_base != -1
+ && static_priority_end == -1
+ && queue == num_queues - 1)
+ /* all queues are static priority */
+ static_priority_end = queue;
+ /*
+ * Check to make sure all static priority
+ * queues are contiguous. Also catches some
+ * cases of static priorites not starting at
+ * queue 0.
+ */
+ if (static_priority_end != -1
+ && (int)queue > static_priority_end
+ && priority[queue] ==
+ CVMX_PKO_QUEUE_STATIC_PRIORITY) {
+ cvmx_dprintf("ERROR: cvmx_pko_config_port: "
+ "Static priority queues aren't "
+ "contiguous or don't start at "
+ "base queue. q: %d, eq: %d\n",
+ (int)queue, static_priority_end);
+ return CVMX_PKO_INVALID_PRIORITY;
+ }
+ }
+ if (static_priority_base > 0) {
+ cvmx_dprintf("ERROR: cvmx_pko_config_port: Static "
+ "priority queues don't start at base "
+ "queue. sq: %d\n",
+ static_priority_base);
+ return CVMX_PKO_INVALID_PRIORITY;
+ }
+#if 0
+ cvmx_dprintf("Port %d: Static priority queue base: %d, "
+ "end: %d\n", port,
+ static_priority_base, static_priority_end);
+#endif
+ }
+ /*
+ * At this point, static_priority_base and static_priority_end
+ * are either both -1, or are valid start/end queue
+ * numbers.
+ */
+
+ result_code = CVMX_PKO_SUCCESS;
+
+#ifdef PKO_DEBUG
+ cvmx_dprintf("num queues: %d (%lld,%lld)\n", num_queues,
+ CVMX_PKO_QUEUES_PER_PORT_INTERFACE0,
+ CVMX_PKO_QUEUES_PER_PORT_INTERFACE1);
+#endif
+
+ for (queue = 0; queue < num_queues; queue++) {
+ uint64_t *buf_ptr = NULL;
+
+ config1.u64 = 0;
+ config1.s.idx3 = queue >> 3;
+ config1.s.qid7 = (base_queue + queue) >> 7;
+
+ config.u64 = 0;
+ config.s.tail = queue == (num_queues - 1);
+ config.s.index = queue;
+ config.s.port = port;
+ config.s.queue = base_queue + queue;
+
+ if (!cvmx_octeon_is_pass1()) {
+ config.s.static_p = static_priority_base >= 0;
+ config.s.static_q = (int)queue <= static_priority_end;
+ config.s.s_tail = (int)queue == static_priority_end;
+ }
+ /*
+ * Convert the priority into an enable bit field. Try
+ * to space the bits out evenly so the packet don't
+ * get grouped up
+ */
+ switch ((int)priority[queue]) {
+ case 0:
+ config.s.qos_mask = 0x00;
+ break;
+ case 1:
+ config.s.qos_mask = 0x01;
+ break;
+ case 2:
+ config.s.qos_mask = 0x11;
+ break;
+ case 3:
+ config.s.qos_mask = 0x49;
+ break;
+ case 4:
+ config.s.qos_mask = 0x55;
+ break;
+ case 5:
+ config.s.qos_mask = 0x57;
+ break;
+ case 6:
+ config.s.qos_mask = 0x77;
+ break;
+ case 7:
+ config.s.qos_mask = 0x7f;
+ break;
+ case 8:
+ config.s.qos_mask = 0xff;
+ break;
+ case CVMX_PKO_QUEUE_STATIC_PRIORITY:
+ /* Pass 1 will fall through to the error case */
+ if (!cvmx_octeon_is_pass1()) {
+ config.s.qos_mask = 0xff;
+ break;
+ }
+ default:
+ cvmx_dprintf("ERROR: cvmx_pko_config_port: Invalid "
+ "priority %llu\n",
+ (unsigned long long)priority[queue]);
+ config.s.qos_mask = 0xff;
+ result_code = CVMX_PKO_INVALID_PRIORITY;
+ break;
+ }
+
+ if (port != CVMX_PKO_MEM_QUEUE_PTRS_ILLEGAL_PID) {
+ cvmx_cmd_queue_result_t cmd_res =
+ cvmx_cmd_queue_initialize(CVMX_CMD_QUEUE_PKO
+ (base_queue + queue),
+ CVMX_PKO_MAX_QUEUE_DEPTH,
+ CVMX_FPA_OUTPUT_BUFFER_POOL,
+ CVMX_FPA_OUTPUT_BUFFER_POOL_SIZE
+ -
+ CVMX_PKO_COMMAND_BUFFER_SIZE_ADJUST
+ * 8);
+ if (cmd_res != CVMX_CMD_QUEUE_SUCCESS) {
+ switch (cmd_res) {
+ case CVMX_CMD_QUEUE_NO_MEMORY:
+ cvmx_dprintf("ERROR: "
+ "cvmx_pko_config_port: "
+ "Unable to allocate "
+ "output buffer.\n");
+ return CVMX_PKO_NO_MEMORY;
+ case CVMX_CMD_QUEUE_ALREADY_SETUP:
+ cvmx_dprintf
+ ("ERROR: cvmx_pko_config_port: Port already setup.\n");
+ return CVMX_PKO_PORT_ALREADY_SETUP;
+ case CVMX_CMD_QUEUE_INVALID_PARAM:
+ default:
+ cvmx_dprintf
+ ("ERROR: cvmx_pko_config_port: Command queue initialization failed.\n");
+ return CVMX_PKO_CMD_QUEUE_INIT_ERROR;
+ }
+ }
+
+ buf_ptr =
+ (uint64_t *)
+ cvmx_cmd_queue_buffer(CVMX_CMD_QUEUE_PKO
+ (base_queue + queue));
+ config.s.buf_ptr = cvmx_ptr_to_phys(buf_ptr);
+ } else
+ config.s.buf_ptr = 0;
+
+ CVMX_SYNCWS;
+
+ if (!OCTEON_IS_MODEL(OCTEON_CN3XXX))
+ cvmx_write_csr(CVMX_PKO_REG_QUEUE_PTRS1, config1.u64);
+ cvmx_write_csr(CVMX_PKO_MEM_QUEUE_PTRS, config.u64);
+ }
+
+ return result_code;
+}
+
+#ifdef PKO_DEBUG
+/**
+ * Show map of ports -> queues for different cores.
+ */
+void cvmx_pko_show_queue_map()
+{
+ int core, port;
+ int pko_output_ports = 36;
+
+ cvmx_dprintf("port");
+ for (port = 0; port < pko_output_ports; port++)
+ cvmx_dprintf("%3d ", port);
+ cvmx_dprintf("\n");
+
+ for (core = 0; core < CVMX_MAX_CORES; core++) {
+ cvmx_dprintf("\n%2d: ", core);
+ for (port = 0; port < pko_output_ports; port++) {
+ cvmx_dprintf("%3d ",
+ cvmx_pko_get_base_queue_per_core(port,
+ core));
+ }
+ }
+ cvmx_dprintf("\n");
+}
+#endif
+
+/**
+ * Rate limit a PKO port to a max packets/sec. This function is only
+ * supported on CN51XX and higher, excluding CN58XX.
+ *
+ * @port: Port to rate limit
+ * @packets_s: Maximum packet/sec
+ * @burst: Maximum number of packets to burst in a row before rate
+ * limiting cuts in.
+ *
+ * Returns Zero on success, negative on failure
+ */
+int cvmx_pko_rate_limit_packets(int port, int packets_s, int burst)
+{
+ union cvmx_pko_mem_port_rate0 pko_mem_port_rate0;
+ union cvmx_pko_mem_port_rate1 pko_mem_port_rate1;
+
+ pko_mem_port_rate0.u64 = 0;
+ pko_mem_port_rate0.s.pid = port;
+ pko_mem_port_rate0.s.rate_pkt =
+ cvmx_sysinfo_get()->cpu_clock_hz / packets_s / 16;
+ /* No cost per word since we are limited by packets/sec, not bits/sec */
+ pko_mem_port_rate0.s.rate_word = 0;
+
+ pko_mem_port_rate1.u64 = 0;
+ pko_mem_port_rate1.s.pid = port;
+ pko_mem_port_rate1.s.rate_lim =
+ ((uint64_t) pko_mem_port_rate0.s.rate_pkt * burst) >> 8;
+
+ cvmx_write_csr(CVMX_PKO_MEM_PORT_RATE0, pko_mem_port_rate0.u64);
+ cvmx_write_csr(CVMX_PKO_MEM_PORT_RATE1, pko_mem_port_rate1.u64);
+ return 0;
+}
+
+/**
+ * Rate limit a PKO port to a max bits/sec. This function is only
+ * supported on CN51XX and higher, excluding CN58XX.
+ *
+ * @port: Port to rate limit
+ * @bits_s: PKO rate limit in bits/sec
+ * @burst: Maximum number of bits to burst before rate
+ * limiting cuts in.
+ *
+ * Returns Zero on success, negative on failure
+ */
+int cvmx_pko_rate_limit_bits(int port, uint64_t bits_s, int burst)
+{
+ union cvmx_pko_mem_port_rate0 pko_mem_port_rate0;
+ union cvmx_pko_mem_port_rate1 pko_mem_port_rate1;
+ uint64_t clock_rate = cvmx_sysinfo_get()->cpu_clock_hz;
+ uint64_t tokens_per_bit = clock_rate * 16 / bits_s;
+
+ pko_mem_port_rate0.u64 = 0;
+ pko_mem_port_rate0.s.pid = port;
+ /*
+ * Each packet has a 12 bytes of interframe gap, an 8 byte
+ * preamble, and a 4 byte CRC. These are not included in the
+ * per word count. Multiply by 8 to covert to bits and divide
+ * by 256 for limit granularity.
+ */
+ pko_mem_port_rate0.s.rate_pkt = (12 + 8 + 4) * 8 * tokens_per_bit / 256;
+ /* Each 8 byte word has 64bits */
+ pko_mem_port_rate0.s.rate_word = 64 * tokens_per_bit;
+
+ pko_mem_port_rate1.u64 = 0;
+ pko_mem_port_rate1.s.pid = port;
+ pko_mem_port_rate1.s.rate_lim = tokens_per_bit * burst / 256;
+
+ cvmx_write_csr(CVMX_PKO_MEM_PORT_RATE0, pko_mem_port_rate0.u64);
+ cvmx_write_csr(CVMX_PKO_MEM_PORT_RATE1, pko_mem_port_rate1.u64);
+ return 0;
+}
diff --git a/arch/mips/cavium-octeon/executive/cvmx-spi.c b/arch/mips/cavium-octeon/executive/cvmx-spi.c
new file mode 100644
index 00000000000..459e3b1eb61
--- /dev/null
+++ b/arch/mips/cavium-octeon/executive/cvmx-spi.c
@@ -0,0 +1,668 @@
+/***********************license start***************
+ * Author: Cavium Networks
+ *
+ * Contact: support@caviumnetworks.com
+ * This file is part of the OCTEON SDK
+ *
+ * Copyright (c) 2003-2008 Cavium Networks
+ *
+ * This file is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, Version 2, as
+ * published by the Free Software Foundation.
+ *
+ * This file is distributed in the hope that it will be useful, but
+ * AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or
+ * NONINFRINGEMENT. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this file; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ * or visit http://www.gnu.org/licenses/.
+ *
+ * This file may also be available under a different license from Cavium.
+ * Contact Cavium Networks for more information
+ ***********************license end**************************************/
+
+/*
+ *
+ * Support library for the SPI
+ */
+#include <asm/octeon/octeon.h>
+
+#include <asm/octeon/cvmx-config.h>
+
+#include <asm/octeon/cvmx-pko.h>
+#include <asm/octeon/cvmx-spi.h>
+
+#include <asm/octeon/cvmx-spxx-defs.h>
+#include <asm/octeon/cvmx-stxx-defs.h>
+#include <asm/octeon/cvmx-srxx-defs.h>
+
+#define INVOKE_CB(function_p, args...) \
+ do { \
+ if (function_p) { \
+ res = function_p(args); \
+ if (res) \
+ return res; \
+ } \
+ } while (0)
+
+#if CVMX_ENABLE_DEBUG_PRINTS
+static const char *modes[] =
+ { "UNKNOWN", "TX Halfplex", "Rx Halfplex", "Duplex" };
+#endif
+
+/* Default callbacks, can be overridden
+ * using cvmx_spi_get_callbacks/cvmx_spi_set_callbacks
+ */
+static cvmx_spi_callbacks_t cvmx_spi_callbacks = {
+ .reset_cb = cvmx_spi_reset_cb,
+ .calendar_setup_cb = cvmx_spi_calendar_setup_cb,
+ .clock_detect_cb = cvmx_spi_clock_detect_cb,
+ .training_cb = cvmx_spi_training_cb,
+ .calendar_sync_cb = cvmx_spi_calendar_sync_cb,
+ .interface_up_cb = cvmx_spi_interface_up_cb
+};
+
+/**
+ * Get current SPI4 initialization callbacks
+ *
+ * @callbacks: Pointer to the callbacks structure.to fill
+ *
+ * Returns Pointer to cvmx_spi_callbacks_t structure.
+ */
+void cvmx_spi_get_callbacks(cvmx_spi_callbacks_t *callbacks)
+{
+ memcpy(callbacks, &cvmx_spi_callbacks, sizeof(cvmx_spi_callbacks));
+}
+
+/**
+ * Set new SPI4 initialization callbacks
+ *
+ * @new_callbacks: Pointer to an updated callbacks structure.
+ */
+void cvmx_spi_set_callbacks(cvmx_spi_callbacks_t *new_callbacks)
+{
+ memcpy(&cvmx_spi_callbacks, new_callbacks, sizeof(cvmx_spi_callbacks));
+}
+
+/**
+ * Initialize and start the SPI interface.
+ *
+ * @interface: The identifier of the packet interface to configure and
+ * use as a SPI interface.
+ * @mode: The operating mode for the SPI interface. The interface
+ * can operate as a full duplex (both Tx and Rx data paths
+ * active) or as a halfplex (either the Tx data path is
+ * active or the Rx data path is active, but not both).
+ * @timeout: Timeout to wait for clock synchronization in seconds
+ * @num_ports: Number of SPI ports to configure
+ *
+ * Returns Zero on success, negative of failure.
+ */
+int cvmx_spi_start_interface(int interface, cvmx_spi_mode_t mode, int timeout,
+ int num_ports)
+{
+ int res = -1;
+
+ if (!(OCTEON_IS_MODEL(OCTEON_CN38XX) || OCTEON_IS_MODEL(OCTEON_CN58XX)))
+ return res;
+
+ /* Callback to perform SPI4 reset */
+ INVOKE_CB(cvmx_spi_callbacks.reset_cb, interface, mode);
+
+ /* Callback to perform calendar setup */
+ INVOKE_CB(cvmx_spi_callbacks.calendar_setup_cb, interface, mode,
+ num_ports);
+
+ /* Callback to perform clock detection */
+ INVOKE_CB(cvmx_spi_callbacks.clock_detect_cb, interface, mode, timeout);
+
+ /* Callback to perform SPI4 link training */
+ INVOKE_CB(cvmx_spi_callbacks.training_cb, interface, mode, timeout);
+
+ /* Callback to perform calendar sync */
+ INVOKE_CB(cvmx_spi_callbacks.calendar_sync_cb, interface, mode,
+ timeout);
+
+ /* Callback to handle interface coming up */
+ INVOKE_CB(cvmx_spi_callbacks.interface_up_cb, interface, mode);
+
+ return res;
+}
+
+/**
+ * This routine restarts the SPI interface after it has lost synchronization
+ * with its correspondent system.
+ *
+ * @interface: The identifier of the packet interface to configure and
+ * use as a SPI interface.
+ * @mode: The operating mode for the SPI interface. The interface
+ * can operate as a full duplex (both Tx and Rx data paths
+ * active) or as a halfplex (either the Tx data path is
+ * active or the Rx data path is active, but not both).
+ * @timeout: Timeout to wait for clock synchronization in seconds
+ *
+ * Returns Zero on success, negative of failure.
+ */
+int cvmx_spi_restart_interface(int interface, cvmx_spi_mode_t mode, int timeout)
+{
+ int res = -1;
+
+ if (!(OCTEON_IS_MODEL(OCTEON_CN38XX) || OCTEON_IS_MODEL(OCTEON_CN58XX)))
+ return res;
+
+ cvmx_dprintf("SPI%d: Restart %s\n", interface, modes[mode]);
+
+ /* Callback to perform SPI4 reset */
+ INVOKE_CB(cvmx_spi_callbacks.reset_cb, interface, mode);
+
+ /* NOTE: Calendar setup is not performed during restart */
+ /* Refer to cvmx_spi_start_interface() for the full sequence */
+
+ /* Callback to perform clock detection */
+ INVOKE_CB(cvmx_spi_callbacks.clock_detect_cb, interface, mode, timeout);
+
+ /* Callback to perform SPI4 link training */
+ INVOKE_CB(cvmx_spi_callbacks.training_cb, interface, mode, timeout);
+
+ /* Callback to perform calendar sync */
+ INVOKE_CB(cvmx_spi_callbacks.calendar_sync_cb, interface, mode,
+ timeout);
+
+ /* Callback to handle interface coming up */
+ INVOKE_CB(cvmx_spi_callbacks.interface_up_cb, interface, mode);
+
+ return res;
+}
+EXPORT_SYMBOL_GPL(cvmx_spi_restart_interface);
+
+/**
+ * Callback to perform SPI4 reset
+ *
+ * @interface: The identifier of the packet interface to configure and
+ * use as a SPI interface.
+ * @mode: The operating mode for the SPI interface. The interface
+ * can operate as a full duplex (both Tx and Rx data paths
+ * active) or as a halfplex (either the Tx data path is
+ * active or the Rx data path is active, but not both).
+ *
+ * Returns Zero on success, non-zero error code on failure (will cause
+ * SPI initialization to abort)
+ */
+int cvmx_spi_reset_cb(int interface, cvmx_spi_mode_t mode)
+{
+ union cvmx_spxx_dbg_deskew_ctl spxx_dbg_deskew_ctl;
+ union cvmx_spxx_clk_ctl spxx_clk_ctl;
+ union cvmx_spxx_bist_stat spxx_bist_stat;
+ union cvmx_spxx_int_msk spxx_int_msk;
+ union cvmx_stxx_int_msk stxx_int_msk;
+ union cvmx_spxx_trn4_ctl spxx_trn4_ctl;
+ int index;
+ uint64_t MS = cvmx_sysinfo_get()->cpu_clock_hz / 1000;
+
+ /* Disable SPI error events while we run BIST */
+ spxx_int_msk.u64 = cvmx_read_csr(CVMX_SPXX_INT_MSK(interface));
+ cvmx_write_csr(CVMX_SPXX_INT_MSK(interface), 0);
+ stxx_int_msk.u64 = cvmx_read_csr(CVMX_STXX_INT_MSK(interface));
+ cvmx_write_csr(CVMX_STXX_INT_MSK(interface), 0);
+
+ /* Run BIST in the SPI interface */
+ cvmx_write_csr(CVMX_SRXX_COM_CTL(interface), 0);
+ cvmx_write_csr(CVMX_STXX_COM_CTL(interface), 0);
+ spxx_clk_ctl.u64 = 0;
+ spxx_clk_ctl.s.runbist = 1;
+ cvmx_write_csr(CVMX_SPXX_CLK_CTL(interface), spxx_clk_ctl.u64);
+ cvmx_wait(10 * MS);
+ spxx_bist_stat.u64 = cvmx_read_csr(CVMX_SPXX_BIST_STAT(interface));
+ if (spxx_bist_stat.s.stat0)
+ cvmx_dprintf
+ ("ERROR SPI%d: BIST failed on receive datapath FIFO\n",
+ interface);
+ if (spxx_bist_stat.s.stat1)
+ cvmx_dprintf("ERROR SPI%d: BIST failed on RX calendar table\n",
+ interface);
+ if (spxx_bist_stat.s.stat2)
+ cvmx_dprintf("ERROR SPI%d: BIST failed on TX calendar table\n",
+ interface);
+
+ /* Clear the calendar table after BIST to fix parity errors */
+ for (index = 0; index < 32; index++) {
+ union cvmx_srxx_spi4_calx srxx_spi4_calx;
+ union cvmx_stxx_spi4_calx stxx_spi4_calx;
+
+ srxx_spi4_calx.u64 = 0;
+ srxx_spi4_calx.s.oddpar = 1;
+ cvmx_write_csr(CVMX_SRXX_SPI4_CALX(index, interface),
+ srxx_spi4_calx.u64);
+
+ stxx_spi4_calx.u64 = 0;
+ stxx_spi4_calx.s.oddpar = 1;
+ cvmx_write_csr(CVMX_STXX_SPI4_CALX(index, interface),
+ stxx_spi4_calx.u64);
+ }
+
+ /* Re enable reporting of error interrupts */
+ cvmx_write_csr(CVMX_SPXX_INT_REG(interface),
+ cvmx_read_csr(CVMX_SPXX_INT_REG(interface)));
+ cvmx_write_csr(CVMX_SPXX_INT_MSK(interface), spxx_int_msk.u64);
+ cvmx_write_csr(CVMX_STXX_INT_REG(interface),
+ cvmx_read_csr(CVMX_STXX_INT_REG(interface)));
+ cvmx_write_csr(CVMX_STXX_INT_MSK(interface), stxx_int_msk.u64);
+
+ /* Setup the CLKDLY right in the middle */
+ spxx_clk_ctl.u64 = 0;
+ spxx_clk_ctl.s.seetrn = 0;
+ spxx_clk_ctl.s.clkdly = 0x10;
+ spxx_clk_ctl.s.runbist = 0;
+ spxx_clk_ctl.s.statdrv = 0;
+ /* This should always be on the opposite edge as statdrv */
+ spxx_clk_ctl.s.statrcv = 1;
+ spxx_clk_ctl.s.sndtrn = 0;
+ spxx_clk_ctl.s.drptrn = 0;
+ spxx_clk_ctl.s.rcvtrn = 0;
+ spxx_clk_ctl.s.srxdlck = 0;
+ cvmx_write_csr(CVMX_SPXX_CLK_CTL(interface), spxx_clk_ctl.u64);
+ cvmx_wait(100 * MS);
+
+ /* Reset SRX0 DLL */
+ spxx_clk_ctl.s.srxdlck = 1;
+ cvmx_write_csr(CVMX_SPXX_CLK_CTL(interface), spxx_clk_ctl.u64);
+
+ /* Waiting for Inf0 Spi4 RX DLL to lock */
+ cvmx_wait(100 * MS);
+
+ /* Enable dynamic alignment */
+ spxx_trn4_ctl.s.trntest = 0;
+ spxx_trn4_ctl.s.jitter = 1;
+ spxx_trn4_ctl.s.clr_boot = 1;
+ spxx_trn4_ctl.s.set_boot = 0;
+ if (OCTEON_IS_MODEL(OCTEON_CN58XX))
+ spxx_trn4_ctl.s.maxdist = 3;
+ else
+ spxx_trn4_ctl.s.maxdist = 8;
+ spxx_trn4_ctl.s.macro_en = 1;
+ spxx_trn4_ctl.s.mux_en = 1;
+ cvmx_write_csr(CVMX_SPXX_TRN4_CTL(interface), spxx_trn4_ctl.u64);
+
+ spxx_dbg_deskew_ctl.u64 = 0;
+ cvmx_write_csr(CVMX_SPXX_DBG_DESKEW_CTL(interface),
+ spxx_dbg_deskew_ctl.u64);
+
+ return 0;
+}
+
+/**
+ * Callback to setup calendar and miscellaneous settings before clock detection
+ *
+ * @interface: The identifier of the packet interface to configure and
+ * use as a SPI interface.
+ * @mode: The operating mode for the SPI interface. The interface
+ * can operate as a full duplex (both Tx and Rx data paths
+ * active) or as a halfplex (either the Tx data path is
+ * active or the Rx data path is active, but not both).
+ * @num_ports: Number of ports to configure on SPI
+ *
+ * Returns Zero on success, non-zero error code on failure (will cause
+ * SPI initialization to abort)
+ */
+int cvmx_spi_calendar_setup_cb(int interface, cvmx_spi_mode_t mode,
+ int num_ports)
+{
+ int port;
+ int index;
+ if (mode & CVMX_SPI_MODE_RX_HALFPLEX) {
+ union cvmx_srxx_com_ctl srxx_com_ctl;
+ union cvmx_srxx_spi4_stat srxx_spi4_stat;
+
+ /* SRX0 number of Ports */
+ srxx_com_ctl.u64 = 0;
+ srxx_com_ctl.s.prts = num_ports - 1;
+ srxx_com_ctl.s.st_en = 0;
+ srxx_com_ctl.s.inf_en = 0;
+ cvmx_write_csr(CVMX_SRXX_COM_CTL(interface), srxx_com_ctl.u64);
+
+ /* SRX0 Calendar Table. This round robbins through all ports */
+ port = 0;
+ index = 0;
+ while (port < num_ports) {
+ union cvmx_srxx_spi4_calx srxx_spi4_calx;
+ srxx_spi4_calx.u64 = 0;
+ srxx_spi4_calx.s.prt0 = port++;
+ srxx_spi4_calx.s.prt1 = port++;
+ srxx_spi4_calx.s.prt2 = port++;
+ srxx_spi4_calx.s.prt3 = port++;
+ srxx_spi4_calx.s.oddpar =
+ ~(cvmx_dpop(srxx_spi4_calx.u64) & 1);
+ cvmx_write_csr(CVMX_SRXX_SPI4_CALX(index, interface),
+ srxx_spi4_calx.u64);
+ index++;
+ }
+ srxx_spi4_stat.u64 = 0;
+ srxx_spi4_stat.s.len = num_ports;
+ srxx_spi4_stat.s.m = 1;
+ cvmx_write_csr(CVMX_SRXX_SPI4_STAT(interface),
+ srxx_spi4_stat.u64);
+ }
+
+ if (mode & CVMX_SPI_MODE_TX_HALFPLEX) {
+ union cvmx_stxx_arb_ctl stxx_arb_ctl;
+ union cvmx_gmxx_tx_spi_max gmxx_tx_spi_max;
+ union cvmx_gmxx_tx_spi_thresh gmxx_tx_spi_thresh;
+ union cvmx_gmxx_tx_spi_ctl gmxx_tx_spi_ctl;
+ union cvmx_stxx_spi4_stat stxx_spi4_stat;
+ union cvmx_stxx_spi4_dat stxx_spi4_dat;
+
+ /* STX0 Config */
+ stxx_arb_ctl.u64 = 0;
+ stxx_arb_ctl.s.igntpa = 0;
+ stxx_arb_ctl.s.mintrn = 0;
+ cvmx_write_csr(CVMX_STXX_ARB_CTL(interface), stxx_arb_ctl.u64);
+
+ gmxx_tx_spi_max.u64 = 0;
+ gmxx_tx_spi_max.s.max1 = 8;
+ gmxx_tx_spi_max.s.max2 = 4;
+ gmxx_tx_spi_max.s.slice = 0;
+ cvmx_write_csr(CVMX_GMXX_TX_SPI_MAX(interface),
+ gmxx_tx_spi_max.u64);
+
+ gmxx_tx_spi_thresh.u64 = 0;
+ gmxx_tx_spi_thresh.s.thresh = 4;
+ cvmx_write_csr(CVMX_GMXX_TX_SPI_THRESH(interface),
+ gmxx_tx_spi_thresh.u64);
+
+ gmxx_tx_spi_ctl.u64 = 0;
+ gmxx_tx_spi_ctl.s.tpa_clr = 0;
+ gmxx_tx_spi_ctl.s.cont_pkt = 0;
+ cvmx_write_csr(CVMX_GMXX_TX_SPI_CTL(interface),
+ gmxx_tx_spi_ctl.u64);
+
+ /* STX0 Training Control */
+ stxx_spi4_dat.u64 = 0;
+ /*Minimum needed by dynamic alignment */
+ stxx_spi4_dat.s.alpha = 32;
+ stxx_spi4_dat.s.max_t = 0xFFFF; /*Minimum interval is 0x20 */
+ cvmx_write_csr(CVMX_STXX_SPI4_DAT(interface),
+ stxx_spi4_dat.u64);
+
+ /* STX0 Calendar Table. This round robbins through all ports */
+ port = 0;
+ index = 0;
+ while (port < num_ports) {
+ union cvmx_stxx_spi4_calx stxx_spi4_calx;
+ stxx_spi4_calx.u64 = 0;
+ stxx_spi4_calx.s.prt0 = port++;
+ stxx_spi4_calx.s.prt1 = port++;
+ stxx_spi4_calx.s.prt2 = port++;
+ stxx_spi4_calx.s.prt3 = port++;
+ stxx_spi4_calx.s.oddpar =
+ ~(cvmx_dpop(stxx_spi4_calx.u64) & 1);
+ cvmx_write_csr(CVMX_STXX_SPI4_CALX(index, interface),
+ stxx_spi4_calx.u64);
+ index++;
+ }
+ stxx_spi4_stat.u64 = 0;
+ stxx_spi4_stat.s.len = num_ports;
+ stxx_spi4_stat.s.m = 1;
+ cvmx_write_csr(CVMX_STXX_SPI4_STAT(interface),
+ stxx_spi4_stat.u64);
+ }
+
+ return 0;
+}
+
+/**
+ * Callback to perform clock detection
+ *
+ * @interface: The identifier of the packet interface to configure and
+ * use as a SPI interface.
+ * @mode: The operating mode for the SPI interface. The interface
+ * can operate as a full duplex (both Tx and Rx data paths
+ * active) or as a halfplex (either the Tx data path is
+ * active or the Rx data path is active, but not both).
+ * @timeout: Timeout to wait for clock synchronization in seconds
+ *
+ * Returns Zero on success, non-zero error code on failure (will cause
+ * SPI initialization to abort)
+ */
+int cvmx_spi_clock_detect_cb(int interface, cvmx_spi_mode_t mode, int timeout)
+{
+ int clock_transitions;
+ union cvmx_spxx_clk_stat stat;
+ uint64_t timeout_time;
+ uint64_t MS = cvmx_sysinfo_get()->cpu_clock_hz / 1000;
+
+ /*
+ * Regardless of operating mode, both Tx and Rx clocks must be
+ * present for the SPI interface to operate.
+ */
+ cvmx_dprintf("SPI%d: Waiting to see TsClk...\n", interface);
+ timeout_time = cvmx_get_cycle() + 1000ull * MS * timeout;
+ /*
+ * Require 100 clock transitions in order to avoid any noise
+ * in the beginning.
+ */
+ clock_transitions = 100;
+ do {
+ stat.u64 = cvmx_read_csr(CVMX_SPXX_CLK_STAT(interface));
+ if (stat.s.s4clk0 && stat.s.s4clk1 && clock_transitions) {
+ /*
+ * We've seen a clock transition, so decrement
+ * the number we still need.
+ */
+ clock_transitions--;
+ cvmx_write_csr(CVMX_SPXX_CLK_STAT(interface), stat.u64);
+ stat.s.s4clk0 = 0;
+ stat.s.s4clk1 = 0;
+ }
+ if (cvmx_get_cycle() > timeout_time) {
+ cvmx_dprintf("SPI%d: Timeout\n", interface);
+ return -1;
+ }
+ } while (stat.s.s4clk0 == 0 || stat.s.s4clk1 == 0);
+
+ cvmx_dprintf("SPI%d: Waiting to see RsClk...\n", interface);
+ timeout_time = cvmx_get_cycle() + 1000ull * MS * timeout;
+ /*
+ * Require 100 clock transitions in order to avoid any noise in the
+ * beginning.
+ */
+ clock_transitions = 100;
+ do {
+ stat.u64 = cvmx_read_csr(CVMX_SPXX_CLK_STAT(interface));
+ if (stat.s.d4clk0 && stat.s.d4clk1 && clock_transitions) {
+ /*
+ * We've seen a clock transition, so decrement
+ * the number we still need
+ */
+ clock_transitions--;
+ cvmx_write_csr(CVMX_SPXX_CLK_STAT(interface), stat.u64);
+ stat.s.d4clk0 = 0;
+ stat.s.d4clk1 = 0;
+ }
+ if (cvmx_get_cycle() > timeout_time) {
+ cvmx_dprintf("SPI%d: Timeout\n", interface);
+ return -1;
+ }
+ } while (stat.s.d4clk0 == 0 || stat.s.d4clk1 == 0);
+
+ return 0;
+}
+
+/**
+ * Callback to perform link training
+ *
+ * @interface: The identifier of the packet interface to configure and
+ * use as a SPI interface.
+ * @mode: The operating mode for the SPI interface. The interface
+ * can operate as a full duplex (both Tx and Rx data paths
+ * active) or as a halfplex (either the Tx data path is
+ * active or the Rx data path is active, but not both).
+ * @timeout: Timeout to wait for link to be trained (in seconds)
+ *
+ * Returns Zero on success, non-zero error code on failure (will cause
+ * SPI initialization to abort)
+ */
+int cvmx_spi_training_cb(int interface, cvmx_spi_mode_t mode, int timeout)
+{
+ union cvmx_spxx_trn4_ctl spxx_trn4_ctl;
+ union cvmx_spxx_clk_stat stat;
+ uint64_t MS = cvmx_sysinfo_get()->cpu_clock_hz / 1000;
+ uint64_t timeout_time = cvmx_get_cycle() + 1000ull * MS * timeout;
+ int rx_training_needed;
+
+ /* SRX0 & STX0 Inf0 Links are configured - begin training */
+ union cvmx_spxx_clk_ctl spxx_clk_ctl;
+ spxx_clk_ctl.u64 = 0;
+ spxx_clk_ctl.s.seetrn = 0;
+ spxx_clk_ctl.s.clkdly = 0x10;
+ spxx_clk_ctl.s.runbist = 0;
+ spxx_clk_ctl.s.statdrv = 0;
+ /* This should always be on the opposite edge as statdrv */
+ spxx_clk_ctl.s.statrcv = 1;
+ spxx_clk_ctl.s.sndtrn = 1;
+ spxx_clk_ctl.s.drptrn = 1;
+ spxx_clk_ctl.s.rcvtrn = 1;
+ spxx_clk_ctl.s.srxdlck = 1;
+ cvmx_write_csr(CVMX_SPXX_CLK_CTL(interface), spxx_clk_ctl.u64);
+ cvmx_wait(1000 * MS);
+
+ /* SRX0 clear the boot bit */
+ spxx_trn4_ctl.u64 = cvmx_read_csr(CVMX_SPXX_TRN4_CTL(interface));
+ spxx_trn4_ctl.s.clr_boot = 1;
+ cvmx_write_csr(CVMX_SPXX_TRN4_CTL(interface), spxx_trn4_ctl.u64);
+
+ /* Wait for the training sequence to complete */
+ cvmx_dprintf("SPI%d: Waiting for training\n", interface);
+ cvmx_wait(1000 * MS);
+ /* Wait a really long time here */
+ timeout_time = cvmx_get_cycle() + 1000ull * MS * 600;
+ /*
+ * The HRM says we must wait for 34 + 16 * MAXDIST training sequences.
+ * We'll be pessimistic and wait for a lot more.
+ */
+ rx_training_needed = 500;
+ do {
+ stat.u64 = cvmx_read_csr(CVMX_SPXX_CLK_STAT(interface));
+ if (stat.s.srxtrn && rx_training_needed) {
+ rx_training_needed--;
+ cvmx_write_csr(CVMX_SPXX_CLK_STAT(interface), stat.u64);
+ stat.s.srxtrn = 0;
+ }
+ if (cvmx_get_cycle() > timeout_time) {
+ cvmx_dprintf("SPI%d: Timeout\n", interface);
+ return -1;
+ }
+ } while (stat.s.srxtrn == 0);
+
+ return 0;
+}
+
+/**
+ * Callback to perform calendar data synchronization
+ *
+ * @interface: The identifier of the packet interface to configure and
+ * use as a SPI interface.
+ * @mode: The operating mode for the SPI interface. The interface
+ * can operate as a full duplex (both Tx and Rx data paths
+ * active) or as a halfplex (either the Tx data path is
+ * active or the Rx data path is active, but not both).
+ * @timeout: Timeout to wait for calendar data in seconds
+ *
+ * Returns Zero on success, non-zero error code on failure (will cause
+ * SPI initialization to abort)
+ */
+int cvmx_spi_calendar_sync_cb(int interface, cvmx_spi_mode_t mode, int timeout)
+{
+ uint64_t MS = cvmx_sysinfo_get()->cpu_clock_hz / 1000;
+ if (mode & CVMX_SPI_MODE_RX_HALFPLEX) {
+ /* SRX0 interface should be good, send calendar data */
+ union cvmx_srxx_com_ctl srxx_com_ctl;
+ cvmx_dprintf
+ ("SPI%d: Rx is synchronized, start sending calendar data\n",
+ interface);
+ srxx_com_ctl.u64 = cvmx_read_csr(CVMX_SRXX_COM_CTL(interface));
+ srxx_com_ctl.s.inf_en = 1;
+ srxx_com_ctl.s.st_en = 1;
+ cvmx_write_csr(CVMX_SRXX_COM_CTL(interface), srxx_com_ctl.u64);
+ }
+
+ if (mode & CVMX_SPI_MODE_TX_HALFPLEX) {
+ /* STX0 has achieved sync */
+ /* The corespondant board should be sending calendar data */
+ /* Enable the STX0 STAT receiver. */
+ union cvmx_spxx_clk_stat stat;
+ uint64_t timeout_time;
+ union cvmx_stxx_com_ctl stxx_com_ctl;
+ stxx_com_ctl.u64 = 0;
+ stxx_com_ctl.s.st_en = 1;
+ cvmx_write_csr(CVMX_STXX_COM_CTL(interface), stxx_com_ctl.u64);
+
+ /* Waiting for calendar sync on STX0 STAT */
+ cvmx_dprintf("SPI%d: Waiting to sync on STX[%d] STAT\n",
+ interface, interface);
+ timeout_time = cvmx_get_cycle() + 1000ull * MS * timeout;
+ /* SPX0_CLK_STAT - SPX0_CLK_STAT[STXCAL] should be 1 (bit10) */
+ do {
+ stat.u64 = cvmx_read_csr(CVMX_SPXX_CLK_STAT(interface));
+ if (cvmx_get_cycle() > timeout_time) {
+ cvmx_dprintf("SPI%d: Timeout\n", interface);
+ return -1;
+ }
+ } while (stat.s.stxcal == 0);
+ }
+
+ return 0;
+}
+
+/**
+ * Callback to handle interface up
+ *
+ * @interface: The identifier of the packet interface to configure and
+ * use as a SPI interface.
+ * @mode: The operating mode for the SPI interface. The interface
+ * can operate as a full duplex (both Tx and Rx data paths
+ * active) or as a halfplex (either the Tx data path is
+ * active or the Rx data path is active, but not both).
+ *
+ * Returns Zero on success, non-zero error code on failure (will cause
+ * SPI initialization to abort)
+ */
+int cvmx_spi_interface_up_cb(int interface, cvmx_spi_mode_t mode)
+{
+ union cvmx_gmxx_rxx_frm_min gmxx_rxx_frm_min;
+ union cvmx_gmxx_rxx_frm_max gmxx_rxx_frm_max;
+ union cvmx_gmxx_rxx_jabber gmxx_rxx_jabber;
+
+ if (mode & CVMX_SPI_MODE_RX_HALFPLEX) {
+ union cvmx_srxx_com_ctl srxx_com_ctl;
+ srxx_com_ctl.u64 = cvmx_read_csr(CVMX_SRXX_COM_CTL(interface));
+ srxx_com_ctl.s.inf_en = 1;
+ cvmx_write_csr(CVMX_SRXX_COM_CTL(interface), srxx_com_ctl.u64);
+ cvmx_dprintf("SPI%d: Rx is now up\n", interface);
+ }
+
+ if (mode & CVMX_SPI_MODE_TX_HALFPLEX) {
+ union cvmx_stxx_com_ctl stxx_com_ctl;
+ stxx_com_ctl.u64 = cvmx_read_csr(CVMX_STXX_COM_CTL(interface));
+ stxx_com_ctl.s.inf_en = 1;
+ cvmx_write_csr(CVMX_STXX_COM_CTL(interface), stxx_com_ctl.u64);
+ cvmx_dprintf("SPI%d: Tx is now up\n", interface);
+ }
+
+ gmxx_rxx_frm_min.u64 = 0;
+ gmxx_rxx_frm_min.s.len = 64;
+ cvmx_write_csr(CVMX_GMXX_RXX_FRM_MIN(0, interface),
+ gmxx_rxx_frm_min.u64);
+ gmxx_rxx_frm_max.u64 = 0;
+ gmxx_rxx_frm_max.s.len = 64 * 1024 - 4;
+ cvmx_write_csr(CVMX_GMXX_RXX_FRM_MAX(0, interface),
+ gmxx_rxx_frm_max.u64);
+ gmxx_rxx_jabber.u64 = 0;
+ gmxx_rxx_jabber.s.cnt = 64 * 1024 - 4;
+ cvmx_write_csr(CVMX_GMXX_RXX_JABBER(0, interface), gmxx_rxx_jabber.u64);
+
+ return 0;
+}
diff --git a/arch/mips/cavium-octeon/executive/cvmx-sysinfo.c b/arch/mips/cavium-octeon/executive/cvmx-sysinfo.c
index 4812370706a..3d17fac2935 100644
--- a/arch/mips/cavium-octeon/executive/cvmx-sysinfo.c
+++ b/arch/mips/cavium-octeon/executive/cvmx-sysinfo.c
@@ -29,6 +29,7 @@
* 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>
@@ -69,29 +70,30 @@ 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
+ * 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
+ * Pointer to global physical memory descriptor
+ * (bootmem descriptor) @board_type: Octeon board
+ * type enumeration
*
* @board_rev_major:
- * Board major revision
+ * Board major revision
* @board_rev_minor:
- * Board minor revision
+ * Board minor revision
* @cpu_clock_hz:
- * CPU clock freqency in hertz
+ * CPU clock freqency in hertz
*
* Returns 0: Failure
- * 1: success
+ * 1: success
*/
int cvmx_sysinfo_minimal_initialize(void *phy_mem_desc_ptr,
uint16_t board_type,
@@ -113,4 +115,3 @@ int cvmx_sysinfo_minimal_initialize(void *phy_mem_desc_ptr,
return 1;
}
-
diff --git a/arch/mips/cavium-octeon/executive/octeon-model.c b/arch/mips/cavium-octeon/executive/octeon-model.c
index 9afc3794ed1..f4c1b36fdf6 100644
--- a/arch/mips/cavium-octeon/executive/octeon-model.c
+++ b/arch/mips/cavium-octeon/executive/octeon-model.c
@@ -4,7 +4,7 @@
* Contact: support@caviumnetworks.com
* This file is part of the OCTEON SDK
*
- * Copyright (c) 2003-2008 Cavium Networks
+ * 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
@@ -25,10 +25,6 @@
* Contact Cavium Networks for more information
***********************license end**************************************/
-/*
- * File defining functions for working with different Octeon
- * models.
- */
#include <asm/octeon/octeon.h>
/**
@@ -69,13 +65,14 @@ const char *octeon_model_get_string_buffer(uint32_t chip_id, char *buffer)
char fuse_model[10];
uint32_t fuse_data = 0;
- fus3.u64 = cvmx_read_csr(CVMX_L2D_FUS3);
+ 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));
- num_cores = cvmx_octeon_num_cores();
-
- /* Make sure the non existant devices look disabled */
+ /* Make sure the non existent devices look disabled */
switch ((chip_id >> 8) & 0xff) {
case 6: /* CN50XX */
case 2: /* CN30XX */
@@ -108,7 +105,7 @@ const char *octeon_model_get_string_buffer(uint32_t chip_id, char *buffer)
* Assume pass number is encoded using <5:3><2:0>. Exceptions
* will be fixed later.
*/
- sprintf(pass, "%u.%u", ((chip_id >> 3) & 7) + 1, chip_id & 7);
+ 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
@@ -116,6 +113,12 @@ const char *octeon_model_get_string_buffer(uint32_t chip_id, char *buffer)
* later.
*/
switch (num_cores) {
+ case 32:
+ core_model = "80";
+ break;
+ case 24:
+ core_model = "70";
+ break;
case 16:
core_model = "60";
break;
@@ -246,8 +249,8 @@ const char *octeon_model_get_string_buffer(uint32_t chip_id, char *buffer)
break;
case 3: /* CN58XX */
family = "58";
- /* Special case. 4 core, no crypto */
- if ((num_cores == 4) && fus_dat2.cn38xx.nocrypto)
+ /* 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 */
@@ -285,6 +288,9 @@ const char *octeon_model_get_string_buffer(uint32_t chip_id, char *buffer)
suffix = "NSP";
if (fus_dat3.s.nozip)
suffix = "SCP";
+
+ if (fus_dat3.s.bar2_en)
+ suffix = "NSPB2";
}
if (fus3.cn56xx.crip_1024k)
family = "54";
@@ -301,6 +307,60 @@ const char *octeon_model_get_string_buffer(uint32_t chip_id, char *buffer)
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";
@@ -310,49 +370,40 @@ const char *octeon_model_get_string_buffer(uint32_t chip_id, char *buffer)
}
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(51);
+ fuse_data |= cvmx_fuse_read_byte(fuse_base + 3);
fuse_data = fuse_data << 8;
- fuse_data |= cvmx_fuse_read_byte(50);
+ fuse_data |= cvmx_fuse_read_byte(fuse_base + 2);
fuse_data = fuse_data << 8;
- fuse_data |= cvmx_fuse_read_byte(49);
+ fuse_data |= cvmx_fuse_read_byte(fuse_base + 1);
fuse_data = fuse_data << 8;
- fuse_data |= cvmx_fuse_read_byte(48);
+ 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);
+ /* 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);
+ /* 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.
- */
+ /* 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);
+ sprintf(buffer, "CN%s%sp%s-%d-%s", family, core_model, pass, clock_mhz, suffix);
return buffer;
}
diff --git a/arch/mips/cavium-octeon/flash_setup.c b/arch/mips/cavium-octeon/flash_setup.c
index 553d36cbcc4..237e5b1a72d 100644
--- a/arch/mips/cavium-octeon/flash_setup.c
+++ b/arch/mips/cavium-octeon/flash_setup.c
@@ -8,6 +8,7 @@
* 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>
@@ -16,9 +17,6 @@
static struct map_info flash_map;
static struct mtd_info *mymtd;
-#ifdef CONFIG_MTD_PARTITIONS
-static int nr_parts;
-static struct mtd_partition *parts;
static const char *part_probe_types[] = {
"cmdlinepart",
#ifdef CONFIG_MTD_REDBOOT_PARTS
@@ -26,7 +24,6 @@ static const char *part_probe_types[] = {
#endif
NULL
};
-#endif
/**
* Module/ driver initialization.
@@ -54,26 +51,17 @@ static int __init flash_init(void)
flash_map.name = "phys_mapped_flash";
flash_map.phys = region_cfg.s.base << 16;
flash_map.size = 0x1fc00000 - flash_map.phys;
- flash_map.bankwidth = 1;
+ /* 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%08lx\n", flash_map.size >> 20, flash_map.phys);
+ "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;
-
-#ifdef CONFIG_MTD_PARTITIONS
- nr_parts = parse_mtd_partitions(mymtd,
- part_probe_types,
- &parts, 0);
- if (nr_parts > 0)
- add_mtd_partitions(mymtd, parts, nr_parts);
- else
- add_mtd_device(mymtd);
-#else
- add_mtd_device(mymtd);
-#endif
+ mtd_device_parse_register(mymtd, part_probe_types,
+ NULL, NULL, 0);
} else {
pr_err("Failed to register MTD device for flash\n");
}
diff --git a/arch/mips/cavium-octeon/oct_ilm.c b/arch/mips/cavium-octeon/oct_ilm.c
new file mode 100644
index 00000000000..71b213dbb62
--- /dev/null
+++ b/arch/mips/cavium-octeon/oct_ilm.c
@@ -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");
diff --git a/arch/mips/cavium-octeon/octeon-irq.c b/arch/mips/cavium-octeon/octeon-irq.c
index fc72984a5da..1b82ac6921e 100644
--- a/arch/mips/cavium-octeon/octeon-irq.c
+++ b/arch/mips/cavium-octeon/octeon-irq.c
@@ -3,21 +3,94 @@
* License. See the file "COPYING" in the main directory of this archive
* for more details.
*
- * Copyright (C) 2004-2008 Cavium Networks
+ * Copyright (C) 2004-2012 Cavium, Inc.
*/
-#include <linux/irq.h>
+
#include <linux/interrupt.h>
-#include <linux/hardirq.h>
+#include <linux/irqdomain.h>
+#include <linux/bitops.h>
+#include <linux/percpu.h>
+#include <linux/slab.h>
+#include <linux/irq.h>
+#include <linux/smp.h>
+#include <linux/of.h>
#include <asm/octeon/octeon.h>
+#include <asm/octeon/cvmx-ciu2-defs.h>
+
+static DEFINE_PER_CPU(unsigned long, octeon_irq_ciu0_en_mirror);
+static DEFINE_PER_CPU(unsigned long, octeon_irq_ciu1_en_mirror);
+static DEFINE_PER_CPU(raw_spinlock_t, octeon_irq_ciu_spinlock);
+
+static __read_mostly u8 octeon_irq_ciu_to_irq[8][64];
+
+union octeon_ciu_chip_data {
+ void *p;
+ unsigned long l;
+ struct {
+ unsigned long line:6;
+ unsigned long bit:6;
+ unsigned long gpio_line:6;
+ } s;
+};
+
+struct octeon_core_chip_data {
+ struct mutex core_irq_mutex;
+ bool current_en;
+ bool desired_en;
+ u8 bit;
+};
+
+#define MIPS_CORE_IRQ_LINES 8
-DEFINE_RWLOCK(octeon_irq_ciu0_rwlock);
-DEFINE_RWLOCK(octeon_irq_ciu1_rwlock);
-DEFINE_SPINLOCK(octeon_irq_msi_lock);
+static struct octeon_core_chip_data octeon_irq_core_chip_data[MIPS_CORE_IRQ_LINES];
-static void octeon_irq_core_ack(unsigned int irq)
+static void octeon_irq_set_ciu_mapping(int irq, int line, int bit, int gpio_line,
+ struct irq_chip *chip,
+ irq_flow_handler_t handler)
{
- unsigned int bit = irq - OCTEON_IRQ_SW0;
+ union octeon_ciu_chip_data cd;
+
+ irq_set_chip_and_handler(irq, chip, handler);
+
+ cd.l = 0;
+ cd.s.line = line;
+ cd.s.bit = bit;
+ cd.s.gpio_line = gpio_line;
+
+ irq_set_chip_data(irq, cd.p);
+ octeon_irq_ciu_to_irq[line][bit] = irq;
+}
+
+static void octeon_irq_force_ciu_mapping(struct irq_domain *domain,
+ int irq, int line, int bit)
+{
+ irq_domain_associate(domain, irq, line << 6 | bit);
+}
+
+static int octeon_coreid_for_cpu(int cpu)
+{
+#ifdef CONFIG_SMP
+ return cpu_logical_map(cpu);
+#else
+ return cvmx_get_core_num();
+#endif
+}
+
+static int octeon_cpu_for_coreid(int coreid)
+{
+#ifdef CONFIG_SMP
+ return cpu_number_map(coreid);
+#else
+ return smp_processor_id();
+#endif
+}
+
+static void octeon_irq_core_ack(struct irq_data *data)
+{
+ struct octeon_core_chip_data *cd = irq_data_get_irq_chip_data(data);
+ unsigned int bit = cd->bit;
+
/*
* We don't need to disable IRQs to make these atomic since
* they are already disabled earlier in the low level
@@ -29,442 +102,1662 @@ static void octeon_irq_core_ack(unsigned int irq)
clear_c0_cause(0x100 << bit);
}
-static void octeon_irq_core_eoi(unsigned int irq)
+static void octeon_irq_core_eoi(struct irq_data *data)
{
- irq_desc_t *desc = irq_desc + irq;
- unsigned int bit = irq - OCTEON_IRQ_SW0;
- /*
- * If an IRQ is being processed while we are disabling it the
- * handler will attempt to unmask the interrupt after it has
- * been disabled.
- */
- if (desc->status & IRQ_DISABLED)
- return;
-
- /* There is a race here. We should fix it. */
+ struct octeon_core_chip_data *cd = irq_data_get_irq_chip_data(data);
/*
* We don't need to disable IRQs to make these atomic since
* they are already disabled earlier in the low level
* interrupt code.
*/
- set_c0_status(0x100 << bit);
+ set_c0_status(0x100 << cd->bit);
}
-static void octeon_irq_core_enable(unsigned int irq)
+static void octeon_irq_core_set_enable_local(void *arg)
{
- unsigned long flags;
- unsigned int bit = irq - OCTEON_IRQ_SW0;
+ struct irq_data *data = arg;
+ struct octeon_core_chip_data *cd = irq_data_get_irq_chip_data(data);
+ unsigned int mask = 0x100 << cd->bit;
/*
- * We need to disable interrupts to make sure our updates are
- * atomic.
+ * Interrupts are already disabled, so these are atomic.
*/
- local_irq_save(flags);
- set_c0_status(0x100 << bit);
- local_irq_restore(flags);
+ if (cd->desired_en)
+ set_c0_status(mask);
+ else
+ clear_c0_status(mask);
+
}
-static void octeon_irq_core_disable_local(unsigned int irq)
+static void octeon_irq_core_disable(struct irq_data *data)
{
- unsigned long flags;
- unsigned int bit = irq - OCTEON_IRQ_SW0;
- /*
- * We need to disable interrupts to make sure our updates are
- * atomic.
- */
- local_irq_save(flags);
- clear_c0_status(0x100 << bit);
- local_irq_restore(flags);
+ struct octeon_core_chip_data *cd = irq_data_get_irq_chip_data(data);
+ cd->desired_en = false;
}
-static void octeon_irq_core_disable(unsigned int irq)
+static void octeon_irq_core_enable(struct irq_data *data)
{
-#ifdef CONFIG_SMP
- on_each_cpu((void (*)(void *)) octeon_irq_core_disable_local,
- (void *) (long) irq, 1);
-#else
- octeon_irq_core_disable_local(irq);
-#endif
+ struct octeon_core_chip_data *cd = irq_data_get_irq_chip_data(data);
+ cd->desired_en = true;
+}
+
+static void octeon_irq_core_bus_lock(struct irq_data *data)
+{
+ struct octeon_core_chip_data *cd = irq_data_get_irq_chip_data(data);
+
+ mutex_lock(&cd->core_irq_mutex);
+}
+
+static void octeon_irq_core_bus_sync_unlock(struct irq_data *data)
+{
+ struct octeon_core_chip_data *cd = irq_data_get_irq_chip_data(data);
+
+ if (cd->desired_en != cd->current_en) {
+ on_each_cpu(octeon_irq_core_set_enable_local, data, 1);
+
+ cd->current_en = cd->desired_en;
+ }
+
+ mutex_unlock(&cd->core_irq_mutex);
}
static struct irq_chip octeon_irq_chip_core = {
.name = "Core",
- .enable = octeon_irq_core_enable,
- .disable = octeon_irq_core_disable,
- .ack = octeon_irq_core_ack,
- .eoi = octeon_irq_core_eoi,
+ .irq_enable = octeon_irq_core_enable,
+ .irq_disable = octeon_irq_core_disable,
+ .irq_ack = octeon_irq_core_ack,
+ .irq_eoi = octeon_irq_core_eoi,
+ .irq_bus_lock = octeon_irq_core_bus_lock,
+ .irq_bus_sync_unlock = octeon_irq_core_bus_sync_unlock,
+
+ .irq_cpu_online = octeon_irq_core_eoi,
+ .irq_cpu_offline = octeon_irq_core_ack,
+ .flags = IRQCHIP_ONOFFLINE_ENABLED,
};
+static void __init octeon_irq_init_core(void)
+{
+ int i;
+ int irq;
+ struct octeon_core_chip_data *cd;
+
+ for (i = 0; i < MIPS_CORE_IRQ_LINES; i++) {
+ cd = &octeon_irq_core_chip_data[i];
+ cd->current_en = false;
+ cd->desired_en = false;
+ cd->bit = i;
+ mutex_init(&cd->core_irq_mutex);
+
+ irq = OCTEON_IRQ_SW0 + i;
+ irq_set_chip_data(irq, cd);
+ irq_set_chip_and_handler(irq, &octeon_irq_chip_core,
+ handle_percpu_irq);
+ }
+}
-static void octeon_irq_ciu0_ack(unsigned int irq)
+static int next_cpu_for_irq(struct irq_data *data)
{
- /*
- * In order to avoid any locking accessing the CIU, we
- * acknowledge CIU interrupts by disabling all of them. This
- * way we can use a per core register and avoid any out of
- * core locking requirements. This has the side affect that
- * CIU interrupts can't be processed recursively.
- *
- * We don't need to disable IRQs to make these atomic since
- * they are already disabled earlier in the low level
- * interrupt code.
- */
- clear_c0_status(0x100 << 2);
+
+#ifdef CONFIG_SMP
+ int cpu;
+ int weight = cpumask_weight(data->affinity);
+
+ if (weight > 1) {
+ cpu = smp_processor_id();
+ for (;;) {
+ cpu = cpumask_next(cpu, data->affinity);
+ if (cpu >= nr_cpu_ids) {
+ cpu = -1;
+ continue;
+ } else if (cpumask_test_cpu(cpu, cpu_online_mask)) {
+ break;
+ }
+ }
+ } else if (weight == 1) {
+ cpu = cpumask_first(data->affinity);
+ } else {
+ cpu = smp_processor_id();
+ }
+ return cpu;
+#else
+ return smp_processor_id();
+#endif
}
-static void octeon_irq_ciu0_eoi(unsigned int irq)
+static void octeon_irq_ciu_enable(struct irq_data *data)
{
- /*
- * Enable all CIU interrupts again. We don't need to disable
- * IRQs to make these atomic since they are already disabled
- * earlier in the low level interrupt code.
- */
- set_c0_status(0x100 << 2);
+ int cpu = next_cpu_for_irq(data);
+ int coreid = octeon_coreid_for_cpu(cpu);
+ unsigned long *pen;
+ unsigned long flags;
+ union octeon_ciu_chip_data cd;
+ raw_spinlock_t *lock = &per_cpu(octeon_irq_ciu_spinlock, cpu);
+
+ cd.p = irq_data_get_irq_chip_data(data);
+
+ raw_spin_lock_irqsave(lock, flags);
+ if (cd.s.line == 0) {
+ pen = &per_cpu(octeon_irq_ciu0_en_mirror, cpu);
+ __set_bit(cd.s.bit, pen);
+ /*
+ * Must be visible to octeon_irq_ip{2,3}_ciu() before
+ * enabling the irq.
+ */
+ wmb();
+ cvmx_write_csr(CVMX_CIU_INTX_EN0(coreid * 2), *pen);
+ } else {
+ pen = &per_cpu(octeon_irq_ciu1_en_mirror, cpu);
+ __set_bit(cd.s.bit, pen);
+ /*
+ * Must be visible to octeon_irq_ip{2,3}_ciu() before
+ * enabling the irq.
+ */
+ wmb();
+ cvmx_write_csr(CVMX_CIU_INTX_EN1(coreid * 2 + 1), *pen);
+ }
+ raw_spin_unlock_irqrestore(lock, flags);
}
-static void octeon_irq_ciu0_enable(unsigned int irq)
+static void octeon_irq_ciu_enable_local(struct irq_data *data)
{
- int coreid = cvmx_get_core_num();
+ unsigned long *pen;
unsigned long flags;
- uint64_t en0;
- int bit = irq - OCTEON_IRQ_WORKQ0; /* Bit 0-63 of EN0 */
+ union octeon_ciu_chip_data cd;
+ raw_spinlock_t *lock = &__get_cpu_var(octeon_irq_ciu_spinlock);
- /*
- * A read lock is used here to make sure only one core is ever
- * updating the CIU enable bits at a time. During an enable
- * the cores don't interfere with each other. During a disable
- * the write lock stops any enables that might cause a
- * problem.
- */
- read_lock_irqsave(&octeon_irq_ciu0_rwlock, flags);
- en0 = cvmx_read_csr(CVMX_CIU_INTX_EN0(coreid * 2));
- en0 |= 1ull << bit;
- cvmx_write_csr(CVMX_CIU_INTX_EN0(coreid * 2), en0);
- cvmx_read_csr(CVMX_CIU_INTX_EN0(coreid * 2));
- read_unlock_irqrestore(&octeon_irq_ciu0_rwlock, flags);
+ cd.p = irq_data_get_irq_chip_data(data);
+
+ raw_spin_lock_irqsave(lock, flags);
+ if (cd.s.line == 0) {
+ pen = &__get_cpu_var(octeon_irq_ciu0_en_mirror);
+ __set_bit(cd.s.bit, pen);
+ /*
+ * Must be visible to octeon_irq_ip{2,3}_ciu() before
+ * enabling the irq.
+ */
+ wmb();
+ cvmx_write_csr(CVMX_CIU_INTX_EN0(cvmx_get_core_num() * 2), *pen);
+ } else {
+ pen = &__get_cpu_var(octeon_irq_ciu1_en_mirror);
+ __set_bit(cd.s.bit, pen);
+ /*
+ * Must be visible to octeon_irq_ip{2,3}_ciu() before
+ * enabling the irq.
+ */
+ wmb();
+ cvmx_write_csr(CVMX_CIU_INTX_EN1(cvmx_get_core_num() * 2 + 1), *pen);
+ }
+ raw_spin_unlock_irqrestore(lock, flags);
}
-static void octeon_irq_ciu0_disable(unsigned int irq)
+static void octeon_irq_ciu_disable_local(struct irq_data *data)
{
- int bit = irq - OCTEON_IRQ_WORKQ0; /* Bit 0-63 of EN0 */
+ unsigned long *pen;
unsigned long flags;
- uint64_t en0;
-#ifdef CONFIG_SMP
+ union octeon_ciu_chip_data cd;
+ raw_spinlock_t *lock = &__get_cpu_var(octeon_irq_ciu_spinlock);
+
+ cd.p = irq_data_get_irq_chip_data(data);
+
+ raw_spin_lock_irqsave(lock, flags);
+ if (cd.s.line == 0) {
+ pen = &__get_cpu_var(octeon_irq_ciu0_en_mirror);
+ __clear_bit(cd.s.bit, pen);
+ /*
+ * Must be visible to octeon_irq_ip{2,3}_ciu() before
+ * enabling the irq.
+ */
+ wmb();
+ cvmx_write_csr(CVMX_CIU_INTX_EN0(cvmx_get_core_num() * 2), *pen);
+ } else {
+ pen = &__get_cpu_var(octeon_irq_ciu1_en_mirror);
+ __clear_bit(cd.s.bit, pen);
+ /*
+ * Must be visible to octeon_irq_ip{2,3}_ciu() before
+ * enabling the irq.
+ */
+ wmb();
+ cvmx_write_csr(CVMX_CIU_INTX_EN1(cvmx_get_core_num() * 2 + 1), *pen);
+ }
+ raw_spin_unlock_irqrestore(lock, flags);
+}
+
+static void octeon_irq_ciu_disable_all(struct irq_data *data)
+{
+ unsigned long flags;
+ unsigned long *pen;
+ int cpu;
+ union octeon_ciu_chip_data cd;
+ raw_spinlock_t *lock;
+
+ cd.p = irq_data_get_irq_chip_data(data);
+
+ for_each_online_cpu(cpu) {
+ int coreid = octeon_coreid_for_cpu(cpu);
+ lock = &per_cpu(octeon_irq_ciu_spinlock, cpu);
+ if (cd.s.line == 0)
+ pen = &per_cpu(octeon_irq_ciu0_en_mirror, cpu);
+ else
+ pen = &per_cpu(octeon_irq_ciu1_en_mirror, cpu);
+
+ raw_spin_lock_irqsave(lock, flags);
+ __clear_bit(cd.s.bit, pen);
+ /*
+ * Must be visible to octeon_irq_ip{2,3}_ciu() before
+ * enabling the irq.
+ */
+ wmb();
+ if (cd.s.line == 0)
+ cvmx_write_csr(CVMX_CIU_INTX_EN0(coreid * 2), *pen);
+ else
+ cvmx_write_csr(CVMX_CIU_INTX_EN1(coreid * 2 + 1), *pen);
+ raw_spin_unlock_irqrestore(lock, flags);
+ }
+}
+
+static void octeon_irq_ciu_enable_all(struct irq_data *data)
+{
+ unsigned long flags;
+ unsigned long *pen;
int cpu;
- write_lock_irqsave(&octeon_irq_ciu0_rwlock, flags);
+ union octeon_ciu_chip_data cd;
+ raw_spinlock_t *lock;
+
+ cd.p = irq_data_get_irq_chip_data(data);
+
for_each_online_cpu(cpu) {
- int coreid = cpu_logical_map(cpu);
- en0 = cvmx_read_csr(CVMX_CIU_INTX_EN0(coreid * 2));
- en0 &= ~(1ull << bit);
- cvmx_write_csr(CVMX_CIU_INTX_EN0(coreid * 2), en0);
+ int coreid = octeon_coreid_for_cpu(cpu);
+ lock = &per_cpu(octeon_irq_ciu_spinlock, cpu);
+ if (cd.s.line == 0)
+ pen = &per_cpu(octeon_irq_ciu0_en_mirror, cpu);
+ else
+ pen = &per_cpu(octeon_irq_ciu1_en_mirror, cpu);
+
+ raw_spin_lock_irqsave(lock, flags);
+ __set_bit(cd.s.bit, pen);
+ /*
+ * Must be visible to octeon_irq_ip{2,3}_ciu() before
+ * enabling the irq.
+ */
+ wmb();
+ if (cd.s.line == 0)
+ cvmx_write_csr(CVMX_CIU_INTX_EN0(coreid * 2), *pen);
+ else
+ cvmx_write_csr(CVMX_CIU_INTX_EN1(coreid * 2 + 1), *pen);
+ raw_spin_unlock_irqrestore(lock, flags);
}
+}
+
+/*
+ * Enable the irq on the next core in the affinity set for chips that
+ * have the EN*_W1{S,C} registers.
+ */
+static void octeon_irq_ciu_enable_v2(struct irq_data *data)
+{
+ u64 mask;
+ int cpu = next_cpu_for_irq(data);
+ union octeon_ciu_chip_data cd;
+
+ cd.p = irq_data_get_irq_chip_data(data);
+ mask = 1ull << (cd.s.bit);
+
/*
- * We need to do a read after the last update to make sure all
- * of them are done.
+ * Called under the desc lock, so these should never get out
+ * of sync.
*/
- cvmx_read_csr(CVMX_CIU_INTX_EN0(cvmx_get_core_num() * 2));
- write_unlock_irqrestore(&octeon_irq_ciu0_rwlock, flags);
-#else
- int coreid = cvmx_get_core_num();
- local_irq_save(flags);
- en0 = cvmx_read_csr(CVMX_CIU_INTX_EN0(coreid * 2));
- en0 &= ~(1ull << bit);
- cvmx_write_csr(CVMX_CIU_INTX_EN0(coreid * 2), en0);
- cvmx_read_csr(CVMX_CIU_INTX_EN0(coreid * 2));
- local_irq_restore(flags);
-#endif
+ if (cd.s.line == 0) {
+ int index = octeon_coreid_for_cpu(cpu) * 2;
+ set_bit(cd.s.bit, &per_cpu(octeon_irq_ciu0_en_mirror, cpu));
+ cvmx_write_csr(CVMX_CIU_INTX_EN0_W1S(index), mask);
+ } else {
+ int index = octeon_coreid_for_cpu(cpu) * 2 + 1;
+ set_bit(cd.s.bit, &per_cpu(octeon_irq_ciu1_en_mirror, cpu));
+ cvmx_write_csr(CVMX_CIU_INTX_EN1_W1S(index), mask);
+ }
+}
+
+/*
+ * Enable the irq on the current CPU for chips that
+ * have the EN*_W1{S,C} registers.
+ */
+static void octeon_irq_ciu_enable_local_v2(struct irq_data *data)
+{
+ u64 mask;
+ union octeon_ciu_chip_data cd;
+
+ cd.p = irq_data_get_irq_chip_data(data);
+ mask = 1ull << (cd.s.bit);
+
+ if (cd.s.line == 0) {
+ int index = cvmx_get_core_num() * 2;
+ set_bit(cd.s.bit, &__get_cpu_var(octeon_irq_ciu0_en_mirror));
+ cvmx_write_csr(CVMX_CIU_INTX_EN0_W1S(index), mask);
+ } else {
+ int index = cvmx_get_core_num() * 2 + 1;
+ set_bit(cd.s.bit, &__get_cpu_var(octeon_irq_ciu1_en_mirror));
+ cvmx_write_csr(CVMX_CIU_INTX_EN1_W1S(index), mask);
+ }
+}
+
+static void octeon_irq_ciu_disable_local_v2(struct irq_data *data)
+{
+ u64 mask;
+ union octeon_ciu_chip_data cd;
+
+ cd.p = irq_data_get_irq_chip_data(data);
+ mask = 1ull << (cd.s.bit);
+
+ if (cd.s.line == 0) {
+ int index = cvmx_get_core_num() * 2;
+ clear_bit(cd.s.bit, &__get_cpu_var(octeon_irq_ciu0_en_mirror));
+ cvmx_write_csr(CVMX_CIU_INTX_EN0_W1C(index), mask);
+ } else {
+ int index = cvmx_get_core_num() * 2 + 1;
+ clear_bit(cd.s.bit, &__get_cpu_var(octeon_irq_ciu1_en_mirror));
+ cvmx_write_csr(CVMX_CIU_INTX_EN1_W1C(index), mask);
+ }
+}
+
+/*
+ * Write to the W1C bit in CVMX_CIU_INTX_SUM0 to clear the irq.
+ */
+static void octeon_irq_ciu_ack(struct irq_data *data)
+{
+ u64 mask;
+ union octeon_ciu_chip_data cd;
+
+ cd.p = irq_data_get_irq_chip_data(data);
+ mask = 1ull << (cd.s.bit);
+
+ if (cd.s.line == 0) {
+ int index = cvmx_get_core_num() * 2;
+ cvmx_write_csr(CVMX_CIU_INTX_SUM0(index), mask);
+ } else {
+ cvmx_write_csr(CVMX_CIU_INT_SUM1, mask);
+ }
+}
+
+/*
+ * Disable the irq on the all cores for chips that have the EN*_W1{S,C}
+ * registers.
+ */
+static void octeon_irq_ciu_disable_all_v2(struct irq_data *data)
+{
+ int cpu;
+ u64 mask;
+ union octeon_ciu_chip_data cd;
+
+ cd.p = irq_data_get_irq_chip_data(data);
+ mask = 1ull << (cd.s.bit);
+
+ if (cd.s.line == 0) {
+ for_each_online_cpu(cpu) {
+ int index = octeon_coreid_for_cpu(cpu) * 2;
+ clear_bit(cd.s.bit, &per_cpu(octeon_irq_ciu0_en_mirror, cpu));
+ cvmx_write_csr(CVMX_CIU_INTX_EN0_W1C(index), mask);
+ }
+ } else {
+ for_each_online_cpu(cpu) {
+ int index = octeon_coreid_for_cpu(cpu) * 2 + 1;
+ clear_bit(cd.s.bit, &per_cpu(octeon_irq_ciu1_en_mirror, cpu));
+ cvmx_write_csr(CVMX_CIU_INTX_EN1_W1C(index), mask);
+ }
+ }
+}
+
+/*
+ * Enable the irq on the all cores for chips that have the EN*_W1{S,C}
+ * registers.
+ */
+static void octeon_irq_ciu_enable_all_v2(struct irq_data *data)
+{
+ int cpu;
+ u64 mask;
+ union octeon_ciu_chip_data cd;
+
+ cd.p = irq_data_get_irq_chip_data(data);
+ mask = 1ull << (cd.s.bit);
+
+ if (cd.s.line == 0) {
+ for_each_online_cpu(cpu) {
+ int index = octeon_coreid_for_cpu(cpu) * 2;
+ set_bit(cd.s.bit, &per_cpu(octeon_irq_ciu0_en_mirror, cpu));
+ cvmx_write_csr(CVMX_CIU_INTX_EN0_W1S(index), mask);
+ }
+ } else {
+ for_each_online_cpu(cpu) {
+ int index = octeon_coreid_for_cpu(cpu) * 2 + 1;
+ set_bit(cd.s.bit, &per_cpu(octeon_irq_ciu1_en_mirror, cpu));
+ cvmx_write_csr(CVMX_CIU_INTX_EN1_W1S(index), mask);
+ }
+ }
+}
+
+static void octeon_irq_gpio_setup(struct irq_data *data)
+{
+ union cvmx_gpio_bit_cfgx cfg;
+ union octeon_ciu_chip_data cd;
+ u32 t = irqd_get_trigger_type(data);
+
+ cd.p = irq_data_get_irq_chip_data(data);
+
+ cfg.u64 = 0;
+ cfg.s.int_en = 1;
+ cfg.s.int_type = (t & IRQ_TYPE_EDGE_BOTH) != 0;
+ cfg.s.rx_xor = (t & (IRQ_TYPE_LEVEL_LOW | IRQ_TYPE_EDGE_FALLING)) != 0;
+
+ /* 140 nS glitch filter*/
+ cfg.s.fil_cnt = 7;
+ cfg.s.fil_sel = 3;
+
+ cvmx_write_csr(CVMX_GPIO_BIT_CFGX(cd.s.gpio_line), cfg.u64);
+}
+
+static void octeon_irq_ciu_enable_gpio_v2(struct irq_data *data)
+{
+ octeon_irq_gpio_setup(data);
+ octeon_irq_ciu_enable_v2(data);
+}
+
+static void octeon_irq_ciu_enable_gpio(struct irq_data *data)
+{
+ octeon_irq_gpio_setup(data);
+ octeon_irq_ciu_enable(data);
+}
+
+static int octeon_irq_ciu_gpio_set_type(struct irq_data *data, unsigned int t)
+{
+ irqd_set_trigger_type(data, t);
+ octeon_irq_gpio_setup(data);
+
+ return IRQ_SET_MASK_OK;
+}
+
+static void octeon_irq_ciu_disable_gpio_v2(struct irq_data *data)
+{
+ union octeon_ciu_chip_data cd;
+
+ cd.p = irq_data_get_irq_chip_data(data);
+ cvmx_write_csr(CVMX_GPIO_BIT_CFGX(cd.s.gpio_line), 0);
+
+ octeon_irq_ciu_disable_all_v2(data);
+}
+
+static void octeon_irq_ciu_disable_gpio(struct irq_data *data)
+{
+ union octeon_ciu_chip_data cd;
+
+ cd.p = irq_data_get_irq_chip_data(data);
+ cvmx_write_csr(CVMX_GPIO_BIT_CFGX(cd.s.gpio_line), 0);
+
+ octeon_irq_ciu_disable_all(data);
+}
+
+static void octeon_irq_ciu_gpio_ack(struct irq_data *data)
+{
+ union octeon_ciu_chip_data cd;
+ u64 mask;
+
+ cd.p = irq_data_get_irq_chip_data(data);
+ mask = 1ull << (cd.s.gpio_line);
+
+ cvmx_write_csr(CVMX_GPIO_INT_CLR, mask);
+}
+
+static void octeon_irq_handle_gpio(unsigned int irq, struct irq_desc *desc)
+{
+ if (irq_get_trigger_type(irq) & IRQ_TYPE_EDGE_BOTH)
+ handle_edge_irq(irq, desc);
+ else
+ handle_level_irq(irq, desc);
}
#ifdef CONFIG_SMP
-static void octeon_irq_ciu0_set_affinity(unsigned int irq, const struct cpumask *dest)
+
+static void octeon_irq_cpu_offline_ciu(struct irq_data *data)
+{
+ int cpu = smp_processor_id();
+ cpumask_t new_affinity;
+
+ if (!cpumask_test_cpu(cpu, data->affinity))
+ return;
+
+ if (cpumask_weight(data->affinity) > 1) {
+ /*
+ * It has multi CPU affinity, just remove this CPU
+ * from the affinity set.
+ */
+ cpumask_copy(&new_affinity, data->affinity);
+ cpumask_clear_cpu(cpu, &new_affinity);
+ } else {
+ /* Otherwise, put it on lowest numbered online CPU. */
+ cpumask_clear(&new_affinity);
+ cpumask_set_cpu(cpumask_first(cpu_online_mask), &new_affinity);
+ }
+ irq_set_affinity_locked(data, &new_affinity, false);
+}
+
+static int octeon_irq_ciu_set_affinity(struct irq_data *data,
+ const struct cpumask *dest, bool force)
{
int cpu;
- int bit = irq - OCTEON_IRQ_WORKQ0; /* Bit 0-63 of EN0 */
+ bool enable_one = !irqd_irq_disabled(data) && !irqd_irq_masked(data);
+ unsigned long flags;
+ union octeon_ciu_chip_data cd;
+ unsigned long *pen;
+ raw_spinlock_t *lock;
+
+ cd.p = irq_data_get_irq_chip_data(data);
+
+ /*
+ * For non-v2 CIU, we will allow only single CPU affinity.
+ * This removes the need to do locking in the .ack/.eoi
+ * functions.
+ */
+ if (cpumask_weight(dest) != 1)
+ return -EINVAL;
+
+ if (!enable_one)
+ return 0;
+
- write_lock(&octeon_irq_ciu0_rwlock);
for_each_online_cpu(cpu) {
- int coreid = cpu_logical_map(cpu);
- uint64_t en0 =
- cvmx_read_csr(CVMX_CIU_INTX_EN0(coreid * 2));
- if (cpumask_test_cpu(cpu, dest))
- en0 |= 1ull << bit;
+ int coreid = octeon_coreid_for_cpu(cpu);
+
+ lock = &per_cpu(octeon_irq_ciu_spinlock, cpu);
+ raw_spin_lock_irqsave(lock, flags);
+
+ if (cd.s.line == 0)
+ pen = &per_cpu(octeon_irq_ciu0_en_mirror, cpu);
+ else
+ pen = &per_cpu(octeon_irq_ciu1_en_mirror, cpu);
+
+ if (cpumask_test_cpu(cpu, dest) && enable_one) {
+ enable_one = 0;
+ __set_bit(cd.s.bit, pen);
+ } else {
+ __clear_bit(cd.s.bit, pen);
+ }
+ /*
+ * Must be visible to octeon_irq_ip{2,3}_ciu() before
+ * enabling the irq.
+ */
+ wmb();
+
+ if (cd.s.line == 0)
+ cvmx_write_csr(CVMX_CIU_INTX_EN0(coreid * 2), *pen);
else
- en0 &= ~(1ull << bit);
- cvmx_write_csr(CVMX_CIU_INTX_EN0(coreid * 2), en0);
+ cvmx_write_csr(CVMX_CIU_INTX_EN1(coreid * 2 + 1), *pen);
+
+ raw_spin_unlock_irqrestore(lock, flags);
}
- /*
- * We need to do a read after the last update to make sure all
- * of them are done.
- */
- cvmx_read_csr(CVMX_CIU_INTX_EN0(cvmx_get_core_num() * 2));
- write_unlock(&octeon_irq_ciu0_rwlock);
+ return 0;
+}
+
+/*
+ * Set affinity for the irq for chips that have the EN*_W1{S,C}
+ * registers.
+ */
+static int octeon_irq_ciu_set_affinity_v2(struct irq_data *data,
+ const struct cpumask *dest,
+ bool force)
+{
+ int cpu;
+ bool enable_one = !irqd_irq_disabled(data) && !irqd_irq_masked(data);
+ u64 mask;
+ union octeon_ciu_chip_data cd;
+
+ if (!enable_one)
+ return 0;
+
+ cd.p = irq_data_get_irq_chip_data(data);
+ mask = 1ull << cd.s.bit;
+
+ if (cd.s.line == 0) {
+ for_each_online_cpu(cpu) {
+ unsigned long *pen = &per_cpu(octeon_irq_ciu0_en_mirror, cpu);
+ int index = octeon_coreid_for_cpu(cpu) * 2;
+ if (cpumask_test_cpu(cpu, dest) && enable_one) {
+ enable_one = false;
+ set_bit(cd.s.bit, pen);
+ cvmx_write_csr(CVMX_CIU_INTX_EN0_W1S(index), mask);
+ } else {
+ clear_bit(cd.s.bit, pen);
+ cvmx_write_csr(CVMX_CIU_INTX_EN0_W1C(index), mask);
+ }
+ }
+ } else {
+ for_each_online_cpu(cpu) {
+ unsigned long *pen = &per_cpu(octeon_irq_ciu1_en_mirror, cpu);
+ int index = octeon_coreid_for_cpu(cpu) * 2 + 1;
+ if (cpumask_test_cpu(cpu, dest) && enable_one) {
+ enable_one = false;
+ set_bit(cd.s.bit, pen);
+ cvmx_write_csr(CVMX_CIU_INTX_EN1_W1S(index), mask);
+ } else {
+ clear_bit(cd.s.bit, pen);
+ cvmx_write_csr(CVMX_CIU_INTX_EN1_W1C(index), mask);
+ }
+ }
+ }
+ return 0;
}
#endif
-static struct irq_chip octeon_irq_chip_ciu0 = {
- .name = "CIU0",
- .enable = octeon_irq_ciu0_enable,
- .disable = octeon_irq_ciu0_disable,
- .ack = octeon_irq_ciu0_ack,
- .eoi = octeon_irq_ciu0_eoi,
+/*
+ * Newer octeon chips have support for lockless CIU operation.
+ */
+static struct irq_chip octeon_irq_chip_ciu_v2 = {
+ .name = "CIU",
+ .irq_enable = octeon_irq_ciu_enable_v2,
+ .irq_disable = octeon_irq_ciu_disable_all_v2,
+ .irq_ack = octeon_irq_ciu_ack,
+ .irq_mask = octeon_irq_ciu_disable_local_v2,
+ .irq_unmask = octeon_irq_ciu_enable_v2,
+#ifdef CONFIG_SMP
+ .irq_set_affinity = octeon_irq_ciu_set_affinity_v2,
+ .irq_cpu_offline = octeon_irq_cpu_offline_ciu,
+#endif
+};
+
+static struct irq_chip octeon_irq_chip_ciu = {
+ .name = "CIU",
+ .irq_enable = octeon_irq_ciu_enable,
+ .irq_disable = octeon_irq_ciu_disable_all,
+ .irq_ack = octeon_irq_ciu_ack,
+ .irq_mask = octeon_irq_ciu_disable_local,
+ .irq_unmask = octeon_irq_ciu_enable,
+#ifdef CONFIG_SMP
+ .irq_set_affinity = octeon_irq_ciu_set_affinity,
+ .irq_cpu_offline = octeon_irq_cpu_offline_ciu,
+#endif
+};
+
+/* The mbox versions don't do any affinity or round-robin. */
+static struct irq_chip octeon_irq_chip_ciu_mbox_v2 = {
+ .name = "CIU-M",
+ .irq_enable = octeon_irq_ciu_enable_all_v2,
+ .irq_disable = octeon_irq_ciu_disable_all_v2,
+ .irq_ack = octeon_irq_ciu_disable_local_v2,
+ .irq_eoi = octeon_irq_ciu_enable_local_v2,
+
+ .irq_cpu_online = octeon_irq_ciu_enable_local_v2,
+ .irq_cpu_offline = octeon_irq_ciu_disable_local_v2,
+ .flags = IRQCHIP_ONOFFLINE_ENABLED,
+};
+
+static struct irq_chip octeon_irq_chip_ciu_mbox = {
+ .name = "CIU-M",
+ .irq_enable = octeon_irq_ciu_enable_all,
+ .irq_disable = octeon_irq_ciu_disable_all,
+ .irq_ack = octeon_irq_ciu_disable_local,
+ .irq_eoi = octeon_irq_ciu_enable_local,
+
+ .irq_cpu_online = octeon_irq_ciu_enable_local,
+ .irq_cpu_offline = octeon_irq_ciu_disable_local,
+ .flags = IRQCHIP_ONOFFLINE_ENABLED,
+};
+
+static struct irq_chip octeon_irq_chip_ciu_gpio_v2 = {
+ .name = "CIU-GPIO",
+ .irq_enable = octeon_irq_ciu_enable_gpio_v2,
+ .irq_disable = octeon_irq_ciu_disable_gpio_v2,
+ .irq_ack = octeon_irq_ciu_gpio_ack,
+ .irq_mask = octeon_irq_ciu_disable_local_v2,
+ .irq_unmask = octeon_irq_ciu_enable_v2,
+ .irq_set_type = octeon_irq_ciu_gpio_set_type,
#ifdef CONFIG_SMP
- .set_affinity = octeon_irq_ciu0_set_affinity,
+ .irq_set_affinity = octeon_irq_ciu_set_affinity_v2,
#endif
+ .flags = IRQCHIP_SET_TYPE_MASKED,
};
+static struct irq_chip octeon_irq_chip_ciu_gpio = {
+ .name = "CIU-GPIO",
+ .irq_enable = octeon_irq_ciu_enable_gpio,
+ .irq_disable = octeon_irq_ciu_disable_gpio,
+ .irq_mask = octeon_irq_ciu_disable_local,
+ .irq_unmask = octeon_irq_ciu_enable,
+ .irq_ack = octeon_irq_ciu_gpio_ack,
+ .irq_set_type = octeon_irq_ciu_gpio_set_type,
+#ifdef CONFIG_SMP
+ .irq_set_affinity = octeon_irq_ciu_set_affinity,
+#endif
+ .flags = IRQCHIP_SET_TYPE_MASKED,
+};
-static void octeon_irq_ciu1_ack(unsigned int irq)
+/*
+ * Watchdog interrupts are special. They are associated with a single
+ * core, so we hardwire the affinity to that core.
+ */
+static void octeon_irq_ciu_wd_enable(struct irq_data *data)
{
+ unsigned long flags;
+ unsigned long *pen;
+ int coreid = data->irq - OCTEON_IRQ_WDOG0; /* Bit 0-63 of EN1 */
+ int cpu = octeon_cpu_for_coreid(coreid);
+ raw_spinlock_t *lock = &per_cpu(octeon_irq_ciu_spinlock, cpu);
+
+ raw_spin_lock_irqsave(lock, flags);
+ pen = &per_cpu(octeon_irq_ciu1_en_mirror, cpu);
+ __set_bit(coreid, pen);
/*
- * In order to avoid any locking accessing the CIU, we
- * acknowledge CIU interrupts by disabling all of them. This
- * way we can use a per core register and avoid any out of
- * core locking requirements. This has the side affect that
- * CIU interrupts can't be processed recursively. We don't
- * need to disable IRQs to make these atomic since they are
- * already disabled earlier in the low level interrupt code.
+ * Must be visible to octeon_irq_ip{2,3}_ciu() before enabling
+ * the irq.
*/
- clear_c0_status(0x100 << 3);
+ wmb();
+ cvmx_write_csr(CVMX_CIU_INTX_EN1(coreid * 2 + 1), *pen);
+ raw_spin_unlock_irqrestore(lock, flags);
}
-static void octeon_irq_ciu1_eoi(unsigned int irq)
+/*
+ * Watchdog interrupts are special. They are associated with a single
+ * core, so we hardwire the affinity to that core.
+ */
+static void octeon_irq_ciu1_wd_enable_v2(struct irq_data *data)
{
+ int coreid = data->irq - OCTEON_IRQ_WDOG0;
+ int cpu = octeon_cpu_for_coreid(coreid);
+
+ set_bit(coreid, &per_cpu(octeon_irq_ciu1_en_mirror, cpu));
+ cvmx_write_csr(CVMX_CIU_INTX_EN1_W1S(coreid * 2 + 1), 1ull << coreid);
+}
+
+
+static struct irq_chip octeon_irq_chip_ciu_wd_v2 = {
+ .name = "CIU-W",
+ .irq_enable = octeon_irq_ciu1_wd_enable_v2,
+ .irq_disable = octeon_irq_ciu_disable_all_v2,
+ .irq_mask = octeon_irq_ciu_disable_local_v2,
+ .irq_unmask = octeon_irq_ciu_enable_local_v2,
+};
+
+static struct irq_chip octeon_irq_chip_ciu_wd = {
+ .name = "CIU-W",
+ .irq_enable = octeon_irq_ciu_wd_enable,
+ .irq_disable = octeon_irq_ciu_disable_all,
+ .irq_mask = octeon_irq_ciu_disable_local,
+ .irq_unmask = octeon_irq_ciu_enable_local,
+};
+
+static bool octeon_irq_ciu_is_edge(unsigned int line, unsigned int bit)
+{
+ bool edge = false;
+
+ if (line == 0)
+ switch (bit) {
+ case 48 ... 49: /* GMX DRP */
+ case 50: /* IPD_DRP */
+ case 52 ... 55: /* Timers */
+ case 58: /* MPI */
+ edge = true;
+ break;
+ default:
+ break;
+ }
+ else /* line == 1 */
+ switch (bit) {
+ case 47: /* PTP */
+ edge = true;
+ break;
+ default:
+ break;
+ }
+ return edge;
+}
+
+struct octeon_irq_gpio_domain_data {
+ unsigned int base_hwirq;
+};
+
+static int octeon_irq_gpio_xlat(struct irq_domain *d,
+ struct device_node *node,
+ const u32 *intspec,
+ unsigned int intsize,
+ unsigned long *out_hwirq,
+ unsigned int *out_type)
+{
+ unsigned int type;
+ unsigned int pin;
+ unsigned int trigger;
+
+ if (d->of_node != node)
+ return -EINVAL;
+
+ if (intsize < 2)
+ return -EINVAL;
+
+ pin = intspec[0];
+ if (pin >= 16)
+ return -EINVAL;
+
+ trigger = intspec[1];
+
+ switch (trigger) {
+ case 1:
+ type = IRQ_TYPE_EDGE_RISING;
+ break;
+ case 2:
+ type = IRQ_TYPE_EDGE_FALLING;
+ break;
+ case 4:
+ type = IRQ_TYPE_LEVEL_HIGH;
+ break;
+ case 8:
+ type = IRQ_TYPE_LEVEL_LOW;
+ break;
+ default:
+ pr_err("Error: (%s) Invalid irq trigger specification: %x\n",
+ node->name,
+ trigger);
+ type = IRQ_TYPE_LEVEL_LOW;
+ break;
+ }
+ *out_type = type;
+ *out_hwirq = pin;
+
+ return 0;
+}
+
+static int octeon_irq_ciu_xlat(struct irq_domain *d,
+ struct device_node *node,
+ const u32 *intspec,
+ unsigned int intsize,
+ unsigned long *out_hwirq,
+ unsigned int *out_type)
+{
+ unsigned int ciu, bit;
+
+ ciu = intspec[0];
+ bit = intspec[1];
+
+ if (ciu > 1 || bit > 63)
+ return -EINVAL;
+
+ *out_hwirq = (ciu << 6) | bit;
+ *out_type = 0;
+
+ return 0;
+}
+
+static struct irq_chip *octeon_irq_ciu_chip;
+static struct irq_chip *octeon_irq_gpio_chip;
+
+static bool octeon_irq_virq_in_range(unsigned int virq)
+{
+ /* We cannot let it overflow the mapping array. */
+ if (virq < (1ul << 8 * sizeof(octeon_irq_ciu_to_irq[0][0])))
+ return true;
+
+ WARN_ONCE(true, "virq out of range %u.\n", virq);
+ return false;
+}
+
+static int octeon_irq_ciu_map(struct irq_domain *d,
+ unsigned int virq, irq_hw_number_t hw)
+{
+ unsigned int line = hw >> 6;
+ unsigned int bit = hw & 63;
+
+ if (!octeon_irq_virq_in_range(virq))
+ return -EINVAL;
+
+ /* Don't map irq if it is reserved for GPIO. */
+ if (line == 0 && bit >= 16 && bit <32)
+ return 0;
+
+ if (line > 1 || octeon_irq_ciu_to_irq[line][bit] != 0)
+ return -EINVAL;
+
+ if (octeon_irq_ciu_is_edge(line, bit))
+ octeon_irq_set_ciu_mapping(virq, line, bit, 0,
+ octeon_irq_ciu_chip,
+ handle_edge_irq);
+ else
+ octeon_irq_set_ciu_mapping(virq, line, bit, 0,
+ octeon_irq_ciu_chip,
+ handle_level_irq);
+
+ return 0;
+}
+
+static int octeon_irq_gpio_map_common(struct irq_domain *d,
+ unsigned int virq, irq_hw_number_t hw,
+ int line_limit, struct irq_chip *chip)
+{
+ struct octeon_irq_gpio_domain_data *gpiod = d->host_data;
+ unsigned int line, bit;
+
+ if (!octeon_irq_virq_in_range(virq))
+ return -EINVAL;
+
+ line = (hw + gpiod->base_hwirq) >> 6;
+ bit = (hw + gpiod->base_hwirq) & 63;
+ if (line > line_limit || octeon_irq_ciu_to_irq[line][bit] != 0)
+ return -EINVAL;
+
+ octeon_irq_set_ciu_mapping(virq, line, bit, hw,
+ chip, octeon_irq_handle_gpio);
+ return 0;
+}
+
+static int octeon_irq_gpio_map(struct irq_domain *d,
+ unsigned int virq, irq_hw_number_t hw)
+{
+ return octeon_irq_gpio_map_common(d, virq, hw, 1, octeon_irq_gpio_chip);
+}
+
+static struct irq_domain_ops octeon_irq_domain_ciu_ops = {
+ .map = octeon_irq_ciu_map,
+ .xlate = octeon_irq_ciu_xlat,
+};
+
+static struct irq_domain_ops octeon_irq_domain_gpio_ops = {
+ .map = octeon_irq_gpio_map,
+ .xlate = octeon_irq_gpio_xlat,
+};
+
+static void octeon_irq_ip2_ciu(void)
+{
+ const unsigned long core_id = cvmx_get_core_num();
+ u64 ciu_sum = cvmx_read_csr(CVMX_CIU_INTX_SUM0(core_id * 2));
+
+ ciu_sum &= __get_cpu_var(octeon_irq_ciu0_en_mirror);
+ if (likely(ciu_sum)) {
+ int bit = fls64(ciu_sum) - 1;
+ int irq = octeon_irq_ciu_to_irq[0][bit];
+ if (likely(irq))
+ do_IRQ(irq);
+ else
+ spurious_interrupt();
+ } else {
+ spurious_interrupt();
+ }
+}
+
+static void octeon_irq_ip3_ciu(void)
+{
+ u64 ciu_sum = cvmx_read_csr(CVMX_CIU_INT_SUM1);
+
+ ciu_sum &= __get_cpu_var(octeon_irq_ciu1_en_mirror);
+ if (likely(ciu_sum)) {
+ int bit = fls64(ciu_sum) - 1;
+ int irq = octeon_irq_ciu_to_irq[1][bit];
+ if (likely(irq))
+ do_IRQ(irq);
+ else
+ spurious_interrupt();
+ } else {
+ spurious_interrupt();
+ }
+}
+
+static bool octeon_irq_use_ip4;
+
+static void octeon_irq_local_enable_ip4(void *arg)
+{
+ set_c0_status(STATUSF_IP4);
+}
+
+static void octeon_irq_ip4_mask(void)
+{
+ clear_c0_status(STATUSF_IP4);
+ spurious_interrupt();
+}
+
+static void (*octeon_irq_ip2)(void);
+static void (*octeon_irq_ip3)(void);
+static void (*octeon_irq_ip4)(void);
+
+void (*octeon_irq_setup_secondary)(void);
+
+void octeon_irq_set_ip4_handler(octeon_irq_ip4_handler_t h)
+{
+ octeon_irq_ip4 = h;
+ octeon_irq_use_ip4 = true;
+ on_each_cpu(octeon_irq_local_enable_ip4, NULL, 1);
+}
+
+static void octeon_irq_percpu_enable(void)
+{
+ irq_cpu_online();
+}
+
+static void octeon_irq_init_ciu_percpu(void)
+{
+ int coreid = cvmx_get_core_num();
+
+
+ __get_cpu_var(octeon_irq_ciu0_en_mirror) = 0;
+ __get_cpu_var(octeon_irq_ciu1_en_mirror) = 0;
+ wmb();
+ raw_spin_lock_init(&__get_cpu_var(octeon_irq_ciu_spinlock));
/*
- * Enable all CIU interrupts again. We don't need to disable
- * IRQs to make these atomic since they are already disabled
- * earlier in the low level interrupt code.
+ * Disable All CIU Interrupts. The ones we need will be
+ * enabled later. Read the SUM register so we know the write
+ * completed.
*/
- set_c0_status(0x100 << 3);
+ cvmx_write_csr(CVMX_CIU_INTX_EN0((coreid * 2)), 0);
+ cvmx_write_csr(CVMX_CIU_INTX_EN0((coreid * 2 + 1)), 0);
+ cvmx_write_csr(CVMX_CIU_INTX_EN1((coreid * 2)), 0);
+ cvmx_write_csr(CVMX_CIU_INTX_EN1((coreid * 2 + 1)), 0);
+ cvmx_read_csr(CVMX_CIU_INTX_SUM0((coreid * 2)));
}
-static void octeon_irq_ciu1_enable(unsigned int irq)
+static void octeon_irq_init_ciu2_percpu(void)
{
+ u64 regx, ipx;
int coreid = cvmx_get_core_num();
- unsigned long flags;
- uint64_t en1;
- int bit = irq - OCTEON_IRQ_WDOG0; /* Bit 0-63 of EN1 */
+ u64 base = CVMX_CIU2_EN_PPX_IP2_WRKQ(coreid);
/*
- * A read lock is used here to make sure only one core is ever
- * updating the CIU enable bits at a time. During an enable
- * the cores don't interfere with each other. During a disable
- * the write lock stops any enables that might cause a
- * problem.
+ * Disable All CIU2 Interrupts. The ones we need will be
+ * enabled later. Read the SUM register so we know the write
+ * completed.
+ *
+ * There are 9 registers and 3 IPX levels with strides 0x1000
+ * and 0x200 respectivly. Use loops to clear them.
*/
- read_lock_irqsave(&octeon_irq_ciu1_rwlock, flags);
- en1 = cvmx_read_csr(CVMX_CIU_INTX_EN1(coreid * 2 + 1));
- en1 |= 1ull << bit;
- cvmx_write_csr(CVMX_CIU_INTX_EN1(coreid * 2 + 1), en1);
- cvmx_read_csr(CVMX_CIU_INTX_EN1(coreid * 2 + 1));
- read_unlock_irqrestore(&octeon_irq_ciu1_rwlock, flags);
+ for (regx = 0; regx <= 0x8000; regx += 0x1000) {
+ for (ipx = 0; ipx <= 0x400; ipx += 0x200)
+ cvmx_write_csr(base + regx + ipx, 0);
+ }
+
+ cvmx_read_csr(CVMX_CIU2_SUM_PPX_IP2(coreid));
}
-static void octeon_irq_ciu1_disable(unsigned int irq)
+static void octeon_irq_setup_secondary_ciu(void)
+{
+ octeon_irq_init_ciu_percpu();
+ octeon_irq_percpu_enable();
+
+ /* Enable the CIU lines */
+ set_c0_status(STATUSF_IP3 | STATUSF_IP2);
+ clear_c0_status(STATUSF_IP4);
+}
+
+static void octeon_irq_setup_secondary_ciu2(void)
+{
+ octeon_irq_init_ciu2_percpu();
+ octeon_irq_percpu_enable();
+
+ /* Enable the CIU lines */
+ set_c0_status(STATUSF_IP3 | STATUSF_IP2);
+ if (octeon_irq_use_ip4)
+ set_c0_status(STATUSF_IP4);
+ else
+ clear_c0_status(STATUSF_IP4);
+}
+
+static void __init octeon_irq_init_ciu(void)
+{
+ unsigned int i;
+ struct irq_chip *chip;
+ struct irq_chip *chip_mbox;
+ struct irq_chip *chip_wd;
+ struct device_node *gpio_node;
+ struct device_node *ciu_node;
+ struct irq_domain *ciu_domain = NULL;
+
+ octeon_irq_init_ciu_percpu();
+ octeon_irq_setup_secondary = octeon_irq_setup_secondary_ciu;
+
+ octeon_irq_ip2 = octeon_irq_ip2_ciu;
+ octeon_irq_ip3 = octeon_irq_ip3_ciu;
+ if (OCTEON_IS_MODEL(OCTEON_CN58XX_PASS2_X) ||
+ OCTEON_IS_MODEL(OCTEON_CN56XX_PASS2_X) ||
+ OCTEON_IS_MODEL(OCTEON_CN52XX_PASS2_X) ||
+ OCTEON_IS_MODEL(OCTEON_CN6XXX)) {
+ chip = &octeon_irq_chip_ciu_v2;
+ chip_mbox = &octeon_irq_chip_ciu_mbox_v2;
+ chip_wd = &octeon_irq_chip_ciu_wd_v2;
+ octeon_irq_gpio_chip = &octeon_irq_chip_ciu_gpio_v2;
+ } else {
+ chip = &octeon_irq_chip_ciu;
+ chip_mbox = &octeon_irq_chip_ciu_mbox;
+ chip_wd = &octeon_irq_chip_ciu_wd;
+ octeon_irq_gpio_chip = &octeon_irq_chip_ciu_gpio;
+ }
+ octeon_irq_ciu_chip = chip;
+ octeon_irq_ip4 = octeon_irq_ip4_mask;
+
+ /* Mips internal */
+ octeon_irq_init_core();
+
+ gpio_node = of_find_compatible_node(NULL, NULL, "cavium,octeon-3860-gpio");
+ if (gpio_node) {
+ struct octeon_irq_gpio_domain_data *gpiod;
+
+ gpiod = kzalloc(sizeof(*gpiod), GFP_KERNEL);
+ if (gpiod) {
+ /* gpio domain host_data is the base hwirq number. */
+ gpiod->base_hwirq = 16;
+ irq_domain_add_linear(gpio_node, 16, &octeon_irq_domain_gpio_ops, gpiod);
+ of_node_put(gpio_node);
+ } else
+ pr_warn("Cannot allocate memory for GPIO irq_domain.\n");
+ } else
+ pr_warn("Cannot find device node for cavium,octeon-3860-gpio.\n");
+
+ ciu_node = of_find_compatible_node(NULL, NULL, "cavium,octeon-3860-ciu");
+ if (ciu_node) {
+ ciu_domain = irq_domain_add_tree(ciu_node, &octeon_irq_domain_ciu_ops, NULL);
+ irq_set_default_host(ciu_domain);
+ of_node_put(ciu_node);
+ } else
+ panic("Cannot find device node for cavium,octeon-3860-ciu.");
+
+ /* CIU_0 */
+ for (i = 0; i < 16; i++)
+ octeon_irq_force_ciu_mapping(ciu_domain, i + OCTEON_IRQ_WORKQ0, 0, i + 0);
+
+ octeon_irq_set_ciu_mapping(OCTEON_IRQ_MBOX0, 0, 32, 0, chip_mbox, handle_percpu_irq);
+ octeon_irq_set_ciu_mapping(OCTEON_IRQ_MBOX1, 0, 33, 0, chip_mbox, handle_percpu_irq);
+
+ for (i = 0; i < 4; i++)
+ octeon_irq_force_ciu_mapping(ciu_domain, i + OCTEON_IRQ_PCI_INT0, 0, i + 36);
+ for (i = 0; i < 4; i++)
+ octeon_irq_force_ciu_mapping(ciu_domain, i + OCTEON_IRQ_PCI_MSI0, 0, i + 40);
+
+ octeon_irq_force_ciu_mapping(ciu_domain, OCTEON_IRQ_TWSI, 0, 45);
+ octeon_irq_force_ciu_mapping(ciu_domain, OCTEON_IRQ_RML, 0, 46);
+ for (i = 0; i < 4; i++)
+ octeon_irq_force_ciu_mapping(ciu_domain, i + OCTEON_IRQ_TIMER0, 0, i + 52);
+
+ octeon_irq_force_ciu_mapping(ciu_domain, OCTEON_IRQ_USB0, 0, 56);
+ octeon_irq_force_ciu_mapping(ciu_domain, OCTEON_IRQ_TWSI2, 0, 59);
+
+ /* CIU_1 */
+ for (i = 0; i < 16; i++)
+ octeon_irq_set_ciu_mapping(i + OCTEON_IRQ_WDOG0, 1, i + 0, 0, chip_wd, handle_level_irq);
+
+ octeon_irq_force_ciu_mapping(ciu_domain, OCTEON_IRQ_USB1, 1, 17);
+
+ /* Enable the CIU lines */
+ set_c0_status(STATUSF_IP3 | STATUSF_IP2);
+ clear_c0_status(STATUSF_IP4);
+}
+
+/*
+ * Watchdog interrupts are special. They are associated with a single
+ * core, so we hardwire the affinity to that core.
+ */
+static void octeon_irq_ciu2_wd_enable(struct irq_data *data)
+{
+ u64 mask;
+ u64 en_addr;
+ int coreid = data->irq - OCTEON_IRQ_WDOG0;
+ union octeon_ciu_chip_data cd;
+
+ cd.p = irq_data_get_irq_chip_data(data);
+ mask = 1ull << (cd.s.bit);
+
+ en_addr = CVMX_CIU2_EN_PPX_IP2_WRKQ_W1S(coreid) + (0x1000ull * cd.s.line);
+ cvmx_write_csr(en_addr, mask);
+
+}
+
+static void octeon_irq_ciu2_enable(struct irq_data *data)
+{
+ u64 mask;
+ u64 en_addr;
+ int cpu = next_cpu_for_irq(data);
+ int coreid = octeon_coreid_for_cpu(cpu);
+ union octeon_ciu_chip_data cd;
+
+ cd.p = irq_data_get_irq_chip_data(data);
+ mask = 1ull << (cd.s.bit);
+
+ en_addr = CVMX_CIU2_EN_PPX_IP2_WRKQ_W1S(coreid) + (0x1000ull * cd.s.line);
+ cvmx_write_csr(en_addr, mask);
+}
+
+static void octeon_irq_ciu2_enable_local(struct irq_data *data)
+{
+ u64 mask;
+ u64 en_addr;
+ int coreid = cvmx_get_core_num();
+ union octeon_ciu_chip_data cd;
+
+ cd.p = irq_data_get_irq_chip_data(data);
+ mask = 1ull << (cd.s.bit);
+
+ en_addr = CVMX_CIU2_EN_PPX_IP2_WRKQ_W1S(coreid) + (0x1000ull * cd.s.line);
+ cvmx_write_csr(en_addr, mask);
+
+}
+
+static void octeon_irq_ciu2_disable_local(struct irq_data *data)
+{
+ u64 mask;
+ u64 en_addr;
+ int coreid = cvmx_get_core_num();
+ union octeon_ciu_chip_data cd;
+
+ cd.p = irq_data_get_irq_chip_data(data);
+ mask = 1ull << (cd.s.bit);
+
+ en_addr = CVMX_CIU2_EN_PPX_IP2_WRKQ_W1C(coreid) + (0x1000ull * cd.s.line);
+ cvmx_write_csr(en_addr, mask);
+
+}
+
+static void octeon_irq_ciu2_ack(struct irq_data *data)
+{
+ u64 mask;
+ u64 en_addr;
+ int coreid = cvmx_get_core_num();
+ union octeon_ciu_chip_data cd;
+
+ cd.p = irq_data_get_irq_chip_data(data);
+ mask = 1ull << (cd.s.bit);
+
+ en_addr = CVMX_CIU2_RAW_PPX_IP2_WRKQ(coreid) + (0x1000ull * cd.s.line);
+ cvmx_write_csr(en_addr, mask);
+
+}
+
+static void octeon_irq_ciu2_disable_all(struct irq_data *data)
{
- int bit = irq - OCTEON_IRQ_WDOG0; /* Bit 0-63 of EN1 */
- unsigned long flags;
- uint64_t en1;
-#ifdef CONFIG_SMP
int cpu;
- write_lock_irqsave(&octeon_irq_ciu1_rwlock, flags);
+ u64 mask;
+ union octeon_ciu_chip_data cd;
+
+ cd.p = irq_data_get_irq_chip_data(data);
+ mask = 1ull << (cd.s.bit);
+
for_each_online_cpu(cpu) {
- int coreid = cpu_logical_map(cpu);
- en1 = cvmx_read_csr(CVMX_CIU_INTX_EN1(coreid * 2 + 1));
- en1 &= ~(1ull << bit);
- cvmx_write_csr(CVMX_CIU_INTX_EN1(coreid * 2 + 1), en1);
+ u64 en_addr = CVMX_CIU2_EN_PPX_IP2_WRKQ_W1C(octeon_coreid_for_cpu(cpu)) + (0x1000ull * cd.s.line);
+ cvmx_write_csr(en_addr, mask);
}
- /*
- * We need to do a read after the last update to make sure all
- * of them are done.
- */
- cvmx_read_csr(CVMX_CIU_INTX_EN1(cvmx_get_core_num() * 2 + 1));
- write_unlock_irqrestore(&octeon_irq_ciu1_rwlock, flags);
-#else
+}
+
+static void octeon_irq_ciu2_mbox_enable_all(struct irq_data *data)
+{
+ int cpu;
+ u64 mask;
+
+ mask = 1ull << (data->irq - OCTEON_IRQ_MBOX0);
+
+ for_each_online_cpu(cpu) {
+ u64 en_addr = CVMX_CIU2_EN_PPX_IP3_MBOX_W1S(octeon_coreid_for_cpu(cpu));
+ cvmx_write_csr(en_addr, mask);
+ }
+}
+
+static void octeon_irq_ciu2_mbox_disable_all(struct irq_data *data)
+{
+ int cpu;
+ u64 mask;
+
+ mask = 1ull << (data->irq - OCTEON_IRQ_MBOX0);
+
+ for_each_online_cpu(cpu) {
+ u64 en_addr = CVMX_CIU2_EN_PPX_IP3_MBOX_W1C(octeon_coreid_for_cpu(cpu));
+ cvmx_write_csr(en_addr, mask);
+ }
+}
+
+static void octeon_irq_ciu2_mbox_enable_local(struct irq_data *data)
+{
+ u64 mask;
+ u64 en_addr;
int coreid = cvmx_get_core_num();
- local_irq_save(flags);
- en1 = cvmx_read_csr(CVMX_CIU_INTX_EN1(coreid * 2 + 1));
- en1 &= ~(1ull << bit);
- cvmx_write_csr(CVMX_CIU_INTX_EN1(coreid * 2 + 1), en1);
- cvmx_read_csr(CVMX_CIU_INTX_EN1(coreid * 2 + 1));
- local_irq_restore(flags);
-#endif
+
+ mask = 1ull << (data->irq - OCTEON_IRQ_MBOX0);
+ en_addr = CVMX_CIU2_EN_PPX_IP3_MBOX_W1S(coreid);
+ cvmx_write_csr(en_addr, mask);
+}
+
+static void octeon_irq_ciu2_mbox_disable_local(struct irq_data *data)
+{
+ u64 mask;
+ u64 en_addr;
+ int coreid = cvmx_get_core_num();
+
+ mask = 1ull << (data->irq - OCTEON_IRQ_MBOX0);
+ en_addr = CVMX_CIU2_EN_PPX_IP3_MBOX_W1C(coreid);
+ cvmx_write_csr(en_addr, mask);
}
#ifdef CONFIG_SMP
-static void octeon_irq_ciu1_set_affinity(unsigned int irq, const struct cpumask *dest)
+static int octeon_irq_ciu2_set_affinity(struct irq_data *data,
+ const struct cpumask *dest, bool force)
{
int cpu;
- int bit = irq - OCTEON_IRQ_WDOG0; /* Bit 0-63 of EN1 */
+ bool enable_one = !irqd_irq_disabled(data) && !irqd_irq_masked(data);
+ u64 mask;
+ union octeon_ciu_chip_data cd;
+
+ if (!enable_one)
+ return 0;
+
+ cd.p = irq_data_get_irq_chip_data(data);
+ mask = 1ull << cd.s.bit;
- write_lock(&octeon_irq_ciu1_rwlock);
for_each_online_cpu(cpu) {
- int coreid = cpu_logical_map(cpu);
- uint64_t en1 =
- cvmx_read_csr(CVMX_CIU_INTX_EN1
- (coreid * 2 + 1));
- if (cpumask_test_cpu(cpu, dest))
- en1 |= 1ull << bit;
- else
- en1 &= ~(1ull << bit);
- cvmx_write_csr(CVMX_CIU_INTX_EN1(coreid * 2 + 1), en1);
+ u64 en_addr;
+ if (cpumask_test_cpu(cpu, dest) && enable_one) {
+ enable_one = false;
+ en_addr = CVMX_CIU2_EN_PPX_IP2_WRKQ_W1S(octeon_coreid_for_cpu(cpu)) + (0x1000ull * cd.s.line);
+ } else {
+ en_addr = CVMX_CIU2_EN_PPX_IP2_WRKQ_W1C(octeon_coreid_for_cpu(cpu)) + (0x1000ull * cd.s.line);
+ }
+ cvmx_write_csr(en_addr, mask);
}
- /*
- * We need to do a read after the last update to make sure all
- * of them are done.
- */
- cvmx_read_csr(CVMX_CIU_INTX_EN1(cvmx_get_core_num() * 2 + 1));
- write_unlock(&octeon_irq_ciu1_rwlock);
+
+ return 0;
}
#endif
-static struct irq_chip octeon_irq_chip_ciu1 = {
- .name = "CIU1",
- .enable = octeon_irq_ciu1_enable,
- .disable = octeon_irq_ciu1_disable,
- .ack = octeon_irq_ciu1_ack,
- .eoi = octeon_irq_ciu1_eoi,
+static void octeon_irq_ciu2_enable_gpio(struct irq_data *data)
+{
+ octeon_irq_gpio_setup(data);
+ octeon_irq_ciu2_enable(data);
+}
+
+static void octeon_irq_ciu2_disable_gpio(struct irq_data *data)
+{
+ union octeon_ciu_chip_data cd;
+ cd.p = irq_data_get_irq_chip_data(data);
+
+ cvmx_write_csr(CVMX_GPIO_BIT_CFGX(cd.s.gpio_line), 0);
+
+ octeon_irq_ciu2_disable_all(data);
+}
+
+static struct irq_chip octeon_irq_chip_ciu2 = {
+ .name = "CIU2-E",
+ .irq_enable = octeon_irq_ciu2_enable,
+ .irq_disable = octeon_irq_ciu2_disable_all,
+ .irq_ack = octeon_irq_ciu2_ack,
+ .irq_mask = octeon_irq_ciu2_disable_local,
+ .irq_unmask = octeon_irq_ciu2_enable,
#ifdef CONFIG_SMP
- .set_affinity = octeon_irq_ciu1_set_affinity,
+ .irq_set_affinity = octeon_irq_ciu2_set_affinity,
+ .irq_cpu_offline = octeon_irq_cpu_offline_ciu,
#endif
};
-#ifdef CONFIG_PCI_MSI
+static struct irq_chip octeon_irq_chip_ciu2_mbox = {
+ .name = "CIU2-M",
+ .irq_enable = octeon_irq_ciu2_mbox_enable_all,
+ .irq_disable = octeon_irq_ciu2_mbox_disable_all,
+ .irq_ack = octeon_irq_ciu2_mbox_disable_local,
+ .irq_eoi = octeon_irq_ciu2_mbox_enable_local,
+
+ .irq_cpu_online = octeon_irq_ciu2_mbox_enable_local,
+ .irq_cpu_offline = octeon_irq_ciu2_mbox_disable_local,
+ .flags = IRQCHIP_ONOFFLINE_ENABLED,
+};
+
+static struct irq_chip octeon_irq_chip_ciu2_wd = {
+ .name = "CIU2-W",
+ .irq_enable = octeon_irq_ciu2_wd_enable,
+ .irq_disable = octeon_irq_ciu2_disable_all,
+ .irq_mask = octeon_irq_ciu2_disable_local,
+ .irq_unmask = octeon_irq_ciu2_enable_local,
+};
+
+static struct irq_chip octeon_irq_chip_ciu2_gpio = {
+ .name = "CIU-GPIO",
+ .irq_enable = octeon_irq_ciu2_enable_gpio,
+ .irq_disable = octeon_irq_ciu2_disable_gpio,
+ .irq_ack = octeon_irq_ciu_gpio_ack,
+ .irq_mask = octeon_irq_ciu2_disable_local,
+ .irq_unmask = octeon_irq_ciu2_enable,
+ .irq_set_type = octeon_irq_ciu_gpio_set_type,
+#ifdef CONFIG_SMP
+ .irq_set_affinity = octeon_irq_ciu2_set_affinity,
+ .irq_cpu_offline = octeon_irq_cpu_offline_ciu,
+#endif
+ .flags = IRQCHIP_SET_TYPE_MASKED,
+};
-static void octeon_irq_msi_ack(unsigned int irq)
+static int octeon_irq_ciu2_xlat(struct irq_domain *d,
+ struct device_node *node,
+ const u32 *intspec,
+ unsigned int intsize,
+ unsigned long *out_hwirq,
+ unsigned int *out_type)
{
- if (!octeon_has_feature(OCTEON_FEATURE_PCIE)) {
- /* These chips have PCI */
- cvmx_write_csr(CVMX_NPI_NPI_MSI_RCV,
- 1ull << (irq - OCTEON_IRQ_MSI_BIT0));
- } else {
- /*
- * These chips have PCIe. Thankfully the ACK doesn't
- * need any locking.
- */
- cvmx_write_csr(CVMX_PEXP_NPEI_MSI_RCV0,
- 1ull << (irq - OCTEON_IRQ_MSI_BIT0));
- }
+ unsigned int ciu, bit;
+
+ ciu = intspec[0];
+ bit = intspec[1];
+
+ *out_hwirq = (ciu << 6) | bit;
+ *out_type = 0;
+
+ return 0;
}
-static void octeon_irq_msi_eoi(unsigned int irq)
+static bool octeon_irq_ciu2_is_edge(unsigned int line, unsigned int bit)
{
- /* Nothing needed */
+ bool edge = false;
+
+ if (line == 3) /* MIO */
+ switch (bit) {
+ case 2: /* IPD_DRP */
+ case 8 ... 11: /* Timers */
+ case 48: /* PTP */
+ edge = true;
+ break;
+ default:
+ break;
+ }
+ else if (line == 6) /* PKT */
+ switch (bit) {
+ case 52 ... 53: /* ILK_DRP */
+ case 8 ... 12: /* GMX_DRP */
+ edge = true;
+ break;
+ default:
+ break;
+ }
+ return edge;
}
-static void octeon_irq_msi_enable(unsigned int irq)
+static int octeon_irq_ciu2_map(struct irq_domain *d,
+ unsigned int virq, irq_hw_number_t hw)
{
- if (!octeon_has_feature(OCTEON_FEATURE_PCIE)) {
- /*
- * Octeon PCI doesn't have the ability to mask/unmask
- * MSI interrupts individually. Instead of
- * masking/unmasking them in groups of 16, we simple
- * assume MSI devices are well behaved. MSI
- * interrupts are always enable and the ACK is assumed
- * to be enough.
- */
- } else {
- /* These chips have PCIe. Note that we only support
- * the first 64 MSI interrupts. Unfortunately all the
- * MSI enables are in the same register. We use
- * MSI0's lock to control access to them all.
- */
- uint64_t en;
- unsigned long flags;
- spin_lock_irqsave(&octeon_irq_msi_lock, flags);
- en = cvmx_read_csr(CVMX_PEXP_NPEI_MSI_ENB0);
- en |= 1ull << (irq - OCTEON_IRQ_MSI_BIT0);
- cvmx_write_csr(CVMX_PEXP_NPEI_MSI_ENB0, en);
- cvmx_read_csr(CVMX_PEXP_NPEI_MSI_ENB0);
- spin_unlock_irqrestore(&octeon_irq_msi_lock, flags);
- }
-}
+ unsigned int line = hw >> 6;
+ unsigned int bit = hw & 63;
+
+ if (!octeon_irq_virq_in_range(virq))
+ return -EINVAL;
+
+ /*
+ * Don't map irq if it is reserved for GPIO.
+ * (Line 7 are the GPIO lines.)
+ */
+ if (line == 7)
+ return 0;
+
+ if (line > 7 || octeon_irq_ciu_to_irq[line][bit] != 0)
+ return -EINVAL;
+
+ if (octeon_irq_ciu2_is_edge(line, bit))
+ octeon_irq_set_ciu_mapping(virq, line, bit, 0,
+ &octeon_irq_chip_ciu2,
+ handle_edge_irq);
+ else
+ octeon_irq_set_ciu_mapping(virq, line, bit, 0,
+ &octeon_irq_chip_ciu2,
+ handle_level_irq);
-static void octeon_irq_msi_disable(unsigned int irq)
+ return 0;
+}
+static int octeon_irq_ciu2_gpio_map(struct irq_domain *d,
+ unsigned int virq, irq_hw_number_t hw)
{
- if (!octeon_has_feature(OCTEON_FEATURE_PCIE)) {
- /* See comment in enable */
- } else {
- /*
- * These chips have PCIe. Note that we only support
- * the first 64 MSI interrupts. Unfortunately all the
- * MSI enables are in the same register. We use
- * MSI0's lock to control access to them all.
- */
- uint64_t en;
- unsigned long flags;
- spin_lock_irqsave(&octeon_irq_msi_lock, flags);
- en = cvmx_read_csr(CVMX_PEXP_NPEI_MSI_ENB0);
- en &= ~(1ull << (irq - OCTEON_IRQ_MSI_BIT0));
- cvmx_write_csr(CVMX_PEXP_NPEI_MSI_ENB0, en);
- cvmx_read_csr(CVMX_PEXP_NPEI_MSI_ENB0);
- spin_unlock_irqrestore(&octeon_irq_msi_lock, flags);
- }
+ return octeon_irq_gpio_map_common(d, virq, hw, 7, &octeon_irq_chip_ciu2_gpio);
}
-static struct irq_chip octeon_irq_chip_msi = {
- .name = "MSI",
- .enable = octeon_irq_msi_enable,
- .disable = octeon_irq_msi_disable,
- .ack = octeon_irq_msi_ack,
- .eoi = octeon_irq_msi_eoi,
+static struct irq_domain_ops octeon_irq_domain_ciu2_ops = {
+ .map = octeon_irq_ciu2_map,
+ .xlate = octeon_irq_ciu2_xlat,
};
-#endif
-void __init arch_init_irq(void)
+static struct irq_domain_ops octeon_irq_domain_ciu2_gpio_ops = {
+ .map = octeon_irq_ciu2_gpio_map,
+ .xlate = octeon_irq_gpio_xlat,
+};
+
+static void octeon_irq_ciu2(void)
{
+ int line;
+ int bit;
int irq;
+ u64 src_reg, src, sum;
+ const unsigned long core_id = cvmx_get_core_num();
-#ifdef CONFIG_SMP
- /* Set the default affinity to the boot cpu. */
- cpumask_clear(irq_default_affinity);
- cpumask_set_cpu(smp_processor_id(), irq_default_affinity);
-#endif
+ sum = cvmx_read_csr(CVMX_CIU2_SUM_PPX_IP2(core_id)) & 0xfful;
- if (NR_IRQS < OCTEON_IRQ_LAST)
- pr_err("octeon_irq_init: NR_IRQS is set too low\n");
+ if (unlikely(!sum))
+ goto spurious;
- /* 0 - 15 reserved for i8259 master and slave controller. */
+ line = fls64(sum) - 1;
+ src_reg = CVMX_CIU2_SRC_PPX_IP2_WRKQ(core_id) + (0x1000 * line);
+ src = cvmx_read_csr(src_reg);
- /* 17 - 23 Mips internal */
- for (irq = OCTEON_IRQ_SW0; irq <= OCTEON_IRQ_TIMER; irq++) {
- set_irq_chip_and_handler(irq, &octeon_irq_chip_core,
- handle_percpu_irq);
- }
+ if (unlikely(!src))
+ goto spurious;
- /* 24 - 87 CIU_INT_SUM0 */
- for (irq = OCTEON_IRQ_WORKQ0; irq <= OCTEON_IRQ_BOOTDMA; irq++) {
- set_irq_chip_and_handler(irq, &octeon_irq_chip_ciu0,
- handle_percpu_irq);
- }
+ bit = fls64(src) - 1;
+ irq = octeon_irq_ciu_to_irq[line][bit];
+ if (unlikely(!irq))
+ goto spurious;
- /* 88 - 151 CIU_INT_SUM1 */
- for (irq = OCTEON_IRQ_WDOG0; irq <= OCTEON_IRQ_RESERVED151; irq++) {
- set_irq_chip_and_handler(irq, &octeon_irq_chip_ciu1,
- handle_percpu_irq);
- }
+ do_IRQ(irq);
+ goto out;
-#ifdef CONFIG_PCI_MSI
- /* 152 - 215 PCI/PCIe MSI interrupts */
- for (irq = OCTEON_IRQ_MSI_BIT0; irq <= OCTEON_IRQ_MSI_BIT63; irq++) {
- set_irq_chip_and_handler(irq, &octeon_irq_chip_msi,
- handle_percpu_irq);
- }
+spurious:
+ spurious_interrupt();
+out:
+ /* CN68XX pass 1.x has an errata that accessing the ACK registers
+ can stop interrupts from propagating */
+ if (OCTEON_IS_MODEL(OCTEON_CN68XX))
+ cvmx_read_csr(CVMX_CIU2_INTR_CIU_READY);
+ else
+ cvmx_read_csr(CVMX_CIU2_ACK_PPX_IP2(core_id));
+ return;
+}
+
+static void octeon_irq_ciu2_mbox(void)
+{
+ int line;
+
+ const unsigned long core_id = cvmx_get_core_num();
+ u64 sum = cvmx_read_csr(CVMX_CIU2_SUM_PPX_IP3(core_id)) >> 60;
+
+ if (unlikely(!sum))
+ goto spurious;
+
+ line = fls64(sum) - 1;
+
+ do_IRQ(OCTEON_IRQ_MBOX0 + line);
+ goto out;
+
+spurious:
+ spurious_interrupt();
+out:
+ /* CN68XX pass 1.x has an errata that accessing the ACK registers
+ can stop interrupts from propagating */
+ if (OCTEON_IS_MODEL(OCTEON_CN68XX))
+ cvmx_read_csr(CVMX_CIU2_INTR_CIU_READY);
+ else
+ cvmx_read_csr(CVMX_CIU2_ACK_PPX_IP3(core_id));
+ return;
+}
+
+static void __init octeon_irq_init_ciu2(void)
+{
+ unsigned int i;
+ struct device_node *gpio_node;
+ struct device_node *ciu_node;
+ struct irq_domain *ciu_domain = NULL;
+
+ octeon_irq_init_ciu2_percpu();
+ octeon_irq_setup_secondary = octeon_irq_setup_secondary_ciu2;
+
+ octeon_irq_ip2 = octeon_irq_ciu2;
+ octeon_irq_ip3 = octeon_irq_ciu2_mbox;
+ octeon_irq_ip4 = octeon_irq_ip4_mask;
+
+ /* Mips internal */
+ octeon_irq_init_core();
+
+ gpio_node = of_find_compatible_node(NULL, NULL, "cavium,octeon-3860-gpio");
+ if (gpio_node) {
+ struct octeon_irq_gpio_domain_data *gpiod;
+
+ gpiod = kzalloc(sizeof(*gpiod), GFP_KERNEL);
+ if (gpiod) {
+ /* gpio domain host_data is the base hwirq number. */
+ gpiod->base_hwirq = 7 << 6;
+ irq_domain_add_linear(gpio_node, 16, &octeon_irq_domain_ciu2_gpio_ops, gpiod);
+ of_node_put(gpio_node);
+ } else
+ pr_warn("Cannot allocate memory for GPIO irq_domain.\n");
+ } else
+ pr_warn("Cannot find device node for cavium,octeon-3860-gpio.\n");
+
+ ciu_node = of_find_compatible_node(NULL, NULL, "cavium,octeon-6880-ciu2");
+ if (ciu_node) {
+ ciu_domain = irq_domain_add_tree(ciu_node, &octeon_irq_domain_ciu2_ops, NULL);
+ irq_set_default_host(ciu_domain);
+ of_node_put(ciu_node);
+ } else
+ panic("Cannot find device node for cavium,octeon-6880-ciu2.");
+
+ /* CUI2 */
+ for (i = 0; i < 64; i++)
+ octeon_irq_force_ciu_mapping(ciu_domain, i + OCTEON_IRQ_WORKQ0, 0, i);
+
+ for (i = 0; i < 32; i++)
+ octeon_irq_set_ciu_mapping(i + OCTEON_IRQ_WDOG0, 1, i, 0,
+ &octeon_irq_chip_ciu2_wd, handle_level_irq);
+
+ for (i = 0; i < 4; i++)
+ octeon_irq_force_ciu_mapping(ciu_domain, i + OCTEON_IRQ_TIMER0, 3, i + 8);
+
+ octeon_irq_force_ciu_mapping(ciu_domain, OCTEON_IRQ_USB0, 3, 44);
+
+ for (i = 0; i < 4; i++)
+ octeon_irq_force_ciu_mapping(ciu_domain, i + OCTEON_IRQ_PCI_INT0, 4, i);
+
+ for (i = 0; i < 4; i++)
+ octeon_irq_force_ciu_mapping(ciu_domain, i + OCTEON_IRQ_PCI_MSI0, 4, i + 8);
+
+ irq_set_chip_and_handler(OCTEON_IRQ_MBOX0, &octeon_irq_chip_ciu2_mbox, handle_percpu_irq);
+ irq_set_chip_and_handler(OCTEON_IRQ_MBOX1, &octeon_irq_chip_ciu2_mbox, handle_percpu_irq);
+ irq_set_chip_and_handler(OCTEON_IRQ_MBOX2, &octeon_irq_chip_ciu2_mbox, handle_percpu_irq);
+ irq_set_chip_and_handler(OCTEON_IRQ_MBOX3, &octeon_irq_chip_ciu2_mbox, handle_percpu_irq);
+
+ /* Enable the CIU lines */
+ set_c0_status(STATUSF_IP3 | STATUSF_IP2);
+ clear_c0_status(STATUSF_IP4);
+}
+
+void __init arch_init_irq(void)
+{
+#ifdef CONFIG_SMP
+ /* Set the default affinity to the boot cpu. */
+ cpumask_clear(irq_default_affinity);
+ cpumask_set_cpu(smp_processor_id(), irq_default_affinity);
#endif
- set_c0_status(0x300 << 2);
+ if (OCTEON_IS_MODEL(OCTEON_CN68XX))
+ octeon_irq_init_ciu2();
+ else
+ octeon_irq_init_ciu();
}
asmlinkage void plat_irq_dispatch(void)
{
- const unsigned long core_id = cvmx_get_core_num();
- const uint64_t ciu_sum0_address = CVMX_CIU_INTX_SUM0(core_id * 2);
- const uint64_t ciu_en0_address = CVMX_CIU_INTX_EN0(core_id * 2);
- const uint64_t ciu_sum1_address = CVMX_CIU_INT_SUM1;
- const uint64_t ciu_en1_address = CVMX_CIU_INTX_EN1(core_id * 2 + 1);
unsigned long cop0_cause;
unsigned long cop0_status;
- uint64_t ciu_en;
- uint64_t ciu_sum;
while (1) {
cop0_cause = read_c0_cause();
@@ -472,26 +1765,24 @@ asmlinkage void plat_irq_dispatch(void)
cop0_cause &= cop0_status;
cop0_cause &= ST0_IM;
- if (unlikely(cop0_cause & STATUSF_IP2)) {
- ciu_sum = cvmx_read_csr(ciu_sum0_address);
- ciu_en = cvmx_read_csr(ciu_en0_address);
- ciu_sum &= ciu_en;
- if (likely(ciu_sum))
- do_IRQ(fls64(ciu_sum) + OCTEON_IRQ_WORKQ0 - 1);
- else
- spurious_interrupt();
- } else if (unlikely(cop0_cause & STATUSF_IP3)) {
- ciu_sum = cvmx_read_csr(ciu_sum1_address);
- ciu_en = cvmx_read_csr(ciu_en1_address);
- ciu_sum &= ciu_en;
- if (likely(ciu_sum))
- do_IRQ(fls64(ciu_sum) + OCTEON_IRQ_WDOG0 - 1);
- else
- spurious_interrupt();
- } else if (likely(cop0_cause)) {
+ if (unlikely(cop0_cause & STATUSF_IP2))
+ octeon_irq_ip2();
+ else if (unlikely(cop0_cause & STATUSF_IP3))
+ octeon_irq_ip3();
+ else if (unlikely(cop0_cause & STATUSF_IP4))
+ octeon_irq_ip4();
+ else if (likely(cop0_cause))
do_IRQ(fls(cop0_cause) - 9 + MIPS_CPU_IRQ_BASE);
- } else {
+ else
break;
- }
}
}
+
+#ifdef CONFIG_HOTPLUG_CPU
+
+void octeon_fixup_irqs(void)
+{
+ irq_cpu_offline();
+}
+
+#endif /* CONFIG_HOTPLUG_CPU */
diff --git a/arch/mips/cavium-octeon/octeon-memcpy.S b/arch/mips/cavium-octeon/octeon-memcpy.S
index 88e0cddca20..64e08df51d6 100644
--- a/arch/mips/cavium-octeon/octeon-memcpy.S
+++ b/arch/mips/cavium-octeon/octeon-memcpy.S
@@ -79,11 +79,6 @@
/*
* Only on the 64-bit kernel we can made use of 64-bit registers.
*/
-#ifdef CONFIG_64BIT
-#define USE_DOUBLE
-#endif
-
-#ifdef USE_DOUBLE
#define LOAD ld
#define LOADL ldl
@@ -119,37 +114,17 @@
#define t6 $14
#define t7 $15
-#else
-
-#define LOAD lw
-#define LOADL lwl
-#define LOADR lwr
-#define STOREL swl
-#define STORER swr
-#define STORE sw
-#define ADD addu
-#define SUB subu
-#define SRL srl
-#define SLL sll
-#define SRA sra
-#define SLLV sllv
-#define SRLV srlv
-#define NBYTES 4
-#define LOG_NBYTES 2
-
-#endif /* USE_DOUBLE */
-
#ifdef CONFIG_CPU_LITTLE_ENDIAN
#define LDFIRST LOADR
-#define LDREST LOADL
+#define LDREST LOADL
#define STFIRST STORER
-#define STREST STOREL
+#define STREST STOREL
#define SHIFT_DISCARD SLLV
#else
#define LDFIRST LOADL
-#define LDREST LOADR
+#define LDREST LOADR
#define STFIRST STOREL
-#define STREST STORER
+#define STREST STORER
#define SHIFT_DISCARD SRLV
#endif
@@ -164,6 +139,14 @@
.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.
@@ -174,6 +157,8 @@ 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
@@ -331,9 +316,9 @@ EXC( STORE t0, -8(dst), s_exc_p1u)
src_unaligned:
#define rem t8
- SRL t0, len, LOG_NBYTES+2 # +2 for 4 units/iter
+ 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
+ and rem, len, (4*NBYTES-1) # rem = len % 4*NBYTES
1:
/*
* Avoid consecutive LD*'s to the same register since some mips
@@ -341,13 +326,13 @@ src_unaligned:
* 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( 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( 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
@@ -385,12 +370,10 @@ EXC( sb t0, N(dst), s_exc_p1)
COPY_BYTE(0)
COPY_BYTE(1)
-#ifdef USE_DOUBLE
COPY_BYTE(2)
COPY_BYTE(3)
COPY_BYTE(4)
COPY_BYTE(5)
-#endif
EXC( lb t0, NBYTES-2(src), l_exc)
SUB len, len, 1
jr ra
@@ -412,7 +395,6 @@ l_exc_copy:
* Assumes src < THREAD_BUADDR($28)
*/
LOAD t0, TI_TASK($28)
- nop
LOAD t0, THREAD_BUADDR(t0)
1:
EXC( lb t1, 0(src), l_exc)
@@ -422,10 +404,9 @@ EXC( lb t1, 0(src), l_exc)
ADD dst, dst, 1
l_exc:
LOAD t0, TI_TASK($28)
- nop
LOAD t0, THREAD_BUADDR(t0) # t0 is just past last good address
- nop
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.
@@ -443,7 +424,7 @@ l_exc:
ADD dst, dst, 1
bnez src, 1b
SUB src, src, 1
- jr ra
+2: jr ra
nop
diff --git a/arch/mips/cavium-octeon/octeon-platform.c b/arch/mips/cavium-octeon/octeon-platform.c
new file mode 100644
index 00000000000..6df0f4d8f19
--- /dev/null
+++ b/arch/mips/cavium-octeon/octeon-platform.c
@@ -0,0 +1,728 @@
+/*
+ * 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-5750-usbn", },
+ { .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 = 0;
+
+ snprintf(name_buffer, sizeof(name_buffer), "interface@%d", idx);
+ iface = fdt_subnode_offset(initial_boot_params, pip, name_buffer);
+ if (iface < 0)
+ return;
+
+ if (cvmx_helper_interface_enumerate(idx) == 0)
+ count = cvmx_helper_ports_on_interface(idx);
+
+ 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)) {
+ __be32 f;
+
+ f = cpu_to_be32(octeon_get_io_clock_rate());
+ fdt_setprop_inplace(initial_boot_params,
+ uart, "clock-frequency",
+ &f, sizeof(f));
+ 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");
+ }
+ }
+
+ /* DWC2 USB */
+ alias_prop = fdt_getprop(initial_boot_params, aliases,
+ "usbn", NULL);
+ if (alias_prop) {
+ int usbn = fdt_path_offset(initial_boot_params, alias_prop);
+
+ if (usbn >= 0 && (current_cpu_type() == CPU_CAVIUM_OCTEON2 ||
+ !octeon_has_feature(OCTEON_FEATURE_USB))) {
+ pr_debug("Deleting usbn\n");
+ fdt_nop_node(initial_boot_params, usbn);
+ fdt_nop_property(initial_boot_params, aliases, "usbn");
+ } else {
+ __be32 new_f[1];
+ enum cvmx_helper_board_usb_clock_types c;
+ c = __cvmx_helper_board_usb_get_clock_type();
+ switch (c) {
+ case USB_CLOCK_TYPE_REF_48:
+ new_f[0] = cpu_to_be32(48000000);
+ fdt_setprop_inplace(initial_boot_params, usbn,
+ "refclk-frequency", new_f, sizeof(new_f));
+ /* Fall through ...*/
+ case USB_CLOCK_TYPE_REF_12:
+ /* Missing "refclk-type" defaults to external. */
+ fdt_nop_property(initial_boot_params, usbn, "refclk-type");
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ 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");
diff --git a/arch/mips/cavium-octeon/octeon_3xxx.dts b/arch/mips/cavium-octeon/octeon_3xxx.dts
new file mode 100644
index 00000000000..fa33115bde3
--- /dev/null
+++ b/arch/mips/cavium-octeon/octeon_3xxx.dts
@@ -0,0 +1,590 @@
+/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;
+ };
+ };
+
+ usbn: usbn@1180068000000 {
+ compatible = "cavium,octeon-5750-usbn";
+ reg = <0x11800 0x68000000 0x0 0x1000>;
+ ranges; /* Direct mapping */
+ #address-cells = <2>;
+ #size-cells = <2>;
+ /* 12MHz, 24MHz and 48MHz allowed */
+ refclk-frequency = <12000000>;
+ /* Either "crystal" or "external" */
+ refclk-type = "crystal";
+
+ usbc@16f0010000000 {
+ compatible = "cavium,octeon-5750-usbc";
+ reg = <0x16f00 0x10000000 0x0 0x80000>;
+ interrupts = <0 56>;
+ };
+ };
+ };
+
+ 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;
+ usbn = &usbn;
+ led0 = &led0;
+ };
+ };
diff --git a/arch/mips/cavium-octeon/octeon_68xx.dts b/arch/mips/cavium-octeon/octeon_68xx.dts
new file mode 100644
index 00000000000..79b46fcb0a1
--- /dev/null
+++ b/arch/mips/cavium-octeon/octeon_68xx.dts
@@ -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;
+ };
+ };
diff --git a/arch/mips/cavium-octeon/octeon_boot.h b/arch/mips/cavium-octeon/octeon_boot.h
new file mode 100644
index 00000000000..7b066bbca86
--- /dev/null
+++ b/arch/mips/cavium-octeon/octeon_boot.h
@@ -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__ */
diff --git a/arch/mips/cavium-octeon/serial.c b/arch/mips/cavium-octeon/serial.c
deleted file mode 100644
index 8240728d485..00000000000
--- a/arch/mips/cavium-octeon/serial.c
+++ /dev/null
@@ -1,136 +0,0 @@
-/*
- * 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 <asm/time.h>
-
-#include <asm/octeon/octeon.h>
-
-#ifdef CONFIG_GDB_CONSOLE
-#define DEBUG_UART 0
-#else
-#define DEBUG_UART 1
-#endif
-
-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);
-}
-
-/*
- * Allocated in .bss, so it is all zeroed.
- */
-#define OCTEON_MAX_UARTS 3
-static struct plat_serial8250_port octeon_uart8250_data[OCTEON_MAX_UARTS + 1];
-static struct platform_device octeon_uart8250_device = {
- .name = "serial8250",
- .id = PLAT8250_DEV_PLATFORM,
- .dev = {
- .platform_data = octeon_uart8250_data,
- },
-};
-
-static void __init octeon_uart_set_common(struct plat_serial8250_port *p)
-{
- p->flags = ASYNC_SKIP_TEST | UPF_SHARE_IRQ | UPF_FIXED_TYPE;
- p->type = PORT_OCTEON;
- p->iotype = UPIO_MEM;
- p->regshift = 3; /* I/O addresses are every 8 bytes */
- p->uartclk = mips_hpt_frequency;
- p->serial_in = octeon_serial_in;
- p->serial_out = octeon_serial_out;
-}
-
-static int __init octeon_serial_init(void)
-{
- int enable_uart0;
- int enable_uart1;
- int enable_uart2;
- struct plat_serial8250_port *p;
-
-#ifdef CONFIG_CAVIUM_OCTEON_2ND_KERNEL
- /*
- * If we are configured to run as the second of two kernels,
- * disable uart0 and enable uart1. Uart0 is owned by the first
- * kernel
- */
- enable_uart0 = 0;
- enable_uart1 = 1;
-#else
- /*
- * We are configured for the first kernel. We'll enable uart0
- * if the bootloader told us to use 0, otherwise will enable
- * uart 1.
- */
- enable_uart0 = (octeon_get_boot_uart() == 0);
- enable_uart1 = (octeon_get_boot_uart() == 1);
-#ifdef CONFIG_KGDB
- enable_uart1 = 1;
-#endif
-#endif
-
- /* Right now CN52XX is the only chip with a third uart */
- enable_uart2 = OCTEON_IS_MODEL(OCTEON_CN52XX);
-
- p = octeon_uart8250_data;
- if (enable_uart0) {
- /* Add a ttyS device for hardware uart 0 */
- octeon_uart_set_common(p);
- p->membase = (void *) CVMX_MIO_UARTX_RBR(0);
- p->mapbase = CVMX_MIO_UARTX_RBR(0) & ((1ull << 49) - 1);
- p->irq = OCTEON_IRQ_UART0;
- p++;
- }
-
- if (enable_uart1) {
- /* Add a ttyS device for hardware uart 1 */
- octeon_uart_set_common(p);
- p->membase = (void *) CVMX_MIO_UARTX_RBR(1);
- p->mapbase = CVMX_MIO_UARTX_RBR(1) & ((1ull << 49) - 1);
- p->irq = OCTEON_IRQ_UART1;
- p++;
- }
- if (enable_uart2) {
- /* Add a ttyS device for hardware uart 2 */
- octeon_uart_set_common(p);
- p->membase = (void *) CVMX_MIO_UART2_RBR;
- p->mapbase = CVMX_MIO_UART2_RBR & ((1ull << 49) - 1);
- p->irq = OCTEON_IRQ_UART2;
- p++;
- }
-
- BUG_ON(p > &octeon_uart8250_data[OCTEON_MAX_UARTS]);
-
- return platform_device_register(&octeon_uart8250_device);
-}
-
-device_initcall(octeon_serial_init);
diff --git a/arch/mips/cavium-octeon/setup.c b/arch/mips/cavium-octeon/setup.c
index 5f4e49ba471..008e9c8b8ea 100644
--- a/arch/mips/cavium-octeon/setup.c
+++ b/arch/mips/cavium-octeon/setup.c
@@ -4,15 +4,20 @@
* for more details.
*
* Copyright (C) 2004-2007 Cavium Networks
- * Copyright (C) 2008 Wind River Systems
+ * Copyright (C) 2008, 2009 Wind River Systems
+ * written by Ralf Baechle <ralf@linux-mips.org>
*/
+#include <linux/compiler.h>
+#include <linux/vmalloc.h>
#include <linux/init.h>
+#include <linux/kernel.h>
#include <linux/console.h>
#include <linux/delay.h>
+#include <linux/export.h>
#include <linux/interrupt.h>
#include <linux/io.h>
-#include <linux/irq.h>
#include <linux/serial.h>
+#include <linux/smp.h>
#include <linux/types.h>
#include <linux/string.h> /* for memset */
#include <linux/tty.h>
@@ -20,11 +25,13 @@
#include <linux/platform_device.h>
#include <linux/serial_core.h>
#include <linux/serial_8250.h>
+#include <linux/of_fdt.h>
+#include <linux/libfdt.h>
+#include <linux/kexec.h>
#include <asm/processor.h>
#include <asm/reboot.h>
#include <asm/smp-ops.h>
-#include <asm/system.h>
#include <asm/irq_cpu.h>
#include <asm/mipsregs.h>
#include <asm/bootinfo.h>
@@ -32,12 +39,8 @@
#include <asm/time.h>
#include <asm/octeon/octeon.h>
-
-#ifdef CONFIG_CAVIUM_DECODE_RSL
-extern void cvmx_interrupt_rsl_decode(void);
-extern int __cvmx_interrupt_ecc_report_single_bit_errors;
-extern void cvmx_interrupt_rsl_enable(void);
-#endif
+#include <asm/octeon/pci-octeon.h>
+#include <asm/octeon/cvmx-mio-defs.h>
extern struct plat_smp_ops octeon_smp_ops;
@@ -45,9 +48,6 @@ extern struct plat_smp_ops octeon_smp_ops;
extern void pci_console_init(const char *arg);
#endif
-#ifdef CONFIG_CAVIUM_RESERVE32
-extern uint64_t octeon_reserve32_memory;
-#endif
static unsigned long long MAX_MEMORY = 512ull << 20;
struct octeon_boot_descriptor *octeon_boot_desc_ptr;
@@ -55,11 +55,211 @@ struct octeon_boot_descriptor *octeon_boot_desc_ptr;
struct cvmx_bootinfo *octeon_bootinfo;
EXPORT_SYMBOL(octeon_bootinfo);
+static unsigned long long RESERVE_LOW_MEM = 0ull;
+#ifdef CONFIG_KEXEC
+#ifdef CONFIG_SMP
+/*
+ * Wait for relocation code is prepared and send
+ * secondary CPUs to spin until kernel is relocated.
+ */
+static void octeon_kexec_smp_down(void *ignored)
+{
+ int cpu = smp_processor_id();
+
+ local_irq_disable();
+ set_cpu_online(cpu, false);
+ while (!atomic_read(&kexec_ready_to_reboot))
+ cpu_relax();
+
+ asm volatile (
+ " sync \n"
+ " synci ($0) \n");
+
+ relocated_kexec_smp_wait(NULL);
+}
+#endif
+
+#define OCTEON_DDR0_BASE (0x0ULL)
+#define OCTEON_DDR0_SIZE (0x010000000ULL)
+#define OCTEON_DDR1_BASE (0x410000000ULL)
+#define OCTEON_DDR1_SIZE (0x010000000ULL)
+#define OCTEON_DDR2_BASE (0x020000000ULL)
+#define OCTEON_DDR2_SIZE (0x3e0000000ULL)
+#define OCTEON_MAX_PHY_MEM_SIZE (16*1024*1024*1024ULL)
+
+static struct kimage *kimage_ptr;
+
+static void kexec_bootmem_init(uint64_t mem_size, uint32_t low_reserved_bytes)
+{
+ int64_t addr;
+ struct cvmx_bootmem_desc *bootmem_desc;
+
+ bootmem_desc = cvmx_bootmem_get_desc();
+
+ if (mem_size > OCTEON_MAX_PHY_MEM_SIZE) {
+ mem_size = OCTEON_MAX_PHY_MEM_SIZE;
+ pr_err("Error: requested memory too large,"
+ "truncating to maximum size\n");
+ }
+
+ bootmem_desc->major_version = CVMX_BOOTMEM_DESC_MAJ_VER;
+ bootmem_desc->minor_version = CVMX_BOOTMEM_DESC_MIN_VER;
+
+ addr = (OCTEON_DDR0_BASE + RESERVE_LOW_MEM + low_reserved_bytes);
+ bootmem_desc->head_addr = 0;
+
+ if (mem_size <= OCTEON_DDR0_SIZE) {
+ __cvmx_bootmem_phy_free(addr,
+ mem_size - RESERVE_LOW_MEM -
+ low_reserved_bytes, 0);
+ return;
+ }
+
+ __cvmx_bootmem_phy_free(addr,
+ OCTEON_DDR0_SIZE - RESERVE_LOW_MEM -
+ low_reserved_bytes, 0);
+
+ mem_size -= OCTEON_DDR0_SIZE;
+
+ if (mem_size > OCTEON_DDR1_SIZE) {
+ __cvmx_bootmem_phy_free(OCTEON_DDR1_BASE, OCTEON_DDR1_SIZE, 0);
+ __cvmx_bootmem_phy_free(OCTEON_DDR2_BASE,
+ mem_size - OCTEON_DDR1_SIZE, 0);
+ } else
+ __cvmx_bootmem_phy_free(OCTEON_DDR1_BASE, mem_size, 0);
+}
+
+static int octeon_kexec_prepare(struct kimage *image)
+{
+ int i;
+ char *bootloader = "kexec";
+
+ octeon_boot_desc_ptr->argc = 0;
+ for (i = 0; i < image->nr_segments; i++) {
+ if (!strncmp(bootloader, (char *)image->segment[i].buf,
+ strlen(bootloader))) {
+ /*
+ * convert command line string to array
+ * of parameters (as bootloader does).
+ */
+ int argc = 0, offt;
+ char *str = (char *)image->segment[i].buf;
+ char *ptr = strchr(str, ' ');
+ while (ptr && (OCTEON_ARGV_MAX_ARGS > argc)) {
+ *ptr = '\0';
+ if (ptr[1] != ' ') {
+ offt = (int)(ptr - str + 1);
+ octeon_boot_desc_ptr->argv[argc] =
+ image->segment[i].mem + offt;
+ argc++;
+ }
+ ptr = strchr(ptr + 1, ' ');
+ }
+ octeon_boot_desc_ptr->argc = argc;
+ break;
+ }
+ }
+
+ /*
+ * Information about segments will be needed during pre-boot memory
+ * initialization.
+ */
+ kimage_ptr = image;
+ return 0;
+}
+
+static void octeon_generic_shutdown(void)
+{
+ int i;
+#ifdef CONFIG_SMP
+ int cpu;
+#endif
+ struct cvmx_bootmem_desc *bootmem_desc;
+ void *named_block_array_ptr;
+
+ bootmem_desc = cvmx_bootmem_get_desc();
+ named_block_array_ptr =
+ cvmx_phys_to_ptr(bootmem_desc->named_block_array_addr);
+
+#ifdef CONFIG_SMP
+ /* disable watchdogs */
+ for_each_online_cpu(cpu)
+ cvmx_write_csr(CVMX_CIU_WDOGX(cpu_logical_map(cpu)), 0);
+#else
+ cvmx_write_csr(CVMX_CIU_WDOGX(cvmx_get_core_num()), 0);
+#endif
+ if (kimage_ptr != kexec_crash_image) {
+ memset(named_block_array_ptr,
+ 0x0,
+ CVMX_BOOTMEM_NUM_NAMED_BLOCKS *
+ sizeof(struct cvmx_bootmem_named_block_desc));
+ /*
+ * Mark all memory (except low 0x100000 bytes) as free.
+ * It is the same thing that bootloader does.
+ */
+ kexec_bootmem_init(octeon_bootinfo->dram_size*1024ULL*1024ULL,
+ 0x100000);
+ /*
+ * Allocate all segments to avoid their corruption during boot.
+ */
+ for (i = 0; i < kimage_ptr->nr_segments; i++)
+ cvmx_bootmem_alloc_address(
+ kimage_ptr->segment[i].memsz + 2*PAGE_SIZE,
+ kimage_ptr->segment[i].mem - PAGE_SIZE,
+ PAGE_SIZE);
+ } else {
+ /*
+ * Do not mark all memory as free. Free only named sections
+ * leaving the rest of memory unchanged.
+ */
+ struct cvmx_bootmem_named_block_desc *ptr =
+ (struct cvmx_bootmem_named_block_desc *)
+ named_block_array_ptr;
+
+ for (i = 0; i < bootmem_desc->named_block_num_blocks; i++)
+ if (ptr[i].size)
+ cvmx_bootmem_free_named(ptr[i].name);
+ }
+ kexec_args[2] = 1UL; /* running on octeon_main_processor */
+ kexec_args[3] = (unsigned long)octeon_boot_desc_ptr;
+#ifdef CONFIG_SMP
+ secondary_kexec_args[2] = 0UL; /* running on secondary cpu */
+ secondary_kexec_args[3] = (unsigned long)octeon_boot_desc_ptr;
+#endif
+}
+
+static void octeon_shutdown(void)
+{
+ octeon_generic_shutdown();
+#ifdef CONFIG_SMP
+ smp_call_function(octeon_kexec_smp_down, NULL, 0);
+ smp_wmb();
+ while (num_online_cpus() > 1) {
+ cpu_relax();
+ mdelay(1);
+ }
+#endif
+}
+
+static void octeon_crash_shutdown(struct pt_regs *regs)
+{
+ octeon_generic_shutdown();
+ default_machine_crash_shutdown(regs);
+}
+
+#endif /* CONFIG_KEXEC */
+
#ifdef CONFIG_CAVIUM_RESERVE32
uint64_t octeon_reserve32_memory;
EXPORT_SYMBOL(octeon_reserve32_memory);
#endif
+#ifdef CONFIG_KEXEC
+/* crashkernel cmdline parameter is parsed _after_ memory setup
+ * we also parse it here (workaround for EHB5200) */
+static uint64_t crashk_size, crashk_base;
+#endif
+
static int octeon_uart;
extern asmlinkage void handle_int(void);
@@ -98,18 +298,27 @@ int octeon_is_pci_host(void)
*/
uint64_t octeon_get_clock_rate(void)
{
- if (octeon_is_simulation())
- octeon_bootinfo->eclock_hz = 6000000;
- return octeon_bootinfo->eclock_hz;
+ struct cvmx_sysinfo *sysinfo = cvmx_sysinfo_get();
+
+ return sysinfo->cpu_clock_hz;
}
EXPORT_SYMBOL(octeon_get_clock_rate);
+static u64 octeon_io_clock_rate;
+
+u64 octeon_get_io_clock_rate(void)
+{
+ return octeon_io_clock_rate;
+}
+EXPORT_SYMBOL(octeon_get_io_clock_rate);
+
+
/**
* Write to the LCD display connected to the bootbus. This display
* exists on most Cavium evaluation boards. If it doesn't exist, then
* this function doesn't do anything.
*
- * @s: String to write
+ * @s: String to write
*/
void octeon_write_lcd(const char *s)
{
@@ -131,7 +340,7 @@ void octeon_write_lcd(const char *s)
/**
* Return the console uart passed by the bootloader
*
- * Returns uart (0 or 1)
+ * Returns uart (0 or 1)
*/
int octeon_get_boot_uart(void)
{
@@ -186,54 +395,6 @@ void octeon_check_cpu_bist(void)
write_octeon_c0_dcacheerr(0);
}
-#ifdef CONFIG_CAVIUM_RESERVE32_USE_WIRED_TLB
-/**
- * Called on every core to setup the wired tlb entry needed
- * if CONFIG_CAVIUM_RESERVE32_USE_WIRED_TLB is set.
- *
- */
-static void octeon_hal_setup_per_cpu_reserved32(void *unused)
-{
- /*
- * The config has selected to wire the reserve32 memory for all
- * userspace applications. We need to put a wired TLB entry in for each
- * 512MB of reserve32 memory. We only handle double 256MB pages here,
- * so reserve32 must be multiple of 512MB.
- */
- uint32_t size = CONFIG_CAVIUM_RESERVE32;
- uint32_t entrylo0 =
- 0x7 | ((octeon_reserve32_memory & ((1ul << 40) - 1)) >> 6);
- uint32_t entrylo1 = entrylo0 + (256 << 14);
- uint32_t entryhi = (0x80000000UL - (CONFIG_CAVIUM_RESERVE32 << 20));
- while (size >= 512) {
-#if 0
- pr_info("CPU%d: Adding double wired TLB entry for 0x%lx\n",
- smp_processor_id(), entryhi);
-#endif
- add_wired_entry(entrylo0, entrylo1, entryhi, PM_256M);
- entrylo0 += 512 << 14;
- entrylo1 += 512 << 14;
- entryhi += 512 << 20;
- size -= 512;
- }
-}
-#endif /* CONFIG_CAVIUM_RESERVE32_USE_WIRED_TLB */
-
-/**
- * Called to release the named block which was used to made sure
- * that nobody used the memory for something else during
- * init. Now we'll free it so userspace apps can use this
- * memory region with bootmem_alloc.
- *
- * This function is called only once from prom_free_prom_memory().
- */
-void octeon_hal_setup_reserved32(void)
-{
-#ifdef CONFIG_CAVIUM_RESERVE32_USE_WIRED_TLB
- on_each_cpu(octeon_hal_setup_per_cpu_reserved32, NULL, 0, 1);
-#endif
-}
-
/**
* Reboot Octeon
*
@@ -263,13 +424,16 @@ static void octeon_restart(char *command)
*/
static void octeon_kill_core(void *arg)
{
- mb();
- if (octeon_is_simulation()) {
- /* The simulator needs the watchdog to stop for dead cores */
- cvmx_write_csr(CVMX_CIU_WDOGX(cvmx_get_core_num()), 0);
+ if (octeon_is_simulation())
/* A break instruction causes the simulator stop a core */
- asm volatile ("sync\nbreak");
- }
+ asm volatile ("break" ::: "memory");
+
+ local_irq_disable();
+ /* Disable watchdog on this core. */
+ cvmx_write_csr(CVMX_CIU_WDOGX(cvmx_get_core_num()), 0);
+ /* Spin in a low power mode. */
+ while (true)
+ asm volatile ("wait" ::: "memory");
}
@@ -294,30 +458,6 @@ static void octeon_halt(void)
octeon_kill_core(NULL);
}
-#if 0
-/**
- * Platform time init specifics.
- * Returns
- */
-void __init plat_time_init(void)
-{
- /* Nothing special here, but we are required to have one */
-}
-
-#endif
-
-/**
- * Handle all the error condition interrupts that might occur.
- *
- */
-#ifdef CONFIG_CAVIUM_DECODE_RSL
-static irqreturn_t octeon_rlm_interrupt(int cpl, void *dev_id)
-{
- cvmx_interrupt_rsl_decode();
- return IRQ_HANDLED;
-}
-#endif
-
/**
* Return a string representing the system type
*
@@ -340,7 +480,6 @@ void octeon_user_io_init(void)
union octeon_cvmemctl cvmmemctl;
union cvmx_iob_fau_timeout fau_timeout;
union cvmx_pow_nw_tim nm_tim;
- uint64_t cvmctl;
/* Get the current settings for CP0_CVMMEMCTL_REG */
cvmmemctl.u64 = read_c0_cvmmemctl();
@@ -408,8 +547,18 @@ void octeon_user_io_init(void)
cvmmemctl.s.wbfltime = 0;
/* R/W If set, do not put Istream in the L2 cache. */
cvmmemctl.s.istrnol2 = 0;
- /* R/W The write buffer threshold. */
- cvmmemctl.s.wbthresh = 10;
+
+ /*
+ * R/W The write buffer threshold. As per erratum Core-14752
+ * for CN63XX, a sc/scd might fail if the write buffer is
+ * full. Lowering WBTHRESH greatly lowers the chances of the
+ * write buffer ever being full and triggering the erratum.
+ */
+ if (OCTEON_IS_MODEL(OCTEON_CN63XX_PASS1_X))
+ cvmmemctl.s.wbthresh = 4;
+ else
+ cvmmemctl.s.wbthresh = 10;
+
/* R/W If set, CVMSEG is available for loads/stores in
* kernel/debug mode. */
#if CONFIG_CAVIUM_OCTEON_CVMSEG_SIZE > 0
@@ -427,20 +576,13 @@ void octeon_user_io_init(void)
* is max legal value. */
cvmmemctl.s.lmemsz = CONFIG_CAVIUM_OCTEON_CVMSEG_SIZE;
+ write_c0_cvmmemctl(cvmmemctl.u64);
if (smp_processor_id() == 0)
pr_notice("CVMSEG size: %d cache lines (%d bytes)\n",
CONFIG_CAVIUM_OCTEON_CVMSEG_SIZE,
CONFIG_CAVIUM_OCTEON_CVMSEG_SIZE * 128);
- write_c0_cvmmemctl(cvmmemctl.u64);
-
- /* Move the performance counter interrupts to IRQ 6 */
- cvmctl = read_c0_cvmctl();
- cvmctl &= ~(7 << 7);
- cvmctl |= 6 << 7;
- write_c0_cvmctl(cvmctl);
-
/* Set a default for the hardware timeouts */
fau_timeout.u64 = 0;
fau_timeout.s.tout_val = 0xfff;
@@ -463,10 +605,10 @@ void octeon_user_io_init(void)
void __init prom_init(void)
{
struct cvmx_sysinfo *sysinfo;
- const int coreid = cvmx_get_core_num();
+ const char *arg;
+ char *p;
int i;
int argc;
- struct uart_port octeon_port;
#ifdef CONFIG_CAVIUM_RESERVE32
int64_t addr = -1;
#endif
@@ -479,6 +621,41 @@ void __init prom_init(void)
cvmx_phys_to_ptr(octeon_boot_desc_ptr->cvmx_desc_vaddr);
cvmx_bootmem_init(cvmx_phys_to_ptr(octeon_bootinfo->phy_mem_desc_addr));
+ sysinfo = cvmx_sysinfo_get();
+ memset(sysinfo, 0, sizeof(*sysinfo));
+ sysinfo->system_dram_size = octeon_bootinfo->dram_size << 20;
+ sysinfo->phy_mem_desc_ptr =
+ cvmx_phys_to_ptr(octeon_bootinfo->phy_mem_desc_addr);
+ sysinfo->core_mask = octeon_bootinfo->core_mask;
+ sysinfo->exception_base_addr = octeon_bootinfo->exception_base_addr;
+ sysinfo->cpu_clock_hz = octeon_bootinfo->eclock_hz;
+ sysinfo->dram_data_rate_hz = octeon_bootinfo->dclock_hz * 2;
+ sysinfo->board_type = octeon_bootinfo->board_type;
+ sysinfo->board_rev_major = octeon_bootinfo->board_rev_major;
+ sysinfo->board_rev_minor = octeon_bootinfo->board_rev_minor;
+ memcpy(sysinfo->mac_addr_base, octeon_bootinfo->mac_addr_base,
+ sizeof(sysinfo->mac_addr_base));
+ sysinfo->mac_addr_count = octeon_bootinfo->mac_addr_count;
+ memcpy(sysinfo->board_serial_number,
+ octeon_bootinfo->board_serial_number,
+ sizeof(sysinfo->board_serial_number));
+ sysinfo->compact_flash_common_base_addr =
+ octeon_bootinfo->compact_flash_common_base_addr;
+ sysinfo->compact_flash_attribute_base_addr =
+ octeon_bootinfo->compact_flash_attribute_base_addr;
+ sysinfo->led_display_base_addr = octeon_bootinfo->led_display_base_addr;
+ sysinfo->dfa_ref_clock_hz = octeon_bootinfo->dfa_ref_clock_hz;
+ sysinfo->bootloader_config_flags = octeon_bootinfo->config_flags;
+
+ if (OCTEON_IS_MODEL(OCTEON_CN6XXX)) {
+ /* I/O clock runs at a different rate than the CPU. */
+ union cvmx_mio_rst_boot rst_boot;
+ rst_boot.u64 = cvmx_read_csr(CVMX_MIO_RST_BOOT);
+ octeon_io_clock_rate = 50000000 * rst_boot.s.pnr_mul;
+ } else {
+ octeon_io_clock_rate = sysinfo->cpu_clock_hz;
+ }
+
/*
* Only enable the LED controller if we're running on a CN38XX, CN58XX,
* or CN56XX. The CN30XX and CN31XX don't have an LED controller.
@@ -502,25 +679,13 @@ void __init prom_init(void)
* memory when it is getting memory from the
* bootloader. Later, after the memory allocations are
* complete, the reserve32 will be freed.
- */
-#ifdef CONFIG_CAVIUM_RESERVE32_USE_WIRED_TLB
- if (CONFIG_CAVIUM_RESERVE32 & 0x1ff)
- pr_err("CAVIUM_RESERVE32 isn't a multiple of 512MB. "
- "This is required if CAVIUM_RESERVE32_USE_WIRED_TLB "
- "is set\n");
- else
- addr = cvmx_bootmem_phy_named_block_alloc(CONFIG_CAVIUM_RESERVE32 << 20,
- 0, 0, 512 << 20,
- "CAVIUM_RESERVE32", 0);
-#else
- /*
+ *
* Allocate memory for RESERVED32 aligned on 2MB boundary. This
* is in case we later use hugetlb entries with it.
*/
addr = cvmx_bootmem_phy_named_block_alloc(CONFIG_CAVIUM_RESERVE32 << 20,
0, 0, 2 << 20,
"CAVIUM_RESERVE32", 0);
-#endif
if (addr < 0)
pr_err("Failed to allocate CAVIUM_RESERVE32 memory area\n");
else
@@ -531,7 +696,7 @@ void __init prom_init(void)
if (cvmx_read_csr(CVMX_L2D_FUS3) & (3ull << 34)) {
pr_info("Skipping L2 locking due to reduced L2 cache size\n");
} else {
- uint32_t ebase = read_c0_ebase() & 0x3ffff000;
+ uint32_t __maybe_unused ebase = read_c0_ebase() & 0x3ffff000;
#ifdef CONFIG_CAVIUM_OCTEON_LOCK_L2_TLB
/* TLB refill */
cvmx_l2c_lock_mem_region(ebase, 0x100);
@@ -554,64 +719,17 @@ void __init prom_init(void)
}
#endif
- sysinfo = cvmx_sysinfo_get();
- memset(sysinfo, 0, sizeof(*sysinfo));
- sysinfo->system_dram_size = octeon_bootinfo->dram_size << 20;
- sysinfo->phy_mem_desc_ptr =
- cvmx_phys_to_ptr(octeon_bootinfo->phy_mem_desc_addr);
- sysinfo->core_mask = octeon_bootinfo->core_mask;
- sysinfo->exception_base_addr = octeon_bootinfo->exception_base_addr;
- sysinfo->cpu_clock_hz = octeon_bootinfo->eclock_hz;
- sysinfo->dram_data_rate_hz = octeon_bootinfo->dclock_hz * 2;
- sysinfo->board_type = octeon_bootinfo->board_type;
- sysinfo->board_rev_major = octeon_bootinfo->board_rev_major;
- sysinfo->board_rev_minor = octeon_bootinfo->board_rev_minor;
- memcpy(sysinfo->mac_addr_base, octeon_bootinfo->mac_addr_base,
- sizeof(sysinfo->mac_addr_base));
- sysinfo->mac_addr_count = octeon_bootinfo->mac_addr_count;
- memcpy(sysinfo->board_serial_number,
- octeon_bootinfo->board_serial_number,
- sizeof(sysinfo->board_serial_number));
- sysinfo->compact_flash_common_base_addr =
- octeon_bootinfo->compact_flash_common_base_addr;
- sysinfo->compact_flash_attribute_base_addr =
- octeon_bootinfo->compact_flash_attribute_base_addr;
- sysinfo->led_display_base_addr = octeon_bootinfo->led_display_base_addr;
- sysinfo->dfa_ref_clock_hz = octeon_bootinfo->dfa_ref_clock_hz;
- sysinfo->bootloader_config_flags = octeon_bootinfo->config_flags;
-
-
octeon_check_cpu_bist();
octeon_uart = octeon_get_boot_uart();
- /*
- * Disable All CIU Interrupts. The ones we need will be
- * enabled later. Read the SUM register so we know the write
- * completed.
- */
- cvmx_write_csr(CVMX_CIU_INTX_EN0((coreid * 2)), 0);
- cvmx_write_csr(CVMX_CIU_INTX_EN0((coreid * 2 + 1)), 0);
- cvmx_write_csr(CVMX_CIU_INTX_EN1((coreid * 2)), 0);
- cvmx_write_csr(CVMX_CIU_INTX_EN1((coreid * 2 + 1)), 0);
- cvmx_read_csr(CVMX_CIU_INTX_SUM0((coreid * 2)));
-
#ifdef CONFIG_SMP
octeon_write_lcd("LinuxSMP");
#else
octeon_write_lcd("Linux");
#endif
-#ifdef CONFIG_CAVIUM_GDB
- /*
- * When debugging the linux kernel, force the cores to enter
- * the debug exception handler to break in.
- */
- if (octeon_get_boot_debug_flag()) {
- cvmx_write_csr(CVMX_CIU_DINT, 1 << cvmx_get_core_num());
- cvmx_read_csr(CVMX_CIU_DINT);
- }
-#endif
+ octeon_setup_delays();
/*
* BIST should always be enabled when doing a soft reset. L2
@@ -629,6 +747,15 @@ void __init prom_init(void)
if (octeon_is_simulation())
MAX_MEMORY = 64ull << 20;
+ arg = strstr(arcs_cmdline, "mem=");
+ if (arg) {
+ MAX_MEMORY = memparse(arg + 4, &p);
+ if (MAX_MEMORY == 0)
+ MAX_MEMORY = 32ull << 30;
+ if (*p == '@')
+ RESERVE_LOW_MEM = memparse(p + 1, &p);
+ }
+
arcs_cmdline[0] = 0;
argc = octeon_boot_desc_ptr->argc;
for (i = 0; i < argc; i++) {
@@ -636,15 +763,23 @@ void __init prom_init(void)
cvmx_phys_to_ptr(octeon_boot_desc_ptr->argv[i]);
if ((strncmp(arg, "MEM=", 4) == 0) ||
(strncmp(arg, "mem=", 4) == 0)) {
- sscanf(arg + 4, "%llu", &MAX_MEMORY);
- MAX_MEMORY <<= 20;
+ MAX_MEMORY = memparse(arg + 4, &p);
if (MAX_MEMORY == 0)
MAX_MEMORY = 32ull << 30;
- } else if (strcmp(arg, "ecc_verbose") == 0) {
-#ifdef CONFIG_CAVIUM_REPORT_SINGLE_BIT_ECC
- __cvmx_interrupt_ecc_report_single_bit_errors = 1;
- pr_notice("Reporting of single bit ECC errors is "
- "turned on\n");
+ if (*p == '@')
+ RESERVE_LOW_MEM = memparse(p + 1, &p);
+#ifdef CONFIG_KEXEC
+ } else if (strncmp(arg, "crashkernel=", 12) == 0) {
+ crashk_size = memparse(arg+12, &p);
+ if (*p == '@')
+ crashk_base = memparse(p+1, &p);
+ strcat(arcs_cmdline, " ");
+ strcat(arcs_cmdline, arg);
+ /*
+ * To do: switch parsing to new style, something like:
+ * parse_crashkernel(arg, sysinfo->system_dram_size,
+ * &crashk_size, &crashk_base);
+ */
#endif
} else if (strlen(arcs_cmdline) + strlen(arg) + 1 <
sizeof(arcs_cmdline) - 1) {
@@ -654,9 +789,6 @@ void __init prom_init(void)
}
if (strstr(arcs_cmdline, "console=") == NULL) {
-#ifdef CONFIG_GDB_CONSOLE
- strcat(arcs_cmdline, " console=gdb");
-#else
#ifdef CONFIG_CAVIUM_OCTEON_2ND_KERNEL
strcat(arcs_cmdline, " console=ttyS0,115200");
#else
@@ -665,7 +797,6 @@ void __init prom_init(void)
else
strcat(arcs_cmdline, " console=ttyS0,115200");
#endif
-#endif
}
if (octeon_is_simulation()) {
@@ -674,8 +805,7 @@ void __init prom_init(void)
* the filesystem. Also specify the calibration delay
* to avoid calculating it every time.
*/
- strcat(arcs_cmdline, " rw root=1f00"
- " lpj=60176 slram=root,0x40000000,+1073741824");
+ strcat(arcs_cmdline, " rw root=1f00 slram=root,0x40000000,+1073741824");
}
mips_hpt_frequency = octeon_get_clock_rate();
@@ -685,61 +815,70 @@ void __init prom_init(void)
_machine_restart = octeon_restart;
_machine_halt = octeon_halt;
- memset(&octeon_port, 0, sizeof(octeon_port));
- /*
- * For early_serial_setup we don't set the port type or
- * UPF_FIXED_TYPE.
- */
- octeon_port.flags = ASYNC_SKIP_TEST | UPF_SHARE_IRQ;
- octeon_port.iotype = UPIO_MEM;
- /* I/O addresses are every 8 bytes */
- octeon_port.regshift = 3;
- /* Clock rate of the chip */
- octeon_port.uartclk = mips_hpt_frequency;
- octeon_port.fifosize = 64;
- octeon_port.mapbase = 0x0001180000000800ull + (1024 * octeon_uart);
- octeon_port.membase = cvmx_phys_to_ptr(octeon_port.mapbase);
- octeon_port.serial_in = octeon_serial_in;
- octeon_port.serial_out = octeon_serial_out;
-#ifdef CONFIG_CAVIUM_OCTEON_2ND_KERNEL
- octeon_port.line = 0;
-#else
- octeon_port.line = octeon_uart;
+#ifdef CONFIG_KEXEC
+ _machine_kexec_shutdown = octeon_shutdown;
+ _machine_crash_shutdown = octeon_crash_shutdown;
+ _machine_kexec_prepare = octeon_kexec_prepare;
#endif
- octeon_port.irq = 42 + octeon_uart;
- early_serial_setup(&octeon_port);
octeon_user_io_init();
register_smp_ops(&octeon_smp_ops);
}
+/* Exclude a single page from the regions obtained in plat_mem_setup. */
+#ifndef CONFIG_CRASH_DUMP
+static __init void memory_exclude_page(u64 addr, u64 *mem, u64 *size)
+{
+ if (addr > *mem && addr < *mem + *size) {
+ u64 inc = addr - *mem;
+ add_memory_region(*mem, inc, BOOT_MEM_RAM);
+ *mem += inc;
+ *size -= inc;
+ }
+
+ if (addr == *mem && *size > PAGE_SIZE) {
+ *mem += PAGE_SIZE;
+ *size -= PAGE_SIZE;
+ }
+}
+#endif /* CONFIG_CRASH_DUMP */
+
void __init plat_mem_setup(void)
{
uint64_t mem_alloc_size;
uint64_t total;
+ uint64_t crashk_end;
+#ifndef CONFIG_CRASH_DUMP
int64_t memory;
+ uint64_t kernel_start;
+ uint64_t kernel_size;
+#endif
total = 0;
-
- /* First add the init memory we will be returning. */
- memory = __pa_symbol(&__init_begin) & PAGE_MASK;
- mem_alloc_size = (__pa_symbol(&__init_end) & PAGE_MASK) - memory;
- if (mem_alloc_size > 0) {
- add_memory_region(memory, mem_alloc_size, BOOT_MEM_RAM);
- total += mem_alloc_size;
- }
+ crashk_end = 0;
/*
* The Mips memory init uses the first memory location for
* some memory vectors. When SPARSEMEM is in use, it doesn't
* verify that the size is big enough for the final
* vectors. Making the smallest chuck 4MB seems to be enough
- * to consistantly work.
+ * to consistently work.
*/
mem_alloc_size = 4 << 20;
if (mem_alloc_size > MAX_MEMORY)
mem_alloc_size = MAX_MEMORY;
+/* Crashkernel ignores bootmem list. It relies on mem=X@Y option */
+#ifdef CONFIG_CRASH_DUMP
+ add_memory_region(RESERVE_LOW_MEM, MAX_MEMORY, BOOT_MEM_RAM);
+ total += MAX_MEMORY;
+#else
+#ifdef CONFIG_KEXEC
+ if (crashk_size > 0) {
+ add_memory_region(crashk_base, crashk_size, BOOT_MEM_RAM);
+ crashk_end = crashk_base + crashk_size;
+ }
+#endif
/*
* When allocating memory, we want incrementing addresses from
* bootmem_alloc so the code in add_memory_region can merge
@@ -748,33 +887,88 @@ void __init plat_mem_setup(void)
cvmx_bootmem_lock();
while ((boot_mem_map.nr_map < BOOT_MEM_MAP_MAX)
&& (total < MAX_MEMORY)) {
-#if defined(CONFIG_64BIT) || defined(CONFIG_64BIT_PHYS_ADDR)
memory = cvmx_bootmem_phy_alloc(mem_alloc_size,
__pa_symbol(&__init_end), -1,
0x100000,
CVMX_BOOTMEM_FLAG_NO_LOCKING);
-#elif defined(CONFIG_HIGHMEM)
- memory = cvmx_bootmem_phy_alloc(mem_alloc_size, 0, 1ull << 31,
- 0x100000,
- CVMX_BOOTMEM_FLAG_NO_LOCKING);
-#else
- memory = cvmx_bootmem_phy_alloc(mem_alloc_size, 0, 512 << 20,
- 0x100000,
- CVMX_BOOTMEM_FLAG_NO_LOCKING);
-#endif
if (memory >= 0) {
+ u64 size = mem_alloc_size;
+#ifdef CONFIG_KEXEC
+ uint64_t end;
+#endif
+
+ /*
+ * exclude a page at the beginning and end of
+ * the 256MB PCIe 'hole' so the kernel will not
+ * try to allocate multi-page buffers that
+ * span the discontinuity.
+ */
+ memory_exclude_page(CVMX_PCIE_BAR1_PHYS_BASE,
+ &memory, &size);
+ memory_exclude_page(CVMX_PCIE_BAR1_PHYS_BASE +
+ CVMX_PCIE_BAR1_PHYS_SIZE,
+ &memory, &size);
+#ifdef CONFIG_KEXEC
+ end = memory + mem_alloc_size;
+
/*
- * This function automatically merges address
- * regions next to each other if they are
- * received in incrementing order.
+ * This function automatically merges address regions
+ * next to each other if they are received in
+ * incrementing order
*/
+ if (memory < crashk_base && end > crashk_end) {
+ /* region is fully in */
+ add_memory_region(memory,
+ crashk_base - memory,
+ BOOT_MEM_RAM);
+ total += crashk_base - memory;
+ add_memory_region(crashk_end,
+ end - crashk_end,
+ BOOT_MEM_RAM);
+ total += end - crashk_end;
+ continue;
+ }
+
+ if (memory >= crashk_base && end <= crashk_end)
+ /*
+ * Entire memory region is within the new
+ * kernel's memory, ignore it.
+ */
+ continue;
+
+ if (memory > crashk_base && memory < crashk_end &&
+ end > crashk_end) {
+ /*
+ * Overlap with the beginning of the region,
+ * reserve the beginning.
+ */
+ mem_alloc_size -= crashk_end - memory;
+ memory = crashk_end;
+ } else if (memory < crashk_base && end > crashk_base &&
+ end < crashk_end)
+ /*
+ * Overlap with the beginning of the region,
+ * chop of end.
+ */
+ mem_alloc_size -= end - crashk_base;
+#endif
add_memory_region(memory, mem_alloc_size, BOOT_MEM_RAM);
total += mem_alloc_size;
+ /* Recovering mem_alloc_size */
+ mem_alloc_size = 4 << 20;
} else {
break;
}
}
cvmx_bootmem_unlock();
+ /* Add the memory region for the kernel. */
+ kernel_start = (unsigned long) _text;
+ kernel_size = _end - _text;
+
+ /* Adjust for physical offset. */
+ kernel_start &= ~0xffffffff80000000ULL;
+ add_memory_region(kernel_start, kernel_size, BOOT_MEM_RAM);
+#endif /* CONFIG_CRASH_DUMP */
#ifdef CONFIG_CAVIUM_RESERVE32
/*
@@ -788,10 +982,13 @@ void __init plat_mem_setup(void)
if (total == 0)
panic("Unable to allocate memory from "
- "cvmx_bootmem_phy_alloc\n");
+ "cvmx_bootmem_phy_alloc");
}
-
+/*
+ * Emit one character to the boot UART. Exported for use by the
+ * watchdog timer.
+ */
int prom_putchar(char c)
{
uint64_t lsrval;
@@ -802,126 +999,144 @@ int prom_putchar(char c)
} while ((lsrval & 0x20) == 0);
/* Write the byte */
- cvmx_write_csr(CVMX_MIO_UARTX_THR(octeon_uart), c);
+ cvmx_write_csr(CVMX_MIO_UARTX_THR(octeon_uart), c & 0xffull);
return 1;
}
+EXPORT_SYMBOL(prom_putchar);
void prom_free_prom_memory(void)
{
-#ifdef CONFIG_CAVIUM_DECODE_RSL
- cvmx_interrupt_rsl_enable();
-
- /* Add an interrupt handler for general failures. */
- if (request_irq(OCTEON_IRQ_RML, octeon_rlm_interrupt, IRQF_SHARED,
- "RML/RSL", octeon_rlm_interrupt)) {
- panic("Unable to request_irq(OCTEON_IRQ_RML)\n");
+ if (OCTEON_IS_MODEL(OCTEON_CN63XX_PASS1_X)) {
+ /* Check for presence of Core-14449 fix. */
+ u32 insn;
+ u32 *foo;
+
+ foo = &insn;
+
+ asm volatile("# before" : : : "memory");
+ prefetch(foo);
+ asm volatile(
+ ".set push\n\t"
+ ".set noreorder\n\t"
+ "bal 1f\n\t"
+ "nop\n"
+ "1:\tlw %0,-12($31)\n\t"
+ ".set pop\n\t"
+ : "=r" (insn) : : "$31", "memory");
+
+ if ((insn >> 26) != 0x33)
+ panic("No PREF instruction at Core-14449 probe point.");
+
+ if (((insn >> 16) & 0x1f) != 28)
+ panic("Core-14449 WAR not in place (%04x).\n"
+ "Please build kernel with proper options (CONFIG_CAVIUM_CN63XXP1).", insn);
}
-#endif
-
- /* This call is here so that it is performed after any TLB
- initializations. It needs to be after these in case the
- CONFIG_CAVIUM_RESERVE32_USE_WIRED_TLB option is set */
- octeon_hal_setup_reserved32();
}
-static struct octeon_cf_data octeon_cf_data;
+int octeon_prune_device_tree(void);
-static int __init octeon_cf_device_init(void)
+extern const char __dtb_octeon_3xxx_begin;
+extern const char __dtb_octeon_68xx_begin;
+void __init device_tree_init(void)
{
- union cvmx_mio_boot_reg_cfgx mio_boot_reg_cfg;
- unsigned long base_ptr, region_base, region_size;
- struct platform_device *pd;
- struct resource cf_resources[3];
- unsigned int num_resources;
- int i;
- int ret = 0;
-
- /* Setup octeon-cf platform device if present. */
- 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;
+ const void *fdt;
+ bool do_prune;
+
+ if (octeon_bootinfo->minor_version >= 3 && octeon_bootinfo->fdt_addr) {
+ fdt = phys_to_virt(octeon_bootinfo->fdt_addr);
+ if (fdt_check_header(fdt))
+ panic("Corrupt Device Tree passed to kernel.");
+ do_prune = false;
+ } else if (OCTEON_IS_MODEL(OCTEON_CN68XX)) {
+ fdt = &__dtb_octeon_68xx_begin;
+ do_prune = true;
} else {
- base_ptr = 0x1d000800;
+ fdt = &__dtb_octeon_3xxx_begin;
+ do_prune = true;
}
- if (!base_ptr)
- return ret;
+ initial_boot_params = (void *)fdt;
- /* Find CS0 region. */
- for (i = 0; i < 8; i++) {
- mio_boot_reg_cfg.u64 = cvmx_read_csr(CVMX_MIO_BOOT_REG_CFGX(i));
- 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 (i >= 7) {
- /* i and i + 1 are CS0 and CS1, both must be less than 8. */
- goto out;
+ if (do_prune) {
+ octeon_prune_device_tree();
+ pr_info("Using internal Device Tree.\n");
+ } else {
+ pr_info("Using passed Device Tree.\n");
}
- octeon_cf_data.base_region = i;
- octeon_cf_data.is16bit = mio_boot_reg_cfg.s.width;
- octeon_cf_data.base_region_bias = base_ptr - region_base;
- memset(cf_resources, 0, sizeof(cf_resources));
- num_resources = 0;
- cf_resources[num_resources].flags = IORESOURCE_MEM;
- cf_resources[num_resources].start = region_base;
- cf_resources[num_resources].end = region_base + region_size - 1;
- num_resources++;
-
-
- if (!(base_ptr & 0xfffful)) {
- /*
- * Boot loader signals availability of DMA (true_ide
- * mode) by setting low order bits of base_ptr to
- * zero.
- */
+ unflatten_and_copy_device_tree();
+}
- /* Asume that CS1 immediately follows. */
- mio_boot_reg_cfg.u64 =
- cvmx_read_csr(CVMX_MIO_BOOT_REG_CFGX(i + 1));
- 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)
- goto out;
-
- cf_resources[num_resources].flags = IORESOURCE_MEM;
- cf_resources[num_resources].start = region_base;
- cf_resources[num_resources].end = region_base + region_size - 1;
- num_resources++;
-
- octeon_cf_data.dma_engine = 0;
- cf_resources[num_resources].flags = IORESOURCE_IRQ;
- cf_resources[num_resources].start = OCTEON_IRQ_BOOTDMA;
- cf_resources[num_resources].end = OCTEON_IRQ_BOOTDMA;
- num_resources++;
- } else {
- octeon_cf_data.dma_engine = -1;
+static int __initdata disable_octeon_edac_p;
+
+static int __init disable_octeon_edac(char *str)
+{
+ disable_octeon_edac_p = 1;
+ return 0;
+}
+early_param("disable_octeon_edac", disable_octeon_edac);
+
+static char *edac_device_names[] = {
+ "octeon_l2c_edac",
+ "octeon_pc_edac",
+};
+
+static int __init edac_devinit(void)
+{
+ struct platform_device *dev;
+ int i, err = 0;
+ int num_lmc;
+ char *name;
+
+ if (disable_octeon_edac_p)
+ return 0;
+
+ for (i = 0; i < ARRAY_SIZE(edac_device_names); i++) {
+ name = edac_device_names[i];
+ dev = platform_device_register_simple(name, -1, NULL, 0);
+ if (IS_ERR(dev)) {
+ pr_err("Registation of %s failed!\n", name);
+ err = PTR_ERR(dev);
+ }
}
- pd = platform_device_alloc("pata_octeon_cf", -1);
- if (!pd) {
- ret = -ENOMEM;
- goto out;
+ num_lmc = OCTEON_IS_MODEL(OCTEON_CN68XX) ? 4 :
+ (OCTEON_IS_MODEL(OCTEON_CN56XX) ? 2 : 1);
+ for (i = 0; i < num_lmc; i++) {
+ dev = platform_device_register_simple("octeon_lmc_edac",
+ i, NULL, 0);
+ if (IS_ERR(dev)) {
+ pr_err("Registation of octeon_lmc_edac %d failed!\n", i);
+ err = PTR_ERR(dev);
+ }
}
- pd->dev.platform_data = &octeon_cf_data;
- ret = platform_device_add_resources(pd, cf_resources, num_resources);
- if (ret)
- goto fail;
+ return err;
+}
+device_initcall(edac_devinit);
- ret = platform_device_add(pd);
- if (ret)
- goto fail;
+static void __initdata *octeon_dummy_iospace;
- return ret;
-fail:
- platform_device_put(pd);
-out:
- return ret;
+static int __init octeon_no_pci_init(void)
+{
+ /*
+ * Initially assume there is no PCI. The PCI/PCIe platform code will
+ * later re-initialize these to correct values if they are present.
+ */
+ octeon_dummy_iospace = vzalloc(IO_SPACE_LIMIT);
+ set_io_port_base((unsigned long)octeon_dummy_iospace);
+ ioport_resource.start = MAX_RESOURCE;
+ ioport_resource.end = 0;
+ return 0;
+}
+core_initcall(octeon_no_pci_init);
+
+static int __init octeon_no_pci_release(void)
+{
+ /*
+ * Release the allocated memory if a real IO space is there.
+ */
+ if ((unsigned long)octeon_dummy_iospace != mips_io_port_base)
+ vfree(octeon_dummy_iospace);
+ return 0;
}
-device_initcall(octeon_cf_device_init);
+late_initcall(octeon_no_pci_release);
diff --git a/arch/mips/cavium-octeon/smp.c b/arch/mips/cavium-octeon/smp.c
index 24e0ad63980..a7b3ae104d8 100644
--- a/arch/mips/cavium-octeon/smp.c
+++ b/arch/mips/cavium-octeon/smp.c
@@ -3,9 +3,9 @@
* License. See the file "COPYING" in the main directory of this archive
* for more details.
*
- * Copyright (C) 2004-2008 Cavium Networks
+ * Copyright (C) 2004-2008, 2009, 2010 Cavium Networks
*/
-#include <linux/init.h>
+#include <linux/cpu.h>
#include <linux/delay.h>
#include <linux/smp.h>
#include <linux/interrupt.h>
@@ -14,28 +14,37 @@
#include <linux/module.h>
#include <asm/mmu_context.h>
-#include <asm/system.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));
+ 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)
@@ -45,7 +54,7 @@ static irqreturn_t mailbox_interrupt(int irq, void *dev_id)
/**
* Cause the function described by call_data to be executed on the passed
- * cpu. When the function has finished, increment the finished field of
+ * cpu. When the function has finished, increment the finished field of
* call_data.
*/
void octeon_send_ipi_single(int cpu, unsigned int action)
@@ -58,39 +67,79 @@ void octeon_send_ipi_single(int cpu, unsigned int action)
cvmx_write_csr(CVMX_CIU_MBOX_SETX(coreid), action);
}
-static inline void octeon_send_ipi_mask(cpumask_t mask, unsigned int action)
+static inline void octeon_send_ipi_mask(const struct cpumask *mask,
+ unsigned int action)
{
unsigned int i;
- for_each_cpu_mask(i, mask)
+ for_each_cpu_mask(i, *mask)
octeon_send_ipi_single(i, action);
}
/**
- * Detect available CPUs, populate phys_cpu_present_map
+ * 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);
+ }
- cpus_clear(cpu_possible_map);
__cpu_number_map[coreid] = 0;
__cpu_logical_map[0] = coreid;
- cpu_set(0, cpu_possible_map);
+ /* The present CPUs get the lowest CPU numbers. */
cpus = 1;
- for (id = 0; id < 16; id++) {
+ for (id = 0; id < NR_CPUS; id++) {
if ((id != coreid) && (core_mask & (1 << id))) {
- cpu_set(cpus, cpu_possible_map);
+ 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();
}
/**
@@ -125,25 +174,16 @@ static void octeon_boot_secondary(int cpu, struct task_struct *idle)
*/
static void octeon_init_secondary(void)
{
- const int coreid = cvmx_get_core_num();
- union cvmx_ciu_intx_sum0 interrupt_enable;
+ 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();
- /*
- pr_info("SMP: CPU%d (CoreId %lu) started\n", cpu, coreid);
- */
- /* Enable Mailbox interrupts to this core. These are the only
- interrupts allowed on line 3 */
- cvmx_write_csr(CVMX_CIU_MBOX_CLRX(coreid), 0xffffffff);
- interrupt_enable.u64 = 0;
- interrupt_enable.s.mbox = 0x3;
- cvmx_write_csr(CVMX_CIU_INTX_EN0((coreid * 2)), interrupt_enable.u64);
- cvmx_write_csr(CVMX_CIU_INTX_EN0((coreid * 2 + 1)), 0);
- cvmx_write_csr(CVMX_CIU_INTX_EN1((coreid * 2)), 0);
- cvmx_write_csr(CVMX_CIU_INTX_EN1((coreid * 2 + 1)), 0);
- /* Enable core interrupt processing for 2,3 and 7 */
- set_c0_status(0x8c01);
+
+ octeon_irq_setup_secondary();
}
/**
@@ -152,14 +192,23 @@ static void octeon_init_secondary(void)
*/
void octeon_prepare_cpus(unsigned int max_cpus)
{
- cvmx_write_csr(CVMX_CIU_MBOX_CLRX(cvmx_get_core_num()), 0xffffffff);
- if (request_irq(OCTEON_IRQ_MBOX0, mailbox_interrupt, IRQF_SHARED,
- "mailbox0", mailbox_interrupt)) {
- panic("Cannot request_irq(OCTEON_IRQ_MBOX0)\n");
- }
- if (request_irq(OCTEON_IRQ_MBOX1, mailbox_interrupt, IRQF_SHARED,
- "mailbox1", mailbox_interrupt)) {
- panic("Cannot request_irq(OCTEON_IRQ_MBOX1)\n");
+#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)");
}
}
@@ -169,43 +218,174 @@ void octeon_prepare_cpus(unsigned int max_cpus)
*/
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);
+
+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();
+ octeon_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 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 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
};