14577cf37SConrad Meyer /*- 24d846d26SWarner Losh * SPDX-License-Identifier: BSD-2-Clause 34577cf37SConrad Meyer * 44577cf37SConrad Meyer * Copyright (c) 2005 Nate Lawson 54577cf37SConrad Meyer * Copyright (c) 2004 Colin Percival 64577cf37SConrad Meyer * Copyright (c) 2004-2005 Bruno Durcot 74577cf37SConrad Meyer * Copyright (c) 2004 FUKUDA Nobuhiko 84577cf37SConrad Meyer * Copyright (c) 2009 Michael Reifenberger 94577cf37SConrad Meyer * Copyright (c) 2009 Norikatsu Shigemura 104577cf37SConrad Meyer * Copyright (c) 2008-2009 Gen Otsuji 114577cf37SConrad Meyer * 124577cf37SConrad Meyer * This code is depending on kern_cpu.c, est.c, powernow.c, p4tcc.c, smist.c 134577cf37SConrad Meyer * in various parts. The authors of these files are Nate Lawson, 144577cf37SConrad Meyer * Colin Percival, Bruno Durcot, and FUKUDA Nobuhiko. 154577cf37SConrad Meyer * This code contains patches by Michael Reifenberger and Norikatsu Shigemura. 164577cf37SConrad Meyer * Thank you. 174577cf37SConrad Meyer * 184577cf37SConrad Meyer * Redistribution and use in source and binary forms, with or without 194577cf37SConrad Meyer * modification, are permitted providing that the following conditions 204577cf37SConrad Meyer * are met: 214577cf37SConrad Meyer * 1. Redistributions of source code must retain the above copyright 224577cf37SConrad Meyer * notice, this list of conditions and the following disclaimer. 234577cf37SConrad Meyer * 2. Redistributions in binary form must reproduce the above copyright 244577cf37SConrad Meyer * notice, this list of conditions and the following disclaimer in the 254577cf37SConrad Meyer * documentation and/or other materials provided with the distribution. 264577cf37SConrad Meyer * 274577cf37SConrad Meyer * THIS SOFTWARE IS PROVIDED BY THE AUTHOR``AS IS'' AND ANY EXPRESS OR 284577cf37SConrad Meyer * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 294577cf37SConrad Meyer * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 304577cf37SConrad Meyer * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY 314577cf37SConrad Meyer * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 324577cf37SConrad Meyer * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 334577cf37SConrad Meyer * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 344577cf37SConrad Meyer * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 354577cf37SConrad Meyer * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 364577cf37SConrad Meyer * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 374577cf37SConrad Meyer * POSSIBILITY OF SUCH DAMAGE. 384577cf37SConrad Meyer */ 394577cf37SConrad Meyer 404577cf37SConrad Meyer /* 414577cf37SConrad Meyer * For more info: 424577cf37SConrad Meyer * BIOS and Kernel Developer's Guide(BKDG) for AMD Family 10h Processors 434577cf37SConrad Meyer * 31116 Rev 3.20 February 04, 2009 444577cf37SConrad Meyer * BIOS and Kernel Developer's Guide(BKDG) for AMD Family 11h Processors 454577cf37SConrad Meyer * 41256 Rev 3.00 - July 07, 2008 46*0e6e1f14SSHENGYI HONG * Processor Programming Reference (PPR) for AMD Family 1Ah Model 02h, 47*0e6e1f14SSHENGYI HONG * Revision C1 Processors Volume 1 of 7 - Sep 29, 2024 484577cf37SConrad Meyer */ 494577cf37SConrad Meyer 504577cf37SConrad Meyer #include <sys/param.h> 514577cf37SConrad Meyer #include <sys/bus.h> 524577cf37SConrad Meyer #include <sys/cpu.h> 534577cf37SConrad Meyer #include <sys/kernel.h> 544577cf37SConrad Meyer #include <sys/module.h> 554577cf37SConrad Meyer #include <sys/malloc.h> 564577cf37SConrad Meyer #include <sys/proc.h> 574577cf37SConrad Meyer #include <sys/pcpu.h> 584577cf37SConrad Meyer #include <sys/smp.h> 594577cf37SConrad Meyer #include <sys/sched.h> 604577cf37SConrad Meyer 614577cf37SConrad Meyer #include <machine/md_var.h> 624577cf37SConrad Meyer #include <machine/cputypes.h> 634577cf37SConrad Meyer #include <machine/specialreg.h> 644577cf37SConrad Meyer 654577cf37SConrad Meyer #include <contrib/dev/acpica/include/acpi.h> 664577cf37SConrad Meyer 674577cf37SConrad Meyer #include <dev/acpica/acpivar.h> 684577cf37SConrad Meyer 694577cf37SConrad Meyer #include "acpi_if.h" 704577cf37SConrad Meyer #include "cpufreq_if.h" 714577cf37SConrad Meyer 724577cf37SConrad Meyer #define MSR_AMD_10H_11H_LIMIT 0xc0010061 734577cf37SConrad Meyer #define MSR_AMD_10H_11H_CONTROL 0xc0010062 744577cf37SConrad Meyer #define MSR_AMD_10H_11H_STATUS 0xc0010063 754577cf37SConrad Meyer #define MSR_AMD_10H_11H_CONFIG 0xc0010064 764577cf37SConrad Meyer 774577cf37SConrad Meyer #define AMD_10H_11H_MAX_STATES 16 784577cf37SConrad Meyer 794577cf37SConrad Meyer /* for MSR_AMD_10H_11H_LIMIT C001_0061 */ 804577cf37SConrad Meyer #define AMD_10H_11H_GET_PSTATE_MAX_VAL(msr) (((msr) >> 4) & 0x7) 814577cf37SConrad Meyer #define AMD_10H_11H_GET_PSTATE_LIMIT(msr) (((msr)) & 0x7) 824577cf37SConrad Meyer /* for MSR_AMD_10H_11H_CONFIG 10h:C001_0064:68 / 11h:C001_0064:6B */ 834577cf37SConrad Meyer #define AMD_10H_11H_CUR_VID(msr) (((msr) >> 9) & 0x7F) 844577cf37SConrad Meyer #define AMD_10H_11H_CUR_DID(msr) (((msr) >> 6) & 0x07) 854577cf37SConrad Meyer #define AMD_10H_11H_CUR_FID(msr) ((msr) & 0x3F) 864577cf37SConrad Meyer 87e74dd957SJohannes Totz #define AMD_17H_CUR_IDIV(msr) (((msr) >> 30) & 0x03) 88e74dd957SJohannes Totz #define AMD_17H_CUR_IDD(msr) (((msr) >> 22) & 0xFF) 894577cf37SConrad Meyer #define AMD_17H_CUR_VID(msr) (((msr) >> 14) & 0xFF) 904577cf37SConrad Meyer #define AMD_17H_CUR_DID(msr) (((msr) >> 8) & 0x3F) 914577cf37SConrad Meyer #define AMD_17H_CUR_FID(msr) ((msr) & 0xFF) 924577cf37SConrad Meyer 93*0e6e1f14SSHENGYI HONG #define AMD_1AH_CUR_FID(msr) ((msr) & 0xFFF) 94*0e6e1f14SSHENGYI HONG 954577cf37SConrad Meyer #define HWPSTATE_DEBUG(dev, msg...) \ 964577cf37SConrad Meyer do { \ 974577cf37SConrad Meyer if (hwpstate_verbose) \ 984577cf37SConrad Meyer device_printf(dev, msg); \ 994577cf37SConrad Meyer } while (0) 1004577cf37SConrad Meyer 1014577cf37SConrad Meyer struct hwpstate_setting { 1024577cf37SConrad Meyer int freq; /* CPU clock in Mhz or 100ths of a percent. */ 1034577cf37SConrad Meyer int volts; /* Voltage in mV. */ 1044577cf37SConrad Meyer int power; /* Power consumed in mW. */ 1054577cf37SConrad Meyer int lat; /* Transition latency in us. */ 1064577cf37SConrad Meyer int pstate_id; /* P-State id */ 1074577cf37SConrad Meyer }; 1084577cf37SConrad Meyer 1094577cf37SConrad Meyer struct hwpstate_softc { 1104577cf37SConrad Meyer device_t dev; 1114577cf37SConrad Meyer struct hwpstate_setting hwpstate_settings[AMD_10H_11H_MAX_STATES]; 1124577cf37SConrad Meyer int cfnum; 1134577cf37SConrad Meyer }; 1144577cf37SConrad Meyer 1154577cf37SConrad Meyer static void hwpstate_identify(driver_t *driver, device_t parent); 1164577cf37SConrad Meyer static int hwpstate_probe(device_t dev); 1174577cf37SConrad Meyer static int hwpstate_attach(device_t dev); 1184577cf37SConrad Meyer static int hwpstate_detach(device_t dev); 1194577cf37SConrad Meyer static int hwpstate_set(device_t dev, const struct cf_setting *cf); 1204577cf37SConrad Meyer static int hwpstate_get(device_t dev, struct cf_setting *cf); 1214577cf37SConrad Meyer static int hwpstate_settings(device_t dev, struct cf_setting *sets, int *count); 1224577cf37SConrad Meyer static int hwpstate_type(device_t dev, int *type); 1234577cf37SConrad Meyer static int hwpstate_shutdown(device_t dev); 1244577cf37SConrad Meyer static int hwpstate_features(driver_t *driver, u_int *features); 1254577cf37SConrad Meyer static int hwpstate_get_info_from_acpi_perf(device_t dev, device_t perf_dev); 1264577cf37SConrad Meyer static int hwpstate_get_info_from_msr(device_t dev); 1274577cf37SConrad Meyer static int hwpstate_goto_pstate(device_t dev, int pstate_id); 1284577cf37SConrad Meyer 1294577cf37SConrad Meyer static int hwpstate_verbose; 1304577cf37SConrad Meyer SYSCTL_INT(_debug, OID_AUTO, hwpstate_verbose, CTLFLAG_RWTUN, 1314577cf37SConrad Meyer &hwpstate_verbose, 0, "Debug hwpstate"); 1324577cf37SConrad Meyer 1334577cf37SConrad Meyer static int hwpstate_verify; 1344577cf37SConrad Meyer SYSCTL_INT(_debug, OID_AUTO, hwpstate_verify, CTLFLAG_RWTUN, 1354577cf37SConrad Meyer &hwpstate_verify, 0, "Verify P-state after setting"); 1364577cf37SConrad Meyer 13743524989SConrad Meyer static bool hwpstate_pstate_limit; 13843524989SConrad Meyer SYSCTL_BOOL(_debug, OID_AUTO, hwpstate_pstate_limit, CTLFLAG_RWTUN, 13943524989SConrad Meyer &hwpstate_pstate_limit, 0, 14043524989SConrad Meyer "If enabled (1), limit administrative control of P-states to the value in " 14143524989SConrad Meyer "CurPstateLimit"); 14243524989SConrad Meyer 1434577cf37SConrad Meyer static device_method_t hwpstate_methods[] = { 1444577cf37SConrad Meyer /* Device interface */ 1454577cf37SConrad Meyer DEVMETHOD(device_identify, hwpstate_identify), 1464577cf37SConrad Meyer DEVMETHOD(device_probe, hwpstate_probe), 1474577cf37SConrad Meyer DEVMETHOD(device_attach, hwpstate_attach), 1484577cf37SConrad Meyer DEVMETHOD(device_detach, hwpstate_detach), 1494577cf37SConrad Meyer DEVMETHOD(device_shutdown, hwpstate_shutdown), 1504577cf37SConrad Meyer 1514577cf37SConrad Meyer /* cpufreq interface */ 1524577cf37SConrad Meyer DEVMETHOD(cpufreq_drv_set, hwpstate_set), 1534577cf37SConrad Meyer DEVMETHOD(cpufreq_drv_get, hwpstate_get), 1544577cf37SConrad Meyer DEVMETHOD(cpufreq_drv_settings, hwpstate_settings), 1554577cf37SConrad Meyer DEVMETHOD(cpufreq_drv_type, hwpstate_type), 1564577cf37SConrad Meyer 1574577cf37SConrad Meyer /* ACPI interface */ 1584577cf37SConrad Meyer DEVMETHOD(acpi_get_features, hwpstate_features), 1594577cf37SConrad Meyer {0, 0} 1604577cf37SConrad Meyer }; 1614577cf37SConrad Meyer 1624577cf37SConrad Meyer static driver_t hwpstate_driver = { 1634577cf37SConrad Meyer "hwpstate", 1644577cf37SConrad Meyer hwpstate_methods, 1654577cf37SConrad Meyer sizeof(struct hwpstate_softc), 1664577cf37SConrad Meyer }; 1674577cf37SConrad Meyer 168b3407dccSJohn Baldwin DRIVER_MODULE(hwpstate, cpu, hwpstate_driver, 0, 0); 1694577cf37SConrad Meyer 170*0e6e1f14SSHENGYI HONG static int 171*0e6e1f14SSHENGYI HONG hwpstate_amd_iscale(int val, int div) 172*0e6e1f14SSHENGYI HONG { 173*0e6e1f14SSHENGYI HONG switch (div) { 174*0e6e1f14SSHENGYI HONG case 3: /* divide by 1000 */ 175*0e6e1f14SSHENGYI HONG val /= 10; 176*0e6e1f14SSHENGYI HONG case 2: /* divide by 100 */ 177*0e6e1f14SSHENGYI HONG val /= 10; 178*0e6e1f14SSHENGYI HONG case 1: /* divide by 10 */ 179*0e6e1f14SSHENGYI HONG val /= 10; 180*0e6e1f14SSHENGYI HONG case 0: /* divide by 1 */ 181*0e6e1f14SSHENGYI HONG ; 182*0e6e1f14SSHENGYI HONG } 183*0e6e1f14SSHENGYI HONG 184*0e6e1f14SSHENGYI HONG return (val); 185*0e6e1f14SSHENGYI HONG } 186*0e6e1f14SSHENGYI HONG 1874577cf37SConrad Meyer /* 18843524989SConrad Meyer * Go to Px-state on all cpus, considering the limit register (if so 18943524989SConrad Meyer * configured). 1904577cf37SConrad Meyer */ 1914577cf37SConrad Meyer static int 1924577cf37SConrad Meyer hwpstate_goto_pstate(device_t dev, int id) 1934577cf37SConrad Meyer { 1944577cf37SConrad Meyer sbintime_t sbt; 1954577cf37SConrad Meyer uint64_t msr; 1964577cf37SConrad Meyer int cpu, i, j, limit; 1974577cf37SConrad Meyer 19843524989SConrad Meyer if (hwpstate_pstate_limit) { 1994577cf37SConrad Meyer /* get the current pstate limit */ 2004577cf37SConrad Meyer msr = rdmsr(MSR_AMD_10H_11H_LIMIT); 2014577cf37SConrad Meyer limit = AMD_10H_11H_GET_PSTATE_LIMIT(msr); 2029ea85092SConrad Meyer if (limit > id) { 20343524989SConrad Meyer HWPSTATE_DEBUG(dev, "Restricting requested P%d to P%d " 20443524989SConrad Meyer "due to HW limit\n", id, limit); 2054577cf37SConrad Meyer id = limit; 2069ea85092SConrad Meyer } 20743524989SConrad Meyer } 2084577cf37SConrad Meyer 2094577cf37SConrad Meyer cpu = curcpu; 2104577cf37SConrad Meyer HWPSTATE_DEBUG(dev, "setting P%d-state on cpu%d\n", id, cpu); 2114577cf37SConrad Meyer /* Go To Px-state */ 2124577cf37SConrad Meyer wrmsr(MSR_AMD_10H_11H_CONTROL, id); 2134577cf37SConrad Meyer 2144577cf37SConrad Meyer /* 2154577cf37SConrad Meyer * We are going to the same Px-state on all cpus. 2164577cf37SConrad Meyer * Probably should take _PSD into account. 2174577cf37SConrad Meyer */ 2184577cf37SConrad Meyer CPU_FOREACH(i) { 2194577cf37SConrad Meyer if (i == cpu) 2204577cf37SConrad Meyer continue; 2214577cf37SConrad Meyer 2224577cf37SConrad Meyer /* Bind to each cpu. */ 2234577cf37SConrad Meyer thread_lock(curthread); 2244577cf37SConrad Meyer sched_bind(curthread, i); 2254577cf37SConrad Meyer thread_unlock(curthread); 2264577cf37SConrad Meyer HWPSTATE_DEBUG(dev, "setting P%d-state on cpu%d\n", id, i); 2274577cf37SConrad Meyer /* Go To Px-state */ 2284577cf37SConrad Meyer wrmsr(MSR_AMD_10H_11H_CONTROL, id); 2294577cf37SConrad Meyer } 2304577cf37SConrad Meyer 2314577cf37SConrad Meyer /* 2324577cf37SConrad Meyer * Verify whether each core is in the requested P-state. 2334577cf37SConrad Meyer */ 2344577cf37SConrad Meyer if (hwpstate_verify) { 2354577cf37SConrad Meyer CPU_FOREACH(i) { 2364577cf37SConrad Meyer thread_lock(curthread); 2374577cf37SConrad Meyer sched_bind(curthread, i); 2384577cf37SConrad Meyer thread_unlock(curthread); 2394577cf37SConrad Meyer /* wait loop (100*100 usec is enough ?) */ 2404577cf37SConrad Meyer for (j = 0; j < 100; j++) { 2414577cf37SConrad Meyer /* get the result. not assure msr=id */ 2424577cf37SConrad Meyer msr = rdmsr(MSR_AMD_10H_11H_STATUS); 2434577cf37SConrad Meyer if (msr == id) 2444577cf37SConrad Meyer break; 2454577cf37SConrad Meyer sbt = SBT_1MS / 10; 2464577cf37SConrad Meyer tsleep_sbt(dev, PZERO, "pstate_goto", sbt, 2474577cf37SConrad Meyer sbt >> tc_precexp, 0); 2484577cf37SConrad Meyer } 2494577cf37SConrad Meyer HWPSTATE_DEBUG(dev, "result: P%d-state on cpu%d\n", 2504577cf37SConrad Meyer (int)msr, i); 2514577cf37SConrad Meyer if (msr != id) { 2524577cf37SConrad Meyer HWPSTATE_DEBUG(dev, 2534577cf37SConrad Meyer "error: loop is not enough.\n"); 2544577cf37SConrad Meyer return (ENXIO); 2554577cf37SConrad Meyer } 2564577cf37SConrad Meyer } 2574577cf37SConrad Meyer } 2584577cf37SConrad Meyer 2594577cf37SConrad Meyer return (0); 2604577cf37SConrad Meyer } 2614577cf37SConrad Meyer 2624577cf37SConrad Meyer static int 2634577cf37SConrad Meyer hwpstate_set(device_t dev, const struct cf_setting *cf) 2644577cf37SConrad Meyer { 2654577cf37SConrad Meyer struct hwpstate_softc *sc; 2664577cf37SConrad Meyer struct hwpstate_setting *set; 2674577cf37SConrad Meyer int i; 2684577cf37SConrad Meyer 2694577cf37SConrad Meyer if (cf == NULL) 2704577cf37SConrad Meyer return (EINVAL); 2714577cf37SConrad Meyer sc = device_get_softc(dev); 2724577cf37SConrad Meyer set = sc->hwpstate_settings; 2734577cf37SConrad Meyer for (i = 0; i < sc->cfnum; i++) 2744577cf37SConrad Meyer if (CPUFREQ_CMP(cf->freq, set[i].freq)) 2754577cf37SConrad Meyer break; 2764577cf37SConrad Meyer if (i == sc->cfnum) 2774577cf37SConrad Meyer return (EINVAL); 2784577cf37SConrad Meyer 2794577cf37SConrad Meyer return (hwpstate_goto_pstate(dev, set[i].pstate_id)); 2804577cf37SConrad Meyer } 2814577cf37SConrad Meyer 2824577cf37SConrad Meyer static int 2834577cf37SConrad Meyer hwpstate_get(device_t dev, struct cf_setting *cf) 2844577cf37SConrad Meyer { 2854577cf37SConrad Meyer struct hwpstate_softc *sc; 2864577cf37SConrad Meyer struct hwpstate_setting set; 2874577cf37SConrad Meyer uint64_t msr; 2884577cf37SConrad Meyer 2894577cf37SConrad Meyer sc = device_get_softc(dev); 2904577cf37SConrad Meyer if (cf == NULL) 2914577cf37SConrad Meyer return (EINVAL); 2924577cf37SConrad Meyer msr = rdmsr(MSR_AMD_10H_11H_STATUS); 2934577cf37SConrad Meyer if (msr >= sc->cfnum) 2944577cf37SConrad Meyer return (EINVAL); 2954577cf37SConrad Meyer set = sc->hwpstate_settings[msr]; 2964577cf37SConrad Meyer 2974577cf37SConrad Meyer cf->freq = set.freq; 2984577cf37SConrad Meyer cf->volts = set.volts; 2994577cf37SConrad Meyer cf->power = set.power; 3004577cf37SConrad Meyer cf->lat = set.lat; 3014577cf37SConrad Meyer cf->dev = dev; 3024577cf37SConrad Meyer return (0); 3034577cf37SConrad Meyer } 3044577cf37SConrad Meyer 3054577cf37SConrad Meyer static int 3064577cf37SConrad Meyer hwpstate_settings(device_t dev, struct cf_setting *sets, int *count) 3074577cf37SConrad Meyer { 3084577cf37SConrad Meyer struct hwpstate_softc *sc; 3094577cf37SConrad Meyer struct hwpstate_setting set; 3104577cf37SConrad Meyer int i; 3114577cf37SConrad Meyer 3124577cf37SConrad Meyer if (sets == NULL || count == NULL) 3134577cf37SConrad Meyer return (EINVAL); 3144577cf37SConrad Meyer sc = device_get_softc(dev); 3154577cf37SConrad Meyer if (*count < sc->cfnum) 3164577cf37SConrad Meyer return (E2BIG); 3174577cf37SConrad Meyer for (i = 0; i < sc->cfnum; i++, sets++) { 3184577cf37SConrad Meyer set = sc->hwpstate_settings[i]; 3194577cf37SConrad Meyer sets->freq = set.freq; 3204577cf37SConrad Meyer sets->volts = set.volts; 3214577cf37SConrad Meyer sets->power = set.power; 3224577cf37SConrad Meyer sets->lat = set.lat; 3234577cf37SConrad Meyer sets->dev = dev; 3244577cf37SConrad Meyer } 3254577cf37SConrad Meyer *count = sc->cfnum; 3264577cf37SConrad Meyer 3274577cf37SConrad Meyer return (0); 3284577cf37SConrad Meyer } 3294577cf37SConrad Meyer 3304577cf37SConrad Meyer static int 3314577cf37SConrad Meyer hwpstate_type(device_t dev, int *type) 3324577cf37SConrad Meyer { 3334577cf37SConrad Meyer 3344577cf37SConrad Meyer if (type == NULL) 3354577cf37SConrad Meyer return (EINVAL); 3364577cf37SConrad Meyer 3374577cf37SConrad Meyer *type = CPUFREQ_TYPE_ABSOLUTE; 3384577cf37SConrad Meyer return (0); 3394577cf37SConrad Meyer } 3404577cf37SConrad Meyer 3414577cf37SConrad Meyer static void 3424577cf37SConrad Meyer hwpstate_identify(driver_t *driver, device_t parent) 3434577cf37SConrad Meyer { 3444577cf37SConrad Meyer 3454577cf37SConrad Meyer if (device_find_child(parent, "hwpstate", -1) != NULL) 3464577cf37SConrad Meyer return; 3474577cf37SConrad Meyer 3484577cf37SConrad Meyer if ((cpu_vendor_id != CPU_VENDOR_AMD || CPUID_TO_FAMILY(cpu_id) < 0x10) && 3494577cf37SConrad Meyer cpu_vendor_id != CPU_VENDOR_HYGON) 3504577cf37SConrad Meyer return; 3514577cf37SConrad Meyer 3524577cf37SConrad Meyer /* 3534577cf37SConrad Meyer * Check if hardware pstate enable bit is set. 3544577cf37SConrad Meyer */ 3554577cf37SConrad Meyer if ((amd_pminfo & AMDPM_HW_PSTATE) == 0) { 3564577cf37SConrad Meyer HWPSTATE_DEBUG(parent, "hwpstate enable bit is not set.\n"); 3574577cf37SConrad Meyer return; 3584577cf37SConrad Meyer } 3594577cf37SConrad Meyer 3604577cf37SConrad Meyer if (resource_disabled("hwpstate", 0)) 3614577cf37SConrad Meyer return; 3624577cf37SConrad Meyer 363d3a8f98aSAlexander Motin if (BUS_ADD_CHILD(parent, 10, "hwpstate", device_get_unit(parent)) 364d3a8f98aSAlexander Motin == NULL) 3654577cf37SConrad Meyer device_printf(parent, "hwpstate: add child failed\n"); 3664577cf37SConrad Meyer } 3674577cf37SConrad Meyer 3684577cf37SConrad Meyer static int 3694577cf37SConrad Meyer hwpstate_probe(device_t dev) 3704577cf37SConrad Meyer { 3714577cf37SConrad Meyer struct hwpstate_softc *sc; 3724577cf37SConrad Meyer device_t perf_dev; 3734577cf37SConrad Meyer uint64_t msr; 3744577cf37SConrad Meyer int error, type; 3754577cf37SConrad Meyer 3764577cf37SConrad Meyer /* 3774577cf37SConrad Meyer * Only hwpstate0. 3784577cf37SConrad Meyer * It goes well with acpi_throttle. 3794577cf37SConrad Meyer */ 3804577cf37SConrad Meyer if (device_get_unit(dev) != 0) 3814577cf37SConrad Meyer return (ENXIO); 3824577cf37SConrad Meyer 3834577cf37SConrad Meyer sc = device_get_softc(dev); 3844577cf37SConrad Meyer sc->dev = dev; 3854577cf37SConrad Meyer 3864577cf37SConrad Meyer /* 3874577cf37SConrad Meyer * Check if acpi_perf has INFO only flag. 3884577cf37SConrad Meyer */ 3894577cf37SConrad Meyer perf_dev = device_find_child(device_get_parent(dev), "acpi_perf", -1); 3904577cf37SConrad Meyer error = TRUE; 3914577cf37SConrad Meyer if (perf_dev && device_is_attached(perf_dev)) { 3924577cf37SConrad Meyer error = CPUFREQ_DRV_TYPE(perf_dev, &type); 3934577cf37SConrad Meyer if (error == 0) { 3944577cf37SConrad Meyer if ((type & CPUFREQ_FLAG_INFO_ONLY) == 0) { 3954577cf37SConrad Meyer /* 3964577cf37SConrad Meyer * If acpi_perf doesn't have INFO_ONLY flag, 3974577cf37SConrad Meyer * it will take care of pstate transitions. 3984577cf37SConrad Meyer */ 3994577cf37SConrad Meyer HWPSTATE_DEBUG(dev, "acpi_perf will take care of pstate transitions.\n"); 4004577cf37SConrad Meyer return (ENXIO); 4014577cf37SConrad Meyer } else { 4024577cf37SConrad Meyer /* 4034577cf37SConrad Meyer * If acpi_perf has INFO_ONLY flag, (_PCT has FFixedHW) 4044577cf37SConrad Meyer * we can get _PSS info from acpi_perf 4054577cf37SConrad Meyer * without going into ACPI. 4064577cf37SConrad Meyer */ 4074577cf37SConrad Meyer HWPSTATE_DEBUG(dev, "going to fetch info from acpi_perf\n"); 4084577cf37SConrad Meyer error = hwpstate_get_info_from_acpi_perf(dev, perf_dev); 4094577cf37SConrad Meyer } 4104577cf37SConrad Meyer } 4114577cf37SConrad Meyer } 4124577cf37SConrad Meyer 4134577cf37SConrad Meyer if (error == 0) { 4144577cf37SConrad Meyer /* 4154577cf37SConrad Meyer * Now we get _PSS info from acpi_perf without error. 4164577cf37SConrad Meyer * Let's check it. 4174577cf37SConrad Meyer */ 4184577cf37SConrad Meyer msr = rdmsr(MSR_AMD_10H_11H_LIMIT); 4194577cf37SConrad Meyer if (sc->cfnum != 1 + AMD_10H_11H_GET_PSTATE_MAX_VAL(msr)) { 4204577cf37SConrad Meyer HWPSTATE_DEBUG(dev, "MSR (%jd) and ACPI _PSS (%d)" 4214577cf37SConrad Meyer " count mismatch\n", (intmax_t)msr, sc->cfnum); 4224577cf37SConrad Meyer error = TRUE; 4234577cf37SConrad Meyer } 4244577cf37SConrad Meyer } 4254577cf37SConrad Meyer 4264577cf37SConrad Meyer /* 4274577cf37SConrad Meyer * If we cannot get info from acpi_perf, 4284577cf37SConrad Meyer * Let's get info from MSRs. 4294577cf37SConrad Meyer */ 4304577cf37SConrad Meyer if (error) 4314577cf37SConrad Meyer error = hwpstate_get_info_from_msr(dev); 4324577cf37SConrad Meyer if (error) 4334577cf37SConrad Meyer return (error); 4344577cf37SConrad Meyer 4354577cf37SConrad Meyer device_set_desc(dev, "Cool`n'Quiet 2.0"); 4364577cf37SConrad Meyer return (0); 4374577cf37SConrad Meyer } 4384577cf37SConrad Meyer 4394577cf37SConrad Meyer static int 4404577cf37SConrad Meyer hwpstate_attach(device_t dev) 4414577cf37SConrad Meyer { 4424577cf37SConrad Meyer 4434577cf37SConrad Meyer return (cpufreq_register(dev)); 4444577cf37SConrad Meyer } 4454577cf37SConrad Meyer 4464577cf37SConrad Meyer static int 4474577cf37SConrad Meyer hwpstate_get_info_from_msr(device_t dev) 4484577cf37SConrad Meyer { 4494577cf37SConrad Meyer struct hwpstate_softc *sc; 4504577cf37SConrad Meyer struct hwpstate_setting *hwpstate_set; 4514577cf37SConrad Meyer uint64_t msr; 4524577cf37SConrad Meyer int family, i, fid, did; 4534577cf37SConrad Meyer 4544577cf37SConrad Meyer family = CPUID_TO_FAMILY(cpu_id); 4554577cf37SConrad Meyer sc = device_get_softc(dev); 4564577cf37SConrad Meyer /* Get pstate count */ 4574577cf37SConrad Meyer msr = rdmsr(MSR_AMD_10H_11H_LIMIT); 4584577cf37SConrad Meyer sc->cfnum = 1 + AMD_10H_11H_GET_PSTATE_MAX_VAL(msr); 4594577cf37SConrad Meyer hwpstate_set = sc->hwpstate_settings; 4604577cf37SConrad Meyer for (i = 0; i < sc->cfnum; i++) { 4614577cf37SConrad Meyer msr = rdmsr(MSR_AMD_10H_11H_CONFIG + i); 4624577cf37SConrad Meyer if ((msr & ((uint64_t)1 << 63)) == 0) { 4634577cf37SConrad Meyer HWPSTATE_DEBUG(dev, "msr is not valid.\n"); 4644577cf37SConrad Meyer return (ENXIO); 4654577cf37SConrad Meyer } 4664577cf37SConrad Meyer did = AMD_10H_11H_CUR_DID(msr); 4674577cf37SConrad Meyer fid = AMD_10H_11H_CUR_FID(msr); 4684577cf37SConrad Meyer 469e74dd957SJohannes Totz hwpstate_set[i].volts = CPUFREQ_VAL_UNKNOWN; 470e74dd957SJohannes Totz hwpstate_set[i].power = CPUFREQ_VAL_UNKNOWN; 471e74dd957SJohannes Totz hwpstate_set[i].lat = CPUFREQ_VAL_UNKNOWN; 4724577cf37SConrad Meyer /* Convert fid/did to frequency. */ 4734577cf37SConrad Meyer switch (family) { 4744577cf37SConrad Meyer case 0x11: 4754577cf37SConrad Meyer hwpstate_set[i].freq = (100 * (fid + 0x08)) >> did; 4764577cf37SConrad Meyer break; 4774577cf37SConrad Meyer case 0x10: 4784577cf37SConrad Meyer case 0x12: 4794577cf37SConrad Meyer case 0x15: 4804577cf37SConrad Meyer case 0x16: 4814577cf37SConrad Meyer hwpstate_set[i].freq = (100 * (fid + 0x10)) >> did; 4824577cf37SConrad Meyer break; 4834577cf37SConrad Meyer case 0x17: 4844577cf37SConrad Meyer case 0x18: 48562156925SSHENGYI HONG case 0x19: 486*0e6e1f14SSHENGYI HONG case 0x1A: 487*0e6e1f14SSHENGYI HONG /* calculate freq */ 488*0e6e1f14SSHENGYI HONG if (family == 0x1A) { 489*0e6e1f14SSHENGYI HONG fid = AMD_1AH_CUR_FID(msr); 490*0e6e1f14SSHENGYI HONG /* 1Ah CPU don't use a divisor */ 491*0e6e1f14SSHENGYI HONG hwpstate_set[i].freq = fid; 492*0e6e1f14SSHENGYI HONG if (fid > 0x0f) 493*0e6e1f14SSHENGYI HONG hwpstate_set[i].freq *= 5; 494*0e6e1f14SSHENGYI HONG else { 495*0e6e1f14SSHENGYI HONG HWPSTATE_DEBUG(dev, 496*0e6e1f14SSHENGYI HONG "unexpected fid: %d\n", fid); 497*0e6e1f14SSHENGYI HONG return (ENXIO); 498*0e6e1f14SSHENGYI HONG } 499*0e6e1f14SSHENGYI HONG } else { 5004577cf37SConrad Meyer did = AMD_17H_CUR_DID(msr); 5014577cf37SConrad Meyer if (did == 0) { 502*0e6e1f14SSHENGYI HONG HWPSTATE_DEBUG(dev, 503*0e6e1f14SSHENGYI HONG "unexpected did: 0\n"); 5044577cf37SConrad Meyer did = 1; 5054577cf37SConrad Meyer } 5064577cf37SConrad Meyer fid = AMD_17H_CUR_FID(msr); 5074577cf37SConrad Meyer hwpstate_set[i].freq = (200 * fid) / did; 508*0e6e1f14SSHENGYI HONG } 509*0e6e1f14SSHENGYI HONG 510e74dd957SJohannes Totz /* Vid step is 6.25mV, so scale by 100. */ 511e74dd957SJohannes Totz hwpstate_set[i].volts = 512e74dd957SJohannes Totz (155000 - (625 * AMD_17H_CUR_VID(msr))) / 100; 513e74dd957SJohannes Totz /* 514e74dd957SJohannes Totz * Calculate current first. 515e74dd957SJohannes Totz * This equation is mentioned in 516e74dd957SJohannes Totz * "BKDG for AMD Family 15h Models 70h-7fh Processors", 517e74dd957SJohannes Totz * section 2.5.2.1.6. 518e74dd957SJohannes Totz */ 519e74dd957SJohannes Totz hwpstate_set[i].power = AMD_17H_CUR_IDD(msr) * 1000; 520*0e6e1f14SSHENGYI HONG hwpstate_set[i].power = hwpstate_amd_iscale( 521*0e6e1f14SSHENGYI HONG hwpstate_set[i].power, AMD_17H_CUR_IDIV(msr)); 522e74dd957SJohannes Totz hwpstate_set[i].power *= hwpstate_set[i].volts; 523e74dd957SJohannes Totz /* Milli amps * milli volts to milli watts. */ 524e74dd957SJohannes Totz hwpstate_set[i].power /= 1000; 5254577cf37SConrad Meyer break; 5264577cf37SConrad Meyer default: 5274577cf37SConrad Meyer HWPSTATE_DEBUG(dev, "get_info_from_msr: %s family" 5284577cf37SConrad Meyer " 0x%02x CPUs are not supported yet\n", 5294577cf37SConrad Meyer cpu_vendor_id == CPU_VENDOR_HYGON ? "Hygon" : "AMD", 5304577cf37SConrad Meyer family); 5314577cf37SConrad Meyer return (ENXIO); 5324577cf37SConrad Meyer } 5334577cf37SConrad Meyer hwpstate_set[i].pstate_id = i; 5344577cf37SConrad Meyer } 5354577cf37SConrad Meyer return (0); 5364577cf37SConrad Meyer } 5374577cf37SConrad Meyer 5384577cf37SConrad Meyer static int 5394577cf37SConrad Meyer hwpstate_get_info_from_acpi_perf(device_t dev, device_t perf_dev) 5404577cf37SConrad Meyer { 5414577cf37SConrad Meyer struct hwpstate_softc *sc; 5424577cf37SConrad Meyer struct cf_setting *perf_set; 5434577cf37SConrad Meyer struct hwpstate_setting *hwpstate_set; 5444577cf37SConrad Meyer int count, error, i; 5454577cf37SConrad Meyer 5464577cf37SConrad Meyer perf_set = malloc(MAX_SETTINGS * sizeof(*perf_set), M_TEMP, M_NOWAIT); 5474577cf37SConrad Meyer if (perf_set == NULL) { 5484577cf37SConrad Meyer HWPSTATE_DEBUG(dev, "nomem\n"); 5494577cf37SConrad Meyer return (ENOMEM); 5504577cf37SConrad Meyer } 5514577cf37SConrad Meyer /* 5524577cf37SConrad Meyer * Fetch settings from acpi_perf. 5534577cf37SConrad Meyer * Now it is attached, and has info only flag. 5544577cf37SConrad Meyer */ 5554577cf37SConrad Meyer count = MAX_SETTINGS; 5564577cf37SConrad Meyer error = CPUFREQ_DRV_SETTINGS(perf_dev, perf_set, &count); 5574577cf37SConrad Meyer if (error) { 5584577cf37SConrad Meyer HWPSTATE_DEBUG(dev, "error: CPUFREQ_DRV_SETTINGS.\n"); 5594577cf37SConrad Meyer goto out; 5604577cf37SConrad Meyer } 5614577cf37SConrad Meyer sc = device_get_softc(dev); 5624577cf37SConrad Meyer sc->cfnum = count; 5634577cf37SConrad Meyer hwpstate_set = sc->hwpstate_settings; 5644577cf37SConrad Meyer for (i = 0; i < count; i++) { 5654577cf37SConrad Meyer if (i == perf_set[i].spec[0]) { 5664577cf37SConrad Meyer hwpstate_set[i].pstate_id = i; 5674577cf37SConrad Meyer hwpstate_set[i].freq = perf_set[i].freq; 5684577cf37SConrad Meyer hwpstate_set[i].volts = perf_set[i].volts; 5694577cf37SConrad Meyer hwpstate_set[i].power = perf_set[i].power; 5704577cf37SConrad Meyer hwpstate_set[i].lat = perf_set[i].lat; 5714577cf37SConrad Meyer } else { 5724577cf37SConrad Meyer HWPSTATE_DEBUG(dev, "ACPI _PSS object mismatch.\n"); 5734577cf37SConrad Meyer error = ENXIO; 5744577cf37SConrad Meyer goto out; 5754577cf37SConrad Meyer } 5764577cf37SConrad Meyer } 5774577cf37SConrad Meyer out: 5784577cf37SConrad Meyer if (perf_set) 5794577cf37SConrad Meyer free(perf_set, M_TEMP); 5804577cf37SConrad Meyer return (error); 5814577cf37SConrad Meyer } 5824577cf37SConrad Meyer 5834577cf37SConrad Meyer static int 5844577cf37SConrad Meyer hwpstate_detach(device_t dev) 5854577cf37SConrad Meyer { 5864577cf37SConrad Meyer 5874577cf37SConrad Meyer hwpstate_goto_pstate(dev, 0); 5884577cf37SConrad Meyer return (cpufreq_unregister(dev)); 5894577cf37SConrad Meyer } 5904577cf37SConrad Meyer 5914577cf37SConrad Meyer static int 5924577cf37SConrad Meyer hwpstate_shutdown(device_t dev) 5934577cf37SConrad Meyer { 5944577cf37SConrad Meyer 5954577cf37SConrad Meyer /* hwpstate_goto_pstate(dev, 0); */ 5964577cf37SConrad Meyer return (0); 5974577cf37SConrad Meyer } 5984577cf37SConrad Meyer 5994577cf37SConrad Meyer static int 6004577cf37SConrad Meyer hwpstate_features(driver_t *driver, u_int *features) 6014577cf37SConrad Meyer { 6024577cf37SConrad Meyer 6034577cf37SConrad Meyer /* Notify the ACPI CPU that we support direct access to MSRs */ 6044577cf37SConrad Meyer *features = ACPI_CAP_PERF_MSRS; 6054577cf37SConrad Meyer return (0); 6064577cf37SConrad Meyer } 607