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 ®len) != 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