acpi_perf.c revision 144145
1141242Snjl/*- 2141242Snjl * Copyright (c) 2003-2005 Nate Lawson (SDG) 3141242Snjl * All rights reserved. 4141242Snjl * 5141242Snjl * Redistribution and use in source and binary forms, with or without 6141242Snjl * modification, are permitted provided that the following conditions 7141242Snjl * are met: 8141242Snjl * 1. Redistributions of source code must retain the above copyright 9141242Snjl * notice, this list of conditions and the following disclaimer. 10141242Snjl * 2. Redistributions in binary form must reproduce the above copyright 11141242Snjl * notice, this list of conditions and the following disclaimer in the 12141242Snjl * documentation and/or other materials provided with the distribution. 13141242Snjl * 14141242Snjl * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15141242Snjl * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16141242Snjl * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17141242Snjl * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18141242Snjl * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19141242Snjl * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20141242Snjl * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21141242Snjl * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22141242Snjl * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23141242Snjl * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24141242Snjl * SUCH DAMAGE. 25141242Snjl */ 26141242Snjl 27141242Snjl#include <sys/cdefs.h> 28141242Snjl__FBSDID("$FreeBSD: head/sys/dev/acpica/acpi_perf.c 144145 2005-03-26 17:30:34Z njl $"); 29141242Snjl 30141242Snjl#include "opt_acpi.h" 31141242Snjl#include <sys/param.h> 32141242Snjl#include <sys/kernel.h> 33141242Snjl#include <sys/proc.h> 34141242Snjl#include <sys/sched.h> 35141242Snjl#include <sys/bus.h> 36141242Snjl#include <sys/cpu.h> 37141242Snjl#include <sys/power.h> 38141242Snjl#include <sys/malloc.h> 39141242Snjl#include <sys/module.h> 40141242Snjl#include <sys/sbuf.h> 41141242Snjl#include <sys/pcpu.h> 42141242Snjl 43141242Snjl#include <machine/bus_pio.h> 44141242Snjl#include <machine/bus.h> 45141242Snjl#include <machine/resource.h> 46141242Snjl#include <sys/rman.h> 47141242Snjl 48141242Snjl#include "acpi.h" 49141242Snjl#include <dev/acpica/acpivar.h> 50141242Snjl 51141242Snjl#include "cpufreq_if.h" 52141242Snjl 53141242Snjl/* 54141242Snjl * Support for ACPI processor performance states (Px) according to 55141824Snjl * section 8.3.3 of the ACPI 2.0c specification. 56141242Snjl */ 57141242Snjl 58141242Snjlstruct acpi_px { 59141242Snjl uint32_t core_freq; 60141242Snjl uint32_t power; 61141242Snjl uint32_t trans_lat; 62141242Snjl uint32_t bm_lat; 63141242Snjl uint32_t ctrl_val; 64141242Snjl uint32_t sts_val; 65141242Snjl}; 66141242Snjl 67142073Snjl/* Offsets in struct cf_setting array for storing driver-specific values. */ 68142073Snjl#define PX_SPEC_CONTROL 0 69142073Snjl#define PX_SPEC_STATUS 1 70141242Snjl 71142073Snjl#define MAX_PX_STATES 16 72142073Snjl 73141242Snjlstruct acpi_perf_softc { 74141242Snjl device_t dev; 75141242Snjl ACPI_HANDLE handle; 76141242Snjl struct resource *perf_ctrl; /* Set new performance state. */ 77141373Snjl int perf_ctrl_type; /* Resource type for perf_ctrl. */ 78141242Snjl struct resource *perf_status; /* Check that transition succeeded. */ 79141373Snjl int perf_sts_type; /* Resource type for perf_status. */ 80141242Snjl struct acpi_px *px_states; /* ACPI perf states. */ 81141242Snjl uint32_t px_count; /* Total number of perf states. */ 82141242Snjl uint32_t px_max_avail; /* Lowest index state available. */ 83141242Snjl int px_curr_state; /* Active state index. */ 84141242Snjl int px_rid; 85141824Snjl int info_only; /* Can we set new states? */ 86141242Snjl}; 87141242Snjl 88141242Snjl#define PX_GET_REG(reg) \ 89141242Snjl (bus_space_read_4(rman_get_bustag((reg)), \ 90141242Snjl rman_get_bushandle((reg)), 0)) 91141242Snjl#define PX_SET_REG(reg, val) \ 92141242Snjl (bus_space_write_4(rman_get_bustag((reg)), \ 93141242Snjl rman_get_bushandle((reg)), 0, (val))) 94141242Snjl 95141429Snjl#define ACPI_NOTIFY_PERF_STATES 0x80 /* _PSS changed. */ 96141429Snjl 97141242Snjlstatic void acpi_perf_identify(driver_t *driver, device_t parent); 98141242Snjlstatic int acpi_perf_probe(device_t dev); 99141242Snjlstatic int acpi_perf_attach(device_t dev); 100141242Snjlstatic int acpi_perf_detach(device_t dev); 101141242Snjlstatic int acpi_perf_evaluate(device_t dev); 102141242Snjlstatic int acpi_px_to_set(device_t dev, struct acpi_px *px, 103141242Snjl struct cf_setting *set); 104141242Snjlstatic void acpi_px_available(struct acpi_perf_softc *sc); 105141411Snjlstatic void acpi_px_startup(void *arg); 106141242Snjlstatic void acpi_px_notify(ACPI_HANDLE h, UINT32 notify, void *context); 107141242Snjlstatic int acpi_px_settings(device_t dev, struct cf_setting *sets, 108142032Snjl int *count); 109141242Snjlstatic int acpi_px_set(device_t dev, const struct cf_setting *set); 110141242Snjlstatic int acpi_px_get(device_t dev, struct cf_setting *set); 111142032Snjlstatic int acpi_px_type(device_t dev, int *type); 112141242Snjl 113141242Snjlstatic device_method_t acpi_perf_methods[] = { 114141242Snjl /* Device interface */ 115141242Snjl DEVMETHOD(device_identify, acpi_perf_identify), 116141242Snjl DEVMETHOD(device_probe, acpi_perf_probe), 117141242Snjl DEVMETHOD(device_attach, acpi_perf_attach), 118141242Snjl DEVMETHOD(device_detach, acpi_perf_detach), 119141242Snjl 120141242Snjl /* cpufreq interface */ 121141242Snjl DEVMETHOD(cpufreq_drv_set, acpi_px_set), 122141242Snjl DEVMETHOD(cpufreq_drv_get, acpi_px_get), 123142032Snjl DEVMETHOD(cpufreq_drv_type, acpi_px_type), 124141242Snjl DEVMETHOD(cpufreq_drv_settings, acpi_px_settings), 125141242Snjl {0, 0} 126141242Snjl}; 127141242Snjl 128141242Snjlstatic driver_t acpi_perf_driver = { 129141242Snjl "acpi_perf", 130141242Snjl acpi_perf_methods, 131141242Snjl sizeof(struct acpi_perf_softc), 132141242Snjl}; 133141242Snjl 134141242Snjlstatic devclass_t acpi_perf_devclass; 135141242SnjlDRIVER_MODULE(acpi_perf, cpu, acpi_perf_driver, acpi_perf_devclass, 0, 0); 136141242SnjlMODULE_DEPEND(acpi_perf, acpi, 1, 1, 1); 137141242Snjl 138141242SnjlMALLOC_DEFINE(M_ACPIPERF, "acpi_perf", "ACPI Performance states"); 139141242Snjl 140141242Snjlstatic void 141141242Snjlacpi_perf_identify(driver_t *driver, device_t parent) 142141242Snjl{ 143141242Snjl ACPI_HANDLE handle; 144143865Snjl device_t dev; 145141242Snjl 146141242Snjl /* Make sure we're not being doubly invoked. */ 147141779Snjl if (device_find_child(parent, "acpi_perf", -1) != NULL) 148141242Snjl return; 149141242Snjl 150141242Snjl /* Get the handle for the Processor object and check for perf states. */ 151141242Snjl handle = acpi_get_handle(parent); 152141242Snjl if (handle == NULL) 153141242Snjl return; 154141242Snjl if (ACPI_FAILURE(AcpiEvaluateObject(handle, "_PSS", NULL, NULL))) 155141242Snjl return; 156142625Snjl 157142625Snjl /* 158142625Snjl * Add a child to every CPU that has the right methods. In future 159142625Snjl * versions of the ACPI spec, CPUs can have different settings. 160143865Snjl * We probe this child now so that other devices that depend 161143865Snjl * on it (i.e., for info about supported states) will see it. 162142625Snjl */ 163143865Snjl if ((dev = BUS_ADD_CHILD(parent, 0, "acpi_perf", -1)) != NULL) 164143865Snjl device_probe_and_attach(dev); 165143865Snjl else 166141429Snjl device_printf(parent, "add acpi_perf child failed\n"); 167141242Snjl} 168141242Snjl 169141242Snjlstatic int 170141242Snjlacpi_perf_probe(device_t dev) 171141242Snjl{ 172141373Snjl ACPI_HANDLE handle; 173141373Snjl ACPI_OBJECT *pkg; 174141373Snjl struct resource *res; 175141373Snjl ACPI_BUFFER buf; 176141373Snjl int error, rid, type; 177141242Snjl 178142203Snjl if (resource_disabled("acpi_perf", 0)) 179142203Snjl return (ENXIO); 180142203Snjl 181141373Snjl /* 182141373Snjl * Check the performance state registers. If they are of type 183141824Snjl * "functional fixed hardware", we attach quietly since we will 184141824Snjl * only be providing information on settings to other drivers. 185141373Snjl */ 186141373Snjl error = ENXIO; 187141373Snjl handle = acpi_get_handle(dev); 188141373Snjl buf.Pointer = NULL; 189141373Snjl buf.Length = ACPI_ALLOCATE_BUFFER; 190141373Snjl if (ACPI_FAILURE(AcpiEvaluateObject(handle, "_PCT", NULL, &buf))) 191141373Snjl return (error); 192141373Snjl pkg = (ACPI_OBJECT *)buf.Pointer; 193141824Snjl if (ACPI_PKG_VALID(pkg, 2)) { 194141824Snjl rid = 0; 195141824Snjl error = acpi_PkgGas(dev, pkg, 0, &type, &rid, &res); 196141824Snjl switch (error) { 197141824Snjl case 0: 198141824Snjl bus_release_resource(dev, type, rid, res); 199141824Snjl device_set_desc(dev, "ACPI CPU Frequency Control"); 200141824Snjl break; 201141824Snjl case EOPNOTSUPP: 202141824Snjl device_quiet(dev); 203141824Snjl error = 0; 204141824Snjl break; 205141824Snjl } 206141373Snjl } 207141373Snjl AcpiOsFree(buf.Pointer); 208141373Snjl 209141373Snjl return (error); 210141242Snjl} 211141242Snjl 212141242Snjlstatic int 213141242Snjlacpi_perf_attach(device_t dev) 214141242Snjl{ 215141242Snjl struct acpi_perf_softc *sc; 216141242Snjl 217141242Snjl sc = device_get_softc(dev); 218141242Snjl sc->dev = dev; 219141242Snjl sc->handle = acpi_get_handle(dev); 220141242Snjl sc->px_max_avail = 0; 221141242Snjl sc->px_curr_state = CPUFREQ_VAL_UNKNOWN; 222141242Snjl if (acpi_perf_evaluate(dev) != 0) 223141242Snjl return (ENXIO); 224141411Snjl AcpiOsQueueForExecution(OSD_PRIORITY_LO, acpi_px_startup, NULL); 225142587Snjl if (!sc->info_only) 226142587Snjl cpufreq_register(dev); 227141242Snjl 228141242Snjl return (0); 229141242Snjl} 230141242Snjl 231141242Snjlstatic int 232141242Snjlacpi_perf_detach(device_t dev) 233141242Snjl{ 234141242Snjl /* TODO: teardown registers, remove notify handler. */ 235141242Snjl return (ENXIO); 236141242Snjl} 237141242Snjl 238141242Snjl/* Probe and setup any valid performance states (Px). */ 239141242Snjlstatic int 240141242Snjlacpi_perf_evaluate(device_t dev) 241141242Snjl{ 242141242Snjl struct acpi_perf_softc *sc; 243141242Snjl ACPI_BUFFER buf; 244141242Snjl ACPI_OBJECT *pkg, *res; 245141242Snjl ACPI_STATUS status; 246144145Snjl int count, error, i, j; 247141242Snjl uint32_t *p; 248141242Snjl 249141242Snjl /* Get the control values and parameters for each state. */ 250141295Snjl error = ENXIO; 251141242Snjl sc = device_get_softc(dev); 252141242Snjl buf.Pointer = NULL; 253141242Snjl buf.Length = ACPI_ALLOCATE_BUFFER; 254141242Snjl status = AcpiEvaluateObject(sc->handle, "_PSS", NULL, &buf); 255141242Snjl if (ACPI_FAILURE(status)) 256141242Snjl return (ENXIO); 257141242Snjl 258141242Snjl pkg = (ACPI_OBJECT *)buf.Pointer; 259141242Snjl if (!ACPI_PKG_VALID(pkg, 1)) { 260141242Snjl device_printf(dev, "invalid top level _PSS package\n"); 261141295Snjl goto out; 262141242Snjl } 263141242Snjl sc->px_count = pkg->Package.Count; 264141242Snjl 265141242Snjl sc->px_states = malloc(sc->px_count * sizeof(struct acpi_px), 266141242Snjl M_ACPIPERF, M_WAITOK | M_ZERO); 267141242Snjl if (sc->px_states == NULL) 268141295Snjl goto out; 269141242Snjl 270141242Snjl /* 271141242Snjl * Each state is a package of {CoreFreq, Power, TransitionLatency, 272141242Snjl * BusMasterLatency, ControlVal, StatusVal}, sorted from highest 273141242Snjl * performance to lowest. 274141242Snjl */ 275144145Snjl count = 0; 276141242Snjl for (i = 0; i < sc->px_count; i++) { 277141242Snjl res = &pkg->Package.Elements[i]; 278141242Snjl if (!ACPI_PKG_VALID(res, 6)) { 279141242Snjl device_printf(dev, "invalid _PSS package\n"); 280141242Snjl continue; 281141242Snjl } 282143119Snjl 283144145Snjl /* Parse the rest of the package into the struct. */ 284144145Snjl p = &sc->px_states[count].core_freq; 285144145Snjl for (j = 0; j < 6; j++, p++) 286144145Snjl acpi_PkgInt32(res, j, p); 287144145Snjl 288143119Snjl /* 289143119Snjl * Check for some impossible frequencies that some systems 290144145Snjl * use to indicate they don't actually support this Px state. 291143119Snjl */ 292144145Snjl if (sc->px_states[count].core_freq == 0 || 293144145Snjl sc->px_states[count].core_freq == 9999 || 294144145Snjl sc->px_states[count].core_freq == 0x9999 || 295144145Snjl sc->px_states[count].core_freq >= 0xffff) 296144145Snjl continue; 297143119Snjl 298144145Snjl count++; 299141242Snjl } 300141242Snjl AcpiOsFree(buf.Pointer); 301144145Snjl sc->px_count = count; 302141242Snjl 303144145Snjl /* No valid Px state found. */ 304144145Snjl if (count == 0) 305144145Snjl goto out; 306144145Snjl 307141242Snjl /* Get the control and status registers (one of each). */ 308141242Snjl buf.Pointer = NULL; 309141242Snjl buf.Length = ACPI_ALLOCATE_BUFFER; 310141242Snjl status = AcpiEvaluateObject(sc->handle, "_PCT", NULL, &buf); 311141295Snjl if (ACPI_FAILURE(status)) 312141295Snjl goto out; 313141242Snjl 314141242Snjl /* Check the package of two registers, each a Buffer in GAS format. */ 315141242Snjl pkg = (ACPI_OBJECT *)buf.Pointer; 316141242Snjl if (!ACPI_PKG_VALID(pkg, 2)) { 317141242Snjl device_printf(dev, "invalid perf register package\n"); 318141295Snjl goto out; 319141242Snjl } 320141242Snjl 321141373Snjl error = acpi_PkgGas(sc->dev, pkg, 0, &sc->perf_ctrl_type, &sc->px_rid, 322141373Snjl &sc->perf_ctrl); 323141373Snjl if (error) { 324141824Snjl /* 325141824Snjl * If the register is of type FFixedHW, we can only return 326141824Snjl * info, we can't get or set new settings. 327141824Snjl */ 328141824Snjl if (error == EOPNOTSUPP) { 329141824Snjl sc->info_only = TRUE; 330141824Snjl error = 0; 331141824Snjl } else 332141295Snjl device_printf(dev, "failed in PERF_CTL attach\n"); 333141295Snjl goto out; 334141242Snjl } 335141242Snjl sc->px_rid++; 336141242Snjl 337141373Snjl error = acpi_PkgGas(sc->dev, pkg, 1, &sc->perf_sts_type, &sc->px_rid, 338141373Snjl &sc->perf_status); 339141373Snjl if (error) { 340141824Snjl if (error == EOPNOTSUPP) { 341141824Snjl sc->info_only = TRUE; 342141824Snjl error = 0; 343141824Snjl } else 344141295Snjl device_printf(dev, "failed in PERF_STATUS attach\n"); 345141295Snjl goto out; 346141242Snjl } 347141242Snjl sc->px_rid++; 348141242Snjl 349141242Snjl /* Get our current limit and register for notifies. */ 350141242Snjl acpi_px_available(sc); 351141242Snjl AcpiInstallNotifyHandler(sc->handle, ACPI_DEVICE_NOTIFY, 352141242Snjl acpi_px_notify, sc); 353141295Snjl error = 0; 354141242Snjl 355141295Snjlout: 356141295Snjl if (error) { 357141295Snjl if (sc->px_states) 358141295Snjl free(sc->px_states, M_ACPIPERF); 359141295Snjl sc->px_count = 0; 360141295Snjl } 361141295Snjl if (buf.Pointer) 362141295Snjl AcpiOsFree(buf.Pointer); 363141295Snjl return (error); 364141242Snjl} 365141242Snjl 366141242Snjlstatic void 367141411Snjlacpi_px_startup(void *arg) 368141411Snjl{ 369141411Snjl 370141411Snjl /* Signal to the platform that we are taking over CPU control. */ 371141411Snjl if (AcpiGbl_FADT->PstateCnt == 0) 372141411Snjl return; 373141411Snjl ACPI_LOCK(acpi); 374141411Snjl AcpiOsWritePort(AcpiGbl_FADT->SmiCmd, AcpiGbl_FADT->PstateCnt, 8); 375141411Snjl ACPI_UNLOCK(acpi); 376141411Snjl} 377141411Snjl 378141411Snjlstatic void 379141242Snjlacpi_px_notify(ACPI_HANDLE h, UINT32 notify, void *context) 380141242Snjl{ 381141242Snjl struct acpi_perf_softc *sc; 382141242Snjl 383141242Snjl sc = context; 384141429Snjl if (notify != ACPI_NOTIFY_PERF_STATES) 385141429Snjl return; 386141429Snjl 387141242Snjl acpi_px_available(sc); 388141242Snjl 389141242Snjl /* TODO: Implement notification when frequency changes. */ 390141242Snjl} 391141242Snjl 392141242Snjl/* 393141242Snjl * Find the highest currently-supported performance state. 394141242Snjl * This can be called at runtime (e.g., due to a docking event) at 395141242Snjl * the request of a Notify on the processor object. 396141242Snjl */ 397141242Snjlstatic void 398141242Snjlacpi_px_available(struct acpi_perf_softc *sc) 399141242Snjl{ 400141242Snjl ACPI_STATUS status; 401141242Snjl struct cf_setting set; 402141242Snjl 403141242Snjl status = acpi_GetInteger(sc->handle, "_PPC", &sc->px_max_avail); 404141242Snjl 405141242Snjl /* If the old state is too high, set current state to the new max. */ 406141242Snjl if (ACPI_SUCCESS(status)) { 407141242Snjl if (sc->px_curr_state != CPUFREQ_VAL_UNKNOWN && 408141242Snjl sc->px_curr_state > sc->px_max_avail) { 409141242Snjl acpi_px_to_set(sc->dev, 410141242Snjl &sc->px_states[sc->px_max_avail], &set); 411141242Snjl acpi_px_set(sc->dev, &set); 412141242Snjl } 413141242Snjl } else 414141242Snjl sc->px_max_avail = 0; 415141242Snjl} 416141242Snjl 417141242Snjlstatic int 418141242Snjlacpi_px_to_set(device_t dev, struct acpi_px *px, struct cf_setting *set) 419141242Snjl{ 420141242Snjl 421141242Snjl if (px == NULL || set == NULL) 422141242Snjl return (EINVAL); 423141242Snjl 424141242Snjl set->freq = px->core_freq; 425141242Snjl set->power = px->power; 426141242Snjl /* XXX Include BM latency too? */ 427141242Snjl set->lat = px->trans_lat; 428141242Snjl set->volts = CPUFREQ_VAL_UNKNOWN; 429141242Snjl set->dev = dev; 430142073Snjl set->spec[PX_SPEC_CONTROL] = px->ctrl_val; 431142073Snjl set->spec[PX_SPEC_STATUS] = px->sts_val; 432141242Snjl 433141242Snjl return (0); 434141242Snjl} 435141242Snjl 436141242Snjlstatic int 437142032Snjlacpi_px_settings(device_t dev, struct cf_setting *sets, int *count) 438141242Snjl{ 439141242Snjl struct acpi_perf_softc *sc; 440141242Snjl int x, y; 441141242Snjl 442141242Snjl sc = device_get_softc(dev); 443141242Snjl if (sets == NULL || count == NULL) 444141242Snjl return (EINVAL); 445141242Snjl if (*count < sc->px_count - sc->px_max_avail) 446142003Snjl return (E2BIG); 447141242Snjl 448141242Snjl /* Return a list of settings that are currently valid. */ 449141242Snjl y = 0; 450141242Snjl for (x = sc->px_max_avail; x < sc->px_count; x++, y++) 451141242Snjl acpi_px_to_set(dev, &sc->px_states[x], &sets[y]); 452141242Snjl *count = sc->px_count - sc->px_max_avail; 453141242Snjl 454141242Snjl return (0); 455141242Snjl} 456141242Snjl 457141242Snjlstatic int 458141242Snjlacpi_px_set(device_t dev, const struct cf_setting *set) 459141242Snjl{ 460141242Snjl struct acpi_perf_softc *sc; 461141242Snjl int i, status, sts_val, tries; 462141242Snjl 463141242Snjl if (set == NULL) 464141242Snjl return (EINVAL); 465141242Snjl sc = device_get_softc(dev); 466141242Snjl 467141824Snjl /* If we can't set new states, return immediately. */ 468141824Snjl if (sc->info_only) 469141824Snjl return (ENXIO); 470141824Snjl 471141242Snjl /* Look up appropriate state, based on frequency. */ 472141242Snjl for (i = sc->px_max_avail; i < sc->px_count; i++) { 473141242Snjl if (CPUFREQ_CMP(set->freq, sc->px_states[i].core_freq)) 474141242Snjl break; 475141242Snjl } 476141242Snjl if (i == sc->px_count) 477141242Snjl return (EINVAL); 478141242Snjl 479141242Snjl /* Write the appropriate value to the register. */ 480141242Snjl PX_SET_REG(sc->perf_ctrl, sc->px_states[i].ctrl_val); 481141242Snjl 482142204Snjl /* 483142204Snjl * Try for up to 10 ms to verify the desired state was selected. 484142204Snjl * This is longer than the standard says (1 ms) but in some modes, 485142204Snjl * systems may take longer to respond. 486142204Snjl */ 487141242Snjl sts_val = sc->px_states[i].sts_val; 488142204Snjl for (tries = 0; tries < 1000; tries++) { 489141242Snjl status = PX_GET_REG(sc->perf_status); 490142204Snjl 491142204Snjl /* 492142204Snjl * If we match the status or the desired status is 8 bits 493142204Snjl * and matches the relevant bits, assume we succeeded. It 494142204Snjl * appears some systems (IBM R32) expect byte-wide access 495142204Snjl * even though the standard says the register is 32-bit. 496142204Snjl */ 497142204Snjl if (status == sts_val || 498142204Snjl ((sts_val & ~0xff) == 0 && (status & 0xff) == sts_val)) 499141242Snjl break; 500141242Snjl DELAY(10); 501141242Snjl } 502142204Snjl if (tries == 1000) { 503141242Snjl device_printf(dev, "Px transition to %d failed\n", 504141242Snjl sc->px_states[i].core_freq); 505141242Snjl return (ENXIO); 506141242Snjl } 507141242Snjl sc->px_curr_state = i; 508141242Snjl 509141242Snjl return (0); 510141242Snjl} 511141242Snjl 512141242Snjlstatic int 513141242Snjlacpi_px_get(device_t dev, struct cf_setting *set) 514141242Snjl{ 515141242Snjl struct acpi_perf_softc *sc; 516141242Snjl uint64_t rate; 517141242Snjl int i; 518141242Snjl struct pcpu *pc; 519141242Snjl 520141242Snjl if (set == NULL) 521141242Snjl return (EINVAL); 522141242Snjl sc = device_get_softc(dev); 523141242Snjl 524141824Snjl /* If we can't get new states, return immediately. */ 525141824Snjl if (sc->info_only) 526141824Snjl return (ENXIO); 527141824Snjl 528141242Snjl /* If we've set the rate before, use the cached value. */ 529141242Snjl if (sc->px_curr_state != CPUFREQ_VAL_UNKNOWN) { 530141242Snjl acpi_px_to_set(dev, &sc->px_states[sc->px_curr_state], set); 531141242Snjl return (0); 532141242Snjl } 533141242Snjl 534141242Snjl /* Otherwise, estimate and try to match against our settings. */ 535141242Snjl pc = cpu_get_pcpu(dev); 536141242Snjl if (pc == NULL) 537141242Snjl return (ENXIO); 538141242Snjl cpu_est_clockrate(pc->pc_cpuid, &rate); 539141242Snjl rate /= 1000000; 540141242Snjl for (i = 0; i < sc->px_count; i++) { 541141242Snjl if (CPUFREQ_CMP(sc->px_states[i].core_freq, rate)) { 542141242Snjl sc->px_curr_state = i; 543141242Snjl acpi_px_to_set(dev, &sc->px_states[i], set); 544141242Snjl break; 545141242Snjl } 546141242Snjl } 547141242Snjl 548141242Snjl /* No match, give up. */ 549141242Snjl if (i == sc->px_count) { 550141242Snjl sc->px_curr_state = CPUFREQ_VAL_UNKNOWN; 551141242Snjl set->freq = CPUFREQ_VAL_UNKNOWN; 552141242Snjl } 553141242Snjl 554141242Snjl return (0); 555141242Snjl} 556142032Snjl 557142032Snjlstatic int 558142032Snjlacpi_px_type(device_t dev, int *type) 559142032Snjl{ 560142032Snjl struct acpi_perf_softc *sc; 561142032Snjl 562142032Snjl if (type == NULL) 563142032Snjl return (EINVAL); 564142032Snjl sc = device_get_softc(dev); 565142032Snjl 566142032Snjl *type = CPUFREQ_TYPE_ABSOLUTE; 567142032Snjl if (sc->info_only) 568142032Snjl *type |= CPUFREQ_FLAG_INFO_ONLY; 569142032Snjl return (0); 570142032Snjl} 571