Files
John Baldwin 34cf45a93c acpi_timer: Add a softc to avoid use of global variables
Add a softc and use it to mostly replace the use of global variables
in this driver.  Simplify the suspend and resume event handlers by
saving the old timecounter in the softc and passing the softc pointer
to the handlers.

Differential Revision:	https://reviews.freebsd.org/D59936
2026-09-30 12:04:59 -04:00

295 lines
8.1 KiB
C

/*-
* Copyright (c) 2000, 2001 Michael Smith
* Copyright (c) 2000 BSDi
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <sys/cdefs.h>
#include "opt_acpi.h"
#include <sys/param.h>
#include <sys/bus.h>
#include <sys/eventhandler.h>
#include <sys/kernel.h>
#include <sys/module.h>
#include <sys/sysctl.h>
#include <sys/timetc.h>
#include <sys/power.h>
#include <machine/bus.h>
#include <machine/resource.h>
#include <sys/rman.h>
#include <contrib/dev/acpica/include/acpi.h>
#include <contrib/dev/acpica/include/accommon.h>
#include <dev/acpica/acpivar.h>
#include <dev/pci/pcivar.h>
/*
* A timecounter based on the free-running ACPI timer.
*
* Based on the i386-only mp_clock.c by <[email protected]>.
*/
/* Hooks for the ACPI CA debugging infrastructure */
#define _COMPONENT ACPI_TIMER
ACPI_MODULE_NAME("TIMER")
struct acpi_timer_softc {
device_t dev;
struct resource *reg;
struct timecounter *tc;
struct timecounter *old_tc;
};
#define ACPI_TIMER_FREQUENCY (14318182 / 4)
static void acpi_timer_identify(driver_t *driver, device_t parent);
static int acpi_timer_probe(device_t dev);
static int acpi_timer_attach(device_t dev);
static void acpi_timer_resume_handler(struct acpi_timer_softc *,
enum power_stype);
static void acpi_timer_suspend_handler(struct acpi_timer_softc *,
enum power_stype);
static u_int acpi_timer_get_timecount(struct timecounter *tc);
static int acpi_timer_sysctl_freq(SYSCTL_HANDLER_ARGS);
static device_method_t acpi_timer_methods[] = {
DEVMETHOD(device_identify, acpi_timer_identify),
DEVMETHOD(device_probe, acpi_timer_probe),
DEVMETHOD(device_attach, acpi_timer_attach),
DEVMETHOD_END
};
static driver_t acpi_timer_driver = {
"acpi_timer",
acpi_timer_methods,
sizeof(struct acpi_timer_softc),
};
DRIVER_MODULE(acpi_timer, acpi, acpi_timer_driver, 0, 0);
MODULE_DEPEND(acpi_timer, acpi, 1, 1, 1);
static struct timecounter acpi_timer_timecounter = {
acpi_timer_get_timecount, /* get_timecount function */
0, /* no poll_pps */
0, /* no default counter_mask */
0, /* no default frequency */
"ACPI", /* name */
900, /* quality */
TC_FLAGS_SUSPEND_SAFE /* flags */
};
static __inline uint32_t
acpi_timer_read(struct acpi_timer_softc *sc)
{
return (bus_read_4(sc->reg, 0));
}
/*
* Locate the ACPI timer using the FADT, set up and allocate the I/O resources
* we will be using.
*/
static void
acpi_timer_identify(driver_t *driver, device_t parent)
{
device_t dev;
rman_res_t rlen, rstart;
int rtype;
ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
if (acpi_disabled("timer") || (acpi_quirks & ACPI_Q_TIMER) ||
device_find_child(parent, "acpi_timer", DEVICE_UNIT_ANY) != NULL ||
AcpiGbl_FADT.PmTimerLength == 0)
return_VOID;
if ((dev = BUS_ADD_CHILD(parent, 2, "acpi_timer", 0)) == NULL) {
device_printf(parent, "could not add acpi_timer0\n");
return_VOID;
}
switch (AcpiGbl_FADT.XPmTimerBlock.SpaceId) {
case ACPI_ADR_SPACE_SYSTEM_MEMORY:
rtype = SYS_RES_MEMORY;
break;
case ACPI_ADR_SPACE_SYSTEM_IO:
rtype = SYS_RES_IOPORT;
break;
default:
return_VOID;
}
rlen = AcpiGbl_FADT.PmTimerLength;
rstart = AcpiGbl_FADT.XPmTimerBlock.Address;
if (bus_set_resource(dev, rtype, 0, rstart, rlen))
device_printf(dev, "couldn't set resource (%s 0x%jx+0x%jx)\n",
(rtype == SYS_RES_IOPORT) ? "port" : "mem", rstart, rlen);
return_VOID;
}
static int
acpi_timer_probe(device_t dev)
{
ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
device_set_descf(dev, "%d-bit timer at %u.%06uMHz",
(AcpiGbl_FADT.Flags & ACPI_FADT_32BIT_TIMER) != 0 ? 32 : 24,
ACPI_TIMER_FREQUENCY / 1000000, ACPI_TIMER_FREQUENCY % 1000000);
return (BUS_PROBE_NOWILDCARD);
}
static int
acpi_timer_attach(device_t dev)
{
struct acpi_timer_softc *sc = device_get_softc(dev);
eventhandler_tag eh;
int rtype;
ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
sc->dev = dev;
switch (AcpiGbl_FADT.XPmTimerBlock.SpaceId) {
case ACPI_ADR_SPACE_SYSTEM_MEMORY:
rtype = SYS_RES_MEMORY;
break;
case ACPI_ADR_SPACE_SYSTEM_IO:
rtype = SYS_RES_IOPORT;
break;
default:
return (ENXIO);
}
sc->reg = bus_alloc_resource_any(dev, rtype, 0, RF_ACTIVE);
if (sc->reg == NULL)
return (ENXIO);
/* Register resume and suspend event handlers. */
eh = EVENTHANDLER_REGISTER(power_suspend, acpi_timer_suspend_handler,
sc, EVENTHANDLER_PRI_LAST);
if (eh == NULL) {
device_printf(dev, "failed to register suspend event handler\n");
bus_release_resource(dev, sc->reg);
return (ENXIO);
}
if (EVENTHANDLER_REGISTER(power_resume, acpi_timer_resume_handler,
sc, EVENTHANDLER_PRI_LAST) == NULL) {
device_printf(dev, "failed to register resume event handler\n");
EVENTHANDLER_DEREGISTER(power_suspend, eh);
bus_release_resource(dev, sc->reg);
return (ENXIO);
}
if (AcpiGbl_FADT.Flags & ACPI_FADT_32BIT_TIMER)
acpi_timer_timecounter.tc_counter_mask = 0xffffffff;
else
acpi_timer_timecounter.tc_counter_mask = 0x00ffffff;
acpi_timer_timecounter.tc_frequency = ACPI_TIMER_FREQUENCY;
acpi_timer_timecounter.tc_priv = sc;
sc->tc = &acpi_timer_timecounter;
tc_init(sc->tc);
SYSCTL_ADD_PROC(NULL, SYSCTL_STATIC_CHILDREN(_machdep), OID_AUTO,
"acpi_timer_freq", CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE,
sc->tc, 0, acpi_timer_sysctl_freq, "I", "ACPI timer frequency");
return (0);
}
static void
acpi_timer_resume_handler(struct acpi_timer_softc *sc, enum power_stype stype)
{
struct timecounter *newtc, *tc;
tc = timecounter;
newtc = sc->old_tc;
sc->old_tc = NULL;
if (newtc != NULL && tc != newtc) {
if (bootverbose)
device_printf(sc->dev,
"restoring timecounter, %s -> %s\n",
tc->tc_name, newtc->tc_name);
(void)newtc->tc_get_timecount(newtc);
timecounter = newtc;
}
}
static void
acpi_timer_suspend_handler(struct acpi_timer_softc *sc, enum power_stype stype)
{
struct timecounter *tc;
tc = timecounter;
sc->old_tc = NULL;
if ((tc->tc_flags & TC_FLAGS_SUSPEND_SAFE) != 0) {
/*
* If we are using a suspend safe timecounter, don't
* save/restore it across suspend/resume.
*/
return;
}
/*
* Our timecounter is suspend safe, so must not be currently
* active.
*/
MPASS(tc != sc->tc);
if (bootverbose)
device_printf(sc->dev, "switching timecounter, %s -> %s\n",
tc->tc_name, sc->tc->tc_name);
(void)acpi_timer_read(sc);
(void)acpi_timer_read(sc);
timecounter = sc->tc;
sc->old_tc = tc;
}
static u_int
acpi_timer_get_timecount(struct timecounter *tc)
{
struct acpi_timer_softc *sc = tc->tc_priv;
return (acpi_timer_read(sc));
}
/*
* Timecounter frequency adjustment interface.
*/
static int
acpi_timer_sysctl_freq(SYSCTL_HANDLER_ARGS)
{
struct timecounter *tc = arg1;
int error;
u_int freq;
freq = tc->tc_frequency;
error = sysctl_handle_int(oidp, &freq, 0, req);
if (error == 0 && req->newptr != NULL) {
tc->tc_frequency = freq;
}
return (error);
}