xref: /illumos-gate/usr/src/uts/sun4u/io/pmubus.c (revision fa084259)
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 #pragma ident	"%Z%%M%	%I%	%E% SMI"
27 
28 #include <sys/types.h>
29 #include <sys/conf.h>
30 #include <sys/ddi.h>
31 #include <sys/sunddi.h>
32 #include <sys/ddi_impldefs.h>
33 #include <sys/ddi_subrdefs.h>
34 #include <sys/pci.h>
35 #include <sys/autoconf.h>
36 #include <sys/cmn_err.h>
37 #include <sys/errno.h>
38 #include <sys/kmem.h>
39 #include <sys/debug.h>
40 #include <sys/sysmacros.h>
41 #include <sys/pmubus.h>
42 
43 #include <sys/nexusdebug.h>
44 /* Bitfield debugging definitions for this file */
45 #define	PMUBUS_MAP_DEBUG	0x1
46 #define	PMUBUS_REGACCESS_DEBUG	0x2
47 #define	PMUBUS_RW_DEBUG		0x4
48 
49 /*
50  * The pmubus nexus is used to manage a shared register space.  Rather
51  * than having several driver's physically alias register mappings and
52  * have potential problems with register collisions, this nexus will
53  * serialize the access to this space.
54  *
55  * There are two types of sharing going on here:
56  * 1) Registers within the address space may be shared, however the registers
57  * themselves are unique.  The upper bit of the child's high address being zero
58  * signifies this register type.
59  *
60  * 2) The second type of register is one where a device may only own a few
61  * bits in the register.  I'll term this as "bit lane" access.  This is a more
62  * complicated scenario.  The drivers themselves are responsible for knowing
63  * which bit lanes in the register they own.  The read of a register only
64  * guarantees that those bits the driver is interested in are valid.  If a
65  * driver needs to set bits in a register, a read must be done first to
66  * identify the state of the drivers bits.  Depending on which way a bit needs
67  * to be driven, the driver will write a 1 to the bit to toggle it.  If a bit
68  * is to remain unchanged, a 0 is written to the bit.  So the access to the
69  * bit lane is an xor operation.
70  */
71 /*
72  * Function prototypes for busops routines:
73  */
74 static int pmubus_map(dev_info_t *dip, dev_info_t *rdip, ddi_map_req_t *mp,
75     off_t off, off_t len, caddr_t *addrp);
76 static int pmubus_ctlops(dev_info_t *dip, dev_info_t *rdip,
77     ddi_ctl_enum_t op, void *arg, void *result);
78 
79 /*
80  * function prototypes for dev ops routines:
81  */
82 static int pmubus_attach(dev_info_t *dip, ddi_attach_cmd_t cmd);
83 static int pmubus_detach(dev_info_t *dip, ddi_detach_cmd_t cmd);
84 
85 /*
86  * general function prototypes:
87  */
88 
89 /*
90  * bus ops and dev ops structures:
91  */
92 static struct bus_ops pmubus_bus_ops = {
93 	BUSO_REV,
94 	pmubus_map,
95 	NULL,
96 	NULL,
97 	NULL,
98 	i_ddi_map_fault,
99 	ddi_dma_map,
100 	ddi_dma_allochdl,
101 	ddi_dma_freehdl,
102 	ddi_dma_bindhdl,
103 	ddi_dma_unbindhdl,
104 	ddi_dma_flush,
105 	ddi_dma_win,
106 	ddi_dma_mctl,
107 	pmubus_ctlops,
108 	ddi_bus_prop_op,
109 	0,			/* (*bus_get_eventcookie)();	*/
110 	0,			/* (*bus_add_eventcall)();	*/
111 	0,			/* (*bus_remove_eventcall)();	*/
112 	0,			/* (*bus_post_event)();		*/
113 	0,			/* interrupt control		*/
114 	0,			/* bus_config			*/
115 	0,			/* bus_unconfig			*/
116 	0,			/* bus_fm_init			*/
117 	0,			/* bus_fm_fini			*/
118 	0,			/* bus_fm_access_enter		*/
119 	0,			/* bus_fm_access_exit		*/
120 	0,			/* bus_power			*/
121 	i_ddi_intr_ops		/* bus_intr_op			*/
122 };
123 
124 static struct dev_ops pmubus_ops = {
125 	DEVO_REV,
126 	0,
127 	ddi_no_info,
128 	nulldev,
129 	0,
130 	pmubus_attach,
131 	pmubus_detach,
132 	nodev,
133 	(struct cb_ops *)0,
134 	&pmubus_bus_ops
135 };
136 
137 /*
138  * module definitions:
139  */
140 #include <sys/modctl.h>
141 extern struct mod_ops mod_driverops;
142 
143 static struct modldrv modldrv = {
144 	&mod_driverops, 	/* Type of module.  This one is a driver */
145 	"pmubus nexus driver",	/* Name of module. */
146 	&pmubus_ops,		/* driver ops */
147 };
148 
149 static struct modlinkage modlinkage = {
150 	MODREV_1, (void *)&modldrv, NULL
151 };
152 
153 /*
154  * driver global data:
155  */
156 static void *per_pmubus_state;		/* per-pmubus soft state pointer */
157 
158 int
159 _init(void)
160 {
161 	int e;
162 
163 	/*
164 	 * Initialize per-pmubus soft state pointer.
165 	 */
166 	e = ddi_soft_state_init(&per_pmubus_state,
167 	    sizeof (pmubus_devstate_t), 1);
168 	if (e != 0)
169 		return (e);
170 
171 	/*
172 	 * Install the module.
173 	 */
174 	e = mod_install(&modlinkage);
175 	if (e != 0)
176 		ddi_soft_state_fini(&per_pmubus_state);
177 
178 	return (e);
179 }
180 
181 int
182 _fini(void)
183 {
184 	int e;
185 
186 	/*
187 	 * Remove the module.
188 	 */
189 	e = mod_remove(&modlinkage);
190 	if (e != 0)
191 		return (e);
192 
193 	/*
194 	 * Free the soft state info.
195 	 */
196 	ddi_soft_state_fini(&per_pmubus_state);
197 	return (e);
198 }
199 
200 int
201 _info(struct modinfo *modinfop)
202 {
203 	return (mod_info(&modlinkage, modinfop));
204 }
205 
206 /* device driver entry points */
207 
208 /*
209  * attach entry point:
210  *
211  * normal attach:
212  *
213  *	create soft state structure (dip, reg, nreg and state fields)
214  *	map in configuration header
215  *	make sure device is properly configured
216  *	report device
217  */
218 static int
219 pmubus_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
220 {
221 	pmubus_devstate_t *pmubusp;	/* per pmubus state pointer */
222 	int32_t instance;
223 
224 	switch (cmd) {
225 	case DDI_ATTACH:
226 		/*
227 		 * Allocate soft state for this instance.
228 		 */
229 		instance = ddi_get_instance(dip);
230 		if (ddi_soft_state_zalloc(per_pmubus_state, instance) !=
231 		    DDI_SUCCESS) {
232 			cmn_err(CE_WARN, "pmubus_attach: Can't allocate soft "
233 			    "state.\n");
234 			goto fail_exit;
235 		}
236 
237 		pmubusp = ddi_get_soft_state(per_pmubus_state, instance);
238 		pmubusp->pmubus_dip = dip;
239 
240 		/* Cache our register property */
241 		if (ddi_getlongprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
242 		    "reg", (caddr_t)&pmubusp->pmubus_regp,
243 		    &pmubusp->pmubus_reglen) != DDI_SUCCESS) {
244 			cmn_err(CE_WARN, "pmubus_attach: Can't acquire reg "
245 			    "property.\n");
246 			goto fail_get_regs;
247 		}
248 
249 		/* Cache our ranges property */
250 		if (ddi_getlongprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
251 		    "ranges", (caddr_t)&pmubusp->pmubus_rangep,
252 		    &pmubusp->pmubus_rnglen) != DDI_SUCCESS) {
253 			cmn_err(CE_WARN, "pmubus_attach: Can't acquire the "
254 			    "ranges property.\n");
255 			goto fail_get_ranges;
256 
257 		}
258 
259 		/* Calculate the number of ranges */
260 		pmubusp->pmubus_nranges =
261 		    pmubusp->pmubus_rnglen / sizeof (pmu_rangespec_t);
262 
263 		/* Set up the mapping to our registers */
264 		if (pci_config_setup(dip, &pmubusp->pmubus_reghdl) !=
265 		    DDI_SUCCESS) {
266 			cmn_err(CE_WARN, "pmubus_attach: Can't map in "
267 			    "register space.\n");
268 			goto fail_map_regs;
269 		}
270 
271 		/* Initialize our register access mutex */
272 		mutex_init(&pmubusp->pmubus_reg_access_lock, NULL,
273 		    MUTEX_DRIVER, NULL);
274 
275 		ddi_report_dev(dip);
276 		return (DDI_SUCCESS);
277 
278 	case DDI_RESUME:
279 		return (DDI_SUCCESS);
280 	}
281 
282 fail_map_regs:
283 	kmem_free(pmubusp->pmubus_rangep, pmubusp->pmubus_rnglen);
284 
285 fail_get_ranges:
286 	kmem_free(pmubusp->pmubus_regp, pmubusp->pmubus_reglen);
287 
288 fail_get_regs:
289 	ddi_soft_state_free(per_pmubus_state, instance);
290 
291 fail_exit:
292 	return (DDI_FAILURE);
293 }
294 
295 /*
296  * detach entry point:
297  */
298 static int
299 pmubus_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
300 {
301 	int instance = ddi_get_instance(dip);
302 	pmubus_devstate_t *pmubusp = ddi_get_soft_state(per_pmubus_state,
303 	    instance);
304 
305 	switch (cmd) {
306 	case DDI_DETACH:
307 		mutex_destroy(&pmubusp->pmubus_reg_access_lock);
308 
309 		/* Tear down our register mappings */
310 		pci_config_teardown(&pmubusp->pmubus_reghdl);
311 
312 		/* Free our ranges property */
313 		kmem_free(pmubusp->pmubus_rangep, pmubusp->pmubus_rnglen);
314 
315 		/* Free the register property */
316 		kmem_free(pmubusp->pmubus_regp, pmubusp->pmubus_reglen);
317 
318 		ddi_soft_state_free(per_pmubus_state, instance);
319 		break;
320 
321 	case DDI_SUSPEND:
322 	default:
323 		break;
324 	}
325 
326 	return (DDI_SUCCESS);
327 }
328 
329 /*ARGSUSED*/
330 void
331 pmubus_norep_get8(ddi_acc_impl_t *handle, uint8_t *host_addr,
332     uint8_t *dev_addr, size_t repcount, uint_t flags)
333 {
334 }
335 
336 /*ARGSUSED*/
337 void
338 pmubus_norep_get16(ddi_acc_impl_t *handle, uint16_t *host_addr,
339     uint16_t *dev_addr, size_t repcount, uint_t flags)
340 {
341 }
342 
343 /*ARGSUSED*/
344 void
345 pmubus_norep_get32(ddi_acc_impl_t *handle, uint32_t *host_addr,
346     uint32_t *dev_addr, size_t repcount, uint_t flags)
347 {
348 }
349 
350 /*ARGSUSED*/
351 void
352 pmubus_norep_get64(ddi_acc_impl_t *handle, uint64_t *host_addr,
353     uint64_t *dev_addr, size_t repcount, uint_t flags)
354 {
355 }
356 
357 /*ARGSUSED*/
358 void
359 pmubus_norep_put8(ddi_acc_impl_t *handle, uint8_t *host_addr,
360     uint8_t *dev_addr, size_t repcount, uint_t flags)
361 {
362 }
363 
364 /*ARGSUSED*/
365 void
366 pmubus_norep_put16(ddi_acc_impl_t *handle, uint16_t *host_addr,
367     uint16_t *dev_addr, size_t repcount, uint_t flags)
368 {
369 }
370 
371 /*ARGSUSED*/
372 void
373 pmubus_norep_put32(ddi_acc_impl_t *handle, uint32_t *host_addr,
374     uint32_t *dev_addr, size_t repcount, uint_t flags)
375 {
376 }
377 
378 /*ARGSUSED*/
379 void
380 pmubus_norep_put64(ddi_acc_impl_t *handle, uint64_t *host_addr,
381     uint64_t *dev_addr, size_t repcount, uint_t flags)
382 {
383 }
384 
385 /*ARGSUSED*/
386 uint8_t
387 pmubus_get8(ddi_acc_impl_t *hdlp, uint8_t *addr)
388 {
389 	ddi_acc_hdl_t *hp = (ddi_acc_hdl_t *)hdlp;
390 	pmubus_mapreq_t *pmubus_mapreqp = hp->ah_bus_private;
391 	pmubus_devstate_t *softsp = pmubus_mapreqp->mapreq_softsp;
392 	off_t offset;
393 	uint8_t value;
394 	uint8_t mask;
395 
396 	offset = pmubus_mapreqp->mapreq_addr + (uintptr_t)addr;
397 	offset &= PMUBUS_REGOFFSET;
398 
399 	if ((pmubus_mapreqp->mapreq_flags) & MAPREQ_SHARED_BITS) {
400 		if (addr != 0 ||
401 		    pmubus_mapreqp->mapreq_size != sizeof (value)) {
402 			cmn_err(CE_WARN, "pmubus_get8: load discarded, "
403 			    "incorrect access addr/size");
404 			return ((uint8_t)-1);
405 		}
406 		mask = pmubus_mapreqp->mapreq_mask;
407 	} else {
408 		mask = (uint8_t)-1;
409 	}
410 
411 	/* gets are simple, we just issue them no locking necessary */
412 	value = pci_config_get8(softsp->pmubus_reghdl, offset) & mask;
413 
414 	DPRINTF(PMUBUS_RW_DEBUG, ("pmubus_get8: addr=%p offset=%lx value=%x "
415 	    "mask=%x\n", (void *)addr, offset, value, mask));
416 
417 	return (value);
418 }
419 
420 
421 /*ARGSUSED*/
422 uint16_t
423 pmubus_noget16(ddi_acc_impl_t *hdlp, uint16_t *addr)
424 {
425 	return ((uint16_t)-1);
426 }
427 
428 /*ARGSUSED*/
429 uint32_t
430 pmubus_get32(ddi_acc_impl_t *hdlp, uint32_t *addr)
431 {
432 	ddi_acc_hdl_t *hp = (ddi_acc_hdl_t *)hdlp;
433 	pmubus_mapreq_t *pmubus_mapreqp = hp->ah_bus_private;
434 	pmubus_devstate_t *softsp = pmubus_mapreqp->mapreq_softsp;
435 	off_t offset = (uintptr_t)addr & PMUBUS_REGOFFSET;
436 	uint32_t value;
437 	uint32_t mask;
438 
439 	offset = pmubus_mapreqp->mapreq_addr + (uintptr_t)addr;
440 	offset &= PMUBUS_REGOFFSET;
441 
442 	if ((pmubus_mapreqp->mapreq_flags) & MAPREQ_SHARED_BITS) {
443 		if (addr != 0 ||
444 		    pmubus_mapreqp->mapreq_size != sizeof (value)) {
445 			cmn_err(CE_WARN, "pmubus_get32: load discarded, "
446 			    "incorrect access addr/size");
447 			return ((uint32_t)-1);
448 		}
449 		mask = pmubus_mapreqp->mapreq_mask;
450 	} else {
451 		mask = (uint32_t)-1;
452 	}
453 
454 	/* gets are simple, we just issue them no locking necessary */
455 	value = pci_config_get32(softsp->pmubus_reghdl, offset) & mask;
456 
457 	DPRINTF(PMUBUS_RW_DEBUG, ("pmubus_get32: addr=%p offset=%lx value=%x "
458 	    "mask=%x\n", (void *)addr, offset, value, mask));
459 
460 	return (value);
461 }
462 
463 /*ARGSUSED*/
464 uint64_t
465 pmubus_noget64(ddi_acc_impl_t *hdlp, uint64_t *addr)
466 {
467 	return ((uint64_t)-1);
468 }
469 
470 /*ARGSUSED*/
471 void
472 pmubus_put8(ddi_acc_impl_t *hdlp, uint8_t *addr, uint8_t value)
473 {
474 	ddi_acc_hdl_t *hp = (ddi_acc_hdl_t *)hdlp;
475 	pmubus_mapreq_t *pmubus_mapreqp = hp->ah_bus_private;
476 	pmubus_devstate_t *softsp = pmubus_mapreqp->mapreq_softsp;
477 	off_t offset;
478 	uint8_t tmp;
479 
480 	offset = pmubus_mapreqp->mapreq_addr + (uintptr_t)addr;
481 	offset &= PMUBUS_REGOFFSET;
482 
483 	if ((pmubus_mapreqp->mapreq_flags) & MAPREQ_SHARED_BITS) {
484 		/*
485 		 * Process "bit lane" register
486 		 */
487 		DPRINTF(PMUBUS_RW_DEBUG, ("pmubus_put8: addr=%p offset=%lx "
488 		    "value=%x mask=%lx\n", (void *)addr, offset, value,
489 		    pmubus_mapreqp->mapreq_mask));
490 
491 		if (addr != 0 ||
492 		    pmubus_mapreqp->mapreq_size != sizeof (value)) {
493 			cmn_err(CE_WARN, "pmubus_put8: store discarded, "
494 			    "incorrect access addr/size");
495 			return;
496 		}
497 
498 		mutex_enter(&softsp->pmubus_reg_access_lock);
499 		tmp = pci_config_get8(softsp->pmubus_reghdl, offset);
500 		tmp &= ~pmubus_mapreqp->mapreq_mask;
501 		value &= pmubus_mapreqp->mapreq_mask;
502 		tmp |= value;
503 		pci_config_put8(softsp->pmubus_reghdl, offset, tmp);
504 		mutex_exit(&softsp->pmubus_reg_access_lock);
505 	} else {
506 		/*
507 		 * Process shared register
508 		 */
509 		DPRINTF(PMUBUS_RW_DEBUG, ("pmubus_put8: addr=%p offset=%lx "
510 		    "value=%x\n", (void *)addr, offset, value));
511 		pci_config_put8(softsp->pmubus_reghdl, offset, value);
512 	}
513 
514 	/* Flush store buffers XXX Should let drivers do this. */
515 	tmp = pci_config_get8(softsp->pmubus_reghdl, offset);
516 }
517 
518 /*ARGSUSED*/
519 void
520 pmubus_noput16(ddi_acc_impl_t *hdlp, uint16_t *addr, uint16_t value)
521 {
522 }
523 
524 /*ARGSUSED*/
525 void
526 pmubus_put32(ddi_acc_impl_t *hdlp, uint32_t *addr, uint32_t value)
527 {
528 	ddi_acc_hdl_t *hp = (ddi_acc_hdl_t *)hdlp;
529 	pmubus_mapreq_t *pmubus_mapreqp = hp->ah_bus_private;
530 	pmubus_devstate_t *softsp = pmubus_mapreqp->mapreq_softsp;
531 	off_t offset;
532 	uint32_t tmp;
533 
534 	offset = pmubus_mapreqp->mapreq_addr + (uintptr_t)addr;
535 	offset &= PMUBUS_REGOFFSET;
536 
537 	if ((pmubus_mapreqp->mapreq_flags) & MAPREQ_SHARED_BITS) {
538 		/*
539 		 * Process "bit lane" register
540 		 */
541 		DPRINTF(PMUBUS_RW_DEBUG, ("pmubus_put32: addr=%p offset=%lx "
542 		    "value=%x mask=%lx\n", (void *)addr, offset, value,
543 		    pmubus_mapreqp->mapreq_mask));
544 
545 		if (addr != 0 ||
546 		    pmubus_mapreqp->mapreq_size != sizeof (value)) {
547 			cmn_err(CE_WARN, "pmubus_put32: store discarded, "
548 			    "incorrect access addr/size");
549 			return;
550 		}
551 
552 		mutex_enter(&softsp->pmubus_reg_access_lock);
553 		tmp = pci_config_get32(softsp->pmubus_reghdl, offset);
554 		tmp &= ~pmubus_mapreqp->mapreq_mask;
555 		value &= pmubus_mapreqp->mapreq_mask;
556 		tmp |= value;
557 		pci_config_put32(softsp->pmubus_reghdl, offset, tmp);
558 		mutex_exit(&softsp->pmubus_reg_access_lock);
559 	} else {
560 		/*
561 		 * Process shared register
562 		 */
563 		DPRINTF(PMUBUS_RW_DEBUG, ("pmubus_put32: addr=%p offset=%lx "
564 		    "value=%x\n", (void *)addr, offset, value));
565 		pci_config_put32(softsp->pmubus_reghdl, offset, value);
566 	}
567 
568 	/* Flush store buffers XXX Should let drivers do this. */
569 	tmp = pci_config_get32(softsp->pmubus_reghdl, offset);
570 }
571 
572 /*ARGSUSED*/
573 void
574 pmubus_noput64(ddi_acc_impl_t *hdlp, uint64_t *addr, uint64_t value)
575 {
576 }
577 
578 /*
579  * This routine is used to translate our children's register properties.
580  * The return value specifies which type of register has been translated.
581  */
582 /*ARGSUSED*/
583 int
584 pmubus_apply_range(pmubus_devstate_t *pmubusp, dev_info_t *rdip,
585     pmubus_regspec_t *regp, pci_regspec_t *pci_regp)
586 {
587 	pmu_rangespec_t *rangep;
588 	int nranges = pmubusp->pmubus_nranges;
589 	int i;
590 	off_t offset;
591 	int ret = DDI_ME_REGSPEC_RANGE;
592 	uint64_t addr;
593 
594 	addr = regp->reg_addr & ~MAPPING_SHARED_BITS_MASK;
595 
596 	/* Scan the ranges for a match */
597 	for (i = 0, rangep = pmubusp->pmubus_rangep; i < nranges; i++, rangep++)
598 		if ((rangep->rng_child <= addr) &&
599 		    ((addr + regp->reg_size) <=
600 		    (rangep->rng_child + rangep->rng_size))) {
601 			ret = DDI_SUCCESS;
602 			break;
603 		}
604 
605 	if (ret != DDI_SUCCESS)
606 		return (ret);
607 
608 	/* Get the translated register */
609 	offset = addr - rangep->rng_child;
610 	pci_regp->pci_phys_hi = rangep->rng_parent_hi;
611 	pci_regp->pci_phys_mid = rangep->rng_parent_mid;
612 	pci_regp->pci_phys_low = rangep->rng_parent_low + offset;
613 	pci_regp->pci_size_hi = 0;
614 	pci_regp->pci_size_low = MIN(regp->reg_size, rangep->rng_size);
615 
616 	/* Figure out the type of reg space we have */
617 	if (pci_regp->pci_phys_hi == pmubusp->pmubus_regp->pci_phys_hi) {
618 		ret = MAPREQ_SHARED_REG;
619 		if (regp->reg_addr & MAPPING_SHARED_BITS_MASK)
620 			ret |= MAPREQ_SHARED_BITS;
621 	}
622 
623 	return (ret);
624 }
625 
626 static uint64_t
627 pmubus_mask(pmubus_obpregspec_t *regs, int32_t rnumber,
628     uint64_t *masks)
629 {
630 	int i;
631 	long n = -1;
632 
633 	for (i = 0; i <= rnumber; i++)
634 		if (regs[i].reg_addr_hi & 0x80000000)
635 			n++;
636 
637 	if (n == -1) {
638 		cmn_err(CE_WARN, "pmubus_mask: missing mask");
639 		return (0);
640 	}
641 
642 	return (masks[n]);
643 }
644 
645 /*
646  * The pmubus_map routine determines if it's child is attempting to map a
647  * shared reg.  If it is, it installs it's own vectors and bus private pointer.
648  */
649 static int
650 pmubus_map(dev_info_t *dip, dev_info_t *rdip, ddi_map_req_t *mp,
651 	off_t off, off_t len, caddr_t *addrp)
652 {
653 	pmubus_devstate_t *pmubusp = ddi_get_soft_state(per_pmubus_state,
654 	    ddi_get_instance(dip));
655 	dev_info_t *pdip = (dev_info_t *)DEVI(dip)->devi_parent;
656 	pmubus_regspec_t pmubus_rp;
657 	pmubus_obpregspec_t *pmubus_regs = NULL;
658 	int pmubus_regs_size;
659 	uint64_t *pmubus_regmask = NULL;
660 	int pmubus_regmask_size;
661 	pci_regspec_t pci_reg;
662 	int32_t rnumber = mp->map_obj.rnumber;
663 	pmubus_mapreq_t *pmubus_mapreqp;
664 	int ret = DDI_SUCCESS;
665 	char *map_fail1 = "Map Type Unknown";
666 	char *map_fail2 = "DDI_MT_REGSPEC";
667 	char *s = map_fail1;
668 
669 	*addrp = NULL;
670 
671 	/*
672 	 * Handle the mapping according to its type.
673 	 */
674 	DPRINTF(PMUBUS_MAP_DEBUG, ("rdip=%s%d: off=%lx len=%lx\n",
675 	    ddi_get_name(rdip), ddi_get_instance(rdip), off, len));
676 	switch (mp->map_type) {
677 	case DDI_MT_RNUMBER: {
678 		int n;
679 
680 		/*
681 		 * Get the "reg" property from the device node and convert
682 		 * it to our parent's format.
683 		 */
684 		rnumber = mp->map_obj.rnumber;
685 		DPRINTF(PMUBUS_MAP_DEBUG, ("rdip=%s%d: rnumber=%x "
686 		    "handlep=%p\n", ddi_get_name(rdip), ddi_get_instance(rdip),
687 		    rnumber, (void *)mp->map_handlep));
688 
689 		if (ddi_getlongprop(DDI_DEV_T_ANY, rdip, DDI_PROP_DONTPASS,
690 		    "reg", (caddr_t)&pmubus_regs, &pmubus_regs_size) !=
691 		    DDI_SUCCESS) {
692 			DPRINTF(PMUBUS_MAP_DEBUG, ("can't get reg "
693 			    "property\n"));
694 			ret = DDI_ME_RNUMBER_RANGE;
695 			goto done;
696 		}
697 		n = pmubus_regs_size / sizeof (pmubus_obpregspec_t);
698 
699 		if (rnumber < 0 || rnumber >= n) {
700 			DPRINTF(PMUBUS_MAP_DEBUG, ("rnumber out of range\n"));
701 			ret = DDI_ME_RNUMBER_RANGE;
702 			goto done;
703 		}
704 
705 		pmubus_rp.reg_addr = ((uint64_t)
706 		    pmubus_regs[rnumber].reg_addr_hi << 32) |
707 		    (uint64_t)pmubus_regs[rnumber].reg_addr_lo;
708 		pmubus_rp.reg_size = pmubus_regs[rnumber].reg_size;
709 
710 		(void) ddi_getlongprop(DDI_DEV_T_ANY, rdip, DDI_PROP_DONTPASS,
711 		    "register-mask", (caddr_t)&pmubus_regmask,
712 		    &pmubus_regmask_size);
713 
714 		/* Create our own mapping private structure */
715 		break;
716 
717 	}
718 	case DDI_MT_REGSPEC:
719 		/*
720 		 * This bus has no bus children that have to map in an address
721 		 * space, so we can assume that we'll never see an
722 		 * DDI_MT_REGSPEC request
723 		 */
724 		s = map_fail2;
725 		ret = DDI_ME_REGSPEC_RANGE;
726 		/*FALLTHROUGH*/
727 
728 	default:
729 		if (ret == DDI_SUCCESS)
730 			ret = DDI_ME_INVAL;
731 		DPRINTF(PMUBUS_MAP_DEBUG, ("rdip=%s%d: pmubus_map: "
732 		    "%s is an invalid map type.\nmap request handlep=0x%p\n",
733 		    ddi_get_name(rdip), ddi_get_instance(rdip), s, (void *)mp));
734 
735 		ret = DDI_ME_RNUMBER_RANGE;
736 		goto done;
737 	}
738 
739 	/* Adjust our reg property with offset and length */
740 	if ((pmubus_rp.reg_addr + off) >
741 	    (pmubus_rp.reg_addr + pmubus_rp.reg_size)) {
742 		ret = DDI_ME_INVAL;
743 		goto done;
744 	}
745 
746 	pmubus_rp.reg_addr += off;
747 	if (len && (len < pmubus_rp.reg_size))
748 		pmubus_rp.reg_size = len;
749 
750 	/* Translate our child regspec into our parents address domain */
751 	ret = pmubus_apply_range(pmubusp, rdip, &pmubus_rp, &pci_reg);
752 
753 	/* Check if the apply range failed */
754 	if (ret < DDI_SUCCESS)
755 		goto done;
756 
757 	/*
758 	 * If our childs xlated address falls into our shared address range,
759 	 * setup our mapping handle.
760 	 */
761 	if (ret > DDI_SUCCESS) {
762 		/* Figure out if we're mapping or unmapping */
763 		switch (mp->map_op) {
764 		case DDI_MO_MAP_LOCKED: {
765 			ddi_acc_impl_t *hp = (ddi_acc_impl_t *)mp->map_handlep;
766 
767 			pmubus_mapreqp = kmem_alloc(sizeof (*pmubus_mapreqp),
768 			    KM_SLEEP);
769 
770 			pmubus_mapreqp->mapreq_flags = ret;
771 			pmubus_mapreqp->mapreq_softsp = pmubusp;
772 			pmubus_mapreqp->mapreq_addr = pmubus_rp.reg_addr;
773 			pmubus_mapreqp->mapreq_size = pmubus_rp.reg_size;
774 
775 			if (ret & MAPREQ_SHARED_BITS) {
776 				pmubus_mapreqp->mapreq_mask =
777 				    pmubus_mask(pmubus_regs, rnumber,
778 				    pmubus_regmask);
779 				DPRINTF(PMUBUS_MAP_DEBUG, ("rnumber=%d "
780 				    "mask=%lx\n", rnumber,
781 				    pmubus_mapreqp->mapreq_mask));
782 				if (pmubus_mapreqp->mapreq_mask == 0) {
783 					kmem_free(pmubus_mapreqp,
784 					    sizeof (pmubus_mapreq_t));
785 					ret = DDI_ME_INVAL;
786 					break;
787 				}
788 			}
789 
790 			hp->ahi_common.ah_bus_private = pmubus_mapreqp;
791 
792 			/* Initialize the access vectors */
793 			hp->ahi_get8 = pmubus_get8;
794 			hp->ahi_get16 = pmubus_noget16;
795 			hp->ahi_get32 = pmubus_get32;
796 			hp->ahi_get64 = pmubus_noget64;
797 			hp->ahi_put8 = pmubus_put8;
798 			hp->ahi_put16 = pmubus_noput16;
799 			hp->ahi_put32 = pmubus_put32;
800 			hp->ahi_put64 = pmubus_noput64;
801 			hp->ahi_rep_get8 = pmubus_norep_get8;
802 			hp->ahi_rep_get16 = pmubus_norep_get16;
803 			hp->ahi_rep_get32 = pmubus_norep_get32;
804 			hp->ahi_rep_get64 = pmubus_norep_get64;
805 			hp->ahi_rep_put8 = pmubus_norep_put8;
806 			hp->ahi_rep_put16 = pmubus_norep_put16;
807 			hp->ahi_rep_put32 = pmubus_norep_put32;
808 			hp->ahi_rep_put64 = pmubus_norep_put64;
809 
810 			ret = DDI_SUCCESS;
811 			break;
812 		}
813 
814 		case DDI_MO_UNMAP: {
815 			ddi_acc_impl_t *hp = (ddi_acc_impl_t *)mp->map_handlep;
816 
817 			pmubus_mapreqp = hp->ahi_common.ah_bus_private;
818 
819 			/* Free the our map request struct */
820 			kmem_free(pmubus_mapreqp, sizeof (pmubus_mapreq_t));
821 
822 			ret = DDI_SUCCESS;
823 			break;
824 		}
825 
826 		default:
827 			ret = DDI_ME_UNSUPPORTED;
828 		}
829 	} else {
830 		/* Prepare the map request struct for a call to our parent */
831 		mp->map_type = DDI_MT_REGSPEC;
832 		mp->map_obj.rp = (struct regspec *)&pci_reg;
833 
834 		/* Pass the mapping operation up the device tree */
835 		ret = (DEVI(pdip)->devi_ops->devo_bus_ops->bus_map)
836 		    (pdip, rdip, mp, off, len, addrp);
837 	}
838 
839 done:
840 	if (pmubus_regs != NULL)
841 		kmem_free(pmubus_regs, pmubus_regs_size);
842 	if (pmubus_regmask != NULL)
843 		kmem_free(pmubus_regmask, pmubus_regmask_size);
844 	return (ret);
845 }
846 
847 static int
848 pmubus_ctlops(dev_info_t *dip, dev_info_t *rdip,
849     ddi_ctl_enum_t op, void *arg, void *result)
850 {
851 	dev_info_t *child = (dev_info_t *)arg;
852 	pmubus_obpregspec_t *pmubus_rp;
853 	char name[9];
854 	int reglen;
855 
856 	switch (op) {
857 	case DDI_CTLOPS_INITCHILD:
858 
859 		if (ddi_getlongprop(DDI_DEV_T_ANY, child,
860 		    DDI_PROP_DONTPASS, "reg", (caddr_t)&pmubus_rp,
861 		    &reglen) != DDI_SUCCESS) {
862 
863 			return (DDI_FAILURE);
864 		}
865 
866 		if ((reglen % sizeof (pmubus_obpregspec_t)) != 0) {
867 			cmn_err(CE_WARN,
868 			    "pmubus: reg property not well-formed for "
869 			    "%s size=%d\n", ddi_node_name(child), reglen);
870 			kmem_free(pmubus_rp, reglen);
871 
872 			return (DDI_FAILURE);
873 		}
874 		(void) snprintf(name, sizeof (name), "%x,%x",
875 		    pmubus_rp->reg_addr_hi, pmubus_rp->reg_addr_lo);
876 		ddi_set_name_addr(child, name);
877 		kmem_free(pmubus_rp, reglen);
878 
879 		return (DDI_SUCCESS);
880 
881 	case DDI_CTLOPS_UNINITCHILD:
882 
883 		ddi_set_name_addr(child, NULL);
884 		ddi_remove_minor_node(child, NULL);
885 		impl_rem_dev_props(child);
886 
887 		return (DDI_SUCCESS);
888 	default:
889 		break;
890 	}
891 
892 	return (ddi_ctlops(dip, rdip, op, arg, result));
893 }
894