diff options
Diffstat (limited to 'arch/mips/netlogic/xlr/fmn.c')
| -rw-r--r-- | arch/mips/netlogic/xlr/fmn.c | 204 | 
1 files changed, 204 insertions, 0 deletions
diff --git a/arch/mips/netlogic/xlr/fmn.c b/arch/mips/netlogic/xlr/fmn.c new file mode 100644 index 00000000000..d428e8471ee --- /dev/null +++ b/arch/mips/netlogic/xlr/fmn.c @@ -0,0 +1,204 @@ +/* + * Copyright (c) 2003-2012 Broadcom Corporation + * All Rights Reserved + * + * This software is available to you under a choice of one of two + * licenses.  You may choose to be licensed under the terms of the GNU + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the Broadcom + * license below: + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + *    notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + *    notice, this list of conditions and the following disclaimer in + *    the documentation and/or other materials provided with the + *    distribution. + * + * THIS SOFTWARE IS PROVIDED BY BROADCOM ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL BROADCOM OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <linux/kernel.h> +#include <linux/irqreturn.h> +#include <linux/irq.h> +#include <linux/interrupt.h> + +#include <asm/mipsregs.h> +#include <asm/netlogic/interrupt.h> +#include <asm/netlogic/xlr/fmn.h> +#include <asm/netlogic/common.h> + +#define COP2_CC_INIT_CPU_DEST(dest, conf) \ +do { \ +	nlm_write_c2_cc##dest(0, conf[(dest * 8) + 0]); \ +	nlm_write_c2_cc##dest(1, conf[(dest * 8) + 1]); \ +	nlm_write_c2_cc##dest(2, conf[(dest * 8) + 2]); \ +	nlm_write_c2_cc##dest(3, conf[(dest * 8) + 3]); \ +	nlm_write_c2_cc##dest(4, conf[(dest * 8) + 4]); \ +	nlm_write_c2_cc##dest(5, conf[(dest * 8) + 5]); \ +	nlm_write_c2_cc##dest(6, conf[(dest * 8) + 6]); \ +	nlm_write_c2_cc##dest(7, conf[(dest * 8) + 7]); \ +} while (0) + +struct fmn_message_handler { +	void (*action)(int, int, int, int, struct nlm_fmn_msg *, void *); +	void *arg; +} msg_handlers[128]; + +/* + * FMN interrupt handler. We configure the FMN so that any messages in + * any of the CPU buckets will trigger an interrupt on the CPU. + * The message can be from any device on the FMN (like NAE/SAE/DMA). + * The source station id is used to figure out which of the registered + * handlers have to be called. + */ +static irqreturn_t fmn_message_handler(int irq, void *data) +{ +	struct fmn_message_handler *hndlr; +	int bucket, rv; +	int size = 0, code = 0, src_stnid = 0; +	struct nlm_fmn_msg msg; +	uint32_t mflags, bkt_status; + +	mflags = nlm_cop2_enable_irqsave(); +	/* Disable message ring interrupt */ +	nlm_fmn_setup_intr(irq, 0); +	while (1) { +		/* 8 bkts per core, [24:31] each bit represents one bucket +		 * Bit is Zero if bucket is not empty */ +		bkt_status = (nlm_read_c2_status0() >> 24) & 0xff; +		if (bkt_status == 0xff) +			break; +		for (bucket = 0; bucket < 8; bucket++) { +			/* Continue on empty bucket */ +			if (bkt_status & (1 << bucket)) +				continue; +			rv = nlm_fmn_receive(bucket, &size, &code, &src_stnid, +						&msg); +			if (rv != 0) +				continue; + +			hndlr = &msg_handlers[src_stnid]; +			if (hndlr->action == NULL) +				pr_warn("No msgring handler for stnid %d\n", +						src_stnid); +			else { +				nlm_cop2_disable_irqrestore(mflags); +				hndlr->action(bucket, src_stnid, size, code, +					&msg, hndlr->arg); +				mflags = nlm_cop2_enable_irqsave(); +			} +		} +	}; +	/* Enable message ring intr, to any thread in core */ +	nlm_fmn_setup_intr(irq, (1 << nlm_threads_per_core) - 1); +	nlm_cop2_disable_irqrestore(mflags); +	return IRQ_HANDLED; +} + +struct irqaction fmn_irqaction = { +	.handler = fmn_message_handler, +	.flags = IRQF_PERCPU, +	.name = "fmn", +}; + +void xlr_percpu_fmn_init(void) +{ +	struct xlr_fmn_info *cpu_fmn_info; +	int *bucket_sizes; +	uint32_t flags; +	int id; + +	BUG_ON(nlm_thread_id() != 0); +	id = nlm_core_id(); + +	bucket_sizes = xlr_board_fmn_config.bucket_size; +	cpu_fmn_info = &xlr_board_fmn_config.cpu[id]; +	flags = nlm_cop2_enable_irqsave(); + +	/* Setup bucket sizes for the core. */ +	nlm_write_c2_bucksize(0, bucket_sizes[id * 8 + 0]); +	nlm_write_c2_bucksize(1, bucket_sizes[id * 8 + 1]); +	nlm_write_c2_bucksize(2, bucket_sizes[id * 8 + 2]); +	nlm_write_c2_bucksize(3, bucket_sizes[id * 8 + 3]); +	nlm_write_c2_bucksize(4, bucket_sizes[id * 8 + 4]); +	nlm_write_c2_bucksize(5, bucket_sizes[id * 8 + 5]); +	nlm_write_c2_bucksize(6, bucket_sizes[id * 8 + 6]); +	nlm_write_c2_bucksize(7, bucket_sizes[id * 8 + 7]); + +	/* +	 * For sending FMN messages, we need credits on the destination +	 * bucket. Program the credits this core has on the 128 possible +	 * destination buckets. +	 * We cannot use a loop here, because the the first argument has +	 * to be a constant integer value. +	 */ +	COP2_CC_INIT_CPU_DEST(0, cpu_fmn_info->credit_config); +	COP2_CC_INIT_CPU_DEST(1, cpu_fmn_info->credit_config); +	COP2_CC_INIT_CPU_DEST(2, cpu_fmn_info->credit_config); +	COP2_CC_INIT_CPU_DEST(3, cpu_fmn_info->credit_config); +	COP2_CC_INIT_CPU_DEST(4, cpu_fmn_info->credit_config); +	COP2_CC_INIT_CPU_DEST(5, cpu_fmn_info->credit_config); +	COP2_CC_INIT_CPU_DEST(6, cpu_fmn_info->credit_config); +	COP2_CC_INIT_CPU_DEST(7, cpu_fmn_info->credit_config); +	COP2_CC_INIT_CPU_DEST(8, cpu_fmn_info->credit_config); +	COP2_CC_INIT_CPU_DEST(9, cpu_fmn_info->credit_config); +	COP2_CC_INIT_CPU_DEST(10, cpu_fmn_info->credit_config); +	COP2_CC_INIT_CPU_DEST(11, cpu_fmn_info->credit_config); +	COP2_CC_INIT_CPU_DEST(12, cpu_fmn_info->credit_config); +	COP2_CC_INIT_CPU_DEST(13, cpu_fmn_info->credit_config); +	COP2_CC_INIT_CPU_DEST(14, cpu_fmn_info->credit_config); +	COP2_CC_INIT_CPU_DEST(15, cpu_fmn_info->credit_config); + +	/* enable FMN interrupts on this CPU */ +	nlm_fmn_setup_intr(IRQ_FMN, (1 << nlm_threads_per_core) - 1); +	nlm_cop2_disable_irqrestore(flags); +} + + +/* + * Register a FMN message handler with respect to the source station id + * @stnid: source station id + * @action: Handler function pointer + */ +int nlm_register_fmn_handler(int start_stnid, int end_stnid, +	void (*action)(int, int, int, int, struct nlm_fmn_msg *, void *), +	void *arg) +{ +	int sstnid; + +	for (sstnid = start_stnid; sstnid <= end_stnid; sstnid++) { +		msg_handlers[sstnid].arg = arg; +		smp_wmb(); +		msg_handlers[sstnid].action = action; +	} +	pr_debug("Registered FMN msg handler for stnid %d-%d\n", +			start_stnid, end_stnid); +	return 0; +} + +void nlm_setup_fmn_irq(void) +{ +	uint32_t flags; + +	/* setup irq only once */ +	setup_irq(IRQ_FMN, &fmn_irqaction); + +	flags = nlm_cop2_enable_irqsave(); +	nlm_fmn_setup_intr(IRQ_FMN, (1 << nlm_threads_per_core) - 1); +	nlm_cop2_disable_irqrestore(flags); +}  | 
