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
53 static int
54 rootnex_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
57 static int
58 rootnex_intr_ops(dev_info_t *, dev_info_t *, ddi_intr_op_t,
59 ddi_intr_handle_impl_t *, void *);
60
61 static int
62 rootnex_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
66 static int
67 rootnex_ctlops(dev_info_t *, dev_info_t *, ddi_ctl_enum_t, void *, void *);
68
69 static int
70 rootnex_busop_fminit(dev_info_t *dip, dev_info_t *tdip, int tcap,
71 ddi_iblock_cookie_t *ibc);
72
73 static void
74 rootnex_fm_init(dev_info_t *);
75
76 static int
77 rootnex_ctlops_peekpoke(ddi_ctl_enum_t, peekpoke_ctlops_t *, void *result);
78
79 /*
80 * Defined in $KARCH/io/mach_rootnex.c
81 */
82 int 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
86 int 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
90 int 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
94 int rootnex_name_child_impl(dev_info_t *child, char *name, int namelen);
95 #pragma weak rootnex_name_child_impl
96
97 int rootnex_ctl_initchild_impl(dev_info_t *dip);
98 #pragma weak rootnex_initchild_impl
99
100 void rootnex_ctl_uninitchild_impl(dev_info_t *dip);
101 #pragma weak rootnex_uninitchild_impl
102
103 int rootnex_ctl_reportdev_impl(dev_info_t *dev);
104 #pragma weak rootnex_reportdev_impl
105
106 static 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
127 static 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
159 static int rootnex_attach(dev_info_t *devi, ddi_attach_cmd_t cmd);
160 static int rootnex_detach(dev_info_t *devi, ddi_detach_cmd_t cmd);
161
162 static 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
178 extern uint_t root_phys_addr_lo_mask;
179 extern uint_t root_phys_addr_hi_mask;
180 extern struct mod_ops mod_driverops;
181 extern struct dev_ops rootnex_ops;
182 extern struct cpu cpu0;
183 extern ddi_iblock_cookie_t rootnex_err_ibc;
184
185
186 /*
187 * Add statically defined root properties to this list...
188 */
189 static const int pagesize = PAGESIZE;
190 static const int mmu_pagesize = MMU_PAGESIZE;
191 static const int mmu_pageoffset = MMU_PAGEOFFSET;
192
193 struct prop_def {
194 char *prop_name;
195 caddr_t prop_value;
196 };
197
198 static 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
204 static 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
214 static 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
220 static struct modlinkage modlinkage = {
221 MODREV_1, (void *)&modldrv, NULL
222 };
223
224 int
_init(void)225 _init(void)
226 {
227 return (mod_install(&modlinkage));
228 }
229
230 int
_fini(void)231 _fini(void)
232 {
233 return (EBUSY);
234 }
235
236 int
_info(struct modinfo * modinfop)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 */
247 static void add_root_props(dev_info_t *);
248
249 /*ARGSUSED*/
250 static int
rootnex_attach(dev_info_t * devi,ddi_attach_cmd_t cmd)251 rootnex_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*/
287 static int
rootnex_detach(dev_info_t * devi,ddi_detach_cmd_t cmd)288 rootnex_detach(dev_info_t *devi, ddi_detach_cmd_t cmd)
289 {
290 return (DDI_SUCCESS);
291 }
292
293 static void
add_root_props(dev_info_t * devi)294 add_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
327 static int
rootnex_map_regspec(ddi_map_req_t * mp,caddr_t * vaddrp,uint_t mapping_attr)328 rootnex_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
392 static int
rootnex_unmap_regspec(ddi_map_req_t * mp,caddr_t * vaddrp)393 rootnex_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
426 static int
rootnex_map_handle(ddi_map_req_t * mp)427 rootnex_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
482 static int
rootnex_map(dev_info_t * dip,dev_info_t * rdip,ddi_map_req_t * mp,off_t offset,off_t len,caddr_t * vaddrp)483 rootnex_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
643 static int
rootnex_intr_ops(dev_info_t * dip,dev_info_t * rdip,ddi_intr_op_t intr_op,ddi_intr_handle_impl_t * hdlp,void * result)644 rootnex_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*/
717 static int
rootnex_map_fault(dev_info_t * dip,dev_info_t * rdip,struct hat * hat,struct seg * seg,caddr_t addr,struct devpage * dp,pfn_t pfn,uint_t prot,uint_t lock)718 rootnex_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));
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 */
768 int
rootnex_name_child(dev_info_t * child,char * name,int namelen)769 rootnex_name_child(dev_info_t *child, char *name, int namelen)
770 {
771 return (rootnex_name_child_impl(child, name, namelen));
772 }
773
774
775 static int
rootnex_ctl_initchild(dev_info_t * dip)776 rootnex_ctl_initchild(dev_info_t *dip)
777 {
778 return (rootnex_ctl_initchild_impl(dip));
779 }
780
781
782 int
rootnex_ctl_uninitchild(dev_info_t * dip)783 rootnex_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
799 static int
rootnex_ctl_reportdev(dev_info_t * dev)800 rootnex_ctl_reportdev(dev_info_t *dev)
801 {
802 return (rootnex_ctl_reportdev_impl(dev));
803 }
804
805
806 static int
rootnex_ctlops_peekpoke(ddi_ctl_enum_t cmd,peekpoke_ctlops_t * in_args,void * result)807 rootnex_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*/
842 static int
rootnex_ctlops(dev_info_t * dip,dev_info_t * rdip,ddi_ctl_enum_t ctlop,void * arg,void * result)843 rootnex_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 */
955 int
rootnex_busop_fminit(dev_info_t * dip,dev_info_t * tdip,int cap,ddi_iblock_cookie_t * ibc)956 rootnex_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
964 static void
rootnex_fm_init(dev_info_t * dip)965 rootnex_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