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.
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.