1 /*
2  * Copyright (c) 2022 Intel Corporation
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <errno.h>
8 
9 #include <zephyr/device.h>
10 #include <zephyr/kernel.h>
11 #include <zephyr/spinlock.h>
12 #include <zephyr/sys/util.h>
13 
14 #include <adsp_clk.h>
15 #include <adsp_shim.h>
16 
17 static struct adsp_cpu_clock_info platform_cpu_clocks[CONFIG_MP_MAX_NUM_CPUS];
18 static struct k_spinlock lock;
19 
20 int adsp_clock_freq_enc[] = ADSP_CPU_CLOCK_FREQ_ENC;
21 int adsp_clock_freq_mask[] = ADSP_CPU_CLOCK_FREQ_MASK;
22 
23 #define HW_CLK_CHANGE_TIMEOUT_USEC 10000
24 
select_cpu_clock_hw(uint32_t freq_idx)25 static void select_cpu_clock_hw(uint32_t freq_idx)
26 {
27 	uint32_t enc = adsp_clock_freq_enc[freq_idx];
28 
29 #ifdef CONFIG_SOC_SERIES_INTEL_ADSP_ACE
30 	uint32_t clk_ctl = ADSP_CLKCTL;
31 
32 	clk_ctl &= ~ADSP_CLKCTL_OSC_SOURCE_MASK;
33 	clk_ctl |= (enc & ADSP_CLKCTL_OSC_SOURCE_MASK);
34 
35 	ADSP_CLKCTL = clk_ctl;
36 #else
37 	uint32_t status_mask = adsp_clock_freq_mask[freq_idx];
38 
39 	/* Request clock */
40 	ADSP_CLKCTL |= enc;
41 
42 	/* Wait for requested clock to be on */
43 	if (!WAIT_FOR((ADSP_CLKCTL & status_mask) == status_mask,
44 		      HW_CLK_CHANGE_TIMEOUT_USEC, k_busy_wait(1))) {
45 		k_panic();
46 	}
47 
48 	/* Switch to requested clock */
49 	ADSP_CLKCTL = (ADSP_CLKCTL & ~ADSP_CLKCTL_OSC_SOURCE_MASK) |
50 			    enc;
51 
52 	/* Release other clocks */
53 	ADSP_CLKCTL &= ~ADSP_CLKCTL_OSC_REQUEST_MASK | enc;
54 #endif
55 }
56 
adsp_clock_set_cpu_freq(uint32_t freq_idx)57 int adsp_clock_set_cpu_freq(uint32_t freq_idx)
58 {
59 	k_spinlock_key_t k;
60 	int i;
61 
62 	if (freq_idx >= ADSP_CPU_CLOCK_FREQ_LEN) {
63 		return -EINVAL;
64 	}
65 
66 	k = k_spin_lock(&lock);
67 
68 	select_cpu_clock_hw(freq_idx);
69 
70 	unsigned int num_cpus = arch_num_cpus();
71 
72 	for (i = 0; i < num_cpus; i++) {
73 		platform_cpu_clocks[i].current_freq = freq_idx;
74 	}
75 
76 	k_spin_unlock(&lock, k);
77 
78 	return 0;
79 }
80 
adsp_cpu_clocks_get(void)81 struct adsp_cpu_clock_info *adsp_cpu_clocks_get(void)
82 {
83 	return platform_cpu_clocks;
84 }
85 
adsp_clock_init(void)86 void adsp_clock_init(void)
87 {
88 	uint32_t platform_lowest_freq_idx = ADSP_CPU_CLOCK_FREQ_LOWEST;
89 	int i;
90 
91 #ifdef ADSP_CLOCK_HAS_WOVCRO
92 #ifdef CONFIG_SOC_SERIES_INTEL_ADSP_ACE
93 	ACE_DfPMCCU.dfclkctl |= ACE_CLKCTL_WOVCRO;
94 	if (ACE_DfPMCCU.dfclkctl & ACE_CLKCTL_WOVCRO) {
95 		ACE_DfPMCCU.dfclkctl = ACE_DfPMCCU.dfclkctl & ~ACE_CLKCTL_WOVCRO;
96 	} else {
97 		platform_lowest_freq_idx = ADSP_CPU_CLOCK_FREQ_IPLL;
98 	}
99 #if CONFIG_SOC_INTEL_ACE30
100 	/* Set the Cardinal clock divider to 18 to get 24.576MHz */
101 	ACE_DfPMCCU.dfcrodiv &= ACE_CRODIV_CARCDS_MASK;
102 	ACE_DfPMCCU.dfcrodiv |= ACE_CRODIV_CARCDS(0x12);
103 #endif
104 #else
105 	CAVS_SHIM.clkctl |= CAVS_CLKCTL_WOVCRO;
106 	if (CAVS_SHIM.clkctl & CAVS_CLKCTL_WOVCRO) {
107 		CAVS_SHIM.clkctl = CAVS_SHIM.clkctl & ~CAVS_CLKCTL_WOVCRO;
108 	} else {
109 		platform_lowest_freq_idx = ADSP_CPU_CLOCK_FREQ_LPRO;
110 	}
111 #endif /* CONFIG_SOC_SERIES_INTEL_ADSP_ACE */
112 #endif /* ADSP_CLOCK_HAS_WOVCRO */
113 
114 	unsigned int num_cpus = arch_num_cpus();
115 
116 	for (i = 0; i < num_cpus; i++) {
117 		platform_cpu_clocks[i].default_freq = ADSP_CPU_CLOCK_FREQ_DEFAULT;
118 		platform_cpu_clocks[i].current_freq = ADSP_CPU_CLOCK_FREQ_DEFAULT;
119 		platform_cpu_clocks[i].lowest_freq = platform_lowest_freq_idx;
120 	}
121 }
122 
123 struct adsp_clock_source_desc adsp_clk_src_info[ADSP_CLOCK_SOURCE_COUNT] = {
124 #ifndef CONFIG_DAI_DMIC_HW_IOCLK
125 	[ADSP_CLOCK_SOURCE_XTAL_OSC] = { DT_PROP(DT_NODELABEL(sysclk), clock_frequency) },
126 #else
127 	/* Temporarily use the values from the configuration until set xtal frequency via ipc
128 	 * support is added.
129 	 */
130 	[ADSP_CLOCK_SOURCE_XTAL_OSC] = { CONFIG_DAI_DMIC_HW_IOCLK },
131 #endif
132 #if DT_NODE_HAS_STATUS_OKAY(DT_NODELABEL(audioclk))
133 	[ADSP_CLOCK_SOURCE_AUDIO_CARDINAL] = { DT_PROP(DT_NODELABEL(audioclk), clock_frequency) },
134 #endif
135 #if DT_NODE_HAS_STATUS_OKAY(DT_NODELABEL(pllclk))
136 	[ADSP_CLOCK_SOURCE_AUDIO_PLL_FIXED] = { DT_PROP(DT_NODELABEL(pllclk), clock_frequency) },
137 #endif
138 	[ADSP_CLOCK_SOURCE_MLCK_INPUT] = { 0 },
139 #ifdef ADSP_CLOCK_HAS_WOVCRO
140 	[ADSP_CLOCK_SOURCE_WOV_RING_OSC] = { DT_PROP(DT_NODELABEL(sysclk), clock_frequency) },
141 #endif
142 };
143 
adsp_clock_source_is_supported(int source)144 bool adsp_clock_source_is_supported(int source)
145 {
146 	if (source < 0 || source >= ADSP_CLOCK_SOURCE_COUNT) {
147 		return false;
148 	}
149 
150 	return !!adsp_clk_src_info[source].frequency;
151 }
152 
adsp_clock_source_frequency(int source)153 uint32_t adsp_clock_source_frequency(int source)
154 {
155 	if (source < 0 || source >= ADSP_CLOCK_SOURCE_COUNT) {
156 		return 0;
157 	}
158 
159 	return adsp_clk_src_info[source].frequency;
160 }
161