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