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 2009 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #include <sys/types.h> 28 #include <sys/pghw.h> 29 #include <sys/cmn_err.h> 30 #include <sys/sysmacros.h> 31 #include <sys/fm/protocol.h> 32 #include <sys/x86_archext.h> 33 #include <sys/pci_cfgspace.h> 34 35 #include "ao.h" 36 37 /* 38 * AMD Opteron CPU Subroutines 39 * 40 * The following two tunables are used to determine the scrubbing rates for 41 * the D$ and L2$. The values range from 0x00-0x16 as described in BKDG 42 * Scrub Control Register. A value of zero disables the scrubber. Values 43 * above zero indicate rates in descending order. 44 * 45 * The current default values are used on several Sun systems. In the future 46 * this code should assign values dynamically based on cache sizing. If you 47 * tune these values manually be aware of the following architectural issue: 48 * At present, Opteron can only survive certain kinds of multi-bit errors if 49 * they are detected by the scrubbers. Therefore in general we want these 50 * values tuned as high as possible without impacting workload performance. 51 */ 52 uint32_t ao_scrub_rate_dcache = 8; /* 64B every 5.12 us */ 53 uint32_t ao_scrub_rate_l2cache = 9; /* 64B every 10.2 us */ 54 55 enum { 56 AO_SCRUB_BIOSDEFAULT, /* retain system default values */ 57 AO_SCRUB_FIXED, /* assign ao_scrub_rate_* values */ 58 AO_SCRUB_MAX /* assign max of system and tunables */ 59 } ao_scrub_policy = AO_SCRUB_MAX; 60 61 void 62 ao_pcicfg_write(uint_t procnodeid, uint_t func, uint_t reg, uint32_t val) 63 { 64 ASSERT(procnodeid + 24 <= 31); 65 ASSERT((func & 7) == func); 66 ASSERT((reg & 3) == 0 && reg < 256); 67 68 cmi_pci_putl(0, procnodeid + 24, func, reg, 0, val); 69 } 70 71 uint32_t 72 ao_pcicfg_read(uint_t procnodeid, uint_t func, uint_t reg) 73 { 74 ASSERT(procnodeid + 24 <= 31); 75 ASSERT((func & 7) == func); 76 ASSERT((reg & 3) == 0 && reg < 256); 77 78 return (cmi_pci_getl(0, procnodeid + 24, func, reg, 0, 0)); 79 } 80 81 82 /* 83 * Return the maximum scrubbing rate between r1 and r2, where r2 is extracted 84 * from the specified 'cfg' register value using 'mask' and 'shift'. If a 85 * value is zero, scrubbing is off so return the opposite value. Otherwise 86 * the maximum rate is the smallest non-zero value of the two values. 87 */ 88 static uint32_t 89 ao_scrubber_max(uint32_t r1, uint32_t r2) 90 { 91 if (r1 != 0 && r2 != 0) 92 return (MIN(r1, r2)); 93 94 return (r1 ? r1 : r2); 95 } 96 97 /* 98 * Enable the node-specific hardware scrubbers for the D$ and L2$. We set 99 * the scrubber rate based on a set of tunables defined at the top of the file. 100 */ 101 void 102 ao_procnode_scrubber_enable(cmi_hdl_t hdl, ao_ms_data_t *ao) 103 { 104 uint_t procnodeid = cmi_hdl_procnodeid(hdl); 105 union mcreg_scrubctl scrubctl; 106 107 ao->ao_ms_shared->aos_bcfg_scrubctl = MCREG_VAL32(&scrubctl) = 108 ao_pcicfg_read(procnodeid, MC_FUNC_MISCCTL, MC_CTL_REG_SCRUBCTL); 109 110 if (ao_scrub_policy == AO_SCRUB_BIOSDEFAULT) 111 return; 112 113 if (ao_scrub_rate_dcache > AMD_NB_SCRUBCTL_RATE_MAX) { 114 cmn_err(CE_WARN, "ao_scrub_rate_dcache is too large; " 115 "resetting to 0x%x\n", AMD_NB_SCRUBCTL_RATE_MAX); 116 ao_scrub_rate_dcache = AMD_NB_SCRUBCTL_RATE_MAX; 117 } 118 119 if (ao_scrub_rate_l2cache > AMD_NB_SCRUBCTL_RATE_MAX) { 120 cmn_err(CE_WARN, "ao_scrub_rate_l2cache is too large; " 121 "resetting to 0x%x\n", AMD_NB_SCRUBCTL_RATE_MAX); 122 ao_scrub_rate_l2cache = AMD_NB_SCRUBCTL_RATE_MAX; 123 } 124 125 switch (ao_scrub_policy) { 126 case AO_SCRUB_FIXED: 127 /* Use the system values checked above */ 128 break; 129 130 default: 131 cmn_err(CE_WARN, "Unknown ao_scrub_policy value %d - " 132 "using default policy of AO_SCRUB_MAX", ao_scrub_policy); 133 /*FALLTHRU*/ 134 135 case AO_SCRUB_MAX: 136 ao_scrub_rate_dcache = 137 ao_scrubber_max(ao_scrub_rate_dcache, 138 MCREG_FIELD_CMN(&scrubctl, DcacheScrub)); 139 140 ao_scrub_rate_l2cache = 141 ao_scrubber_max(ao_scrub_rate_l2cache, 142 MCREG_FIELD_CMN(&scrubctl, L2Scrub)); 143 break; 144 } 145 146 MCREG_FIELD_CMN(&scrubctl, DcacheScrub) = ao_scrub_rate_dcache; 147 MCREG_FIELD_CMN(&scrubctl, L2Scrub) = ao_scrub_rate_l2cache; 148 149 ao_pcicfg_write(procnodeid, MC_FUNC_MISCCTL, MC_CTL_REG_SCRUBCTL, 150 MCREG_VAL32(&scrubctl)); 151 } 152