pmubus.c revision 903a11ebdc8df157c4700150f41f1f262f4a8ae8
/*
* 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
* 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.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <sys/ddi_impldefs.h>
#include <sys/ddi_subrdefs.h>
#include <sys/autoconf.h>
#include <sys/sysmacros.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:
*/
/*
* function prototypes for dev ops routines:
*/
/*
* general function prototypes:
*/
/*
* bus ops and dev ops structures:
*/
static struct bus_ops pmubus_bus_ops = {
NULL,
NULL,
NULL,
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 = {
0,
0,
(struct cb_ops *)0,
};
/*
* module definitions:
*/
extern struct mod_ops mod_driverops;
&mod_driverops, /* Type of module. This one is a driver */
"pmubus nexus driver", /* Name of module. */
&pmubus_ops, /* driver ops */
};
static struct modlinkage modlinkage = {
};
/*
* driver global data:
*/
static void *per_pmubus_state; /* per-pmubus soft state pointer */
int
_init(void)
{
int e;
/*
* Initialize per-pmubus soft state pointer.
*/
sizeof (pmubus_devstate_t), 1);
if (e != 0)
return (e);
/*
* Install the module.
*/
e = mod_install(&modlinkage);
if (e != 0)
return (e);
}
int
_fini(void)
{
int e;
/*
* Remove the module.
*/
e = mod_remove(&modlinkage);
if (e != 0)
return (e);
/*
* Free the soft state info.
*/
return (e);
}
int
{
}
/* 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
{
switch (cmd) {
case DDI_ATTACH:
/*
* Allocate soft state for this instance.
*/
DDI_SUCCESS) {
"state.\n");
goto fail_exit;
}
/* Cache our register property */
"property.\n");
goto fail_get_regs;
}
/* Cache our ranges property */
"ranges property.\n");
goto fail_get_ranges;
}
/* Calculate the number of ranges */
/* Set up the mapping to our registers */
DDI_SUCCESS) {
"register space.\n");
goto fail_map_regs;
}
/* Initialize our register access mutex */
MUTEX_DRIVER, NULL);
return (DDI_SUCCESS);
case DDI_RESUME:
return (DDI_SUCCESS);
}
return (DDI_FAILURE);
}
/*
* detach entry point:
*/
static int
{
instance);
switch (cmd) {
case DDI_DETACH:
/* Tear down our register mappings */
/* Free our ranges property */
/* Free the register property */
break;
case DDI_SUSPEND:
default:
break;
}
return (DDI_SUCCESS);
}
/*ARGSUSED*/
void
{
}
/*ARGSUSED*/
void
{
}
/*ARGSUSED*/
void
{
}
/*ARGSUSED*/
void
{
}
/*ARGSUSED*/
void
{
}
/*ARGSUSED*/
void
{
}
/*ARGSUSED*/
void
{
}
/*ARGSUSED*/
void
{
}
/*ARGSUSED*/
{
if (addr != 0 ||
return ((uint8_t)-1);
}
} else {
}
/* gets are simple, we just issue them no locking necessary */
return (value);
}
/*ARGSUSED*/
{
return ((uint16_t)-1);
}
/*ARGSUSED*/
{
if (addr != 0 ||
return ((uint32_t)-1);
}
} else {
}
/* gets are simple, we just issue them no locking necessary */
return (value);
}
/*ARGSUSED*/
{
return ((uint64_t)-1);
}
/*ARGSUSED*/
void
{
/*
* Process "bit lane" register
*/
if (addr != 0 ||
return;
}
} else {
/*
* Process shared register
*/
}
/* Flush store buffers XXX Should let drivers do this. */
}
/*ARGSUSED*/
void
{
}
/*ARGSUSED*/
void
{
/*
* Process "bit lane" register
*/
if (addr != 0 ||
return;
}
} else {
/*
* Process shared register
*/
}
/* Flush store buffers XXX Should let drivers do this. */
}
/*ARGSUSED*/
void
{
}
/*
* This routine is used to translate our children's register properties.
* The return value specifies which type of register has been translated.
*/
/*ARGSUSED*/
int
{
int i;
int ret = DDI_ME_REGSPEC_RANGE;
/* Scan the ranges for a match */
ret = DDI_SUCCESS;
break;
}
if (ret != DDI_SUCCESS)
return (ret);
/* Get the translated register */
pci_regp->pci_size_hi = 0;
/* Figure out the type of reg space we have */
}
return (ret);
}
static uint64_t
{
int i;
long n = -1;
for (i = 0; i <= rnumber; i++)
n++;
if (n == -1) {
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
{
int pmubus_regs_size;
int pmubus_regmask_size;
int ret = DDI_SUCCESS;
char *map_fail1 = "Map Type Unknown";
char *map_fail2 = "DDI_MT_REGSPEC";
char *s = map_fail1;
/*
* Handle the mapping according to its type.
*/
case DDI_MT_RNUMBER: {
int n;
/*
* Get the "reg" property from the device node and convert
* it to our parent's format.
*/
DDI_SUCCESS) {
"property\n"));
goto done;
}
n = pmubus_regs_size / sizeof (pmubus_obpregspec_t);
goto done;
}
/* 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;
/*FALLTHROUGH*/
default:
if (ret == DDI_SUCCESS)
ret = DDI_ME_INVAL;
"%s is an invalid map type.\nmap request handlep=0x%p\n",
goto done;
}
/* Adjust our reg property with offset and length */
ret = DDI_ME_INVAL;
goto done;
}
/* Translate our child regspec into our parents address domain */
/* 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 */
case DDI_MO_MAP_LOCKED: {
KM_SLEEP);
if (ret & MAPREQ_SHARED_BITS) {
"mask=%lx\n", rnumber,
if (pmubus_mapreqp->mapreq_mask == 0) {
sizeof (pmubus_mapreq_t));
ret = DDI_ME_INVAL;
break;
}
}
/* Initialize the access vectors */
ret = DDI_SUCCESS;
break;
}
case DDI_MO_UNMAP: {
/* Free the our map request struct */
ret = DDI_SUCCESS;
break;
}
default:
}
} else {
/* Prepare the map request struct for a call to our parent */
/* Pass the mapping operation up the device tree */
}
done:
if (pmubus_regs != NULL)
if (pmubus_regmask != NULL)
return (ret);
}
static int
{
char name[9];
int reglen;
switch (op) {
case DDI_CTLOPS_INITCHILD:
®len) != DDI_SUCCESS) {
return (DDI_FAILURE);
}
if ((reglen % sizeof (pmubus_obpregspec_t)) != 0) {
"pmubus: reg property not well-formed for "
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
case DDI_CTLOPS_UNINITCHILD:
return (DDI_SUCCESS);
default:
break;
}
}