xref: /freebsd/sys/dev/acpica/acpi_throttle.c (revision 685dc743dc3b5645e34836464128e1c0558b404b)
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 #include "opt_acpi.h"
307150dfc7SNate Lawson #include <sys/param.h>
317150dfc7SNate Lawson #include <sys/bus.h>
327150dfc7SNate Lawson #include <sys/cpu.h>
337150dfc7SNate Lawson #include <sys/kernel.h>
347150dfc7SNate Lawson #include <sys/malloc.h>
357150dfc7SNate Lawson #include <sys/module.h>
367150dfc7SNate Lawson #include <sys/rman.h>
377150dfc7SNate Lawson 
387150dfc7SNate Lawson #include <machine/bus.h>
397150dfc7SNate Lawson 
40129d3046SJung-uk Kim #include <contrib/dev/acpica/include/acpi.h>
41129d3046SJung-uk Kim 
427150dfc7SNate Lawson #include <dev/acpica/acpivar.h>
437150dfc7SNate Lawson #include <dev/pci/pcivar.h>
447150dfc7SNate Lawson 
457150dfc7SNate Lawson #include "cpufreq_if.h"
467150dfc7SNate Lawson 
477150dfc7SNate Lawson /*
487150dfc7SNate Lawson  * Throttling provides relative frequency control.  It involves modulating
497150dfc7SNate Lawson  * the clock so that the CPU is active for only a fraction of the normal
507150dfc7SNate Lawson  * clock cycle.  It does not change voltage and so is less efficient than
517150dfc7SNate Lawson  * other mechanisms.  Since it is relative, it can be used in addition to
527150dfc7SNate Lawson  * absolute cpufreq drivers.  We support the ACPI 2.0 specification.
537150dfc7SNate Lawson  */
547150dfc7SNate Lawson 
557150dfc7SNate Lawson struct acpi_throttle_softc {
567150dfc7SNate Lawson 	device_t	 cpu_dev;
577150dfc7SNate Lawson 	ACPI_HANDLE	 cpu_handle;
587150dfc7SNate Lawson 	uint32_t	 cpu_p_blk;	/* ACPI P_BLK location */
597150dfc7SNate Lawson 	uint32_t	 cpu_p_blk_len;	/* P_BLK length (must be 6). */
607150dfc7SNate Lawson 	struct resource	*cpu_p_cnt;	/* Throttling control register */
617150dfc7SNate Lawson 	int		 cpu_p_type;	/* Resource type for cpu_p_cnt. */
623bd74b84SNate Lawson 	uint32_t	 cpu_thr_state;	/* Current throttle setting. */
637150dfc7SNate Lawson };
647150dfc7SNate Lawson 
657150dfc7SNate Lawson #define THR_GET_REG(reg) 					\
667150dfc7SNate Lawson 	(bus_space_read_4(rman_get_bustag((reg)), 		\
677150dfc7SNate Lawson 			  rman_get_bushandle((reg)), 0))
687150dfc7SNate Lawson #define THR_SET_REG(reg, val)					\
697150dfc7SNate Lawson 	(bus_space_write_4(rman_get_bustag((reg)), 		\
707150dfc7SNate Lawson 			   rman_get_bushandle((reg)), 0, (val)))
717150dfc7SNate Lawson 
727150dfc7SNate Lawson /*
737150dfc7SNate Lawson  * Speeds are stored in counts, from 1 to CPU_MAX_SPEED, and
747150dfc7SNate Lawson  * reported to the user in hundredths of a percent.
757150dfc7SNate Lawson  */
767150dfc7SNate Lawson #define CPU_MAX_SPEED		(1 << cpu_duty_width)
777150dfc7SNate Lawson #define CPU_SPEED_PERCENT(x)	((10000 * (x)) / CPU_MAX_SPEED)
787150dfc7SNate Lawson #define CPU_SPEED_PRINTABLE(x)	(CPU_SPEED_PERCENT(x) / 10),	\
797150dfc7SNate Lawson 				(CPU_SPEED_PERCENT(x) % 10)
807150dfc7SNate Lawson #define CPU_P_CNT_THT_EN	(1<<4)
817150dfc7SNate Lawson #define CPU_QUIRK_NO_THROTTLE	(1<<1)	/* Throttling is not usable. */
827150dfc7SNate Lawson 
837150dfc7SNate Lawson #define PCI_VENDOR_INTEL	0x8086
847150dfc7SNate Lawson #define PCI_DEVICE_82371AB_3	0x7113	/* PIIX4 chipset for quirks. */
857150dfc7SNate Lawson #define PCI_REVISION_A_STEP	0
867150dfc7SNate Lawson #define PCI_REVISION_B_STEP	1
877150dfc7SNate Lawson 
887150dfc7SNate Lawson static uint32_t	cpu_duty_offset;	/* Offset in P_CNT of throttle val. */
897150dfc7SNate Lawson static uint32_t	cpu_duty_width;		/* Bit width of throttle value. */
907150dfc7SNate Lawson static int	thr_rid;		/* Driver-wide resource id. */
917150dfc7SNate Lawson static int	thr_quirks;		/* Indicate any hardware bugs. */
927150dfc7SNate Lawson 
937150dfc7SNate Lawson static void	acpi_throttle_identify(driver_t *driver, device_t parent);
947150dfc7SNate Lawson static int	acpi_throttle_probe(device_t dev);
957150dfc7SNate Lawson static int	acpi_throttle_attach(device_t dev);
967150dfc7SNate Lawson static int	acpi_throttle_evaluate(struct acpi_throttle_softc *sc);
979cf4cabeSJung-uk Kim static void	acpi_throttle_quirks(struct acpi_throttle_softc *sc);
987150dfc7SNate Lawson static int	acpi_thr_settings(device_t dev, struct cf_setting *sets,
99e94a0c1aSNate Lawson 		    int *count);
1007150dfc7SNate Lawson static int	acpi_thr_set(device_t dev, const struct cf_setting *set);
1017150dfc7SNate Lawson static int	acpi_thr_get(device_t dev, struct cf_setting *set);
102e94a0c1aSNate Lawson static int	acpi_thr_type(device_t dev, int *type);
1037150dfc7SNate Lawson 
1047150dfc7SNate Lawson static device_method_t acpi_throttle_methods[] = {
1057150dfc7SNate Lawson 	/* Device interface */
1067150dfc7SNate Lawson 	DEVMETHOD(device_identify,	acpi_throttle_identify),
1077150dfc7SNate Lawson 	DEVMETHOD(device_probe,		acpi_throttle_probe),
1087150dfc7SNate Lawson 	DEVMETHOD(device_attach,	acpi_throttle_attach),
1097150dfc7SNate Lawson 
1107150dfc7SNate Lawson 	/* cpufreq interface */
1117150dfc7SNate Lawson 	DEVMETHOD(cpufreq_drv_set,	acpi_thr_set),
1127150dfc7SNate Lawson 	DEVMETHOD(cpufreq_drv_get,	acpi_thr_get),
113e94a0c1aSNate Lawson 	DEVMETHOD(cpufreq_drv_type,	acpi_thr_type),
1147150dfc7SNate Lawson 	DEVMETHOD(cpufreq_drv_settings,	acpi_thr_settings),
11561bfd867SSofian Brabez 	DEVMETHOD_END
1167150dfc7SNate Lawson };
1177150dfc7SNate Lawson 
1187150dfc7SNate Lawson static driver_t acpi_throttle_driver = {
1197150dfc7SNate Lawson 	"acpi_throttle",
1207150dfc7SNate Lawson 	acpi_throttle_methods,
1217150dfc7SNate Lawson 	sizeof(struct acpi_throttle_softc),
1227150dfc7SNate Lawson };
1237150dfc7SNate Lawson 
124*916a5d8aSJohn Baldwin DRIVER_MODULE(acpi_throttle, cpu, acpi_throttle_driver, 0, 0);
1257150dfc7SNate Lawson 
1267150dfc7SNate Lawson static void
acpi_throttle_identify(driver_t * driver,device_t parent)1277150dfc7SNate Lawson acpi_throttle_identify(driver_t *driver, device_t parent)
1287150dfc7SNate Lawson {
1293bd74b84SNate Lawson 	ACPI_BUFFER buf;
1303bd74b84SNate Lawson 	ACPI_HANDLE handle;
1313bd74b84SNate Lawson 	ACPI_OBJECT *obj;
1327150dfc7SNate Lawson 
1337150dfc7SNate Lawson 	/* Make sure we're not being doubly invoked. */
1345e517feeSNate Lawson 	if (device_find_child(parent, "acpi_throttle", -1))
1357150dfc7SNate Lawson 		return;
1367150dfc7SNate Lawson 
1377150dfc7SNate Lawson 	/* Check for a valid duty width and parent CPU type. */
1383bd74b84SNate Lawson 	handle = acpi_get_handle(parent);
1393bd74b84SNate Lawson 	if (handle == NULL)
1407150dfc7SNate Lawson 		return;
1412be4e471SJung-uk Kim 	if (AcpiGbl_FADT.DutyWidth == 0 ||
1427150dfc7SNate Lawson 	    acpi_get_type(parent) != ACPI_TYPE_PROCESSOR)
1437150dfc7SNate Lawson 		return;
1443bd74b84SNate Lawson 
1453bd74b84SNate Lawson 	/*
1463bd74b84SNate Lawson 	 * Add a child if there's a non-NULL P_BLK and correct length, or
1473bd74b84SNate Lawson 	 * if the _PTC method is present.
1483bd74b84SNate Lawson 	 */
1493bd74b84SNate Lawson 	buf.Pointer = NULL;
1503bd74b84SNate Lawson 	buf.Length = ACPI_ALLOCATE_BUFFER;
1513bd74b84SNate Lawson 	if (ACPI_FAILURE(AcpiEvaluateObject(handle, NULL, NULL, &buf)))
1523bd74b84SNate Lawson 		return;
1533bd74b84SNate Lawson 	obj = (ACPI_OBJECT *)buf.Pointer;
1543bd74b84SNate Lawson 	if ((obj->Processor.PblkAddress && obj->Processor.PblkLength >= 4) ||
1553bd74b84SNate Lawson 	    ACPI_SUCCESS(AcpiEvaluateObject(handle, "_PTC", NULL, NULL))) {
156d3a8f98aSAlexander Motin 		if (BUS_ADD_CHILD(parent, 0, "acpi_throttle",
157d3a8f98aSAlexander Motin 		    device_get_unit(parent)) == NULL)
1583bd74b84SNate Lawson 			device_printf(parent, "add throttle child failed\n");
1593bd74b84SNate Lawson 	}
1603bd74b84SNate Lawson 	AcpiOsFree(obj);
1617150dfc7SNate Lawson }
1627150dfc7SNate Lawson 
1637150dfc7SNate Lawson static int
acpi_throttle_probe(device_t dev)1647150dfc7SNate Lawson acpi_throttle_probe(device_t dev)
1657150dfc7SNate Lawson {
1667150dfc7SNate Lawson 
167a8de37b0SEitan Adler 	if (resource_disabled("acpi_throttle", 0))
168a8de37b0SEitan Adler 		return (ENXIO);
169a8de37b0SEitan Adler 
1705e517feeSNate Lawson 	/*
1715e517feeSNate Lawson 	 * On i386 platforms at least, ACPI throttling is accomplished by
1725e517feeSNate Lawson 	 * the chipset modulating the STPCLK# pin based on the duty cycle.
1735e517feeSNate Lawson 	 * Since p4tcc uses the same mechanism (but internal to the CPU),
1745e517feeSNate Lawson 	 * we disable acpi_throttle when p4tcc is also present.
1755e517feeSNate Lawson 	 */
1760a133d67SNate Lawson 	if (device_find_child(device_get_parent(dev), "p4tcc", -1) &&
1770a133d67SNate Lawson 	    !resource_disabled("p4tcc", 0))
1785e517feeSNate Lawson 		return (ENXIO);
1795e517feeSNate Lawson 
1807150dfc7SNate Lawson 	device_set_desc(dev, "ACPI CPU Throttling");
1817150dfc7SNate Lawson 	return (0);
1827150dfc7SNate Lawson }
1837150dfc7SNate Lawson 
1847150dfc7SNate Lawson static int
acpi_throttle_attach(device_t dev)1857150dfc7SNate Lawson acpi_throttle_attach(device_t dev)
1867150dfc7SNate Lawson {
1877150dfc7SNate Lawson 	struct acpi_throttle_softc *sc;
1880a133d67SNate Lawson 	struct cf_setting set;
1897150dfc7SNate Lawson 	ACPI_BUFFER buf;
1907150dfc7SNate Lawson 	ACPI_OBJECT *obj;
1917150dfc7SNate Lawson 	ACPI_STATUS status;
1923bd74b84SNate Lawson 	int error;
1937150dfc7SNate Lawson 
1947150dfc7SNate Lawson 	sc = device_get_softc(dev);
1957150dfc7SNate Lawson 	sc->cpu_dev = dev;
1967150dfc7SNate Lawson 	sc->cpu_handle = acpi_get_handle(dev);
1977150dfc7SNate Lawson 
1987150dfc7SNate Lawson 	buf.Pointer = NULL;
1997150dfc7SNate Lawson 	buf.Length = ACPI_ALLOCATE_BUFFER;
2007150dfc7SNate Lawson 	status = AcpiEvaluateObject(sc->cpu_handle, NULL, NULL, &buf);
2017150dfc7SNate Lawson 	if (ACPI_FAILURE(status)) {
2027150dfc7SNate Lawson 		device_printf(dev, "attach failed to get Processor obj - %s\n",
2037150dfc7SNate Lawson 		    AcpiFormatException(status));
2047150dfc7SNate Lawson 		return (ENXIO);
2057150dfc7SNate Lawson 	}
2067150dfc7SNate Lawson 	obj = (ACPI_OBJECT *)buf.Pointer;
2077150dfc7SNate Lawson 	sc->cpu_p_blk = obj->Processor.PblkAddress;
2087150dfc7SNate Lawson 	sc->cpu_p_blk_len = obj->Processor.PblkLength;
2097150dfc7SNate Lawson 	AcpiOsFree(obj);
2107150dfc7SNate Lawson 
2117150dfc7SNate Lawson 	/* If this is the first device probed, check for quirks. */
2127150dfc7SNate Lawson 	if (device_get_unit(dev) == 0)
2137150dfc7SNate Lawson 		acpi_throttle_quirks(sc);
2147150dfc7SNate Lawson 
2153bd74b84SNate Lawson 	/* Attempt to attach the actual throttling register. */
2163bd74b84SNate Lawson 	error = acpi_throttle_evaluate(sc);
2173bd74b84SNate Lawson 	if (error)
2183bd74b84SNate Lawson 		return (error);
2193bd74b84SNate Lawson 
2200a133d67SNate Lawson 	/*
2210a133d67SNate Lawson 	 * Set our initial frequency to the highest since some systems
2220a133d67SNate Lawson 	 * seem to boot with this at the lowest setting.
2230a133d67SNate Lawson 	 */
2240a133d67SNate Lawson 	set.freq = 10000;
2250a133d67SNate Lawson 	acpi_thr_set(dev, &set);
2260a133d67SNate Lawson 
2273bd74b84SNate Lawson 	/* Everything went ok, register with cpufreq(4). */
2283bd74b84SNate Lawson 	cpufreq_register(dev);
2293bd74b84SNate Lawson 	return (0);
2307150dfc7SNate Lawson }
2317150dfc7SNate Lawson 
2327150dfc7SNate Lawson static int
acpi_throttle_evaluate(struct acpi_throttle_softc * sc)2337150dfc7SNate Lawson acpi_throttle_evaluate(struct acpi_throttle_softc *sc)
2347150dfc7SNate Lawson {
2357150dfc7SNate Lawson 	uint32_t duty_end;
2367150dfc7SNate Lawson 	ACPI_BUFFER buf;
2377150dfc7SNate Lawson 	ACPI_OBJECT obj;
2387150dfc7SNate Lawson 	ACPI_GENERIC_ADDRESS gas;
2397150dfc7SNate Lawson 	ACPI_STATUS status;
2407150dfc7SNate Lawson 
2417150dfc7SNate Lawson 	/* Get throttling parameters from the FADT.  0 means not supported. */
2427150dfc7SNate Lawson 	if (device_get_unit(sc->cpu_dev) == 0) {
2432be4e471SJung-uk Kim 		cpu_duty_offset = AcpiGbl_FADT.DutyOffset;
2442be4e471SJung-uk Kim 		cpu_duty_width = AcpiGbl_FADT.DutyWidth;
2457150dfc7SNate Lawson 	}
2467150dfc7SNate Lawson 	if (cpu_duty_width == 0 || (thr_quirks & CPU_QUIRK_NO_THROTTLE) != 0)
2477150dfc7SNate Lawson 		return (ENXIO);
2487150dfc7SNate Lawson 
2497150dfc7SNate Lawson 	/* Validate the duty offset/width. */
2507150dfc7SNate Lawson 	duty_end = cpu_duty_offset + cpu_duty_width - 1;
2517150dfc7SNate Lawson 	if (duty_end > 31) {
2527150dfc7SNate Lawson 		device_printf(sc->cpu_dev,
2537150dfc7SNate Lawson 		    "CLK_VAL field overflows P_CNT register\n");
2547150dfc7SNate Lawson 		return (ENXIO);
2557150dfc7SNate Lawson 	}
2567150dfc7SNate Lawson 	if (cpu_duty_offset <= 4 && duty_end >= 4) {
2577150dfc7SNate Lawson 		device_printf(sc->cpu_dev,
2587150dfc7SNate Lawson 		    "CLK_VAL field overlaps THT_EN bit\n");
2597150dfc7SNate Lawson 		return (ENXIO);
2607150dfc7SNate Lawson 	}
2617150dfc7SNate Lawson 
2627150dfc7SNate Lawson 	/*
2637150dfc7SNate Lawson 	 * If not present, fall back to using the processor's P_BLK to find
2647150dfc7SNate Lawson 	 * the P_CNT register.
2657150dfc7SNate Lawson 	 *
2667150dfc7SNate Lawson 	 * Note that some systems seem to duplicate the P_BLK pointer
2677150dfc7SNate Lawson 	 * across multiple CPUs, so not getting the resource is not fatal.
2687150dfc7SNate Lawson 	 */
2697150dfc7SNate Lawson 	buf.Pointer = &obj;
2707150dfc7SNate Lawson 	buf.Length = sizeof(obj);
2717150dfc7SNate Lawson 	status = AcpiEvaluateObject(sc->cpu_handle, "_PTC", NULL, &buf);
2727150dfc7SNate Lawson 	if (ACPI_SUCCESS(status)) {
2737150dfc7SNate Lawson 		if (obj.Buffer.Length < sizeof(ACPI_GENERIC_ADDRESS) + 3) {
2747150dfc7SNate Lawson 			device_printf(sc->cpu_dev, "_PTC buffer too small\n");
2757150dfc7SNate Lawson 			return (ENXIO);
2767150dfc7SNate Lawson 		}
2777150dfc7SNate Lawson 		memcpy(&gas, obj.Buffer.Pointer + 3, sizeof(gas));
2787150dfc7SNate Lawson 		acpi_bus_alloc_gas(sc->cpu_dev, &sc->cpu_p_type, &thr_rid,
279907b6777SNate Lawson 		    &gas, &sc->cpu_p_cnt, 0);
2807150dfc7SNate Lawson 		if (sc->cpu_p_cnt != NULL && bootverbose) {
2817150dfc7SNate Lawson 			device_printf(sc->cpu_dev, "P_CNT from _PTC %#jx\n",
2827150dfc7SNate Lawson 			    gas.Address);
2837150dfc7SNate Lawson 		}
2847150dfc7SNate Lawson 	}
2857150dfc7SNate Lawson 
2867150dfc7SNate Lawson 	/* If _PTC not present or other failure, try the P_BLK. */
2877150dfc7SNate Lawson 	if (sc->cpu_p_cnt == NULL) {
2887150dfc7SNate Lawson 		/*
2897150dfc7SNate Lawson 		 * The spec says P_BLK must be 6 bytes long.  However, some
2907150dfc7SNate Lawson 		 * systems use it to indicate a fractional set of features
2917150dfc7SNate Lawson 		 * present so we take anything >= 4.
2927150dfc7SNate Lawson 		 */
2937150dfc7SNate Lawson 		if (sc->cpu_p_blk_len < 4)
2947150dfc7SNate Lawson 			return (ENXIO);
2957150dfc7SNate Lawson 		gas.Address = sc->cpu_p_blk;
2962be4e471SJung-uk Kim 		gas.SpaceId = ACPI_ADR_SPACE_SYSTEM_IO;
2972be4e471SJung-uk Kim 		gas.BitWidth = 32;
2987150dfc7SNate Lawson 		acpi_bus_alloc_gas(sc->cpu_dev, &sc->cpu_p_type, &thr_rid,
299907b6777SNate Lawson 		    &gas, &sc->cpu_p_cnt, 0);
3007150dfc7SNate Lawson 		if (sc->cpu_p_cnt != NULL) {
3017150dfc7SNate Lawson 			if (bootverbose)
3027150dfc7SNate Lawson 				device_printf(sc->cpu_dev,
3037150dfc7SNate Lawson 				    "P_CNT from P_BLK %#x\n", sc->cpu_p_blk);
3047150dfc7SNate Lawson 		} else {
3057150dfc7SNate Lawson 			device_printf(sc->cpu_dev, "failed to attach P_CNT\n");
3067150dfc7SNate Lawson 			return (ENXIO);
3077150dfc7SNate Lawson 		}
3087150dfc7SNate Lawson 	}
3097150dfc7SNate Lawson 	thr_rid++;
3107150dfc7SNate Lawson 
3117150dfc7SNate Lawson 	return (0);
3127150dfc7SNate Lawson }
3137150dfc7SNate Lawson 
3149cf4cabeSJung-uk Kim static void
acpi_throttle_quirks(struct acpi_throttle_softc * sc)3157150dfc7SNate Lawson acpi_throttle_quirks(struct acpi_throttle_softc *sc)
3167150dfc7SNate Lawson {
3179cf4cabeSJung-uk Kim #ifdef __i386__
3187150dfc7SNate Lawson 	device_t acpi_dev;
3197150dfc7SNate Lawson 
3207150dfc7SNate Lawson 	/* Look for various quirks of the PIIX4 part. */
3217150dfc7SNate Lawson 	acpi_dev = pci_find_device(PCI_VENDOR_INTEL, PCI_DEVICE_82371AB_3);
3227150dfc7SNate Lawson 	if (acpi_dev) {
3237150dfc7SNate Lawson 		switch (pci_get_revid(acpi_dev)) {
3247150dfc7SNate Lawson 		/*
3257150dfc7SNate Lawson 		 * Disable throttling control on PIIX4 A and B-step.
3267150dfc7SNate Lawson 		 * See specification changes #13 ("Manual Throttle Duty Cycle")
3277150dfc7SNate Lawson 		 * and #14 ("Enabling and Disabling Manual Throttle"), plus
3287150dfc7SNate Lawson 		 * erratum #5 ("STPCLK# Deassertion Time") from the January
3297150dfc7SNate Lawson 		 * 2002 PIIX4 specification update.  Note that few (if any)
3307150dfc7SNate Lawson 		 * mobile systems ever used this part.
3317150dfc7SNate Lawson 		 */
3327150dfc7SNate Lawson 		case PCI_REVISION_A_STEP:
3337150dfc7SNate Lawson 		case PCI_REVISION_B_STEP:
3347150dfc7SNate Lawson 			thr_quirks |= CPU_QUIRK_NO_THROTTLE;
3357150dfc7SNate Lawson 			break;
3367150dfc7SNate Lawson 		default:
3377150dfc7SNate Lawson 			break;
3387150dfc7SNate Lawson 		}
3397150dfc7SNate Lawson 	}
3409cf4cabeSJung-uk Kim #endif
3417150dfc7SNate Lawson }
3427150dfc7SNate Lawson 
3437150dfc7SNate Lawson static int
acpi_thr_settings(device_t dev,struct cf_setting * sets,int * count)344e94a0c1aSNate Lawson acpi_thr_settings(device_t dev, struct cf_setting *sets, int *count)
3457150dfc7SNate Lawson {
3467150dfc7SNate Lawson 	int i, speed;
3477150dfc7SNate Lawson 
3487150dfc7SNate Lawson 	if (sets == NULL || count == NULL)
3497150dfc7SNate Lawson 		return (EINVAL);
3507150dfc7SNate Lawson 	if (*count < CPU_MAX_SPEED)
351ededc31dSNate Lawson 		return (E2BIG);
3527150dfc7SNate Lawson 
3537150dfc7SNate Lawson 	/* Return a list of valid settings for this driver. */
3547150dfc7SNate Lawson 	memset(sets, CPUFREQ_VAL_UNKNOWN, sizeof(*sets) * CPU_MAX_SPEED);
3557150dfc7SNate Lawson 	for (i = 0, speed = CPU_MAX_SPEED; speed != 0; i++, speed--) {
3567150dfc7SNate Lawson 		sets[i].freq = CPU_SPEED_PERCENT(speed);
3577150dfc7SNate Lawson 		sets[i].dev = dev;
3587150dfc7SNate Lawson 	}
3597150dfc7SNate Lawson 	*count = CPU_MAX_SPEED;
3607150dfc7SNate Lawson 
3617150dfc7SNate Lawson 	return (0);
3627150dfc7SNate Lawson }
3637150dfc7SNate Lawson 
3647150dfc7SNate Lawson static int
acpi_thr_set(device_t dev,const struct cf_setting * set)3657150dfc7SNate Lawson acpi_thr_set(device_t dev, const struct cf_setting *set)
3667150dfc7SNate Lawson {
3677150dfc7SNate Lawson 	struct acpi_throttle_softc *sc;
3687150dfc7SNate Lawson 	uint32_t clk_val, p_cnt, speed;
3697150dfc7SNate Lawson 
3707150dfc7SNate Lawson 	if (set == NULL)
3717150dfc7SNate Lawson 		return (EINVAL);
3727150dfc7SNate Lawson 	sc = device_get_softc(dev);
3737150dfc7SNate Lawson 
3747150dfc7SNate Lawson 	/*
3757150dfc7SNate Lawson 	 * Validate requested state converts to a duty cycle that is an
3767150dfc7SNate Lawson 	 * integer from [1 .. CPU_MAX_SPEED].
3777150dfc7SNate Lawson 	 */
3787150dfc7SNate Lawson 	speed = set->freq * CPU_MAX_SPEED / 10000;
3797150dfc7SNate Lawson 	if (speed * 10000 != set->freq * CPU_MAX_SPEED ||
3807150dfc7SNate Lawson 	    speed < 1 || speed > CPU_MAX_SPEED)
3817150dfc7SNate Lawson 		return (EINVAL);
3827150dfc7SNate Lawson 
3837150dfc7SNate Lawson 	/* If we're at this setting, don't bother applying it again. */
3843bd74b84SNate Lawson 	if (speed == sc->cpu_thr_state)
3857150dfc7SNate Lawson 		return (0);
3867150dfc7SNate Lawson 
3877150dfc7SNate Lawson 	/* Get the current P_CNT value and disable throttling */
3887150dfc7SNate Lawson 	p_cnt = THR_GET_REG(sc->cpu_p_cnt);
3897150dfc7SNate Lawson 	p_cnt &= ~CPU_P_CNT_THT_EN;
3907150dfc7SNate Lawson 	THR_SET_REG(sc->cpu_p_cnt, p_cnt);
3917150dfc7SNate Lawson 
3927150dfc7SNate Lawson 	/* If we're at maximum speed, that's all */
3937150dfc7SNate Lawson 	if (speed < CPU_MAX_SPEED) {
3947150dfc7SNate Lawson 		/* Mask the old CLK_VAL off and OR in the new value */
3957150dfc7SNate Lawson 		clk_val = (CPU_MAX_SPEED - 1) << cpu_duty_offset;
3967150dfc7SNate Lawson 		p_cnt &= ~clk_val;
3977150dfc7SNate Lawson 		p_cnt |= (speed << cpu_duty_offset);
3987150dfc7SNate Lawson 
3997150dfc7SNate Lawson 		/* Write the new P_CNT value and then enable throttling */
4007150dfc7SNate Lawson 		THR_SET_REG(sc->cpu_p_cnt, p_cnt);
4017150dfc7SNate Lawson 		p_cnt |= CPU_P_CNT_THT_EN;
4027150dfc7SNate Lawson 		THR_SET_REG(sc->cpu_p_cnt, p_cnt);
4037150dfc7SNate Lawson 	}
4043bd74b84SNate Lawson 	sc->cpu_thr_state = speed;
4057150dfc7SNate Lawson 
4067150dfc7SNate Lawson 	return (0);
4077150dfc7SNate Lawson }
4087150dfc7SNate Lawson 
4097150dfc7SNate Lawson static int
acpi_thr_get(device_t dev,struct cf_setting * set)4107150dfc7SNate Lawson acpi_thr_get(device_t dev, struct cf_setting *set)
4117150dfc7SNate Lawson {
4127150dfc7SNate Lawson 	struct acpi_throttle_softc *sc;
4137150dfc7SNate Lawson 	uint32_t p_cnt, clk_val;
4147150dfc7SNate Lawson 
4157150dfc7SNate Lawson 	if (set == NULL)
4167150dfc7SNate Lawson 		return (EINVAL);
4177150dfc7SNate Lawson 	sc = device_get_softc(dev);
4187150dfc7SNate Lawson 
4197150dfc7SNate Lawson 	/* Get the current throttling setting from P_CNT. */
4207150dfc7SNate Lawson 	p_cnt = THR_GET_REG(sc->cpu_p_cnt);
4217150dfc7SNate Lawson 	clk_val = (p_cnt >> cpu_duty_offset) & (CPU_MAX_SPEED - 1);
4223bd74b84SNate Lawson 	sc->cpu_thr_state = clk_val;
4237150dfc7SNate Lawson 
4247150dfc7SNate Lawson 	memset(set, CPUFREQ_VAL_UNKNOWN, sizeof(*set));
4257150dfc7SNate Lawson 	set->freq = CPU_SPEED_PERCENT(clk_val);
4267150dfc7SNate Lawson 	set->dev = dev;
4277150dfc7SNate Lawson 
4287150dfc7SNate Lawson 	return (0);
4297150dfc7SNate Lawson }
430e94a0c1aSNate Lawson 
431e94a0c1aSNate Lawson static int
acpi_thr_type(device_t dev,int * type)432e94a0c1aSNate Lawson acpi_thr_type(device_t dev, int *type)
433e94a0c1aSNate Lawson {
434e94a0c1aSNate Lawson 
435e94a0c1aSNate Lawson 	if (type == NULL)
436e94a0c1aSNate Lawson 		return (EINVAL);
437e94a0c1aSNate Lawson 
438e94a0c1aSNate Lawson 	*type = CPUFREQ_TYPE_RELATIVE;
439e94a0c1aSNate Lawson 	return (0);
440e94a0c1aSNate Lawson }
441