#include <sys/types.h>
#include <sys/param.h>
#include <sys/sysmacros.h>
#include <sys/systm.h>
#include <sys/errno.h>
#include <sys/modctl.h>
#include <sys/autoconf.h>
#include <sys/debug.h>
#include <sys/clock.h>
#include <sys/cmn_err.h>
#include <sys/prom_plat.h>
#include <sys/cpuvar.h>
#include <sys/opl_module.h>
#ifdef DEBUG
int todopl_debug = 0;
#define TODOPL_DEBUG(args) if (todopl_debug) cmn_err args
#else
#define TODOPL_DEBUG(args)
#endif
#define abs(x) ((x) < 0 ? -(x) : (x))
#define TODOPL_SET_THRESHOLD 30
static timestruc_t todopl_get(void);
static void todopl_set(timestruc_t);
static uint_t todopl_set_watchdog_timer(uint_t);
static uint_t todopl_clear_watchdog_timer(void);
static void todopl_set_power_alarm(timestruc_t);
static void todopl_clear_power_alarm(void);
static uint64_t todopl_get_cpufrequency(void);
static struct modlmisc modlmisc = {
&mod_miscops, "Soft tod module for OPL 1.11"
};
static struct modlinkage modlinkage = {
MODREV_1, (void *)&modlmisc, NULL
};
static int enable_time_sync = 1;
int
_init(void)
{
int64_t stick;
time_t obp_time = 0;
int64_t obp_stick;
if (strcmp(tod_module_name, "todopl") == 0) {
prom_opl_get_tod(&obp_time, &obp_stick);
TODOPL_DEBUG((CE_NOTE, "todopl: OBP time 0x%lx stick 0x%lx\n",
obp_time, obp_stick));
if (obp_time != 0) {
stick_timestamp(&stick);
obp_time += ((stick - obp_stick) / system_clock_freq);
TODOPL_DEBUG((CE_NOTE,
"todopl: cpu stick 0x%lx sys_time 0x%lx\n",
stick, obp_time));
} else {
enable_time_sync = 0;
cmn_err(CE_WARN, "Initial date is invalid.");
cmn_err(CE_CONT, "Attempting to set the date and time "
"based on the last shutdown.\n");
cmn_err(CE_CONT, "The time could not be synchronized "
"between Domain and Service Processor.\n");
cmn_err(CE_CONT, "Please inspect the date and time and "
"correct if necessary.\n");
}
hrestime.tv_sec = obp_time;
if (TIMESPEC_OVERFLOW(&hrestime)) {
cmn_err(CE_WARN, "Date overflow detected.");
cmn_err(CE_CONT, "Attempting to set the date and time "
"based on the last shutdown.\n");
cmn_err(CE_CONT, "Please inspect the date and time and "
"correct if necessary.\n");
hrestime.tv_sec = (time_t)0;
}
tod_ops.tod_get = todopl_get;
tod_ops.tod_set = todopl_set;
tod_ops.tod_set_watchdog_timer = todopl_set_watchdog_timer;
tod_ops.tod_clear_watchdog_timer = todopl_clear_watchdog_timer;
tod_ops.tod_set_power_alarm = todopl_set_power_alarm;
tod_ops.tod_clear_power_alarm = todopl_clear_power_alarm;
tod_ops.tod_get_cpufrequency = todopl_get_cpufrequency;
if (watchdog_enable) {
cmn_err(CE_WARN, "Hardware watchdog unavailable");
}
}
return (mod_install(&modlinkage));
}
int
_fini(void)
{
if (strcmp(tod_module_name, "todopl") == 0)
return (EBUSY);
else
return (mod_remove(&modlinkage));
}
int
_info(struct modinfo *modinfop)
{
return (mod_info(&modlinkage, modinfop));
}
static timestruc_t
todopl_get(void)
{
ASSERT(MUTEX_HELD(&tod_lock));
return (hrestime);
}
static void
todopl_set(timestruc_t ts)
{
ASSERT(MUTEX_HELD(&tod_lock));
if (abs(ts.tv_sec - hrestime.tv_sec) > TODOPL_SET_THRESHOLD) {
if (enable_time_sync)
prom_opl_set_diff(ts.tv_sec - hrestime.tv_sec);
else {
time_t obp_time = 0;
int64_t obp_stick;
int64_t stick;
prom_opl_get_tod(&obp_time, &obp_stick);
if (obp_time != 0) {
stick_timestamp(&stick);
obp_time += ((stick - obp_stick) /
system_clock_freq);
prom_opl_set_diff(ts.tv_sec - obp_time);
enable_time_sync = 1;
}
}
TODOPL_DEBUG((CE_NOTE, "todopl_set: new domain time 0x%lx\n",
ts.tv_sec));
}
}
static uint_t
todopl_set_watchdog_timer(uint_t timeoutval)
{
ASSERT(MUTEX_HELD(&tod_lock));
return (0);
}
static uint_t
todopl_clear_watchdog_timer(void)
{
ASSERT(MUTEX_HELD(&tod_lock));
return (0);
}
static void
todopl_set_power_alarm(timestruc_t ts)
{
ASSERT(MUTEX_HELD(&tod_lock));
}
static void
todopl_clear_power_alarm()
{
ASSERT(MUTEX_HELD(&tod_lock));
}
uint64_t
todopl_get_cpufrequency(void)
{
return (cpunodes[CPU->cpu_id].clock_freq);
}