1 /* 2 * Copyright (c) 2004 Martin V\xe9giard. 3 * Copyright (c) 2004-2005 Bruno Ducrot 4 * Copyright (c) 2004 FUKUDA Nobuhiko <nfukuda@spa.is.uec.ac.jp> 5 * Copyright (c) 2004, 2006 The NetBSD Foundation, Inc. 6 * All rights reserved. 7 * 8 * This code is derived from software contributed to The NetBSD Foundation 9 * by Juan Romero Pardines and Martin Vegiard. 10 * 11 * Redistribution and use in source and binary forms, with or without 12 * modification, are permitted provided that the following conditions 13 * are met: 14 * 1. Redistributions of source code must retain the above copyright 15 * notice, this list of conditions and the following disclaimer. 16 * 2. Redistributions in binary form must reproduce the above copyright 17 * notice, this list of conditions and the following disclaimer in the 18 * documentation and/or other materials provided with the distribution. 19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 20 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 21 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 22 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 23 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 24 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 28 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 */ 30 31 #include <sys/param.h> 32 #include <sys/systm.h> 33 34 #include <machine/cpufunc.h> 35 #include <machine/cpufreq.h> 36 37 #define AMD0F_MSR_FIDVID_CTL 0xc0010041 38 #define AMD0F_MSR_FIDVID_STATUS 0xc0010042 39 40 /* AMD0F_MSR_FIDVID_STATUS */ 41 #define AMD0F_STA_CFID(x) ((x) & 0x3f) 42 #define AMD0F_STA_SFID(x) (((x) >> 8) & 0x3f) 43 #define AMD0F_STA_MFID(x) (((x) >> 16) & 0x3f) 44 #define AMD0F_STA_PENDING(x) (((x) >> 31) & 0x01) 45 #define AMD0F_STA_CVID(x) (((x) >> 32) & 0x1f) 46 #define AMD0F_STA_SVID(x) (((x) >> 40) & 0x1f) 47 #define AMD0F_STA_MVID(x) (((x) >> 48) & 0x1f) 48 49 #define AMD0F_WRITE_FIDVID(fid, vid, ctrl) \ 50 wrmsr(AMD0F_MSR_FIDVID_CTL, \ 51 (((ctrl) << 32) | (1ULL << 16) | ((vid) << 8) | (fid))) 52 53 #define AMD0F_WAIT_FIDVID_CHG(status) \ 54 do { \ 55 (status) = rdmsr(AMD0F_MSR_FIDVID_STATUS); \ 56 } while (AMD0F_STA_PENDING(status)) 57 58 #define AMD0F_FID2VCO(fid) \ 59 (((fid) < 8) ? (8 + ((fid) << 1)) : (fid)) 60 61 #define AMD0F_DELAY_VST(vst) DELAY(20 * (vst)) 62 #define AMD0F_DELAY_IRT(irt) DELAY(10 * (1 << (irt))) 63 64 /* XXX */ 65 #define abs(x) ((x) < 0 ? -(x) : (x)) 66 67 int 68 amd0f_set_fidvid(const struct amd0f_fidvid *fv, const struct amd0f_xsit *xsit) 69 { 70 uint32_t val, cfid, cvid; 71 int rvo; 72 uint64_t status; 73 74 /* 75 * We don't wait change pending bit here, need to ensure 76 * that it isn't stuck. 77 */ 78 status = rdmsr(AMD0F_MSR_FIDVID_STATUS); 79 if (AMD0F_STA_PENDING(status)) 80 return EBUSY; 81 82 cfid = AMD0F_STA_CFID(status); 83 cvid = AMD0F_STA_CVID(status); 84 if (fv->fid == cfid && fv->vid == cvid) 85 return 0; 86 87 /* 88 * Phase 1: Raise core voltage to requested VID if frequency is 89 * going up. 90 */ 91 if ((fv->fid & ~0x1) > (cfid & ~0x1) || cvid > fv->vid) { 92 KKASSERT(fv->vid >= xsit->rvo); 93 } else { 94 KKASSERT(cvid >= xsit->rvo); 95 } 96 while (cvid > fv->vid) { 97 if (cvid > (1 << xsit->mvs)) 98 val = cvid - (1 << xsit->mvs); 99 else 100 val = 0; 101 AMD0F_WRITE_FIDVID(cfid, val, 0ULL); 102 AMD0F_WAIT_FIDVID_CHG(status); 103 cvid = AMD0F_STA_CVID(status); 104 AMD0F_DELAY_VST(xsit->vst); 105 } 106 /* ... then raise to voltage + RVO (if required) */ 107 for (rvo = xsit->rvo; rvo > 0 && cvid > 0; --rvo) { 108 /* XXX It's not clear from spec if we have to do that 109 * in 0.25 step or in MVS. Therefore do it as it's done 110 * under Linux */ 111 AMD0F_WRITE_FIDVID(cfid, cvid - 1, 0ULL); 112 AMD0F_WAIT_FIDVID_CHG(status); 113 cvid = AMD0F_STA_CVID(status); 114 AMD0F_DELAY_VST(xsit->vst); 115 } 116 117 /* 118 * Phase 2: Change to requested core frequency 119 */ 120 if (cfid != fv->fid) { 121 /* NOTE: Keep type as int, else following 'abs' will break */ 122 int vco_fid, vco_cfid; 123 124 vco_fid = AMD0F_FID2VCO(fv->fid); 125 vco_cfid = AMD0F_FID2VCO(cfid); 126 while (abs(vco_fid - vco_cfid) > 2) { 127 if (fv->fid > cfid) { 128 if (cfid > 6) 129 val = cfid + 2; 130 else 131 val = AMD0F_FID2VCO(cfid) + 2; 132 } else { 133 KKASSERT(cfid >= 2); 134 val = cfid - 2; 135 } 136 AMD0F_WRITE_FIDVID(val, cvid, 137 (uint64_t)xsit->pll_time * 1000 / 5); 138 AMD0F_WAIT_FIDVID_CHG(status); 139 cfid = AMD0F_STA_CFID(status); 140 AMD0F_DELAY_IRT(xsit->irt); 141 vco_cfid = AMD0F_FID2VCO(cfid); 142 } 143 if (cfid != fv->fid) { 144 AMD0F_WRITE_FIDVID(fv->fid, cvid, 145 (uint64_t)xsit->pll_time * 1000 / 5); 146 AMD0F_WAIT_FIDVID_CHG(status); 147 cfid = AMD0F_STA_CFID(status); 148 AMD0F_DELAY_IRT(xsit->irt); 149 } 150 } 151 152 /* 153 * Phase 3: Change to requested voltage 154 */ 155 if (cvid != fv->vid) { 156 AMD0F_WRITE_FIDVID(cfid, fv->vid, 0ULL); 157 AMD0F_WAIT_FIDVID_CHG(status); 158 cvid = AMD0F_STA_CVID(status); 159 AMD0F_DELAY_VST(xsit->vst); 160 } 161 return 0; 162 } 163 164 int 165 amd0f_get_fidvid(struct amd0f_fidvid *fv) 166 { 167 uint64_t status; 168 169 status = rdmsr(AMD0F_MSR_FIDVID_STATUS); 170 if (AMD0F_STA_PENDING(status)) 171 return EBUSY; 172 173 fv->fid = AMD0F_STA_CFID(status); 174 fv->vid = AMD0F_STA_CVID(status); 175 return 0; 176 } 177 178 void 179 amd0f_fidvid_limit(struct amd0f_fidvid *fv_min, struct amd0f_fidvid *fv_max) 180 { 181 uint32_t max_fid, max_vid, start_fid, start_vid; 182 uint64_t status; 183 184 status = rdmsr(AMD0F_MSR_FIDVID_STATUS); 185 186 start_fid = AMD0F_STA_SFID(status); 187 max_fid = AMD0F_STA_MFID(status); 188 start_vid = AMD0F_STA_SVID(status); 189 max_vid = AMD0F_STA_MVID(status); 190 191 if (max_fid == 0x2a && max_vid != 0) { 192 fv_max->fid = start_fid + 0xa; 193 fv_max->vid = max_vid + 0x2; 194 fv_min->fid = 0x2; 195 fv_min->vid = start_vid; 196 } else { 197 fv_max->fid = max_fid; 198 fv_max->vid = max_vid + 0x2; 199 fv_min->fid = start_fid; 200 fv_min->vid = start_vid; 201 } 202 } 203