mach_ddi_impl.c revision 737d277a27d4872543f597e35c470e7510f61f03
/*
* 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 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* sun4u specific DDI implementation
*/
#include <sys/bootconf.h>
#include <sys/ddi_subrdefs.h>
#include <sys/ethernet.h>
#include <sys/machsystm.h>
#include <sys/prom_plat.h>
#include <sys/systeminfo.h>
/*
* Favored drivers of this implementation
* architecture. These drivers MUST be present for
* the system to boot at all.
*/
char *impl_module_list[] = {
"rootnex",
"options",
"sad", /* Referenced via init_tbl[] */
"pseudo",
"clone",
"scsi_vhci",
(char *)0
};
/*
* These strings passed to not_serviced in locore.s
*/
const char busname_ovec[] = "onboard ";
const char busname_svec[] = "SBus ";
const char busname_vec[] = "";
/*
* Forward declarations
*/
static int getlongprop_buf();
/*
* Check the status of the device node passed as an argument.
*
* if ((status is OKAY) || (status is DISABLED))
* return DDI_SUCCESS
* else
* print a warning and return DDI_FAILURE
*/
/*ARGSUSED*/
int
{
char status_buf[64];
char devtype_buf[OBP_MAXPROPNAME];
char board_buf[32];
char path[OBP_MAXPATHLEN];
int boardnum;
int retval = DDI_FAILURE;
extern int status_okay(int, char *, int);
/*
* is the status okay?
*/
return (DDI_SUCCESS);
/*
* a status property indicating bad memory will be associated
* with a node which has a "device_type" property with a value of
* "memory-controller". in this situation, return DDI_SUCCESS
*/
sizeof (devtype_buf)) > 0) {
}
/*
* get the full OBP pathname of this node
*/
/*
* get the board number, if one exists
*/
else
board_buf[0] = '\0';
/*
* print the status property information
*/
return (retval);
}
/*
* determine the board number associated with this nodeid
*/
static int
{
int board_num;
return (board_num);
/*
* Look at current node and up the parent chain
* till we find a node with an OBP_BOARDNUM.
*/
while (par) {
return (board_num);
}
return (-1);
}
/*
* Note that this routine does not take into account the endianness
* of the host or the device (or PROM) when retrieving properties.
*/
static int
{
int size;
return (-1);
return (-1);
/*
* Workaround for bugid 1085575 - OBP may return a "name" property
* without null terminating the string with '\0'. When this occurs,
* append a '\0' and return (size + 1).
*/
size += 1;
}
}
return (size);
}
/*
* set_intr_mapping_reg() is called by the UPA master to register the address
* of an interrupt mapping register. The upa id is that of the master. If
* this routine is called on behalf of a slave device, the framework
* determines the upa id of the slave based on that supplied by the master.
*
* get_intr_mapping_reg() is called by the UPA nexus driver on behalf
* of a child device to get and program the interrupt mapping register of
* one of it's child nodes. It uses the upa id of the child device to
* index into a table of mapping registers. If the routine is called on
* behalf of a slave device and the mapping register has not been set,
* the framework determines the devinfo node of the corresponding master
* nexus which owns the mapping register of the slave and installs that
* driver. The device driver which owns the mapping register must call
* set_intr_mapping_reg() in its attach routine to register the slaves
* mapping register with the system.
*/
void
{
int affin_upaid;
/* For UPA master devices, set the mapping reg addr and we're done */
if (slave == 0) {
return;
}
/*
* If we get here, we're adding an entry for a UPA slave only device.
* The UPA id of the device which has affinity with that requesting,
* will be the device with the same UPA id minus the slave number.
* If the affin_upaid is negative, silently return to the caller.
*/
return;
/*
* Load the address of the mapping register in the correct slot
* for the slave device.
*/
}
uint64_t *
{
int affin_upaid;
/* If we're a UPA master, or we have a valid mapping register. */
return (addr);
/*
* We only get here if we're a UPA slave only device whose interrupt
* mapping register has not been set.
* We need to try and install the nexus whose physical address
* space is where the slaves mapping register resides. They
* should call set_intr_mapping_reg() in their xxattach() to register
* the mapping register with the system.
*/
/*
* We don't know if a single- or multi-interrupt proxy is fielding
* our UPA slave interrupt, we must check both cases.
* Start out by assuming the multi-interrupt case.
* We assume that single- and multi- interrupters are not
* overlapping in UPA portid space.
*/
/*
* We start looking for the multi-interrupter affinity node.
* We know it's ONLY a child of the root node since the root
* node defines UPA space.
*/
break;
if (affin_dip) {
/* try again to get the mapping register. */
}
}
/*
* If we still don't have a mapping register try single -interrupter
* case.
*/
break;
if (affin_dip) {
== DDI_SUCCESS) {
/* try again to get the mapping register. */
}
}
}
return (addr);
}
static struct upa_dma_pfns {
static int upa_dma_pfn_ndx = 0;
/*
* Certain UPA busses cannot accept dma transactions from any other source
* except for memory due to livelock conditions in their hardware. (e.g. sbus
* and PCI). These routines allow devices or busses on the UPA to register
* a physical address block within it's own register space where DMA can be
* performed. Currently, the FFB is the only such device which supports
* device DMA on the UPA.
*/
void
{
int i = upa_dma_pfn_ndx;
}
void
{
int i;
for (i = 0; i < upa_dma_pfn_ndx; i++) {
upa_dma_pfn_array[i].hipfn =
upa_dma_pfn_array[i].lopfn =
break;
}
}
}
/*
* This routine should only be called using a pfn that is known to reside
* in IO space. The function pf_is_memory() can be used to determine this.
*/
int
{
int i, j;
/* If the caller passed in a memory pfn, return true. */
if (pf_is_memory(pfn))
return (1);
for (i = upa_dma_pfn_ndx, j = 0; j < i; j++)
return (1);
return (0);
}
/*
* Find cpu_id corresponding to the dip of a CPU device node
*/
int
{
int i;
for (i = 0; i < NCPU; i++) {
*cpu_id = i;
return (DDI_SUCCESS);
}
}
return (DDI_FAILURE);
}
/*
* Platform independent DR routines
*/
static int
ndi2errno(int n)
{
int err = 0;
switch (n) {
case NDI_NOMEM:
break;
case NDI_BUSY:
break;
case NDI_FAULT:
break;
case NDI_FAILURE:
break;
case NDI_SUCCESS:
break;
case NDI_BADHANDLE:
default:
break;
}
return (err);
}
/*
* Prom tree node list
*/
struct ptnode {
};
/*
* Prom tree walk arg
*/
struct pta {
};
static void
{
;
}
return;
}
}
/*ARGSUSED*/
static int
{
if (!DEVI_IS_DEVICE_OFFLINE(dip))
return (DDI_WALK_CONTINUE);
}
/*ARGSUSED*/
static int
{
int circ, c;
"nodeid: 0x%x", nodeid);
return (EINVAL);
}
}
return (ENODEV);
rv = 0;
/*
* Check if the branch already exists.
*/
exists = 0;
exists = 1;
/* Parent is held busy, so release hold */
#ifdef DEBUG
#endif
} else {
}
continue;
}
/*
* Hold the branch if it is not already held
*/
if (!exists)
/*
* Set all dips in the branch offline so that
* only a "configure" operation can attach
* the branch
*/
ndi_devi_enter(dip, &c);
ndi_devi_exit(dip, c);
}
/*
* Invoke devi_branch_callback() (if it exists) only for
* newly created branches
*/
}
return (rv);
}
static int
{
int i, flags;
char *nbuf;
static const char *noname = "<none>";
flags = 0;
/*
* Creating the root of a branch ?
*/
if (rdipp) {
}
if (rv == DDI_WALK_ERROR) {
" properties on devinfo node %p", (void *)dip);
goto fail;
}
!= DDI_PROP_SUCCESS) {
"no name property", (void *)dip);
goto fail;
}
goto fail;
}
/*
* Ignore bind failures just like boot does
*/
(void) ndi_devi_bind_driver(dip, 0);
switch (rv) {
case DDI_WALK_CONTINUE:
case DDI_WALK_PRUNESIB:
i = DDI_WALK_CONTINUE;
for (; i == DDI_WALK_CONTINUE; ) {
}
if (i == DDI_WALK_ERROR)
rv = i;
/*
* If PRUNESIB stop creating siblings
* of dip's child. Subsequent walk behavior
* is determined by rv returned by dip.
*/
break;
case DDI_WALK_TERMINATE:
/*
* Don't create children and ask our parent
* to not create siblings either.
*/
break;
case DDI_WALK_PRUNECHILD:
/*
* Don't create children, but ask parent to continue
* with siblings.
*/
break;
default:
ASSERT(0);
break;
}
if (rdipp)
/*
* Set device offline - only the "configure" op should cause an attach
*/
return (rv);
fail:
(void) ndi_devi_free(dip);
return (DDI_WALK_ERROR);
}
static int
dev_info_t **dipp,
{
while (state == DDI_WALK_CONTINUE) {
int circ;
break;
}
if (flags & DEVI_BRANCH_CONFIGURE) {
}
/*
* devi_branch_callback() is optional
*/
if (bp->devi_branch_callback)
}
}
int
dev_info_t **dipp,
{
return (EINVAL);
return (EINVAL);
return (EINVAL);
return (EINVAL);
if (flags & DEVI_BRANCH_EVENT)
return (EINVAL);
if (prom_devi) {
if (dipp)
} else {
}
return (error);
}
int
{
char *devnm;
if (dipp)
return (EINVAL);
if (!e_ddi_branch_held(rdip)) {
"dip(%p) not held", (void *)rdip);
return (EINVAL);
}
/*
* First attempt to bind a driver. If we fail, return
* success (On some platforms, dips for some device
* types (CPUs) may not have a driver)
*/
return (0);
}
rv = NDI_FAILURE;
goto out;
}
}
/* release hold from ndi_devi_config_one() */
}
out:
}
}
void
{
if (e_ddi_branch_held(rdip)) {
return;
}
}
}
int
{
int rv = 0;
rv = 1;
}
return (rv);
}
void
{
}
int
dev_info_t **dipp,
{
int destroy;
char *devnm;
if (dipp)
return (EINVAL);
/*
* Check if caller holds pdip busy - can cause deadlocks during
* devfs_clean()
*/
if (DEVI_BUSY_OWNED(pdip)) {
" devinfo node(%p) is busy held", (void *)pdip);
return (EINVAL);
}
/*
* ddi_deviname() returns a component name with / prepended.
*/
if (rv) {
return (rv);
}
/*
* when parent busy lock was dropped for devfs_clean()
*/
if (!e_ddi_branch_held(rdip)) {
return (EINVAL);
}
/*
* Release hold on the branch. This is ok since we are holding the
* parent busy. If rdip is not removed, we must do a hold on the
* branch before returning.
*/
destroy = 1;
} else {
}
if (flags & DEVI_BRANCH_EVENT)
nflags |= NDI_POST_EVENT;
if (i_ddi_devi_attached(pdip) &&
} else {
if (rv == NDI_SUCCESS) {
}
}
/* The dip still exists, so do a hold */
}
out:
}
int
{
}
/*
* Number of chains for hash table
*/
#define NUMCHAINS 17
/*
* Devinfo busy arg
*/
struct devi_busy {
int dv_total;
int s_total;
void *arg;
};
static int
{
/*
* A dip cannot be busy if its reference count is 0
*/
}
dvbusy = 0;
/*
* To catch device opens currently maintained on specfs common snodes.
*/
sbusy = 0;
#ifdef DEBUG
}
#endif
}
static int
{
int count;
/*
* The stable lock is held. This prevents
* the snode and its associated dip from
* going away.
*/
if (count <= 0)
return (DDI_WALK_CONTINUE);
else
"sbusy = %lu", "e_ddi_branch_referenced",
}
return (DDI_WALK_CONTINUE);
}
static void
{
if (!count)
return;
(mod_hash_val_t *)&dvbusy))
else
(mod_hash_val_t)dvbusy)) {
"dvbusy=%lu", "e_ddi_branch_referenced",
}
}
/*
* Returns reference count on success or -1 on failure.
*/
int
void *arg)
{
int circ;
char *path;
/*
* Check if caller holds pdip busy - can cause deadlocks during
* devfs_walk()
*/
"devinfo branch(%p) not held or parent busy held",
(void *)rdip);
return (-1);
}
mod_hash_null_valdtor, sizeof (struct dev_info));
mod_hash_null_valdtor, sizeof (struct snode));
"devfs walk failed for: %s", path);
goto out;
}
/*
* Walk the snode table to detect device opens, which are currently
* maintained on specfs common snodes.
*/
goto out;
}
out:
}