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
amd0f_set_fidvid(const struct amd0f_fidvid * fv,const struct amd0f_xsit * xsit)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
amd0f_get_fidvid(struct amd0f_fidvid * fv)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
amd0f_fidvid_limit(struct amd0f_fidvid * fv_min,struct amd0f_fidvid * fv_max)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