17150dfc7SNate Lawson /*- 27150dfc7SNate Lawson * Copyright (c) 2003-2005 Nate Lawson (SDG) 37150dfc7SNate Lawson * Copyright (c) 2001 Michael Smith 47150dfc7SNate Lawson * All rights reserved. 57150dfc7SNate Lawson * 67150dfc7SNate Lawson * Redistribution and use in source and binary forms, with or without 77150dfc7SNate Lawson * modification, are permitted provided that the following conditions 87150dfc7SNate Lawson * are met: 97150dfc7SNate Lawson * 1. Redistributions of source code must retain the above copyright 107150dfc7SNate Lawson * notice, this list of conditions and the following disclaimer. 117150dfc7SNate Lawson * 2. Redistributions in binary form must reproduce the above copyright 127150dfc7SNate Lawson * notice, this list of conditions and the following disclaimer in the 137150dfc7SNate Lawson * documentation and/or other materials provided with the distribution. 147150dfc7SNate Lawson * 157150dfc7SNate Lawson * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 167150dfc7SNate Lawson * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 177150dfc7SNate Lawson * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 187150dfc7SNate Lawson * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 197150dfc7SNate Lawson * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 207150dfc7SNate Lawson * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 217150dfc7SNate Lawson * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 227150dfc7SNate Lawson * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 237150dfc7SNate Lawson * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 247150dfc7SNate Lawson * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 257150dfc7SNate Lawson * SUCH DAMAGE. 267150dfc7SNate Lawson */ 277150dfc7SNate Lawson 287150dfc7SNate Lawson #include <sys/cdefs.h> 297150dfc7SNate Lawson __FBSDID("$FreeBSD$"); 307150dfc7SNate Lawson 317150dfc7SNate Lawson #include "opt_acpi.h" 327150dfc7SNate Lawson #include <sys/param.h> 337150dfc7SNate Lawson #include <sys/bus.h> 347150dfc7SNate Lawson #include <sys/cpu.h> 357150dfc7SNate Lawson #include <sys/kernel.h> 367150dfc7SNate Lawson #include <sys/malloc.h> 377150dfc7SNate Lawson #include <sys/module.h> 387150dfc7SNate Lawson #include <sys/rman.h> 397150dfc7SNate Lawson 407150dfc7SNate Lawson #include <machine/bus.h> 417150dfc7SNate Lawson 42129d3046SJung-uk Kim #include <contrib/dev/acpica/include/acpi.h> 43129d3046SJung-uk Kim 447150dfc7SNate Lawson #include <dev/acpica/acpivar.h> 457150dfc7SNate Lawson #include <dev/pci/pcivar.h> 467150dfc7SNate Lawson 477150dfc7SNate Lawson #include "cpufreq_if.h" 487150dfc7SNate Lawson 497150dfc7SNate Lawson /* 507150dfc7SNate Lawson * Throttling provides relative frequency control. It involves modulating 517150dfc7SNate Lawson * the clock so that the CPU is active for only a fraction of the normal 527150dfc7SNate Lawson * clock cycle. It does not change voltage and so is less efficient than 537150dfc7SNate Lawson * other mechanisms. Since it is relative, it can be used in addition to 547150dfc7SNate Lawson * absolute cpufreq drivers. We support the ACPI 2.0 specification. 557150dfc7SNate Lawson */ 567150dfc7SNate Lawson 577150dfc7SNate Lawson struct acpi_throttle_softc { 587150dfc7SNate Lawson device_t cpu_dev; 597150dfc7SNate Lawson ACPI_HANDLE cpu_handle; 607150dfc7SNate Lawson uint32_t cpu_p_blk; /* ACPI P_BLK location */ 617150dfc7SNate Lawson uint32_t cpu_p_blk_len; /* P_BLK length (must be 6). */ 627150dfc7SNate Lawson struct resource *cpu_p_cnt; /* Throttling control register */ 637150dfc7SNate Lawson int cpu_p_type; /* Resource type for cpu_p_cnt. */ 643bd74b84SNate Lawson uint32_t cpu_thr_state; /* Current throttle setting. */ 657150dfc7SNate Lawson }; 667150dfc7SNate Lawson 677150dfc7SNate Lawson #define THR_GET_REG(reg) \ 687150dfc7SNate Lawson (bus_space_read_4(rman_get_bustag((reg)), \ 697150dfc7SNate Lawson rman_get_bushandle((reg)), 0)) 707150dfc7SNate Lawson #define THR_SET_REG(reg, val) \ 717150dfc7SNate Lawson (bus_space_write_4(rman_get_bustag((reg)), \ 727150dfc7SNate Lawson rman_get_bushandle((reg)), 0, (val))) 737150dfc7SNate Lawson 747150dfc7SNate Lawson /* 757150dfc7SNate Lawson * Speeds are stored in counts, from 1 to CPU_MAX_SPEED, and 767150dfc7SNate Lawson * reported to the user in hundredths of a percent. 777150dfc7SNate Lawson */ 787150dfc7SNate Lawson #define CPU_MAX_SPEED (1 << cpu_duty_width) 797150dfc7SNate Lawson #define CPU_SPEED_PERCENT(x) ((10000 * (x)) / CPU_MAX_SPEED) 807150dfc7SNate Lawson #define CPU_SPEED_PRINTABLE(x) (CPU_SPEED_PERCENT(x) / 10), \ 817150dfc7SNate Lawson (CPU_SPEED_PERCENT(x) % 10) 827150dfc7SNate Lawson #define CPU_P_CNT_THT_EN (1<<4) 837150dfc7SNate Lawson #define CPU_QUIRK_NO_THROTTLE (1<<1) /* Throttling is not usable. */ 847150dfc7SNate Lawson 857150dfc7SNate Lawson #define PCI_VENDOR_INTEL 0x8086 867150dfc7SNate Lawson #define PCI_DEVICE_82371AB_3 0x7113 /* PIIX4 chipset for quirks. */ 877150dfc7SNate Lawson #define PCI_REVISION_A_STEP 0 887150dfc7SNate Lawson #define PCI_REVISION_B_STEP 1 897150dfc7SNate Lawson 907150dfc7SNate Lawson static uint32_t cpu_duty_offset; /* Offset in P_CNT of throttle val. */ 917150dfc7SNate Lawson static uint32_t cpu_duty_width; /* Bit width of throttle value. */ 927150dfc7SNate Lawson static int thr_rid; /* Driver-wide resource id. */ 937150dfc7SNate Lawson static int thr_quirks; /* Indicate any hardware bugs. */ 947150dfc7SNate Lawson 957150dfc7SNate Lawson static void acpi_throttle_identify(driver_t *driver, device_t parent); 967150dfc7SNate Lawson static int acpi_throttle_probe(device_t dev); 977150dfc7SNate Lawson static int acpi_throttle_attach(device_t dev); 987150dfc7SNate Lawson static int acpi_throttle_evaluate(struct acpi_throttle_softc *sc); 999cf4cabeSJung-uk Kim static void acpi_throttle_quirks(struct acpi_throttle_softc *sc); 1007150dfc7SNate Lawson static int acpi_thr_settings(device_t dev, struct cf_setting *sets, 101e94a0c1aSNate Lawson int *count); 1027150dfc7SNate Lawson static int acpi_thr_set(device_t dev, const struct cf_setting *set); 1037150dfc7SNate Lawson static int acpi_thr_get(device_t dev, struct cf_setting *set); 104e94a0c1aSNate Lawson static int acpi_thr_type(device_t dev, int *type); 1057150dfc7SNate Lawson 1067150dfc7SNate Lawson static device_method_t acpi_throttle_methods[] = { 1077150dfc7SNate Lawson /* Device interface */ 1087150dfc7SNate Lawson DEVMETHOD(device_identify, acpi_throttle_identify), 1097150dfc7SNate Lawson DEVMETHOD(device_probe, acpi_throttle_probe), 1107150dfc7SNate Lawson DEVMETHOD(device_attach, acpi_throttle_attach), 1117150dfc7SNate Lawson 1127150dfc7SNate Lawson /* cpufreq interface */ 1137150dfc7SNate Lawson DEVMETHOD(cpufreq_drv_set, acpi_thr_set), 1147150dfc7SNate Lawson DEVMETHOD(cpufreq_drv_get, acpi_thr_get), 115e94a0c1aSNate Lawson DEVMETHOD(cpufreq_drv_type, acpi_thr_type), 1167150dfc7SNate Lawson DEVMETHOD(cpufreq_drv_settings, acpi_thr_settings), 11761bfd867SSofian Brabez DEVMETHOD_END 1187150dfc7SNate Lawson }; 1197150dfc7SNate Lawson 1207150dfc7SNate Lawson static driver_t acpi_throttle_driver = { 1217150dfc7SNate Lawson "acpi_throttle", 1227150dfc7SNate Lawson acpi_throttle_methods, 1237150dfc7SNate Lawson sizeof(struct acpi_throttle_softc), 1247150dfc7SNate Lawson }; 1257150dfc7SNate Lawson 1267150dfc7SNate Lawson static devclass_t acpi_throttle_devclass; 1277150dfc7SNate Lawson DRIVER_MODULE(acpi_throttle, cpu, acpi_throttle_driver, acpi_throttle_devclass, 1287150dfc7SNate Lawson 0, 0); 1297150dfc7SNate Lawson 1307150dfc7SNate Lawson static void 1317150dfc7SNate Lawson acpi_throttle_identify(driver_t *driver, device_t parent) 1327150dfc7SNate Lawson { 1333bd74b84SNate Lawson ACPI_BUFFER buf; 1343bd74b84SNate Lawson ACPI_HANDLE handle; 1353bd74b84SNate Lawson ACPI_OBJECT *obj; 1367150dfc7SNate Lawson 1377150dfc7SNate Lawson /* Make sure we're not being doubly invoked. */ 1385e517feeSNate Lawson if (device_find_child(parent, "acpi_throttle", -1)) 1397150dfc7SNate Lawson return; 1407150dfc7SNate Lawson 1417150dfc7SNate Lawson /* Check for a valid duty width and parent CPU type. */ 1423bd74b84SNate Lawson handle = acpi_get_handle(parent); 1433bd74b84SNate Lawson if (handle == NULL) 1447150dfc7SNate Lawson return; 1452be4e471SJung-uk Kim if (AcpiGbl_FADT.DutyWidth == 0 || 1467150dfc7SNate Lawson acpi_get_type(parent) != ACPI_TYPE_PROCESSOR) 1477150dfc7SNate Lawson return; 1483bd74b84SNate Lawson 1493bd74b84SNate Lawson /* 1503bd74b84SNate Lawson * Add a child if there's a non-NULL P_BLK and correct length, or 1513bd74b84SNate Lawson * if the _PTC method is present. 1523bd74b84SNate Lawson */ 1533bd74b84SNate Lawson buf.Pointer = NULL; 1543bd74b84SNate Lawson buf.Length = ACPI_ALLOCATE_BUFFER; 1553bd74b84SNate Lawson if (ACPI_FAILURE(AcpiEvaluateObject(handle, NULL, NULL, &buf))) 1563bd74b84SNate Lawson return; 1573bd74b84SNate Lawson obj = (ACPI_OBJECT *)buf.Pointer; 1583bd74b84SNate Lawson if ((obj->Processor.PblkAddress && obj->Processor.PblkLength >= 4) || 1593bd74b84SNate Lawson ACPI_SUCCESS(AcpiEvaluateObject(handle, "_PTC", NULL, NULL))) { 160*d3a8f98aSAlexander Motin if (BUS_ADD_CHILD(parent, 0, "acpi_throttle", 161*d3a8f98aSAlexander Motin device_get_unit(parent)) == NULL) 1623bd74b84SNate Lawson device_printf(parent, "add throttle child failed\n"); 1633bd74b84SNate Lawson } 1643bd74b84SNate Lawson AcpiOsFree(obj); 1657150dfc7SNate Lawson } 1667150dfc7SNate Lawson 1677150dfc7SNate Lawson static int 1687150dfc7SNate Lawson acpi_throttle_probe(device_t dev) 1697150dfc7SNate Lawson { 1707150dfc7SNate Lawson 171a8de37b0SEitan Adler if (resource_disabled("acpi_throttle", 0)) 172a8de37b0SEitan Adler return (ENXIO); 173a8de37b0SEitan Adler 1745e517feeSNate Lawson /* 1755e517feeSNate Lawson * On i386 platforms at least, ACPI throttling is accomplished by 1765e517feeSNate Lawson * the chipset modulating the STPCLK# pin based on the duty cycle. 1775e517feeSNate Lawson * Since p4tcc uses the same mechanism (but internal to the CPU), 1785e517feeSNate Lawson * we disable acpi_throttle when p4tcc is also present. 1795e517feeSNate Lawson */ 1800a133d67SNate Lawson if (device_find_child(device_get_parent(dev), "p4tcc", -1) && 1810a133d67SNate Lawson !resource_disabled("p4tcc", 0)) 1825e517feeSNate Lawson return (ENXIO); 1835e517feeSNate Lawson 1847150dfc7SNate Lawson device_set_desc(dev, "ACPI CPU Throttling"); 1857150dfc7SNate Lawson return (0); 1867150dfc7SNate Lawson } 1877150dfc7SNate Lawson 1887150dfc7SNate Lawson static int 1897150dfc7SNate Lawson acpi_throttle_attach(device_t dev) 1907150dfc7SNate Lawson { 1917150dfc7SNate Lawson struct acpi_throttle_softc *sc; 1920a133d67SNate Lawson struct cf_setting set; 1937150dfc7SNate Lawson ACPI_BUFFER buf; 1947150dfc7SNate Lawson ACPI_OBJECT *obj; 1957150dfc7SNate Lawson ACPI_STATUS status; 1963bd74b84SNate Lawson int error; 1977150dfc7SNate Lawson 1987150dfc7SNate Lawson sc = device_get_softc(dev); 1997150dfc7SNate Lawson sc->cpu_dev = dev; 2007150dfc7SNate Lawson sc->cpu_handle = acpi_get_handle(dev); 2017150dfc7SNate Lawson 2027150dfc7SNate Lawson buf.Pointer = NULL; 2037150dfc7SNate Lawson buf.Length = ACPI_ALLOCATE_BUFFER; 2047150dfc7SNate Lawson status = AcpiEvaluateObject(sc->cpu_handle, NULL, NULL, &buf); 2057150dfc7SNate Lawson if (ACPI_FAILURE(status)) { 2067150dfc7SNate Lawson device_printf(dev, "attach failed to get Processor obj - %s\n", 2077150dfc7SNate Lawson AcpiFormatException(status)); 2087150dfc7SNate Lawson return (ENXIO); 2097150dfc7SNate Lawson } 2107150dfc7SNate Lawson obj = (ACPI_OBJECT *)buf.Pointer; 2117150dfc7SNate Lawson sc->cpu_p_blk = obj->Processor.PblkAddress; 2127150dfc7SNate Lawson sc->cpu_p_blk_len = obj->Processor.PblkLength; 2137150dfc7SNate Lawson AcpiOsFree(obj); 2147150dfc7SNate Lawson 2157150dfc7SNate Lawson /* If this is the first device probed, check for quirks. */ 2167150dfc7SNate Lawson if (device_get_unit(dev) == 0) 2177150dfc7SNate Lawson acpi_throttle_quirks(sc); 2187150dfc7SNate Lawson 2193bd74b84SNate Lawson /* Attempt to attach the actual throttling register. */ 2203bd74b84SNate Lawson error = acpi_throttle_evaluate(sc); 2213bd74b84SNate Lawson if (error) 2223bd74b84SNate Lawson return (error); 2233bd74b84SNate Lawson 2240a133d67SNate Lawson /* 2250a133d67SNate Lawson * Set our initial frequency to the highest since some systems 2260a133d67SNate Lawson * seem to boot with this at the lowest setting. 2270a133d67SNate Lawson */ 2280a133d67SNate Lawson set.freq = 10000; 2290a133d67SNate Lawson acpi_thr_set(dev, &set); 2300a133d67SNate Lawson 2313bd74b84SNate Lawson /* Everything went ok, register with cpufreq(4). */ 2323bd74b84SNate Lawson cpufreq_register(dev); 2333bd74b84SNate Lawson return (0); 2347150dfc7SNate Lawson } 2357150dfc7SNate Lawson 2367150dfc7SNate Lawson static int 2377150dfc7SNate Lawson acpi_throttle_evaluate(struct acpi_throttle_softc *sc) 2387150dfc7SNate Lawson { 2397150dfc7SNate Lawson uint32_t duty_end; 2407150dfc7SNate Lawson ACPI_BUFFER buf; 2417150dfc7SNate Lawson ACPI_OBJECT obj; 2427150dfc7SNate Lawson ACPI_GENERIC_ADDRESS gas; 2437150dfc7SNate Lawson ACPI_STATUS status; 2447150dfc7SNate Lawson 2457150dfc7SNate Lawson /* Get throttling parameters from the FADT. 0 means not supported. */ 2467150dfc7SNate Lawson if (device_get_unit(sc->cpu_dev) == 0) { 2472be4e471SJung-uk Kim cpu_duty_offset = AcpiGbl_FADT.DutyOffset; 2482be4e471SJung-uk Kim cpu_duty_width = AcpiGbl_FADT.DutyWidth; 2497150dfc7SNate Lawson } 2507150dfc7SNate Lawson if (cpu_duty_width == 0 || (thr_quirks & CPU_QUIRK_NO_THROTTLE) != 0) 2517150dfc7SNate Lawson return (ENXIO); 2527150dfc7SNate Lawson 2537150dfc7SNate Lawson /* Validate the duty offset/width. */ 2547150dfc7SNate Lawson duty_end = cpu_duty_offset + cpu_duty_width - 1; 2557150dfc7SNate Lawson if (duty_end > 31) { 2567150dfc7SNate Lawson device_printf(sc->cpu_dev, 2577150dfc7SNate Lawson "CLK_VAL field overflows P_CNT register\n"); 2587150dfc7SNate Lawson return (ENXIO); 2597150dfc7SNate Lawson } 2607150dfc7SNate Lawson if (cpu_duty_offset <= 4 && duty_end >= 4) { 2617150dfc7SNate Lawson device_printf(sc->cpu_dev, 2627150dfc7SNate Lawson "CLK_VAL field overlaps THT_EN bit\n"); 2637150dfc7SNate Lawson return (ENXIO); 2647150dfc7SNate Lawson } 2657150dfc7SNate Lawson 2667150dfc7SNate Lawson /* 2677150dfc7SNate Lawson * If not present, fall back to using the processor's P_BLK to find 2687150dfc7SNate Lawson * the P_CNT register. 2697150dfc7SNate Lawson * 2707150dfc7SNate Lawson * Note that some systems seem to duplicate the P_BLK pointer 2717150dfc7SNate Lawson * across multiple CPUs, so not getting the resource is not fatal. 2727150dfc7SNate Lawson */ 2737150dfc7SNate Lawson buf.Pointer = &obj; 2747150dfc7SNate Lawson buf.Length = sizeof(obj); 2757150dfc7SNate Lawson status = AcpiEvaluateObject(sc->cpu_handle, "_PTC", NULL, &buf); 2767150dfc7SNate Lawson if (ACPI_SUCCESS(status)) { 2777150dfc7SNate Lawson if (obj.Buffer.Length < sizeof(ACPI_GENERIC_ADDRESS) + 3) { 2787150dfc7SNate Lawson device_printf(sc->cpu_dev, "_PTC buffer too small\n"); 2797150dfc7SNate Lawson return (ENXIO); 2807150dfc7SNate Lawson } 2817150dfc7SNate Lawson memcpy(&gas, obj.Buffer.Pointer + 3, sizeof(gas)); 2827150dfc7SNate Lawson acpi_bus_alloc_gas(sc->cpu_dev, &sc->cpu_p_type, &thr_rid, 283907b6777SNate Lawson &gas, &sc->cpu_p_cnt, 0); 2847150dfc7SNate Lawson if (sc->cpu_p_cnt != NULL && bootverbose) { 2857150dfc7SNate Lawson device_printf(sc->cpu_dev, "P_CNT from _PTC %#jx\n", 2867150dfc7SNate Lawson gas.Address); 2877150dfc7SNate Lawson } 2887150dfc7SNate Lawson } 2897150dfc7SNate Lawson 2907150dfc7SNate Lawson /* If _PTC not present or other failure, try the P_BLK. */ 2917150dfc7SNate Lawson if (sc->cpu_p_cnt == NULL) { 2927150dfc7SNate Lawson /* 2937150dfc7SNate Lawson * The spec says P_BLK must be 6 bytes long. However, some 2947150dfc7SNate Lawson * systems use it to indicate a fractional set of features 2957150dfc7SNate Lawson * present so we take anything >= 4. 2967150dfc7SNate Lawson */ 2977150dfc7SNate Lawson if (sc->cpu_p_blk_len < 4) 2987150dfc7SNate Lawson return (ENXIO); 2997150dfc7SNate Lawson gas.Address = sc->cpu_p_blk; 3002be4e471SJung-uk Kim gas.SpaceId = ACPI_ADR_SPACE_SYSTEM_IO; 3012be4e471SJung-uk Kim gas.BitWidth = 32; 3027150dfc7SNate Lawson acpi_bus_alloc_gas(sc->cpu_dev, &sc->cpu_p_type, &thr_rid, 303907b6777SNate Lawson &gas, &sc->cpu_p_cnt, 0); 3047150dfc7SNate Lawson if (sc->cpu_p_cnt != NULL) { 3057150dfc7SNate Lawson if (bootverbose) 3067150dfc7SNate Lawson device_printf(sc->cpu_dev, 3077150dfc7SNate Lawson "P_CNT from P_BLK %#x\n", sc->cpu_p_blk); 3087150dfc7SNate Lawson } else { 3097150dfc7SNate Lawson device_printf(sc->cpu_dev, "failed to attach P_CNT\n"); 3107150dfc7SNate Lawson return (ENXIO); 3117150dfc7SNate Lawson } 3127150dfc7SNate Lawson } 3137150dfc7SNate Lawson thr_rid++; 3147150dfc7SNate Lawson 3157150dfc7SNate Lawson return (0); 3167150dfc7SNate Lawson } 3177150dfc7SNate Lawson 3189cf4cabeSJung-uk Kim static void 3197150dfc7SNate Lawson acpi_throttle_quirks(struct acpi_throttle_softc *sc) 3207150dfc7SNate Lawson { 3219cf4cabeSJung-uk Kim #ifdef __i386__ 3227150dfc7SNate Lawson device_t acpi_dev; 3237150dfc7SNate Lawson 3247150dfc7SNate Lawson /* Look for various quirks of the PIIX4 part. */ 3257150dfc7SNate Lawson acpi_dev = pci_find_device(PCI_VENDOR_INTEL, PCI_DEVICE_82371AB_3); 3267150dfc7SNate Lawson if (acpi_dev) { 3277150dfc7SNate Lawson switch (pci_get_revid(acpi_dev)) { 3287150dfc7SNate Lawson /* 3297150dfc7SNate Lawson * Disable throttling control on PIIX4 A and B-step. 3307150dfc7SNate Lawson * See specification changes #13 ("Manual Throttle Duty Cycle") 3317150dfc7SNate Lawson * and #14 ("Enabling and Disabling Manual Throttle"), plus 3327150dfc7SNate Lawson * erratum #5 ("STPCLK# Deassertion Time") from the January 3337150dfc7SNate Lawson * 2002 PIIX4 specification update. Note that few (if any) 3347150dfc7SNate Lawson * mobile systems ever used this part. 3357150dfc7SNate Lawson */ 3367150dfc7SNate Lawson case PCI_REVISION_A_STEP: 3377150dfc7SNate Lawson case PCI_REVISION_B_STEP: 3387150dfc7SNate Lawson thr_quirks |= CPU_QUIRK_NO_THROTTLE; 3397150dfc7SNate Lawson break; 3407150dfc7SNate Lawson default: 3417150dfc7SNate Lawson break; 3427150dfc7SNate Lawson } 3437150dfc7SNate Lawson } 3449cf4cabeSJung-uk Kim #endif 3457150dfc7SNate Lawson } 3467150dfc7SNate Lawson 3477150dfc7SNate Lawson static int 348e94a0c1aSNate Lawson acpi_thr_settings(device_t dev, struct cf_setting *sets, int *count) 3497150dfc7SNate Lawson { 3507150dfc7SNate Lawson int i, speed; 3517150dfc7SNate Lawson 3527150dfc7SNate Lawson if (sets == NULL || count == NULL) 3537150dfc7SNate Lawson return (EINVAL); 3547150dfc7SNate Lawson if (*count < CPU_MAX_SPEED) 355ededc31dSNate Lawson return (E2BIG); 3567150dfc7SNate Lawson 3577150dfc7SNate Lawson /* Return a list of valid settings for this driver. */ 3587150dfc7SNate Lawson memset(sets, CPUFREQ_VAL_UNKNOWN, sizeof(*sets) * CPU_MAX_SPEED); 3597150dfc7SNate Lawson for (i = 0, speed = CPU_MAX_SPEED; speed != 0; i++, speed--) { 3607150dfc7SNate Lawson sets[i].freq = CPU_SPEED_PERCENT(speed); 3617150dfc7SNate Lawson sets[i].dev = dev; 3627150dfc7SNate Lawson } 3637150dfc7SNate Lawson *count = CPU_MAX_SPEED; 3647150dfc7SNate Lawson 3657150dfc7SNate Lawson return (0); 3667150dfc7SNate Lawson } 3677150dfc7SNate Lawson 3687150dfc7SNate Lawson static int 3697150dfc7SNate Lawson acpi_thr_set(device_t dev, const struct cf_setting *set) 3707150dfc7SNate Lawson { 3717150dfc7SNate Lawson struct acpi_throttle_softc *sc; 3727150dfc7SNate Lawson uint32_t clk_val, p_cnt, speed; 3737150dfc7SNate Lawson 3747150dfc7SNate Lawson if (set == NULL) 3757150dfc7SNate Lawson return (EINVAL); 3767150dfc7SNate Lawson sc = device_get_softc(dev); 3777150dfc7SNate Lawson 3787150dfc7SNate Lawson /* 3797150dfc7SNate Lawson * Validate requested state converts to a duty cycle that is an 3807150dfc7SNate Lawson * integer from [1 .. CPU_MAX_SPEED]. 3817150dfc7SNate Lawson */ 3827150dfc7SNate Lawson speed = set->freq * CPU_MAX_SPEED / 10000; 3837150dfc7SNate Lawson if (speed * 10000 != set->freq * CPU_MAX_SPEED || 3847150dfc7SNate Lawson speed < 1 || speed > CPU_MAX_SPEED) 3857150dfc7SNate Lawson return (EINVAL); 3867150dfc7SNate Lawson 3877150dfc7SNate Lawson /* If we're at this setting, don't bother applying it again. */ 3883bd74b84SNate Lawson if (speed == sc->cpu_thr_state) 3897150dfc7SNate Lawson return (0); 3907150dfc7SNate Lawson 3917150dfc7SNate Lawson /* Get the current P_CNT value and disable throttling */ 3927150dfc7SNate Lawson p_cnt = THR_GET_REG(sc->cpu_p_cnt); 3937150dfc7SNate Lawson p_cnt &= ~CPU_P_CNT_THT_EN; 3947150dfc7SNate Lawson THR_SET_REG(sc->cpu_p_cnt, p_cnt); 3957150dfc7SNate Lawson 3967150dfc7SNate Lawson /* If we're at maximum speed, that's all */ 3977150dfc7SNate Lawson if (speed < CPU_MAX_SPEED) { 3987150dfc7SNate Lawson /* Mask the old CLK_VAL off and OR in the new value */ 3997150dfc7SNate Lawson clk_val = (CPU_MAX_SPEED - 1) << cpu_duty_offset; 4007150dfc7SNate Lawson p_cnt &= ~clk_val; 4017150dfc7SNate Lawson p_cnt |= (speed << cpu_duty_offset); 4027150dfc7SNate Lawson 4037150dfc7SNate Lawson /* Write the new P_CNT value and then enable throttling */ 4047150dfc7SNate Lawson THR_SET_REG(sc->cpu_p_cnt, p_cnt); 4057150dfc7SNate Lawson p_cnt |= CPU_P_CNT_THT_EN; 4067150dfc7SNate Lawson THR_SET_REG(sc->cpu_p_cnt, p_cnt); 4077150dfc7SNate Lawson } 4083bd74b84SNate Lawson sc->cpu_thr_state = speed; 4097150dfc7SNate Lawson 4107150dfc7SNate Lawson return (0); 4117150dfc7SNate Lawson } 4127150dfc7SNate Lawson 4137150dfc7SNate Lawson static int 4147150dfc7SNate Lawson acpi_thr_get(device_t dev, struct cf_setting *set) 4157150dfc7SNate Lawson { 4167150dfc7SNate Lawson struct acpi_throttle_softc *sc; 4177150dfc7SNate Lawson uint32_t p_cnt, clk_val; 4187150dfc7SNate Lawson 4197150dfc7SNate Lawson if (set == NULL) 4207150dfc7SNate Lawson return (EINVAL); 4217150dfc7SNate Lawson sc = device_get_softc(dev); 4227150dfc7SNate Lawson 4237150dfc7SNate Lawson /* Get the current throttling setting from P_CNT. */ 4247150dfc7SNate Lawson p_cnt = THR_GET_REG(sc->cpu_p_cnt); 4257150dfc7SNate Lawson clk_val = (p_cnt >> cpu_duty_offset) & (CPU_MAX_SPEED - 1); 4263bd74b84SNate Lawson sc->cpu_thr_state = clk_val; 4277150dfc7SNate Lawson 4287150dfc7SNate Lawson memset(set, CPUFREQ_VAL_UNKNOWN, sizeof(*set)); 4297150dfc7SNate Lawson set->freq = CPU_SPEED_PERCENT(clk_val); 4307150dfc7SNate Lawson set->dev = dev; 4317150dfc7SNate Lawson 4327150dfc7SNate Lawson return (0); 4337150dfc7SNate Lawson } 434e94a0c1aSNate Lawson 435e94a0c1aSNate Lawson static int 436e94a0c1aSNate Lawson acpi_thr_type(device_t dev, int *type) 437e94a0c1aSNate Lawson { 438e94a0c1aSNate Lawson 439e94a0c1aSNate Lawson if (type == NULL) 440e94a0c1aSNate Lawson return (EINVAL); 441e94a0c1aSNate Lawson 442e94a0c1aSNate Lawson *type = CPUFREQ_TYPE_RELATIVE; 443e94a0c1aSNate Lawson return (0); 444e94a0c1aSNate Lawson } 445