xref: /illumos-gate/usr/src/uts/sun4u/lw8/os/lw8_platmod.c (revision 07d06da5)
103831d35Sstevel /*
203831d35Sstevel  * CDDL HEADER START
303831d35Sstevel  *
403831d35Sstevel  * The contents of this file are subject to the terms of the
503831d35Sstevel  * Common Development and Distribution License (the "License").
603831d35Sstevel  * You may not use this file except in compliance with the License.
703831d35Sstevel  *
803831d35Sstevel  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
903831d35Sstevel  * or http://www.opensolaris.org/os/licensing.
1003831d35Sstevel  * See the License for the specific language governing permissions
1103831d35Sstevel  * and limitations under the License.
1203831d35Sstevel  *
1303831d35Sstevel  * When distributing Covered Code, include this CDDL HEADER in each
1403831d35Sstevel  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1503831d35Sstevel  * If applicable, add the following below this CDDL HEADER, with the
1603831d35Sstevel  * fields enclosed by brackets "[]" replaced with your own identifying
1703831d35Sstevel  * information: Portions Copyright [yyyy] [name of copyright owner]
1803831d35Sstevel  *
1903831d35Sstevel  * CDDL HEADER END
2003831d35Sstevel  */
2103831d35Sstevel 
2203831d35Sstevel /*
23d3d50737SRafael Vanoni  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
2403831d35Sstevel  * Use is subject to license terms.
2503831d35Sstevel  */
2603831d35Sstevel 
2703831d35Sstevel #include <sys/time.h>
2803831d35Sstevel #include <sys/cpuvar.h>
2903831d35Sstevel #include <sys/dditypes.h>
3003831d35Sstevel #include <sys/ddipropdefs.h>
3103831d35Sstevel #include <sys/ddi_impldefs.h>
3203831d35Sstevel #include <sys/sunddi.h>
3303831d35Sstevel #include <sys/esunddi.h>
3403831d35Sstevel #include <sys/sunndi.h>
3503831d35Sstevel #include <sys/platform_module.h>
3603831d35Sstevel #include <sys/errno.h>
3703831d35Sstevel #include <sys/conf.h>
3803831d35Sstevel #include <sys/modctl.h>
3903831d35Sstevel #include <sys/promif.h>
4003831d35Sstevel #include <sys/promimpl.h>
4103831d35Sstevel #include <sys/prom_plat.h>
4203831d35Sstevel #include <sys/cmn_err.h>
4303831d35Sstevel #include <sys/sysmacros.h>
4403831d35Sstevel #include <sys/mem_cage.h>
4503831d35Sstevel #include <sys/kobj.h>
4603831d35Sstevel #include <sys/utsname.h>
4703831d35Sstevel #include <sys/cpu_sgnblk_defs.h>
4803831d35Sstevel #include <sys/atomic.h>
4903831d35Sstevel #include <sys/kdi_impl.h>
5003831d35Sstevel 
5103831d35Sstevel #include <sys/sgsbbc.h>
5203831d35Sstevel #include <sys/sgsbbc_iosram.h>
5303831d35Sstevel #include <sys/sgsbbc_iosram_priv.h>
5403831d35Sstevel #include <sys/sgsbbc_mailbox.h>
5503831d35Sstevel #include <sys/sgsgn.h>
5603831d35Sstevel #include <sys/serengeti.h>
5703831d35Sstevel #include <sys/sgfrutypes.h>
5803831d35Sstevel #include <sys/machsystm.h>
5903831d35Sstevel #include <sys/sbd_ioctl.h>
6003831d35Sstevel #include <sys/sbd.h>
6103831d35Sstevel #include <sys/sbdp_mem.h>
6203831d35Sstevel #include <sys/sgcn.h>
6303831d35Sstevel 
6403831d35Sstevel #include <sys/memnode.h>
6503831d35Sstevel #include <vm/vm_dep.h>
6603831d35Sstevel #include <vm/page.h>
6703831d35Sstevel 
6803831d35Sstevel #include <sys/cheetahregs.h>
6903831d35Sstevel #include <sys/plat_ecc_unum.h>
7003831d35Sstevel #include <sys/plat_ecc_dimm.h>
7103831d35Sstevel 
7203831d35Sstevel #include <sys/lgrp.h>
73d3d50737SRafael Vanoni #include <sys/clock_impl.h>
7403831d35Sstevel 
7503831d35Sstevel static int sg_debug = 0;
7603831d35Sstevel 
7703831d35Sstevel #ifdef DEBUG
7803831d35Sstevel #define	DCMNERR if (sg_debug) cmn_err
7903831d35Sstevel #else
8003831d35Sstevel #define	DCMNERR
8103831d35Sstevel #endif
8203831d35Sstevel 
8303831d35Sstevel int (*p2get_mem_unum)(int, uint64_t, char *, int, int *);
8403831d35Sstevel 
8503831d35Sstevel /* local functions */
8603831d35Sstevel static void cpu_sgn_update(ushort_t sgn, uchar_t state,
8703831d35Sstevel     uchar_t sub_state, int cpuid);
8803831d35Sstevel 
8903831d35Sstevel 
9003831d35Sstevel /*
9103831d35Sstevel  * Local data.
9203831d35Sstevel  *
9303831d35Sstevel  * iosram_write_ptr is a pointer to iosram_write().  Because of
9403831d35Sstevel  * kernel dynamic linking, we can't get to the function by name,
9503831d35Sstevel  * but we can look up its address, and store it in this variable
9603831d35Sstevel  * instead.
9703831d35Sstevel  *
9803831d35Sstevel  * We include the extern for iosram_write() here not because we call
9903831d35Sstevel  * it, but to force compilation errors if its prototype doesn't
10003831d35Sstevel  * match the prototype of iosram_write_ptr.
10103831d35Sstevel  *
10203831d35Sstevel  * The same issues apply to iosram_read() and iosram_read_ptr.
10303831d35Sstevel  */
10403831d35Sstevel /*CSTYLED*/
10503831d35Sstevel extern int   iosram_write     (int, uint32_t, caddr_t, uint32_t);
10603831d35Sstevel static int (*iosram_write_ptr)(int, uint32_t, caddr_t, uint32_t) = NULL;
10703831d35Sstevel /*CSTYLED*/
10803831d35Sstevel extern int   iosram_read     (int, uint32_t, caddr_t, uint32_t);
10903831d35Sstevel static int (*iosram_read_ptr)(int, uint32_t, caddr_t, uint32_t) = NULL;
11003831d35Sstevel 
11103831d35Sstevel 
11203831d35Sstevel /*
11303831d35Sstevel  * Variable to indicate if the date should be obtained from the SC or not.
11403831d35Sstevel  */
11503831d35Sstevel int todsg_use_sc = FALSE;	/* set the false at the beginning */
11603831d35Sstevel 
11703831d35Sstevel /*
11803831d35Sstevel  * Preallocation of spare tsb's for DR
11903831d35Sstevel  *
12003831d35Sstevel  * We don't allocate spares for Wildcat since TSBs should come
12103831d35Sstevel  * out of memory local to the node.
12203831d35Sstevel  */
12303831d35Sstevel #define	IOMMU_PER_SCHIZO	2
12403831d35Sstevel int serengeti_tsb_spares = (SG_MAX_IO_BDS * SG_SCHIZO_PER_IO_BD *
12503831d35Sstevel 	IOMMU_PER_SCHIZO);
12603831d35Sstevel 
12703831d35Sstevel /*
1281e2e7a75Shuah  * sg_max_ncpus is the maximum number of CPUs supported on lw8.
1291e2e7a75Shuah  * sg_max_ncpus is set to be smaller than NCPU to reduce the amount of
1301e2e7a75Shuah  * memory the logs take up until we have a dynamic log memory allocation
1311e2e7a75Shuah  * solution.
13203831d35Sstevel  */
1331e2e7a75Shuah int sg_max_ncpus = (12 * 2);    /* (max # of processors * # of cores/proc) */
13403831d35Sstevel 
13503831d35Sstevel /*
13603831d35Sstevel  * variables to control mailbox message timeouts.
13703831d35Sstevel  * These can be patched via /etc/system or mdb.
13803831d35Sstevel  */
13903831d35Sstevel int	sbbc_mbox_default_timeout = MBOX_DEFAULT_TIMEOUT;
14003831d35Sstevel int	sbbc_mbox_min_timeout = MBOX_MIN_TIMEOUT;
14103831d35Sstevel 
14203831d35Sstevel /* cached 'chosen' node_id */
14303831d35Sstevel pnode_t chosen_nodeid = (pnode_t)0;
14403831d35Sstevel 
14503831d35Sstevel /*
14603831d35Sstevel  * Table that maps memory slices to a specific memnode.
14703831d35Sstevel  */
14803831d35Sstevel int slice_to_memnode[SG_MAX_SLICE];
14903831d35Sstevel 
15003831d35Sstevel /*
15103831d35Sstevel  * We define and use LW8_MAX_CPU_BDS here instead of SG_MAX_CPU_BDS
15203831d35Sstevel  * since a LW8 machine will never have a CPU/Mem board #5 (SB5).
15303831d35Sstevel  * A LW8 machine can only have a maximum of three CPU/Mem boards, but
15403831d35Sstevel  * the board numbers assigned are 0, 2, and 4.  LW8_MAX_CPU_BDS is
15503831d35Sstevel  * defined to be 5 since the entries in the domain_dimm_sids array
15603831d35Sstevel  * are keyed by board number.  Not perfect but some wasted space
15703831d35Sstevel  * is avoided.
15803831d35Sstevel  */
15903831d35Sstevel #define	LW8_MAX_CPU_BDS		5
16003831d35Sstevel 
16103831d35Sstevel plat_dimm_sid_board_t	domain_dimm_sids[LW8_MAX_CPU_BDS];
16203831d35Sstevel 
16303831d35Sstevel int
set_platform_tsb_spares()16403831d35Sstevel set_platform_tsb_spares()
16503831d35Sstevel {
16603831d35Sstevel 	return (MIN(serengeti_tsb_spares, MAX_UPA));
16703831d35Sstevel }
16803831d35Sstevel 
16903831d35Sstevel #pragma weak mmu_init_large_pages
17003831d35Sstevel 
17103831d35Sstevel void
set_platform_defaults(void)17203831d35Sstevel set_platform_defaults(void)
17303831d35Sstevel {
17403831d35Sstevel 	extern int watchdog_enable;
17503831d35Sstevel 	extern uint64_t xc_tick_limit_scale;
17603831d35Sstevel 	extern void mmu_init_large_pages(size_t);
17703831d35Sstevel 
17803831d35Sstevel #ifdef DEBUG
17903831d35Sstevel 	char *todsg_name = "todsg";
18003831d35Sstevel 	ce_verbose_memory = 2;
18103831d35Sstevel 	ce_verbose_other = 2;
18203831d35Sstevel #endif /* DEBUG */
18303831d35Sstevel 
18403831d35Sstevel 	watchdog_enable = TRUE;
18503831d35Sstevel 	watchdog_available = TRUE;
18603831d35Sstevel 
18703831d35Sstevel 	cpu_sgn_func = cpu_sgn_update;
18803831d35Sstevel 
18903831d35Sstevel #ifdef DEBUG
19003831d35Sstevel 	/* tod_module_name should be set to "todsg" from OBP property */
19103831d35Sstevel 	if (tod_module_name && (strcmp(tod_module_name, todsg_name) == 0))
19203831d35Sstevel 		prom_printf("Using todsg driver\n");
19303831d35Sstevel 	else {
19403831d35Sstevel 		prom_printf("Force using todsg driver\n");
19503831d35Sstevel 		tod_module_name = todsg_name;
19603831d35Sstevel 	}
19703831d35Sstevel #endif /* DEBUG */
19803831d35Sstevel 
19903831d35Sstevel 	/* lw8 does not support forthdebug */
20003831d35Sstevel 	forthdebug_supported = 0;
20103831d35Sstevel 
20203831d35Sstevel 
20303831d35Sstevel 	/*
20403831d35Sstevel 	 * Some DR operations require the system to be sync paused.
20503831d35Sstevel 	 * Sync pause on Serengeti could potentially take up to 4
20603831d35Sstevel 	 * seconds to complete depending on the load on the SC.  To
20703831d35Sstevel 	 * avoid send_mond panics during such operations, we need to
20803831d35Sstevel 	 * increase xc_tick_limit to a larger value on Serengeti by
20903831d35Sstevel 	 * setting xc_tick_limit_scale to 5.
21003831d35Sstevel 	 */
21103831d35Sstevel 	xc_tick_limit_scale = 5;
21203831d35Sstevel 
21303831d35Sstevel 	if ((mmu_page_sizes == max_mmu_page_sizes) &&
214e12a8a13Ssusans 	    (mmu_ism_pagesize != DEFAULT_ISM_PAGESIZE)) {
21503831d35Sstevel 		if (&mmu_init_large_pages)
21603831d35Sstevel 			mmu_init_large_pages(mmu_ism_pagesize);
21703831d35Sstevel 	}
21803831d35Sstevel }
21903831d35Sstevel 
22003831d35Sstevel void
load_platform_modules(void)22103831d35Sstevel load_platform_modules(void)
22203831d35Sstevel {
22303831d35Sstevel 	if (modload("misc", "pcihp") < 0) {
22403831d35Sstevel 		cmn_err(CE_NOTE, "pcihp driver failed to load");
22503831d35Sstevel 	}
22603831d35Sstevel }
22703831d35Sstevel 
22803831d35Sstevel /*ARGSUSED*/
22903831d35Sstevel int
plat_cpu_poweron(struct cpu * cp)23003831d35Sstevel plat_cpu_poweron(struct cpu *cp)
23103831d35Sstevel {
23203831d35Sstevel 	int (*serengeti_cpu_poweron)(struct cpu *) = NULL;
23303831d35Sstevel 
23403831d35Sstevel 	serengeti_cpu_poweron =
23503831d35Sstevel 	    (int (*)(struct cpu *))modgetsymvalue("sbdp_cpu_poweron", 0);
23603831d35Sstevel 
23703831d35Sstevel 	if (serengeti_cpu_poweron == NULL)
23803831d35Sstevel 		return (ENOTSUP);
23903831d35Sstevel 	else
24003831d35Sstevel 		return ((serengeti_cpu_poweron)(cp));
24103831d35Sstevel }
24203831d35Sstevel 
24303831d35Sstevel /*ARGSUSED*/
24403831d35Sstevel int
plat_cpu_poweroff(struct cpu * cp)24503831d35Sstevel plat_cpu_poweroff(struct cpu *cp)
24603831d35Sstevel {
24703831d35Sstevel 	int (*serengeti_cpu_poweroff)(struct cpu *) = NULL;
24803831d35Sstevel 
24903831d35Sstevel 	serengeti_cpu_poweroff =
25003831d35Sstevel 	    (int (*)(struct cpu *))modgetsymvalue("sbdp_cpu_poweroff", 0);
25103831d35Sstevel 
25203831d35Sstevel 	if (serengeti_cpu_poweroff == NULL)
25303831d35Sstevel 		return (ENOTSUP);
25403831d35Sstevel 	else
25503831d35Sstevel 		return ((serengeti_cpu_poweroff)(cp));
25603831d35Sstevel }
25703831d35Sstevel 
25803831d35Sstevel #ifdef DEBUG
25903831d35Sstevel pgcnt_t serengeti_cage_size_limit;
26003831d35Sstevel #endif
26103831d35Sstevel 
26203831d35Sstevel /* Preferred minimum cage size (expressed in pages)... for DR */
26303831d35Sstevel pgcnt_t serengeti_minimum_cage_size = 0;
26403831d35Sstevel 
26503831d35Sstevel void
set_platform_cage_params(void)26603831d35Sstevel set_platform_cage_params(void)
26703831d35Sstevel {
26803831d35Sstevel 	extern pgcnt_t total_pages;
26903831d35Sstevel 	extern struct memlist *phys_avail;
27003831d35Sstevel 
27103831d35Sstevel 	if (kernel_cage_enable) {
27203831d35Sstevel 		pgcnt_t preferred_cage_size;
27303831d35Sstevel 
27403831d35Sstevel 		preferred_cage_size =
27503831d35Sstevel 		    MAX(serengeti_minimum_cage_size, total_pages / 256);
27603831d35Sstevel #ifdef DEBUG
27703831d35Sstevel 		if (serengeti_cage_size_limit)
27803831d35Sstevel 			preferred_cage_size = serengeti_cage_size_limit;
27903831d35Sstevel #endif
28003831d35Sstevel 		/*
28103831d35Sstevel 		 * Post copies obp into the lowest slice.  This requires the
28203831d35Sstevel 		 * cage to grow upwards
28303831d35Sstevel 		 */
28485f58038Sdp 		kcage_range_init(phys_avail, KCAGE_UP, preferred_cage_size);
28503831d35Sstevel 	}
28603831d35Sstevel 
2875832075cSsetje 	kcage_startup_dir = KCAGE_UP;
2885832075cSsetje 
28903831d35Sstevel 	/* Only note when the cage is off since it should always be on. */
29003831d35Sstevel 	if (!kcage_on)
29103831d35Sstevel 		cmn_err(CE_NOTE, "!DR Kernel Cage is DISABLED");
29203831d35Sstevel }
29303831d35Sstevel 
29403831d35Sstevel #define	ALIGN(x, a)	((a) == 0 ? (uint64_t)(x) : \
29503831d35Sstevel 	(((uint64_t)(x) + (uint64_t)(a) - 1l) & ~((uint64_t)(a) - 1l)))
29603831d35Sstevel 
29703831d35Sstevel void
update_mem_bounds(int brd,uint64_t base,uint64_t sz)29803831d35Sstevel update_mem_bounds(int brd, uint64_t base, uint64_t sz)
29903831d35Sstevel {
30003831d35Sstevel 	uint64_t	end;
30103831d35Sstevel 	int		mnode;
30203831d35Sstevel 
30303831d35Sstevel 	end = base + sz - 1;
30403831d35Sstevel 
30503831d35Sstevel 	/*
30603831d35Sstevel 	 * First see if this board already has a memnode associated
30703831d35Sstevel 	 * with it.  If not, see if this slice has a memnode.  This
30803831d35Sstevel 	 * covers the cases where a single slice covers multiple
30903831d35Sstevel 	 * boards (cross-board interleaving) and where a single
31003831d35Sstevel 	 * board has multiple slices (1+GB DIMMs).
31103831d35Sstevel 	 */
31203831d35Sstevel 	if ((mnode = plat_lgrphand_to_mem_node(brd)) == -1) {
31303831d35Sstevel 		if ((mnode = slice_to_memnode[PA_2_SLICE(base)]) == -1)
31403831d35Sstevel 			mnode = mem_node_alloc();
31503831d35Sstevel 		plat_assign_lgrphand_to_mem_node(brd, mnode);
31603831d35Sstevel 	}
31703831d35Sstevel 
31803831d35Sstevel 	/*
31903831d35Sstevel 	 * Align base at 16GB boundary
32003831d35Sstevel 	 */
32103831d35Sstevel 	base = ALIGN(base, (1ul << PA_SLICE_SHIFT));
32203831d35Sstevel 
32303831d35Sstevel 	while (base < end) {
32403831d35Sstevel 		slice_to_memnode[PA_2_SLICE(base)] = mnode;
32503831d35Sstevel 		base += (1ul << PA_SLICE_SHIFT);
32603831d35Sstevel 	}
32703831d35Sstevel }
32803831d35Sstevel 
32903831d35Sstevel /*
33003831d35Sstevel  * Dynamically detect memory slices in the system by decoding
33103831d35Sstevel  * the cpu memory decoder registers at boot time.
33203831d35Sstevel  */
33303831d35Sstevel void
plat_fill_mc(pnode_t nodeid)33403831d35Sstevel plat_fill_mc(pnode_t nodeid)
33503831d35Sstevel {
33603831d35Sstevel 	uint64_t	mc_addr, mask;
33703831d35Sstevel 	uint64_t	mc_decode[SG_MAX_BANKS_PER_MC];
33803831d35Sstevel 	uint64_t	base, size;
33903831d35Sstevel 	uint32_t	regs[4];
34003831d35Sstevel 	int		len;
34103831d35Sstevel 	int		local_mc;
34203831d35Sstevel 	int		portid;
34303831d35Sstevel 	int		boardid;
34403831d35Sstevel 	int		i;
34503831d35Sstevel 
34603831d35Sstevel 	if ((prom_getprop(nodeid, "portid", (caddr_t)&portid) < 0) ||
34703831d35Sstevel 	    (portid == -1))
34803831d35Sstevel 		return;
34903831d35Sstevel 
35003831d35Sstevel 	/*
35103831d35Sstevel 	 * Decode the board number from the MC portid
35203831d35Sstevel 	 */
35303831d35Sstevel 	boardid = SG_PORTID_TO_BOARD_NUM(portid);
35403831d35Sstevel 
35503831d35Sstevel 	/*
35603831d35Sstevel 	 * The "reg" property returns 4 32-bit values. The first two are
35703831d35Sstevel 	 * combined to form a 64-bit address.  The second two are for a
35803831d35Sstevel 	 * 64-bit size, but we don't actually need to look at that value.
35903831d35Sstevel 	 */
36003831d35Sstevel 	len = prom_getproplen(nodeid, "reg");
36103831d35Sstevel 	if (len != (sizeof (uint32_t) * 4)) {
36203831d35Sstevel 		prom_printf("Warning: malformed 'reg' property\n");
36303831d35Sstevel 		return;
36403831d35Sstevel 	}
36503831d35Sstevel 	if (prom_getprop(nodeid, "reg", (caddr_t)regs) < 0)
36603831d35Sstevel 		return;
36703831d35Sstevel 	mc_addr = ((uint64_t)regs[0]) << 32;
36803831d35Sstevel 	mc_addr |= (uint64_t)regs[1];
36903831d35Sstevel 
37003831d35Sstevel 	/*
37103831d35Sstevel 	 * Figure out whether the memory controller we are examining
37203831d35Sstevel 	 * belongs to this CPU or a different one.
37303831d35Sstevel 	 */
37403831d35Sstevel 	if (portid == cpunodes[CPU->cpu_id].portid)
37503831d35Sstevel 		local_mc = 1;
37603831d35Sstevel 	else
37703831d35Sstevel 		local_mc = 0;
37803831d35Sstevel 
37903831d35Sstevel 	for (i = 0; i < SG_MAX_BANKS_PER_MC; i++) {
38003831d35Sstevel 		mask = SG_REG_2_OFFSET(i);
38103831d35Sstevel 
38203831d35Sstevel 		/*
38303831d35Sstevel 		 * If the memory controller is local to this CPU, we use
38403831d35Sstevel 		 * the special ASI to read the decode registers.
38503831d35Sstevel 		 * Otherwise, we load the values from a magic address in
38603831d35Sstevel 		 * I/O space.
38703831d35Sstevel 		 */
38803831d35Sstevel 		if (local_mc)
38903831d35Sstevel 			mc_decode[i] = lddmcdecode(mask & MC_OFFSET_MASK);
39003831d35Sstevel 		else
39103831d35Sstevel 			mc_decode[i] = lddphysio((mc_addr | mask));
39203831d35Sstevel 
39303831d35Sstevel 		if (mc_decode[i] >> MC_VALID_SHIFT) {
39403831d35Sstevel 			/*
39503831d35Sstevel 			 * The memory decode register is a bitmask field,
39603831d35Sstevel 			 * so we can decode that into both a base and
39703831d35Sstevel 			 * a span.
39803831d35Sstevel 			 */
39903831d35Sstevel 			base = MC_BASE(mc_decode[i]) << PHYS2UM_SHIFT;
40003831d35Sstevel 			size = MC_UK2SPAN(mc_decode[i]);
40103831d35Sstevel 			update_mem_bounds(boardid, base, size);
40203831d35Sstevel 		}
40303831d35Sstevel 	}
40403831d35Sstevel }
40503831d35Sstevel 
40603831d35Sstevel /*
40703831d35Sstevel  * This routine is run midway through the boot process.  By the time we get
40803831d35Sstevel  * here, we know about all the active CPU boards in the system, and we have
40903831d35Sstevel  * extracted information about each board's memory from the memory
41003831d35Sstevel  * controllers.  We have also figured out which ranges of memory will be
41103831d35Sstevel  * assigned to which memnodes, so we walk the slice table to build the table
41203831d35Sstevel  * of memnodes.
41303831d35Sstevel  */
41403831d35Sstevel /* ARGSUSED */
41503831d35Sstevel void
plat_build_mem_nodes(prom_memlist_t * list,size_t nelems)416986fd29aSsetje plat_build_mem_nodes(prom_memlist_t *list, size_t  nelems)
41703831d35Sstevel {
41803831d35Sstevel 	int	slice;
41903831d35Sstevel 	pfn_t	basepfn;
42003831d35Sstevel 	pgcnt_t	npgs;
42103831d35Sstevel 
42203831d35Sstevel 	mem_node_pfn_shift = PFN_SLICE_SHIFT;
42303831d35Sstevel 	mem_node_physalign = (1ull << PA_SLICE_SHIFT);
42403831d35Sstevel 
42503831d35Sstevel 	for (slice = 0; slice < SG_MAX_SLICE; slice++) {
42603831d35Sstevel 		if (slice_to_memnode[slice] == -1)
42703831d35Sstevel 			continue;
42803831d35Sstevel 		basepfn = (uint64_t)slice << PFN_SLICE_SHIFT;
42903831d35Sstevel 		npgs = 1ull << PFN_SLICE_SHIFT;
43003831d35Sstevel 		mem_node_add_slice(basepfn, basepfn + npgs - 1);
43103831d35Sstevel 	}
43203831d35Sstevel }
43303831d35Sstevel 
43403831d35Sstevel int
plat_pfn_to_mem_node(pfn_t pfn)43503831d35Sstevel plat_pfn_to_mem_node(pfn_t pfn)
43603831d35Sstevel {
43703831d35Sstevel 	int node;
43803831d35Sstevel 
43903831d35Sstevel 	node = slice_to_memnode[PFN_2_SLICE(pfn)];
44003831d35Sstevel 
44103831d35Sstevel 	return (node);
44203831d35Sstevel }
44303831d35Sstevel 
44403831d35Sstevel /*
44503831d35Sstevel  * Serengeti support for lgroups.
44603831d35Sstevel  *
44703831d35Sstevel  * On Serengeti, an lgroup platform handle == board number.
44803831d35Sstevel  *
44903831d35Sstevel  * Mappings between lgroup handles and memnodes are managed
45003831d35Sstevel  * in addition to mappings between memory slices and memnodes
45103831d35Sstevel  * to support cross-board interleaving as well as multiple
45203831d35Sstevel  * slices per board (e.g. >1GB DIMMs). The initial mapping
45303831d35Sstevel  * of memnodes to lgroup handles is determined at boot time.
45403831d35Sstevel  * A DR addition of memory adds a new mapping. A DR copy-rename
45503831d35Sstevel  * swaps mappings.
45603831d35Sstevel  */
45703831d35Sstevel 
45803831d35Sstevel /*
45903831d35Sstevel  * Macro for extracting the board number from the CPU id
46003831d35Sstevel  */
46103831d35Sstevel #define	CPUID_TO_BOARD(id)	(((id) >> 2) & 0x7)
46203831d35Sstevel 
46303831d35Sstevel /*
46403831d35Sstevel  * Return the platform handle for the lgroup containing the given CPU
46503831d35Sstevel  *
46603831d35Sstevel  * For Serengeti, lgroup platform handle == board number
46703831d35Sstevel  */
46803831d35Sstevel lgrp_handle_t
plat_lgrp_cpu_to_hand(processorid_t id)46903831d35Sstevel plat_lgrp_cpu_to_hand(processorid_t id)
47003831d35Sstevel {
47103831d35Sstevel 	return (CPUID_TO_BOARD(id));
47203831d35Sstevel }
47303831d35Sstevel 
47403831d35Sstevel /*
47503831d35Sstevel  * Platform specific lgroup initialization
47603831d35Sstevel  */
47703831d35Sstevel void
plat_lgrp_init(void)47803831d35Sstevel plat_lgrp_init(void)
47903831d35Sstevel {
48003831d35Sstevel 	int i;
48103831d35Sstevel 	extern uint32_t lgrp_expand_proc_thresh;
48203831d35Sstevel 	extern uint32_t lgrp_expand_proc_diff;
48303831d35Sstevel 
48403831d35Sstevel 	/*
48503831d35Sstevel 	 * Initialize lookup tables to invalid values so we catch
48603831d35Sstevel 	 * any illegal use of them.
48703831d35Sstevel 	 */
48803831d35Sstevel 	for (i = 0; i < SG_MAX_SLICE; i++) {
48903831d35Sstevel 		slice_to_memnode[i] = -1;
49003831d35Sstevel 	}
49103831d35Sstevel 
49203831d35Sstevel 	/*
49303831d35Sstevel 	 * Set tuneables for Serengeti architecture
49403831d35Sstevel 	 *
49503831d35Sstevel 	 * lgrp_expand_proc_thresh is the minimum load on the lgroups
49603831d35Sstevel 	 * this process is currently running on before considering
49703831d35Sstevel 	 * expanding threads to another lgroup.
49803831d35Sstevel 	 *
49903831d35Sstevel 	 * lgrp_expand_proc_diff determines how much less the remote lgroup
50003831d35Sstevel 	 * must be loaded before expanding to it.
50103831d35Sstevel 	 *
50203831d35Sstevel 	 * Bandwidth is maximized on Serengeti by spreading load across
50303831d35Sstevel 	 * the machine. The impact to inter-thread communication isn't
50403831d35Sstevel 	 * too costly since remote latencies are relatively low.  These
50503831d35Sstevel 	 * values equate to one CPU's load and so attempt to spread the
50603831d35Sstevel 	 * load out across as many lgroups as possible one CPU at a time.
50703831d35Sstevel 	 */
50803831d35Sstevel 	lgrp_expand_proc_thresh = LGRP_LOADAVG_THREAD_MAX;
50903831d35Sstevel 	lgrp_expand_proc_diff = LGRP_LOADAVG_THREAD_MAX;
51003831d35Sstevel }
51103831d35Sstevel 
51203831d35Sstevel /*
51303831d35Sstevel  * Platform notification of lgroup (re)configuration changes
51403831d35Sstevel  */
51503831d35Sstevel /*ARGSUSED*/
51603831d35Sstevel void
plat_lgrp_config(lgrp_config_flag_t evt,uintptr_t arg)51703831d35Sstevel plat_lgrp_config(lgrp_config_flag_t evt, uintptr_t arg)
51803831d35Sstevel {
51903831d35Sstevel 	update_membounds_t	*umb;
52003831d35Sstevel 	lgrp_config_mem_rename_t lmr;
52103831d35Sstevel 	lgrp_handle_t		shand, thand;
52203831d35Sstevel 	int			snode, tnode;
52303831d35Sstevel 
52403831d35Sstevel 	switch (evt) {
52503831d35Sstevel 
52603831d35Sstevel 	case LGRP_CONFIG_MEM_ADD:
52703831d35Sstevel 		umb = (update_membounds_t *)arg;
52803831d35Sstevel 		update_mem_bounds(umb->u_board, umb->u_base, umb->u_len);
52903831d35Sstevel 
53003831d35Sstevel 		break;
53103831d35Sstevel 
53203831d35Sstevel 	case LGRP_CONFIG_MEM_DEL:
53303831d35Sstevel 		/* We don't have to do anything */
53403831d35Sstevel 
53503831d35Sstevel 		break;
53603831d35Sstevel 
53703831d35Sstevel 	case LGRP_CONFIG_MEM_RENAME:
53803831d35Sstevel 		/*
53903831d35Sstevel 		 * During a DR copy-rename operation, all of the memory
54003831d35Sstevel 		 * on one board is moved to another board -- but the
54103831d35Sstevel 		 * addresses/pfns and memnodes don't change. This means
54203831d35Sstevel 		 * the memory has changed locations without changing identity.
54303831d35Sstevel 		 *
54403831d35Sstevel 		 * Source is where we are copying from and target is where we
54503831d35Sstevel 		 * are copying to.  After source memnode is copied to target
54603831d35Sstevel 		 * memnode, the physical addresses of the target memnode are
54703831d35Sstevel 		 * renamed to match what the source memnode had.  Then target
54803831d35Sstevel 		 * memnode can be removed and source memnode can take its
54903831d35Sstevel 		 * place.
55003831d35Sstevel 		 *
55103831d35Sstevel 		 * To do this, swap the lgroup handle to memnode mappings for
55203831d35Sstevel 		 * the boards, so target lgroup will have source memnode and
55303831d35Sstevel 		 * source lgroup will have empty target memnode which is where
55403831d35Sstevel 		 * its memory will go (if any is added to it later).
55503831d35Sstevel 		 *
55603831d35Sstevel 		 * Then source memnode needs to be removed from its lgroup
55703831d35Sstevel 		 * and added to the target lgroup where the memory was living
55803831d35Sstevel 		 * but under a different name/memnode.  The memory was in the
55903831d35Sstevel 		 * target memnode and now lives in the source memnode with
56003831d35Sstevel 		 * different physical addresses even though it is the same
56103831d35Sstevel 		 * memory.
56203831d35Sstevel 		 */
56303831d35Sstevel 		shand = arg & 0xffff;
56403831d35Sstevel 		thand = (arg & 0xffff0000) >> 16;
56503831d35Sstevel 		snode = plat_lgrphand_to_mem_node(shand);
56603831d35Sstevel 		tnode = plat_lgrphand_to_mem_node(thand);
56703831d35Sstevel 
56803831d35Sstevel 		plat_assign_lgrphand_to_mem_node(thand, snode);
56903831d35Sstevel 		plat_assign_lgrphand_to_mem_node(shand, tnode);
57003831d35Sstevel 
57103831d35Sstevel 		/*
57203831d35Sstevel 		 * Remove source memnode of copy rename from its lgroup
57303831d35Sstevel 		 * and add it to its new target lgroup
57403831d35Sstevel 		 */
57503831d35Sstevel 		lmr.lmem_rename_from = shand;
57603831d35Sstevel 		lmr.lmem_rename_to = thand;
57703831d35Sstevel 
57803831d35Sstevel 		lgrp_config(LGRP_CONFIG_MEM_RENAME, (uintptr_t)snode,
57903831d35Sstevel 		    (uintptr_t)&lmr);
58003831d35Sstevel 
58103831d35Sstevel 		break;
58203831d35Sstevel 
58303831d35Sstevel 	default:
58403831d35Sstevel 		break;
58503831d35Sstevel 	}
58603831d35Sstevel }
58703831d35Sstevel 
58803831d35Sstevel /*
58903831d35Sstevel  * Return latency between "from" and "to" lgroups
59003831d35Sstevel  *
59103831d35Sstevel  * This latency number can only be used for relative comparison
59203831d35Sstevel  * between lgroups on the running system, cannot be used across platforms,
59303831d35Sstevel  * and may not reflect the actual latency.  It is platform and implementation
59403831d35Sstevel  * specific, so platform gets to decide its value.  It would be nice if the
59503831d35Sstevel  * number was at least proportional to make comparisons more meaningful though.
59603831d35Sstevel  * NOTE: The numbers below are supposed to be load latencies for uncached
59703831d35Sstevel  * memory divided by 10.
59803831d35Sstevel  */
59903831d35Sstevel int
plat_lgrp_latency(lgrp_handle_t from,lgrp_handle_t to)60003831d35Sstevel plat_lgrp_latency(lgrp_handle_t from, lgrp_handle_t to)
60103831d35Sstevel {
60203831d35Sstevel 	/*
60303831d35Sstevel 	 * Return min remote latency when there are more than two lgroups
60403831d35Sstevel 	 * (root and child) and getting latency between two different lgroups
60503831d35Sstevel 	 * or root is involved
60603831d35Sstevel 	 */
60703831d35Sstevel 	if (lgrp_optimizations() && (from != to ||
60803831d35Sstevel 	    from == LGRP_DEFAULT_HANDLE || to == LGRP_DEFAULT_HANDLE))
60903831d35Sstevel 		return (28);
61003831d35Sstevel 	else
61103831d35Sstevel 		return (23);
61203831d35Sstevel }
61303831d35Sstevel 
61403831d35Sstevel /* ARGSUSED */
61503831d35Sstevel void
plat_freelist_process(int mnode)61603831d35Sstevel plat_freelist_process(int mnode)
61703831d35Sstevel {
61803831d35Sstevel }
61903831d35Sstevel 
62003831d35Sstevel /*
62103831d35Sstevel  * Find dip for chosen IOSRAM
62203831d35Sstevel  */
62303831d35Sstevel dev_info_t *
find_chosen_dip(void)62403831d35Sstevel find_chosen_dip(void)
62503831d35Sstevel {
62603831d35Sstevel 	dev_info_t	*dip;
62703831d35Sstevel 	char		master_sbbc[MAXNAMELEN];
62803831d35Sstevel 	int		nodeid;
62903831d35Sstevel 	uint_t		tunnel;
63003831d35Sstevel 
63103831d35Sstevel 	/*
63203831d35Sstevel 	 * find the /chosen SBBC node, prom interface will handle errors
63303831d35Sstevel 	 */
63403831d35Sstevel 	nodeid = prom_chosennode();
63503831d35Sstevel 	/*
63603831d35Sstevel 	 * get the 'iosram' property from the /chosen node
63703831d35Sstevel 	 */
63803831d35Sstevel 	if (prom_getprop(nodeid, IOSRAM_CHOSEN_PROP, (caddr_t)&tunnel) <= 0) {
63903831d35Sstevel 		SBBC_ERR(CE_PANIC, "No iosram property found! \n");
64003831d35Sstevel 	}
64103831d35Sstevel 
64203831d35Sstevel 	if (prom_phandle_to_path((phandle_t)tunnel, master_sbbc,
64303831d35Sstevel 	    sizeof (master_sbbc)) < 0) {
64403831d35Sstevel 		SBBC_ERR1(CE_PANIC, "prom_phandle_to_path(%d) failed\n",
64503831d35Sstevel 		    tunnel);
64603831d35Sstevel 	}
64703831d35Sstevel 
64803831d35Sstevel 	chosen_nodeid = nodeid;
64903831d35Sstevel 
65003831d35Sstevel 	/*
65103831d35Sstevel 	 * load and attach the sgsbbc driver.
65203831d35Sstevel 	 * This will also attach all the sgsbbc driver instances
65303831d35Sstevel 	 */
65403831d35Sstevel 	if (i_ddi_attach_hw_nodes("sgsbbc") != DDI_SUCCESS) {
65503831d35Sstevel 		cmn_err(CE_WARN, "sgsbbc failed to load\n");
65603831d35Sstevel 	}
65703831d35Sstevel 	/* translate a path name to a dev_info_t */
65803831d35Sstevel 	dip = e_ddi_hold_devi_by_path(master_sbbc, 0);
65903831d35Sstevel 	if ((dip == NULL) || (ddi_get_nodeid(dip) != tunnel)) {
66003831d35Sstevel 		cmn_err(CE_PANIC,
661986fd29aSsetje 		    "e_ddi_hold_devi_by_path(%x) failed for SBBC\n", tunnel);
66203831d35Sstevel 	}
66303831d35Sstevel 
66403831d35Sstevel 	/* make sure devi_ref is ZERO */
66503831d35Sstevel 	ndi_rele_devi(dip);
66603831d35Sstevel 	DCMNERR(CE_CONT, "Chosen IOSRAM is at %s \n", master_sbbc);
66703831d35Sstevel 
66803831d35Sstevel 	return (dip);
66903831d35Sstevel }
67003831d35Sstevel 
67103831d35Sstevel void
load_platform_drivers(void)67203831d35Sstevel load_platform_drivers(void)
67303831d35Sstevel {
67403831d35Sstevel 	int ret;
67503831d35Sstevel 
67603831d35Sstevel 	/*
67703831d35Sstevel 	 * Load the mc-us3 memory driver.
67803831d35Sstevel 	 */
67903831d35Sstevel 	if (i_ddi_attach_hw_nodes("mc-us3") != DDI_SUCCESS)
68003831d35Sstevel 		cmn_err(CE_WARN, "mc-us3 failed to load");
68103831d35Sstevel 	else
68203831d35Sstevel 		(void) ddi_hold_driver(ddi_name_to_major("mc-us3"));
68303831d35Sstevel 
68403831d35Sstevel 	/*
68503831d35Sstevel 	 * Initialize the chosen IOSRAM before its clients
68603831d35Sstevel 	 * are loaded.
68703831d35Sstevel 	 */
68803831d35Sstevel 	(void) find_chosen_dip();
68903831d35Sstevel 
69003831d35Sstevel 	/*
69103831d35Sstevel 	 * Load the environmentals driver (sgenv)
69203831d35Sstevel 	 *
69303831d35Sstevel 	 * We need this driver to handle events from the SC when state
69403831d35Sstevel 	 * changes occur in the environmental data.
69503831d35Sstevel 	 */
69603831d35Sstevel 	if (i_ddi_attach_hw_nodes("sgenv") != DDI_SUCCESS)
69703831d35Sstevel 		cmn_err(CE_WARN, "sgenv failed to load");
69803831d35Sstevel 
69903831d35Sstevel 	/*
70003831d35Sstevel 	 * Ideally, we'd do this in set_platform_defaults(), but
70103831d35Sstevel 	 * at that point it's too early to look up symbols.
70203831d35Sstevel 	 */
70303831d35Sstevel 	iosram_write_ptr = (int (*)(int, uint32_t, caddr_t, uint32_t))
70403831d35Sstevel 	    modgetsymvalue("iosram_write", 0);
70503831d35Sstevel 
70603831d35Sstevel 	if (iosram_write_ptr == NULL) {
70703831d35Sstevel 		DCMNERR(CE_WARN, "load_platform_defaults: iosram_write()"
70803831d35Sstevel 		    " not found; signatures will not be updated\n");
70903831d35Sstevel 	} else {
71003831d35Sstevel 		/*
71103831d35Sstevel 		 * The iosram read ptr is only needed if we can actually
71203831d35Sstevel 		 * write CPU signatures, so only bother setting it if we
71303831d35Sstevel 		 * set a valid write pointer, above.
71403831d35Sstevel 		 */
71503831d35Sstevel 		iosram_read_ptr = (int (*)(int, uint32_t, caddr_t, uint32_t))
71603831d35Sstevel 		    modgetsymvalue("iosram_read", 0);
71703831d35Sstevel 
71803831d35Sstevel 		if (iosram_read_ptr == NULL)
71903831d35Sstevel 			DCMNERR(CE_WARN, "load_platform_defaults: iosram_read()"
72003831d35Sstevel 			    " not found\n");
72103831d35Sstevel 	}
72203831d35Sstevel 
72303831d35Sstevel 	/*
72403831d35Sstevel 	 * Set todsg_use_sc to TRUE so that we will be getting date
72503831d35Sstevel 	 * from the SC.
72603831d35Sstevel 	 */
72703831d35Sstevel 	todsg_use_sc = TRUE;
72803831d35Sstevel 
72903831d35Sstevel 	/*
73003831d35Sstevel 	 * Now is a good time to activate hardware watchdog (if one exists).
73103831d35Sstevel 	 */
73203831d35Sstevel 	mutex_enter(&tod_lock);
73303831d35Sstevel 	if (watchdog_enable)
73403831d35Sstevel 		ret = tod_ops.tod_set_watchdog_timer(watchdog_timeout_seconds);
73503831d35Sstevel 	mutex_exit(&tod_lock);
73603831d35Sstevel 	if (ret != 0)
73703831d35Sstevel 		printf("Hardware watchdog enabled\n");
73803831d35Sstevel 
73903831d35Sstevel 	plat_ecc_init();
74003831d35Sstevel }
74103831d35Sstevel 
74203831d35Sstevel /*
74303831d35Sstevel  * No platform drivers on this platform
74403831d35Sstevel  */
74503831d35Sstevel char *platform_module_list[] = {
74603831d35Sstevel 	(char *)0
74703831d35Sstevel };
74803831d35Sstevel 
74903831d35Sstevel /*ARGSUSED*/
75003831d35Sstevel void
plat_tod_fault(enum tod_fault_type tod_bad)75103831d35Sstevel plat_tod_fault(enum tod_fault_type tod_bad)
75203831d35Sstevel {
75303831d35Sstevel }
75403831d35Sstevel int
plat_max_boards()75503831d35Sstevel plat_max_boards()
75603831d35Sstevel {
75703831d35Sstevel 	return (SG_MAX_BDS);
75803831d35Sstevel }
75903831d35Sstevel int
plat_max_io_units_per_board()76003831d35Sstevel plat_max_io_units_per_board()
76103831d35Sstevel {
76203831d35Sstevel 	return (SG_MAX_IO_PER_BD);
76303831d35Sstevel }
76403831d35Sstevel int
plat_max_cmp_units_per_board()76503831d35Sstevel plat_max_cmp_units_per_board()
76603831d35Sstevel {
76703831d35Sstevel 	return (SG_MAX_CMPS_PER_BD);
76803831d35Sstevel }
76903831d35Sstevel int
plat_max_cpu_units_per_board()77003831d35Sstevel plat_max_cpu_units_per_board()
77103831d35Sstevel {
77203831d35Sstevel 	return (SG_MAX_CPUS_PER_BD);
77303831d35Sstevel }
77403831d35Sstevel 
77503831d35Sstevel int
plat_max_mc_units_per_board()77603831d35Sstevel plat_max_mc_units_per_board()
77703831d35Sstevel {
77803831d35Sstevel 	return (SG_MAX_CMPS_PER_BD); /* each CPU die has a memory controller */
77903831d35Sstevel }
78003831d35Sstevel 
78103831d35Sstevel int
plat_max_mem_units_per_board()78203831d35Sstevel plat_max_mem_units_per_board()
78303831d35Sstevel {
78403831d35Sstevel 	return (SG_MAX_MEM_PER_BD);
78503831d35Sstevel }
78603831d35Sstevel 
78703831d35Sstevel int
plat_max_cpumem_boards(void)78803831d35Sstevel plat_max_cpumem_boards(void)
78903831d35Sstevel {
79003831d35Sstevel 	return (LW8_MAX_CPU_BDS);
79103831d35Sstevel }
79203831d35Sstevel 
79303831d35Sstevel int
set_platform_max_ncpus(void)79403831d35Sstevel set_platform_max_ncpus(void)
79503831d35Sstevel {
79603831d35Sstevel 	return (sg_max_ncpus);
79703831d35Sstevel }
79803831d35Sstevel 
79903831d35Sstevel void
plat_dmv_params(uint_t * hwint,uint_t * swint)80003831d35Sstevel plat_dmv_params(uint_t *hwint, uint_t *swint)
80103831d35Sstevel {
80203831d35Sstevel 	*hwint = MAX_UPA;
80303831d35Sstevel 	*swint = 0;
80403831d35Sstevel }
80503831d35Sstevel 
80603831d35Sstevel static int (*sg_mbox)(sbbc_msg_t *, sbbc_msg_t *, time_t) = NULL;
80703831d35Sstevel 
80803831d35Sstevel /*
80903831d35Sstevel  * Our nodename has been set, pass it along to the SC.
81003831d35Sstevel  */
81103831d35Sstevel void
plat_nodename_set(void)81203831d35Sstevel plat_nodename_set(void)
81303831d35Sstevel {
81403831d35Sstevel 	sbbc_msg_t	req;	/* request */
81503831d35Sstevel 	sbbc_msg_t	resp;	/* response */
81603831d35Sstevel 	int		rv;	/* return value from call to mbox */
81703831d35Sstevel 	struct nodename_info {
81803831d35Sstevel 		int32_t	namelen;
81903831d35Sstevel 		char	nodename[_SYS_NMLN];
82003831d35Sstevel 	} nni;
82103831d35Sstevel 
82203831d35Sstevel 	/*
82303831d35Sstevel 	 * find the symbol for the mailbox routine
82403831d35Sstevel 	 */
82503831d35Sstevel 	if (sg_mbox == NULL)
82603831d35Sstevel 		sg_mbox = (int (*)(sbbc_msg_t *, sbbc_msg_t *, time_t))
827986fd29aSsetje 		    modgetsymvalue("sbbc_mbox_request_response", 0);
82803831d35Sstevel 
82903831d35Sstevel 	if (sg_mbox == NULL) {
83003831d35Sstevel 		cmn_err(CE_NOTE, "!plat_nodename_set: sg_mbox not found\n");
83103831d35Sstevel 		return;
83203831d35Sstevel 	}
83303831d35Sstevel 
83403831d35Sstevel 	/*
83503831d35Sstevel 	 * construct the message telling the SC our nodename
83603831d35Sstevel 	 */
83703831d35Sstevel 	(void) strcpy(nni.nodename, utsname.nodename);
83803831d35Sstevel 	nni.namelen = (int32_t)strlen(nni.nodename);
83903831d35Sstevel 
84003831d35Sstevel 	req.msg_type.type = INFO_MBOX;
84103831d35Sstevel 	req.msg_type.sub_type = INFO_MBOX_NODENAME;
84203831d35Sstevel 	req.msg_status = 0;
84303831d35Sstevel 	req.msg_len = (int)(nni.namelen + sizeof (nni.namelen));
84403831d35Sstevel 	req.msg_bytes = 0;
84503831d35Sstevel 	req.msg_buf = (caddr_t)&nni;
84603831d35Sstevel 	req.msg_data[0] = 0;
84703831d35Sstevel 	req.msg_data[1] = 0;
84803831d35Sstevel 
84903831d35Sstevel 	/*
85003831d35Sstevel 	 * initialize the response back from the SC
85103831d35Sstevel 	 */
85203831d35Sstevel 	resp.msg_type.type = INFO_MBOX;
85303831d35Sstevel 	resp.msg_type.sub_type = INFO_MBOX_NODENAME;
85403831d35Sstevel 	resp.msg_status = 0;
85503831d35Sstevel 	resp.msg_len = 0;
85603831d35Sstevel 	resp.msg_bytes = 0;
85703831d35Sstevel 	resp.msg_buf = (caddr_t)0;
85803831d35Sstevel 	resp.msg_data[0] = 0;
85903831d35Sstevel 	resp.msg_data[1] = 0;
86003831d35Sstevel 
86103831d35Sstevel 	/*
86203831d35Sstevel 	 * ship it and check for success
86303831d35Sstevel 	 */
86403831d35Sstevel 	rv = (sg_mbox)(&req, &resp, sbbc_mbox_default_timeout);
86503831d35Sstevel 
86603831d35Sstevel 	if (rv != 0) {
86703831d35Sstevel 		cmn_err(CE_NOTE, "!plat_nodename_set: sg_mbox retval %d\n", rv);
86803831d35Sstevel 	} else if (resp.msg_status != 0) {
86903831d35Sstevel 		cmn_err(CE_NOTE, "!plat_nodename_set: msg_status %d\n",
870986fd29aSsetje 		    resp.msg_status);
87103831d35Sstevel 	} else {
87203831d35Sstevel 		DCMNERR(CE_NOTE, "!plat_nodename_set was successful\n");
87303831d35Sstevel 
87403831d35Sstevel 		/*
87503831d35Sstevel 		 * It is necessary to exchange capability the bitmap
87603831d35Sstevel 		 * with SC before sending any ecc error information and
87703831d35Sstevel 		 * indictment. We are calling the plat_ecc_capability_send()
87803831d35Sstevel 		 * here just after sending the nodename successfully.
87903831d35Sstevel 		 */
88003831d35Sstevel 		rv = plat_ecc_capability_send();
88103831d35Sstevel 		if (rv == 0) {
88203831d35Sstevel 			DCMNERR(CE_NOTE, "!plat_ecc_capability_send was"
88303831d35Sstevel 			    "successful\n");
88403831d35Sstevel 		}
88503831d35Sstevel 	}
88603831d35Sstevel }
88703831d35Sstevel 
88803831d35Sstevel /*
88903831d35Sstevel  * flag to allow users switch between using OBP's
89003831d35Sstevel  * prom_get_unum() and mc-us3 driver's p2get_mem_unum()
89103831d35Sstevel  * (for main memory errors only).
89203831d35Sstevel  */
89303831d35Sstevel int sg_use_prom_get_unum = 0;
89403831d35Sstevel 
89503831d35Sstevel /*
89603831d35Sstevel  * Debugging flag: set to 1 to call into obp for get_unum, or set it to 0
89703831d35Sstevel  * to call into the unum cache system.  This is the E$ equivalent of
89803831d35Sstevel  * sg_use_prom_get_unum.
89903831d35Sstevel  */
90003831d35Sstevel int sg_use_prom_ecache_unum = 0;
90103831d35Sstevel 
90203831d35Sstevel /* used for logging ECC errors to the SC */
90303831d35Sstevel #define	SG_MEMORY_ECC	1
90403831d35Sstevel #define	SG_ECACHE_ECC	2
90503831d35Sstevel #define	SG_UNKNOWN_ECC	(-1)
90603831d35Sstevel 
90703831d35Sstevel /*
90803831d35Sstevel  * plat_get_mem_unum() generates a string identifying either the
90903831d35Sstevel  * memory or E$ DIMM(s) during error logging. Depending on whether
91003831d35Sstevel  * the error is E$ or memory related, the appropriate support
91103831d35Sstevel  * routine is called to assist in the string generation.
91203831d35Sstevel  *
91303831d35Sstevel  * - For main memory errors we can use the mc-us3 drivers p2getunum()
91403831d35Sstevel  *   (or prom_get_unum() for debugging purposes).
91503831d35Sstevel  *
91603831d35Sstevel  * - For E$ errors we call sg_get_ecacheunum() to generate the unum (or
91703831d35Sstevel  *   prom_serengeti_get_ecacheunum() for debugging purposes).
91803831d35Sstevel  */
91903831d35Sstevel 
92003831d35Sstevel static int
sg_prom_get_unum(int synd_code,uint64_t paddr,char * buf,int buflen,int * lenp)92103831d35Sstevel sg_prom_get_unum(int synd_code, uint64_t paddr, char *buf, int buflen,
92203831d35Sstevel     int *lenp)
92303831d35Sstevel {
92403831d35Sstevel 	if ((prom_get_unum(synd_code, (unsigned long long)paddr,
92503831d35Sstevel 	    buf, buflen, lenp)) != 0)
92603831d35Sstevel 		return (EIO);
92703831d35Sstevel 	else if (*lenp <= 1)
92803831d35Sstevel 		return (EINVAL);
92903831d35Sstevel 	else
93003831d35Sstevel 		return (0);
93103831d35Sstevel }
93203831d35Sstevel 
93303831d35Sstevel /*ARGSUSED*/
93403831d35Sstevel int
plat_get_mem_unum(int synd_code,uint64_t flt_addr,int flt_bus_id,int flt_in_memory,ushort_t flt_status,char * buf,int buflen,int * lenp)93503831d35Sstevel plat_get_mem_unum(int synd_code, uint64_t flt_addr, int flt_bus_id,
93603831d35Sstevel     int flt_in_memory, ushort_t flt_status, char *buf, int buflen, int *lenp)
93703831d35Sstevel {
93803831d35Sstevel 	/*
93903831d35Sstevel 	 * unum_func will either point to the memory drivers p2get_mem_unum()
94003831d35Sstevel 	 * or to prom_get_unum() for memory errors.
94103831d35Sstevel 	 */
94203831d35Sstevel 	int (*unum_func)(int synd_code, uint64_t paddr, char *buf,
94303831d35Sstevel 	    int buflen, int *lenp) = p2get_mem_unum;
94403831d35Sstevel 
94503831d35Sstevel 	/*
94603831d35Sstevel 	 * check if it's a Memory or an Ecache error.
94703831d35Sstevel 	 */
94803831d35Sstevel 	if (flt_in_memory) {
94903831d35Sstevel 		/*
95003831d35Sstevel 		 * It's a main memory error.
95103831d35Sstevel 		 *
95203831d35Sstevel 		 * For debugging we allow the user to switch between
95303831d35Sstevel 		 * using OBP's get_unum and the memory driver's get_unum
95403831d35Sstevel 		 * so we create a pointer to the functions and switch
95503831d35Sstevel 		 * depending on the sg_use_prom_get_unum flag.
95603831d35Sstevel 		 */
95703831d35Sstevel 		if (sg_use_prom_get_unum) {
95803831d35Sstevel 			DCMNERR(CE_NOTE, "Using prom_get_unum from OBP");
95903831d35Sstevel 			return (sg_prom_get_unum(synd_code,
96003831d35Sstevel 			    P2ALIGN(flt_addr, 8), buf, buflen, lenp));
96103831d35Sstevel 		} else if (unum_func != NULL) {
96203831d35Sstevel 			return (unum_func(synd_code, P2ALIGN(flt_addr, 8),
96303831d35Sstevel 			    buf, buflen, lenp));
96403831d35Sstevel 		} else {
96503831d35Sstevel 			return (ENOTSUP);
96603831d35Sstevel 		}
96703831d35Sstevel 	} else if (flt_status & ECC_ECACHE) {
96803831d35Sstevel 		/*
96903831d35Sstevel 		 * It's an E$ error.
97003831d35Sstevel 		 */
97103831d35Sstevel 		if (sg_use_prom_ecache_unum) {
97203831d35Sstevel 			/*
97303831d35Sstevel 			 * We call to OBP to handle this.
97403831d35Sstevel 			 */
97503831d35Sstevel 			DCMNERR(CE_NOTE,
97603831d35Sstevel 			    "Using prom_serengeti_get_ecacheunum from OBP");
97703831d35Sstevel 			if (prom_serengeti_get_ecacheunum(flt_bus_id,
97803831d35Sstevel 			    P2ALIGN(flt_addr, 8), buf, buflen, lenp) != 0) {
97903831d35Sstevel 				return (EIO);
98003831d35Sstevel 			}
98103831d35Sstevel 		} else {
98203831d35Sstevel 			return (sg_get_ecacheunum(flt_bus_id, flt_addr,
98303831d35Sstevel 			    buf, buflen, lenp));
98403831d35Sstevel 		}
98503831d35Sstevel 	} else {
98603831d35Sstevel 		return (ENOTSUP);
98703831d35Sstevel 	}
98803831d35Sstevel 
98903831d35Sstevel 	return (0);
99003831d35Sstevel }
99103831d35Sstevel 
99203831d35Sstevel /*
99303831d35Sstevel  * This platform hook gets called from mc_add_mem_unum_label() in the mc-us3
99403831d35Sstevel  * driver giving each platform the opportunity to add platform
99503831d35Sstevel  * specific label information to the unum for ECC error logging purposes.
99603831d35Sstevel  */
99703831d35Sstevel void
plat_add_mem_unum_label(char * unum,int mcid,int bank,int dimm)99803831d35Sstevel plat_add_mem_unum_label(char *unum, int mcid, int bank, int dimm)
99903831d35Sstevel {
100003831d35Sstevel 	char	new_unum[UNUM_NAMLEN] = "";
100103831d35Sstevel 	int	node = SG_PORTID_TO_NODEID(mcid);
100203831d35Sstevel 	int	board = SG_CPU_BD_PORTID_TO_BD_NUM(mcid);
100303831d35Sstevel 	int	position = SG_PORTID_TO_CPU_POSN(mcid);
100403831d35Sstevel 
100503831d35Sstevel 	/*
100603831d35Sstevel 	 * The mc-us3 driver deals with logical banks but for unum
100703831d35Sstevel 	 * purposes we need to use physical banks so that the correct
100803831d35Sstevel 	 * dimm can be physically located. Logical banks 0 and 2
100903831d35Sstevel 	 * make up physical bank 0. Logical banks 1 and 3 make up
101003831d35Sstevel 	 * physical bank 1. Here we do the necessary conversion.
101103831d35Sstevel 	 */
101203831d35Sstevel 	bank = (bank % 2);
101303831d35Sstevel 
101403831d35Sstevel 	if (dimm == -1) {
101503831d35Sstevel 		SG_SET_FRU_NAME_NODE(new_unum, node);
101603831d35Sstevel 		SG_SET_FRU_NAME_CPU_BOARD(new_unum, board);
101703831d35Sstevel 		SG_SET_FRU_NAME_MODULE(new_unum, position);
101803831d35Sstevel 		SG_SET_FRU_NAME_BANK(new_unum, bank);
101903831d35Sstevel 
102003831d35Sstevel 	} else {
102103831d35Sstevel 		SG_SET_FRU_NAME_NODE(new_unum, node);
102203831d35Sstevel 		SG_SET_FRU_NAME_CPU_BOARD(new_unum, board);
102303831d35Sstevel 		SG_SET_FRU_NAME_MODULE(new_unum, position);
102403831d35Sstevel 		SG_SET_FRU_NAME_BANK(new_unum, bank);
102503831d35Sstevel 		SG_SET_FRU_NAME_DIMM(new_unum, dimm);
102603831d35Sstevel 
1027*07d06da5SSurya Prakki 		(void) strcat(new_unum, " ");
1028*07d06da5SSurya Prakki 		(void) strcat(new_unum, unum);
102903831d35Sstevel 	}
103003831d35Sstevel 
1031*07d06da5SSurya Prakki 	(void) strcpy(unum, new_unum);
103203831d35Sstevel }
103303831d35Sstevel 
103403831d35Sstevel int
plat_get_cpu_unum(int cpuid,char * buf,int buflen,int * lenp)103503831d35Sstevel plat_get_cpu_unum(int cpuid, char *buf, int buflen, int *lenp)
103603831d35Sstevel {
103703831d35Sstevel 	int	node = SG_PORTID_TO_NODEID(cpuid);
103803831d35Sstevel 	int	board = SG_CPU_BD_PORTID_TO_BD_NUM(cpuid);
103903831d35Sstevel 
104003831d35Sstevel 	if (snprintf(buf, buflen, "/N%d/%s%d", node,
104103831d35Sstevel 	    SG_HPU_TYPE_CPU_BOARD_ID, board) >= buflen) {
104203831d35Sstevel 		return (ENOSPC);
104303831d35Sstevel 	} else {
104403831d35Sstevel 		*lenp = strlen(buf);
104503831d35Sstevel 		return (0);
104603831d35Sstevel 	}
104703831d35Sstevel }
104803831d35Sstevel 
104903831d35Sstevel static void (*sg_ecc_taskq_func)(sbbc_ecc_mbox_t *) = NULL;
105003831d35Sstevel static int (*sg_ecc_mbox_func)(sbbc_ecc_mbox_t *) = NULL;
105103831d35Sstevel 
105203831d35Sstevel /*
105303831d35Sstevel  * We log all ECC errors to the SC so we send a mailbox
105403831d35Sstevel  * message to the SC passing it the relevant data.
105503831d35Sstevel  * ECC mailbox messages are sent via a taskq mechanism to
105603831d35Sstevel  * prevent impaired system performance during ECC floods.
105703831d35Sstevel  * Indictments have already passed through a taskq, so they
105803831d35Sstevel  * are not queued here.
105903831d35Sstevel  */
106003831d35Sstevel int
plat_send_ecc_mailbox_msg(plat_ecc_message_type_t msg_type,void * datap)106103831d35Sstevel plat_send_ecc_mailbox_msg(plat_ecc_message_type_t msg_type, void *datap)
106203831d35Sstevel {
106303831d35Sstevel 	sbbc_ecc_mbox_t	*msgp;
106403831d35Sstevel 	uint16_t	msg_subtype;
106503831d35Sstevel 	int		sleep_flag, log_error;
106603831d35Sstevel 	size_t		msg_size;
106703831d35Sstevel 
106803831d35Sstevel 	if (sg_ecc_taskq_func == NULL) {
106903831d35Sstevel 		sg_ecc_taskq_func = (void (*)(sbbc_ecc_mbox_t *))
107003831d35Sstevel 		    modgetsymvalue("sbbc_mbox_queue_ecc_event", 0);
107103831d35Sstevel 		if (sg_ecc_taskq_func == NULL) {
107203831d35Sstevel 			cmn_err(CE_NOTE, "!plat_send_ecc_mailbox_msg: "
107303831d35Sstevel 			    "sbbc_mbox_queue_ecc_event not found");
107403831d35Sstevel 			return (ENODEV);
107503831d35Sstevel 		}
107603831d35Sstevel 	}
107703831d35Sstevel 	if (sg_ecc_mbox_func == NULL) {
107803831d35Sstevel 		sg_ecc_mbox_func = (int (*)(sbbc_ecc_mbox_t *))
107903831d35Sstevel 		    modgetsymvalue("sbbc_mbox_ecc_output", 0);
108003831d35Sstevel 		if (sg_ecc_mbox_func == NULL) {
108103831d35Sstevel 			cmn_err(CE_NOTE, "!plat_send_ecc_mailbox_msg: "
108203831d35Sstevel 			    "sbbc_mbox_ecc_output not found");
108303831d35Sstevel 			return (ENODEV);
108403831d35Sstevel 		}
108503831d35Sstevel 	}
108603831d35Sstevel 
108703831d35Sstevel 	/*
108803831d35Sstevel 	 * Initialize the request and response structures
108903831d35Sstevel 	 */
109003831d35Sstevel 	switch (msg_type) {
109103831d35Sstevel 	case PLAT_ECC_ERROR_MESSAGE:
109203831d35Sstevel 		msg_subtype = INFO_MBOX_ERROR_ECC;
109303831d35Sstevel 		msg_size = sizeof (plat_ecc_error_data_t);
109403831d35Sstevel 		sleep_flag = KM_NOSLEEP;
109503831d35Sstevel 		log_error = 1;
109603831d35Sstevel 		break;
109703831d35Sstevel 	case PLAT_ECC_ERROR2_MESSAGE:
109803831d35Sstevel 		msg_subtype = INFO_MBOX_ECC;
109903831d35Sstevel 		msg_size = sizeof (plat_ecc_error2_data_t);
110003831d35Sstevel 		sleep_flag = KM_NOSLEEP;
110103831d35Sstevel 		log_error = 1;
110203831d35Sstevel 		break;
110303831d35Sstevel 	case PLAT_ECC_INDICTMENT_MESSAGE:
110403831d35Sstevel 		msg_subtype = INFO_MBOX_ERROR_INDICT;
110503831d35Sstevel 		msg_size = sizeof (plat_ecc_indictment_data_t);
110603831d35Sstevel 		sleep_flag = KM_SLEEP;
110703831d35Sstevel 		log_error = 0;
110803831d35Sstevel 		break;
110903831d35Sstevel 	case PLAT_ECC_INDICTMENT2_MESSAGE:
111003831d35Sstevel 		msg_subtype = INFO_MBOX_ECC;
111103831d35Sstevel 		msg_size = sizeof (plat_ecc_indictment2_data_t);
111203831d35Sstevel 		sleep_flag = KM_SLEEP;
111303831d35Sstevel 		log_error = 0;
111403831d35Sstevel 		break;
111503831d35Sstevel 	case PLAT_ECC_CAPABILITY_MESSAGE:
111603831d35Sstevel 		msg_subtype = INFO_MBOX_ECC_CAP;
111703831d35Sstevel 		msg_size = sizeof (plat_capability_data_t) +
111803831d35Sstevel 		    strlen(utsname.release) + strlen(utsname.version) + 2;
111903831d35Sstevel 		sleep_flag = KM_SLEEP;
112003831d35Sstevel 		log_error = 0;
112103831d35Sstevel 		break;
112203831d35Sstevel 	case PLAT_ECC_DIMM_SID_MESSAGE:
112303831d35Sstevel 		msg_subtype = INFO_MBOX_ECC;
112403831d35Sstevel 		msg_size = sizeof (plat_dimm_sid_request_data_t);
112503831d35Sstevel 		sleep_flag = KM_SLEEP;
112603831d35Sstevel 		log_error = 0;
112703831d35Sstevel 		break;
112803831d35Sstevel 	default:
112903831d35Sstevel 		return (EINVAL);
113003831d35Sstevel 	}
113103831d35Sstevel 
113203831d35Sstevel 	msgp = (sbbc_ecc_mbox_t *)kmem_zalloc(sizeof (sbbc_ecc_mbox_t),
1133986fd29aSsetje 	    sleep_flag);
113403831d35Sstevel 	if (msgp == NULL) {
113503831d35Sstevel 		cmn_err(CE_NOTE, "!plat_send_ecc_mailbox_msg: "
1136986fd29aSsetje 		    "unable to allocate sbbc_ecc_mbox");
113703831d35Sstevel 		return (ENOMEM);
113803831d35Sstevel 	}
113903831d35Sstevel 
114003831d35Sstevel 	msgp->ecc_log_error = log_error;
114103831d35Sstevel 
114203831d35Sstevel 	msgp->ecc_req.msg_type.type = INFO_MBOX;
114303831d35Sstevel 	msgp->ecc_req.msg_type.sub_type = msg_subtype;
114403831d35Sstevel 	msgp->ecc_req.msg_status = 0;
114503831d35Sstevel 	msgp->ecc_req.msg_len = (int)msg_size;
114603831d35Sstevel 	msgp->ecc_req.msg_bytes = 0;
114703831d35Sstevel 	msgp->ecc_req.msg_buf = (caddr_t)kmem_zalloc(msg_size, sleep_flag);
114803831d35Sstevel 	msgp->ecc_req.msg_data[0] = 0;
114903831d35Sstevel 	msgp->ecc_req.msg_data[1] = 0;
115003831d35Sstevel 
115103831d35Sstevel 	if (msgp->ecc_req.msg_buf == NULL) {
115203831d35Sstevel 		cmn_err(CE_NOTE, "!plat_send_ecc_mailbox_msg: "
1153986fd29aSsetje 		    "unable to allocate request msg_buf");
115403831d35Sstevel 		kmem_free((void *)msgp, sizeof (sbbc_ecc_mbox_t));
115503831d35Sstevel 		return (ENOMEM);
115603831d35Sstevel 	}
115703831d35Sstevel 
115803831d35Sstevel 	bcopy(datap, (void *)msgp->ecc_req.msg_buf, msg_size);
115903831d35Sstevel 
116003831d35Sstevel 	/*
116103831d35Sstevel 	 * initialize the response back from the SC
116203831d35Sstevel 	 */
116303831d35Sstevel 	msgp->ecc_resp.msg_type.type = INFO_MBOX;
116403831d35Sstevel 	msgp->ecc_resp.msg_type.sub_type = msg_subtype;
116503831d35Sstevel 	msgp->ecc_resp.msg_status = 0;
116603831d35Sstevel 	msgp->ecc_resp.msg_len = 0;
116703831d35Sstevel 	msgp->ecc_resp.msg_bytes = 0;
116803831d35Sstevel 	msgp->ecc_resp.msg_buf = NULL;
116903831d35Sstevel 	msgp->ecc_resp.msg_data[0] = 0;
117003831d35Sstevel 	msgp->ecc_resp.msg_data[1] = 0;
117103831d35Sstevel 
117203831d35Sstevel 	switch (msg_type) {
117303831d35Sstevel 	case PLAT_ECC_ERROR_MESSAGE:
117403831d35Sstevel 	case PLAT_ECC_ERROR2_MESSAGE:
117503831d35Sstevel 		/*
117603831d35Sstevel 		 * For Error Messages, we go through a taskq.
117703831d35Sstevel 		 * Queue up message for processing
117803831d35Sstevel 		 */
117903831d35Sstevel 		(*sg_ecc_taskq_func)(msgp);
118003831d35Sstevel 		return (0);
118103831d35Sstevel 
118203831d35Sstevel 	case PLAT_ECC_CAPABILITY_MESSAGE:
118303831d35Sstevel 		/*
118403831d35Sstevel 		 * For indictment and capability messages, we've already gone
118503831d35Sstevel 		 * through the taskq, so we can call the mailbox routine
118603831d35Sstevel 		 * directly.  Find the symbol for the routine that sends
118703831d35Sstevel 		 * the mailbox msg
118803831d35Sstevel 		 */
118903831d35Sstevel 		msgp->ecc_resp.msg_len = (int)msg_size;
119003831d35Sstevel 		msgp->ecc_resp.msg_buf = (caddr_t)kmem_zalloc(msg_size,
119103831d35Sstevel 		    sleep_flag);
119203831d35Sstevel 		/* FALLTHRU */
119303831d35Sstevel 
119403831d35Sstevel 	case PLAT_ECC_INDICTMENT_MESSAGE:
119503831d35Sstevel 	case PLAT_ECC_INDICTMENT2_MESSAGE:
119603831d35Sstevel 		return ((*sg_ecc_mbox_func)(msgp));
119703831d35Sstevel 
119803831d35Sstevel 	case PLAT_ECC_DIMM_SID_MESSAGE:
119903831d35Sstevel 		msgp->ecc_resp.msg_len = sizeof (plat_dimm_sid_board_data_t);
120003831d35Sstevel 		msgp->ecc_resp.msg_buf = (caddr_t)kmem_zalloc(
120103831d35Sstevel 		    sizeof (plat_dimm_sid_board_data_t), sleep_flag);
120203831d35Sstevel 
120303831d35Sstevel 		return ((*sg_ecc_mbox_func)(msgp));
120403831d35Sstevel 
120503831d35Sstevel 	default:
120603831d35Sstevel 		ASSERT(0);
120703831d35Sstevel 		return (EINVAL);
120803831d35Sstevel 	}
120903831d35Sstevel }
121003831d35Sstevel 
121103831d35Sstevel /*
121203831d35Sstevel  * m is redundant on serengeti as the multiplyer is always 4
121303831d35Sstevel  */
121403831d35Sstevel /*ARGSUSED*/
121503831d35Sstevel int
plat_make_fru_cpuid(int sb,int m,int proc)121603831d35Sstevel plat_make_fru_cpuid(int sb, int m, int proc)
121703831d35Sstevel {
121803831d35Sstevel 	return (MAKE_CPUID(sb, proc));
121903831d35Sstevel }
122003831d35Sstevel 
122103831d35Sstevel /*
122203831d35Sstevel  * board number for a given proc
122303831d35Sstevel  */
122403831d35Sstevel int
plat_make_fru_boardnum(int proc)122503831d35Sstevel plat_make_fru_boardnum(int proc)
122603831d35Sstevel {
122703831d35Sstevel 	return (SG_PORTID_TO_BOARD_NUM(proc));
122803831d35Sstevel }
122903831d35Sstevel 
123003831d35Sstevel static
123103831d35Sstevel void
cpu_sgn_update(ushort_t sig,uchar_t state,uchar_t sub_state,int cpuid)123203831d35Sstevel cpu_sgn_update(ushort_t sig, uchar_t state, uchar_t sub_state, int cpuid)
123303831d35Sstevel {
123403831d35Sstevel 	uint32_t signature = CPU_SIG_BLD(sig, state, sub_state);
123503831d35Sstevel 	sig_state_t current_sgn;
123603831d35Sstevel 	int i;
123703831d35Sstevel 
123803831d35Sstevel 	if (iosram_write_ptr == NULL) {
123903831d35Sstevel 		/*
124003831d35Sstevel 		 * If the IOSRAM write pointer isn't set, we won't be able
124103831d35Sstevel 		 * to write signatures to ANYTHING, so we may as well just
124203831d35Sstevel 		 * write out an error message (if desired) and exit this
124303831d35Sstevel 		 * routine now...
124403831d35Sstevel 		 */
124503831d35Sstevel 		DCMNERR(CE_WARN,
124603831d35Sstevel 		    "cpu_sgn_update: iosram_write() not found;"
124703831d35Sstevel 		    " cannot write signature 0x%x for CPU(s) or domain\n",
124803831d35Sstevel 		    signature);
124903831d35Sstevel 		return;
125003831d35Sstevel 	}
125103831d35Sstevel 
125203831d35Sstevel 
125303831d35Sstevel 	/*
125403831d35Sstevel 	 * Differentiate a panic reboot from a non-panic reboot in the
125503831d35Sstevel 	 * setting of the substate of the signature.
125603831d35Sstevel 	 *
125703831d35Sstevel 	 * If the new substate is REBOOT and we're rebooting due to a panic,
125803831d35Sstevel 	 * then set the new substate to a special value indicating a panic
125903831d35Sstevel 	 * reboot, SIGSUBST_PANIC_REBOOT.
126003831d35Sstevel 	 *
126103831d35Sstevel 	 * A panic reboot is detected by a current (previous) domain signature
126203831d35Sstevel 	 * state of SIGST_EXIT, and a new signature substate of SIGSUBST_REBOOT.
126303831d35Sstevel 	 * The domain signature state SIGST_EXIT is used as the panic flow
126403831d35Sstevel 	 * progresses.
126503831d35Sstevel 	 *
126603831d35Sstevel 	 * At the end of the panic flow, the reboot occurs but we should now
126703831d35Sstevel 	 * one that was involuntary, something that may be quite useful to know
126803831d35Sstevel 	 * at OBP level.
126903831d35Sstevel 	 */
127003831d35Sstevel 	if (sub_state == SIGSUBST_REBOOT) {
127103831d35Sstevel 		if (iosram_read_ptr == NULL) {
127203831d35Sstevel 			DCMNERR(CE_WARN,
127303831d35Sstevel 			    "cpu_sgn_update: iosram_read() not found;"
127403831d35Sstevel 			    " could not check current domain signature\n");
127503831d35Sstevel 		} else {
127603831d35Sstevel 			(void) (*iosram_read_ptr)(SBBC_SIGBLCK_KEY,
1277986fd29aSsetje 			    SG_SGNBLK_DOMAINSIG_OFFSET,
1278986fd29aSsetje 			    (char *)&current_sgn, sizeof (current_sgn));
127903831d35Sstevel 			if (current_sgn.state_t.state == SIGST_EXIT)
128003831d35Sstevel 				signature = CPU_SIG_BLD(sig, state,
1281986fd29aSsetje 				    SIGSUBST_PANIC_REBOOT);
128203831d35Sstevel 		}
128303831d35Sstevel 	}
128403831d35Sstevel 
128503831d35Sstevel 	/*
128603831d35Sstevel 	 * cpuid == -1 indicates that the operation applies to all cpus.
128703831d35Sstevel 	 */
128803831d35Sstevel 	if (cpuid >= 0) {
128903831d35Sstevel 		(void) (*iosram_write_ptr)(SBBC_SIGBLCK_KEY,
1290986fd29aSsetje 		    SG_SGNBLK_CPUSIG_OFFSET(cpuid), (char *)&signature,
1291986fd29aSsetje 		    sizeof (signature));
129203831d35Sstevel 	} else {
129303831d35Sstevel 		for (i = 0; i < NCPU; i++) {
129403831d35Sstevel 			if (cpu[i] == NULL || !(cpu[i]->cpu_flags &
1295986fd29aSsetje 			    (CPU_EXISTS|CPU_QUIESCED))) {
129603831d35Sstevel 				continue;
129703831d35Sstevel 			}
129803831d35Sstevel 			(void) (*iosram_write_ptr)(SBBC_SIGBLCK_KEY,
1299986fd29aSsetje 			    SG_SGNBLK_CPUSIG_OFFSET(i), (char *)&signature,
1300986fd29aSsetje 			    sizeof (signature));
130103831d35Sstevel 		}
130203831d35Sstevel 	}
130303831d35Sstevel 
130403831d35Sstevel 	if (state == SIGST_OFFLINE || state == SIGST_DETACHED) {
130503831d35Sstevel 		return;
130603831d35Sstevel 	}
130703831d35Sstevel 
130803831d35Sstevel 	(void) (*iosram_write_ptr)(SBBC_SIGBLCK_KEY,
1309986fd29aSsetje 	    SG_SGNBLK_DOMAINSIG_OFFSET, (char *)&signature,
1310986fd29aSsetje 	    sizeof (signature));
131103831d35Sstevel }
131203831d35Sstevel 
131303831d35Sstevel void
startup_platform(void)131403831d35Sstevel startup_platform(void)
131503831d35Sstevel {
131603831d35Sstevel }
131703831d35Sstevel 
131803831d35Sstevel /*
131903831d35Sstevel  * A routine to convert a number (represented as a string) to
132003831d35Sstevel  * the integer value it represents.
132103831d35Sstevel  */
132203831d35Sstevel 
132303831d35Sstevel static int
isdigit(int ch)132403831d35Sstevel isdigit(int ch)
132503831d35Sstevel {
132603831d35Sstevel 	return (ch >= '0' && ch <= '9');
132703831d35Sstevel }
132803831d35Sstevel 
132903831d35Sstevel #define	isspace(c)	((c) == ' ' || (c) == '\t' || (c) == '\n')
133003831d35Sstevel 
133103831d35Sstevel static int
strtoi(char * p,char ** pos)133203831d35Sstevel strtoi(char *p, char **pos)
133303831d35Sstevel {
133403831d35Sstevel 	int n;
133503831d35Sstevel 	int c, neg = 0;
133603831d35Sstevel 
133703831d35Sstevel 	if (!isdigit(c = *p)) {
133803831d35Sstevel 		while (isspace(c))
133903831d35Sstevel 			c = *++p;
134003831d35Sstevel 		switch (c) {
134103831d35Sstevel 			case '-':
134203831d35Sstevel 				neg++;
134303831d35Sstevel 				/* FALLTHROUGH */
134403831d35Sstevel 			case '+':
134503831d35Sstevel 			c = *++p;
134603831d35Sstevel 		}
134703831d35Sstevel 		if (!isdigit(c)) {
134803831d35Sstevel 			if (pos != NULL)
134903831d35Sstevel 				*pos = p;
135003831d35Sstevel 			return (0);
135103831d35Sstevel 		}
135203831d35Sstevel 	}
135303831d35Sstevel 	for (n = '0' - c; isdigit(c = *++p); ) {
135403831d35Sstevel 		n *= 10; /* two steps to avoid unnecessary overflow */
135503831d35Sstevel 		n += '0' - c; /* accum neg to avoid surprises at MAX */
135603831d35Sstevel 	}
135703831d35Sstevel 	if (pos != NULL)
135803831d35Sstevel 		*pos = p;
135903831d35Sstevel 	return (neg ? n : -n);
136003831d35Sstevel }
136103831d35Sstevel 
136203831d35Sstevel /*
136303831d35Sstevel  * Get the three parts of the Serengeti PROM version.
136403831d35Sstevel  * Used for feature readiness tests.
136503831d35Sstevel  *
136603831d35Sstevel  * Return 0 if version extracted successfully, -1 otherwise.
136703831d35Sstevel  */
136803831d35Sstevel 
136903831d35Sstevel int
sg_get_prom_version(int * sysp,int * intfp,int * bldp)137003831d35Sstevel sg_get_prom_version(int *sysp, int *intfp, int *bldp)
137103831d35Sstevel {
137203831d35Sstevel 	int plen;
137303831d35Sstevel 	char vers[512];
137403831d35Sstevel 	static pnode_t node;
137503831d35Sstevel 	static char version[] = "version";
137603831d35Sstevel 	char *verp, *ep;
137703831d35Sstevel 
137803831d35Sstevel 	node = prom_finddevice("/openprom");
137903831d35Sstevel 	if (node == OBP_BADNODE)
138003831d35Sstevel 		return (-1);
138103831d35Sstevel 
138203831d35Sstevel 	plen = prom_getproplen(node, version);
138303831d35Sstevel 	if (plen <= 0 || plen >= sizeof (vers))
138403831d35Sstevel 		return (-1);
138503831d35Sstevel 	(void) prom_getprop(node, version, vers);
138603831d35Sstevel 	vers[plen] = '\0';
138703831d35Sstevel 
138803831d35Sstevel 	/* Make sure it's an OBP flashprom */
138903831d35Sstevel 	if (vers[0] != 'O' && vers[1] != 'B' && vers[2] != 'P') {
139003831d35Sstevel 		cmn_err(CE_WARN, "sg_get_prom_version: "
139103831d35Sstevel 		    "unknown <version> string in </openprom>\n");
139203831d35Sstevel 		return (-1);
139303831d35Sstevel 	}
139403831d35Sstevel 	verp = &vers[4];
139503831d35Sstevel 
139603831d35Sstevel 	*sysp = strtoi(verp, &ep);
139703831d35Sstevel 	if (ep == verp || *ep != '.')
139803831d35Sstevel 		return (-1);
139903831d35Sstevel 	verp = ep + 1;
140003831d35Sstevel 
140103831d35Sstevel 	*intfp = strtoi(verp, &ep);
140203831d35Sstevel 	if (ep == verp || *ep != '.')
140303831d35Sstevel 		return (-1);
140403831d35Sstevel 	verp = ep + 1;
140503831d35Sstevel 
140603831d35Sstevel 	*bldp = strtoi(verp, &ep);
140703831d35Sstevel 	if (ep == verp || (*ep != '\0' && !isspace(*ep)))
140803831d35Sstevel 		return (-1);
140903831d35Sstevel 	return (0);
141003831d35Sstevel }
141103831d35Sstevel 
141203831d35Sstevel /*
141303831d35Sstevel  * Return 0 if system board Dynamic Reconfiguration
141403831d35Sstevel  * is supported by the firmware, -1 otherwise.
141503831d35Sstevel  */
141603831d35Sstevel int
sg_prom_sb_dr_check(void)141703831d35Sstevel sg_prom_sb_dr_check(void)
141803831d35Sstevel {
141903831d35Sstevel 	static int prom_res = 1;
142003831d35Sstevel 
142103831d35Sstevel 	if (prom_res == 1) {
142203831d35Sstevel 		int sys, intf, bld;
142303831d35Sstevel 		int rv;
142403831d35Sstevel 
142503831d35Sstevel 		rv = sg_get_prom_version(&sys, &intf, &bld);
142603831d35Sstevel 		if (rv == 0 && sys == 5 &&
142703831d35Sstevel 		    (intf >= 12 || (intf == 11 && bld >= 200))) {
142803831d35Sstevel 			prom_res = 0;
142903831d35Sstevel 		} else {
143003831d35Sstevel 			prom_res = -1;
143103831d35Sstevel 		}
143203831d35Sstevel 	}
143303831d35Sstevel 	return (prom_res);
143403831d35Sstevel }
143503831d35Sstevel 
143603831d35Sstevel /*
143703831d35Sstevel  * Return 0 if cPCI Dynamic Reconfiguration
143803831d35Sstevel  * is supported by the firmware, -1 otherwise.
143903831d35Sstevel  */
144003831d35Sstevel int
sg_prom_cpci_dr_check(void)144103831d35Sstevel sg_prom_cpci_dr_check(void)
144203831d35Sstevel {
144303831d35Sstevel 	/*
144403831d35Sstevel 	 * The version check is currently the same as for
144503831d35Sstevel 	 * system boards. Since the two DR sub-systems are
144603831d35Sstevel 	 * independent, this could change.
144703831d35Sstevel 	 */
144803831d35Sstevel 	return (sg_prom_sb_dr_check());
144903831d35Sstevel }
145003831d35Sstevel 
145103831d35Sstevel /*
145203831d35Sstevel  * Our implementation of this KDI op updates the CPU signature in the system
145303831d35Sstevel  * controller.  Note that we set the signature to OBP_SIG, rather than DBG_SIG.
145403831d35Sstevel  * The Forth words we execute will, among other things, transform our OBP_SIG
145503831d35Sstevel  * into DBG_SIG.  They won't function properly if we try to use DBG_SIG.
145603831d35Sstevel  */
145703831d35Sstevel static void
sg_system_claim(void)145803831d35Sstevel sg_system_claim(void)
145903831d35Sstevel {
1460d3d50737SRafael Vanoni 	lbolt_debug_entry();
1461d3d50737SRafael Vanoni 
146203831d35Sstevel 	prom_interpret("sigb-sig! my-sigb-sig!", OBP_SIG, OBP_SIG, 0, 0, 0);
146303831d35Sstevel }
146403831d35Sstevel 
146503831d35Sstevel static void
sg_system_release(void)146603831d35Sstevel sg_system_release(void)
146703831d35Sstevel {
146803831d35Sstevel 	prom_interpret("sigb-sig! my-sigb-sig!", OS_SIG, OS_SIG, 0, 0, 0);
1469d3d50737SRafael Vanoni 
1470d3d50737SRafael Vanoni 	lbolt_debug_return();
147103831d35Sstevel }
147203831d35Sstevel 
147303831d35Sstevel static void
sg_console_claim(void)147403831d35Sstevel sg_console_claim(void)
147503831d35Sstevel {
1476*07d06da5SSurya Prakki 	(void) prom_serengeti_set_console_input(SGCN_OBP_STR);
147703831d35Sstevel }
147803831d35Sstevel 
147903831d35Sstevel static void
sg_console_release(void)148003831d35Sstevel sg_console_release(void)
148103831d35Sstevel {
1482*07d06da5SSurya Prakki 	(void) prom_serengeti_set_console_input(SGCN_CLNT_STR);
148303831d35Sstevel }
148403831d35Sstevel 
148503831d35Sstevel void
plat_kdi_init(kdi_t * kdi)148603831d35Sstevel plat_kdi_init(kdi_t *kdi)
148703831d35Sstevel {
148803831d35Sstevel 	kdi->pkdi_system_claim = sg_system_claim;
148903831d35Sstevel 	kdi->pkdi_system_release = sg_system_release;
149003831d35Sstevel 	kdi->pkdi_console_claim = sg_console_claim;
149103831d35Sstevel 	kdi->pkdi_console_release = sg_console_release;
149203831d35Sstevel }
1493