xref: /illumos-gate/usr/src/uts/sun4u/io/sbbc.c (revision 4bba12ca5cd6f92aaf0d4c0d19d05528110bc095)
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 2008 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 
27 /*
28  * Starcat PCI SBBC Nexus Driver.
29  *
30  * This source code's compiled binary runs on both a Starcat System
31  * Controller (SSC) and a Starcat Domain.  One of the SBBC hardware
32  * registers is read during attach(9e) in order to determine which
33  * environment the driver is executing on.
34  *
35  * On both the SSC and the Domain, this driver provides nexus driver
36  * services to its Device Tree children.  Note that the children in
37  * each environment are not necessarily the same.
38  *
39  * This driver allows one concurrent open(2) of its associated device
40  * (/dev/sbbc0).  The client uses the file descriptor to issue
41  * ioctl(2)'s in order to read and write from the 2MB (PCI) space
42  * reserved for "SBBC Internal Registers".  Among other things,
43  * these registers consist of command/control/status registers for
44  * devices such as Console Bus, I2C, EPLD, IOSRAM, and JTAG.  The 2MB
45  * space is very sparse; EINVAL is returned if a reserved or unaligned
46  * address is specified in the ioctl(2).
47  *
48  * Note that the 2MB region reserved for SBBC Internal Registers is
49  * a subset of the 128MB of PCI address space addressable by the SBBC
50  * ASIC.  Address space outside of the 2MB (such as the 64MB reserved
51  * for the Console Bus) is not accessible via this driver.
52  *
53  * Also, note that the SBBC Internal Registers are only read and
54  * written by the SSC; no process on the Domain accesses these
55  * registers.  As a result, the registers are unmapped (when running
56  * on the Domain) near the end of attach(9e) processing.  This conserves
57  * kernel virtual address space resources (as one instance of the driver
58  * is created for each Domain-side IO assembly).  (To be complete, only
59  * one instance of the driver is created on the SSC).
60  */
61 
62 #include <sys/types.h>
63 
64 #include <sys/conf.h>		/* req. by dev_ops flags MTSAFE etc. */
65 #include <sys/ddi.h>
66 #include <sys/sunddi.h>
67 #include <sys/ddi_impldefs.h>
68 #include <sys/ddi_subrdefs.h>
69 #include <sys/pci.h>
70 #include <sys/pci/pci_nexus.h>
71 #include <sys/autoconf.h>
72 #include <sys/cmn_err.h>
73 #include <sys/param.h>
74 #include <sys/errno.h>
75 #include <sys/kmem.h>
76 #include <sys/debug.h>
77 #include <sys/sysmacros.h>
78 #include <sys/machsystm.h>
79 #include <sys/modctl.h>
80 #include <sys/stat.h>
81 
82 
83 #include <sys/sbbcreg.h>	/* hw description */
84 #include <sys/sbbcvar.h>	/* driver description */
85 #include <sys/sbbcio.h>		/* ioctl description */
86 
87 #define	getprop(dip, name, addr, intp)		\
88 		ddi_getlongprop(DDI_DEV_T_ANY, (dip), DDI_PROP_DONTPASS, \
89 				(name), (caddr_t)(addr), (intp))
90 
91 /* driver entry point fn definitions */
92 static int sbbc_open(dev_t *, int, int, cred_t *);
93 static int sbbc_close(dev_t, int, int, cred_t *);
94 static int sbbc_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
95 
96 /* configuration entry point fn definitions */
97 static int sbbc_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **);
98 static int sbbc_attach(dev_info_t *, ddi_attach_cmd_t);
99 static int sbbc_detach(dev_info_t *, ddi_detach_cmd_t);
100 
101 /* local utility routines */
102 /*
103  * NOTE - sbbc_offset_valid contains detailed address information taken from
104  * the Serengeti Architecture Programmer's Reference Manual.  If any
105  * changes are made to the SBBC registers, this routine may need to be
106  * updated.
107  */
108 static int sbbc_offset_valid(uint32_t offset);
109 
110 /*
111  * function prototypes for bus ops routines:
112  */
113 static int sbbc_busmap(dev_info_t *dip, dev_info_t *rdip, ddi_map_req_t *mp,
114 	off_t offset, off_t len, caddr_t *addrp);
115 static int sbbc_ctlops(dev_info_t *dip, dev_info_t *rdip,
116 	ddi_ctl_enum_t op, void *arg, void *result);
117 
118 static int sbbc_intr_ops(dev_info_t *dip, dev_info_t *rdip,
119 	ddi_intr_op_t intr_op, ddi_intr_handle_impl_t *hdlp, void *result);
120 static int sbbc_add_intr_impl(dev_info_t *dip, dev_info_t *rdip,
121 	ddi_intr_op_t intr_op, ddi_intr_handle_impl_t *hdlp, void *result);
122 static int sbbc_remove_intr_impl(dev_info_t *dip, dev_info_t *rdip,
123 	ddi_intr_op_t intr_op, ddi_intr_handle_impl_t *hdlp, void *result);
124 static int sbbc_update_intr_state(dev_info_t *dip, dev_info_t *rdip,
125 	ddi_intr_op_t intr_op, ddi_intr_handle_impl_t *hdlp, void *result);
126 
127 static int sbbc_apply_range(struct sbbcsoft *sbbc_p, dev_info_t *rdip,
128     sbbc_child_regspec_t *child_rp, pci_regspec_t *rp);
129 
130 static int sbbc_init(struct sbbcsoft *);
131 
132 static uint_t sbbc_intr_wrapper(caddr_t arg);
133 
134 static int sbbc_get_ranges(struct sbbcsoft *);
135 static int sbbc_config4pci(struct sbbcsoft *);
136 static int sbbc_initchild(dev_info_t *, dev_info_t *, dev_info_t *);
137 static int sbbc_uninitchild(dev_info_t *, dev_info_t *);
138 static void sbbc_remove_reg_maps(struct sbbcsoft *);
139 
140 /* debugging functions */
141 #ifdef DEBUG
142 uint32_t sbbc_dbg_flags = 0x0;
143 static void sbbc_dbg(uint32_t flag, dev_info_t *dip, char *fmt,
144 	uintptr_t a1, uintptr_t a2, uintptr_t a3, uintptr_t a4, uintptr_t a5);
145 static void sbbc_dump_devid(dev_info_t *, struct sbbcsoft *, int instance);
146 #endif
147 
148 /*
149  * For tracing, allocate space for the trace buffer
150  */
151 #if defined(SBBC_TRACE)
152 struct sbbctrace sbbctrace_buffer[NSBBCTRACE+1];
153 struct sbbctrace *sbbctrace_ptr;
154 int sbbctrace_count;
155 #endif
156 
157 /*
158  * Local declarations and variables
159  */
160 
161 static void *sbbcsoft_statep;
162 
163 /* Determines whether driver is executing on System Controller or Domain */
164 int sbbc_scmode = FALSE;
165 
166 /*
167  * ops stuff.
168  */
169 static struct bus_ops sbbc_bus_ops = {
170 	BUSO_REV,
171 	sbbc_busmap,
172 	0,
173 	0,
174 	0,
175 	NULL,			/* (*bus_map_fault)() */
176 	ddi_no_dma_map,
177 	ddi_no_dma_allochdl,
178 	ddi_no_dma_freehdl,	/* (*bus_dma_freehdl)() */
179 	ddi_no_dma_bindhdl,	/* (*bus_dma_bindhdl)() */
180 	ddi_no_dma_unbindhdl,	/* (*bus_dma_unbindhdl)() */
181 	ddi_no_dma_flush,	/* (*bus_dma_flush)() */
182 	ddi_no_dma_win,		/* (*bus_dma_win)() */
183 	ddi_no_dma_mctl,	/* (*bus_dma_ctl)() */
184 	sbbc_ctlops,
185 	ddi_bus_prop_op,
186 	0,			/* (*bus_get_eventcookie)();	*/
187 	0,			/* (*bus_add_eventcall)();	*/
188 	0,			/* (*bus_remove_eventcall)();	*/
189 	0,			/* (*bus_post_event)();		*/
190 	0,			/* (*bus_intr_ctl)();	*/
191 	0,			/* (*bus_config)();	*/
192 	0,			/* (*bus_unconfig)();	*/
193 	0,			/* (*bus_fm_init)();	*/
194 	0,			/* (*bus_fm_fini)();	*/
195 	0,			/* (*bus_fm_access_enter)();	*/
196 	0,			/* (*bus_fm_access_exit)();	*/
197 	0,			/* (*bus_power)();	*/
198 	sbbc_intr_ops		/* (*bus_intr_op)();	*/
199 };
200 
201 /*
202  * cb_ops
203  */
204 static struct cb_ops sbbc_cb_ops = {
205 	sbbc_open,		/* cb_open */
206 	sbbc_close,		/* cb_close */
207 	nodev,			/* cb_strategy */
208 	nodev,			/* cb_print */
209 	nodev,			/* cb_dump */
210 	nodev,			/* cb_read */
211 	nodev,			/* cb_write */
212 	sbbc_ioctl,		/* cb_ioctl */
213 	nodev,			/* cb_devmap */
214 	nodev,			/* cb_mmap */
215 	nodev,			/* cb_segmap */
216 	nochpoll,		/* cb_chpoll */
217 	ddi_prop_op,		/* cb_prop_op */
218 	NULL,			/* cb_stream */
219 	(int)(D_NEW | D_MP)	/* cb_flag */
220 };
221 
222 /*
223  * Declare ops vectors for auto configuration.
224  */
225 struct dev_ops  sbbc_ops = {
226 	DEVO_REV,		/* devo_rev */
227 	0,			/* devo_refcnt */
228 	sbbc_getinfo,		/* devo_getinfo */
229 	nulldev,		/* devo_identify */
230 	nulldev,		/* devo_probe */
231 	sbbc_attach,		/* devo_attach */
232 	sbbc_detach,		/* devo_detach */
233 	nodev,			/* devo_reset */
234 	&sbbc_cb_ops,		/* devo_cb_ops */
235 	&sbbc_bus_ops,		/* devo_bus_ops */
236 	nulldev,			/* devo_power */
237 	ddi_quiesce_not_supported,	/* devo_quiesce */
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",
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 	    0) == 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] == NULL) {
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 		if (i < MAX_SBBC_DEVICES)
709 			sbbcsoftp->child_intr[i] = NULL;
710 	}
711 
712 	/*
713 	 * Restore original interrupt handler
714 	 * and arguments in interrupt handle.
715 	 */
716 	DDI_INTR_ASSIGN_HDLR_N_ARGS(hdlp, childintr->intr_handler,
717 	    childintr->arg1, childintr->arg2);
718 
719 	return (rval);
720 }
721 
722 static int
723 sbbc_remove_intr_impl(dev_info_t *dip, dev_info_t *rdip, ddi_intr_op_t intr_op,
724     ddi_intr_handle_impl_t *hdlp, void *result)
725 {
726 	sbbcsoft_t *sbbcsoftp;
727 	sbbc_child_intr_t *childintr;
728 	int instance, i, rval = DDI_SUCCESS;
729 
730 	SBBC_DBG2(SBBC_DBG_INTR, dip,
731 	    "remove: rdip 0x%llx hdlp 0x%llx\n", rdip, hdlp);
732 
733 	instance = ddi_get_instance(dip);
734 	if (!(sbbcsoftp = ddi_get_soft_state(sbbcsoft_statep, instance)))
735 		return (DDI_FAILURE);
736 
737 	/* remove the sbbc isr wrapper instead */
738 	for (i = 0; i < MAX_SBBC_DEVICES; i++) {
739 		if (sbbcsoftp->child_intr[i]) {
740 			childintr = sbbcsoftp->child_intr[i];
741 			if (childintr->status == SBBC_INTR_STATE_DISABLE &&
742 			    childintr->name == ddi_get_name(rdip)) {
743 				/* put back child's inum */
744 				hdlp->ih_inum = childintr->inum;
745 				break;
746 			}
747 		}
748 	}
749 
750 	if (i >= MAX_SBBC_DEVICES) {
751 		cmn_err(CE_WARN, "sbbc%d:obound failed to remove intr for %s",
752 		    instance, ddi_get_name(rdip));
753 		return (DDI_FAILURE);
754 	}
755 
756 	if ((rval = i_ddi_intr_ops(dip, rdip, intr_op,
757 	    hdlp, result)) != DDI_SUCCESS) {
758 		cmn_err(CE_WARN, "sbbc%d: failed to remove intr for %s",
759 		    instance, ddi_get_name(rdip));
760 		return (rval);
761 	}
762 
763 	kmem_free(childintr, sizeof (struct sbbc_child_intr));
764 	sbbcsoftp->child_intr[i] = NULL;
765 
766 	return (rval);
767 }
768 
769 
770 static int
771 sbbc_update_intr_state(dev_info_t *dip, dev_info_t *rdip, ddi_intr_op_t intr_op,
772     ddi_intr_handle_impl_t *hdlp, void *result)
773 {
774 	sbbcsoft_t		*sbbcsoftp;
775 	sbbc_child_intr_t	*childintr;
776 	int			instance, i;
777 	int			ret = DDI_SUCCESS;
778 
779 	SBBC_DBG2(SBBC_DBG_INTR, dip, "sbbc_update_intr_state: "
780 	    "rdip 0x%llx hdlp 0x%llx state 0x%x\n", rdip, hdlp);
781 
782 	instance = ddi_get_instance(dip);
783 	if (!(sbbcsoftp = ddi_get_soft_state(sbbcsoft_statep, instance)))
784 		return (DDI_FAILURE);
785 
786 	for (i = 0; i < MAX_SBBC_DEVICES; i++) {
787 		if (sbbcsoftp->child_intr[i]) {
788 			childintr = sbbcsoftp->child_intr[i];
789 			if (childintr->name == ddi_get_name(rdip))
790 				break;
791 		}
792 	}
793 
794 	if (i >= MAX_SBBC_DEVICES) {
795 		cmn_err(CE_WARN, "sbbc%d: failed to update intr state for %s",
796 		    instance, ddi_get_name(rdip));
797 		return (DDI_FAILURE);
798 	}
799 
800 	if ((ret = i_ddi_intr_ops(dip, rdip, intr_op,
801 	    hdlp, result)) != DDI_SUCCESS) {
802 		cmn_err(CE_WARN, "sbbc%d: failed to update intr state for %s",
803 		    instance, ddi_get_name(rdip));
804 		return (ret);
805 	}
806 
807 	/* Update the interrupt state */
808 	childintr->status = (intr_op == DDI_INTROP_ENABLE) ?
809 	    SBBC_INTR_STATE_ENABLE : SBBC_INTR_STATE_DISABLE;
810 
811 	return (ret);
812 }
813 
814 
815 /*
816  * This entry point is called before a child's probe or attach is called.
817  * The arg pointer points to child's dev_info_t structure.
818  */
819 static int
820 sbbc_ctlops(dev_info_t *dip, dev_info_t *rdip, ddi_ctl_enum_t op,
821     void *arg, void *result)
822 {
823 	sbbc_child_regspec_t *child_rp;
824 	int i, n;
825 
826 	SBBC_DBG3(SBBC_DBG_CTLOPS, dip,
827 	    "Initializing %s, arg %x, op %x\n",
828 	    ddi_driver_name(rdip), arg, op);
829 
830 	SBBCTRACE(sbbc_ctlops, 'CTLO', arg);
831 
832 	switch (op) {
833 	case DDI_CTLOPS_INITCHILD: {
834 		return (sbbc_initchild(dip, rdip, (dev_info_t *)arg));
835 	}
836 
837 	case DDI_CTLOPS_UNINITCHILD: {
838 		return (sbbc_uninitchild(rdip, (dev_info_t *)arg));
839 	}
840 
841 	case DDI_CTLOPS_REPORTDEV:
842 
843 		cmn_err(CE_CONT, "?%s%d at %s%d: offset %s\n",
844 		    ddi_driver_name(rdip), ddi_get_instance(rdip),
845 		    ddi_driver_name(dip), ddi_get_instance(dip),
846 		    ddi_get_name_addr(rdip));
847 		return (DDI_SUCCESS);
848 
849 	case DDI_CTLOPS_REGSIZE:
850 
851 		if (getprop(rdip, "reg", &child_rp, &i) != DDI_SUCCESS) {
852 			return (DDI_FAILURE);
853 		}
854 		n = i / sizeof (sbbc_child_regspec_t);
855 		if (*(int *)arg < 0 || *(int *)arg >= n) {
856 			kmem_free(child_rp, i);
857 			return (DDI_FAILURE);
858 		}
859 		*((off_t *)result) = child_rp[*(int *)arg].size;
860 		kmem_free(child_rp, i);
861 		return (DDI_SUCCESS);
862 
863 	case DDI_CTLOPS_NREGS:
864 
865 		if (getprop(rdip, "reg", &child_rp, &i) != DDI_SUCCESS) {
866 			return (DDI_FAILURE);
867 		}
868 		*((uint_t *)result) = i / sizeof (sbbc_child_regspec_t);
869 		kmem_free(child_rp, i);
870 		return (DDI_SUCCESS);
871 	}
872 
873 	/*
874 	 * Now pass the request up to our parent.
875 	 */
876 	SBBC_DBG0(SBBC_DBG_CTLOPS, dip, "Calling ddi_ctlops\n");
877 
878 	return (ddi_ctlops(dip, rdip, op, arg, result));
879 }
880 
881 
882 /*
883  * The following routine uses ranges property, that was read earlier, and
884  * takes child's reg property, and computes the complete address and size
885  * for the PCI parent to map.
886  */
887 static int
888 sbbc_apply_range(struct sbbcsoft *sbbc_p, dev_info_t *rdip,
889     sbbc_child_regspec_t *child_rp, pci_regspec_t *rp)
890 {
891 	int b;
892 	int rval = DDI_SUCCESS;
893 	struct sbbc_pci_rangespec *rangep = sbbc_p->rangep;
894 	int nrange = sbbc_p->range_cnt;
895 
896 	SBBC_DBG4(SBBC_DBG_MAPRANGES, rdip,
897 	    "Applying ranges for %s, rangep %llx, child_rp %llx, range %x\n",
898 	    ddi_driver_name(rdip), sbbc_p->rangep, child_rp, nrange);
899 
900 	SBBCTRACE(sbbc_apply_range, 'APPL', sbbc_p);
901 
902 	for (b = 0; b < nrange; ++b, ++rangep) {
903 
904 		/* Make sure the correct range is being mapped */
905 		if (child_rp->addr_hi == rangep->sbbc_phys_hi)
906 			/* See if we fit in this range */
907 			if ((child_rp->addr_low >=
908 			    rangep->sbbc_phys_low) &&
909 			    ((child_rp->addr_low + child_rp->size - 1)
910 			    <= (rangep->sbbc_phys_low +
911 			    rangep->rng_size - 1))) {
912 				uint_t addr_offset = child_rp->addr_low -
913 				    rangep->sbbc_phys_low;
914 				/*
915 				 * Use the range entry to translate
916 				 * the SBBC physical address into the
917 				 * parents PCI space.
918 				 */
919 				rp->pci_phys_hi =
920 				    rangep->pci_phys_hi;
921 				rp->pci_phys_mid = rangep->pci_phys_mid;
922 				rp->pci_phys_low =
923 				    rangep->pci_phys_low + addr_offset;
924 				rp->pci_size_hi = 0;
925 				rp->pci_size_low =
926 				    min(child_rp->size, (rangep->rng_size -
927 				    addr_offset));
928 
929 				break;
930 			}
931 	}
932 
933 	if (b == nrange)  {
934 		cmn_err(CE_WARN, "out_of_range %s", ddi_get_name(rdip));
935 		return (DDI_ME_REGSPEC_RANGE);
936 	}
937 
938 	return (rval);
939 }
940 
941 
942 /*
943  * The following routine reads sbbc's ranges property from OBP and sets up
944  * its soft structure with it.
945  */
946 static int
947 sbbc_get_ranges(struct sbbcsoft *sbbcsoftp)
948 {
949 	struct sbbc_pci_rangespec *rangep;
950 	int range_len, nrange;
951 
952 	if (ddi_getlongprop(DDI_DEV_T_ANY, sbbcsoftp->dip, DDI_PROP_DONTPASS,
953 	    "ranges", (caddr_t)&rangep, &range_len) != DDI_SUCCESS) {
954 		cmn_err(CE_WARN, "SBBC: couldn't get %s ranges property %d",
955 		    ddi_get_name(sbbcsoftp->dip), sbbcsoftp->instance);
956 		return (DDI_ME_REGSPEC_RANGE);
957 	}
958 
959 	nrange = range_len / sizeof (struct sbbc_pci_rangespec);
960 
961 	if (!nrange) {
962 		kmem_free(rangep, range_len);
963 		return (DDI_FAILURE);
964 	}
965 
966 	/* setup the soft structure with ranges info. */
967 	sbbcsoftp->rangep = rangep;
968 	sbbcsoftp->range_cnt = nrange;
969 	sbbcsoftp->range_len = range_len;
970 
971 	return (DDI_SUCCESS);
972 }
973 
974 
975 /*
976  * Configure the SBBC for PCI
977  */
978 static int
979 sbbc_config4pci(struct sbbcsoft *sbbcsoftp)
980 {
981 	ddi_acc_handle_t conf_handle;
982 	uint16_t comm, vendid, devid, stat;
983 	uint8_t revid;
984 
985 #ifdef DEBUG
986 	if (sbbc_dbg_flags & SBBC_DBG_PCICONF) {
987 		cmn_err(CE_CONT,
988 		    "sbbc_config4pci: sbbcsoftp %p\n", (void *)sbbcsoftp);
989 	}
990 #endif
991 	if (pci_config_setup(sbbcsoftp->dip, &conf_handle) != DDI_SUCCESS)
992 		return (1);
993 
994 	vendid = pci_config_get16(conf_handle, PCI_CONF_VENID);
995 	devid = pci_config_get16(conf_handle, PCI_CONF_DEVID);
996 	comm = pci_config_get16(conf_handle, PCI_CONF_COMM);
997 	stat = pci_config_get16(conf_handle, PCI_CONF_STAT);
998 	revid = pci_config_get8(conf_handle, PCI_CONF_REVID);
999 
1000 #ifdef DEBUG
1001 	if (sbbc_dbg_flags & SBBC_DBG_PCICONF) {
1002 		cmn_err(CE_CONT,
1003 		    "SBBC vendid %x, devid %x, comm %x, stat %x, revid %x\n",
1004 		    vendid, devid, comm, stat, revid);
1005 	}
1006 #endif
1007 	comm = (PCI_COMM_ME | PCI_COMM_MAE | PCI_COMM_SERR_ENABLE |
1008 	    PCI_COMM_PARITY_DETECT);
1009 
1010 	pci_config_put16(conf_handle, PCI_CONF_COMM, comm);
1011 
1012 	comm = pci_config_get16(conf_handle, PCI_CONF_COMM);
1013 
1014 #ifdef DEBUG
1015 	if (sbbc_dbg_flags & SBBC_DBG_PCICONF) {
1016 		cmn_err(CE_CONT, "comm %x\n", comm);
1017 	}
1018 #endif
1019 	pci_config_teardown(&conf_handle);
1020 
1021 	return (0);
1022 }
1023 
1024 
1025 /* ARGSUSED0 */
1026 int
1027 sbbc_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
1028 {
1029 	dev_t	dev = (dev_t)arg;
1030 	struct sbbcsoft *sbbcsoftp;
1031 	int	instance, ret;
1032 
1033 	instance = getminor(dev);
1034 
1035 	SBBCTRACE(sbbc_getinfo, 'GINF', instance);
1036 
1037 	switch (infocmd) {
1038 		case DDI_INFO_DEVT2DEVINFO:
1039 			sbbcsoftp = (struct sbbcsoft *)
1040 			    ddi_get_soft_state(sbbcsoft_statep, instance);
1041 			if (sbbcsoftp == NULL) {
1042 				*result = (void *) NULL;
1043 				ret = DDI_FAILURE;
1044 			} else {
1045 				*result = sbbcsoftp->dip;
1046 				ret = DDI_SUCCESS;
1047 			}
1048 			break;
1049 		case DDI_INFO_DEVT2INSTANCE:
1050 			*result = (void *)(uintptr_t)instance;
1051 			ret = DDI_SUCCESS;
1052 			break;
1053 		default:
1054 			ret = DDI_FAILURE;
1055 			break;
1056 	}
1057 
1058 	return (ret);
1059 }
1060 
1061 /*ARGSUSED1*/
1062 static int
1063 sbbc_open(dev_t *dev, int flag, int otype, cred_t *credp)
1064 {
1065 	struct sbbcsoft *sbbcsoftp;
1066 	int		instance;
1067 
1068 	/* check privilege of caller process */
1069 	if (drv_priv(credp)) {
1070 		return (EPERM);
1071 	}
1072 
1073 	instance = getminor(*dev);
1074 	if (instance < 0)
1075 		return (ENXIO);
1076 	sbbcsoftp = (struct sbbcsoft *)ddi_get_soft_state(sbbcsoft_statep,
1077 	    instance);
1078 	SBBCTRACE(sbbc_open, 'OPEN', sbbcsoftp);
1079 
1080 	if (sbbcsoftp == NULL)
1081 		return (ENXIO);
1082 
1083 	mutex_enter(&sbbcsoftp->umutex);
1084 
1085 	/* check for exclusive access */
1086 	if ((sbbcsoftp->oflag == TRUE)) {
1087 		mutex_exit(&sbbcsoftp->umutex);
1088 		return (EBUSY);
1089 	}
1090 	sbbcsoftp->oflag = TRUE;
1091 
1092 	mutex_exit(&sbbcsoftp->umutex);
1093 
1094 	return (0);
1095 }
1096 
1097 /*ARGSUSED1*/
1098 static int
1099 sbbc_close(dev_t dev, int flag, int otype, cred_t *credp)
1100 {
1101 	struct sbbcsoft *sbbcsoftp;
1102 	int		instance;
1103 
1104 	instance = getminor(dev);
1105 	if (instance < 0)
1106 		return (ENXIO);
1107 	sbbcsoftp = (struct sbbcsoft *)ddi_get_soft_state(sbbcsoft_statep,
1108 	    instance);
1109 	/* wait till all output activity has ceased */
1110 
1111 	mutex_enter(&sbbcsoftp->umutex);
1112 
1113 	SBBCTRACE(sbbc_close, 'CLOS', sbbcsoftp);
1114 
1115 	sbbcsoftp->oflag = FALSE;
1116 
1117 	mutex_exit(&sbbcsoftp->umutex);
1118 
1119 	return (0);
1120 }
1121 
1122 /*ARGSUSED2*/
1123 static int
1124 sbbc_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp,
1125     int *rvalp)
1126 {
1127 	struct sbbcsoft *sbbcsoftp;
1128 
1129 	SBBCTRACE(sbbc_ioctl, 'IOCT', arg);
1130 
1131 	sbbcsoftp = ddi_get_soft_state(sbbcsoft_statep, getminor(dev));
1132 
1133 	if (sbbcsoftp == NULL) {
1134 		return (ENXIO);
1135 	}
1136 
1137 	switch (cmd) {
1138 	case SBBC_SBBCREG_WR:
1139 		{
1140 		struct ssc_sbbc_regio sbbcregs;
1141 		uint64_t offset;
1142 
1143 		if (sbbc_scmode == FALSE) {
1144 			/* then we're executing on Domain; Writes not allowed */
1145 			return (EINVAL);
1146 		}
1147 
1148 		if (arg == (intptr_t)NULL) {
1149 			return (ENXIO);
1150 		}
1151 
1152 		if (ddi_copyin((caddr_t)arg, (caddr_t)&sbbcregs,
1153 				    sizeof (struct ssc_sbbc_regio), mode)) {
1154 			cmn_err(CE_WARN, "sbbc_ioctl: copyin failed arg %p",
1155 			    (void *)arg);
1156 			return (EFAULT);
1157 		}
1158 
1159 		/*
1160 		 * Bug #4287186: SBBC driver on cp1500 doesn't check length for
1161 		 *		reads or writes
1162 		 * Note that I've also added a check to make sure the offset is
1163 		 * valid, since misaligned (i.e. not on 16-byte boundary)
1164 		 * accesses or accesses to "Reserved" register offsets are
1165 		 * treated as unmapped by the SBBC.
1166 		 */
1167 		if ((sbbcregs.len != 4) ||
1168 		    !sbbc_offset_valid(sbbcregs.offset)) {
1169 			return (EINVAL);
1170 		}
1171 
1172 		offset = (uint64_t)sbbcsoftp->pci_sbbc_map;
1173 		offset += sbbcregs.offset;
1174 		ddi_put32(sbbcsoftp->pci_sbbc_map_handle, (uint32_t *)offset,
1175 		    sbbcregs.value);
1176 		}
1177 		break;
1178 	case SBBC_SBBCREG_RD:
1179 		{
1180 		struct ssc_sbbc_regio sbbcregs;
1181 		uint64_t offset;
1182 
1183 		if (sbbc_scmode == FALSE) {
1184 			/* then we're executing on Domain; Reads not allowed */
1185 			return (EINVAL);
1186 		}
1187 
1188 		if (arg == (intptr_t)NULL) {
1189 			return (ENXIO);
1190 		}
1191 
1192 		if (ddi_copyin((caddr_t)arg, (caddr_t)&sbbcregs,
1193 				    sizeof (struct ssc_sbbc_regio), mode)) {
1194 			cmn_err(CE_WARN, "sbbc_ioctl: copyin failed arg %p",
1195 			    (void *)arg);
1196 			return (EFAULT);
1197 		}
1198 
1199 		/*
1200 		 * Bug #4287186: SBBC driver on cp1500 doesn't check length for
1201 		 *		reads or writes
1202 		 * Note that I've also added a check to make sure the offset is
1203 		 * valid, since misaligned (i.e. not on 16-byte boundary)
1204 		 * accesses or accesses to "Reserved" register offsets are
1205 		 * treated as unmapped by the SBBC.
1206 		 */
1207 		if ((sbbcregs.len != 4) ||
1208 		    !sbbc_offset_valid(sbbcregs.offset)) {
1209 			return (EINVAL);
1210 		}
1211 
1212 		offset = (uint64_t)sbbcsoftp->pci_sbbc_map;
1213 		offset += sbbcregs.offset;
1214 
1215 		sbbcregs.value = ddi_get32(sbbcsoftp->pci_sbbc_map_handle,
1216 		    (uint32_t *)offset);
1217 
1218 		if (ddi_copyout((caddr_t)&sbbcregs.value,
1219 		    &((struct ssc_sbbc_regio *)arg)->value,
1220 		    sbbcregs.len, mode)) {
1221 			cmn_err(CE_WARN, "sbbc_ioctl:copyout failed arg %p",
1222 			    (void *)arg);
1223 			return (EFAULT);
1224 		}
1225 		}
1226 		break;
1227 	default:
1228 		cmn_err(CE_WARN, "sbbc_ioctl:Illegal command 0x%08x", cmd);
1229 		return (ENOTTY);
1230 	}
1231 
1232 	return (DDI_SUCCESS);
1233 }
1234 
1235 static void
1236 sbbc_remove_reg_maps(struct sbbcsoft *sbbcsoftp)
1237 {
1238 	SBBCTRACE(sbbc_remove_reg_maps, 'RMAP', sbbcsoftp);
1239 	if (sbbcsoftp->pci_sbbc_map_handle)
1240 		ddi_regs_map_free(&sbbcsoftp->pci_sbbc_map_handle);
1241 }
1242 
1243 
1244 static int
1245 sbbc_init(struct sbbcsoft *sbbcsoftp)
1246 {
1247 	/* Mask all the interrupts until we are ready. */
1248 	ddi_put32(sbbcsoftp->pci_sbbc_map_handle,
1249 	    &sbbcsoftp->pci_sbbc_map->sys_intr_enable,
1250 	    0x00000000);
1251 
1252 	return (1);
1253 }
1254 
1255 /*
1256  * The following routine is a generic routine to initialize any child of
1257  * sbbc nexus driver information into parent private data structure.
1258  */
1259 /* ARGSUSED0 */
1260 static int
1261 sbbc_initchild(dev_info_t *dip, dev_info_t *rdip, dev_info_t *child)
1262 {
1263 	sbbc_child_regspec_t *child_rp;
1264 	int reglen, slot;
1265 	char name[10];
1266 
1267 	SBBC_DBG1(SBBC_DBG_INITCHILD, dip, "Initializing %s\n",
1268 	    ddi_driver_name(rdip));
1269 
1270 	/*
1271 	 * Initialize a child
1272 	 * Set the address portion of the node name based on the
1273 	 * address/offset.
1274 	 */
1275 	if (ddi_getlongprop(DDI_DEV_T_ANY, child, DDI_PROP_DONTPASS,
1276 	    "reg", (caddr_t)&child_rp, &reglen) != DDI_SUCCESS) {
1277 		if (strcmp(ddi_node_name(child), "hotplug-controller") == 0) {
1278 			slot = 1;
1279 			(void) sprintf(name, "%x", slot);
1280 			ddi_set_name_addr(child, name);
1281 			return (DDI_SUCCESS);
1282 		}
1283 		return (DDI_FAILURE);
1284 	}
1285 
1286 	SBBC_DBG3(SBBC_DBG_INITCHILD, dip, "hi 0x%x, low 0x%x, size 0x%x\n",
1287 	    child_rp->addr_hi, child_rp->addr_low, child_rp->size);
1288 
1289 	(void) sprintf(name, "%x,%x", child_rp->addr_hi, child_rp->addr_low);
1290 
1291 	/*
1292 	 * set child's addresses from the reg property into parent private
1293 	 * data structure.
1294 	 */
1295 	ddi_set_name_addr(child, name);
1296 	kmem_free(child_rp, reglen);
1297 
1298 	ddi_set_parent_data(child, NULL);
1299 
1300 	return (DDI_SUCCESS);
1301 }
1302 
1303 
1304 /* ARGSUSED0 */
1305 static int
1306 sbbc_uninitchild(dev_info_t *rdip, dev_info_t *child)
1307 {
1308 
1309 	SBBC_DBG1(SBBC_DBG_UNINITCHILD, rdip, "Uninitializing %s\n",
1310 	    ddi_driver_name(rdip));
1311 
1312 	ddi_set_name_addr(child, NULL);
1313 	ddi_remove_minor_node(child, NULL);
1314 	impl_rem_dev_props(child);
1315 
1316 	return (DDI_SUCCESS);
1317 
1318 }
1319 
1320 
1321 /*
1322  * The following routine is an interrupt service routine that is used
1323  * as a wrapper to all the children requiring interrupt services.
1324  */
1325 static uint_t
1326 sbbc_intr_wrapper(caddr_t arg)
1327 {
1328 
1329 	struct sbbcsoft *sbbcsoftp = (struct sbbcsoft *)arg;
1330 	int i, rval;
1331 
1332 	SBBC_DBG1(SBBC_DBG_INTR, sbbcsoftp->dip, "Isr arg 0x%llx\n", arg);
1333 
1334 	mutex_enter(&sbbcsoftp->sbbc_intr_mutex);
1335 
1336 	for (i = 0; i < MAX_SBBC_DEVICES; i++) {
1337 		/*
1338 		 * Check the interrupt status reg. to determine the cause.
1339 		 */
1340 		/*
1341 		 * Check the error status reg. to determine the cause.
1342 		 */
1343 		if (sbbcsoftp->child_intr[i] &&
1344 		    sbbcsoftp->child_intr[i]->status ==
1345 		    SBBC_INTR_STATE_ENABLE) {
1346 			/*
1347 			 * Dispatch the children interrupt service routines and
1348 			 * look for someone to claim.
1349 			 */
1350 			rval = sbbcsoftp->child_intr[i]->intr_handler(
1351 			    sbbcsoftp->child_intr[i]->arg1,
1352 			    sbbcsoftp->child_intr[i]->arg2);
1353 
1354 			if (rval == DDI_INTR_CLAIMED) {
1355 				mutex_exit(&sbbcsoftp->sbbc_intr_mutex);
1356 				return (rval);
1357 			}
1358 		}
1359 	}
1360 
1361 	mutex_exit(&sbbcsoftp->sbbc_intr_mutex);
1362 
1363 	/* for now do not claim since we know its not enabled */
1364 	return (DDI_INTR_UNCLAIMED);
1365 }
1366 
1367 
1368 /*
1369  * This function checks an SBBC register offset to make sure that it is properly
1370  * aligned (i.e. on a 16-byte boundary) and that it corresponds to an accessible
1371  * register.  Since the SBBC treates accesses to unaligned or reserved addresses
1372  * as unmapped, failing to check for these would leave a loophole that could be
1373  * used to crash the system.
1374  */
1375 static int
1376 sbbc_offset_valid(uint32_t offset)
1377 {
1378 	/*
1379 	 * Check for proper alignment first.
1380 	 */
1381 	if ((offset % 16) != 0) {
1382 		return (0);
1383 	}
1384 
1385 	/*
1386 	 * Now start checking for the various reserved ranges.
1387 	 * While sticking a bunch of constants in the code (rather than
1388 	 * #define'd values) is usually best avoided, it would probably
1389 	 * do more harm than good here.  These values were taken from the
1390 	 * Serengeti Architecture Programmer's Reference Manual dated
1391 	 * August 10, 1999, pages 2-99 through 2-103.  While there are
1392 	 * various "clever" ways this check could be performed that would
1393 	 * be slightly more efficient, arranging the code in this fashion
1394 	 * should maximize maintainability.
1395 	 */
1396 	if (((offset >= 0x001a0) && (offset <= 0x001ff)) ||
1397 	    ((offset >= 0x002a0) && (offset <= 0x002ff)) ||
1398 	    ((offset >= 0x00350) && (offset <= 0x003ff)) ||
1399 	    ((offset >= 0x00500) && (offset <= 0x00fff)) ||
1400 	    ((offset >= 0x01160) && (offset <= 0x011ff)) ||
1401 	    ((offset >= 0x01210) && (offset <= 0x017ff)) ||
1402 	    ((offset >= 0x01810) && (offset <= 0x01fff)) ||
1403 	    ((offset >= 0x02030) && (offset <= 0x022ff)) ||
1404 	    ((offset >= 0x02340) && (offset <= 0x03fff)) ||
1405 	    ((offset >= 0x04030) && (offset <= 0x05fff)) ||
1406 	    ((offset >= 0x060a0) && (offset <= 0x060ff)) ||
1407 	    (offset == 0x06120) ||
1408 	    ((offset >= 0x06190) && (offset <= 0x061ff)) ||
1409 	    ((offset >= 0x06230) && (offset <= 0x062f0)) ||
1410 	    (offset > 0x06320)) {
1411 		return (0);
1412 	}
1413 
1414 	return (1);
1415 }
1416 
1417 #ifdef DEBUG
1418 void
1419 sbbc_dbg(uint32_t flag, dev_info_t *dip, char *fmt,
1420     uintptr_t a1, uintptr_t a2, uintptr_t a3, uintptr_t a4, uintptr_t a5)
1421 {
1422 	char *s = NULL;
1423 
1424 	if (sbbc_dbg_flags && ((sbbc_dbg_flags & flag) == flag)) {
1425 		switch (flag) {
1426 		case SBBC_DBG_ATTACH:
1427 			s = "attach";
1428 			break;
1429 		case SBBC_DBG_DETACH:
1430 			s = "detach";
1431 			break;
1432 		case SBBC_DBG_CTLOPS:
1433 			s = "ctlops";
1434 			break;
1435 		case SBBC_DBG_INITCHILD:
1436 			s = "initchild";
1437 			break;
1438 		case SBBC_DBG_UNINITCHILD:
1439 			s = "uninitchild";
1440 			break;
1441 		case SBBC_DBG_BUSMAP:
1442 			s = "busmap";
1443 			break;
1444 		case SBBC_DBG_INTR:
1445 			s = "intr";
1446 			break;
1447 		case SBBC_DBG_INTROPS:
1448 			s = "intr_ops";
1449 			break;
1450 		case SBBC_DBG_PCICONF:
1451 			s = "pciconfig";
1452 			break;
1453 		case SBBC_DBG_MAPRANGES:
1454 			s = "mapranges";
1455 			break;
1456 		case SBBC_DBG_PROPERTIES:
1457 			s = "properties";
1458 			break;
1459 		case SBBC_DBG_OPEN:
1460 			s = "open";
1461 			break;
1462 		case SBBC_DBG_CLOSE:
1463 			s = "close";
1464 			break;
1465 		case SBBC_DBG_IOCTL:
1466 			s = "ioctl";
1467 			break;
1468 		default:
1469 			s = "Unknown debug flag";
1470 			break;
1471 		}
1472 
1473 		cmn_err(CE_CONT, "%s_%s(%d): ", ddi_driver_name(dip), s,
1474 		    ddi_get_instance(dip));
1475 		cmn_err(CE_CONT, fmt, a1, a2, a3, a4, a5);
1476 	}
1477 }
1478 
1479 /*
1480  * Dump the SBBC chip's Device ID Register
1481  */
1482 static void sbbc_dump_devid(dev_info_t *dip, struct sbbcsoft *sbbcsoftp,
1483 	int instance)
1484 {
1485 	uint32_t sbbc_id_reg;
1486 	uint16_t sbbc_id_reg_partid;
1487 	uint16_t sbbc_id_reg_manfid;
1488 
1489 	sbbc_id_reg = ddi_get32(sbbcsoftp->pci_sbbc_map_handle,
1490 	    (uint32_t *)&sbbcsoftp->pci_sbbc_map->devid);
1491 
1492 	sbbc_id_reg_partid = ((sbbc_id_reg << 4) >> 16);
1493 	sbbc_id_reg_manfid = ((sbbc_id_reg << 20) >> 21);
1494 
1495 	SBBC_DBG4(SBBC_DBG_ATTACH, dip,
1496 	    "FOUND SBBC(%d) Version %x, Partid %x, Manfid %x\n",
1497 	    instance, (sbbc_id_reg >> 28), sbbc_id_reg_partid,
1498 	    sbbc_id_reg_manfid);
1499 }
1500 
1501 #endif /* DEBUG */
1502