18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci * Copyright (c) 2003-2012 Broadcom Corporation
38c2ecf20Sopenharmony_ci * All Rights Reserved
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * This software is available to you under a choice of one of two
68c2ecf20Sopenharmony_ci * licenses.  You may choose to be licensed under the terms of the GNU
78c2ecf20Sopenharmony_ci * General Public License (GPL) Version 2, available from the file
88c2ecf20Sopenharmony_ci * COPYING in the main directory of this source tree, or the Broadcom
98c2ecf20Sopenharmony_ci * license below:
108c2ecf20Sopenharmony_ci *
118c2ecf20Sopenharmony_ci * Redistribution and use in source and binary forms, with or without
128c2ecf20Sopenharmony_ci * modification, are permitted provided that the following conditions
138c2ecf20Sopenharmony_ci * are met:
148c2ecf20Sopenharmony_ci *
158c2ecf20Sopenharmony_ci * 1. Redistributions of source code must retain the above copyright
168c2ecf20Sopenharmony_ci *    notice, this list of conditions and the following disclaimer.
178c2ecf20Sopenharmony_ci * 2. Redistributions in binary form must reproduce the above copyright
188c2ecf20Sopenharmony_ci *    notice, this list of conditions and the following disclaimer in
198c2ecf20Sopenharmony_ci *    the documentation and/or other materials provided with the
208c2ecf20Sopenharmony_ci *    distribution.
218c2ecf20Sopenharmony_ci *
228c2ecf20Sopenharmony_ci * THIS SOFTWARE IS PROVIDED BY BROADCOM ``AS IS'' AND ANY EXPRESS OR
238c2ecf20Sopenharmony_ci * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
248c2ecf20Sopenharmony_ci * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
258c2ecf20Sopenharmony_ci * ARE DISCLAIMED. IN NO EVENT SHALL BROADCOM OR CONTRIBUTORS BE LIABLE
268c2ecf20Sopenharmony_ci * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
278c2ecf20Sopenharmony_ci * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
288c2ecf20Sopenharmony_ci * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
298c2ecf20Sopenharmony_ci * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
308c2ecf20Sopenharmony_ci * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
318c2ecf20Sopenharmony_ci * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
328c2ecf20Sopenharmony_ci * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
338c2ecf20Sopenharmony_ci */
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci#include <linux/kernel.h>
368c2ecf20Sopenharmony_ci#include <linux/irqreturn.h>
378c2ecf20Sopenharmony_ci#include <linux/irq.h>
388c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_ci#include <asm/mipsregs.h>
418c2ecf20Sopenharmony_ci#include <asm/netlogic/interrupt.h>
428c2ecf20Sopenharmony_ci#include <asm/netlogic/xlr/fmn.h>
438c2ecf20Sopenharmony_ci#include <asm/netlogic/common.h>
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci#define COP2_CC_INIT_CPU_DEST(dest, conf) \
468c2ecf20Sopenharmony_cido { \
478c2ecf20Sopenharmony_ci	nlm_write_c2_cc##dest(0, conf[(dest * 8) + 0]); \
488c2ecf20Sopenharmony_ci	nlm_write_c2_cc##dest(1, conf[(dest * 8) + 1]); \
498c2ecf20Sopenharmony_ci	nlm_write_c2_cc##dest(2, conf[(dest * 8) + 2]); \
508c2ecf20Sopenharmony_ci	nlm_write_c2_cc##dest(3, conf[(dest * 8) + 3]); \
518c2ecf20Sopenharmony_ci	nlm_write_c2_cc##dest(4, conf[(dest * 8) + 4]); \
528c2ecf20Sopenharmony_ci	nlm_write_c2_cc##dest(5, conf[(dest * 8) + 5]); \
538c2ecf20Sopenharmony_ci	nlm_write_c2_cc##dest(6, conf[(dest * 8) + 6]); \
548c2ecf20Sopenharmony_ci	nlm_write_c2_cc##dest(7, conf[(dest * 8) + 7]); \
558c2ecf20Sopenharmony_ci} while (0)
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_cistruct fmn_message_handler {
588c2ecf20Sopenharmony_ci	void (*action)(int, int, int, int, struct nlm_fmn_msg *, void *);
598c2ecf20Sopenharmony_ci	void *arg;
608c2ecf20Sopenharmony_ci} msg_handlers[128];
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci/*
638c2ecf20Sopenharmony_ci * FMN interrupt handler. We configure the FMN so that any messages in
648c2ecf20Sopenharmony_ci * any of the CPU buckets will trigger an interrupt on the CPU.
658c2ecf20Sopenharmony_ci * The message can be from any device on the FMN (like NAE/SAE/DMA).
668c2ecf20Sopenharmony_ci * The source station id is used to figure out which of the registered
678c2ecf20Sopenharmony_ci * handlers have to be called.
688c2ecf20Sopenharmony_ci */
698c2ecf20Sopenharmony_cistatic irqreturn_t fmn_message_handler(int irq, void *data)
708c2ecf20Sopenharmony_ci{
718c2ecf20Sopenharmony_ci	struct fmn_message_handler *hndlr;
728c2ecf20Sopenharmony_ci	int bucket, rv;
738c2ecf20Sopenharmony_ci	int size = 0, code = 0, src_stnid = 0;
748c2ecf20Sopenharmony_ci	struct nlm_fmn_msg msg;
758c2ecf20Sopenharmony_ci	uint32_t mflags, bkt_status;
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_ci	mflags = nlm_cop2_enable_irqsave();
788c2ecf20Sopenharmony_ci	/* Disable message ring interrupt */
798c2ecf20Sopenharmony_ci	nlm_fmn_setup_intr(irq, 0);
808c2ecf20Sopenharmony_ci	while (1) {
818c2ecf20Sopenharmony_ci		/* 8 bkts per core, [24:31] each bit represents one bucket
828c2ecf20Sopenharmony_ci		 * Bit is Zero if bucket is not empty */
838c2ecf20Sopenharmony_ci		bkt_status = (nlm_read_c2_status0() >> 24) & 0xff;
848c2ecf20Sopenharmony_ci		if (bkt_status == 0xff)
858c2ecf20Sopenharmony_ci			break;
868c2ecf20Sopenharmony_ci		for (bucket = 0; bucket < 8; bucket++) {
878c2ecf20Sopenharmony_ci			/* Continue on empty bucket */
888c2ecf20Sopenharmony_ci			if (bkt_status & (1 << bucket))
898c2ecf20Sopenharmony_ci				continue;
908c2ecf20Sopenharmony_ci			rv = nlm_fmn_receive(bucket, &size, &code, &src_stnid,
918c2ecf20Sopenharmony_ci						&msg);
928c2ecf20Sopenharmony_ci			if (rv != 0)
938c2ecf20Sopenharmony_ci				continue;
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci			hndlr = &msg_handlers[src_stnid];
968c2ecf20Sopenharmony_ci			if (hndlr->action == NULL)
978c2ecf20Sopenharmony_ci				pr_warn("No msgring handler for stnid %d\n",
988c2ecf20Sopenharmony_ci						src_stnid);
998c2ecf20Sopenharmony_ci			else {
1008c2ecf20Sopenharmony_ci				nlm_cop2_disable_irqrestore(mflags);
1018c2ecf20Sopenharmony_ci				hndlr->action(bucket, src_stnid, size, code,
1028c2ecf20Sopenharmony_ci					&msg, hndlr->arg);
1038c2ecf20Sopenharmony_ci				mflags = nlm_cop2_enable_irqsave();
1048c2ecf20Sopenharmony_ci			}
1058c2ecf20Sopenharmony_ci		}
1068c2ecf20Sopenharmony_ci	}
1078c2ecf20Sopenharmony_ci	/* Enable message ring intr, to any thread in core */
1088c2ecf20Sopenharmony_ci	nlm_fmn_setup_intr(irq, (1 << nlm_threads_per_core) - 1);
1098c2ecf20Sopenharmony_ci	nlm_cop2_disable_irqrestore(mflags);
1108c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
1118c2ecf20Sopenharmony_ci}
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_civoid xlr_percpu_fmn_init(void)
1148c2ecf20Sopenharmony_ci{
1158c2ecf20Sopenharmony_ci	struct xlr_fmn_info *cpu_fmn_info;
1168c2ecf20Sopenharmony_ci	int *bucket_sizes;
1178c2ecf20Sopenharmony_ci	uint32_t flags;
1188c2ecf20Sopenharmony_ci	int id;
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci	BUG_ON(nlm_thread_id() != 0);
1218c2ecf20Sopenharmony_ci	id = nlm_core_id();
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci	bucket_sizes = xlr_board_fmn_config.bucket_size;
1248c2ecf20Sopenharmony_ci	cpu_fmn_info = &xlr_board_fmn_config.cpu[id];
1258c2ecf20Sopenharmony_ci	flags = nlm_cop2_enable_irqsave();
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_ci	/* Setup bucket sizes for the core. */
1288c2ecf20Sopenharmony_ci	nlm_write_c2_bucksize(0, bucket_sizes[id * 8 + 0]);
1298c2ecf20Sopenharmony_ci	nlm_write_c2_bucksize(1, bucket_sizes[id * 8 + 1]);
1308c2ecf20Sopenharmony_ci	nlm_write_c2_bucksize(2, bucket_sizes[id * 8 + 2]);
1318c2ecf20Sopenharmony_ci	nlm_write_c2_bucksize(3, bucket_sizes[id * 8 + 3]);
1328c2ecf20Sopenharmony_ci	nlm_write_c2_bucksize(4, bucket_sizes[id * 8 + 4]);
1338c2ecf20Sopenharmony_ci	nlm_write_c2_bucksize(5, bucket_sizes[id * 8 + 5]);
1348c2ecf20Sopenharmony_ci	nlm_write_c2_bucksize(6, bucket_sizes[id * 8 + 6]);
1358c2ecf20Sopenharmony_ci	nlm_write_c2_bucksize(7, bucket_sizes[id * 8 + 7]);
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_ci	/*
1388c2ecf20Sopenharmony_ci	 * For sending FMN messages, we need credits on the destination
1398c2ecf20Sopenharmony_ci	 * bucket. Program the credits this core has on the 128 possible
1408c2ecf20Sopenharmony_ci	 * destination buckets.
1418c2ecf20Sopenharmony_ci	 * We cannot use a loop here, because the the first argument has
1428c2ecf20Sopenharmony_ci	 * to be a constant integer value.
1438c2ecf20Sopenharmony_ci	 */
1448c2ecf20Sopenharmony_ci	COP2_CC_INIT_CPU_DEST(0, cpu_fmn_info->credit_config);
1458c2ecf20Sopenharmony_ci	COP2_CC_INIT_CPU_DEST(1, cpu_fmn_info->credit_config);
1468c2ecf20Sopenharmony_ci	COP2_CC_INIT_CPU_DEST(2, cpu_fmn_info->credit_config);
1478c2ecf20Sopenharmony_ci	COP2_CC_INIT_CPU_DEST(3, cpu_fmn_info->credit_config);
1488c2ecf20Sopenharmony_ci	COP2_CC_INIT_CPU_DEST(4, cpu_fmn_info->credit_config);
1498c2ecf20Sopenharmony_ci	COP2_CC_INIT_CPU_DEST(5, cpu_fmn_info->credit_config);
1508c2ecf20Sopenharmony_ci	COP2_CC_INIT_CPU_DEST(6, cpu_fmn_info->credit_config);
1518c2ecf20Sopenharmony_ci	COP2_CC_INIT_CPU_DEST(7, cpu_fmn_info->credit_config);
1528c2ecf20Sopenharmony_ci	COP2_CC_INIT_CPU_DEST(8, cpu_fmn_info->credit_config);
1538c2ecf20Sopenharmony_ci	COP2_CC_INIT_CPU_DEST(9, cpu_fmn_info->credit_config);
1548c2ecf20Sopenharmony_ci	COP2_CC_INIT_CPU_DEST(10, cpu_fmn_info->credit_config);
1558c2ecf20Sopenharmony_ci	COP2_CC_INIT_CPU_DEST(11, cpu_fmn_info->credit_config);
1568c2ecf20Sopenharmony_ci	COP2_CC_INIT_CPU_DEST(12, cpu_fmn_info->credit_config);
1578c2ecf20Sopenharmony_ci	COP2_CC_INIT_CPU_DEST(13, cpu_fmn_info->credit_config);
1588c2ecf20Sopenharmony_ci	COP2_CC_INIT_CPU_DEST(14, cpu_fmn_info->credit_config);
1598c2ecf20Sopenharmony_ci	COP2_CC_INIT_CPU_DEST(15, cpu_fmn_info->credit_config);
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ci	/* enable FMN interrupts on this CPU */
1628c2ecf20Sopenharmony_ci	nlm_fmn_setup_intr(IRQ_FMN, (1 << nlm_threads_per_core) - 1);
1638c2ecf20Sopenharmony_ci	nlm_cop2_disable_irqrestore(flags);
1648c2ecf20Sopenharmony_ci}
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_ci/*
1688c2ecf20Sopenharmony_ci * Register a FMN message handler with respect to the source station id
1698c2ecf20Sopenharmony_ci * @stnid: source station id
1708c2ecf20Sopenharmony_ci * @action: Handler function pointer
1718c2ecf20Sopenharmony_ci */
1728c2ecf20Sopenharmony_ciint nlm_register_fmn_handler(int start_stnid, int end_stnid,
1738c2ecf20Sopenharmony_ci	void (*action)(int, int, int, int, struct nlm_fmn_msg *, void *),
1748c2ecf20Sopenharmony_ci	void *arg)
1758c2ecf20Sopenharmony_ci{
1768c2ecf20Sopenharmony_ci	int sstnid;
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci	for (sstnid = start_stnid; sstnid <= end_stnid; sstnid++) {
1798c2ecf20Sopenharmony_ci		msg_handlers[sstnid].arg = arg;
1808c2ecf20Sopenharmony_ci		smp_wmb();
1818c2ecf20Sopenharmony_ci		msg_handlers[sstnid].action = action;
1828c2ecf20Sopenharmony_ci	}
1838c2ecf20Sopenharmony_ci	pr_debug("Registered FMN msg handler for stnid %d-%d\n",
1848c2ecf20Sopenharmony_ci			start_stnid, end_stnid);
1858c2ecf20Sopenharmony_ci	return 0;
1868c2ecf20Sopenharmony_ci}
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_civoid nlm_setup_fmn_irq(void)
1898c2ecf20Sopenharmony_ci{
1908c2ecf20Sopenharmony_ci	uint32_t flags;
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci	/* request irq only once */
1938c2ecf20Sopenharmony_ci	if (request_irq(IRQ_FMN, fmn_message_handler, IRQF_PERCPU, "fmn", NULL))
1948c2ecf20Sopenharmony_ci		pr_err("Failed to request irq %d (fmn)\n", IRQ_FMN);
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_ci	flags = nlm_cop2_enable_irqsave();
1978c2ecf20Sopenharmony_ci	nlm_fmn_setup_intr(IRQ_FMN, (1 << nlm_threads_per_core) - 1);
1988c2ecf20Sopenharmony_ci	nlm_cop2_disable_irqrestore(flags);
1998c2ecf20Sopenharmony_ci}
200