11708Sstevel /*
21708Sstevel * CDDL HEADER START
31708Sstevel *
41708Sstevel * The contents of this file are subject to the terms of the
51772Sjl139090 * Common Development and Distribution License (the "License").
61772Sjl139090 * You may not use this file except in compliance with the License.
71708Sstevel *
81708Sstevel * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
91708Sstevel * or http://www.opensolaris.org/os/licensing.
101708Sstevel * See the License for the specific language governing permissions
111708Sstevel * and limitations under the License.
121708Sstevel *
131708Sstevel * When distributing Covered Code, include this CDDL HEADER in each
141708Sstevel * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
151708Sstevel * If applicable, add the following below this CDDL HEADER, with the
161708Sstevel * fields enclosed by brackets "[]" replaced with your own identifying
171708Sstevel * information: Portions Copyright [yyyy] [name of copyright owner]
181708Sstevel *
191708Sstevel * CDDL HEADER END
201708Sstevel */
211708Sstevel /*
22*11066Srafael.vanoni@sun.com * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
231708Sstevel * Use is subject to license terms.
241708Sstevel */
251708Sstevel
261708Sstevel #include <sys/param.h>
271708Sstevel #include <sys/systm.h>
281708Sstevel #include <sys/sysmacros.h>
291708Sstevel #include <sys/sunddi.h>
301708Sstevel #include <sys/esunddi.h>
311708Sstevel #include <sys/sunndi.h>
321708Sstevel #include <sys/modctl.h>
331708Sstevel #include <sys/promif.h>
341708Sstevel #include <sys/machparam.h>
351708Sstevel #include <sys/kobj.h>
361708Sstevel #include <sys/cpuvar.h>
371708Sstevel #include <sys/mem_cage.h>
381708Sstevel #include <sys/promif.h>
391708Sstevel #include <sys/promimpl.h>
401708Sstevel #include <sys/platform_module.h>
411708Sstevel #include <sys/errno.h>
421708Sstevel #include <sys/cpu_sgnblk_defs.h>
431708Sstevel #include <sys/iosramio.h>
441708Sstevel #include <sys/domaind.h>
451708Sstevel #include <sys/starcat.h>
461708Sstevel #include <sys/machsystm.h>
471708Sstevel #include <sys/bootconf.h>
481708Sstevel #include <sys/memnode.h>
491708Sstevel #include <vm/vm_dep.h>
501708Sstevel #include <vm/page.h>
511708Sstevel #include <sys/cheetahregs.h>
521708Sstevel #include <sys/plat_ecc_unum.h>
531708Sstevel #include <sys/plat_ecc_dimm.h>
541708Sstevel #include <sys/lgrp.h>
551708Sstevel #include <sys/dr.h>
561708Sstevel #include <sys/post/scat_dcd.h>
571708Sstevel #include <sys/kdi_impl.h>
581708Sstevel #include <sys/iosramreg.h>
591708Sstevel #include <sys/iosramvar.h>
601708Sstevel #include <sys/mc-us3.h>
61*11066Srafael.vanoni@sun.com #include <sys/clock_impl.h>
621708Sstevel
631708Sstevel /* Preallocation of spare tsb's for DR */
641708Sstevel int starcat_tsb_spares = STARCAT_SPARE_TSB_MAX;
651708Sstevel
661708Sstevel /* Set the maximum number of slot0 + slot1 boards. .. for DR */
671708Sstevel int starcat_boards = STARCAT_BDSET_MAX * STARCAT_BDSET_SLOT_MAX;
681708Sstevel
691708Sstevel /* Maximum number of cpus per board... for DR */
701708Sstevel int starcat_cpu_per_board = MAX(STARCAT_SLOT0_CPU_MAX, STARCAT_SLOT1_CPU_MAX);
711708Sstevel
721708Sstevel /* Maximum number of mem-units per board... for DR */
731708Sstevel int starcat_mem_per_board = MAX(STARCAT_SLOT0_MEM_MAX, STARCAT_SLOT1_MEM_MAX);
741708Sstevel
751708Sstevel /* Maximum number of io-units (buses) per board... for DR */
761708Sstevel int starcat_io_per_board = 2 * MAX(STARCAT_SLOT0_IO_MAX, STARCAT_SLOT1_IO_MAX);
771708Sstevel
781708Sstevel /* Preferred minimum cage size (expressed in pages)... for DR */
791708Sstevel pgcnt_t starcat_startup_cage_size = 0;
801708Sstevel
811708Sstevel /* Platform specific function to get unum information */
821708Sstevel int (*p2get_mem_unum)(int, uint64_t, char *, int, int *);
831708Sstevel
841708Sstevel /* Memory for fcode claims. 16k times # maximum possible schizos */
851708Sstevel #define EFCODE_SIZE (STARCAT_BDSET_MAX * 4 * 0x4000)
861708Sstevel int efcode_size = EFCODE_SIZE;
871708Sstevel
881708Sstevel void sgn_update_all_cpus(ushort_t, uchar_t, uchar_t);
891708Sstevel
901708Sstevel /*
911708Sstevel * The IOSRAM driver is loaded in load_platform_drivers() any cpu signature
921708Sstevel * usage prior to that time will have not have a function to call.
931708Sstevel */
941708Sstevel static int (*iosram_rdp)(uint32_t key, uint32_t off, uint32_t len,
951708Sstevel caddr_t dptr) = prom_starcat_iosram_read;
961708Sstevel static int (*iosram_wrp)(uint32_t key, uint32_t off, uint32_t len,
971708Sstevel caddr_t dptr) = prom_starcat_iosram_write;
981708Sstevel
991708Sstevel plat_dimm_sid_board_t domain_dimm_sids[STARCAT_BDSET_MAX];
1001708Sstevel
1011708Sstevel /*
1021708Sstevel * set_platform_max_ncpus should return the maximum number of CPUs that the
1031708Sstevel * platform supports. This function is called from check_cpus() to set the
1041708Sstevel * value of max_ncpus [see PSARC 1997/165 CPU Dynamic Reconfiguration].
1051708Sstevel * Data elements which are allocated based upon max_ncpus are all accessed
1061708Sstevel * via cpu_seqid and not physical IDs. Previously, the value of max_ncpus
1071708Sstevel * was being set to the largest physical ID, which led to boot problems on
1081708Sstevel * systems with less than 1.25GB of memory.
1091708Sstevel */
1101708Sstevel
1111708Sstevel int
set_platform_max_ncpus(void)1121708Sstevel set_platform_max_ncpus(void)
1131708Sstevel {
1141708Sstevel int n;
1151708Sstevel
1161708Sstevel /*
1171708Sstevel * Convert number of slot0 + slot1 boards to number of expander brds
1181708Sstevel * and constrain the value to an architecturally plausible range
1191708Sstevel */
1201708Sstevel n = MAX(starcat_boards, STARCAT_BDSET_MIN * STARCAT_BDSET_SLOT_MAX);
1211708Sstevel n = MIN(n, STARCAT_BDSET_MAX * STARCAT_BDSET_SLOT_MAX);
1221708Sstevel n = (n + STARCAT_BDSET_SLOT_MAX - 1) / STARCAT_BDSET_SLOT_MAX;
1231708Sstevel
1241708Sstevel /* return maximum number of cpus possible on N expander boards */
1251708Sstevel return (n * STARCAT_BDSET_CPU_MAX - STARCAT_SLOT1_CPU_MAX);
1261708Sstevel }
1271708Sstevel
1281708Sstevel int
set_platform_tsb_spares()1291708Sstevel set_platform_tsb_spares()
1301708Sstevel {
1311708Sstevel return (MIN(starcat_tsb_spares, MAX_UPA));
1321708Sstevel }
1331708Sstevel
1341708Sstevel #pragma weak mmu_init_large_pages
1351708Sstevel
1361708Sstevel void
set_platform_defaults(void)1371708Sstevel set_platform_defaults(void)
1381708Sstevel {
1391708Sstevel extern char *tod_module_name;
1401708Sstevel extern int ts_dispatch_extended;
1411708Sstevel extern void cpu_sgn_update(ushort_t, uchar_t, uchar_t, int);
1421708Sstevel extern int tsb_lgrp_affinity;
1431708Sstevel extern int segkmem_reloc;
1441708Sstevel extern void mmu_init_large_pages(size_t);
1451708Sstevel extern int ncpunode; /* number of CPUs detected by OBP */
1461708Sstevel
1471708Sstevel #ifdef DEBUG
1481708Sstevel ce_verbose_memory = 2;
1491708Sstevel ce_verbose_other = 2;
1501708Sstevel #endif
1511708Sstevel
1521708Sstevel /* Set the CPU signature function pointer */
1531708Sstevel cpu_sgn_func = cpu_sgn_update;
1541708Sstevel
1551708Sstevel /* Set appropriate tod module for starcat */
1561708Sstevel ASSERT(tod_module_name == NULL);
1571708Sstevel tod_module_name = "todstarcat";
1581708Sstevel
1591708Sstevel /*
1601708Sstevel * Use the alternate TS dispatch table, which is better
1611708Sstevel * tuned for large servers.
1621708Sstevel */
1631708Sstevel if (ts_dispatch_extended == -1)
1641708Sstevel ts_dispatch_extended = 1;
1651708Sstevel
1661708Sstevel /*
1671708Sstevel * Use lgroup-aware TSB allocations on this platform,
1681708Sstevel * since they are a considerable performance win.
1691708Sstevel */
1701708Sstevel tsb_lgrp_affinity = 1;
1711708Sstevel
1721708Sstevel if ((mmu_page_sizes == max_mmu_page_sizes) &&
1732659Ssusans (mmu_ism_pagesize != DEFAULT_ISM_PAGESIZE)) {
1741708Sstevel if (&mmu_init_large_pages)
1751708Sstevel mmu_init_large_pages(mmu_ism_pagesize);
1761708Sstevel }
1771708Sstevel
1781708Sstevel /*
1791708Sstevel * KPR (kernel page relocation) is supported on this platform.
1801708Sstevel */
1811708Sstevel if (hat_kpr_enabled && kernel_cage_enable && ncpunode >= 32) {
1821708Sstevel segkmem_reloc = 1;
1831708Sstevel cmn_err(CE_NOTE, "!Kernel Page Relocation is ENABLED");
1841708Sstevel } else {
1851708Sstevel cmn_err(CE_NOTE, "!Kernel Page Relocation is DISABLED");
1861708Sstevel }
1871708Sstevel }
1881708Sstevel
1891708Sstevel #ifdef DEBUG
1901708Sstevel pgcnt_t starcat_cage_size_limit;
1911708Sstevel #endif
1921708Sstevel
1931708Sstevel void
set_platform_cage_params(void)1941708Sstevel set_platform_cage_params(void)
1951708Sstevel {
1961708Sstevel extern pgcnt_t total_pages;
1971708Sstevel extern struct memlist *phys_avail;
1981708Sstevel
1991708Sstevel if (kernel_cage_enable) {
2001708Sstevel pgcnt_t preferred_cage_size;
2011708Sstevel
2021708Sstevel preferred_cage_size =
2035048Smb91622 MAX(starcat_startup_cage_size, total_pages / 256);
2041708Sstevel
2051708Sstevel #ifdef DEBUG
2061708Sstevel if (starcat_cage_size_limit)
2071708Sstevel preferred_cage_size = starcat_cage_size_limit;
2081708Sstevel #endif
2091708Sstevel /*
2101708Sstevel * Note: we are assuming that post has load the
2111708Sstevel * whole show in to the high end of memory. Having
2121708Sstevel * taken this leap, we copy the whole of phys_avail
2131708Sstevel * the glist and arrange for the cage to grow
2141708Sstevel * downward (descending pfns).
2151708Sstevel */
2164266Sdp78419 kcage_range_init(phys_avail, KCAGE_DOWN, preferred_cage_size);
2171708Sstevel }
2181708Sstevel
2191708Sstevel if (kcage_on)
2201708Sstevel cmn_err(CE_NOTE, "!DR Kernel Cage is ENABLED");
2211708Sstevel else
2221708Sstevel cmn_err(CE_NOTE, "!DR Kernel Cage is DISABLED");
2231708Sstevel }
2241708Sstevel
2251708Sstevel void
load_platform_modules(void)2261708Sstevel load_platform_modules(void)
2271708Sstevel {
2281708Sstevel if (modload("misc", "pcihp") < 0) {
2291708Sstevel cmn_err(CE_NOTE, "pcihp driver failed to load");
2301708Sstevel }
2311708Sstevel }
2321708Sstevel
2331708Sstevel /*
2341708Sstevel * Starcat does not support power control of CPUs from the OS.
2351708Sstevel */
2361708Sstevel /*ARGSUSED*/
2371708Sstevel int
plat_cpu_poweron(struct cpu * cp)2381708Sstevel plat_cpu_poweron(struct cpu *cp)
2391708Sstevel {
2401708Sstevel int (*starcat_cpu_poweron)(struct cpu *) = NULL;
2411708Sstevel
2421708Sstevel starcat_cpu_poweron =
2435048Smb91622 (int (*)(struct cpu *))kobj_getsymvalue("drmach_cpu_poweron", 0);
2441708Sstevel
2451708Sstevel if (starcat_cpu_poweron == NULL)
2461708Sstevel return (ENOTSUP);
2471708Sstevel else
2481708Sstevel return ((starcat_cpu_poweron)(cp));
2491708Sstevel }
2501708Sstevel
2511708Sstevel /*ARGSUSED*/
2521708Sstevel int
plat_cpu_poweroff(struct cpu * cp)2531708Sstevel plat_cpu_poweroff(struct cpu *cp)
2541708Sstevel {
2551708Sstevel int (*starcat_cpu_poweroff)(struct cpu *) = NULL;
2561708Sstevel
2571708Sstevel starcat_cpu_poweroff =
2585048Smb91622 (int (*)(struct cpu *))kobj_getsymvalue("drmach_cpu_poweroff", 0);
2591708Sstevel
2601708Sstevel if (starcat_cpu_poweroff == NULL)
2611708Sstevel return (ENOTSUP);
2621708Sstevel else
2631708Sstevel return ((starcat_cpu_poweroff)(cp));
2641708Sstevel }
2651708Sstevel
2661708Sstevel /*
2671708Sstevel * The following are currently private to Starcat DR
2681708Sstevel */
2691708Sstevel int
plat_max_boards()2701708Sstevel plat_max_boards()
2711708Sstevel {
2721708Sstevel return (starcat_boards);
2731708Sstevel }
2741708Sstevel
2751708Sstevel int
plat_max_cpu_units_per_board()2761708Sstevel plat_max_cpu_units_per_board()
2771708Sstevel {
2781708Sstevel return (starcat_cpu_per_board);
2791708Sstevel }
2801708Sstevel
2811708Sstevel int
plat_max_mc_units_per_board()2821708Sstevel plat_max_mc_units_per_board()
2831708Sstevel {
2841708Sstevel return (starcat_mem_per_board); /* each CPU has a memory controller */
2851708Sstevel }
2861708Sstevel
2871708Sstevel int
plat_max_mem_units_per_board()2881708Sstevel plat_max_mem_units_per_board()
2891708Sstevel {
2901708Sstevel return (starcat_mem_per_board);
2911708Sstevel }
2921708Sstevel
2931708Sstevel int
plat_max_io_units_per_board()2941708Sstevel plat_max_io_units_per_board()
2951708Sstevel {
2961708Sstevel return (starcat_io_per_board);
2971708Sstevel }
2981708Sstevel
2991708Sstevel int
plat_max_cpumem_boards(void)3001708Sstevel plat_max_cpumem_boards(void)
3011708Sstevel {
3021708Sstevel return (STARCAT_BDSET_MAX);
3031708Sstevel }
3041708Sstevel
3051708Sstevel int
plat_pfn_to_mem_node(pfn_t pfn)3061708Sstevel plat_pfn_to_mem_node(pfn_t pfn)
3071708Sstevel {
3081708Sstevel return (pfn >> mem_node_pfn_shift);
3091708Sstevel }
3101708Sstevel
3111708Sstevel #define STARCAT_MC_MEMBOARD_SHIFT 37 /* Boards on 128BG boundary */
3121708Sstevel
3131708Sstevel /* ARGSUSED */
3141708Sstevel void
plat_build_mem_nodes(prom_memlist_t * list,size_t nelems)3155648Ssetje plat_build_mem_nodes(prom_memlist_t *list, size_t nelems)
3161708Sstevel {
3171708Sstevel size_t elem;
3181708Sstevel pfn_t basepfn;
3191708Sstevel pgcnt_t npgs;
3201708Sstevel
3211708Sstevel /*
3221708Sstevel * Starcat mem slices are always aligned on a 128GB boundary,
3231708Sstevel * fixed, and limited to one slice per expander due to design
3241708Sstevel * of the centerplane ASICs.
3251708Sstevel */
3261708Sstevel mem_node_pfn_shift = STARCAT_MC_MEMBOARD_SHIFT - MMU_PAGESHIFT;
3271708Sstevel mem_node_physalign = 0;
3281708Sstevel
3291708Sstevel /*
3301708Sstevel * Boot install lists are arranged <addr, len>, <addr, len>, ...
3311708Sstevel */
3325648Ssetje for (elem = 0; elem < nelems; list++, elem++) {
3335648Ssetje basepfn = btop(list->addr);
3345648Ssetje npgs = btop(list->size);
3351708Sstevel mem_node_add_slice(basepfn, basepfn + npgs - 1);
3361708Sstevel }
3371708Sstevel }
3381708Sstevel
3391708Sstevel /*
3401708Sstevel * Find the CPU associated with a slice at boot-time.
3411708Sstevel */
3421708Sstevel void
plat_fill_mc(pnode_t nodeid)3431708Sstevel plat_fill_mc(pnode_t nodeid)
3441708Sstevel {
3451708Sstevel int len;
3461708Sstevel uint64_t mc_addr, mask;
3471708Sstevel uint64_t mc_decode[MAX_BANKS_PER_MC];
3481708Sstevel uint32_t regs[4];
3491708Sstevel int local_mc;
3501708Sstevel int portid;
3511708Sstevel int expnum;
3521708Sstevel int i;
3531708Sstevel
3541708Sstevel /*
3551708Sstevel * Memory address decoding registers
3561708Sstevel * (see Chap 9 of SPARCV9 JSP-1 US-III implementation)
3571708Sstevel */
3581708Sstevel const uint64_t mc_decode_addr[MAX_BANKS_PER_MC] = {
3591708Sstevel 0x400028, 0x400010, 0x400018, 0x400020
3601708Sstevel };
3611708Sstevel
3621708Sstevel /*
3631708Sstevel * Starcat memory controller portid == global CPU id
3641708Sstevel */
3651708Sstevel if ((prom_getprop(nodeid, "portid", (caddr_t)&portid) < 0) ||
3665048Smb91622 (portid == -1))
3671708Sstevel return;
3681708Sstevel
3691708Sstevel expnum = STARCAT_CPUID_TO_EXPANDER(portid);
3701708Sstevel
3711708Sstevel /*
3721708Sstevel * The "reg" property returns 4 32-bit values. The first two are
3731708Sstevel * combined to form a 64-bit address. The second two are for a
3741708Sstevel * 64-bit size, but we don't actually need to look at that value.
3751708Sstevel */
3761708Sstevel len = prom_getproplen(nodeid, "reg");
3771708Sstevel if (len != (sizeof (uint32_t) * 4)) {
3781708Sstevel prom_printf("Warning: malformed 'reg' property\n");
3791708Sstevel return;
3801708Sstevel }
3811708Sstevel if (prom_getprop(nodeid, "reg", (caddr_t)regs) < 0)
3821708Sstevel return;
3831708Sstevel mc_addr = ((uint64_t)regs[0]) << 32;
3841708Sstevel mc_addr |= (uint64_t)regs[1];
3851708Sstevel
3861708Sstevel /*
3871708Sstevel * Figure out whether the memory controller we are examining
3881708Sstevel * belongs to this CPU/CMP or a different one.
3891708Sstevel */
3901708Sstevel if (portid == cpunodes[CPU->cpu_id].portid)
3911708Sstevel local_mc = 1;
3921708Sstevel else
3931708Sstevel local_mc = 0;
3941708Sstevel
3951708Sstevel for (i = 0; i < MAX_BANKS_PER_MC; i++) {
3961708Sstevel
3971708Sstevel mask = mc_decode_addr[i];
3981708Sstevel
3991708Sstevel /*
4001708Sstevel * If the memory controller is local to this CPU, we use
4011708Sstevel * the special ASI to read the decode registers.
4021708Sstevel * Otherwise, we load the values from a magic address in
4031708Sstevel * I/O space.
4041708Sstevel */
4051708Sstevel if (local_mc)
4061708Sstevel mc_decode[i] = lddmcdecode(mask & MC_OFFSET_MASK);
4071708Sstevel else
4081708Sstevel mc_decode[i] = lddphysio((mc_addr | mask));
4091708Sstevel
4101708Sstevel if (mc_decode[i] >> MC_VALID_SHIFT) {
4111708Sstevel uint64_t base = MC_BASE(mc_decode[i]) << PHYS2UM_SHIFT;
4121708Sstevel int sliceid = (base >> STARCAT_MC_MEMBOARD_SHIFT);
4131708Sstevel
4141708Sstevel if (sliceid < max_mem_nodes) {
4151708Sstevel /*
4161708Sstevel * Establish start-of-day mappings of
4171708Sstevel * lgroup platform handles to memnodes.
4181708Sstevel * Handle == Expander Number
4191708Sstevel * Memnode == Fixed 128GB Slice
4201708Sstevel */
4211708Sstevel plat_assign_lgrphand_to_mem_node(expnum,
4221708Sstevel sliceid);
4231708Sstevel }
4241708Sstevel }
4251708Sstevel }
4261708Sstevel }
4271708Sstevel
4281708Sstevel /*
4291708Sstevel * Starcat support for lgroups.
4301708Sstevel *
4311708Sstevel * On Starcat, an lgroup platform handle == expander number.
4321708Sstevel * For split-slot configurations (e.g. slot 0 and slot 1 boards
4331708Sstevel * in different domains) an MCPU board has only remote memory.
4341708Sstevel *
4351708Sstevel * The centerplane logic provides fixed 128GB memory slices
4361708Sstevel * each of which map to a memnode. The initial mapping of
4371708Sstevel * memnodes to lgroup handles is determined at boot time.
4381708Sstevel * A DR addition of memory adds a new mapping. A DR copy-rename
4391708Sstevel * swaps mappings.
4401708Sstevel */
4411708Sstevel
4421708Sstevel /*
4431708Sstevel * Convert board number to expander number.
4441708Sstevel */
4451708Sstevel #define BOARDNUM_2_EXPANDER(b) (b >> 1)
4461708Sstevel
4471708Sstevel /*
4481708Sstevel * Return the number of boards configured with NULL LPA.
4491708Sstevel */
4501708Sstevel static int
check_for_null_lpa(void)4511708Sstevel check_for_null_lpa(void)
4521708Sstevel {
4531708Sstevel gdcd_t *gdcd;
4541708Sstevel uint_t exp, nlpa;
4551708Sstevel
4561708Sstevel /*
4571708Sstevel * Read GDCD from IOSRAM.
4581708Sstevel * If this fails indicate a NULL LPA condition.
4591708Sstevel */
4601708Sstevel if ((gdcd = kmem_zalloc(sizeof (gdcd_t), KM_NOSLEEP)) == NULL)
4611708Sstevel return (EXP_COUNT+1);
4621708Sstevel
4631708Sstevel if ((*iosram_rdp)(GDCD_MAGIC, 0, sizeof (gdcd_t), (caddr_t)gdcd) ||
4641708Sstevel (gdcd->h.dcd_magic != GDCD_MAGIC) ||
4651708Sstevel (gdcd->h.dcd_version != DCD_VERSION)) {
4661708Sstevel kmem_free(gdcd, sizeof (gdcd_t));
4671708Sstevel cmn_err(CE_WARN, "check_for_null_lpa: failed to access GDCD\n");
4681708Sstevel return (EXP_COUNT+2);
4691708Sstevel }
4701708Sstevel
4711708Sstevel /*
4721708Sstevel * Check for NULL LPAs on all slot 0 boards in domain
4731708Sstevel * (i.e. in all expanders marked good for this domain).
4741708Sstevel */
4751708Sstevel nlpa = 0;
4761708Sstevel for (exp = 0; exp < EXP_COUNT; exp++) {
4771708Sstevel if (RSV_GOOD(gdcd->dcd_slot[exp][0].l1ss_rsv) &&
4781708Sstevel (gdcd->dcd_slot[exp][0].l1ss_flags &
4791708Sstevel L1SSFLG_THIS_L1_NULL_PROC_LPA))
4801708Sstevel nlpa++;
4811708Sstevel }
4821708Sstevel
4831708Sstevel kmem_free(gdcd, sizeof (gdcd_t));
4841708Sstevel return (nlpa);
4851708Sstevel }
4861708Sstevel
4871708Sstevel /*
4881708Sstevel * Return the platform handle for the lgroup containing the given CPU
4891708Sstevel *
4901708Sstevel * For Starcat, lgroup platform handle == expander.
4911708Sstevel */
4921708Sstevel
4931708Sstevel extern int mpo_disabled;
4941708Sstevel extern lgrp_handle_t lgrp_default_handle;
4951708Sstevel int null_lpa_boards = -1;
4961708Sstevel
4971708Sstevel lgrp_handle_t
plat_lgrp_cpu_to_hand(processorid_t id)4981708Sstevel plat_lgrp_cpu_to_hand(processorid_t id)
4991708Sstevel {
5001708Sstevel lgrp_handle_t plathand;
5011708Sstevel
5021708Sstevel plathand = STARCAT_CPUID_TO_EXPANDER(id);
5031708Sstevel
5041708Sstevel /*
5051708Sstevel * Return the real platform handle for the CPU until
5061708Sstevel * such time as we know that MPO should be disabled.
5071708Sstevel * At that point, we set the "mpo_disabled" flag to true,
5081708Sstevel * and from that point on, return the default handle.
5091708Sstevel *
5101708Sstevel * By the time we know that MPO should be disabled, the
5111708Sstevel * first CPU will have already been added to a leaf
5121708Sstevel * lgroup, but that's ok. The common lgroup code will
5131708Sstevel * double check that the boot CPU is in the correct place,
5141708Sstevel * and in the case where mpo should be disabled, will move
5151708Sstevel * it to the root if necessary.
5161708Sstevel */
5171708Sstevel if (mpo_disabled) {
5181708Sstevel /* If MPO is disabled, return the default (UMA) handle */
5191708Sstevel plathand = lgrp_default_handle;
5201708Sstevel } else {
5211708Sstevel if (null_lpa_boards > 0) {
5221708Sstevel /* Determine if MPO should be disabled */
5231708Sstevel mpo_disabled = 1;
5241708Sstevel plathand = lgrp_default_handle;
5251708Sstevel }
5261708Sstevel }
5271708Sstevel return (plathand);
5281708Sstevel }
5291708Sstevel
5301708Sstevel /*
5311708Sstevel * Platform specific lgroup initialization
5321708Sstevel */
5331708Sstevel void
plat_lgrp_init(void)5341708Sstevel plat_lgrp_init(void)
5351708Sstevel {
5361708Sstevel extern uint32_t lgrp_expand_proc_thresh;
5371708Sstevel extern uint32_t lgrp_expand_proc_diff;
5381708Sstevel
5391708Sstevel /*
5401708Sstevel * Set tuneables for Starcat architecture
5411708Sstevel *
5421708Sstevel * lgrp_expand_proc_thresh is the minimum load on the lgroups
5431708Sstevel * this process is currently running on before considering
5441708Sstevel * expanding threads to another lgroup.
5451708Sstevel *
5461708Sstevel * lgrp_expand_proc_diff determines how much less the remote lgroup
5471708Sstevel * must be loaded before expanding to it.
5481708Sstevel *
5491708Sstevel * Since remote latencies can be costly, attempt to keep 3 threads
5501708Sstevel * within the same lgroup before expanding to the next lgroup.
5511708Sstevel */
5521708Sstevel lgrp_expand_proc_thresh = LGRP_LOADAVG_THREAD_MAX * 3;
5531708Sstevel lgrp_expand_proc_diff = LGRP_LOADAVG_THREAD_MAX;
5541708Sstevel }
5551708Sstevel
5561708Sstevel /*
5571708Sstevel * Platform notification of lgroup (re)configuration changes
5581708Sstevel */
5591708Sstevel /*ARGSUSED*/
5601708Sstevel void
plat_lgrp_config(lgrp_config_flag_t evt,uintptr_t arg)5611708Sstevel plat_lgrp_config(lgrp_config_flag_t evt, uintptr_t arg)
5621708Sstevel {
5631708Sstevel update_membounds_t *umb;
5641708Sstevel lgrp_config_mem_rename_t lmr;
5651708Sstevel int sbd, tbd;
5661708Sstevel lgrp_handle_t hand, shand, thand;
5671708Sstevel int mnode, snode, tnode;
5681708Sstevel
5691708Sstevel if (mpo_disabled)
5701708Sstevel return;
5711708Sstevel
5721708Sstevel switch (evt) {
5731708Sstevel
5741708Sstevel case LGRP_CONFIG_MEM_ADD:
5751708Sstevel /*
5761708Sstevel * Establish the lgroup handle to memnode translation.
5771708Sstevel */
5781708Sstevel umb = (update_membounds_t *)arg;
5791708Sstevel
5801708Sstevel hand = BOARDNUM_2_EXPANDER(umb->u_board);
5811708Sstevel mnode = plat_pfn_to_mem_node(umb->u_base >> MMU_PAGESHIFT);
5821708Sstevel plat_assign_lgrphand_to_mem_node(hand, mnode);
5831708Sstevel
5841708Sstevel break;
5851708Sstevel
5861708Sstevel case LGRP_CONFIG_MEM_DEL:
5871708Sstevel /* We don't have to do anything */
5881708Sstevel
5891708Sstevel break;
5901708Sstevel
5911708Sstevel case LGRP_CONFIG_MEM_RENAME:
5921708Sstevel /*
5931708Sstevel * During a DR copy-rename operation, all of the memory
5941708Sstevel * on one board is moved to another board -- but the
5951708Sstevel * addresses/pfns and memnodes don't change. This means
5961708Sstevel * the memory has changed locations without changing identity.
5971708Sstevel *
5981708Sstevel * Source is where we are copying from and target is where we
5991708Sstevel * are copying to. After source memnode is copied to target
6001708Sstevel * memnode, the physical addresses of the target memnode are
6011708Sstevel * renamed to match what the source memnode had. Then target
6021708Sstevel * memnode can be removed and source memnode can take its
6031708Sstevel * place.
6041708Sstevel *
6051708Sstevel * To do this, swap the lgroup handle to memnode mappings for
6061708Sstevel * the boards, so target lgroup will have source memnode and
6071708Sstevel * source lgroup will have empty target memnode which is where
6081708Sstevel * its memory will go (if any is added to it later).
6091708Sstevel *
6101708Sstevel * Then source memnode needs to be removed from its lgroup
6111708Sstevel * and added to the target lgroup where the memory was living
6121708Sstevel * but under a different name/memnode. The memory was in the
6131708Sstevel * target memnode and now lives in the source memnode with
6141708Sstevel * different physical addresses even though it is the same
6151708Sstevel * memory.
6161708Sstevel */
6171708Sstevel sbd = arg & 0xffff;
6181708Sstevel tbd = (arg & 0xffff0000) >> 16;
6191708Sstevel shand = BOARDNUM_2_EXPANDER(sbd);
6201708Sstevel thand = BOARDNUM_2_EXPANDER(tbd);
6211708Sstevel snode = plat_lgrphand_to_mem_node(shand);
6221708Sstevel tnode = plat_lgrphand_to_mem_node(thand);
6231708Sstevel
6241708Sstevel plat_assign_lgrphand_to_mem_node(thand, snode);
6251708Sstevel plat_assign_lgrphand_to_mem_node(shand, tnode);
6261708Sstevel
6271708Sstevel lmr.lmem_rename_from = shand;
6281708Sstevel lmr.lmem_rename_to = thand;
6291708Sstevel
6301708Sstevel /*
6311708Sstevel * Remove source memnode of copy rename from its lgroup
6321708Sstevel * and add it to its new target lgroup
6331708Sstevel */
6341708Sstevel lgrp_config(LGRP_CONFIG_MEM_RENAME, (uintptr_t)snode,
6351708Sstevel (uintptr_t)&lmr);
6361708Sstevel
6371708Sstevel break;
6381708Sstevel
6391708Sstevel default:
6401708Sstevel break;
6411708Sstevel }
6421708Sstevel }
6431708Sstevel
6441708Sstevel /*
6451708Sstevel * Return latency between "from" and "to" lgroups
6461708Sstevel *
6471708Sstevel * This latency number can only be used for relative comparison
6481708Sstevel * between lgroups on the running system, cannot be used across platforms,
6491708Sstevel * and may not reflect the actual latency. It is platform and implementation
6501708Sstevel * specific, so platform gets to decide its value. It would be nice if the
6511708Sstevel * number was at least proportional to make comparisons more meaningful though.
6521708Sstevel * NOTE: The numbers below are supposed to be load latencies for uncached
6531708Sstevel * memory divided by 10.
6541708Sstevel */
6551708Sstevel int
plat_lgrp_latency(lgrp_handle_t from,lgrp_handle_t to)6561708Sstevel plat_lgrp_latency(lgrp_handle_t from, lgrp_handle_t to)
6571708Sstevel {
6581708Sstevel /*
6591708Sstevel * Return min remote latency when there are more than two lgroups
6601708Sstevel * (root and child) and getting latency between two different lgroups
6611708Sstevel * or root is involved
6621708Sstevel */
6631708Sstevel if (lgrp_optimizations() && (from != to ||
6641708Sstevel from == LGRP_DEFAULT_HANDLE || to == LGRP_DEFAULT_HANDLE))
6651708Sstevel return (48);
6661708Sstevel else
6671708Sstevel return (28);
6681708Sstevel }
6691708Sstevel
6701708Sstevel /*
6711708Sstevel * Return platform handle for root lgroup
6721708Sstevel */
6731708Sstevel lgrp_handle_t
plat_lgrp_root_hand(void)6741708Sstevel plat_lgrp_root_hand(void)
6751708Sstevel {
6761708Sstevel if (mpo_disabled)
6771708Sstevel return (lgrp_default_handle);
6781708Sstevel
6791708Sstevel return (LGRP_DEFAULT_HANDLE);
6801708Sstevel }
6811708Sstevel
6821708Sstevel /* ARGSUSED */
6831708Sstevel void
plat_freelist_process(int mnode)6841708Sstevel plat_freelist_process(int mnode)
6851708Sstevel {
6861708Sstevel }
6871708Sstevel
6881708Sstevel void
load_platform_drivers(void)6891708Sstevel load_platform_drivers(void)
6901708Sstevel {
6911708Sstevel uint_t tunnel;
6921708Sstevel pnode_t nodeid;
6931708Sstevel dev_info_t *chosen_devi;
6941708Sstevel char chosen_iosram[MAXNAMELEN];
6951708Sstevel
6961708Sstevel /*
6971708Sstevel * Get /chosen node - that's where the tunnel property is
6981708Sstevel */
6991708Sstevel nodeid = prom_chosennode();
7001708Sstevel
7011708Sstevel /*
7021708Sstevel * Get the iosram property from the chosen node.
7031708Sstevel */
7041708Sstevel if (prom_getprop(nodeid, IOSRAM_CHOSEN_PROP, (caddr_t)&tunnel) <= 0) {
7051708Sstevel prom_printf("Unable to get iosram property\n");
7061708Sstevel cmn_err(CE_PANIC, "Unable to get iosram property\n");
7071708Sstevel }
7081708Sstevel
7091708Sstevel if (prom_phandle_to_path((phandle_t)tunnel, chosen_iosram,
7105048Smb91622 sizeof (chosen_iosram)) < 0) {
7111708Sstevel (void) prom_printf("prom_phandle_to_path(0x%x) failed\n",
7125048Smb91622 tunnel);
7131708Sstevel cmn_err(CE_PANIC, "prom_phandle_to_path(0x%x) failed\n",
7145048Smb91622 tunnel);
7151708Sstevel }
7161708Sstevel
7171708Sstevel /*
7181708Sstevel * Attach all driver instances along the iosram's device path
7191708Sstevel */
7201708Sstevel if (i_ddi_attach_hw_nodes("iosram") != DDI_SUCCESS) {
7211708Sstevel cmn_err(CE_WARN, "IOSRAM failed to load\n");
7221708Sstevel }
7231708Sstevel
7241708Sstevel if ((chosen_devi = e_ddi_hold_devi_by_path(chosen_iosram, 0)) == NULL) {
7251708Sstevel (void) prom_printf("e_ddi_hold_devi_by_path(%s) failed\n",
7265048Smb91622 chosen_iosram);
7271708Sstevel cmn_err(CE_PANIC, "e_ddi_hold_devi_by_path(%s) failed\n",
7285048Smb91622 chosen_iosram);
7291708Sstevel }
7301708Sstevel ndi_rele_devi(chosen_devi);
7311708Sstevel
7321708Sstevel /*
7331708Sstevel * iosram driver is now loaded so we need to set our read and
7341708Sstevel * write pointers.
7351708Sstevel */
7361708Sstevel iosram_rdp = (int (*)(uint32_t, uint32_t, uint32_t, caddr_t))
7375048Smb91622 modgetsymvalue("iosram_rd", 0);
7381708Sstevel iosram_wrp = (int (*)(uint32_t, uint32_t, uint32_t, caddr_t))
7395048Smb91622 modgetsymvalue("iosram_wr", 0);
7401708Sstevel
7411708Sstevel /*
7421708Sstevel * Need to check for null proc LPA after IOSRAM driver is loaded
7431708Sstevel * and before multiple lgroups created (when start_other_cpus() called)
7441708Sstevel */
7451708Sstevel null_lpa_boards = check_for_null_lpa();
7461708Sstevel
7471708Sstevel /* load and attach the axq driver */
7481708Sstevel if (i_ddi_attach_hw_nodes("axq") != DDI_SUCCESS) {
7491708Sstevel cmn_err(CE_WARN, "AXQ failed to load\n");
7501708Sstevel }
7511708Sstevel
7521708Sstevel /* load Starcat Solaris Mailbox Client driver */
7531708Sstevel if (modload("misc", "scosmb") < 0) {
7541708Sstevel cmn_err(CE_WARN, "SCOSMB failed to load\n");
7551708Sstevel }
7561708Sstevel
7571708Sstevel /* load the DR driver */
7581708Sstevel if (i_ddi_attach_hw_nodes("dr") != DDI_SUCCESS) {
7591708Sstevel cmn_err(CE_WARN, "dr failed to load");
7601708Sstevel }
7611708Sstevel
7621708Sstevel /*
7631708Sstevel * Load the mc-us3 memory driver.
7641708Sstevel */
7651708Sstevel if (i_ddi_attach_hw_nodes("mc-us3") != DDI_SUCCESS)
7661708Sstevel cmn_err(CE_WARN, "mc-us3 failed to load");
7671708Sstevel else
7681708Sstevel (void) ddi_hold_driver(ddi_name_to_major("mc-us3"));
7691708Sstevel
7701708Sstevel /* Load the schizo pci bus nexus driver. */
7711708Sstevel if (i_ddi_attach_hw_nodes("pcisch") != DDI_SUCCESS)
7721708Sstevel cmn_err(CE_WARN, "pcisch failed to load");
7731708Sstevel
7741708Sstevel plat_ecc_init();
7751708Sstevel }
7761708Sstevel
7771708Sstevel
7781708Sstevel /*
7791708Sstevel * No platform drivers on this platform
7801708Sstevel */
7811708Sstevel char *platform_module_list[] = {
7821708Sstevel (char *)0
7831708Sstevel };
7841708Sstevel
7851708Sstevel
7861708Sstevel /*ARGSUSED*/
7871708Sstevel void
plat_tod_fault(enum tod_fault_type tod_bad)7881708Sstevel plat_tod_fault(enum tod_fault_type tod_bad)
7891708Sstevel {
7901708Sstevel }
7911708Sstevel
7921708Sstevel /*
7931708Sstevel * Update the signature(s) in the IOSRAM's domain data section.
7941708Sstevel */
7951708Sstevel void
cpu_sgn_update(ushort_t sgn,uchar_t state,uchar_t sub_state,int cpuid)7961708Sstevel cpu_sgn_update(ushort_t sgn, uchar_t state, uchar_t sub_state, int cpuid)
7971708Sstevel {
7981708Sstevel sig_state_t new_sgn;
7991708Sstevel sig_state_t current_sgn;
8001708Sstevel
8011708Sstevel /*
8021708Sstevel * If the substate is REBOOT, then check for panic flow
8031708Sstevel */
8041708Sstevel if (sub_state == SIGSUBST_REBOOT) {
8051708Sstevel (*iosram_rdp)(DOMD_MAGIC, DOMD_DSTATE_OFFSET,
8061708Sstevel sizeof (sig_state_t), (caddr_t)¤t_sgn);
8071708Sstevel if (current_sgn.state_t.state == SIGST_EXIT)
8081708Sstevel sub_state = SIGSUBST_PANIC_REBOOT;
8091708Sstevel }
8101708Sstevel
8111708Sstevel /*
8121708Sstevel * cpuid == -1 indicates that the operation applies to all cpus.
8131708Sstevel */
8141708Sstevel if (cpuid < 0) {
8151708Sstevel sgn_update_all_cpus(sgn, state, sub_state);
8161708Sstevel return;
8171708Sstevel }
8181708Sstevel
8191708Sstevel new_sgn.signature = CPU_SIG_BLD(sgn, state, sub_state);
8201708Sstevel (*iosram_wrp)(DOMD_MAGIC,
8211708Sstevel DOMD_CPUSIGS_OFFSET + cpuid * sizeof (sig_state_t),
8221708Sstevel sizeof (sig_state_t), (caddr_t)&new_sgn);
8231708Sstevel
8241708Sstevel /*
8251708Sstevel * Under certain conditions we don't update the signature
8261708Sstevel * of the domain_state.
8271708Sstevel */
8281708Sstevel if ((sgn == OS_SIG) &&
8291708Sstevel ((state == SIGST_OFFLINE) || (state == SIGST_DETACHED)))
8301708Sstevel return;
8311708Sstevel (*iosram_wrp)(DOMD_MAGIC, DOMD_DSTATE_OFFSET, sizeof (sig_state_t),
8321708Sstevel (caddr_t)&new_sgn);
8331708Sstevel }
8341708Sstevel
8351708Sstevel /*
8361708Sstevel * Update the signature(s) in the IOSRAM's domain data section for all CPUs.
8371708Sstevel */
8381708Sstevel void
sgn_update_all_cpus(ushort_t sgn,uchar_t state,uchar_t sub_state)8391708Sstevel sgn_update_all_cpus(ushort_t sgn, uchar_t state, uchar_t sub_state)
8401708Sstevel {
8411708Sstevel sig_state_t new_sgn;
8421708Sstevel int i = 0;
8431708Sstevel
8441708Sstevel new_sgn.signature = CPU_SIG_BLD(sgn, state, sub_state);
8451708Sstevel
8461708Sstevel /*
8471708Sstevel * First update the domain_state signature
8481708Sstevel */
8491708Sstevel (*iosram_wrp)(DOMD_MAGIC, DOMD_DSTATE_OFFSET, sizeof (sig_state_t),
8501708Sstevel (caddr_t)&new_sgn);
8511708Sstevel
8521708Sstevel for (i = 0; i < NCPU; i++) {
8531708Sstevel if (cpu[i] != NULL && (cpu[i]->cpu_flags &
8541708Sstevel (CPU_EXISTS|CPU_QUIESCED))) {
8551708Sstevel (*iosram_wrp)(DOMD_MAGIC,
8561708Sstevel DOMD_CPUSIGS_OFFSET + i * sizeof (sig_state_t),
8571708Sstevel sizeof (sig_state_t), (caddr_t)&new_sgn);
8581708Sstevel }
8591708Sstevel }
8601708Sstevel }
8611708Sstevel
8621708Sstevel ushort_t
get_cpu_sgn(int cpuid)8631708Sstevel get_cpu_sgn(int cpuid)
8641708Sstevel {
8651708Sstevel sig_state_t cpu_sgn;
8661708Sstevel
8671708Sstevel (*iosram_rdp)(DOMD_MAGIC,
8681708Sstevel DOMD_CPUSIGS_OFFSET + cpuid * sizeof (sig_state_t),
8691708Sstevel sizeof (sig_state_t), (caddr_t)&cpu_sgn);
8701708Sstevel
8711708Sstevel return (cpu_sgn.state_t.sig);
8721708Sstevel }
8731708Sstevel
8741708Sstevel uchar_t
get_cpu_sgn_state(int cpuid)8751708Sstevel get_cpu_sgn_state(int cpuid)
8761708Sstevel {
8771708Sstevel sig_state_t cpu_sgn;
8781708Sstevel
8791708Sstevel (*iosram_rdp)(DOMD_MAGIC,
8801708Sstevel DOMD_CPUSIGS_OFFSET + cpuid * sizeof (sig_state_t),
8811708Sstevel sizeof (sig_state_t), (caddr_t)&cpu_sgn);
8821708Sstevel
8831708Sstevel return (cpu_sgn.state_t.state);
8841708Sstevel }
8851708Sstevel
8861708Sstevel
8871708Sstevel /*
8881708Sstevel * Type of argument passed into plat_get_ecache_cpu via ddi_walk_devs
8891708Sstevel * for matching on specific CPU node in device tree
8901708Sstevel */
8911708Sstevel
8921708Sstevel typedef struct {
8931708Sstevel char *jnum; /* output, kmem_alloc'd if successful */
8941708Sstevel int cpuid; /* input, to match cpuid/portid/upa-portid */
8951708Sstevel uint_t dimm; /* input, index into ecache-dimm-label */
8961708Sstevel } plat_ecache_cpu_arg_t;
8971708Sstevel
8981708Sstevel
8991708Sstevel /*
9001708Sstevel * plat_get_ecache_cpu is called repeatedly by ddi_walk_devs with pointers
9011708Sstevel * to device tree nodes (dip) and to a plat_ecache_cpu_arg_t structure (arg).
9021708Sstevel * Returning DDI_WALK_CONTINUE tells ddi_walk_devs to keep going, returning
9031708Sstevel * DDI_WALK_TERMINATE ends the walk. When the node for the specific CPU
9041708Sstevel * being searched for is found, the walk is done. But before returning to
9051708Sstevel * ddi_walk_devs and plat_get_ecacheunum, we grab this CPU's ecache-dimm-label
9061708Sstevel * property and set the jnum member of the plat_ecache_cpu_arg_t structure to
9071708Sstevel * point to the label corresponding to this specific ecache DIMM. It is up
9081708Sstevel * to plat_get_ecacheunum to kmem_free this string.
9091708Sstevel */
9101708Sstevel
9111708Sstevel static int
plat_get_ecache_cpu(dev_info_t * dip,void * arg)9121708Sstevel plat_get_ecache_cpu(dev_info_t *dip, void *arg)
9131708Sstevel {
9141708Sstevel char *devtype;
9151708Sstevel plat_ecache_cpu_arg_t *cpuarg;
9161708Sstevel char **dimm_labels;
9171708Sstevel uint_t numlabels;
9181708Sstevel int portid;
9191708Sstevel
9201708Sstevel /*
9211708Sstevel * Check device_type, must be "cpu"
9221708Sstevel */
9231708Sstevel
9241708Sstevel if (ddi_prop_lookup_string(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
9255048Smb91622 "device_type", &devtype) != DDI_PROP_SUCCESS)
9261708Sstevel return (DDI_WALK_CONTINUE);
9271708Sstevel
9281708Sstevel if (strcmp(devtype, "cpu")) {
9291708Sstevel ddi_prop_free((void *)devtype);
9301708Sstevel return (DDI_WALK_CONTINUE);
9311708Sstevel }
9321708Sstevel
9331708Sstevel ddi_prop_free((void *)devtype);
9341708Sstevel
9351708Sstevel /*
9361708Sstevel * Check cpuid, portid, upa-portid (in that order), must
9371708Sstevel * match the cpuid being sought
9381708Sstevel */
9391708Sstevel
9401708Sstevel portid = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
9415048Smb91622 DDI_PROP_DONTPASS, "cpuid", -1);
9421708Sstevel
9431708Sstevel if (portid == -1)
9441708Sstevel portid = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
9455048Smb91622 DDI_PROP_DONTPASS, "portid", -1);
9461708Sstevel
9471708Sstevel if (portid == -1)
9481708Sstevel portid = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
9495048Smb91622 DDI_PROP_DONTPASS, "upa-portid", -1);
9501708Sstevel
9511708Sstevel cpuarg = (plat_ecache_cpu_arg_t *)arg;
9521708Sstevel
9531708Sstevel if (portid != cpuarg->cpuid)
9541708Sstevel return (DDI_WALK_CONTINUE);
9551708Sstevel
9561708Sstevel /*
9571708Sstevel * Found the right CPU, fetch ecache-dimm-label property
9581708Sstevel */
9591708Sstevel
9601708Sstevel if (ddi_prop_lookup_string_array(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
9615048Smb91622 "ecache-dimm-label", &dimm_labels, &numlabels)
9625048Smb91622 != DDI_PROP_SUCCESS) {
9631708Sstevel #ifdef DEBUG
9641708Sstevel cmn_err(CE_NOTE, "cpuid=%d missing ecache-dimm-label property",
9655048Smb91622 portid);
9661708Sstevel #endif /* DEBUG */
9671708Sstevel return (DDI_WALK_TERMINATE);
9681708Sstevel }
9691708Sstevel
9701708Sstevel if (cpuarg->dimm < numlabels) {
9715048Smb91622 cpuarg->jnum = kmem_alloc(strlen(dimm_labels[cpuarg->dimm]) + 1,
9725048Smb91622 KM_SLEEP);
9731708Sstevel if (cpuarg->jnum != (char *)NULL)
9741708Sstevel (void) strcpy(cpuarg->jnum, dimm_labels[cpuarg->dimm]);
9751708Sstevel #ifdef DEBUG
9761708Sstevel else
9771708Sstevel cmn_err(CE_WARN,
9785048Smb91622 "cannot kmem_alloc for ecache dimm label");
9791708Sstevel #endif /* DEBUG */
9801708Sstevel }
9811708Sstevel
9821708Sstevel ddi_prop_free((void *)dimm_labels);
9831708Sstevel return (DDI_WALK_TERMINATE);
9841708Sstevel }
9851708Sstevel
9861708Sstevel
9871708Sstevel /*
9881708Sstevel * Bit 4 of physical address indicates ecache 0 or 1
9891708Sstevel */
9901708Sstevel
9911708Sstevel #define ECACHE_DIMM_MASK 0x10
9921708Sstevel
9931708Sstevel /*
9941708Sstevel * plat_get_ecacheunum is called to generate the unum for an ecache error.
9951708Sstevel * After some initialization, nearly all of the work is done by ddi_walk_devs
9961708Sstevel * and plat_get_ecache_cpu.
9971708Sstevel */
9981708Sstevel
9991708Sstevel int
plat_get_ecacheunum(int cpuid,unsigned long long physaddr,char * buf,int buflen,int * ustrlen)10001708Sstevel plat_get_ecacheunum(int cpuid, unsigned long long physaddr, char *buf,
10011708Sstevel int buflen, int *ustrlen)
10021708Sstevel {
10031708Sstevel plat_ecache_cpu_arg_t findcpu;
10041708Sstevel uint_t expander, slot, proc;
10051708Sstevel
10061708Sstevel findcpu.jnum = (char *)NULL;
10071708Sstevel findcpu.cpuid = cpuid;
10085048Smb91622
10095048Smb91622 /*
10105048Smb91622 * Bit 4 of physaddr equal 0 maps to E0 and 1 maps to E1
10115048Smb91622 * except for Panther and Jaguar where it indicates the reverse
10125048Smb91622 */
10135048Smb91622 if (IS_PANTHER(cpunodes[CPU->cpu_id].implementation) ||
10145048Smb91622 IS_JAGUAR(cpunodes[CPU->cpu_id].implementation))
10155048Smb91622 findcpu.dimm = (physaddr & ECACHE_DIMM_MASK) ? 0 : 1;
10165048Smb91622 else
10175048Smb91622 findcpu.dimm = (physaddr & ECACHE_DIMM_MASK) ? 1 : 0;
10181708Sstevel
10191708Sstevel /*
10201708Sstevel * Walk the device tree, find this specific CPU, and get the label
10211708Sstevel * for this ecache, returned here in findcpu.jnum
10221708Sstevel */
10231708Sstevel
10241708Sstevel ddi_walk_devs(ddi_root_node(), plat_get_ecache_cpu, (void *)&findcpu);
10251708Sstevel
10261708Sstevel if (findcpu.jnum == (char *)NULL)
10271708Sstevel return (-1);
10281708Sstevel
10291708Sstevel expander = STARCAT_CPUID_TO_EXPANDER(cpuid);
10301708Sstevel slot = STARCAT_CPUID_TO_BOARDSLOT(cpuid);
10311708Sstevel
10321708Sstevel /*
10331708Sstevel * STARCAT_CPUID_TO_PORTID clears the CoreID bit so that
10341708Sstevel * STARCAT_CPUID_TO_AGENT will return a physical proc (0 - 3).
10351708Sstevel */
10361708Sstevel proc = STARCAT_CPUID_TO_AGENT(STARCAT_CPUID_TO_PORTID(cpuid));
10371708Sstevel
10381708Sstevel /*
10391708Sstevel * NOTE: Any modifications to the snprintf() call below will require
10401708Sstevel * changing plat_log_fruid_error() as well!
10411708Sstevel */
10421708Sstevel (void) snprintf(buf, buflen, "%s%u/P%u/E%u J%s", (slot ? "IO" : "SB"),
10435048Smb91622 expander, proc, findcpu.dimm, findcpu.jnum);
10441708Sstevel
10451708Sstevel *ustrlen = strlen(buf);
10461708Sstevel
10471708Sstevel kmem_free(findcpu.jnum, strlen(findcpu.jnum) + 1);
10481708Sstevel
10491708Sstevel return (0);
10501708Sstevel }
10511708Sstevel
10521708Sstevel /*ARGSUSED*/
10531708Sstevel 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)10541708Sstevel plat_get_mem_unum(int synd_code, uint64_t flt_addr, int flt_bus_id,
10551708Sstevel int flt_in_memory, ushort_t flt_status, char *buf, int buflen, int *lenp)
10561708Sstevel {
10571708Sstevel int ret;
10581708Sstevel
10591708Sstevel /*
10601708Sstevel * check if it's a Memory or an Ecache error.
10611708Sstevel */
10621708Sstevel if (flt_in_memory) {
10631708Sstevel if (p2get_mem_unum != NULL) {
10641708Sstevel return (p2get_mem_unum(synd_code, P2ALIGN(flt_addr, 8),
10655048Smb91622 buf, buflen, lenp));
10661708Sstevel } else {
10671708Sstevel return (ENOTSUP);
10681708Sstevel }
10691708Sstevel } else if (flt_status & ECC_ECACHE) {
10701708Sstevel if ((ret = plat_get_ecacheunum(flt_bus_id,
10711708Sstevel P2ALIGN(flt_addr, 8), buf, buflen, lenp)) != 0)
10721708Sstevel return (EIO);
10731708Sstevel } else {
10741708Sstevel return (ENOTSUP);
10751708Sstevel }
10761708Sstevel
10771708Sstevel return (ret);
10781708Sstevel }
10791708Sstevel
10801708Sstevel static int (*ecc_mailbox_msg_func)(plat_ecc_message_type_t, void *) = NULL;
10811708Sstevel
10821708Sstevel /*
10831708Sstevel * To keep OS mailbox handling localized, all we do is forward the call to the
10841708Sstevel * scosmb module (if it is available).
10851708Sstevel */
10861708Sstevel int
plat_send_ecc_mailbox_msg(plat_ecc_message_type_t msg_type,void * datap)10871708Sstevel plat_send_ecc_mailbox_msg(plat_ecc_message_type_t msg_type, void *datap)
10881708Sstevel {
10891708Sstevel /*
10901708Sstevel * find the symbol for the mailbox sender routine in the scosmb module
10911708Sstevel */
10921708Sstevel if (ecc_mailbox_msg_func == NULL)
10931708Sstevel ecc_mailbox_msg_func = (int (*)(plat_ecc_message_type_t,
10941708Sstevel void *))modgetsymvalue("scosmb_log_ecc_error", 0);
10951708Sstevel
10961708Sstevel /*
10971708Sstevel * If the symbol was found, call it. Otherwise, there is not much
10981708Sstevel * else we can do and console messages will have to suffice.
10991708Sstevel */
11001708Sstevel if (ecc_mailbox_msg_func)
11011708Sstevel return ((*ecc_mailbox_msg_func)(msg_type, datap));
11021708Sstevel else
11031708Sstevel return (ENODEV);
11041708Sstevel }
11051708Sstevel
11061708Sstevel int
plat_make_fru_cpuid(int sb,int m,int proc)11071708Sstevel plat_make_fru_cpuid(int sb, int m, int proc)
11081708Sstevel {
11091708Sstevel return (MAKE_CPUID(sb, m, proc));
11101708Sstevel }
11111708Sstevel
11121708Sstevel /*
11131708Sstevel * board number for a given proc
11141708Sstevel */
11151708Sstevel int
plat_make_fru_boardnum(int proc)11161708Sstevel plat_make_fru_boardnum(int proc)
11171708Sstevel {
11181708Sstevel return (STARCAT_CPUID_TO_EXPANDER(proc));
11191708Sstevel }
11201708Sstevel
11211708Sstevel /*
11221708Sstevel * This platform hook gets called from mc_add_mem_unum_label() in the mc-us3
11231708Sstevel * driver giving each platform the opportunity to add platform
11241708Sstevel * specific label information to the unum for ECC error logging purposes.
11251708Sstevel */
11261708Sstevel void
plat_add_mem_unum_label(char * unum,int mcid,int bank,int dimm)11271708Sstevel plat_add_mem_unum_label(char *unum, int mcid, int bank, int dimm)
11281708Sstevel {
11291708Sstevel char new_unum[UNUM_NAMLEN];
11301708Sstevel uint_t expander = STARCAT_CPUID_TO_EXPANDER(mcid);
11311708Sstevel uint_t slot = STARCAT_CPUID_TO_BOARDSLOT(mcid);
11321708Sstevel
11331708Sstevel /*
11341708Sstevel * STARCAT_CPUID_TO_PORTID clears the CoreID bit so that
11351708Sstevel * STARCAT_CPUID_TO_AGENT will return a physical proc (0 - 3).
11361708Sstevel */
11371708Sstevel uint_t proc = STARCAT_CPUID_TO_AGENT(STARCAT_CPUID_TO_PORTID(mcid));
11381708Sstevel
11391708Sstevel /*
11401708Sstevel * NOTE: Any modifications to the two sprintf() calls below will
11411708Sstevel * require changing plat_log_fruid_error() as well!
11421708Sstevel */
11431708Sstevel if (dimm == -1)
11441708Sstevel (void) snprintf(new_unum, UNUM_NAMLEN, "%s%u/P%u/B%d %s",
11455048Smb91622 (slot ? "IO" : "SB"), expander, proc, (bank & 0x1), unum);
11461708Sstevel else
11471708Sstevel (void) snprintf(new_unum, UNUM_NAMLEN, "%s%u/P%u/B%d/D%d %s",
11485048Smb91622 (slot ? "IO" : "SB"), expander,
11495048Smb91622 proc, (bank & 0x1), (dimm & 0x3), unum);
11501708Sstevel
11511708Sstevel (void) strcpy(unum, new_unum);
11521708Sstevel }
11531708Sstevel
11541708Sstevel int
plat_get_cpu_unum(int cpuid,char * buf,int buflen,int * lenp)11551708Sstevel plat_get_cpu_unum(int cpuid, char *buf, int buflen, int *lenp)
11561708Sstevel {
11571708Sstevel int expander = STARCAT_CPUID_TO_EXPANDER(cpuid);
11581708Sstevel int slot = STARCAT_CPUID_TO_BOARDSLOT(cpuid);
11591708Sstevel
11601708Sstevel if (snprintf(buf, buflen, "%s%d", (slot ? "IO" : "SB"), expander)
11611708Sstevel >= buflen) {
11621708Sstevel return (ENOSPC);
11631708Sstevel } else {
11641708Sstevel *lenp = strlen(buf);
11651708Sstevel return (0);
11661708Sstevel }
11671708Sstevel }
11681708Sstevel
11691708Sstevel /*
11701708Sstevel * This routine is used by the data bearing mondo (DMV) initialization
11711708Sstevel * routine to determine the number of hardware and software DMV interrupts
11721708Sstevel * that a platform supports.
11731708Sstevel */
11741708Sstevel void
plat_dmv_params(uint_t * hwint,uint_t * swint)11751708Sstevel plat_dmv_params(uint_t *hwint, uint_t *swint)
11761708Sstevel {
11771708Sstevel *hwint = STARCAT_DMV_HWINT;
11781708Sstevel *swint = 0;
11791708Sstevel }
11801708Sstevel
11811708Sstevel /*
11821708Sstevel * If provided, this function will be called whenever the nodename is updated.
11831708Sstevel * To keep OS mailbox handling localized, all we do is forward the call to the
11841708Sstevel * scosmb module (if it is available).
11851708Sstevel */
11861708Sstevel void
plat_nodename_set(void)11871708Sstevel plat_nodename_set(void)
11881708Sstevel {
11891708Sstevel void (*nodename_update_func)(uint64_t) = NULL;
11901708Sstevel
11911708Sstevel /*
11921708Sstevel * find the symbol for the nodename update routine in the scosmb module
11931708Sstevel */
11941708Sstevel nodename_update_func = (void (*)(uint64_t))
11951708Sstevel modgetsymvalue("scosmb_update_nodename", 0);
11961708Sstevel
11971708Sstevel /*
11981708Sstevel * If the symbol was found, call it. Otherwise, log a note (but not to
11991708Sstevel * the console).
12001708Sstevel */
12011708Sstevel if (nodename_update_func != NULL) {
12021708Sstevel nodename_update_func(0);
12031708Sstevel } else {
12041708Sstevel cmn_err(CE_NOTE,
12051708Sstevel "!plat_nodename_set: scosmb_update_nodename not found\n");
12061708Sstevel }
12071708Sstevel }
12081708Sstevel
12091708Sstevel caddr_t efcode_vaddr = NULL;
12101708Sstevel caddr_t efcode_paddr = NULL;
12111708Sstevel /*
12121708Sstevel * Preallocate enough memory for fcode claims.
12131708Sstevel */
12141708Sstevel
12151708Sstevel caddr_t
efcode_alloc(caddr_t alloc_base)12161708Sstevel efcode_alloc(caddr_t alloc_base)
12171708Sstevel {
12181708Sstevel caddr_t efcode_alloc_base = (caddr_t)roundup((uintptr_t)alloc_base,
12191708Sstevel MMU_PAGESIZE);
12201708Sstevel caddr_t vaddr;
12211708Sstevel
12221708Sstevel /*
12231708Sstevel * allocate the physical memory schizo fcode.
12241708Sstevel */
12251708Sstevel if ((vaddr = (caddr_t)BOP_ALLOC(bootops, efcode_alloc_base,
12261708Sstevel efcode_size, MMU_PAGESIZE)) == NULL)
12271708Sstevel cmn_err(CE_PANIC, "Cannot allocate Efcode Memory");
12281708Sstevel
12291708Sstevel efcode_vaddr = vaddr;
12301708Sstevel
12311708Sstevel return (efcode_alloc_base + efcode_size);
12321708Sstevel }
12331708Sstevel
12341708Sstevel caddr_t
plat_startup_memlist(caddr_t alloc_base)12351772Sjl139090 plat_startup_memlist(caddr_t alloc_base)
12361708Sstevel {
12371708Sstevel caddr_t tmp_alloc_base;
12381708Sstevel
12391708Sstevel tmp_alloc_base = efcode_alloc(alloc_base);
12401708Sstevel tmp_alloc_base = (caddr_t)roundup((uintptr_t)tmp_alloc_base,
12415048Smb91622 ecache_alignsize);
12421708Sstevel return (tmp_alloc_base);
12431708Sstevel }
12441708Sstevel
12451708Sstevel /*
12461708Sstevel * This is a helper function to determine if a given
12471708Sstevel * node should be considered for a dr operation according
12481708Sstevel * to predefined dr names. This is accomplished using
12491708Sstevel * a function defined in drmach module. The drmach module
12501708Sstevel * owns the definition of dr allowable names.
12511708Sstevel * Formal Parameter: The name of a device node.
12521708Sstevel * Expected Return Value: -1, device node name does not map to a valid dr name.
12531708Sstevel * A value greater or equal to 0, name is valid.
12541708Sstevel */
12551708Sstevel int
starcat_dr_name(char * name)12561708Sstevel starcat_dr_name(char *name)
12571708Sstevel {
12581708Sstevel int (*drmach_name2type)(char *) = NULL;
12591708Sstevel
12601708Sstevel /* Get a pointer to helper function in the dramch module. */
12611708Sstevel drmach_name2type =
12621708Sstevel (int (*)(char *))kobj_getsymvalue("drmach_name2type_idx", 0);
12631708Sstevel
12641708Sstevel if (drmach_name2type == NULL)
12651708Sstevel return (-1);
12661708Sstevel
12671708Sstevel return ((*drmach_name2type)(name));
12681708Sstevel }
12691708Sstevel
12701708Sstevel void
startup_platform(void)12711708Sstevel startup_platform(void)
12721708Sstevel {
12735834Spt157919 /* set per platform constants for mutex backoff */
12745834Spt157919 mutex_backoff_base = 2;
12755834Spt157919 mutex_cap_factor = 64;
12761708Sstevel }
12771708Sstevel
12781708Sstevel /*
12791708Sstevel * KDI functions - used by the in-situ kernel debugger (kmdb) to perform
12801708Sstevel * platform-specific operations. These functions execute when the world is
12811708Sstevel * stopped, and as such cannot make any blocking calls, hold locks, etc.
12821708Sstevel * promif functions are a special case, and may be used.
12831708Sstevel */
12841708Sstevel
12851708Sstevel static void
starcat_system_claim(void)12861708Sstevel starcat_system_claim(void)
12871708Sstevel {
1288*11066Srafael.vanoni@sun.com lbolt_debug_entry();
1289*11066Srafael.vanoni@sun.com
12901708Sstevel prom_interpret("sigb-sig! my-sigb-sig!", OBP_SIG, OBP_SIG, 0, 0, 0);
12911708Sstevel }
12921708Sstevel
12931708Sstevel static void
starcat_system_release(void)12941708Sstevel starcat_system_release(void)
12951708Sstevel {
12961708Sstevel prom_interpret("sigb-sig! my-sigb-sig!", OS_SIG, OS_SIG, 0, 0, 0);
1297*11066Srafael.vanoni@sun.com
1298*11066Srafael.vanoni@sun.com lbolt_debug_return();
12991708Sstevel }
13001708Sstevel
13011708Sstevel void
plat_kdi_init(kdi_t * kdi)13021708Sstevel plat_kdi_init(kdi_t *kdi)
13031708Sstevel {
13041708Sstevel kdi->pkdi_system_claim = starcat_system_claim;
13051708Sstevel kdi->pkdi_system_release = starcat_system_release;
13061708Sstevel }
13071708Sstevel
13081708Sstevel /*
13091708Sstevel * This function returns 1 if large pages for kernel heap are supported
13101708Sstevel * and 0 otherwise.
13111708Sstevel *
13121708Sstevel * Currently we disable lp kmem support if kpr is going to be enabled
13131708Sstevel * because in the case of large pages hat_add_callback()/hat_delete_callback()
13141708Sstevel * cause network performance degradation
13151708Sstevel */
13161708Sstevel int
plat_lpkmem_is_supported(void)13171708Sstevel plat_lpkmem_is_supported(void)
13181708Sstevel {
13191708Sstevel extern int segkmem_reloc;
13201708Sstevel
13211708Sstevel if (hat_kpr_enabled && kernel_cage_enable &&
13221708Sstevel (ncpunode >= 32 || segkmem_reloc == 1))
13231708Sstevel return (0);
13241708Sstevel
13251708Sstevel return (1);
13261708Sstevel }
1327