/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2005 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #pragma ident "%Z%%M% %I% %E% SMI" /* * Starfire Memory Controller specific routines. */ #include <sys/debug.h> #include <sys/types.h> #include <sys/errno.h> #include <sys/dditypes.h> #include <sys/conf.h> #include <sys/ddi.h> #include <sys/sunddi.h> #include <sys/sunndi.h> #include <sys/ddi_impldefs.h> #include <sys/promif.h> #include <sys/machsystm.h> #include <sys/starfire.h> struct mc_dimm_table { int mc_type; int mc_module_size; /* module size in MB */ }; static struct mc_dimm_table dimmsize_table[] = { { 4, 8 }, { 6, 8 }, { 11, 32 }, { 15, 128 }, { 0, 0 } }; #define MC_MB(mb) ((mb) * 1048576ull) struct mc_seg_size { uint_t seg_mask; uint64_t seg_size; }; struct mc_seg_size mc_seg_table[] = { { 0x7f, MC_MB(64) }, { 0x7e, MC_MB(128) }, { 0x7c, MC_MB(256) }, { 0x78, MC_MB(512) }, { 0x70, MC_MB(1024) }, { 0x60, MC_MB(2048) }, { 0x40, MC_MB(4096) }, { 0, 0 } }; /* * Alignment of memory between MC's. */ uint64_t mc_get_mem_alignment() { return (STARFIRE_MC_MEMBOARD_ALIGNMENT); } uint64_t mc_get_asr_addr(pnode_t nodeid) { int rlen; uint64_t psi_addr; struct sf_memunit_regspec reg; rlen = prom_getproplen(nodeid, "reg"); if (rlen != sizeof (struct sf_memunit_regspec)) return ((uint64_t)-1); if (prom_getprop(nodeid, "reg", (caddr_t)®) < 0) return ((uint64_t)-1); psi_addr = ((uint64_t)reg.regspec_addr_hi) << 32; psi_addr |= (uint64_t)reg.regspec_addr_lo; return (STARFIRE_MC_ASR_ADDR(psi_addr)); } uint64_t mc_get_idle_addr(pnode_t nodeid) { int rlen; uint64_t psi_addr; struct sf_memunit_regspec reg; rlen = prom_getproplen(nodeid, "reg"); if (rlen != sizeof (struct sf_memunit_regspec)) return ((uint64_t)-1); if (prom_getprop(nodeid, "reg", (caddr_t)®) < 0) return ((uint64_t)-1); psi_addr = ((uint64_t)reg.regspec_addr_hi) << 32; psi_addr |= (uint64_t)reg.regspec_addr_lo; return (STARFIRE_MC_IDLE_ADDR(psi_addr)); } int mc_get_dimm_size(pnode_t nodeid) { uint64_t psi_addr; uint_t dimmtype; int i, rlen; struct sf_memunit_regspec reg; rlen = prom_getproplen(nodeid, "reg"); if (rlen != sizeof (struct sf_memunit_regspec)) return (-1); if (prom_getprop(nodeid, "reg", (caddr_t)®) < 0) return (-1); psi_addr = ((uint64_t)reg.regspec_addr_hi) << 32; psi_addr |= (uint64_t)reg.regspec_addr_lo; psi_addr = STARFIRE_MC_DIMMTYPE_ADDR(psi_addr); if (psi_addr == (uint64_t)-1) return (-1); dimmtype = ldphysio(psi_addr); dimmtype &= STARFIRE_MC_DIMMSIZE_MASK; for (i = 0; dimmsize_table[i].mc_type != 0; i++) if (dimmsize_table[i].mc_type == dimmtype) break; return (dimmsize_table[i].mc_module_size); } uint64_t mc_get_alignment_mask(pnode_t nodeid) { uint64_t psi_addr, seg_sz; uint_t mcreg, seg_sz_mask; int i, rlen; struct sf_memunit_regspec reg; rlen = prom_getproplen(nodeid, "reg"); if (rlen != sizeof (struct sf_memunit_regspec)) return (-1); if (prom_getprop(nodeid, "reg", (caddr_t)®) < 0) return (-1); psi_addr = ((uint64_t)reg.regspec_addr_hi) << 32; psi_addr |= (uint64_t)reg.regspec_addr_lo; psi_addr = STARFIRE_MC_ASR_ADDR(psi_addr); if (psi_addr == (uint64_t)-1) return (-1); mcreg = ldphysio(psi_addr); seg_sz_mask = (mcreg & STARFIRE_MC_MASK_MASK) >> 8; for (i = 0; mc_seg_table[i].seg_size != 0; i++) if (mc_seg_table[i].seg_mask == seg_sz_mask) break; if (mc_seg_table[i].seg_size == 0) seg_sz = mc_get_mem_alignment(); else seg_sz = mc_seg_table[i].seg_size; #ifdef DEBUG printf("nodeid %x, mc asr addr %lx, val %x, seg_sz_mask %x, " "seg_sz %lx\n", nodeid, psi_addr, mcreg, seg_sz_mask, seg_sz); #endif /* DEBUG */ return (seg_sz - 1); } int mc_read_asr(pnode_t nodeid, uint_t *mcregp) { uint64_t psi_addr; *mcregp = 0; psi_addr = mc_get_asr_addr(nodeid); if (psi_addr == (uint64_t)-1) return (-1); *mcregp = ldphysio(psi_addr); return (0); } int mc_write_asr(pnode_t nodeid, uint_t mcreg) { uint_t mcreg_rd; uint64_t psi_addr; psi_addr = mc_get_asr_addr(nodeid); if (psi_addr == (uint64_t)-1) return (-1); stphysio(psi_addr, mcreg); mcreg_rd = ldphysio(psi_addr); ASSERT(mcreg_rd == mcreg); return ((mcreg_rd != mcreg) ? -1 : 0); } uint64_t mc_asr_to_pa(uint_t mcreg) { uint64_t pa, masr, addrmask, lowbitmask; /* * Remove memory present bit. */ masr = (uint64_t)(mcreg & ~STARFIRE_MC_MEM_PRESENT_MASK); /* * Get mask for bits 32-26. */ lowbitmask = masr & (uint64_t)STARFIRE_MC_MASK_MASK; lowbitmask <<= STARFIRE_MC_MASK_SHIFT; addrmask = STARFIRE_MC_ADDR_HIBITS | lowbitmask; pa = (masr << STARFIRE_MC_BASE_SHIFT) & addrmask; return (pa); } uint_t mc_pa_to_asr(uint_t masr, uint64_t pa) { uint64_t addrmask, lowbitmask; uint_t base; /* * Get mask for bits 32-26. */ lowbitmask = masr & (uint64_t)STARFIRE_MC_MASK_MASK; lowbitmask <<= STARFIRE_MC_MASK_SHIFT; addrmask = STARFIRE_MC_ADDR_HIBITS | lowbitmask; base = (pa & addrmask) >> STARFIRE_MC_BASE_SHIFT; masr &= ~ STARFIRE_MC_MEM_BASEADDR_MASK; masr |= base & STARFIRE_MC_MEM_BASEADDR_MASK; ASSERT(mc_asr_to_pa(masr) == pa); return (masr); }