patch-2.4.19 linux-2.4.19/arch/ia64/sn/io/sn2/ml_SN_intr.c

Next file: linux-2.4.19/arch/ia64/sn/io/sn2/pcibr/pcibr_ate.c
Previous file: linux-2.4.19/arch/ia64/sn/io/sn2/bte_error.c
Back to the patch index
Back to the overall index

diff -urN linux-2.4.18/arch/ia64/sn/io/sn2/ml_SN_intr.c linux-2.4.19/arch/ia64/sn/io/sn2/ml_SN_intr.c
@@ -0,0 +1,469 @@
+/* $Id: ml_SN_intr.c,v 1.1 2002/02/28 17:31:25 marcelo Exp $
+ *
+ * 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) 1992-1997, 2000-2002 Silicon Graphics, Inc.  All Rights Reserved.
+ */
+
+/*
+ * intr.c-
+ *	This file contains all of the routines necessary to set up and
+ *	handle interrupts on an IPXX board.
+ */
+
+#ident  "$Revision: 1.1 $"
+
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <asm/smp.h>
+#include <asm/irq.h>
+#include <asm/hw_irq.h>
+#include <asm/sn/sgi.h>
+#include <asm/sn/iograph.h>
+#include <asm/sn/invent.h>
+#include <asm/sn/hcl.h>
+#include <asm/sn/labelcl.h>
+#include <asm/sn/io.h>
+#include <asm/sn/sn_private.h>
+#include <asm/sn/klconfig.h>
+#include <asm/sn/sn_cpuid.h>
+#include <asm/sn/pci/pciio.h>
+#include <asm/sn/pci/pcibr.h>
+#include <asm/sn/xtalk/xtalk.h>
+#include <asm/sn/pci/pcibr_private.h>
+#include <asm/sn/intr.h>
+#include <asm/sn/sn2/shub_mmr_t.h>
+#include <asm/sal.h>
+#include <asm/sn/sn_sal.h>
+
+extern irqpda_t	*irqpdaindr[];
+extern cnodeid_t master_node_get(devfs_handle_t vhdl);
+extern nasid_t master_nasid;
+
+//  Initialize some shub registers for interrupts, both IO and error.
+
+void
+intr_init_vecblk( nodepda_t *npda,
+		cnodeid_t node,
+		int sn)
+{
+	int 			nasid = cnodeid_to_nasid(node);
+	nasid_t			console_nasid;
+	sh_ii_int0_config_u_t	ii_int_config;
+	cpuid_t			cpu;
+	cpuid_t			cpu0, cpu1;
+	nodepda_t		*lnodepda;
+	sh_ii_int0_enable_u_t	ii_int_enable;
+	sh_local_int0_config_u_t	local_int_config;
+	sh_local_int0_enable_u_t	local_int_enable;
+	sh_fsb_system_agent_config_u_t	fsb_system_agent;
+	sh_int_node_id_config_u_t	node_id_config;
+	int is_console;
+
+	console_nasid = get_console_nasid();
+	if (console_nasid < 0) {
+		console_nasid = master_nasid;
+	}
+
+	is_console = nasid == console_nasid;
+
+	if (is_headless_node(node) ) {
+		int cnode;
+		struct ia64_sal_retval ret_stuff;
+
+		// retarget all interrupts on this node to the master node.
+		node_id_config.sh_int_node_id_config_regval = 0;
+		node_id_config.sh_int_node_id_config_s.node_id = master_nasid;
+		node_id_config.sh_int_node_id_config_s.id_sel = 1;
+		HUB_S( (unsigned long *)GLOBAL_MMR_ADDR(nasid, SH_INT_NODE_ID_CONFIG),
+			node_id_config.sh_int_node_id_config_regval);
+		cnode = nasid_to_cnodeid(master_nasid);
+		lnodepda = NODEPDA(cnode);
+		cpu = lnodepda->node_first_cpu;
+		cpu = cpu_physical_id(cpu);
+		SAL_CALL(ret_stuff, SN_SAL_REGISTER_CE, nasid, cpu, master_nasid,0,0,0,0);
+		if (ret_stuff.status < 0) {
+			printk("%s: SN_SAL_REGISTER_CE SAL_CALL failed\n",__FUNCTION__);
+		}
+	} else {
+		lnodepda = NODEPDA(node);
+		cpu = lnodepda->node_first_cpu;
+		cpu = cpu_physical_id(cpu);
+	}
+
+	// Get the physical id's of the cpu's on this node.
+	cpu0 = id_eid_to_cpu_physical_id(nasid, 0);
+	cpu1 = id_eid_to_cpu_physical_id(nasid, 1);
+
+	HUB_S( (unsigned long *)GLOBAL_MMR_ADDR(nasid, SH_PI_ERROR_MASK), 0);
+	HUB_S( (unsigned long *)GLOBAL_MMR_ADDR(nasid, SH_PI_CRBP_ERROR_MASK), 0);
+
+	// The II_INT_CONFIG register for cpu 0.
+	ii_int_config.sh_ii_int0_config_s.type = 0;
+	ii_int_config.sh_ii_int0_config_s.agt = 0;
+	ii_int_config.sh_ii_int0_config_s.pid = cpu0;
+	ii_int_config.sh_ii_int0_config_s.base = 0;
+
+	HUB_S((unsigned long *)GLOBAL_MMR_ADDR(nasid, SH_II_INT0_CONFIG),
+		ii_int_config.sh_ii_int0_config_regval);
+
+	// The II_INT_CONFIG register for cpu 1.
+	ii_int_config.sh_ii_int0_config_s.type = 0;
+	ii_int_config.sh_ii_int0_config_s.agt = 0;
+	ii_int_config.sh_ii_int0_config_s.pid = cpu1;
+	ii_int_config.sh_ii_int0_config_s.base = 0;
+
+	HUB_S((unsigned long *)GLOBAL_MMR_ADDR(nasid, SH_II_INT1_CONFIG),
+		ii_int_config.sh_ii_int0_config_regval);
+
+	// Enable interrupts for II_INT0 and 1.
+	ii_int_enable.sh_ii_int0_enable_s.ii_enable = 1;
+
+	HUB_S((unsigned long *)GLOBAL_MMR_ADDR(nasid, SH_II_INT0_ENABLE),
+		ii_int_enable.sh_ii_int0_enable_regval);
+	HUB_S((unsigned long *)GLOBAL_MMR_ADDR(nasid, SH_II_INT1_ENABLE),
+		ii_int_enable.sh_ii_int0_enable_regval);
+
+	// init error regs
+	// LOCAL_INT0 is for the UART only.
+
+	local_int_config.sh_local_int0_config_s.type = 0;
+	local_int_config.sh_local_int0_config_s.agt = 0;
+	local_int_config.sh_local_int0_config_s.pid = cpu;
+	local_int_config.sh_local_int0_config_s.base = 0;
+	local_int_config.sh_local_int0_config_s.idx = SGI_UART_VECTOR;
+
+	HUB_S((unsigned long *)GLOBAL_MMR_ADDR(nasid, SH_LOCAL_INT0_CONFIG),
+		local_int_config.sh_local_int0_config_regval);
+
+	// LOCAL_INT1 is for all hardware errors.
+	// It will send a BERR, which will result in an MCA.
+	local_int_config.sh_local_int0_config_s.idx = 0;
+
+	HUB_S((unsigned long *)GLOBAL_MMR_ADDR(nasid, SH_LOCAL_INT1_CONFIG),
+		local_int_config.sh_local_int0_config_regval);
+
+	// Clear the LOCAL_INT_ENABLE register.
+	local_int_enable.sh_local_int0_enable_regval = 0;
+
+	if (is_console) {
+		// Enable the UART interrupt.  Only applies to the console nasid.
+		local_int_enable.sh_local_int0_enable_s.uart_int = 1;
+
+		HUB_S((unsigned long *)GLOBAL_MMR_ADDR(nasid, SH_LOCAL_INT0_ENABLE),
+			local_int_enable.sh_local_int0_enable_regval);
+	}
+
+	// Enable all the error interrupts.
+	local_int_enable.sh_local_int0_enable_s.uart_int = 0;
+	local_int_enable.sh_local_int0_enable_s.pi_hw_int = 1;
+	local_int_enable.sh_local_int0_enable_s.md_hw_int = 1;
+	local_int_enable.sh_local_int0_enable_s.xn_hw_int = 1;
+	local_int_enable.sh_local_int0_enable_s.lb_hw_int = 1;
+	local_int_enable.sh_local_int0_enable_s.ii_hw_int = 1;
+	local_int_enable.sh_local_int0_enable_s.pi_uce_int = 1;
+	local_int_enable.sh_local_int0_enable_s.md_uce_int = 1;
+	local_int_enable.sh_local_int0_enable_s.xn_uce_int = 1;
+	local_int_enable.sh_local_int0_enable_s.system_shutdown_int = 1;
+	local_int_enable.sh_local_int0_enable_s.l1_nmi_int = 1;
+	local_int_enable.sh_local_int0_enable_s.stop_clock = 1;
+
+
+	// Send BERR, rather than an interrupt, for shub errors.
+	local_int_config.sh_local_int0_config_s.agt = 1;
+	HUB_S((unsigned long *)GLOBAL_MMR_ADDR(nasid, SH_LOCAL_INT1_CONFIG),
+		local_int_config.sh_local_int0_config_regval);
+
+	HUB_S((unsigned long *)GLOBAL_MMR_ADDR(nasid, SH_LOCAL_INT1_ENABLE),
+		local_int_enable.sh_local_int0_enable_regval);
+
+	// Make sure BERR is enabled.
+	fsb_system_agent.sh_fsb_system_agent_config_regval = 
+		HUB_L( (unsigned long *)GLOBAL_MMR_ADDR(nasid, SH_FSB_SYSTEM_AGENT_CONFIG) );
+	fsb_system_agent.sh_fsb_system_agent_config_s.berr_assert_en = 1;
+	HUB_S((unsigned long *)GLOBAL_MMR_ADDR(nasid, SH_FSB_SYSTEM_AGENT_CONFIG),
+		fsb_system_agent.sh_fsb_system_agent_config_regval);
+
+	// Set LOCAL_INT2 to field CEs
+
+	local_int_enable.sh_local_int0_enable_regval = 0;
+
+	local_int_config.sh_local_int0_config_s.agt = 0;
+	local_int_config.sh_local_int0_config_s.idx = SGI_SHUB_ERROR_VECTOR;
+	HUB_S((unsigned long *)GLOBAL_MMR_ADDR(nasid, SH_LOCAL_INT2_CONFIG),
+		local_int_config.sh_local_int0_config_regval);
+
+	local_int_enable.sh_local_int0_enable_s.pi_ce_int = 1;
+	local_int_enable.sh_local_int0_enable_s.md_ce_int = 1;
+	local_int_enable.sh_local_int0_enable_s.xn_ce_int = 1;
+
+	HUB_S((unsigned long *)GLOBAL_MMR_ADDR(nasid, SH_LOCAL_INT2_ENABLE),
+		local_int_enable.sh_local_int0_enable_regval);
+
+	// Make sure all the rest of the LOCAL_INT regs are disabled.
+	local_int_enable.sh_local_int0_enable_regval = 0;
+	HUB_S((unsigned long *)GLOBAL_MMR_ADDR(nasid, SH_LOCAL_INT3_ENABLE),
+		local_int_enable.sh_local_int0_enable_regval);
+
+	HUB_S((unsigned long *)GLOBAL_MMR_ADDR(nasid, SH_LOCAL_INT4_ENABLE),
+		local_int_enable.sh_local_int0_enable_regval);
+
+	HUB_S((unsigned long *)GLOBAL_MMR_ADDR(nasid, SH_LOCAL_INT5_ENABLE),
+		local_int_enable.sh_local_int0_enable_regval);
+
+}
+
+// (Un)Reserve an irq on this cpu.
+
+static int
+do_intr_reserve_level(cpuid_t cpu,
+			int bit,
+			int reserve)
+{
+	int i;
+	irqpda_t	*irqs = irqpdaindr[cpu];
+
+	if (reserve) {
+		if (bit < 0) {
+			for (i = IA64_SN2_FIRST_DEVICE_VECTOR; i <= IA64_SN2_LAST_DEVICE_VECTOR; i++) {
+				if (irqs->irq_flags[i] == 0) {
+					bit = i;
+					break;
+				}
+			}
+		}
+		if (bit < 0) {
+			return -1;
+		}
+		if (irqs->irq_flags[bit] & SN2_IRQ_RESERVED) {
+			return -1;
+		} else {
+			irqs->num_irq_used++;
+			irqs->irq_flags[bit] |= SN2_IRQ_RESERVED;
+			return bit;
+		}
+	} else {
+		if (irqs->irq_flags[bit] & SN2_IRQ_RESERVED) {
+			irqs->num_irq_used--;
+			irqs->irq_flags[bit] &= ~SN2_IRQ_RESERVED;
+			return bit;
+		} else {
+			return -1;
+		}
+	}
+}
+
+int
+intr_reserve_level(cpuid_t cpu,
+		int bit,
+		int resflags,
+		devfs_handle_t owner_dev,
+		char *name)
+{
+	return(do_intr_reserve_level(cpu, bit, 1));
+}
+
+void
+intr_unreserve_level(cpuid_t cpu,
+		int bit)
+{
+	(void)do_intr_reserve_level(cpu, bit, 0);
+}
+
+// Mark an irq on this cpu as (dis)connected.
+
+static int
+do_intr_connect_level(cpuid_t cpu,
+			int bit,
+			int connect)
+{
+	irqpda_t	*irqs = irqpdaindr[cpu];
+
+	if (connect) {
+		if (irqs->irq_flags[bit] & SN2_IRQ_CONNECTED) {
+			return -1;
+		} else {
+			irqs->irq_flags[bit] |= SN2_IRQ_CONNECTED;
+			return bit;
+		}
+	} else {
+		if (irqs->irq_flags[bit] & SN2_IRQ_CONNECTED) {
+			irqs->irq_flags[bit] &= ~SN2_IRQ_CONNECTED;
+			return bit;
+		} else {
+			return -1;
+		}
+	}
+	return(bit);
+}
+
+int
+intr_connect_level(cpuid_t cpu,
+		int bit,
+		ilvl_t is,
+		intr_func_t intr_prefunc)
+{
+	return(do_intr_connect_level(cpu, bit, 1));
+}
+
+int
+intr_disconnect_level(cpuid_t cpu,
+		int bit)
+{
+	return(do_intr_connect_level(cpu, bit, 0));
+}
+
+// Choose a cpu on this node.
+// We choose the one with the least number of int's assigned to it.
+
+static cpuid_t
+do_intr_cpu_choose(cnodeid_t cnode) {
+	cpuid_t		cpu, best_cpu = CPU_NONE;
+	int		slice, min_count = 1000;
+	irqpda_t	*irqs;
+
+	for (slice = 0; slice < CPUS_PER_NODE; slice++) {
+		int intrs;
+
+		cpu = cnode_slice_to_cpuid(cnode, slice);
+		if (cpu == CPU_NONE) {
+			continue;
+		}
+
+		if (!cpu_enabled(cpu)) {
+			continue;
+		}
+
+		irqs = irqpdaindr[cpu];
+		intrs = irqs->num_irq_used;
+
+		if (min_count > intrs) {
+			min_count = intrs;
+			best_cpu = cpu;
+		}
+	}
+	return best_cpu;
+}
+
+static cpuid_t
+intr_cpu_choose_from_node(cnodeid_t cnode)
+{
+	return(do_intr_cpu_choose(cnode));
+}
+
+// See if we can use this cpu/vect.
+
+static cpuid_t
+intr_bit_reserve_test(cpuid_t cpu,
+			int favor_subnode,
+			cnodeid_t cnode,
+			int req_bit,
+			int resflags,
+			devfs_handle_t owner_dev,
+			char *name,
+			int *resp_bit)
+{
+	ASSERT( (cpu == CPU_NONE) || (cnode == CNODEID_NONE) );
+
+	if (cnode != CNODEID_NONE) {
+		cpu = intr_cpu_choose_from_node(cnode);
+	}
+
+	if (cpu != CPU_NONE) {
+		*resp_bit = do_intr_reserve_level(cpu, req_bit, 1);
+		if (*resp_bit >= 0) {
+			return(cpu);
+		}
+	}
+	return CPU_NONE;
+}
+
+// Find the node to assign for this interrupt.
+
+cpuid_t
+intr_heuristic(devfs_handle_t dev,
+		device_desc_t dev_desc,
+		int	req_bit,
+		int resflags,
+		devfs_handle_t owner_dev,
+		char *name,
+		int *resp_bit)
+{
+	cpuid_t		cpuid;
+	cnodeid_t	candidate = -1;
+	devfs_handle_t	pconn_vhdl;
+	pcibr_soft_t	pcibr_soft;
+
+/* SN2 + pcibr addressing limitation */
+/* Due to this limitation, all interrupts from a given bridge must go to the name node.*/
+/* This limitation does not exist on PIC. */
+
+	if ( (hwgraph_edge_get(dev, EDGE_LBL_PCI, &pconn_vhdl) == GRAPH_SUCCESS) &&
+		( (pcibr_soft = pcibr_soft_get(pconn_vhdl) ) != NULL) ) {
+			if (pcibr_soft->bsi_err_intr) {
+				candidate = cpuid_to_cnodeid( ((hub_intr_t)pcibr_soft->bsi_err_intr)->i_cpuid);
+			}
+	}
+
+	if (candidate >= 0) {
+		// The node was chosen already when we assigned the error interrupt.
+		cpuid = intr_bit_reserve_test(CPU_NONE,
+						0,
+						candidate,
+						req_bit,
+						0,
+						owner_dev,
+						name,
+						resp_bit);
+	} else {
+		// Need to choose one.  Try the controlling c-brick first.
+		cpuid = intr_bit_reserve_test(CPU_NONE,
+						0,
+						master_node_get(dev),
+						req_bit,
+						0,
+						owner_dev,
+						name,
+						resp_bit);
+	}
+
+	if (cpuid != CPU_NONE) {
+		return cpuid;
+	}
+
+	if (candidate >= 0) {
+		printk("Cannot target interrupt to target node (%d).\n",candidate);
+		return CPU_NONE;
+	} else {
+		printk("Cannot target interrupt to closest node (%d) 0x%p\n",
+			master_node_get(dev), (void *)owner_dev);
+	}
+
+	// We couldn't put it on the closest node.  Try to find another one.
+	// Do a stupid round-robin assignment of the node.
+
+	{
+		static cnodeid_t last_node = -1;
+		if (last_node >= numnodes) last_node = 0;
+		for (candidate = last_node + 1; candidate != last_node; candidate++) {
+			if (candidate == numnodes) candidate = 0;
+			cpuid = intr_bit_reserve_test(CPU_NONE,
+							0,
+							candidate,
+							req_bit,
+							0,
+							owner_dev,
+							name,
+							resp_bit);
+			if (cpuid != CPU_NONE) {
+				return cpuid;
+			}
+		}
+	}
+
+	printk("cannot target interrupt: 0x%p\n",(void *)owner_dev);
+	return CPU_NONE;
+}

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)