/* * 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(); /* 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_status() >> 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_restore(mflags); hndlr->action(bucket, src_stnid, size, code, &msg, hndlr->arg); mflags = nlm_cop2_enable(); } } }; /* Enable message ring intr, to any thread in core */ nlm_fmn_setup_intr(irq, (1 << nlm_threads_per_core) - 1); nlm_cop2_restore(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(); /* 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_restore(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(); nlm_fmn_setup_intr(IRQ_FMN, (1 << nlm_threads_per_core) - 1); nlm_cop2_restore(flags); }