/* * 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 <cpu/cpuMath.h> #include <plat/gpio.h> #include <plat/usart.h> #include <plat/cmsis.h> #include <plat/pwr.h> #include <plat/rtc.h> #include <plat/plat.h> #include <plat/exti.h> #include <plat/wdt.h> #include <plat/dma.h> #include <stdbool.h> #include <stdio.h> #include <string.h> #include <pthread.h> #include <unistd.h> #include <platform.h> #include <seos.h> #include <heap.h> #include <timer.h> #include <usart.h> #include <gpio.h> #include <mpu.h> #include <cpu.h> #include <hostIntf.h> #include <atomic.h> #include <hostIntf.h> #include <nanohubPacket.h> #include <sensType.h> #include <variant/variant.h> struct StmDbgmcu { volatile uint32_t IDCODE; volatile uint32_t CR; volatile uint32_t APB1FZ; volatile uint32_t APB2FZ; }; struct StmTim { volatile uint16_t CR1; uint8_t unused0[2]; volatile uint16_t CR2; uint8_t unused1[2]; volatile uint16_t SMCR; uint8_t unused2[2]; volatile uint16_t DIER; uint8_t unused3[2]; volatile uint16_t SR; uint8_t unused4[2]; volatile uint16_t EGR; uint8_t unused5[2]; volatile uint16_t CCMR1; uint8_t unused6[2]; volatile uint16_t CCMR2; uint8_t unused7[2]; volatile uint16_t CCER; uint8_t unused8[2]; volatile uint32_t CNT; volatile uint16_t PSC; uint8_t unused9[2]; volatile uint32_t ARR; volatile uint16_t RCR; uint8_t unused10[2]; volatile uint32_t CCR1; volatile uint32_t CCR2; volatile uint32_t CCR3; volatile uint32_t CCR4; volatile uint16_t BDTR; uint8_t unused11[2]; volatile uint16_t DCR; uint8_t unused12[2]; volatile uint16_t DMAR; uint8_t unused13[2]; volatile uint16_t OR; uint8_t unused14[2]; }; #define TIM2 ((struct StmTim*)TIM2_BASE) #define DBGMCU ((struct StmDbgmcu*)DBGMCU_BASE) /* RTC bit defintions */ #define TIM_EGR_UG 0x0001 /* DBGMCU bit definition */ #define DBG_SLEEP 0x00000001 #define DBG_STOP 0x00000002 #define DBG_STANDBY 0x00000004 #ifdef DEBUG_UART_UNITNO static struct usart mDbgUart; #endif #ifdef DEBUG_LOG_EVT #ifndef EARLY_LOG_BUF_SIZE #define EARLY_LOG_BUF_SIZE 2048 #endif #define HOSTINTF_HEADER_SIZE 4 uint8_t *mEarlyLogBuffer; uint16_t mEarlyLogBufferCnt; uint16_t mEarlyLogBufferOffset; bool mLateBoot; #endif static uint64_t mTimeAccumulated = 0; static uint32_t mMaxJitterPpm = 0, mMaxDriftPpm = 0, mMaxErrTotalPpm = 0; static uint32_t mSleepDevsToKeepAlive = 0; static uint64_t mWakeupTime = 0; static uint32_t mDevsMaxWakeTime[PLAT_MAX_SLEEP_DEVS] = {0,}; void platUninitialize(void) { #ifdef DEBUG_UART_UNITNO usartClose(&mDbgUart); #endif } void *platLogAllocUserData() { #if defined(DEBUG_LOG_EVT) struct HostIntfDataBuffer *userData = NULL; if (mLateBoot) { userData = heapAlloc(sizeof(struct HostIntfDataBuffer)); } else if (mEarlyLogBufferOffset < EARLY_LOG_BUF_SIZE - HOSTINTF_HEADER_SIZE) { userData = (struct HostIntfDataBuffer *)(mEarlyLogBuffer + mEarlyLogBufferOffset); mEarlyLogBufferOffset += HOSTINTF_HEADER_SIZE; } if (userData) { userData->sensType = SENS_TYPE_INVALID; userData->length = 0; userData->dataType = HOSTINTF_DATA_TYPE_LOG; userData->interrupt = NANOHUB_INT_NONWAKEUP; } return userData; #else return NULL; #endif } #if defined(DEBUG_LOG_EVT) static void platEarlyLogFree(void *buf) { struct HostIntfDataBuffer *userData = (struct HostIntfDataBuffer *)buf; mEarlyLogBufferCnt += userData->length + HOSTINTF_HEADER_SIZE; if (mEarlyLogBufferCnt >= mEarlyLogBufferOffset) { heapFree(mEarlyLogBuffer); } } #endif void platEarlyLogFlush(void) { #if defined(DEBUG_LOG_EVT) uint16_t i = 0; struct HostIntfDataBuffer *userData; mLateBoot = true; while (i < mEarlyLogBufferOffset) { userData = (struct HostIntfDataBuffer *)(mEarlyLogBuffer + i); osEnqueueEvt(EVENT_TYPE_BIT_DISCARDABLE | EVT_DEBUG_LOG, userData, platEarlyLogFree); i += HOSTINTF_HEADER_SIZE + userData->length; } #endif } void platLogFlush(void *userData) { #ifdef DEBUG_UART_UNITNO usartFlush(&mDbgUart); #endif #if defined(DEBUG_LOG_EVT) if (userData && mLateBoot) osEnqueueEvtOrFree(EVENT_TYPE_BIT_DISCARDABLE | EVT_DEBUG_LOG, userData, heapFree); #endif } bool platLogPutcharF(void *userData, char ch) { #if defined(DEBUG) && defined(DEBUG_UART_PIN) if (ch == '\n') gpioBitbangedUartOut('\r'); gpioBitbangedUartOut(ch); #endif #if defined(DEBUG_UART_UNITNO) if (ch == '\n') usartPutchar(&mDbgUart, '\r'); usartPutchar(&mDbgUart, ch); #endif #if defined(DEBUG_LOG_EVT) struct HostIntfDataBuffer *buffer; if (userData) { buffer = userData; size_t maxSize = sizeof(buffer->buffer); // if doing early logging, and early log buffer is full, ignore the rest of early output if (!mLateBoot && mEarlyLogBufferOffset >= EARLY_LOG_BUF_SIZE && buffer->length < maxSize) maxSize = buffer->length; if (buffer->length < maxSize) { buffer->buffer[buffer->length++] = ch; if (!mLateBoot) mEarlyLogBufferOffset++; } else { buffer->buffer[maxSize - 1] = '\n'; return false; } } #endif return true; } void platInitialize(void) { const uint32_t debugStateInSleepMode = DBG_SLEEP | DBG_STOP | DBG_STANDBY; uint32_t i; pwrSystemInit(); //prepare for sleep mode(s) SCB->SCR &=~ SCB_SCR_SLEEPONEXIT_Msk; //set ints up for a sane state //3 bits preemptPriority, 1 bit subPriority NVIC_SetPriorityGrouping(4); for (i = 0; i < NUM_INTERRUPTS; i++) { NVIC_SetPriority(i, NVIC_EncodePriority(4, 2, 1)); NVIC_DisableIRQ(i); NVIC_ClearPendingIRQ(i); } /* disable pins */ for (i = 0; i < 16; i++) { #if defined(DEBUG) && defined(DEBUG_SWD) /* pins PA13 and PA14 are used for SWD */ if ((i != 13) && (i != 14)) gpioConfigAnalog(gpioRequest(GPIO_PA(i))); #else gpioConfigAnalog(gpioRequest(GPIO_PA(i))); #endif gpioConfigAnalog(gpioRequest(GPIO_PB(i))); gpioConfigAnalog(gpioRequest(GPIO_PC(i))); gpioConfigAnalog(gpioRequest(GPIO_PD(i))); gpioConfigAnalog(gpioRequest(GPIO_PE(i))); gpioConfigAnalog(gpioRequest(GPIO_PH(i))); } #ifdef DEBUG_UART_UNITNO /* Open mDbgUart on PA2 and PA3 */ usartOpen(&mDbgUart, DEBUG_UART_UNITNO, DEBUG_UART_GPIO_TX, DEBUG_UART_GPIO_RX, 115200, USART_DATA_BITS_8, USART_STOP_BITS_1_0, USART_PARITY_NONE, USART_FLOW_CONTROL_NONE); #endif /* set up debugging */ #if defined(DEBUG) && defined(DEBUG_SWD) DBGMCU->CR |= debugStateInSleepMode; #else DBGMCU->CR &=~ debugStateInSleepMode; #endif /* enable MPU */ mpuStart(); /* set up timer used for alarms */ pwrUnitClock(PERIPH_BUS_APB1, PERIPH_APB1_TIM2, true); TIM2->CR1 = (TIM2->CR1 &~ 0x03E1) | 0x0010; //count down mode with no clock division, disabled TIM2->PSC = 15; // prescale by 16, so that at 16MHz CPU clock, we get 1MHz timer TIM2->DIER |= 1; // interrupt when updated (underflowed) TIM2->ARR = 0xffffffff; TIM2->EGR = TIM_EGR_UG; // force a reload of the prescaler NVIC_EnableIRQ(TIM2_IRQn); rtcInit(); /* bring up systick */ SysTick->CTRL = 0; SysTick->LOAD = 0x00FFFFFF; SysTick->VAL = 0; SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk | SysTick_CTRL_TICKINT_Msk | SysTick_CTRL_ENABLE_Msk; #ifdef DEBUG_LOG_EVT /* allocate buffer for early boot log message*/ mEarlyLogBuffer = heapAlloc(EARLY_LOG_BUF_SIZE); #endif } static uint64_t platsystickTicksToNs(uint32_t systickTicks) { return (uint64_t)systickTicks * 125 / 2; } uint64_t platGetTicks(void) { uint64_t ret; uint32_t val; do { mem_reorder_barrier(); //mTimeAccumulated may change since it was read in condition check ret = mTimeAccumulated; val = SysTick->VAL; mem_reorder_barrier(); //mTimeAccumulated may change since it was read above } while (mTimeAccumulated != ret || SysTick->VAL > val); return platsystickTicksToNs(0x01000000 - val) + ret; } /* Timer interrupt handler */ void TIM2_IRQHandler(void); void TIM2_IRQHandler(void) { struct StmTim *tim = (struct StmTim*)TIM2_BASE; /* int clear */ tim->SR &=~ 1; /* timer off */ tim->CR1 &=~ 1; /* call timer handler since it might need to reschedule an interrupt (eg: in case where initial delay was too far off & we were limited by timer length) */ timIntHandler(); } /* SysTick interrupt handler */ void SysTick_Handler(void); void SysTick_Handler(void) { mTimeAccumulated += platsystickTicksToNs(SysTick->LOAD + 1); //todo - incremenet by actual elapsed nanoseconds and not just "1" } bool platRequestDevInSleepMode(uint32_t sleepDevID, uint32_t maxWakeupTime) { if (sleepDevID >= PLAT_MAX_SLEEP_DEVS || sleepDevID >= Stm32sleepDevNum) return false; mDevsMaxWakeTime[sleepDevID] = maxWakeupTime; while (!atomicCmpXchg32bits(&mSleepDevsToKeepAlive, mSleepDevsToKeepAlive, mSleepDevsToKeepAlive | (1UL << sleepDevID))); return true; } bool platAdjustDevInSleepMode(uint32_t sleepDevID, uint32_t maxWakeupTime) { if (sleepDevID >= PLAT_MAX_SLEEP_DEVS || sleepDevID >= Stm32sleepDevNum) return false; mDevsMaxWakeTime[sleepDevID] = maxWakeupTime; return true; } bool platReleaseDevInSleepMode(uint32_t sleepDevID) { if (sleepDevID >= PLAT_MAX_SLEEP_DEVS || sleepDevID >= Stm32sleepDevNum) return false; while (!atomicCmpXchg32bits(&mSleepDevsToKeepAlive, mSleepDevsToKeepAlive, mSleepDevsToKeepAlive &~ (1UL << sleepDevID))); return true; } static uint64_t platSetTimerAlarm(uint64_t delay) //delay at most that many nsec { struct StmTim *tim = (struct StmTim*)TIM2_BASE; uint32_t delayInUsecs; //turn off timer to prevent interrupts now tim->CR1 &=~ 1; if (delay >= (1000ULL << 32)) //it is only a 32-bit counter - we cannot set delays bigger than that delayInUsecs = 0xffffffff; else delayInUsecs = cpuMathUint44Div1000ToUint32(delay); tim->CNT = delayInUsecs; tim->SR &=~ 1; //clear int tim->CR1 |= 1; return delayInUsecs; } bool platSleepClockRequest(uint64_t wakeupTime, uint32_t maxJitterPpm, uint32_t maxDriftPpm, uint32_t maxErrTotalPpm) { uint64_t intState, curTime = timGetTime(); if (wakeupTime && curTime >= wakeupTime) return false; intState = cpuIntsOff(); mMaxJitterPpm = maxJitterPpm; mMaxDriftPpm = maxDriftPpm; mMaxErrTotalPpm = maxErrTotalPpm; mWakeupTime = wakeupTime; //TODO: set an actual alarm here so that if we keep running and do not sleep till this is due, we still fire an interrupt for it! if (wakeupTime) platSetTimerAlarm(wakeupTime - curTime); cpuIntsRestore(intState); return true; } #if !(defined(STM32F4xx_DISABLE_LPLV_SLEEP) && defined(STM32F4xx_DISABLE_LPFD_SLEEP) \ && defined(STM32F4xx_DISABLE_MRFPD_SLEEP) && defined(STM32F4xx_DISABLE_MR_SLEEP)) static bool sleepClockRtcPrepare(uint64_t delay, uint32_t acceptableJitter, uint32_t acceptableDrift, uint32_t maxAcceptableError, void *userData, uint64_t *savedData) { pwrSetSleepType((uint32_t)userData); *savedData = rtcGetTime(); if (delay && rtcSetWakeupTimer(delay) < 0) return false; //sleep with systick off (for timing) and interrupts off (for power due to HWR errata) SysTick->CTRL &= ~(SysTick_CTRL_TICKINT_Msk | SysTick_CTRL_ENABLE_Msk); return true; } static void sleepClockRtcWake(void *userData, uint64_t *savedData) { //re-enable Systic and its interrupt SysTick->CTRL |= SysTick_CTRL_TICKINT_Msk | SysTick_CTRL_ENABLE_Msk; mTimeAccumulated += rtcGetTime() - *savedData; } #endif static bool sleepClockTmrPrepare(uint64_t delay, uint32_t acceptableJitter, uint32_t acceptableDrift, uint32_t maxAcceptableError, void *userData, uint64_t *savedData) { pwrSetSleepType(stm32f411SleepModeSleep); platRequestDevInSleepMode(Stm32sleepDevTim2, 0); *savedData = platSetTimerAlarm(delay ?: ~0ull); //sleep with systick off (for timing) and interrupts off (for power due to HWR errata) SysTick->CTRL &= ~(SysTick_CTRL_TICKINT_Msk | SysTick_CTRL_ENABLE_Msk); return true; } static void sleepClockTmrWake(void *userData, uint64_t *savedData) { struct StmTim *tim = (struct StmTim*)TIM2_BASE; uint32_t cnt; uint16_t sr; uint64_t leftTicks; //re-enable Systic and its interrupt SysTick->CTRL |= SysTick_CTRL_TICKINT_Msk | SysTick_CTRL_ENABLE_Msk; //stop the timer counting; tim->CR1 &=~ 1; //If we are within one time tick of overflow, it is possible for SR to //not indicate a pending overflow, but CNT contain 0xFFFFFFFF or vice versa, //depending on the read order of SR and CNT //read both values until they are stable do { sr = tim->SR; cnt = tim->CNT; } while (sr != tim->SR || cnt != tim->CNT); leftTicks = cnt; //if we wake NOT from timer, only count the ticks that actually ticked as "time passed" if (sr & 1) //if there was an overflow, account for it leftTicks -= 0x100000000ull; mTimeAccumulated += (*savedData - leftTicks) * 1000; //this clock runs at 1MHz platReleaseDevInSleepMode(Stm32sleepDevTim2); } static bool sleepClockJustWfiPrepare(uint64_t delay, uint32_t acceptableJitter, uint32_t acceptableDrift, uint32_t maxAcceptableError, void *userData, uint64_t *savedData) { pwrSetSleepType(stm32f411SleepModeSleep); return true; } struct PlatSleepAndClockInfo { uint64_t resolution; uint64_t resolutionReciprocal; // speed up runtime by using 48 more code bytes? yes please! uint32_t maxCounter; uint32_t jitterPpm; uint32_t driftPpm; uint32_t maxWakeupTime; uint32_t devsAvail; //what is available in sleep mode? bool (*prepare)(uint64_t delay, uint32_t acceptableJitter, uint32_t acceptableDrift, uint32_t maxAcceptableError, void *userData, uint64_t *savedData); void (*wake)(void *userData, uint64_t *savedData); void *userData; } static const platSleepClocks[] = { #ifndef STM32F4xx_DISABLE_LPLV_SLEEP { /* RTC + LPLV STOP MODE */ .resolution = 1000000000ull/32768, .resolutionReciprocal = U64_RECIPROCAL_CALCULATE(1000000000ull/32768), .maxCounter = 0xffffffff, .jitterPpm = 0, .driftPpm = 50, .maxWakeupTime = 407000ull, .devsAvail = (1 << Stm32sleepDevExti), .prepare = sleepClockRtcPrepare, .wake = sleepClockRtcWake, .userData = (void*)stm32f411SleepModeStopLPLV, }, #endif #ifndef STM32F4xx_DISABLE_LPFD_SLEEP { /* RTC + LPFD STOP MODE */ .resolution = 1000000000ull/32768, .resolutionReciprocal = U64_RECIPROCAL_CALCULATE(1000000000ull/32768), .maxCounter = 0xffffffff, .jitterPpm = 0, .driftPpm = 50, .maxWakeupTime = 130000ull, .devsAvail = (1 << Stm32sleepDevExti), .prepare = sleepClockRtcPrepare, .wake = sleepClockRtcWake, .userData = (void*)stm32f411SleepModeStopLPFD, }, #endif #ifndef STM32F4xx_DISABLE_MRFPD_SLEEP { /* RTC + MRFPD STOP MODE */ .resolution = 1000000000ull/32768, .resolutionReciprocal = U64_RECIPROCAL_CALCULATE(1000000000ull/32768), .maxCounter = 0xffffffff, .jitterPpm = 0, .driftPpm = 50, .maxWakeupTime = 111000ull, .devsAvail = (1 << Stm32sleepDevExti), .prepare = sleepClockRtcPrepare, .wake = sleepClockRtcWake, .userData = (void*)stm32f411SleepModeStopMRFPD, }, #endif #ifndef STM32F4xx_DISABLE_MR_SLEEP { /* RTC + MR STOP MODE */ .resolution = 1000000000ull/32768, .resolutionReciprocal = U64_RECIPROCAL_CALCULATE(1000000000ull/32768), .maxCounter = 0xffffffff, .jitterPpm = 0, .driftPpm = 50, .maxWakeupTime = 14500ull, .devsAvail = (1 << Stm32sleepDevExti), .prepare = sleepClockRtcPrepare, .wake = sleepClockRtcWake, .userData = (void*)stm32f411SleepModeStopMR, }, #endif #ifndef STM32F4xx_DISABLE_TIM2_SLEEP { /* TIM2 + SLEEP MODE */ .resolution = 1000000000ull/1000000, .resolutionReciprocal = U64_RECIPROCAL_CALCULATE(1000000000ull/1000000), .maxCounter = 0xffffffff, .jitterPpm = 0, .driftPpm = 30, .maxWakeupTime = 12ull, .devsAvail = (1 << Stm32sleepDevTim2) | (1 << Stm32sleepDevTim4) | (1 << Stm32sleepDevTim5) | (1 << Stm32sleepDevTim9) | (1 << Stm32sleepWakeup) | (1 << Stm32sleepDevSpi2) | (1 << Stm32sleepDevSpi3) | (1 << Stm32sleepDevI2c1) | (1 << Stm32sleepDevI2c2) | (1 << Stm32sleepDevI2c3) | (1 << Stm32sleepDevExti), .prepare = sleepClockTmrPrepare, .wake = sleepClockTmrWake, }, #endif { /* just WFI */ .resolution = 16000000000ull/1000000, .resolutionReciprocal = U64_RECIPROCAL_CALCULATE(16000000000ull/1000000), .maxCounter = 0xffffffff, .jitterPpm = 0, .driftPpm = 0, .maxWakeupTime = 0, .devsAvail = (1 << Stm32sleepDevTim2) | (1 << Stm32sleepDevTim4) | (1 << Stm32sleepDevTim5) | (1 << Stm32sleepDevTim9) | (1 << Stm32sleepWakeup) | (1 << Stm32sleepDevSpi2) | (1 << Stm32sleepDevSpi3) | (1 << Stm32sleepDevI2c1) | (1 << Stm32sleepDevI2c2) | (1 << Stm32sleepDevI2c3) | (1 << Stm32sleepDevExti), .prepare = sleepClockJustWfiPrepare, }, /* terminator */ {0}, }; void platSleep(void) { uint64_t predecrement = 0, curTime = timGetTime(), length = mWakeupTime - curTime, intState; const struct PlatSleepAndClockInfo *sleepClock, *leastBadOption = NULL; uint64_t savedData; uint32_t i; //shortcut the sleep if it is time to wake up already if (mWakeupTime && mWakeupTime < curTime) return; for (sleepClock = platSleepClocks; sleepClock->maxCounter; sleepClock++) { bool potentialLeastBadOption = false; //if we have timers, consider them if (mWakeupTime) { //calculate how much we WOULD predecerement by predecrement = sleepClock->resolution + sleepClock->maxWakeupTime; //skip options with too much jitter (after accounting for error if (sleepClock->jitterPpm > mMaxJitterPpm) continue; //skip options that will take too long to wake up to be of use if (predecrement > length) continue; //skip options with too much drift if (sleepClock->driftPpm > mMaxDriftPpm) continue; //skip options that do not let us sleep enough, but save them for later if we simply must pick something if (cpuMathRecipAssistedUdiv64by64(length, sleepClock->resolution, sleepClock->resolutionReciprocal) > sleepClock->maxCounter && !leastBadOption) potentialLeastBadOption = true; } //skip all options that do not keep enough deviceas awake if ((sleepClock->devsAvail & mSleepDevsToKeepAlive) != mSleepDevsToKeepAlive) continue; //skip all options that wake up too slowly for (i = 0; i < Stm32sleepDevNum; i++) { if (!(mSleepDevsToKeepAlive & (1 << i))) continue; if (mDevsMaxWakeTime[i] < sleepClock->maxWakeupTime) break; } if (i != Stm32sleepDevNum) continue; //if it will not let us sleep long enough save it as a possibility and go on if (potentialLeastBadOption && !leastBadOption) leastBadOption = sleepClock; else //if it fits us perfectly, pick it break; } if (!sleepClock->maxCounter) sleepClock = leastBadOption; if (!sleepClock) { //should never happen - this will spin the CPU and be bad, but it WILL work in all cases return; } //turn ints off in prep for sleep wdtDisableClk(); intState = cpuIntsOff(); //options? config it if (sleepClock->prepare && sleepClock->prepare(mWakeupTime ? length - sleepClock->maxWakeupTime : 0, mMaxJitterPpm, mMaxDriftPpm, mMaxErrTotalPpm, sleepClock->userData, &savedData)) { asm volatile ("wfi\n" "nop" :::"memory"); //wakeup if (sleepClock->wake) sleepClock->wake(sleepClock->userData, &savedData); } //re-enable interrupts and let the handlers run cpuIntsRestore(intState); wdtEnableClk(); } void* platGetPersistentRamStore(uint32_t *bytes) { *bytes = sizeof(uint32_t[RTC_NUM_BACKUP_REGS]); return rtcGetBackupStorage(); } uint32_t platFreeResources(uint32_t tid) { uint32_t dmaCount = dmaStopAll(tid); uint32_t irqCount = extiUnchainAll(tid); return (dmaCount << 8) | irqCount; } void platPeriodic() { wdtPing(); }