1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 22 /* 23 * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 #include <sys/types.h> 30 #include <sys/pghw.h> 31 #include <sys/cmn_err.h> 32 #include <sys/sysmacros.h> 33 #include <sys/fm/protocol.h> 34 #include <sys/x86_archext.h> 35 36 #include "ao.h" 37 38 /* 39 * AMD Opteron CPU Subroutines 40 * 41 * The following three tunables are used to determine the scrubbing rates for 42 * the D$, L2$, and DRAM hardware scrubbers. The values range from 0x00-0x16 43 * as described in BKDG 3.6.6 Scrub Control Register. A value of zero disables 44 * the scrubber. Values above zero indicate rates in descending order. 45 * 46 * The current default values are used on several Sun systems. In the future 47 * this code should assign values dynamically based on memory sizing. If you 48 * tune these values manually be aware of the following architectural issue: 49 * At present, Opteron can only survive certain kinds of multi-bit errors if 50 * they are detected by the scrubbers. Therefore in general we want these 51 * values tuned as high as possible without impacting workload performance. 52 */ 53 uint32_t ao_scrub_rate_dcache = 8; /* 64B every 5.12 us */ 54 uint32_t ao_scrub_rate_l2cache = 9; /* 64B every 10.2 us */ 55 uint32_t ao_scrub_rate_dram = 0xd; /* 64B every 163.8 us */ 56 57 uint32_t ao_scrub_system; /* debug stash for system's value */ 58 uint32_t ao_scrub_bios; /* debug stash for bios's value */ 59 uint32_t ao_scrub_lo; /* debug stash for system low addr */ 60 uint32_t ao_scrub_hi; /* debug stash for system high addr */ 61 62 enum { 63 AO_SCRUB_BIOSDEFAULT, /* retain system default values */ 64 AO_SCRUB_FIXED, /* assign ao_scrub_rate_* values */ 65 AO_SCRUB_MAX /* assign max of system and tunables */ 66 } ao_scrub_policy = AO_SCRUB_MAX; 67 68 nvlist_t * 69 ao_fmri_create(ao_data_t *ao, nv_alloc_t *nva) 70 { 71 nvlist_t *nvl = fm_nvlist_create(nva); 72 73 fm_fmri_hc_set(nvl, FM_HC_SCHEME_VERSION, NULL, NULL, 3, 74 "motherboard", 0, 75 "chip", pg_plat_hw_instance_id(ao->ao_cpu, PGHW_CHIP), 76 "cpu", cpuid_get_clogid(ao->ao_cpu)); 77 78 return (nvl); 79 } 80 81 /* 82 * Return the maximum scrubbing rate between r1 and r2, where r2 is extracted 83 * from the specified 'cfg' register value using 'mask' and 'shift'. If a 84 * value is zero, scrubbing is off so return the opposite value. Otherwise 85 * the maximum rate is the smallest non-zero value of the two values. 86 */ 87 static uint32_t 88 ao_scrubber_max(uint32_t r1, uint32_t cfg, uint32_t mask, uint32_t shift) 89 { 90 uint32_t r2 = (cfg & mask) >> shift; 91 92 if (r1 != 0 && r2 != 0) 93 return (MIN(r1, r2)); 94 95 return (r1 ? r1 : r2); 96 } 97 98 /* 99 * Enable the chip-specific hardware scrubbers for the D$, L2$, and DRAM, and 100 * return a boolean value indicating if we enabled the DRAM scrubber. We set 101 * the scrubber rate based on a set of tunables defined at the top of the file. 102 * The 'base' parameter is the DRAM Base Address for this chip and is used to 103 * determine where the scrubber starts. The 'ilen' value is the IntvlEn field 104 * from the DRAM configuration indicating the node-interleaving configuration. 105 * 106 * Where chip-select sparing is available the DRAM scrub address registers 107 * must not be modified while a swap is in-progress. This can't happen 108 * because we (the amd cpu module) take control of the online spare 109 * away from the BIOS when we perform NB configuration and we complete 110 * that operation before the memory controller driver loads. 111 */ 112 int 113 ao_scrubber_enable(void *data, uint64_t base, uint64_t ilen, int csdiscontig) 114 { 115 ao_data_t *ao = data; 116 chipid_t chipid = pg_plat_hw_instance_id(ao->ao_cpu, PGHW_CHIP); 117 uint32_t rev = cpuid_getchiprev(ao->ao_cpu); 118 uint32_t scrubctl, lo, hi; 119 int rv = 1; 120 121 /* 122 * Read the initial scrubber configuration and save it for debugging. 123 * If ao_scrub_policy is DEFAULT, return immediately. Otherwise we 124 * disable scrubbing activity while we fiddle with the configuration. 125 */ 126 scrubctl = ao_pcicfg_read(chipid, AMD_NB_FUNC, AMD_NB_REG_SCRUBCTL); 127 cas32(&ao_scrub_bios, 0, scrubctl); 128 129 if (ao_scrub_policy == AO_SCRUB_BIOSDEFAULT) 130 return ((scrubctl & AMD_NB_SCRUBCTL_DRAM_MASK) != 0); 131 132 scrubctl &= ~AMD_NB_SCRUBCTL_DRAM_MASK; 133 scrubctl &= ~AMD_NB_SCRUBCTL_L2_MASK; 134 scrubctl &= ~AMD_NB_SCRUBCTL_DC_MASK; 135 136 ao_pcicfg_write(chipid, AMD_NB_FUNC, AMD_NB_REG_SCRUBCTL, scrubctl); 137 138 /* 139 * Read the DRAM Scrub Address Low and High registers, clear their 140 * address fields, enable sequential-redirect mode, and update the 141 * address fields using the specified DRAM Base Address. 142 */ 143 lo = ao_pcicfg_read(chipid, AMD_NB_FUNC, AMD_NB_REG_SCRUBADDR_LO); 144 hi = ao_pcicfg_read(chipid, AMD_NB_FUNC, AMD_NB_REG_SCRUBADDR_HI); 145 146 lo &= ~AMD_NB_SCRUBADDR_LO_MASK; 147 hi &= ~AMD_NB_SCRUBADDR_HI_MASK; 148 149 lo |= AMD_NB_SCRUBADDR_MKLO(base) | AMD_NB_SCRUBADDR_LO_SCRUBREDIREN; 150 hi |= AMD_NB_SCRUBADDR_MKHI(base); 151 152 ao_scrub_lo = lo; 153 ao_scrub_hi = hi; 154 155 ao_pcicfg_write(chipid, AMD_NB_FUNC, AMD_NB_REG_SCRUBADDR_LO, lo); 156 ao_pcicfg_write(chipid, AMD_NB_FUNC, AMD_NB_REG_SCRUBADDR_HI, hi); 157 158 if (ao_scrub_rate_dcache > AMD_NB_SCRUBCTL_RATE_MAX) { 159 cmn_err(CE_WARN, "ao_scrub_rate_dcache is too large; " 160 "resetting to 0x%x\n", AMD_NB_SCRUBCTL_RATE_MAX); 161 ao_scrub_rate_dcache = AMD_NB_SCRUBCTL_RATE_MAX; 162 } 163 164 if (ao_scrub_rate_l2cache > AMD_NB_SCRUBCTL_RATE_MAX) { 165 cmn_err(CE_WARN, "ao_scrub_rate_l2cache is too large; " 166 "resetting to 0x%x\n", AMD_NB_SCRUBCTL_RATE_MAX); 167 ao_scrub_rate_l2cache = AMD_NB_SCRUBCTL_RATE_MAX; 168 } 169 170 if (ao_scrub_rate_dram > AMD_NB_SCRUBCTL_RATE_MAX) { 171 cmn_err(CE_WARN, "ao_scrub_rate_dram is too large; " 172 "resetting to 0x%x\n", AMD_NB_SCRUBCTL_RATE_MAX); 173 ao_scrub_rate_dram = AMD_NB_SCRUBCTL_RATE_MAX; 174 } 175 176 switch (ao_scrub_policy) { 177 case AO_SCRUB_FIXED: 178 /* Use the system values checked above */ 179 break; 180 181 default: 182 cmn_err(CE_WARN, "Unknown ao_scrub_policy value %d - " 183 "using default policy of AO_SCRUB_MAX", ao_scrub_policy); 184 /*FALLTHRU*/ 185 186 case AO_SCRUB_MAX: 187 ao_scrub_rate_dcache = 188 ao_scrubber_max(ao_scrub_rate_dcache, ao_scrub_bios, 189 AMD_NB_SCRUBCTL_DC_MASK, AMD_NB_SCRUBCTL_DC_SHIFT); 190 191 ao_scrub_rate_l2cache = 192 ao_scrubber_max(ao_scrub_rate_l2cache, ao_scrub_bios, 193 AMD_NB_SCRUBCTL_L2_MASK, AMD_NB_SCRUBCTL_L2_SHIFT); 194 195 ao_scrub_rate_dram = 196 ao_scrubber_max(ao_scrub_rate_dram, ao_scrub_bios, 197 AMD_NB_SCRUBCTL_DRAM_MASK, AMD_NB_SCRUBCTL_DRAM_SHIFT); 198 break; 199 } 200 201 #ifdef OPTERON_ERRATUM_99 202 /* 203 * This erratum applies on revisions D and earlier. 204 * 205 * Do not enable the dram scrubber is the chip-select ranges 206 * for the node are not contiguous. 207 */ 208 if (csdiscontig && !X86_CHIPREV_ATLEAST(rev, X86_CHIPREV_AMD_F_REV_E)) { 209 cmn_err(CE_CONT, "?Opteron DRAM scrubber disabled on revision " 210 "%s chip because DRAM hole is present on this node", 211 cpuid_getchiprevstr(ao->ao_cpu)); 212 ao_scrub_rate_dram = 0; 213 rv = 0; 214 } 215 #endif 216 217 #ifdef OPTERON_ERRATUM_101 218 /* 219 * This erratum applies on revisions D and earlier. 220 * 221 * If the DRAM Base Address register's IntlvEn field indicates that 222 * node interleaving is enabled, we must disable the DRAM scrubber 223 * and return zero to indicate that Solaris should use s/w instead. 224 */ 225 if (ilen != 0 && ao_scrub_rate_dram != 0 && 226 !X86_CHIPREV_ATLEAST(rev, X86_CHIPREV_AMD_F_REV_E)) { 227 cmn_err(CE_CONT, "?Opteron DRAM scrubber disabled on revision " 228 "%s chip because DRAM memory is node-interleaved", 229 cpuid_getchiprevstr(ao->ao_cpu)); 230 ao_scrub_rate_dram = 0; 231 rv = 0; 232 } 233 #endif 234 scrubctl |= AMD_NB_MKSCRUBCTL(ao_scrub_rate_dcache, 235 ao_scrub_rate_l2cache, ao_scrub_rate_dram); 236 237 ao_scrub_system = scrubctl; 238 ao_pcicfg_write(chipid, AMD_NB_FUNC, AMD_NB_REG_SCRUBCTL, scrubctl); 239 240 return (rv); 241 } 242