/* * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include #include #include #include #ifndef NS_PER_S #define NS_PER_S UINT64_C(1000000000) #endif struct StmRtc { volatile uint32_t TR; /* 0x00 */ volatile uint32_t DR; /* 0x04 */ volatile uint32_t CR; /* 0x08 */ volatile uint32_t ISR; /* 0x0C */ volatile uint32_t PRER; /* 0x10 */ volatile uint32_t WUTR; /* 0x14 */ volatile uint32_t CALIBR; /* 0x18 */ volatile uint32_t ALRMAR; /* 0x1C */ volatile uint32_t ALRMBR; /* 0x20 */ volatile uint32_t WPR; /* 0x24 */ volatile uint32_t SSR; /* 0x28 */ volatile uint32_t SHIFTR; /* 0x2C */ volatile uint32_t TSTR; /* 0x30 */ volatile uint32_t TSDR; /* 0x34 */ volatile uint32_t TSSSR; /* 0x38 */ volatile uint32_t CALR; /* 0x3C */ volatile uint32_t TAFCR; /* 0x40 */ volatile uint32_t ALRMASSR; /* 0x44 */ volatile uint32_t ALRMBSSR; /* 0x48 */ uint8_t unused0[4]; /* 0x4C */ volatile uint32_t BKPR[20]; /* 0x50 - 0x9C */ }; #define RTC ((struct StmRtc*)RTC_BASE) /* RTC bit defintions */ #define RTC_CR_WUCKSEL_MASK 0x00000007UL #define RTC_CR_WUCKSEL_16DIV 0x00000000UL #define RTC_CR_WUCKSEL_8DIV 0x00000001UL #define RTC_CR_WUCKSEL_4DIV 0x00000002UL #define RTC_CR_WUCKSEL_2DIV 0x00000003UL #define RTC_CR_WUCKSEL_CK_SPRE 0x00000004UL #define RTC_CR_WUCKSEL_CK_SPRE_2 0x00000006UL #define RTC_CR_BYPSHAD 0x00000020UL #define RTC_CR_FMT 0x00000040UL #define RTC_CR_ALRAE 0x00000100UL #define RTC_CR_WUTE 0x00000400UL #define RTC_CR_ALRAIE 0x00001000UL #define RTC_CR_WUTIE 0x00004000UL #define RTC_ISR_ALRAWF 0x00000001UL #define RTC_ISR_WUTWF 0x00000004UL #define RTC_ISR_RSF 0x00000020UL #define RTC_ISR_INITF 0x00000040UL #define RTC_ISR_INIT 0x00000080UL #define RTC_ISR_WUTF 0x00000400UL /* RTC internal values */ #define RTC_FREQ_HZ 32768UL #define RTC_WKUP_DOWNCOUNT_MAX 0x10000UL /* TODO: Reset to crystal PPM once known */ #define RTC_PPM 50UL /* Default prescalars of P[async] = 127 and P[sync] = 255 are appropriate * produce a 1 Hz clock when using a 32.768kHZ clock source */ #ifndef RTC_PREDIV_A #define RTC_PREDIV_A 31UL #endif #ifndef RTC_PREDIV_S #define RTC_PREDIV_S 1023UL #endif #ifndef RTC_CALM #define RTC_CALM 0 #endif #ifndef RTC_CALP #define RTC_CALP 0 #endif /* Jitter = max wakeup timer resolution (61.035 us) * + 2 RTC cycles for synchronization (61.035 us) */ #define RTC_DIV2_PERIOD_NS UINT64_C(61035) #define RTC_DIV4_PERIOD_NS UINT64_C(122070) #define RTC_DIV8_PERIOD_NS UINT64_C(244141) #define RTC_DIV16_PERIOD_NS UINT64_C(488281) #define RTC_VALID_DELAY_FOR_PERIOD(delay, period) \ (delay < (period * (RTC_WKUP_DOWNCOUNT_MAX + 1))) static void rtcSetDefaultDateTimeAndPrescalar(void) { /* Enable writability of RTC registers */ RTC->WPR = 0xCA; RTC->WPR = 0x53; /* Enter RTC init mode */ RTC->ISR |= RTC_ISR_INIT; mem_reorder_barrier(); /* Wait for initialization mode to be entered. */ while ((RTC->ISR & RTC_ISR_INITF) == 0); /* Set prescalar rtc register. Two writes required. */ RTC->PRER = RTC_PREDIV_S; RTC->PRER |= (RTC_PREDIV_A << 16); RTC->CALR = (RTC_CALP << 15) | (RTC_CALM & 0x1FF); /* 24 hour format */ RTC->CR &= ~RTC_CR_FMT; /* disable shadow registers */ RTC->CR |= RTC_CR_BYPSHAD; /* Set time and date registers to defaults */ /* Midnight */ RTC->TR = 0x0; RTC->SSR = 0x0; /* Sat Jan 1st, 2000 BCD */ RTC->DR = 0b1100000100000001; /* Exit init mode for RTC */ RTC->ISR &= ~RTC_ISR_INIT; /* Re-enable register write protection. RTC counting doesn't start for * 4 RTC cycles after set - must poll RSF before read DR or TR */ RTC->WPR = 0xFF; extiEnableIntLine(EXTI_LINE_RTC_WKUP, EXTI_TRIGGER_RISING); NVIC_EnableIRQ(RTC_WKUP_IRQn); } void rtcInit(void) { pwrEnableAndClockRtc(RTC_CLK); rtcSetDefaultDateTimeAndPrescalar(); } /* Set calendar alarm to go off after delay has expired. uint64_t delay must * be in valid uint64_t format */ int rtcSetWakeupTimer(uint64_t delay) { uint64_t intState; uint64_t periodNsRecip; uint32_t wakeupClock; uint32_t periodNs; /* Minimum wakeup interrupt period is 122 us, max is 36.4 hours */ if (delay < (RTC_DIV2_PERIOD_NS * 2)) { return RTC_ERR_TOO_SMALL; } else if (delay > (NS_PER_S * 2 * RTC_WKUP_DOWNCOUNT_MAX)) { delay = NS_PER_S * 2 * RTC_WKUP_DOWNCOUNT_MAX; } /* Get appropriate clock period for delay size. Wakeup clock = RTC/x. */ if (RTC_VALID_DELAY_FOR_PERIOD(delay, RTC_DIV2_PERIOD_NS)) { wakeupClock = RTC_CR_WUCKSEL_2DIV; periodNs = RTC_DIV2_PERIOD_NS; periodNsRecip = U64_RECIPROCAL_CALCULATE(RTC_DIV2_PERIOD_NS); } else if (RTC_VALID_DELAY_FOR_PERIOD(delay, RTC_DIV4_PERIOD_NS)) { wakeupClock = RTC_CR_WUCKSEL_4DIV; periodNs = RTC_DIV4_PERIOD_NS; periodNsRecip = U64_RECIPROCAL_CALCULATE(RTC_DIV4_PERIOD_NS); } else if (RTC_VALID_DELAY_FOR_PERIOD(delay, RTC_DIV8_PERIOD_NS)) { wakeupClock = RTC_CR_WUCKSEL_8DIV; periodNs = RTC_DIV8_PERIOD_NS; periodNsRecip = U64_RECIPROCAL_CALCULATE(RTC_DIV8_PERIOD_NS); } else if (RTC_VALID_DELAY_FOR_PERIOD(delay, RTC_DIV16_PERIOD_NS)) { wakeupClock = RTC_CR_WUCKSEL_16DIV; periodNs = RTC_DIV16_PERIOD_NS; periodNsRecip = U64_RECIPROCAL_CALCULATE(RTC_DIV16_PERIOD_NS); } else { if (RTC_VALID_DELAY_FOR_PERIOD(delay, NS_PER_S)) wakeupClock = RTC_CR_WUCKSEL_CK_SPRE; else wakeupClock = RTC_CR_WUCKSEL_CK_SPRE_2; periodNs = NS_PER_S; periodNsRecip = U64_RECIPROCAL_CALCULATE(NS_PER_S); } intState = cpuIntsOff(); /* Enable RTC register write */ RTC->WPR = 0xCA; RTC->WPR = 0x53; /* Disable wakeup timer */ RTC->CR &= ~RTC_CR_WUTE; /* Wait for access enabled for wakeup timer registers */ while ((RTC->ISR & RTC_ISR_WUTWF) == 0); /* Clear wakeup clock source */ RTC->CR &= ~RTC_CR_WUCKSEL_MASK; RTC->CR |= wakeupClock; /* Downcounter value for wakeup clock. Wakeup flag is set every * RTC->WUTR[15:0] + 1 cycles of the WUT clock. */ RTC->WUTR = cpuMathRecipAssistedUdiv64by32(delay, periodNs, periodNsRecip) - 1; /* Enable wakeup interrupts */ RTC->CR |= RTC_CR_WUTIE; extiClearPendingLine(EXTI_LINE_RTC_WKUP); /* Enable wakeup timer */ RTC->CR |= RTC_CR_WUTE; /* Clear overflow flag */ RTC->ISR &= ~RTC_ISR_WUTF; /* Write-protect RTC registers */ RTC->WPR = 0xFF; cpuIntsRestore(intState); return 0; } uint64_t rtcGetTime(void) { int32_t time_s; uint32_t dr, tr, ssr; // cumulative adjustments from 32 day months (year 2000) // 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 // 1, 3, 1, 2, 1, 2, 1, 1, 2, 1, 2, 1 // 0 1, 4, 5, 7, 8, 10, 11, 12, 14, 15, 17 static const uint8_t adjust[] = { 0, 1, 4, 5, 7, 8, 10, 11, 12, 14, 15, 17 }; uint8_t month; // need to loop incase an interrupt occurs in the middle or ssr // decrements (which can propagate changes to tr and dr) do { ssr = RTC->SSR; tr = RTC->TR; dr = RTC->DR; } while (ssr != RTC->SSR); month = (((dr >> 12) & 0x1) * 10) + ((dr >> 8) & 0xf) - 1; time_s = (((((dr >> 4) & 0x3) * 10) + (dr & 0xF) - 1) + (month << 5) - adjust[month]) * 86400ULL; time_s += ((((tr >> 22) & 0x1) * 43200ULL) + (((tr >> 20) & 0x3) * 36000ULL) + (((tr >> 16) & 0xF) * 3600ULL) + (((tr >> 12) & 0x7) * 600ULL) + (((tr >> 8) & 0xF) * 60ULL) + (((tr >> 4) & 0x7) * 10ULL) + (((tr) & 0xF))); return (time_s * NS_PER_S) + U64_DIV_BY_CONST_U16(((RTC_PREDIV_S - ssr) * NS_PER_S), (RTC_PREDIV_S + 1)); } void EXTI22_RTC_WKUP_IRQHandler(void); void EXTI22_RTC_WKUP_IRQHandler(void) { extiClearPendingLine(EXTI_LINE_RTC_WKUP); timIntHandler(); } uint32_t* rtcGetBackupStorage(void) { return (uint32_t*)RTC->BKPR; }