#include #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; struct machine machine ; sys_getmachine(&machine); if(! BOARD_IS_BB(machine.board_id)){ /* 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; } 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"); }