pmubus.c revision 7656:2621e50fdf4a
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 */
73static 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);
75static 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 */
81static int pmubus_attach(dev_info_t *dip, ddi_attach_cmd_t cmd);
82static 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 */
91static 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
123static 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>
142extern struct mod_ops mod_driverops;
143
144static 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
150static struct modlinkage modlinkage = {
151	MODREV_1, (void *)&modldrv, NULL
152};
153
154/*
155 * driver global data:
156 */
157static void *per_pmubus_state;		/* per-pmubus soft state pointer */
158
159int
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
182int
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
201int
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 */
219static int
220pmubus_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
283fail_map_regs:
284	kmem_free(pmubusp->pmubus_rangep, pmubusp->pmubus_rnglen);
285
286fail_get_ranges:
287	kmem_free(pmubusp->pmubus_regp, pmubusp->pmubus_reglen);
288
289fail_get_regs:
290	ddi_soft_state_free(per_pmubus_state, instance);
291
292fail_exit:
293	return (DDI_FAILURE);
294}
295
296/*
297 * detach entry point:
298 */
299static int
300pmubus_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*/
331void
332pmubus_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*/
338void
339pmubus_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*/
345void
346pmubus_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*/
352void
353pmubus_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*/
359void
360pmubus_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*/
366void
367pmubus_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*/
373void
374pmubus_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*/
380void
381pmubus_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*/
387uint8_t
388pmubus_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*/
423uint16_t
424pmubus_noget16(ddi_acc_impl_t *hdlp, uint16_t *addr)
425{
426	return ((uint16_t)-1);
427}
428
429/*ARGSUSED*/
430uint32_t
431pmubus_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*/
465uint64_t
466pmubus_noget64(ddi_acc_impl_t *hdlp, uint64_t *addr)
467{
468	return ((uint64_t)-1);
469}
470
471/*ARGSUSED*/
472void
473pmubus_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*/
520void
521pmubus_noput16(ddi_acc_impl_t *hdlp, uint16_t *addr, uint16_t value)
522{
523}
524
525/*ARGSUSED*/
526void
527pmubus_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*/
574void
575pmubus_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*/
584int
585pmubus_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
627static uint64_t
628pmubus_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 */
650static int
651pmubus_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
840done:
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
848static int
849pmubus_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