acpi_perf.c revision 241885
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 241885 2012-10-22 13:06:09Z eadler $"); 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.h> 44141242Snjl#include <machine/resource.h> 45141242Snjl#include <sys/rman.h> 46141242Snjl 47193530Sjkim#include <contrib/dev/acpica/include/acpi.h> 48193530Sjkim 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 138227293Sedstatic MALLOC_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 178241885Seadler if (resource_disabled("acpi_perf", 0)) 179241885Seadler return (ENXIO); 180241885Seadler 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; 195165875Snjl error = acpi_PkgGas(dev, pkg, 0, &type, &rid, &res, 0); 196141824Snjl switch (error) { 197141824Snjl case 0: 198141824Snjl bus_release_resource(dev, type, rid, res); 199144197Snjl bus_delete_resource(dev, type, rid); 200141824Snjl device_set_desc(dev, "ACPI CPU Frequency Control"); 201141824Snjl break; 202141824Snjl case EOPNOTSUPP: 203141824Snjl device_quiet(dev); 204141824Snjl error = 0; 205141824Snjl break; 206141824Snjl } 207141373Snjl } 208141373Snjl AcpiOsFree(buf.Pointer); 209141373Snjl 210141373Snjl return (error); 211141242Snjl} 212141242Snjl 213141242Snjlstatic int 214141242Snjlacpi_perf_attach(device_t dev) 215141242Snjl{ 216141242Snjl struct acpi_perf_softc *sc; 217141242Snjl 218141242Snjl sc = device_get_softc(dev); 219141242Snjl sc->dev = dev; 220141242Snjl sc->handle = acpi_get_handle(dev); 221141242Snjl sc->px_max_avail = 0; 222141242Snjl sc->px_curr_state = CPUFREQ_VAL_UNKNOWN; 223141242Snjl if (acpi_perf_evaluate(dev) != 0) 224141242Snjl return (ENXIO); 225167814Sjkim AcpiOsExecute(OSL_NOTIFY_HANDLER, acpi_px_startup, NULL); 226142587Snjl if (!sc->info_only) 227142587Snjl cpufreq_register(dev); 228141242Snjl 229141242Snjl return (0); 230141242Snjl} 231141242Snjl 232141242Snjlstatic int 233141242Snjlacpi_perf_detach(device_t dev) 234141242Snjl{ 235141242Snjl /* TODO: teardown registers, remove notify handler. */ 236141242Snjl return (ENXIO); 237141242Snjl} 238141242Snjl 239141242Snjl/* Probe and setup any valid performance states (Px). */ 240141242Snjlstatic int 241141242Snjlacpi_perf_evaluate(device_t dev) 242141242Snjl{ 243141242Snjl struct acpi_perf_softc *sc; 244141242Snjl ACPI_BUFFER buf; 245141242Snjl ACPI_OBJECT *pkg, *res; 246141242Snjl ACPI_STATUS status; 247144145Snjl int count, error, i, j; 248153336Sbruno static int once = 1; 249141242Snjl uint32_t *p; 250141242Snjl 251141242Snjl /* Get the control values and parameters for each state. */ 252141295Snjl error = ENXIO; 253141242Snjl sc = device_get_softc(dev); 254141242Snjl buf.Pointer = NULL; 255141242Snjl buf.Length = ACPI_ALLOCATE_BUFFER; 256141242Snjl status = AcpiEvaluateObject(sc->handle, "_PSS", NULL, &buf); 257141242Snjl if (ACPI_FAILURE(status)) 258141242Snjl return (ENXIO); 259141242Snjl 260141242Snjl pkg = (ACPI_OBJECT *)buf.Pointer; 261141242Snjl if (!ACPI_PKG_VALID(pkg, 1)) { 262141242Snjl device_printf(dev, "invalid top level _PSS package\n"); 263141295Snjl goto out; 264141242Snjl } 265141242Snjl sc->px_count = pkg->Package.Count; 266141242Snjl 267141242Snjl sc->px_states = malloc(sc->px_count * sizeof(struct acpi_px), 268141242Snjl M_ACPIPERF, M_WAITOK | M_ZERO); 269141242Snjl if (sc->px_states == NULL) 270141295Snjl goto out; 271141242Snjl 272141242Snjl /* 273141242Snjl * Each state is a package of {CoreFreq, Power, TransitionLatency, 274141242Snjl * BusMasterLatency, ControlVal, StatusVal}, sorted from highest 275141242Snjl * performance to lowest. 276141242Snjl */ 277144145Snjl count = 0; 278141242Snjl for (i = 0; i < sc->px_count; i++) { 279141242Snjl res = &pkg->Package.Elements[i]; 280141242Snjl if (!ACPI_PKG_VALID(res, 6)) { 281153336Sbruno if (once) { 282153336Sbruno once = 0; 283153336Sbruno device_printf(dev, "invalid _PSS package\n"); 284153336Sbruno } 285141242Snjl continue; 286141242Snjl } 287143119Snjl 288144145Snjl /* Parse the rest of the package into the struct. */ 289144145Snjl p = &sc->px_states[count].core_freq; 290144145Snjl for (j = 0; j < 6; j++, p++) 291144145Snjl acpi_PkgInt32(res, j, p); 292144145Snjl 293143119Snjl /* 294143119Snjl * Check for some impossible frequencies that some systems 295144145Snjl * use to indicate they don't actually support this Px state. 296143119Snjl */ 297144145Snjl if (sc->px_states[count].core_freq == 0 || 298144145Snjl sc->px_states[count].core_freq == 9999 || 299144145Snjl sc->px_states[count].core_freq == 0x9999 || 300144145Snjl sc->px_states[count].core_freq >= 0xffff) 301144145Snjl continue; 302143119Snjl 303160381Sbruno /* Check for duplicate entries */ 304160381Sbruno if (count > 0 && 305160381Sbruno sc->px_states[count - 1].core_freq == 306160381Sbruno sc->px_states[count].core_freq) 307160381Sbruno continue; 308160381Sbruno 309144145Snjl count++; 310141242Snjl } 311144752Snjl sc->px_count = count; 312141242Snjl 313144752Snjl /* No valid Px state found so give up. */ 314144145Snjl if (count == 0) 315144145Snjl goto out; 316144684Sjhb AcpiOsFree(buf.Pointer); 317144145Snjl 318141242Snjl /* Get the control and status registers (one of each). */ 319141242Snjl buf.Pointer = NULL; 320141242Snjl buf.Length = ACPI_ALLOCATE_BUFFER; 321141242Snjl status = AcpiEvaluateObject(sc->handle, "_PCT", NULL, &buf); 322141295Snjl if (ACPI_FAILURE(status)) 323141295Snjl goto out; 324141242Snjl 325141242Snjl /* Check the package of two registers, each a Buffer in GAS format. */ 326141242Snjl pkg = (ACPI_OBJECT *)buf.Pointer; 327141242Snjl if (!ACPI_PKG_VALID(pkg, 2)) { 328141242Snjl device_printf(dev, "invalid perf register package\n"); 329141295Snjl goto out; 330141242Snjl } 331141242Snjl 332141373Snjl error = acpi_PkgGas(sc->dev, pkg, 0, &sc->perf_ctrl_type, &sc->px_rid, 333165875Snjl &sc->perf_ctrl, 0); 334141373Snjl if (error) { 335141824Snjl /* 336141824Snjl * If the register is of type FFixedHW, we can only return 337141824Snjl * info, we can't get or set new settings. 338141824Snjl */ 339141824Snjl if (error == EOPNOTSUPP) { 340141824Snjl sc->info_only = TRUE; 341141824Snjl error = 0; 342141824Snjl } else 343141295Snjl device_printf(dev, "failed in PERF_CTL attach\n"); 344141295Snjl goto out; 345141242Snjl } 346141242Snjl sc->px_rid++; 347141242Snjl 348141373Snjl error = acpi_PkgGas(sc->dev, pkg, 1, &sc->perf_sts_type, &sc->px_rid, 349165875Snjl &sc->perf_status, 0); 350141373Snjl if (error) { 351141824Snjl if (error == EOPNOTSUPP) { 352141824Snjl sc->info_only = TRUE; 353141824Snjl error = 0; 354141824Snjl } else 355141295Snjl device_printf(dev, "failed in PERF_STATUS attach\n"); 356141295Snjl goto out; 357141242Snjl } 358141242Snjl sc->px_rid++; 359141242Snjl 360141242Snjl /* Get our current limit and register for notifies. */ 361141242Snjl acpi_px_available(sc); 362141242Snjl AcpiInstallNotifyHandler(sc->handle, ACPI_DEVICE_NOTIFY, 363141242Snjl acpi_px_notify, sc); 364141295Snjl error = 0; 365141242Snjl 366141295Snjlout: 367141295Snjl if (error) { 368144197Snjl if (sc->px_states) { 369141295Snjl free(sc->px_states, M_ACPIPERF); 370144197Snjl sc->px_states = NULL; 371144197Snjl } 372144197Snjl if (sc->perf_ctrl) { 373144197Snjl bus_release_resource(sc->dev, sc->perf_ctrl_type, 0, 374144197Snjl sc->perf_ctrl); 375144197Snjl bus_delete_resource(sc->dev, sc->perf_ctrl_type, 0); 376144197Snjl sc->perf_ctrl = NULL; 377144197Snjl } 378144197Snjl if (sc->perf_status) { 379144197Snjl bus_release_resource(sc->dev, sc->perf_sts_type, 1, 380144197Snjl sc->perf_status); 381144197Snjl bus_delete_resource(sc->dev, sc->perf_sts_type, 1); 382144197Snjl sc->perf_status = NULL; 383144197Snjl } 384144197Snjl sc->px_rid = 0; 385141295Snjl sc->px_count = 0; 386141295Snjl } 387141295Snjl if (buf.Pointer) 388141295Snjl AcpiOsFree(buf.Pointer); 389141295Snjl return (error); 390141242Snjl} 391141242Snjl 392141242Snjlstatic void 393141411Snjlacpi_px_startup(void *arg) 394141411Snjl{ 395141411Snjl 396141411Snjl /* Signal to the platform that we are taking over CPU control. */ 397167814Sjkim if (AcpiGbl_FADT.PstateControl == 0) 398141411Snjl return; 399141411Snjl ACPI_LOCK(acpi); 400167814Sjkim AcpiOsWritePort(AcpiGbl_FADT.SmiCommand, AcpiGbl_FADT.PstateControl, 8); 401141411Snjl ACPI_UNLOCK(acpi); 402141411Snjl} 403141411Snjl 404141411Snjlstatic void 405141242Snjlacpi_px_notify(ACPI_HANDLE h, UINT32 notify, void *context) 406141242Snjl{ 407141242Snjl struct acpi_perf_softc *sc; 408141242Snjl 409141242Snjl sc = context; 410141429Snjl if (notify != ACPI_NOTIFY_PERF_STATES) 411141429Snjl return; 412141429Snjl 413141242Snjl acpi_px_available(sc); 414141242Snjl 415141242Snjl /* TODO: Implement notification when frequency changes. */ 416141242Snjl} 417141242Snjl 418141242Snjl/* 419141242Snjl * Find the highest currently-supported performance state. 420141242Snjl * This can be called at runtime (e.g., due to a docking event) at 421141242Snjl * the request of a Notify on the processor object. 422141242Snjl */ 423141242Snjlstatic void 424141242Snjlacpi_px_available(struct acpi_perf_softc *sc) 425141242Snjl{ 426141242Snjl ACPI_STATUS status; 427141242Snjl struct cf_setting set; 428141242Snjl 429141242Snjl status = acpi_GetInteger(sc->handle, "_PPC", &sc->px_max_avail); 430141242Snjl 431141242Snjl /* If the old state is too high, set current state to the new max. */ 432141242Snjl if (ACPI_SUCCESS(status)) { 433141242Snjl if (sc->px_curr_state != CPUFREQ_VAL_UNKNOWN && 434141242Snjl sc->px_curr_state > sc->px_max_avail) { 435141242Snjl acpi_px_to_set(sc->dev, 436141242Snjl &sc->px_states[sc->px_max_avail], &set); 437141242Snjl acpi_px_set(sc->dev, &set); 438141242Snjl } 439141242Snjl } else 440141242Snjl sc->px_max_avail = 0; 441141242Snjl} 442141242Snjl 443141242Snjlstatic int 444141242Snjlacpi_px_to_set(device_t dev, struct acpi_px *px, struct cf_setting *set) 445141242Snjl{ 446141242Snjl 447141242Snjl if (px == NULL || set == NULL) 448141242Snjl return (EINVAL); 449141242Snjl 450141242Snjl set->freq = px->core_freq; 451141242Snjl set->power = px->power; 452141242Snjl /* XXX Include BM latency too? */ 453141242Snjl set->lat = px->trans_lat; 454141242Snjl set->volts = CPUFREQ_VAL_UNKNOWN; 455141242Snjl set->dev = dev; 456142073Snjl set->spec[PX_SPEC_CONTROL] = px->ctrl_val; 457142073Snjl set->spec[PX_SPEC_STATUS] = px->sts_val; 458141242Snjl 459141242Snjl return (0); 460141242Snjl} 461141242Snjl 462141242Snjlstatic int 463142032Snjlacpi_px_settings(device_t dev, struct cf_setting *sets, int *count) 464141242Snjl{ 465141242Snjl struct acpi_perf_softc *sc; 466141242Snjl int x, y; 467141242Snjl 468141242Snjl sc = device_get_softc(dev); 469141242Snjl if (sets == NULL || count == NULL) 470141242Snjl return (EINVAL); 471141242Snjl if (*count < sc->px_count - sc->px_max_avail) 472142003Snjl return (E2BIG); 473141242Snjl 474141242Snjl /* Return a list of settings that are currently valid. */ 475141242Snjl y = 0; 476141242Snjl for (x = sc->px_max_avail; x < sc->px_count; x++, y++) 477141242Snjl acpi_px_to_set(dev, &sc->px_states[x], &sets[y]); 478141242Snjl *count = sc->px_count - sc->px_max_avail; 479141242Snjl 480141242Snjl return (0); 481141242Snjl} 482141242Snjl 483141242Snjlstatic int 484141242Snjlacpi_px_set(device_t dev, const struct cf_setting *set) 485141242Snjl{ 486141242Snjl struct acpi_perf_softc *sc; 487141242Snjl int i, status, sts_val, tries; 488141242Snjl 489141242Snjl if (set == NULL) 490141242Snjl return (EINVAL); 491141242Snjl sc = device_get_softc(dev); 492141242Snjl 493141824Snjl /* If we can't set new states, return immediately. */ 494141824Snjl if (sc->info_only) 495141824Snjl return (ENXIO); 496141824Snjl 497141242Snjl /* Look up appropriate state, based on frequency. */ 498141242Snjl for (i = sc->px_max_avail; i < sc->px_count; i++) { 499141242Snjl if (CPUFREQ_CMP(set->freq, sc->px_states[i].core_freq)) 500141242Snjl break; 501141242Snjl } 502141242Snjl if (i == sc->px_count) 503141242Snjl return (EINVAL); 504141242Snjl 505141242Snjl /* Write the appropriate value to the register. */ 506141242Snjl PX_SET_REG(sc->perf_ctrl, sc->px_states[i].ctrl_val); 507141242Snjl 508142204Snjl /* 509142204Snjl * Try for up to 10 ms to verify the desired state was selected. 510142204Snjl * This is longer than the standard says (1 ms) but in some modes, 511142204Snjl * systems may take longer to respond. 512142204Snjl */ 513141242Snjl sts_val = sc->px_states[i].sts_val; 514142204Snjl for (tries = 0; tries < 1000; tries++) { 515141242Snjl status = PX_GET_REG(sc->perf_status); 516142204Snjl 517142204Snjl /* 518142204Snjl * If we match the status or the desired status is 8 bits 519142204Snjl * and matches the relevant bits, assume we succeeded. It 520142204Snjl * appears some systems (IBM R32) expect byte-wide access 521142204Snjl * even though the standard says the register is 32-bit. 522142204Snjl */ 523142204Snjl if (status == sts_val || 524142204Snjl ((sts_val & ~0xff) == 0 && (status & 0xff) == sts_val)) 525141242Snjl break; 526141242Snjl DELAY(10); 527141242Snjl } 528142204Snjl if (tries == 1000) { 529141242Snjl device_printf(dev, "Px transition to %d failed\n", 530141242Snjl sc->px_states[i].core_freq); 531141242Snjl return (ENXIO); 532141242Snjl } 533141242Snjl sc->px_curr_state = i; 534141242Snjl 535141242Snjl return (0); 536141242Snjl} 537141242Snjl 538141242Snjlstatic int 539141242Snjlacpi_px_get(device_t dev, struct cf_setting *set) 540141242Snjl{ 541141242Snjl struct acpi_perf_softc *sc; 542141242Snjl uint64_t rate; 543141242Snjl int i; 544141242Snjl struct pcpu *pc; 545141242Snjl 546141242Snjl if (set == NULL) 547141242Snjl return (EINVAL); 548141242Snjl sc = device_get_softc(dev); 549141242Snjl 550141824Snjl /* If we can't get new states, return immediately. */ 551141824Snjl if (sc->info_only) 552141824Snjl return (ENXIO); 553141824Snjl 554141242Snjl /* If we've set the rate before, use the cached value. */ 555141242Snjl if (sc->px_curr_state != CPUFREQ_VAL_UNKNOWN) { 556141242Snjl acpi_px_to_set(dev, &sc->px_states[sc->px_curr_state], set); 557141242Snjl return (0); 558141242Snjl } 559141242Snjl 560141242Snjl /* Otherwise, estimate and try to match against our settings. */ 561141242Snjl pc = cpu_get_pcpu(dev); 562141242Snjl if (pc == NULL) 563141242Snjl return (ENXIO); 564141242Snjl cpu_est_clockrate(pc->pc_cpuid, &rate); 565141242Snjl rate /= 1000000; 566141242Snjl for (i = 0; i < sc->px_count; i++) { 567141242Snjl if (CPUFREQ_CMP(sc->px_states[i].core_freq, rate)) { 568141242Snjl sc->px_curr_state = i; 569141242Snjl acpi_px_to_set(dev, &sc->px_states[i], set); 570141242Snjl break; 571141242Snjl } 572141242Snjl } 573141242Snjl 574141242Snjl /* No match, give up. */ 575141242Snjl if (i == sc->px_count) { 576141242Snjl sc->px_curr_state = CPUFREQ_VAL_UNKNOWN; 577141242Snjl set->freq = CPUFREQ_VAL_UNKNOWN; 578141242Snjl } 579141242Snjl 580141242Snjl return (0); 581141242Snjl} 582142032Snjl 583142032Snjlstatic int 584142032Snjlacpi_px_type(device_t dev, int *type) 585142032Snjl{ 586142032Snjl struct acpi_perf_softc *sc; 587142032Snjl 588142032Snjl if (type == NULL) 589142032Snjl return (EINVAL); 590142032Snjl sc = device_get_softc(dev); 591142032Snjl 592142032Snjl *type = CPUFREQ_TYPE_ABSOLUTE; 593142032Snjl if (sc->info_only) 594142032Snjl *type |= CPUFREQ_FLAG_INFO_ONLY; 595142032Snjl return (0); 596142032Snjl} 597