Commit 5b3b1688 authored by David Daney's avatar David Daney Committed by Ralf Baechle
Browse files

MIPS: Add Cavium OCTEON processor support files to arch/mips/cavium-octeon.



These are the rest of the new files needed to add OCTEON processor
support to the Linux kernel.  Other than Makefile and Kconfig which
should be obvious, we have:

csrc-octeon.c   -- Clock source driver for OCTEON.
dma-octeon.c    -- Helper functions for mapping DMA memory.
flash_setup.c   -- Register on-board flash with the MTD subsystem.
octeon-irq.c    -- OCTEON interrupt controller managment.
octeon-memcpy.S -- Optimized memcpy() implementation.
serial.c        -- Register 8250 platform driver and early console.
setup.c         -- Early architecture initialization.
smp.c           -- OCTEON SMP support.
octeon_switch.S -- Scheduler context switch for OCTEON.
c-octeon.c      -- OCTEON cache controller support.
cex-oct.S       -- OCTEON cache exception handler.

asm/mach-cavium-octeon/*.h -- Architecture include files.

Signed-off-by: default avatarTomaso Paoletti <tpaoletti@caviumnetworks.com>
Signed-off-by: default avatarDavid Daney <ddaney@caviumnetworks.com>
Signed-off-by: default avatarRalf Baechle <ralf@linux-mips.org>

 create mode 100644 arch/mips/cavium-octeon/Kconfig
 create mode 100644 arch/mips/cavium-octeon/Makefile
 create mode 100644 arch/mips/cavium-octeon/csrc-octeon.c
 create mode 100644 arch/mips/cavium-octeon/dma-octeon.c
 create mode 100644 arch/mips/cavium-octeon/flash_setup.c
 create mode 100644 arch/mips/cavium-octeon/octeon-irq.c
 create mode 100644 arch/mips/cavium-octeon/octeon-memcpy.S
 create mode 100644 arch/mips/cavium-octeon/serial.c
 create mode 100644 arch/mips/cavium-octeon/setup.c
 create mode 100644 arch/mips/cavium-octeon/smp.c
 create mode 100644 arch/mips/include/asm/mach-cavium-octeon/cpu-feature-overrides.h
 create mode 100644 arch/mips/include/asm/mach-cavium-octeon/dma-coherence.h
 create mode 100644 arch/mips/include/asm/mach-cavium-octeon/irq.h
 create mode 100644 arch/mips/include/asm/mach-cavium-octeon/kernel-entry-init.h
 create mode 100644 arch/mips/include/asm/mach-cavium-octeon/war.h
 create mode 100644 arch/mips/include/asm/octeon/octeon.h
 create mode 100644 arch/mips/kernel/octeon_switch.S
 create mode 100644 arch/mips/mm/c-octeon.c
 create mode 100644 arch/mips/mm/cex-oct.S
parent 58f07778
config CAVIUM_OCTEON_SPECIFIC_OPTIONS
bool "Enable Octeon specific options"
depends on CPU_CAVIUM_OCTEON
default "y"
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
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.
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
CVMSEG LM is a segment that accesses portions of the dcache as a
local memory; the larger CVMSEG is, the smaller the cache is.
This selects the size of CVMSEG LM, which is in cache blocks. The
legally range is from zero to 54 cache blocks (i.e. CVMSEG LM is
between zero and 6192 bytes).
config CAVIUM_OCTEON_LOCK_L2
bool "Lock often used kernel code in the L2"
depends on CAVIUM_OCTEON_SPECIFIC_OPTIONS
default "y"
help
Enable locking parts of the kernel into the L2 cache.
config CAVIUM_OCTEON_LOCK_L2_TLB
bool "Lock the TLB handler in L2"
depends on CAVIUM_OCTEON_LOCK_L2
default "y"
help
Lock the low level TLB fast path into L2.
config CAVIUM_OCTEON_LOCK_L2_EXCEPTION
bool "Lock the exception handler in L2"
depends on CAVIUM_OCTEON_LOCK_L2
default "y"
help
Lock the low level exception handler into L2.
config CAVIUM_OCTEON_LOCK_L2_LOW_LEVEL_INTERRUPT
bool "Lock the interrupt handler in L2"
depends on CAVIUM_OCTEON_LOCK_L2
default "y"
help
Lock the low level interrupt handler into L2.
config CAVIUM_OCTEON_LOCK_L2_INTERRUPT
bool "Lock the 2nd level interrupt handler in L2"
depends on CAVIUM_OCTEON_LOCK_L2
default "y"
help
Lock the 2nd level interrupt handler in L2.
config CAVIUM_OCTEON_LOCK_L2_MEMCPY
bool "Lock memcpy() in L2"
depends on CAVIUM_OCTEON_LOCK_L2
default "y"
help
Lock the kernel's implementation of memcpy() into L2.
config ARCH_SPARSEMEM_ENABLE
def_bool y
select SPARSEMEM_STATIC
depends on CPU_CAVIUM_OCTEON
#
# Makefile for the Cavium Octeon specific kernel interface routines
# under Linux.
#
# This file is subject to the terms and conditions of the GNU General Public
# License. See the file "COPYING" in the main directory of this archive
# for more details.
#
# Copyright (C) 2005-2008 Cavium Networks
#
obj-y := setup.o serial.o octeon-irq.o csrc-octeon.o
obj-y += dma-octeon.o flash_setup.o
obj-y += octeon-memcpy.o
obj-$(CONFIG_SMP) += smp.o
/*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file "COPYING" in the main directory of this archive
* for more details.
*
* Copyright (C) 2007 by Ralf Baechle
*/
#include <linux/clocksource.h>
#include <linux/init.h>
#include <asm/time.h>
#include <asm/octeon/octeon.h>
#include <asm/octeon/cvmx-ipd-defs.h>
/*
* Set the current core's cvmcount counter to the value of the
* IPD_CLK_COUNT. We do this on all cores as they are brought
* on-line. This allows for a read from a local cpu register to
* access a synchronized counter.
*
*/
void octeon_init_cvmcount(void)
{
unsigned long flags;
unsigned loops = 2;
/* Clobber loops so GCC will not unroll the following while loop. */
asm("" : "+r" (loops));
local_irq_save(flags);
/*
* Loop several times so we are executing from the cache,
* which should give more deterministic timing.
*/
while (loops--)
write_c0_cvmcount(cvmx_read_csr(CVMX_IPD_CLK_COUNT));
local_irq_restore(flags);
}
static cycle_t octeon_cvmcount_read(void)
{
return read_c0_cvmcount();
}
static struct clocksource clocksource_mips = {
.name = "OCTEON_CVMCOUNT",
.read = octeon_cvmcount_read,
.mask = CLOCKSOURCE_MASK(64),
.flags = CLOCK_SOURCE_IS_CONTINUOUS,
};
void __init plat_time_init(void)
{
clocksource_mips.rating = 300;
clocksource_set_clock(&clocksource_mips, mips_hpt_frequency);
clocksource_register(&clocksource_mips);
}
/*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file "COPYING" in the main directory of this archive
* for more details.
*
* Copyright (C) 2000 Ani Joshi <ajoshi@unixbox.com>
* Copyright (C) 2000, 2001 Ralf Baechle <ralf@gnu.org>
* Copyright (C) 2005 Ilya A. Volynets-Evenbakh <ilya@total-knowledge.com>
* swiped from i386, and cloned for MIPS by Geert, polished by Ralf.
* IP32 changes by Ilya.
* Cavium Networks: Create new dma setup for Cavium Networks Octeon based on
* the kernels original.
*/
#include <linux/types.h>
#include <linux/mm.h>
#include <dma-coherence.h>
dma_addr_t octeon_map_dma_mem(struct device *dev, void *ptr, size_t size)
{
/* Without PCI/PCIe this function can be called for Octeon internal
devices such as USB. These devices all support 64bit addressing */
mb();
return virt_to_phys(ptr);
}
void octeon_unmap_dma_mem(struct device *dev, dma_addr_t dma_addr)
{
/* Without PCI/PCIe this function can be called for Octeon internal
* devices such as USB. These devices all support 64bit addressing */
return;
}
/*
* Octeon Bootbus flash setup
*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file "COPYING" in the main directory of this archive
* for more details.
*
* Copyright (C) 2007, 2008 Cavium Networks
*/
#include <linux/kernel.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/map.h>
#include <linux/mtd/partitions.h>
#include <asm/octeon/octeon.h>
static struct map_info flash_map;
static struct mtd_info *mymtd;
#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
"RedBoot",
#endif
NULL
};
#endif
/**
* Module/ driver initialization.
*
* Returns Zero on success
*/
static int __init flash_init(void)
{
/*
* Read the bootbus region 0 setup to determine the base
* address of the flash.
*/
union cvmx_mio_boot_reg_cfgx region_cfg;
region_cfg.u64 = cvmx_read_csr(CVMX_MIO_BOOT_REG_CFGX(0));
if (region_cfg.s.en) {
/*
* The bootloader always takes the flash and sets its
* address so the entire flash fits below
* 0x1fc00000. This way the flash aliases to
* 0x1fc00000 for booting. Software can access the
* full flash at the true address, while core boot can
* access 4MB.
*/
/* Use this name so old part lines work */
flash_map.name = "phys_mapped_flash";
flash_map.phys = region_cfg.s.base << 16;
flash_map.size = 0x1fc00000 - flash_map.phys;
flash_map.bankwidth = 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);
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
} else {
pr_err("Failed to register MTD device for flash\n");
}
}
return 0;
}
late_initcall(flash_init);
/*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file "COPYING" in the main directory of this archive
* for more details.
*
* Copyright (C) 2004-2008 Cavium Networks
*/
#include <linux/irq.h>
#include <linux/interrupt.h>
#include <linux/hardirq.h>
#include <asm/octeon/octeon.h>
DEFINE_RWLOCK(octeon_irq_ciu0_rwlock);
DEFINE_RWLOCK(octeon_irq_ciu1_rwlock);
DEFINE_SPINLOCK(octeon_irq_msi_lock);
static void octeon_irq_core_ack(unsigned int irq)
{
unsigned int bit = irq - OCTEON_IRQ_SW0;
/*
* 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 << bit);
/* The two user interrupts must be cleared manually. */
if (bit < 2)
clear_c0_cause(0x100 << bit);
}
static void octeon_irq_core_eoi(unsigned int irq)
{
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. */
/*
* 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);
}
static void octeon_irq_core_enable(unsigned int irq)
{
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);
set_c0_status(0x100 << bit);
local_irq_restore(flags);
}
static void octeon_irq_core_disable_local(unsigned int irq)
{
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);
}
static void octeon_irq_core_disable(unsigned int irq)
{
#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
}
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,
};
static void octeon_irq_ciu0_ack(unsigned int irq)
{
/*
* 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);
}
static void octeon_irq_ciu0_eoi(unsigned int irq)
{
/*
* 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);
}
static void octeon_irq_ciu0_enable(unsigned int irq)
{
int coreid = cvmx_get_core_num();
unsigned long flags;
uint64_t en0;
int bit = irq - OCTEON_IRQ_WORKQ0; /* Bit 0-63 of EN0 */
/*
* 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);
}
static void octeon_irq_ciu0_disable(unsigned int irq)
{
int bit = irq - OCTEON_IRQ_WORKQ0; /* Bit 0-63 of EN0 */
unsigned long flags;
uint64_t en0;
#ifdef CONFIG_SMP
int cpu;
write_lock_irqsave(&octeon_irq_ciu0_rwlock, flags);
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);
}
/*
* 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_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
}
#ifdef CONFIG_SMP
static void octeon_irq_ciu0_set_affinity(unsigned int irq, const struct cpumask *dest)
{
int cpu;
int bit = irq - OCTEON_IRQ_WORKQ0; /* Bit 0-63 of EN0 */
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;
else
en0 &= ~(1ull << bit);
cvmx_write_csr(CVMX_CIU_INTX_EN0(coreid * 2), en0);
}
/*
* 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);
}
#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,
#ifdef CONFIG_SMP
.set_affinity = octeon_irq_ciu0_set_affinity,
#endif
};
static void octeon_irq_ciu1_ack(unsigned int irq)
{
/*
* 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 << 3);
}
static void octeon_irq_ciu1_eoi(unsigned int irq)
{
/*
* 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 << 3);
}
static void octeon_irq_ciu1_enable(unsigned int irq)
{
int coreid = cvmx_get_core_num();
unsigned long flags;
uint64_t en1;
int bit = irq - OCTEON_IRQ_WDOG0; /* Bit 0-63 of EN1 */
/*
* 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_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);
}
static void octeon_irq_ciu1_disable(unsigned int irq)
{
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);
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);
}
/*
* 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
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
}
#ifdef CONFIG_SMP
static void octeon_irq_ciu1_set_affinity(unsigned int irq, const struct cpumask *dest)
{
int cpu;
int bit = irq - OCTEON_IRQ_WDOG0; /* Bit 0-63 of EN1 */
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);
}
/*
* 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);
}
#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,
#ifdef CONFIG_SMP
.set_affinity = octeon_irq_ciu1_set_affinity,
#endif
};
#ifdef CONFIG_PCI_MSI