altos: add ao_time_ns API
[fw/altos] / src / stmf0 / ao_timer.c
index 82a4cad68471829a14a395bc617f632b10bf723b..58e52995cf32f35cbb4146b092f60e6217771972 100644 (file)
@@ -3,7 +3,8 @@
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
  *
  * This program is distributed in the hope that it will be useful, but
  * WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -34,9 +35,25 @@ ao_time(void)
        return ao_tick_count;
 }
 
+uint64_t
+ao_time_ns(void)
+{
+       AO_TICK_TYPE    before, after;
+       uint32_t        cvr;
+
+       do {
+               before = ao_tick_count;
+               cvr = stm_systick.cvr;
+               after = ao_tick_count;
+       } while (before != after);
+
+       return (uint64_t) after * (1000000000ULL / AO_HERTZ) +
+               (uint64_t) cvr * (1000000000ULL / AO_SYSTICK);
+}
+
 #if AO_DATA_ALL
-volatile __data uint8_t        ao_data_interval = 1;
-volatile __data uint8_t        ao_data_count;
+volatile uint8_t       ao_data_interval = 1;
+volatile uint8_t       ao_data_count;
 #endif
 
 void stm_systick_isr(void)
@@ -50,12 +67,14 @@ void stm_systick_isr(void)
 #if AO_DATA_ALL
                if (++ao_data_count == ao_data_interval) {
                        ao_data_count = 0;
+#if HAS_ADC
 #if HAS_FAKE_FLIGHT
                        if (ao_fake_flight_active)
                                ao_fake_flight_poll();
                        else
 #endif
                                ao_adc_poll();
+#endif
 #if (AO_DATA_ALL & ~(AO_DATA_ADC))
                        ao_wakeup((void *) &ao_data_count);
 #endif
@@ -83,6 +102,7 @@ ao_timer_set_adc_interval(uint8_t interval)
 void
 ao_timer_init(void)
 {
+       stm_systick.csr = 0;
        stm_systick.rvr = SYSTICK_RELOAD;
        stm_systick.cvr = 0;
        stm_systick.csr = ((1 << STM_SYSTICK_CSR_ENABLE) |
@@ -92,6 +112,7 @@ ao_timer_init(void)
 
 #endif
 
+#if AO_HSI48
 static void
 ao_clock_enable_crs(void)
 {
@@ -127,15 +148,12 @@ ao_clock_enable_crs(void)
                      (0 << STM_CRS_CR_ERRIE) |
                      (0 << STM_CRS_CR_SYNCWARNIE) |
                      (0 << STM_CRS_CR_SYNCOKIE));
-
 }
+#endif
 
-void
-ao_clock_init(void)
+static void
+ao_clock_hsi(void)
 {
-       uint32_t        cfgr;
-
-       /* Switch to HSI while messing about */
        stm_rcc.cr |= (1 << STM_RCC_CR_HSION);
        while (!(stm_rcc.cr & (1 << STM_RCC_CR_HSIRDY)))
                ao_arch_nop();
@@ -153,15 +171,17 @@ ao_clock_init(void)
 
        /* reset PLLON, CSSON, HSEBYP, HSEON */
        stm_rcc.cr &= 0x0000ffff;
+}
 
-       /* Disable all interrupts */
-       stm_rcc.cir = 0;
-
+static void
+ao_clock_normal_start(void)
+{
 #if AO_HSE
-#define STM_RCC_CFGR_SWS_TARGET_CLOCK          STM_RCC_CFGR_SWS_HSE
-#define STM_RCC_CFGR_SW_TARGET_CLOCK           STM_RCC_CFGR_SW_HSE
+       uint32_t        cfgr;
+#define STM_RCC_CFGR_SWS_TARGET_CLOCK          STM_RCC_CFGR_SWS_PLL
+#define STM_RCC_CFGR_SW_TARGET_CLOCK           STM_RCC_CFGR_SW_PLL
 #define STM_PLLSRC                             AO_HSE
-#define STM_RCC_CFGR_PLLSRC_TARGET_CLOCK       1
+#define STM_RCC_CFGR_PLLSRC_TARGET_CLOCK       STM_RCC_CFGR_PLLSRC_HSE
 
 #if AO_HSE_BYPASS
        stm_rcc.cr |= (1 << STM_RCC_CR_HSEBYP);
@@ -172,8 +192,31 @@ ao_clock_init(void)
        stm_rcc.cr |= (1 << STM_RCC_CR_HSEON);
        while (!(stm_rcc.cr & (1 << STM_RCC_CR_HSERDY)))
                asm("nop");
-#endif
 
+       /* Disable the PLL */
+       stm_rcc.cr &= ~(1 << STM_RCC_CR_PLLON);
+       while (stm_rcc.cr & (1 << STM_RCC_CR_PLLRDY))
+               asm("nop");
+
+       /* Set multiplier */
+       cfgr = stm_rcc.cfgr;
+       cfgr &= ~(STM_RCC_CFGR_PLLMUL_MASK << STM_RCC_CFGR_PLLMUL);
+       cfgr |= (AO_RCC_CFGR_PLLMUL << STM_RCC_CFGR_PLLMUL);
+
+       /* PLL source */
+       cfgr &= ~(1 << STM_RCC_CFGR_PLLSRC);
+       cfgr |= (STM_RCC_CFGR_PLLSRC_TARGET_CLOCK  << STM_RCC_CFGR_PLLSRC);
+       stm_rcc.cfgr = cfgr;
+
+       /* Set pre divider */
+       stm_rcc.cfgr2 = (AO_RCC_CFGR2_PLLDIV << STM_RCC_CFGR2_PREDIV);
+
+       /* Enable the PLL and wait for it */
+       stm_rcc.cr |= (1 << STM_RCC_CR_PLLON);
+       while (!(stm_rcc.cr & (1 << STM_RCC_CR_PLLRDY)))
+               asm("nop");
+
+#endif
 
 #if AO_HSI48
 #define STM_RCC_CFGR_SWS_TARGET_CLOCK          STM_RCC_CFGR_SWS_HSI48
@@ -197,9 +240,51 @@ ao_clock_init(void)
 #define STM_RCC_CFGR_PLLSRC_TARGET_CLOCK       0
 #endif
 
+
+}
+
+static void
+ao_clock_normal_switch(void)
+{
+       uint32_t        cfgr;
+
+       cfgr = stm_rcc.cfgr;
+       cfgr &= ~(STM_RCC_CFGR_SW_MASK << STM_RCC_CFGR_SW);
+       cfgr |= (STM_RCC_CFGR_SW_TARGET_CLOCK << STM_RCC_CFGR_SW);
+       stm_rcc.cfgr = cfgr;
+       for (;;) {
+               uint32_t        c, part, mask, val;
+
+               c = stm_rcc.cfgr;
+               mask = (STM_RCC_CFGR_SWS_MASK << STM_RCC_CFGR_SWS);
+               val = (STM_RCC_CFGR_SWS_TARGET_CLOCK << STM_RCC_CFGR_SWS);
+               part = c & mask;
+               if (part == val)
+                       break;
+       }
+#if !AO_HSI && !AO_NEED_HSI
+       /* Turn off the HSI clock */
+       stm_rcc.cr &= ~(1 << STM_RCC_CR_HSION);
+#endif
 #ifdef STM_PLLSRC
-#error No code for PLL initialization yet
+       /* USB PLL source */
+       stm_rcc.cfgr3 |= (1 << STM_RCC_CFGR3_USBSW);
 #endif
+}
+
+void
+ao_clock_init(void)
+{
+       uint32_t        cfgr;
+
+       /* Switch to HSI while messing about */
+       ao_clock_hsi();
+
+       /* Disable all interrupts */
+       stm_rcc.cir = 0;
+
+       /* Start high speed clock */
+       ao_clock_normal_start();
 
        /* Set flash latency to tolerate 48MHz SYSCLK  -> 1 wait state */
 
@@ -228,24 +313,31 @@ ao_clock_init(void)
        stm_rcc.cfgr = cfgr;
 
        /* Switch to the desired system clock */
+       ao_clock_normal_switch();
+
+       /* Clear reset flags */
+       stm_rcc.csr |= (1 << STM_RCC_CSR_RMVF);
 
+#ifdef AO_MCO_PORT
        cfgr = stm_rcc.cfgr;
-       cfgr &= ~(STM_RCC_CFGR_SW_MASK << STM_RCC_CFGR_SW);
-       cfgr |= (STM_RCC_CFGR_SW_TARGET_CLOCK << STM_RCC_CFGR_SW);
-       stm_rcc.cfgr = cfgr;
-       for (;;) {
-               uint32_t        c, part, mask, val;
 
-               c = stm_rcc.cfgr;
-               mask = (STM_RCC_CFGR_SWS_MASK << STM_RCC_CFGR_SWS);
-               val = (STM_RCC_CFGR_SWS_TARGET_CLOCK << STM_RCC_CFGR_SWS);
-               part = c & mask;
-               if (part == val)
-                       break;
-       }
+       /* Send PLL clock to MCO */
+       cfgr &= ~(STM_RCC_CFGR_MCO_MASK << STM_RCC_CFGR_MCO);
+       cfgr |= (STM_RCC_CFGR_MCO_PLLCLK << STM_RCC_CFGR_MCO);
 
-       /* Clear reset flags */
-       stm_rcc.csr |= (1 << STM_RCC_CSR_RMVF);
+       /* Divide by 1 */
+       cfgr &= ~(STM_RCC_CFGR_MCOPRE_DIV_MASK << STM_RCC_CFGR_MCOPRE);
+       cfgr |= (STM_RCC_CFGR_MCOPRE_DIV_1 << STM_RCC_CFGR_MCOPRE);
+
+       /* Don't divide PLL */
+       cfgr |= (1 << STM_RCC_CFGR_PLL_NODIV);
+
+       stm_rcc.cfgr = cfgr;
+
+       ao_enable_port(AO_MCO_PORT);
+       stm_ospeedr_set(AO_MCO_PORT, AO_MCO_PIN, STM_OSPEEDR_HIGH);
+       stm_afr_set(AO_MCO_PORT, AO_MCO_PIN, AO_MCO_AF);
+#endif
 
 #if DEBUG_THE_CLOCK
        /* Output SYSCLK on PA8 for measurments */
@@ -253,10 +345,24 @@ ao_clock_init(void)
        stm_rcc.ahbenr |= (1 << STM_RCC_AHBENR_GPIOAEN);
 
        stm_afr_set(&stm_gpioa, 8, STM_AFR_AF0);
-       stm_moder_set(&stm_gpioa, 8, STM_MODER_ALTERNATE);
-       stm_ospeedr_set(&stm_gpioa, 8, STM_OSPEEDR_40MHz);
+       stm_ospeedr_set(&stm_gpioa, 8, STM_OSPEEDR_HIGH);
 
        stm_rcc.cfgr |= (STM_RCC_CFGR_MCOPRE_DIV_1 << STM_RCC_CFGR_MCOPRE);
        stm_rcc.cfgr |= (STM_RCC_CFGR_MCOSEL_HSE << STM_RCC_CFGR_MCOSEL);
 #endif
 }
+
+#if AO_POWER_MANAGEMENT
+void
+ao_clock_suspend(void)
+{
+       ao_clock_hsi();
+}
+
+void
+ao_clock_resume(void)
+{
+       ao_clock_normal_start();
+       ao_clock_normal_switch();
+}
+#endif