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$"); 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), 125246128Ssbz 126246128Ssbz DEVMETHOD_END 127141242Snjl}; 128141242Snjl 129141242Snjlstatic driver_t acpi_perf_driver = { 130141242Snjl "acpi_perf", 131141242Snjl acpi_perf_methods, 132141242Snjl sizeof(struct acpi_perf_softc), 133141242Snjl}; 134141242Snjl 135141242Snjlstatic devclass_t acpi_perf_devclass; 136141242SnjlDRIVER_MODULE(acpi_perf, cpu, acpi_perf_driver, acpi_perf_devclass, 0, 0); 137141242SnjlMODULE_DEPEND(acpi_perf, acpi, 1, 1, 1); 138141242Snjl 139227293Sedstatic MALLOC_DEFINE(M_ACPIPERF, "acpi_perf", "ACPI Performance states"); 140141242Snjl 141141242Snjlstatic void 142141242Snjlacpi_perf_identify(driver_t *driver, device_t parent) 143141242Snjl{ 144141242Snjl ACPI_HANDLE handle; 145143865Snjl device_t dev; 146141242Snjl 147141242Snjl /* Make sure we're not being doubly invoked. */ 148141779Snjl if (device_find_child(parent, "acpi_perf", -1) != NULL) 149141242Snjl return; 150141242Snjl 151141242Snjl /* Get the handle for the Processor object and check for perf states. */ 152141242Snjl handle = acpi_get_handle(parent); 153141242Snjl if (handle == NULL) 154141242Snjl return; 155141242Snjl if (ACPI_FAILURE(AcpiEvaluateObject(handle, "_PSS", NULL, NULL))) 156141242Snjl return; 157142625Snjl 158142625Snjl /* 159142625Snjl * Add a child to every CPU that has the right methods. In future 160142625Snjl * versions of the ACPI spec, CPUs can have different settings. 161143865Snjl * We probe this child now so that other devices that depend 162143865Snjl * on it (i.e., for info about supported states) will see it. 163142625Snjl */ 164143865Snjl if ((dev = BUS_ADD_CHILD(parent, 0, "acpi_perf", -1)) != NULL) 165143865Snjl device_probe_and_attach(dev); 166143865Snjl else 167141429Snjl device_printf(parent, "add acpi_perf child failed\n"); 168141242Snjl} 169141242Snjl 170141242Snjlstatic int 171141242Snjlacpi_perf_probe(device_t dev) 172141242Snjl{ 173141373Snjl ACPI_HANDLE handle; 174141373Snjl ACPI_OBJECT *pkg; 175141373Snjl struct resource *res; 176141373Snjl ACPI_BUFFER buf; 177141373Snjl int error, rid, type; 178141242Snjl 179241885Seadler if (resource_disabled("acpi_perf", 0)) 180241885Seadler return (ENXIO); 181241885Seadler 182141373Snjl /* 183141373Snjl * Check the performance state registers. If they are of type 184141824Snjl * "functional fixed hardware", we attach quietly since we will 185141824Snjl * only be providing information on settings to other drivers. 186141373Snjl */ 187141373Snjl error = ENXIO; 188141373Snjl handle = acpi_get_handle(dev); 189141373Snjl buf.Pointer = NULL; 190141373Snjl buf.Length = ACPI_ALLOCATE_BUFFER; 191141373Snjl if (ACPI_FAILURE(AcpiEvaluateObject(handle, "_PCT", NULL, &buf))) 192141373Snjl return (error); 193141373Snjl pkg = (ACPI_OBJECT *)buf.Pointer; 194141824Snjl if (ACPI_PKG_VALID(pkg, 2)) { 195141824Snjl rid = 0; 196165875Snjl error = acpi_PkgGas(dev, pkg, 0, &type, &rid, &res, 0); 197141824Snjl switch (error) { 198141824Snjl case 0: 199141824Snjl bus_release_resource(dev, type, rid, res); 200144197Snjl bus_delete_resource(dev, type, rid); 201141824Snjl device_set_desc(dev, "ACPI CPU Frequency Control"); 202141824Snjl break; 203141824Snjl case EOPNOTSUPP: 204141824Snjl device_quiet(dev); 205141824Snjl error = 0; 206141824Snjl break; 207141824Snjl } 208141373Snjl } 209141373Snjl AcpiOsFree(buf.Pointer); 210141373Snjl 211141373Snjl return (error); 212141242Snjl} 213141242Snjl 214141242Snjlstatic int 215141242Snjlacpi_perf_attach(device_t dev) 216141242Snjl{ 217141242Snjl struct acpi_perf_softc *sc; 218141242Snjl 219141242Snjl sc = device_get_softc(dev); 220141242Snjl sc->dev = dev; 221141242Snjl sc->handle = acpi_get_handle(dev); 222141242Snjl sc->px_max_avail = 0; 223141242Snjl sc->px_curr_state = CPUFREQ_VAL_UNKNOWN; 224141242Snjl if (acpi_perf_evaluate(dev) != 0) 225141242Snjl return (ENXIO); 226167814Sjkim AcpiOsExecute(OSL_NOTIFY_HANDLER, acpi_px_startup, NULL); 227142587Snjl if (!sc->info_only) 228142587Snjl cpufreq_register(dev); 229141242Snjl 230141242Snjl return (0); 231141242Snjl} 232141242Snjl 233141242Snjlstatic int 234141242Snjlacpi_perf_detach(device_t dev) 235141242Snjl{ 236141242Snjl /* TODO: teardown registers, remove notify handler. */ 237141242Snjl return (ENXIO); 238141242Snjl} 239141242Snjl 240141242Snjl/* Probe and setup any valid performance states (Px). */ 241141242Snjlstatic int 242141242Snjlacpi_perf_evaluate(device_t dev) 243141242Snjl{ 244141242Snjl struct acpi_perf_softc *sc; 245141242Snjl ACPI_BUFFER buf; 246141242Snjl ACPI_OBJECT *pkg, *res; 247141242Snjl ACPI_STATUS status; 248144145Snjl int count, error, i, j; 249153336Sbruno static int once = 1; 250141242Snjl uint32_t *p; 251141242Snjl 252141242Snjl /* Get the control values and parameters for each state. */ 253141295Snjl error = ENXIO; 254141242Snjl sc = device_get_softc(dev); 255141242Snjl buf.Pointer = NULL; 256141242Snjl buf.Length = ACPI_ALLOCATE_BUFFER; 257141242Snjl status = AcpiEvaluateObject(sc->handle, "_PSS", NULL, &buf); 258141242Snjl if (ACPI_FAILURE(status)) 259141242Snjl return (ENXIO); 260141242Snjl 261141242Snjl pkg = (ACPI_OBJECT *)buf.Pointer; 262141242Snjl if (!ACPI_PKG_VALID(pkg, 1)) { 263141242Snjl device_printf(dev, "invalid top level _PSS package\n"); 264141295Snjl goto out; 265141242Snjl } 266141242Snjl sc->px_count = pkg->Package.Count; 267141242Snjl 268141242Snjl sc->px_states = malloc(sc->px_count * sizeof(struct acpi_px), 269141242Snjl M_ACPIPERF, M_WAITOK | M_ZERO); 270141242Snjl if (sc->px_states == NULL) 271141295Snjl goto out; 272141242Snjl 273141242Snjl /* 274141242Snjl * Each state is a package of {CoreFreq, Power, TransitionLatency, 275141242Snjl * BusMasterLatency, ControlVal, StatusVal}, sorted from highest 276141242Snjl * performance to lowest. 277141242Snjl */ 278144145Snjl count = 0; 279141242Snjl for (i = 0; i < sc->px_count; i++) { 280141242Snjl res = &pkg->Package.Elements[i]; 281141242Snjl if (!ACPI_PKG_VALID(res, 6)) { 282153336Sbruno if (once) { 283153336Sbruno once = 0; 284153336Sbruno device_printf(dev, "invalid _PSS package\n"); 285153336Sbruno } 286141242Snjl continue; 287141242Snjl } 288143119Snjl 289144145Snjl /* Parse the rest of the package into the struct. */ 290144145Snjl p = &sc->px_states[count].core_freq; 291144145Snjl for (j = 0; j < 6; j++, p++) 292144145Snjl acpi_PkgInt32(res, j, p); 293144145Snjl 294143119Snjl /* 295143119Snjl * Check for some impossible frequencies that some systems 296144145Snjl * use to indicate they don't actually support this Px state. 297143119Snjl */ 298144145Snjl if (sc->px_states[count].core_freq == 0 || 299144145Snjl sc->px_states[count].core_freq == 9999 || 300144145Snjl sc->px_states[count].core_freq == 0x9999 || 301144145Snjl sc->px_states[count].core_freq >= 0xffff) 302144145Snjl continue; 303143119Snjl 304160381Sbruno /* Check for duplicate entries */ 305160381Sbruno if (count > 0 && 306160381Sbruno sc->px_states[count - 1].core_freq == 307160381Sbruno sc->px_states[count].core_freq) 308160381Sbruno continue; 309160381Sbruno 310144145Snjl count++; 311141242Snjl } 312144752Snjl sc->px_count = count; 313141242Snjl 314144752Snjl /* No valid Px state found so give up. */ 315144145Snjl if (count == 0) 316144145Snjl goto out; 317144684Sjhb AcpiOsFree(buf.Pointer); 318144145Snjl 319141242Snjl /* Get the control and status registers (one of each). */ 320141242Snjl buf.Pointer = NULL; 321141242Snjl buf.Length = ACPI_ALLOCATE_BUFFER; 322141242Snjl status = AcpiEvaluateObject(sc->handle, "_PCT", NULL, &buf); 323141295Snjl if (ACPI_FAILURE(status)) 324141295Snjl goto out; 325141242Snjl 326141242Snjl /* Check the package of two registers, each a Buffer in GAS format. */ 327141242Snjl pkg = (ACPI_OBJECT *)buf.Pointer; 328141242Snjl if (!ACPI_PKG_VALID(pkg, 2)) { 329141242Snjl device_printf(dev, "invalid perf register package\n"); 330141295Snjl goto out; 331141242Snjl } 332141242Snjl 333141373Snjl error = acpi_PkgGas(sc->dev, pkg, 0, &sc->perf_ctrl_type, &sc->px_rid, 334165875Snjl &sc->perf_ctrl, 0); 335141373Snjl if (error) { 336141824Snjl /* 337141824Snjl * If the register is of type FFixedHW, we can only return 338141824Snjl * info, we can't get or set new settings. 339141824Snjl */ 340141824Snjl if (error == EOPNOTSUPP) { 341141824Snjl sc->info_only = TRUE; 342141824Snjl error = 0; 343141824Snjl } else 344141295Snjl device_printf(dev, "failed in PERF_CTL attach\n"); 345141295Snjl goto out; 346141242Snjl } 347141242Snjl sc->px_rid++; 348141242Snjl 349141373Snjl error = acpi_PkgGas(sc->dev, pkg, 1, &sc->perf_sts_type, &sc->px_rid, 350165875Snjl &sc->perf_status, 0); 351141373Snjl if (error) { 352141824Snjl if (error == EOPNOTSUPP) { 353141824Snjl sc->info_only = TRUE; 354141824Snjl error = 0; 355141824Snjl } else 356141295Snjl device_printf(dev, "failed in PERF_STATUS attach\n"); 357141295Snjl goto out; 358141242Snjl } 359141242Snjl sc->px_rid++; 360141242Snjl 361141242Snjl /* Get our current limit and register for notifies. */ 362141242Snjl acpi_px_available(sc); 363141242Snjl AcpiInstallNotifyHandler(sc->handle, ACPI_DEVICE_NOTIFY, 364141242Snjl acpi_px_notify, sc); 365141295Snjl error = 0; 366141242Snjl 367141295Snjlout: 368141295Snjl if (error) { 369144197Snjl if (sc->px_states) { 370141295Snjl free(sc->px_states, M_ACPIPERF); 371144197Snjl sc->px_states = NULL; 372144197Snjl } 373144197Snjl if (sc->perf_ctrl) { 374144197Snjl bus_release_resource(sc->dev, sc->perf_ctrl_type, 0, 375144197Snjl sc->perf_ctrl); 376144197Snjl bus_delete_resource(sc->dev, sc->perf_ctrl_type, 0); 377144197Snjl sc->perf_ctrl = NULL; 378144197Snjl } 379144197Snjl if (sc->perf_status) { 380144197Snjl bus_release_resource(sc->dev, sc->perf_sts_type, 1, 381144197Snjl sc->perf_status); 382144197Snjl bus_delete_resource(sc->dev, sc->perf_sts_type, 1); 383144197Snjl sc->perf_status = NULL; 384144197Snjl } 385144197Snjl sc->px_rid = 0; 386141295Snjl sc->px_count = 0; 387141295Snjl } 388141295Snjl if (buf.Pointer) 389141295Snjl AcpiOsFree(buf.Pointer); 390141295Snjl return (error); 391141242Snjl} 392141242Snjl 393141242Snjlstatic void 394141411Snjlacpi_px_startup(void *arg) 395141411Snjl{ 396141411Snjl 397141411Snjl /* Signal to the platform that we are taking over CPU control. */ 398167814Sjkim if (AcpiGbl_FADT.PstateControl == 0) 399141411Snjl return; 400141411Snjl ACPI_LOCK(acpi); 401167814Sjkim AcpiOsWritePort(AcpiGbl_FADT.SmiCommand, AcpiGbl_FADT.PstateControl, 8); 402141411Snjl ACPI_UNLOCK(acpi); 403141411Snjl} 404141411Snjl 405141411Snjlstatic void 406141242Snjlacpi_px_notify(ACPI_HANDLE h, UINT32 notify, void *context) 407141242Snjl{ 408141242Snjl struct acpi_perf_softc *sc; 409141242Snjl 410141242Snjl sc = context; 411141429Snjl if (notify != ACPI_NOTIFY_PERF_STATES) 412141429Snjl return; 413141429Snjl 414141242Snjl acpi_px_available(sc); 415141242Snjl 416141242Snjl /* TODO: Implement notification when frequency changes. */ 417141242Snjl} 418141242Snjl 419141242Snjl/* 420141242Snjl * Find the highest currently-supported performance state. 421141242Snjl * This can be called at runtime (e.g., due to a docking event) at 422141242Snjl * the request of a Notify on the processor object. 423141242Snjl */ 424141242Snjlstatic void 425141242Snjlacpi_px_available(struct acpi_perf_softc *sc) 426141242Snjl{ 427141242Snjl ACPI_STATUS status; 428141242Snjl struct cf_setting set; 429141242Snjl 430141242Snjl status = acpi_GetInteger(sc->handle, "_PPC", &sc->px_max_avail); 431141242Snjl 432141242Snjl /* If the old state is too high, set current state to the new max. */ 433141242Snjl if (ACPI_SUCCESS(status)) { 434141242Snjl if (sc->px_curr_state != CPUFREQ_VAL_UNKNOWN && 435141242Snjl sc->px_curr_state > sc->px_max_avail) { 436141242Snjl acpi_px_to_set(sc->dev, 437141242Snjl &sc->px_states[sc->px_max_avail], &set); 438141242Snjl acpi_px_set(sc->dev, &set); 439141242Snjl } 440141242Snjl } else 441141242Snjl sc->px_max_avail = 0; 442141242Snjl} 443141242Snjl 444141242Snjlstatic int 445141242Snjlacpi_px_to_set(device_t dev, struct acpi_px *px, struct cf_setting *set) 446141242Snjl{ 447141242Snjl 448141242Snjl if (px == NULL || set == NULL) 449141242Snjl return (EINVAL); 450141242Snjl 451141242Snjl set->freq = px->core_freq; 452141242Snjl set->power = px->power; 453141242Snjl /* XXX Include BM latency too? */ 454141242Snjl set->lat = px->trans_lat; 455141242Snjl set->volts = CPUFREQ_VAL_UNKNOWN; 456141242Snjl set->dev = dev; 457142073Snjl set->spec[PX_SPEC_CONTROL] = px->ctrl_val; 458142073Snjl set->spec[PX_SPEC_STATUS] = px->sts_val; 459141242Snjl 460141242Snjl return (0); 461141242Snjl} 462141242Snjl 463141242Snjlstatic int 464142032Snjlacpi_px_settings(device_t dev, struct cf_setting *sets, int *count) 465141242Snjl{ 466141242Snjl struct acpi_perf_softc *sc; 467141242Snjl int x, y; 468141242Snjl 469141242Snjl sc = device_get_softc(dev); 470141242Snjl if (sets == NULL || count == NULL) 471141242Snjl return (EINVAL); 472141242Snjl if (*count < sc->px_count - sc->px_max_avail) 473142003Snjl return (E2BIG); 474141242Snjl 475141242Snjl /* Return a list of settings that are currently valid. */ 476141242Snjl y = 0; 477141242Snjl for (x = sc->px_max_avail; x < sc->px_count; x++, y++) 478141242Snjl acpi_px_to_set(dev, &sc->px_states[x], &sets[y]); 479141242Snjl *count = sc->px_count - sc->px_max_avail; 480141242Snjl 481141242Snjl return (0); 482141242Snjl} 483141242Snjl 484141242Snjlstatic int 485141242Snjlacpi_px_set(device_t dev, const struct cf_setting *set) 486141242Snjl{ 487141242Snjl struct acpi_perf_softc *sc; 488141242Snjl int i, status, sts_val, tries; 489141242Snjl 490141242Snjl if (set == NULL) 491141242Snjl return (EINVAL); 492141242Snjl sc = device_get_softc(dev); 493141242Snjl 494141824Snjl /* If we can't set new states, return immediately. */ 495141824Snjl if (sc->info_only) 496141824Snjl return (ENXIO); 497141824Snjl 498141242Snjl /* Look up appropriate state, based on frequency. */ 499141242Snjl for (i = sc->px_max_avail; i < sc->px_count; i++) { 500141242Snjl if (CPUFREQ_CMP(set->freq, sc->px_states[i].core_freq)) 501141242Snjl break; 502141242Snjl } 503141242Snjl if (i == sc->px_count) 504141242Snjl return (EINVAL); 505141242Snjl 506141242Snjl /* Write the appropriate value to the register. */ 507141242Snjl PX_SET_REG(sc->perf_ctrl, sc->px_states[i].ctrl_val); 508141242Snjl 509142204Snjl /* 510142204Snjl * Try for up to 10 ms to verify the desired state was selected. 511142204Snjl * This is longer than the standard says (1 ms) but in some modes, 512142204Snjl * systems may take longer to respond. 513142204Snjl */ 514141242Snjl sts_val = sc->px_states[i].sts_val; 515142204Snjl for (tries = 0; tries < 1000; tries++) { 516141242Snjl status = PX_GET_REG(sc->perf_status); 517142204Snjl 518142204Snjl /* 519142204Snjl * If we match the status or the desired status is 8 bits 520142204Snjl * and matches the relevant bits, assume we succeeded. It 521142204Snjl * appears some systems (IBM R32) expect byte-wide access 522142204Snjl * even though the standard says the register is 32-bit. 523142204Snjl */ 524142204Snjl if (status == sts_val || 525142204Snjl ((sts_val & ~0xff) == 0 && (status & 0xff) == sts_val)) 526141242Snjl break; 527141242Snjl DELAY(10); 528141242Snjl } 529142204Snjl if (tries == 1000) { 530141242Snjl device_printf(dev, "Px transition to %d failed\n", 531141242Snjl sc->px_states[i].core_freq); 532141242Snjl return (ENXIO); 533141242Snjl } 534141242Snjl sc->px_curr_state = i; 535141242Snjl 536141242Snjl return (0); 537141242Snjl} 538141242Snjl 539141242Snjlstatic int 540141242Snjlacpi_px_get(device_t dev, struct cf_setting *set) 541141242Snjl{ 542141242Snjl struct acpi_perf_softc *sc; 543141242Snjl uint64_t rate; 544141242Snjl int i; 545141242Snjl struct pcpu *pc; 546141242Snjl 547141242Snjl if (set == NULL) 548141242Snjl return (EINVAL); 549141242Snjl sc = device_get_softc(dev); 550141242Snjl 551141824Snjl /* If we can't get new states, return immediately. */ 552141824Snjl if (sc->info_only) 553141824Snjl return (ENXIO); 554141824Snjl 555141242Snjl /* If we've set the rate before, use the cached value. */ 556141242Snjl if (sc->px_curr_state != CPUFREQ_VAL_UNKNOWN) { 557141242Snjl acpi_px_to_set(dev, &sc->px_states[sc->px_curr_state], set); 558141242Snjl return (0); 559141242Snjl } 560141242Snjl 561141242Snjl /* Otherwise, estimate and try to match against our settings. */ 562141242Snjl pc = cpu_get_pcpu(dev); 563141242Snjl if (pc == NULL) 564141242Snjl return (ENXIO); 565141242Snjl cpu_est_clockrate(pc->pc_cpuid, &rate); 566141242Snjl rate /= 1000000; 567141242Snjl for (i = 0; i < sc->px_count; i++) { 568141242Snjl if (CPUFREQ_CMP(sc->px_states[i].core_freq, rate)) { 569141242Snjl sc->px_curr_state = i; 570141242Snjl acpi_px_to_set(dev, &sc->px_states[i], set); 571141242Snjl break; 572141242Snjl } 573141242Snjl } 574141242Snjl 575141242Snjl /* No match, give up. */ 576141242Snjl if (i == sc->px_count) { 577141242Snjl sc->px_curr_state = CPUFREQ_VAL_UNKNOWN; 578141242Snjl set->freq = CPUFREQ_VAL_UNKNOWN; 579141242Snjl } 580141242Snjl 581141242Snjl return (0); 582141242Snjl} 583142032Snjl 584142032Snjlstatic int 585142032Snjlacpi_px_type(device_t dev, int *type) 586142032Snjl{ 587142032Snjl struct acpi_perf_softc *sc; 588142032Snjl 589142032Snjl if (type == NULL) 590142032Snjl return (EINVAL); 591142032Snjl sc = device_get_softc(dev); 592142032Snjl 593142032Snjl *type = CPUFREQ_TYPE_ABSOLUTE; 594142032Snjl if (sc->info_only) 595142032Snjl *type |= CPUFREQ_FLAG_INFO_ONLY; 596142032Snjl return (0); 597142032Snjl} 598