ddi_impl.c revision b08160e2ab6be5348af95236e7cb23afbdfeb556
/*
* 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
* 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"
/*
* sun4 specific DDI implementation
*/
#include <sys/ddi_subrdefs.h>
#include <sys/machsystm.h>
#include <sys/sysmacros.h>
#include <vm/seg_kmem.h>
#include <sys/dditypes.h>
#include <sys/bootconf.h>
#include <sys/ethernet.h>
#include <sys/prom_plat.h>
#include <sys/systeminfo.h>
ddi_ispec_t *, ddi_ispec_t **);
#pragma weak get_intr_parent
ddi_intr_handle_impl_t *, void *);
#pragma weak process_intr_ops
#pragma weak cells_1275_copy
/*
* Wrapper for ddi_prop_lookup_int_array().
* This is handy because it returns the prop length in
* bytes which is what most of the callers require.
*/
static int
{
int ret;
}
return (ret);
}
/*
* SECTION: DDI Node Configuration
*/
/*
* init_regspec_64:
*
* If the parent #size-cells is 2, convert the upa-style or
* safari-style reg property from 2-size cells to 1 size cell
* format, ignoring the size_hi, which must be zero for devices.
* (It won't be zero in the memory list properties in the memory
* nodes, but that doesn't matter here.)
*/
struct ddi_parent_private_data *
{
struct ddi_parent_private_data *pd;
int size_cells;
/*
* If there are no "reg"s in the child node, return.
*/
return (pd);
}
if (size_cells != 1) {
int n, j;
struct reg_64 {
};
int *reg_prop;
/*
* We already looked the property up once before if
* pd is non-NULL.
*/
n = sizeof (struct reg_64) / sizeof (int);
n = len / n;
/*
* We're allocating a buffer the size of the PROM's property,
* but we're only using a smaller portion when we assign it
* to a regspec. We do this so that in the
* impl_ddi_sunbus_removechild function, we will
* always free the right amount of memory.
*/
}
}
return (pd);
}
/*
* Create a ddi_parent_private_data structure from the ddi properties of
* the dev_info node.
*
* The "reg" is required if the driver wishes to create mappings on behalf
* of the device. The "reg" property is assumed to be a list of at least
* one triplet
*
* <bustype, address, size>*1
*
* The "interrupt" property is no longer part of parent private data on
* sun4u. The interrupt parent is may not be the device tree parent.
*
* The "ranges" property describes the mapping of child addresses to parent
* addresses.
*
* N.B. struct rangespec is defined for the following default values:
* parent child
* #address-cells 2 2
* #size-cells 1 1
* This function doesn't deal with non-default cells and will not create
* ranges in such cases.
*/
void
{
struct ddi_parent_private_data *pdptr;
/*
* root node has no parent private data, so *ppd should
* be initialized for naming to work properly.
*/
return;
/*
* Set reg field of parent data from "reg" property
*/
== DDI_PROP_SUCCESS) && (reg_len != 0)) {
}
/*
* "ranges" property ...
*
* This function does not handle cases where #address-cells != 2
* and * min(parent, child) #size-cells != 1 (see bugid 4211124).
*
* Nexus drivers with such exceptions (e.g. pci ranges)
* should either create a separate function for handling
* ranges or not use parent private data to store ranges.
*/
/* root node has no ranges */
return;
"#address-cells or #size-cells have non-default value"));
return;
}
== DDI_PROP_SUCCESS) {
}
}
/*
* Free ddi_parent_private_data structure
*/
void
{
return;
}
/*
* Name a child of sun busses based on the reg spec.
* Handles the following properties:
*
* Property value
* Name type
*
* reg register spec
* interrupts new (bus-oriented) interrupt spec
* ranges range spec
*
* This may be called multiple times, independent of
* initchild calls.
*/
static int
{
struct ddi_parent_private_data *pdptr;
/*
* Fill in parent-private data and this function returns to us
* an indication if it used "registers" to fill in the data.
*/
}
/*
* No reg property, return null string as address
* (e.g. root node)
*/
name[0] = '\0';
if (sparc_pd_getnreg(child) == 0) {
return (DDI_SUCCESS);
}
return (DDI_SUCCESS);
}
/*
* Called from the bus_ctl op of some drivers.
* to implement the DDI_CTLOPS_INITCHILD operation.
*
* NEW drivers should NOT use this function, but should declare
* there own initchild/uninitchild handlers. (This function assumes
* the layout of the parent private data and the format of "reg",
* "ranges", "interrupts" properties and that #address-cells and
* #size-cells of the parent bus are defined to be default values.)
*/
int
{
char name[MAXNAMELEN];
/*
* Try to merge .conf node. If successful, return failure to
* remove this child.
*/
if ((ndi_dev_is_persistent_node(child) == 0) &&
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}
/*
* A better name for this function would be impl_ddi_sunbus_uninitchild()
* It does not remove the child, it uninitializes it, reclaiming the
* resources taken by impl_ddi_sunbus_initchild.
*/
void
{
/*
* Strip the node to properly convert it back to prototype form
*/
}
/*
* SECTION: DDI Interrupt
*/
void
{
int i;
for (i = 0; i < len; i++)
}
{
prop_1275_cell_t *match_cell = 0;
int32_t i;
for (i = 0; i < len; i++)
match_cell = &cell1[i];
break;
}
return (match_cell);
}
/*
* Wrapper functions used by New DDI interrupt framework.
*/
/*
* i_ddi_handle_intr_ops:
*/
int
{
int ret;
return (DDI_FAILURE);
return (ret);
}
/*
* i_ddi_intr_ops:
*/
int
{
int ret = DDI_FAILURE;
switch (op) {
case DDI_INTROP_ADDISR:
case DDI_INTROP_REMISR:
case DDI_INTROP_ENABLE:
case DDI_INTROP_DISABLE:
case DDI_INTROP_BLOCKENABLE:
case DDI_INTROP_BLOCKDISABLE:
/* Save the ispec */
/*
* If we have an ispec struct, try and determine our
* parent and possibly an interrupt translation.
* intr parent dip returned held
*/
/* Insert the interrupt info structure */
} else
goto done;
}
done:
switch (op) {
case DDI_INTROP_ADDISR:
case DDI_INTROP_REMISR:
case DDI_INTROP_ENABLE:
case DDI_INTROP_DISABLE:
case DDI_INTROP_BLOCKENABLE:
case DDI_INTROP_BLOCKDISABLE:
/* Release hold acquired in get_intr_parent() */
if (pdip)
if (ip) {
/* Set the PIL according to what the parent did */
/* Free the stacked ispec structure */
}
/* Restore the interrupt info */
}
return (ret);
}
/*
* process_intr_ops:
*
* Process the interrupt op via the interrupt parent.
*/
int
{
int ret = DDI_FAILURE;
if (NEXUS_HAS_INTR_OP(pdip)) {
} else {
"for %s%d due to down-rev nexus driver %s%d",
}
return (ret);
}
/*
* i_ddi_add_ivintr:
*/
/*ARGSUSED*/
int
{
/* Sanity check the entry we're about to add */
return (DDI_FAILURE);
}
/*
* If the PIL was set and is valid use it, otherwise
* default it to 1
*/
return (DDI_SUCCESS);
}
/*
* i_ddi_rem_ivintr:
*/
/*ARGSUSED*/
void
{
}
/*
* i_ddi_add_softint - allocate and add a soft interrupt to the system
*/
int
{
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}
void
{
/* disable */
}
int
{
int ret;
/* update the vector table for the 2nd arg */
if (ret == DDI_SUCCESS)
return (ret);
}
/* ARGSUSED */
int
{
int ret;
/* update the vector table for the new priority */
return (ret);
}
/*
* Support routine for allocating and initializing an interrupt specification.
* The bus interrupt value will be allocated at the end of this structure, so
* the corresponding routine i_ddi_free_ispec() should be used to free the
* interrupt specification.
*/
void
{
"#interrupt-cells", 1);
/* adjust for number of bytes */
/* Calculate the number of interrupts */
*ispecp = kmem_zalloc(
/* Index into interrupt property */
}
}
}
/*
* Analog routine to i_ddi_alloc_ispec() used to free the interrupt
* specification and the associated bus interrupt value.
*/
void
{
}
/*
* i_ddi_get_intr_pri - Get the interrupt-priorities property from
* the specified device.
*/
{
int32_t i;
/*
* Use the "interrupt-priorities" property to determine the
*/
&i) == DDI_SUCCESS) {
kmem_free(intr_prio_p, i);
}
return (pri);
}
/*
*/
static vmem_t *little_endian_arena;
static vmem_t *big_endian_arena;
static void *
{
}
static void *
{
}
void
ka_init(void)
{
}
/*
* Allocate from the system, aligned on a specific boundary.
* The alignment, if non-zero, must be a power of 2.
*/
static void *
{
/*
* We need to allocate
* rsize = size + hdrsize + align - MIN(hdrsize, buffer_alignment)
* bytes to be sure we have enough freedom to satisfy the request.
* Since the buffer alignment depends on the request size, this is
* not straightforward to use directly.
*
* kmem guarantees that any allocation of a 64-byte multiple will be
* 64-byte aligned. Since rounding up the request could add more
* than we save, we compute the size with and without alignment, and
* use the smaller of the two.
*/
if (endian_flags == DDI_STRUCTURE_LE_ACC) {
} else {
}
return (NULL);
return (addr);
}
static void
{
else
}
int
{
caddr_t a;
#if defined(lint)
#endif
/*
* Check legality of arguments
*/
return (DDI_FAILURE);
}
return (DDI_FAILURE);
}
/*
* Drivers for 64-bit capable SBus devices will encode
* the burtsizes for 64-bit xfers in the upper 16-bits.
* For DMA alignment, we use the most restrictive
* alignment of 32-bit and 64-bit xfers.
*/
/*
* If a driver set burtsizes to 0, we give him byte alignment.
* Otherwise align at the burtsizes boundary.
*/
if (iomin == 0)
iomin = 1;
else
if (iomin == 0)
return (DDI_FAILURE);
if ((*kaddrp = a) == 0) {
return (DDI_FAILURE);
} else {
if (real_length) {
*real_length = length;
}
if (handlep) {
/*
* assign handle information
*/
}
return (DDI_SUCCESS);
}
}
/*
* covert old DMA limits structure to DMA attribute structure
* and continue
*/
int
{
int ret;
attrp->dma_attr_flags = 0;
if (ret == DDI_SUCCESS) {
if (real_length)
}
return (ret);
}
/* ARGSUSED */
void
{
}
/*
* SECTION: DDI Data Access
*/
static uintptr_t impl_acc_hdl_id = 0;
/*
* access handle allocator
*/
{
/*
* Extract the access handle address from the DDI implemented
* access handle
*/
}
{
int sleepflag;
/*
* Allocate and initialize the data access handle and error status.
*/
goto fail;
goto fail;
}
goto fail;
}
return ((ddi_acc_handle_t)hp);
fail:
(waitfp != (int (*)())KM_NOSLEEP))
return (NULL);
}
void
{
/*
* The supplied (ddi_acc_handle_t) is actually a (ddi_acc_impl_t *),
* because that's what we allocated in impl_acc_hdl_alloc() above.
*/
if (hp) {
if (impl_acc_hdl_id)
}
}
void
{
int fmcap;
!DDI_FM_ACC_ERR_CAP(fmcap)) {
} else if (DDI_FM_ACC_ERR_CAP(fmcap)) {
NULL, DDI_NOSLEEP);
} else {
otp->ot_trampoline =
else
otp->ot_trampoline =
}
}
}
void
{
/*
* check for SW byte-swapping
*/
} else {
}
/* Legacy fault flags and support */
}
void
{
}
}
void
{
}
}
/* ARGSUSED */
void
{
/* Default version, does nothing */
}
/*
* SECTION: Misc functions
*/
/*
* instance wrappers
*/
/*ARGSUSED*/
{
return ((uint_t)-1);
}
/*ARGSUSED*/
int
{
return (DDI_FAILURE);
}
/*ARGSUSED*/
int
{
return (DDI_FAILURE);
}
/*ARGSUSED*/
int
{
return (DDI_SUCCESS);
}
static const char *nocopydevs[] = {
"SUNW,ffb",
"SUNW,afb",
};
/*
* Perform a copy from a memory mapped device (whose devinfo pointer is devi)
* separately mapped at devaddr in the kernel to a kernel buffer at kaddr.
*/
/*ARGSUSED*/
int
{
const char **argv;
return (0);
}
return (0);
}
/*
* Perform a copy to a memory mapped device (whose devinfo pointer is devi)
* separately mapped at devaddr in the kernel from a kernel buffer at kaddr.
*/
/*ARGSUSED*/
int
{
const char **argv;
return (1);
return (0);
}
/*
* Boot Configuration
*/
/*
* Configure the hardware on the system.
* Called before the rootfs is mounted
*/
void
configure(void)
{
extern void i_ddi_init_root();
/* We better have released boot by this time! */
/*
* Determine whether or not to use the fpu, V9 SPARC cpus
* always have one. Could check for existence of a fp queue,
* Ultra I, II and IIa do not have a fp queue.
*/
if (fpu_exists)
fpu_probe();
else
#if 0 /* XXXQ - not necessary for sun4u */
/*
* This following line fixes bugid 1041296; we need to do a
* prom_nextnode(0) because this call ALSO patches the DMA+
* bug in Campus-B and Phoenix. The prom uncaches the traptable
* page as a side-effect of devr_next(0) (which prom_nextnode calls),
* so this *must* be executed early on. (XXX This is untrue for sun4u)
*/
(void) prom_nextnode((dnode_t)0);
#endif
/*
* Initialize devices on the machine.
* Uses configuration tree built by the PROMs to determine what
* is present, and builds a tree of prototype dev_info nodes
* corresponding to the hardware which identified itself.
*/
#ifdef DDI_PROP_DEBUG
#endif /* DDI_PROP_DEBUG */
}
/*
* The "status" property indicates the operational status of a device.
* If this property is present, the value is a string indicating the
* status of the device as follows:
*
* "okay" operational.
* "disabled" not operational, but might become operational.
* "fail" not operational because a fault has been detected,
* and it is unlikely that the device will become
* operational without repair. no additional details
* are available.
* "fail-xxx" not operational because a fault has been detected,
* and it is unlikely that the device will become
* operational without repair. "xxx" is additional
* human-readable information about the particular
* fault condition that was detected.
*
* The absence of this property means that the operational status is
* unknown or okay.
*
* This routine checks the status property of the specified device node
* and returns 0 if the operational status indicates failure, and 1 otherwise.
*
* The property may exist on plug-in cards the existed before IEEE 1275-1994.
* And, in that case, the property may not even be a string. So we carefully
* check for the value "fail", in the beginning of the string, noting
* the property length.
*/
int
{
char status_buf[OBP_MAXPROPNAME];
int proplen;
static const char *status = "status";
static const char *fail = "fail";
/*
* Get the proplen ... if it's smaller than "fail",
* or doesn't exist ... then we don't care, since
* the value can't begin with the char string "fail".
*
* NB: proplen, if it's a string, includes the NULL in the
* the size of the property, and fail_len does not.
*/
return (1);
/*
* if a buffer was provided, use it
*/
bufp = status_buf;
len = sizeof (status_buf);
}
*bufp = (char)0;
/*
* Get the property into the buffer, to the extent of the buffer,
* and in case the buffer is smaller than the property size,
* NULL terminate the buffer. (This handles the case where
* a buffer was passed in and the caller wants to print the
* value, but the buffer was too small).
*/
/*
* If the value begins with the char string "fail",
* then it means the node is failed. We don't care
* about any other values. We assume the node is ok
* although it might be 'disabled'.
*/
return (0);
return (1);
}
/*
* We set the cpu type from the idprom, if we can.
* Note that we just read out the contents of it, for the most part.
*/
void
setcputype(void)
{
/*
* We cache the idprom info early on so that we don't
* rummage through the NVRAM unnecessarily later.
*/
}
/*
* Here is where we actually infer meanings to the members of idprom_t
*/
void
parse_idprom(void)
{
uint_t i;
(struct ether_addr *)NULL);
} else
prom_printf("Invalid format code in IDprom.\n");
}
/*
* Allow for implementation specific correction of PROM property values.
*/
/*ARGSUSED*/
void
{
/*
* There are no adjustments needed in this implementation.
*/
}
/*
* SECTION: DDI Interrupt
*/
/*
* get_intr_parent() is a generic routine that process a 1275 interrupt
* map (imap) property. This function returns a dev_info_t structure
* which claims ownership of the interrupt domain.
* It also returns the new interrupt translation within this new domain.
* If an interrupt-parent or interrupt-map property are not found,
* then we fallback to using the device tree's parent.
*
* imap entry format:
* <reg>,<interrupt>,<phandle>,<translated interrupt>
* reg - The register specification in the interrupts domain
* interrupt - The interrupt specification
* phandle - PROM handle of the device that owns the xlated interrupt domain
* translated interrupt - interrupt specifier in the parents domain
* note: <reg>,<interrupt> - The reg and interrupt can be combined to create
* a unique entry called a unit interrupt specifier.
*
* Here's the processing steps:
* step1 - If the interrupt-parent property exists, create the ispec and
* return the dip of the interrupt parent.
* step2 - Extract the interrupt-map property and the interrupt-map-mask
* If these don't exist, just return the device tree parent.
* step3 - build up the unit interrupt specifier to match against the
* interrupt map property
* step4 - Scan the interrupt-map property until a match is found
* step4a - Extract the interrupt parent
* step4b - Compare the unit interrupt specifier
*/
{
int32_t match_found = 0;
#ifdef DEBUG
static int debug = 0;
#endif
/*
* step1
* If we have an interrupt-parent property, this property represents
* the nodeid of our interrupt parent.
*/
"interrupt-parent", -1)) != -1) {
/*
* Attach the interrupt parent.
*
* N.B. e_ddi_nodeid_to_dip() isn't safe under DR.
* Also, interrupt parent isn't held. This needs
* to be revisited if DR-capable platforms implement
* interrupt redirection.
*/
!= DDI_SUCCESS) {
return (NULL);
}
/* Create a new interrupt info struct and initialize it. */
*new_ispecp = ispecp;
return (intr_parent_dip);
}
/*
* step2
* Get interrupt map structure from PROM property
*/
!= DDI_PROP_SUCCESS) {
/*
* If we don't have an imap property, default to using the
* device tree.
*/
/* Create a new interrupt info struct and initialize it. */
*new_ispecp = ispecp;
return (pdip);
}
/* Get the interrupt mask property */
!= DDI_PROP_SUCCESS) {
/*
* If we don't find this property, we have to fail the request
* because the 1275 imap property wasn't defined correctly.
*/
goto exit2;
}
/* Get the address cell size */
"#address-cells", 2);
/* Get the interrupts cell size */
"#interrupt-cells", 1);
/*
* step3
* Now lets build up the unit interrupt specifier e.g. reg,intr
* and apply the imap mask. match_req will hold this when we're
* through.
*/
goto exit3;
}
for (i = 0; i < addr_cells; i++)
for (j = 0; j < intr_cells; i++, j++)
/* Calculate the imap size in cells */
#ifdef DEBUG
if (debug)
prom_printf("reg cell size 0x%x, intr cell size 0x%x, "
#endif
/*
* Scan the imap property looking for a match of the interrupt unit
* specifier. This loop is rather complex since the data within the
* imap property may vary in size.
*/
int new_intr_cells;
/* Set the index to the nodeid field */
i = addr_cells + intr_cells;
/*
* step4a
* Translate the nodeid field to a dip
*/
ASSERT(intr_parent_dip != 0);
#ifdef DEBUG
if (debug)
#endif
/*
* The tmp_dip describes the new domain, get it's interrupt
* cell size
*/
"#interrupts-cells", 1);
/*
* step4b
* See if we have a match on the interrupt unit specifier
*/
== 0) {
/*
* Copy The childs ispec info excluding the interrupt
*/
ispec = *child_ispecp;
match_found = 1;
/*
* If we have an imap parent whose not in our device
* tree path, we need to hold and install that driver.
*/
!= DDI_SUCCESS) {
goto exit4;
}
/*
* We need to handcraft an ispec along with a bus
* interrupt value, so we can dup it into our
* standard ispec structure.
*/
/* Extract the translated interrupt information */
intr = kmem_alloc(
for (j = 0; j < new_intr_cells; j++, i++)
#ifdef DEBUG
if (debug)
prom_printf("dip 0x%x, intr info 0x%x\n",
#endif
break;
} else {
#ifdef DEBUG
if (debug)
#endif
i += new_intr_cells;
}
}
/*
* If we haven't found our interrupt parent at this point, fallback
* to using the device tree.
*/
if (!match_found) {
/* Create a new interrupt info struct and initialize it. */
}
*new_ispecp = ispecp;
return (intr_parent_dip);
}
/*
* Support routine for duplicating and initializing an interrupt specification.
* The bus interrupt value will be allocated at the end of this structure, so
* the corresponding routine i_ddi_free_ispec() should be used to free the
* interrupt specification.
*/
static ddi_ispec_t *
{
KM_SLEEP);
/* Copy the contents of the ispec */
*new_ispecp = *ispecp;
/* Reset the intr pointer to the one just created */
return (new_ispecp);
}
int
{
"#interrupt-cells", 1);
/* adjust for number of bytes */
}
return (ret);
}
/*ARGSUSED*/
{
extern int siron_pending;
siron_pending = 0;
softint();
return (1);
}
/*
* indirection table, to save us some large switch statements
* NOTE: This must agree with "INTLEVEL_foo" constants in
*/
struct autovec *const vectorlist[] = { 0 };
/*
* This value is exported here for the functions in avintr.c
*/
/*
* Check for machine specific interrupt levels which cannot be reassigned by
* settrap(), sun4u version.
*
* sun4u does not support V8 SPARC "fast trap" handlers.
*/
/*ARGSUSED*/
int
exclude_settrap(int lvl)
{
return (1);
}
/*
* Check for machine specific interrupt levels which cannot have interrupt
* handlers added. We allow levels 1 through 15; level 0 is nonsense.
*/
/*ARGSUSED*/
int
exclude_level(int lvl)
{
}
/*
* The following functions ready a cautious request to go up to the nexus
* driver. It is up to the nexus driver to decide how to process the request.
* It may choose to call i_ddi_do_caut_get/put in this file, or do it
* differently.
*/
static void
{
}
{
return (value);
}
{
return (value);
}
{
return (value);
}
{
return (value);
}
void
{
}
void
{
}
void
{
}
void
{
}
void
{
}
void
{
}
void
{
}
void
{
}
void
{
}
void
{
}
void
{
}
void
{
}
/*
* Assume that this is for memory, as nexi take care of device safe accesses.
*/
int
{
int err = DDI_SUCCESS;
/* Set up protected environment. */
if (cmd == DDI_CTLOPS_POKE) {
} else {
}
} else
err = DDI_FAILURE;
/* Take down protected environment. */
no_trap();
return (err);
}