From: Hasso Tepper Date: Fri, 5 Sep 2008 10:28:35 +0000 (+0000) Subject: acpi_cpu(4) update. It's now possible to use higher (lower power usage) C X-Git-Tag: v2.1.1~492 X-Git-Url: https://gitweb.dragonflybsd.org/dragonfly.git/commitdiff_plain/5ebadb2c919e68287bf639bd3ad330c74a3247b3 acpi_cpu(4) update. It's now possible to use higher (lower power usage) C states than C1 in modern (multicore) CPUs. Obtained-from: FreeBSD --- diff --git a/sys/dev/acpica5/acpi.c b/sys/dev/acpica5/acpi.c index 5bec581090..92893c17f2 100644 --- a/sys/dev/acpica5/acpi.c +++ b/sys/dev/acpica5/acpi.c @@ -27,7 +27,7 @@ * SUCH DAMAGE. * * $FreeBSD: src/sys/dev/acpica/acpi.c,v 1.160 2004/06/14 03:52:19 njl Exp $ - * $DragonFly: src/sys/dev/acpica5/acpi.c,v 1.35 2008/08/27 16:35:19 hasso Exp $ + * $DragonFly: src/sys/dev/acpica5/acpi.c,v 1.36 2008/09/05 10:28:35 hasso Exp $ */ #include "opt_acpi.h" @@ -1010,7 +1010,8 @@ acpi_release_resource(device_t bus, device_t child, int type, int rid, /* Allocate an IO port or memory resource, given its GAS. */ struct resource * -acpi_bus_alloc_gas(device_t dev, int *rid, ACPI_GENERIC_ADDRESS *gas) +acpi_bus_alloc_gas(device_t dev, int *rid, ACPI_GENERIC_ADDRESS *gas, + u_int flags) { int type; @@ -1029,7 +1030,7 @@ acpi_bus_alloc_gas(device_t dev, int *rid, ACPI_GENERIC_ADDRESS *gas) } bus_set_resource(dev, type, *rid, gas->Address, gas->BitWidth / 8); - return (bus_alloc_resource_any(dev, type, rid, RF_ACTIVE)); + return (bus_alloc_resource_any(dev, type, rid, RF_ACTIVE | flags)); } /* diff --git a/sys/dev/acpica5/acpi_cpu.c b/sys/dev/acpica5/acpi_cpu.c index 8290dc9432..992f7263f5 100644 --- a/sys/dev/acpica5/acpi_cpu.c +++ b/sys/dev/acpica5/acpi_cpu.c @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2003 Nate Lawson (SDG) + * Copyright (c) 2003-2005 Nate Lawson (SDG) * Copyright (c) 2001 Michael Smith * All rights reserved. * @@ -24,8 +24,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $FreeBSD: src/sys/dev/acpica/acpi_cpu.c,v 1.41 2004/06/24 00:38:51 njl Exp $ - * $DragonFly: src/sys/dev/acpica5/acpi_cpu.c,v 1.20 2007/03/21 22:19:00 y0netan1 Exp $ + * $FreeBSD: src/sys/dev/acpica/acpi_cpu.c,v 1.72 2008/04/12 12:06:00 rpaulo Exp $ + * $DragonFly: src/sys/dev/acpica5/acpi_cpu.c,v 1.21 2008/09/05 10:28:35 hasso Exp $ */ #include "opt_acpi.h" @@ -50,11 +50,7 @@ #include "acpivar.h" /* - * Support for ACPI Processor devices, including ACPI 2.0 throttling - * and C[1-3] sleep states. - * - * TODO: implement scans of all CPUs to be sure all Cx states are - * equivalent. + * Support for ACPI Processor devices, including C[1-3] sleep states. */ /* Hooks for the ACPI CA debugging infrastructure */ @@ -66,19 +62,35 @@ struct acpi_cx { uint32_t type; /* C1-3 (C4 and up treated as C3). */ uint32_t trans_lat; /* Transition latency (usec). */ uint32_t power; /* Power consumed (mW). */ + int res_type; /* Resource type for p_lvlx. */ }; #define MAX_CX_STATES 8 struct acpi_cpu_softc { device_t cpu_dev; ACPI_HANDLE cpu_handle; - uint32_t acpi_id; /* ACPI processor id */ + struct mdglobaldata *md; + uint32_t cpu_acpi_id; /* ACPI processor id */ uint32_t cpu_p_blk; /* ACPI P_BLK location */ uint32_t cpu_p_blk_len; /* P_BLK length (must be 6). */ - struct resource *cpu_p_cnt; /* Throttling control register */ struct acpi_cx cpu_cx_states[MAX_CX_STATES]; int cpu_cx_count; /* Number of valid Cx states. */ int cpu_prev_sleep;/* Last idle sleep duration. */ + int cpu_features; /* Child driver supported features. */ + /* Runtime state. */ + int cpu_non_c3; /* Index of lowest non-C3 state. */ + int cpu_short_slp; /* Count of < 1us sleeps. */ + u_int cpu_cx_stats[MAX_CX_STATES];/* Cx usage history. */ + /* Values for sysctl. */ + struct sysctl_ctx_list cpu_sysctl_ctx; + struct sysctl_oid *cpu_sysctl_tree; + int cpu_cx_lowest; + char cpu_cx_supported[64]; + int cpu_rid; +}; + +struct acpi_cpu_device { + struct resource_list ad_rl; }; #define CPU_GET_REG(reg, width) \ @@ -88,24 +100,11 @@ struct acpi_cpu_softc { (bus_space_write_ ## width(rman_get_bustag((reg)), \ rman_get_bushandle((reg)), 0, (val))) -/* - * Speeds are stored in counts, from 1 to CPU_MAX_SPEED, and - * reported to the user in tenths of a percent. - */ -static uint32_t cpu_duty_offset; -static uint32_t cpu_duty_width; -#define CPU_MAX_SPEED (1 << cpu_duty_width) -#define CPU_SPEED_PERCENT(x) ((1000 * (x)) / CPU_MAX_SPEED) -#define CPU_SPEED_PRINTABLE(x) (CPU_SPEED_PERCENT(x) / 10), \ - (CPU_SPEED_PERCENT(x) % 10) -#define CPU_P_CNT_THT_EN (1<<4) #define PM_USEC(x) ((x) >> 2) /* ~4 clocks per usec (3.57955 Mhz) */ -#define ACPI_CPU_NOTIFY_PERF_STATES 0x80 /* _PSS changed. */ -#define ACPI_CPU_NOTIFY_CX_STATES 0x81 /* _CST changed. */ +#define ACPI_NOTIFY_CX_STATES 0x81 /* _CST changed. */ #define CPU_QUIRK_NO_C3 (1<<0) /* C3-type states are not usable. */ -#define CPU_QUIRK_NO_THROTTLE (1<<1) /* Throttling is not usable. */ #define CPU_QUIRK_NO_BM_CTRL (1<<2) /* No bus mastering control. */ #define PCI_VENDOR_INTEL 0x8086 @@ -114,60 +113,82 @@ static uint32_t cpu_duty_width; #define PCI_REVISION_B_STEP 1 #define PCI_REVISION_4E 2 #define PCI_REVISION_4M 3 +#define PIIX4_DEVACTB_REG 0x58 +#define PIIX4_BRLD_EN_IRQ0 (1<<0) +#define PIIX4_BRLD_EN_IRQ (1<<1) +#define PIIX4_BRLD_EN_IRQ8 (1<<5) +#define PIIX4_STOP_BREAK_MASK (PIIX4_BRLD_EN_IRQ0 | PIIX4_BRLD_EN_IRQ | PIIX4_BRLD_EN_IRQ8) +#define PIIX4_PCNTRL_BST_EN (1<<10) /* Platform hardware resource information. */ static uint32_t cpu_smi_cmd; /* Value to write to SMI_CMD. */ static uint8_t cpu_cst_cnt; /* Indicate we are _CST aware. */ -static int cpu_rid; /* Driver-wide resource id. */ static int cpu_quirks; /* Indicate any hardware bugs. */ /* Runtime state. */ -static int cpu_cx_count; /* Number of valid states */ -static int cpu_non_c3; /* Index of lowest non-C3 state. */ -static u_int cpu_cx_stats[MAX_CX_STATES];/* Cx usage history. */ +static int cpu_disable_idle; /* Disable entry to idle function */ +static int cpu_cx_count; /* Number of valid Cx states */ /* Values for sysctl. */ -static uint32_t cpu_throttle_state; -static uint32_t cpu_throttle_max; -static uint32_t cpu_throttle_performance; -static uint32_t cpu_throttle_economy; +static struct sysctl_ctx_list cpu_sysctl_ctx; +static struct sysctl_oid *cpu_sysctl_tree; +static int cpu_cx_generic; static int cpu_cx_lowest; -static char cpu_cx_supported[64]; static device_t *cpu_devices; static int cpu_ndevices; static struct acpi_cpu_softc **cpu_softc; -static struct sysctl_ctx_list acpi_cpu_sysctl_ctx; -static struct sysctl_oid *acpi_cpu_sysctl_tree; - static int acpi_cpu_probe(device_t dev); static int acpi_cpu_attach(device_t dev); +static int acpi_cpu_suspend(device_t dev); +static int acpi_cpu_resume(device_t dev); static int acpi_pcpu_get_id(uint32_t idx, uint32_t *acpi_id, - uint32_t *cpu_id); + uint32_t *cpu_id); +static struct resource_list *acpi_cpu_get_rlist(device_t dev, device_t child); +static device_t acpi_cpu_add_child(device_t dev, int order, const char *name, + int unit); +static int acpi_cpu_read_ivar(device_t dev, device_t child, int index, + uintptr_t *result); static int acpi_cpu_shutdown(device_t dev); -static int acpi_cpu_throttle_probe(struct acpi_cpu_softc *sc); -static void acpi_cpu_power_profile(void *arg); -static int acpi_cpu_cx_probe(struct acpi_cpu_softc *sc); +static void acpi_cpu_cx_probe(struct acpi_cpu_softc *sc); +static void acpi_cpu_generic_cx_probe(struct acpi_cpu_softc *sc); static int acpi_cpu_cx_cst(struct acpi_cpu_softc *sc); static void acpi_cpu_startup(void *arg); -static void acpi_cpu_startup_throttling(void); -static void acpi_cpu_startup_cx(void); -static void acpi_cpu_throttle_set(uint32_t speed); +static void acpi_cpu_startup_cx(struct acpi_cpu_softc *sc); +static void acpi_cpu_cx_list(struct acpi_cpu_softc *sc); static void acpi_cpu_idle(void); -static void acpi_cpu_c1(void); static void acpi_cpu_notify(ACPI_HANDLE h, UINT32 notify, void *context); -static int acpi_cpu_quirks(struct acpi_cpu_softc *sc); -static int acpi_cpu_throttle_sysctl(SYSCTL_HANDLER_ARGS); +static int acpi_cpu_quirks(void); static int acpi_cpu_usage_sysctl(SYSCTL_HANDLER_ARGS); +static int acpi_cpu_set_cx_lowest(struct acpi_cpu_softc *sc, int val); static int acpi_cpu_cx_lowest_sysctl(SYSCTL_HANDLER_ARGS); +static int acpi_cpu_global_cx_lowest_sysctl(SYSCTL_HANDLER_ARGS); + +static void acpi_cpu_c1(void); /* XXX */ static device_method_t acpi_cpu_methods[] = { /* Device interface */ DEVMETHOD(device_probe, acpi_cpu_probe), DEVMETHOD(device_attach, acpi_cpu_attach), + DEVMETHOD(device_detach, bus_generic_detach), DEVMETHOD(device_shutdown, acpi_cpu_shutdown), - + DEVMETHOD(device_suspend, acpi_cpu_suspend), + DEVMETHOD(device_resume, acpi_cpu_resume), + + /* Bus interface */ + DEVMETHOD(bus_add_child, acpi_cpu_add_child), + DEVMETHOD(bus_read_ivar, acpi_cpu_read_ivar), + DEVMETHOD(bus_get_resource_list, acpi_cpu_get_rlist), + DEVMETHOD(bus_get_resource, bus_generic_rl_get_resource), + DEVMETHOD(bus_set_resource, bus_generic_rl_set_resource), + DEVMETHOD(bus_alloc_resource, bus_generic_rl_alloc_resource), + DEVMETHOD(bus_release_resource, bus_generic_rl_release_resource), + DEVMETHOD(bus_driver_added, bus_generic_driver_added), + DEVMETHOD(bus_activate_resource, bus_generic_activate_resource), + DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource), + DEVMETHOD(bus_setup_intr, bus_generic_setup_intr), + DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr), {0, 0} }; @@ -184,10 +205,9 @@ MODULE_DEPEND(cpu, acpi, 1, 1, 1); static int acpi_cpu_probe(device_t dev) { - int acpi_id, cpu_id, cx_count; + int acpi_id, cpu_id; ACPI_BUFFER buf; ACPI_HANDLE handle; - char msg[32]; ACPI_OBJECT *obj; ACPI_STATUS status; @@ -232,33 +252,10 @@ acpi_cpu_probe(device_t dev) if (cpu_softc[cpu_id] != NULL) return (ENXIO); - /* Get a count of Cx states for our device string. */ - cx_count = 0; - buf.Pointer = NULL; - buf.Length = ACPI_ALLOCATE_BUFFER; - status = AcpiEvaluateObject(handle, "_CST", NULL, &buf); - if (ACPI_SUCCESS(status)) { - obj = (ACPI_OBJECT *)buf.Pointer; - if (ACPI_PKG_VALID(obj, 2)) - acpi_PkgInt32(obj, 0, &cx_count); - AcpiOsFree(obj); - } else { - if (AcpiGbl_FADT.C2Latency <= 100) - cx_count++; - if (AcpiGbl_FADT.C3Latency <= 1000) - cx_count++; - if (cx_count > 0) - cx_count++; - } - if (cx_count > 0) - ksnprintf(msg, sizeof(msg), "ACPI CPU (%d Cx states)", cx_count); - else - strlcpy(msg, "ACPI CPU", sizeof(msg)); - device_set_desc_copy(dev, msg); - /* Mark this processor as in-use and save our derived id for attach. */ cpu_softc[cpu_id] = (void *)1; acpi_set_magic(dev, cpu_id); + device_set_desc(dev, "ACPI CPU"); return (0); } @@ -267,20 +264,33 @@ static int acpi_cpu_attach(device_t dev) { ACPI_BUFFER buf; - ACPI_OBJECT *obj; + ACPI_OBJECT arg[4], *obj; + ACPI_OBJECT_LIST arglist; + struct mdglobaldata *md; struct acpi_cpu_softc *sc; struct acpi_softc *acpi_sc; ACPI_STATUS status; - int thr_ret, cx_ret; + u_int features; + int cpu_id, drv_count, i; + driver_t **drivers; + uint32_t cap_set[3]; - ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); + /* UUID needed by _OSC evaluation */ + static uint8_t cpu_oscuuid[16] = { 0x16, 0xA6, 0x77, 0x40, 0x0C, 0x29, + 0xBE, 0x47, 0x9E, 0xBD, 0xD8, 0x70, + 0x58, 0x71, 0x39, 0x53 }; - ACPI_ASSERTLOCK; + ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); sc = device_get_softc(dev); sc->cpu_dev = dev; sc->cpu_handle = acpi_get_handle(dev); - cpu_softc[acpi_get_magic(dev)] = sc; + cpu_id = acpi_get_magic(dev); + cpu_softc[cpu_id] = sc; + md = (struct mdglobaldata *)globaldata_find(device_get_unit(dev)); + sc->md = md; + cpu_smi_cmd = AcpiGbl_FADT.SmiCommand; + cpu_cst_cnt = AcpiGbl_FADT.CstControl; buf.Pointer = NULL; buf.Length = ACPI_ALLOCATE_BUFFER; @@ -293,37 +303,121 @@ acpi_cpu_attach(device_t dev) obj = (ACPI_OBJECT *)buf.Pointer; sc->cpu_p_blk = obj->Processor.PblkAddress; sc->cpu_p_blk_len = obj->Processor.PblkLength; - sc->acpi_id = obj->Processor.ProcId; + sc->cpu_acpi_id = obj->Processor.ProcId; AcpiOsFree(obj); ACPI_DEBUG_PRINT((ACPI_DB_INFO, "acpi_cpu%d: P_BLK at %#x/%d\n", device_get_unit(dev), sc->cpu_p_blk, sc->cpu_p_blk_len)); - acpi_sc = acpi_device_get_parent_softc(dev); - sysctl_ctx_init(&acpi_cpu_sysctl_ctx); - acpi_cpu_sysctl_tree = SYSCTL_ADD_NODE(&acpi_cpu_sysctl_ctx, - SYSCTL_CHILDREN(acpi_sc->acpi_sysctl_tree), - OID_AUTO, "cpu", CTLFLAG_RD, 0, ""); + /* + * If this is the first cpu we attach, create and initialize the generic + * resources that will be used by all acpi cpu devices. + */ + if (device_get_unit(dev) == 0) { + /* Assume we won't be using generic Cx mode by default */ + cpu_cx_generic = FALSE; + + /* Install hw.acpi.cpu sysctl tree */ + acpi_sc = acpi_device_get_parent_softc(dev); + sysctl_ctx_init(&cpu_sysctl_ctx); + cpu_sysctl_tree = SYSCTL_ADD_NODE(&cpu_sysctl_ctx, + SYSCTL_CHILDREN(acpi_sc->acpi_sysctl_tree), OID_AUTO, "cpu", + CTLFLAG_RD, 0, "node for CPU children"); + + /* Queue post cpu-probing task handler */ + AcpiOsExecute(OSL_NOTIFY_HANDLER, acpi_cpu_startup, NULL); + } - /* If this is the first device probed, check for quirks. */ - if (device_get_unit(dev) == 0) - acpi_cpu_quirks(sc); + sysctl_ctx_init(&sc->cpu_sysctl_ctx); + sc->cpu_sysctl_tree = SYSCTL_ADD_NODE(&sc->cpu_sysctl_ctx, + SYSCTL_STATIC_CHILDREN(_hw), OID_AUTO, + device_get_nameunit(dev), CTLFLAG_RD, + 0, ""); /* - * Probe for throttling and Cx state support. - * If none of these is present, free up unused resources. + * Before calling any CPU methods, collect child driver feature hints + * and notify ACPI of them. We support unified SMP power control + * so advertise this ourselves. Note this is not the same as independent + * SMP control where each CPU can have different settings. */ - thr_ret = acpi_cpu_throttle_probe(sc); - cx_ret = acpi_cpu_cx_probe(sc); - if (thr_ret == 0 || cx_ret == 0) { - status = AcpiInstallNotifyHandler(sc->cpu_handle, ACPI_DEVICE_NOTIFY, - acpi_cpu_notify, sc); - if (device_get_unit(dev) == 0) - AcpiOsExecute(OSL_NOTIFY_HANDLER, acpi_cpu_startup, NULL); - } else { - sysctl_ctx_free(&acpi_cpu_sysctl_ctx); + sc->cpu_features = ACPI_CAP_SMP_SAME | ACPI_CAP_SMP_SAME_C3; + if (devclass_get_drivers(acpi_cpu_devclass, &drivers, &drv_count) == 0) { + for (i = 0; i < drv_count; i++) { + if (ACPI_GET_FEATURES(drivers[i], &features) == 0) + sc->cpu_features |= features; + } + kfree(drivers, M_TEMP); } - return_VALUE (0); + /* + * CPU capabilities are specified as a buffer of 32-bit integers: + * revision, count, and one or more capabilities. The revision of + * "1" is not specified anywhere but seems to match Linux. + */ + if (sc->cpu_features) { + arglist.Pointer = arg; + arglist.Count = 1; + arg[0].Type = ACPI_TYPE_BUFFER; + arg[0].Buffer.Length = sizeof(cap_set); + arg[0].Buffer.Pointer = (uint8_t *)cap_set; + cap_set[0] = 1; /* revision */ + cap_set[1] = 1; /* number of capabilities integers */ + cap_set[2] = sc->cpu_features; + AcpiEvaluateObject(sc->cpu_handle, "_PDC", &arglist, NULL); + + /* + * On some systems we need to evaluate _OSC so that the ASL + * loads the _PSS and/or _PDC methods at runtime. + * + * TODO: evaluate failure of _OSC. + */ + arglist.Pointer = arg; + arglist.Count = 4; + arg[0].Type = ACPI_TYPE_BUFFER; + arg[0].Buffer.Length = sizeof(cpu_oscuuid); + arg[0].Buffer.Pointer = cpu_oscuuid; /* UUID */ + arg[1].Type = ACPI_TYPE_INTEGER; + arg[1].Integer.Value = 1; /* revision */ + arg[2].Type = ACPI_TYPE_INTEGER; + arg[2].Integer.Value = 1; /* count */ + arg[3].Type = ACPI_TYPE_BUFFER; + arg[3].Buffer.Length = sizeof(cap_set); /* Capabilities buffer */ + arg[3].Buffer.Pointer = (uint8_t *)cap_set; + cap_set[0] = 0; + AcpiEvaluateObject(sc->cpu_handle, "_OSC", &arglist, NULL); + } + + /* Probe for Cx state support. */ + acpi_cpu_cx_probe(sc); + + /* Finally, call identify and probe/attach for child devices. */ + bus_generic_probe(dev); + bus_generic_attach(dev); + + return (0); +} + +/* + * Disable any entry to the idle function during suspend and re-enable it + * during resume. + */ +static int +acpi_cpu_suspend(device_t dev) +{ + int error; + + error = bus_generic_suspend(dev); + if (error) + return (error); + cpu_disable_idle = TRUE; + return (0); +} + +static int +acpi_cpu_resume(device_t dev) +{ + + cpu_disable_idle = FALSE; + return (bus_generic_resume(dev)); } /* @@ -351,7 +445,7 @@ acpi_pcpu_get_id(uint32_t idx, uint32_t *acpi_id, uint32_t *cpu_id) * return the pc_cpuid to reference this processor. */ if (md->gd_acpi_id == 0xffffffff) - md->gd_acpi_id = *acpi_id; + md->gd_acpi_id = *acpi_id; else if (md->gd_acpi_id != *acpi_id) *acpi_id = md->gd_acpi_id; *cpu_id = md->mi.gd_cpuid; @@ -362,206 +456,170 @@ acpi_pcpu_get_id(uint32_t idx, uint32_t *acpi_id, uint32_t *cpu_id) return (ESRCH); } -static int -acpi_cpu_shutdown(device_t dev) +static struct resource_list * +acpi_cpu_get_rlist(device_t dev, device_t child) { - ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); + struct acpi_cpu_device *ad; - /* Disable any entry to the idle function. */ - cpu_cx_count = 0; + ad = device_get_ivars(child); + if (ad == NULL) + return (NULL); + return (&ad->ad_rl); +} - /* Signal and wait for all processors to exit acpi_cpu_idle(). */ -#ifdef SMP - if (mycpu->gd_cpuid == 0) - lwkt_cpusync_simple(0, NULL, NULL); -#endif - DELAY(1); +static device_t +acpi_cpu_add_child(device_t dev, int order, const char *name, int unit) +{ + struct acpi_cpu_device *ad; + device_t child; - return_VALUE (0); + if ((ad = kmalloc(sizeof(*ad), M_TEMP, M_NOWAIT | M_ZERO)) == NULL) + return (NULL); + + resource_list_init(&ad->ad_rl); + + child = device_add_child_ordered(dev, order, name, unit); + if (child != NULL) + device_set_ivars(child, ad); + else + kfree(ad, M_TEMP); + return (child); } static int -acpi_cpu_throttle_probe(struct acpi_cpu_softc *sc) +acpi_cpu_read_ivar(device_t dev, device_t child, int index, uintptr_t *result) { - uint32_t duty_end; - ACPI_BUFFER buf; - ACPI_OBJECT obj; - ACPI_GENERIC_ADDRESS gas; - ACPI_STATUS status; - - ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); - - ACPI_ASSERTLOCK; + struct acpi_cpu_softc *sc; - /* Get throttling parameters from the FADT. 0 means not supported. */ - if (device_get_unit(sc->cpu_dev) == 0) { - cpu_smi_cmd = AcpiGbl_FADT.SmiCommand; - cpu_cst_cnt = AcpiGbl_FADT.CstControl; - cpu_duty_offset = AcpiGbl_FADT.DutyOffset; - cpu_duty_width = AcpiGbl_FADT.DutyWidth; + sc = device_get_softc(dev); + switch (index) { + case ACPI_IVAR_HANDLE: + *result = (uintptr_t)sc->cpu_handle; + break; +#if 0 + case CPU_IVAR_PCPU: + *result = (uintptr_t)sc->cpu_pcpu; + break; +#endif + default: + return (ENOENT); } - if (cpu_duty_width == 0 || (cpu_quirks & CPU_QUIRK_NO_THROTTLE) != 0) - return (ENXIO); + return (0); +} - /* Validate the duty offset/width. */ - duty_end = cpu_duty_offset + cpu_duty_width - 1; - if (duty_end > 31) { - device_printf(sc->cpu_dev, "CLK_VAL field overflows P_CNT register\n"); - return (ENXIO); - } - if (cpu_duty_offset <= 4 && duty_end >= 4) { - device_printf(sc->cpu_dev, "CLK_VAL field overlaps THT_EN bit\n"); - return (ENXIO); - } +static int +acpi_cpu_shutdown(device_t dev) +{ + ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); + + /* Allow children to shutdown first. */ + bus_generic_shutdown(dev); /* - * If not present, fall back to using the processor's P_BLK to find - * the P_CNT register. - * - * Note that some systems seem to duplicate the P_BLK pointer - * across multiple CPUs, so not getting the resource is not fatal. + * Disable any entry to the idle function. There is a small race where + * an idle thread have passed this check but not gone to sleep. This + * is ok since device_shutdown() does not free the softc, otherwise + * we'd have to be sure all threads were evicted before returning. */ - buf.Pointer = &obj; - buf.Length = sizeof(obj); - status = AcpiEvaluateObject(sc->cpu_handle, "_PTC", NULL, &buf); - if (ACPI_SUCCESS(status)) { - if (obj.Buffer.Length < sizeof(ACPI_GENERIC_ADDRESS) + 3) { - device_printf(sc->cpu_dev, "_PTC buffer too small\n"); - return (ENXIO); - } - memcpy(&gas, obj.Buffer.Pointer + 3, sizeof(gas)); - sc->cpu_p_cnt = acpi_bus_alloc_gas(sc->cpu_dev, &cpu_rid, &gas); - if (sc->cpu_p_cnt != NULL) { - ACPI_DEBUG_PRINT((ACPI_DB_INFO, "acpi_cpu%d: P_CNT from _PTC\n", - device_get_unit(sc->cpu_dev))); - } - } + cpu_disable_idle = TRUE; - /* If _PTC not present or other failure, try the P_BLK. */ - if (sc->cpu_p_cnt == NULL) { - /* - * The spec says P_BLK must be 6 bytes long. However, some - * systems use it to indicate a fractional set of features - * present so we take anything >= 4. - */ - if (sc->cpu_p_blk_len < 4) - return (ENXIO); - gas.Address = sc->cpu_p_blk; - gas.SpaceId = ACPI_ADR_SPACE_SYSTEM_IO; - gas.BitWidth = 32; - sc->cpu_p_cnt = acpi_bus_alloc_gas(sc->cpu_dev, &cpu_rid, &gas); - if (sc->cpu_p_cnt != NULL) { - ACPI_DEBUG_PRINT((ACPI_DB_INFO, "acpi_cpu%d: P_CNT from P_BLK\n", - device_get_unit(sc->cpu_dev))); - } else { - device_printf(sc->cpu_dev, "Failed to attach throttling P_CNT\n"); - return (ENXIO); - } - } - cpu_rid++; - - return (0); + return_VALUE (0); } -static int +static void acpi_cpu_cx_probe(struct acpi_cpu_softc *sc) { - ACPI_GENERIC_ADDRESS gas; - struct acpi_cx *cx_ptr; - int error; - ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); + /* Use initial sleep value of 1 sec. to start with lowest idle state. */ + sc->cpu_prev_sleep = 1000000; + sc->cpu_cx_lowest = 0; + /* - * Bus mastering arbitration control is needed to keep caches coherent - * while sleeping in C3. If it's not present but a working flush cache - * instruction is present, flush the caches before entering C3 instead. - * Otherwise, just disable C3 completely. + * Check for the ACPI 2.0 _CST sleep states object. If we can't find + * any, we'll revert to generic FADT/P_BLK Cx control method which will + * be handled by acpi_cpu_startup. We need to defer to after having + * probed all the cpus in the system before probing for generic Cx + * states as we may already have found cpus with valid _CST packages */ - if (AcpiGbl_FADT.Pm2ControlBlock == 0 || AcpiGbl_FADT.Pm2ControlLength == 0) { - if ((AcpiGbl_FADT.Flags & ACPI_FADT_WBINVD) != 0 && - (AcpiGbl_FADT.Flags & ACPI_FADT_WBINVD_FLUSH) == 0) { - cpu_quirks |= CPU_QUIRK_NO_BM_CTRL; - ACPI_DEBUG_PRINT((ACPI_DB_INFO, - "acpi_cpu%d: no BM control, using flush cache method\n", - device_get_unit(sc->cpu_dev))); - } else { - cpu_quirks |= CPU_QUIRK_NO_C3; - ACPI_DEBUG_PRINT((ACPI_DB_INFO, - "acpi_cpu%d: no BM control, C3 not available\n", - device_get_unit(sc->cpu_dev))); - } + if (!cpu_cx_generic && acpi_cpu_cx_cst(sc) != 0) { + /* + * We were unable to find a _CST package for this cpu or there + * was an error parsing it. Switch back to generic mode. + */ + cpu_cx_generic = TRUE; + if (bootverbose) + device_printf(sc->cpu_dev, "switching to generic Cx mode\n"); } /* - * First, check for the ACPI 2.0 _CST sleep states object. - * If not usable, fall back to the P_BLK's P_LVL2 and P_LVL3. + * TODO: _CSD Package should be checked here. */ - sc->cpu_cx_count = 0; - error = acpi_cpu_cx_cst(sc); - if (error != 0) { - cx_ptr = sc->cpu_cx_states; - - /* C1 has been required since just after ACPI 1.0 */ - cx_ptr->type = ACPI_STATE_C1; - cx_ptr->trans_lat = 0; - cpu_non_c3 = 0; - cx_ptr++; - sc->cpu_cx_count++; - - /* - * The spec says P_BLK must be 6 bytes long. However, some systems - * use it to indicate a fractional set of features present so we - * take 5 as C2. Some may also have a value of 7 to indicate - * another C3 but most use _CST for this (as required) and having - * "only" C1-C3 is not a hardship. - */ - if (sc->cpu_p_blk_len < 5) - goto done; - - /* Validate and allocate resources for C2 (P_LVL2). */ - gas.SpaceId = ACPI_ADR_SPACE_SYSTEM_IO; - gas.BitWidth = 8; - if (AcpiGbl_FADT.C2Latency <= 100) { - gas.Address = sc->cpu_p_blk + 4; - cx_ptr->p_lvlx = acpi_bus_alloc_gas(sc->cpu_dev, &cpu_rid, &gas); - if (cx_ptr->p_lvlx != NULL) { - cpu_rid++; - cx_ptr->type = ACPI_STATE_C2; - cx_ptr->trans_lat = AcpiGbl_FADT.C2Latency; - cpu_non_c3 = 1; - cx_ptr++; - sc->cpu_cx_count++; - } - } - if (sc->cpu_p_blk_len < 6) - goto done; - - /* Validate and allocate resources for C3 (P_LVL3). */ - if (AcpiGbl_FADT.C3Latency <= 1000 && - (cpu_quirks & CPU_QUIRK_NO_C3) == 0) { - - gas.Address = sc->cpu_p_blk + 5; - cx_ptr->p_lvlx = acpi_bus_alloc_gas(sc->cpu_dev, &cpu_rid, &gas); - if (cx_ptr->p_lvlx != NULL) { - cpu_rid++; - cx_ptr->type = ACPI_STATE_C3; - cx_ptr->trans_lat = AcpiGbl_FADT.C3Latency; - cx_ptr++; - sc->cpu_cx_count++; - } - } - } +} -done: - /* If no valid registers were found, don't attach. */ - if (sc->cpu_cx_count == 0) - return (ENXIO); +static void +acpi_cpu_generic_cx_probe(struct acpi_cpu_softc *sc) +{ + ACPI_GENERIC_ADDRESS gas; + struct acpi_cx *cx_ptr; + + sc->cpu_cx_count = 0; + cx_ptr = sc->cpu_cx_states; /* Use initial sleep value of 1 sec. to start with lowest idle state. */ sc->cpu_prev_sleep = 1000000; - return (0); + /* C1 has been required since just after ACPI 1.0 */ + cx_ptr->type = ACPI_STATE_C1; + cx_ptr->trans_lat = 0; + cx_ptr++; + sc->cpu_cx_count++; + + /* + * The spec says P_BLK must be 6 bytes long. However, some systems + * use it to indicate a fractional set of features present so we + * take 5 as C2. Some may also have a value of 7 to indicate + * another C3 but most use _CST for this (as required) and having + * "only" C1-C3 is not a hardship. + */ + if (sc->cpu_p_blk_len < 5) + return; + + /* Validate and allocate resources for C2 (P_LVL2). */ + gas.SpaceId = ACPI_ADR_SPACE_SYSTEM_IO; + gas.BitWidth = 8; + if (AcpiGbl_FADT.C2Latency <= 100) { + gas.Address = sc->cpu_p_blk + 4; + cx_ptr->p_lvlx = acpi_bus_alloc_gas(sc->cpu_dev, &sc->cpu_rid, &gas, + RF_SHAREABLE); + if (cx_ptr->p_lvlx != NULL) { + sc->cpu_rid++; + cx_ptr->type = ACPI_STATE_C2; + cx_ptr->trans_lat = AcpiGbl_FADT.C2Latency; + cx_ptr++; + sc->cpu_cx_count++; + } + } + if (sc->cpu_p_blk_len < 6) + return; + + /* Validate and allocate resources for C3 (P_LVL3). */ + if (AcpiGbl_FADT.C3Latency <= 1000 && !(cpu_quirks & CPU_QUIRK_NO_C3)) { + gas.Address = sc->cpu_p_blk + 5; + cx_ptr->p_lvlx = acpi_bus_alloc_gas(sc->cpu_dev, &sc->cpu_rid, &gas, + RF_SHAREABLE); + if (cx_ptr->p_lvlx != NULL) { + sc->cpu_rid++; + cx_ptr->type = ACPI_STATE_C3; + cx_ptr->trans_lat = AcpiGbl_FADT.C3Latency; + cx_ptr++; + sc->cpu_cx_count++; + } + } + + /* Update the largest cx_count seen so far */ + if (sc->cpu_cx_count > cpu_cx_count) + cpu_cx_count = sc->cpu_cx_count; } /* @@ -591,12 +649,12 @@ acpi_cpu_cx_cst(struct acpi_cpu_softc *sc) /* _CST is a package with a count and at least one Cx package. */ top = (ACPI_OBJECT *)buf.Pointer; if (!ACPI_PKG_VALID(top, 2) || acpi_PkgInt32(top, 0, &count) != 0) { - device_printf(sc->cpu_dev, "Invalid _CST package\n"); + device_printf(sc->cpu_dev, "invalid _CST package\n"); AcpiOsFree(buf.Pointer); return (ENXIO); } if (count != top->Package.Count - 1) { - device_printf(sc->cpu_dev, "Invalid _CST state count (%d != %d)\n", + device_printf(sc->cpu_dev, "invalid _CST state count (%d != %d)\n", count, top->Package.Count - 1); count = top->Package.Count - 1; } @@ -615,14 +673,14 @@ acpi_cpu_cx_cst(struct acpi_cpu_softc *sc) acpi_PkgInt32(pkg, 2, &cx_ptr->trans_lat) != 0 || acpi_PkgInt32(pkg, 3, &cx_ptr->power) != 0) { - device_printf(sc->cpu_dev, "Skipping invalid Cx state package\n"); + device_printf(sc->cpu_dev, "skipping invalid Cx state package\n"); continue; } /* Validate the state to see if we should use it. */ switch (cx_ptr->type) { case ACPI_STATE_C1: - cpu_non_c3 = i; + sc->cpu_non_c3 = i; cx_ptr++; sc->cpu_cx_count++; continue; @@ -633,7 +691,7 @@ acpi_cpu_cx_cst(struct acpi_cpu_softc *sc) device_get_unit(sc->cpu_dev), i)); continue; } - cpu_non_c3 = i; + sc->cpu_non_c3 = i; break; case ACPI_STATE_C3: default: @@ -657,9 +715,10 @@ acpi_cpu_cx_cst(struct acpi_cpu_softc *sc) #endif /* Allocate the control register for C2 or C3. */ - acpi_PkgGas(sc->cpu_dev, pkg, 0, &cpu_rid, &cx_ptr->p_lvlx); - if (cx_ptr->p_lvlx != NULL) { - cpu_rid++; + acpi_PkgGas(sc->cpu_dev, pkg, 0, &sc->cpu_rid, &cx_ptr->p_lvlx, + RF_SHAREABLE); + if (cx_ptr->p_lvlx) { + sc->cpu_rid++; ACPI_DEBUG_PRINT((ACPI_DB_INFO, "acpi_cpu%d: Got C%d - %d latency\n", device_get_unit(sc->cpu_dev), cx_ptr->type, @@ -680,203 +739,125 @@ static void acpi_cpu_startup(void *arg) { struct acpi_cpu_softc *sc; - int count, i; + int i; /* Get set of CPU devices */ devclass_get_devices(acpi_cpu_devclass, &cpu_devices, &cpu_ndevices); /* - * Make sure all the processors' Cx counts match. We should probably - * also check the contents of each. However, no known systems have - * non-matching Cx counts so we'll deal with this later. + * Setup any quirks that might necessary now that we have probed + * all the CPUs */ - count = MAX_CX_STATES; - for (i = 0; i < cpu_ndevices; i++) { - sc = device_get_softc(cpu_devices[i]); - count = min(sc->cpu_cx_count, count); - } - cpu_cx_count = count; + acpi_cpu_quirks(); - /* Perform throttling and Cx final initialization. */ - sc = device_get_softc(cpu_devices[0]); - if (sc->cpu_p_cnt != NULL) - acpi_cpu_startup_throttling(); - if (cpu_cx_count > 0) - acpi_cpu_startup_cx(); - - /* register performance profile change handler */ - EVENTHANDLER_REGISTER(power_profile_change, acpi_cpu_power_profile, NULL, 0); -} - -/* - * Power profile change hook. - * - * Uses the ACPI lock to avoid reentrancy. - */ -static void -acpi_cpu_power_profile(void *arg) -{ - int state; - int speed; - ACPI_LOCK_DECL; + cpu_cx_count = 0; + if (cpu_cx_generic) { + /* + * We are using generic Cx mode, probe for available Cx states + * for all processors. + */ + for (i = 0; i < cpu_ndevices; i++) { + sc = device_get_softc(cpu_devices[i]); + acpi_cpu_generic_cx_probe(sc); + } - state = power_profile_get_state(); - if (state != POWER_PROFILE_PERFORMANCE && - state != POWER_PROFILE_ECONOMY) { - return; + /* + * Find the highest Cx state common to all CPUs + * in the system, taking quirks into account. + */ + for (i = 0; i < cpu_ndevices; i++) { + sc = device_get_softc(cpu_devices[i]); + if (sc->cpu_cx_count < cpu_cx_count) + cpu_cx_count = sc->cpu_cx_count; + } + } else { + /* + * We are using _CST mode, remove C3 state if necessary. + * Update the largest Cx state supported in the global cpu_cx_count. + * It will be used in the global Cx sysctl handler. + * As we now know for sure that we will be using _CST mode + * install our notify handler. + */ + for (i = 0; i < cpu_ndevices; i++) { + sc = device_get_softc(cpu_devices[i]); + if (cpu_quirks & CPU_QUIRK_NO_C3) { + sc->cpu_cx_count = sc->cpu_non_c3 + 1; + } + if (sc->cpu_cx_count > cpu_cx_count) + cpu_cx_count = sc->cpu_cx_count; + AcpiInstallNotifyHandler(sc->cpu_handle, ACPI_DEVICE_NOTIFY, + acpi_cpu_notify, sc); + } } - ACPI_LOCK; - switch(state) { - case POWER_PROFILE_PERFORMANCE: - speed = cpu_throttle_performance; - break; - case POWER_PROFILE_ECONOMY: - speed = cpu_throttle_economy; - break; - default: - speed = cpu_throttle_state; - break; + /* Perform Cx final initialization. */ + for (i = 0; i < cpu_ndevices; i++) { + sc = device_get_softc(cpu_devices[i]); + acpi_cpu_startup_cx(sc); } - if (speed != cpu_throttle_state) - acpi_cpu_throttle_set(speed); - ACPI_UNLOCK; -} -/* - * Takes the ACPI lock to avoid fighting anyone over the SMI command - * port. - */ -static void -acpi_cpu_startup_throttling(void) -{ - ACPI_LOCK_DECL; - - /* Initialise throttling states */ - cpu_throttle_max = CPU_MAX_SPEED; - cpu_throttle_state = CPU_MAX_SPEED; - cpu_throttle_performance = cpu_throttle_max; - cpu_throttle_economy = cpu_throttle_performance / 2; - - SYSCTL_ADD_INT(&acpi_cpu_sysctl_ctx, - SYSCTL_CHILDREN(acpi_cpu_sysctl_tree), - OID_AUTO, "throttle_max", CTLFLAG_RD, - &cpu_throttle_max, 0, "maximum CPU speed"); - SYSCTL_ADD_PROC(&acpi_cpu_sysctl_ctx, - SYSCTL_CHILDREN(acpi_cpu_sysctl_tree), - OID_AUTO, "throttle_state", - CTLTYPE_INT | CTLFLAG_RW, &cpu_throttle_state, - 0, acpi_cpu_throttle_sysctl, "I", "current CPU speed"); + /* Add a sysctl handler to handle global Cx lowest setting */ + SYSCTL_ADD_PROC(&cpu_sysctl_ctx, SYSCTL_CHILDREN(cpu_sysctl_tree), + OID_AUTO, "cx_lowest", CTLTYPE_STRING | CTLFLAG_RW, + NULL, 0, acpi_cpu_global_cx_lowest_sysctl, "A", + "Global lowest Cx sleep state to use"); - /* - * Performance/Economy throttle settings - */ - SYSCTL_ADD_PROC(&acpi_cpu_sysctl_ctx, - SYSCTL_CHILDREN(acpi_cpu_sysctl_tree), - OID_AUTO, "performance_speed", - CTLTYPE_INT | CTLFLAG_RW, &cpu_throttle_performance, - 0, acpi_cpu_throttle_sysctl, "I", "performance CPU speed"); - SYSCTL_ADD_PROC(&acpi_cpu_sysctl_ctx, - SYSCTL_CHILDREN(acpi_cpu_sysctl_tree), - OID_AUTO, "economy_speed", - CTLTYPE_INT | CTLFLAG_RW, &cpu_throttle_economy, - 0, acpi_cpu_throttle_sysctl, "I", "economy CPU speed"); - - /* Set initial speed to maximum. */ - ACPI_LOCK; - acpi_cpu_throttle_set(cpu_throttle_max); - ACPI_UNLOCK; - - kprintf("acpi_cpu: throttling enabled, %d steps (100%% to %d.%d%%), " - "currently %d.%d%%\n", CPU_MAX_SPEED, CPU_SPEED_PRINTABLE(1), - CPU_SPEED_PRINTABLE(cpu_throttle_state)); + /* Take over idling from cpu_idle_default(). */ + cpu_cx_lowest = 0; + cpu_disable_idle = FALSE; + cpu_idle_hook = acpi_cpu_idle; } static void -acpi_cpu_startup_cx(void) +acpi_cpu_cx_list(struct acpi_cpu_softc *sc) { - struct acpi_cpu_softc *sc; - struct sbuf sb; + struct sbuf sb; int i; - sc = device_get_softc(cpu_devices[0]); - sbuf_new(&sb, cpu_cx_supported, sizeof(cpu_cx_supported), SBUF_FIXEDLEN); - for (i = 0; i < cpu_cx_count; i++) + /* + * Set up the list of Cx states + */ + sc->cpu_non_c3 = 0; + sbuf_new(&sb, sc->cpu_cx_supported, sizeof(sc->cpu_cx_supported), + SBUF_FIXEDLEN); + for (i = 0; i < sc->cpu_cx_count; i++) { sbuf_printf(&sb, "C%d/%d ", i + 1, sc->cpu_cx_states[i].trans_lat); + if (sc->cpu_cx_states[i].type < ACPI_STATE_C3) + sc->cpu_non_c3 = i; + } sbuf_trim(&sb); sbuf_finish(&sb); - SYSCTL_ADD_STRING(&acpi_cpu_sysctl_ctx, - SYSCTL_CHILDREN(acpi_cpu_sysctl_tree), - OID_AUTO, "cx_supported", CTLFLAG_RD, cpu_cx_supported, - 0, "Cx/microsecond values for supported Cx states"); - SYSCTL_ADD_PROC(&acpi_cpu_sysctl_ctx, - SYSCTL_CHILDREN(acpi_cpu_sysctl_tree), +} + +static void +acpi_cpu_startup_cx(struct acpi_cpu_softc *sc) +{ + acpi_cpu_cx_list(sc); + + SYSCTL_ADD_STRING(&sc->cpu_sysctl_ctx, + SYSCTL_CHILDREN(sc->cpu_sysctl_tree), + OID_AUTO, "cx_supported", CTLFLAG_RD, + sc->cpu_cx_supported, 0, + "Cx/microsecond values for supported Cx states"); + SYSCTL_ADD_PROC(&sc->cpu_sysctl_ctx, + SYSCTL_CHILDREN(sc->cpu_sysctl_tree), OID_AUTO, "cx_lowest", CTLTYPE_STRING | CTLFLAG_RW, - NULL, 0, acpi_cpu_cx_lowest_sysctl, "A", + (void *)sc, 0, acpi_cpu_cx_lowest_sysctl, "A", "lowest Cx sleep state to use"); - SYSCTL_ADD_PROC(&acpi_cpu_sysctl_ctx, - SYSCTL_CHILDREN(acpi_cpu_sysctl_tree), + SYSCTL_ADD_PROC(&sc->cpu_sysctl_ctx, + SYSCTL_CHILDREN(sc->cpu_sysctl_tree), OID_AUTO, "cx_usage", CTLTYPE_STRING | CTLFLAG_RD, - NULL, 0, acpi_cpu_usage_sysctl, "A", + (void *)sc, 0, acpi_cpu_usage_sysctl, "A", "percent usage for each Cx state"); #ifdef notyet /* Signal platform that we can handle _CST notification. */ - if (cpu_cst_cnt != 0) { - ACPI_LOCK; + if (!cpu_cx_generic && cpu_cst_cnt != 0) { + ACPI_LOCK(acpi); AcpiOsWritePort(cpu_smi_cmd, cpu_cst_cnt, 8); - ACPI_UNLOCK; + ACPI_UNLOCK(acpi); } #endif - - /* Take over idling from cpu_idle_default_hook(). */ - if (ncpus == 1) - cpu_idle_hook = acpi_cpu_idle; - else - kprintf("Warning: ACPI idle hook not yet supported for SMP\n"); -} - -/* - * Set CPUs to the new state. - * - * Must be called with the ACPI lock held. - */ -static void -acpi_cpu_throttle_set(uint32_t speed) -{ - struct acpi_cpu_softc *sc; - int i; - uint32_t p_cnt, clk_val; - - ACPI_ASSERTLOCK; - - /* Iterate over processors */ - for (i = 0; i < cpu_ndevices; i++) { - sc = device_get_softc(cpu_devices[i]); - if (sc->cpu_p_cnt == NULL) - continue; - - /* Get the current P_CNT value and disable throttling */ - p_cnt = CPU_GET_REG(sc->cpu_p_cnt, 4); - p_cnt &= ~CPU_P_CNT_THT_EN; - CPU_SET_REG(sc->cpu_p_cnt, 4, p_cnt); - - /* If we're at maximum speed, that's all */ - if (speed < CPU_MAX_SPEED) { - /* Mask the old CLK_VAL off and or-in the new value */ - clk_val = (CPU_MAX_SPEED - 1) << cpu_duty_offset; - p_cnt &= ~clk_val; - p_cnt |= (speed << cpu_duty_offset); - - /* Write the new P_CNT value and then enable throttling */ - CPU_SET_REG(sc->cpu_p_cnt, 4, p_cnt); - p_cnt |= CPU_P_CNT_THT_EN; - CPU_SET_REG(sc->cpu_p_cnt, 4, p_cnt); - } - ACPI_VPRINT(sc->cpu_dev, acpi_device_get_parent_softc(sc->cpu_dev), - "set speed to %d.%d%%\n", CPU_SPEED_PRINTABLE(speed)); - } - cpu_throttle_state = speed; } /* @@ -886,7 +867,7 @@ acpi_cpu_throttle_set(uint32_t speed) * interrupts are re-enabled. */ static void -acpi_cpu_idle(void) +acpi_cpu_idle() { struct acpi_cpu_softc *sc; struct acpi_cx *cx_next; @@ -894,7 +875,7 @@ acpi_cpu_idle(void) int bm_active, cx_next_idx, i; /* If disabled, return immediately. */ - if (cpu_cx_count == 0) { + if (cpu_disable_idle) { ACPI_ENABLE_IRQS(); return; } @@ -915,13 +896,39 @@ acpi_cpu_idle(void) * find the lowest state that has a latency less than or equal to * the length of our last sleep. */ - cx_next_idx = cpu_cx_lowest; - if (sc->cpu_prev_sleep < 100) - for (i = cpu_cx_lowest; i >= 0; i--) + cx_next_idx = sc->cpu_cx_lowest; + if (sc->cpu_prev_sleep < 100) { + /* + * If we sleep too short all the time, this system may not implement + * C2/3 correctly (i.e. reads return immediately). In this case, + * back off and use the next higher level. + * It seems that when you have a dual core cpu (like the Intel Core Duo) + * that both cores will get out of C3 state as soon as one of them + * requires it. This breaks the sleep detection logic as the sleep + * counter is local to each cpu. Disable the sleep logic for now as a + * workaround if there's more than one CPU. The right fix would probably + * be to add quirks for system that don't really support C3 state. + */ + if (ncpus < 2 && sc->cpu_prev_sleep <= 1) { + sc->cpu_short_slp++; + if (sc->cpu_short_slp == 1000 && sc->cpu_cx_lowest != 0) { + if (sc->cpu_non_c3 == sc->cpu_cx_lowest && sc->cpu_non_c3 != 0) + sc->cpu_non_c3--; + sc->cpu_cx_lowest--; + sc->cpu_short_slp = 0; + device_printf(sc->cpu_dev, + "too many short sleeps, backing off to C%d\n", + sc->cpu_cx_lowest + 1); + } + } else + sc->cpu_short_slp = 0; + + for (i = sc->cpu_cx_lowest; i >= 0; i--) if (sc->cpu_cx_states[i].trans_lat <= sc->cpu_prev_sleep) { cx_next_idx = i; break; } + } /* * Check for bus master activity. If there was activity, clear @@ -933,13 +940,13 @@ acpi_cpu_idle(void) AcpiGetRegister(ACPI_BITREG_BUS_MASTER_STATUS, &bm_active); if (bm_active != 0) { AcpiSetRegister(ACPI_BITREG_BUS_MASTER_STATUS, 1); - cx_next_idx = min(cx_next_idx, cpu_non_c3); + cx_next_idx = min(cx_next_idx, sc->cpu_non_c3); } } /* Select the next state and update statistics. */ cx_next = &sc->cpu_cx_states[cx_next_idx]; - cpu_cx_stats[cx_next_idx]++; + sc->cpu_cx_stats[cx_next_idx]++; KASSERT(cx_next->type != ACPI_STATE_C0, ("acpi_cpu_idle: C0 sleep")); /* @@ -989,38 +996,15 @@ acpi_cpu_idle(void) AcpiSetRegister(ACPI_BITREG_ARB_DISABLE, 0); AcpiSetRegister(ACPI_BITREG_BUS_MASTER_RLD, 0); } + ACPI_ENABLE_IRQS(); /* Find the actual time asleep in microseconds, minus overhead. */ end_time = acpi_TimerDelta(end_time, start_time); sc->cpu_prev_sleep = PM_USEC(end_time) - cx_next->trans_lat; - ACPI_ENABLE_IRQS(); -} - -/* Put the CPU in C1 in a machine-dependant way. */ -static void -acpi_cpu_c1(void) -{ -#ifdef __ia64__ - ia64_call_pal_static(PAL_HALT_LIGHT, 0, 0, 0); -#else - splz(); -#ifdef SMP - if (!lwkt_runnable()) - __asm __volatile("sti; hlt"); - else - __asm __volatile("sti; pause"); -#else - if (!lwkt_runnable()) - __asm __volatile("sti; hlt"); - else - __asm __volatile("sti"); -#endif -#endif /* !__ia64__ */ } /* - * Re-evaluate the _PSS and _CST objects when we are notified that they - * have changed. + * Re-evaluate the _CST object when we are notified that it changed. * * XXX Re-evaluation disabled until locking is done. */ @@ -1028,50 +1012,69 @@ static void acpi_cpu_notify(ACPI_HANDLE h, UINT32 notify, void *context) { struct acpi_cpu_softc *sc = (struct acpi_cpu_softc *)context; + struct acpi_cpu_softc *isc; + int i; + + if (notify != ACPI_NOTIFY_CX_STATES) + return; - switch (notify) { - case ACPI_CPU_NOTIFY_PERF_STATES: - device_printf(sc->cpu_dev, "Performance states changed\n"); - /* acpi_cpu_px_available(sc); */ - break; - case ACPI_CPU_NOTIFY_CX_STATES: - device_printf(sc->cpu_dev, "Cx states changed\n"); - /* acpi_cpu_cx_cst(sc); */ - break; - default: - device_printf(sc->cpu_dev, "Unknown notify %#x\n", notify); - break; + /* Update the list of Cx states. */ + acpi_cpu_cx_cst(sc); + acpi_cpu_cx_list(sc); + + /* Update the new lowest useable Cx state for all CPUs. */ + crit_enter(); + cpu_cx_count = 0; + for (i = 0; i < cpu_ndevices; i++) { + isc = device_get_softc(cpu_devices[i]); + if (isc->cpu_cx_count > cpu_cx_count) + cpu_cx_count = isc->cpu_cx_count; } + crit_exit(); } static int -acpi_cpu_quirks(struct acpi_cpu_softc *sc) +acpi_cpu_quirks(void) { + device_t acpi_dev; + uint32_t val; + + ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); /* - * C3 on multiple CPUs requires using the expensive flush cache - * instruction. + * Bus mastering arbitration control is needed to keep caches coherent + * while sleeping in C3. If it's not present but a working flush cache + * instruction is present, flush the caches before entering C3 instead. + * Otherwise, just disable C3 completely. */ - if (ncpus > 1) + if (AcpiGbl_FADT.Pm2ControlBlock == 0 || + AcpiGbl_FADT.Pm2ControlLength == 0) { + if ((AcpiGbl_FADT.Flags & ACPI_FADT_WBINVD) && + (AcpiGbl_FADT.Flags & ACPI_FADT_WBINVD_FLUSH) == 0) { + cpu_quirks |= CPU_QUIRK_NO_BM_CTRL; + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "acpi_cpu: no BM control, using flush cache method\n")); + } else { + cpu_quirks |= CPU_QUIRK_NO_C3; + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "acpi_cpu: no BM control, C3 not available\n")); + } + } + + /* + * If we are using generic Cx mode, C3 on multiple CPUs requires using + * the expensive flush cache instruction. + */ + if (cpu_cx_generic && ncpus > 1) { cpu_quirks |= CPU_QUIRK_NO_BM_CTRL; + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "acpi_cpu: SMP, using flush cache mode for C3\n")); + } -#ifdef notyet /* Look for various quirks of the PIIX4 part. */ acpi_dev = pci_find_device(PCI_VENDOR_INTEL, PCI_DEVICE_82371AB_3); if (acpi_dev != NULL) { switch (pci_get_revid(acpi_dev)) { - /* - * Disable throttling control on PIIX4 A and B-step. - * See specification changes #13 ("Manual Throttle Duty Cycle") - * and #14 ("Enabling and Disabling Manual Throttle"), plus - * erratum #5 ("STPCLK# Deassertion Time") from the January - * 2002 PIIX4 specification update. Note that few (if any) - * mobile systems ever used this part. - */ - case PCI_REVISION_A_STEP: - case PCI_REVISION_B_STEP: - cpu_quirks |= CPU_QUIRK_NO_THROTTLE; - /* FALLTHROUGH */ /* * Disable C3 support for all PIIX4 chipsets. Some of these parts * do not report the BMIDE status to the BM status register and @@ -1083,46 +1086,30 @@ acpi_cpu_quirks(struct acpi_cpu_softc *sc) * See erratum #18 ("C3 Power State/BMIDE and Type-F DMA * Livelock") from the January 2002 PIIX4 specification update. * Applies to all PIIX4 models. + * + * Also, make sure that all interrupts cause a "Stop Break" + * event to exit from C2 state. */ + case PCI_REVISION_A_STEP: + case PCI_REVISION_B_STEP: case PCI_REVISION_4E: case PCI_REVISION_4M: cpu_quirks |= CPU_QUIRK_NO_C3; + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "acpi_cpu: working around PIIX4 bug, disabling C3\n")); + + val = pci_read_config(acpi_dev, PIIX4_DEVACTB_REG, 4); + if ((val & PIIX4_STOP_BREAK_MASK) != PIIX4_STOP_BREAK_MASK) { + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "PIIX4: enabling IRQs to generate Stop Break\n")); + val |= PIIX4_STOP_BREAK_MASK; + pci_write_config(acpi_dev, PIIX4_DEVACTB_REG, val, 4); + } break; default: break; } } -#endif - - return (0); -} - -/* Handle changes in the CPU throttling setting. */ -static int -acpi_cpu_throttle_sysctl(SYSCTL_HANDLER_ARGS) -{ - uint32_t *argp; - uint32_t arg; - int error; - ACPI_LOCK_DECL; - - argp = (uint32_t *)oidp->oid_arg1; - arg = *argp; - error = sysctl_handle_int(oidp, &arg, 0, req); - - /* Error or no new value */ - if (error != 0 || req->newptr == NULL) - return (error); - if (arg < 1 || arg > cpu_throttle_max) - return (EINVAL); - - /* If throttling changed, notify the BIOS of the new rate. */ - ACPI_LOCK; - if (*argp != arg) { - *argp = arg; - acpi_cpu_throttle_set(arg); - } - ACPI_UNLOCK; return (0); } @@ -1130,18 +1117,20 @@ acpi_cpu_throttle_sysctl(SYSCTL_HANDLER_ARGS) static int acpi_cpu_usage_sysctl(SYSCTL_HANDLER_ARGS) { + struct acpi_cpu_softc *sc; struct sbuf sb; char buf[128]; int i; uintmax_t fract, sum, whole; + sc = (struct acpi_cpu_softc *) arg1; sum = 0; - for (i = 0; i < cpu_cx_count; i++) - sum += cpu_cx_stats[i]; + for (i = 0; i < sc->cpu_cx_count; i++) + sum += sc->cpu_cx_stats[i]; sbuf_new(&sb, buf, sizeof(buf), SBUF_FIXEDLEN); - for (i = 0; i < cpu_cx_count; i++) { + for (i = 0; i < sc->cpu_cx_count; i++) { if (sum > 0) { - whole = (uintmax_t)cpu_cx_stats[i] * 100; + whole = (uintmax_t)sc->cpu_cx_stats[i] * 100; fract = (whole % sum) * 100; sbuf_printf(&sb, "%u.%02u%% ", (u_int)(whole / sum), (u_int)(fract / sum)); @@ -1156,14 +1145,59 @@ acpi_cpu_usage_sysctl(SYSCTL_HANDLER_ARGS) return (0); } +static int +acpi_cpu_set_cx_lowest(struct acpi_cpu_softc *sc, int val) +{ + int i; + + sc->cpu_cx_lowest = val; + + /* If not disabling, cache the new lowest non-C3 state. */ + sc->cpu_non_c3 = 0; + for (i = sc->cpu_cx_lowest; i >= 0; i--) { + if (sc->cpu_cx_states[i].type < ACPI_STATE_C3) { + sc->cpu_non_c3 = i; + break; + } + } + + /* Reset the statistics counters. */ + bzero(sc->cpu_cx_stats, sizeof(sc->cpu_cx_stats)); + return (0); +} + static int acpi_cpu_cx_lowest_sysctl(SYSCTL_HANDLER_ARGS) { struct acpi_cpu_softc *sc; char state[8]; - int val, error, i; + int val, error; + + sc = (struct acpi_cpu_softc *) arg1; + ksnprintf(state, sizeof(state), "C%d", sc->cpu_cx_lowest + 1); + error = sysctl_handle_string(oidp, state, sizeof(state), req); + if (error != 0 || req->newptr == NULL) + return (error); + if (strlen(state) < 2 || toupper(state[0]) != 'C') + return (EINVAL); + val = (int) strtol(state + 1, NULL, 10) - 1; + if (val < 0 || val > sc->cpu_cx_count - 1) + return (EINVAL); + + crit_enter(); + acpi_cpu_set_cx_lowest(sc, val); + crit_exit(); + + return (0); +} + +static int +acpi_cpu_global_cx_lowest_sysctl(SYSCTL_HANDLER_ARGS) +{ + struct acpi_cpu_softc *sc; + char state[8]; + int val, error, i; - sc = device_get_softc(cpu_devices[0]); ksnprintf(state, sizeof(state), "C%d", cpu_cx_lowest + 1); error = sysctl_handle_string(oidp, state, sizeof(state), req); if (error != 0 || req->newptr == NULL) @@ -1173,20 +1207,41 @@ acpi_cpu_cx_lowest_sysctl(SYSCTL_HANDLER_ARGS) val = (int) strtol(state + 1, NULL, 10) - 1; if (val < 0 || val > cpu_cx_count - 1) return (EINVAL); - cpu_cx_lowest = val; - /* If not disabling, cache the new lowest non-C3 state. */ - cpu_non_c3 = 0; - for (i = cpu_cx_lowest; i >= 0; i--) { - if (sc->cpu_cx_states[i].type < ACPI_STATE_C3) { - cpu_non_c3 = i; - break; - } + /* Update the new lowest useable Cx state for all CPUs. */ + crit_enter(); + for (i = 0; i < cpu_ndevices; i++) { + sc = device_get_softc(cpu_devices[i]); + acpi_cpu_set_cx_lowest(sc, val); } - - /* Reset the statistics counters. */ - bzero(cpu_cx_stats, sizeof(cpu_cx_stats)); + crit_exit(); return (0); } + +/* + * Put the CPU in C1 in a machine-dependant way. + * XXX: shouldn't be here! + */ +static void +acpi_cpu_c1(void) +{ +#ifdef __ia64__ + ia64_call_pal_static(PAL_HALT_LIGHT, 0, 0, 0); +#else + splz(); +#ifdef SMP + if (!lwkt_runnable()) + __asm __volatile("sti; hlt"); + else + __asm __volatile("sti; pause"); +#else + if (!lwkt_runnable()) + __asm __volatile("sti; hlt"); + else + __asm __volatile("sti"); +#endif +#endif /* !__ia64__ */ +} + diff --git a/sys/dev/acpica5/acpi_if.m b/sys/dev/acpica5/acpi_if.m index 9c46815240..ab8fc0fc87 100644 --- a/sys/dev/acpica5/acpi_if.m +++ b/sys/dev/acpica5/acpi_if.m @@ -23,7 +23,7 @@ # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF # SUCH DAMAGE. # -# $DragonFly: src/sys/dev/acpica5/acpi_if.m,v 1.1 2008/08/27 16:35:19 hasso Exp $ +# $DragonFly: src/sys/dev/acpica5/acpi_if.m,v 1.2 2008/09/05 10:28:35 hasso Exp $ # #include @@ -61,6 +61,19 @@ METHOD char * id_probe { char **ids; } DEFAULT acpi_generic_id_probe; +# +# Query a given driver for its supported feature(s). This should be +# called by the parent bus before the driver is probed. +# +# driver_t *driver: child driver +# +# u_int *features: returned bitmask of all supported features +# +STATICMETHOD int get_features { + driver_t *driver; + u_int *features; +}; + # # Read embedded controller (EC) address space # diff --git a/sys/dev/acpica5/acpi_package.c b/sys/dev/acpica5/acpi_package.c index 30c704f741..310dba7fc7 100644 --- a/sys/dev/acpica5/acpi_package.c +++ b/sys/dev/acpica5/acpi_package.c @@ -24,7 +24,7 @@ * SUCH DAMAGE. * * $FreeBSD: src/sys/dev/acpica/acpi_package.c,v 1.3 2004/04/09 06:40:03 njl Exp $ - * $DragonFly: src/sys/dev/acpica5/acpi_package.c,v 1.3 2006/10/25 20:55:52 dillon Exp $ + * $DragonFly: src/sys/dev/acpica5/acpi_package.c,v 1.4 2008/09/05 10:28:35 hasso Exp $ */ #include @@ -101,7 +101,7 @@ acpi_PkgStr(ACPI_OBJECT *res, int idx, void *dst, size_t size) int acpi_PkgGas(device_t dev, ACPI_OBJECT *res, int idx, int *rid, - struct resource **dst) + struct resource **dst, u_int flags) { ACPI_GENERIC_ADDRESS gas; ACPI_OBJECT *obj; @@ -114,7 +114,7 @@ acpi_PkgGas(device_t dev, ACPI_OBJECT *res, int idx, int *rid, } memcpy(&gas, obj->Buffer.Pointer + 3, sizeof(gas)); - *dst = acpi_bus_alloc_gas(dev, rid, &gas); + *dst = acpi_bus_alloc_gas(dev, rid, &gas, flags); if (*dst == NULL) return (ENXIO); diff --git a/sys/dev/acpica5/acpivar.h b/sys/dev/acpica5/acpivar.h index c43eccd3ce..a21e7975ec 100644 --- a/sys/dev/acpica5/acpivar.h +++ b/sys/dev/acpica5/acpivar.h @@ -26,7 +26,7 @@ * SUCH DAMAGE. * * $FreeBSD: src/sys/dev/acpica/acpivar.h,v 1.71 2004/06/13 22:52:30 njl Exp $ - * $DragonFly: src/sys/dev/acpica5/acpivar.h,v 1.14 2008/08/28 16:46:54 hasso Exp $ + * $DragonFly: src/sys/dev/acpica5/acpivar.h,v 1.15 2008/09/05 10:28:35 hasso Exp $ */ #include "acpi_if.h" @@ -147,6 +147,20 @@ struct acpi_prw_data { #define ACPI_INTR_APIC 1 #define ACPI_INTR_SAPIC 2 +/* + * Various features and capabilities for the acpi_get_features() method. + * In particular, these are used for the ACPI 3.0 _PDC and _OSC methods. + */ +#define ACPI_CAP_PERF_MSRS (1 << 0) /* Intel SpeedStep PERF_CTL MSRs */ +#define ACPI_CAP_C1_IO_HALT (1 << 1) /* Intel C1 "IO then halt" sequence */ +#define ACPI_CAP_THR_MSRS (1 << 2) /* Intel OnDemand throttling MSRs */ +#define ACPI_CAP_SMP_SAME (1 << 3) /* MP C1, Px, and Tx (all the same) */ +#define ACPI_CAP_SMP_SAME_C3 (1 << 4) /* MP C2 and C3 (all the same) */ +#define ACPI_CAP_SMP_DIFF_PX (1 << 5) /* MP Px (different, using _PSD) */ +#define ACPI_CAP_SMP_DIFF_CX (1 << 6) /* MP Cx (different, using _CSD) */ +#define ACPI_CAP_SMP_DIFF_TX (1 << 7) /* MP Tx (different, using _TSD) */ +#define ACPI_CAP_SMP_C1_NATIVE (1 << 8) /* MP C1 support other than halt */ + /* * Note that the low ivar values are reserved to provide * interface compatibility with ISA drivers which can also @@ -252,7 +266,7 @@ ACPI_STATUS acpi_Disable(struct acpi_softc *sc); void acpi_UserNotify(const char *subsystem, ACPI_HANDLE h, uint8_t notify); struct resource *acpi_bus_alloc_gas(device_t dev, int *rid, - ACPI_GENERIC_ADDRESS *gas); + ACPI_GENERIC_ADDRESS *gas, u_int flags); struct acpi_parse_resource_set { void (*set_init)(device_t dev, void *arg, void **context); @@ -354,7 +368,7 @@ int acpi_PkgInt(ACPI_OBJECT *res, int idx, ACPI_INTEGER *dst); int acpi_PkgInt32(ACPI_OBJECT *res, int idx, uint32_t *dst); int acpi_PkgStr(ACPI_OBJECT *res, int idx, void *dst, size_t size); int acpi_PkgGas(device_t dev, ACPI_OBJECT *res, int idx, int *rid, - struct resource **dst); + struct resource **dst, u_int flags); ACPI_HANDLE acpi_GetReference(ACPI_HANDLE scope, ACPI_OBJECT *obj); /* ACPI task kernel thread initialization. */ diff --git a/sys/kern/subr_bus.c b/sys/kern/subr_bus.c index 3975dca634..4bda88552d 100644 --- a/sys/kern/subr_bus.c +++ b/sys/kern/subr_bus.c @@ -24,7 +24,7 @@ * SUCH DAMAGE. * * $FreeBSD: src/sys/kern/subr_bus.c,v 1.54.2.9 2002/10/10 15:13:32 jhb Exp $ - * $DragonFly: src/sys/kern/subr_bus.c,v 1.42 2008/09/02 17:48:26 hasso Exp $ + * $DragonFly: src/sys/kern/subr_bus.c,v 1.43 2008/09/05 10:28:35 hasso Exp $ */ #include "opt_bus.h" @@ -357,6 +357,47 @@ devclass_get_devices(devclass_t dc, device_t **devlistp, int *devcountp) return(0); } +/** + * @brief Get a list of drivers in the devclass + * + * An array containing a list of pointers to all the drivers in the + * given devclass is allocated and returned in @p *listp. The number + * of drivers in the array is returned in @p *countp. The caller should + * free the array using @c free(p, M_TEMP). + * + * @param dc the devclass to examine + * @param listp gives location for array pointer return value + * @param countp gives location for number of array elements + * return value + * + * @retval 0 success + * @retval ENOMEM the array allocation failed + */ +int +devclass_get_drivers(devclass_t dc, driver_t ***listp, int *countp) +{ + driverlink_t dl; + driver_t **list; + int count; + + count = 0; + TAILQ_FOREACH(dl, &dc->drivers, link) + count++; + list = kmalloc(count * sizeof(driver_t *), M_TEMP, M_NOWAIT); + if (list == NULL) + return (ENOMEM); + + count = 0; + TAILQ_FOREACH(dl, &dc->drivers, link) { + list[count] = dl->driver; + count++; + } + *listp = list; + *countp = count; + + return (0); +} + int devclass_get_maxunit(devclass_t dc) { diff --git a/sys/sys/bus.h b/sys/sys/bus.h index 79b4b1df5e..38402c26be 100644 --- a/sys/sys/bus.h +++ b/sys/sys/bus.h @@ -24,7 +24,7 @@ * SUCH DAMAGE. * * $FreeBSD: src/sys/sys/bus.h,v 1.30.2.5 2004/03/17 17:54:25 njl Exp $ - * $DragonFly: src/sys/sys/bus.h,v 1.27 2008/08/17 04:32:32 sephe Exp $ + * $DragonFly: src/sys/sys/bus.h,v 1.28 2008/09/05 10:28:35 hasso Exp $ */ #ifndef _SYS_BUS_H_ @@ -352,6 +352,7 @@ const char *devclass_get_name(devclass_t dc); device_t devclass_get_device(devclass_t dc, int unit); void *devclass_get_softc(devclass_t dc, int unit); int devclass_get_devices(devclass_t dc, device_t **listp, int *countp); +int devclass_get_drivers(devclass_t dc, driver_t ***listp, int *countp); int devclass_get_maxunit(devclass_t dc); void devclass_set_parent(devclass_t dc, devclass_t pdc); devclass_t devclass_get_parent(devclass_t dc);