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 */
92static int sbbc_open(dev_t *, int, int, cred_t *);
93static int sbbc_close(dev_t, int, int, cred_t *);
94static int sbbc_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
95
96/* configuration entry point fn definitions */
97static int sbbc_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **);
98static int sbbc_attach(dev_info_t *, ddi_attach_cmd_t);
99static 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 */
108static int sbbc_offset_valid(uint32_t offset);
109
110/*
111 * function prototypes for bus ops routines:
112 */
113static 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);
115static int sbbc_ctlops(dev_info_t *dip, dev_info_t *rdip,
116	ddi_ctl_enum_t op, void *arg, void *result);
117
118static 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);
120static 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);
122static 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);
124static 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
127static int sbbc_apply_range(struct sbbcsoft *sbbc_p, dev_info_t *rdip,
128    sbbc_child_regspec_t *child_rp, pci_regspec_t *rp);
129
130static int sbbc_init(struct sbbcsoft *);
131
132static uint_t sbbc_intr_wrapper(caddr_t arg);
133
134static int sbbc_get_ranges(struct sbbcsoft *);
135static int sbbc_config4pci(struct sbbcsoft *);
136static int sbbc_initchild(dev_info_t *, dev_info_t *, dev_info_t *);
137static int sbbc_uninitchild(dev_info_t *, dev_info_t *);
138static void sbbc_remove_reg_maps(struct sbbcsoft *);
139
140/* debugging functions */
141#ifdef DEBUG
142uint32_t sbbc_dbg_flags = 0x0;
143static 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);
145static 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)
152struct sbbctrace sbbctrace_buffer[NSBBCTRACE+1];
153struct sbbctrace *sbbctrace_ptr;
154int sbbctrace_count;
155#endif
156
157/*
158 * Local declarations and variables
159 */
160
161static void *sbbcsoft_statep;
162
163/* Determines whether driver is executing on System Controller or Domain */
164int sbbc_scmode = FALSE;
165
166/*
167 * ops stuff.
168 */
169static 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 */
204static 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 */
225struct 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 */
243extern struct mod_ops mod_driverops;
244
245static struct modldrv sbbcmodldrv = {
246	&mod_driverops,		/* type of module - driver */
247	"PCI Sbbc Nexus Driver",
248	&sbbc_ops,
249};
250
251static struct modlinkage sbbcmodlinkage = {
252	MODREV_1,
253	&sbbcmodldrv,
254	NULL
255};
256
257int
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
271int
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
282int
283_info(struct modinfo *modinfop)
284{
285	return (mod_info(&sbbcmodlinkage, modinfop));
286}
287
288static int
289sbbc_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
439failed:
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
452static int
453sbbc_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 */
511static int
512sbbc_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 */
609static int
610sbbc_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
667static int
668sbbc_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
722static int
723sbbc_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
770static int
771sbbc_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 */
819static int
820sbbc_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 */
887static int
888sbbc_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 */
946static int
947sbbc_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 */
978static int
979sbbc_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 */
1026int
1027sbbc_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*/
1062static int
1063sbbc_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*/
1098static int
1099sbbc_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*/
1123static int
1124sbbc_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
1235static void
1236sbbc_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
1244static int
1245sbbc_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 */
1260static int
1261sbbc_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 */
1305static int
1306sbbc_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 */
1325static uint_t
1326sbbc_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 */
1375static int
1376sbbc_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
1418void
1419sbbc_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 */
1482static 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