1*03831d35Sstevel /*
2*03831d35Sstevel  * CDDL HEADER START
3*03831d35Sstevel  *
4*03831d35Sstevel  * The contents of this file are subject to the terms of the
5*03831d35Sstevel  * Common Development and Distribution License (the "License").
6*03831d35Sstevel  * You may not use this file except in compliance with the License.
7*03831d35Sstevel  *
8*03831d35Sstevel  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9*03831d35Sstevel  * or http://www.opensolaris.org/os/licensing.
10*03831d35Sstevel  * See the License for the specific language governing permissions
11*03831d35Sstevel  * and limitations under the License.
12*03831d35Sstevel  *
13*03831d35Sstevel  * When distributing Covered Code, include this CDDL HEADER in each
14*03831d35Sstevel  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15*03831d35Sstevel  * If applicable, add the following below this CDDL HEADER, with the
16*03831d35Sstevel  * fields enclosed by brackets "[]" replaced with your own identifying
17*03831d35Sstevel  * information: Portions Copyright [yyyy] [name of copyright owner]
18*03831d35Sstevel  *
19*03831d35Sstevel  * CDDL HEADER END
20*03831d35Sstevel  */
21*03831d35Sstevel 
22*03831d35Sstevel /*
23*03831d35Sstevel  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24*03831d35Sstevel  * Use is subject to license terms.
25*03831d35Sstevel  */
26*03831d35Sstevel 
27*03831d35Sstevel #pragma ident	"%Z%%M%	%I%	%E% SMI"
28*03831d35Sstevel 
29*03831d35Sstevel /*
30*03831d35Sstevel  * PCI SBBC Device Driver that provides interfaces into
31*03831d35Sstevel  * EPLD and IO-SRAM
32*03831d35Sstevel  *
33*03831d35Sstevel  */
34*03831d35Sstevel #include <sys/types.h>
35*03831d35Sstevel #include <sys/param.h>
36*03831d35Sstevel #include <sys/errno.h>
37*03831d35Sstevel #include <sys/file.h>
38*03831d35Sstevel #include <sys/cmn_err.h>
39*03831d35Sstevel #include <sys/stropts.h>
40*03831d35Sstevel #include <sys/kmem.h>
41*03831d35Sstevel #include <sys/sunndi.h>
42*03831d35Sstevel #include <sys/conf.h>		/* req. by dev_ops flags MTSAFE etc. */
43*03831d35Sstevel #include <sys/modctl.h>		/* for modldrv */
44*03831d35Sstevel #include <sys/promif.h>
45*03831d35Sstevel #include <sys/stat.h>
46*03831d35Sstevel #include <sys/ddi.h>
47*03831d35Sstevel 
48*03831d35Sstevel #include <sys/serengeti.h>
49*03831d35Sstevel #include <sys/sgsbbc_priv.h>
50*03831d35Sstevel #include <sys/sgsbbc_iosram_priv.h>
51*03831d35Sstevel #include <sys/sgsbbc_mailbox_priv.h>
52*03831d35Sstevel 
53*03831d35Sstevel #ifdef DEBUG
54*03831d35Sstevel /* debug flag */
55*03831d35Sstevel uint_t sgsbbc_debug = 0;
56*03831d35Sstevel #endif /* DEBUG */
57*03831d35Sstevel 
58*03831d35Sstevel /* driver entry point fn definitions */
59*03831d35Sstevel static int	sbbc_attach(dev_info_t *, ddi_attach_cmd_t);
60*03831d35Sstevel static int	sbbc_detach(dev_info_t *, ddi_detach_cmd_t);
61*03831d35Sstevel 
62*03831d35Sstevel /*
63*03831d35Sstevel  * SBBC soft state hook
64*03831d35Sstevel  */
65*03831d35Sstevel static void    *sbbcp;
66*03831d35Sstevel 
67*03831d35Sstevel /*
68*03831d35Sstevel  * Chosen IOSRAM
69*03831d35Sstevel  */
70*03831d35Sstevel struct chosen_iosram *master_iosram = NULL;
71*03831d35Sstevel 
72*03831d35Sstevel /*
73*03831d35Sstevel  * define new iosram's sbbc and liked list of sbbc.
74*03831d35Sstevel  */
75*03831d35Sstevel struct sbbc_softstate *sgsbbc_instances = NULL;
76*03831d35Sstevel 
77*03831d35Sstevel /*
78*03831d35Sstevel  * At attach time, check if the device is the 'chosen' node
79*03831d35Sstevel  * if it is, set up the IOSRAM Solaris<->SC Comm tunnel
80*03831d35Sstevel  * Its like 'Highlander' - there can be only one !
81*03831d35Sstevel  */
82*03831d35Sstevel static int	master_chosen = FALSE;
83*03831d35Sstevel kmutex_t	chosen_lock;
84*03831d35Sstevel 
85*03831d35Sstevel /*
86*03831d35Sstevel  * Local variable to save intr_in_enabled when the driver is suspended
87*03831d35Sstevel  */
88*03831d35Sstevel static uint32_t	intr_in_enabled;
89*03831d35Sstevel 
90*03831d35Sstevel /*
91*03831d35Sstevel  * Local declarations
92*03831d35Sstevel  */
93*03831d35Sstevel static void	softsp_init(sbbc_softstate_t *, dev_info_t *);
94*03831d35Sstevel static void	sbbc_chosen_init(sbbc_softstate_t *);
95*03831d35Sstevel static void	sbbc_add_instance(sbbc_softstate_t *);
96*03831d35Sstevel static void	sbbc_remove_instance(sbbc_softstate_t *);
97*03831d35Sstevel static int	sbbc_find_dip(dev_info_t *, void *);
98*03831d35Sstevel static void	sbbc_unmap_regs(sbbc_softstate_t *);
99*03831d35Sstevel 
100*03831d35Sstevel /*
101*03831d35Sstevel  * ops stuff.
102*03831d35Sstevel  */
103*03831d35Sstevel static struct cb_ops sbbc_cb_ops = {
104*03831d35Sstevel 	nodev,					/* cb_open */
105*03831d35Sstevel 	nodev,					/* cb_close */
106*03831d35Sstevel 	nodev,					/* cb_strategy */
107*03831d35Sstevel 	nodev,					/* cb_print */
108*03831d35Sstevel 	nodev,					/* cb_dump */
109*03831d35Sstevel 	nodev,					/* cb_read */
110*03831d35Sstevel 	nodev,					/* cb_write */
111*03831d35Sstevel 	nodev,					/* cb_ioctl */
112*03831d35Sstevel 	nodev,					/* cb_devmap */
113*03831d35Sstevel 	nodev,					/* cb_mmap */
114*03831d35Sstevel 	nodev,					/* cb_segmap */
115*03831d35Sstevel 	nochpoll,				/* cb_chpoll */
116*03831d35Sstevel 	ddi_prop_op,				/* cb_prop_op */
117*03831d35Sstevel 	NULL,					/* cb_stream */
118*03831d35Sstevel 	D_NEW | D_MP				/* cb_flag */
119*03831d35Sstevel };
120*03831d35Sstevel 
121*03831d35Sstevel /*
122*03831d35Sstevel  * Declare ops vectors for auto configuration.
123*03831d35Sstevel  */
124*03831d35Sstevel struct dev_ops  sbbc_ops = {
125*03831d35Sstevel 	DEVO_REV,		/* devo_rev */
126*03831d35Sstevel 	0,			/* devo_refcnt */
127*03831d35Sstevel 	ddi_getinfo_1to1,	/* devo_getinfo */
128*03831d35Sstevel 	nulldev,		/* devo_identify */
129*03831d35Sstevel 	nulldev,		/* devo_probe */
130*03831d35Sstevel 	sbbc_attach,		/* devo_attach */
131*03831d35Sstevel 	sbbc_detach,		/* devo_detach */
132*03831d35Sstevel 	nodev,			/* devo_reset */
133*03831d35Sstevel 	&sbbc_cb_ops,		/* devo_cb_ops */
134*03831d35Sstevel 	(struct bus_ops *)NULL,	/* devo_bus_ops */
135*03831d35Sstevel 	nulldev			/* devo_power */
136*03831d35Sstevel };
137*03831d35Sstevel 
138*03831d35Sstevel /*
139*03831d35Sstevel  * Loadable module support.
140*03831d35Sstevel  */
141*03831d35Sstevel extern struct mod_ops mod_driverops;
142*03831d35Sstevel 
143*03831d35Sstevel static struct modldrv modldrv = {
144*03831d35Sstevel 	&mod_driverops,		/* type of module - driver */
145*03831d35Sstevel 	"PCI SBBC %I%",
146*03831d35Sstevel 	&sbbc_ops,
147*03831d35Sstevel };
148*03831d35Sstevel 
149*03831d35Sstevel static struct modlinkage modlinkage = {
150*03831d35Sstevel 	MODREV_1,
151*03831d35Sstevel 	(void *)&modldrv,
152*03831d35Sstevel 	NULL
153*03831d35Sstevel };
154*03831d35Sstevel 
155*03831d35Sstevel int
156*03831d35Sstevel _init(void)
157*03831d35Sstevel {
158*03831d35Sstevel 	int    error;
159*03831d35Sstevel 
160*03831d35Sstevel 	if ((error = ddi_soft_state_init(&sbbcp,
161*03831d35Sstevel 		sizeof (sbbc_softstate_t), 1)) != 0)
162*03831d35Sstevel 		return (error);
163*03831d35Sstevel 
164*03831d35Sstevel 	if ((error = mod_install(&modlinkage)) != 0) {
165*03831d35Sstevel 		ddi_soft_state_fini(&sbbcp);
166*03831d35Sstevel 		return (error);
167*03831d35Sstevel 	}
168*03831d35Sstevel 
169*03831d35Sstevel 	/*
170*03831d35Sstevel 	 * Initialise the global 'chosen' IOSRAM mutex
171*03831d35Sstevel 	 */
172*03831d35Sstevel 	mutex_init(&chosen_lock, NULL, MUTEX_DEFAULT, NULL);
173*03831d35Sstevel 
174*03831d35Sstevel 	/*
175*03831d35Sstevel 	 * Initialise the iosram driver
176*03831d35Sstevel 	 */
177*03831d35Sstevel 	iosram_init();
178*03831d35Sstevel 
179*03831d35Sstevel 	/*
180*03831d35Sstevel 	 * Initialize the mailbox
181*03831d35Sstevel 	 */
182*03831d35Sstevel 	sbbc_mbox_init();
183*03831d35Sstevel 
184*03831d35Sstevel 	return (error);
185*03831d35Sstevel 
186*03831d35Sstevel }
187*03831d35Sstevel 
188*03831d35Sstevel int
189*03831d35Sstevel _fini(void)
190*03831d35Sstevel {
191*03831d35Sstevel 	int    error;
192*03831d35Sstevel 
193*03831d35Sstevel 	if ((error = mod_remove(&modlinkage)) == 0)
194*03831d35Sstevel 		ddi_soft_state_fini(&sbbcp);
195*03831d35Sstevel 
196*03831d35Sstevel 	master_chosen = FALSE;
197*03831d35Sstevel 
198*03831d35Sstevel 	mutex_destroy(&chosen_lock);
199*03831d35Sstevel 
200*03831d35Sstevel 	/*
201*03831d35Sstevel 	 * remove the mailbox
202*03831d35Sstevel 	 */
203*03831d35Sstevel 	sbbc_mbox_fini();
204*03831d35Sstevel 
205*03831d35Sstevel 	/*
206*03831d35Sstevel 	 * remove the iosram driver
207*03831d35Sstevel 	 */
208*03831d35Sstevel 	iosram_fini();
209*03831d35Sstevel 
210*03831d35Sstevel 	return (error);
211*03831d35Sstevel }
212*03831d35Sstevel 
213*03831d35Sstevel int
214*03831d35Sstevel _info(struct modinfo *modinfop)
215*03831d35Sstevel {
216*03831d35Sstevel 	return (mod_info(&modlinkage, modinfop));
217*03831d35Sstevel }
218*03831d35Sstevel 
219*03831d35Sstevel static int
220*03831d35Sstevel sbbc_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
221*03831d35Sstevel {
222*03831d35Sstevel 	int			instance;
223*03831d35Sstevel 	sbbc_softstate_t	*softsp;
224*03831d35Sstevel 	uint32_t		*pci_intr_enable_reg;
225*03831d35Sstevel 	int			len;
226*03831d35Sstevel #ifdef	DEBUG
227*03831d35Sstevel 	char			name[8];
228*03831d35Sstevel #endif	/* DEBUG */
229*03831d35Sstevel 
230*03831d35Sstevel 	instance = ddi_get_instance(devi);
231*03831d35Sstevel 
232*03831d35Sstevel 	switch (cmd) {
233*03831d35Sstevel 	case DDI_ATTACH:
234*03831d35Sstevel 
235*03831d35Sstevel 		if (ddi_soft_state_zalloc(sbbcp, instance) != 0)
236*03831d35Sstevel 			return (DDI_FAILURE);
237*03831d35Sstevel 
238*03831d35Sstevel 		softsp = ddi_get_soft_state(sbbcp, instance);
239*03831d35Sstevel 		softsp->sbbc_instance = instance;
240*03831d35Sstevel 
241*03831d35Sstevel 		/*
242*03831d35Sstevel 		 * Set the dip in the soft state
243*03831d35Sstevel 		 * And get interrupt cookies and initialize the
244*03831d35Sstevel 		 * per instance mutex.
245*03831d35Sstevel 		 */
246*03831d35Sstevel 		softsp_init(softsp, devi);
247*03831d35Sstevel 
248*03831d35Sstevel 
249*03831d35Sstevel 		/*
250*03831d35Sstevel 		 * Verify that an 'interrupts' property exists for
251*03831d35Sstevel 		 * this device. If not, this instance will be ignored.
252*03831d35Sstevel 		 */
253*03831d35Sstevel 		if (ddi_getproplen(DDI_DEV_T_ANY, softsp->dip,
254*03831d35Sstevel 			DDI_PROP_DONTPASS, "interrupts",
255*03831d35Sstevel 			&len) != DDI_PROP_SUCCESS) {
256*03831d35Sstevel 			SBBC_ERR1(CE_WARN, "No 'interrupts' property for the "
257*03831d35Sstevel 					"SBBC instance %d\n", instance);
258*03831d35Sstevel 			return (DDI_FAILURE);
259*03831d35Sstevel 		}
260*03831d35Sstevel 		/*
261*03831d35Sstevel 		 * Add this instance to the sbbc chosen iosram list
262*03831d35Sstevel 		 * so that it can be used for tunnel switch.
263*03831d35Sstevel 		 */
264*03831d35Sstevel 		mutex_enter(&chosen_lock);
265*03831d35Sstevel 		softsp->sbbc_state = SBBC_STATE_INIT;
266*03831d35Sstevel 		sbbc_add_instance(softsp);
267*03831d35Sstevel 
268*03831d35Sstevel 		/*
269*03831d35Sstevel 		 * If this is the chosen IOSRAM and there is no master IOSRAM
270*03831d35Sstevel 		 * yet, then let's set this instance as the master.
271*03831d35Sstevel 		 * if there is a master alreay due to the previous tunnel switch
272*03831d35Sstevel 		 * then keep as is even though this is the chosen.
273*03831d35Sstevel 		 */
274*03831d35Sstevel 		if (sgsbbc_iosram_is_chosen(softsp)) {
275*03831d35Sstevel 			ASSERT(master_iosram);
276*03831d35Sstevel 			softsp->iosram = master_iosram;
277*03831d35Sstevel 			master_iosram->sgsbbc = softsp;
278*03831d35Sstevel 
279*03831d35Sstevel 			/* Do 'chosen' init only */
280*03831d35Sstevel 			sbbc_chosen_init(softsp);
281*03831d35Sstevel 		}
282*03831d35Sstevel 
283*03831d35Sstevel 		mutex_exit(&chosen_lock);
284*03831d35Sstevel #ifdef	DEBUG
285*03831d35Sstevel 		(void) sprintf(name, "sbbc%d", instance);
286*03831d35Sstevel 
287*03831d35Sstevel 		if (ddi_create_minor_node(devi, name, S_IFCHR, instance,
288*03831d35Sstevel 			NULL, NULL) == DDI_FAILURE) {
289*03831d35Sstevel 			mutex_destroy(&softsp->sbbc_lock);
290*03831d35Sstevel 			ddi_remove_minor_node(devi, NULL);
291*03831d35Sstevel 			ddi_soft_state_free(sbbcp, instance);
292*03831d35Sstevel 			return (DDI_FAILURE);
293*03831d35Sstevel 		}
294*03831d35Sstevel #endif	/* DEBUG */
295*03831d35Sstevel 
296*03831d35Sstevel 		ddi_report_dev(devi);
297*03831d35Sstevel 
298*03831d35Sstevel 		return (DDI_SUCCESS);
299*03831d35Sstevel 
300*03831d35Sstevel 	case DDI_RESUME:
301*03831d35Sstevel 
302*03831d35Sstevel 		if (!(softsp = ddi_get_soft_state(sbbcp, instance)))
303*03831d35Sstevel 			return (DDI_FAILURE);
304*03831d35Sstevel 
305*03831d35Sstevel 		mutex_enter(&softsp->sbbc_lock);
306*03831d35Sstevel 		if ((softsp->suspended == TRUE) && (softsp->chosen == TRUE)) {
307*03831d35Sstevel 			/*
308*03831d35Sstevel 			 * Enable Interrupts now, turn on both INT#A lines
309*03831d35Sstevel 			 */
310*03831d35Sstevel 			pci_intr_enable_reg =  (uint32_t *)
311*03831d35Sstevel 					((char *)softsp->sbbc_regs +
312*03831d35Sstevel 						SBBC_PCI_INT_ENABLE);
313*03831d35Sstevel 
314*03831d35Sstevel 			ddi_put32(softsp->sbbc_reg_handle1,
315*03831d35Sstevel 				pci_intr_enable_reg,
316*03831d35Sstevel 				(uint32_t)SBBC_PCI_ENABLE_INT_A);
317*03831d35Sstevel 
318*03831d35Sstevel 			/*
319*03831d35Sstevel 			 * Reset intr_in_enabled to the original value
320*03831d35Sstevel 			 * so the SC can send us interrupt.
321*03831d35Sstevel 			 */
322*03831d35Sstevel 			if (iosram_write(SBBC_SC_INTR_ENABLED_KEY,
323*03831d35Sstevel 				0, (caddr_t)&intr_in_enabled,
324*03831d35Sstevel 				sizeof (intr_in_enabled))) {
325*03831d35Sstevel 
326*03831d35Sstevel 				mutex_exit(&softsp->sbbc_lock);
327*03831d35Sstevel 				return (DDI_FAILURE);
328*03831d35Sstevel 			}
329*03831d35Sstevel 		}
330*03831d35Sstevel 		softsp->suspended = FALSE;
331*03831d35Sstevel 
332*03831d35Sstevel 		mutex_exit(&softsp->sbbc_lock);
333*03831d35Sstevel 
334*03831d35Sstevel 		return (DDI_SUCCESS);
335*03831d35Sstevel 
336*03831d35Sstevel 	default:
337*03831d35Sstevel 		return (DDI_FAILURE);
338*03831d35Sstevel 	}
339*03831d35Sstevel }
340*03831d35Sstevel 
341*03831d35Sstevel static int
342*03831d35Sstevel sbbc_detach(dev_info_t *devi, ddi_detach_cmd_t cmd)
343*03831d35Sstevel {
344*03831d35Sstevel 	sbbc_softstate_t	*softsp;
345*03831d35Sstevel 	int			instance;
346*03831d35Sstevel 	uint32_t		*pci_intr_enable_reg;
347*03831d35Sstevel 	int			rc = DDI_SUCCESS;
348*03831d35Sstevel 
349*03831d35Sstevel 	instance = ddi_get_instance(devi);
350*03831d35Sstevel 
351*03831d35Sstevel 	if (!(softsp = ddi_get_soft_state(sbbcp, instance)))
352*03831d35Sstevel 		return (DDI_FAILURE);
353*03831d35Sstevel 
354*03831d35Sstevel 	switch (cmd) {
355*03831d35Sstevel 	case DDI_DETACH:
356*03831d35Sstevel 		mutex_enter(&chosen_lock);
357*03831d35Sstevel 		softsp->sbbc_state |= SBBC_STATE_DETACH;
358*03831d35Sstevel 		mutex_exit(&chosen_lock);
359*03831d35Sstevel 
360*03831d35Sstevel 		/* only tunnel switch the instance with iosram chosen */
361*03831d35Sstevel 		if (softsp->chosen == TRUE) {
362*03831d35Sstevel 			if (sgsbbc_iosram_switchfrom(softsp) == DDI_FAILURE) {
363*03831d35Sstevel 				SBBC_ERR(CE_WARN, "Cannot unconfigure: "
364*03831d35Sstevel 				    "tunnel switch failed\n");
365*03831d35Sstevel 				return (DDI_FAILURE);
366*03831d35Sstevel 			}
367*03831d35Sstevel 		}
368*03831d35Sstevel 
369*03831d35Sstevel 		/* Adjust linked list */
370*03831d35Sstevel 		mutex_enter(&chosen_lock);
371*03831d35Sstevel 		sbbc_remove_instance(softsp);
372*03831d35Sstevel 		mutex_exit(&chosen_lock);
373*03831d35Sstevel 
374*03831d35Sstevel 		sbbc_unmap_regs(softsp);
375*03831d35Sstevel 		mutex_destroy(&softsp->sbbc_lock);
376*03831d35Sstevel 		ddi_soft_state_free(sbbcp, instance);
377*03831d35Sstevel 
378*03831d35Sstevel 		return (DDI_SUCCESS);
379*03831d35Sstevel 
380*03831d35Sstevel 	case DDI_SUSPEND:
381*03831d35Sstevel 
382*03831d35Sstevel 		mutex_enter(&softsp->sbbc_lock);
383*03831d35Sstevel 
384*03831d35Sstevel 		if ((softsp->suspended == FALSE) && (softsp->chosen == TRUE)) {
385*03831d35Sstevel 			uint32_t	tmp_intr_enabled = 0;
386*03831d35Sstevel 
387*03831d35Sstevel 			/*
388*03831d35Sstevel 			 * Disable Interrupts now, turn OFF both INT#A lines
389*03831d35Sstevel 			 */
390*03831d35Sstevel 			pci_intr_enable_reg =  (uint32_t *)
391*03831d35Sstevel 						((char *)softsp->sbbc_regs +
392*03831d35Sstevel 							SBBC_PCI_INT_ENABLE);
393*03831d35Sstevel 
394*03831d35Sstevel 			ddi_put32(softsp->sbbc_reg_handle1,
395*03831d35Sstevel 				pci_intr_enable_reg, 0);
396*03831d35Sstevel 
397*03831d35Sstevel 			/*
398*03831d35Sstevel 			 * Set intr_in_enabled to 0 so the SC won't send
399*03831d35Sstevel 			 * us interrupt.
400*03831d35Sstevel 			 */
401*03831d35Sstevel 			rc = iosram_read(SBBC_SC_INTR_ENABLED_KEY,
402*03831d35Sstevel 				0, (caddr_t)&intr_in_enabled,
403*03831d35Sstevel 				sizeof (intr_in_enabled));
404*03831d35Sstevel 
405*03831d35Sstevel 			if (rc) {
406*03831d35Sstevel 				mutex_exit(&softsp->sbbc_lock);
407*03831d35Sstevel 				return (DDI_FAILURE);
408*03831d35Sstevel 			}
409*03831d35Sstevel 
410*03831d35Sstevel 			rc = iosram_write(SBBC_SC_INTR_ENABLED_KEY,
411*03831d35Sstevel 				0, (caddr_t)&tmp_intr_enabled,
412*03831d35Sstevel 				sizeof (tmp_intr_enabled));
413*03831d35Sstevel 
414*03831d35Sstevel 			if (rc) {
415*03831d35Sstevel 				mutex_exit(&softsp->sbbc_lock);
416*03831d35Sstevel 				return (DDI_FAILURE);
417*03831d35Sstevel 			}
418*03831d35Sstevel 		}
419*03831d35Sstevel 		softsp->suspended = TRUE;
420*03831d35Sstevel 
421*03831d35Sstevel 		mutex_exit(&softsp->sbbc_lock);
422*03831d35Sstevel 
423*03831d35Sstevel 		return (DDI_SUCCESS);
424*03831d35Sstevel 
425*03831d35Sstevel 	default:
426*03831d35Sstevel 		return (DDI_FAILURE);
427*03831d35Sstevel 	}
428*03831d35Sstevel 
429*03831d35Sstevel }
430*03831d35Sstevel 
431*03831d35Sstevel static void
432*03831d35Sstevel softsp_init(sbbc_softstate_t *softsp, dev_info_t *devi)
433*03831d35Sstevel {
434*03831d35Sstevel 	softsp->dip = devi;
435*03831d35Sstevel 
436*03831d35Sstevel 	/*
437*03831d35Sstevel 	 * XXXX
438*03831d35Sstevel 	 * ddi_get_iblock_cookie() here because we need
439*03831d35Sstevel 	 * to initialise the mutex regardless of whether
440*03831d35Sstevel 	 * or not this SBBC will eventually
441*03831d35Sstevel 	 * register an interrupt handler
442*03831d35Sstevel 	 */
443*03831d35Sstevel 
444*03831d35Sstevel 	(void) ddi_get_iblock_cookie(devi, 0, &softsp->iblock);
445*03831d35Sstevel 
446*03831d35Sstevel 	mutex_init(&softsp->sbbc_lock, NULL, MUTEX_DRIVER,
447*03831d35Sstevel 		(void *)softsp->iblock);
448*03831d35Sstevel 
449*03831d35Sstevel 	softsp->suspended = FALSE;
450*03831d35Sstevel 	softsp->chosen = FALSE;
451*03831d35Sstevel }
452*03831d35Sstevel 
453*03831d35Sstevel static int
454*03831d35Sstevel sbbc_find_dip(dev_info_t *dip, void *arg)
455*03831d35Sstevel {
456*03831d35Sstevel 	char		*node_name;
457*03831d35Sstevel 	sbbc_find_dip_t	*dip_struct = (sbbc_find_dip_t *)arg;
458*03831d35Sstevel 	char		status[OBP_MAXPROPNAME];
459*03831d35Sstevel 
460*03831d35Sstevel 	/*
461*03831d35Sstevel 	 * Need to find a node named "bootbus-controller" that is neither
462*03831d35Sstevel 	 * disabled nor failed.  If a node is not ok, there will be an
463*03831d35Sstevel 	 * OBP status property.  Therefore, we will look for a node
464*03831d35Sstevel 	 * without the status property.
465*03831d35Sstevel 	 */
466*03831d35Sstevel 	node_name = ddi_node_name(dip);
467*03831d35Sstevel 	if (strcmp(node_name, "bootbus-controller") == 0 && DDI_CF2(dip) &&
468*03831d35Sstevel 		(prom_getprop(ddi_get_nodeid(dip),
469*03831d35Sstevel 		"status", (caddr_t)status) == -1) &&
470*03831d35Sstevel 		(prom_getprop(ddi_get_nodeid(ddi_get_parent(dip)),
471*03831d35Sstevel 		"status", (caddr_t)status) == -1)) {
472*03831d35Sstevel 
473*03831d35Sstevel 		if (dip != dip_struct->cur_dip) {
474*03831d35Sstevel 			dip_struct->new_dip = (void *)dip;
475*03831d35Sstevel 			return (DDI_WALK_TERMINATE);
476*03831d35Sstevel 		}
477*03831d35Sstevel 	}
478*03831d35Sstevel 
479*03831d35Sstevel 	return (DDI_WALK_CONTINUE);
480*03831d35Sstevel }
481*03831d35Sstevel 
482*03831d35Sstevel /*
483*03831d35Sstevel  * SBBC Interrupt Handler
484*03831d35Sstevel  *
485*03831d35Sstevel  * Check the SBBC Port Interrupt Status
486*03831d35Sstevel  * register to verify that its our interrupt.
487*03831d35Sstevel  * If yes, clear the register.
488*03831d35Sstevel  *
489*03831d35Sstevel  * Then read the 'interrupt reason' field from SRAM,
490*03831d35Sstevel  * this triggers the appropriate soft_intr handler
491*03831d35Sstevel  */
492*03831d35Sstevel uint_t
493*03831d35Sstevel sbbc_intr_handler(caddr_t arg)
494*03831d35Sstevel {
495*03831d35Sstevel 	sbbc_softstate_t	*softsp = (sbbc_softstate_t *)arg;
496*03831d35Sstevel 	uint32_t		*port_int_reg;
497*03831d35Sstevel 	volatile uint32_t	port_int_status;
498*03831d35Sstevel 	volatile uint32_t	intr_reason;
499*03831d35Sstevel 	uint32_t		intr_enabled;
500*03831d35Sstevel 	sbbc_intrs_t		*intr;
501*03831d35Sstevel 	int			i, intr_mask;
502*03831d35Sstevel 	struct tunnel_key	tunnel_key;
503*03831d35Sstevel 	ddi_acc_handle_t	intr_in_handle;
504*03831d35Sstevel 	uint32_t		*intr_in_reason;
505*03831d35Sstevel 
506*03831d35Sstevel 	if (softsp == (sbbc_softstate_t *)NULL) {
507*03831d35Sstevel 
508*03831d35Sstevel 		return (DDI_INTR_UNCLAIMED);
509*03831d35Sstevel 	}
510*03831d35Sstevel 
511*03831d35Sstevel 	mutex_enter(&softsp->sbbc_lock);
512*03831d35Sstevel 
513*03831d35Sstevel 	if (softsp->port_int_regs == NULL) {
514*03831d35Sstevel 		mutex_exit(&softsp->sbbc_lock);
515*03831d35Sstevel 		return (DDI_INTR_UNCLAIMED);
516*03831d35Sstevel 	}
517*03831d35Sstevel 
518*03831d35Sstevel 	/*
519*03831d35Sstevel 	 * Normally if port_int_status is 0, we assume it is not
520*03831d35Sstevel 	 * our interrupt.  However, we don't want to miss the
521*03831d35Sstevel 	 * ones that come in during tunnel switch.  Therefore,
522*03831d35Sstevel 	 * we always check the interrupt reason bits in IOSRAM
523*03831d35Sstevel 	 * to be sure.
524*03831d35Sstevel 	 */
525*03831d35Sstevel 	port_int_reg = softsp->port_int_regs;
526*03831d35Sstevel 
527*03831d35Sstevel 	port_int_status = ddi_get32(softsp->sbbc_reg_handle1, port_int_reg);
528*03831d35Sstevel 
529*03831d35Sstevel 	/*
530*03831d35Sstevel 	 * Generate a softint for each interrupt
531*03831d35Sstevel 	 * bit set in the intr_in_reason field in SRAM
532*03831d35Sstevel 	 * that has a corresponding bit set in the
533*03831d35Sstevel 	 * intr_in_enabled field in SRAM
534*03831d35Sstevel 	 */
535*03831d35Sstevel 
536*03831d35Sstevel 	if (iosram_read(SBBC_SC_INTR_ENABLED_KEY, 0,
537*03831d35Sstevel 		(caddr_t)&intr_enabled, sizeof (intr_enabled))) {
538*03831d35Sstevel 
539*03831d35Sstevel 		goto intr_handler_exit;
540*03831d35Sstevel 	}
541*03831d35Sstevel 
542*03831d35Sstevel 	tunnel_key = master_iosram->tunnel->tunnel_keys[SBBC_SC_INTR_KEY];
543*03831d35Sstevel 	intr_in_reason = (uint32_t *)tunnel_key.base;
544*03831d35Sstevel 	intr_in_handle = tunnel_key.reg_handle;
545*03831d35Sstevel 
546*03831d35Sstevel 	intr_reason = ddi_get32(intr_in_handle, intr_in_reason);
547*03831d35Sstevel 
548*03831d35Sstevel 	SGSBBC_DBG_INTR(CE_CONT, "intr_reason = %x\n", intr_reason);
549*03831d35Sstevel 
550*03831d35Sstevel 	intr_reason &= intr_enabled;
551*03831d35Sstevel 
552*03831d35Sstevel 	for (i = 0; i < SBBC_MAX_INTRS; i++) {
553*03831d35Sstevel 		intr_mask = (1 << i);
554*03831d35Sstevel 		if (intr_reason & intr_mask) {
555*03831d35Sstevel 			intr = &softsp->intr_hdlrs[i];
556*03831d35Sstevel 			if ((intr != NULL) &&
557*03831d35Sstevel 				(intr->sbbc_intr_id != 0)) {
558*03831d35Sstevel 				/*
559*03831d35Sstevel 				 * XXXX
560*03831d35Sstevel 				 * The model we agree with a handler
561*03831d35Sstevel 				 * is that they run until they have
562*03831d35Sstevel 				 * exhausted all work. To avoid
563*03831d35Sstevel 				 * triggering them again, they pass
564*03831d35Sstevel 				 * a state flag and lock when registering.
565*03831d35Sstevel 				 * We check the flag, if they are idle,
566*03831d35Sstevel 				 * we trigger.
567*03831d35Sstevel 				 * The interrupt handler should so
568*03831d35Sstevel 				 *   intr_func()
569*03831d35Sstevel 				 *	mutex_enter(sbbc_intr_lock);
570*03831d35Sstevel 				 *	sbbc_intr_state = RUNNING;
571*03831d35Sstevel 				 *	mutex_exit(sbbc_intr_lock);
572*03831d35Sstevel 				 *	  ..........
573*03831d35Sstevel 				 *	  ..........
574*03831d35Sstevel 				 *	  ..........
575*03831d35Sstevel 				 *	mutex_enter(sbbc_intr_lock);
576*03831d35Sstevel 				 *	sbbc_intr_state = IDLE;
577*03831d35Sstevel 				 *	mutex_exit(sbbc_intr_lock);
578*03831d35Sstevel 				 *
579*03831d35Sstevel 				 * XXXX
580*03831d35Sstevel 				 */
581*03831d35Sstevel 				mutex_enter(intr->sbbc_intr_lock);
582*03831d35Sstevel 				if (*(intr->sbbc_intr_state) ==
583*03831d35Sstevel 					SBBC_INTR_IDLE) {
584*03831d35Sstevel 					mutex_exit(intr->sbbc_intr_lock);
585*03831d35Sstevel 					ddi_trigger_softintr(
586*03831d35Sstevel 						intr->sbbc_intr_id);
587*03831d35Sstevel 				} else {
588*03831d35Sstevel 					/*
589*03831d35Sstevel 					 * The handler is running
590*03831d35Sstevel 					 */
591*03831d35Sstevel 					mutex_exit(intr->sbbc_intr_lock);
592*03831d35Sstevel 				}
593*03831d35Sstevel 				intr_reason &= ~intr_mask;
594*03831d35Sstevel 				/*
595*03831d35Sstevel 				 * Clear the corresponding reason bit in SRAM
596*03831d35Sstevel 				 *
597*03831d35Sstevel 				 * Since there is no interlocking between
598*03831d35Sstevel 				 * Solaris and the SC when writing to SRAM,
599*03831d35Sstevel 				 * it is possible for the SC to set another
600*03831d35Sstevel 				 * bit in the interrupt reason field while
601*03831d35Sstevel 				 * we are handling the current interrupt.
602*03831d35Sstevel 				 * To minimize the window in which an
603*03831d35Sstevel 				 * additional bit can be set, reading
604*03831d35Sstevel 				 * and writing the interrupt reason
605*03831d35Sstevel 				 * in SRAM must be as close as possible.
606*03831d35Sstevel 				 */
607*03831d35Sstevel 				ddi_put32(intr_in_handle, intr_in_reason,
608*03831d35Sstevel 					ddi_get32(intr_in_handle,
609*03831d35Sstevel 					intr_in_reason) & ~intr_mask);
610*03831d35Sstevel 			}
611*03831d35Sstevel 		}
612*03831d35Sstevel 		if (intr_reason == 0)	/* No more interrupts to be processed */
613*03831d35Sstevel 			break;
614*03831d35Sstevel 	}
615*03831d35Sstevel 
616*03831d35Sstevel 	/*
617*03831d35Sstevel 	 * Clear the Interrupt Status Register (RW1C)
618*03831d35Sstevel 	 */
619*03831d35Sstevel 	ddi_put32(softsp->sbbc_reg_handle1, port_int_reg, port_int_status);
620*03831d35Sstevel 
621*03831d35Sstevel 	port_int_status = ddi_get32(softsp->sbbc_reg_handle1, port_int_reg);
622*03831d35Sstevel 
623*03831d35Sstevel intr_handler_exit:
624*03831d35Sstevel 
625*03831d35Sstevel 	mutex_exit(&softsp->sbbc_lock);
626*03831d35Sstevel 
627*03831d35Sstevel 	return (DDI_INTR_CLAIMED);
628*03831d35Sstevel 
629*03831d35Sstevel }
630*03831d35Sstevel 
631*03831d35Sstevel /*
632*03831d35Sstevel  * If we don't already have a master SBBC selected,
633*03831d35Sstevel  * get the <sbbc> property from the /chosen node. If
634*03831d35Sstevel  * the pathname matches, this is the master SBBC and
635*03831d35Sstevel  * we set up the console/TOD SRAM mapping here.
636*03831d35Sstevel  */
637*03831d35Sstevel static void
638*03831d35Sstevel sbbc_chosen_init(sbbc_softstate_t *softsp)
639*03831d35Sstevel {
640*03831d35Sstevel 	char		master_sbbc[MAXNAMELEN];
641*03831d35Sstevel 	char		pn[MAXNAMELEN];
642*03831d35Sstevel 	int		nodeid, len;
643*03831d35Sstevel 	pnode_t		dnode;
644*03831d35Sstevel 
645*03831d35Sstevel 	if (master_chosen != FALSE) {
646*03831d35Sstevel 		/*
647*03831d35Sstevel 		 * We've got one already
648*03831d35Sstevel 		 */
649*03831d35Sstevel 		return;
650*03831d35Sstevel 	}
651*03831d35Sstevel 
652*03831d35Sstevel 	/*
653*03831d35Sstevel 	 * Get /chosen node info. prom interface will handle errors.
654*03831d35Sstevel 	 */
655*03831d35Sstevel 	dnode = prom_chosennode();
656*03831d35Sstevel 
657*03831d35Sstevel 	/*
658*03831d35Sstevel 	 * Look for the "iosram" property on the chosen node with a prom
659*03831d35Sstevel 	 * interface as ddi_find_devinfo() couldn't be used (calls
660*03831d35Sstevel 	 * ddi_walk_devs() that creates one extra lock on the device tree).
661*03831d35Sstevel 	 */
662*03831d35Sstevel 	if (prom_getprop(dnode, IOSRAM_CHOSEN_PROP, (caddr_t)&nodeid) <= 0) {
663*03831d35Sstevel 		/*
664*03831d35Sstevel 		 * No I/O Board SBBC set up as console, what to do ?
665*03831d35Sstevel 		 */
666*03831d35Sstevel 		SBBC_ERR(CE_PANIC, "No SBBC found for Console/TOD \n");
667*03831d35Sstevel 	}
668*03831d35Sstevel 
669*03831d35Sstevel 	if (prom_getprop(dnode, IOSRAM_TOC_PROP,
670*03831d35Sstevel 	    (caddr_t)&softsp->sram_toc) <= 0) {
671*03831d35Sstevel 		/*
672*03831d35Sstevel 		 * SRAM TOC Offset defaults to 0
673*03831d35Sstevel 		 */
674*03831d35Sstevel 		SBBC_ERR(CE_WARN, "No SBBC TOC Offset found\n");
675*03831d35Sstevel 		softsp->sram_toc = 0;
676*03831d35Sstevel 	}
677*03831d35Sstevel 
678*03831d35Sstevel 	/*
679*03831d35Sstevel 	 * get the full OBP pathname of this node
680*03831d35Sstevel 	 */
681*03831d35Sstevel 	if (prom_phandle_to_path((phandle_t)nodeid, master_sbbc,
682*03831d35Sstevel 		sizeof (master_sbbc)) < 0) {
683*03831d35Sstevel 
684*03831d35Sstevel 		SBBC_ERR1(CE_PANIC, "prom_phandle_to_path(%d) failed\n",
685*03831d35Sstevel 		    nodeid);
686*03831d35Sstevel 	}
687*03831d35Sstevel 	SGSBBC_DBG_ALL("chosen pathname : %s\n", master_sbbc);
688*03831d35Sstevel 	SGSBBC_DBG_ALL("device pathname : %s\n", ddi_pathname(softsp->dip, pn));
689*03831d35Sstevel 	if (strcmp(master_sbbc, ddi_pathname(softsp->dip, pn)) == 0) {
690*03831d35Sstevel 
691*03831d35Sstevel 		/*
692*03831d35Sstevel 		 * map in the SBBC regs
693*03831d35Sstevel 		 */
694*03831d35Sstevel 
695*03831d35Sstevel 		if (sbbc_map_regs(softsp) != DDI_SUCCESS) {
696*03831d35Sstevel 			SBBC_ERR(CE_PANIC, "Can't map the SBBC regs \n");
697*03831d35Sstevel 		}
698*03831d35Sstevel 		/*
699*03831d35Sstevel 		 * Only the 'chosen' node is used for iosram_read()/_write()
700*03831d35Sstevel 		 * Must initialise the tunnel before the console/tod
701*03831d35Sstevel 		 *
702*03831d35Sstevel 		 */
703*03831d35Sstevel 		if (iosram_tunnel_init(softsp) == DDI_FAILURE) {
704*03831d35Sstevel 			SBBC_ERR(CE_PANIC, "Can't create the SRAM <-> SC "
705*03831d35Sstevel 				"comm. tunnel \n");
706*03831d35Sstevel 		}
707*03831d35Sstevel 
708*03831d35Sstevel 		master_chosen = TRUE;
709*03831d35Sstevel 
710*03831d35Sstevel 		/*
711*03831d35Sstevel 		 * Verify that an 'interrupts' property
712*03831d35Sstevel 		 * exists for this device
713*03831d35Sstevel 		 */
714*03831d35Sstevel 
715*03831d35Sstevel 		if (ddi_getproplen(DDI_DEV_T_ANY, softsp->dip,
716*03831d35Sstevel 			DDI_PROP_DONTPASS, "interrupts",
717*03831d35Sstevel 			&len) != DDI_PROP_SUCCESS) {
718*03831d35Sstevel 
719*03831d35Sstevel 			SBBC_ERR(CE_PANIC, "No 'interrupts' property for the "
720*03831d35Sstevel 					"'chosen' SBBC \n");
721*03831d35Sstevel 		}
722*03831d35Sstevel 
723*03831d35Sstevel 		/*
724*03831d35Sstevel 		 * add the interrupt handler
725*03831d35Sstevel 		 * NB
726*03831d35Sstevel 		 * should this be a high-level interrupt ?
727*03831d35Sstevel 		 * NB
728*03831d35Sstevel 		 */
729*03831d35Sstevel 		if (sbbc_add_intr(softsp) == DDI_FAILURE) {
730*03831d35Sstevel 			SBBC_ERR(CE_PANIC, "Can't add interrupt handler for "
731*03831d35Sstevel 					"'chosen' SBBC \n");
732*03831d35Sstevel 		}
733*03831d35Sstevel 
734*03831d35Sstevel 		sbbc_enable_intr(softsp);
735*03831d35Sstevel 
736*03831d35Sstevel 		/*
737*03831d35Sstevel 		 * Create the mailbox
738*03831d35Sstevel 		 */
739*03831d35Sstevel 		if (sbbc_mbox_create(softsp) != 0) {
740*03831d35Sstevel 			cmn_err(CE_WARN, "No IOSRAM MailBox created!\n");
741*03831d35Sstevel 		}
742*03831d35Sstevel 
743*03831d35Sstevel 	}
744*03831d35Sstevel }
745*03831d35Sstevel /*
746*03831d35Sstevel  * sbbc_add_instance
747*03831d35Sstevel  * Must be called to hold chosen_lock.
748*03831d35Sstevel  */
749*03831d35Sstevel static void
750*03831d35Sstevel sbbc_add_instance(sbbc_softstate_t *softsp)
751*03831d35Sstevel {
752*03831d35Sstevel #ifdef DEBUG
753*03831d35Sstevel 	struct  sbbc_softstate *sp;
754*03831d35Sstevel #endif
755*03831d35Sstevel 
756*03831d35Sstevel 	ASSERT(mutex_owned(&chosen_lock));
757*03831d35Sstevel 
758*03831d35Sstevel #if defined(DEBUG)
759*03831d35Sstevel 	/* Verify that this instance is not in the list yet */
760*03831d35Sstevel 	for (sp = sgsbbc_instances; sp != NULL; sp = sp->next) {
761*03831d35Sstevel 		ASSERT(sp != softsp);
762*03831d35Sstevel 	}
763*03831d35Sstevel #endif
764*03831d35Sstevel 
765*03831d35Sstevel 	/*
766*03831d35Sstevel 	 * Add this instance to the front of the list.
767*03831d35Sstevel 	 */
768*03831d35Sstevel 	if (sgsbbc_instances != NULL) {
769*03831d35Sstevel 		sgsbbc_instances->prev = softsp;
770*03831d35Sstevel 	}
771*03831d35Sstevel 
772*03831d35Sstevel 	softsp->next = sgsbbc_instances;
773*03831d35Sstevel 	softsp->prev = NULL;
774*03831d35Sstevel 	sgsbbc_instances = softsp;
775*03831d35Sstevel }
776*03831d35Sstevel 
777*03831d35Sstevel static void
778*03831d35Sstevel sbbc_remove_instance(sbbc_softstate_t *softsp)
779*03831d35Sstevel {
780*03831d35Sstevel 	struct sbbc_softstate *sp;
781*03831d35Sstevel 
782*03831d35Sstevel 	for (sp = sgsbbc_instances; sp != NULL; sp = sp->next) {
783*03831d35Sstevel 		if (sp == softsp) {
784*03831d35Sstevel 			if (sp->next != NULL) {
785*03831d35Sstevel 				sp->next->prev = sp->prev;
786*03831d35Sstevel 			}
787*03831d35Sstevel 			if (sp->prev != NULL) {
788*03831d35Sstevel 				sp->prev->next = sp->next;
789*03831d35Sstevel 			}
790*03831d35Sstevel 			if (sgsbbc_instances == softsp) {
791*03831d35Sstevel 				sgsbbc_instances = sp->next;
792*03831d35Sstevel 			}
793*03831d35Sstevel 			break;
794*03831d35Sstevel 		}
795*03831d35Sstevel 	}
796*03831d35Sstevel }
797*03831d35Sstevel 
798*03831d35Sstevel /*
799*03831d35Sstevel  * Generate an SBBC interrupt to the SC
800*03831d35Sstevel  * Called from iosram_send_intr()
801*03831d35Sstevel  *
802*03831d35Sstevel  * send_intr == 0, check if EPLD register clear
803*03831d35Sstevel  *	           for sync'ing SC/OS
804*03831d35Sstevel  * send_intr == 1, send the interrupt
805*03831d35Sstevel  */
806*03831d35Sstevel int
807*03831d35Sstevel sbbc_send_intr(sbbc_softstate_t *softsp, int send_intr)
808*03831d35Sstevel {
809*03831d35Sstevel 
810*03831d35Sstevel 	uchar_t			*epld_int;
811*03831d35Sstevel 	volatile uchar_t 	epld_status;
812*03831d35Sstevel 
813*03831d35Sstevel 	ASSERT(MUTEX_HELD(&master_iosram->iosram_lock));
814*03831d35Sstevel 
815*03831d35Sstevel 	if ((softsp == (sbbc_softstate_t *)NULL) ||
816*03831d35Sstevel 		(softsp->epld_regs == (struct sbbc_epld_regs *)NULL))
817*03831d35Sstevel 		return (ENXIO);
818*03831d35Sstevel 
819*03831d35Sstevel 	/*
820*03831d35Sstevel 	 * Check the L1 EPLD Interrupt register. If the
821*03831d35Sstevel 	 * interrupt bit is set, theres an interrupt outstanding
822*03831d35Sstevel 	 * (we assume) so return (EBUSY).
823*03831d35Sstevel 	 */
824*03831d35Sstevel 
825*03831d35Sstevel 	epld_int = &softsp->epld_regs->epld_reg[EPLD_INTERRUPT];
826*03831d35Sstevel 
827*03831d35Sstevel 	epld_status = ddi_get8(softsp->sbbc_reg_handle2, epld_int);
828*03831d35Sstevel 
829*03831d35Sstevel 	if (epld_status & INTERRUPT_ON)
830*03831d35Sstevel 		return (EBUSY);
831*03831d35Sstevel 
832*03831d35Sstevel 	if (send_intr == TRUE)
833*03831d35Sstevel 		ddi_put8(softsp->sbbc_reg_handle2, epld_int,
834*03831d35Sstevel 			(epld_status | INTERRUPT_ON));
835*03831d35Sstevel 
836*03831d35Sstevel 	return (0);
837*03831d35Sstevel }
838*03831d35Sstevel 
839*03831d35Sstevel /*
840*03831d35Sstevel  * Map SBBC Internal registers
841*03831d35Sstevel  *
842*03831d35Sstevel  * The call to function should be protected by
843*03831d35Sstevel  * chosen_lock or master_iosram->iosram_lock
844*03831d35Sstevel  * to make sure a tunnel switch will not occur
845*03831d35Sstevel  * in a middle of mapping.
846*03831d35Sstevel  */
847*03831d35Sstevel int
848*03831d35Sstevel sbbc_map_regs(sbbc_softstate_t *softsp)
849*03831d35Sstevel {
850*03831d35Sstevel 	struct ddi_device_acc_attr attr;
851*03831d35Sstevel 
852*03831d35Sstevel 	attr.devacc_attr_version = DDI_DEVICE_ATTR_V0;
853*03831d35Sstevel 	attr.devacc_attr_endian_flags = DDI_NEVERSWAP_ACC;
854*03831d35Sstevel 	attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC;
855*03831d35Sstevel 
856*03831d35Sstevel 	/*
857*03831d35Sstevel 	 * Map in register set 1, Common Device Regs
858*03831d35Sstevel 	 * SBCC offset 0x0
859*03831d35Sstevel 	 */
860*03831d35Sstevel 	if (ddi_regs_map_setup(softsp->dip, RNUM_SBBC_REGS,
861*03831d35Sstevel 		(caddr_t *)&softsp->sbbc_regs,
862*03831d35Sstevel 		SBBC_REGS_OFFSET, SBBC_REGS_SIZE,
863*03831d35Sstevel 		&attr, &softsp->sbbc_reg_handle1) != DDI_SUCCESS) {
864*03831d35Sstevel 
865*03831d35Sstevel 		cmn_err(CE_WARN, "sbbc%d: unable to map interrupt "
866*03831d35Sstevel 			"registers", ddi_get_instance(softsp->dip));
867*03831d35Sstevel 		return (DDI_FAILURE);
868*03831d35Sstevel 	}
869*03831d35Sstevel 	/*
870*03831d35Sstevel 	 * Map in using register set 1, EPLD
871*03831d35Sstevel 	 * SBCC offset 0xe000
872*03831d35Sstevel 	 */
873*03831d35Sstevel 	if (ddi_regs_map_setup(softsp->dip, RNUM_SBBC_REGS,
874*03831d35Sstevel 		(caddr_t *)&softsp->epld_regs,
875*03831d35Sstevel 		SBBC_EPLD_OFFSET, SBBC_EPLD_SIZE,
876*03831d35Sstevel 		&attr, &softsp->sbbc_reg_handle2) != DDI_SUCCESS) {
877*03831d35Sstevel 
878*03831d35Sstevel 		cmn_err(CE_WARN, "sbbc%d: unable to map EPLD "
879*03831d35Sstevel 			"registers", ddi_get_instance(softsp->dip));
880*03831d35Sstevel 		return (DDI_FAILURE);
881*03831d35Sstevel 	}
882*03831d35Sstevel 
883*03831d35Sstevel 	/*
884*03831d35Sstevel 	 * Set up pointers for registers
885*03831d35Sstevel 	 */
886*03831d35Sstevel 	softsp->port_int_regs =  (uint32_t *)((char *)softsp->sbbc_regs +
887*03831d35Sstevel 		SBBC_PCI_INT_STATUS);
888*03831d35Sstevel 
889*03831d35Sstevel map_regs_exit:
890*03831d35Sstevel 	return (DDI_SUCCESS);
891*03831d35Sstevel }
892*03831d35Sstevel 
893*03831d35Sstevel 
894*03831d35Sstevel /*
895*03831d35Sstevel  * Unmap SBBC Internal registers
896*03831d35Sstevel  */
897*03831d35Sstevel static void
898*03831d35Sstevel sbbc_unmap_regs(sbbc_softstate_t *softsp)
899*03831d35Sstevel {
900*03831d35Sstevel 	if (softsp == NULL)
901*03831d35Sstevel 		return;
902*03831d35Sstevel 
903*03831d35Sstevel 	mutex_enter(&master_iosram->iosram_lock);
904*03831d35Sstevel 
905*03831d35Sstevel 	if (softsp->sbbc_regs) {
906*03831d35Sstevel 		ddi_regs_map_free(&softsp->sbbc_reg_handle1);
907*03831d35Sstevel 		softsp->sbbc_regs = NULL;
908*03831d35Sstevel 		softsp->port_int_regs = NULL;
909*03831d35Sstevel 	}
910*03831d35Sstevel 
911*03831d35Sstevel 	if (softsp->epld_regs) {
912*03831d35Sstevel 		ddi_regs_map_free(&softsp->sbbc_reg_handle2);
913*03831d35Sstevel 		softsp->epld_regs = NULL;
914*03831d35Sstevel 	}
915*03831d35Sstevel 
916*03831d35Sstevel 	mutex_exit(&master_iosram->iosram_lock);
917*03831d35Sstevel 
918*03831d35Sstevel 	return;
919*03831d35Sstevel 
920*03831d35Sstevel }
921*03831d35Sstevel /*
922*03831d35Sstevel  * This is here to allow the IOSRAM driver get the softstate
923*03831d35Sstevel  * for a chosen node when doing a tunnel switch. Just enables
924*03831d35Sstevel  * us to avoid exporting the sbbcp softstate hook
925*03831d35Sstevel  */
926*03831d35Sstevel sbbc_softstate_t *
927*03831d35Sstevel sbbc_get_soft_state(int instance)
928*03831d35Sstevel {
929*03831d35Sstevel 	return (ddi_get_soft_state(sbbcp, instance));
930*03831d35Sstevel }
931*03831d35Sstevel 
932*03831d35Sstevel /*
933*03831d35Sstevel  * Add interrupt handlers
934*03831d35Sstevel  */
935*03831d35Sstevel int
936*03831d35Sstevel sbbc_add_intr(sbbc_softstate_t *softsp)
937*03831d35Sstevel {
938*03831d35Sstevel 	int		rc = DDI_SUCCESS;
939*03831d35Sstevel 
940*03831d35Sstevel 	/*
941*03831d35Sstevel 	 * map in the SBBC interrupts
942*03831d35Sstevel 	 * Note that the iblock_cookie was initialised
943*03831d35Sstevel 	 * in the 'attach' routine
944*03831d35Sstevel 	 */
945*03831d35Sstevel 
946*03831d35Sstevel 	if (ddi_add_intr(softsp->dip, 0, &softsp->iblock,
947*03831d35Sstevel 		&softsp->idevice, sbbc_intr_handler,
948*03831d35Sstevel 		(caddr_t)softsp) != DDI_SUCCESS) {
949*03831d35Sstevel 
950*03831d35Sstevel 		cmn_err(CE_WARN, "Can't register SBBC "
951*03831d35Sstevel 			" interrupt handler\n");
952*03831d35Sstevel 		rc = DDI_FAILURE;
953*03831d35Sstevel 	}
954*03831d35Sstevel 
955*03831d35Sstevel 	return (rc);
956*03831d35Sstevel }
957*03831d35Sstevel 
958*03831d35Sstevel void
959*03831d35Sstevel sbbc_enable_intr(sbbc_softstate_t *softsp)
960*03831d35Sstevel {
961*03831d35Sstevel 	uint32_t	*pci_intr_enable_reg;
962*03831d35Sstevel 
963*03831d35Sstevel 	/*
964*03831d35Sstevel 	 * Enable Interrupts now, turn on both INT#A lines
965*03831d35Sstevel 	 */
966*03831d35Sstevel 	pci_intr_enable_reg =  (uint32_t *)((char *)softsp->sbbc_regs +
967*03831d35Sstevel 		SBBC_PCI_INT_ENABLE);
968*03831d35Sstevel 	ddi_put32(softsp->sbbc_reg_handle1, pci_intr_enable_reg,
969*03831d35Sstevel 		(uint32_t)SBBC_PCI_ENABLE_INT_A);
970*03831d35Sstevel }
971*03831d35Sstevel 
972*03831d35Sstevel void
973*03831d35Sstevel sbbc_disable_intr(sbbc_softstate_t *softsp)
974*03831d35Sstevel {
975*03831d35Sstevel 	uint32_t	*pci_intr_enable_reg;
976*03831d35Sstevel 
977*03831d35Sstevel 	/*
978*03831d35Sstevel 	 * Disable Interrupts now, turn off both INT#A lines
979*03831d35Sstevel 	 */
980*03831d35Sstevel 	pci_intr_enable_reg =  (uint32_t *)((char *)softsp->sbbc_regs +
981*03831d35Sstevel 		SBBC_PCI_INT_ENABLE);
982*03831d35Sstevel 	ddi_put32(softsp->sbbc_reg_handle1, pci_intr_enable_reg, 0);
983*03831d35Sstevel }
984