/*
 * 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 2004 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

#pragma ident	"%Z%%M%	%I%	%E% SMI"

/*
 * 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 %I%",
};

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