xref: /illumos-gate/usr/src/uts/sun4u/io/sbbc.c (revision a54f81fbf66e3e4c14a94d571c0fe241e4cf2394)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #pragma ident	"%Z%%M%	%I%	%E% SMI"
27 
28 /*
29  * Starcat PCI SBBC Nexus Driver.
30  *
31  * This source code's compiled binary runs on both a Starcat System
32  * Controller (SSC) and a Starcat Domain.  One of the SBBC hardware
33  * registers is read during attach(9e) in order to determine which
34  * environment the driver is executing on.
35  *
36  * On both the SSC and the Domain, this driver provides nexus driver
37  * services to its Device Tree children.  Note that the children in
38  * each environment are not necessarily the same.
39  *
40  * This driver allows one concurrent open(2) of its associated device
41  * (/dev/sbbc0).  The client uses the file descriptor to issue
42  * ioctl(2)'s in order to read and write from the 2MB (PCI) space
43  * reserved for "SBBC Internal Registers".  Among other things,
44  * these registers consist of command/control/status registers for
45  * devices such as Console Bus, I2C, EPLD, IOSRAM, and JTAG.  The 2MB
46  * space is very sparse; EINVAL is returned if a reserved or unaligned
47  * address is specified in the ioctl(2).
48  *
49  * Note that the 2MB region reserved for SBBC Internal Registers is
50  * a subset of the 128MB of PCI address space addressable by the SBBC
51  * ASIC.  Address space outside of the 2MB (such as the 64MB reserved
52  * for the Console Bus) is not accessible via this driver.
53  *
54  * Also, note that the SBBC Internal Registers are only read and
55  * written by the SSC; no process on the Domain accesses these
56  * registers.  As a result, the registers are unmapped (when running
57  * on the Domain) near the end of attach(9e) processing.  This conserves
58  * kernel virtual address space resources (as one instance of the driver
59  * is created for each Domain-side IO assembly).  (To be complete, only
60  * one instance of the driver is created on the SSC).
61  */
62 
63 #include <sys/types.h>
64 
65 #include <sys/conf.h>		/* req. by dev_ops flags MTSAFE etc. */
66 #include <sys/ddi.h>
67 #include <sys/sunddi.h>
68 #include <sys/ddi_impldefs.h>
69 #include <sys/ddi_subrdefs.h>
70 #include <sys/pci.h>
71 #include <sys/pci/pci_nexus.h>
72 #include <sys/autoconf.h>
73 #include <sys/cmn_err.h>
74 #include <sys/param.h>
75 #include <sys/errno.h>
76 #include <sys/kmem.h>
77 #include <sys/debug.h>
78 #include <sys/sysmacros.h>
79 #include <sys/machsystm.h>
80 #include <sys/modctl.h>
81 #include <sys/stat.h>
82 
83 
84 #include <sys/sbbcreg.h>	/* hw description */
85 #include <sys/sbbcvar.h>	/* driver description */
86 #include <sys/sbbcio.h>		/* ioctl description */
87 
88 #define	getprop(dip, name, addr, intp)		\
89 		ddi_getlongprop(DDI_DEV_T_ANY, (dip), DDI_PROP_DONTPASS, \
90 				(name), (caddr_t)(addr), (intp))
91 
92 /* driver entry point fn definitions */
93 static int sbbc_open(dev_t *, int, int, cred_t *);
94 static int sbbc_close(dev_t, int, int, cred_t *);
95 static int sbbc_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
96 
97 /* configuration entry point fn definitions */
98 static int sbbc_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **);
99 static int sbbc_attach(dev_info_t *, ddi_attach_cmd_t);
100 static int sbbc_detach(dev_info_t *, ddi_detach_cmd_t);
101 
102 /* local utility routines */
103 /*
104  * NOTE - sbbc_offset_valid contains detailed address information taken from
105  * the Serengeti Architecture Programmer's Reference Manual.  If any
106  * changes are made to the SBBC registers, this routine may need to be
107  * updated.
108  */
109 static int sbbc_offset_valid(uint32_t offset);
110 
111 /*
112  * function prototypes for bus ops routines:
113  */
114 static int sbbc_busmap(dev_info_t *dip, dev_info_t *rdip, ddi_map_req_t *mp,
115 	off_t offset, off_t len, caddr_t *addrp);
116 static int sbbc_ctlops(dev_info_t *dip, dev_info_t *rdip,
117 	ddi_ctl_enum_t op, void *arg, void *result);
118 
119 static int sbbc_intr_ops(dev_info_t *dip, dev_info_t *rdip,
120 	ddi_intr_op_t intr_op, ddi_intr_handle_impl_t *hdlp, void *result);
121 static int sbbc_add_intr_impl(dev_info_t *dip, dev_info_t *rdip,
122 	ddi_intr_op_t intr_op, ddi_intr_handle_impl_t *hdlp, void *result);
123 static int sbbc_remove_intr_impl(dev_info_t *dip, dev_info_t *rdip,
124 	ddi_intr_op_t intr_op, ddi_intr_handle_impl_t *hdlp, void *result);
125 static int sbbc_update_intr_state(dev_info_t *dip, dev_info_t *rdip,
126 	ddi_intr_op_t intr_op, ddi_intr_handle_impl_t *hdlp, void *result);
127 
128 static int sbbc_apply_range(struct sbbcsoft *sbbc_p, dev_info_t *rdip,
129     sbbc_child_regspec_t *child_rp, pci_regspec_t *rp);
130 
131 static int sbbc_init(struct sbbcsoft *);
132 
133 static uint_t sbbc_intr_wrapper(caddr_t arg);
134 
135 static int sbbc_get_ranges(struct sbbcsoft *);
136 static int sbbc_config4pci(struct sbbcsoft *);
137 static int sbbc_initchild(dev_info_t *, dev_info_t *, dev_info_t *);
138 static int sbbc_uninitchild(dev_info_t *, dev_info_t *);
139 static void sbbc_remove_reg_maps(struct sbbcsoft *);
140 
141 /* debugging functions */
142 #ifdef DEBUG
143 uint32_t sbbc_dbg_flags = 0x0;
144 static void sbbc_dbg(uint32_t flag, dev_info_t *dip, char *fmt,
145 	uintptr_t a1, uintptr_t a2, uintptr_t a3, uintptr_t a4, uintptr_t a5);
146 static void sbbc_dump_devid(dev_info_t *, struct sbbcsoft *, int instance);
147 #endif
148 
149 /*
150  * For tracing, allocate space for the trace buffer
151  */
152 #if defined(SBBC_TRACE)
153 struct sbbctrace sbbctrace_buffer[NSBBCTRACE+1];
154 struct sbbctrace *sbbctrace_ptr;
155 int sbbctrace_count;
156 #endif
157 
158 /*
159  * Local declarations and variables
160  */
161 
162 static void *sbbcsoft_statep;
163 
164 /* Determines whether driver is executing on System Controller or Domain */
165 int sbbc_scmode = FALSE;
166 
167 /*
168  * ops stuff.
169  */
170 static struct bus_ops sbbc_bus_ops = {
171 	BUSO_REV,
172 	sbbc_busmap,
173 	0,
174 	0,
175 	0,
176 	NULL, 			/* (*bus_map_fault)() */
177 	ddi_no_dma_map,
178 	ddi_no_dma_allochdl,
179 	ddi_no_dma_freehdl, 	/* (*bus_dma_freehdl)() */
180 	ddi_no_dma_bindhdl, 	/* (*bus_dma_bindhdl)() */
181 	ddi_no_dma_unbindhdl, 	/* (*bus_dma_unbindhdl)() */
182 	ddi_no_dma_flush, 	/* (*bus_dma_flush)() */
183 	ddi_no_dma_win, 	/* (*bus_dma_win)() */
184 	ddi_no_dma_mctl, 	/* (*bus_dma_ctl)() */
185 	sbbc_ctlops,
186 	ddi_bus_prop_op,
187 	0,			/* (*bus_get_eventcookie)();	*/
188 	0,			/* (*bus_add_eventcall)();	*/
189 	0,			/* (*bus_remove_eventcall)();	*/
190 	0,			/* (*bus_post_event)();		*/
191 	0,			/* (*bus_intr_ctl)();	*/
192 	0,			/* (*bus_config)();	*/
193 	0,			/* (*bus_unconfig)();	*/
194 	0,			/* (*bus_fm_init)();	*/
195 	0,			/* (*bus_fm_fini)();	*/
196 	0,			/* (*bus_fm_access_enter)();	*/
197 	0,			/* (*bus_fm_access_exit)();	*/
198 	0,			/* (*bus_power)();	*/
199 	sbbc_intr_ops		/* (*bus_intr_op)();	*/
200 };
201 
202 /*
203  * cb_ops
204  */
205 static struct cb_ops sbbc_cb_ops = {
206 	sbbc_open,		/* cb_open */
207 	sbbc_close,		/* cb_close */
208 	nodev,			/* cb_strategy */
209 	nodev,			/* cb_print */
210 	nodev,			/* cb_dump */
211 	nodev,			/* cb_read */
212 	nodev,			/* cb_write */
213 	sbbc_ioctl,		/* cb_ioctl */
214 	nodev,			/* cb_devmap */
215 	nodev,			/* cb_mmap */
216 	nodev,			/* cb_segmap */
217 	nochpoll,		/* cb_chpoll */
218 	ddi_prop_op,		/* cb_prop_op */
219 	NULL,			/* cb_stream */
220 	(int)(D_NEW | D_MP)	/* cb_flag */
221 };
222 
223 /*
224  * Declare ops vectors for auto configuration.
225  */
226 struct dev_ops  sbbc_ops = {
227 	DEVO_REV,		/* devo_rev */
228 	0,			/* devo_refcnt */
229 	sbbc_getinfo,		/* devo_getinfo */
230 	nulldev,		/* devo_identify */
231 	nulldev,		/* devo_probe */
232 	sbbc_attach,		/* devo_attach */
233 	sbbc_detach,		/* devo_detach */
234 	nodev,			/* devo_reset */
235 	&sbbc_cb_ops,		/* devo_cb_ops */
236 	&sbbc_bus_ops,		/* devo_bus_ops */
237 	nulldev			/* devo_power */
238 };
239 
240 /*
241  * Loadable module support.
242  */
243 extern struct mod_ops mod_driverops;
244 
245 static struct modldrv sbbcmodldrv = {
246 	&mod_driverops,		/* type of module - driver */
247 	"PCI Sbbc Nexus Driver v%I%",
248 	&sbbc_ops,
249 };
250 
251 static struct modlinkage sbbcmodlinkage = {
252 	MODREV_1,
253 	&sbbcmodldrv,
254 	NULL
255 };
256 
257 int
258 _init(void)
259 {
260 	int    error;
261 
262 	if ((error = ddi_soft_state_init(&sbbcsoft_statep,
263 		    sizeof (struct sbbcsoft), 1)) != 0)
264 		return (error);
265 	if ((error = mod_install(&sbbcmodlinkage)) != 0)
266 		ddi_soft_state_fini(&sbbcsoft_statep);
267 
268 	return (error);
269 }
270 
271 int
272 _fini(void)
273 {
274 	int    error;
275 
276 	if ((error = mod_remove(&sbbcmodlinkage)) == 0)
277 		ddi_soft_state_fini(&sbbcsoft_statep);
278 
279 	return (error);
280 }
281 
282 int
283 _info(struct modinfo *modinfop)
284 {
285 	return (mod_info(&sbbcmodlinkage, modinfop));
286 }
287 
288 static int
289 sbbc_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
290 {
291 	int	instance;
292 	char	name[32];
293 	struct	sbbcsoft *sbbcsoftp;
294 	struct ddi_device_acc_attr attr;
295 	uint32_t sbbc_id_reg;
296 
297 	attr.devacc_attr_version = DDI_DEVICE_ATTR_V0;
298 	attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC;
299 	attr.devacc_attr_endian_flags = DDI_NEVERSWAP_ACC;
300 
301 	/* initialize tracing */
302 	SBBCTRACEINIT();
303 
304 	SBBC_DBG0(SBBC_DBG_ATTACH, dip, "Attaching\n");
305 
306 	instance = ddi_get_instance(dip);
307 	switch (cmd) {
308 	case DDI_ATTACH:
309 		break;
310 	case DDI_RESUME:
311 		if (!(sbbcsoftp =
312 		    ddi_get_soft_state(sbbcsoft_statep, instance))) {
313 			cmn_err(CE_WARN, "sbbc_attach:resume: unable "
314 			    "to acquire sbbcsoftp for instance %d",
315 			    instance);
316 			return (DDI_FAILURE);
317 		}
318 		mutex_enter(&sbbcsoftp->umutex);
319 		if (!sbbcsoftp->suspended) {
320 			mutex_exit(&sbbcsoftp->umutex);
321 			return (DDI_FAILURE);
322 		}
323 		sbbcsoftp->suspended = 0;
324 		mutex_exit(&sbbcsoftp->umutex);
325 		return (DDI_SUCCESS);
326 
327 	default:
328 		return (DDI_FAILURE);
329 	}
330 
331 	if (ddi_soft_state_zalloc(sbbcsoft_statep, instance) != 0) {
332 		cmn_err(CE_WARN, "sbbc_attach: Unable to allocate statep "
333 		    "for instance %d", instance);
334 		return (DDI_FAILURE);
335 	}
336 
337 	sbbcsoftp = ddi_get_soft_state(sbbcsoft_statep, instance);
338 
339 	if (sbbcsoftp == NULL) {
340 		cmn_err(CE_WARN, "sbbc_attach: Unable to acquire "
341 		    "sbbcsoftp for instance %d", instance);
342 		ddi_soft_state_free(sbbcsoft_statep, instance);
343 		return (DDI_FAILURE);
344 	}
345 
346 	sbbcsoftp->instance = instance;
347 	sbbcsoftp->dip = dip;
348 	sbbcsoftp->oflag = FALSE;
349 
350 	/*
351 	 * Read our ranges property from OBP to map children space.
352 	 * And setup the internal structure for a later use when
353 	 * a child gets initialized.
354 	 */
355 	if (sbbc_get_ranges(sbbcsoftp)) {
356 		cmn_err(CE_WARN, "sbbc_attach: Unable to read sbbc "
357 		    "ranges from OBP %d", instance);
358 		ddi_soft_state_free(sbbcsoft_statep, instance);
359 		return (DDI_FAILURE);
360 	}
361 
362 	if (sbbc_config4pci(sbbcsoftp)) {
363 		cmn_err(CE_WARN, "sbbc_attach: Unable to configure "
364 		    "sbbc on PCI %d", instance);
365 		kmem_free(sbbcsoftp->rangep, sbbcsoftp->range_len);
366 		ddi_soft_state_free(sbbcsoft_statep, instance);
367 		return (DDI_FAILURE);
368 	}
369 
370 	mutex_init(&sbbcsoftp->umutex, NULL, MUTEX_DRIVER, (void *)NULL);
371 	mutex_init(&sbbcsoftp->sbbc_intr_mutex, NULL,
372 	    MUTEX_DRIVER, (void *)NULL);
373 
374 	/* Map SBBC's Internal Registers */
375 	if (ddi_regs_map_setup(dip, 1, (caddr_t *)&sbbcsoftp->pci_sbbc_map,
376 	    offsetof(struct pci_sbbc, sbbc_internal_regs),
377 	    sizeof (struct sbbc_regs_map), &attr,
378 	    &sbbcsoftp->pci_sbbc_map_handle) != DDI_SUCCESS) {
379 		cmn_err(CE_WARN, "(%d):sbbc_attach failed to map sbbc_reg",
380 		    instance);
381 		goto failed;
382 	}
383 
384 	SBBC_DBG1(SBBC_DBG_ATTACH, dip, "Mapped sbbc at %lx\n",
385 	    sbbcsoftp->pci_sbbc_map);
386 #ifdef DEBUG
387 	sbbc_dump_devid(dip, sbbcsoftp, instance);
388 #endif
389 	/*
390 	 * Read a hardware register to determine if we are executing on
391 	 * a Starcat System Controller or a Starcat Domain.
392 	 */
393 	sbbc_id_reg = ddi_get32(sbbcsoftp->pci_sbbc_map_handle,
394 	    &sbbcsoftp->pci_sbbc_map->device_conf);
395 
396 	if (sbbc_id_reg & SBBC_SC_MODE) {
397 		sbbc_scmode = TRUE;
398 		SBBC_DBG1(SBBC_DBG_ATTACH, dip, "SBBC(%d) nexus running "
399 		    "in System Controller Mode.\n", instance);
400 
401 		/* initialize SBBC ASIC */
402 		if (!sbbc_init(sbbcsoftp)) {
403 			goto failed;
404 		}
405 	} else {
406 		sbbc_scmode = FALSE;
407 		SBBC_DBG1(SBBC_DBG_ATTACH, dip, "SBBC(%d) nexus "
408 		    "running in Domain Mode.\n", instance);
409 
410 		/* initialize SBBC ASIC before we unmap registers */
411 		if (!sbbc_init(sbbcsoftp)) {
412 			goto failed;
413 		}
414 
415 		/*
416 		 * Access to SBBC registers is no longer needed.  Unmap
417 		 * the registers to conserve kernel virtual address space.
418 		 */
419 		SBBC_DBG1(SBBC_DBG_ATTACH, dip, "SBBC(%d): unmap "
420 		    "SBBC registers\n", instance);
421 		sbbc_remove_reg_maps(sbbcsoftp);
422 		sbbcsoftp->pci_sbbc_map = NULL;
423 	}
424 
425 	(void) sprintf(name, "sbbc%d", instance);
426 
427 	if (ddi_create_minor_node(dip, name, S_IFCHR, instance, NULL,
428 	    NULL) == DDI_FAILURE) {
429 		ddi_remove_minor_node(dip, NULL);
430 		goto failed;
431 	}
432 
433 	ddi_report_dev(dip);
434 
435 	SBBC_DBG0(SBBC_DBG_ATTACH, dip, "Attached successfully\n");
436 
437 	return (DDI_SUCCESS);
438 
439 failed:
440 	mutex_destroy(&sbbcsoftp->sbbc_intr_mutex);
441 	mutex_destroy(&sbbcsoftp->umutex);
442 
443 	sbbc_remove_reg_maps(sbbcsoftp);
444 	kmem_free(sbbcsoftp->rangep, sbbcsoftp->range_len);
445 	ddi_soft_state_free(sbbcsoft_statep, instance);
446 
447 	SBBC_DBG0(SBBC_DBG_ATTACH, dip, "Attach failed\n");
448 
449 	return (DDI_FAILURE);
450 }
451 
452 static int
453 sbbc_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
454 {
455 	int		instance;
456 	struct sbbcsoft *sbbcsoftp;
457 
458 	SBBCTRACE(sbbc_detach, 'DETA', dip);
459 
460 	instance = ddi_get_instance(dip);
461 
462 	switch (cmd) {
463 	case DDI_DETACH:
464 		break;
465 
466 	case DDI_SUSPEND:
467 		if (!(sbbcsoftp =
468 		    ddi_get_soft_state(sbbcsoft_statep, instance))) {
469 			cmn_err(CE_WARN,
470 			    "sbbc_detach: unable to get softstate %p",
471 			    (void *)sbbcsoftp);
472 			return (DDI_FAILURE);
473 		}
474 		mutex_enter(&sbbcsoftp->umutex);
475 		if (sbbcsoftp->suspended) {
476 		    mutex_exit(&sbbcsoftp->umutex);
477 		    return (DDI_FAILURE);
478 		}
479 		sbbcsoftp->suspended = 1;
480 		mutex_exit(&sbbcsoftp->umutex);
481 		return (DDI_SUCCESS);
482 
483 	default:
484 		return (DDI_FAILURE);
485 	}
486 
487 	if (!(sbbcsoftp = ddi_get_soft_state(sbbcsoft_statep, instance))) {
488 		cmn_err(CE_WARN, "sbbc_detach: unable to get softstate %p",
489 		    (void *)sbbcsoftp);
490 	    return (DDI_FAILURE);
491 	}
492 
493 	ddi_remove_minor_node(dip, NULL);
494 
495 	mutex_destroy(&sbbcsoftp->sbbc_intr_mutex);
496 	mutex_destroy(&sbbcsoftp->umutex);
497 
498 	sbbc_remove_reg_maps(sbbcsoftp);
499 	kmem_free(sbbcsoftp->rangep, sbbcsoftp->range_len);
500 
501 	ddi_soft_state_free(sbbcsoft_statep, instance);
502 
503 	return (DDI_SUCCESS);
504 
505 }
506 
507 
508 /*
509  * Translate child's address into parents.
510  */
511 static int
512 sbbc_busmap(dev_info_t *dip, dev_info_t *rdip, ddi_map_req_t *mp,
513 	    off_t off, off_t len, caddr_t *addrp)
514 {
515 	struct sbbcsoft *sbbcsoftp;
516 	sbbc_child_regspec_t *child_rp, *child_regs;
517 	pci_regspec_t pci_reg;
518 	ddi_map_req_t p_map_request;
519 	int rnumber, i, n;
520 	int rval = DDI_SUCCESS;
521 	int instance;
522 
523 	SBBC_DBG4(SBBC_DBG_BUSMAP, dip,
524 	    "mapping child %s, type %llx, off %llx, len %llx\n",
525 	    ddi_driver_name(rdip), mp->map_type, off, len);
526 
527 	SBBCTRACE(sbbc_busmap, 'BMAP', mp);
528 
529 	/*
530 	 * Handle the mapping according to its type.
531 	 */
532 	instance = ddi_get_instance(dip);
533 	if (!(sbbcsoftp = ddi_get_soft_state(sbbcsoft_statep, instance)))
534 	    return (DDI_FAILURE);
535 
536 	switch (mp->map_type) {
537 	case DDI_MT_REGSPEC:
538 
539 		/*
540 		 * We assume the register specification is in sbbc format.
541 		 * We must convert it into a PCI format regspec and pass
542 		 * the request to our parent.
543 		 */
544 		child_rp = (sbbc_child_regspec_t *)mp->map_obj.rp;
545 		break;
546 
547 	case DDI_MT_RNUMBER:
548 
549 		/*
550 		 * map_type 0
551 		 * Get the "reg" property from the device node and convert
552 		 * it to our parent's format.
553 		 */
554 		rnumber = mp->map_obj.rnumber;
555 
556 		/* get the requester's reg property */
557 		if (ddi_getlongprop(DDI_DEV_T_ANY, rdip, DDI_PROP_DONTPASS,
558 		    "reg", (caddr_t)&child_regs, &i) != DDI_SUCCESS) {
559 			cmn_err(CE_WARN,
560 			    "SBBC: couldn't get %s ranges property %d",
561 			    ddi_get_name(sbbcsoftp->dip), instance);
562 			return (DDI_ME_RNUMBER_RANGE);
563 		}
564 		n = i / sizeof (sbbc_child_regspec_t);
565 
566 		if (rnumber < 0 || rnumber >= n) {
567 			kmem_free(child_regs, i);
568 			return (DDI_ME_RNUMBER_RANGE);
569 		}
570 		child_rp = &child_regs[rnumber];
571 		break;
572 
573 	default:
574 		return (DDI_ME_INVAL);
575 
576 	}
577 
578 	/* Adjust our reg property with offset and length */
579 	child_rp->addr_low += off;
580 
581 	if (len)
582 		child_rp->size = len;
583 
584 	/*
585 	 * Combine this reg prop. into our parents PCI address using the ranges
586 	 * property.
587 	 */
588 	rval = sbbc_apply_range(sbbcsoftp, rdip, child_rp, &pci_reg);
589 
590 	if (mp->map_type == DDI_MT_RNUMBER)
591 		kmem_free(child_regs, i);
592 
593 	if (rval != DDI_SUCCESS)
594 		return (rval);
595 
596 	p_map_request = *mp;
597 	p_map_request.map_type = DDI_MT_REGSPEC;
598 	p_map_request.map_obj.rp = (struct regspec *)&pci_reg;
599 
600 	/* Send it to PCI nexus to map into the PCI space */
601 	rval = ddi_map(dip, &p_map_request, 0, 0, addrp);
602 
603 	return (rval);
604 
605 }
606 
607 
608 /* new intr_ops structure */
609 static int
610 sbbc_intr_ops(dev_info_t *dip, dev_info_t *rdip, ddi_intr_op_t intr_op,
611     ddi_intr_handle_impl_t *hdlp, void *result)
612 {
613 	int	ret = DDI_SUCCESS;
614 
615 	switch (intr_op) {
616 	case DDI_INTROP_GETCAP:
617 		*(int *)result = DDI_INTR_FLAG_LEVEL;
618 		break;
619 	case DDI_INTROP_ALLOC:
620 		*(int *)result = hdlp->ih_scratch1;
621 		break;
622 	case DDI_INTROP_FREE:
623 		break;
624 	case DDI_INTROP_GETPRI:
625 		if (hdlp->ih_pri == 0) {
626 			hdlp->ih_pri = 0x1;
627 
628 			cmn_err(CE_WARN, "%s%d assigning default interrupt "
629 			    "level %d for device %s%d", ddi_driver_name(dip),
630 			    ddi_get_instance(dip), hdlp->ih_pri,
631 			    ddi_driver_name(rdip), ddi_get_instance(rdip));
632 		}
633 
634 		*(int *)result = hdlp->ih_pri;
635 
636 		break;
637 	case DDI_INTROP_ADDISR:
638 		ret = sbbc_add_intr_impl(dip, rdip, intr_op, hdlp, result);
639 		break;
640 	case DDI_INTROP_REMISR:
641 		ret = sbbc_remove_intr_impl(dip, rdip, intr_op, hdlp, result);
642 		break;
643 	case DDI_INTROP_ENABLE:
644 		ret = sbbc_update_intr_state(dip, rdip, intr_op, hdlp, &result);
645 		break;
646 	case DDI_INTROP_DISABLE:
647 		ret = sbbc_update_intr_state(dip, rdip, intr_op, hdlp, &result);
648 		break;
649 	case DDI_INTROP_NINTRS:
650 	case DDI_INTROP_NAVAIL:
651 		*(int *)result = i_ddi_get_intx_nintrs(rdip);
652 		break;
653 	case DDI_INTROP_SUPPORTED_TYPES:
654 		/* PCI nexus driver supports only fixed interrupts */
655 		*(int *)result = i_ddi_get_intx_nintrs(rdip) ?
656 		    DDI_INTR_TYPE_FIXED : 0;
657 		break;
658 	default:
659 		ret = DDI_ENOTSUP;
660 		break;
661 	}
662 
663 	return (ret);
664 }
665 
666 
667 static int
668 sbbc_add_intr_impl(dev_info_t *dip, dev_info_t *rdip, ddi_intr_op_t intr_op,
669     ddi_intr_handle_impl_t *hdlp, void *result)
670 {
671 	sbbcsoft_t *sbbcsoftp;
672 	sbbc_child_intr_t *childintr;
673 	int instance, i, rval = DDI_SUCCESS;
674 
675 	SBBC_DBG2(SBBC_DBG_INTR, dip,
676 	    "add: rdip 0x%llx hdlp 0x%llx\n", rdip, hdlp);
677 
678 	/* insert the sbbc isr wrapper instead */
679 	instance = ddi_get_instance(dip);
680 	if (!(sbbcsoftp = ddi_get_soft_state(sbbcsoft_statep, instance)))
681 		return (DDI_FAILURE);
682 
683 	childintr = kmem_zalloc(sizeof (struct sbbc_child_intr), KM_SLEEP);
684 
685 	childintr->name = ddi_get_name(rdip);
686 	childintr->inum = hdlp->ih_inum;
687 	childintr->intr_handler = hdlp->ih_cb_func;
688 	childintr->arg1 = hdlp->ih_cb_arg1;
689 	childintr->arg2 = hdlp->ih_cb_arg2;
690 	childintr->status = SBBC_INTR_STATE_DISABLE;
691 
692 	for (i = 0; i < MAX_SBBC_DEVICES; i++) {
693 		if (sbbcsoftp->child_intr[i] == 0) {
694 			sbbcsoftp->child_intr[i] = childintr;
695 			break;
696 		}
697 	}
698 
699 	DDI_INTR_ASSIGN_HDLR_N_ARGS(hdlp,
700 	    (ddi_intr_handler_t *)sbbc_intr_wrapper,
701 	    (caddr_t)sbbcsoftp, NULL);
702 
703 	if ((rval = i_ddi_intr_ops(dip, rdip, intr_op,
704 	    hdlp, result)) != DDI_SUCCESS) {
705 		cmn_err(CE_WARN, "sbbc%d: failed to add intr for %s",
706 		    instance, ddi_get_name(rdip));
707 		kmem_free(childintr, sizeof (struct sbbc_child_intr));
708 		sbbcsoftp->child_intr[i] = NULL;
709 	}
710 
711 	/*
712 	 * Restore original interrupt handler
713 	 * and arguments in interrupt handle.
714 	 */
715 	DDI_INTR_ASSIGN_HDLR_N_ARGS(hdlp, childintr->intr_handler,
716 	    childintr->arg1, childintr->arg2);
717 
718 	return (rval);
719 }
720 
721 static int
722 sbbc_remove_intr_impl(dev_info_t *dip, dev_info_t *rdip, ddi_intr_op_t intr_op,
723     ddi_intr_handle_impl_t *hdlp, void *result)
724 {
725 	sbbcsoft_t *sbbcsoftp;
726 	sbbc_child_intr_t *childintr;
727 	int instance, i, rval = DDI_SUCCESS;
728 
729 	SBBC_DBG2(SBBC_DBG_INTR, dip,
730 	    "remove: rdip 0x%llx hdlp 0x%llx\n", rdip, hdlp);
731 
732 	instance = ddi_get_instance(dip);
733 	if (!(sbbcsoftp = ddi_get_soft_state(sbbcsoft_statep, instance)))
734 		return (DDI_FAILURE);
735 
736 	/* remove the sbbc isr wrapper instead */
737 	for (i = 0; i < MAX_SBBC_DEVICES; i++) {
738 		if (sbbcsoftp->child_intr[i]) {
739 			childintr = sbbcsoftp->child_intr[i];
740 			if (childintr->status == SBBC_INTR_STATE_DISABLE &&
741 			    childintr->name == ddi_get_name(rdip)) {
742 				/* put back child's inum */
743 				hdlp->ih_inum = childintr->inum;
744 				break;
745 			}
746 		}
747 	}
748 
749 	if (i >= MAX_SBBC_DEVICES) {
750 		cmn_err(CE_WARN, "sbbc%d:obound failed to remove intr for %s",
751 		    instance, ddi_get_name(rdip));
752 		return (DDI_FAILURE);
753 	}
754 
755 	if ((rval = i_ddi_intr_ops(dip, rdip, intr_op,
756 	    hdlp, result)) != DDI_SUCCESS) {
757 		cmn_err(CE_WARN, "sbbc%d: failed to remove intr for %s",
758 		    instance, ddi_get_name(rdip));
759 		return (rval);
760 	}
761 
762 	kmem_free(childintr, sizeof (struct sbbc_child_intr));
763 	sbbcsoftp->child_intr[i] = NULL;
764 
765 	return (rval);
766 }
767 
768 
769 static int
770 sbbc_update_intr_state(dev_info_t *dip, dev_info_t *rdip, ddi_intr_op_t intr_op,
771     ddi_intr_handle_impl_t *hdlp, void *result)
772 {
773 	sbbcsoft_t		*sbbcsoftp;
774 	sbbc_child_intr_t	*childintr;
775 	int			instance, i;
776 	int			ret = DDI_SUCCESS;
777 
778 	SBBC_DBG2(SBBC_DBG_INTR, dip, "sbbc_update_intr_state: "
779 	    "rdip 0x%llx hdlp 0x%llx state 0x%x\n", rdip, hdlp);
780 
781 	instance = ddi_get_instance(dip);
782 	if (!(sbbcsoftp = ddi_get_soft_state(sbbcsoft_statep, instance)))
783 		return (DDI_FAILURE);
784 
785 	for (i = 0; i < MAX_SBBC_DEVICES; i++) {
786 		if (sbbcsoftp->child_intr[i]) {
787 			childintr = sbbcsoftp->child_intr[i];
788 			if (childintr->name == ddi_get_name(rdip))
789 				break;
790 		}
791 	}
792 
793 	if (i >= MAX_SBBC_DEVICES) {
794 		cmn_err(CE_WARN, "sbbc%d: failed to update intr state for %s",
795 		    instance, ddi_get_name(rdip));
796 		return (DDI_FAILURE);
797 	}
798 
799 	if ((ret = i_ddi_intr_ops(dip, rdip, intr_op,
800 	    hdlp, result)) != DDI_SUCCESS) {
801 		cmn_err(CE_WARN, "sbbc%d: failed to update intr state for %s",
802 		    instance, ddi_get_name(rdip));
803 		return (ret);
804 	}
805 
806 	/* Update the interrupt state */
807 	childintr->status = (intr_op == DDI_INTROP_ENABLE) ?
808 	    SBBC_INTR_STATE_ENABLE : SBBC_INTR_STATE_DISABLE;
809 
810 	return (ret);
811 }
812 
813 
814 /*
815  * This entry point is called before a child's probe or attach is called.
816  * The arg pointer points to child's dev_info_t structure.
817  */
818 static int
819 sbbc_ctlops(dev_info_t *dip, dev_info_t *rdip, ddi_ctl_enum_t op,
820 	    void *arg, void *result)
821 {
822 	sbbc_child_regspec_t *child_rp;
823 	int i, n;
824 
825 	SBBC_DBG3(SBBC_DBG_CTLOPS, dip,
826 	    "Initializing %s, arg %x, op %x\n",
827 	    ddi_driver_name(rdip), arg, op);
828 
829 	SBBCTRACE(sbbc_ctlops, 'CTLO', arg);
830 
831 	switch (op) {
832 	case DDI_CTLOPS_INITCHILD: {
833 		return (sbbc_initchild(dip, rdip, (dev_info_t *)arg));
834 	}
835 
836 	case DDI_CTLOPS_UNINITCHILD: {
837 		return (sbbc_uninitchild(rdip, (dev_info_t *)arg));
838 	}
839 
840 	case DDI_CTLOPS_REPORTDEV:
841 
842 		cmn_err(CE_CONT, "?%s%d at %s%d: offset %s\n",
843 		    ddi_driver_name(rdip), ddi_get_instance(rdip),
844 		    ddi_driver_name(dip), ddi_get_instance(dip),
845 		    ddi_get_name_addr(rdip));
846 		return (DDI_SUCCESS);
847 
848 	case DDI_CTLOPS_REGSIZE:
849 
850 		if (getprop(rdip, "reg", &child_rp, &i) != DDI_SUCCESS) {
851 			return (DDI_FAILURE);
852 		}
853 		n = i / sizeof (sbbc_child_regspec_t);
854 		if (*(int *)arg < 0 || *(int *)arg >= n) {
855 			kmem_free(child_rp, i);
856 			return (DDI_FAILURE);
857 		}
858 		*((off_t *)result) = child_rp[*(int *)arg].size;
859 		kmem_free(child_rp, i);
860 		return (DDI_SUCCESS);
861 
862 	case DDI_CTLOPS_NREGS:
863 
864 		if (getprop(rdip, "reg", &child_rp, &i) != DDI_SUCCESS) {
865 			return (DDI_FAILURE);
866 		}
867 		*((uint_t *)result) = i / sizeof (sbbc_child_regspec_t);
868 		kmem_free(child_rp, i);
869 		return (DDI_SUCCESS);
870 	}
871 
872 	/*
873 	 * Now pass the request up to our parent.
874 	 */
875 	SBBC_DBG0(SBBC_DBG_CTLOPS, dip, "Calling ddi_ctlops\n");
876 
877 	return (ddi_ctlops(dip, rdip, op, arg, result));
878 }
879 
880 
881 /*
882  * The following routine uses ranges property, that was read earlier, and
883  * takes child's reg property, and computes the complete address and size
884  * for the PCI parent to map.
885  */
886 static int
887 sbbc_apply_range(struct sbbcsoft *sbbc_p, dev_info_t *rdip,
888     sbbc_child_regspec_t *child_rp, pci_regspec_t *rp)
889 {
890 	int b;
891 	int rval = DDI_SUCCESS;
892 	struct sbbc_pci_rangespec *rangep = sbbc_p->rangep;
893 	int nrange = sbbc_p->range_cnt;
894 
895 	SBBC_DBG4(SBBC_DBG_MAPRANGES, rdip,
896 	    "Applying ranges for %s, rangep %llx, child_rp %llx, range %x\n",
897 	    ddi_driver_name(rdip), sbbc_p->rangep, child_rp, nrange);
898 
899 	SBBCTRACE(sbbc_apply_range, 'APPL', sbbc_p);
900 
901 	for (b = 0; b < nrange; ++b, ++rangep) {
902 
903 		/* Make sure the correct range is being mapped */
904 		if (child_rp->addr_hi == rangep->sbbc_phys_hi)
905 			/* See if we fit in this range */
906 			if ((child_rp->addr_low >=
907 			    rangep->sbbc_phys_low) &&
908 			    ((child_rp->addr_low + child_rp->size - 1)
909 				<= (rangep->sbbc_phys_low +
910 				    rangep->rng_size - 1))) {
911 				uint_t addr_offset = child_rp->addr_low -
912 				    rangep->sbbc_phys_low;
913 				/*
914 				 * Use the range entry to translate
915 				 * the SBBC physical address into the
916 				 * parents PCI space.
917 				 */
918 				rp->pci_phys_hi =
919 				    rangep->pci_phys_hi;
920 				rp->pci_phys_mid = rangep->pci_phys_mid;
921 				rp->pci_phys_low =
922 				    rangep->pci_phys_low + addr_offset;
923 				rp->pci_size_hi = 0;
924 				rp->pci_size_low =
925 				    min(child_rp->size, (rangep->rng_size -
926 					addr_offset));
927 
928 				break;
929 			}
930 	}
931 
932 	if (b == nrange)  {
933 		cmn_err(CE_WARN, "out_of_range %s", ddi_get_name(rdip));
934 		return (DDI_ME_REGSPEC_RANGE);
935 	}
936 
937 	return (rval);
938 }
939 
940 
941 /*
942  * The following routine reads sbbc's ranges property from OBP and sets up
943  * its soft structure with it.
944  */
945 static int
946 sbbc_get_ranges(struct sbbcsoft *sbbcsoftp)
947 {
948 	struct sbbc_pci_rangespec *rangep;
949 	int range_len, nrange;
950 
951 	if (ddi_getlongprop(DDI_DEV_T_ANY, sbbcsoftp->dip, DDI_PROP_DONTPASS,
952 	    "ranges", (caddr_t)&rangep, &range_len) != DDI_SUCCESS) {
953 		cmn_err(CE_WARN, "SBBC: couldn't get %s ranges property %d",
954 		    ddi_get_name(sbbcsoftp->dip), sbbcsoftp->instance);
955 		return (DDI_ME_REGSPEC_RANGE);
956 	}
957 
958 	nrange = range_len / sizeof (struct sbbc_pci_rangespec);
959 
960 	if (!nrange) {
961 		kmem_free(rangep, range_len);
962 		return (DDI_FAILURE);
963 	}
964 
965 	/* setup the soft structure with ranges info. */
966 	sbbcsoftp->rangep = rangep;
967 	sbbcsoftp->range_cnt = nrange;
968 	sbbcsoftp->range_len = range_len;
969 
970 	return (DDI_SUCCESS);
971 }
972 
973 
974 /*
975  * Configure the SBBC for PCI
976  */
977 static int
978 sbbc_config4pci(struct sbbcsoft *sbbcsoftp)
979 {
980 	ddi_acc_handle_t conf_handle;
981 	uint16_t comm, vendid, devid, stat;
982 	uint8_t revid;
983 
984 #ifdef DEBUG
985 	if (sbbc_dbg_flags & SBBC_DBG_PCICONF) {
986 		cmn_err(CE_CONT,
987 		    "sbbc_config4pci: sbbcsoftp %p\n", (void *)sbbcsoftp);
988 	}
989 #endif
990 	if (pci_config_setup(sbbcsoftp->dip, &conf_handle) != DDI_SUCCESS)
991 		return (1);
992 
993 	vendid = pci_config_get16(conf_handle, PCI_CONF_VENID);
994 	devid = pci_config_get16(conf_handle, PCI_CONF_DEVID);
995 	comm = pci_config_get16(conf_handle, PCI_CONF_COMM);
996 	stat = pci_config_get16(conf_handle, PCI_CONF_STAT);
997 	revid = pci_config_get8(conf_handle, PCI_CONF_REVID);
998 
999 #ifdef DEBUG
1000 	if (sbbc_dbg_flags & SBBC_DBG_PCICONF) {
1001 		cmn_err(CE_CONT,
1002 		    "SBBC vendid %x, devid %x, comm %x, stat %x, revid %x\n",
1003 		    vendid, devid, comm, stat, revid);
1004 	}
1005 #endif
1006 	comm = (PCI_COMM_ME | PCI_COMM_MAE | PCI_COMM_SERR_ENABLE |
1007 		    PCI_COMM_PARITY_DETECT);
1008 
1009 	pci_config_put16(conf_handle, PCI_CONF_COMM, comm);
1010 
1011 	comm = pci_config_get16(conf_handle, PCI_CONF_COMM);
1012 
1013 #ifdef DEBUG
1014 	if (sbbc_dbg_flags & SBBC_DBG_PCICONF) {
1015 		cmn_err(CE_CONT, "comm %x\n", comm);
1016 	}
1017 #endif
1018 	pci_config_teardown(&conf_handle);
1019 
1020 	return (0);
1021 }
1022 
1023 
1024 /* ARGSUSED0 */
1025 int
1026 sbbc_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
1027 {
1028 	dev_t	dev = (dev_t)arg;
1029 	struct sbbcsoft *sbbcsoftp;
1030 	int	instance, ret;
1031 
1032 	instance = getminor(dev);
1033 
1034 	SBBCTRACE(sbbc_getinfo, 'GINF', instance);
1035 
1036 	switch (infocmd) {
1037 		case DDI_INFO_DEVT2DEVINFO:
1038 			sbbcsoftp = (struct sbbcsoft *)
1039 				ddi_get_soft_state(sbbcsoft_statep, instance);
1040 			*result = sbbcsoftp->dip;
1041 			ret = DDI_SUCCESS;
1042 			break;
1043 		case DDI_INFO_DEVT2INSTANCE:
1044 			*result = (void *)(uintptr_t)instance;
1045 			ret = DDI_SUCCESS;
1046 			break;
1047 		default:
1048 			ret = DDI_FAILURE;
1049 			break;
1050 	}
1051 
1052 	return (ret);
1053 }
1054 
1055 /*ARGSUSED1*/
1056 static int
1057 sbbc_open(dev_t *dev, int flag, int otype, cred_t *credp)
1058 {
1059 	struct sbbcsoft *sbbcsoftp;
1060 	int		instance;
1061 
1062 	/* check privilege of caller process */
1063 	if (drv_priv(credp)) {
1064 		return (EPERM);
1065 	}
1066 
1067 	instance = getminor(*dev);
1068 	if (instance < 0)
1069 		return (ENXIO);
1070 	sbbcsoftp = (struct sbbcsoft *)ddi_get_soft_state(sbbcsoft_statep,
1071 							    instance);
1072 	SBBCTRACE(sbbc_open, 'OPEN', sbbcsoftp);
1073 
1074 	if (sbbcsoftp == NULL)
1075 		return (ENXIO);
1076 
1077 	mutex_enter(&sbbcsoftp->umutex);
1078 
1079 	/* check for exclusive access */
1080 	if ((sbbcsoftp->oflag == TRUE)) {
1081 		mutex_exit(&sbbcsoftp->umutex);
1082 		return (EBUSY);
1083 	}
1084 	sbbcsoftp->oflag = TRUE;
1085 
1086 	mutex_exit(&sbbcsoftp->umutex);
1087 
1088 	return (0);
1089 }
1090 
1091 /*ARGSUSED1*/
1092 static int
1093 sbbc_close(dev_t dev, int flag, int otype, cred_t *credp)
1094 {
1095 	struct sbbcsoft *sbbcsoftp;
1096 	int		instance;
1097 
1098 	instance = getminor(dev);
1099 	if (instance < 0)
1100 		return (ENXIO);
1101 	sbbcsoftp = (struct sbbcsoft *)ddi_get_soft_state(sbbcsoft_statep,
1102 							    instance);
1103 	/* wait till all output activity has ceased */
1104 
1105 	mutex_enter(&sbbcsoftp->umutex);
1106 
1107 	SBBCTRACE(sbbc_close, 'CLOS', sbbcsoftp);
1108 
1109 	sbbcsoftp->oflag = FALSE;
1110 
1111 	mutex_exit(&sbbcsoftp->umutex);
1112 
1113 	return (0);
1114 }
1115 
1116 /*ARGSUSED2*/
1117 static int
1118 sbbc_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp,
1119 		int *rvalp)
1120 {
1121 	struct sbbcsoft *sbbcsoftp;
1122 
1123 	SBBCTRACE(sbbc_ioctl, 'IOCT', arg);
1124 
1125 	sbbcsoftp = ddi_get_soft_state(sbbcsoft_statep, getminor(dev));
1126 
1127 	if (sbbcsoftp == NULL) {
1128 		return (ENXIO);
1129 	}
1130 
1131 	switch (cmd) {
1132 	case SBBC_SBBCREG_WR:
1133 		{
1134 		struct ssc_sbbc_regio sbbcregs;
1135 		uint64_t offset;
1136 
1137 		if (sbbc_scmode == FALSE) {
1138 			/* then we're executing on Domain; Writes not allowed */
1139 			return (EINVAL);
1140 		}
1141 
1142 		if (arg == NULL) {
1143 			return (ENXIO);
1144 		}
1145 
1146 		if (ddi_copyin((caddr_t)arg, (caddr_t)&sbbcregs,
1147 				    sizeof (struct ssc_sbbc_regio), mode)) {
1148 			cmn_err(CE_WARN, "sbbc_ioctl: copyin failed arg %p",
1149 			    (void *)arg);
1150 			return (EFAULT);
1151 		}
1152 
1153 		/*
1154 		 * Bug #4287186: SBBC driver on cp1500 doesn't check length for
1155 		 *		reads or writes
1156 		 * Note that I've also added a check to make sure the offset is
1157 		 * valid, since misaligned (i.e. not on 16-byte boundary)
1158 		 * accesses or accesses to "Reserved" register offsets are
1159 		 * treated as unmapped by the SBBC.
1160 		 */
1161 		if ((sbbcregs.len != 4) ||
1162 		    !sbbc_offset_valid(sbbcregs.offset)) {
1163 			return (EINVAL);
1164 		}
1165 
1166 		offset = (uint64_t)sbbcsoftp->pci_sbbc_map;
1167 		offset += sbbcregs.offset;
1168 		ddi_put32(sbbcsoftp->pci_sbbc_map_handle, (uint32_t *)offset,
1169 			    sbbcregs.value);
1170 		}
1171 		break;
1172 	case SBBC_SBBCREG_RD:
1173 		{
1174 		struct ssc_sbbc_regio sbbcregs;
1175 		uint64_t offset;
1176 
1177 		if (sbbc_scmode == FALSE) {
1178 			/* then we're executing on Domain; Reads not allowed */
1179 			return (EINVAL);
1180 		}
1181 
1182 		if (arg == NULL) {
1183 			return (ENXIO);
1184 		}
1185 
1186 		if (ddi_copyin((caddr_t)arg, (caddr_t)&sbbcregs,
1187 				    sizeof (struct ssc_sbbc_regio), mode)) {
1188 			cmn_err(CE_WARN, "sbbc_ioctl: copyin failed arg %p",
1189 			    (void *)arg);
1190 			return (EFAULT);
1191 		}
1192 
1193 		/*
1194 		 * Bug #4287186: SBBC driver on cp1500 doesn't check length for
1195 		 *		reads or writes
1196 		 * Note that I've also added a check to make sure the offset is
1197 		 * valid, since misaligned (i.e. not on 16-byte boundary)
1198 		 * accesses or accesses to "Reserved" register offsets are
1199 		 * treated as unmapped by the SBBC.
1200 		 */
1201 		if ((sbbcregs.len != 4) ||
1202 		    !sbbc_offset_valid(sbbcregs.offset)) {
1203 			return (EINVAL);
1204 		}
1205 
1206 		offset = (uint64_t)sbbcsoftp->pci_sbbc_map;
1207 		offset += sbbcregs.offset;
1208 
1209 		sbbcregs.value = ddi_get32(sbbcsoftp->pci_sbbc_map_handle,
1210 						    (uint32_t *)offset);
1211 
1212 		if (ddi_copyout((caddr_t)&sbbcregs.value,
1213 	    &((struct ssc_sbbc_regio *)arg)->value, sbbcregs.len, mode)) {
1214 			cmn_err(CE_WARN, "sbbc_ioctl:copyout failed arg %p",
1215 			    (void *)arg);
1216 			return (EFAULT);
1217 		}
1218 		}
1219 		break;
1220 	default:
1221 		cmn_err(CE_WARN, "sbbc_ioctl:Illegal command 0x%08x", cmd);
1222 		return (ENOTTY);
1223 	}
1224 
1225 	return (DDI_SUCCESS);
1226 }
1227 
1228 static void
1229 sbbc_remove_reg_maps(struct sbbcsoft *sbbcsoftp)
1230 {
1231 	SBBCTRACE(sbbc_remove_reg_maps, 'RMAP', sbbcsoftp);
1232 	if (sbbcsoftp->pci_sbbc_map_handle)
1233 		ddi_regs_map_free(&sbbcsoftp->pci_sbbc_map_handle);
1234 }
1235 
1236 
1237 static int
1238 sbbc_init(struct sbbcsoft *sbbcsoftp)
1239 {
1240 	/* Mask all the interrupts until we are ready. */
1241 	ddi_put32(sbbcsoftp->pci_sbbc_map_handle,
1242 	    &sbbcsoftp->pci_sbbc_map->sys_intr_enable,
1243 	    0x00000000);
1244 
1245 	return (1);
1246 }
1247 
1248 /*
1249  * The following routine is a generic routine to initialize any child of
1250  * sbbc nexus driver information into parent private data structure.
1251  */
1252 /* ARGSUSED0 */
1253 static int
1254 sbbc_initchild(dev_info_t *dip, dev_info_t *rdip, dev_info_t *child)
1255 {
1256 	sbbc_child_regspec_t *child_rp;
1257 	int reglen, slot;
1258 	char name[10];
1259 
1260 	SBBC_DBG1(SBBC_DBG_INITCHILD, dip, "Initializing %s\n",
1261 	    ddi_driver_name(rdip));
1262 
1263 	/*
1264 	 * Initialize a child
1265 	 * Set the address portion of the node name based on the
1266 	 * address/offset.
1267 	 */
1268 	if (ddi_getlongprop(DDI_DEV_T_ANY, child, DDI_PROP_DONTPASS,
1269 	    "reg", (caddr_t)&child_rp, &reglen) != DDI_SUCCESS) {
1270 		if (strcmp(ddi_node_name(child), "hotplug-controller") == 0) {
1271 			slot = 1;
1272 			(void) sprintf(name, "%x", slot);
1273 			ddi_set_name_addr(child, name);
1274 			return (DDI_SUCCESS);
1275 		}
1276 		return (DDI_FAILURE);
1277 	}
1278 
1279 	SBBC_DBG3(SBBC_DBG_INITCHILD, dip, "hi 0x%x, low 0x%x, size 0x%x\n",
1280 	    child_rp->addr_hi, child_rp->addr_low, child_rp->size);
1281 
1282 	(void) sprintf(name, "%x,%x", child_rp->addr_hi, child_rp->addr_low);
1283 
1284 	/*
1285 	 * set child's addresses from the reg property into parent private
1286 	 * data structure.
1287 	 */
1288 	ddi_set_name_addr(child, name);
1289 	kmem_free(child_rp, reglen);
1290 
1291 	ddi_set_parent_data(child, NULL);
1292 
1293 	return (DDI_SUCCESS);
1294 }
1295 
1296 
1297 /* ARGSUSED0 */
1298 static int
1299 sbbc_uninitchild(dev_info_t *rdip, dev_info_t *child)
1300 {
1301 
1302 	SBBC_DBG1(SBBC_DBG_UNINITCHILD, rdip, "Uninitializing %s\n",
1303 	    ddi_driver_name(rdip));
1304 
1305 	ddi_set_name_addr(child, NULL);
1306 	ddi_remove_minor_node(child, NULL);
1307 	impl_rem_dev_props(child);
1308 
1309 	return (DDI_SUCCESS);
1310 
1311 }
1312 
1313 
1314 /*
1315  * The following routine is an interrupt service routine that is used
1316  * as a wrapper to all the children requiring interrupt services.
1317  */
1318 static uint_t
1319 sbbc_intr_wrapper(caddr_t arg)
1320 {
1321 
1322 	struct sbbcsoft *sbbcsoftp = (struct sbbcsoft *)arg;
1323 	int i, rval;
1324 
1325 	SBBC_DBG1(SBBC_DBG_INTR, sbbcsoftp->dip, "Isr arg 0x%llx\n", arg);
1326 
1327 	mutex_enter(&sbbcsoftp->sbbc_intr_mutex);
1328 
1329 	for (i = 0; i < MAX_SBBC_DEVICES; i++) {
1330 		/*
1331 		 * Check the interrupt status reg. to determine the cause.
1332 		 */
1333 		/*
1334 		 * Check the error status reg. to determine the cause.
1335 		 */
1336 		if (sbbcsoftp->child_intr[i] &&
1337 		    sbbcsoftp->child_intr[i]->status ==
1338 		    SBBC_INTR_STATE_ENABLE) {
1339 			/*
1340 			 * Dispatch the children interrupt service routines and
1341 			 * look for someone to claim.
1342 			 */
1343 			rval = sbbcsoftp->child_intr[i]->intr_handler(
1344 			    sbbcsoftp->child_intr[i]->arg1,
1345 			    sbbcsoftp->child_intr[i]->arg2);
1346 
1347 			if (rval == DDI_INTR_CLAIMED) {
1348 				mutex_exit(&sbbcsoftp->sbbc_intr_mutex);
1349 				return (rval);
1350 			}
1351 		}
1352 	}
1353 
1354 	mutex_exit(&sbbcsoftp->sbbc_intr_mutex);
1355 
1356 	/* for now do not claim since we know its not enabled */
1357 	return (DDI_INTR_UNCLAIMED);
1358 }
1359 
1360 
1361 /*
1362  * This function checks an SBBC register offset to make sure that it is properly
1363  * aligned (i.e. on a 16-byte boundary) and that it corresponds to an accessible
1364  * register.  Since the SBBC treates accesses to unaligned or reserved addresses
1365  * as unmapped, failing to check for these would leave a loophole that could be
1366  * used to crash the system.
1367  */
1368 static int
1369 sbbc_offset_valid(uint32_t offset) {
1370 	/*
1371 	 * Check for proper alignment first.
1372 	 */
1373 	if ((offset % 16) != 0) {
1374 		return (0);
1375 	}
1376 
1377 	/*
1378 	 * Now start checking for the various reserved ranges.
1379 	 * While sticking a bunch of constants in the code (rather than
1380 	 * #define'd values) is usually best avoided, it would probably
1381 	 * do more harm than good here.  These values were taken from the
1382 	 * Serengeti Architecture Programmer's Reference Manual dated
1383 	 * August 10, 1999, pages 2-99 through 2-103.  While there are
1384 	 * various "clever" ways this check could be performed that would
1385 	 * be slightly more efficient, arranging the code in this fashion
1386 	 * should maximize maintainability.
1387 	 */
1388 	if (((offset >= 0x001a0) && (offset <= 0x001ff)) ||
1389 	    ((offset >= 0x002a0) && (offset <= 0x002ff)) ||
1390 	    ((offset >= 0x00350) && (offset <= 0x003ff)) ||
1391 	    ((offset >= 0x00500) && (offset <= 0x00fff)) ||
1392 	    ((offset >= 0x01160) && (offset <= 0x011ff)) ||
1393 	    ((offset >= 0x01210) && (offset <= 0x017ff)) ||
1394 	    ((offset >= 0x01810) && (offset <= 0x01fff)) ||
1395 	    ((offset >= 0x02030) && (offset <= 0x022ff)) ||
1396 	    ((offset >= 0x02340) && (offset <= 0x03fff)) ||
1397 	    ((offset >= 0x04030) && (offset <= 0x05fff)) ||
1398 	    ((offset >= 0x060a0) && (offset <= 0x060ff)) ||
1399 	    (offset == 0x06120) ||
1400 	    ((offset >= 0x06190) && (offset <= 0x061ff)) ||
1401 	    ((offset >= 0x06230) && (offset <= 0x062f0)) ||
1402 	    (offset > 0x06320)) {
1403 		return (0);
1404 	}
1405 
1406 	return (1);
1407 }
1408 
1409 #ifdef DEBUG
1410 void
1411 sbbc_dbg(uint32_t flag, dev_info_t *dip, char *fmt,
1412 	uintptr_t a1, uintptr_t a2, uintptr_t a3, uintptr_t a4, uintptr_t a5)
1413 {
1414 	char *s = NULL;
1415 
1416 	if (sbbc_dbg_flags && ((sbbc_dbg_flags & flag) == flag)) {
1417 		switch (flag) {
1418 		case SBBC_DBG_ATTACH:
1419 			s = "attach";
1420 			break;
1421 		case SBBC_DBG_DETACH:
1422 			s = "detach";
1423 			break;
1424 		case SBBC_DBG_CTLOPS:
1425 			s = "ctlops";
1426 			break;
1427 		case SBBC_DBG_INITCHILD:
1428 			s = "initchild";
1429 			break;
1430 		case SBBC_DBG_UNINITCHILD:
1431 			s = "uninitchild";
1432 			break;
1433 		case SBBC_DBG_BUSMAP:
1434 			s = "busmap";
1435 			break;
1436 		case SBBC_DBG_INTR:
1437 			s = "intr";
1438 			break;
1439 		case SBBC_DBG_INTROPS:
1440 			s = "intr_ops";
1441 			break;
1442 		case SBBC_DBG_PCICONF:
1443 			s = "pciconfig";
1444 			break;
1445 		case SBBC_DBG_MAPRANGES:
1446 			s = "mapranges";
1447 			break;
1448 		case SBBC_DBG_PROPERTIES:
1449 			s = "properties";
1450 			break;
1451 		case SBBC_DBG_OPEN:
1452 			s = "open";
1453 			break;
1454 		case SBBC_DBG_CLOSE:
1455 			s = "close";
1456 			break;
1457 		case SBBC_DBG_IOCTL:
1458 			s = "ioctl";
1459 			break;
1460 		default:
1461 			s = "Unknown debug flag";
1462 			break;
1463 		}
1464 
1465 		cmn_err(CE_CONT, "%s_%s(%d): ", ddi_driver_name(dip), s,
1466 			ddi_get_instance(dip));
1467 		cmn_err(CE_CONT, fmt, a1, a2, a3, a4, a5);
1468 	}
1469 }
1470 
1471 /*
1472  * Dump the SBBC chip's Device ID Register
1473  */
1474 static void sbbc_dump_devid(dev_info_t *dip, struct sbbcsoft *sbbcsoftp,
1475 	int instance)
1476 {
1477 	uint32_t sbbc_id_reg;
1478 	uint16_t sbbc_id_reg_partid;
1479 	uint16_t sbbc_id_reg_manfid;
1480 
1481 	sbbc_id_reg = ddi_get32(sbbcsoftp->pci_sbbc_map_handle,
1482 	    (uint32_t *)&sbbcsoftp->pci_sbbc_map->devid);
1483 
1484 	sbbc_id_reg_partid = ((sbbc_id_reg << 4) >> 16);
1485 	sbbc_id_reg_manfid = ((sbbc_id_reg << 20) >> 21);
1486 
1487 	SBBC_DBG4(SBBC_DBG_ATTACH, dip,
1488 	    "FOUND SBBC(%d) Version %x, Partid %x, Manfid %x\n",
1489 	    instance, (sbbc_id_reg >> 28), sbbc_id_reg_partid,
1490 	    sbbc_id_reg_manfid);
1491 }
1492 
1493 #endif /* DEBUG */
1494