Fix a few minor issues based on a bug report and reading over the HPET

spec:
- Use read/modify/write cycles to enable and disable the HPET instead of
  writing 0 to reserved bits.
- Shutdown the HPET during suspend as encouraged by the spec.
- Fail to attach to an HPET with a period of zero.

MFC after:	1 week
PR:		kern/119675 [3]
Reported by:	Leo Bicknell | bicknell ufp.org
This commit is contained in:
John Baldwin 2008-01-15 18:50:47 +00:00
parent dd8c2454a8
commit 572f347d9f
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=175361

View file

@ -82,6 +82,24 @@ hpet_get_timecount(struct timecounter *tc)
return (bus_read_4(sc->mem_res, HPET_OFFSET_VALUE)); return (bus_read_4(sc->mem_res, HPET_OFFSET_VALUE));
} }
static void
hpet_enable(struct acpi_hpet_softc *sc)
{
uint32_t val;
val = bus_read_4(sc->mem_res, HPET_OFFSET_ENABLE);
bus_write_4(sc->mem_res, HPET_OFFSET_ENABLE, val | 1);
}
static void
hpet_disable(struct acpi_hpet_softc *sc)
{
uint32_t val;
val = bus_read_4(sc->mem_res, HPET_OFFSET_ENABLE);
bus_write_4(sc->mem_res, HPET_OFFSET_ENABLE, val & ~1);
}
/* Discover the HPET via the ACPI table of the same name. */ /* Discover the HPET via the ACPI table of the same name. */
static void static void
acpi_hpet_identify(driver_t *driver, device_t parent) acpi_hpet_identify(driver_t *driver, device_t parent)
@ -166,10 +184,17 @@ acpi_hpet_attach(device_t dev)
} }
/* Be sure timer is enabled. */ /* Be sure timer is enabled. */
bus_write_4(sc->mem_res, HPET_OFFSET_ENABLE, 1); hpet_enable(sc);
/* Read basic statistics about the timer. */ /* Read basic statistics about the timer. */
val = bus_read_4(sc->mem_res, HPET_OFFSET_PERIOD); val = bus_read_4(sc->mem_res, HPET_OFFSET_PERIOD);
if (val == 0) {
device_printf(dev, "invalid period\n");
hpet_disable(sc);
bus_free_resource(dev, SYS_RES_MEMORY, sc->mem_res);
return (ENXIO);
}
freq = (1000000000000000LL + val / 2) / val; freq = (1000000000000000LL + val / 2) / val;
if (bootverbose) { if (bootverbose) {
val = bus_read_4(sc->mem_res, HPET_OFFSET_INFO); val = bus_read_4(sc->mem_res, HPET_OFFSET_INFO);
@ -192,7 +217,7 @@ acpi_hpet_attach(device_t dev)
val2 = bus_read_4(sc->mem_res, HPET_OFFSET_VALUE); val2 = bus_read_4(sc->mem_res, HPET_OFFSET_VALUE);
if (val == val2) { if (val == val2) {
device_printf(dev, "HPET never increments, disabling\n"); device_printf(dev, "HPET never increments, disabling\n");
bus_write_4(sc->mem_res, HPET_OFFSET_ENABLE, 0); hpet_disable(sc);
bus_free_resource(dev, SYS_RES_MEMORY, sc->mem_res); bus_free_resource(dev, SYS_RES_MEMORY, sc->mem_res);
return (ENXIO); return (ENXIO);
} }
@ -213,6 +238,22 @@ acpi_hpet_detach(device_t dev)
return (EBUSY); return (EBUSY);
} }
static int
acpi_hpet_suspend(device_t dev)
{
struct acpi_hpet_softc *sc;
/*
* Disable the timer during suspend. The timer will not lose
* its state in S1 or S2, but we are required to disable
* it.
*/
sc = device_get_softc(dev);
hpet_disable(sc);
return (0);
}
static int static int
acpi_hpet_resume(device_t dev) acpi_hpet_resume(device_t dev)
{ {
@ -220,7 +261,7 @@ acpi_hpet_resume(device_t dev)
/* Re-enable the timer after a resume to keep the clock advancing. */ /* Re-enable the timer after a resume to keep the clock advancing. */
sc = device_get_softc(dev); sc = device_get_softc(dev);
bus_write_4(sc->mem_res, HPET_OFFSET_ENABLE, 1); hpet_enable(sc);
return (0); return (0);
} }
@ -260,6 +301,7 @@ static device_method_t acpi_hpet_methods[] = {
DEVMETHOD(device_probe, acpi_hpet_probe), DEVMETHOD(device_probe, acpi_hpet_probe),
DEVMETHOD(device_attach, acpi_hpet_attach), DEVMETHOD(device_attach, acpi_hpet_attach),
DEVMETHOD(device_detach, acpi_hpet_detach), DEVMETHOD(device_detach, acpi_hpet_detach),
DEVMETHOD(device_suspend, acpi_hpet_suspend),
DEVMETHOD(device_resume, acpi_hpet_resume), DEVMETHOD(device_resume, acpi_hpet_resume),
{0, 0} {0, 0}