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