Index: sys/dev/acpica/acpi_cpu.c =================================================================== RCS file: /home/ncvs/src/sys/dev/acpica/acpi_cpu.c,v retrieving revision 1.19 diff -u -r1.19 acpi_cpu.c --- sys/dev/acpica/acpi_cpu.c 15 Nov 2003 19:26:05 -0000 1.19 +++ sys/dev/acpica/acpi_cpu.c 18 Nov 2003 17:46:23 -0000 @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2003 Nate Lawson + * Copyright (c) 2003 Nate Lawson (SDG) * Copyright (c) 2001 Michael Smith * All rights reserved. * @@ -77,9 +77,11 @@ device_t cpu_dev; ACPI_HANDLE cpu_handle; uint32_t cpu_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_bm_ok; /* Bus mastering control available. */ + int cpu_cx_count; /* Number of valid Cx states. */ }; #define CPU_GET_REG(reg, width) \ @@ -116,10 +118,9 @@ #define PCI_REVISION_4M 3 /* Platform hardware resource information. */ -static uint32_t cpu_p_blk; /* ACPI P_BLK location */ -static uint32_t cpu_p_blk_len; /* P_BLK length (must be 6). */ static uint32_t cpu_smi_cmd; /* Value to write to SMI_CMD. */ static uint8_t cpu_pstate_cnt;/* Register to take over throttling. */ +static uint8_t cpu_cst_cnt; /* Indicate we are _CST aware. */ static uint32_t cpu_rid; /* Driver-wide resource id. */ static uint32_t cpu_quirks; /* Indicate any hardware bugs. */ @@ -146,11 +147,13 @@ static int acpi_cpu_probe(device_t dev); static int acpi_cpu_attach(device_t dev); +static int acpi_cpu_detach(device_t dev); static int acpi_cpu_throttle_probe(struct acpi_cpu_softc *sc); static int acpi_cpu_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_idle(void); static void acpi_cpu_c1(void); @@ -166,6 +169,7 @@ /* Device interface */ DEVMETHOD(device_probe, acpi_cpu_probe), DEVMETHOD(device_attach, acpi_cpu_attach), + DEVMETHOD(device_detach, acpi_cpu_detach), {0, 0} }; @@ -178,6 +182,7 @@ static devclass_t acpi_cpu_devclass; DRIVER_MODULE(acpi_cpu, acpi, acpi_cpu_driver, acpi_cpu_devclass, 0, 0); + static int acpi_cpu_probe(device_t dev) { @@ -272,11 +277,10 @@ AcpiEvaluateObject(sc->cpu_handle, "_INI", NULL, NULL); /* Get various global values from the Processor object. */ - cpu_p_blk = pobj.Processor.PblkAddress; - cpu_p_blk_len = pobj.Processor.PblkLength; + sc->cpu_p_blk = pobj.Processor.PblkAddress; + sc->cpu_p_blk_len = pobj.Processor.PblkLength; ACPI_DEBUG_PRINT((ACPI_DB_IO, "acpi_cpu%d: P_BLK at %#x/%d%s\n", - device_get_unit(dev), cpu_p_blk, cpu_p_blk_len, - sc->cpu_p_cnt ? "" : " (shadowed)")); + 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); @@ -297,7 +301,8 @@ if (thr_ret == 0 || cx_ret == 0) { status = AcpiInstallNotifyHandler(sc->cpu_handle, ACPI_DEVICE_NOTIFY, acpi_cpu_notify, sc); - AcpiOsQueueForExecution(OSD_PRIORITY_LO, acpi_cpu_startup, sc); + if (device_get_unit(dev) == 0) + AcpiOsQueueForExecution(OSD_PRIORITY_LO, acpi_cpu_startup, NULL); } else { sysctl_ctx_free(&acpi_cpu_sysctl_ctx); } @@ -306,6 +311,21 @@ } static int +acpi_cpu_detach(device_t dev) +{ + + /* Disable any entry to the idle function. */ + cpu_cx_count = 0; + +#ifdef SMP + /* Wait for all processors to exit acpi_cpu_idle(). */ + smp_rendezvous(NULL, NULL, NULL, NULL); +#endif + + return_VALUE (0); +} + +static int acpi_cpu_throttle_probe(struct acpi_cpu_softc *sc) { uint32_t duty_end; @@ -319,10 +339,13 @@ ACPI_ASSERTLOCK; /* Get throttling parameters from the FADT. 0 means not supported. */ - cpu_smi_cmd = AcpiGbl_FADT->SmiCmd; - cpu_pstate_cnt = AcpiGbl_FADT->PstateCnt; - cpu_duty_offset = AcpiGbl_FADT->DutyOffset; - cpu_duty_width = AcpiGbl_FADT->DutyWidth; + if (device_get_unit(sc->cpu_dev) == 0) { + cpu_smi_cmd = AcpiGbl_FADT->SmiCmd; + cpu_pstate_cnt = AcpiGbl_FADT->PstateCnt; + cpu_cst_cnt = AcpiGbl_FADT->CstCnt; + cpu_duty_offset = AcpiGbl_FADT->DutyOffset; + cpu_duty_width = AcpiGbl_FADT->DutyWidth; + } if (cpu_duty_width == 0 || (cpu_quirks & CPU_QUIRK_NO_THROTTLE) != 0) return (ENXIO); @@ -363,9 +386,9 @@ /* If _PTC not present or other failure, try the P_BLK. */ if (sc->cpu_p_cnt == NULL) { /* The spec says P_BLK must be at least 6 bytes long. */ - if (cpu_p_blk_len != 6) + if (sc->cpu_p_blk_len != 6) return (ENXIO); - gas.Address = cpu_p_blk; + gas.Address = sc->cpu_p_blk; gas.AddressSpaceId = ACPI_ADR_SPACE_SYSTEM_IO; gas.RegisterBitWidth = 32; sc->cpu_p_cnt = acpi_bus_alloc_gas(sc->cpu_dev, &cpu_rid, &gas); @@ -379,25 +402,6 @@ } cpu_rid++; - SYSCTL_ADD_INT(&acpi_cpu_sysctl_ctx, - SYSCTL_CHILDREN(acpi_cpu_sysctl_tree), - OID_AUTO, "max_speed", CTLFLAG_RD, - &cpu_max_state, 0, "maximum CPU speed"); - SYSCTL_ADD_INT(&acpi_cpu_sysctl_ctx, - SYSCTL_CHILDREN(acpi_cpu_sysctl_tree), - OID_AUTO, "current_speed", CTLFLAG_RD, - &cpu_current_state, 0, "current CPU speed"); - SYSCTL_ADD_PROC(&acpi_cpu_sysctl_ctx, - SYSCTL_CHILDREN(acpi_cpu_sysctl_tree), - OID_AUTO, "performance_speed", - CTLTYPE_INT | CTLFLAG_RW, &cpu_performance_state, - 0, acpi_cpu_throttle_sysctl, "I", ""); - SYSCTL_ADD_PROC(&acpi_cpu_sysctl_ctx, - SYSCTL_CHILDREN(acpi_cpu_sysctl_tree), - OID_AUTO, "economy_speed", - CTLTYPE_INT | CTLFLAG_RW, &cpu_economy_state, - 0, acpi_cpu_throttle_sysctl, "I", ""); - return (0); } @@ -406,8 +410,7 @@ { ACPI_GENERIC_ADDRESS gas; struct acpi_cx *cx_ptr; - struct sbuf sb; - int i, error; + int error; /* Bus mastering arbitration control is needed for C3. */ if (AcpiGbl_FADT->V1_Pm2CntBlk == 0 || AcpiGbl_FADT->Pm2CntLen == 0) { @@ -420,11 +423,9 @@ * 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. */ - cpu_cx_count = 0; + sc->cpu_cx_count = 0; error = acpi_cpu_cx_cst(sc); if (error != 0) { - if (cpu_p_blk_len != 6) - return (ENXIO); cx_ptr = sc->cpu_cx_states; /* C1 has been required since just after ACPI 1.0 */ @@ -432,13 +433,16 @@ cx_ptr->trans_lat = 0; cpu_non_c3 = 0; cx_ptr++; - cpu_cx_count++; + sc->cpu_cx_count++; + + if (sc->cpu_p_blk_len != 6) + goto done; /* Validate and allocate resources for C2 (P_LVL2). */ gas.AddressSpaceId = ACPI_ADR_SPACE_SYSTEM_IO; gas.RegisterBitWidth = 8; if (AcpiGbl_FADT->Plvl2Lat < 100) { - gas.Address = cpu_p_blk + 4; + 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++; @@ -446,7 +450,7 @@ cx_ptr->trans_lat = AcpiGbl_FADT->Plvl2Lat; cpu_non_c3 = 1; cx_ptr++; - cpu_cx_count++; + sc->cpu_cx_count++; } } @@ -454,47 +458,23 @@ if (AcpiGbl_FADT->Plvl3Lat < 1000 && (cpu_quirks & CPU_QUIRK_NO_C3) == 0) { - gas.Address = cpu_p_blk + 5; + 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->Plvl3Lat; cx_ptr++; - cpu_cx_count++; + sc->cpu_cx_count++; } } } +done: /* If no valid registers were found, don't attach. */ - if (cpu_cx_count == 0) + if (sc->cpu_cx_count == 0) return (ENXIO); - sbuf_new(&sb, cpu_cx_supported, sizeof(cpu_cx_supported), SBUF_FIXEDLEN); - for (i = 0; i < cpu_cx_count; i++) { - sbuf_printf(&sb, "C%d/%d ", sc->cpu_cx_states[i].type, - sc->cpu_cx_states[i].trans_lat); - } - 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), - OID_AUTO, "cx_lowest", CTLTYPE_INT | CTLFLAG_RW, - NULL, 0, acpi_cpu_cx_lowest_sysctl, "I", - "lowest Cx sleep state to use"); - SYSCTL_ADD_PROC(&acpi_cpu_sysctl_ctx, - SYSCTL_CHILDREN(acpi_cpu_sysctl_tree), - OID_AUTO, "cx_history", CTLTYPE_STRING | CTLFLAG_RD, - NULL, 0, acpi_cpu_history_sysctl, "A", ""); - - /* Set next sleep state and hook the idle function. */ - cpu_cx_next = cpu_cx_lowest; - cpu_idle_hook = acpi_cpu_idle; - return (0); } @@ -534,13 +514,12 @@ count = top->Package.Count - 1; } if (count > MAX_CX_STATES) { - device_printf(sc->cpu_dev, "_CST has too many states (%d)\n", - count); + device_printf(sc->cpu_dev, "_CST has too many states (%d)\n", count); count = MAX_CX_STATES; } /* Set up all valid states. */ - cpu_cx_count = 0; + sc->cpu_cx_count = 0; cx_ptr = sc->cpu_cx_states; for (i = 0; i < count; i++) { pkg = &top->Package.Elements[i + 1]; @@ -559,7 +538,7 @@ case ACPI_STATE_C1: cpu_non_c3 = i; cx_ptr++; - cpu_cx_count++; + sc->cpu_cx_count++; continue; case ACPI_STATE_C2: if (cx_ptr->trans_lat > 100) { @@ -594,7 +573,7 @@ device_printf(sc->cpu_dev, "C%d state %d lat\n", cx_ptr->type, cx_ptr->trans_lat); cx_ptr++; - cpu_cx_count++; + sc->cpu_cx_count++; } } AcpiOsFree(buf.Pointer); @@ -604,17 +583,12 @@ /* * Call this *after* all CPUs have been attached. - * - * Takes the ACPI lock to avoid fighting anyone over the SMI command - * port. Could probably lock less code. */ static void acpi_cpu_startup(void *arg) { - struct acpi_cpu_softc *sc = (struct acpi_cpu_softc *)arg; - ACPI_LOCK_DECL; - - ACPI_LOCK; + struct acpi_cpu_softc *sc; + int count, i; /* Get set of CPU devices */ devclass_get_devices(acpi_cpu_devclass, &cpu_devices, &cpu_ndevices); @@ -623,16 +597,35 @@ EVENTHANDLER_REGISTER(power_profile_change, acpi_cpu_power_profile, NULL, 0); + /* + * 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. + */ + 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; + + /* Perform throttling and Cx final initialization. */ + sc = device_get_softc(cpu_devices[0]); if (sc->cpu_p_cnt != NULL) acpi_cpu_startup_throttling(); - else - ACPI_UNLOCK; + if (cpu_cx_count > 0) + acpi_cpu_startup_cx(); } +/* + * Takes the ACPI lock to avoid fighting anyone over the SMI command + * port. + */ static void acpi_cpu_startup_throttling() { int cpu_temp_speed; + ACPI_LOCK_DECL; /* Initialise throttling states */ cpu_max_state = CPU_MAX_SPEED; @@ -653,11 +646,31 @@ cpu_economy_state = cpu_temp_speed; } + SYSCTL_ADD_INT(&acpi_cpu_sysctl_ctx, + SYSCTL_CHILDREN(acpi_cpu_sysctl_tree), + OID_AUTO, "max_speed", CTLFLAG_RD, + &cpu_max_state, 0, "maximum CPU speed"); + SYSCTL_ADD_INT(&acpi_cpu_sysctl_ctx, + SYSCTL_CHILDREN(acpi_cpu_sysctl_tree), + OID_AUTO, "current_speed", CTLFLAG_RD, + &cpu_current_state, 0, "current CPU speed"); + SYSCTL_ADD_PROC(&acpi_cpu_sysctl_ctx, + SYSCTL_CHILDREN(acpi_cpu_sysctl_tree), + OID_AUTO, "performance_speed", + CTLTYPE_INT | CTLFLAG_RW, &cpu_performance_state, + 0, acpi_cpu_throttle_sysctl, "I", ""); + SYSCTL_ADD_PROC(&acpi_cpu_sysctl_ctx, + SYSCTL_CHILDREN(acpi_cpu_sysctl_tree), + OID_AUTO, "economy_speed", + CTLTYPE_INT | CTLFLAG_RW, &cpu_economy_state, + 0, acpi_cpu_throttle_sysctl, "I", ""); + /* If ACPI 2.0+, signal platform that we are taking over throttling. */ - if (cpu_pstate_cnt != 0) + if (cpu_pstate_cnt != 0) { + ACPI_LOCK; AcpiOsWritePort(cpu_smi_cmd, cpu_pstate_cnt, 8); - - ACPI_UNLOCK; + ACPI_UNLOCK; + } /* Set initial speed */ acpi_cpu_power_profile(NULL); @@ -667,6 +680,50 @@ CPU_SPEED_PRINTABLE(cpu_current_state)); } +static void +acpi_cpu_startup_cx() +{ + struct acpi_cpu_softc *sc; + struct sbuf sb; + int i; + ACPI_LOCK_DECL; + + 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++) { + sbuf_printf(&sb, "C%d/%d ", sc->cpu_cx_states[i].type, + sc->cpu_cx_states[i].trans_lat); + } + 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), + OID_AUTO, "cx_lowest", CTLTYPE_INT | CTLFLAG_RW, + NULL, 0, acpi_cpu_cx_lowest_sysctl, "I", + "lowest Cx sleep state to use"); + SYSCTL_ADD_PROC(&acpi_cpu_sysctl_ctx, + SYSCTL_CHILDREN(acpi_cpu_sysctl_tree), + OID_AUTO, "cx_history", CTLTYPE_STRING | CTLFLAG_RD, + NULL, 0, acpi_cpu_history_sysctl, "A", ""); + +#ifdef notyet + /* Signal platform that we can handle _CST notification. */ + if (cpu_cst_cnt != 0) { + ACPI_LOCK; + AcpiOsWritePort(cpu_smi_cmd, cpu_cst_cnt, 8); + ACPI_UNLOCK; + } +#endif + + /* Take over idling from cpu_idle_default(). */ + cpu_cx_next = cpu_cx_lowest; + cpu_idle_hook = acpi_cpu_idle; +} + /* * Set CPUs to the new state. * @@ -727,7 +784,7 @@ KASSERT(sc != NULL, ("NULL softc for %d", PCPU_GET(acpi_id))); /* If disabled, return immediately. */ - if (cpu_cx_lowest < 0 || cpu_cx_count == 0) { + if (cpu_cx_count == 0) { ACPI_ENABLE_IRQS(); return; } @@ -749,6 +806,17 @@ /* Perform the actual sleep based on the Cx-specific semantics. */ cx_next = &sc->cpu_cx_states[cpu_cx_next]; switch (cx_next->type) { + case ACPI_STATE_C0: + { + static int once; + + if (once == 0) { + printf("C0? cx_next %d cx_count %d\n", cpu_cx_next, cpu_cx_count); + once = 1; + } + ACPI_ENABLE_IRQS(); + return; + } case ACPI_STATE_C1: /* Execute HLT (or equivalent) and wait for an interrupt. */ acpi_cpu_c1(); @@ -854,6 +922,7 @@ /* * Re-evaluate the _PSS and _CST objects when we are notified that they * have changed. + * * XXX Re-evaluation disabled until locking is done. */ static void @@ -1027,7 +1096,7 @@ error = sysctl_handle_int(oidp, &val, 0, req); if (error != 0 || req->newptr == NULL) return (error); - if (val < -1 || val > cpu_cx_count - 1) + if (val < 0 || val > cpu_cx_count - 1) return (EINVAL); /* Use the new value for the next idle slice. */ Index: share/man/man4/acpi.4 =================================================================== RCS file: /home/ncvs/src/share/man/man4/acpi.4,v retrieving revision 1.17 diff -u -r1.17 acpi.4 --- share/man/man4/acpi.4 15 Nov 2003 19:26:05 -0000 1.17 +++ share/man/man4/acpi.4 17 Nov 2003 17:18:09 -0000 @@ -342,7 +342,6 @@ is modified. .It Va hw.acpi.cpu.cx_lowest Zero-based index of the lowest CPU idle state to use. -A value of -1 disables ACPI CPU idle states. To enable ACPI CPU idling control, .Va machdep.cpu_idle_hlt must be set to 1.