readclock.drv: add support for the TPS65950 RTC
On the BeagleBoard-xM, the RTC is located on the Power Management IC (PMIC). To keep things consistent, access to the PMIC's RTC is done through the readclock driver. The readclock driver forwards the request on to the TPS65950 driver which does the work of manipulating the registers on the chip. Change-Id: I53cefbb59c5a9ab87fab90df3cc1a75a6e430f58
This commit is contained in:
parent
7f98bdf040
commit
039c8db774
11 changed files with 618 additions and 457 deletions
|
@ -3,7 +3,7 @@ PROG= readclock.drv
|
|||
|
||||
.include "arch/${MACHINE_ARCH}/Makefile.inc"
|
||||
|
||||
SRCS+= readclock.c
|
||||
SRCS+= readclock.c forward.c forward.h
|
||||
|
||||
DPADD+= ${LIBSYS} ${LIBTIMERS}
|
||||
LDADD+= -lsys -ltimers
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
HERE=${.CURDIR}/arch/${MACHINE_ARCH}
|
||||
.PATH: ${HERE}
|
||||
|
||||
SRCS += arch_readclock.c omap_rtc.h
|
||||
SRCS += arch_readclock.c omap_rtc.c omap_rtc.h
|
||||
|
||||
DPADD+= ${CLKCONF}
|
||||
LDADD+= -lclkconf
|
||||
|
|
|
@ -1,8 +1,5 @@
|
|||
#include <minix/syslib.h>
|
||||
#include <minix/drvlib.h>
|
||||
#include <minix/log.h>
|
||||
#include <minix/mmio.h>
|
||||
#include <minix/clkconf.h>
|
||||
#include <minix/sysutil.h>
|
||||
|
||||
#include <sys/mman.h>
|
||||
|
@ -17,413 +14,28 @@
|
|||
#include <time.h>
|
||||
|
||||
#include "omap_rtc.h"
|
||||
#include "forward.h"
|
||||
#include "readclock.h"
|
||||
|
||||
/* defines the set of register */
|
||||
|
||||
typedef struct omap_rtc_registers
|
||||
{
|
||||
vir_bytes RTC_SS_SECONDS_REG;
|
||||
vir_bytes RTC_SS_MINUTES_REG;
|
||||
vir_bytes RTC_SS_HOURS_REG;
|
||||
vir_bytes RTC_SS_DAYS_REG;
|
||||
vir_bytes RTC_SS_MONTHS_REG;
|
||||
vir_bytes RTC_SS_YEARS_REG;
|
||||
vir_bytes RTC_SS_WEEKS_REG;
|
||||
vir_bytes RTC_SS_ALARM_SECONDS_REG;
|
||||
vir_bytes RTC_SS_ALARM_MINUTES_REG;
|
||||
vir_bytes RTC_SS_ALARM_HOURS_REG;
|
||||
vir_bytes RTC_SS_ALARM_DAYS_REG;
|
||||
vir_bytes RTC_SS_ALARM_MONTHS_REG;
|
||||
vir_bytes RTC_SS_ALARM_YEARS_REG;
|
||||
vir_bytes RTC_SS_RTC_CTRL_REG;
|
||||
vir_bytes RTC_SS_RTC_STATUS_REG;
|
||||
vir_bytes RTC_SS_RTC_INTERRUPTS_REG;
|
||||
vir_bytes RTC_SS_RTC_COMP_LSB_REG;
|
||||
vir_bytes RTC_SS_RTC_COMP_MSB_REG;
|
||||
vir_bytes RTC_SS_RTC_OSC_REG;
|
||||
vir_bytes RTC_SS_RTC_SCRATCH0_REG;
|
||||
vir_bytes RTC_SS_RTC_SCRATCH1_REG;
|
||||
vir_bytes RTC_SS_RTC_SCRATCH2_REG;
|
||||
vir_bytes RTC_SS_KICK0R;
|
||||
vir_bytes RTC_SS_KICK1R;
|
||||
vir_bytes RTC_SS_RTC_REVISION;
|
||||
vir_bytes RTC_SS_RTC_SYSCONFIG;
|
||||
vir_bytes RTC_SS_RTC_IRQWAKEEN;
|
||||
vir_bytes RTC_SS_ALARM2_SECONDS_REG;
|
||||
vir_bytes RTC_SS_ALARM2_MINUTES_REG;
|
||||
vir_bytes RTC_SS_ALARM2_HOURS_REG;
|
||||
vir_bytes RTC_SS_ALARM2_DAYS_REG;
|
||||
vir_bytes RTC_SS_ALARM2_MONTHS_REG;
|
||||
vir_bytes RTC_SS_ALARM2_YEARS_REG;
|
||||
vir_bytes RTC_SS_RTC_PMIC;
|
||||
vir_bytes RTC_SS_RTC_DEBOUNCE;
|
||||
} omap_rtc_registers_t;
|
||||
|
||||
typedef struct omap_rtc_clock
|
||||
{
|
||||
enum rtc_clock_type
|
||||
{ am335x } clock_type;
|
||||
phys_bytes mr_base;
|
||||
phys_bytes mr_size;
|
||||
vir_bytes mapped_addr;
|
||||
omap_rtc_registers_t *regs;
|
||||
} omap_rtc_clock_t;
|
||||
|
||||
/* Define the registers for each chip */
|
||||
|
||||
static omap_rtc_registers_t am335x_rtc_regs = {
|
||||
.RTC_SS_SECONDS_REG = AM335X_RTC_SS_SECONDS_REG,
|
||||
.RTC_SS_MINUTES_REG = AM335X_RTC_SS_MINUTES_REG,
|
||||
.RTC_SS_HOURS_REG = AM335X_RTC_SS_HOURS_REG,
|
||||
.RTC_SS_DAYS_REG = AM335X_RTC_SS_DAYS_REG,
|
||||
.RTC_SS_MONTHS_REG = AM335X_RTC_SS_MONTHS_REG,
|
||||
.RTC_SS_YEARS_REG = AM335X_RTC_SS_YEARS_REG,
|
||||
.RTC_SS_WEEKS_REG = AM335X_RTC_SS_WEEKS_REG,
|
||||
.RTC_SS_ALARM_SECONDS_REG = AM335X_RTC_SS_ALARM_SECONDS_REG,
|
||||
.RTC_SS_ALARM_MINUTES_REG = AM335X_RTC_SS_ALARM_MINUTES_REG,
|
||||
.RTC_SS_ALARM_HOURS_REG = AM335X_RTC_SS_ALARM_HOURS_REG,
|
||||
.RTC_SS_ALARM_DAYS_REG = AM335X_RTC_SS_ALARM_DAYS_REG,
|
||||
.RTC_SS_ALARM_MONTHS_REG = AM335X_RTC_SS_ALARM_MONTHS_REG,
|
||||
.RTC_SS_ALARM_YEARS_REG = AM335X_RTC_SS_ALARM_YEARS_REG,
|
||||
.RTC_SS_RTC_CTRL_REG = AM335X_RTC_SS_RTC_CTRL_REG,
|
||||
.RTC_SS_RTC_STATUS_REG = AM335X_RTC_SS_RTC_STATUS_REG,
|
||||
.RTC_SS_RTC_INTERRUPTS_REG = AM335X_RTC_SS_RTC_INTERRUPTS_REG,
|
||||
.RTC_SS_RTC_COMP_LSB_REG = AM335X_RTC_SS_RTC_COMP_LSB_REG,
|
||||
.RTC_SS_RTC_COMP_MSB_REG = AM335X_RTC_SS_RTC_COMP_MSB_REG,
|
||||
.RTC_SS_RTC_OSC_REG = AM335X_RTC_SS_RTC_OSC_REG,
|
||||
.RTC_SS_RTC_SCRATCH0_REG = AM335X_RTC_SS_RTC_SCRATCH0_REG,
|
||||
.RTC_SS_RTC_SCRATCH1_REG = AM335X_RTC_SS_RTC_SCRATCH1_REG,
|
||||
.RTC_SS_RTC_SCRATCH2_REG = AM335X_RTC_SS_RTC_SCRATCH2_REG,
|
||||
.RTC_SS_KICK0R = AM335X_RTC_SS_KICK0R,
|
||||
.RTC_SS_KICK1R = AM335X_RTC_SS_KICK1R,
|
||||
.RTC_SS_RTC_REVISION = AM335X_RTC_SS_RTC_REVISION,
|
||||
.RTC_SS_RTC_SYSCONFIG = AM335X_RTC_SS_RTC_SYSCONFIG,
|
||||
.RTC_SS_RTC_IRQWAKEEN = AM335X_RTC_SS_RTC_IRQWAKEEN,
|
||||
.RTC_SS_ALARM2_SECONDS_REG = AM335X_RTC_SS_ALARM2_SECONDS_REG,
|
||||
.RTC_SS_ALARM2_MINUTES_REG = AM335X_RTC_SS_ALARM2_MINUTES_REG,
|
||||
.RTC_SS_ALARM2_HOURS_REG = AM335X_RTC_SS_ALARM2_HOURS_REG,
|
||||
.RTC_SS_ALARM2_DAYS_REG = AM335X_RTC_SS_ALARM2_DAYS_REG,
|
||||
.RTC_SS_ALARM2_MONTHS_REG = AM335X_RTC_SS_ALARM2_MONTHS_REG,
|
||||
.RTC_SS_ALARM2_YEARS_REG = AM335X_RTC_SS_ALARM2_YEARS_REG,
|
||||
.RTC_SS_RTC_PMIC = AM335X_RTC_SS_RTC_PMIC,
|
||||
.RTC_SS_RTC_DEBOUNCE = AM335X_RTC_SS_RTC_DEBOUNCE
|
||||
};
|
||||
|
||||
static omap_rtc_clock_t rtc = {
|
||||
am335x, AM335X_RTC_SS_BASE, AM335X_RTC_SS_SIZE, 0, &am335x_rtc_regs
|
||||
};
|
||||
|
||||
/* used for logging */
|
||||
static struct log log = {
|
||||
.name = "omap_rtc",
|
||||
.log_level = LEVEL_INFO,
|
||||
.log_func = default_log
|
||||
};
|
||||
|
||||
static u32_t use_count = 0;
|
||||
static u32_t pwr_off_in_progress = 0;
|
||||
|
||||
static void omap_rtc_unlock(void);
|
||||
static void omap_rtc_lock(void);
|
||||
static int omap_rtc_clkconf(void);
|
||||
|
||||
/* Helper Functions for Register Access */
|
||||
#define reg_read(a) (*(volatile uint32_t *)(rtc.mapped_addr + a))
|
||||
#define reg_write(a,v) (*(volatile uint32_t *)(rtc.mapped_addr + a) = (v))
|
||||
#define reg_set_bit(a,v) reg_write((a), reg_read((a)) | (1<<v))
|
||||
#define reg_clear_bit(a,v) reg_write((a), reg_read((a)) & ~(1<<v))
|
||||
#define RTC_IS_BUSY (reg_read(rtc.regs->RTC_SS_RTC_STATUS_REG) & (1<<RTC_BUSY_BIT))
|
||||
|
||||
/* When the RTC is running, writes should not happen when the RTC is busy.
|
||||
* This macro waits until the RTC is free before doing the write.
|
||||
*/
|
||||
#define safe_reg_write(a,v) do { while (RTC_IS_BUSY) {micro_delay(1);} reg_write((a),(v)); } while (0)
|
||||
#define safe_reg_set_bit(a,v) safe_reg_write((a), reg_read((a)) | (1<<v))
|
||||
#define safe_reg_clear_bit(a,v) safe_reg_write((a), reg_read((a)) & ~(1<<v))
|
||||
|
||||
static void
|
||||
omap_rtc_unlock(void)
|
||||
{
|
||||
/* Specific bit patterns need to be written to specific registers in a
|
||||
* specific order to enable writing to RTC_SS registers.
|
||||
*/
|
||||
reg_write(rtc.regs->RTC_SS_KICK0R, AM335X_RTC_SS_KICK0R_UNLOCK_MASK);
|
||||
reg_write(rtc.regs->RTC_SS_KICK1R, AM335X_RTC_SS_KICK1R_UNLOCK_MASK);
|
||||
}
|
||||
|
||||
static void
|
||||
omap_rtc_lock(void)
|
||||
{
|
||||
/* Write garbage to the KICK registers to enable write protect. */
|
||||
reg_write(rtc.regs->RTC_SS_KICK0R, AM335X_RTC_SS_KICK0R_LOCK_MASK);
|
||||
reg_write(rtc.regs->RTC_SS_KICK1R, AM335X_RTC_SS_KICK1R_LOCK_MASK);
|
||||
}
|
||||
|
||||
static int
|
||||
omap_rtc_clkconf(void)
|
||||
{
|
||||
int r;
|
||||
|
||||
/* Configure the clocks need to run the RTC */
|
||||
r = clkconf_init();
|
||||
if (r != OK) {
|
||||
return r;
|
||||
}
|
||||
|
||||
r = clkconf_set(CM_RTC_RTC_CLKCTRL, 0xffffffff,
|
||||
CM_RTC_RTC_CLKCTRL_MASK);
|
||||
if (r != OK) {
|
||||
return r;
|
||||
}
|
||||
|
||||
r = clkconf_set(CM_RTC_CLKSTCTRL, 0xffffffff, CM_RTC_CLKSTCTRL_MASK);
|
||||
if (r != OK) {
|
||||
return r;
|
||||
}
|
||||
|
||||
r = clkconf_release();
|
||||
if (r != OK) {
|
||||
return r;
|
||||
}
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
int
|
||||
arch_init(void)
|
||||
arch_setup(struct rtc *r)
|
||||
{
|
||||
int r;
|
||||
int rtc_rev, major, minor;
|
||||
struct minix_mem_range mr;
|
||||
|
||||
#ifndef AM335X
|
||||
/* Only the am335x (BeagleBone & BeagleBone Black) is supported ATM.
|
||||
* The dm37xx (BeagleBoard-xM) doesn't have a real time clock
|
||||
* built-in. Instead, it uses the RTC on the PMIC. A driver for
|
||||
* the BeagleBoard-xM's PMIC still needs to be developed.
|
||||
*/
|
||||
log_warn(&log, "unsupported processor\n");
|
||||
#ifdef AM335X
|
||||
r->init = omap_rtc_init;
|
||||
r->get_time = omap_rtc_get_time;
|
||||
r->set_time = omap_rtc_set_time;
|
||||
r->pwr_off = omap_rtc_pwr_off;
|
||||
r->exit = omap_rtc_exit;
|
||||
return OK;
|
||||
#elif DM37XX
|
||||
fwd_set_label("tps65950.1.48");
|
||||
r->init = fwd_init;
|
||||
r->get_time = fwd_get_time;
|
||||
r->set_time = fwd_set_time;
|
||||
r->pwr_off = fwd_pwr_off;
|
||||
r->exit = fwd_exit;
|
||||
return OK;
|
||||
#else
|
||||
return ENOSYS;
|
||||
#endif /* !AM335X */
|
||||
|
||||
if (pwr_off_in_progress) return EINVAL;
|
||||
|
||||
use_count++;
|
||||
if (rtc.mapped_addr != 0) {
|
||||
/* already intialized */
|
||||
return OK;
|
||||
}
|
||||
|
||||
/* Enable Clocks */
|
||||
r = omap_rtc_clkconf();
|
||||
if (r != OK) {
|
||||
log_warn(&log, "Failed to enable clocks for RTC.\n");
|
||||
return r;
|
||||
}
|
||||
|
||||
/*
|
||||
* Map RTC_SS Registers
|
||||
*/
|
||||
|
||||
/* Configure memory access */
|
||||
mr.mr_base = rtc.mr_base; /* start addr */
|
||||
mr.mr_limit = mr.mr_base + rtc.mr_size; /* end addr */
|
||||
|
||||
/* ask for privileges to access the RTC_SS memory range */
|
||||
if (sys_privctl(SELF, SYS_PRIV_ADD_MEM, &mr) != OK) {
|
||||
log_warn(&log,
|
||||
"Unable to obtain RTC memory range privileges.");
|
||||
return EPERM;
|
||||
}
|
||||
|
||||
/* map the memory into this process */
|
||||
rtc.mapped_addr = (vir_bytes) vm_map_phys(SELF,
|
||||
(void *) rtc.mr_base, rtc.mr_size);
|
||||
if (rtc.mapped_addr == (vir_bytes) MAP_FAILED) {
|
||||
log_warn(&log, "Unable to map RTC registers\n");
|
||||
return EPERM;
|
||||
}
|
||||
|
||||
rtc_rev = reg_read(rtc.regs->RTC_SS_RTC_REVISION);
|
||||
major = (rtc_rev & 0x0700) >> 8;
|
||||
minor = (rtc_rev & 0x001f);
|
||||
log_debug(&log, "omap rtc rev %d.%d\n", major, minor);
|
||||
|
||||
/* Disable register write protect */
|
||||
omap_rtc_unlock();
|
||||
|
||||
/* Set NOIDLE */
|
||||
reg_write(rtc.regs->RTC_SS_RTC_SYSCONFIG, (1 << NOIDLE_BIT));
|
||||
|
||||
/* Enable 32kHz clock */
|
||||
reg_set_bit(rtc.regs->RTC_SS_RTC_OSC_REG, EN_32KCLK_BIT);
|
||||
|
||||
/* Setting the stop bit starts the RTC running */
|
||||
reg_set_bit(rtc.regs->RTC_SS_RTC_CTRL_REG, RTC_STOP_BIT);
|
||||
|
||||
/* Re-enable Write Protection */
|
||||
omap_rtc_lock();
|
||||
|
||||
log_debug(&log, "OMAP RTC Initialized\n");
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* These are the ranges used by the real time clock and struct tm.
|
||||
*
|
||||
* Field OMAP RTC struct tm
|
||||
* ----- -------- ---------
|
||||
* seconds 0 to 59 (Mask 0x7f) 0 to 59 (60 for leap seconds)
|
||||
* minutes 0 to 59 (Mask 0x7f) 0 to 59
|
||||
* hours 0 to 23 (Mask 0x3f) 0 to 23
|
||||
* day 1 to 31 (Mask 0x3f) 1 to 31
|
||||
* month 1 to 12 (Mask 0x1f) 0 to 11
|
||||
* year last 2 digits of year X + 1900
|
||||
*/
|
||||
|
||||
int
|
||||
arch_get_time(struct tm *t, int flags)
|
||||
{
|
||||
int r;
|
||||
|
||||
if (pwr_off_in_progress) return EINVAL;
|
||||
|
||||
memset(t, '\0', sizeof(struct tm));
|
||||
|
||||
/* Read and Convert BCD to binary (default RTC mode). */
|
||||
t->tm_sec = bcd_to_dec(reg_read(rtc.regs->RTC_SS_SECONDS_REG) & 0x7f);
|
||||
t->tm_min = bcd_to_dec(reg_read(rtc.regs->RTC_SS_MINUTES_REG) & 0x7f);
|
||||
t->tm_hour = bcd_to_dec(reg_read(rtc.regs->RTC_SS_HOURS_REG) & 0x3f);
|
||||
t->tm_mday = bcd_to_dec(reg_read(rtc.regs->RTC_SS_DAYS_REG) & 0x3f);
|
||||
t->tm_mon = bcd_to_dec(reg_read(rtc.regs->RTC_SS_MONTHS_REG) & 0x1f) - 1;
|
||||
t->tm_year = bcd_to_dec(reg_read(rtc.regs->RTC_SS_YEARS_REG) & 0xff) + 100;
|
||||
|
||||
if (t->tm_year == 100) {
|
||||
/* Cold start - no date/time set - default to 2013-01-01 */
|
||||
t->tm_sec = 0;
|
||||
t->tm_min = 0;
|
||||
t->tm_hour = 0;
|
||||
t->tm_mday = 1;
|
||||
t->tm_mon = 0;
|
||||
t->tm_year = 113;
|
||||
|
||||
arch_set_time(t, RTCDEV_NOFLAGS);
|
||||
}
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
int
|
||||
arch_set_time(struct tm *t, int flags)
|
||||
{
|
||||
int r;
|
||||
|
||||
if (pwr_off_in_progress) return EINVAL;
|
||||
|
||||
/* Disable Write Protection */
|
||||
omap_rtc_unlock();
|
||||
|
||||
/* Write the date/time to the RTC registers. */
|
||||
safe_reg_write(rtc.regs->RTC_SS_SECONDS_REG,
|
||||
(dec_to_bcd(t->tm_sec) & 0x7f));
|
||||
safe_reg_write(rtc.regs->RTC_SS_MINUTES_REG,
|
||||
(dec_to_bcd(t->tm_min) & 0x7f));
|
||||
safe_reg_write(rtc.regs->RTC_SS_HOURS_REG,
|
||||
(dec_to_bcd(t->tm_hour) & 0x3f));
|
||||
safe_reg_write(rtc.regs->RTC_SS_DAYS_REG,
|
||||
(dec_to_bcd(t->tm_mday) & 0x3f));
|
||||
safe_reg_write(rtc.regs->RTC_SS_MONTHS_REG,
|
||||
(dec_to_bcd(t->tm_mon + 1) & 0x1f));
|
||||
safe_reg_write(rtc.regs->RTC_SS_YEARS_REG,
|
||||
(dec_to_bcd(t->tm_year % 100) & 0xff));
|
||||
|
||||
/* Re-enable Write Protection */
|
||||
omap_rtc_lock();
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
int
|
||||
arch_pwr_off(void)
|
||||
{
|
||||
int r;
|
||||
struct tm t;
|
||||
|
||||
if (pwr_off_in_progress) return EINVAL;
|
||||
|
||||
/* wait until 3 seconds can be added without overflowing tm_sec */
|
||||
do {
|
||||
arch_get_time(&t, RTCDEV_NOFLAGS);
|
||||
micro_delay(250000);
|
||||
} while (t.tm_sec >= 57);
|
||||
|
||||
/* set the alarm for 3 seconds from now */
|
||||
t.tm_sec += 3;
|
||||
|
||||
/* Disable register write protect */
|
||||
omap_rtc_unlock();
|
||||
|
||||
/* enable power-off via ALARM2 by setting the PWR_ENABLE_EN bit. */
|
||||
safe_reg_set_bit(rtc.regs->RTC_SS_RTC_PMIC, PWR_ENABLE_EN_BIT);
|
||||
|
||||
/* Write the date/time to the RTC registers. */
|
||||
safe_reg_write(rtc.regs->RTC_SS_ALARM2_SECONDS_REG,
|
||||
(dec_to_bcd(t.tm_sec) & 0x7f));
|
||||
safe_reg_write(rtc.regs->RTC_SS_ALARM2_MINUTES_REG,
|
||||
(dec_to_bcd(t.tm_min) & 0x7f));
|
||||
safe_reg_write(rtc.regs->RTC_SS_ALARM2_HOURS_REG,
|
||||
(dec_to_bcd(t.tm_hour) & 0x3f));
|
||||
safe_reg_write(rtc.regs->RTC_SS_ALARM2_DAYS_REG,
|
||||
(dec_to_bcd(t.tm_mday) & 0x3f));
|
||||
safe_reg_write(rtc.regs->RTC_SS_ALARM2_MONTHS_REG,
|
||||
(dec_to_bcd(t.tm_mon + 1) & 0x1f));
|
||||
safe_reg_write(rtc.regs->RTC_SS_ALARM2_YEARS_REG,
|
||||
(dec_to_bcd(t.tm_year % 100) & 0xff));
|
||||
|
||||
/* enable interrupt to trigger POWER_EN to go low when alarm2 hits. */
|
||||
safe_reg_set_bit(rtc.regs->RTC_SS_RTC_INTERRUPTS_REG, IT_ALARM2_BIT);
|
||||
|
||||
/* pause the realtime clock. the kernel will enable it when safe. */
|
||||
reg_clear_bit(rtc.regs->RTC_SS_RTC_CTRL_REG, RTC_STOP_BIT);
|
||||
|
||||
/* Set this flag to block all other operations so that the clock isn't
|
||||
* accidentally re-startered and so write protect isn't re-enabled. */
|
||||
pwr_off_in_progress = 1;
|
||||
|
||||
/* Make the kernel's job easier by not re-enabling write protection */
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
void
|
||||
arch_exit(void)
|
||||
{
|
||||
use_count--;
|
||||
if (use_count == 0) {
|
||||
vm_unmap_phys(SELF, (void *) rtc.mapped_addr, rtc.mr_size);
|
||||
rtc.mapped_addr = 0;
|
||||
}
|
||||
log_debug(&log, "Exiting\n");
|
||||
}
|
||||
|
||||
int
|
||||
arch_sef_cb_lu_state_save(int UNUSED(state))
|
||||
{
|
||||
/* Nothing to save yet -- the TPS65950 RTC driver will need this */
|
||||
return OK;
|
||||
}
|
||||
|
||||
int
|
||||
arch_lu_state_restore(void)
|
||||
{
|
||||
/* Nothing to restore yet -- the TPS65950 RTC driver will need this */
|
||||
return OK;
|
||||
}
|
||||
|
||||
void
|
||||
arch_announce(void)
|
||||
{
|
||||
/* Nothing to announce yet -- the TPS65950 RTC driver will need this */
|
||||
#endif
|
||||
}
|
||||
|
|
415
drivers/readclock/arch/earm/omap_rtc.c
Normal file
415
drivers/readclock/arch/earm/omap_rtc.c
Normal file
|
@ -0,0 +1,415 @@
|
|||
#include <minix/syslib.h>
|
||||
#include <minix/drvlib.h>
|
||||
#include <minix/log.h>
|
||||
#include <minix/mmio.h>
|
||||
#include <minix/clkconf.h>
|
||||
#include <minix/sysutil.h>
|
||||
|
||||
#include <sys/mman.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <assert.h>
|
||||
#include <time.h>
|
||||
|
||||
#include "omap_rtc.h"
|
||||
#include "readclock.h"
|
||||
|
||||
/* defines the set of register */
|
||||
|
||||
typedef struct omap_rtc_registers
|
||||
{
|
||||
vir_bytes RTC_SS_SECONDS_REG;
|
||||
vir_bytes RTC_SS_MINUTES_REG;
|
||||
vir_bytes RTC_SS_HOURS_REG;
|
||||
vir_bytes RTC_SS_DAYS_REG;
|
||||
vir_bytes RTC_SS_MONTHS_REG;
|
||||
vir_bytes RTC_SS_YEARS_REG;
|
||||
vir_bytes RTC_SS_WEEKS_REG;
|
||||
vir_bytes RTC_SS_ALARM_SECONDS_REG;
|
||||
vir_bytes RTC_SS_ALARM_MINUTES_REG;
|
||||
vir_bytes RTC_SS_ALARM_HOURS_REG;
|
||||
vir_bytes RTC_SS_ALARM_DAYS_REG;
|
||||
vir_bytes RTC_SS_ALARM_MONTHS_REG;
|
||||
vir_bytes RTC_SS_ALARM_YEARS_REG;
|
||||
vir_bytes RTC_SS_RTC_CTRL_REG;
|
||||
vir_bytes RTC_SS_RTC_STATUS_REG;
|
||||
vir_bytes RTC_SS_RTC_INTERRUPTS_REG;
|
||||
vir_bytes RTC_SS_RTC_COMP_LSB_REG;
|
||||
vir_bytes RTC_SS_RTC_COMP_MSB_REG;
|
||||
vir_bytes RTC_SS_RTC_OSC_REG;
|
||||
vir_bytes RTC_SS_RTC_SCRATCH0_REG;
|
||||
vir_bytes RTC_SS_RTC_SCRATCH1_REG;
|
||||
vir_bytes RTC_SS_RTC_SCRATCH2_REG;
|
||||
vir_bytes RTC_SS_KICK0R;
|
||||
vir_bytes RTC_SS_KICK1R;
|
||||
vir_bytes RTC_SS_RTC_REVISION;
|
||||
vir_bytes RTC_SS_RTC_SYSCONFIG;
|
||||
vir_bytes RTC_SS_RTC_IRQWAKEEN;
|
||||
vir_bytes RTC_SS_ALARM2_SECONDS_REG;
|
||||
vir_bytes RTC_SS_ALARM2_MINUTES_REG;
|
||||
vir_bytes RTC_SS_ALARM2_HOURS_REG;
|
||||
vir_bytes RTC_SS_ALARM2_DAYS_REG;
|
||||
vir_bytes RTC_SS_ALARM2_MONTHS_REG;
|
||||
vir_bytes RTC_SS_ALARM2_YEARS_REG;
|
||||
vir_bytes RTC_SS_RTC_PMIC;
|
||||
vir_bytes RTC_SS_RTC_DEBOUNCE;
|
||||
} omap_rtc_registers_t;
|
||||
|
||||
typedef struct omap_rtc_clock
|
||||
{
|
||||
enum rtc_clock_type
|
||||
{ am335x } clock_type;
|
||||
phys_bytes mr_base;
|
||||
phys_bytes mr_size;
|
||||
vir_bytes mapped_addr;
|
||||
omap_rtc_registers_t *regs;
|
||||
} omap_rtc_clock_t;
|
||||
|
||||
/* Define the registers for each chip */
|
||||
|
||||
static omap_rtc_registers_t am335x_rtc_regs = {
|
||||
.RTC_SS_SECONDS_REG = AM335X_RTC_SS_SECONDS_REG,
|
||||
.RTC_SS_MINUTES_REG = AM335X_RTC_SS_MINUTES_REG,
|
||||
.RTC_SS_HOURS_REG = AM335X_RTC_SS_HOURS_REG,
|
||||
.RTC_SS_DAYS_REG = AM335X_RTC_SS_DAYS_REG,
|
||||
.RTC_SS_MONTHS_REG = AM335X_RTC_SS_MONTHS_REG,
|
||||
.RTC_SS_YEARS_REG = AM335X_RTC_SS_YEARS_REG,
|
||||
.RTC_SS_WEEKS_REG = AM335X_RTC_SS_WEEKS_REG,
|
||||
.RTC_SS_ALARM_SECONDS_REG = AM335X_RTC_SS_ALARM_SECONDS_REG,
|
||||
.RTC_SS_ALARM_MINUTES_REG = AM335X_RTC_SS_ALARM_MINUTES_REG,
|
||||
.RTC_SS_ALARM_HOURS_REG = AM335X_RTC_SS_ALARM_HOURS_REG,
|
||||
.RTC_SS_ALARM_DAYS_REG = AM335X_RTC_SS_ALARM_DAYS_REG,
|
||||
.RTC_SS_ALARM_MONTHS_REG = AM335X_RTC_SS_ALARM_MONTHS_REG,
|
||||
.RTC_SS_ALARM_YEARS_REG = AM335X_RTC_SS_ALARM_YEARS_REG,
|
||||
.RTC_SS_RTC_CTRL_REG = AM335X_RTC_SS_RTC_CTRL_REG,
|
||||
.RTC_SS_RTC_STATUS_REG = AM335X_RTC_SS_RTC_STATUS_REG,
|
||||
.RTC_SS_RTC_INTERRUPTS_REG = AM335X_RTC_SS_RTC_INTERRUPTS_REG,
|
||||
.RTC_SS_RTC_COMP_LSB_REG = AM335X_RTC_SS_RTC_COMP_LSB_REG,
|
||||
.RTC_SS_RTC_COMP_MSB_REG = AM335X_RTC_SS_RTC_COMP_MSB_REG,
|
||||
.RTC_SS_RTC_OSC_REG = AM335X_RTC_SS_RTC_OSC_REG,
|
||||
.RTC_SS_RTC_SCRATCH0_REG = AM335X_RTC_SS_RTC_SCRATCH0_REG,
|
||||
.RTC_SS_RTC_SCRATCH1_REG = AM335X_RTC_SS_RTC_SCRATCH1_REG,
|
||||
.RTC_SS_RTC_SCRATCH2_REG = AM335X_RTC_SS_RTC_SCRATCH2_REG,
|
||||
.RTC_SS_KICK0R = AM335X_RTC_SS_KICK0R,
|
||||
.RTC_SS_KICK1R = AM335X_RTC_SS_KICK1R,
|
||||
.RTC_SS_RTC_REVISION = AM335X_RTC_SS_RTC_REVISION,
|
||||
.RTC_SS_RTC_SYSCONFIG = AM335X_RTC_SS_RTC_SYSCONFIG,
|
||||
.RTC_SS_RTC_IRQWAKEEN = AM335X_RTC_SS_RTC_IRQWAKEEN,
|
||||
.RTC_SS_ALARM2_SECONDS_REG = AM335X_RTC_SS_ALARM2_SECONDS_REG,
|
||||
.RTC_SS_ALARM2_MINUTES_REG = AM335X_RTC_SS_ALARM2_MINUTES_REG,
|
||||
.RTC_SS_ALARM2_HOURS_REG = AM335X_RTC_SS_ALARM2_HOURS_REG,
|
||||
.RTC_SS_ALARM2_DAYS_REG = AM335X_RTC_SS_ALARM2_DAYS_REG,
|
||||
.RTC_SS_ALARM2_MONTHS_REG = AM335X_RTC_SS_ALARM2_MONTHS_REG,
|
||||
.RTC_SS_ALARM2_YEARS_REG = AM335X_RTC_SS_ALARM2_YEARS_REG,
|
||||
.RTC_SS_RTC_PMIC = AM335X_RTC_SS_RTC_PMIC,
|
||||
.RTC_SS_RTC_DEBOUNCE = AM335X_RTC_SS_RTC_DEBOUNCE
|
||||
};
|
||||
|
||||
static omap_rtc_clock_t rtc = {
|
||||
am335x, AM335X_RTC_SS_BASE, AM335X_RTC_SS_SIZE, 0, &am335x_rtc_regs
|
||||
};
|
||||
|
||||
/* used for logging */
|
||||
static struct log log = {
|
||||
.name = "omap_rtc",
|
||||
.log_level = LEVEL_INFO,
|
||||
.log_func = default_log
|
||||
};
|
||||
|
||||
static u32_t use_count = 0;
|
||||
static u32_t pwr_off_in_progress = 0;
|
||||
|
||||
static void omap_rtc_unlock(void);
|
||||
static void omap_rtc_lock(void);
|
||||
static int omap_rtc_clkconf(void);
|
||||
|
||||
/* Helper Functions for Register Access */
|
||||
#define reg_read(a) (*(volatile uint32_t *)(rtc.mapped_addr + a))
|
||||
#define reg_write(a,v) (*(volatile uint32_t *)(rtc.mapped_addr + a) = (v))
|
||||
#define reg_set_bit(a,v) reg_write((a), reg_read((a)) | (1<<v))
|
||||
#define reg_clear_bit(a,v) reg_write((a), reg_read((a)) & ~(1<<v))
|
||||
#define RTC_IS_BUSY (reg_read(rtc.regs->RTC_SS_RTC_STATUS_REG) & (1<<RTC_BUSY_BIT))
|
||||
|
||||
/* When the RTC is running, writes should not happen when the RTC is busy.
|
||||
* This macro waits until the RTC is free before doing the write.
|
||||
*/
|
||||
#define safe_reg_write(a,v) do { while (RTC_IS_BUSY) {micro_delay(1);} reg_write((a),(v)); } while (0)
|
||||
#define safe_reg_set_bit(a,v) safe_reg_write((a), reg_read((a)) | (1<<v))
|
||||
#define safe_reg_clear_bit(a,v) safe_reg_write((a), reg_read((a)) & ~(1<<v))
|
||||
|
||||
static void
|
||||
omap_rtc_unlock(void)
|
||||
{
|
||||
/* Specific bit patterns need to be written to specific registers in a
|
||||
* specific order to enable writing to RTC_SS registers.
|
||||
*/
|
||||
reg_write(rtc.regs->RTC_SS_KICK0R, AM335X_RTC_SS_KICK0R_UNLOCK_MASK);
|
||||
reg_write(rtc.regs->RTC_SS_KICK1R, AM335X_RTC_SS_KICK1R_UNLOCK_MASK);
|
||||
}
|
||||
|
||||
static void
|
||||
omap_rtc_lock(void)
|
||||
{
|
||||
/* Write garbage to the KICK registers to enable write protect. */
|
||||
reg_write(rtc.regs->RTC_SS_KICK0R, AM335X_RTC_SS_KICK0R_LOCK_MASK);
|
||||
reg_write(rtc.regs->RTC_SS_KICK1R, AM335X_RTC_SS_KICK1R_LOCK_MASK);
|
||||
}
|
||||
|
||||
static int
|
||||
omap_rtc_clkconf(void)
|
||||
{
|
||||
int r;
|
||||
|
||||
/* Configure the clocks need to run the RTC */
|
||||
r = clkconf_init();
|
||||
if (r != OK) {
|
||||
return r;
|
||||
}
|
||||
|
||||
r = clkconf_set(CM_RTC_RTC_CLKCTRL, 0xffffffff,
|
||||
CM_RTC_RTC_CLKCTRL_MASK);
|
||||
if (r != OK) {
|
||||
return r;
|
||||
}
|
||||
|
||||
r = clkconf_set(CM_RTC_CLKSTCTRL, 0xffffffff, CM_RTC_CLKSTCTRL_MASK);
|
||||
if (r != OK) {
|
||||
return r;
|
||||
}
|
||||
|
||||
r = clkconf_release();
|
||||
if (r != OK) {
|
||||
return r;
|
||||
}
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
int
|
||||
omap_rtc_init(void)
|
||||
{
|
||||
int r;
|
||||
int rtc_rev, major, minor;
|
||||
struct minix_mem_range mr;
|
||||
|
||||
#ifndef AM335X
|
||||
/* Only the am335x (BeagleBone & BeagleBone Black) is supported ATM.
|
||||
* The dm37xx (BeagleBoard-xM) doesn't have a real time clock
|
||||
* built-in. Instead, it uses the RTC on the PMIC. A driver for
|
||||
* the BeagleBoard-xM's PMIC still needs to be developed.
|
||||
*/
|
||||
log_warn(&log, "unsupported processor\n");
|
||||
return ENOSYS;
|
||||
#endif /* !AM335X */
|
||||
|
||||
if (pwr_off_in_progress)
|
||||
return EINVAL;
|
||||
|
||||
use_count++;
|
||||
if (rtc.mapped_addr != 0) {
|
||||
/* already intialized */
|
||||
return OK;
|
||||
}
|
||||
|
||||
/* Enable Clocks */
|
||||
r = omap_rtc_clkconf();
|
||||
if (r != OK) {
|
||||
log_warn(&log, "Failed to enable clocks for RTC.\n");
|
||||
return r;
|
||||
}
|
||||
|
||||
/*
|
||||
* Map RTC_SS Registers
|
||||
*/
|
||||
|
||||
/* Configure memory access */
|
||||
mr.mr_base = rtc.mr_base; /* start addr */
|
||||
mr.mr_limit = mr.mr_base + rtc.mr_size; /* end addr */
|
||||
|
||||
/* ask for privileges to access the RTC_SS memory range */
|
||||
if (sys_privctl(SELF, SYS_PRIV_ADD_MEM, &mr) != OK) {
|
||||
log_warn(&log,
|
||||
"Unable to obtain RTC memory range privileges.");
|
||||
return EPERM;
|
||||
}
|
||||
|
||||
/* map the memory into this process */
|
||||
rtc.mapped_addr = (vir_bytes) vm_map_phys(SELF,
|
||||
(void *) rtc.mr_base, rtc.mr_size);
|
||||
if (rtc.mapped_addr == (vir_bytes) MAP_FAILED) {
|
||||
log_warn(&log, "Unable to map RTC registers\n");
|
||||
return EPERM;
|
||||
}
|
||||
|
||||
rtc_rev = reg_read(rtc.regs->RTC_SS_RTC_REVISION);
|
||||
major = (rtc_rev & 0x0700) >> 8;
|
||||
minor = (rtc_rev & 0x001f);
|
||||
log_debug(&log, "omap rtc rev %d.%d\n", major, minor);
|
||||
|
||||
/* Disable register write protect */
|
||||
omap_rtc_unlock();
|
||||
|
||||
/* Set NOIDLE */
|
||||
reg_write(rtc.regs->RTC_SS_RTC_SYSCONFIG, (1 << NOIDLE_BIT));
|
||||
|
||||
/* Enable 32kHz clock */
|
||||
reg_set_bit(rtc.regs->RTC_SS_RTC_OSC_REG, EN_32KCLK_BIT);
|
||||
|
||||
/* Setting the stop bit starts the RTC running */
|
||||
reg_set_bit(rtc.regs->RTC_SS_RTC_CTRL_REG, RTC_STOP_BIT);
|
||||
|
||||
/* Re-enable Write Protection */
|
||||
omap_rtc_lock();
|
||||
|
||||
log_debug(&log, "OMAP RTC Initialized\n");
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* These are the ranges used by the real time clock and struct tm.
|
||||
*
|
||||
* Field OMAP RTC struct tm
|
||||
* ----- -------- ---------
|
||||
* seconds 0 to 59 (Mask 0x7f) 0 to 59 (60 for leap seconds)
|
||||
* minutes 0 to 59 (Mask 0x7f) 0 to 59
|
||||
* hours 0 to 23 (Mask 0x3f) 0 to 23
|
||||
* day 1 to 31 (Mask 0x3f) 1 to 31
|
||||
* month 1 to 12 (Mask 0x1f) 0 to 11
|
||||
* year last 2 digits of year X + 1900
|
||||
*/
|
||||
|
||||
int
|
||||
omap_rtc_get_time(struct tm *t, int flags)
|
||||
{
|
||||
int r;
|
||||
|
||||
if (pwr_off_in_progress)
|
||||
return EINVAL;
|
||||
|
||||
memset(t, '\0', sizeof(struct tm));
|
||||
|
||||
/* Read and Convert BCD to binary (default RTC mode). */
|
||||
t->tm_sec = bcd_to_dec(reg_read(rtc.regs->RTC_SS_SECONDS_REG) & 0x7f);
|
||||
t->tm_min = bcd_to_dec(reg_read(rtc.regs->RTC_SS_MINUTES_REG) & 0x7f);
|
||||
t->tm_hour = bcd_to_dec(reg_read(rtc.regs->RTC_SS_HOURS_REG) & 0x3f);
|
||||
t->tm_mday = bcd_to_dec(reg_read(rtc.regs->RTC_SS_DAYS_REG) & 0x3f);
|
||||
t->tm_mon =
|
||||
bcd_to_dec(reg_read(rtc.regs->RTC_SS_MONTHS_REG) & 0x1f) - 1;
|
||||
t->tm_year =
|
||||
bcd_to_dec(reg_read(rtc.regs->RTC_SS_YEARS_REG) & 0xff) + 100;
|
||||
|
||||
if (t->tm_year == 100) {
|
||||
/* Cold start - no date/time set - default to 2013-01-01 */
|
||||
t->tm_sec = 0;
|
||||
t->tm_min = 0;
|
||||
t->tm_hour = 0;
|
||||
t->tm_mday = 1;
|
||||
t->tm_mon = 0;
|
||||
t->tm_year = 113;
|
||||
|
||||
omap_rtc_set_time(t, RTCDEV_NOFLAGS);
|
||||
}
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
int
|
||||
omap_rtc_set_time(struct tm *t, int flags)
|
||||
{
|
||||
int r;
|
||||
|
||||
if (pwr_off_in_progress)
|
||||
return EINVAL;
|
||||
|
||||
/* Disable Write Protection */
|
||||
omap_rtc_unlock();
|
||||
|
||||
/* Write the date/time to the RTC registers. */
|
||||
safe_reg_write(rtc.regs->RTC_SS_SECONDS_REG,
|
||||
(dec_to_bcd(t->tm_sec) & 0x7f));
|
||||
safe_reg_write(rtc.regs->RTC_SS_MINUTES_REG,
|
||||
(dec_to_bcd(t->tm_min) & 0x7f));
|
||||
safe_reg_write(rtc.regs->RTC_SS_HOURS_REG,
|
||||
(dec_to_bcd(t->tm_hour) & 0x3f));
|
||||
safe_reg_write(rtc.regs->RTC_SS_DAYS_REG,
|
||||
(dec_to_bcd(t->tm_mday) & 0x3f));
|
||||
safe_reg_write(rtc.regs->RTC_SS_MONTHS_REG,
|
||||
(dec_to_bcd(t->tm_mon + 1) & 0x1f));
|
||||
safe_reg_write(rtc.regs->RTC_SS_YEARS_REG,
|
||||
(dec_to_bcd(t->tm_year % 100) & 0xff));
|
||||
|
||||
/* Re-enable Write Protection */
|
||||
omap_rtc_lock();
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
int
|
||||
omap_rtc_pwr_off(void)
|
||||
{
|
||||
int r;
|
||||
struct tm t;
|
||||
|
||||
if (pwr_off_in_progress)
|
||||
return EINVAL;
|
||||
|
||||
/* wait until 3 seconds can be added without overflowing tm_sec */
|
||||
do {
|
||||
omap_rtc_get_time(&t, RTCDEV_NOFLAGS);
|
||||
micro_delay(250000);
|
||||
} while (t.tm_sec >= 57);
|
||||
|
||||
/* set the alarm for 3 seconds from now */
|
||||
t.tm_sec += 3;
|
||||
|
||||
/* Disable register write protect */
|
||||
omap_rtc_unlock();
|
||||
|
||||
/* enable power-off via ALARM2 by setting the PWR_ENABLE_EN bit. */
|
||||
safe_reg_set_bit(rtc.regs->RTC_SS_RTC_PMIC, PWR_ENABLE_EN_BIT);
|
||||
|
||||
/* Write the date/time to the RTC registers. */
|
||||
safe_reg_write(rtc.regs->RTC_SS_ALARM2_SECONDS_REG,
|
||||
(dec_to_bcd(t.tm_sec) & 0x7f));
|
||||
safe_reg_write(rtc.regs->RTC_SS_ALARM2_MINUTES_REG,
|
||||
(dec_to_bcd(t.tm_min) & 0x7f));
|
||||
safe_reg_write(rtc.regs->RTC_SS_ALARM2_HOURS_REG,
|
||||
(dec_to_bcd(t.tm_hour) & 0x3f));
|
||||
safe_reg_write(rtc.regs->RTC_SS_ALARM2_DAYS_REG,
|
||||
(dec_to_bcd(t.tm_mday) & 0x3f));
|
||||
safe_reg_write(rtc.regs->RTC_SS_ALARM2_MONTHS_REG,
|
||||
(dec_to_bcd(t.tm_mon + 1) & 0x1f));
|
||||
safe_reg_write(rtc.regs->RTC_SS_ALARM2_YEARS_REG,
|
||||
(dec_to_bcd(t.tm_year % 100) & 0xff));
|
||||
|
||||
/* enable interrupt to trigger POWER_EN to go low when alarm2 hits. */
|
||||
safe_reg_set_bit(rtc.regs->RTC_SS_RTC_INTERRUPTS_REG, IT_ALARM2_BIT);
|
||||
|
||||
/* pause the realtime clock. the kernel will enable it when safe. */
|
||||
reg_clear_bit(rtc.regs->RTC_SS_RTC_CTRL_REG, RTC_STOP_BIT);
|
||||
|
||||
/* Set this flag to block all other operations so that the clock isn't
|
||||
* accidentally re-startered and so write protect isn't re-enabled. */
|
||||
pwr_off_in_progress = 1;
|
||||
|
||||
/* Make the kernel's job easier by not re-enabling write protection */
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
void
|
||||
omap_rtc_exit(void)
|
||||
{
|
||||
use_count--;
|
||||
if (use_count == 0) {
|
||||
vm_unmap_phys(SELF, (void *) rtc.mapped_addr, rtc.mr_size);
|
||||
rtc.mapped_addr = 0;
|
||||
}
|
||||
log_debug(&log, "Exiting\n");
|
||||
}
|
|
@ -85,4 +85,10 @@
|
|||
#define CLKTRCTRL ((0<<1)|(0<<0))
|
||||
#define CM_RTC_CLKSTCTRL_MASK (CLKACTIVITY_RTC_32KCLK|CLKACTIVITY_L4_RTC_GCLK|CLKTRCTRL)
|
||||
|
||||
int omap_rtc_init(void);
|
||||
int omap_rtc_get_time(struct tm *t, int flags);
|
||||
int omap_rtc_set_time(struct tm *t, int flags);
|
||||
int omap_rtc_pwr_off(void);
|
||||
void omap_rtc_exit(void);
|
||||
|
||||
#endif /* __OMAP_RTC_REGISTERS_H */
|
||||
|
|
|
@ -80,7 +80,23 @@ static struct log log = {
|
|||
static int read_register(int reg_addr);
|
||||
static int write_register(int reg_addr, int value);
|
||||
|
||||
static int arch_init(void);
|
||||
static int arch_get_time(struct tm *t, int flags);
|
||||
static int arch_set_time(struct tm *t, int flags);
|
||||
static int arch_pwr_off(void);
|
||||
static void arch_exit(void);
|
||||
|
||||
int
|
||||
arch_setup(struct rtc *r)
|
||||
{
|
||||
r->init = arch_init;
|
||||
r->get_time = arch_get_time;
|
||||
r->set_time = arch_set_time;
|
||||
r->pwr_off = arch_pwr_off;
|
||||
r->exit = arch_exit;
|
||||
}
|
||||
|
||||
static int
|
||||
arch_init(void)
|
||||
{
|
||||
int s;
|
||||
|
@ -130,7 +146,7 @@ arch_init(void)
|
|||
/* */
|
||||
/***********************************************************************/
|
||||
|
||||
int
|
||||
static int
|
||||
arch_get_time(struct tm *t, int flags)
|
||||
{
|
||||
int osec, n;
|
||||
|
@ -218,7 +234,7 @@ read_register(int reg_addr)
|
|||
/* */
|
||||
/***********************************************************************/
|
||||
|
||||
int
|
||||
static int
|
||||
arch_set_time(struct tm *t, int flags)
|
||||
{
|
||||
int regA, regB;
|
||||
|
@ -287,36 +303,17 @@ write_register(int reg_addr, int value)
|
|||
return OK;
|
||||
}
|
||||
|
||||
int
|
||||
static int
|
||||
arch_pwr_off(void)
|
||||
{
|
||||
/* Not Implemented */
|
||||
return ENOSYS;
|
||||
}
|
||||
|
||||
void
|
||||
static void
|
||||
arch_exit(void)
|
||||
{
|
||||
/* Nothing to clean up here */
|
||||
log_debug(&log, "Exiting...");
|
||||
}
|
||||
|
||||
int
|
||||
arch_sef_cb_lu_state_save(int UNUSED(state))
|
||||
{
|
||||
/* This arch doesn't have state to save */
|
||||
return OK;
|
||||
}
|
||||
|
||||
int
|
||||
arch_lu_state_restore(void)
|
||||
{
|
||||
/* This arch doesn't have state to restore */
|
||||
return OK;
|
||||
}
|
||||
|
||||
void
|
||||
arch_announce(void)
|
||||
{
|
||||
/* This arch doesn't need to do anything here. */
|
||||
}
|
||||
|
|
120
drivers/readclock/forward.c
Normal file
120
drivers/readclock/forward.c
Normal file
|
@ -0,0 +1,120 @@
|
|||
/*
|
||||
* Some real time clocks are embedded within other multi-function chips.
|
||||
* Drivers for such chips will implement the RTCDEV protocol and the
|
||||
* readclock driver will simply forward on the message to the driver.
|
||||
* This keeps things simple for any other services that need to access
|
||||
* the RTC as they only have to know / care about the readclock driver.
|
||||
*/
|
||||
|
||||
#include <minix/syslib.h>
|
||||
#include <minix/drvlib.h>
|
||||
#include <minix/sysutil.h>
|
||||
#include <minix/log.h>
|
||||
#include <minix/rs.h>
|
||||
#include <minix/ds.h>
|
||||
#include <minix/safecopies.h>
|
||||
|
||||
#include <sys/mman.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <assert.h>
|
||||
#include <time.h>
|
||||
#include <lib.h>
|
||||
|
||||
#include "forward.h"
|
||||
#include "readclock.h"
|
||||
|
||||
static int fwd_msg(int type, struct tm *t, int t_access, int flags);
|
||||
|
||||
static struct log log = {
|
||||
.name = "readclock.fwd",
|
||||
.log_level = LEVEL_INFO,
|
||||
.log_func = default_log
|
||||
};
|
||||
|
||||
/*
|
||||
* Label of the driver that messages should be forwarded to.
|
||||
*/
|
||||
static char *target_label;
|
||||
|
||||
int
|
||||
fwd_set_label(char *label)
|
||||
{
|
||||
target_label = label;
|
||||
return OK;
|
||||
}
|
||||
|
||||
int
|
||||
fwd_init(void)
|
||||
{
|
||||
if (target_label == NULL) {
|
||||
return EINVAL;
|
||||
}
|
||||
return OK;
|
||||
}
|
||||
|
||||
static int
|
||||
fwd_msg(int type, struct tm *t, int t_access, int flags)
|
||||
{
|
||||
int r;
|
||||
message m;
|
||||
endpoint_t ep;
|
||||
cp_grant_id_t gid;
|
||||
|
||||
r = ds_retrieve_label_endpt(target_label, &ep);
|
||||
if (r != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (type == RTCDEV_PWR_OFF) {
|
||||
/* RTCDEV_PWR_OFF messages don't contain any data/flags. */
|
||||
return _syscall(ep, RTCDEV_PWR_OFF, &m);
|
||||
}
|
||||
|
||||
gid = cpf_grant_direct(ep, (vir_bytes) t, sizeof(struct tm), t_access);
|
||||
if (!GRANT_VALID(gid)) {
|
||||
log_warn(&log, "Could not create grant.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
m.RTCDEV_GRANT = gid;
|
||||
m.RTCDEV_FLAGS = flags;
|
||||
|
||||
r = _syscall(ep, type, &m);
|
||||
cpf_revoke(gid);
|
||||
if (r != RTCDEV_REPLY || m.RTCDEV_STATUS != 0) {
|
||||
log_warn(&log, "Call to '%s' failed.\n", target_label);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
int
|
||||
fwd_get_time(struct tm *t, int flags)
|
||||
{
|
||||
return fwd_msg(RTCDEV_GET_TIME_G, t, CPF_WRITE, flags);
|
||||
}
|
||||
|
||||
int
|
||||
fwd_set_time(struct tm *t, int flags)
|
||||
{
|
||||
return fwd_msg(RTCDEV_SET_TIME_G, t, CPF_READ, flags);
|
||||
}
|
||||
|
||||
int
|
||||
fwd_pwr_off(void)
|
||||
{
|
||||
return fwd_msg(RTCDEV_PWR_OFF, NULL, 0, RTCDEV_NOFLAGS);
|
||||
}
|
||||
|
||||
void
|
||||
fwd_exit(void)
|
||||
{
|
||||
target_label = NULL;
|
||||
}
|
11
drivers/readclock/forward.h
Normal file
11
drivers/readclock/forward.h
Normal file
|
@ -0,0 +1,11 @@
|
|||
#ifndef __FORWARD_H
|
||||
#define __FORWARD_H
|
||||
|
||||
int fwd_set_label(char *label);
|
||||
int fwd_init(void);
|
||||
int fwd_get_time(struct tm *t, int flags);
|
||||
int fwd_set_time(struct tm *t, int flags);
|
||||
int fwd_pwr_off(void);
|
||||
void fwd_exit(void);
|
||||
|
||||
#endif /* __FORWARD_H */
|
|
@ -19,6 +19,8 @@
|
|||
|
||||
#include "readclock.h"
|
||||
|
||||
static struct rtc rtc;
|
||||
|
||||
static struct log log = {
|
||||
.name = "readclock",
|
||||
.log_level = LEVEL_INFO,
|
||||
|
@ -62,12 +64,13 @@ main(int argc, char **argv)
|
|||
|
||||
caller = m.m_source;
|
||||
|
||||
log_debug(&log, "Got message 0x%x from 0x%x\n", m.m_type, caller);
|
||||
log_debug(&log, "Got message 0x%x from 0x%x\n", m.m_type,
|
||||
caller);
|
||||
|
||||
switch (m.m_type) {
|
||||
case RTCDEV_GET_TIME:
|
||||
/* Any user can read the time */
|
||||
reply_status = arch_get_time(&t, m.RTCDEV_FLAGS);
|
||||
reply_status = rtc.get_time(&t, m.RTCDEV_FLAGS);
|
||||
if (reply_status != OK) {
|
||||
break;
|
||||
}
|
||||
|
@ -89,7 +92,7 @@ main(int argc, char **argv)
|
|||
}
|
||||
|
||||
reply_status =
|
||||
arch_set_time(&t, m.RTCDEV_FLAGS);
|
||||
rtc.set_time(&t, m.RTCDEV_FLAGS);
|
||||
} else {
|
||||
reply_status = EPERM;
|
||||
}
|
||||
|
@ -98,7 +101,7 @@ main(int argc, char **argv)
|
|||
case RTCDEV_PWR_OFF:
|
||||
/* Only PM is allowed to set the power off time */
|
||||
if (caller == PM_PROC_NR) {
|
||||
reply_status = arch_pwr_off();
|
||||
reply_status = rtc.pwr_off();
|
||||
} else {
|
||||
reply_status = EPERM;
|
||||
}
|
||||
|
@ -123,7 +126,7 @@ main(int argc, char **argv)
|
|||
}
|
||||
}
|
||||
|
||||
arch_exit();
|
||||
rtc.exit();
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -132,19 +135,16 @@ sef_cb_init(int type, sef_init_info_t * UNUSED(info))
|
|||
{
|
||||
int r;
|
||||
|
||||
if (type == SEF_INIT_LU) {
|
||||
/* Restore the state. */
|
||||
arch_lu_state_restore();
|
||||
}
|
||||
|
||||
r = arch_init();
|
||||
r = arch_setup(&rtc);
|
||||
if (r != OK) {
|
||||
log_warn(&log, "Clock setup failed\n");
|
||||
return r;
|
||||
}
|
||||
|
||||
if (type != SEF_INIT_LU) {
|
||||
/* Some RTCs need to do a driver announcement */
|
||||
arch_announce();
|
||||
r = rtc.init();
|
||||
if (r != OK) {
|
||||
log_warn(&log, "Clock initalization failed\n");
|
||||
return r;
|
||||
}
|
||||
|
||||
return OK;
|
||||
|
@ -167,8 +167,6 @@ sef_local_startup()
|
|||
sef_setcb_lu_prepare(sef_cb_lu_prepare_always_ready);
|
||||
/* Support live update starting from any standard state. */
|
||||
sef_setcb_lu_state_isvalid(sef_cb_lu_state_isvalid_standard);
|
||||
/* Register a custom routine to save the state. */
|
||||
sef_setcb_lu_state_save(arch_sef_cb_lu_state_save);
|
||||
|
||||
/* Let SEF perform startup. */
|
||||
sef_startup();
|
||||
|
|
|
@ -4,16 +4,15 @@
|
|||
#include <time.h>
|
||||
|
||||
/* implementations provided by arch/${MACHINE_ARCH}/arch_readclock.c */
|
||||
int arch_init(void); /* setup */
|
||||
int arch_get_time(struct tm *t, int flags); /* read the hardware clock into t */
|
||||
int arch_set_time(struct tm *t, int flags); /* set the hardware clock to t */
|
||||
int arch_pwr_off(void); /* set the power off alarm to 5 sec from now. */
|
||||
void arch_exit(void); /* clean up */
|
||||
struct rtc {
|
||||
int (*init)(void);
|
||||
int (*get_time)(struct tm *t, int flags);
|
||||
int (*set_time)(struct tm *t, int flags);
|
||||
int (*pwr_off)(void);
|
||||
void (*exit)(void);
|
||||
};
|
||||
|
||||
/* arch specific driver related functions */
|
||||
int arch_sef_cb_lu_state_save(int);
|
||||
int arch_lu_state_restore(void);
|
||||
void arch_announce(void);
|
||||
int arch_setup(struct rtc *r);
|
||||
|
||||
/* utility functions provided by readclock.c */
|
||||
int bcd_to_dec(int n);
|
||||
|
|
|
@ -233,6 +233,9 @@ start)
|
|||
up tps65950 -label tps65950.1.48 \
|
||||
-args 'bus=1 address=0x48'
|
||||
|
||||
# Set the system time to the time in the TPS65950's RTC
|
||||
readclock
|
||||
|
||||
;;
|
||||
esac
|
||||
|
||||
|
|
Loading…
Reference in a new issue