1/* 2 * Copyright (c) 2003-2012 Broadcom Corporation 3 * All Rights Reserved 4 * 5 * This software is available to you under a choice of one of two 6 * licenses. You may choose to be licensed under the terms of the GNU 7 * General Public License (GPL) Version 2, available from the file 8 * COPYING in the main directory of this source tree, or the Broadcom 9 * license below: 10 * 11 * Redistribution and use in source and binary forms, with or without 12 * modification, are permitted provided that the following conditions 13 * are met: 14 * 15 * 1. Redistributions of source code must retain the above copyright 16 * notice, this list of conditions and the following disclaimer. 17 * 2. Redistributions in binary form must reproduce the above copyright 18 * notice, this list of conditions and the following disclaimer in 19 * the documentation and/or other materials provided with the 20 * distribution. 21 * 22 * THIS SOFTWARE IS PROVIDED BY BROADCOM ``AS IS'' AND ANY EXPRESS OR 23 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 24 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 25 * ARE DISCLAIMED. IN NO EVENT SHALL BROADCOM OR CONTRIBUTORS BE LIABLE 26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 27 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 28 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 29 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 30 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 31 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN 32 * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 33 */ 34 35#include <linux/kernel.h> 36#include <linux/irqreturn.h> 37#include <linux/irq.h> 38#include <linux/interrupt.h> 39 40#include <asm/mipsregs.h> 41#include <asm/netlogic/interrupt.h> 42#include <asm/netlogic/xlr/fmn.h> 43#include <asm/netlogic/common.h> 44 45#define COP2_CC_INIT_CPU_DEST(dest, conf) \ 46do { \ 47 nlm_write_c2_cc##dest(0, conf[(dest * 8) + 0]); \ 48 nlm_write_c2_cc##dest(1, conf[(dest * 8) + 1]); \ 49 nlm_write_c2_cc##dest(2, conf[(dest * 8) + 2]); \ 50 nlm_write_c2_cc##dest(3, conf[(dest * 8) + 3]); \ 51 nlm_write_c2_cc##dest(4, conf[(dest * 8) + 4]); \ 52 nlm_write_c2_cc##dest(5, conf[(dest * 8) + 5]); \ 53 nlm_write_c2_cc##dest(6, conf[(dest * 8) + 6]); \ 54 nlm_write_c2_cc##dest(7, conf[(dest * 8) + 7]); \ 55} while (0) 56 57struct fmn_message_handler { 58 void (*action)(int, int, int, int, struct nlm_fmn_msg *, void *); 59 void *arg; 60} msg_handlers[128]; 61 62/* 63 * FMN interrupt handler. We configure the FMN so that any messages in 64 * any of the CPU buckets will trigger an interrupt on the CPU. 65 * The message can be from any device on the FMN (like NAE/SAE/DMA). 66 * The source station id is used to figure out which of the registered 67 * handlers have to be called. 68 */ 69static irqreturn_t fmn_message_handler(int irq, void *data) 70{ 71 struct fmn_message_handler *hndlr; 72 int bucket, rv; 73 int size = 0, code = 0, src_stnid = 0; 74 struct nlm_fmn_msg msg; 75 uint32_t mflags, bkt_status; 76 77 mflags = nlm_cop2_enable_irqsave(); 78 /* Disable message ring interrupt */ 79 nlm_fmn_setup_intr(irq, 0); 80 while (1) { 81 /* 8 bkts per core, [24:31] each bit represents one bucket 82 * Bit is Zero if bucket is not empty */ 83 bkt_status = (nlm_read_c2_status0() >> 24) & 0xff; 84 if (bkt_status == 0xff) 85 break; 86 for (bucket = 0; bucket < 8; bucket++) { 87 /* Continue on empty bucket */ 88 if (bkt_status & (1 << bucket)) 89 continue; 90 rv = nlm_fmn_receive(bucket, &size, &code, &src_stnid, 91 &msg); 92 if (rv != 0) 93 continue; 94 95 hndlr = &msg_handlers[src_stnid]; 96 if (hndlr->action == NULL) 97 pr_warn("No msgring handler for stnid %d\n", 98 src_stnid); 99 else { 100 nlm_cop2_disable_irqrestore(mflags); 101 hndlr->action(bucket, src_stnid, size, code, 102 &msg, hndlr->arg); 103 mflags = nlm_cop2_enable_irqsave(); 104 } 105 } 106 }; 107 /* Enable message ring intr, to any thread in core */ 108 nlm_fmn_setup_intr(irq, (1 << nlm_threads_per_core) - 1); 109 nlm_cop2_disable_irqrestore(mflags); 110 return IRQ_HANDLED; 111} 112 113struct irqaction fmn_irqaction = { 114 .handler = fmn_message_handler, 115 .flags = IRQF_PERCPU, 116 .name = "fmn", 117}; 118 119void xlr_percpu_fmn_init(void) 120{ 121 struct xlr_fmn_info *cpu_fmn_info; 122 int *bucket_sizes; 123 uint32_t flags; 124 int id; 125 126 BUG_ON(nlm_thread_id() != 0); 127 id = nlm_core_id(); 128 129 bucket_sizes = xlr_board_fmn_config.bucket_size; 130 cpu_fmn_info = &xlr_board_fmn_config.cpu[id]; 131 flags = nlm_cop2_enable_irqsave(); 132 133 /* Setup bucket sizes for the core. */ 134 nlm_write_c2_bucksize(0, bucket_sizes[id * 8 + 0]); 135 nlm_write_c2_bucksize(1, bucket_sizes[id * 8 + 1]); 136 nlm_write_c2_bucksize(2, bucket_sizes[id * 8 + 2]); 137 nlm_write_c2_bucksize(3, bucket_sizes[id * 8 + 3]); 138 nlm_write_c2_bucksize(4, bucket_sizes[id * 8 + 4]); 139 nlm_write_c2_bucksize(5, bucket_sizes[id * 8 + 5]); 140 nlm_write_c2_bucksize(6, bucket_sizes[id * 8 + 6]); 141 nlm_write_c2_bucksize(7, bucket_sizes[id * 8 + 7]); 142 143 /* 144 * For sending FMN messages, we need credits on the destination 145 * bucket. Program the credits this core has on the 128 possible 146 * destination buckets. 147 * We cannot use a loop here, because the the first argument has 148 * to be a constant integer value. 149 */ 150 COP2_CC_INIT_CPU_DEST(0, cpu_fmn_info->credit_config); 151 COP2_CC_INIT_CPU_DEST(1, cpu_fmn_info->credit_config); 152 COP2_CC_INIT_CPU_DEST(2, cpu_fmn_info->credit_config); 153 COP2_CC_INIT_CPU_DEST(3, cpu_fmn_info->credit_config); 154 COP2_CC_INIT_CPU_DEST(4, cpu_fmn_info->credit_config); 155 COP2_CC_INIT_CPU_DEST(5, cpu_fmn_info->credit_config); 156 COP2_CC_INIT_CPU_DEST(6, cpu_fmn_info->credit_config); 157 COP2_CC_INIT_CPU_DEST(7, cpu_fmn_info->credit_config); 158 COP2_CC_INIT_CPU_DEST(8, cpu_fmn_info->credit_config); 159 COP2_CC_INIT_CPU_DEST(9, cpu_fmn_info->credit_config); 160 COP2_CC_INIT_CPU_DEST(10, cpu_fmn_info->credit_config); 161 COP2_CC_INIT_CPU_DEST(11, cpu_fmn_info->credit_config); 162 COP2_CC_INIT_CPU_DEST(12, cpu_fmn_info->credit_config); 163 COP2_CC_INIT_CPU_DEST(13, cpu_fmn_info->credit_config); 164 COP2_CC_INIT_CPU_DEST(14, cpu_fmn_info->credit_config); 165 COP2_CC_INIT_CPU_DEST(15, cpu_fmn_info->credit_config); 166 167 /* enable FMN interrupts on this CPU */ 168 nlm_fmn_setup_intr(IRQ_FMN, (1 << nlm_threads_per_core) - 1); 169 nlm_cop2_disable_irqrestore(flags); 170} 171 172 173/* 174 * Register a FMN message handler with respect to the source station id 175 * @stnid: source station id 176 * @action: Handler function pointer 177 */ 178int nlm_register_fmn_handler(int start_stnid, int end_stnid, 179 void (*action)(int, int, int, int, struct nlm_fmn_msg *, void *), 180 void *arg) 181{ 182 int sstnid; 183 184 for (sstnid = start_stnid; sstnid <= end_stnid; sstnid++) { 185 msg_handlers[sstnid].arg = arg; 186 smp_wmb(); 187 msg_handlers[sstnid].action = action; 188 } 189 pr_debug("Registered FMN msg handler for stnid %d-%d\n", 190 start_stnid, end_stnid); 191 return 0; 192} 193 194void nlm_setup_fmn_irq(void) 195{ 196 uint32_t flags; 197 198 /* setup irq only once */ 199 setup_irq(IRQ_FMN, &fmn_irqaction); 200 201 flags = nlm_cop2_enable_irqsave(); 202 nlm_fmn_setup_intr(IRQ_FMN, (1 << nlm_threads_per_core) - 1); 203 nlm_cop2_disable_irqrestore(flags); 204} 205