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