Changeset View
Changeset View
Standalone View
Standalone View
sys/x86/isa/atrtc.c
Show All 35 Lines | |||||
#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/mutex_irql.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 | #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 <contrib/dev/acpica/include/accommon.h> | ||||
#include <dev/acpica/acpivar.h> | #include <dev/acpica/acpivar.h> | ||||
#include <machine/md_var.h> | #include <machine/md_var.h> | ||||
#endif | #endif | ||||
/* tunable to configure lock below to use irq-level instead of disable-int */ | |||||
static int atrtc_use_high_irql = 0; | |||||
SYSCTL_INT(_machdep, OID_AUTO, atrtc_use_high_irql, CTLFLAG_RDTUN, | |||||
&atrtc_use_high_irql, 0, "lock with high irql instead of disabling interupts" ); | |||||
/* | /* | ||||
* 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_un atrtc_lock; | ||||
MTX_SYSINIT(atrtc_lock_init, &atrtc_lock, "atrtc", MTX_SPIN); | |||||
/** | |||||
* init and cleanup routines for rtc (spin-)lock. this is done with dedicated | |||||
* routines to read tuneable for it | |||||
*/ | |||||
static void atrtc_lock_init(const void* udata) | |||||
{ | |||||
int irql = (atrtc_use_high_irql == 0) ? 0 : MTX_IRQL_RT_LEVEL; | |||||
mtx_init_un(&atrtc_lock, "atrtc", irql); | |||||
} | |||||
SYSINIT(atcrtc_lock_un_sysinit, SI_SUB_LOCK, SI_ORDER_MIDDLE, atrtc_lock_init, 0); | |||||
static void atrtc_lock_destroy(const void* udata) | |||||
{ | |||||
mtx_destroy_un(&atrtc_lock); | |||||
} | |||||
SYSUNINIT(atrtc_lock_un_sysuninit, SI_SUB_LOCK, SI_ORDER_MIDDLE, atrtc_lock_destroy, 0); | |||||
/* Force RTC enabled/disabled. */ | /* Force RTC enabled/disabled. */ | ||||
static int atrtc_enabled = -1; | static int atrtc_enabled = -1; | ||||
TUNABLE_INT("hw.atrtc.enabled", &atrtc_enabled); | TUNABLE_INT("hw.atrtc.enabled", &atrtc_enabled); | ||||
struct mtx atrtc_time_lock; | struct mtx atrtc_time_lock; | ||||
MTX_SYSINIT(atrtc_time_lock_init, &atrtc_time_lock, "atrtc_time", MTX_DEF); | MTX_SYSINIT(atrtc_time_lock_init, &atrtc_time_lock, "atrtc_time", MTX_DEF); | ||||
/* some global constants */ | |||||
int atrtcclock_disable = 0; | int atrtcclock_disable = 0; | ||||
static int rtc_reg = -1; | static int rtc_reg = -1; | ||||
static u_char rtc_statusa = RTCSA_DIVIDER | RTCSA_NOPROF; | static u_char rtc_statusa = RTCSA_DIVIDER | RTCSA_NOPROF; | ||||
static u_char rtc_statusb = RTCSB_24HR; | static u_char rtc_statusb = RTCSB_24HR; | ||||
#ifdef DEV_ACPI | #ifdef DEV_ACPI | ||||
#define _COMPONENT ACPI_TIMER | #define _COMPONENT ACPI_TIMER | ||||
Show All 31 Lines | rtcout_locked(int reg, u_char val) | ||||
inb(0x84); | inb(0x84); | ||||
} | } | ||||
int | int | ||||
rtcin(int reg) | rtcin(int reg) | ||||
{ | { | ||||
u_char val; | u_char val; | ||||
mtx_lock_spin(&atrtc_lock); | mtx_lock_un(&atrtc_lock); | ||||
val = rtcin_locked(reg); | val = rtcin_locked(reg); | ||||
mtx_unlock_spin(&atrtc_lock); | mtx_unlock_un(&atrtc_lock); | ||||
return (val); | return (val); | ||||
} | } | ||||
void | void | ||||
writertc(int reg, u_char val) | writertc(int reg, u_char val) | ||||
{ | { | ||||
mtx_lock_spin(&atrtc_lock); | mtx_lock_un(&atrtc_lock); | ||||
rtcout_locked(reg, val); | rtcout_locked(reg, val); | ||||
mtx_unlock_spin(&atrtc_lock); | mtx_unlock_un(&atrtc_lock); | ||||
} | } | ||||
static void | static void | ||||
atrtc_start(void) | atrtc_start(void) | ||||
{ | { | ||||
mtx_lock_spin(&atrtc_lock); | mtx_lock_un(&atrtc_lock); | ||||
rtcout_locked(RTC_STATUSA, rtc_statusa); | rtcout_locked(RTC_STATUSA, rtc_statusa); | ||||
rtcout_locked(RTC_STATUSB, RTCSB_24HR); | rtcout_locked(RTC_STATUSB, RTCSB_24HR); | ||||
mtx_unlock_spin(&atrtc_lock); | mtx_unlock_un(&atrtc_lock); | ||||
} | } | ||||
static void | static void | ||||
atrtc_rate(unsigned rate) | atrtc_rate(unsigned rate) | ||||
{ | { | ||||
rtc_statusa = RTCSA_DIVIDER | rate; | rtc_statusa = RTCSA_DIVIDER | rate; | ||||
writertc(RTC_STATUSA, rtc_statusa); | writertc(RTC_STATUSA, rtc_statusa); | ||||
} | } | ||||
static void | static void | ||||
atrtc_enable_intr(void) | atrtc_enable_intr(void) | ||||
{ | { | ||||
rtc_statusb |= RTCSB_PINTR; | rtc_statusb |= RTCSB_PINTR; | ||||
mtx_lock_spin(&atrtc_lock); | mtx_lock_un(&atrtc_lock); | ||||
rtcout_locked(RTC_STATUSB, rtc_statusb); | rtcout_locked(RTC_STATUSB, rtc_statusb); | ||||
rtcin_locked(RTC_INTR); | rtcin_locked(RTC_INTR); | ||||
mtx_unlock_spin(&atrtc_lock); | mtx_unlock_un(&atrtc_lock); | ||||
} | } | ||||
static void | static void | ||||
atrtc_disable_intr(void) | atrtc_disable_intr(void) | ||||
{ | { | ||||
rtc_statusb &= ~RTCSB_PINTR; | rtc_statusb &= ~RTCSB_PINTR; | ||||
mtx_lock_spin(&atrtc_lock); | mtx_lock_un(&atrtc_lock); | ||||
rtcout_locked(RTC_STATUSB, rtc_statusb); | rtcout_locked(RTC_STATUSB, rtc_statusb); | ||||
rtcin_locked(RTC_INTR); | rtcin_locked(RTC_INTR); | ||||
mtx_unlock_spin(&atrtc_lock); | mtx_unlock_un(&atrtc_lock); | ||||
} | } | ||||
void | void | ||||
atrtc_restore(void) | atrtc_restore(void) | ||||
{ | { | ||||
/* Restore all of the RTC's "status" (actually, control) registers. */ | /* Restore all of the RTC's "status" (actually, control) registers. */ | ||||
mtx_lock_spin(&atrtc_lock); | mtx_lock_un(&atrtc_lock); | ||||
rtcin_locked(RTC_STATUSA); /* dummy to get rtc_reg set */ | rtcin_locked(RTC_STATUSA); /* dummy to get rtc_reg set */ | ||||
rtcout_locked(RTC_STATUSB, RTCSB_24HR); | rtcout_locked(RTC_STATUSB, RTCSB_24HR); | ||||
rtcout_locked(RTC_STATUSA, rtc_statusa); | rtcout_locked(RTC_STATUSA, rtc_statusa); | ||||
rtcout_locked(RTC_STATUSB, rtc_statusb); | rtcout_locked(RTC_STATUSB, rtc_statusb); | ||||
rtcin_locked(RTC_INTR); | rtcin_locked(RTC_INTR); | ||||
mtx_unlock_spin(&atrtc_lock); | mtx_unlock_un(&atrtc_lock); | ||||
} | } | ||||
/********************************************************************** | /********************************************************************** | ||||
* RTC driver for subr_rtc | * RTC driver for subr_rtc | ||||
*/ | */ | ||||
struct atrtc_softc { | struct atrtc_softc { | ||||
int port_rid, intr_rid; | int port_rid, intr_rid; | ||||
▲ Show 20 Lines • Show All 328 Lines • ▼ Show 20 Lines | |||||
atrtc_settime(device_t dev __unused, struct timespec *ts) | atrtc_settime(device_t dev __unused, struct timespec *ts) | ||||
{ | { | ||||
struct bcd_clocktime bct; | struct bcd_clocktime bct; | ||||
clock_ts_to_bcd(ts, &bct, false); | clock_ts_to_bcd(ts, &bct, false); | ||||
clock_dbgprint_bcd(dev, CLOCK_DBG_WRITE, &bct); | clock_dbgprint_bcd(dev, CLOCK_DBG_WRITE, &bct); | ||||
mtx_lock(&atrtc_time_lock); | mtx_lock(&atrtc_time_lock); | ||||
mtx_lock_spin(&atrtc_lock); | mtx_lock_un(&atrtc_lock); | ||||
/* Disable RTC updates and interrupts. */ | /* Disable RTC updates and interrupts. */ | ||||
rtcout_locked(RTC_STATUSB, RTCSB_HALT | RTCSB_24HR); | rtcout_locked(RTC_STATUSB, RTCSB_HALT | RTCSB_24HR); | ||||
/* Write all the time registers. */ | /* Write all the time registers. */ | ||||
rtcout_locked(RTC_SEC, bct.sec); | rtcout_locked(RTC_SEC, bct.sec); | ||||
rtcout_locked(RTC_MIN, bct.min); | rtcout_locked(RTC_MIN, bct.min); | ||||
rtcout_locked(RTC_HRS, bct.hour); | rtcout_locked(RTC_HRS, bct.hour); | ||||
rtcout_locked(RTC_WDAY, bct.dow + 1); | rtcout_locked(RTC_WDAY, bct.dow + 1); | ||||
rtcout_locked(RTC_DAY, bct.day); | rtcout_locked(RTC_DAY, bct.day); | ||||
rtcout_locked(RTC_MONTH, bct.mon); | rtcout_locked(RTC_MONTH, bct.mon); | ||||
rtcout_locked(RTC_YEAR, bct.year & 0xff); | rtcout_locked(RTC_YEAR, bct.year & 0xff); | ||||
#ifdef USE_RTC_CENTURY | #ifdef USE_RTC_CENTURY | ||||
rtcout_locked(RTC_CENTURY, bct.year >> 8); | rtcout_locked(RTC_CENTURY, bct.year >> 8); | ||||
#endif | #endif | ||||
/* | /* | ||||
* Re-enable RTC updates and interrupts. | * Re-enable RTC updates and interrupts. | ||||
*/ | */ | ||||
rtcout_locked(RTC_STATUSB, rtc_statusb); | rtcout_locked(RTC_STATUSB, rtc_statusb); | ||||
rtcin_locked(RTC_INTR); | rtcin_locked(RTC_INTR); | ||||
mtx_unlock_spin(&atrtc_lock); | mtx_unlock_un(&atrtc_lock); | ||||
mtx_unlock(&atrtc_time_lock); | mtx_unlock(&atrtc_time_lock); | ||||
return (0); | return (0); | ||||
} | } | ||||
static int | static int | ||||
atrtc_gettime(device_t dev, struct timespec *ts) | atrtc_gettime(device_t dev, struct timespec *ts) | ||||
{ | { | ||||
Show All 10 Lines | atrtc_gettime(device_t dev, struct timespec *ts) | ||||
* If RTCSA_TUP is zero, we have at least 244us before next update. | * If RTCSA_TUP is zero, we have at least 244us before next update. | ||||
* This is fast enough on most hardware, but a refinement would be | * This is fast enough on most hardware, but a refinement would be | ||||
* to make sure that no more than 240us pass after we start reading, | * to make sure that no more than 240us pass after we start reading, | ||||
* and try again if so. | * and try again if so. | ||||
*/ | */ | ||||
mtx_lock(&atrtc_time_lock); | mtx_lock(&atrtc_time_lock); | ||||
while (rtcin(RTC_STATUSA) & RTCSA_TUP) | while (rtcin(RTC_STATUSA) & RTCSA_TUP) | ||||
continue; | continue; | ||||
mtx_lock_spin(&atrtc_lock); | mtx_lock_un(&atrtc_lock); | ||||
bct.sec = rtcin_locked(RTC_SEC); | bct.sec = rtcin_locked(RTC_SEC); | ||||
bct.min = rtcin_locked(RTC_MIN); | bct.min = rtcin_locked(RTC_MIN); | ||||
bct.hour = rtcin_locked(RTC_HRS); | bct.hour = rtcin_locked(RTC_HRS); | ||||
bct.day = rtcin_locked(RTC_DAY); | bct.day = rtcin_locked(RTC_DAY); | ||||
bct.mon = rtcin_locked(RTC_MONTH); | bct.mon = rtcin_locked(RTC_MONTH); | ||||
bct.year = rtcin_locked(RTC_YEAR); | bct.year = rtcin_locked(RTC_YEAR); | ||||
#ifdef USE_RTC_CENTURY | #ifdef USE_RTC_CENTURY | ||||
bct.year |= rtcin_locked(RTC_CENTURY) << 8; | bct.year |= rtcin_locked(RTC_CENTURY) << 8; | ||||
#endif | #endif | ||||
mtx_unlock_spin(&atrtc_lock); | mtx_unlock_un(&atrtc_lock); | ||||
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)); | ||||
} | } | ||||
▲ Show 20 Lines • Show All 53 Lines • Show Last 20 Lines |