.include "arch/${MACHINE_ARCH}/Makefile.inc"
-SRCS+= readclock.c
+SRCS+= readclock.c forward.c forward.h
DPADD+= ${LIBSYS} ${LIBTIMERS}
LDADD+= -lsys -ltimers
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
#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 <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)
-{
- 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
-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 */
+arch_setup(struct rtc *r)
+{
+#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;
-}
-
-int
-arch_lu_state_restore(void)
-{
- /* Nothing to restore yet -- the TPS65950 RTC driver will need this */
+#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;
-}
-
-void
-arch_announce(void)
-{
- /* Nothing to announce yet -- the TPS65950 RTC driver will need this */
+#else
+ return ENOSYS;
+#endif
}
--- /dev/null
+#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");
+}
#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 */
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;
/* */
/***********************************************************************/
-int
+static int
arch_get_time(struct tm *t, int flags)
{
int osec, n;
/* */
/***********************************************************************/
-int
+static int
arch_set_time(struct tm *t, int flags)
{
int regA, regB;
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. */
-}
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+#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 */
#include "readclock.h"
+static struct rtc rtc;
+
static struct log log = {
.name = "readclock",
.log_level = LEVEL_INFO,
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;
}
}
reply_status =
- arch_set_time(&t, m.RTCDEV_FLAGS);
+ rtc.set_time(&t, m.RTCDEV_FLAGS);
} else {
reply_status = EPERM;
}
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;
}
}
}
- arch_exit();
+ rtc.exit();
return 0;
}
{
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;
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();
#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);
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