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