1 /*
2 * Copyright (c) 2024 Silicon Laboratories Inc.
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #define DT_DRV_COMPAT silabs_series_clock
8
9 #include <zephyr/drivers/clock_control.h>
10 #include <zephyr/drivers/clock_control/clock_control_silabs.h>
11 #include <zephyr/sys/util.h>
12 #include <soc.h>
13
14 #include "sl_clock_manager.h"
15 #include "sl_status.h"
16
17 struct silabs_clock_control_config {
18 CMU_TypeDef *cmu;
19 };
20
21 static enum clock_control_status silabs_clock_control_get_status(const struct device *dev,
22 clock_control_subsys_t sys);
23
silabs_clock_control_on(const struct device * dev,clock_control_subsys_t sys)24 static int silabs_clock_control_on(const struct device *dev, clock_control_subsys_t sys)
25 {
26 const struct silabs_clock_control_cmu_config *cfg = sys;
27 sl_status_t status;
28
29 if (silabs_clock_control_get_status(dev, sys) == CLOCK_CONTROL_STATUS_ON) {
30 return -EALREADY;
31 }
32
33 status = sl_clock_manager_enable_bus_clock(&cfg->bus_clock);
34 if (status != SL_STATUS_OK) {
35 return -ENOTSUP;
36 }
37
38 return 0;
39 }
40
silabs_clock_control_off(const struct device * dev,clock_control_subsys_t sys)41 static int silabs_clock_control_off(const struct device *dev, clock_control_subsys_t sys)
42 {
43 const struct silabs_clock_control_cmu_config *cfg = sys;
44 sl_status_t status;
45
46 status = sl_clock_manager_disable_bus_clock(&cfg->bus_clock);
47 if (status != SL_STATUS_OK) {
48 return -ENOTSUP;
49 }
50
51 return 0;
52 }
53
silabs_clock_control_get_rate(const struct device * dev,clock_control_subsys_t sys,uint32_t * rate)54 static int silabs_clock_control_get_rate(const struct device *dev, clock_control_subsys_t sys,
55 uint32_t *rate)
56 {
57 const struct silabs_clock_control_cmu_config *cfg = sys;
58 sl_status_t status;
59
60 status = sl_clock_manager_get_clock_branch_frequency(cfg->branch, rate);
61 if (status != SL_STATUS_OK) {
62 return -ENOTSUP;
63 }
64
65 return 0;
66 }
67
silabs_clock_control_get_status(const struct device * dev,clock_control_subsys_t sys)68 static enum clock_control_status silabs_clock_control_get_status(const struct device *dev,
69 clock_control_subsys_t sys)
70 {
71 const struct silabs_clock_control_cmu_config *cfg = sys;
72 __maybe_unused const struct silabs_clock_control_config *reg = dev->config;
73 uint32_t clock_status = 0;
74
75 if (cfg->bus_clock == 0xFFFFFFFFUL) {
76 return CLOCK_CONTROL_STATUS_UNKNOWN;
77 }
78
79 switch (FIELD_GET(CLOCK_REG_MASK, cfg->bus_clock)) {
80 #if defined(_CMU_CLKEN0_MASK)
81 case 0:
82 clock_status = reg->cmu->CLKEN0;
83 break;
84 #endif
85 #if defined(_CMU_CLKEN1_MASK)
86 case 1:
87 clock_status = reg->cmu->CLKEN1;
88 break;
89 #endif
90 #if defined(_CMU_CLKEN2_MASK)
91 case 2:
92 clock_status = reg->cmu->CLKEN2;
93 break;
94 #endif
95 default:
96 __ASSERT(false, "Invalid bus clock: %x\n", cfg->bus_clock);
97 break;
98 }
99
100 if (clock_status & BIT(FIELD_GET(CLOCK_BIT_MASK, cfg->bus_clock))) {
101 return CLOCK_CONTROL_STATUS_ON;
102 } else {
103 return CLOCK_CONTROL_STATUS_OFF;
104 }
105 }
106
silabs_clock_control_init(const struct device * dev)107 static int silabs_clock_control_init(const struct device *dev)
108 {
109 ARG_UNUSED(dev);
110 sl_clock_manager_runtime_init();
111
112 return 0;
113 }
114
115 static DEVICE_API(clock_control, silabs_clock_control_api) = {
116 .on = silabs_clock_control_on,
117 .off = silabs_clock_control_off,
118 .get_rate = silabs_clock_control_get_rate,
119 .get_status = silabs_clock_control_get_status,
120 };
121
122 static const struct silabs_clock_control_config silabs_clock_control_config = {
123 .cmu = (CMU_TypeDef *)DT_INST_REG_ADDR(0),
124 };
125
126 DEVICE_DT_INST_DEFINE(0, silabs_clock_control_init, NULL, NULL, &silabs_clock_control_config,
127 PRE_KERNEL_1, CONFIG_CLOCK_CONTROL_INIT_PRIORITY, &silabs_clock_control_api);
128