root/usr/src/uts/sun4u/io/gptwo_cpu.c
/*
 * 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
 * or http://www.opensolaris.org/os/licensing.
 * 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.
 */

/*
 * CPU functions to the Safari Configurator  (gptwo_cpu)
 */

#include <sys/types.h>
#include <sys/cred.h>
#include <sys/mman.h>
#include <sys/kmem.h>
#include <sys/conf.h>
#include <sys/ddi.h>
#include <sys/sunddi.h>
#include <sys/sunndi.h>
#include <sys/modctl.h>
#include <sys/stat.h>
#include <sys/param.h>
#include <sys/autoconf.h>
#include <sys/ksynch.h>
#include <sys/promif.h>
#include <sys/ndi_impldefs.h>
#include <sys/ddi_impldefs.h>
#include <sys/machsystm.h>
#include <sys/gp2cfg.h>
#include <sys/gptwo_cpu.h>
#include <sys/cheetahregs.h>

#ifdef DEBUG
int gptwo_cpu_debug = 0;

static void debug(char *, uintptr_t, uintptr_t,
    uintptr_t, uintptr_t, uintptr_t);

#define GPTWO_DEBUG0(level, flag, s) if (gptwo_cpu_debug >= level) \
    cmn_err(flag, s)
#define GPTWO_DEBUG1(level, flag, fmt, a1) if (gptwo_cpu_debug >= level) \
    debug(fmt, (uintptr_t)(a1), 0, 0, 0, 0);
#define GPTWO_DEBUG2(level, flag, fmt, a1, a2) if (gptwo_cpu_debug >= level) \
    debug(fmt, (uintptr_t)(a1), (uintptr_t)(a2), 0, 0, 0);
#define GPTWO_DEBUG3(level, flag, fmt, a1, a2, a3) \
    if (gptwo_cpu_debug >= level) \
    debug(fmt, (uintptr_t)(a1), (uintptr_t)(a2), (uintptr_t)(a3), 0, 0);
#else
#define GPTWO_DEBUG0(level, flag, s)
#define GPTWO_DEBUG1(level, flag, fmt, a1)
#define GPTWO_DEBUG2(level, flag, fmt, a1, a2)
#define GPTWO_DEBUG3(level, flag, fmt, a1, a2, a3)
#endif

/*
 * Devinfo branch create arg
 */
struct bca {
        spcd_t *pcd;
        uint_t portid;
        uint_t cpuid;
        uint_t coreid;
        uint_t impl;
        dev_info_t *new_child;
};

static dev_info_t *gptwocfg_create_cpu_node(dev_info_t *, spcd_t *,
    uint_t, uint_t, uint_t, uint_t);
static dev_info_t *gptwocfg_create_mc_node(dev_info_t *, spcd_t *, uint_t);
static dev_info_t *gptwocfg_create_cmp_node(dev_info_t *, spcd_t *, uint_t);
static int gptwocfg_create_core_node(dev_info_t *, spcd_t *, uint_t, uint_t);
static int set_mc_props(dev_info_t *new_child, void *arg, uint_t flags);
static int set_cmp_props(dev_info_t *new_child, void *arg, uint_t flags);
static int set_cpu_props(dev_info_t *new_child, void *arg, uint_t flags);
static int set_cpu_common_props(dev_info_t *new_child, struct bca *bcp);
static int set_cpu_us3_props(dev_info_t *new_child, struct bca *bcp);
static int set_cpu_us4_props(dev_info_t *new_child, struct bca *bcp);
static void get_new_child(dev_info_t *rdip, void *arg, uint_t flags);


/*
 * Module linkage information for the kernel.
 */

extern struct mod_ops mod_miscops;

static struct modlmisc modlmisc = {
        &mod_miscops, /* Type of module */
        "gptwo->cpu configurator",
};

static struct modlinkage modlinkage = {
        MODREV_1, (void *)&modlmisc, NULL
};

int
_init(void)
{
        int err = 0;

        /* register device with the configurator */
        gptwocfg_register_ops(SAFPTYPE_CPU, gptwocfg_configure_cpu, NULL);

        if ((err = mod_install(&modlinkage)) != 0) {
                GPTWO_DEBUG1(1, CE_WARN, "gptwo_cpu (CPU/MC Functions) "
                "failed to load, error=%d\n", err);
                gptwocfg_unregister_ops(SAFPTYPE_CPU);
        } else {
                GPTWO_DEBUG0(1, CE_WARN, "gptwo_cpu (CPU/MC Functions) "
                "has been loaded.\n");
        }
        return (err);
}

int
_fini(void)
{
        /* cleanup/freeup structs with configurator */
        gptwocfg_unregister_ops(SAFPTYPE_CPU);
        return (mod_remove(&modlinkage));
}

int
_info(struct modinfo *modinfop)
{
        return (mod_info(&modlinkage, modinfop));
}

gptwo_new_nodes_t *
gptwocfg_configure_cpu(dev_info_t *ap, spcd_t *pcd, uint_t portid)
{
        dev_info_t *cpu_node[AGENTS_PER_PORT], *mc_node[AGENTS_PER_PORT];
        dev_info_t *cmp_node = NULL;
        gptwo_new_nodes_t *new_nodes;
        int nodes = 0;
        int i, j = 0;
        uint_t implementation;

        GPTWO_DEBUG2(1, CE_CONT, "gptwocfg_configure_cpu: portid=%x pcd=%lx\n",
            portid, pcd);

        for (i = 0; i < AGENTS_PER_PORT; i++) {
                cpu_node[i] = NULL;
                mc_node[i] = NULL;
        }

        implementation = (pcd->spcd_ver_reg >> 32) & 0x000000000000ffff;

        switch (implementation) {
        case CHEETAH_IMPL:
        case CHEETAH_PLUS_IMPL:
        case JAGUAR_IMPL:
        case PANTHER_IMPL:
                break;
        default:
                cmn_err(CE_WARN, "Unsupported cpu implementation=0x%x : "
                    "skipping configure of portid=0x%x", implementation,
                    portid);
                ASSERT(0);
                return (NULL);
        }

        if (CPU_IMPL_IS_CMP(implementation)) {
                if (cmp_node = gptwocfg_create_cmp_node(ap, pcd, portid))
                        nodes++;
                else
                        return (NULL);
        }

        for (i = 0; i < AGENTS_PER_PORT; i++) {
                if (pcd->spcd_agent[i] != SPCD_RSV_PASS)
                        continue;

                if (cpu_node[i] = gptwocfg_create_cpu_node(cmp_node ?
                    cmp_node : ap, pcd, portid, pcd->spcd_cpuid[i], i,
                    implementation)) {
                        /*
                         * If the CPU is a CMP, the entire branch is
                         * manipulated using just the top node. Thus,
                         * the dips of the individual cores do not need
                         * to be held or stored in the new node list.
                         */
                        if (cmp_node) {
                                e_ddi_branch_rele(cpu_node[i]);
                        } else {
                                nodes++;
                        }
                }
        }

        /* current implementations have 1 MC node per Safari port */
        if (pcd->spcd_prsv == SPCD_RSV_PASS &&
            (mc_node[0] = gptwocfg_create_mc_node(ap, pcd, portid)))
                nodes++;

        new_nodes = gptwocfg_allocate_node_list(nodes);

        j = 0;
        for (i = 0; i < AGENTS_PER_PORT; i++) {
                if ((cpu_node[i] != NULL) && (!CPU_IMPL_IS_CMP(implementation)))
                        new_nodes->gptwo_nodes[j++] = cpu_node[i];
                if (mc_node[i] != NULL)
                        new_nodes->gptwo_nodes[j++] = mc_node[i];
        }

        if (cmp_node)
                new_nodes->gptwo_nodes[j++] = cmp_node;

        return (new_nodes);
}


static dev_info_t *
gptwocfg_create_cmp_node(dev_info_t *ap, spcd_t *pcd, uint_t portid)
{
        struct bca arg;
        devi_branch_t b;

        arg.pcd = pcd;
        arg.portid = portid;
        arg.cpuid = 0;
        arg.coreid = 0;
        arg.new_child = NULL;

        b.arg = &arg;
        b.type = DEVI_BRANCH_SID;
        b.create.sid_branch_create = set_cmp_props;
        b.devi_branch_callback = get_new_child;

        if (e_ddi_branch_create(ap, &b, NULL, 0))
                return (NULL);

        return (arg.new_child);
}

/*ARGSUSED*/
static int
set_cmp_props(dev_info_t *new_child, void *arg, uint_t flags)
{
        struct bca *bap = (struct bca *)arg;
        gptwo_regspec_t reg;
        spcd_t *pcd;
        uint_t portid;

        pcd = bap->pcd;
        portid = bap->portid;

        GPTWO_DEBUG2(1, CE_CONT, "set_cmp_props: portid=%x pcd=%lx\n",
            portid, pcd);

        if (ndi_prop_update_string(DDI_DEV_T_NONE, new_child,
            "name", "cmp") != DDI_SUCCESS) {
                GPTWO_DEBUG0(1, CE_CONT, "set_cmp_props: failed to "
                    "create name property\n");
                return (DDI_WALK_ERROR);
        }

        if (ndi_prop_update_int(DDI_DEV_T_NONE, new_child,
            "portid", portid) != DDI_SUCCESS) {
                GPTWO_DEBUG0(1, CE_CONT, "set_cmp_props: failed to "
                    "create portid property\n");
                return (DDI_WALK_ERROR);
        }

        reg.gptwo_phys_hi = 0x400 | (portid >> 9);
        reg.gptwo_phys_low = (portid << 23);
        reg.gptwo_size_hi = 0;
        reg.gptwo_size_low = 0x10000;

        if (ndi_prop_update_int_array(DDI_DEV_T_NONE,
            new_child, "reg", (int *)&reg,
            sizeof (gptwo_regspec_t) / sizeof (int)) != DDI_SUCCESS) {
                GPTWO_DEBUG0(1, CE_CONT, "set_cmp_props: failed to "
                    "create reg property\n");
                return (DDI_WALK_ERROR);
        }

        return (DDI_WALK_TERMINATE);
}

static dev_info_t *
gptwocfg_create_cpu_node(dev_info_t *ap, spcd_t *pcd, uint_t portid,
    uint_t cpuid, uint_t coreid, uint_t impl)
{
        struct bca arg;
        devi_branch_t b = {0};

        arg.pcd = pcd;
        arg.portid = portid;
        arg.cpuid = cpuid;
        arg.coreid = coreid;
        arg.impl = impl;
        arg.new_child = NULL;

        b.arg = &arg;
        b.type = DEVI_BRANCH_SID;
        b.create.sid_branch_create = set_cpu_props;
        b.devi_branch_callback = get_new_child;

        if (e_ddi_branch_create(ap, &b, NULL, 0))
                return (NULL);

        return (arg.new_child);
}

/*ARGSUSED*/
static int
set_cpu_props(dev_info_t *new_child, void *arg, uint_t flags)
{
        struct bca *bcp = arg;
        uint_t impl = bcp->impl;
        int rc;

        if (set_cpu_common_props(new_child, bcp) != DDI_WALK_CONTINUE)
                return (DDI_WALK_ERROR);

        switch (impl) {
        case CHEETAH_IMPL:
        case CHEETAH_PLUS_IMPL:
                rc = set_cpu_us3_props(new_child, bcp);
                break;
        case JAGUAR_IMPL:
        case PANTHER_IMPL:
                rc = set_cpu_us4_props(new_child, bcp);
                break;
        default:
                ASSERT(0);
                return (DDI_WALK_ERROR);
        }

        return (rc);
}

/*
 * Set properties common to cpu (non-CMP) and core (CMP) nodes.
 *
 *      cpuid
 *      device_type
 *      manufacturer#
 *      implementation#
 *      mask#
 *      sparc-version
 *      clock-frequency
 *      #dtlb-entries
 *      #itlb-entries
 */
static int
set_cpu_common_props(dev_info_t *new_child, struct bca *bcp)
{
        uint_t  cpuid, impl;
        spcd_t  *pcd;
        int     mask, manufacturer;

        cpuid = bcp->cpuid;
        pcd = bcp->pcd;
        impl = bcp->impl;

        mask = (pcd->spcd_ver_reg >> 24) & 0x00000000000000ff;
        manufacturer = (pcd->spcd_ver_reg >> 48) & 0x000000000000ffff;

        if (ndi_prop_update_int(DDI_DEV_T_NONE, new_child,
            "cpuid", cpuid) != DDI_SUCCESS) {
                GPTWO_DEBUG0(1, CE_CONT, "set_cpu_common_props: failed "
                    "to create cpuid property\n");
                return (DDI_WALK_ERROR);
        }

        if (ndi_prop_update_string(DDI_DEV_T_NONE, new_child,
            "device_type", "cpu") != DDI_SUCCESS) {
                GPTWO_DEBUG0(1, CE_CONT, "set_cpu_common_props: failed "
                    "to create device_type property\n");
                return (DDI_WALK_ERROR);
        }

        if (ndi_prop_update_int(DDI_DEV_T_NONE, new_child, "manufacturer#",
            manufacturer) != DDI_SUCCESS) {
                GPTWO_DEBUG0(1, CE_CONT, "set_cpu_common_props: failed "
                    "to create manufacturer# property\n");
                return (DDI_WALK_ERROR);
        }

        if (ndi_prop_update_int(DDI_DEV_T_NONE, new_child, "implementation#",
            impl) != DDI_SUCCESS) {
                GPTWO_DEBUG0(1, CE_CONT, "set_cpu_common_props: failed "
                    "to create implementation# property\n");
                return (DDI_WALK_ERROR);
        }

        if (ndi_prop_update_int(DDI_DEV_T_NONE, new_child, "mask#",
            mask) != DDI_SUCCESS) {
                GPTWO_DEBUG0(1, CE_CONT, "set_cpu_common_props: failed "
                    "to create mask# property\n");
                return (DDI_WALK_ERROR);
        }

        if (ndi_prop_update_int(DDI_DEV_T_NONE, new_child,
            "sparc-version", 9) != DDI_SUCCESS) {
                GPTWO_DEBUG0(1, CE_CONT, "set_cpu_common_props: failed "
                    "to create sparc-version property\n");
                return (DDI_WALK_ERROR);
        }

        if (ndi_prop_update_int(DDI_DEV_T_NONE, new_child,
            "clock-frequency", (pcd->spcd_afreq * 1000000)) != DDI_SUCCESS) {
                GPTWO_DEBUG0(1, CE_CONT, "set_cpu_common_props: failed "
                    "to create clock-frequency property\n");
                return (DDI_WALK_ERROR);
        }

        if (ndi_prop_update_int(DDI_DEV_T_NONE, new_child,
            "#dtlb-entries", 0x10) != DDI_SUCCESS) {
                GPTWO_DEBUG0(1, CE_CONT, "set_cpu_common_props: failed "
                    "to create #dtlb-entries property\n");
                return (DDI_WALK_ERROR);
        }

        if (ndi_prop_update_int(DDI_DEV_T_NONE, new_child,
            "#itlb-entries", 0x10) != DDI_SUCCESS) {
                GPTWO_DEBUG0(1, CE_CONT, "set_cpu_common_props: failed "
                    "to create #itlb-entries property\n");
                return (DDI_WALK_ERROR);
        }

        return (DDI_WALK_CONTINUE);
}

/*
 * Set cpu node properties for Cheetah and Cheetah+.
 *
 *      name
 *      portid
 *      reg
 *      icache-size
 *      icache-line-size
 *      icache-associativity
 *      dcache-size
 *      dcache-line-size
 *      dcache-associativity
 *      ecache-size
 *      ecache-line-size
 *      ecache-associativity
 */
static int
set_cpu_us3_props(dev_info_t *new_child, struct bca *bcp)
{
        char *node_name;
        gptwo_regspec_t reg;
        int ecache_size, ecache_line_size;
        int dimms, ecache_assoc;
        spcd_t *pcd;
        uint_t portid, impl;

        pcd = bcp->pcd;
        portid = bcp->portid;
        impl = bcp->impl;

        ASSERT(IS_CHEETAH(impl) || IS_CHEETAH_PLUS(impl));

        switch (impl) {
        case CHEETAH_IMPL:
                ecache_assoc = CH_ECACHE_NWAY;
                node_name = "SUNW,UltraSPARC-III";
                break;
        case CHEETAH_PLUS_IMPL:
                /*
                 * Hard coding the ecache-associativity to 2 for Cheetah+.
                 * We probably should add this to the PCD.
                 */
                ecache_assoc = CHP_ECACHE_NWAY;
                node_name = "SUNW,UltraSPARC-III+";
                break;
        default:
                GPTWO_DEBUG1(1, CE_CONT, "set_cpu_us3_props: invalid "
                    "implementation=0x%x\n", impl);
                return (DDI_WALK_ERROR);
        }

        if (ndi_prop_update_string(DDI_DEV_T_NONE, new_child,
            "name", node_name) != DDI_SUCCESS) {
                GPTWO_DEBUG0(1, CE_CONT, "set_cpu_us3_props: failed "
                    "to create name property\n");
                return (DDI_WALK_ERROR);
        }

        if (ndi_prop_update_int(DDI_DEV_T_NONE, new_child,
            "portid", portid) != DDI_SUCCESS) {
                GPTWO_DEBUG0(1, CE_CONT, "set_cpu_us3_props: failed "
                    "to create portid property\n");
                return (DDI_WALK_ERROR);
        }

        reg.gptwo_phys_hi = 0x400 | (portid >> 9);
        reg.gptwo_phys_low = (portid << 23);
        reg.gptwo_size_hi = 0;
        reg.gptwo_size_low = 0x10000;

        if (ndi_prop_update_int_array(DDI_DEV_T_NONE,
            new_child, "reg", (int *)&reg,
            sizeof (gptwo_regspec_t) / sizeof (int)) != DDI_SUCCESS) {
                GPTWO_DEBUG0(1, CE_CONT, "set_cpu_us3_props: failed "
                    "to create reg property\n");
                return (DDI_WALK_ERROR);
        }

        if (ndi_prop_update_int(DDI_DEV_T_NONE, new_child,
            "icache-size", CH_ICACHE_SIZE) != DDI_SUCCESS) {
                GPTWO_DEBUG0(1, CE_CONT, "set_cpu_us3_props: failed "
                    "to create icache-size property\n");
                return (DDI_WALK_ERROR);
        }

        if (ndi_prop_update_int(DDI_DEV_T_NONE, new_child,
            "icache-line-size", CH_ICACHE_LSIZE) != DDI_SUCCESS) {
                GPTWO_DEBUG0(1, CE_CONT, "set_cpu_us3_props: failed "
                    "to create icache-line-size property\n");
                return (DDI_WALK_ERROR);
        }

        if (ndi_prop_update_int(DDI_DEV_T_NONE, new_child,
            "icache-associativity", CH_ICACHE_NWAY) != DDI_SUCCESS) {
                GPTWO_DEBUG0(1, CE_CONT, "set_cpu_us3_props: failed "
                    "to create icache-associativity property\n");
                return (DDI_WALK_ERROR);
        }

        if (ndi_prop_update_int(DDI_DEV_T_NONE, new_child,
            "dcache-size", CH_DCACHE_SIZE) != DDI_SUCCESS) {
                GPTWO_DEBUG0(1, CE_CONT, "set_cpu_us3_props: failed "
                    "to create dcache-size property\n");
                return (DDI_WALK_ERROR);
        }

        if (ndi_prop_update_int(DDI_DEV_T_NONE, new_child,
            "dcache-line-size", CH_DCACHE_LSIZE) != DDI_SUCCESS) {
                GPTWO_DEBUG0(1, CE_CONT, "set_cpu_us3_props: failed "
                    "to create dcache-line-size property\n");
                return (DDI_WALK_ERROR);
        }

        if (ndi_prop_update_int(DDI_DEV_T_NONE, new_child,
            "dcache-associativity", CH_DCACHE_NWAY) != DDI_SUCCESS) {
                GPTWO_DEBUG0(1, CE_CONT, "set_cpu_us3_props: failed "
                    "to create dcache-associativity property\n");
                return (DDI_WALK_ERROR);
        }

        /*
         * Get the External Cache Size from the Common PCD.
         */
        ecache_size = pcd->spcd_cache * 0x100000;

        if (ndi_prop_update_int(DDI_DEV_T_NONE, new_child,
            "ecache-size", ecache_size) != DDI_SUCCESS) {
                GPTWO_DEBUG0(1, CE_CONT, "set_cpu_us3_props: failed "
                    "to create ecache-line-size property\n");
                return (DDI_WALK_ERROR);
        }

        switch (ecache_size) {
        case CH_ECACHE_1M_SIZE:
                ecache_line_size = 64;
                break;
        case CH_ECACHE_4M_SIZE:
                ecache_line_size = 256;
                break;
        case CH_ECACHE_8M_SIZE:
                ecache_line_size = 512;
                break;
        default:
                GPTWO_DEBUG1(1, CE_CONT, "set_cpu_us3_props: invalid "
                    "ecache-size 0x%x\b", ecache_size);
                return (DDI_WALK_ERROR);
        }

        if (ndi_prop_update_int(DDI_DEV_T_NONE, new_child,
            "ecache-line-size", ecache_line_size) != DDI_SUCCESS) {
                GPTWO_DEBUG0(1, CE_CONT, "set_cpu_us3_props: failed "
                    "to create ecache-line-size property\n");
                return (DDI_WALK_ERROR);
        }

        if (ndi_prop_update_int(DDI_DEV_T_NONE, new_child,
            "ecache-associativity", ecache_assoc) != DDI_SUCCESS) {
                GPTWO_DEBUG0(1, CE_CONT, "set_cpu_us3_props: failed "
                    "to create ecache-associativity property\n");
                return (DDI_WALK_ERROR);
        }

        /*
         * Create the ecache-dimm-label property.
         */
        dimms = 0;

        while ((pcd->sprd_ecache_dimm_label[dimms] != NULL) &&
            (dimms < MAX_DIMMS_PER_PORT))
                dimms++;

        if (dimms) {
                (void) ndi_prop_update_string_array(DDI_DEV_T_NONE, new_child,
                    "ecache-dimm-label", (char **)pcd->sprd_ecache_dimm_label,
                    dimms);
        }

        return (DDI_WALK_TERMINATE);
}

/*
 * Set cmp core node properties for Jaguar and Panther.
 *
 *      name
 *      compatible
 *      reg
 *      l1-icache-size
 *      l1-icache-line-size
 *      l1-icache-associativity
 *      l1-dcache-size
 *      l1-dcache-line-size
 *      l1-dcache-associativity
 *      l2-cache-size
 *      l2-cache-line-size
 *      l2-cache-associativity
 *      l2-cache-sharing
 *      l3-cache-size
 *      l3-cache-line-size
 *      l3-cache-associativity
 *      l3-cache-sharing
 */
static int
set_cpu_us4_props(dev_info_t *new_child, struct bca *bcp)
{
        uint_t l1_icache_size, l1_icache_line_size;
        uint_t l2_cache_size, l2_cache_line_size, l2_cache_assoc;
        uint_t l2_cache_share;
        uint_t pcd_cache_size;
        uint_t coreid, impl;
        spcd_t *pcd;
        char *compatible;
        int dimms;
        int i;

        pcd = bcp->pcd;
        coreid = bcp->coreid;
        impl = bcp->impl;

        ASSERT(IS_JAGUAR(impl) || IS_PANTHER(impl));

        /*
         * Get the External Cache Size from the Common PCD.
         */
        pcd_cache_size = pcd->spcd_cache * 0x100000;

        switch (impl) {
        case JAGUAR_IMPL:
                compatible = "SUNW,UltraSPARC-IV";
                l1_icache_size = CH_ICACHE_SIZE;
                l1_icache_line_size = CH_ICACHE_LSIZE;
                l2_cache_assoc = CHP_ECACHE_NWAY;

                /*
                 * Jaguar has no logical sharing of L2 cache, so the sharing
                 * bit-map will represent this core only.
                 */
                l2_cache_share = coreid ? 0x2 : 0x1;

                /*
                 * Jaguar has a split ecache, so the total ecache must be
                 * divided in half to get the ecache for the individual core.
                 */
                l2_cache_size = pcd_cache_size / 2;

                switch (l2_cache_size) {
                case JG_ECACHE_4M_SIZE:
                        l2_cache_line_size = 64;
                        break;
                case JG_ECACHE_8M_SIZE:
                        l2_cache_line_size = 128;
                        break;
                default:
                        GPTWO_DEBUG1(1, CE_CONT, "set_cpu_us4_props: "
                            "invalid l2_cache-size 0x%x\n", l2_cache_size);
                        return (DDI_WALK_ERROR);
                }
                break;
        case PANTHER_IMPL:
                ASSERT(pcd_cache_size == PN_L3_SIZE);
                compatible = "SUNW,UltraSPARC-IV+";
                l1_icache_size = PN_ICACHE_SIZE;
                l1_icache_line_size = PN_ICACHE_LSIZE;
                l2_cache_size = PN_L2_SIZE;
                l2_cache_line_size = PN_L2_LINESIZE;
                l2_cache_assoc = PN_ECACHE_NWAY;

                /*
                 * For Panther, the L2 and L3 caches are logically shared by
                 * all enabled cores, so the sharing bit-map will represent
                 * all enabled cores.  Panther split-mode is still considered
                 * shared.
                 *
                 * Check the PCD status to determine enabled cores.
                 */
                ASSERT(pcd->spcd_ptype == SAFPTYPE_CPU);
                l2_cache_share = 0;
                for (i = 0; i < AGENTS_PER_PORT; i++) {
                        if (pcd->spcd_agent[i] == SPCD_RSV_PASS) {
                                l2_cache_share |= (1 << i);
                        }
                }

                break;
        default:
                GPTWO_DEBUG1(1, CE_CONT, "set_cpu_us4_props: invalid "
                    "implementation=0x%x\n", impl);
                return (DDI_WALK_ERROR);
        }

        if (ndi_prop_update_string(DDI_DEV_T_NONE, new_child,
            "name", "cpu") != DDI_SUCCESS) {
                GPTWO_DEBUG0(1, CE_CONT, "set_cpu_us4_props: failed "
                    "to create name property\n");
                return (DDI_WALK_ERROR);
        }

        if (ndi_prop_update_string(DDI_DEV_T_NONE, new_child,
            "compatible", compatible) != DDI_SUCCESS) {
                GPTWO_DEBUG0(1, CE_CONT, "set_cpu_us4_props: failed "
                    "to create compatible property\n");
                return (DDI_WALK_ERROR);
        }

        if (ndi_prop_update_int(DDI_DEV_T_NONE, new_child,
            "reg", coreid) != DDI_SUCCESS) {
                GPTWO_DEBUG0(1, CE_CONT, "set_cpu_us4_props: failed "
                    "to create reg property\n");
                return (DDI_WALK_ERROR);
        }

        if (ndi_prop_update_int(DDI_DEV_T_NONE, new_child,
            "l1-icache-size", l1_icache_size) != DDI_SUCCESS) {
                GPTWO_DEBUG0(1, CE_CONT, "set_cpu_us4_props: failed "
                    "to create l1-icache-size property\n");
                return (DDI_WALK_ERROR);
        }

        if (ndi_prop_update_int(DDI_DEV_T_NONE, new_child,
            "l1-icache-line-size", l1_icache_line_size) != DDI_SUCCESS) {
                GPTWO_DEBUG0(1, CE_CONT, "set_cpu_us4_props: failed "
                    "to create icache-line-size property\n");
                return (DDI_WALK_ERROR);
        }

        if (ndi_prop_update_int(DDI_DEV_T_NONE, new_child,
            "l1-icache-associativity", CH_ICACHE_NWAY) != DDI_SUCCESS) {
                GPTWO_DEBUG0(1, CE_CONT, "set_cpu_us4_props: failed "
                    "to create l1-icache-associativity property\n");
                return (DDI_WALK_ERROR);
        }

        if (ndi_prop_update_int(DDI_DEV_T_NONE, new_child,
            "l1-dcache-size", CH_DCACHE_SIZE) != DDI_SUCCESS) {
                GPTWO_DEBUG0(1, CE_CONT, "set_cpu_us4_props: failed "
                    "to create l1-dcache-size property\n");
                return (DDI_WALK_ERROR);
        }

        if (ndi_prop_update_int(DDI_DEV_T_NONE, new_child,
            "l1-dcache-line-size", CH_DCACHE_LSIZE) != DDI_SUCCESS) {
                GPTWO_DEBUG0(1, CE_CONT, "set_cpu_us4_props: failed "
                    "to create dcache-line-size property\n");
                return (DDI_WALK_ERROR);
        }

        if (ndi_prop_update_int(DDI_DEV_T_NONE, new_child,
            "l1-dcache-associativity", CH_DCACHE_NWAY) != DDI_SUCCESS) {
                GPTWO_DEBUG0(1, CE_CONT, "set_cpu_us4_props: failed "
                    "to create l1-dcache-associativity property\n");
                return (DDI_WALK_ERROR);
        }

        if (ndi_prop_update_int(DDI_DEV_T_NONE, new_child,
            "l2-cache-size", l2_cache_size) != DDI_SUCCESS) {
                GPTWO_DEBUG0(1, CE_CONT, "set_cpu_us4_props: failed "
                    "to create l2-cache-size property\n");
                return (DDI_WALK_ERROR);
        }

        if (ndi_prop_update_int(DDI_DEV_T_NONE, new_child,
            "l2-cache-line-size", l2_cache_line_size) != DDI_SUCCESS) {
                GPTWO_DEBUG0(1, CE_CONT, "set_cpu_us4_props: failed "
                    "to create l2_cache-line-size property\n");
                return (DDI_WALK_ERROR);
        }

        if (ndi_prop_update_int(DDI_DEV_T_NONE, new_child,
            "l2-cache-associativity", l2_cache_assoc) != DDI_SUCCESS) {
                GPTWO_DEBUG0(1, CE_CONT, "set_cpu_us4_props: failed "
                    "to create l2-cache-associativity property\n");
                return (DDI_WALK_ERROR);
        }

        if (ndi_prop_update_int(DDI_DEV_T_NONE, new_child,
            "l2-cache-sharing", l2_cache_share) != DDI_SUCCESS) {
                GPTWO_DEBUG0(1, CE_CONT, "set_cpu_us4_props: failed "
                    "to create l2-cache-sharing property\n");
                return (DDI_WALK_ERROR);
        }

        /*
         * Create the ecache-dimm-label property.
         */
        dimms = 0;

        while ((pcd->sprd_ecache_dimm_label[dimms] != NULL) &&
            (dimms < MAX_DIMMS_PER_PORT))
                dimms++;

        if (dimms) {
                (void) ndi_prop_update_string_array(DDI_DEV_T_NONE, new_child,
                    "ecache-dimm-label", (char **)pcd->sprd_ecache_dimm_label,
                    dimms);
        }

        if (IS_PANTHER(impl)) {
                int l3_cache_share = l2_cache_share;

                if (ndi_prop_update_int(DDI_DEV_T_NONE, new_child,
                    "l3-cache-size", PN_L3_SIZE) != DDI_SUCCESS) {
                        GPTWO_DEBUG0(1, CE_CONT, "set_cpu_us4_props: "
                            "failed to create l3-cache-size property\n");
                        return (DDI_WALK_ERROR);
                }

                if (ndi_prop_update_int(DDI_DEV_T_NONE, new_child,
                    "l3-cache-line-size", PN_L3_LINESIZE) != DDI_SUCCESS) {
                        GPTWO_DEBUG0(1, CE_CONT, "set_cpu_us4_props: "
                            "failed to create l3-cache-line-size property\n");
                        return (DDI_WALK_ERROR);
                }

                if (ndi_prop_update_int(DDI_DEV_T_NONE, new_child,
                    "l3-cache-associativity", PN_ECACHE_NWAY) != DDI_SUCCESS) {
                        GPTWO_DEBUG0(1, CE_CONT, "set_cpu_us4_props: "
                            "failed to create l3-cache-associativity "
                            "property\n");
                        return (DDI_WALK_ERROR);
                }

                if (ndi_prop_update_int(DDI_DEV_T_NONE, new_child,
                    "l3-cache-sharing", l3_cache_share) != DDI_SUCCESS) {
                        GPTWO_DEBUG0(1, CE_CONT, "set_cpu_us4_props: "
                            "failed to create l3-cache-sharing property\n");
                        return (DDI_WALK_ERROR);
                }
        }

        return (DDI_WALK_TERMINATE);
}

static dev_info_t *
gptwocfg_create_mc_node(dev_info_t *ap, spcd_t *pcd, uint_t portid)
{
        struct bca arg;
        devi_branch_t b = {0};

        arg.pcd = pcd;
        arg.portid = portid;
        arg.cpuid = portid;
        arg.new_child = NULL;

        b.arg = &arg;
        b.type = DEVI_BRANCH_SID;
        b.create.sid_branch_create = set_mc_props;
        b.devi_branch_callback = get_new_child;

        if (e_ddi_branch_create(ap, &b, NULL, 0))
                return (NULL);

        return (arg.new_child);
}

/*ARGSUSED*/
static int
set_mc_props(dev_info_t *new_child, void *arg, uint_t flags)
{
        struct bca *bcp = arg;
        gptwo_regspec_t reg;
        int banks, dimms;
        spcd_t *pcd = bcp->pcd;
        uint_t portid = bcp->portid;
        uint_t cpuid = bcp->cpuid;

        GPTWO_DEBUG3(1, CE_CONT, "set_mc_props: ap=0x%lx portid=0x%x "
            "cpuid=0x%x\n", ddi_get_parent(new_child), portid, cpuid);

        if (ndi_prop_update_string(DDI_DEV_T_NONE, new_child,
            "name", "memory-controller") != DDI_SUCCESS) {
                GPTWO_DEBUG0(1, CE_CONT, "set_mc_props: failed "
                    "to create name property\n");
                return (DDI_WALK_ERROR);
        }

        if (ndi_prop_update_string(DDI_DEV_T_NONE, new_child,
            "compatible", "SUNW,UltraSPARC-III,mc") != DDI_SUCCESS) {
                GPTWO_DEBUG0(1, CE_CONT, "set_mc_props: failed "
                    "to create compatible property\n");
                return (DDI_WALK_ERROR);
        }

        if (ndi_prop_update_string(DDI_DEV_T_NONE, new_child,
            "device_type", "memory-controller") != DDI_SUCCESS) {
                GPTWO_DEBUG0(1, CE_CONT, "set_mc_props: failed "
                    "to create device_type property\n");
                return (DDI_WALK_ERROR);
        }

        if (ndi_prop_update_int(DDI_DEV_T_NONE, new_child,
            "portid", portid) != DDI_SUCCESS) {
                GPTWO_DEBUG0(1, CE_CONT, "set_mc_props: failed "
                    "to create portid property\n");
                return (DDI_WALK_ERROR);
        }

        if (ndi_prop_update_int(DDI_DEV_T_NONE, new_child,
            "cpuid", cpuid) != DDI_SUCCESS) {
                GPTWO_DEBUG0(1, CE_CONT, "set_mc_props: failed "
                    "to create cpuid property\n");
                return (DDI_WALK_ERROR);
        }

        reg.gptwo_phys_hi = 0x400 | (portid >> 9);
        reg.gptwo_phys_low = (portid << 23) | 0x400000;
        reg.gptwo_size_hi = 0;
        reg.gptwo_size_low = 0x48;

        if (ndi_prop_update_int_array(DDI_DEV_T_NONE,
            new_child, "reg", (int *)&reg,
            sizeof (gptwo_regspec_t) / sizeof (int)) != DDI_SUCCESS) {
                GPTWO_DEBUG0(1, CE_CONT, "set_mc_props: failed "
                    "to create reg property\n");
                return (DDI_WALK_ERROR);
        }

        if (pcd->memory_layout) {
                if (ndi_prop_update_byte_array(DDI_DEV_T_NONE,
                    new_child, "memory-layout", (uchar_t *)pcd->memory_layout,
                    pcd->memory_layout_size) != DDI_SUCCESS) {

                        GPTWO_DEBUG0(1, CE_CONT, "set_mc_props: failed "
                            "to create memory-layout property\n");

                        return (DDI_WALK_ERROR);
                }
        }

        /*
         * Create the bank-status property.
         */
        banks = 0;

        while ((pcd->sprd_bank_rsv[banks] != NULL) &&
            (banks < MAX_BANKS_PER_PORT))
                banks++;

        if (banks) {
                (void) ndi_prop_update_string_array(DDI_DEV_T_NONE, new_child,
                    "bank-status", (char **)pcd->sprd_bank_rsv, banks);
        }

        /*
         * Create the dimm-status property.
         */
        dimms = 0;

        while ((pcd->sprd_dimm[dimms] != NULL) &&
            (dimms < MAX_DIMMS_PER_PORT))
                dimms++;

        if (dimms) {
                (void) ndi_prop_update_string_array(DDI_DEV_T_NONE, new_child,
                    "dimm-status", (char **)pcd->sprd_dimm, dimms);
        }


        return (DDI_WALK_TERMINATE);
}

/*ARGSUSED*/
static void
get_new_child(dev_info_t *rdip, void *arg, uint_t flags)
{
        struct bca *bcp = arg;

        bcp->new_child = rdip;

}

#ifdef DEBUG
static void
debug(char *fmt, uintptr_t a1, uintptr_t a2, uintptr_t a3,
        uintptr_t a4, uintptr_t a5)
{
        cmn_err(CE_CONT, fmt, a1, a2, a3, a4, a5);
}
#endif