/*
* 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.
*/
#include <sys/autoconf.h>
#include <sys/ddi_impldefs.h>
#include <sys/ndi_impldefs.h>
#include <sys/machsystm.h>
#include <sys/promimpl.h>
static unsigned int opl_cfg_inited;
/*
* Module control operations
*/
extern struct mod_ops mod_miscops;
&mod_miscops, /* Type of module */
"OPL opl_cfg"
};
};
static void opl_unmap_phys(ddi_acc_handle_t *);
extern int prom_get_fcode_size(char *);
extern int prom_get_fcode(char *, char *);
{ FC_MAP_IN, opl_map_in},
{ FC_MAP_OUT, opl_map_out},
{ "rx@", opl_register_fetch},
{ "rx!", opl_register_store},
{ "claim-memory", opl_claim_memory},
{ "release-memory", opl_release_memory},
{ "vtop", opl_vtop},
{ "get-hwd-va", opl_get_hwd_va},
{ "master-interrupt", opl_master_interrupt},
};
extern caddr_t efcode_vaddr;
extern int efcode_size;
#ifdef DEBUG
#endif
static int master_interrupt_inited = 0;
int
_init()
{
int err = 0;
/*
* Create a resource map for the contiguous memory allocated
* at start-of-day in startup.c
*/
if (err == NDI_FAILURE) {
return (1);
}
/*
* Put the allocated memory into the pool.
*/
}
return (err);
}
int
_fini(void)
{
int ret;
if (ret != 0)
return (ret);
return (ret);
}
int
{
}
#ifdef DEBUG
static void
{
if (hwddump_flags & HWDDUMP_OFFSETS) {
printf("HWD:status offset = 0x%x\n",
printf("HWD:domain offset = 0x%x\n",
}
if (hwddump_flags & HWDDUMP_SBP)
if (hwddump_flags & HWDDUMP_ALL_STATUS) {
int bd;
printf("HWD:board status =");
printf("\n");
} else {
}
printf("HWD:chip status:\n");
for (i = 0; i < HWD_CPU_CHIPS_PER_CMU; i++) {
printf("chip[%d] = ", i);
if (HWD_STATUS_NONE(status))
printf("none");
else if (HWD_STATUS_FAILED(status))
printf("fail");
else if (HWD_STATUS_OK(status))
printf("ok");
printf("\n");
}
if (hwddump_flags & HWDDUMP_CHUNKS) {
int chunk;
printf("HWD:chunks:\n");
}
printf("HWD:channel status:\n");
for (i = 0; i < HWD_PCI_CHANNELS_PER_SB; i++) {
printf("channels[%d] = ", i);
if (HWD_STATUS_NONE(status))
printf("none");
else if (HWD_STATUS_FAILED(status))
printf("fail");
else if (HWD_STATUS_OK(status))
printf("ok");
printf("\n");
}
printf("channels[%d] = ", i);
if (HWD_STATUS_NONE(status))
printf("none");
else if (HWD_STATUS_FAILED(status))
printf("fail");
else if (HWD_STATUS_OK(status))
printf("ok");
printf("\n");
}
#endif /* DEBUG */
#ifdef UCTEST
/*
* For SesamI debugging, just map the SRAM directly to a kernel
* VA and read it out from there
*/
#include <vm/seg_kmem.h>
/*
* 0x4081F1323000LL is the HWD base address for LSB 0. But we need to map
* at page boundaries. So, we use a base address of 0x4081F1322000LL.
* Note that this has to match the HWD base pa set in .sesami-common-defs.
*
* The size specified for the HWD in the SCF spec is 36K. But since
* we adjusted the base address by 4K, we need to use 40K for the
* mapping size to cover the HWD. And 40K is also a multiple of the
* base page size.
*/
void *opl_hwd_vaddr;
#endif /* UCTEST */
/*
* Get the hardware descriptor from SCF.
*/
/*ARGSUSED*/
int
{
void *) = NULL;
void *hwdp;
int ret;
#ifdef UCTEST
/*
* Just map the HWD in SRAM to a kernel VA
*/
size = 0xA000;
if (opl_hwd_vaddr == NULL) {
return (-1);
}
ret = 0;
#else
/* find the scf_service_getinfo() function */
uint32_t *,
void *))modgetsymvalue("scf_service_getinfo", 0);
return (-1);
/* allocate memory to receive the data */
/* get the HWD */
if (ret == 0)
else
#endif
} else {
ret = 0;
}
/* copy the data to the destination */
if (ret == 0) {
st = (hwd_sb_status_t *)
di = (hwd_domain_info_t *)
}
return (ret);
}
/*
* The opl_probe_t probe structure is used to pass all sorts of parameters
* to callback functions during probing. It also contains a snapshot of
* the hardware descriptor that is taken at the beginning of a probe.
*/
static int
{
/*
* Read the hardware descriptor.
*/
if (ret != 0) {
return (-1);
}
#ifdef DEBUG
#endif
return (0);
}
/*
* This function is used to obtain pointers to relevant device nodes
* which are created by Solaris at boot time.
*
* This function walks the child nodes of a given node, extracts
* the "name" property, if it exists, and passes the node to a
* callback init function. The callback determines if this node is
* interesting or not. If it is, then a pointer to the node is
* stored away by the callback for use during unprobe.
*
* The DDI get property function allocates storage for the name
* property. That needs to be freed within this function.
*/
static int
{
char *name;
int len;
/*
* Hold parent node busy to walk its child list
*/
if (ret != DDI_PROP_SUCCESS) {
/*
* The property does not exist for this node.
*/
continue;
}
if (ret != 0) {
return (-1);
}
}
return (0);
}
/*
* This init function finds all the interesting nodes under the
* root node and stores pointers to them. The following nodes
* are considered interesting by this implementation:
*
* "cmp"
* These are nodes that represent processor chips.
*
* "pci"
* These are nodes that represent PCI leaves.
*
* "pseudo-mc"
* These are nodes that contain memory information.
*/
static int
{
int ret;
if (ret != DDI_PROP_SUCCESS)
return (-1);
if (ret != DDI_PROP_SUCCESS)
return (-1);
if (ret != DDI_PROP_SUCCESS)
return (-1);
if (channel == OPL_CMU_CHANNEL) {
} else {
}
if (ret != DDI_PROP_SUCCESS)
return (-1);
}
return (0);
}
/*
* This function initializes the OPL IKP feature. Currently, all it does
* is find the interesting nodes that Solaris has created at boot time
* for boards present at boot time and store pointers to them. This
* is useful if those boards are unprobed by DR.
*/
int
{
if (opl_cfg_inited == 0) {
root = ddi_root_node();
return (1);
}
opl_cfg_inited = 1;
}
return (0);
}
/*
* When DR is initialized, we walk the device tree and acquire a hold on
* all the nodes that are interesting to IKP. This is so that the corresponding
* branches cannot be deleted.
*
* The following function informs the walk about which nodes are interesting
* so that it can hold the corresponding branches.
*/
static int
{
/*
* represent separate branches that must be managed.
*/
}
static int
{
/*
* represent separate branches that must be managed.
*/
if (opl_hold_node(name) == 0) {
/* Not of interest to us */
return (DDI_WALK_PRUNECHILD);
}
if (*holdp) {
} else {
}
return (DDI_WALK_PRUNECHILD);
}
void
{
int circ;
dip = ddi_root_node();
}
void
{
int circ;
int hold = 0;
dip = ddi_root_node();
}
/*
* This is a helper function that allows opl_create_node() to return a
* pointer to a newly created node to its caller.
*/
/*ARGSUSED*/
static void
{
}
/*
* Function to create a node in the device tree under a specified parent.
*
* e_ddi_branch_create() allows the creation of a whole branch with a
* single call of the function. However, we only use it to create one node
* at a time in the case of non-I/O device nodes. In other words, we
* create branches by repeatedly using this function. This makes the
* code more readable.
*
* The branch descriptor passed to e_ddi_branch_create() takes two
* callbacks. The create() callback is used to set the properties of a
* newly created node. The other callback is used to return a pointer
* to the newly created node. The create() callback is passed by the
* caller of this function based on the kind of node he wishes to
* create.
*
* e_ddi_branch_create() returns with the newly created node held. We
* only need to hold the top nodes of the branches we create. We release
* the hold for the others. E.g., the "cmp" node needs to be held. Since
* we hold the "cmp" node, there is no need to hold the "core" and "cpu"
* nodes below it.
*/
static dev_info_t *
{
return (NULL);
}
/*
* Function to tear down a whole branch rooted at the specified node.
*
* Although we create each node of a branch individually, we destroy
* a whole branch in one call. This is more efficient.
*/
static int
{
(void *)node);
return (-1);
}
return (0);
}
/*
* Set the properties for a "cpu" node.
*/
/*ARGSUSED*/
static int
{
int ret;
return (DDI_WALK_TERMINATE);
}
/*
* Create "cpu" nodes as child nodes of a given "core" node.
*/
static int
{
int i;
for (i = 0; i < HWD_CPUS_PER_CORE; i++) {
/*
* Olympus-C has 2 cpus per core.
* Jupiter has 4 cpus per core.
* For the Olympus-C based platform, we expect the cpu_status
* of the non-existent cpus to be set to missing.
*/
continue;
return (-1);
}
}
return (0);
}
/*
* Set the properties for a "core" node.
*/
/*ARGSUSED*/
static int
{
int ret;
sharing[0] = 0;
return (DDI_WALK_TERMINATE);
}
/*
* Create "core" nodes as child nodes of a given "cmp" node.
*
* Create the branch below each "core" node".
*/
static int
{
int i;
for (i = 0; i < HWD_CORES_PER_CPU_CHIP; i++) {
continue;
return (-1);
}
/*
* Create "cpu" nodes below "core".
*/
if (opl_probe_cpus(probe) != 0)
return (-1);
}
return (0);
}
/*
* Set the properties for a "cmp" node.
*/
/*ARGSUSED*/
static int
{
int ret;
range.rg_size_hi = 0;
range.rg_size_lo = 0;
return (DDI_WALK_TERMINATE);
}
/*
* Create "cmp" nodes as child nodes of the root node.
*
* Create the branch below each "cmp" node.
*/
static int
{
int i;
for (i = 0; i < HWD_CPU_CHIPS_PER_CMU; i++) {
continue;
probe->pr_cpu_chip = i;
return (-1);
}
cfg_cpu_chips[i] = node;
/*
* Create "core" nodes below "cmp".
* We hold the "cmp" node. So, there is no need to hold
* the "core" and "cpu" nodes below it.
*/
if (opl_probe_cores(probe) != 0)
return (-1);
}
return (0);
}
/*
* Set the properties for a "pseudo-mc" node.
*/
/*ARGSUSED*/
static int
{
int i, j;
int ret;
range.rg_size_hi = 0;
range.rg_size_lo = 0;
for (i = 0, j = 0; i < HWD_BANKS_PER_CMU; i++) {
continue;
j++;
}
if (j > 0) {
} else {
/*
* If there is no memory, we need the mc-addr property, but
* it is length 0. The only way to do this using ndi seems
* to be by creating a boolean property.
*/
}
for (i = 0, j = 0; i < CS_PER_MEM; i++) {
status[j][0] = i;
status[j][1] = 0;
else
status[j][2] =
status[j][3] =
j++;
}
}
if (j > 0) {
j*7);
} else {
/*
* If there is no memory, we need the cs-status property, but
* it is length 0. The only way to do this using ndi seems
* to be by creating a boolean property.
*/
"cs-status");
}
return (DDI_WALK_TERMINATE);
}
/*
* Create "pseudo-mc" nodes
*/
static int
{
int board;
return (-1);
}
return (0);
}
/*
* Allocate the fcode ops handle.
*/
/*ARGSUSED*/
static
char *my_args)
{
phandle_t h;
char *buf;
unit_address, NULL);
if (unit_address) {
}
/*
* Add the child's nodeid to our table...
*/
return (rp);
}
static void
{
if (rp->next_handle)
if (rp->unit_address)
/*
* Release all the resources from the resource list
*/
case RT_MAP:
/*
* If this is still mapped, we'd better unmap it now,
* or all our structures that are tracking it will
* be leaked.
*/
break;
case RT_DMA:
/*
* DMA has to be freed up at exit time.
*/
"opl_fc_ops_free_handle: Unexpected DMA seen!");
break;
case RT_CONTIGIOUS:
"Free claim-memory resource 0x%lx size 0x%x\n",
(void) ndi_ra_free(ddi_root_node(),
break;
default:
break;
}
}
}
int
{
/*
* First try the generic fc_ops.
*/
return (0);
/*
* Now try the Jupiter-specific ops.
*/
return (-1);
}
/*
* map-in (phys.lo phys.hi size -- virt)
*/
static int
{
int error;
if (error) {
}
/*
* Log this resource ...
*/
resp->fc_map_handle = h;
}
/*
* map-out (virt size -- )
*/
static int
{
/*
* Find if this request matches a mapping resource we set up.
*/
continue;
continue;
break;
}
"known mapping"));
/*
* remove the resource from the list and release it.
*/
}
static int
{
int error = 0;
uint64_t v;
uint64_t x;
uint32_t l;
uint16_t w;
uint8_t b;
/*
* Determine the access width .. we can switch on the 2nd
* character of the name which is "rx@", "rl@", "rb@" or "rw@"
*/
switch (*(service + 1)) {
case 'x': len = sizeof (x); break;
case 'l': len = sizeof (l); break;
case 'w': len = sizeof (w); break;
case 'b': len = sizeof (b); break;
}
/*
* Check the alignment ...
*/
/*
* Find if this virt is 'within' a request we know about
*/
break;
resp->fc_contig_len)))
break;
}
}
"known mappings"));
}
switch (len) {
case sizeof (x):
(int64_t *)&x);
else /* RT_CONTIGIOUS */
v = x;
break;
case sizeof (l):
(int32_t *)&l);
else /* RT_CONTIGIOUS */
v = l;
break;
case sizeof (w):
(int16_t *)&w);
else /* RT_CONTIGIOUS */
v = w;
break;
case sizeof (b):
(int8_t *)&b);
else /* RT_CONTIGIOUS */
v = b;
break;
}
if (error == DDI_FAILURE) {
}
switch (len) {
}
}
static int
{
uint64_t v;
uint64_t x;
uint32_t l;
uint16_t w;
uint8_t b;
int error = 0;
/*
* Determine the access width .. we can switch on the 2nd
* character of the name which is "rx!", "rl!", "rb!" or "rw!"
*/
switch (*(service + 1)) {
case 'x':
len = sizeof (x);
v = x;
break;
case 'l':
len = sizeof (l);
v = l;
break;
case 'w':
len = sizeof (w);
v = w;
break;
case 'b':
len = sizeof (b);
v = b;
break;
}
/*
* Check the alignment ...
*/
/*
* Find if this virt is 'within' a request we know about
*/
break;
resp->fc_contig_len)))
break;
}
}
"known mappings"));
switch (len) {
case sizeof (x):
break;
case sizeof (l):
break;
case sizeof (w):
break;
case sizeof (b):
break;
}
if (error == DDI_FAILURE) {
}
}
/*
* opl_claim_memory
*
* claim-memory (align size vhint -- vaddr)
*/
static int
{
if (size == 0) {
"contiguous memory of size zero\n");
}
if (vhint) {
"vhint=0x%x - Ignoring Argument\n", vhint);
}
request.ra_boundbase = 0;
"contiguous memory\n");
}
/*
* Log this resource ...
*/
}
/*
* opl_release_memory
*
* release-memory (size vaddr -- )
*/
static int
{
/*
* Find if this request matches a mapping resource we set up.
*/
continue;
continue;
break;
}
"known mapping"));
/*
* remove the resource from the list and release it.
*/
}
/*
* opl_vtop
*
* vtop (vaddr -- paddr.lo paddr.hi)
*/
static int
{
int vaddr;
/*
* Find if this request matches a mapping resource we set up.
*/
continue;
break;
}
"known mapping"));
}
static int
{
fc_phandle_t h;
}
static int
{
"fault copying in drop in name %p\n", dropin_name_virt);
status = 0;
} else {
fcode_len)) {
"to copy out fcode image");
status = 0;
}
}
}
}
static int
{
char *dropin_name;
int len;
"fault copying in drop in name %p\n", virt);
len = 0;
} else {
}
}
static int
{
int result;
acc_handlep->ah_rnumber = 0;
acc_handlep->ah_offset = 0;
acc_handlep->ah_len = 0;
/*
* cache a copy of the reg spec
*/
if (result != DDI_SUCCESS) {
} else {
}
return (result);
}
static void
{
/*
* Free the cached copy
*/
}
static int
{
void *hwd_virt;
/* Check the argument */
/* Get the parameters */
/* Get the ID numbers */
/* Set the pointer of hwd. */
}
/* Set the pointer of hwd sb. */
== NULL) {
}
if (ch == OPL_CMU_CHANNEL) {
/* Copyout CMU-CH HW Descriptor */
(void *)hwd_virt, sizeof (hwd_cmu_chan_t))) {
"Unable to copy out cmuch descriptor for %x",
portid);
status = 0;
}
} else {
/* Copyout PCI-CH HW Descriptor */
(void *)hwd_virt, sizeof (hwd_leaf_t))) {
"Unable to copy out pcich descriptor for %x",
portid);
status = 0;
}
}
}
/*
* After Solaris boots, a user can enter OBP using L1A, etc. While in OBP,
* interrupts may be received from PCI devices. These interrupts
* cannot be handled meaningfully since the system is in OBP. These
* interrupts need to be cleared on the CPU side so that the CPU may
* continue with whatever it is doing. Devices that have raised the
* interrupts are expected to reraise the interrupts after sometime
* as they have not been handled. At that time, Solaris will have a
* chance to properly service the interrupts.
*
* The location of the interrupt registers depends on what is present
* at a port. OPL currently supports the Oberon and the CMU channel.
* The following handler handles both kinds of ports and computes
* interrupt register addresses from the specifications and Jupiter Bus
* device bindings.
*
* Fcode drivers install their interrupt handler via a "master-interrupt"
* service. For boot time devices, this takes place within OBP. In the case
* of DR, OPL uses IKP. The Fcode drivers that run within the efcode framework
* attempt to install their handler via the "master-interrupt" service.
* However, we cannot meaningfully install the Fcode driver's handler.
* Instead, we install our own handler in OBP which does the same thing.
*
* Note that the only handling done for interrupts here is to clear it
* on the CPU side. If any device in the future requires more special
* handling, we would have to put in some kind of framework for adding
* device-specific handlers. This is *highly* unlikely, but possible.
*
* Finally, OBP provides a hook called "unix-interrupt-handler" to install
* a Solaris-defined master-interrupt handler for a port. The default
* definition for this method does nothing. Solaris may override this
* with its own definition. This is the way the following handler gets
* control from OBP when interrupts happen at a port after L1A, etc.
*/
static char define_master_interrupt_handler[] =
/*
* This method translates an Oberon port id to the base (physical) address
* of the interrupt clear registers for that port id.
*/
": pcich-mid>clear-int-pa ( mid -- pa ) "
" dup 1 >> 7 and ( mid ch# ) "
" over 4 >> h# 1f and ( mid ch# lsb# ) "
" 1 d# 46 << ( mid ch# lsb# pa ) "
" swap d# 40 << or ( mid ch# pa ) "
" swap d# 37 << or ( mid pa ) "
" swap 1 and if h# 70.0000 else h# 60.0000 then "
" or h# 1400 or ( pa ) "
"; "
/*
* This method translates a CMU channel port id to the base (physical) address
* of the interrupt clear registers for that port id. There are two classes of
* interrupts that need to be handled for a CMU channel:
* - obio interrupts
* - pci interrupts
* So, there are two addresses that need to be computed.
*/
": cmuch-mid>clear-int-pa ( mid -- obio-pa pci-pa ) "
" dup 1 >> 7 and ( mid ch# ) "
" over 4 >> h# 1f and ( mid ch# lsb# ) "
" 1 d# 46 << ( mid ch# lsb# pa ) "
" swap d# 40 << or ( mid ch# pa ) "
" swap d# 37 << or ( mid pa ) "
" nip dup h# 1800 + ( pa obio-pa ) "
" swap h# 1400 + ( obio-pa pci-pa ) "
"; "
/*
* This method checks if a given I/O port ID is valid or not.
* For a given LSB,
* Oberon ports range from 0 - 3
* CMU ch ports range from 4 - 4
*
* Also, the Oberon supports leaves 0 and 1.
* The CMU ch supports only one leaf, leaf 0.
*/
": valid-io-mid? ( mid -- flag ) "
" dup 1 >> 7 and ( mid ch# ) "
" dup 4 > if 2drop false exit then ( mid ch# ) "
" 4 = swap 1 and 1 = and not "
"; "
/*
* This method checks if a given port id is a CMU ch.
*/
": cmuch? ( mid -- flag ) 1 >> 7 and 4 = ; "
/*
* Given the base address of the array of interrupt clear registers for
* a port id, this method iterates over the given interrupt number bitmap
* and resets the interrupt on the CPU side for every interrupt number
* in the bitmap. Note that physical addresses are used to perform the
* writes, not virtual addresses. This allows the handler to work without
* any involvement from Solaris.
*/
": clear-ints ( pa bitmap count -- ) "
" 0 do ( pa bitmap ) "
" dup 0= if 2drop unloop exit then "
" tuck ( bitmap pa bitmap ) "
" 1 and if ( bitmap pa ) "
" dup i 8 * + 0 swap ( bitmap pa 0 pa' ) "
" h# 15 spacex! ( bitmap pa ) "
" then ( bitmap pa ) "
" swap 1 >> ( pa bitmap ) "
" loop "
"; "
/*
* This method replaces the master-interrupt handler in OBP. Once
* this method is plumbed into OBP, OBP transfers control to this
* handler while returning to Solaris from OBP after L1A. This method's
* task is to simply reset received interrupts on the CPU side.
* When the devices reassert the interrupts later, Solaris will
* be able to see them and handle them.
*
* For each port ID that has interrupts, this method is called
* once by OBP. The input arguments are:
* mid portid
* bitmap bitmap of interrupts that have happened
*
* This method returns true, if it is able to handle the interrupts.
* OBP does nothing further.
*
* This method returns false, if it encountered a problem. Currently,
* the only problem could be an invalid port id. OBP needs to do
* its own processing in that case. If this method returns false,
* it preserves the mid and bitmap arguments for OBP.
*/
": unix-resend-mondos ( mid bitmap -- [ mid bitmap false ] | true ) "
/*
* Uncomment the following line if you want to display the input arguments.
* This is meant for debugging.
* " .\" Bitmap=\" dup u. .\" MID=\" over u. cr "
*/
/*
* If the port id is not valid (according to the Oberon and CMU ch
* specifications, then return false to OBP to continue further
* processing.
*/
" over valid-io-mid? not if ( mid bitmap ) "
" false exit "
" then "
/*
* If the port is a CMU ch, then the 64-bit bitmap represents
* 2 32-bit bitmaps:
* - obio interrupt bitmap (20 bits)
* - pci interrupt bitmap (32 bits)
*
* - Split the bitmap into two
* - Compute the base addresses of the interrupt clear registers
* for both pci interrupts and obio interrupts
* - Clear obio interrupts
* - Clear pci interrupts
*/
" over cmuch? if ( mid bitmap ) "
" xlsplit ( mid pci-bit obio-bit ) "
" rot cmuch-mid>clear-int-pa ( pci-bit obio-bit obio-pa pci-pa ) "
" >r ( pci-bit obio-bit obio-pa ) ( r: pci-pa ) "
" swap d# 20 clear-ints ( pci-bit ) ( r: pci-pa ) "
" r> swap d# 32 clear-ints ( ) ( r: ) "
/*
* If the port is an Oberon, then the 64-bit bitmap is used fully.
*
* - Compute the base address of the interrupt clear registers
* - Clear interrupts
*/
" else ( mid bitmap ) "
" swap pcich-mid>clear-int-pa ( bitmap pa ) "
" swap d# 64 clear-ints ( ) "
" then "
/*
* Always return true from here.
*/
" true ( true ) "
"; "
;
static char install_master_interrupt_handler[] =
"' unix-resend-mondos to unix-interrupt-handler";
/*ARGSUSED*/
static int
{
uint_t defined;
return (1);
/*
* Check if the defer word "unix-interrupt-handler" is defined.
* This must be defined for OPL systems. So, this is only a
* sanity check.
*/
if (!defined) {
"%s is not defined\n", handler);
return (0);
}
/*
* Install the generic master-interrupt handler. Note that
* this is only done one time on the first DR operation.
* This is because, for OPL, one, single generic handler
* handles all ports (Oberon and CMU channel) and all
* interrupt sources within each port.
*
* The current support is only for the Oberon and CMU-channel.
* If any others need to be supported, the handler has to be
* modified accordingly.
*/
/*
* Define the OPL master interrupt handler
*/
prom_interpret(define_master_interrupt_handler, 0, 0, 0, 0, 0);
/*
* Take over the master interrupt handler from OBP.
*/
prom_interpret(install_master_interrupt_handler, 0, 0, 0, 0, 0);
/*
* prom_interpret() does not return a status. So, we assume
* that the calls succeeded. In reality, the calls may fail
* if there is a syntax error, etc in the strings.
*/
return (1);
}
/*
* Install the master-interrupt handler for a device.
*/
static int
{
int status;
/* Check the argument */
/* Get the parameters */
portid);
status = 0;
} else {
}
}
/*
* Set the properties for a leaf node (Oberon leaf or CMU channel leaf).
*/
/*ARGSUSED*/
static int
{
int ret;
return (DDI_WALK_TERMINATE);
}
static char *
{
char *probe_string;
int portid;
if (channel == OPL_CMU_CHANNEL)
else
return (probe_string);
}
static int
{
int board;
char *probe_string;
parent = ddi_root_node();
if (channel == OPL_CMU_CHANNEL) {
} else {
}
/*
* Prevent any changes to leaf_node until we have bound
* it to the correct driver.
*/
/*
* Ideally, fcode would be run from the "sid_branch_create"
* callback (that is the primary purpose of that callback).
* However, the fcode interpreter was written with the
* assumption that the "new_child" was linked into the
* device tree. The callback is invoked with the devinfo node
* in the DS_PROTO state. More investigation is needed before
* we can invoke the interpreter from the callback. For now,
* we create the "new_child" in the BOUND state, invoke the
* fcode interpreter and then rebind the dip to use any
* compatible properties created by fcode.
*/
return (-1);
}
/*
* The platform DR interfaces created the dip in
* bound state. Bring devinfo node down to linked
* state and hold it there until compatible
* properties are created.
*/
/*
* Drop the busy-hold on parent before calling
* fcode_interpreter to prevent potential deadlocks
*/
/*
* Get the probe string
*/
/*
* The fcode pointer specified here is NULL and the fcode
* size specified here is 0. This causes the user-level
* fcode interpreter to issue a request to the fcode
*/
if (error != 0) {
if (probe_string != NULL)
(void) opl_destroy_node(leaf_node);
} else {
*cfg_handle = fco_handle;
if (channel == OPL_CMU_CHANNEL)
else
= probe_string;
/*
* Compatible properties (if any) have been created,
* so bind driver.
*/
}
}
return (-1);
return (0);
}
static void
{
char *name;
parent = ddi_root_node();
/*
* Hold parent node busy to walk its child list
*/
if (ret != DDI_PROP_SUCCESS) {
/*
* The property does not exist for this node.
*/
continue;
}
if (ret == DDI_PROP_SUCCESS) {
&board, -1);
if ((ret != DDI_PROP_SUCCESS) ||
continue;
}
if (channel == OPL_CMU_CHANNEL) {
} else {
if (cfg->cfg_pcich_handle[
}
}
}
if (ret != DDI_PROP_SUCCESS)
break;
}
}
/*
* Create "pci" node and hierarchy for the Oberon channels and the
* CMU channel.
*/
/*ARGSUSED*/
static int
{
int i, j;
if (opl_probe_leaf(probe) != 0)
return (-1);
}
for (i = 0; i < HWD_PCI_CHANNELS_PER_SB; i++) {
continue;
probe->pr_channel = i;
for (j = 0; j < HWD_LEAVES_PER_PCI_CHANNEL; j++) {
continue;
(void) opl_probe_leaf(probe);
}
}
return (0);
}
/*
* Perform the probe in the following order:
*
* processors
* memory
* IO
*
* Each probe function returns 0 on sucess and a non-zero value on failure.
* What is a failure is determined by the implementor of the probe function.
* For example, while probing CPUs, any error encountered during probe
* is considered a failure and causes the whole probe operation to fail.
* However, for I/O, an error encountered while probing one device
* should not prevent other devices from being probed. It should not cause
* the whole probe operation to fail.
*/
int
{
int ret;
return (-1);
ASSERT(opl_cfg_inited != 0);
/*
* If the previous probe failed and left a partially configured
* board, we need to unprobe the board and start with a clean slate.
*/
(opl_unprobe_sb(board) != 0))
return (-1);
ret = 0;
if ((opl_probe_init(probe) != 0) ||
(opl_probe_cpu_chips(probe) != 0) ||
(opl_probe_memory(probe) != 0) ||
(opl_probe_io(probe) != 0)) {
/*
* Probe failed. Perform cleanup.
*/
(void) opl_unprobe_sb(board);
ret = -1;
}
return (ret);
}
/*
* This unprobing also includes CMU-CH.
*/
/*ARGSUSED*/
static int
{
int i, j, ret;
char **probe_str;
for (i = 0; i < HWD_PCI_CHANNELS_PER_SB; i++) {
for (j = 0; j < HWD_LEAVES_PER_PCI_CHANNEL; j++) {
continue;
}
}
if (ret != 0) {
"failed", board, i, j);
return (-1);
}
}
}
return (0);
}
}
if (opl_destroy_node(*node) != 0) {
OPL_CMU_CHANNEL, 0);
return (-1);
}
return (0);
}
/*
* Destroy the "pseudo-mc" node for a board.
*/
static int
{
return (0);
return (-1);
}
return (0);
}
/*
* Destroy the "cmp" nodes for a board. This also destroys the "core"
* and "cpu" nodes below the "cmp" nodes.
*/
static int
{
int i;
for (i = 0; i < HWD_CPU_CHIPS_PER_CMU; i++) {
if (cfg_cpu_chips[i] == NULL)
continue;
if (opl_destroy_node(cfg_cpu_chips[i]) != 0) {
board, i);
return (-1);
}
cfg_cpu_chips[i] = NULL;
}
return (0);
}
/*
* Perform the unprobe in the following order:
*
* IO
* memory
* processors
*/
int
{
return (-1);
ASSERT(opl_cfg_inited != 0);
if ((opl_unprobe_io(board) != 0) ||
(opl_unprobe_memory(board) != 0) ||
(opl_unprobe_processors(board) != 0))
return (-1);
#ifdef UCTEST
#endif
/* Release the memory for the HWD */
#ifdef UCTEST
#else
#endif
}
return (0);
}
/*
* For MAC patrol support, we need to update the PA-related properties
* when there is a copy-rename event. This should be called after the
* physical copy and rename has been done by DR, and before the MAC
* patrol is restarted.
*/
int
{
int elems;
int ret;
/* XXX -- bad news */
return (-1);
}
/* XXX -- bad news */
return (-1);
}
4);
4);
return (0);
}