Search code examples
stm32zephyr-rtos

I need to initialize and start the PLLI2S for MCO2 output at 50Mz for eth ref clock at boot, is there a good way to do it?


Im new to Zephyr. Im using a STM32F429xx MCU where we want to use MCO2 to output a 50Mhz clock for eth reference clock. If i run the syslock at 150Mhz and use that for MCO2 with a div3 it works fine. The problem is we want to run the sysclk at 180Mhz (max) and use the PLLI2S to output to MCO2, i looked and looked but i cant find a way to init the PLLI2S and start it at boot.

The I2S driver dont start the PLLI2S until you do a i2s_configure in your app. I tried making my own simple SYS_INIT function that starts the PLLI2S, but i cant get the code to run on boot (it works if i put it in main) but then HAL_ETH_Init dont work at boot. If anyone is willing to help me get the "driver" working or knows of a better way to setup and start the PLLI2S that would be nice.


Solution

  • So after a lot of checking i found my zephyr environment was incorrectly setup and thats why zephyr would not pickup my driver. After fixing that i got it to work with this code:

    Kconfig:

    # PLLI2S Clock configuration options
    
    menuconfig PLLI2S_INIT
        bool "STM32 PLLI2S Clk Output config"
        depends on SOC_SERIES_STM32F4X
        help
          Enable PLLI2SCLK for use as MCO2
    
    if PLLI2S_INIT
    
    config PLLI2S_INIT_PRIORITY
        int "Init priority"
        default 52
        help
          Device driver initialization priority.
    
    module = PLLI2S_INIT
    module-str = PllI2S_Init
    source "subsys/logging/Kconfig.template.log_config"
    
    config PLLI2S_INIT_DIV_M
        int "DIV-M (same as main PLL)"
        default 8
    
    config PLLI2S_INIT_MUL_N
        int "MUL-N"
        default 150
    
    config PLLI2S_INIT_DIV_R
        int "DIV-R"
        default 3
    
    endif # STM32_INIT_PLLI2S
    

    and the driver code:

    #include <soc.h>
    #include <kernel.h>
    #include <device.h>
    #include <stm32_ll_rcc.h>
    #include <zephyr/drivers/clock_control/stm32_clock_control.h>
    #include <zephyr/drivers/clock_control.h>
    
    #include <zephyr/logging/log.h>
    LOG_MODULE_REGISTER(PLLI2S_INIT, CONFIG_PLLI2S_INIT_LOG_LEVEL);
    
    #define PLLI2S_MAX_MS_TIME  2 /* PLLI2S lock time is 300us max */
    static uint16_t plli2s_ms_count;
    
    #define z_pllr(v) LL_RCC_PLLI2SR_DIV_ ## v
    #define pllr(v) z_pllr(v)
    
    static int plli2s_init(const struct device *dev)
    {
        ARG_UNUSED(dev);
    
        LOG_INF("Starting PLLI2S init..");
        uint32_t pll_src = LL_RCC_PLL_GetMainSource();
    
        /* Set PLLI2S */
        LL_RCC_PLLI2S_Disable();
        LL_RCC_PLLI2S_ConfigDomain_I2S(pll_src,
                           CONFIG_PLLI2S_INIT_DIV_M,
                           CONFIG_PLLI2S_INIT_MUL_N,
                           pllr(CONFIG_PLLI2S_INIT_DIV_R));
        LL_RCC_PLLI2S_Enable();
    
        /* wait until PLLI2S gets locked */
        while (!LL_RCC_PLLI2S_IsReady())
        {
            if (plli2s_ms_count++ > PLLI2S_MAX_MS_TIME)
            {
                LOG_DBG("Failed to LOCK PLLI2S!");
                return -EIO;
            }
    
            /* wait 1 ms */
            k_sleep(K_MSEC(1));
        }
    
        LOG_INF("PLLI2S is ready for use!");
    
        return 0;
    }
    
    SYS_INIT(plli2s_init, POST_KERNEL, CONFIG_PLLI2S_INIT_PRIORITY);
    

    It's not the best looking code but it works. Hope this might help someone else.