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