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