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