You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
89 lines
2.9 KiB
89 lines
2.9 KiB
/* |
|
* SPDX-License-Identifier: Apache-2.0 |
|
* |
|
* Copyright (C) 2024, Joakim Andersson |
|
*/ |
|
|
|
#include <zephyr/device.h> |
|
#include <zephyr/drivers/pinctrl.h> |
|
#include <zephyr/devicetree.h> |
|
#include <zephyr/arch/cpu.h> |
|
#include <zephyr/drivers/clock_control/stm32_clock_control.h> |
|
#include "clock_stm32_ll_common.h" |
|
|
|
#if DT_HAS_COMPAT_STATUS_OKAY(st_stm32_clock_mco) |
|
#define DT_DRV_COMPAT st_stm32_clock_mco |
|
#define HAS_PRESCALER 1 |
|
#elif DT_HAS_COMPAT_STATUS_OKAY(st_stm32f1_clock_mco) |
|
#define DT_DRV_COMPAT st_stm32f1_clock_mco |
|
#endif |
|
|
|
struct stm32_mco_config { |
|
const struct pinctrl_dev_config *pcfg; |
|
#if defined(HAS_PRESCALER) |
|
uint32_t prescaler; |
|
#endif |
|
/* clock subsystem driving this peripheral */ |
|
const struct stm32_pclken pclken[1]; |
|
}; |
|
|
|
static int stm32_mco_init(const struct device *dev) |
|
{ |
|
const struct stm32_mco_config *config = dev->config; |
|
const struct stm32_pclken *pclken = &config->pclken[0]; |
|
int err; |
|
|
|
err = enabled_clock(pclken->bus); |
|
if (err < 0) { |
|
/* Attempt to configure a src clock not available or not valid */ |
|
return err; |
|
} |
|
|
|
/* MCO source */ |
|
sys_clear_bits( |
|
DT_REG_ADDR(DT_NODELABEL(rcc)) + STM32_DT_CLKSEL_REG_GET(pclken->enr), |
|
STM32_DT_CLKSEL_MASK_GET(pclken->enr) << |
|
STM32_DT_CLKSEL_SHIFT_GET(pclken->enr)); |
|
sys_set_bits( |
|
DT_REG_ADDR(DT_NODELABEL(rcc)) + STM32_DT_CLKSEL_REG_GET(pclken->enr), |
|
STM32_DT_CLKSEL_VAL_GET(pclken->enr) << |
|
STM32_DT_CLKSEL_SHIFT_GET(pclken->enr)); |
|
|
|
#if defined(MCOX_ON) |
|
sys_set_bits(DT_REG_ADDR(DT_NODELABEL(rcc)) + STM32_DT_CLKSEL_REG_GET(pclken->enr), |
|
MCOX_ON); |
|
#endif |
|
|
|
#if defined(HAS_PRESCALER) |
|
/* MCO prescaler */ |
|
sys_clear_bits( |
|
DT_REG_ADDR(DT_NODELABEL(rcc)) + STM32_DT_CLKSEL_REG_GET(config->prescaler), |
|
STM32_DT_CLKSEL_MASK_GET(config->prescaler) << |
|
STM32_DT_CLKSEL_SHIFT_GET(config->prescaler)); |
|
sys_set_bits( |
|
DT_REG_ADDR(DT_NODELABEL(rcc)) + STM32_DT_CLKSEL_REG_GET(config->prescaler), |
|
STM32_DT_CLKSEL_VAL_GET(config->prescaler) << |
|
STM32_DT_CLKSEL_SHIFT_GET(config->prescaler)); |
|
#endif |
|
|
|
return pinctrl_apply_state(config->pcfg, PINCTRL_STATE_DEFAULT); |
|
} |
|
|
|
#define STM32_MCO_INIT(inst) \ |
|
\ |
|
PINCTRL_DT_INST_DEFINE(inst); \ |
|
\ |
|
const static struct stm32_mco_config stm32_mco_config_##inst = { \ |
|
.pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(inst), \ |
|
.pclken = STM32_DT_INST_CLOCKS(inst), \ |
|
IF_ENABLED(HAS_PRESCALER, \ |
|
(.prescaler = DT_PROP(DT_DRV_INST(inst), prescaler),)) \ |
|
}; \ |
|
\ |
|
DEVICE_DT_INST_DEFINE(inst, stm32_mco_init, NULL, \ |
|
NULL, \ |
|
&stm32_mco_config_##inst, \ |
|
PRE_KERNEL_1, CONFIG_KERNEL_INIT_PRIORITY_DEVICE, \ |
|
NULL); |
|
|
|
DT_INST_FOREACH_STATUS_OKAY(STM32_MCO_INIT);
|
|
|