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
_init(void)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
_fini(void)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
_info(struct modinfo * modinfop)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
pmubus_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)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
pmubus_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)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
pmubus_norep_get8(ddi_acc_impl_t * handle,uint8_t * host_addr,uint8_t * dev_addr,size_t repcount,uint_t flags)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
pmubus_norep_get16(ddi_acc_impl_t * handle,uint16_t * host_addr,uint16_t * dev_addr,size_t repcount,uint_t flags)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
pmubus_norep_get32(ddi_acc_impl_t * handle,uint32_t * host_addr,uint32_t * dev_addr,size_t repcount,uint_t flags)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
pmubus_norep_get64(ddi_acc_impl_t * handle,uint64_t * host_addr,uint64_t * dev_addr,size_t repcount,uint_t flags)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
pmubus_norep_put8(ddi_acc_impl_t * handle,uint8_t * host_addr,uint8_t * dev_addr,size_t repcount,uint_t flags)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
pmubus_norep_put16(ddi_acc_impl_t * handle,uint16_t * host_addr,uint16_t * dev_addr,size_t repcount,uint_t flags)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
pmubus_norep_put32(ddi_acc_impl_t * handle,uint32_t * host_addr,uint32_t * dev_addr,size_t repcount,uint_t flags)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
pmubus_norep_put64(ddi_acc_impl_t * handle,uint64_t * host_addr,uint64_t * dev_addr,size_t repcount,uint_t flags)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
pmubus_get8(ddi_acc_impl_t * hdlp,uint8_t * addr)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
pmubus_noget16(ddi_acc_impl_t * hdlp,uint16_t * addr)424 pmubus_noget16(ddi_acc_impl_t *hdlp, uint16_t *addr)
425 {
426 return ((uint16_t)-1);
427 }
428
429 /*ARGSUSED*/
430 uint32_t
pmubus_get32(ddi_acc_impl_t * hdlp,uint32_t * addr)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
pmubus_noget64(ddi_acc_impl_t * hdlp,uint64_t * addr)466 pmubus_noget64(ddi_acc_impl_t *hdlp, uint64_t *addr)
467 {
468 return ((uint64_t)-1);
469 }
470
471 /*ARGSUSED*/
472 void
pmubus_put8(ddi_acc_impl_t * hdlp,uint8_t * addr,uint8_t value)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
pmubus_noput16(ddi_acc_impl_t * hdlp,uint16_t * addr,uint16_t value)521 pmubus_noput16(ddi_acc_impl_t *hdlp, uint16_t *addr, uint16_t value)
522 {
523 }
524
525 /*ARGSUSED*/
526 void
pmubus_put32(ddi_acc_impl_t * hdlp,uint32_t * addr,uint32_t value)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
pmubus_noput64(ddi_acc_impl_t * hdlp,uint64_t * addr,uint64_t value)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
pmubus_apply_range(pmubus_devstate_t * pmubusp,dev_info_t * rdip,pmubus_regspec_t * regp,pci_regspec_t * pci_regp)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
pmubus_mask(pmubus_obpregspec_t * regs,int32_t rnumber,uint64_t * masks)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
pmubus_map(dev_info_t * dip,dev_info_t * rdip,ddi_map_req_t * mp,off_t off,off_t len,caddr_t * addrp)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
pmubus_ctlops(dev_info_t * dip,dev_info_t * rdip,ddi_ctl_enum_t op,void * arg,void * result)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 ®len) != 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