1/* $Id: hub_intr.c,v 1.1.1.1 2008/10/15 03:26:03 james26_jang Exp $
2 *
3 * This file is subject to the terms and conditions of the GNU General Public
4 * License.  See the file "COPYING" in the main directory of this archive
5 * for more details.
6 *
7 * Copyright (C) 1992-1997, 2000-2002 Silicon Graphics, Inc.  All Rights Reserved.
8 */
9
10#include <linux/types.h>
11#include <linux/slab.h>
12#include <asm/sn/types.h>
13#include <asm/sn/sgi.h>
14#include <asm/sn/driver.h>
15#include <asm/sn/iograph.h>
16#include <asm/param.h>
17#include <asm/sn/pio.h>
18#include <asm/sn/xtalk/xwidget.h>
19#include <asm/sn/io.h>
20#include <asm/sn/sn_private.h>
21#include <asm/sn/addrs.h>
22#include <asm/sn/invent.h>
23#include <asm/sn/hcl.h>
24#include <asm/sn/hcl_util.h>
25#include <asm/sn/intr.h>
26#include <asm/sn/xtalk/xtalkaddrs.h>
27#include <asm/sn/klconfig.h>
28#include <asm/sn/sn_cpuid.h>
29
30extern xtalk_provider_t hub_provider;
31
32/* ARGSUSED */
33void
34hub_intr_init(devfs_handle_t hubv)
35{
36}
37
38/*
39 * hub_device_desc_update
40 *	Update the passed in device descriptor with the actual the
41 * 	target cpu number and interrupt priority level.
42 *	NOTE : These might be the same as the ones passed in thru
43 *	the descriptor.
44 */
45static void
46hub_device_desc_update(device_desc_t 	dev_desc,
47		       ilvl_t 		intr_swlevel,
48		       cpuid_t		cpu)
49{
50}
51
52int allocate_my_bit = INTRCONNECT_ANYBIT;
53
54/*
55 * Allocate resources required for an interrupt as specified in dev_desc.
56 * Returns a hub interrupt handle on success, or 0 on failure.
57 */
58static hub_intr_t
59do_hub_intr_alloc(devfs_handle_t dev,		/* which crosstalk device */
60		  device_desc_t dev_desc,	/* device descriptor */
61		  devfs_handle_t owner_dev,	/* owner of this interrupt, if known */
62		  int uncond_nothread)		/* unconditionally non-threaded */
63{
64	cpuid_t cpu = (cpuid_t)0;			/* cpu to receive interrupt */
65        int cpupicked = 0;
66	int bit;			/* interrupt vector */
67	/*REFERENCED*/
68	int intr_resflags = 0;
69	hub_intr_t intr_hdl;
70	cnodeid_t nodeid;		/* node to receive interrupt */
71	/*REFERENCED*/
72	nasid_t nasid;			/* nasid to receive interrupt */
73	struct xtalk_intr_s *xtalk_info;
74	iopaddr_t xtalk_addr;		/* xtalk addr on hub to set intr */
75	xwidget_info_t xwidget_info;	/* standard crosstalk widget info handle */
76	char *intr_name = NULL;
77	ilvl_t intr_swlevel = (ilvl_t)0;
78	extern int default_intr_pri;
79	extern void synergy_intr_alloc(int, int);
80
81
82	if (dev_desc) {
83		if (dev_desc->flags & D_INTR_ISERR) {
84			intr_resflags = II_ERRORINT;
85		} else if (!uncond_nothread && !(dev_desc->flags & D_INTR_NOTHREAD)) {
86			intr_resflags = II_THREADED;
87		} else {
88			/* Neither an error nor a thread. */
89			intr_resflags = 0;
90		}
91	} else {
92		intr_swlevel = default_intr_pri;
93		if (!uncond_nothread)
94			intr_resflags = II_THREADED;
95	}
96
97
98	/* If the cpu has not been picked already then choose a candidate
99	 * interrupt target and reserve the interrupt bit
100	 */
101	if (!cpupicked) {
102		cpu = intr_heuristic(dev,dev_desc,allocate_my_bit,
103				     intr_resflags,owner_dev,
104				     intr_name,&bit);
105	}
106
107	/* At this point we SHOULD have a valid cpu */
108	if (cpu == CPU_NONE) {
109#if defined(SUPPORT_PRINTING_V_FORMAT)
110		printk(KERN_WARNING  "%v hub_intr_alloc could not allocate interrupt\n",
111			owner_dev);
112#else
113		printk(KERN_WARNING  "%p hub_intr_alloc could not allocate interrupt\n",
114			(void *)owner_dev);
115#endif
116		return(0);
117
118	}
119
120	/* If the cpu has been picked already (due to the bridge data
121	 * corruption bug) then try to reserve an interrupt bit .
122	 */
123	if (cpupicked) {
124		bit = intr_reserve_level(cpu, allocate_my_bit,
125					 intr_resflags,
126					 owner_dev, intr_name);
127		if (bit < 0) {
128#if defined(SUPPORT_PRINTING_V_FORMAT)
129			printk(KERN_WARNING  "Could not reserve an interrupt bit for cpu "
130				" %d and dev %v\n",
131				cpu,owner_dev);
132#else
133			printk(KERN_WARNING  "Could not reserve an interrupt bit for cpu "
134				" %d and dev %p\n",
135				(int)cpu, (void *)owner_dev);
136#endif
137
138			return(0);
139		}
140	}
141
142	nodeid = cpuid_to_cnodeid(cpu);
143	nasid = cpuid_to_nasid(cpu);
144	xtalk_addr = HUBREG_AS_XTALKADDR(nasid, PIREG(PI_INT_PEND_MOD, cpuid_to_subnode(cpu)));
145
146	/*
147	 * Allocate an interrupt handle, and fill it in.  There are two
148	 * pieces to an interrupt handle: the piece needed by generic
149	 * xtalk code which is used by crosstalk device drivers, and
150	 * the piece needed by low-level IP27 hardware code.
151	 */
152	intr_hdl = snia_kmem_alloc_node(sizeof(struct hub_intr_s), KM_NOSLEEP, nodeid);
153	ASSERT_ALWAYS(intr_hdl);
154
155	/*
156	 * Fill in xtalk information for generic xtalk interfaces that
157	 * operate on xtalk_intr_hdl's.
158	 */
159	xtalk_info = &intr_hdl->i_xtalk_info;
160	xtalk_info->xi_dev = dev;
161	xtalk_info->xi_vector = bit;
162	xtalk_info->xi_addr = xtalk_addr;
163
164	/*
165	 * Regardless of which CPU we ultimately interrupt, a given crosstalk
166	 * widget always handles interrupts (and PIO and DMA) through its
167	 * designated "master" crosstalk provider.
168	 */
169	xwidget_info = xwidget_info_get(dev);
170	if (xwidget_info)
171		xtalk_info->xi_target = xwidget_info_masterid_get(xwidget_info);
172
173	/* Fill in low level hub information for hub_* interrupt interface */
174	intr_hdl->i_swlevel = intr_swlevel;
175	intr_hdl->i_cpuid = cpu;
176	intr_hdl->i_bit = bit;
177	intr_hdl->i_flags = HUB_INTR_IS_ALLOCED;
178
179	/* Store the actual interrupt priority level & interrupt target
180	 * cpu back in the device descriptor.
181	 */
182	hub_device_desc_update(dev_desc, intr_swlevel, cpu);
183	synergy_intr_alloc((int)bit, (int)cpu);
184	return(intr_hdl);
185}
186
187/*
188 * Allocate resources required for an interrupt as specified in dev_desc.
189 * Returns a hub interrupt handle on success, or 0 on failure.
190 */
191hub_intr_t
192hub_intr_alloc(	devfs_handle_t dev,		/* which crosstalk device */
193		device_desc_t dev_desc,		/* device descriptor */
194		devfs_handle_t owner_dev)		/* owner of this interrupt, if known */
195{
196	return(do_hub_intr_alloc(dev, dev_desc, owner_dev, 0));
197}
198
199/*
200 * Allocate resources required for an interrupt as specified in dev_desc.
201 * Uncondtionally request non-threaded, regardless of what the device
202 * descriptor might say.
203 * Returns a hub interrupt handle on success, or 0 on failure.
204 */
205hub_intr_t
206hub_intr_alloc_nothd(devfs_handle_t dev,		/* which crosstalk device */
207		device_desc_t dev_desc,		/* device descriptor */
208		devfs_handle_t owner_dev)		/* owner of this interrupt, if known */
209{
210	return(do_hub_intr_alloc(dev, dev_desc, owner_dev, 1));
211}
212
213/*
214 * Free resources consumed by intr_alloc.
215 */
216void
217hub_intr_free(hub_intr_t intr_hdl)
218{
219	cpuid_t cpu = intr_hdl->i_cpuid;
220	int bit = intr_hdl->i_bit;
221	xtalk_intr_t xtalk_info;
222
223	if (intr_hdl->i_flags & HUB_INTR_IS_CONNECTED) {
224		/* Setting the following fields in the xtalk interrupt info
225	 	 * clears the interrupt target register in the xtalk user
226	 	 */
227		xtalk_info = &intr_hdl->i_xtalk_info;
228		xtalk_info->xi_dev = NODEV;
229		xtalk_info->xi_vector = 0;
230		xtalk_info->xi_addr = 0;
231		hub_intr_disconnect(intr_hdl);
232	}
233
234	if (intr_hdl->i_flags & HUB_INTR_IS_ALLOCED)
235		kfree(intr_hdl);
236
237	intr_unreserve_level(cpu, bit);
238}
239
240
241/*
242 * Associate resources allocated with a previous hub_intr_alloc call with the
243 * described handler, arg, name, etc.
244 */
245/*ARGSUSED*/
246int
247hub_intr_connect(	hub_intr_t intr_hdl,		/* xtalk intr resource handle */
248			xtalk_intr_setfunc_t setfunc,	/* func to set intr hw */
249			void *setfunc_arg)		/* arg to setfunc */
250{
251	int rv;
252	cpuid_t cpu = intr_hdl->i_cpuid;
253	int bit = intr_hdl->i_bit;
254	extern int synergy_intr_connect(int, int);
255
256	ASSERT(intr_hdl->i_flags & HUB_INTR_IS_ALLOCED);
257
258	rv = intr_connect_level(cpu, bit, intr_hdl->i_swlevel, NULL);
259	if (rv < 0)
260		return(rv);
261
262	intr_hdl->i_xtalk_info.xi_setfunc = setfunc;
263	intr_hdl->i_xtalk_info.xi_sfarg = setfunc_arg;
264
265	if (setfunc) (*setfunc)((xtalk_intr_t)intr_hdl);
266
267	intr_hdl->i_flags |= HUB_INTR_IS_CONNECTED;
268	return(synergy_intr_connect((int)bit, (int)cpu));
269}
270
271
272/*
273 * Disassociate handler with the specified interrupt.
274 */
275void
276hub_intr_disconnect(hub_intr_t intr_hdl)
277{
278	/*REFERENCED*/
279	int rv;
280	cpuid_t cpu = intr_hdl->i_cpuid;
281	int bit = intr_hdl->i_bit;
282	xtalk_intr_setfunc_t setfunc;
283
284	setfunc = intr_hdl->i_xtalk_info.xi_setfunc;
285
286	/* TBD: send disconnected interrupts somewhere harmless */
287	if (setfunc) (*setfunc)((xtalk_intr_t)intr_hdl);
288
289	rv = intr_disconnect_level(cpu, bit);
290	ASSERT(rv == 0);
291	intr_hdl->i_flags &= ~HUB_INTR_IS_CONNECTED;
292}
293
294
295/*
296 * Return a hwgraph vertex that represents the CPU currently
297 * targeted by an interrupt.
298 */
299devfs_handle_t
300hub_intr_cpu_get(hub_intr_t intr_hdl)
301{
302	cpuid_t cpuid = intr_hdl->i_cpuid;
303	ASSERT(cpuid != CPU_NONE);
304
305	return(cpuid_to_vertex(cpuid));
306}
307