opl_cfg.c revision 25cf1a301a396c38e8adf52c15f537b80d2483f7
/*
* 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 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#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 %I%"
};
static struct modlinkage modlinkage = {
};
static void opl_unmap_phys(ddi_acc_handle_t *);
extern int prom_get_fcode_size(char *);
extern int prom_get_fcode(char *, char *);
#define PROBE_STR_SIZE 64
#define UNIT_ADDR_SIZE 64
opl_fc_ops_t opl_fc_ops[] = {
{ 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},
};
extern caddr_t efcode_vaddr;
extern int efcode_size;
#ifdef DEBUG
#define HWDDUMP_OFFSETS 1
#define HWDDUMP_ALL_STATUS 2
#define HWDDUMP_CHUNKS 3
#define HWDDUMP_SBP 4
#endif
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.
*/
#define OPL_HWD_BASE(lsb) \
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
{
hwd_header_t **hdrp;
/*
* 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
opl_hold_node(char *name)
{
/*
* 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;
int hold = 1;
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
{
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 sharing[2];
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++;
}
ASSERT(j > 0);
#define CS_PER_MEM 2
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++;
}
}
ASSERT(j > 0);
j*7);
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:
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;
struct fc_resource *resp;
if (error) {
}
/*
* Log this resource ...
*/
resp->fc_map_handle = h;
}
/*
* map-out (virt size -- )
*/
static int
{
struct fc_resource *resp;
/*
* 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;
struct fc_resource *resp;
/*
* 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):
else /* RT_CONTIGIOUS */
v = x;
break;
case sizeof (l):
else /* RT_CONTIGIOUS */
v = l;
break;
case sizeof (w):
else /* RT_CONTIGIOUS */
v = w;
break;
case sizeof (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;
struct fc_resource *resp;
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
{
struct fc_resource *resp;
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
{
struct fc_resource *resp;
/*
* Find if this request matches a mapping resource we set up.
*/
continue;
continue;
break;
}
"known mapping"));
"opl-fcodemem", NDI_RA_PASS);
/*
* remove the resource from the list and release it.
*/
}
/*
* opl_vtop
*
* vtop (vaddr -- paddr.lo paddr.hi)
*/
static int
{
int vaddr;
struct fc_resource *resp;
/*
* Find if this request matches a mapping resource we set up.
*/
continue;
break;
}
"known mapping"));
}
static int
{
fc_phandle_t h;
}
static int
{
char *dropin_name, *fcode;
"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;
int status = 1;
/* 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;
}
}
}
/*
* 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 unit_address[UNIT_ADDR_SIZE];
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.
*/
if (ndi_devi_bind_driver(leaf_node, 0) !=
DDI_SUCCESS) {
"IKP: Unable to bind PCI leaf (%d-%d-%d)",
}
}
return (-1);
return (0);
}
static void
opl_init_leaves(int myboard)
{
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
opl_probe_sb(int board)
{
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
opl_unprobe_io(int board)
{
int i, j, ret;
dev_info_t **node;
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) {
"IKP: destroy pci (%d-%d-%d) failed",
board, i, j);
return (-1);
}
}
}
return (0);
}
}
if (opl_destroy_node(*node) != 0) {
board, OPL_CMU_CHANNEL, 0);
return (-1);
}
return (0);
}
/*
* Destroy the "pseudo-mc" node for a board.
*/
static int
opl_unprobe_memory(int board)
{
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) {
"IKP: destroy chip (%d-%d) failed", board, i);
return (-1);
}
cfg_cpu_chips[i] = NULL;
}
return (0);
}
/*
* Perform the unprobe in the following order:
*
* IO
* memory
* processors
*/
int
opl_unprobe_sb(int board)
{
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);
}