/* * Copyright (c) 2004 Martin V\xe9giard. * Copyright (c) 2004-2005 Bruno Ducrot * Copyright (c) 2004 FUKUDA Nobuhiko * Copyright (c) 2004, 2006 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Juan Romero Pardines and Martin Vegiard. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include #define AMD0F_MSR_FIDVID_CTL 0xc0010041 #define AMD0F_MSR_FIDVID_STATUS 0xc0010042 /* AMD0F_MSR_FIDVID_STATUS */ #define AMD0F_STA_CFID(x) ((x) & 0x3f) #define AMD0F_STA_SFID(x) (((x) >> 8) & 0x3f) #define AMD0F_STA_MFID(x) (((x) >> 16) & 0x3f) #define AMD0F_STA_PENDING(x) (((x) >> 31) & 0x01) #define AMD0F_STA_CVID(x) (((x) >> 32) & 0x1f) #define AMD0F_STA_SVID(x) (((x) >> 40) & 0x1f) #define AMD0F_STA_MVID(x) (((x) >> 48) & 0x1f) #define AMD0F_WRITE_FIDVID(fid, vid, ctrl) \ wrmsr(AMD0F_MSR_FIDVID_CTL, \ (((ctrl) << 32) | (1ULL << 16) | ((vid) << 8) | (fid))) #define AMD0F_WAIT_FIDVID_CHG(status) \ do { \ (status) = rdmsr(AMD0F_MSR_FIDVID_STATUS); \ } while (AMD0F_STA_PENDING(status)) #define AMD0F_FID2VCO(fid) \ (((fid) < 8) ? (8 + ((fid) << 1)) : (fid)) #define AMD0F_DELAY_VST(vst) DELAY(20 * (vst)) #define AMD0F_DELAY_IRT(irt) DELAY(10 * (1 << (irt))) /* XXX */ #define abs(x) ((x) < 0 ? -(x) : (x)) int amd0f_set_fidvid(const struct amd0f_fidvid *fv, const struct amd0f_xsit *xsit) { uint32_t val, cfid, cvid, tvid; uint64_t status; /* * We don't wait change pending bit here, need to ensure * that it isn't stuck. */ status = rdmsr(AMD0F_MSR_FIDVID_STATUS); if (AMD0F_STA_PENDING(status)) return EBUSY; cfid = AMD0F_STA_CFID(status); cvid = AMD0F_STA_CVID(status); if (fv->fid == cfid && fv->vid == cvid) return 0; /* * Phase 1: Raise core voltage to the TargetVID */ if ((fv->fid & ~0x1) > (cfid & ~0x1) || cvid > fv->vid) { KKASSERT(fv->vid >= xsit->rvo); tvid = fv->vid - xsit->rvo; } else { KKASSERT(cvid >= xsit->rvo); tvid = cvid - xsit->rvo; } while (cvid > tvid) { if (cvid > (1 << xsit->mvs)) val = cvid - (1 << xsit->mvs); else val = 0; AMD0F_WRITE_FIDVID(cfid, val, 0ULL); AMD0F_WAIT_FIDVID_CHG(status); cvid = AMD0F_STA_CVID(status); AMD0F_DELAY_VST(xsit->vst); } /* * Phase 2: Change to requested core frequency */ if (cfid != fv->fid) { /* NOTE: Keep type as int, else following 'abs' will break */ int vco_fid, vco_cfid; vco_fid = AMD0F_FID2VCO(fv->fid); vco_cfid = AMD0F_FID2VCO(cfid); while (abs(vco_fid - vco_cfid) > 2) { if (fv->fid > cfid) { if (cfid > 6) val = cfid + 2; else val = AMD0F_FID2VCO(cfid) + 2; } else { KKASSERT(cfid >= 2); val = cfid - 2; } AMD0F_WRITE_FIDVID(val, cvid, (uint64_t)xsit->pll_time * 1000 / 5); AMD0F_WAIT_FIDVID_CHG(status); cfid = AMD0F_STA_CFID(status); AMD0F_DELAY_IRT(xsit->irt); vco_cfid = AMD0F_FID2VCO(cfid); } if (cfid != fv->fid) { AMD0F_WRITE_FIDVID(fv->fid, cvid, (uint64_t)xsit->pll_time * 1000 / 5); AMD0F_WAIT_FIDVID_CHG(status); cfid = AMD0F_STA_CFID(status); AMD0F_DELAY_IRT(xsit->irt); } } /* * Phase 3: Change to requested voltage */ if (cvid != fv->vid) { AMD0F_WRITE_FIDVID(cfid, fv->vid, 0ULL); AMD0F_WAIT_FIDVID_CHG(status); cvid = AMD0F_STA_CVID(status); AMD0F_DELAY_VST(xsit->vst); } return 0; } int amd0f_get_fidvid(struct amd0f_fidvid *fv) { uint64_t status; status = rdmsr(AMD0F_MSR_FIDVID_STATUS); if (AMD0F_STA_PENDING(status)) return EBUSY; fv->fid = AMD0F_STA_CFID(status); fv->vid = AMD0F_STA_CVID(status); return 0; } void amd0f_fidvid_limit(struct amd0f_fidvid *fv_min, struct amd0f_fidvid *fv_max) { uint32_t max_fid, max_vid, start_fid, start_vid; uint64_t status; status = rdmsr(AMD0F_MSR_FIDVID_STATUS); start_fid = AMD0F_STA_SFID(status); max_fid = AMD0F_STA_MFID(status); start_vid = AMD0F_STA_SVID(status); max_vid = AMD0F_STA_MVID(status); if (max_fid == 0x2a && max_vid != 0) { fv_max->fid = start_fid + 0xa; fv_max->vid = max_vid + 0x2; fv_min->fid = 0x2; fv_min->vid = start_vid; } else { fv_max->fid = max_fid; fv_max->vid = max_vid + 0x2; fv_min->fid = start_fid; fv_min->vid = start_vid; } }