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 271141242Snjl /* 272141242Snjl * Each state is a package of {CoreFreq, Power, TransitionLatency, 273141242Snjl * BusMasterLatency, ControlVal, StatusVal}, sorted from highest 274141242Snjl * performance to lowest. 275141242Snjl */ 276144145Snjl count = 0; 277141242Snjl for (i = 0; i < sc->px_count; i++) { 278141242Snjl res = &pkg->Package.Elements[i]; 279141242Snjl if (!ACPI_PKG_VALID(res, 6)) { 280153336Sbruno if (once) { 281153336Sbruno once = 0; 282153336Sbruno device_printf(dev, "invalid _PSS package\n"); 283153336Sbruno } 284141242Snjl continue; 285141242Snjl } 286143119Snjl 287144145Snjl /* Parse the rest of the package into the struct. */ 288144145Snjl p = &sc->px_states[count].core_freq; 289144145Snjl for (j = 0; j < 6; j++, p++) 290144145Snjl acpi_PkgInt32(res, j, p); 291144145Snjl 292143119Snjl /* 293143119Snjl * Check for some impossible frequencies that some systems 294144145Snjl * use to indicate they don't actually support this Px state. 295143119Snjl */ 296144145Snjl if (sc->px_states[count].core_freq == 0 || 297144145Snjl sc->px_states[count].core_freq == 9999 || 298144145Snjl sc->px_states[count].core_freq == 0x9999 || 299144145Snjl sc->px_states[count].core_freq >= 0xffff) 300144145Snjl continue; 301143119Snjl 302160381Sbruno /* Check for duplicate entries */ 303160381Sbruno if (count > 0 && 304160381Sbruno sc->px_states[count - 1].core_freq == 305160381Sbruno sc->px_states[count].core_freq) 306160381Sbruno continue; 307160381Sbruno 308144145Snjl count++; 309141242Snjl } 310144752Snjl sc->px_count = count; 311141242Snjl 312144752Snjl /* No valid Px state found so give up. */ 313144145Snjl if (count == 0) 314144145Snjl goto out; 315144684Sjhb AcpiOsFree(buf.Pointer); 316144145Snjl 317141242Snjl /* Get the control and status registers (one of each). */ 318141242Snjl buf.Pointer = NULL; 319141242Snjl buf.Length = ACPI_ALLOCATE_BUFFER; 320141242Snjl status = AcpiEvaluateObject(sc->handle, "_PCT", NULL, &buf); 321141295Snjl if (ACPI_FAILURE(status)) 322141295Snjl goto out; 323141242Snjl 324141242Snjl /* Check the package of two registers, each a Buffer in GAS format. */ 325141242Snjl pkg = (ACPI_OBJECT *)buf.Pointer; 326141242Snjl if (!ACPI_PKG_VALID(pkg, 2)) { 327141242Snjl device_printf(dev, "invalid perf register package\n"); 328141295Snjl goto out; 329141242Snjl } 330141242Snjl 331141373Snjl error = acpi_PkgGas(sc->dev, pkg, 0, &sc->perf_ctrl_type, &sc->px_rid, 332165875Snjl &sc->perf_ctrl, 0); 333141373Snjl if (error) { 334141824Snjl /* 335141824Snjl * If the register is of type FFixedHW, we can only return 336141824Snjl * info, we can't get or set new settings. 337141824Snjl */ 338141824Snjl if (error == EOPNOTSUPP) { 339141824Snjl sc->info_only = TRUE; 340141824Snjl error = 0; 341141824Snjl } else 342141295Snjl device_printf(dev, "failed in PERF_CTL attach\n"); 343141295Snjl goto out; 344141242Snjl } 345141242Snjl sc->px_rid++; 346141242Snjl 347141373Snjl error = acpi_PkgGas(sc->dev, pkg, 1, &sc->perf_sts_type, &sc->px_rid, 348165875Snjl &sc->perf_status, 0); 349141373Snjl if (error) { 350141824Snjl if (error == EOPNOTSUPP) { 351141824Snjl sc->info_only = TRUE; 352141824Snjl error = 0; 353141824Snjl } else 354141295Snjl device_printf(dev, "failed in PERF_STATUS attach\n"); 355141295Snjl goto out; 356141242Snjl } 357141242Snjl sc->px_rid++; 358141242Snjl 359141242Snjl /* Get our current limit and register for notifies. */ 360141242Snjl acpi_px_available(sc); 361141242Snjl AcpiInstallNotifyHandler(sc->handle, ACPI_DEVICE_NOTIFY, 362141242Snjl acpi_px_notify, sc); 363141295Snjl error = 0; 364141242Snjl 365141295Snjlout: 366141295Snjl if (error) { 367144197Snjl if (sc->px_states) { 368141295Snjl free(sc->px_states, M_ACPIPERF); 369144197Snjl sc->px_states = NULL; 370144197Snjl } 371144197Snjl if (sc->perf_ctrl) { 372144197Snjl bus_release_resource(sc->dev, sc->perf_ctrl_type, 0, 373144197Snjl sc->perf_ctrl); 374144197Snjl bus_delete_resource(sc->dev, sc->perf_ctrl_type, 0); 375144197Snjl sc->perf_ctrl = NULL; 376144197Snjl } 377144197Snjl if (sc->perf_status) { 378144197Snjl bus_release_resource(sc->dev, sc->perf_sts_type, 1, 379144197Snjl sc->perf_status); 380144197Snjl bus_delete_resource(sc->dev, sc->perf_sts_type, 1); 381144197Snjl sc->perf_status = NULL; 382144197Snjl } 383144197Snjl sc->px_rid = 0; 384141295Snjl sc->px_count = 0; 385141295Snjl } 386141295Snjl if (buf.Pointer) 387141295Snjl AcpiOsFree(buf.Pointer); 388141295Snjl return (error); 389141242Snjl} 390141242Snjl 391141242Snjlstatic void 392141411Snjlacpi_px_startup(void *arg) 393141411Snjl{ 394141411Snjl 395141411Snjl /* Signal to the platform that we are taking over CPU control. */ 396167814Sjkim if (AcpiGbl_FADT.PstateControl == 0) 397141411Snjl return; 398141411Snjl ACPI_LOCK(acpi); 399167814Sjkim AcpiOsWritePort(AcpiGbl_FADT.SmiCommand, AcpiGbl_FADT.PstateControl, 8); 400141411Snjl ACPI_UNLOCK(acpi); 401141411Snjl} 402141411Snjl 403141411Snjlstatic void 404141242Snjlacpi_px_notify(ACPI_HANDLE h, UINT32 notify, void *context) 405141242Snjl{ 406141242Snjl struct acpi_perf_softc *sc; 407141242Snjl 408141242Snjl sc = context; 409141429Snjl if (notify != ACPI_NOTIFY_PERF_STATES) 410141429Snjl return; 411141429Snjl 412141242Snjl acpi_px_available(sc); 413141242Snjl 414141242Snjl /* TODO: Implement notification when frequency changes. */ 415141242Snjl} 416141242Snjl 417141242Snjl/* 418141242Snjl * Find the highest currently-supported performance state. 419141242Snjl * This can be called at runtime (e.g., due to a docking event) at 420141242Snjl * the request of a Notify on the processor object. 421141242Snjl */ 422141242Snjlstatic void 423141242Snjlacpi_px_available(struct acpi_perf_softc *sc) 424141242Snjl{ 425141242Snjl ACPI_STATUS status; 426141242Snjl struct cf_setting set; 427141242Snjl 428141242Snjl status = acpi_GetInteger(sc->handle, "_PPC", &sc->px_max_avail); 429141242Snjl 430141242Snjl /* If the old state is too high, set current state to the new max. */ 431141242Snjl if (ACPI_SUCCESS(status)) { 432141242Snjl if (sc->px_curr_state != CPUFREQ_VAL_UNKNOWN && 433141242Snjl sc->px_curr_state > sc->px_max_avail) { 434141242Snjl acpi_px_to_set(sc->dev, 435141242Snjl &sc->px_states[sc->px_max_avail], &set); 436141242Snjl acpi_px_set(sc->dev, &set); 437141242Snjl } 438141242Snjl } else 439141242Snjl sc->px_max_avail = 0; 440141242Snjl} 441141242Snjl 442141242Snjlstatic int 443141242Snjlacpi_px_to_set(device_t dev, struct acpi_px *px, struct cf_setting *set) 444141242Snjl{ 445141242Snjl 446141242Snjl if (px == NULL || set == NULL) 447141242Snjl return (EINVAL); 448141242Snjl 449141242Snjl set->freq = px->core_freq; 450141242Snjl set->power = px->power; 451141242Snjl /* XXX Include BM latency too? */ 452141242Snjl set->lat = px->trans_lat; 453141242Snjl set->volts = CPUFREQ_VAL_UNKNOWN; 454141242Snjl set->dev = dev; 455142073Snjl set->spec[PX_SPEC_CONTROL] = px->ctrl_val; 456142073Snjl set->spec[PX_SPEC_STATUS] = px->sts_val; 457141242Snjl 458141242Snjl return (0); 459141242Snjl} 460141242Snjl 461141242Snjlstatic int 462142032Snjlacpi_px_settings(device_t dev, struct cf_setting *sets, int *count) 463141242Snjl{ 464141242Snjl struct acpi_perf_softc *sc; 465141242Snjl int x, y; 466141242Snjl 467141242Snjl sc = device_get_softc(dev); 468141242Snjl if (sets == NULL || count == NULL) 469141242Snjl return (EINVAL); 470141242Snjl if (*count < sc->px_count - sc->px_max_avail) 471142003Snjl return (E2BIG); 472141242Snjl 473141242Snjl /* Return a list of settings that are currently valid. */ 474141242Snjl y = 0; 475141242Snjl for (x = sc->px_max_avail; x < sc->px_count; x++, y++) 476141242Snjl acpi_px_to_set(dev, &sc->px_states[x], &sets[y]); 477141242Snjl *count = sc->px_count - sc->px_max_avail; 478141242Snjl 479141242Snjl return (0); 480141242Snjl} 481141242Snjl 482141242Snjlstatic int 483141242Snjlacpi_px_set(device_t dev, const struct cf_setting *set) 484141242Snjl{ 485141242Snjl struct acpi_perf_softc *sc; 486141242Snjl int i, status, sts_val, tries; 487141242Snjl 488141242Snjl if (set == NULL) 489141242Snjl return (EINVAL); 490141242Snjl sc = device_get_softc(dev); 491141242Snjl 492141824Snjl /* If we can't set new states, return immediately. */ 493141824Snjl if (sc->info_only) 494141824Snjl return (ENXIO); 495141824Snjl 496141242Snjl /* Look up appropriate state, based on frequency. */ 497141242Snjl for (i = sc->px_max_avail; i < sc->px_count; i++) { 498141242Snjl if (CPUFREQ_CMP(set->freq, sc->px_states[i].core_freq)) 499141242Snjl break; 500141242Snjl } 501141242Snjl if (i == sc->px_count) 502141242Snjl return (EINVAL); 503141242Snjl 504141242Snjl /* Write the appropriate value to the register. */ 505141242Snjl PX_SET_REG(sc->perf_ctrl, sc->px_states[i].ctrl_val); 506141242Snjl 507142204Snjl /* 508142204Snjl * Try for up to 10 ms to verify the desired state was selected. 509142204Snjl * This is longer than the standard says (1 ms) but in some modes, 510142204Snjl * systems may take longer to respond. 511142204Snjl */ 512141242Snjl sts_val = sc->px_states[i].sts_val; 513142204Snjl for (tries = 0; tries < 1000; tries++) { 514141242Snjl status = PX_GET_REG(sc->perf_status); 515142204Snjl 516142204Snjl /* 517142204Snjl * If we match the status or the desired status is 8 bits 518142204Snjl * and matches the relevant bits, assume we succeeded. It 519142204Snjl * appears some systems (IBM R32) expect byte-wide access 520142204Snjl * even though the standard says the register is 32-bit. 521142204Snjl */ 522142204Snjl if (status == sts_val || 523142204Snjl ((sts_val & ~0xff) == 0 && (status & 0xff) == sts_val)) 524141242Snjl break; 525141242Snjl DELAY(10); 526141242Snjl } 527142204Snjl if (tries == 1000) { 528141242Snjl device_printf(dev, "Px transition to %d failed\n", 529141242Snjl sc->px_states[i].core_freq); 530141242Snjl return (ENXIO); 531141242Snjl } 532141242Snjl sc->px_curr_state = i; 533141242Snjl 534141242Snjl return (0); 535141242Snjl} 536141242Snjl 537141242Snjlstatic int 538141242Snjlacpi_px_get(device_t dev, struct cf_setting *set) 539141242Snjl{ 540141242Snjl struct acpi_perf_softc *sc; 541141242Snjl uint64_t rate; 542141242Snjl int i; 543141242Snjl struct pcpu *pc; 544141242Snjl 545141242Snjl if (set == NULL) 546141242Snjl return (EINVAL); 547141242Snjl sc = device_get_softc(dev); 548141242Snjl 549141824Snjl /* If we can't get new states, return immediately. */ 550141824Snjl if (sc->info_only) 551141824Snjl return (ENXIO); 552141824Snjl 553141242Snjl /* If we've set the rate before, use the cached value. */ 554141242Snjl if (sc->px_curr_state != CPUFREQ_VAL_UNKNOWN) { 555141242Snjl acpi_px_to_set(dev, &sc->px_states[sc->px_curr_state], set); 556141242Snjl return (0); 557141242Snjl } 558141242Snjl 559141242Snjl /* Otherwise, estimate and try to match against our settings. */ 560141242Snjl pc = cpu_get_pcpu(dev); 561141242Snjl if (pc == NULL) 562141242Snjl return (ENXIO); 563141242Snjl cpu_est_clockrate(pc->pc_cpuid, &rate); 564141242Snjl rate /= 1000000; 565141242Snjl for (i = 0; i < sc->px_count; i++) { 566141242Snjl if (CPUFREQ_CMP(sc->px_states[i].core_freq, rate)) { 567141242Snjl sc->px_curr_state = i; 568141242Snjl acpi_px_to_set(dev, &sc->px_states[i], set); 569141242Snjl break; 570141242Snjl } 571141242Snjl } 572141242Snjl 573141242Snjl /* No match, give up. */ 574141242Snjl if (i == sc->px_count) { 575141242Snjl sc->px_curr_state = CPUFREQ_VAL_UNKNOWN; 576141242Snjl set->freq = CPUFREQ_VAL_UNKNOWN; 577141242Snjl } 578141242Snjl 579141242Snjl return (0); 580141242Snjl} 581142032Snjl 582142032Snjlstatic int 583142032Snjlacpi_px_type(device_t dev, int *type) 584142032Snjl{ 585142032Snjl struct acpi_perf_softc *sc; 586142032Snjl 587142032Snjl if (type == NULL) 588142032Snjl return (EINVAL); 589142032Snjl sc = device_get_softc(dev); 590142032Snjl 591142032Snjl *type = CPUFREQ_TYPE_ABSOLUTE; 592142032Snjl if (sc->info_only) 593142032Snjl *type |= CPUFREQ_FLAG_INFO_ONLY; 594142032Snjl return (0); 595142032Snjl} 596