/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * Copyright 2012 Garrett D'Amore . All rights reserved. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* Bitfield debugging definitions for this file */ #define PMUBUS_MAP_DEBUG 0x1 #define PMUBUS_REGACCESS_DEBUG 0x2 #define PMUBUS_RW_DEBUG 0x4 /* * The pmubus nexus is used to manage a shared register space. Rather * than having several driver's physically alias register mappings and * have potential problems with register collisions, this nexus will * serialize the access to this space. * * There are two types of sharing going on here: * 1) Registers within the address space may be shared, however the registers * themselves are unique. The upper bit of the child's high address being zero * signifies this register type. * * 2) The second type of register is one where a device may only own a few * bits in the register. I'll term this as "bit lane" access. This is a more * complicated scenario. The drivers themselves are responsible for knowing * which bit lanes in the register they own. The read of a register only * guarantees that those bits the driver is interested in are valid. If a * driver needs to set bits in a register, a read must be done first to * identify the state of the drivers bits. Depending on which way a bit needs * to be driven, the driver will write a 1 to the bit to toggle it. If a bit * is to remain unchanged, a 0 is written to the bit. So the access to the * bit lane is an xor operation. */ /* * Function prototypes for busops routines: */ 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); static int pmubus_ctlops(dev_info_t *dip, dev_info_t *rdip, ddi_ctl_enum_t op, void *arg, void *result); /* * function prototypes for dev ops routines: */ static int pmubus_attach(dev_info_t *dip, ddi_attach_cmd_t cmd); static int pmubus_detach(dev_info_t *dip, ddi_detach_cmd_t cmd); /* * general function prototypes: */ /* * bus ops and dev ops structures: */ static struct bus_ops pmubus_bus_ops = { BUSO_REV, pmubus_map, NULL, NULL, NULL, i_ddi_map_fault, NULL, ddi_dma_allochdl, ddi_dma_freehdl, ddi_dma_bindhdl, ddi_dma_unbindhdl, ddi_dma_flush, ddi_dma_win, ddi_dma_mctl, pmubus_ctlops, ddi_bus_prop_op, 0, /* (*bus_get_eventcookie)(); */ 0, /* (*bus_add_eventcall)(); */ 0, /* (*bus_remove_eventcall)(); */ 0, /* (*bus_post_event)(); */ 0, /* interrupt control */ 0, /* bus_config */ 0, /* bus_unconfig */ 0, /* bus_fm_init */ 0, /* bus_fm_fini */ 0, /* bus_fm_access_enter */ 0, /* bus_fm_access_exit */ 0, /* bus_power */ i_ddi_intr_ops /* bus_intr_op */ }; static struct dev_ops pmubus_ops = { DEVO_REV, 0, ddi_no_info, nulldev, 0, pmubus_attach, pmubus_detach, nodev, (struct cb_ops *)0, &pmubus_bus_ops, NULL, ddi_quiesce_not_needed, /* quiesce */ }; /* * module definitions: */ #include extern struct mod_ops mod_driverops; static struct modldrv modldrv = { &mod_driverops, /* Type of module. This one is a driver */ "pmubus nexus driver", /* Name of module. */ &pmubus_ops, /* driver ops */ }; static struct modlinkage modlinkage = { MODREV_1, (void *)&modldrv, NULL }; /* * driver global data: */ static void *per_pmubus_state; /* per-pmubus soft state pointer */ int _init(void) { int e; /* * Initialize per-pmubus soft state pointer. */ e = ddi_soft_state_init(&per_pmubus_state, sizeof (pmubus_devstate_t), 1); if (e != 0) return (e); /* * Install the module. */ e = mod_install(&modlinkage); if (e != 0) ddi_soft_state_fini(&per_pmubus_state); return (e); } int _fini(void) { int e; /* * Remove the module. */ e = mod_remove(&modlinkage); if (e != 0) return (e); /* * Free the soft state info. */ ddi_soft_state_fini(&per_pmubus_state); return (e); } int _info(struct modinfo *modinfop) { return (mod_info(&modlinkage, modinfop)); } /* device driver entry points */ /* * attach entry point: * * normal attach: * * create soft state structure (dip, reg, nreg and state fields) * map in configuration header * make sure device is properly configured * report device */ static int pmubus_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) { pmubus_devstate_t *pmubusp; /* per pmubus state pointer */ int32_t instance; switch (cmd) { case DDI_ATTACH: /* * Allocate soft state for this instance. */ instance = ddi_get_instance(dip); if (ddi_soft_state_zalloc(per_pmubus_state, instance) != DDI_SUCCESS) { cmn_err(CE_WARN, "pmubus_attach: Can't allocate soft " "state.\n"); goto fail_exit; } pmubusp = ddi_get_soft_state(per_pmubus_state, instance); pmubusp->pmubus_dip = dip; /* Cache our register property */ if (ddi_getlongprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, "reg", (caddr_t)&pmubusp->pmubus_regp, &pmubusp->pmubus_reglen) != DDI_SUCCESS) { cmn_err(CE_WARN, "pmubus_attach: Can't acquire reg " "property.\n"); goto fail_get_regs; } /* Cache our ranges property */ if (ddi_getlongprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, "ranges", (caddr_t)&pmubusp->pmubus_rangep, &pmubusp->pmubus_rnglen) != DDI_SUCCESS) { cmn_err(CE_WARN, "pmubus_attach: Can't acquire the " "ranges property.\n"); goto fail_get_ranges; } /* Calculate the number of ranges */ pmubusp->pmubus_nranges = pmubusp->pmubus_rnglen / sizeof (pmu_rangespec_t); /* Set up the mapping to our registers */ if (pci_config_setup(dip, &pmubusp->pmubus_reghdl) != DDI_SUCCESS) { cmn_err(CE_WARN, "pmubus_attach: Can't map in " "register space.\n"); goto fail_map_regs; } /* Initialize our register access mutex */ mutex_init(&pmubusp->pmubus_reg_access_lock, NULL, MUTEX_DRIVER, NULL); ddi_report_dev(dip); return (DDI_SUCCESS); case DDI_RESUME: return (DDI_SUCCESS); } fail_map_regs: kmem_free(pmubusp->pmubus_rangep, pmubusp->pmubus_rnglen); fail_get_ranges: kmem_free(pmubusp->pmubus_regp, pmubusp->pmubus_reglen); fail_get_regs: ddi_soft_state_free(per_pmubus_state, instance); fail_exit: return (DDI_FAILURE); } /* * detach entry point: */ static int pmubus_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) { int instance = ddi_get_instance(dip); pmubus_devstate_t *pmubusp = ddi_get_soft_state(per_pmubus_state, instance); switch (cmd) { case DDI_DETACH: mutex_destroy(&pmubusp->pmubus_reg_access_lock); /* Tear down our register mappings */ pci_config_teardown(&pmubusp->pmubus_reghdl); /* Free our ranges property */ kmem_free(pmubusp->pmubus_rangep, pmubusp->pmubus_rnglen); /* Free the register property */ kmem_free(pmubusp->pmubus_regp, pmubusp->pmubus_reglen); ddi_soft_state_free(per_pmubus_state, instance); break; case DDI_SUSPEND: default: break; } return (DDI_SUCCESS); } /*ARGSUSED*/ void pmubus_norep_get8(ddi_acc_impl_t *handle, uint8_t *host_addr, uint8_t *dev_addr, size_t repcount, uint_t flags) { } /*ARGSUSED*/ void pmubus_norep_get16(ddi_acc_impl_t *handle, uint16_t *host_addr, uint16_t *dev_addr, size_t repcount, uint_t flags) { } /*ARGSUSED*/ void pmubus_norep_get32(ddi_acc_impl_t *handle, uint32_t *host_addr, uint32_t *dev_addr, size_t repcount, uint_t flags) { } /*ARGSUSED*/ void pmubus_norep_get64(ddi_acc_impl_t *handle, uint64_t *host_addr, uint64_t *dev_addr, size_t repcount, uint_t flags) { } /*ARGSUSED*/ void pmubus_norep_put8(ddi_acc_impl_t *handle, uint8_t *host_addr, uint8_t *dev_addr, size_t repcount, uint_t flags) { } /*ARGSUSED*/ void pmubus_norep_put16(ddi_acc_impl_t *handle, uint16_t *host_addr, uint16_t *dev_addr, size_t repcount, uint_t flags) { } /*ARGSUSED*/ void pmubus_norep_put32(ddi_acc_impl_t *handle, uint32_t *host_addr, uint32_t *dev_addr, size_t repcount, uint_t flags) { } /*ARGSUSED*/ void pmubus_norep_put64(ddi_acc_impl_t *handle, uint64_t *host_addr, uint64_t *dev_addr, size_t repcount, uint_t flags) { } /*ARGSUSED*/ uint8_t pmubus_get8(ddi_acc_impl_t *hdlp, uint8_t *addr) { ddi_acc_hdl_t *hp = (ddi_acc_hdl_t *)hdlp; pmubus_mapreq_t *pmubus_mapreqp = hp->ah_bus_private; pmubus_devstate_t *softsp = pmubus_mapreqp->mapreq_softsp; off_t offset; uint8_t value; uint8_t mask; offset = pmubus_mapreqp->mapreq_addr + (uintptr_t)addr; offset &= PMUBUS_REGOFFSET; if ((pmubus_mapreqp->mapreq_flags) & MAPREQ_SHARED_BITS) { if (addr != 0 || pmubus_mapreqp->mapreq_size != sizeof (value)) { cmn_err(CE_WARN, "pmubus_get8: load discarded, " "incorrect access addr/size"); return ((uint8_t)-1); } mask = pmubus_mapreqp->mapreq_mask; } else { mask = (uint8_t)-1; } /* gets are simple, we just issue them no locking necessary */ value = pci_config_get8(softsp->pmubus_reghdl, offset) & mask; DPRINTF(PMUBUS_RW_DEBUG, ("pmubus_get8: addr=%p offset=%lx value=%x " "mask=%x\n", (void *)addr, offset, value, mask)); return (value); } /*ARGSUSED*/ uint16_t pmubus_noget16(ddi_acc_impl_t *hdlp, uint16_t *addr) { return ((uint16_t)-1); } /*ARGSUSED*/ uint32_t pmubus_get32(ddi_acc_impl_t *hdlp, uint32_t *addr) { ddi_acc_hdl_t *hp = (ddi_acc_hdl_t *)hdlp; pmubus_mapreq_t *pmubus_mapreqp = hp->ah_bus_private; pmubus_devstate_t *softsp = pmubus_mapreqp->mapreq_softsp; off_t offset = (uintptr_t)addr & PMUBUS_REGOFFSET; uint32_t value; uint32_t mask; offset = pmubus_mapreqp->mapreq_addr + (uintptr_t)addr; offset &= PMUBUS_REGOFFSET; if ((pmubus_mapreqp->mapreq_flags) & MAPREQ_SHARED_BITS) { if (addr != 0 || pmubus_mapreqp->mapreq_size != sizeof (value)) { cmn_err(CE_WARN, "pmubus_get32: load discarded, " "incorrect access addr/size"); return ((uint32_t)-1); } mask = pmubus_mapreqp->mapreq_mask; } else { mask = (uint32_t)-1; } /* gets are simple, we just issue them no locking necessary */ value = pci_config_get32(softsp->pmubus_reghdl, offset) & mask; DPRINTF(PMUBUS_RW_DEBUG, ("pmubus_get32: addr=%p offset=%lx value=%x " "mask=%x\n", (void *)addr, offset, value, mask)); return (value); } /*ARGSUSED*/ uint64_t pmubus_noget64(ddi_acc_impl_t *hdlp, uint64_t *addr) { return ((uint64_t)-1); } /*ARGSUSED*/ void pmubus_put8(ddi_acc_impl_t *hdlp, uint8_t *addr, uint8_t value) { ddi_acc_hdl_t *hp = (ddi_acc_hdl_t *)hdlp; pmubus_mapreq_t *pmubus_mapreqp = hp->ah_bus_private; pmubus_devstate_t *softsp = pmubus_mapreqp->mapreq_softsp; off_t offset; uint8_t tmp; offset = pmubus_mapreqp->mapreq_addr + (uintptr_t)addr; offset &= PMUBUS_REGOFFSET; if ((pmubus_mapreqp->mapreq_flags) & MAPREQ_SHARED_BITS) { /* * Process "bit lane" register */ DPRINTF(PMUBUS_RW_DEBUG, ("pmubus_put8: addr=%p offset=%lx " "value=%x mask=%lx\n", (void *)addr, offset, value, pmubus_mapreqp->mapreq_mask)); if (addr != 0 || pmubus_mapreqp->mapreq_size != sizeof (value)) { cmn_err(CE_WARN, "pmubus_put8: store discarded, " "incorrect access addr/size"); return; } mutex_enter(&softsp->pmubus_reg_access_lock); tmp = pci_config_get8(softsp->pmubus_reghdl, offset); tmp &= ~pmubus_mapreqp->mapreq_mask; value &= pmubus_mapreqp->mapreq_mask; tmp |= value; pci_config_put8(softsp->pmubus_reghdl, offset, tmp); mutex_exit(&softsp->pmubus_reg_access_lock); } else { /* * Process shared register */ DPRINTF(PMUBUS_RW_DEBUG, ("pmubus_put8: addr=%p offset=%lx " "value=%x\n", (void *)addr, offset, value)); pci_config_put8(softsp->pmubus_reghdl, offset, value); } /* Flush store buffers XXX Should let drivers do this. */ tmp = pci_config_get8(softsp->pmubus_reghdl, offset); } /*ARGSUSED*/ void pmubus_noput16(ddi_acc_impl_t *hdlp, uint16_t *addr, uint16_t value) { } /*ARGSUSED*/ void pmubus_put32(ddi_acc_impl_t *hdlp, uint32_t *addr, uint32_t value) { ddi_acc_hdl_t *hp = (ddi_acc_hdl_t *)hdlp; pmubus_mapreq_t *pmubus_mapreqp = hp->ah_bus_private; pmubus_devstate_t *softsp = pmubus_mapreqp->mapreq_softsp; off_t offset; uint32_t tmp; offset = pmubus_mapreqp->mapreq_addr + (uintptr_t)addr; offset &= PMUBUS_REGOFFSET; if ((pmubus_mapreqp->mapreq_flags) & MAPREQ_SHARED_BITS) { /* * Process "bit lane" register */ DPRINTF(PMUBUS_RW_DEBUG, ("pmubus_put32: addr=%p offset=%lx " "value=%x mask=%lx\n", (void *)addr, offset, value, pmubus_mapreqp->mapreq_mask)); if (addr != 0 || pmubus_mapreqp->mapreq_size != sizeof (value)) { cmn_err(CE_WARN, "pmubus_put32: store discarded, " "incorrect access addr/size"); return; } mutex_enter(&softsp->pmubus_reg_access_lock); tmp = pci_config_get32(softsp->pmubus_reghdl, offset); tmp &= ~pmubus_mapreqp->mapreq_mask; value &= pmubus_mapreqp->mapreq_mask; tmp |= value; pci_config_put32(softsp->pmubus_reghdl, offset, tmp); mutex_exit(&softsp->pmubus_reg_access_lock); } else { /* * Process shared register */ DPRINTF(PMUBUS_RW_DEBUG, ("pmubus_put32: addr=%p offset=%lx " "value=%x\n", (void *)addr, offset, value)); pci_config_put32(softsp->pmubus_reghdl, offset, value); } /* Flush store buffers XXX Should let drivers do this. */ tmp = pci_config_get32(softsp->pmubus_reghdl, offset); } /*ARGSUSED*/ void pmubus_noput64(ddi_acc_impl_t *hdlp, uint64_t *addr, uint64_t value) { } /* * This routine is used to translate our children's register properties. * The return value specifies which type of register has been translated. */ /*ARGSUSED*/ int pmubus_apply_range(pmubus_devstate_t *pmubusp, dev_info_t *rdip, pmubus_regspec_t *regp, pci_regspec_t *pci_regp) { pmu_rangespec_t *rangep; int nranges = pmubusp->pmubus_nranges; int i; off_t offset; int ret = DDI_ME_REGSPEC_RANGE; uint64_t addr; addr = regp->reg_addr & ~MAPPING_SHARED_BITS_MASK; /* Scan the ranges for a match */ for (i = 0, rangep = pmubusp->pmubus_rangep; i < nranges; i++, rangep++) if ((rangep->rng_child <= addr) && ((addr + regp->reg_size) <= (rangep->rng_child + rangep->rng_size))) { ret = DDI_SUCCESS; break; } if (ret != DDI_SUCCESS) return (ret); /* Get the translated register */ offset = addr - rangep->rng_child; pci_regp->pci_phys_hi = rangep->rng_parent_hi; pci_regp->pci_phys_mid = rangep->rng_parent_mid; pci_regp->pci_phys_low = rangep->rng_parent_low + offset; pci_regp->pci_size_hi = 0; pci_regp->pci_size_low = MIN(regp->reg_size, rangep->rng_size); /* Figure out the type of reg space we have */ if (pci_regp->pci_phys_hi == pmubusp->pmubus_regp->pci_phys_hi) { ret = MAPREQ_SHARED_REG; if (regp->reg_addr & MAPPING_SHARED_BITS_MASK) ret |= MAPREQ_SHARED_BITS; } return (ret); } static uint64_t pmubus_mask(pmubus_obpregspec_t *regs, int32_t rnumber, uint64_t *masks) { int i; long n = -1; for (i = 0; i <= rnumber; i++) if (regs[i].reg_addr_hi & 0x80000000) n++; if (n == -1) { cmn_err(CE_WARN, "pmubus_mask: missing mask"); return (0); } return (masks[n]); } /* * The pmubus_map routine determines if it's child is attempting to map a * shared reg. If it is, it installs it's own vectors and bus private pointer. */ 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) { pmubus_devstate_t *pmubusp = ddi_get_soft_state(per_pmubus_state, ddi_get_instance(dip)); dev_info_t *pdip = (dev_info_t *)DEVI(dip)->devi_parent; pmubus_regspec_t pmubus_rp; pmubus_obpregspec_t *pmubus_regs = NULL; int pmubus_regs_size; uint64_t *pmubus_regmask = NULL; int pmubus_regmask_size; pci_regspec_t pci_reg; int32_t rnumber = mp->map_obj.rnumber; pmubus_mapreq_t *pmubus_mapreqp; int ret = DDI_SUCCESS; char *map_fail1 = "Map Type Unknown"; char *map_fail2 = "DDI_MT_REGSPEC"; char *s = map_fail1; *addrp = NULL; /* * Handle the mapping according to its type. */ DPRINTF(PMUBUS_MAP_DEBUG, ("rdip=%s%d: off=%lx len=%lx\n", ddi_get_name(rdip), ddi_get_instance(rdip), off, len)); switch (mp->map_type) { case DDI_MT_RNUMBER: { int n; /* * Get the "reg" property from the device node and convert * it to our parent's format. */ rnumber = mp->map_obj.rnumber; DPRINTF(PMUBUS_MAP_DEBUG, ("rdip=%s%d: rnumber=%x " "handlep=%p\n", ddi_get_name(rdip), ddi_get_instance(rdip), rnumber, (void *)mp->map_handlep)); if (ddi_getlongprop(DDI_DEV_T_ANY, rdip, DDI_PROP_DONTPASS, "reg", (caddr_t)&pmubus_regs, &pmubus_regs_size) != DDI_SUCCESS) { DPRINTF(PMUBUS_MAP_DEBUG, ("can't get reg " "property\n")); ret = DDI_ME_RNUMBER_RANGE; goto done; } n = pmubus_regs_size / sizeof (pmubus_obpregspec_t); if (rnumber < 0 || rnumber >= n) { DPRINTF(PMUBUS_MAP_DEBUG, ("rnumber out of range\n")); ret = DDI_ME_RNUMBER_RANGE; goto done; } pmubus_rp.reg_addr = ((uint64_t) pmubus_regs[rnumber].reg_addr_hi << 32) | (uint64_t)pmubus_regs[rnumber].reg_addr_lo; pmubus_rp.reg_size = pmubus_regs[rnumber].reg_size; (void) ddi_getlongprop(DDI_DEV_T_ANY, rdip, DDI_PROP_DONTPASS, "register-mask", (caddr_t)&pmubus_regmask, &pmubus_regmask_size); /* Create our own mapping private structure */ break; } case DDI_MT_REGSPEC: /* * This bus has no bus children that have to map in an address * space, so we can assume that we'll never see an * DDI_MT_REGSPEC request */ s = map_fail2; ret = DDI_ME_REGSPEC_RANGE; /*FALLTHROUGH*/ default: if (ret == DDI_SUCCESS) ret = DDI_ME_INVAL; DPRINTF(PMUBUS_MAP_DEBUG, ("rdip=%s%d: pmubus_map: " "%s is an invalid map type.\nmap request handlep=0x%p\n", ddi_get_name(rdip), ddi_get_instance(rdip), s, (void *)mp)); ret = DDI_ME_RNUMBER_RANGE; goto done; } /* Adjust our reg property with offset and length */ if ((pmubus_rp.reg_addr + off) > (pmubus_rp.reg_addr + pmubus_rp.reg_size)) { ret = DDI_ME_INVAL; goto done; } pmubus_rp.reg_addr += off; if (len && (len < pmubus_rp.reg_size)) pmubus_rp.reg_size = len; /* Translate our child regspec into our parents address domain */ ret = pmubus_apply_range(pmubusp, rdip, &pmubus_rp, &pci_reg); /* Check if the apply range failed */ if (ret < DDI_SUCCESS) goto done; /* * If our childs xlated address falls into our shared address range, * setup our mapping handle. */ if (ret > DDI_SUCCESS) { /* Figure out if we're mapping or unmapping */ switch (mp->map_op) { case DDI_MO_MAP_LOCKED: { ddi_acc_impl_t *hp = (ddi_acc_impl_t *)mp->map_handlep; pmubus_mapreqp = kmem_alloc(sizeof (*pmubus_mapreqp), KM_SLEEP); pmubus_mapreqp->mapreq_flags = ret; pmubus_mapreqp->mapreq_softsp = pmubusp; pmubus_mapreqp->mapreq_addr = pmubus_rp.reg_addr; pmubus_mapreqp->mapreq_size = pmubus_rp.reg_size; if (ret & MAPREQ_SHARED_BITS) { pmubus_mapreqp->mapreq_mask = pmubus_mask(pmubus_regs, rnumber, pmubus_regmask); DPRINTF(PMUBUS_MAP_DEBUG, ("rnumber=%d " "mask=%lx\n", rnumber, pmubus_mapreqp->mapreq_mask)); if (pmubus_mapreqp->mapreq_mask == 0) { kmem_free(pmubus_mapreqp, sizeof (pmubus_mapreq_t)); ret = DDI_ME_INVAL; break; } } hp->ahi_common.ah_bus_private = pmubus_mapreqp; /* Initialize the access vectors */ hp->ahi_get8 = pmubus_get8; hp->ahi_get16 = pmubus_noget16; hp->ahi_get32 = pmubus_get32; hp->ahi_get64 = pmubus_noget64; hp->ahi_put8 = pmubus_put8; hp->ahi_put16 = pmubus_noput16; hp->ahi_put32 = pmubus_put32; hp->ahi_put64 = pmubus_noput64; hp->ahi_rep_get8 = pmubus_norep_get8; hp->ahi_rep_get16 = pmubus_norep_get16; hp->ahi_rep_get32 = pmubus_norep_get32; hp->ahi_rep_get64 = pmubus_norep_get64; hp->ahi_rep_put8 = pmubus_norep_put8; hp->ahi_rep_put16 = pmubus_norep_put16; hp->ahi_rep_put32 = pmubus_norep_put32; hp->ahi_rep_put64 = pmubus_norep_put64; ret = DDI_SUCCESS; break; } case DDI_MO_UNMAP: { ddi_acc_impl_t *hp = (ddi_acc_impl_t *)mp->map_handlep; pmubus_mapreqp = hp->ahi_common.ah_bus_private; /* Free the our map request struct */ kmem_free(pmubus_mapreqp, sizeof (pmubus_mapreq_t)); ret = DDI_SUCCESS; break; } default: ret = DDI_ME_UNSUPPORTED; } } else { /* Prepare the map request struct for a call to our parent */ mp->map_type = DDI_MT_REGSPEC; mp->map_obj.rp = (struct regspec *)&pci_reg; /* Pass the mapping operation up the device tree */ ret = (DEVI(pdip)->devi_ops->devo_bus_ops->bus_map) (pdip, rdip, mp, off, len, addrp); } done: if (pmubus_regs != NULL) kmem_free(pmubus_regs, pmubus_regs_size); if (pmubus_regmask != NULL) kmem_free(pmubus_regmask, pmubus_regmask_size); return (ret); } static int pmubus_ctlops(dev_info_t *dip, dev_info_t *rdip, ddi_ctl_enum_t op, void *arg, void *result) { dev_info_t *child = (dev_info_t *)arg; pmubus_obpregspec_t *pmubus_rp; char name[9]; int reglen; switch (op) { case DDI_CTLOPS_INITCHILD: if (ddi_getlongprop(DDI_DEV_T_ANY, child, DDI_PROP_DONTPASS, "reg", (caddr_t)&pmubus_rp, ®len) != DDI_SUCCESS) { return (DDI_FAILURE); } if ((reglen % sizeof (pmubus_obpregspec_t)) != 0) { cmn_err(CE_WARN, "pmubus: reg property not well-formed for " "%s size=%d\n", ddi_node_name(child), reglen); kmem_free(pmubus_rp, reglen); return (DDI_FAILURE); } (void) snprintf(name, sizeof (name), "%x,%x", pmubus_rp->reg_addr_hi, pmubus_rp->reg_addr_lo); ddi_set_name_addr(child, name); kmem_free(pmubus_rp, reglen); return (DDI_SUCCESS); case DDI_CTLOPS_UNINITCHILD: ddi_set_name_addr(child, NULL); ddi_remove_minor_node(child, NULL); impl_rem_dev_props(child); return (DDI_SUCCESS); default: break; } return (ddi_ctlops(dip, rdip, op, arg, result)); }