zephyr/drivers/clock_control/clock_stm32_mco.c
Joakim Andersson 929c4507fc drivers: Add driver for STM32 MCO peripheral
Add device driver for STM32 MCO peripheral which takes configures
the MCO clock source and prescaler, and outputs it on one of the GPIO
pins.

Signed-off-by: Joakim Andersson <joerchan@gmail.com>
2024-09-16 20:19:57 +02:00

84 lines
2.8 KiB
C

/*
* 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_MCO_CFGR_REG_GET(pclken->enr),
STM32_MCO_CFGR_MASK_GET(pclken->enr) <<
STM32_MCO_CFGR_SHIFT_GET(pclken->enr));
sys_set_bits(
DT_REG_ADDR(DT_NODELABEL(rcc)) + STM32_MCO_CFGR_REG_GET(pclken->enr),
STM32_MCO_CFGR_VAL_GET(pclken->enr) <<
STM32_MCO_CFGR_SHIFT_GET(pclken->enr));
#if defined(HAS_PRESCALER)
/* MCO prescaler */
sys_clear_bits(
DT_REG_ADDR(DT_NODELABEL(rcc)) + STM32_MCO_CFGR_REG_GET(config->prescaler),
STM32_MCO_CFGR_MASK_GET(config->prescaler) <<
STM32_MCO_CFGR_SHIFT_GET(config->prescaler));
sys_set_bits(
DT_REG_ADDR(DT_NODELABEL(rcc)) + STM32_MCO_CFGR_REG_GET(config->prescaler),
STM32_MCO_CFGR_VAL_GET(config->prescaler) <<
STM32_MCO_CFGR_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);