1 /*
2  * Copyright 2023 Ambiq Micro Inc.
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #define DT_DRV_COMPAT ambiq_clkctrl
8 
9 #include <errno.h>
10 #include <zephyr/init.h>
11 #include <zephyr/drivers/clock_control.h>
12 #include <zephyr/drivers/pinctrl.h>
13 #include <zephyr/drivers/clock_control/clock_control_ambiq.h>
14 #include <am_mcu_apollo.h>
15 
16 #include <zephyr/logging/log.h>
17 LOG_MODULE_REGISTER(clock_control_ambiq, CONFIG_CLOCK_CONTROL_LOG_LEVEL);
18 
19 struct ambiq_clock_config {
20 	uint32_t clock_freq;
21 	const struct pinctrl_dev_config *pcfg;
22 };
23 
ambiq_clock_on(const struct device * dev,clock_control_subsys_t sub_system)24 static int ambiq_clock_on(const struct device *dev, clock_control_subsys_t sub_system)
25 {
26 	ARG_UNUSED(dev);
27 
28 	int ret;
29 	uint32_t clock_name = (uint32_t)sub_system;
30 	am_hal_mcuctrl_control_arg_t arg = {
31 		.b_arg_hfxtal_in_use = true,
32 		.b_arg_apply_ext_source = false,
33 		.b_arg_force_update = false,
34 	};
35 
36 	if (clock_name >= CLOCK_CONTROL_AMBIQ_TYPE_MAX) {
37 		return -EINVAL;
38 	}
39 
40 	switch (clock_name) {
41 	case CLOCK_CONTROL_AMBIQ_TYPE_HFXTAL_BLE:
42 		arg.ui32_arg_hfxtal_user_mask = BIT(AM_HAL_HFXTAL_BLE_CONTROLLER_EN);
43 		arg.b_arg_enable_HfXtalClockout = true;
44 		ret = am_hal_mcuctrl_control(AM_HAL_MCUCTRL_CONTROL_EXTCLK32M_KICK_START, &arg);
45 		break;
46 	case CLOCK_CONTROL_AMBIQ_TYPE_LFXTAL:
47 		ret = am_hal_mcuctrl_control(AM_HAL_MCUCTRL_CONTROL_EXTCLK32K_ENABLE, 0);
48 	default:
49 		ret = -ENOTSUP;
50 		break;
51 	}
52 
53 	return ret;
54 }
55 
ambiq_clock_off(const struct device * dev,clock_control_subsys_t sub_system)56 static int ambiq_clock_off(const struct device *dev, clock_control_subsys_t sub_system)
57 {
58 	ARG_UNUSED(dev);
59 
60 	int ret;
61 	uint32_t clock_name = (uint32_t)sub_system;
62 	am_hal_mcuctrl_control_arg_t arg = {
63 		.b_arg_hfxtal_in_use = true,
64 		.b_arg_apply_ext_source = false,
65 		.b_arg_force_update = false,
66 	};
67 
68 	if (clock_name >= CLOCK_CONTROL_AMBIQ_TYPE_MAX) {
69 		return -EINVAL;
70 	}
71 
72 	switch (clock_name) {
73 	case CLOCK_CONTROL_AMBIQ_TYPE_HFXTAL_BLE:
74 		arg.ui32_arg_hfxtal_user_mask = BIT(AM_HAL_HFXTAL_BLE_CONTROLLER_EN);
75 		arg.b_arg_enable_HfXtalClockout = true;
76 		ret = am_hal_mcuctrl_control(AM_HAL_MCUCTRL_CONTROL_EXTCLK32M_DISABLE, &arg);
77 		break;
78 	case CLOCK_CONTROL_AMBIQ_TYPE_LFXTAL:
79 		ret = am_hal_mcuctrl_control(AM_HAL_MCUCTRL_CONTROL_EXTCLK32K_DISABLE, 0);
80 		break;
81 	default:
82 		ret = -ENOTSUP;
83 		break;
84 	}
85 
86 	return ret;
87 }
88 
ambiq_clock_get_rate(const struct device * dev,clock_control_subsys_t sub_system,uint32_t * rate)89 static inline int ambiq_clock_get_rate(const struct device *dev, clock_control_subsys_t sub_system,
90 				       uint32_t *rate)
91 {
92 	ARG_UNUSED(sub_system);
93 
94 	const struct ambiq_clock_config *cfg = dev->config;
95 	*rate = cfg->clock_freq;
96 
97 	return 0;
98 }
99 
ambiq_clock_configure(const struct device * dev,clock_control_subsys_t sub_system,void * data)100 static inline int ambiq_clock_configure(const struct device *dev, clock_control_subsys_t sub_system,
101 					void *data)
102 {
103 	ARG_UNUSED(sub_system);
104 	ARG_UNUSED(data);
105 
106 	const struct ambiq_clock_config *cfg = dev->config;
107 	int ret;
108 
109 	ret = pinctrl_apply_state(cfg->pcfg, PINCTRL_STATE_DEFAULT);
110 
111 	return ret;
112 }
113 
ambiq_clock_init(const struct device * dev)114 static int ambiq_clock_init(const struct device *dev)
115 {
116 	ARG_UNUSED(dev);
117 
118 	/* Nothing to do.*/
119 	return 0;
120 }
121 
122 static const struct clock_control_driver_api ambiq_clock_driver_api = {
123 	.on = ambiq_clock_on,
124 	.off = ambiq_clock_off,
125 	.get_rate = ambiq_clock_get_rate,
126 	.configure = ambiq_clock_configure,
127 };
128 
129 #define AMBIQ_CLOCK_INIT(n)                                                                        \
130 	PINCTRL_DT_INST_DEFINE(n);                                                                 \
131 	static const struct ambiq_clock_config ambiq_clock_config##n = {                           \
132 		.clock_freq = DT_INST_PROP(n, clock_frequency),                                    \
133 		.pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(n)};                                        \
134 	DEVICE_DT_INST_DEFINE(n, ambiq_clock_init, NULL, NULL, &ambiq_clock_config##n,             \
135 			      POST_KERNEL, CONFIG_CLOCK_CONTROL_INIT_PRIORITY,                     \
136 			      &ambiq_clock_driver_api);
137 
138 DT_INST_FOREACH_STATUS_OKAY(AMBIQ_CLOCK_INIT)
139