pmubus.c revision f47a9c508408507a404eaf38dd597e6ac41f92e6
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (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 2005 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <sys/types.h>
#include <sys/conf.h>
#include <sys/ddi.h>
#include <sys/sunddi.h>
#include <sys/ddi_impldefs.h>
#include <sys/ddi_subrdefs.h>
#include <sys/pci.h>
#include <sys/autoconf.h>
#include <sys/cmn_err.h>
#include <sys/errno.h>
#include <sys/kmem.h>
#include <sys/debug.h>
#include <sys/sysmacros.h>
#include <sys/pmubus.h>
#include <sys/nexusdebug.h>
/* 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,
ddi_dma_map,
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
};
/*
* module definitions:
*/
#include <sys/modctl.h>
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", 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", 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", 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", 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", 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", 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, 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, 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,
&reglen) != 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));
}