xref: /onnv-gate/usr/src/uts/sun4u/starfire/os/bbus_intr.c (revision 0:68f95e015346)
1*0Sstevel@tonic-gate /*
2*0Sstevel@tonic-gate  * CDDL HEADER START
3*0Sstevel@tonic-gate  *
4*0Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5*0Sstevel@tonic-gate  * Common Development and Distribution License, Version 1.0 only
6*0Sstevel@tonic-gate  * (the "License").  You may not use this file except in compliance
7*0Sstevel@tonic-gate  * with the License.
8*0Sstevel@tonic-gate  *
9*0Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10*0Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
11*0Sstevel@tonic-gate  * See the License for the specific language governing permissions
12*0Sstevel@tonic-gate  * and limitations under the License.
13*0Sstevel@tonic-gate  *
14*0Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
15*0Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16*0Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
17*0Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
18*0Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
19*0Sstevel@tonic-gate  *
20*0Sstevel@tonic-gate  * CDDL HEADER END
21*0Sstevel@tonic-gate  */
22*0Sstevel@tonic-gate /*
23*0Sstevel@tonic-gate  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24*0Sstevel@tonic-gate  * Use is subject to license terms.
25*0Sstevel@tonic-gate  */
26*0Sstevel@tonic-gate 
27*0Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
28*0Sstevel@tonic-gate 
29*0Sstevel@tonic-gate #include <sys/types.h>
30*0Sstevel@tonic-gate #include <sys/param.h>
31*0Sstevel@tonic-gate #include <sys/kmem.h>
32*0Sstevel@tonic-gate #include <sys/cpu_sgnblk_defs.h>
33*0Sstevel@tonic-gate #include <vm/seg.h>
34*0Sstevel@tonic-gate #include <sys/iommu.h>
35*0Sstevel@tonic-gate #include <sys/vtrace.h>
36*0Sstevel@tonic-gate #include <sys/intreg.h>
37*0Sstevel@tonic-gate #include <sys/ivintr.h>
38*0Sstevel@tonic-gate #include <sys/cpuvar.h>
39*0Sstevel@tonic-gate #include <sys/systm.h>
40*0Sstevel@tonic-gate #include <sys/machsystm.h>
41*0Sstevel@tonic-gate #include <sys/cyclic.h>
42*0Sstevel@tonic-gate #include <sys/cpu_sgn.h>
43*0Sstevel@tonic-gate 
44*0Sstevel@tonic-gate extern	cpu_sgnblk_t *cpu_sgnblkp[NCPU];
45*0Sstevel@tonic-gate extern struct cpu *SIGBCPU;
46*0Sstevel@tonic-gate extern	void power_down(const char *);
47*0Sstevel@tonic-gate 
48*0Sstevel@tonic-gate uint_t bbus_intr_inum;
49*0Sstevel@tonic-gate uint_t bbus_poll_inum;
50*0Sstevel@tonic-gate 
51*0Sstevel@tonic-gate /*
52*0Sstevel@tonic-gate  * Support for sgnblk polling.
53*0Sstevel@tonic-gate  */
54*0Sstevel@tonic-gate 
55*0Sstevel@tonic-gate /* Internal function prototypes */
56*0Sstevel@tonic-gate static void sgnblk_poll_init();
57*0Sstevel@tonic-gate static uint_t bbus_poll(caddr_t arg1, caddr_t arg2);
58*0Sstevel@tonic-gate static void sgnblk_poll_handler(void *unused);
59*0Sstevel@tonic-gate #ifdef THROTTLE
60*0Sstevel@tonic-gate static void sgnblk_poll_throttle(uint64_t interval);
61*0Sstevel@tonic-gate #endif /* THROTTLE */
62*0Sstevel@tonic-gate 
63*0Sstevel@tonic-gate /*  Default sgnblk polling interval is every 5 seconds. */
64*0Sstevel@tonic-gate #define	ONE_SECOND		(1000000)	/* in usecs */
65*0Sstevel@tonic-gate #ifdef THROTTLE
66*0Sstevel@tonic-gate #define	SGNBLK_POLL_INTERVAL	(5 * ONE_SECOND)
67*0Sstevel@tonic-gate #define	SGNBLK_POLL_FAST	(ONE_SECOND >> 1)
68*0Sstevel@tonic-gate #define	SGNBLK_POLL_FAST_WIN	((60 * ONE_SECOND) / \
69*0Sstevel@tonic-gate 					SGNBLK_POLL_FAST)
70*0Sstevel@tonic-gate #else /* THROTTLE */
71*0Sstevel@tonic-gate /*
72*0Sstevel@tonic-gate  * Until we can find a way to throttle back to 0.5 second intervals
73*0Sstevel@tonic-gate  * we're stuck fixed on 2.5 second intervals.
74*0Sstevel@tonic-gate  */
75*0Sstevel@tonic-gate #define	SGNBLK_POLL_INTERVAL	((2 * ONE_SECOND) + (ONE_SECOND >> 1))
76*0Sstevel@tonic-gate #endif /* THROTTLE */
77*0Sstevel@tonic-gate 
78*0Sstevel@tonic-gate #define	MAX_SGNBLK_POLL_CLNT	5
79*0Sstevel@tonic-gate 
80*0Sstevel@tonic-gate void (*pollclntfunc[MAX_SGNBLK_POLL_CLNT])();
81*0Sstevel@tonic-gate /*
82*0Sstevel@tonic-gate  * sgnblk_mutex		Protects juggling & sgnblk_poll_refs[].
83*0Sstevel@tonic-gate  * sgnblk_poll_mutex	Protects pollclntfunc[].
84*0Sstevel@tonic-gate  */
85*0Sstevel@tonic-gate kmutex_t	sgnblk_mutex;
86*0Sstevel@tonic-gate kmutex_t	sgnblk_poll_mutex;
87*0Sstevel@tonic-gate static uint64_t	sgnblk_poll_interval = SGNBLK_POLL_INTERVAL;
88*0Sstevel@tonic-gate #ifdef THROTTLE
89*0Sstevel@tonic-gate static uint64_t	sgnblk_poll_fast = SGNBLK_POLL_FAST;
90*0Sstevel@tonic-gate static int64_t	sgnblk_poll_fast_win = SGNBLK_POLL_FAST_WIN;
91*0Sstevel@tonic-gate #endif /* THROTTLE */
92*0Sstevel@tonic-gate static processorid_t	sgnblk_pollcpu = -1;
93*0Sstevel@tonic-gate /*
94*0Sstevel@tonic-gate  * Note that the sigblock polling depends on CY_HIGH_LEVEL
95*0Sstevel@tonic-gate  * being higher than PIL_13 since we ultimately need to
96*0Sstevel@tonic-gate  * dispatch a PIL_13 soft handler.
97*0Sstevel@tonic-gate  * Also, we assume one sgnblk handler for the entire system.
98*0Sstevel@tonic-gate  * Once upon a time we had them per-cpu.  With the Cyclic stuff
99*0Sstevel@tonic-gate  * we would have to bind our cyclic handler to a cpu and doing
100*0Sstevel@tonic-gate  * this prevents that cpu from being offlined.  Since the Cyclic
101*0Sstevel@tonic-gate  * subsystem could indirectly juggle us without us knowing we
102*0Sstevel@tonic-gate  * have to assume we're running from any possible cpu and not
103*0Sstevel@tonic-gate  * always SIGBCPU.
104*0Sstevel@tonic-gate  */
105*0Sstevel@tonic-gate #ifdef THROTTLE
106*0Sstevel@tonic-gate static cyclic_id_t	sgnblk_poll_cycid = CYCLIC_NONE;
107*0Sstevel@tonic-gate #endif /* THROTTLE */
108*0Sstevel@tonic-gate static cyc_handler_t	sgnblk_poll_cychandler = {
109*0Sstevel@tonic-gate 	sgnblk_poll_handler,
110*0Sstevel@tonic-gate 	NULL,
111*0Sstevel@tonic-gate 	CY_HIGH_LEVEL
112*0Sstevel@tonic-gate };
113*0Sstevel@tonic-gate static cyc_time_t	sgnblk_poll_time;
114*0Sstevel@tonic-gate 
115*0Sstevel@tonic-gate /*
116*0Sstevel@tonic-gate  * Anybody that references the polling (SIGBCPU) can
117*0Sstevel@tonic-gate  * register a callback function that will be called if
118*0Sstevel@tonic-gate  * the polling cpu is juggled, e.g. during a DR operation.
119*0Sstevel@tonic-gate  */
120*0Sstevel@tonic-gate #define	MAX_SGNBLK_POLL_REFS	10
121*0Sstevel@tonic-gate 
122*0Sstevel@tonic-gate struct sgnblk_poll_refs {
123*0Sstevel@tonic-gate 	void	(*callback)(cpu_sgnblk_t *sigbp, void *arg);
124*0Sstevel@tonic-gate 	void	*arg;
125*0Sstevel@tonic-gate }	sgnblk_poll_refs[MAX_SGNBLK_POLL_REFS];
126*0Sstevel@tonic-gate 
127*0Sstevel@tonic-gate /*
128*0Sstevel@tonic-gate  * Bootbus intr handler: Generic handler for all SSP/CBS
129*0Sstevel@tonic-gate  * interrupt requests initiated via the hw bootbus intr
130*0Sstevel@tonic-gate  * mechanism. This is similar to the level15
131*0Sstevel@tonic-gate  * interrupt handling for sigb commands in the CS6400.
132*0Sstevel@tonic-gate  * Most of these code were stolen from the sigb stuff in
133*0Sstevel@tonic-gate  * in CS6400.
134*0Sstevel@tonic-gate  */
135*0Sstevel@tonic-gate 
136*0Sstevel@tonic-gate extern struct cpu cpu0;
137*0Sstevel@tonic-gate 
138*0Sstevel@tonic-gate /*ARGSUSED*/
139*0Sstevel@tonic-gate static uint_t
140*0Sstevel@tonic-gate bbus_intr(caddr_t arg)
141*0Sstevel@tonic-gate {
142*0Sstevel@tonic-gate 	int	cmd = 0;
143*0Sstevel@tonic-gate 	processorid_t	cpu_id = CPU->cpu_id;
144*0Sstevel@tonic-gate 	int	retflag;
145*0Sstevel@tonic-gate 	int	resp = 0;
146*0Sstevel@tonic-gate 	proc_t	*initpp;
147*0Sstevel@tonic-gate 
148*0Sstevel@tonic-gate 	ASSERT(cpu_sgnblkp[cpu_id] != NULL);
149*0Sstevel@tonic-gate 
150*0Sstevel@tonic-gate 	/*
151*0Sstevel@tonic-gate 	 * Check for unsolicited messages in the host's mailbox.
152*0Sstevel@tonic-gate 	 */
153*0Sstevel@tonic-gate 	retflag = cpu_sgnblkp[cpu_id]->sigb_host_mbox.flag;
154*0Sstevel@tonic-gate 
155*0Sstevel@tonic-gate 	switch (retflag) {
156*0Sstevel@tonic-gate 	case CBS_TO_HOST:
157*0Sstevel@tonic-gate 		retflag = HOST_TO_CBS;
158*0Sstevel@tonic-gate 		break;
159*0Sstevel@tonic-gate 	default:
160*0Sstevel@tonic-gate 		retflag = SIGB_MBOX_EMPTY;
161*0Sstevel@tonic-gate 		break;
162*0Sstevel@tonic-gate 	}
163*0Sstevel@tonic-gate 	if (retflag == SIGB_MBOX_EMPTY)
164*0Sstevel@tonic-gate 		return (0);	/* interrupt not claimed */
165*0Sstevel@tonic-gate 
166*0Sstevel@tonic-gate 	/*
167*0Sstevel@tonic-gate 	 * We only look for UNSOLICITED messages, i.e. commands.
168*0Sstevel@tonic-gate 	 * Responses to these commands are returned into the same
169*0Sstevel@tonic-gate 	 * mailbox from which the command was received, i.e. host's.
170*0Sstevel@tonic-gate 	 *
171*0Sstevel@tonic-gate 	 * If the host should solicit a message from the SSP, that
172*0Sstevel@tonic-gate 	 * message/command goes into the SSP's mailbox (sigb_ssp_mbox).
173*0Sstevel@tonic-gate 	 * The responses (from the SSP) to these messages will be
174*0Sstevel@tonic-gate 	 * read from the ssp mailbox by whomever solicited it, but
175*0Sstevel@tonic-gate 	 * will NOT be handled through this level 15 interrupt
176*0Sstevel@tonic-gate 	 * mechanism.
177*0Sstevel@tonic-gate 	 *
178*0Sstevel@tonic-gate 	 * Note that use of the flag field of the signature block mailbox
179*0Sstevel@tonic-gate 	 * structure and the mailbox protocol itself, serializes access
180*0Sstevel@tonic-gate 	 * to these mailboxes.
181*0Sstevel@tonic-gate 	 */
182*0Sstevel@tonic-gate 
183*0Sstevel@tonic-gate 	resp = 0;
184*0Sstevel@tonic-gate 
185*0Sstevel@tonic-gate 	/*
186*0Sstevel@tonic-gate 	 * The first sizeof (uint_t) bytes of the data field
187*0Sstevel@tonic-gate 	 * is the command.
188*0Sstevel@tonic-gate 	 */
189*0Sstevel@tonic-gate 	cmd = cpu_sgnblkp[cpu_id]->sigb_host_mbox.cmd;
190*0Sstevel@tonic-gate 
191*0Sstevel@tonic-gate 	switch (cmd) {
192*0Sstevel@tonic-gate 		case SSP_GOTO_OBP:
193*0Sstevel@tonic-gate 		/*
194*0Sstevel@tonic-gate 		 * Let's set the mailbox flag to BUSY while we are in OBP
195*0Sstevel@tonic-gate 		 */
196*0Sstevel@tonic-gate 		cpu_sgnblkp[cpu_id]->sigb_host_mbox.flag = SIGB_MBOX_BUSY;
197*0Sstevel@tonic-gate 
198*0Sstevel@tonic-gate 		debug_enter("SSP requested (SSP_GOTO_OBP)");
199*0Sstevel@tonic-gate 		/*
200*0Sstevel@tonic-gate 		 * This command does NOT require a response.
201*0Sstevel@tonic-gate 		 */
202*0Sstevel@tonic-gate 		resp = 0;
203*0Sstevel@tonic-gate 		break;
204*0Sstevel@tonic-gate 
205*0Sstevel@tonic-gate 		case SSP_GOTO_PANIC:
206*0Sstevel@tonic-gate 		/*
207*0Sstevel@tonic-gate 		 * Let's reset the mailbox flag before we bail.
208*0Sstevel@tonic-gate 		 */
209*0Sstevel@tonic-gate 		cpu_sgnblkp[cpu_id]->sigb_host_mbox.flag = SIGB_MBOX_EMPTY;
210*0Sstevel@tonic-gate 
211*0Sstevel@tonic-gate 		cmn_err(CE_PANIC, "SSP requested (SSP_GOTO_PANIC)\n");
212*0Sstevel@tonic-gate 		/* should never reach this point */
213*0Sstevel@tonic-gate 
214*0Sstevel@tonic-gate 		resp = 0;
215*0Sstevel@tonic-gate 		break;
216*0Sstevel@tonic-gate 		case SSP_ENVIRON:
217*0Sstevel@tonic-gate 		/*
218*0Sstevel@tonic-gate 		 * Environmental Interrupt.
219*0Sstevel@tonic-gate 		 */
220*0Sstevel@tonic-gate 
221*0Sstevel@tonic-gate 		/*
222*0Sstevel@tonic-gate 		 * Send SIGPWR to init(1) it will run rc0, which will uadmin to
223*0Sstevel@tonic-gate 		 * powerdown.
224*0Sstevel@tonic-gate 		 */
225*0Sstevel@tonic-gate 
226*0Sstevel@tonic-gate 		mutex_enter(&pidlock);
227*0Sstevel@tonic-gate 		initpp = prfind(P_INITPID);
228*0Sstevel@tonic-gate 		mutex_exit(&pidlock);
229*0Sstevel@tonic-gate 
230*0Sstevel@tonic-gate 		/*
231*0Sstevel@tonic-gate 		 * If we're still booting and init(1) isn't set up yet,
232*0Sstevel@tonic-gate 		 * simply halt.
233*0Sstevel@tonic-gate 		 */
234*0Sstevel@tonic-gate 		if (initpp == NULL) {
235*0Sstevel@tonic-gate 			extern void halt(char *);
236*0Sstevel@tonic-gate 			cmn_err(CE_WARN, "?Environmental Interrupt");
237*0Sstevel@tonic-gate 			power_down((char *)NULL);
238*0Sstevel@tonic-gate 			halt("Power off the System!\n"); /* just in case */
239*0Sstevel@tonic-gate 		}
240*0Sstevel@tonic-gate 
241*0Sstevel@tonic-gate 		/*
242*0Sstevel@tonic-gate 		 * else, graceful shutdown with inittab and all getting involved
243*0Sstevel@tonic-gate 		 *
244*0Sstevel@tonic-gate 		 * XXX: Do we Need to modify the init process for the Cray 6400!
245*0Sstevel@tonic-gate 		 */
246*0Sstevel@tonic-gate 		psignal(initpp, SIGPWR);
247*0Sstevel@tonic-gate 
248*0Sstevel@tonic-gate 		/*
249*0Sstevel@tonic-gate 		 * XXX: kick off a sanity timeout panic in case the /etc/inittab
250*0Sstevel@tonic-gate 		 * or /etc/rc0 files are hosed.  The 6400 needs to hang here
251*0Sstevel@tonic-gate 		 * when we return from psignal.
252*0Sstevel@tonic-gate 		 *
253*0Sstevel@tonic-gate 		 * cmn_err(CE_PANIC, "SSP requested (SSP_ENVIRON)\n");
254*0Sstevel@tonic-gate 		 * should never reach this point
255*0Sstevel@tonic-gate 		 */
256*0Sstevel@tonic-gate 
257*0Sstevel@tonic-gate 		resp = 0;
258*0Sstevel@tonic-gate 		break;
259*0Sstevel@tonic-gate 		/*
260*0Sstevel@tonic-gate 		 * Could handle more mailbox commands right here.
261*0Sstevel@tonic-gate 		 */
262*0Sstevel@tonic-gate 
263*0Sstevel@tonic-gate 		default:
264*0Sstevel@tonic-gate 		resp = SIGB_BAD_MBOX_CMD;
265*0Sstevel@tonic-gate 		break;
266*0Sstevel@tonic-gate 	}
267*0Sstevel@tonic-gate 
268*0Sstevel@tonic-gate 	/*
269*0Sstevel@tonic-gate 	 * If resp is non-zero then we'll automatically reset
270*0Sstevel@tonic-gate 	 * the handler_sigb lock once we've sent the response,
271*0Sstevel@tonic-gate 	 * however if no response is needed, then resetlck must
272*0Sstevel@tonic-gate 	 * be set so that the handler_sigb lock is reset.
273*0Sstevel@tonic-gate 	 */
274*0Sstevel@tonic-gate 	if (resp != 0) {
275*0Sstevel@tonic-gate 		/*
276*0Sstevel@tonic-gate 		 * Had some kind of trouble handling the mailbox
277*0Sstevel@tonic-gate 		 * command.  Need to send back an error response
278*0Sstevel@tonic-gate 		 * and back out of the cpu_sgnblk handling.
279*0Sstevel@tonic-gate 		 */
280*0Sstevel@tonic-gate 		cpu_sgnblkp[cpu_id]->sigb_host_mbox.cmd = resp;
281*0Sstevel@tonic-gate 		bcopy((caddr_t)&cmd,
282*0Sstevel@tonic-gate 			(caddr_t)&cpu_sgnblkp[cpu_id]->sigb_host_mbox.data[0],
283*0Sstevel@tonic-gate 			sizeof (cmd));
284*0Sstevel@tonic-gate 		cpu_sgnblkp[cpu_id]->sigb_host_mbox.flag = retflag;
285*0Sstevel@tonic-gate 	} else {
286*0Sstevel@tonic-gate 		/*
287*0Sstevel@tonic-gate 		 * No response expected, but we still have to
288*0Sstevel@tonic-gate 		 * reset the flag to empty for the next person.
289*0Sstevel@tonic-gate 		 */
290*0Sstevel@tonic-gate 		cpu_sgnblkp[cpu_id]->sigb_host_mbox.flag = SIGB_MBOX_EMPTY;
291*0Sstevel@tonic-gate 	}
292*0Sstevel@tonic-gate 	return (1);	/* interrupt claimed */
293*0Sstevel@tonic-gate }
294*0Sstevel@tonic-gate 
295*0Sstevel@tonic-gate void
296*0Sstevel@tonic-gate register_bbus_intr()
297*0Sstevel@tonic-gate {
298*0Sstevel@tonic-gate 	/*
299*0Sstevel@tonic-gate 	 * Starfire's ASIC have the capability to generate a mondo
300*0Sstevel@tonic-gate 	 * vector. The SSP uses this capability via the Boot Bus to
301*0Sstevel@tonic-gate 	 * send an interrupt to a domain.
302*0Sstevel@tonic-gate 	 *
303*0Sstevel@tonic-gate 	 * The SSP generates a mondo with:
304*0Sstevel@tonic-gate 	 *	ign = UPAID_TO_IGN(bootcpu_upaid)
305*0Sstevel@tonic-gate 	 *	ino = 0
306*0Sstevel@tonic-gate 	 *
307*0Sstevel@tonic-gate 	 * An interrupt handler is added for this inum.
308*0Sstevel@tonic-gate 	 */
309*0Sstevel@tonic-gate 	bbus_intr_inum = UPAID_TO_IGN(cpu0.cpu_id) * MAX_INO;
310*0Sstevel@tonic-gate 	VERIFY(add_ivintr(bbus_intr_inum, PIL_13, bbus_intr, 0, 0) == 0);
311*0Sstevel@tonic-gate 
312*0Sstevel@tonic-gate 
313*0Sstevel@tonic-gate 	/*
314*0Sstevel@tonic-gate 	 * Due to a HW flaw in starfire, liberal use
315*0Sstevel@tonic-gate 	 * of bootbus intrs under heavy system load
316*0Sstevel@tonic-gate 	 * may cause the machine to arbstop. The workaround
317*0Sstevel@tonic-gate 	 * is to provide a polling mechanism thru the signature
318*0Sstevel@tonic-gate 	 * block interface to allow another way for the SSP to
319*0Sstevel@tonic-gate 	 * interrupt the host. Applications like IDN which generate
320*0Sstevel@tonic-gate 	 * a high degree of SSP to host interruptions for
321*0Sstevel@tonic-gate 	 * synchronization will need to use the polling facility
322*0Sstevel@tonic-gate 	 * instead of the hw bootbus interrupt mechanism.
323*0Sstevel@tonic-gate 	 * The HW bootbus intr support is left intact as it
324*0Sstevel@tonic-gate 	 * will still be used by existing SSP applications for system
325*0Sstevel@tonic-gate 	 * recovery in the event of system hangs etc.. In such situations,
326*0Sstevel@tonic-gate 	 * HW bootbus intr is a better mechanism as it is HW generated
327*0Sstevel@tonic-gate 	 * level 15 interrupt that has a better chance of kicking
328*0Sstevel@tonic-gate 	 * a otherwise hung OS into recovery.
329*0Sstevel@tonic-gate 	 *
330*0Sstevel@tonic-gate 	 * Polling is done by scheduling a constant tick timer
331*0Sstevel@tonic-gate 	 * interrupt at a certain predefined interval.
332*0Sstevel@tonic-gate 	 * The handler will do a poll and if there is a
333*0Sstevel@tonic-gate 	 * "intr" request, scheduled a soft level 13 intr
334*0Sstevel@tonic-gate 	 * to handle it. Allocate the inum for the level
335*0Sstevel@tonic-gate 	 * 13 intr here.
336*0Sstevel@tonic-gate 	 */
337*0Sstevel@tonic-gate 	bbus_poll_inum = add_softintr(PIL_13, bbus_poll, 0);
338*0Sstevel@tonic-gate }
339*0Sstevel@tonic-gate 
340*0Sstevel@tonic-gate static void
341*0Sstevel@tonic-gate sgnblk_poll_init()
342*0Sstevel@tonic-gate {
343*0Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&sgnblk_mutex));
344*0Sstevel@tonic-gate 
345*0Sstevel@tonic-gate 	mutex_init(&sgnblk_poll_mutex, NULL,
346*0Sstevel@tonic-gate 			MUTEX_SPIN, (void *)ipltospl(PIL_14));
347*0Sstevel@tonic-gate 	sgnblk_pollcpu = SIGBCPU->cpu_id;
348*0Sstevel@tonic-gate 	mutex_enter(&cpu_lock);
349*0Sstevel@tonic-gate 	sgnblk_poll_time.cyt_when = 0ull;
350*0Sstevel@tonic-gate 	sgnblk_poll_time.cyt_interval = sgnblk_poll_interval * 1000ull;
351*0Sstevel@tonic-gate #ifdef THROTTLE
352*0Sstevel@tonic-gate 	sgnblk_poll_cycid = cyclic_add(&sgnblk_poll_cychandler,
353*0Sstevel@tonic-gate 					&sgnblk_poll_time);
354*0Sstevel@tonic-gate #else /* THROTTLE */
355*0Sstevel@tonic-gate 	(void) cyclic_add(&sgnblk_poll_cychandler, &sgnblk_poll_time);
356*0Sstevel@tonic-gate #endif /* THROTTLE */
357*0Sstevel@tonic-gate 	mutex_exit(&cpu_lock);
358*0Sstevel@tonic-gate 	ASSERT(sgnblk_pollcpu == SIGBCPU->cpu_id);
359*0Sstevel@tonic-gate }
360*0Sstevel@tonic-gate 
361*0Sstevel@tonic-gate int
362*0Sstevel@tonic-gate sgnblk_poll_register(void(*func)(processorid_t cpu_id,
363*0Sstevel@tonic-gate 				cpu_sgnblk_t *cpu_sgnblkp))
364*0Sstevel@tonic-gate {
365*0Sstevel@tonic-gate 	int i;
366*0Sstevel@tonic-gate 
367*0Sstevel@tonic-gate 	/*
368*0Sstevel@tonic-gate 	 * See if we need to initialize
369*0Sstevel@tonic-gate 	 * sgnblk polling
370*0Sstevel@tonic-gate 	 */
371*0Sstevel@tonic-gate 	mutex_enter(&sgnblk_mutex);
372*0Sstevel@tonic-gate 	if (sgnblk_pollcpu == -1)
373*0Sstevel@tonic-gate 		sgnblk_poll_init();
374*0Sstevel@tonic-gate 	mutex_exit(&sgnblk_mutex);
375*0Sstevel@tonic-gate 
376*0Sstevel@tonic-gate 	mutex_enter(&sgnblk_poll_mutex);
377*0Sstevel@tonic-gate 
378*0Sstevel@tonic-gate 	/*
379*0Sstevel@tonic-gate 	 * Look for a empty slot
380*0Sstevel@tonic-gate 	 */
381*0Sstevel@tonic-gate 	for (i = 0; i < MAX_SGNBLK_POLL_CLNT; i++) {
382*0Sstevel@tonic-gate 		if (pollclntfunc[i] == NULL) {
383*0Sstevel@tonic-gate 			pollclntfunc[i] = func;
384*0Sstevel@tonic-gate 			mutex_exit(&sgnblk_poll_mutex);
385*0Sstevel@tonic-gate 			return (1);
386*0Sstevel@tonic-gate 		}
387*0Sstevel@tonic-gate 	}
388*0Sstevel@tonic-gate 	mutex_exit(&sgnblk_poll_mutex);
389*0Sstevel@tonic-gate 	return (0);	/* failed */
390*0Sstevel@tonic-gate }
391*0Sstevel@tonic-gate 
392*0Sstevel@tonic-gate int
393*0Sstevel@tonic-gate sgnblk_poll_unregister(void(*func)(processorid_t cpu_id,
394*0Sstevel@tonic-gate 				cpu_sgnblk_t *cpu_sgnblkp))
395*0Sstevel@tonic-gate {
396*0Sstevel@tonic-gate 	int i;
397*0Sstevel@tonic-gate 
398*0Sstevel@tonic-gate 	mutex_enter(&sgnblk_poll_mutex);
399*0Sstevel@tonic-gate 
400*0Sstevel@tonic-gate 	/*
401*0Sstevel@tonic-gate 	 * Look for the slot matching the function passed in.
402*0Sstevel@tonic-gate 	 */
403*0Sstevel@tonic-gate 	for (i = 0; i < MAX_SGNBLK_POLL_CLNT; i++) {
404*0Sstevel@tonic-gate 		if (pollclntfunc[i] == func) {
405*0Sstevel@tonic-gate 			pollclntfunc[i] = NULL;
406*0Sstevel@tonic-gate 			mutex_exit(&sgnblk_poll_mutex);
407*0Sstevel@tonic-gate 			return (1);
408*0Sstevel@tonic-gate 		}
409*0Sstevel@tonic-gate 	}
410*0Sstevel@tonic-gate 	mutex_exit(&sgnblk_poll_mutex);
411*0Sstevel@tonic-gate 	return (0);	/* failed */
412*0Sstevel@tonic-gate }
413*0Sstevel@tonic-gate 
414*0Sstevel@tonic-gate 
415*0Sstevel@tonic-gate /*
416*0Sstevel@tonic-gate  * For DR support.
417*0Sstevel@tonic-gate  * Juggle poll tick client to another cpu
418*0Sstevel@tonic-gate  * Assumed to be called single threaded.
419*0Sstevel@tonic-gate  */
420*0Sstevel@tonic-gate void
421*0Sstevel@tonic-gate juggle_sgnblk_poll(struct cpu *cp)
422*0Sstevel@tonic-gate {
423*0Sstevel@tonic-gate 	int		i;
424*0Sstevel@tonic-gate 
425*0Sstevel@tonic-gate 	mutex_enter(&sgnblk_mutex);
426*0Sstevel@tonic-gate 
427*0Sstevel@tonic-gate 	if (sgnblk_pollcpu == -1 ||
428*0Sstevel@tonic-gate 	    (cp != NULL && sgnblk_pollcpu == cp->cpu_id)) {
429*0Sstevel@tonic-gate 		mutex_exit(&sgnblk_mutex);
430*0Sstevel@tonic-gate 		return;
431*0Sstevel@tonic-gate 	}
432*0Sstevel@tonic-gate 
433*0Sstevel@tonic-gate 	/*
434*0Sstevel@tonic-gate 	 * Disable by simply returning here
435*0Sstevel@tonic-gate 	 * Passing a null cp is assumed to be
436*0Sstevel@tonic-gate 	 * sgnpoll disable request.
437*0Sstevel@tonic-gate 	 */
438*0Sstevel@tonic-gate 	if (cp == NULL) {
439*0Sstevel@tonic-gate 		for (i = 0; i < MAX_SGNBLK_POLL_REFS; i++) {
440*0Sstevel@tonic-gate 			void	(*func)(), *arg;
441*0Sstevel@tonic-gate 
442*0Sstevel@tonic-gate 			if ((func = sgnblk_poll_refs[i].callback) != NULL) {
443*0Sstevel@tonic-gate 				arg = sgnblk_poll_refs[i].arg;
444*0Sstevel@tonic-gate 				(*func)(NULL, arg);
445*0Sstevel@tonic-gate 			}
446*0Sstevel@tonic-gate 		}
447*0Sstevel@tonic-gate 		mutex_exit(&sgnblk_mutex);
448*0Sstevel@tonic-gate 		return;
449*0Sstevel@tonic-gate 	}
450*0Sstevel@tonic-gate 
451*0Sstevel@tonic-gate 	sgnblk_pollcpu = cp->cpu_id;
452*0Sstevel@tonic-gate 
453*0Sstevel@tonic-gate 	for (i = 0; i < MAX_SGNBLK_POLL_REFS; i++) {
454*0Sstevel@tonic-gate 		void	(*func)(), *arg;
455*0Sstevel@tonic-gate 
456*0Sstevel@tonic-gate 		if ((func = sgnblk_poll_refs[i].callback) != NULL) {
457*0Sstevel@tonic-gate 			arg = sgnblk_poll_refs[i].arg;
458*0Sstevel@tonic-gate 			(*func)(cpu_sgnblkp[sgnblk_pollcpu], arg);
459*0Sstevel@tonic-gate 		}
460*0Sstevel@tonic-gate 	}
461*0Sstevel@tonic-gate 
462*0Sstevel@tonic-gate 	mutex_exit(&sgnblk_mutex);
463*0Sstevel@tonic-gate }
464*0Sstevel@tonic-gate 
465*0Sstevel@tonic-gate #ifdef THROTTLE
466*0Sstevel@tonic-gate /*ARGSUSED0*/
467*0Sstevel@tonic-gate static void
468*0Sstevel@tonic-gate _sgnblk_poll_throttle(void *unused)
469*0Sstevel@tonic-gate {
470*0Sstevel@tonic-gate 	mutex_enter(&cpu_lock);
471*0Sstevel@tonic-gate 	if (sgnblk_poll_cycid != CYCLIC_NONE) {
472*0Sstevel@tonic-gate 		cyclic_remove(sgnblk_poll_cycid);
473*0Sstevel@tonic-gate 		sgnblk_poll_cycid = CYCLIC_NONE;
474*0Sstevel@tonic-gate 	}
475*0Sstevel@tonic-gate 
476*0Sstevel@tonic-gate 	if (sgnblk_poll_time.cyt_interval > 0ull)
477*0Sstevel@tonic-gate 		sgnblk_poll_cycid = cyclic_add(&sgnblk_poll_cychandler,
478*0Sstevel@tonic-gate 						&sgnblk_poll_time);
479*0Sstevel@tonic-gate 	mutex_exit(&cpu_lock);
480*0Sstevel@tonic-gate }
481*0Sstevel@tonic-gate 
482*0Sstevel@tonic-gate /*
483*0Sstevel@tonic-gate  * We don't want to remove the cyclic within the context of
484*0Sstevel@tonic-gate  * the handler so we kick off the throttle in background
485*0Sstevel@tonic-gate  * via a timeout call.
486*0Sstevel@tonic-gate  */
487*0Sstevel@tonic-gate static void
488*0Sstevel@tonic-gate sgnblk_poll_throttle(uint64_t new_interval)
489*0Sstevel@tonic-gate {
490*0Sstevel@tonic-gate 	mutex_enter(&cpu_lock);
491*0Sstevel@tonic-gate 	sgnblk_poll_time.cyt_when = 0ull;
492*0Sstevel@tonic-gate 	sgnblk_poll_time.cyt_interval = new_interval * 1000ull;
493*0Sstevel@tonic-gate 	mutex_exit(&cpu_lock);
494*0Sstevel@tonic-gate 
495*0Sstevel@tonic-gate 	(void) timeout(_sgnblk_poll_throttle, NULL, (clock_t)0);
496*0Sstevel@tonic-gate }
497*0Sstevel@tonic-gate #endif /* THROTTLE */
498*0Sstevel@tonic-gate 
499*0Sstevel@tonic-gate /*
500*0Sstevel@tonic-gate  * High priority interrupt handler (PIL_14)
501*0Sstevel@tonic-gate  * for signature block mbox polling.
502*0Sstevel@tonic-gate  */
503*0Sstevel@tonic-gate /*ARGSUSED0*/
504*0Sstevel@tonic-gate static void
505*0Sstevel@tonic-gate sgnblk_poll_handler(void *unused)
506*0Sstevel@tonic-gate {
507*0Sstevel@tonic-gate 	processorid_t	cpuid = SIGBCPU->cpu_id;
508*0Sstevel@tonic-gate #ifdef THROTTLE
509*0Sstevel@tonic-gate 	static int64_t	sb_window = -1;
510*0Sstevel@tonic-gate 	static uint64_t	sb_interval = 0;
511*0Sstevel@tonic-gate #endif /* THROTTLE */
512*0Sstevel@tonic-gate 
513*0Sstevel@tonic-gate 	if (cpu_sgnblkp[cpuid] == NULL)
514*0Sstevel@tonic-gate 		return;
515*0Sstevel@tonic-gate 
516*0Sstevel@tonic-gate 	/*
517*0Sstevel@tonic-gate 	 * Poll for SSP requests
518*0Sstevel@tonic-gate 	 */
519*0Sstevel@tonic-gate 	if (cpu_sgnblkp[cpuid]->sigb_host_mbox.intr == SIGB_INTR_SEND) {
520*0Sstevel@tonic-gate 		/* reset the flag - sure hope this is atomic */
521*0Sstevel@tonic-gate 		cpu_sgnblkp[cpuid]->sigb_host_mbox.intr = SIGB_INTR_OFF;
522*0Sstevel@tonic-gate 
523*0Sstevel@tonic-gate #ifdef THROTTLE
524*0Sstevel@tonic-gate 		/*
525*0Sstevel@tonic-gate 		 * Go into fast poll mode for a short duration
526*0Sstevel@tonic-gate 		 * (SGNBLK_POLL_FAST_WIN) in SGNBLK_POLL_FAST interval.
527*0Sstevel@tonic-gate 		 * The assumption here is that we just got activity
528*0Sstevel@tonic-gate 		 * on the mbox poll, the probability of more coming down
529*0Sstevel@tonic-gate 		 * the pipe is high - so let's look more often.
530*0Sstevel@tonic-gate 		 */
531*0Sstevel@tonic-gate 		if ((sb_window < 0) && (sb_interval > sgnblk_poll_fast)) {
532*0Sstevel@tonic-gate 			sb_interval = sgnblk_poll_fast;
533*0Sstevel@tonic-gate 			sgnblk_poll_throttle(sb_interval);
534*0Sstevel@tonic-gate 		}
535*0Sstevel@tonic-gate 		sb_window = sgnblk_poll_fast_win;
536*0Sstevel@tonic-gate #endif /* THROTTLE */
537*0Sstevel@tonic-gate 
538*0Sstevel@tonic-gate 		/* schedule poll processing */
539*0Sstevel@tonic-gate 		setsoftint(bbus_poll_inum);
540*0Sstevel@tonic-gate 
541*0Sstevel@tonic-gate #ifdef THROTTLE
542*0Sstevel@tonic-gate 	} else if (sb_window >= 0) {
543*0Sstevel@tonic-gate 		/* Revert to slow polling once fast window ends */
544*0Sstevel@tonic-gate 		if ((--sb_window < 0) &&
545*0Sstevel@tonic-gate 		    (sb_interval < sgnblk_poll_interval)) {
546*0Sstevel@tonic-gate 			sb_interval = sgnblk_poll_interval;
547*0Sstevel@tonic-gate 			sgnblk_poll_throttle(sb_interval);
548*0Sstevel@tonic-gate 		}
549*0Sstevel@tonic-gate #endif /* THROTTLE */
550*0Sstevel@tonic-gate 	}
551*0Sstevel@tonic-gate }
552*0Sstevel@tonic-gate 
553*0Sstevel@tonic-gate /*ARGSUSED*/
554*0Sstevel@tonic-gate static uint_t
555*0Sstevel@tonic-gate bbus_poll(caddr_t arg1, caddr_t arg2)
556*0Sstevel@tonic-gate {
557*0Sstevel@tonic-gate 	int i;
558*0Sstevel@tonic-gate 	processorid_t cpu_id = SIGBCPU->cpu_id;
559*0Sstevel@tonic-gate 	cpu_sgnblk_t *sgnblkp = cpu_sgnblkp[cpu_id];
560*0Sstevel@tonic-gate 
561*0Sstevel@tonic-gate 	/*
562*0Sstevel@tonic-gate 	 * Go thru the poll client array and call the
563*0Sstevel@tonic-gate 	 * poll client functions one by one
564*0Sstevel@tonic-gate 	 */
565*0Sstevel@tonic-gate 	mutex_enter(&sgnblk_poll_mutex);
566*0Sstevel@tonic-gate 
567*0Sstevel@tonic-gate 	for (i = 0; i < MAX_SGNBLK_POLL_CLNT; i++) {
568*0Sstevel@tonic-gate 		void (*func)(processorid_t cpuid, cpu_sgnblk_t *sgnblkp);
569*0Sstevel@tonic-gate 
570*0Sstevel@tonic-gate 		if ((func = pollclntfunc[i]) != NULL) {
571*0Sstevel@tonic-gate 			mutex_exit(&sgnblk_poll_mutex);
572*0Sstevel@tonic-gate 			(*func)(cpu_id, sgnblkp);
573*0Sstevel@tonic-gate 			mutex_enter(&sgnblk_poll_mutex);
574*0Sstevel@tonic-gate 		}
575*0Sstevel@tonic-gate 	}
576*0Sstevel@tonic-gate 	mutex_exit(&sgnblk_poll_mutex);
577*0Sstevel@tonic-gate 
578*0Sstevel@tonic-gate 	return (1);
579*0Sstevel@tonic-gate }
580*0Sstevel@tonic-gate 
581*0Sstevel@tonic-gate int
582*0Sstevel@tonic-gate sgnblk_poll_reference(void (*callback)(cpu_sgnblk_t *sigb, void *arg),
583*0Sstevel@tonic-gate 	void *arg)
584*0Sstevel@tonic-gate {
585*0Sstevel@tonic-gate 	int		i, slot;
586*0Sstevel@tonic-gate 	cpu_sgnblk_t	*sigbp;
587*0Sstevel@tonic-gate 
588*0Sstevel@tonic-gate 	if (callback == NULL)
589*0Sstevel@tonic-gate 		return (-1);
590*0Sstevel@tonic-gate 
591*0Sstevel@tonic-gate 	mutex_enter(&sgnblk_mutex);
592*0Sstevel@tonic-gate 	/*
593*0Sstevel@tonic-gate 	 * First verify caller is not already registered.
594*0Sstevel@tonic-gate 	 */
595*0Sstevel@tonic-gate 	slot = -1;
596*0Sstevel@tonic-gate 	for (i = 0; i < MAX_SGNBLK_POLL_REFS; i++) {
597*0Sstevel@tonic-gate 		if ((slot == -1) && (sgnblk_poll_refs[i].callback == NULL)) {
598*0Sstevel@tonic-gate 			slot = i;
599*0Sstevel@tonic-gate 			continue;
600*0Sstevel@tonic-gate 		}
601*0Sstevel@tonic-gate 		if (sgnblk_poll_refs[i].callback == callback) {
602*0Sstevel@tonic-gate 			mutex_exit(&sgnblk_mutex);
603*0Sstevel@tonic-gate 			return (-1);
604*0Sstevel@tonic-gate 		}
605*0Sstevel@tonic-gate 	}
606*0Sstevel@tonic-gate 	/*
607*0Sstevel@tonic-gate 	 * Now find an empty entry.
608*0Sstevel@tonic-gate 	 */
609*0Sstevel@tonic-gate 	if (slot == -1) {
610*0Sstevel@tonic-gate 		mutex_exit(&sgnblk_mutex);
611*0Sstevel@tonic-gate 		return (-1);
612*0Sstevel@tonic-gate 	}
613*0Sstevel@tonic-gate 	sgnblk_poll_refs[slot].callback = callback;
614*0Sstevel@tonic-gate 	sgnblk_poll_refs[slot].arg = arg;
615*0Sstevel@tonic-gate 
616*0Sstevel@tonic-gate 	sigbp = (sgnblk_pollcpu != -1) ? cpu_sgnblkp[sgnblk_pollcpu] : NULL;
617*0Sstevel@tonic-gate 
618*0Sstevel@tonic-gate 	(*callback)(sigbp, arg);
619*0Sstevel@tonic-gate 
620*0Sstevel@tonic-gate 	mutex_exit(&sgnblk_mutex);
621*0Sstevel@tonic-gate 
622*0Sstevel@tonic-gate 	return (0);
623*0Sstevel@tonic-gate }
624*0Sstevel@tonic-gate 
625*0Sstevel@tonic-gate void
626*0Sstevel@tonic-gate sgnblk_poll_unreference(void (*callback)(cpu_sgnblk_t *sigb, void *arg))
627*0Sstevel@tonic-gate {
628*0Sstevel@tonic-gate 	int	i;
629*0Sstevel@tonic-gate 
630*0Sstevel@tonic-gate 	mutex_enter(&sgnblk_mutex);
631*0Sstevel@tonic-gate 	for (i = 0; i < MAX_SGNBLK_POLL_REFS; i++) {
632*0Sstevel@tonic-gate 		if (sgnblk_poll_refs[i].callback == callback) {
633*0Sstevel@tonic-gate 			void	*arg;
634*0Sstevel@tonic-gate 
635*0Sstevel@tonic-gate 			arg = sgnblk_poll_refs[i].arg;
636*0Sstevel@tonic-gate 			(*callback)(NULL, arg);
637*0Sstevel@tonic-gate 			sgnblk_poll_refs[i].callback = NULL;
638*0Sstevel@tonic-gate 			sgnblk_poll_refs[i].arg = NULL;
639*0Sstevel@tonic-gate 			break;
640*0Sstevel@tonic-gate 		}
641*0Sstevel@tonic-gate 	}
642*0Sstevel@tonic-gate 	mutex_exit(&sgnblk_mutex);
643*0Sstevel@tonic-gate }
644