diff options
Diffstat (limited to 'arch/mips/cavium-octeon')
34 files changed, 3959 insertions, 1474 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 2f4f6d5e05b..60286665793 100644 --- a/arch/mips/cavium-octeon/Kconfig +++ b/arch/mips/cavium-octeon/Kconfig @@ -10,26 +10,6 @@ config CAVIUM_CN63XXP1 non-CN63XXP1 hardware, so it is recommended to select "n" unless it is known the workarounds are needed. -config CAVIUM_OCTEON_2ND_KERNEL - bool "Build the kernel to be used as a 2nd kernel on the same chip" - default "n" - help - This option configures this kernel to be linked at a different - address and use the 2nd uart for output. This allows a kernel built - with this option to be run at the same time as one built without this - option. - -config CAVIUM_OCTEON_HW_FIX_UNALIGNED - bool "Enable hardware fixups of unaligned loads and stores" - default "y" - help - Configure the Octeon hardware to automatically fix unaligned loads - and stores. Normally unaligned accesses are fixed using a kernel - exception handler. This option enables the hardware automatic fixups, - which requires only an extra 3 cycles. Disable this option if you - are running code that relies on address exceptions on unaligned - accesses. - config CAVIUM_OCTEON_CVMSEG_SIZE int "Number of L1 cache lines reserved for CVMSEG memory" range 0 54 @@ -41,6 +21,19 @@ 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" default "y" @@ -93,5 +86,13 @@ config SWIOTLB 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. -endif # CPU_CAVIUM_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 19eb0434269..4e952043c92 100644 --- a/arch/mips/cavium-octeon/Makefile +++ b/arch/mips/cavium-octeon/Makefile @@ -9,9 +9,24 @@ # Copyright (C) 2005-2009 Cavium Networks # -obj-y := cpu.o setup.o serial.o octeon-platform.o octeon-irq.o csrc-octeon.o -obj-y += dma-octeon.o flash_setup.o +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_SMP) += smp.o +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)) + +clean-files += $(DTB_FILES) $(patsubst %.dtb, %.dtb.S, $(DTB_FILES)) diff --git a/arch/mips/cavium-octeon/Platform b/arch/mips/cavium-octeon/Platform index 1e43ccf1a79..8a301cb12d6 100644 --- a/arch/mips/cavium-octeon/Platform +++ b/arch/mips/cavium-octeon/Platform @@ -1,11 +1,11 @@ # # Cavium Octeon # -platform-$(CONFIG_CPU_CAVIUM_OCTEON) += cavium-octeon/ -cflags-$(CONFIG_CPU_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_CPU_CAVIUM_OCTEON) += 0xffffffff84100000 +load-$(CONFIG_CAVIUM_OCTEON_SOC) += 0xffffffff84100000 else -load-$(CONFIG_CPU_CAVIUM_OCTEON) += 0xffffffff81100000 +load-$(CONFIG_CAVIUM_OCTEON_SOC) += 0xffffffff81100000 endif diff --git a/arch/mips/cavium-octeon/csrc-octeon.c b/arch/mips/cavium-octeon/csrc-octeon.c index ce6483a9302..b752c4ed0b7 100644 --- a/arch/mips/cavium-octeon/csrc-octeon.c +++ b/arch/mips/cavium-octeon/csrc-octeon.c @@ -4,7 +4,7 @@ * for more details. * * Copyright (C) 2007 by Ralf Baechle - * Copyright (C) 2009, 2010 Cavium Networks, Inc. + * Copyright (C) 2009, 2012 Cavium, Inc. */ #include <linux/clocksource.h> #include <linux/export.h> @@ -12,12 +12,40 @@ #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 * IPD_CLK_COUNT. We do this on all cores as they are brought @@ -30,17 +58,6 @@ void octeon_init_cvmcount(void) { unsigned long flags; unsigned loops = 2; - u64 f = 0; - u64 rdiv = 0; - u64 sdiv = 0; - 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; - } - /* Clobber loops so GCC will not unroll the following while loop. */ asm("" : "+r" (loops)); @@ -57,9 +74,9 @@ void octeon_init_cvmcount(void) if (f != 0) { asm("dmultu\t%[cnt],%[f]\n\t" "mfhi\t%[cnt]" - : [cnt] "+r" (ipd_clk_count), - [f] "=r" (f) - : : "hi", "lo"); + : [cnt] "+r" (ipd_clk_count) + : [f] "r" (f) + : "hi", "lo"); } } write_c0_cvmcount(ipd_clk_count); @@ -109,21 +126,6 @@ void __init plat_time_init(void) clocksource_register_hz(&clocksource_mips, octeon_get_clock_rate()); } -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; -} - void __udelay(unsigned long us) { u64 cur, end, inc; @@ -163,3 +165,35 @@ void __delay(unsigned long loops) 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 41dd0088497..02f24447520 100644 --- a/arch/mips/cavium-octeon/dma-octeon.c +++ b/arch/mips/cavium-octeon/dma-octeon.c @@ -317,7 +317,8 @@ void __init plat_swiotlb_setup(void) octeon_swiotlb = alloc_bootmem_low_pages(swiotlbsize); - swiotlb_init_with_tbl(octeon_swiotlb, swiotlb_nslabs, 1); + 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; } diff --git a/arch/mips/cavium-octeon/executive/cvmx-bootmem.c b/arch/mips/cavium-octeon/executive/cvmx-bootmem.c index fdf5f19bfdb..504ed61a47c 100644 --- a/arch/mips/cavium-octeon/executive/cvmx-bootmem.c +++ b/arch/mips/cavium-octeon/executive/cvmx-bootmem.c @@ -155,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 @@ -398,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 @@ -424,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, @@ -611,7 +611,7 @@ 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, @@ -688,3 +688,8 @@ int64_t cvmx_bootmem_phy_named_block_alloc(uint64_t size, uint64_t min_addr, 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 index 132bccc66a9..8241fc6aa17 100644 --- a/arch/mips/cavium-octeon/executive/cvmx-cmd-queue.c +++ b/arch/mips/cavium-octeon/executive/cvmx-cmd-queue.c @@ -47,6 +47,7 @@ * 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. diff --git a/arch/mips/cavium-octeon/executive/cvmx-fpa.c b/arch/mips/cavium-octeon/executive/cvmx-fpa.c deleted file mode 100644 index ad44b8bd805..00000000000 --- a/arch/mips/cavium-octeon/executive/cvmx-fpa.c +++ /dev/null @@ -1,183 +0,0 @@ -/***********************license start*************** - * Author: Cavium Networks - * - * Contact: support@caviumnetworks.com - * This file is part of the OCTEON SDK - * - * Copyright (c) 2003-2008 Cavium Networks - * - * This file is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License, Version 2, as - * published by the Free Software Foundation. - * - * This file is distributed in the hope that it will be useful, but - * AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or - * NONINFRINGEMENT. See the GNU General Public License for more - * details. - * - * You should have received a copy of the GNU General Public License - * along with this file; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * or visit http://www.gnu.org/licenses/. - * - * This file may also be available under a different license from Cavium. - * Contact Cavium Networks for more information - ***********************license end**************************************/ - -/** - * @file - * - * Support library for the hardware Free Pool Allocator. - * - * - */ - -#include "cvmx-config.h" -#include "cvmx.h" -#include "cvmx-fpa.h" -#include "cvmx-ipd.h" - -/** - * Current state of all the pools. Use access functions - * instead of using it directly. - */ -CVMX_SHARED cvmx_fpa_pool_info_t cvmx_fpa_pool_info[CVMX_FPA_NUM_POOLS]; - -/** - * Setup a FPA pool to control a new block of memory. The - * buffer pointer must be a physical address. - * - * @pool: Pool to initialize - * 0 <= pool < 8 - * @name: Constant character string to name this pool. - * String is not copied. - * @buffer: Pointer to the block of memory to use. This must be - * accessible by all processors and external hardware. - * @block_size: Size for each block controlled by the FPA - * @num_blocks: Number of blocks - * - * Returns 0 on Success, - * -1 on failure - */ -int cvmx_fpa_setup_pool(uint64_t pool, const char *name, void *buffer, - uint64_t block_size, uint64_t num_blocks) -{ - char *ptr; - if (!buffer) { - cvmx_dprintf - ("ERROR: cvmx_fpa_setup_pool: NULL buffer pointer!\n"); - return -1; - } - if (pool >= CVMX_FPA_NUM_POOLS) { - cvmx_dprintf("ERROR: cvmx_fpa_setup_pool: Illegal pool!\n"); - return -1; - } - - if (block_size < CVMX_FPA_MIN_BLOCK_SIZE) { - cvmx_dprintf - ("ERROR: cvmx_fpa_setup_pool: Block size too small.\n"); - return -1; - } - - if (((unsigned long)buffer & (CVMX_FPA_ALIGNMENT - 1)) != 0) { - cvmx_dprintf - ("ERROR: cvmx_fpa_setup_pool: Buffer not aligned properly.\n"); - return -1; - } - - cvmx_fpa_pool_info[pool].name = name; - cvmx_fpa_pool_info[pool].size = block_size; - cvmx_fpa_pool_info[pool].starting_element_count = num_blocks; - cvmx_fpa_pool_info[pool].base = buffer; - - ptr = (char *)buffer; - while (num_blocks--) { - cvmx_fpa_free(ptr, pool, 0); - ptr += block_size; - } - return 0; -} - -/** - * Shutdown a Memory pool and validate that it had all of - * the buffers originally placed in it. - * - * @pool: Pool to shutdown - * Returns Zero on success - * - Positive is count of missing buffers - * - Negative is too many buffers or corrupted pointers - */ -uint64_t cvmx_fpa_shutdown_pool(uint64_t pool) -{ - uint64_t errors = 0; - uint64_t count = 0; - uint64_t base = cvmx_ptr_to_phys(cvmx_fpa_pool_info[pool].base); - uint64_t finish = - base + - cvmx_fpa_pool_info[pool].size * - cvmx_fpa_pool_info[pool].starting_element_count; - void *ptr; - uint64_t address; - - count = 0; - do { - ptr = cvmx_fpa_alloc(pool); - if (ptr) - address = cvmx_ptr_to_phys(ptr); - else - address = 0; - if (address) { - if ((address >= base) && (address < finish) && - (((address - - base) % cvmx_fpa_pool_info[pool].size) == 0)) { - count++; - } else { - cvmx_dprintf - ("ERROR: cvmx_fpa_shutdown_pool: Illegal address 0x%llx in pool %s(%d)\n", - (unsigned long long)address, - cvmx_fpa_pool_info[pool].name, (int)pool); - errors++; - } - } - } while (address); - -#ifdef CVMX_ENABLE_PKO_FUNCTIONS - if (pool == 0) - cvmx_ipd_free_ptr(); -#endif - - if (errors) { - cvmx_dprintf - ("ERROR: cvmx_fpa_shutdown_pool: Pool %s(%d) started at 0x%llx, ended at 0x%llx, with a step of 0x%llx\n", - cvmx_fpa_pool_info[pool].name, (int)pool, - (unsigned long long)base, (unsigned long long)finish, - (unsigned long long)cvmx_fpa_pool_info[pool].size); - return -errors; - } else - return 0; -} - -uint64_t cvmx_fpa_get_block_size(uint64_t pool) -{ - switch (pool) { - case 0: - return CVMX_FPA_POOL_0_SIZE; - case 1: - return CVMX_FPA_POOL_1_SIZE; - case 2: - return CVMX_FPA_POOL_2_SIZE; - case 3: - return CVMX_FPA_POOL_3_SIZE; - case 4: - return CVMX_FPA_POOL_4_SIZE; - case 5: - return CVMX_FPA_POOL_5_SIZE; - case 6: - return CVMX_FPA_POOL_6_SIZE; - case 7: - return CVMX_FPA_POOL_7_SIZE; - default: - return 0; - } -} diff --git a/arch/mips/cavium-octeon/executive/cvmx-helper-board.c b/arch/mips/cavium-octeon/executive/cvmx-helper-board.c index fd2015331a2..b764df64be4 100644 --- a/arch/mips/cavium-octeon/executive/cvmx-helper-board.c +++ b/arch/mips/cavium-octeon/executive/cvmx-helper-board.c @@ -181,6 +181,11 @@ int cvmx_helper_board_get_mii_address(int ipd_port) 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... */ @@ -203,10 +208,10 @@ int cvmx_helper_board_get_mii_address(int ipd_port) * enumeration from the bootloader. * * @ipd_port: IPD input port associated with the port we want to get link - * status for. + * status for. * * Returns The ports link status. If the link isn't fully resolved, this must - * return zero. + * return zero. */ cvmx_helper_link_info_t __cvmx_helper_board_link_get(int ipd_port) { @@ -357,16 +362,16 @@ cvmx_helper_link_info_t __cvmx_helper_board_link_get(int ipd_port) result.s.link_up = 1; result.s.full_duplex = ((phy_status >> 13) & 1); switch ((phy_status >> 14) & 3) { - case 0: /* 10 Mbps */ + case 0: /* 10 Mbps */ result.s.speed = 10; break; - case 1: /* 100 Mbps */ + case 1: /* 100 Mbps */ result.s.speed = 100; break; - case 2: /* 1 Gbps */ + case 2: /* 1 Gbps */ result.s.speed = 1000; break; - case 3: /* Illegal */ + case 3: /* Illegal */ result.u64 = 0; break; } @@ -391,16 +396,16 @@ cvmx_helper_link_info_t __cvmx_helper_board_link_get(int ipd_port) 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 */ + case 0: /* 10 Mbps */ result.s.speed = 10; break; - case 1: /* 100 Mbps */ + case 1: /* 100 Mbps */ result.s.speed = 100; break; - case 2: /* 1 Gbps */ + case 2: /* 1 Gbps */ result.s.speed = 1000; break; - case 3: /* Illegal */ + case 3: /* Illegal */ result.u64 = 0; break; } @@ -429,9 +434,9 @@ cvmx_helper_link_info_t __cvmx_helper_board_link_get(int ipd_port) * * @phy_addr: The address of the PHY to program * @enable_autoneg: - * Non zero if you want to enable auto-negotiation. + * 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. + * is enabled, all possible negotiation speeds are advertised. * * Returns Zero on success, negative on failure */ @@ -607,10 +612,10 @@ int cvmx_helper_board_link_set_phy(int phy_addr, * * @interface: Interface to probe * @supported_ports: - * Number of ports Octeon supports. + * Number of ports Octeon supports. * * Returns Number of ports the actual board supports. Many times this will - * simple be "support_ports". + * simple be "support_ports". */ int __cvmx_helper_board_interface_probe(int interface, int supported_ports) { @@ -706,6 +711,41 @@ int __cvmx_helper_board_hardware_enable(int interface) } } } + } 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-fpa.c b/arch/mips/cavium-octeon/executive/cvmx-helper-fpa.c deleted file mode 100644 index c239e5f4ab9..00000000000 --- a/arch/mips/cavium-octeon/executive/cvmx-helper-fpa.c +++ /dev/null @@ -1,243 +0,0 @@ -/***********************license start*************** - * Author: Cavium Networks - * - * Contact: support@caviumnetworks.com - * This file is part of the OCTEON SDK - * - * Copyright (c) 2003-2008 Cavium Networks - * - * This file is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License, Version 2, as - * published by the Free Software Foundation. - * - * This file is distributed in the hope that it will be useful, but - * AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or - * NONINFRINGEMENT. See the GNU General Public License for more - * details. - * - * You should have received a copy of the GNU General Public License - * along with this file; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * or visit http://www.gnu.org/licenses/. - * - * This file may also be available under a different license from Cavium. - * Contact Cavium Networks for more information - ***********************license end**************************************/ - -/** - * @file - * - * Helper functions for FPA setup. - * - */ -#include "executive-config.h" -#include "cvmx-config.h" -#include "cvmx.h" -#include "cvmx-bootmem.h" -#include "cvmx-fpa.h" -#include "cvmx-helper-fpa.h" - -/** - * Allocate memory for and initialize a single FPA pool. - * - * @pool: Pool to initialize - * @buffer_size: Size of buffers to allocate in bytes - * @buffers: Number of buffers to put in the pool. Zero is allowed - * @name: String name of the pool for debugging purposes - * Returns Zero on success, non-zero on failure - */ -static int __cvmx_helper_initialize_fpa_pool(int pool, uint64_t buffer_size, - uint64_t buffers, const char *name) -{ - uint64_t current_num; - void *memory; - uint64_t align = CVMX_CACHE_LINE_SIZE; - - /* - * Align the allocation so that power of 2 size buffers are - * naturally aligned. - */ - while (align < buffer_size) - align = align << 1; - - if (buffers == 0) - return 0; - - current_num = cvmx_read_csr(CVMX_FPA_QUEX_AVAILABLE(pool)); - if (current_num) { - cvmx_dprintf("Fpa pool %d(%s) already has %llu buffers. " - "Skipping setup.\n", - pool, name, (unsigned long long)current_num); - return 0; - } - - memory = cvmx_bootmem_alloc(buffer_size * buffers, align); - if (memory == NULL) { - cvmx_dprintf("Out of memory initializing fpa pool %d(%s).\n", - pool, name); - return -1; - } - cvmx_fpa_setup_pool(pool, name, memory, buffer_size, buffers); - return 0; -} - -/** - * Allocate memory and initialize the FPA pools using memory - * from cvmx-bootmem. Specifying zero for the number of - * buffers will cause that FPA pool to not be setup. This is - * useful if you aren't using some of the hardware and want - * to save memory. Use cvmx_helper_initialize_fpa instead of - * this function directly. - * - * @pip_pool: Should always be CVMX_FPA_PACKET_POOL - * @pip_size: Should always be CVMX_FPA_PACKET_POOL_SIZE - * @pip_buffers: - * Number of packet buffers. - * @wqe_pool: Should always be CVMX_FPA_WQE_POOL - * @wqe_size: Should always be CVMX_FPA_WQE_POOL_SIZE - * @wqe_entries: - * Number of work queue entries - * @pko_pool: Should always be CVMX_FPA_OUTPUT_BUFFER_POOL - * @pko_size: Should always be CVMX_FPA_OUTPUT_BUFFER_POOL_SIZE - * @pko_buffers: - * PKO Command buffers. You should at minimum have two per - * each PKO queue. - * @tim_pool: Should always be CVMX_FPA_TIMER_POOL - * @tim_size: Should always be CVMX_FPA_TIMER_POOL_SIZE - * @tim_buffers: - * TIM ring buffer command queues. At least two per timer bucket - * is recommened. - * @dfa_pool: Should always be CVMX_FPA_DFA_POOL - * @dfa_size: Should always be CVMX_FPA_DFA_POOL_SIZE - * @dfa_buffers: - * DFA command buffer. A relatively small (32 for example) - * number should work. - * Returns Zero on success, non-zero if out of memory - */ -static int __cvmx_helper_initialize_fpa(int pip_pool, int pip_size, - int pip_buffers, int wqe_pool, - int wqe_size, int wqe_entries, - int pko_pool, int pko_size, - int pko_buffers, int tim_pool, - int tim_size, int tim_buffers, - int dfa_pool, int dfa_size, - int dfa_buffers) -{ - int status; - - cvmx_fpa_enable(); - - if ((pip_buffers > 0) && (pip_buffers <= 64)) - cvmx_dprintf - ("Warning: %d packet buffers may not be enough for hardware" - " prefetch. 65 or more is recommended.\n", pip_buffers); - - if (pip_pool >= 0) { - status = - __cvmx_helper_initialize_fpa_pool(pip_pool, pip_size, - pip_buffers, - "Packet Buffers"); - if (status) - return status; - } - - if (wqe_pool >= 0) { - status = - __cvmx_helper_initialize_fpa_pool(wqe_pool, wqe_size, - wqe_entries, - "Work Queue Entries"); - if (status) - return status; - } - - if (pko_pool >= 0) { - status = - __cvmx_helper_initialize_fpa_pool(pko_pool, pko_size, - pko_buffers, - "PKO Command Buffers"); - if (status) - return status; - } - - if (tim_pool >= 0) { - status = - __cvmx_helper_initialize_fpa_pool(tim_pool, tim_size, - tim_buffers, - "TIM Command Buffers"); - if (status) - return status; - } - - if (dfa_pool >= 0) { - status = - __cvmx_helper_initialize_fpa_pool(dfa_pool, dfa_size, - dfa_buffers, - "DFA Command Buffers"); - if (status) - return status; - } - - return 0; -} - -/** - * Allocate memory and initialize the FPA pools using memory - * from cvmx-bootmem. Sizes of each element in the pools is - * controlled by the cvmx-config.h header file. Specifying - * zero for any parameter will cause that FPA pool to not be - * setup. This is useful if you aren't using some of the - * hardware and want to save memory. - * - * @packet_buffers: - * Number of packet buffers to allocate - * @work_queue_entries: - * Number of work queue entries - * @pko_buffers: - * PKO Command buffers. You should at minimum have two per - * each PKO queue. - * @tim_buffers: - * TIM ring buffer command queues. At least two per timer bucket - * is recommened. - * @dfa_buffers: - * DFA command buffer. A relatively small (32 for example) - * number should work. - * Returns Zero on success, non-zero if out of memory - */ -int cvmx_helper_initialize_fpa(int packet_buffers, int work_queue_entries, - int pko_buffers, int tim_buffers, - int dfa_buffers) -{ -#ifndef CVMX_FPA_PACKET_POOL -#define CVMX_FPA_PACKET_POOL -1 -#define CVMX_FPA_PACKET_POOL_SIZE 0 -#endif -#ifndef CVMX_FPA_WQE_POOL -#define CVMX_FPA_WQE_POOL -1 -#define CVMX_FPA_WQE_POOL_SIZE 0 -#endif -#ifndef CVMX_FPA_OUTPUT_BUFFER_POOL -#define CVMX_FPA_OUTPUT_BUFFER_POOL -1 -#define CVMX_FPA_OUTPUT_BUFFER_POOL_SIZE 0 -#endif -#ifndef CVMX_FPA_TIMER_POOL -#define CVMX_FPA_TIMER_POOL -1 -#define CVMX_FPA_TIMER_POOL_SIZE 0 -#endif -#ifndef CVMX_FPA_DFA_POOL -#define CVMX_FPA_DFA_POOL -1 -#define CVMX_FPA_DFA_POOL_SIZE 0 -#endif - return __cvmx_helper_initialize_fpa(CVMX_FPA_PACKET_POOL, - CVMX_FPA_PACKET_POOL_SIZE, - packet_buffers, CVMX_FPA_WQE_POOL, - CVMX_FPA_WQE_POOL_SIZE, - work_queue_entries, - CVMX_FPA_OUTPUT_BUFFER_POOL, - CVMX_FPA_OUTPUT_BUFFER_POOL_SIZE, - pko_buffers, CVMX_FPA_TIMER_POOL, - CVMX_FPA_TIMER_POOL_SIZE, - tim_buffers, CVMX_FPA_DFA_POOL, - CVMX_FPA_DFA_POOL_SIZE, - dfa_buffers); -} diff --git a/arch/mips/cavium-octeon/executive/cvmx-helper-jtag.c b/arch/mips/cavium-octeon/executive/cvmx-helper-jtag.c index c1c54890bae..607b4e65957 100644 --- a/arch/mips/cavium-octeon/executive/cvmx-helper-jtag.c +++ b/arch/mips/cavium-octeon/executive/cvmx-helper-jtag.c @@ -79,10 +79,10 @@ void cvmx_helper_qlm_jtag_init(void) * @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. + * bit 1, etc. * * Returns The low order bits of the JTAG chain that shifted out of the - * circle. + * circle. */ uint32_t cvmx_helper_qlm_jtag_shift(int qlm, int bits, uint32_t data) { diff --git a/arch/mips/cavium-octeon/executive/cvmx-helper-rgmii.c b/arch/mips/cavium-octeon/executive/cvmx-helper-rgmii.c index 82b21843421..f59c88ee9b3 100644 --- a/arch/mips/cavium-octeon/executive/cvmx-helper-rgmii.c +++ b/arch/mips/cavium-octeon/executive/cvmx-helper-rgmii.c @@ -131,7 +131,7 @@ void cvmx_helper_rgmii_internal_loopback(int port) * @interface: Interface to setup * @port: Port to setup (0..3) * @cpu_clock_hz: - * Chip frequency in Hertz + * Chip frequency in Hertz * * Returns Zero on success, negative on failure */ @@ -409,14 +409,14 @@ int __cvmx_helper_rgmii_link_set(int ipd_port, 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]. + * 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. */ @@ -464,9 +464,9 @@ int __cvmx_helper_rgmii_link_set(int ipd_port, * * @ipd_port: IPD/PKO port to loopback. * @enable_internal: - * Non zero if you want internal loopback + * Non zero if you want internal loopback * @enable_external: - * Non zero if you want external loopback + * Non zero if you want external loopback * * Returns Zero on success, negative on failure. */ diff --git a/arch/mips/cavium-octeon/executive/cvmx-helper-sgmii.c b/arch/mips/cavium-octeon/executive/cvmx-helper-sgmii.c index 0c0bf5d30e7..45f18cce31a 100644 --- a/arch/mips/cavium-octeon/executive/cvmx-helper-sgmii.c +++ b/arch/mips/cavium-octeon/executive/cvmx-helper-sgmii.c @@ -523,9 +523,9 @@ int __cvmx_helper_sgmii_link_set(int ipd_port, * * @ipd_port: IPD/PKO port to loopback. * @enable_internal: - * Non zero if you want internal loopback + * Non zero if you want internal loopback * @enable_external: - * Non zero if you want external loopback + * Non zero if you want external loopback * * Returns Zero on success, negative on failure. */ diff --git a/arch/mips/cavium-octeon/executive/cvmx-helper-spi.c b/arch/mips/cavium-octeon/executive/cvmx-helper-spi.c index 2830e4bdf7f..1f3030c72d8 100644 --- a/arch/mips/cavium-octeon/executive/cvmx-helper-spi.c +++ b/arch/mips/cavium-octeon/executive/cvmx-helper-spi.c @@ -160,16 +160,16 @@ cvmx_helper_link_info_t __cvmx_helper_spi_link_get(int ipd_port) result.s.link_up = inband.s.status; result.s.full_duplex = inband.s.duplex; switch (inband.s.speed) { - case 0: /* 10 Mbps */ + case 0: /* 10 Mbps */ result.s.speed = 10; break; - case 1: /* 100 Mbps */ + case 1: /* 100 Mbps */ result.s.speed = 100; break; - case 2: /* 1 Gbps */ + case 2: /* 1 Gbps */ result.s.speed = 1000; break; - case 3: /* Illegal */ + case 3: /* Illegal */ result.s.speed = 0; result.s.link_up = 0; break; diff --git a/arch/mips/cavium-octeon/executive/cvmx-helper-util.c b/arch/mips/cavium-octeon/executive/cvmx-helper-util.c index 116dea17acf..453d7f66459 100644 --- a/arch/mips/cavium-octeon/executive/cvmx-helper-util.c +++ b/arch/mips/cavium-octeon/executive/cvmx-helper-util.c @@ -96,9 +96,9 @@ int cvmx_helper_dump_packet(cvmx_wqe_t *work) 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); + 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; @@ -132,14 +132,14 @@ int cvmx_helper_dump_packet(cvmx_wqe_t *work) while (remaining_bytes) { start_of_buffer = ((buffer_ptr.s.addr >> 7) - buffer_ptr.s.back) << 7; - cvmx_dprintf(" Buffer Start:%llx\n", + 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", + 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(" Buffer Size: %u\n", buffer_ptr.s.size); cvmx_dprintf("\t\t"); data_address = (uint8_t *) cvmx_phys_to_ptr(buffer_ptr.s.addr); @@ -172,11 +172,11 @@ int cvmx_helper_dump_packet(cvmx_wqe_t *work) * * @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. + * Packets will begin slowly dropping when there are less than + * this many packet buffers free in FPA 0. * @drop_thresh: - * All incomming packets will be dropped when there are less - * than this many free packet buffers in FPA 0. + * 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) @@ -207,11 +207,11 @@ int cvmx_helper_setup_red_queue(int queue, int pass_thresh, int drop_thresh) * 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. + * Packets will begin slowly dropping when there are less than + * this many packet buffers free in FPA 0. * @drop_thresh: - * All incomming packets will be dropped when there are less - * than this many free packet buffers in FPA 0. + * 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) @@ -251,6 +251,7 @@ int cvmx_helper_setup_red(int pass_thresh, int drop_thresh) return 0; } +EXPORT_SYMBOL_GPL(cvmx_helper_setup_red); /** * Setup the common GMX settings that determine the number of @@ -384,6 +385,7 @@ int cvmx_helper_get_ipd_port(int interface, int port) } return -1; } +EXPORT_SYMBOL_GPL(cvmx_helper_get_ipd_port); /** * Returns the interface number for an IPD/PKO port number. @@ -408,6 +410,7 @@ int cvmx_helper_get_interface_num(int ipd_port) return -1; } +EXPORT_SYMBOL_GPL(cvmx_helper_get_interface_num); /** * Returns the interface index number for an IPD/PKO port @@ -431,3 +434,4 @@ int cvmx_helper_get_interface_index_num(int ipd_port) 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 index 1723248e987..7653b7e9219 100644 --- a/arch/mips/cavium-octeon/executive/cvmx-helper-xaui.c +++ b/arch/mips/cavium-octeon/executive/cvmx-helper-xaui.c @@ -321,9 +321,9 @@ int __cvmx_helper_xaui_link_set(int ipd_port, cvmx_helper_link_info_t link_info) * * @ipd_port: IPD/PKO port to loopback. * @enable_internal: - * Non zero if you want internal loopback + * Non zero if you want internal loopback * @enable_external: - * Non zero if you want external loopback + * Non zero if you want external loopback * * Returns Zero on success, negative on failure. */ diff --git a/arch/mips/cavium-octeon/executive/cvmx-helper.c b/arch/mips/cavium-octeon/executive/cvmx-helper.c index fa496385635..7e5cf7a5e2f 100644 --- a/arch/mips/cavium-octeon/executive/cvmx-helper.c +++ b/arch/mips/cavium-octeon/executive/cvmx-helper.c @@ -67,7 +67,7 @@ void (*cvmx_override_pko_queue_priority) (int pko_port, void (*cvmx_override_ipd_port_setup) (int ipd_port); /* Port count per interface */ -static int interface_port_count[4] = { 0, 0, 0, 0 }; +static int interface_port_count[5]; /* Port last configured link info index by IPD/PKO port */ static cvmx_helper_link_info_t @@ -88,6 +88,7 @@ int cvmx_helper_get_number_of_interfaces(void) else return 3; } +EXPORT_SYMBOL_GPL(cvmx_helper_get_number_of_interfaces); /** * Return the number of ports on an interface. Depending on the @@ -102,6 +103,159 @@ 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 @@ -111,11 +265,25 @@ int cvmx_helper_ports_on_interface(int interface) * @interface: Interface to probe * * Returns Mode of the interface. Unknown or unsupported interfaces return - * DISABLED. + * 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; @@ -179,6 +347,7 @@ cvmx_helper_interface_mode_t cvmx_helper_interface_get_mode(int interface) 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 @@ -187,7 +356,7 @@ cvmx_helper_interface_mode_t cvmx_helper_interface_get_mode(int interface) * the defines in executive-config.h. * * @ipd_port: Port to configure. This follows the IPD numbering, not the - * per interface numbering + * per interface numbering * * Returns Zero on success, negative on failure */ @@ -591,7 +760,7 @@ static int __cvmx_helper_packet_hardware_enable(int interface) * Function to adjust internal IPD pointer alignments * * Returns 0 on success - * !0 on failure + * !0 on failure */ int __cvmx_helper_errata_fix_ipd_ptr_alignment(void) { @@ -825,6 +994,7 @@ int cvmx_helper_ipd_and_packet_input_enable(void) __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 @@ -903,6 +1073,7 @@ int cvmx_helper_initialize_packet_io_global(void) #endif return result; } +EXPORT_SYMBOL_GPL(cvmx_helper_initialize_packet_io_global); /** * Does core local initialization for packet io @@ -947,6 +1118,7 @@ cvmx_helper_link_info_t cvmx_helper_link_autoconf(int ipd_port) */ 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 @@ -1005,6 +1177,7 @@ cvmx_helper_link_info_t cvmx_helper_link_get(int ipd_port) } return result; } +EXPORT_SYMBOL_GPL(cvmx_helper_link_get); /** * Configure an IPD/PKO port for the specified link state. This @@ -1060,6 +1233,7 @@ int cvmx_helper_link_set(int ipd_port, cvmx_helper_link_info_t link_info) 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 @@ -1068,9 +1242,9 @@ int cvmx_helper_link_set(int ipd_port, cvmx_helper_link_info_t link_info) * * @ipd_port: IPD/PKO port to loopback. * @enable_internal: - * Non zero if you want internal loopback + * Non zero if you want internal loopback * @enable_external: - * Non zero if you want external loopback + * Non zero if you want external loopback * * Returns Zero on success, negative on failure. */ diff --git a/arch/mips/cavium-octeon/executive/cvmx-interrupt-rsl.c b/arch/mips/cavium-octeon/executive/cvmx-interrupt-rsl.c index bea7538ea4e..fa327ec891c 100644 --- a/arch/mips/cavium-octeon/executive/cvmx-interrupt-rsl.c +++ b/arch/mips/cavium-octeon/executive/cvmx-interrupt-rsl.c @@ -85,11 +85,11 @@ void __cvmx_interrupt_gmxx_enable(int interface) if (OCTEON_IS_MODEL(OCTEON_CN56XX) || OCTEON_IS_MODEL(OCTEON_CN52XX)) { if (mode.s.en) { switch (mode.cn56xx.mode) { - case 1: /* XAUI */ + case 1: /* XAUI */ num_ports = 1; break; - case 2: /* SGMII */ - case 3: /* PICMG */ + case 2: /* SGMII */ + case 3: /* PICMG */ num_ports = 4; break; default: /* Disabled */ @@ -130,7 +130,7 @@ void __cvmx_interrupt_gmxx_enable(int interface) if (num_ports) { if (OCTEON_IS_MODEL(OCTEON_CN38XX) || OCTEON_IS_MODEL(OCTEON_CN58XX)) - gmx_tx_int_en.s.ncb_nxa = 1; + gmx_tx_int_en.cn38xx.ncb_nxa = 1; gmx_tx_int_en.s.pko_nxa = 1; } gmx_tx_int_en.s.undflw = (1 << num_ports) - 1; diff --git a/arch/mips/cavium-octeon/executive/cvmx-l2c.c b/arch/mips/cavium-octeon/executive/cvmx-l2c.c index d38246e33dd..42e38c30b54 100644 --- a/arch/mips/cavium-octeon/executive/cvmx-l2c.c +++ b/arch/mips/cavium-octeon/executive/cvmx-l2c.c @@ -30,6 +30,8 @@ * 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> @@ -145,7 +147,7 @@ int cvmx_l2c_set_hw_way_partition(uint32_t mask) 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)) + if (mask == valid_mask && !OCTEON_IS_MODEL(OCTEON_CN63XX)) return -1; if (OCTEON_IS_MODEL(OCTEON_CN63XX)) @@ -284,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. */ 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; } @@ -436,7 +438,7 @@ void cvmx_l2c_flush(void) 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)); + (assoc << assoc_shift) | (set << set_shift)); CVMX_CACHE_WBIL2I(address, 0); } } @@ -571,8 +573,8 @@ 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) { @@ -616,12 +618,12 @@ static union __cvmx_l2c_tag __read_l2_tag(uint64_t assoc, uint64_t index) ".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 */ + "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 %[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 */ + "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) @@ -662,10 +664,10 @@ union cvmx_l2c_tag cvmx_l2c_get_tag(uint32_t association, uint32_t index) 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.V = l2c_tadx_tag.s.valid; + tag.s.D = l2c_tadx_tag.s.dirty; + tag.s.L = l2c_tadx_tag.s.lock; + tag.s.U = l2c_tadx_tag.s.use; tag.s.addr = l2c_tadx_tag.s.tag; } else { union __cvmx_l2c_tag tmp_tag; @@ -677,34 +679,34 @@ union cvmx_l2c_tag cvmx_l2c_get_tag(uint32_t association, uint32_t index) * 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.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.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.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.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.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__); @@ -863,7 +865,7 @@ void cvmx_l2c_flush_line(uint32_t assoc, uint32_t index) 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 + * the operation * Bits<16:7> of the effect address select the index */ address = CVMX_ADD_SEG(CVMX_MIPS_SPACE_XKPHYS, diff --git a/arch/mips/cavium-octeon/executive/cvmx-pko.c b/arch/mips/cavium-octeon/executive/cvmx-pko.c index f557084b109..008b881cdf6 100644 --- a/arch/mips/cavium-octeon/executive/cvmx-pko.c +++ b/arch/mips/cavium-octeon/executive/cvmx-pko.c @@ -99,7 +99,7 @@ void cvmx_pko_initialize_global(void) * be called after the FPA has been initialized and filled with pages. * * Returns 0 on success - * !0 on failure + * !0 on failure */ int cvmx_pko_initialize_local(void) { @@ -140,7 +140,7 @@ void cvmx_pko_disable(void) 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. @@ -182,23 +182,24 @@ void cvmx_pko_shutdown(void) } __cvmx_pko_reset(); } +EXPORT_SYMBOL_GPL(cvmx_pko_shutdown); /** * Configure a output port and the associated queues for use. * - * @port: Port to configure. + * @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. + * @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, @@ -440,7 +441,7 @@ void cvmx_pko_show_queue_map() * @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. + * limiting cuts in. * * Returns Zero on success, negative on failure */ @@ -473,7 +474,7 @@ int cvmx_pko_rate_limit_packets(int port, int packets_s, int burst) * @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. + * limiting cuts in. * * Returns Zero on success, negative on failure */ diff --git a/arch/mips/cavium-octeon/executive/cvmx-spi.c b/arch/mips/cavium-octeon/executive/cvmx-spi.c index 74afb1710cd..459e3b1eb61 100644 --- a/arch/mips/cavium-octeon/executive/cvmx-spi.c +++ b/arch/mips/cavium-octeon/executive/cvmx-spi.c @@ -69,7 +69,7 @@ static cvmx_spi_callbacks_t cvmx_spi_callbacks = { /** * Get current SPI4 initialization callbacks * - * @callbacks: Pointer to the callbacks structure.to fill + * @callbacks: Pointer to the callbacks structure.to fill * * Returns Pointer to cvmx_spi_callbacks_t structure. */ @@ -92,11 +92,11 @@ void cvmx_spi_set_callbacks(cvmx_spi_callbacks_t *new_callbacks) * Initialize and start the SPI interface. * * @interface: The identifier of the packet interface to configure and - * use as a SPI interface. + * 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). + * 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 * @@ -138,11 +138,11 @@ int cvmx_spi_start_interface(int interface, cvmx_spi_mode_t mode, int timeout, * with its correspondent system. * * @interface: The identifier of the packet interface to configure and - * use as a SPI interface. + * 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). + * 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. @@ -160,7 +160,7 @@ int cvmx_spi_restart_interface(int interface, cvmx_spi_mode_t mode, int timeout) 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 */ + /* 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); @@ -177,16 +177,17 @@ int cvmx_spi_restart_interface(int interface, cvmx_spi_mode_t mode, int timeout) 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. + * 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). + * 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) @@ -297,11 +298,11 @@ int cvmx_spi_reset_cb(int interface, cvmx_spi_mode_t mode) * 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. + * 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). + * 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 @@ -382,7 +383,7 @@ int cvmx_spi_calendar_setup_cb(int interface, cvmx_spi_mode_t mode, 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 */ + stxx_spi4_dat.s.max_t = 0xFFFF; /*Minimum interval is 0x20 */ cvmx_write_csr(CVMX_STXX_SPI4_DAT(interface), stxx_spi4_dat.u64); @@ -416,11 +417,11 @@ int cvmx_spi_calendar_setup_cb(int interface, cvmx_spi_mode_t mode, * Callback to perform clock detection * * @interface: The identifier of the packet interface to configure and - * use as a SPI interface. + * 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). + * 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 @@ -494,11 +495,11 @@ int cvmx_spi_clock_detect_cb(int interface, cvmx_spi_mode_t mode, int timeout) * Callback to perform link training * * @interface: The identifier of the packet interface to configure and - * use as a SPI interface. + * 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). + * 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 @@ -563,11 +564,11 @@ int cvmx_spi_training_cb(int interface, cvmx_spi_mode_t mode, int timeout) * Callback to perform calendar data synchronization * * @interface: The identifier of the packet interface to configure and - * use as a SPI interface. + * 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). + * 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 @@ -620,11 +621,11 @@ int cvmx_spi_calendar_sync_cb(int interface, cvmx_spi_mode_t mode, int timeout) * Callback to handle interface up * * @interface: The identifier of the packet interface to configure and - * use as a SPI interface. + * 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). + * 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) diff --git a/arch/mips/cavium-octeon/executive/cvmx-sysinfo.c b/arch/mips/cavium-octeon/executive/cvmx-sysinfo.c index 8b18a20cc7b..3d17fac2935 100644 --- a/arch/mips/cavium-octeon/executive/cvmx-sysinfo.c +++ b/arch/mips/cavium-octeon/executive/cvmx-sysinfo.c @@ -74,26 +74,26 @@ 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, diff --git a/arch/mips/cavium-octeon/flash_setup.c b/arch/mips/cavium-octeon/flash_setup.c index e44a55bc7f0..237e5b1a72d 100644 --- a/arch/mips/cavium-octeon/flash_setup.c +++ b/arch/mips/cavium-octeon/flash_setup.c @@ -51,7 +51,8 @@ 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%08llx\n", flash_map.size >> 20, flash_map.phys); 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 ffd4ae660f7..1b82ac6921e 100644 --- a/arch/mips/cavium-octeon/octeon-irq.c +++ b/arch/mips/cavium-octeon/octeon-irq.c @@ -3,22 +3,24 @@ * License. See the file "COPYING" in the main directory of this archive * for more details. * - * Copyright (C) 2004-2008, 2009, 2010, 2011 Cavium Networks + * Copyright (C) 2004-2012 Cavium, Inc. */ #include <linux/interrupt.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> - -static DEFINE_RAW_SPINLOCK(octeon_irq_ciu0_lock); -static DEFINE_RAW_SPINLOCK(octeon_irq_ciu1_lock); +#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]; @@ -26,8 +28,9 @@ union octeon_ciu_chip_data { void *p; unsigned long l; struct { - unsigned int line:6; - unsigned int bit:6; + unsigned long line:6; + unsigned long bit:6; + unsigned long gpio_line:6; } s; }; @@ -42,9 +45,9 @@ struct octeon_core_chip_data { static struct octeon_core_chip_data octeon_irq_core_chip_data[MIPS_CORE_IRQ_LINES]; -static void __init octeon_irq_set_ciu_mapping(int irq, int line, int bit, - struct irq_chip *chip, - irq_flow_handler_t handler) +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) { union octeon_ciu_chip_data cd; @@ -53,11 +56,18 @@ static void __init octeon_irq_set_ciu_mapping(int irq, int line, int bit, 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 @@ -180,19 +190,9 @@ static void __init octeon_irq_init_core(void) mutex_init(&cd->core_irq_mutex); irq = OCTEON_IRQ_SW0 + i; - switch (irq) { - case OCTEON_IRQ_TIMER: - case OCTEON_IRQ_SW0: - case OCTEON_IRQ_SW1: - case OCTEON_IRQ_5: - case OCTEON_IRQ_PERF: - irq_set_chip_data(irq, cd); - irq_set_chip_and_handler(irq, &octeon_irq_chip_core, - handle_percpu_irq); - break; - default: - break; - } + irq_set_chip_data(irq, cd); + irq_set_chip_and_handler(irq, &octeon_irq_chip_core, + handle_percpu_irq); } } @@ -232,22 +232,31 @@ static void octeon_irq_ciu_enable(struct irq_data *data) 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) { - raw_spin_lock_irqsave(&octeon_irq_ciu0_lock, flags); pen = &per_cpu(octeon_irq_ciu0_en_mirror, cpu); - set_bit(cd.s.bit, pen); + __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); - raw_spin_unlock_irqrestore(&octeon_irq_ciu0_lock, flags); } else { - raw_spin_lock_irqsave(&octeon_irq_ciu1_lock, flags); pen = &per_cpu(octeon_irq_ciu1_en_mirror, cpu); - set_bit(cd.s.bit, pen); + __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(&octeon_irq_ciu1_lock, flags); } + raw_spin_unlock_irqrestore(lock, flags); } static void octeon_irq_ciu_enable_local(struct irq_data *data) @@ -255,22 +264,31 @@ static void octeon_irq_ciu_enable_local(struct irq_data *data) unsigned long *pen; unsigned long flags; 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) { - raw_spin_lock_irqsave(&octeon_irq_ciu0_lock, flags); pen = &__get_cpu_var(octeon_irq_ciu0_en_mirror); - set_bit(cd.s.bit, pen); + __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); - raw_spin_unlock_irqrestore(&octeon_irq_ciu0_lock, flags); } else { - raw_spin_lock_irqsave(&octeon_irq_ciu1_lock, flags); pen = &__get_cpu_var(octeon_irq_ciu1_en_mirror); - set_bit(cd.s.bit, pen); + __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(&octeon_irq_ciu1_lock, flags); } + raw_spin_unlock_irqrestore(lock, flags); } static void octeon_irq_ciu_disable_local(struct irq_data *data) @@ -278,22 +296,31 @@ static void octeon_irq_ciu_disable_local(struct irq_data *data) unsigned long *pen; unsigned long flags; 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) { - raw_spin_lock_irqsave(&octeon_irq_ciu0_lock, flags); pen = &__get_cpu_var(octeon_irq_ciu0_en_mirror); - clear_bit(cd.s.bit, pen); + __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); - raw_spin_unlock_irqrestore(&octeon_irq_ciu0_lock, flags); } else { - raw_spin_lock_irqsave(&octeon_irq_ciu1_lock, flags); pen = &__get_cpu_var(octeon_irq_ciu1_en_mirror); - clear_bit(cd.s.bit, pen); + __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(&octeon_irq_ciu1_lock, flags); } + raw_spin_unlock_irqrestore(lock, flags); } static void octeon_irq_ciu_disable_all(struct irq_data *data) @@ -302,29 +329,30 @@ static void octeon_irq_ciu_disable_all(struct irq_data *data) unsigned long *pen; int cpu; union octeon_ciu_chip_data cd; - - wmb(); /* Make sure flag changes arrive before register updates. */ + raw_spinlock_t *lock; cd.p = irq_data_get_irq_chip_data(data); - if (cd.s.line == 0) { - raw_spin_lock_irqsave(&octeon_irq_ciu0_lock, flags); - for_each_online_cpu(cpu) { - int coreid = octeon_coreid_for_cpu(cpu); + 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); - clear_bit(cd.s.bit, pen); - cvmx_write_csr(CVMX_CIU_INTX_EN0(coreid * 2), *pen); - } - raw_spin_unlock_irqrestore(&octeon_irq_ciu0_lock, flags); - } else { - raw_spin_lock_irqsave(&octeon_irq_ciu1_lock, flags); - for_each_online_cpu(cpu) { - int coreid = octeon_coreid_for_cpu(cpu); + else pen = &per_cpu(octeon_irq_ciu1_en_mirror, cpu); - clear_bit(cd.s.bit, pen); + + 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(&octeon_irq_ciu1_lock, flags); + raw_spin_unlock_irqrestore(lock, flags); } } @@ -334,27 +362,30 @@ static void octeon_irq_ciu_enable_all(struct irq_data *data) unsigned long *pen; int cpu; union octeon_ciu_chip_data cd; + raw_spinlock_t *lock; cd.p = irq_data_get_irq_chip_data(data); - if (cd.s.line == 0) { - raw_spin_lock_irqsave(&octeon_irq_ciu0_lock, flags); - for_each_online_cpu(cpu) { - int coreid = octeon_coreid_for_cpu(cpu); + 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); - set_bit(cd.s.bit, pen); - cvmx_write_csr(CVMX_CIU_INTX_EN0(coreid * 2), *pen); - } - raw_spin_unlock_irqrestore(&octeon_irq_ciu0_lock, flags); - } else { - raw_spin_lock_irqsave(&octeon_irq_ciu1_lock, flags); - for_each_online_cpu(cpu) { - int coreid = octeon_coreid_for_cpu(cpu); + else pen = &per_cpu(octeon_irq_ciu1_en_mirror, cpu); - set_bit(cd.s.bit, pen); + + 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(&octeon_irq_ciu1_lock, flags); + raw_spin_unlock_irqrestore(lock, flags); } } @@ -436,7 +467,7 @@ static void octeon_irq_ciu_ack(struct irq_data *data) u64 mask; union octeon_ciu_chip_data cd; - cd.p = data->chip_data; + cd.p = irq_data_get_irq_chip_data(data); mask = 1ull << (cd.s.bit); if (cd.s.line == 0) { @@ -457,9 +488,7 @@ static void octeon_irq_ciu_disable_all_v2(struct irq_data *data) u64 mask; union octeon_ciu_chip_data cd; - wmb(); /* Make sure flag changes arrive before register updates. */ - - cd.p = data->chip_data; + cd.p = irq_data_get_irq_chip_data(data); mask = 1ull << (cd.s.bit); if (cd.s.line == 0) { @@ -487,7 +516,7 @@ static void octeon_irq_ciu_enable_all_v2(struct irq_data *data) u64 mask; union octeon_ciu_chip_data cd; - cd.p = data->chip_data; + cd.p = irq_data_get_irq_chip_data(data); mask = 1ull << (cd.s.bit); if (cd.s.line == 0) { @@ -505,6 +534,85 @@ static void octeon_irq_ciu_enable_all_v2(struct irq_data *data) } } +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_cpu_offline_ciu(struct irq_data *data) @@ -527,7 +635,7 @@ static void octeon_irq_cpu_offline_ciu(struct irq_data *data) cpumask_clear(&new_affinity); cpumask_set_cpu(cpumask_first(cpu_online_mask), &new_affinity); } - __irq_set_affinity_locked(data, &new_affinity); + irq_set_affinity_locked(data, &new_affinity, false); } static int octeon_irq_ciu_set_affinity(struct irq_data *data, @@ -537,8 +645,10 @@ static int octeon_irq_ciu_set_affinity(struct irq_data *data, 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 = data->chip_data; + cd.p = irq_data_get_irq_chip_data(data); /* * For non-v2 CIU, we will allow only single CPU affinity. @@ -551,36 +661,36 @@ static int octeon_irq_ciu_set_affinity(struct irq_data *data, if (!enable_one) return 0; - if (cd.s.line == 0) { - raw_spin_lock_irqsave(&octeon_irq_ciu0_lock, flags); - for_each_online_cpu(cpu) { - int coreid = octeon_coreid_for_cpu(cpu); - unsigned long *pen = &per_cpu(octeon_irq_ciu0_en_mirror, cpu); - if (cpumask_test_cpu(cpu, dest) && enable_one) { - enable_one = false; - set_bit(cd.s.bit, pen); - } else { - clear_bit(cd.s.bit, pen); - } - cvmx_write_csr(CVMX_CIU_INTX_EN0(coreid * 2), *pen); + for_each_online_cpu(cpu) { + 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); } - raw_spin_unlock_irqrestore(&octeon_irq_ciu0_lock, flags); - } else { - raw_spin_lock_irqsave(&octeon_irq_ciu1_lock, flags); - for_each_online_cpu(cpu) { - int coreid = octeon_coreid_for_cpu(cpu); - unsigned long *pen = &per_cpu(octeon_irq_ciu1_en_mirror, cpu); + /* + * Must be visible to octeon_irq_ip{2,3}_ciu() before + * enabling the irq. + */ + wmb(); - if (cpumask_test_cpu(cpu, dest) && enable_one) { - enable_one = false; - set_bit(cd.s.bit, pen); - } else { - clear_bit(cd.s.bit, pen); - } + 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(&octeon_irq_ciu1_lock, flags); + + raw_spin_unlock_irqrestore(lock, flags); } return 0; } @@ -601,7 +711,7 @@ static int octeon_irq_ciu_set_affinity_v2(struct irq_data *data, if (!enable_one) return 0; - cd.p = data->chip_data; + cd.p = irq_data_get_irq_chip_data(data); mask = 1ull << cd.s.bit; if (cd.s.line == 0) { @@ -636,32 +746,12 @@ static int octeon_irq_ciu_set_affinity_v2(struct irq_data *data, #endif /* - * The v1 CIU code already masks things, so supply a dummy version to - * the core chip code. - */ -static void octeon_irq_dummy_mask(struct irq_data *data) -{ -} - -/* * 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_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_edge_v2 = { - .name = "CIU-E", - .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, @@ -675,19 +765,9 @@ static struct irq_chip octeon_irq_chip_ciu = { .name = "CIU", .irq_enable = octeon_irq_ciu_enable, .irq_disable = octeon_irq_ciu_disable_all, - .irq_mask = octeon_irq_dummy_mask, -#ifdef CONFIG_SMP - .irq_set_affinity = octeon_irq_ciu_set_affinity, - .irq_cpu_offline = octeon_irq_cpu_offline_ciu, -#endif -}; - -static struct irq_chip octeon_irq_chip_ciu_edge = { - .name = "CIU-E", - .irq_enable = octeon_irq_ciu_enable, - .irq_disable = octeon_irq_ciu_disable_all, - .irq_mask = octeon_irq_dummy_mask, .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, @@ -711,12 +791,42 @@ 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 + .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, +}; + /* * Watchdog interrupts are special. They are associated with a single * core, so we hardwire the affinity to that core. @@ -727,12 +837,18 @@ static void octeon_irq_ciu_wd_enable(struct irq_data *data) 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(&octeon_irq_ciu1_lock, flags); + raw_spin_lock_irqsave(lock, flags); pen = &per_cpu(octeon_irq_ciu1_en_mirror, cpu); - set_bit(coreid, pen); + __set_bit(coreid, 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(&octeon_irq_ciu1_lock, flags); + raw_spin_unlock_irqrestore(lock, flags); } /* @@ -761,30 +877,188 @@ 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_dummy_mask, + .irq_mask = octeon_irq_ciu_disable_local, + .irq_unmask = octeon_irq_ciu_enable_local, }; -static void octeon_irq_ip2_v1(void) +static bool octeon_irq_ciu_is_edge(unsigned int line, unsigned int bit) { - const unsigned long core_id = cvmx_get_core_num(); - u64 ciu_sum = cvmx_read_csr(CVMX_CIU_INTX_SUM0(core_id * 2)); + 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; +} - ciu_sum &= __get_cpu_var(octeon_irq_ciu0_en_mirror); - clear_c0_status(STATUSF_IP2); - 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(); +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; } - set_c0_status(STATUSF_IP2); + *out_type = type; + *out_hwirq = pin; + + return 0; } -static void octeon_irq_ip2_v2(void) +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)); @@ -801,12 +1075,12 @@ static void octeon_irq_ip2_v2(void) spurious_interrupt(); } } -static void octeon_irq_ip3_v1(void) + +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); - clear_c0_status(STATUSF_IP3); if (likely(ciu_sum)) { int bit = fls64(ciu_sum) - 1; int irq = octeon_irq_ciu_to_irq[1][bit]; @@ -817,24 +1091,13 @@ static void octeon_irq_ip3_v1(void) } else { spurious_interrupt(); } - set_c0_status(STATUSF_IP3); } -static void octeon_irq_ip3_v2(void) -{ - u64 ciu_sum = cvmx_read_csr(CVMX_CIU_INT_SUM1); +static bool octeon_irq_use_ip4; - 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 void octeon_irq_local_enable_ip4(void *arg) +{ + set_c0_status(STATUSF_IP4); } static void octeon_irq_ip4_mask(void) @@ -847,16 +1110,29 @@ static void (*octeon_irq_ip2)(void); static void (*octeon_irq_ip3)(void); static void (*octeon_irq_ip4)(void); -void __cpuinitdata (*octeon_irq_setup_secondary)(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 __cpuinit octeon_irq_percpu_enable(void) +static void octeon_irq_percpu_enable(void) { irq_cpu_online(); } -static void __cpuinit octeon_irq_init_ciu_percpu(void) +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)); /* * Disable All CIU Interrupts. The ones we need will be * enabled later. Read the SUM register so we know the write @@ -869,12 +1145,30 @@ static void __cpuinit octeon_irq_init_ciu_percpu(void) cvmx_read_csr(CVMX_CIU_INTX_SUM0((coreid * 2))); } -static void __cpuinit octeon_irq_setup_secondary_ciu(void) +static void octeon_irq_init_ciu2_percpu(void) { + u64 regx, ipx; + int coreid = cvmx_get_core_num(); + u64 base = CVMX_CIU2_EN_PPX_IP2_WRKQ(coreid); - __get_cpu_var(octeon_irq_ciu0_en_mirror) = 0; - __get_cpu_var(octeon_irq_ciu1_en_mirror) = 0; + /* + * 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. + */ + 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_setup_secondary_ciu(void) +{ octeon_irq_init_ciu_percpu(); octeon_irq_percpu_enable(); @@ -883,117 +1177,564 @@ static void __cpuinit octeon_irq_setup_secondary_ciu(void) 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_edge; 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)) { - octeon_irq_ip2 = octeon_irq_ip2_v2; - octeon_irq_ip3 = octeon_irq_ip3_v2; chip = &octeon_irq_chip_ciu_v2; - chip_edge = &octeon_irq_chip_ciu_edge_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 { - octeon_irq_ip2 = octeon_irq_ip2_v1; - octeon_irq_ip3 = octeon_irq_ip3_v1; chip = &octeon_irq_chip_ciu; - chip_edge = &octeon_irq_chip_ciu_edge; 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_set_ciu_mapping(i + OCTEON_IRQ_WORKQ0, 0, i + 0, chip, handle_level_irq); - for (i = 0; i < 16; i++) - octeon_irq_set_ciu_mapping(i + OCTEON_IRQ_GPIO0, 0, i + 16, chip, handle_level_irq); + octeon_irq_force_ciu_mapping(ciu_domain, i + OCTEON_IRQ_WORKQ0, 0, i + 0); - octeon_irq_set_ciu_mapping(OCTEON_IRQ_MBOX0, 0, 32, chip_mbox, handle_percpu_irq); - octeon_irq_set_ciu_mapping(OCTEON_IRQ_MBOX1, 0, 33, chip_mbox, handle_percpu_irq); - - octeon_irq_set_ciu_mapping(OCTEON_IRQ_UART0, 0, 34, chip, handle_level_irq); - octeon_irq_set_ciu_mapping(OCTEON_IRQ_UART1, 0, 35, chip, handle_level_irq); + 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_set_ciu_mapping(i + OCTEON_IRQ_PCI_INT0, 0, i + 36, chip, handle_level_irq); + 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_set_ciu_mapping(i + OCTEON_IRQ_PCI_MSI0, 0, i + 40, chip, handle_level_irq); + 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 cpu; + 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) { + 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); + } +} + +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(); + + mask = 1ull << (data->irq - OCTEON_IRQ_MBOX0); + en_addr = CVMX_CIU2_EN_PPX_IP3_MBOX_W1S(coreid); + cvmx_write_csr(en_addr, mask); +} - octeon_irq_set_ciu_mapping(OCTEON_IRQ_TWSI, 0, 45, chip, handle_level_irq); - octeon_irq_set_ciu_mapping(OCTEON_IRQ_RML, 0, 46, chip, handle_level_irq); - octeon_irq_set_ciu_mapping(OCTEON_IRQ_TRACE0, 0, 47, chip, handle_level_irq); +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 int octeon_irq_ciu2_set_affinity(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; + + for_each_online_cpu(cpu) { + 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); + } + + return 0; +} +#endif + +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 + .irq_set_affinity = octeon_irq_ciu2_set_affinity, + .irq_cpu_offline = octeon_irq_cpu_offline_ciu, +#endif +}; + +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 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) +{ + unsigned int ciu, bit; + + ciu = intspec[0]; + bit = intspec[1]; + + *out_hwirq = (ciu << 6) | bit; + *out_type = 0; + + return 0; +} + +static bool octeon_irq_ciu2_is_edge(unsigned int line, unsigned int bit) +{ + 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 int octeon_irq_ciu2_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. + * (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); + + return 0; +} +static int octeon_irq_ciu2_gpio_map(struct irq_domain *d, + unsigned int virq, irq_hw_number_t hw) +{ + return octeon_irq_gpio_map_common(d, virq, hw, 7, &octeon_irq_chip_ciu2_gpio); +} + +static struct irq_domain_ops octeon_irq_domain_ciu2_ops = { + .map = octeon_irq_ciu2_map, + .xlate = octeon_irq_ciu2_xlat, +}; + +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(); + + sum = cvmx_read_csr(CVMX_CIU2_SUM_PPX_IP2(core_id)) & 0xfful; + + if (unlikely(!sum)) + goto spurious; + + line = fls64(sum) - 1; + src_reg = CVMX_CIU2_SRC_PPX_IP2_WRKQ(core_id) + (0x1000 * line); + src = cvmx_read_csr(src_reg); + + if (unlikely(!src)) + goto spurious; - for (i = 0; i < 2; i++) - octeon_irq_set_ciu_mapping(i + OCTEON_IRQ_GMX_DRP0, 0, i + 48, chip_edge, handle_edge_irq); + bit = fls64(src) - 1; + irq = octeon_irq_ciu_to_irq[line][bit]; + if (unlikely(!irq)) + goto spurious; - octeon_irq_set_ciu_mapping(OCTEON_IRQ_IPD_DRP, 0, 50, chip_edge, handle_edge_irq); - octeon_irq_set_ciu_mapping(OCTEON_IRQ_KEY_ZERO, 0, 51, chip_edge, handle_edge_irq); + do_IRQ(irq); + 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_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_set_ciu_mapping(i + OCTEON_IRQ_TIMER0, 0, i + 52, chip_edge, handle_edge_irq); + octeon_irq_force_ciu_mapping(ciu_domain, i + OCTEON_IRQ_TIMER0, 3, i + 8); - octeon_irq_set_ciu_mapping(OCTEON_IRQ_USB0, 0, 56, chip, handle_level_irq); - octeon_irq_set_ciu_mapping(OCTEON_IRQ_PCM, 0, 57, chip, handle_level_irq); - octeon_irq_set_ciu_mapping(OCTEON_IRQ_MPI, 0, 58, chip, handle_level_irq); - octeon_irq_set_ciu_mapping(OCTEON_IRQ_TWSI2, 0, 59, chip, handle_level_irq); - octeon_irq_set_ciu_mapping(OCTEON_IRQ_POWIQ, 0, 60, chip, handle_level_irq); - octeon_irq_set_ciu_mapping(OCTEON_IRQ_IPDPPTHR, 0, 61, chip, handle_level_irq); - octeon_irq_set_ciu_mapping(OCTEON_IRQ_MII0, 0, 62, chip, handle_level_irq); - octeon_irq_set_ciu_mapping(OCTEON_IRQ_BOOTDMA, 0, 63, chip, handle_level_irq); + octeon_irq_force_ciu_mapping(ciu_domain, OCTEON_IRQ_USB0, 3, 44); - /* CIU_1 */ - for (i = 0; i < 16; i++) - octeon_irq_set_ciu_mapping(i + OCTEON_IRQ_WDOG0, 1, i + 0, chip_wd, handle_level_irq); - - octeon_irq_set_ciu_mapping(OCTEON_IRQ_UART2, 1, 16, chip, handle_level_irq); - octeon_irq_set_ciu_mapping(OCTEON_IRQ_USB1, 1, 17, chip, handle_level_irq); - octeon_irq_set_ciu_mapping(OCTEON_IRQ_MII1, 1, 18, chip, handle_level_irq); - octeon_irq_set_ciu_mapping(OCTEON_IRQ_NAND, 1, 19, chip, handle_level_irq); - octeon_irq_set_ciu_mapping(OCTEON_IRQ_MIO, 1, 20, chip, handle_level_irq); - octeon_irq_set_ciu_mapping(OCTEON_IRQ_IOB, 1, 21, chip, handle_level_irq); - octeon_irq_set_ciu_mapping(OCTEON_IRQ_FPA, 1, 22, chip, handle_level_irq); - octeon_irq_set_ciu_mapping(OCTEON_IRQ_POW, 1, 23, chip, handle_level_irq); - octeon_irq_set_ciu_mapping(OCTEON_IRQ_L2C, 1, 24, chip, handle_level_irq); - octeon_irq_set_ciu_mapping(OCTEON_IRQ_IPD, 1, 25, chip, handle_level_irq); - octeon_irq_set_ciu_mapping(OCTEON_IRQ_PIP, 1, 26, chip, handle_level_irq); - octeon_irq_set_ciu_mapping(OCTEON_IRQ_PKO, 1, 27, chip, handle_level_irq); - octeon_irq_set_ciu_mapping(OCTEON_IRQ_ZIP, 1, 28, chip, handle_level_irq); - octeon_irq_set_ciu_mapping(OCTEON_IRQ_TIM, 1, 29, chip, handle_level_irq); - octeon_irq_set_ciu_mapping(OCTEON_IRQ_RAD, 1, 30, chip, handle_level_irq); - octeon_irq_set_ciu_mapping(OCTEON_IRQ_KEY, 1, 31, chip, handle_level_irq); - octeon_irq_set_ciu_mapping(OCTEON_IRQ_DFA, 1, 32, chip, handle_level_irq); - octeon_irq_set_ciu_mapping(OCTEON_IRQ_USBCTL, 1, 33, chip, handle_level_irq); - octeon_irq_set_ciu_mapping(OCTEON_IRQ_SLI, 1, 34, chip, handle_level_irq); - octeon_irq_set_ciu_mapping(OCTEON_IRQ_DPI, 1, 35, chip, handle_level_irq); - - octeon_irq_set_ciu_mapping(OCTEON_IRQ_AGX0, 1, 36, chip, handle_level_irq); - - octeon_irq_set_ciu_mapping(OCTEON_IRQ_AGL, 1, 46, chip, handle_level_irq); - - octeon_irq_set_ciu_mapping(OCTEON_IRQ_PTP, 1, 47, chip_edge, handle_edge_irq); - - octeon_irq_set_ciu_mapping(OCTEON_IRQ_PEM0, 1, 48, chip, handle_level_irq); - octeon_irq_set_ciu_mapping(OCTEON_IRQ_PEM1, 1, 49, chip, handle_level_irq); - octeon_irq_set_ciu_mapping(OCTEON_IRQ_SRIO0, 1, 50, chip, handle_level_irq); - octeon_irq_set_ciu_mapping(OCTEON_IRQ_SRIO1, 1, 51, chip, handle_level_irq); - octeon_irq_set_ciu_mapping(OCTEON_IRQ_LMC0, 1, 52, chip, handle_level_irq); - octeon_irq_set_ciu_mapping(OCTEON_IRQ_DFM, 1, 56, chip, handle_level_irq); - octeon_irq_set_ciu_mapping(OCTEON_IRQ_RST, 1, 63, chip, handle_level_irq); + 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); @@ -1007,7 +1748,10 @@ void __init arch_init_irq(void) cpumask_clear(irq_default_affinity); cpumask_set_cpu(smp_processor_id(), irq_default_affinity); #endif - octeon_irq_init_ciu(); + if (OCTEON_IS_MODEL(OCTEON_CN68XX)) + octeon_irq_init_ciu2(); + else + octeon_irq_init_ciu(); } asmlinkage void plat_irq_dispatch(void) @@ -1036,7 +1780,7 @@ asmlinkage void plat_irq_dispatch(void) #ifdef CONFIG_HOTPLUG_CPU -void fixup_irqs(void) +void octeon_fixup_irqs(void) { irq_cpu_offline(); } 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 index cd61d7281d9..6df0f4d8f19 100644 --- a/arch/mips/cavium-octeon/octeon-platform.c +++ b/arch/mips/cavium-octeon/octeon-platform.c @@ -3,7 +3,7 @@ * License. See the file "COPYING" in the main directory of this archive * for more details. * - * Copyright (C) 2004-2010 Cavium Networks + * Copyright (C) 2004-2011 Cavium Networks * Copyright (C) 2008 Wind River Systems */ @@ -13,112 +13,16 @@ #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> - -static struct octeon_cf_data octeon_cf_data; - -static int __init octeon_cf_device_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; - } else { - base_ptr = 0x1d000800; - } - - if (!base_ptr) - return ret; - - /* 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; - } - 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. - */ - - /* Assume 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; - } - - pd = platform_device_alloc("pata_octeon_cf", -1); - if (!pd) { - ret = -ENOMEM; - goto out; - } - pd->dev.platform_data = &octeon_cf_data; - - ret = platform_device_add_resources(pd, cf_resources, num_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_cf_device_init); +#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) @@ -162,182 +66,6 @@ out: } device_initcall(octeon_rng_device_init); -static struct i2c_board_info __initdata octeon_i2c_devices[] = { - { - I2C_BOARD_INFO("ds1337", 0x68), - }, -}; - -static int __init octeon_i2c_devices_init(void) -{ - return i2c_register_board_info(0, octeon_i2c_devices, - ARRAY_SIZE(octeon_i2c_devices)); -} -arch_initcall(octeon_i2c_devices_init); - -#define OCTEON_I2C_IO_BASE 0x1180000001000ull -#define OCTEON_I2C_IO_UNIT_OFFSET 0x200 - -static struct octeon_i2c_data octeon_i2c_data[2]; - -static int __init octeon_i2c_device_init(void) -{ - struct platform_device *pd; - int ret = 0; - int port, num_ports; - - struct resource i2c_resources[] = { - { - .flags = IORESOURCE_MEM, - }, { - .flags = IORESOURCE_IRQ, - } - }; - - if (OCTEON_IS_MODEL(OCTEON_CN56XX) || OCTEON_IS_MODEL(OCTEON_CN52XX)) - num_ports = 2; - else - num_ports = 1; - - for (port = 0; port < num_ports; port++) { - octeon_i2c_data[port].sys_freq = octeon_get_io_clock_rate(); - /*FIXME: should be examined. At the moment is set for 100Khz */ - octeon_i2c_data[port].i2c_freq = 100000; - - pd = platform_device_alloc("i2c-octeon", port); - if (!pd) { - ret = -ENOMEM; - goto out; - } - - pd->dev.platform_data = octeon_i2c_data + port; - - i2c_resources[0].start = - OCTEON_I2C_IO_BASE + (port * OCTEON_I2C_IO_UNIT_OFFSET); - i2c_resources[0].end = i2c_resources[0].start + 0x1f; - switch (port) { - case 0: - i2c_resources[1].start = OCTEON_IRQ_TWSI; - i2c_resources[1].end = OCTEON_IRQ_TWSI; - break; - case 1: - i2c_resources[1].start = OCTEON_IRQ_TWSI2; - i2c_resources[1].end = OCTEON_IRQ_TWSI2; - break; - default: - BUG(); - } - - ret = platform_device_add_resources(pd, - i2c_resources, - ARRAY_SIZE(i2c_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_i2c_device_init); - -/* Octeon SMI/MDIO interface. */ -static int __init octeon_mdiobus_device_init(void) -{ - struct platform_device *pd; - int ret = 0; - - if (octeon_is_simulation()) - return 0; /* No mdio in the simulator. */ - - /* The bus number is the platform_device id. */ - pd = platform_device_alloc("mdio-octeon", 0); - if (!pd) { - ret = -ENOMEM; - goto out; - } - - ret = platform_device_add(pd); - if (ret) - goto fail; - - return ret; -fail: - platform_device_put(pd); - -out: - return ret; - -} -device_initcall(octeon_mdiobus_device_init); - -/* Octeon mgmt port Ethernet interface. */ -static int __init octeon_mgmt_device_init(void) -{ - struct platform_device *pd; - int ret = 0; - int port, num_ports; - - struct resource mgmt_port_resource = { - .flags = IORESOURCE_IRQ, - .start = -1, - .end = -1 - }; - - if (!OCTEON_IS_MODEL(OCTEON_CN56XX) && !OCTEON_IS_MODEL(OCTEON_CN52XX)) - return 0; - - if (OCTEON_IS_MODEL(OCTEON_CN56XX)) - num_ports = 1; - else - num_ports = 2; - - for (port = 0; port < num_ports; port++) { - pd = platform_device_alloc("octeon_mgmt", port); - if (!pd) { - ret = -ENOMEM; - goto out; - } - /* No DMA restrictions */ - pd->dev.coherent_dma_mask = DMA_BIT_MASK(64); - pd->dev.dma_mask = &pd->dev.coherent_dma_mask; - - switch (port) { - case 0: - mgmt_port_resource.start = OCTEON_IRQ_MII0; - break; - case 1: - mgmt_port_resource.start = OCTEON_IRQ_MII1; - break; - default: - BUG(); - } - mgmt_port_resource.end = mgmt_port_resource.start; - - ret = platform_device_add_resources(pd, &mgmt_port_resource, 1); - - 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_mgmt_device_init); - #ifdef CONFIG_USB static int __init octeon_ehci_device_init(void) @@ -440,6 +168,561 @@ 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", + ¤t_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, ¤t_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 index 428864b2ba4..7b066bbca86 100644 --- a/arch/mips/cavium-octeon/octeon_boot.h +++ b/arch/mips/cavium-octeon/octeon_boot.h @@ -31,7 +31,7 @@ struct boot_init_vector { uint32_t k0_val; /* Address of boot info block structure */ uint64_t boot_info_addr; - uint32_t flags; /* flags */ + uint32_t flags; /* flags */ uint32_t pad; }; @@ -53,20 +53,20 @@ struct linux_app_boot_info { /* 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 +#define AVAIL_COREMASK_OFFSET_IN_LINUX_APP_BOOT_BLOCK 0x765c /* hardcoded in bootloader */ -#define LABI_ADDR_IN_BOOTLOADER 0x700 +#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) +#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) +#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 057f0ae88c9..00000000000 --- a/arch/mips/cavium-octeon/serial.c +++ /dev/null @@ -1,137 +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 <linux/irq.h> - -#include <asm/time.h> - -#include <asm/octeon/octeon.h> - -#define DEBUG_UART 1 - -unsigned int octeon_serial_in(struct uart_port *up, int offset) -{ - int rv = cvmx_read_csr((uint64_t)(up->membase + (offset << 3))); - if (offset == UART_IIR && (rv & 0xf) == 7) { - /* Busy interrupt, read the USR (39) and try again. */ - cvmx_read_csr((uint64_t)(up->membase + (39 << 3))); - rv = cvmx_read_csr((uint64_t)(up->membase + (offset << 3))); - } - return rv; -} - -void octeon_serial_out(struct uart_port *up, int offset, int value) -{ - /* - * If bits 6 or 7 of the OCTEON UART's LCR are set, it quits - * working. - */ - if (offset == UART_LCR) - value &= 0x9f; - cvmx_write_csr((uint64_t)(up->membase + (offset << 3)), (u8)value); -} - -/* - * 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 */ - if (octeon_is_simulation()) - /* Make simulator output fast*/ - p->uartclk = 115200 * 16; - else - p->uartclk = octeon_get_io_clock_rate(); - 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 260dc247c05..008e9c8b8ea 100644 --- a/arch/mips/cavium-octeon/setup.c +++ b/arch/mips/cavium-octeon/setup.c @@ -4,9 +4,13 @@ * 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> @@ -21,6 +25,9 @@ #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> @@ -35,12 +42,6 @@ #include <asm/octeon/pci-octeon.h> #include <asm/octeon/cvmx-mio-defs.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 - extern struct plat_smp_ops octeon_smp_ops; #ifdef CONFIG_PCI @@ -54,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); @@ -117,7 +318,7 @@ EXPORT_SYMBOL(octeon_get_io_clock_rate); * 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) { @@ -139,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) { @@ -223,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"); } @@ -255,18 +459,6 @@ static void octeon_halt(void) } /** - * 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 * * Returns @@ -413,6 +605,8 @@ void octeon_user_io_init(void) void __init prom_init(void) { struct cvmx_sysinfo *sysinfo; + const char *arg; + char *p; int i; int argc; #ifdef CONFIG_CAVIUM_RESERVE32 @@ -502,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); @@ -535,16 +729,7 @@ void __init prom_init(void) 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 @@ -562,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++) { @@ -569,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) { @@ -609,16 +811,22 @@ void __init prom_init(void) mips_hpt_frequency = octeon_get_clock_rate(); octeon_init_cvmcount(); - octeon_setup_delays(); _machine_restart = octeon_restart; _machine_halt = octeon_halt; +#ifdef CONFIG_KEXEC + _machine_kexec_shutdown = octeon_shutdown; + _machine_crash_shutdown = octeon_crash_shutdown; + _machine_kexec_prepare = octeon_kexec_prepare; +#endif + 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) { @@ -633,14 +841,21 @@ static __init void memory_exclude_page(u64 addr, u64 *mem, u64 *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; + crashk_end = 0; /* * The Mips memory init uses the first memory location for @@ -653,6 +868,17 @@ void __init plat_mem_setup(void) 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 @@ -661,22 +887,15 @@ 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 @@ -689,20 +908,67 @@ void __init plat_mem_setup(void) 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 (size) - add_memory_region(memory, size, BOOT_MEM_RAM); + 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 /* @@ -716,11 +982,11 @@ 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 + * Emit one character to the boot UART. Exported for use by the * watchdog timer. */ int prom_putchar(char c) @@ -765,13 +1031,112 @@ void prom_free_prom_memory(void) panic("Core-14449 WAR not in place (%04x).\n" "Please build kernel with proper options (CONFIG_CAVIUM_CN63XXP1).", insn); } -#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)"); +int octeon_prune_device_tree(void); + +extern const char __dtb_octeon_3xxx_begin; +extern const char __dtb_octeon_68xx_begin; +void __init device_tree_init(void) +{ + 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 { + fdt = &__dtb_octeon_3xxx_begin; + do_prune = true; } -#endif + + initial_boot_params = (void *)fdt; + + if (do_prune) { + octeon_prune_device_tree(); + pr_info("Using internal Device Tree.\n"); + } else { + pr_info("Using passed Device Tree.\n"); + } + unflatten_and_copy_device_tree(); +} + +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); + } + } + + 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); + } + } + + return err; +} +device_initcall(edac_devinit); + +static void __initdata *octeon_dummy_iospace; + +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; } +late_initcall(octeon_no_pci_release); diff --git a/arch/mips/cavium-octeon/smp.c b/arch/mips/cavium-octeon/smp.c index ee1fb9f7f51..a7b3ae104d8 100644 --- a/arch/mips/cavium-octeon/smp.c +++ b/arch/mips/cavium-octeon/smp.c @@ -6,7 +6,6 @@ * Copyright (C) 2004-2008, 2009, 2010 Cavium Networks */ #include <linux/cpu.h> -#include <linux/init.h> #include <linux/delay.h> #include <linux/smp.h> #include <linux/interrupt.h> @@ -55,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) @@ -126,8 +125,8 @@ static void octeon_smp_setup(void) #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 + * 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++) { @@ -173,7 +172,7 @@ static void octeon_boot_secondary(int cpu, struct task_struct *idle) * After we've done initial boot, this function is called to allow the * board code to clean up state, if needed */ -static void __cpuinit octeon_init_secondary(void) +static void octeon_init_secondary(void) { unsigned int sr; @@ -219,15 +218,6 @@ 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 */ @@ -235,28 +225,11 @@ static void octeon_smp_finish(void) local_irq_enable(); } -/** - * Hook for after all CPUs are online - */ -static void octeon_cpus_done(void) -{ -#ifdef CONFIG_CAVIUM_GDB - unsigned long tmp; - /* Pulse MCD0 signal on Ctrl-C to stop all the cores. Also set the MCD0 - to be not masked by this core so we know the signal is received by - someone */ - asm volatile ("dmfc0 %0, $22\n" - "ori %0, %0, 0x9100\n" "dmtc0 %0, $22\n" : "=r" (tmp)); -#endif -} - #ifdef CONFIG_HOTPLUG_CPU /* State of each CPU. */ DEFINE_PER_CPU(int, cpu_state); -extern void fixup_irqs(void); - static int octeon_cpu_disable(void) { unsigned int cpu = smp_processor_id(); @@ -267,7 +240,7 @@ static int octeon_cpu_disable(void) set_cpu_online(cpu, false); cpu_clear(cpu, cpu_callin_map); local_irq_disable(); - fixup_irqs(); + octeon_fixup_irqs(); local_irq_enable(); flush_cache_all(); @@ -332,7 +305,7 @@ extern void kernel_entry(unsigned long arg1, ...); static void start_after_reset(void) { - kernel_entry(0, 0, 0); /* set a2 = 0 for secondary core */ + kernel_entry(0, 0, 0); /* set a2 = 0 for secondary core */ } static int octeon_update_boot_vector(unsigned int cpu) @@ -375,7 +348,7 @@ static int octeon_update_boot_vector(unsigned int cpu) return 0; } -static int __cpuinit octeon_cpu_callback(struct notifier_block *nfb, +static int octeon_cpu_callback(struct notifier_block *nfb, unsigned long action, void *hcpu) { unsigned int cpu = (unsigned long)hcpu; @@ -394,21 +367,20 @@ static int __cpuinit octeon_cpu_callback(struct notifier_block *nfb, return NOTIFY_OK; } -static int __cpuinit register_cavium_notifier(void) +static int register_cavium_notifier(void) { hotcpu_notifier(octeon_cpu_callback, 0); return 0; } late_initcall(register_cavium_notifier); -#endif /* CONFIG_HOTPLUG_CPU */ +#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, |
