diff --git a/drivers/readclock/Makefile b/drivers/readclock/Makefile index 10f978d49..1d2f73171 100644 --- a/drivers/readclock/Makefile +++ b/drivers/readclock/Makefile @@ -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 diff --git a/drivers/readclock/arch/earm/Makefile.inc b/drivers/readclock/arch/earm/Makefile.inc index 37e9f3c04..24df8bed8 100644 --- a/drivers/readclock/arch/earm/Makefile.inc +++ b/drivers/readclock/arch/earm/Makefile.inc @@ -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 diff --git a/drivers/readclock/arch/earm/arch_readclock.c b/drivers/readclock/arch/earm/arch_readclock.c index 509984b44..f0b45fa18 100644 --- a/drivers/readclock/arch/earm/arch_readclock.c +++ b/drivers/readclock/arch/earm/arch_readclock.c @@ -1,8 +1,5 @@ #include #include -#include -#include -#include #include #include @@ -17,413 +14,28 @@ #include #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<RTC_SS_RTC_STATUS_REG) & (1<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 } diff --git a/drivers/readclock/arch/earm/omap_rtc.c b/drivers/readclock/arch/earm/omap_rtc.c new file mode 100644 index 000000000..dc9cad682 --- /dev/null +++ b/drivers/readclock/arch/earm/omap_rtc.c @@ -0,0 +1,415 @@ +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#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<RTC_SS_RTC_STATUS_REG) & (1<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"); +} diff --git a/drivers/readclock/arch/earm/omap_rtc.h b/drivers/readclock/arch/earm/omap_rtc.h index d22cdbbf7..28a5a1949 100644 --- a/drivers/readclock/arch/earm/omap_rtc.h +++ b/drivers/readclock/arch/earm/omap_rtc.h @@ -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 */ diff --git a/drivers/readclock/arch/i386/arch_readclock.c b/drivers/readclock/arch/i386/arch_readclock.c index dc7e97d8f..68071e783 100644 --- a/drivers/readclock/arch/i386/arch_readclock.c +++ b/drivers/readclock/arch/i386/arch_readclock.c @@ -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. */ -} diff --git a/drivers/readclock/forward.c b/drivers/readclock/forward.c new file mode 100644 index 000000000..69b73f3dd --- /dev/null +++ b/drivers/readclock/forward.c @@ -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 +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#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; +} diff --git a/drivers/readclock/forward.h b/drivers/readclock/forward.h new file mode 100644 index 000000000..82a11335e --- /dev/null +++ b/drivers/readclock/forward.h @@ -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 */ diff --git a/drivers/readclock/readclock.c b/drivers/readclock/readclock.c index da01dc3db..7f3c0d7f3 100644 --- a/drivers/readclock/readclock.c +++ b/drivers/readclock/readclock.c @@ -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(); diff --git a/drivers/readclock/readclock.h b/drivers/readclock/readclock.h index 1ed99110c..a0169a10f 100644 --- a/drivers/readclock/readclock.h +++ b/drivers/readclock/readclock.h @@ -4,16 +4,15 @@ #include /* 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); diff --git a/etc/usr/rc b/etc/usr/rc index 7b830faef..6667e45d4 100644 --- a/etc/usr/rc +++ b/etc/usr/rc @@ -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