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