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/*
23 * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
24 * Use is subject to license terms.
25 */
26
27
28/*
29 * sun4 root nexus driver
30 */
31
32#include <sys/conf.h>
33#include <sys/modctl.h>
34#include <sys/ddi_subrdefs.h>
35#include <sys/sunndi.h>
36#include <sys/vmsystm.h>
37#include <sys/async.h>
38#include <sys/intr.h>
39#include <sys/ndifm.h>
40#include <vm/seg_dev.h>
41#include <vm/seg_kmem.h>
42#include <sys/ontrap.h>
43
44/* Useful debugging Stuff */
45#include <sys/nexusdebug.h>
46#define	ROOTNEX_MAP_DEBUG		0x1
47#define	ROOTNEX_INTR_DEBUG		0x2
48
49/*
50 * config information
51 */
52
53static int
54rootnex_map(dev_info_t *dip, dev_info_t *rdip, ddi_map_req_t *mp,
55    off_t offset, off_t len, caddr_t *vaddrp);
56
57static int
58rootnex_intr_ops(dev_info_t *, dev_info_t *, ddi_intr_op_t,
59    ddi_intr_handle_impl_t *, void *);
60
61static int
62rootnex_map_fault(dev_info_t *dip, dev_info_t *rdip,
63    struct hat *hat, struct seg *seg, caddr_t addr,
64    struct devpage *dp, pfn_t pfn, uint_t prot, uint_t lock);
65
66static int
67rootnex_ctlops(dev_info_t *, dev_info_t *, ddi_ctl_enum_t, void *, void *);
68
69static int
70rootnex_busop_fminit(dev_info_t *dip, dev_info_t *tdip, int tcap,
71    ddi_iblock_cookie_t *ibc);
72
73static void
74rootnex_fm_init(dev_info_t *);
75
76static int
77rootnex_ctlops_peekpoke(ddi_ctl_enum_t, peekpoke_ctlops_t *, void *result);
78
79/*
80 * Defined in $KARCH/io/mach_rootnex.c
81 */
82int rootnex_add_intr_impl(dev_info_t *dip, dev_info_t *rdip,
83    ddi_intr_handle_impl_t *hdlp);
84#pragma weak rootnex_add_intr_impl
85
86int rootnex_remove_intr_impl(dev_info_t *dip, dev_info_t *rdip,
87    ddi_intr_handle_impl_t *hdlp);
88#pragma weak rootnex_remove_intr_impl
89
90int rootnex_get_intr_pri(dev_info_t *dip, dev_info_t *rdip,
91    ddi_intr_handle_impl_t *hdlp);
92#pragma weak rootnex_get_intr_pri
93
94int rootnex_name_child_impl(dev_info_t *child, char *name, int namelen);
95#pragma weak rootnex_name_child_impl
96
97int rootnex_ctl_initchild_impl(dev_info_t *dip);
98#pragma weak rootnex_initchild_impl
99
100void rootnex_ctl_uninitchild_impl(dev_info_t *dip);
101#pragma weak rootnex_uninitchild_impl
102
103int rootnex_ctl_reportdev_impl(dev_info_t *dev);
104#pragma weak rootnex_reportdev_impl
105
106static struct cb_ops rootnex_cb_ops = {
107	nodev,		/* open */
108	nodev,		/* close */
109	nodev,		/* strategy */
110	nodev,		/* print */
111	nodev,		/* dump */
112	nodev,		/* read */
113	nodev,		/* write */
114	nodev,		/* ioctl */
115	nodev,		/* devmap */
116	nodev,		/* mmap */
117	nodev,		/* segmap */
118	nochpoll,	/* chpoll */
119	ddi_prop_op,	/* cb_prop_op */
120	NULL,		/* struct streamtab */
121	D_NEW | D_MP | D_HOTPLUG,	/* compatibility flags */
122	CB_REV,		/* Rev */
123	nodev,		/* cb_aread */
124	nodev		/* cb_awrite */
125};
126
127static struct bus_ops rootnex_bus_ops = {
128	BUSO_REV,
129	rootnex_map,
130	NULL,
131	NULL,
132	NULL,
133	rootnex_map_fault,
134	ddi_no_dma_map,		/* no rootnex_dma_map- now in sysio nexus */
135	ddi_no_dma_allochdl,
136	ddi_no_dma_freehdl,
137	ddi_no_dma_bindhdl,
138	ddi_no_dma_unbindhdl,
139	ddi_no_dma_flush,
140	ddi_no_dma_win,
141	ddi_no_dma_mctl,	/* no rootnex_dma_mctl- now in sysio nexus */
142	rootnex_ctlops,
143	ddi_bus_prop_op,
144	i_ddi_rootnex_get_eventcookie,
145	i_ddi_rootnex_add_eventcall,
146	i_ddi_rootnex_remove_eventcall,
147	i_ddi_rootnex_post_event,
148	NULL,			/* bus_intr_ctl */
149	NULL,			/* bus_config */
150	NULL,			/* bus_unconfig */
151	rootnex_busop_fminit,	/* bus_fm_init */
152	NULL,			/* bus_fm_fini */
153	NULL,			/* bus_fm_access_enter */
154	NULL,			/* bus_fm_access_fini */
155	NULL,			/* bus_power */
156	rootnex_intr_ops	/* bus_intr_op */
157};
158
159static int rootnex_attach(dev_info_t *devi, ddi_attach_cmd_t cmd);
160static int rootnex_detach(dev_info_t *devi, ddi_detach_cmd_t cmd);
161
162static struct dev_ops rootnex_ops = {
163	DEVO_REV,
164	0,			/* refcnt */
165	ddi_no_info,		/* info */
166	nulldev,
167	nulldev,		/* probe */
168	rootnex_attach,
169	rootnex_detach,
170	nodev,			/* reset */
171	&rootnex_cb_ops,
172	&rootnex_bus_ops,
173	NULL,			/* power */
174	ddi_quiesce_not_needed,		/* quiesce */
175};
176
177
178extern uint_t	root_phys_addr_lo_mask;
179extern uint_t	root_phys_addr_hi_mask;
180extern struct mod_ops mod_driverops;
181extern struct dev_ops rootnex_ops;
182extern struct cpu cpu0;
183extern ddi_iblock_cookie_t rootnex_err_ibc;
184
185
186/*
187 * Add statically defined root properties to this list...
188 */
189static const int pagesize = PAGESIZE;
190static const int mmu_pagesize = MMU_PAGESIZE;
191static const int mmu_pageoffset = MMU_PAGEOFFSET;
192
193struct prop_def {
194	char *prop_name;
195	caddr_t prop_value;
196};
197
198static struct prop_def root_props[] = {
199	{ "PAGESIZE",		(caddr_t)&pagesize },
200	{ "MMU_PAGESIZE",	(caddr_t)&mmu_pagesize},
201	{ "MMU_PAGEOFFSET",	(caddr_t)&mmu_pageoffset},
202};
203
204static vmem_t	*rootnex_regspec_arena;
205
206#define	NROOT_PROPS	(sizeof (root_props) / sizeof (struct prop_def))
207
208
209
210/*
211 * Module linkage information for the kernel.
212 */
213
214static struct modldrv modldrv = {
215	&mod_driverops,	/* Type of module.  This one is a nexus driver */
216	"sun4 root nexus",
217	&rootnex_ops,	/* Driver ops */
218};
219
220static struct modlinkage modlinkage = {
221	MODREV_1, (void *)&modldrv, NULL
222};
223
224int
225_init(void)
226{
227	return (mod_install(&modlinkage));
228}
229
230int
231_fini(void)
232{
233	return (EBUSY);
234}
235
236int
237_info(struct modinfo *modinfop)
238{
239	return (mod_info(&modlinkage, modinfop));
240}
241
242/*
243 * rootnex_attach:
244 *
245 *	attach the root nexus.
246 */
247static void add_root_props(dev_info_t *);
248
249/*ARGSUSED*/
250static int
251rootnex_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
252{
253	int length;
254	char *valuep = NULL;
255
256	/*
257	 * Only do these functions when the driver is acting as the
258	 * root nexus, not when it is driving a memory controller.
259	 */
260	if (ddi_root_node() == devi) {
261		rootnex_fm_init(devi);
262		add_root_props(devi);
263		i_ddi_rootnex_init_events(devi);
264		rootnex_regspec_arena = vmem_create("regspec",
265		    (void *)PIOMAPBASE, PIOMAPSIZE, MMU_PAGESIZE, NULL, NULL,
266		    NULL, 0, VM_SLEEP);
267	}
268
269	if (ddi_prop_op(DDI_DEV_T_ANY, devi, PROP_LEN_AND_VAL_ALLOC,
270	    DDI_PROP_DONTPASS, "banner-name", (caddr_t)&valuep,
271	    &length) == DDI_PROP_SUCCESS) {
272		cmn_err(CE_CONT, "?root nexus = %s\n", valuep);
273		kmem_free(valuep, length);
274	}
275	/*
276	 * Add a no-suspend-resume property so that NDI
277	 * does not attempt to suspend/resume the rootnex
278	 * (or any of its aliases) node.
279	 */
280	(void) ddi_prop_update_string(DDI_DEV_T_NONE, devi,
281	    "pm-hardware-state", "no-suspend-resume");
282
283	return (DDI_SUCCESS);
284}
285
286/*ARGSUSED*/
287static int
288rootnex_detach(dev_info_t *devi, ddi_detach_cmd_t cmd)
289{
290	return (DDI_SUCCESS);
291}
292
293static void
294add_root_props(dev_info_t *devi)
295{
296	int i;
297	struct prop_def *rpp;
298
299	/*
300	 * Note that this for loop works because all of the
301	 * properties in root_prop are integers
302	 */
303	for (i = 0, rpp = root_props; i < NROOT_PROPS; ++i, ++rpp) {
304		(void) e_ddi_prop_update_int(DDI_DEV_T_NONE, devi,
305		    rpp->prop_name, *((int *)rpp->prop_value));
306	}
307
308	/*
309	 * Create the root node "boolean" property
310	 * corresponding to addressing type supported in the root node:
311	 *
312	 * Choices are:
313	 *	"relative-addressing" (OBP PROMS)
314	 *	"generic-addressing"  (SunMon -- pseudo OBP/DDI)
315	 */
316
317	(void) e_ddi_prop_update_int(DDI_DEV_T_NONE, devi,
318	    DDI_RELATIVE_ADDRESSING, 1);
319
320	/*
321	 * Create fault management capability property
322	 */
323	(void) e_ddi_prop_update_int(DDI_DEV_T_NONE, devi, "fm-capable",
324	    ddi_fm_capable(devi));
325}
326
327static int
328rootnex_map_regspec(ddi_map_req_t *mp, caddr_t *vaddrp, uint_t mapping_attr)
329{
330	uint64_t base;
331	caddr_t kaddr;
332	pgcnt_t npages;
333	pfn_t 	pfn;
334	uint_t 	pgoffset;
335	struct regspec *rp = mp->map_obj.rp;
336	ddi_acc_hdl_t *hp;
337
338	base = (uint64_t)rp->regspec_addr & (~MMU_PAGEOFFSET); /* base addr */
339
340	/*
341	 * Take the bustype and addr and convert it to a
342	 * page frame number.
343	 */
344	pfn =  mmu_btop(((uint64_t)(rp->regspec_bustype &
345	    root_phys_addr_hi_mask) << 32) | base);
346
347	/*
348	 * Do a quick sanity check to make sure we are in I/O space.
349	 */
350	if (pf_is_memory(pfn))
351		return (DDI_ME_INVAL);
352
353	if (rp->regspec_size == 0) {
354		DPRINTF(ROOTNEX_MAP_DEBUG, ("rootnex_map_regspec: zero "
355		    "regspec_size\n"));
356		return (DDI_ME_INVAL);
357	}
358
359	if (mp->map_flags & DDI_MF_DEVICE_MAPPING)
360		*vaddrp = (caddr_t)pfn;
361	else {
362		pgoffset = (ulong_t)rp->regspec_addr & MMU_PAGEOFFSET;
363		npages = mmu_btopr(rp->regspec_size + pgoffset);
364
365		DPRINTF(ROOTNEX_MAP_DEBUG, ("rootnex_map_regspec: Mapping "
366		    "%lu pages physical %x.%lx ", npages, rp->regspec_bustype,
367		    base));
368
369		if ((kaddr = vmem_alloc(rootnex_regspec_arena,
370		    ptob(npages), VM_NOSLEEP)) == NULL)
371			return (DDI_ME_NORESOURCES);
372
373		/*
374		 * Now map in the pages we've allocated...
375		 */
376		hat_devload(kas.a_hat, kaddr, ptob(npages), pfn,
377		    mp->map_prot | mapping_attr, HAT_LOAD_LOCK);
378
379		*vaddrp = kaddr + pgoffset;
380
381		hp = mp->map_handlep;
382		if (hp) {
383			hp->ah_pfn = pfn;
384			hp->ah_pnum = npages;
385		}
386	}
387
388	DPRINTF(ROOTNEX_MAP_DEBUG, ("at virtual 0x%p\n", (void *)*vaddrp));
389	return (0);
390}
391
392static int
393rootnex_unmap_regspec(ddi_map_req_t *mp, caddr_t *vaddrp)
394{
395	caddr_t addr = *vaddrp;
396	pgcnt_t npages;
397	uint_t  pgoffset;
398	caddr_t base;
399	struct regspec *rp;
400
401	if (mp->map_flags & DDI_MF_DEVICE_MAPPING)
402		return (0);
403
404	rp = mp->map_obj.rp;
405	pgoffset = (uintptr_t)addr & MMU_PAGEOFFSET;
406
407	if (rp->regspec_size == 0) {
408		DPRINTF(ROOTNEX_MAP_DEBUG, ("rootnex_unmap_regspec: "
409		    "zero regspec_size\n"));
410		return (DDI_ME_INVAL);
411	}
412
413	base = addr - pgoffset;
414	npages = mmu_btopr(rp->regspec_size + pgoffset);
415	hat_unload(kas.a_hat, base, ptob(npages), HAT_UNLOAD_UNLOCK);
416	vmem_free(rootnex_regspec_arena, base, ptob(npages));
417
418	/*
419	 * Destroy the pointer - the mapping has logically gone
420	 */
421	*vaddrp = (caddr_t)0;
422
423	return (0);
424}
425
426static int
427rootnex_map_handle(ddi_map_req_t *mp)
428{
429	ddi_acc_hdl_t *hp;
430	uint_t hat_flags;
431	register struct regspec *rp;
432
433	/*
434	 * Set up the hat_flags for the mapping.
435	 */
436	hp = mp->map_handlep;
437
438	switch (hp->ah_acc.devacc_attr_endian_flags) {
439	case DDI_NEVERSWAP_ACC:
440		hat_flags = HAT_NEVERSWAP | HAT_STRICTORDER;
441		break;
442	case DDI_STRUCTURE_BE_ACC:
443		hat_flags = HAT_STRUCTURE_BE;
444		break;
445	case DDI_STRUCTURE_LE_ACC:
446		hat_flags = HAT_STRUCTURE_LE;
447		break;
448	default:
449		return (DDI_REGS_ACC_CONFLICT);
450	}
451
452	switch (hp->ah_acc.devacc_attr_dataorder) {
453	case DDI_STRICTORDER_ACC:
454		break;
455	case DDI_UNORDERED_OK_ACC:
456		hat_flags |= HAT_UNORDERED_OK;
457		break;
458	case DDI_MERGING_OK_ACC:
459		hat_flags |= HAT_MERGING_OK;
460		break;
461	case DDI_LOADCACHING_OK_ACC:
462		hat_flags |= HAT_LOADCACHING_OK;
463		break;
464	case DDI_STORECACHING_OK_ACC:
465		hat_flags |= HAT_STORECACHING_OK;
466		break;
467	default:
468		return (DDI_FAILURE);
469	}
470
471	rp = mp->map_obj.rp;
472	if (rp->regspec_size == 0)
473		return (DDI_ME_INVAL);
474
475	hp->ah_hat_flags = hat_flags;
476	hp->ah_pfn = mmu_btop((ulong_t)rp->regspec_addr & (~MMU_PAGEOFFSET));
477	hp->ah_pnum = mmu_btopr(rp->regspec_size +
478	    (ulong_t)rp->regspec_addr & MMU_PAGEOFFSET);
479	return (DDI_SUCCESS);
480}
481
482static int
483rootnex_map(dev_info_t *dip, dev_info_t *rdip, ddi_map_req_t *mp,
484    off_t offset, off_t len, caddr_t *vaddrp)
485{
486	struct regspec *rp, tmp_reg;
487	ddi_map_req_t mr = *mp;		/* Get private copy of request */
488	int error;
489	uint_t mapping_attr;
490	ddi_acc_hdl_t *hp = NULL;
491
492	mp = &mr;
493
494	switch (mp->map_op)  {
495	case DDI_MO_MAP_LOCKED:
496	case DDI_MO_UNMAP:
497	case DDI_MO_MAP_HANDLE:
498		break;
499	default:
500		DPRINTF(ROOTNEX_MAP_DEBUG, ("rootnex_map: unimplemented map "
501		    "op %d.", mp->map_op));
502		return (DDI_ME_UNIMPLEMENTED);
503	}
504
505	if (mp->map_flags & DDI_MF_USER_MAPPING)  {
506		DPRINTF(ROOTNEX_MAP_DEBUG, ("rootnex_map: unimplemented map "
507		    "type: user."));
508		return (DDI_ME_UNIMPLEMENTED);
509	}
510
511	/*
512	 * First, if given an rnumber, convert it to a regspec...
513	 * (Presumably, this is on behalf of a child of the root node?)
514	 */
515
516	if (mp->map_type == DDI_MT_RNUMBER)  {
517
518		int rnumber = mp->map_obj.rnumber;
519
520		rp = i_ddi_rnumber_to_regspec(rdip, rnumber);
521		if (rp == (struct regspec *)0)  {
522			DPRINTF(ROOTNEX_MAP_DEBUG, ("rootnex_map: Out of "
523			    "range rnumber <%d>, device <%s>", rnumber,
524			    ddi_get_name(rdip)));
525			return (DDI_ME_RNUMBER_RANGE);
526		}
527
528		/*
529		 * Convert the given ddi_map_req_t from rnumber to regspec...
530		 */
531
532		mp->map_type = DDI_MT_REGSPEC;
533		mp->map_obj.rp = rp;
534	}
535
536	/*
537	 * Adjust offset and length corresponding to called values...
538	 * XXX: A non-zero length means override the one in the regspec
539	 * XXX: regardless of what's in the parent's range?.
540	 */
541
542	tmp_reg = *(mp->map_obj.rp);		/* Preserve underlying data */
543	rp = mp->map_obj.rp = &tmp_reg;		/* Use tmp_reg in request */
544
545	rp->regspec_addr += (uint_t)offset;
546	if (len != 0)
547		rp->regspec_size = (uint_t)len;
548
549	/*
550	 * Apply any parent ranges at this level, if applicable.
551	 * (This is where nexus specific regspec translation takes place.
552	 * Use of this function is implicit agreement that translation is
553	 * provided via ddi_apply_range.)
554	 */
555
556	DPRINTF(ROOTNEX_MAP_DEBUG, ("rootnex_map: applying range of parent "
557	    "<%s> to child <%s>...\n", ddi_get_name(dip), ddi_get_name(rdip)));
558
559	if ((error = i_ddi_apply_range(dip, rdip, mp->map_obj.rp)) != 0)
560		return (error);
561
562	switch (mp->map_op)  {
563	case DDI_MO_MAP_LOCKED:
564
565		/*
566		 * Set up the locked down kernel mapping to the regspec...
567		 */
568
569		/*
570		 * If we were passed an access handle we need to determine
571		 * the "endian-ness" of the mapping and fill in the handle.
572		 */
573		if (mp->map_handlep) {
574			hp = mp->map_handlep;
575			switch (hp->ah_acc.devacc_attr_endian_flags) {
576			case DDI_NEVERSWAP_ACC:
577				mapping_attr = HAT_NEVERSWAP | HAT_STRICTORDER;
578				break;
579			case DDI_STRUCTURE_BE_ACC:
580				mapping_attr = HAT_STRUCTURE_BE;
581				break;
582			case DDI_STRUCTURE_LE_ACC:
583				mapping_attr = HAT_STRUCTURE_LE;
584				break;
585			default:
586				return (DDI_REGS_ACC_CONFLICT);
587			}
588
589			switch (hp->ah_acc.devacc_attr_dataorder) {
590			case DDI_STRICTORDER_ACC:
591				break;
592			case DDI_UNORDERED_OK_ACC:
593				mapping_attr |= HAT_UNORDERED_OK;
594				break;
595			case DDI_MERGING_OK_ACC:
596				mapping_attr |= HAT_MERGING_OK;
597				break;
598			case DDI_LOADCACHING_OK_ACC:
599				mapping_attr |= HAT_LOADCACHING_OK;
600				break;
601			case DDI_STORECACHING_OK_ACC:
602				mapping_attr |= HAT_STORECACHING_OK;
603				break;
604			default:
605				return (DDI_REGS_ACC_CONFLICT);
606			}
607		} else {
608			mapping_attr = HAT_NEVERSWAP | HAT_STRICTORDER;
609		}
610
611		/*
612		 * Set up the mapping.
613		 */
614		error = rootnex_map_regspec(mp, vaddrp, mapping_attr);
615
616		/*
617		 * Fill in the access handle if needed.
618		 */
619		if (hp) {
620			hp->ah_addr = *vaddrp;
621			hp->ah_hat_flags = mapping_attr;
622			if (error == 0)
623				impl_acc_hdl_init(hp);
624		}
625		return (error);
626
627	case DDI_MO_UNMAP:
628
629		/*
630		 * Release mapping...
631		 */
632
633		return (rootnex_unmap_regspec(mp, vaddrp));
634
635	case DDI_MO_MAP_HANDLE:
636		return (rootnex_map_handle(mp));
637
638	}
639
640	return (DDI_ME_UNIMPLEMENTED);
641}
642
643static int
644rootnex_intr_ops(dev_info_t *dip, dev_info_t *rdip, ddi_intr_op_t intr_op,
645    ddi_intr_handle_impl_t *hdlp, void *result)
646{
647	int	ret = DDI_SUCCESS;
648
649	DPRINTF(ROOTNEX_INTR_DEBUG, ("rootnex_intr_ops: rdip=%s%d "
650	    "intr_op 0x%x hdlp 0x%p\n", ddi_driver_name(rdip),
651	    ddi_get_instance(rdip), intr_op, (void *)hdlp));
652
653	switch (intr_op) {
654	case DDI_INTROP_GETCAP:
655		*(int *)result = DDI_INTR_FLAG_LEVEL;
656		break;
657	case DDI_INTROP_SETCAP:
658		ret = DDI_ENOTSUP;
659		break;
660	case DDI_INTROP_ALLOC:
661		*(int *)result = hdlp->ih_scratch1;
662		break;
663	case DDI_INTROP_FREE:
664		break;
665	case DDI_INTROP_GETPRI:
666		*(int *)result = rootnex_get_intr_pri(dip, rdip, hdlp);
667		break;
668	case DDI_INTROP_SETPRI:
669		break;
670	case DDI_INTROP_ADDISR:
671		ret = rootnex_add_intr_impl(dip, rdip, hdlp);
672		break;
673	case DDI_INTROP_REMISR:
674		ret = rootnex_remove_intr_impl(dip, rdip, hdlp);
675		break;
676	case DDI_INTROP_ENABLE:
677	case DDI_INTROP_DISABLE:
678		break;
679	case DDI_INTROP_NINTRS:
680	case DDI_INTROP_NAVAIL:
681		*(int *)result = i_ddi_get_intx_nintrs(rdip);
682		break;
683	case DDI_INTROP_SUPPORTED_TYPES:
684		/* Root nexus driver supports only fixed interrupts */
685		*(int *)result = i_ddi_get_intx_nintrs(rdip) ?
686		    DDI_INTR_TYPE_FIXED : 0;
687		break;
688	default:
689		ret = DDI_ENOTSUP;
690		break;
691	}
692
693	return (ret);
694}
695
696
697/*
698 * Shorthand defines
699 */
700
701#define	DMAOBJ_PP_PP	dmao_obj.pp_obj.pp_pp
702#define	DMAOBJ_PP_OFF	dmao_ogj.pp_obj.pp_offset
703#define	ALO		dma_lim->dlim_addr_lo
704#define	AHI		dma_lim->dlim_addr_hi
705#define	OBJSIZE		dmareq->dmar_object.dmao_size
706#define	ORIGVADDR	dmareq->dmar_object.dmao_obj.virt_obj.v_addr
707#define	RED		((mp->dmai_rflags & DDI_DMA_REDZONE)? 1 : 0)
708#define	DIRECTION	(mp->dmai_rflags & DDI_DMA_RDWR)
709
710/*
711 * rootnex_map_fault:
712 *
713 *	fault in mappings for requestors
714 */
715
716/*ARGSUSED*/
717static int
718rootnex_map_fault(dev_info_t *dip, dev_info_t *rdip,
719    struct hat *hat, struct seg *seg, caddr_t addr,
720    struct devpage *dp, pfn_t pfn, uint_t prot, uint_t lock)
721{
722	extern struct seg_ops segdev_ops;
723
724	DPRINTF(ROOTNEX_MAP_DEBUG, ("rootnex_map_fault: address <%p> "
725	    "pfn <%lx>", (void *)addr, pfn));
726	DPRINTF(ROOTNEX_MAP_DEBUG, (" Seg <%s>\n",
727	    seg->s_ops == &segdev_ops ? "segdev" :
728	    seg == &kvseg ? "segkmem" : "NONE!"));
729
730	/*
731	 * This is all terribly broken, but it is a start
732	 *
733	 * XXX	Note that this test means that segdev_ops
734	 *	must be exported from seg_dev.c.
735	 * XXX	What about devices with their own segment drivers?
736	 */
737	if (seg->s_ops == &segdev_ops) {
738		register struct segdev_data *sdp =
739		    (struct segdev_data *)seg->s_data;
740
741		if (hat == NULL) {
742			/*
743			 * This is one plausible interpretation of
744			 * a null hat i.e. use the first hat on the
745			 * address space hat list which by convention is
746			 * the hat of the system MMU.  At alternative
747			 * would be to panic .. this might well be better ..
748			 */
749			ASSERT(AS_READ_HELD(seg->s_as, &seg->s_as->a_lock));
750			hat = seg->s_as->a_hat;
751			cmn_err(CE_NOTE, "rootnex_map_fault: nil hat");
752		}
753		hat_devload(hat, addr, MMU_PAGESIZE, pfn, prot | sdp->hat_attr,
754		    (lock ? HAT_LOAD_LOCK : HAT_LOAD));
755	} else if (seg == &kvseg && dp == (struct devpage *)0) {
756		hat_devload(kas.a_hat, addr, MMU_PAGESIZE, pfn, prot,
757		    HAT_LOAD_LOCK);
758	} else
759		return (DDI_FAILURE);
760	return (DDI_SUCCESS);
761}
762
763/*
764 * Name a child of rootnex
765 *
766 * This may be called multiple times, independent of initchild calls.
767 */
768int
769rootnex_name_child(dev_info_t *child, char *name, int namelen)
770{
771	return (rootnex_name_child_impl(child, name, namelen));
772}
773
774
775static int
776rootnex_ctl_initchild(dev_info_t *dip)
777{
778	return (rootnex_ctl_initchild_impl(dip));
779}
780
781
782int
783rootnex_ctl_uninitchild(dev_info_t *dip)
784{
785	extern void impl_free_ddi_ppd(dev_info_t *);
786
787	rootnex_ctl_uninitchild_impl(dip);
788
789	/*
790	 * strip properties and convert node to prototype form
791	 */
792	impl_free_ddi_ppd(dip);
793	ddi_set_name_addr(dip, NULL);
794	impl_rem_dev_props(dip);
795	return (DDI_SUCCESS);
796}
797
798
799static int
800rootnex_ctl_reportdev(dev_info_t *dev)
801{
802	return (rootnex_ctl_reportdev_impl(dev));
803}
804
805
806static int
807rootnex_ctlops_peekpoke(ddi_ctl_enum_t cmd, peekpoke_ctlops_t *in_args,
808    void *result)
809{
810	int err = DDI_SUCCESS;
811	on_trap_data_t otd;
812
813	/* No safe access except for peek/poke is supported. */
814	if (in_args->handle != NULL)
815		return (DDI_FAILURE);
816
817	/* Set up protected environment. */
818	if (!on_trap(&otd, OT_DATA_ACCESS)) {
819		uintptr_t tramp = otd.ot_trampoline;
820
821		if (cmd == DDI_CTLOPS_POKE) {
822			otd.ot_trampoline = (uintptr_t)&poke_fault;
823			err = do_poke(in_args->size, (void *)in_args->dev_addr,
824			    (void *)in_args->host_addr);
825		} else {
826			otd.ot_trampoline = (uintptr_t)&peek_fault;
827			err = do_peek(in_args->size, (void *)in_args->dev_addr,
828			    (void *)in_args->host_addr);
829			result = (void *)in_args->host_addr;
830		}
831		otd.ot_trampoline = tramp;
832	} else
833		err = DDI_FAILURE;
834
835	/* Take down protected environment. */
836	no_trap();
837
838	return (err);
839}
840
841/*ARGSUSED*/
842static int
843rootnex_ctlops(dev_info_t *dip, dev_info_t *rdip,
844    ddi_ctl_enum_t ctlop, void *arg, void *result)
845{
846	register int n, *ptr;
847	register struct ddi_parent_private_data *pdp;
848
849	static boolean_t reserved_msg_printed = B_FALSE;
850
851	switch (ctlop) {
852	case DDI_CTLOPS_DMAPMAPC:
853		return (DDI_FAILURE);
854
855	case DDI_CTLOPS_BTOP:
856		/*
857		 * Convert byte count input to physical page units.
858		 * (byte counts that are not a page-size multiple
859		 * are rounded down)
860		 */
861		*(ulong_t *)result = btop(*(ulong_t *)arg);
862		return (DDI_SUCCESS);
863
864	case DDI_CTLOPS_PTOB:
865		/*
866		 * Convert size in physical pages to bytes
867		 */
868		*(ulong_t *)result = ptob(*(ulong_t *)arg);
869		return (DDI_SUCCESS);
870
871	case DDI_CTLOPS_BTOPR:
872		/*
873		 * Convert byte count input to physical page units
874		 * (byte counts that are not a page-size multiple
875		 * are rounded up)
876		 */
877		*(ulong_t *)result = btopr(*(ulong_t *)arg);
878		return (DDI_SUCCESS);
879
880	case DDI_CTLOPS_INITCHILD:
881		return (rootnex_ctl_initchild((dev_info_t *)arg));
882
883	case DDI_CTLOPS_UNINITCHILD:
884		return (rootnex_ctl_uninitchild((dev_info_t *)arg));
885
886	case DDI_CTLOPS_REPORTDEV:
887		return (rootnex_ctl_reportdev(rdip));
888
889	case DDI_CTLOPS_IOMIN:
890		/*
891		 * Nothing to do here but reflect back..
892		 */
893		return (DDI_SUCCESS);
894
895	case DDI_CTLOPS_REGSIZE:
896	case DDI_CTLOPS_NREGS:
897		break;
898
899	case DDI_CTLOPS_SIDDEV:
900		if (ndi_dev_is_prom_node(rdip))
901			return (DDI_SUCCESS);
902		if (ndi_dev_is_persistent_node(rdip))
903			return (DDI_SUCCESS);
904		return (DDI_FAILURE);
905
906	case DDI_CTLOPS_POWER: {
907		return ((*pm_platform_power)((power_req_t *)arg));
908	}
909
910	case DDI_CTLOPS_RESERVED0: /* Was DDI_CTLOPS_NINTRS, obsolete */
911	case DDI_CTLOPS_RESERVED1: /* Was DDI_CTLOPS_POKE_INIT, obsolete */
912	case DDI_CTLOPS_RESERVED2: /* Was DDI_CTLOPS_POKE_FLUSH, obsolete */
913	case DDI_CTLOPS_RESERVED3: /* Was DDI_CTLOPS_POKE_FINI, obsolete */
914	case DDI_CTLOPS_RESERVED4: /* Was DDI_CTLOPS_INTR_HILEVEL, obsolete */
915	case DDI_CTLOPS_RESERVED5: /* Was DDI_CTLOPS_XLATE_INTRS, obsolete */
916		if (!reserved_msg_printed) {
917			reserved_msg_printed = B_TRUE;
918			cmn_err(CE_WARN, "Failing ddi_ctlops call(s) for "
919			    "1 or more reserved/obsolete operations.");
920		}
921		return (DDI_FAILURE);
922
923	case DDI_CTLOPS_POKE:
924	case DDI_CTLOPS_PEEK:
925		return (rootnex_ctlops_peekpoke(ctlop, (peekpoke_ctlops_t *)arg,
926		    result));
927
928	default:
929		return (DDI_FAILURE);
930	}
931
932	/*
933	 * The rest are for "hardware" properties
934	 */
935	if ((pdp = ddi_get_parent_data(rdip)) == NULL)
936		return (DDI_FAILURE);
937
938	if (ctlop == DDI_CTLOPS_NREGS) {
939		ptr = (int *)result;
940		*ptr = pdp->par_nreg;
941	} else {	/* ctlop == DDI_CTLOPS_REGSIZE */
942		off_t *size = (off_t *)result;
943
944		ptr = (int *)arg;
945		n = *ptr;
946		if (n >= pdp->par_nreg) {
947			return (DDI_FAILURE);
948		}
949		*size = (off_t)pdp->par_reg[n].regspec_size;
950	}
951	return (DDI_SUCCESS);
952}
953
954/* ARGSUSED */
955int
956rootnex_busop_fminit(dev_info_t *dip, dev_info_t *tdip, int cap,
957    ddi_iblock_cookie_t *ibc)
958{
959	*ibc = rootnex_err_ibc;
960	return (ddi_system_fmcap | DDI_FM_ACCCHK_CAPABLE |
961	    DDI_FM_DMACHK_CAPABLE);
962}
963
964static void
965rootnex_fm_init(dev_info_t *dip)
966{
967	int fmcap;
968
969	/* Minimum fm capability level for sun4u platforms */
970	ddi_system_fmcap = DDI_FM_EREPORT_CAPABLE | DDI_FM_ERRCB_CAPABLE;
971
972	fmcap = ddi_system_fmcap;
973
974	/*
975	 * Initialize ECC error handling
976	 */
977	rootnex_err_ibc = (ddi_iblock_cookie_t)PIL_15;
978	ddi_fm_init(dip, &fmcap, &rootnex_err_ibc);
979}
980