1 /*
2 * SPDX-License-Identifier: Apache-2.0
3 *
4 * Copyright (C) 2024, Joakim Andersson
5 */
6
7 #include <zephyr/device.h>
8 #include <zephyr/drivers/pinctrl.h>
9 #include <zephyr/devicetree.h>
10 #include <zephyr/arch/cpu.h>
11 #include <zephyr/drivers/clock_control/stm32_clock_control.h>
12 #include "clock_stm32_ll_common.h"
13
14 #if DT_HAS_COMPAT_STATUS_OKAY(st_stm32_clock_mco)
15 #define DT_DRV_COMPAT st_stm32_clock_mco
16 #define HAS_PRESCALER 1
17 #elif DT_HAS_COMPAT_STATUS_OKAY(st_stm32f1_clock_mco)
18 #define DT_DRV_COMPAT st_stm32f1_clock_mco
19 #endif
20
21 struct stm32_mco_config {
22 const struct pinctrl_dev_config *pcfg;
23 #if defined(HAS_PRESCALER)
24 uint32_t prescaler;
25 #endif
26 /* clock subsystem driving this peripheral */
27 const struct stm32_pclken pclken[1];
28 };
29
stm32_mco_init(const struct device * dev)30 static int stm32_mco_init(const struct device *dev)
31 {
32 const struct stm32_mco_config *config = dev->config;
33 const struct stm32_pclken *pclken = &config->pclken[0];
34 int err;
35
36 err = enabled_clock(pclken->bus);
37 if (err < 0) {
38 /* Attempt to configure a src clock not available or not valid */
39 return err;
40 }
41
42 /* MCO source */
43 sys_clear_bits(
44 DT_REG_ADDR(DT_NODELABEL(rcc)) + STM32_MCO_CFGR_REG_GET(pclken->enr),
45 STM32_MCO_CFGR_MASK_GET(pclken->enr) <<
46 STM32_MCO_CFGR_SHIFT_GET(pclken->enr));
47 sys_set_bits(
48 DT_REG_ADDR(DT_NODELABEL(rcc)) + STM32_MCO_CFGR_REG_GET(pclken->enr),
49 STM32_MCO_CFGR_VAL_GET(pclken->enr) <<
50 STM32_MCO_CFGR_SHIFT_GET(pclken->enr));
51
52 #if defined(HAS_PRESCALER)
53 /* MCO prescaler */
54 sys_clear_bits(
55 DT_REG_ADDR(DT_NODELABEL(rcc)) + STM32_MCO_CFGR_REG_GET(config->prescaler),
56 STM32_MCO_CFGR_MASK_GET(config->prescaler) <<
57 STM32_MCO_CFGR_SHIFT_GET(config->prescaler));
58 sys_set_bits(
59 DT_REG_ADDR(DT_NODELABEL(rcc)) + STM32_MCO_CFGR_REG_GET(config->prescaler),
60 STM32_MCO_CFGR_VAL_GET(config->prescaler) <<
61 STM32_MCO_CFGR_SHIFT_GET(config->prescaler));
62 #endif
63
64 return pinctrl_apply_state(config->pcfg, PINCTRL_STATE_DEFAULT);
65 }
66
67 #define STM32_MCO_INIT(inst) \
68 \
69 PINCTRL_DT_INST_DEFINE(inst); \
70 \
71 const static struct stm32_mco_config stm32_mco_config_##inst = { \
72 .pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(inst), \
73 .pclken = STM32_DT_INST_CLOCKS(inst), \
74 IF_ENABLED(HAS_PRESCALER, \
75 (.prescaler = DT_PROP(DT_DRV_INST(inst), prescaler),)) \
76 }; \
77 \
78 DEVICE_DT_INST_DEFINE(inst, stm32_mco_init, NULL, \
79 NULL, \
80 &stm32_mco_config_##inst, \
81 PRE_KERNEL_1, CONFIG_KERNEL_INIT_PRIORITY_DEVICE, \
82 NULL);
83
84 DT_INST_FOREACH_STATUS_OKAY(STM32_MCO_INIT);
85