Changeset View
Standalone View
sys/x86/isa/atrtc.c
Show All 26 Lines | |||||
* SUCH DAMAGE. | * SUCH DAMAGE. | ||||
* | * | ||||
* $FreeBSD$ | * $FreeBSD$ | ||||
*/ | */ | ||||
#include <sys/cdefs.h> | #include <sys/cdefs.h> | ||||
__FBSDID("$FreeBSD$"); | __FBSDID("$FreeBSD$"); | ||||
#include "opt_acpi.h" | |||||
#include "opt_isa.h" | #include "opt_isa.h" | ||||
#include <sys/param.h> | #include <sys/param.h> | ||||
#include <sys/systm.h> | #include <sys/systm.h> | ||||
#include <sys/bus.h> | #include <sys/bus.h> | ||||
#include <sys/clock.h> | #include <sys/clock.h> | ||||
#include <sys/lock.h> | #include <sys/lock.h> | ||||
#include <sys/mutex.h> | #include <sys/mutex.h> | ||||
#include <sys/kdb.h> | #include <sys/kdb.h> | ||||
#include <sys/kernel.h> | #include <sys/kernel.h> | ||||
#include <sys/module.h> | #include <sys/module.h> | ||||
#include <sys/proc.h> | #include <sys/proc.h> | ||||
#include <sys/rman.h> | #include <sys/rman.h> | ||||
#include <sys/timeet.h> | #include <sys/timeet.h> | ||||
#include <isa/rtc.h> | #include <isa/rtc.h> | ||||
#ifdef DEV_ISA | #ifdef DEV_ISA | ||||
#include <isa/isareg.h> | #include <isa/isareg.h> | ||||
#include <isa/isavar.h> | #include <isa/isavar.h> | ||||
#endif | #endif | ||||
#include <machine/intr_machdep.h> | #include <machine/intr_machdep.h> | ||||
#include "clock_if.h" | #include "clock_if.h" | ||||
#ifdef DEV_ACPI | |||||
#include <contrib/dev/acpica/include/acpi.h> | #include <contrib/dev/acpica/include/acpi.h> | ||||
#include <contrib/dev/acpica/include/accommon.h> | |||||
#include <dev/acpica/acpivar.h> | |||||
#include <machine/md_var.h> | #include <machine/md_var.h> | ||||
#endif | |||||
/* | /* | ||||
* atrtc_lock protects low-level access to individual hardware registers. | * atrtc_lock protects low-level access to individual hardware registers. | ||||
* atrtc_time_lock protects the entire sequence of accessing multiple registers | * atrtc_time_lock protects the entire sequence of accessing multiple registers | ||||
* to read or write the date and time. | * to read or write the date and time. | ||||
*/ | */ | ||||
static struct mtx atrtc_lock; | static struct mtx atrtc_lock; | ||||
MTX_SYSINIT(atrtc_lock_init, &atrtc_lock, "atrtc", MTX_SPIN); | MTX_SYSINIT(atrtc_lock_init, &atrtc_lock, "atrtc", MTX_SPIN); | ||||
▲ Show 20 Lines • Show All 121 Lines • ▼ Show 20 Lines | |||||
*/ | */ | ||||
struct atrtc_softc { | struct atrtc_softc { | ||||
int port_rid, intr_rid; | int port_rid, intr_rid; | ||||
struct resource *port_res; | struct resource *port_res; | ||||
struct resource *intr_res; | struct resource *intr_res; | ||||
void *intr_handler; | void *intr_handler; | ||||
struct eventtimer et; | struct eventtimer et; | ||||
#ifdef DEV_ACPI | |||||
ACPI_HANDLE acpi_handle; | |||||
#endif | |||||
}; | }; | ||||
static int | static int | ||||
rtc_start(struct eventtimer *et, sbintime_t first, sbintime_t period) | rtc_start(struct eventtimer *et, sbintime_t first, sbintime_t period) | ||||
{ | { | ||||
atrtc_rate(max(fls(period + (period >> 1)) - 17, 1)); | atrtc_rate(max(fls(period + (period >> 1)) - 17, 1)); | ||||
atrtc_enable_intr(); | atrtc_enable_intr(); | ||||
Show All 38 Lines | rtc_intr(void *arg) | ||||
while (rtcin(RTC_INTR) & RTCIR_PERIOD) { | while (rtcin(RTC_INTR) & RTCIR_PERIOD) { | ||||
flag = 1; | flag = 1; | ||||
if (sc->et.et_active) | if (sc->et.et_active) | ||||
sc->et.et_event_cb(&sc->et, sc->et.et_arg); | sc->et.et_event_cb(&sc->et, sc->et.et_arg); | ||||
} | } | ||||
return(flag ? FILTER_HANDLED : FILTER_STRAY); | return(flag ? FILTER_HANDLED : FILTER_STRAY); | ||||
} | } | ||||
#ifdef DEV_ACPI | |||||
/* | /* | ||||
* ACPI RTC CMOS address space handler | |||||
*/ | |||||
#define ATRTC_LAST_REG 0x40 | |||||
static void | |||||
rtcin_region(int reg, void *buf, int len) | |||||
{ | |||||
u_char *ptr = buf; | |||||
while (len-- > 0) | |||||
*ptr++ = rtcin(reg++) & 0xff; | |||||
} | |||||
ian: This probably should be...
```
mtx_lock_spin(&atrtc_lock);
while (len-- > 0)
*ptr++ =… | |||||
wulfAuthorUnsubmitted Done Inline ActionsLocking is intentionally de-optimized to raise interrupt handler and ntpd priorities. ACPI is a second class citizen here. wulf: Locking is intentionally de-optimized to raise interrupt handler and ntpd priorities. ACPI is a… | |||||
static void | |||||
rtcout_region(int reg, const void *buf, int len) | |||||
{ | |||||
const u_char *ptr = buf; | |||||
while (len-- > 0) | |||||
writertc(reg++, *ptr++); | |||||
} | |||||
static bool | |||||
atrtc_check_cmos_access(bool is_read, ACPI_PHYSICAL_ADDRESS addr, UINT32 len) | |||||
{ | |||||
/* Block address space wrapping on out-of-bound access */ | |||||
if (addr >= ATRTC_LAST_REG || addr + len > ATRTC_LAST_REG) | |||||
return (false); | |||||
if (is_read) { | |||||
/* Reading 0x0C will muck with interrupts */ | |||||
if (addr <= RTC_INTR && addr + len > RTC_INTR) | |||||
return (false); | |||||
} else { | |||||
/* | |||||
* Allow single-byte writes to alarm registers and | |||||
* multi-byte writes to addr >= 0x30, else deny. | |||||
*/ | |||||
if (!((len == 1 && (addr == RTC_SECALRM || | |||||
addr == RTC_MINALRM || | |||||
addr == RTC_HRSALRM)) || | |||||
addr >= 0x30)) | |||||
return (false); | |||||
} | |||||
return (true); | |||||
} | |||||
static ACPI_STATUS | |||||
atrtc_acpi_cmos_handler(UINT32 func, ACPI_PHYSICAL_ADDRESS addr, | |||||
UINT32 bitwidth, UINT64 *value, void *context, void *region_context) | |||||
{ | |||||
device_t dev = context; | |||||
UINT32 bytewidth = howmany(bitwidth, 8); | |||||
bool is_read = func == ACPI_READ; | |||||
/* ACPICA is very verbose on CMOS handler failures, so we, too */ | |||||
#define CMOS_HANDLER_ERR(fmt, ...) \ | |||||
device_printf(dev, "ACPI [SystemCMOS] handler: " fmt, ##__VA_ARGS__) | |||||
ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); | |||||
if (value == NULL) { | |||||
CMOS_HANDLER_ERR("NULL parameter\n"); | |||||
return (AE_BAD_PARAMETER); | |||||
} | |||||
if (bitwidth == 0 || bitwidth > 32 || (bitwidth & 0x07) != 0) { | |||||
ianUnsubmitted Not Done Inline ActionsWhy does this check bitwidth > 32? That seems to arbitrarily limit cmos access to a max of 4 bytes at a time. The linux driver cited in the comments with this change doens't have any such check. (FWIW, the bios's access to the cmos at susupend/resume time is likely limited to reading a single byte containing the century number to extend the 2-digit value in the rtc regs.) ian: Why does this check bitwidth > 32? That seems to arbitrarily limit cmos access to a max of 4… | |||||
wulfAuthorUnsubmitted Done Inline ActionsHmmm. I followed all the discussions on this patch on acpi@ list threads dated back to 2014-2015 and was not able find exact origin of this limit. Fortunately original author is subscribed to this review so I can forward this question to him: Hi Anthony, is it necessary to check "bitwidth > 32" here? What is the reason behind this? wulf: Hmmm. I followed all the discussions on this patch on acpi@ list threads dated back to 2014… | |||||
Scoobi_doo_yahoo.comUnsubmitted Done Inline ActionsI honestly don't recall...vaguely remember seeing a 32-bit limit somewhere (I think I had originally put it in to simplify debug printf()s), but if there is no technical reason for a limit, then I'm happy to lose it. Scoobi_doo_yahoo.com: I honestly don't recall...vaguely remember seeing a 32-bit limit //somewhere// (I //think// I… | |||||
CMOS_HANDLER_ERR("Invalid bitwidth: %u\n", bitwidth); | |||||
return (AE_BAD_PARAMETER); | |||||
} | |||||
if (!atrtc_check_cmos_access(is_read, addr, bytewidth)) { | |||||
CMOS_HANDLER_ERR("%s access rejected: addr=%#jx, len=%u\n", | |||||
is_read ? "Read" : "Write", (uintmax_t)addr, bytewidth); | |||||
return (AE_BAD_PARAMETER); | |||||
} | |||||
switch (func) { | |||||
case ACPI_READ: | |||||
rtcin_region(addr, value, bytewidth); | |||||
break; | |||||
case ACPI_WRITE: | |||||
rtcout_region(addr, value, bytewidth); | |||||
break; | |||||
default: | |||||
CMOS_HANDLER_ERR("Invalid function: %u\n", func); | |||||
return (AE_BAD_PARAMETER); | |||||
} | |||||
ACPI_VPRINT(dev, acpi_device_get_parent_softc(dev), | |||||
"ACPI RTC CMOS %s access: addr=%#04x, len=%u, val=%*D\n", | |||||
is_read ? "read" : "write", (unsigned)addr, bytewidth, | |||||
bytewidth, value, " "); | |||||
return (AE_OK); | |||||
} | |||||
static int | |||||
atrtc_reg_acpi_cmos_handler(device_t dev) | |||||
{ | |||||
struct atrtc_softc *sc = device_get_softc(dev); | |||||
ACPI_FUNCTION_TRACE((char *)(uintptr_t) __func__); | |||||
/* Don't handle address space events if driver is disabled. */ | |||||
if (acpi_disabled("atrtc")) | |||||
return (ENXIO); | |||||
sc->acpi_handle = acpi_get_handle(dev); | |||||
if (sc->acpi_handle == NULL || | |||||
ACPI_FAILURE(AcpiInstallAddressSpaceHandler(sc->acpi_handle, | |||||
ACPI_ADR_SPACE_CMOS, atrtc_acpi_cmos_handler, NULL, dev))) { | |||||
sc->acpi_handle = NULL; | |||||
device_printf(dev, | |||||
"Can't register ACPI CMOS address space handler\n"); | |||||
return (ENXIO); | |||||
} | |||||
return (0); | |||||
} | |||||
static int | |||||
atrtc_unreg_acpi_cmos_handler(device_t dev) | |||||
{ | |||||
struct atrtc_softc *sc = device_get_softc(dev); | |||||
ACPI_FUNCTION_TRACE((char *)(uintptr_t) __func__); | |||||
if (sc->acpi_handle != NULL) | |||||
AcpiRemoveAddressSpaceHandler(sc->acpi_handle, | |||||
ACPI_ADR_SPACE_CMOS, atrtc_acpi_cmos_handler); | |||||
return (0); | |||||
} | |||||
#endif /* DEV_ACPI */ | |||||
/* | |||||
* Attach to the ISA PnP descriptors for the timer and realtime clock. | * Attach to the ISA PnP descriptors for the timer and realtime clock. | ||||
*/ | */ | ||||
static struct isa_pnp_id atrtc_ids[] = { | static struct isa_pnp_id atrtc_ids[] = { | ||||
{ 0x000bd041 /* PNP0B00 */, "AT realtime clock" }, | { 0x000bd041 /* PNP0B00 */, "AT realtime clock" }, | ||||
{ 0 } | { 0 } | ||||
}; | }; | ||||
static bool | static bool | ||||
atrtc_acpi_disabled(void) | atrtc_acpi_disabled(void) | ||||
{ | { | ||||
#ifdef DEV_ACPI | |||||
uint16_t flags; | uint16_t flags; | ||||
if (!acpi_get_fadt_bootflags(&flags)) | if (!acpi_get_fadt_bootflags(&flags)) | ||||
return (false); | return (false); | ||||
return ((flags & ACPI_FADT_NO_CMOS_RTC) != 0); | return ((flags & ACPI_FADT_NO_CMOS_RTC) != 0); | ||||
return (true); | #else | ||||
return (false); | |||||
#endif | |||||
} | } | ||||
static int | static int | ||||
atrtc_probe(device_t dev) | atrtc_probe(device_t dev) | ||||
{ | { | ||||
int result; | int result; | ||||
if ((atrtc_enabled == -1 && atrtc_acpi_disabled()) || | if ((atrtc_enabled == -1 && atrtc_acpi_disabled()) || | ||||
▲ Show 20 Lines • Show All 54 Lines • ▼ Show 20 Lines | if (!atrtcclock_disable && | ||||
sc->et.et_stop = rtc_stop; | sc->et.et_stop = rtc_stop; | ||||
sc->et.et_priv = dev; | sc->et.et_priv = dev; | ||||
et_register(&sc->et); | et_register(&sc->et); | ||||
} | } | ||||
return(0); | return(0); | ||||
} | } | ||||
static int | static int | ||||
atrtc_isa_attach(device_t dev) | |||||
{ | |||||
return (atrtc_attach(dev)); | |||||
} | |||||
#ifdef DEV_ACPI | |||||
static int | |||||
atrtc_acpi_attach(device_t dev) | |||||
{ | |||||
int ret; | |||||
ret = atrtc_attach(dev); | |||||
if (ret) | |||||
return (ret); | |||||
(void)atrtc_reg_acpi_cmos_handler(dev); | |||||
return (0); | |||||
} | |||||
static int | |||||
atrtc_acpi_detach(device_t dev) | |||||
{ | |||||
(void)atrtc_unreg_acpi_cmos_handler(dev); | |||||
return (0); | |||||
} | |||||
#endif /* DEV_ACPI */ | |||||
static int | |||||
atrtc_resume(device_t dev) | atrtc_resume(device_t dev) | ||||
{ | { | ||||
atrtc_restore(); | atrtc_restore(); | ||||
return(0); | return(0); | ||||
} | } | ||||
static int | static int | ||||
▲ Show 20 Lines • Show All 69 Lines • ▼ Show 20 Lines | #endif | ||||
mtx_unlock(&atrtc_time_lock); | mtx_unlock(&atrtc_time_lock); | ||||
/* dow is unused in timespec conversion and we have no nsec info. */ | /* dow is unused in timespec conversion and we have no nsec info. */ | ||||
bct.dow = 0; | bct.dow = 0; | ||||
bct.nsec = 0; | bct.nsec = 0; | ||||
clock_dbgprint_bcd(dev, CLOCK_DBG_READ, &bct); | clock_dbgprint_bcd(dev, CLOCK_DBG_READ, &bct); | ||||
return (clock_bcd_to_ts(&bct, ts, false)); | return (clock_bcd_to_ts(&bct, ts, false)); | ||||
} | } | ||||
static device_method_t atrtc_methods[] = { | static device_method_t atrtc_isa_methods[] = { | ||||
/* Device interface */ | /* Device interface */ | ||||
DEVMETHOD(device_probe, atrtc_probe), | DEVMETHOD(device_probe, atrtc_probe), | ||||
DEVMETHOD(device_attach, atrtc_attach), | DEVMETHOD(device_attach, atrtc_isa_attach), | ||||
DEVMETHOD(device_detach, bus_generic_detach), | DEVMETHOD(device_detach, bus_generic_detach), | ||||
DEVMETHOD(device_shutdown, bus_generic_shutdown), | DEVMETHOD(device_shutdown, bus_generic_shutdown), | ||||
DEVMETHOD(device_suspend, bus_generic_suspend), | DEVMETHOD(device_suspend, bus_generic_suspend), | ||||
/* XXX stop statclock? */ | /* XXX stop statclock? */ | ||||
DEVMETHOD(device_resume, atrtc_resume), | DEVMETHOD(device_resume, atrtc_resume), | ||||
/* clock interface */ | /* clock interface */ | ||||
DEVMETHOD(clock_gettime, atrtc_gettime), | DEVMETHOD(clock_gettime, atrtc_gettime), | ||||
DEVMETHOD(clock_settime, atrtc_settime), | DEVMETHOD(clock_settime, atrtc_settime), | ||||
{ 0, 0 } | { 0, 0 } | ||||
}; | }; | ||||
static driver_t atrtc_driver = { | static driver_t atrtc_isa_driver = { | ||||
"atrtc", | "atrtc", | ||||
atrtc_methods, | atrtc_isa_methods, | ||||
sizeof(struct atrtc_softc), | sizeof(struct atrtc_softc), | ||||
}; | }; | ||||
#ifdef DEV_ACPI | |||||
static device_method_t atrtc_acpi_methods[] = { | |||||
/* Device interface */ | |||||
DEVMETHOD(device_probe, atrtc_probe), | |||||
DEVMETHOD(device_attach, atrtc_acpi_attach), | |||||
DEVMETHOD(device_detach, atrtc_acpi_detach), | |||||
/* XXX stop statclock? */ | |||||
DEVMETHOD(device_resume, atrtc_resume), | |||||
/* clock interface */ | |||||
DEVMETHOD(clock_gettime, atrtc_gettime), | |||||
DEVMETHOD(clock_settime, atrtc_settime), | |||||
{ 0, 0 } | |||||
}; | |||||
static driver_t atrtc_acpi_driver = { | |||||
"atrtc", | |||||
atrtc_acpi_methods, | |||||
sizeof(struct atrtc_softc), | |||||
}; | |||||
#endif /* DEV_ACPI */ | |||||
static devclass_t atrtc_devclass; | static devclass_t atrtc_devclass; | ||||
DRIVER_MODULE(atrtc, isa, atrtc_driver, atrtc_devclass, 0, 0); | DRIVER_MODULE(atrtc, isa, atrtc_isa_driver, atrtc_devclass, 0, 0); | ||||
DRIVER_MODULE(atrtc, acpi, atrtc_driver, atrtc_devclass, 0, 0); | #ifdef DEV_ACPI | ||||
DRIVER_MODULE(atrtc, acpi, atrtc_acpi_driver, atrtc_devclass, 0, 0); | |||||
#endif | |||||
ISA_PNP_INFO(atrtc_ids); | ISA_PNP_INFO(atrtc_ids); |
This probably should be...
Likewise in rtcout_region() below using rtcout_locked().