16cb2040bSNate Lawson /*-
26cb2040bSNate Lawson * Copyright (c) 2003-2005 Nate Lawson (SDG)
36cb2040bSNate Lawson * All rights reserved.
46cb2040bSNate Lawson *
56cb2040bSNate Lawson * Redistribution and use in source and binary forms, with or without
66cb2040bSNate Lawson * modification, are permitted provided that the following conditions
76cb2040bSNate Lawson * are met:
86cb2040bSNate Lawson * 1. Redistributions of source code must retain the above copyright
96cb2040bSNate Lawson * notice, this list of conditions and the following disclaimer.
106cb2040bSNate Lawson * 2. Redistributions in binary form must reproduce the above copyright
116cb2040bSNate Lawson * notice, this list of conditions and the following disclaimer in the
126cb2040bSNate Lawson * documentation and/or other materials provided with the distribution.
136cb2040bSNate Lawson *
146cb2040bSNate Lawson * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
156cb2040bSNate Lawson * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
166cb2040bSNate Lawson * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
176cb2040bSNate Lawson * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
186cb2040bSNate Lawson * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
196cb2040bSNate Lawson * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
206cb2040bSNate Lawson * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
216cb2040bSNate Lawson * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
226cb2040bSNate Lawson * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
236cb2040bSNate Lawson * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
246cb2040bSNate Lawson * SUCH DAMAGE.
256cb2040bSNate Lawson */
266cb2040bSNate Lawson
276cb2040bSNate Lawson #include <sys/cdefs.h>
286cb2040bSNate Lawson #include "opt_acpi.h"
296cb2040bSNate Lawson #include <sys/param.h>
306cb2040bSNate Lawson #include <sys/kernel.h>
316cb2040bSNate Lawson #include <sys/proc.h>
326cb2040bSNate Lawson #include <sys/sched.h>
336cb2040bSNate Lawson #include <sys/bus.h>
346cb2040bSNate Lawson #include <sys/cpu.h>
356cb2040bSNate Lawson #include <sys/power.h>
366cb2040bSNate Lawson #include <sys/malloc.h>
376cb2040bSNate Lawson #include <sys/module.h>
386cb2040bSNate Lawson #include <sys/sbuf.h>
396cb2040bSNate Lawson #include <sys/pcpu.h>
406cb2040bSNate Lawson
416cb2040bSNate Lawson #include <machine/bus.h>
426cb2040bSNate Lawson #include <machine/resource.h>
436cb2040bSNate Lawson #include <sys/rman.h>
446cb2040bSNate Lawson
45129d3046SJung-uk Kim #include <contrib/dev/acpica/include/acpi.h>
46129d3046SJung-uk Kim
476cb2040bSNate Lawson #include <dev/acpica/acpivar.h>
486cb2040bSNate Lawson
496cb2040bSNate Lawson #include "cpufreq_if.h"
506cb2040bSNate Lawson
516cb2040bSNate Lawson /*
526cb2040bSNate Lawson * Support for ACPI processor performance states (Px) according to
53e22cd41cSNate Lawson * section 8.3.3 of the ACPI 2.0c specification.
546cb2040bSNate Lawson */
556cb2040bSNate Lawson
566cb2040bSNate Lawson struct acpi_px {
576cb2040bSNate Lawson uint32_t core_freq;
586cb2040bSNate Lawson uint32_t power;
596cb2040bSNate Lawson uint32_t trans_lat;
606cb2040bSNate Lawson uint32_t bm_lat;
616cb2040bSNate Lawson uint32_t ctrl_val;
626cb2040bSNate Lawson uint32_t sts_val;
636cb2040bSNate Lawson };
646cb2040bSNate Lawson
6528d7170fSNate Lawson /* Offsets in struct cf_setting array for storing driver-specific values. */
6628d7170fSNate Lawson #define PX_SPEC_CONTROL 0
6728d7170fSNate Lawson #define PX_SPEC_STATUS 1
6828d7170fSNate Lawson
696cb2040bSNate Lawson #define MAX_PX_STATES 16
706cb2040bSNate Lawson
716cb2040bSNate Lawson struct acpi_perf_softc {
726cb2040bSNate Lawson device_t dev;
736cb2040bSNate Lawson ACPI_HANDLE handle;
746cb2040bSNate Lawson struct resource *perf_ctrl; /* Set new performance state. */
7576ce4cc4SNate Lawson int perf_ctrl_type; /* Resource type for perf_ctrl. */
766cb2040bSNate Lawson struct resource *perf_status; /* Check that transition succeeded. */
7776ce4cc4SNate Lawson int perf_sts_type; /* Resource type for perf_status. */
786cb2040bSNate Lawson struct acpi_px *px_states; /* ACPI perf states. */
796cb2040bSNate Lawson uint32_t px_count; /* Total number of perf states. */
806cb2040bSNate Lawson uint32_t px_max_avail; /* Lowest index state available. */
816cb2040bSNate Lawson int px_curr_state; /* Active state index. */
826cb2040bSNate Lawson int px_rid;
83e22cd41cSNate Lawson int info_only; /* Can we set new states? */
846cb2040bSNate Lawson };
856cb2040bSNate Lawson
866cb2040bSNate Lawson #define PX_GET_REG(reg) \
876cb2040bSNate Lawson (bus_space_read_4(rman_get_bustag((reg)), \
886cb2040bSNate Lawson rman_get_bushandle((reg)), 0))
896cb2040bSNate Lawson #define PX_SET_REG(reg, val) \
906cb2040bSNate Lawson (bus_space_write_4(rman_get_bustag((reg)), \
916cb2040bSNate Lawson rman_get_bushandle((reg)), 0, (val)))
926cb2040bSNate Lawson
938b888c66SNate Lawson #define ACPI_NOTIFY_PERF_STATES 0x80 /* _PSS changed. */
948b888c66SNate Lawson
956cb2040bSNate Lawson static void acpi_perf_identify(driver_t *driver, device_t parent);
966cb2040bSNate Lawson static int acpi_perf_probe(device_t dev);
976cb2040bSNate Lawson static int acpi_perf_attach(device_t dev);
986cb2040bSNate Lawson static int acpi_perf_detach(device_t dev);
996cb2040bSNate Lawson static int acpi_perf_evaluate(device_t dev);
1006cb2040bSNate Lawson static int acpi_px_to_set(device_t dev, struct acpi_px *px,
1016cb2040bSNate Lawson struct cf_setting *set);
1026cb2040bSNate Lawson static void acpi_px_available(struct acpi_perf_softc *sc);
1033cc2f176SNate Lawson static void acpi_px_startup(void *arg);
1046cb2040bSNate Lawson static void acpi_px_notify(ACPI_HANDLE h, UINT32 notify, void *context);
1056cb2040bSNate Lawson static int acpi_px_settings(device_t dev, struct cf_setting *sets,
106e94a0c1aSNate Lawson int *count);
1076cb2040bSNate Lawson static int acpi_px_set(device_t dev, const struct cf_setting *set);
1086cb2040bSNate Lawson static int acpi_px_get(device_t dev, struct cf_setting *set);
109e94a0c1aSNate Lawson static int acpi_px_type(device_t dev, int *type);
1106cb2040bSNate Lawson
1116cb2040bSNate Lawson static device_method_t acpi_perf_methods[] = {
1126cb2040bSNate Lawson /* Device interface */
1136cb2040bSNate Lawson DEVMETHOD(device_identify, acpi_perf_identify),
1146cb2040bSNate Lawson DEVMETHOD(device_probe, acpi_perf_probe),
1156cb2040bSNate Lawson DEVMETHOD(device_attach, acpi_perf_attach),
1166cb2040bSNate Lawson DEVMETHOD(device_detach, acpi_perf_detach),
1176cb2040bSNate Lawson
1186cb2040bSNate Lawson /* cpufreq interface */
1196cb2040bSNate Lawson DEVMETHOD(cpufreq_drv_set, acpi_px_set),
1206cb2040bSNate Lawson DEVMETHOD(cpufreq_drv_get, acpi_px_get),
121e94a0c1aSNate Lawson DEVMETHOD(cpufreq_drv_type, acpi_px_type),
1226cb2040bSNate Lawson DEVMETHOD(cpufreq_drv_settings, acpi_px_settings),
12361bfd867SSofian Brabez
12461bfd867SSofian Brabez DEVMETHOD_END
1256cb2040bSNate Lawson };
1266cb2040bSNate Lawson
1276cb2040bSNate Lawson static driver_t acpi_perf_driver = {
1286cb2040bSNate Lawson "acpi_perf",
1296cb2040bSNate Lawson acpi_perf_methods,
1306cb2040bSNate Lawson sizeof(struct acpi_perf_softc),
1316cb2040bSNate Lawson };
1326cb2040bSNate Lawson
133*916a5d8aSJohn Baldwin DRIVER_MODULE(acpi_perf, cpu, acpi_perf_driver, 0, 0);
1346cb2040bSNate Lawson MODULE_DEPEND(acpi_perf, acpi, 1, 1, 1);
1356cb2040bSNate Lawson
136d745c852SEd Schouten static MALLOC_DEFINE(M_ACPIPERF, "acpi_perf", "ACPI Performance states");
1376cb2040bSNate Lawson
1386cb2040bSNate Lawson static void
acpi_perf_identify(driver_t * driver,device_t parent)1396cb2040bSNate Lawson acpi_perf_identify(driver_t *driver, device_t parent)
1406cb2040bSNate Lawson {
1416cb2040bSNate Lawson ACPI_HANDLE handle;
1426638c61aSNate Lawson device_t dev;
1436cb2040bSNate Lawson
1446cb2040bSNate Lawson /* Make sure we're not being doubly invoked. */
1458b39b8ceSNate Lawson if (device_find_child(parent, "acpi_perf", -1) != NULL)
1466cb2040bSNate Lawson return;
1476cb2040bSNate Lawson
1486cb2040bSNate Lawson /* Get the handle for the Processor object and check for perf states. */
1496cb2040bSNate Lawson handle = acpi_get_handle(parent);
1506cb2040bSNate Lawson if (handle == NULL)
1516cb2040bSNate Lawson return;
1526cb2040bSNate Lawson if (ACPI_FAILURE(AcpiEvaluateObject(handle, "_PSS", NULL, NULL)))
1536cb2040bSNate Lawson return;
15497d31723SNate Lawson
15597d31723SNate Lawson /*
15697d31723SNate Lawson * Add a child to every CPU that has the right methods. In future
15797d31723SNate Lawson * versions of the ACPI spec, CPUs can have different settings.
1586638c61aSNate Lawson * We probe this child now so that other devices that depend
1596638c61aSNate Lawson * on it (i.e., for info about supported states) will see it.
16097d31723SNate Lawson */
161d3a8f98aSAlexander Motin if ((dev = BUS_ADD_CHILD(parent, 0, "acpi_perf",
162d3a8f98aSAlexander Motin device_get_unit(parent))) != NULL)
1636638c61aSNate Lawson device_probe_and_attach(dev);
1646638c61aSNate Lawson else
1658b888c66SNate Lawson device_printf(parent, "add acpi_perf child failed\n");
1666cb2040bSNate Lawson }
1676cb2040bSNate Lawson
1686cb2040bSNate Lawson static int
acpi_perf_probe(device_t dev)1696cb2040bSNate Lawson acpi_perf_probe(device_t dev)
1706cb2040bSNate Lawson {
17176ce4cc4SNate Lawson ACPI_HANDLE handle;
17276ce4cc4SNate Lawson ACPI_OBJECT *pkg;
17376ce4cc4SNate Lawson struct resource *res;
17476ce4cc4SNate Lawson ACPI_BUFFER buf;
17576ce4cc4SNate Lawson int error, rid, type;
1766cb2040bSNate Lawson
177a8de37b0SEitan Adler if (resource_disabled("acpi_perf", 0))
178a8de37b0SEitan Adler return (ENXIO);
179a8de37b0SEitan Adler
18076ce4cc4SNate Lawson /*
18176ce4cc4SNate Lawson * Check the performance state registers. If they are of type
182e22cd41cSNate Lawson * "functional fixed hardware", we attach quietly since we will
183e22cd41cSNate Lawson * only be providing information on settings to other drivers.
18476ce4cc4SNate Lawson */
18576ce4cc4SNate Lawson error = ENXIO;
18676ce4cc4SNate Lawson handle = acpi_get_handle(dev);
18776ce4cc4SNate Lawson buf.Pointer = NULL;
18876ce4cc4SNate Lawson buf.Length = ACPI_ALLOCATE_BUFFER;
18976ce4cc4SNate Lawson if (ACPI_FAILURE(AcpiEvaluateObject(handle, "_PCT", NULL, &buf)))
19076ce4cc4SNate Lawson return (error);
19176ce4cc4SNate Lawson pkg = (ACPI_OBJECT *)buf.Pointer;
192e22cd41cSNate Lawson if (ACPI_PKG_VALID(pkg, 2)) {
19376ce4cc4SNate Lawson rid = 0;
194907b6777SNate Lawson error = acpi_PkgGas(dev, pkg, 0, &type, &rid, &res, 0);
195e22cd41cSNate Lawson switch (error) {
196e22cd41cSNate Lawson case 0:
19776ce4cc4SNate Lawson bus_release_resource(dev, type, rid, res);
198ca2c69c8SNate Lawson bus_delete_resource(dev, type, rid);
1996cb2040bSNate Lawson device_set_desc(dev, "ACPI CPU Frequency Control");
200e22cd41cSNate Lawson break;
201e22cd41cSNate Lawson case EOPNOTSUPP:
202e22cd41cSNate Lawson device_quiet(dev);
203e22cd41cSNate Lawson error = 0;
204e22cd41cSNate Lawson break;
205e22cd41cSNate Lawson }
20676ce4cc4SNate Lawson }
20776ce4cc4SNate Lawson AcpiOsFree(buf.Pointer);
20876ce4cc4SNate Lawson
20976ce4cc4SNate Lawson return (error);
2106cb2040bSNate Lawson }
2116cb2040bSNate Lawson
2126cb2040bSNate Lawson static int
acpi_perf_attach(device_t dev)2136cb2040bSNate Lawson acpi_perf_attach(device_t dev)
2146cb2040bSNate Lawson {
2156cb2040bSNate Lawson struct acpi_perf_softc *sc;
2166cb2040bSNate Lawson
2176cb2040bSNate Lawson sc = device_get_softc(dev);
2186cb2040bSNate Lawson sc->dev = dev;
2196cb2040bSNate Lawson sc->handle = acpi_get_handle(dev);
2206cb2040bSNate Lawson sc->px_max_avail = 0;
2216cb2040bSNate Lawson sc->px_curr_state = CPUFREQ_VAL_UNKNOWN;
2226cb2040bSNate Lawson if (acpi_perf_evaluate(dev) != 0)
2236cb2040bSNate Lawson return (ENXIO);
2242be4e471SJung-uk Kim AcpiOsExecute(OSL_NOTIFY_HANDLER, acpi_px_startup, NULL);
225f81de92fSNate Lawson if (!sc->info_only)
226f81de92fSNate Lawson cpufreq_register(dev);
2276cb2040bSNate Lawson
2286cb2040bSNate Lawson return (0);
2296cb2040bSNate Lawson }
2306cb2040bSNate Lawson
2316cb2040bSNate Lawson static int
acpi_perf_detach(device_t dev)2326cb2040bSNate Lawson acpi_perf_detach(device_t dev)
2336cb2040bSNate Lawson {
2346cb2040bSNate Lawson /* TODO: teardown registers, remove notify handler. */
2356cb2040bSNate Lawson return (ENXIO);
2366cb2040bSNate Lawson }
2376cb2040bSNate Lawson
2386cb2040bSNate Lawson /* Probe and setup any valid performance states (Px). */
2396cb2040bSNate Lawson static int
acpi_perf_evaluate(device_t dev)2406cb2040bSNate Lawson acpi_perf_evaluate(device_t dev)
2416cb2040bSNate Lawson {
2426cb2040bSNate Lawson struct acpi_perf_softc *sc;
2436cb2040bSNate Lawson ACPI_BUFFER buf;
2446cb2040bSNate Lawson ACPI_OBJECT *pkg, *res;
2456cb2040bSNate Lawson ACPI_STATUS status;
24655fa5feaSNate Lawson int count, error, i, j;
247f9ead0fcSBruno Ducrot static int once = 1;
2486cb2040bSNate Lawson uint32_t *p;
2496cb2040bSNate Lawson
2506cb2040bSNate Lawson /* Get the control values and parameters for each state. */
25126d5f7dfSNate Lawson error = ENXIO;
2526cb2040bSNate Lawson sc = device_get_softc(dev);
2536cb2040bSNate Lawson buf.Pointer = NULL;
2546cb2040bSNate Lawson buf.Length = ACPI_ALLOCATE_BUFFER;
2556cb2040bSNate Lawson status = AcpiEvaluateObject(sc->handle, "_PSS", NULL, &buf);
2566cb2040bSNate Lawson if (ACPI_FAILURE(status))
2576cb2040bSNate Lawson return (ENXIO);
2586cb2040bSNate Lawson
2596cb2040bSNate Lawson pkg = (ACPI_OBJECT *)buf.Pointer;
2606cb2040bSNate Lawson if (!ACPI_PKG_VALID(pkg, 1)) {
2616cb2040bSNate Lawson device_printf(dev, "invalid top level _PSS package\n");
26226d5f7dfSNate Lawson goto out;
2636cb2040bSNate Lawson }
2646cb2040bSNate Lawson sc->px_count = pkg->Package.Count;
2656cb2040bSNate Lawson
2666cb2040bSNate Lawson sc->px_states = malloc(sc->px_count * sizeof(struct acpi_px),
2676cb2040bSNate Lawson M_ACPIPERF, M_WAITOK | M_ZERO);
2686cb2040bSNate Lawson
2696cb2040bSNate Lawson /*
2706cb2040bSNate Lawson * Each state is a package of {CoreFreq, Power, TransitionLatency,
2716cb2040bSNate Lawson * BusMasterLatency, ControlVal, StatusVal}, sorted from highest
2726cb2040bSNate Lawson * performance to lowest.
2736cb2040bSNate Lawson */
27455fa5feaSNate Lawson count = 0;
2756cb2040bSNate Lawson for (i = 0; i < sc->px_count; i++) {
2766cb2040bSNate Lawson res = &pkg->Package.Elements[i];
2776cb2040bSNate Lawson if (!ACPI_PKG_VALID(res, 6)) {
278f9ead0fcSBruno Ducrot if (once) {
279f9ead0fcSBruno Ducrot once = 0;
2806cb2040bSNate Lawson device_printf(dev, "invalid _PSS package\n");
281f9ead0fcSBruno Ducrot }
2826cb2040bSNate Lawson continue;
2836cb2040bSNate Lawson }
2840716d65cSNate Lawson
2850716d65cSNate Lawson /* Parse the rest of the package into the struct. */
28655fa5feaSNate Lawson p = &sc->px_states[count].core_freq;
2876cb2040bSNate Lawson for (j = 0; j < 6; j++, p++)
2886cb2040bSNate Lawson acpi_PkgInt32(res, j, p);
28955fa5feaSNate Lawson
29055fa5feaSNate Lawson /*
29155fa5feaSNate Lawson * Check for some impossible frequencies that some systems
29255fa5feaSNate Lawson * use to indicate they don't actually support this Px state.
29355fa5feaSNate Lawson */
29455fa5feaSNate Lawson if (sc->px_states[count].core_freq == 0 ||
29555fa5feaSNate Lawson sc->px_states[count].core_freq == 9999 ||
29655fa5feaSNate Lawson sc->px_states[count].core_freq == 0x9999 ||
29755fa5feaSNate Lawson sc->px_states[count].core_freq >= 0xffff)
29855fa5feaSNate Lawson continue;
29955fa5feaSNate Lawson
3002880646dSBruno Ducrot /* Check for duplicate entries */
3012880646dSBruno Ducrot if (count > 0 &&
3022880646dSBruno Ducrot sc->px_states[count - 1].core_freq ==
3032880646dSBruno Ducrot sc->px_states[count].core_freq)
3042880646dSBruno Ducrot continue;
3052880646dSBruno Ducrot
30655fa5feaSNate Lawson count++;
3076cb2040bSNate Lawson }
308cb3f3e5cSNate Lawson sc->px_count = count;
30955fa5feaSNate Lawson
310cb3f3e5cSNate Lawson /* No valid Px state found so give up. */
31155fa5feaSNate Lawson if (count == 0)
31255fa5feaSNate Lawson goto out;
313fbe648c9SJohn Baldwin AcpiOsFree(buf.Pointer);
3146cb2040bSNate Lawson
3156cb2040bSNate Lawson /* Get the control and status registers (one of each). */
3166cb2040bSNate Lawson buf.Pointer = NULL;
3176cb2040bSNate Lawson buf.Length = ACPI_ALLOCATE_BUFFER;
3186cb2040bSNate Lawson status = AcpiEvaluateObject(sc->handle, "_PCT", NULL, &buf);
31926d5f7dfSNate Lawson if (ACPI_FAILURE(status))
32026d5f7dfSNate Lawson goto out;
3216cb2040bSNate Lawson
3226cb2040bSNate Lawson /* Check the package of two registers, each a Buffer in GAS format. */
3236cb2040bSNate Lawson pkg = (ACPI_OBJECT *)buf.Pointer;
3246cb2040bSNate Lawson if (!ACPI_PKG_VALID(pkg, 2)) {
3256cb2040bSNate Lawson device_printf(dev, "invalid perf register package\n");
32626d5f7dfSNate Lawson goto out;
3276cb2040bSNate Lawson }
3286cb2040bSNate Lawson
32976ce4cc4SNate Lawson error = acpi_PkgGas(sc->dev, pkg, 0, &sc->perf_ctrl_type, &sc->px_rid,
330907b6777SNate Lawson &sc->perf_ctrl, 0);
33176ce4cc4SNate Lawson if (error) {
332e22cd41cSNate Lawson /*
333e22cd41cSNate Lawson * If the register is of type FFixedHW, we can only return
334e22cd41cSNate Lawson * info, we can't get or set new settings.
335e22cd41cSNate Lawson */
336e22cd41cSNate Lawson if (error == EOPNOTSUPP) {
337e22cd41cSNate Lawson sc->info_only = TRUE;
338e22cd41cSNate Lawson error = 0;
339e22cd41cSNate Lawson } else
34026d5f7dfSNate Lawson device_printf(dev, "failed in PERF_CTL attach\n");
34126d5f7dfSNate Lawson goto out;
3426cb2040bSNate Lawson }
3436cb2040bSNate Lawson sc->px_rid++;
3446cb2040bSNate Lawson
34576ce4cc4SNate Lawson error = acpi_PkgGas(sc->dev, pkg, 1, &sc->perf_sts_type, &sc->px_rid,
346907b6777SNate Lawson &sc->perf_status, 0);
34776ce4cc4SNate Lawson if (error) {
348e22cd41cSNate Lawson if (error == EOPNOTSUPP) {
349e22cd41cSNate Lawson sc->info_only = TRUE;
350e22cd41cSNate Lawson error = 0;
351e22cd41cSNate Lawson } else
35226d5f7dfSNate Lawson device_printf(dev, "failed in PERF_STATUS attach\n");
35326d5f7dfSNate Lawson goto out;
3546cb2040bSNate Lawson }
3556cb2040bSNate Lawson sc->px_rid++;
3566cb2040bSNate Lawson
3576cb2040bSNate Lawson /* Get our current limit and register for notifies. */
3586cb2040bSNate Lawson acpi_px_available(sc);
3596cb2040bSNate Lawson AcpiInstallNotifyHandler(sc->handle, ACPI_DEVICE_NOTIFY,
3606cb2040bSNate Lawson acpi_px_notify, sc);
36126d5f7dfSNate Lawson error = 0;
3626cb2040bSNate Lawson
36326d5f7dfSNate Lawson out:
36426d5f7dfSNate Lawson if (error) {
365ca2c69c8SNate Lawson if (sc->px_states) {
36626d5f7dfSNate Lawson free(sc->px_states, M_ACPIPERF);
367ca2c69c8SNate Lawson sc->px_states = NULL;
368ca2c69c8SNate Lawson }
369ca2c69c8SNate Lawson if (sc->perf_ctrl) {
370ca2c69c8SNate Lawson bus_release_resource(sc->dev, sc->perf_ctrl_type, 0,
371ca2c69c8SNate Lawson sc->perf_ctrl);
372ca2c69c8SNate Lawson bus_delete_resource(sc->dev, sc->perf_ctrl_type, 0);
373ca2c69c8SNate Lawson sc->perf_ctrl = NULL;
374ca2c69c8SNate Lawson }
375ca2c69c8SNate Lawson if (sc->perf_status) {
376ca2c69c8SNate Lawson bus_release_resource(sc->dev, sc->perf_sts_type, 1,
377ca2c69c8SNate Lawson sc->perf_status);
378ca2c69c8SNate Lawson bus_delete_resource(sc->dev, sc->perf_sts_type, 1);
379ca2c69c8SNate Lawson sc->perf_status = NULL;
380ca2c69c8SNate Lawson }
381ca2c69c8SNate Lawson sc->px_rid = 0;
38226d5f7dfSNate Lawson sc->px_count = 0;
38326d5f7dfSNate Lawson }
38426d5f7dfSNate Lawson if (buf.Pointer)
38526d5f7dfSNate Lawson AcpiOsFree(buf.Pointer);
38626d5f7dfSNate Lawson return (error);
3876cb2040bSNate Lawson }
3886cb2040bSNate Lawson
3896cb2040bSNate Lawson static void
acpi_px_startup(void * arg)3903cc2f176SNate Lawson acpi_px_startup(void *arg)
3913cc2f176SNate Lawson {
3923cc2f176SNate Lawson
3933cc2f176SNate Lawson /* Signal to the platform that we are taking over CPU control. */
3942be4e471SJung-uk Kim if (AcpiGbl_FADT.PstateControl == 0)
3953cc2f176SNate Lawson return;
3963cc2f176SNate Lawson ACPI_LOCK(acpi);
3972be4e471SJung-uk Kim AcpiOsWritePort(AcpiGbl_FADT.SmiCommand, AcpiGbl_FADT.PstateControl, 8);
3983cc2f176SNate Lawson ACPI_UNLOCK(acpi);
3993cc2f176SNate Lawson }
4003cc2f176SNate Lawson
4013cc2f176SNate Lawson static void
acpi_px_notify(ACPI_HANDLE h,UINT32 notify,void * context)4026cb2040bSNate Lawson acpi_px_notify(ACPI_HANDLE h, UINT32 notify, void *context)
4036cb2040bSNate Lawson {
4046cb2040bSNate Lawson struct acpi_perf_softc *sc;
4056cb2040bSNate Lawson
4066cb2040bSNate Lawson sc = context;
4078b888c66SNate Lawson if (notify != ACPI_NOTIFY_PERF_STATES)
4088b888c66SNate Lawson return;
4098b888c66SNate Lawson
4106cb2040bSNate Lawson acpi_px_available(sc);
4116cb2040bSNate Lawson
4126cb2040bSNate Lawson /* TODO: Implement notification when frequency changes. */
4136cb2040bSNate Lawson }
4146cb2040bSNate Lawson
4156cb2040bSNate Lawson /*
4166cb2040bSNate Lawson * Find the highest currently-supported performance state.
4176cb2040bSNate Lawson * This can be called at runtime (e.g., due to a docking event) at
4186cb2040bSNate Lawson * the request of a Notify on the processor object.
4196cb2040bSNate Lawson */
4206cb2040bSNate Lawson static void
acpi_px_available(struct acpi_perf_softc * sc)4216cb2040bSNate Lawson acpi_px_available(struct acpi_perf_softc *sc)
4226cb2040bSNate Lawson {
4236cb2040bSNate Lawson ACPI_STATUS status;
4246cb2040bSNate Lawson struct cf_setting set;
4256cb2040bSNate Lawson
4266cb2040bSNate Lawson status = acpi_GetInteger(sc->handle, "_PPC", &sc->px_max_avail);
4276cb2040bSNate Lawson
4286cb2040bSNate Lawson /* If the old state is too high, set current state to the new max. */
4296cb2040bSNate Lawson if (ACPI_SUCCESS(status)) {
4306cb2040bSNate Lawson if (sc->px_curr_state != CPUFREQ_VAL_UNKNOWN &&
4316cb2040bSNate Lawson sc->px_curr_state > sc->px_max_avail) {
4326cb2040bSNate Lawson acpi_px_to_set(sc->dev,
4336cb2040bSNate Lawson &sc->px_states[sc->px_max_avail], &set);
4346cb2040bSNate Lawson acpi_px_set(sc->dev, &set);
4356cb2040bSNate Lawson }
4366cb2040bSNate Lawson } else
4376cb2040bSNate Lawson sc->px_max_avail = 0;
4386cb2040bSNate Lawson }
4396cb2040bSNate Lawson
4406cb2040bSNate Lawson static int
acpi_px_to_set(device_t dev,struct acpi_px * px,struct cf_setting * set)4416cb2040bSNate Lawson acpi_px_to_set(device_t dev, struct acpi_px *px, struct cf_setting *set)
4426cb2040bSNate Lawson {
4436cb2040bSNate Lawson
4446cb2040bSNate Lawson if (px == NULL || set == NULL)
4456cb2040bSNate Lawson return (EINVAL);
4466cb2040bSNate Lawson
4476cb2040bSNate Lawson set->freq = px->core_freq;
4486cb2040bSNate Lawson set->power = px->power;
4496cb2040bSNate Lawson /* XXX Include BM latency too? */
4506cb2040bSNate Lawson set->lat = px->trans_lat;
4516cb2040bSNate Lawson set->volts = CPUFREQ_VAL_UNKNOWN;
4526cb2040bSNate Lawson set->dev = dev;
45328d7170fSNate Lawson set->spec[PX_SPEC_CONTROL] = px->ctrl_val;
45428d7170fSNate Lawson set->spec[PX_SPEC_STATUS] = px->sts_val;
4556cb2040bSNate Lawson
4566cb2040bSNate Lawson return (0);
4576cb2040bSNate Lawson }
4586cb2040bSNate Lawson
4596cb2040bSNate Lawson static int
acpi_px_settings(device_t dev,struct cf_setting * sets,int * count)460e94a0c1aSNate Lawson acpi_px_settings(device_t dev, struct cf_setting *sets, int *count)
4616cb2040bSNate Lawson {
4626cb2040bSNate Lawson struct acpi_perf_softc *sc;
4636cb2040bSNate Lawson int x, y;
4646cb2040bSNate Lawson
4656cb2040bSNate Lawson sc = device_get_softc(dev);
4666cb2040bSNate Lawson if (sets == NULL || count == NULL)
4676cb2040bSNate Lawson return (EINVAL);
4686cb2040bSNate Lawson if (*count < sc->px_count - sc->px_max_avail)
469ededc31dSNate Lawson return (E2BIG);
4706cb2040bSNate Lawson
4716cb2040bSNate Lawson /* Return a list of settings that are currently valid. */
4726cb2040bSNate Lawson y = 0;
4736cb2040bSNate Lawson for (x = sc->px_max_avail; x < sc->px_count; x++, y++)
4746cb2040bSNate Lawson acpi_px_to_set(dev, &sc->px_states[x], &sets[y]);
4756cb2040bSNate Lawson *count = sc->px_count - sc->px_max_avail;
4766cb2040bSNate Lawson
4776cb2040bSNate Lawson return (0);
4786cb2040bSNate Lawson }
4796cb2040bSNate Lawson
4806cb2040bSNate Lawson static int
acpi_px_set(device_t dev,const struct cf_setting * set)4816cb2040bSNate Lawson acpi_px_set(device_t dev, const struct cf_setting *set)
4826cb2040bSNate Lawson {
4836cb2040bSNate Lawson struct acpi_perf_softc *sc;
4846cb2040bSNate Lawson int i, status, sts_val, tries;
4856cb2040bSNate Lawson
4866cb2040bSNate Lawson if (set == NULL)
4876cb2040bSNate Lawson return (EINVAL);
4886cb2040bSNate Lawson sc = device_get_softc(dev);
4896cb2040bSNate Lawson
490e22cd41cSNate Lawson /* If we can't set new states, return immediately. */
491e22cd41cSNate Lawson if (sc->info_only)
492e22cd41cSNate Lawson return (ENXIO);
493e22cd41cSNate Lawson
4946cb2040bSNate Lawson /* Look up appropriate state, based on frequency. */
4956cb2040bSNate Lawson for (i = sc->px_max_avail; i < sc->px_count; i++) {
4966cb2040bSNate Lawson if (CPUFREQ_CMP(set->freq, sc->px_states[i].core_freq))
4976cb2040bSNate Lawson break;
4986cb2040bSNate Lawson }
4996cb2040bSNate Lawson if (i == sc->px_count)
5006cb2040bSNate Lawson return (EINVAL);
5016cb2040bSNate Lawson
5026cb2040bSNate Lawson /* Write the appropriate value to the register. */
5036cb2040bSNate Lawson PX_SET_REG(sc->perf_ctrl, sc->px_states[i].ctrl_val);
5046cb2040bSNate Lawson
5050db8fa89SNate Lawson /*
5060db8fa89SNate Lawson * Try for up to 10 ms to verify the desired state was selected.
5070db8fa89SNate Lawson * This is longer than the standard says (1 ms) but in some modes,
5080db8fa89SNate Lawson * systems may take longer to respond.
5090db8fa89SNate Lawson */
5106cb2040bSNate Lawson sts_val = sc->px_states[i].sts_val;
5110db8fa89SNate Lawson for (tries = 0; tries < 1000; tries++) {
5126cb2040bSNate Lawson status = PX_GET_REG(sc->perf_status);
5130db8fa89SNate Lawson
5140db8fa89SNate Lawson /*
5150db8fa89SNate Lawson * If we match the status or the desired status is 8 bits
5160db8fa89SNate Lawson * and matches the relevant bits, assume we succeeded. It
5170db8fa89SNate Lawson * appears some systems (IBM R32) expect byte-wide access
5180db8fa89SNate Lawson * even though the standard says the register is 32-bit.
5190db8fa89SNate Lawson */
5200db8fa89SNate Lawson if (status == sts_val ||
5210db8fa89SNate Lawson ((sts_val & ~0xff) == 0 && (status & 0xff) == sts_val))
5226cb2040bSNate Lawson break;
5236cb2040bSNate Lawson DELAY(10);
5246cb2040bSNate Lawson }
5250db8fa89SNate Lawson if (tries == 1000) {
5266cb2040bSNate Lawson device_printf(dev, "Px transition to %d failed\n",
5276cb2040bSNate Lawson sc->px_states[i].core_freq);
5286cb2040bSNate Lawson return (ENXIO);
5296cb2040bSNate Lawson }
5306cb2040bSNate Lawson sc->px_curr_state = i;
5316cb2040bSNate Lawson
5326cb2040bSNate Lawson return (0);
5336cb2040bSNate Lawson }
5346cb2040bSNate Lawson
5356cb2040bSNate Lawson static int
acpi_px_get(device_t dev,struct cf_setting * set)5366cb2040bSNate Lawson acpi_px_get(device_t dev, struct cf_setting *set)
5376cb2040bSNate Lawson {
5386cb2040bSNate Lawson struct acpi_perf_softc *sc;
5396cb2040bSNate Lawson uint64_t rate;
5406cb2040bSNate Lawson int i;
5416cb2040bSNate Lawson struct pcpu *pc;
5426cb2040bSNate Lawson
5436cb2040bSNate Lawson if (set == NULL)
5446cb2040bSNate Lawson return (EINVAL);
5456cb2040bSNate Lawson sc = device_get_softc(dev);
5466cb2040bSNate Lawson
547e22cd41cSNate Lawson /* If we can't get new states, return immediately. */
548e22cd41cSNate Lawson if (sc->info_only)
549e22cd41cSNate Lawson return (ENXIO);
550e22cd41cSNate Lawson
5516cb2040bSNate Lawson /* If we've set the rate before, use the cached value. */
5526cb2040bSNate Lawson if (sc->px_curr_state != CPUFREQ_VAL_UNKNOWN) {
5536cb2040bSNate Lawson acpi_px_to_set(dev, &sc->px_states[sc->px_curr_state], set);
5546cb2040bSNate Lawson return (0);
5556cb2040bSNate Lawson }
5566cb2040bSNate Lawson
5576cb2040bSNate Lawson /* Otherwise, estimate and try to match against our settings. */
5586cb2040bSNate Lawson pc = cpu_get_pcpu(dev);
5596cb2040bSNate Lawson if (pc == NULL)
5606cb2040bSNate Lawson return (ENXIO);
5616cb2040bSNate Lawson cpu_est_clockrate(pc->pc_cpuid, &rate);
5626cb2040bSNate Lawson rate /= 1000000;
5636cb2040bSNate Lawson for (i = 0; i < sc->px_count; i++) {
5646cb2040bSNate Lawson if (CPUFREQ_CMP(sc->px_states[i].core_freq, rate)) {
5656cb2040bSNate Lawson sc->px_curr_state = i;
5666cb2040bSNate Lawson acpi_px_to_set(dev, &sc->px_states[i], set);
5676cb2040bSNate Lawson break;
5686cb2040bSNate Lawson }
5696cb2040bSNate Lawson }
5706cb2040bSNate Lawson
5716cb2040bSNate Lawson /* No match, give up. */
5726cb2040bSNate Lawson if (i == sc->px_count) {
5736cb2040bSNate Lawson sc->px_curr_state = CPUFREQ_VAL_UNKNOWN;
5746cb2040bSNate Lawson set->freq = CPUFREQ_VAL_UNKNOWN;
5756cb2040bSNate Lawson }
5766cb2040bSNate Lawson
5776cb2040bSNate Lawson return (0);
5786cb2040bSNate Lawson }
579e94a0c1aSNate Lawson
580e94a0c1aSNate Lawson static int
acpi_px_type(device_t dev,int * type)581e94a0c1aSNate Lawson acpi_px_type(device_t dev, int *type)
582e94a0c1aSNate Lawson {
583e94a0c1aSNate Lawson struct acpi_perf_softc *sc;
584e94a0c1aSNate Lawson
585e94a0c1aSNate Lawson if (type == NULL)
586e94a0c1aSNate Lawson return (EINVAL);
587e94a0c1aSNate Lawson sc = device_get_softc(dev);
588e94a0c1aSNate Lawson
589e94a0c1aSNate Lawson *type = CPUFREQ_TYPE_ABSOLUTE;
590e94a0c1aSNate Lawson if (sc->info_only)
591e94a0c1aSNate Lawson *type |= CPUFREQ_FLAG_INFO_ONLY;
592e94a0c1aSNate Lawson return (0);
593e94a0c1aSNate Lawson }
594