1 // SPDX-License-Identifier: BSD-3-Clause
2 //
3 // Copyright(c) 2018 Intel Corporation. All rights reserved.
4 //
5 // Author: Liam Girdwood <liam.r.girdwood@linux.intel.com>
6 // Keyon Jie <yang.jie@linux.intel.com>
7 // Rander Wang <rander.wang@intel.com>
8 // Janusz Jankowski <janusz.jankowski@linux.intel.com>
9
10 #include <rtos/timer.h>
11 #include <rtos/clk.h>
12 #include <sof/lib/memory.h>
13 #include <sof/lib/notifier.h>
14 #include <sof/lib/uuid.h>
15 #include <sof/platform.h>
16 #include <rtos/spinlock.h>
17 #include <sof/trace/trace.h>
18 #include <user/trace.h>
19 #include <stdint.h>
20
21 LOG_MODULE_REGISTER(clock, CONFIG_SOF_LOG_LEVEL);
22
23 /* 8890ea76-0df9-44ae-87e6-994f4c15e9fa */
24 DECLARE_SOF_UUID("clock", clock_uuid, 0x8890ea76, 0x0df9, 0x44ae,
25 0x87, 0xe6, 0x99, 0x4f, 0x4c, 0x15, 0xe9, 0xfa);
26
27 DECLARE_TR_CTX(clock_tr, SOF_UUID(clock_uuid), LOG_LEVEL_INFO);
28
29 SHARED_DATA struct k_spinlock clk_lock;
30
31 struct clock_notify_data clk_notify_data;
32
clock_get_nearest_freq_idx(const struct freq_table * tab,uint32_t size,uint32_t hz)33 static inline uint32_t clock_get_nearest_freq_idx(const struct freq_table *tab,
34 uint32_t size, uint32_t hz)
35 {
36 uint32_t i;
37
38 /* find lowest available frequency that is >= requested hz */
39 for (i = 0; i < size; i++) {
40 if (hz <= tab[i].freq)
41 return i;
42 }
43
44 /* not found, so return max frequency */
45 return size - 1;
46 }
47
clock_get_freq(int clock)48 uint32_t clock_get_freq(int clock)
49 {
50 struct clock_info *clk_info = clocks_get() + clock;
51 uint32_t freq = clk_info->freqs[clk_info->current_freq_idx].freq;
52
53 return freq;
54 }
55
clock_set_freq(int clock,uint32_t hz)56 void clock_set_freq(int clock, uint32_t hz)
57 {
58 struct clock_info *clk_info = clocks_get() + clock;
59 uint32_t idx;
60 k_spinlock_key_t key;
61
62 clk_notify_data.old_freq =
63 clk_info->freqs[clk_info->current_freq_idx].freq;
64 clk_notify_data.old_ticks_per_msec =
65 clk_info->freqs[clk_info->current_freq_idx].ticks_per_msec;
66
67 /* atomic context for changing clocks */
68 key = clock_lock();
69
70 /* get nearest frequency that is >= requested Hz */
71 idx = clock_get_nearest_freq_idx(clk_info->freqs, clk_info->freqs_num,
72 hz);
73 clk_notify_data.freq = clk_info->freqs[idx].freq;
74
75 tr_info(&clock_tr, "clock %d set freq %dHz freq_idx %d",
76 clock, hz, idx);
77
78 /* tell anyone interested we are about to change freq */
79 clk_notify_data.message = CLOCK_NOTIFY_PRE;
80 notifier_event(clk_info, clk_info->notification_id,
81 clk_info->notification_mask, &clk_notify_data,
82 sizeof(clk_notify_data));
83
84 if (!clk_info->set_freq ||
85 clk_info->set_freq(clock, idx) == 0)
86 /* update clock frequency */
87 clk_info->current_freq_idx = idx;
88
89 /* tell anyone interested we have now changed freq */
90 clk_notify_data.message = CLOCK_NOTIFY_POST;
91 notifier_event(clk_info, clk_info->notification_id,
92 clk_info->notification_mask, &clk_notify_data,
93 sizeof(clk_notify_data));
94
95 clock_unlock(key);
96 }
97
clock_low_power_mode(int clock,bool enable)98 void clock_low_power_mode(int clock, bool enable)
99 {
100 struct clock_info *clk_info = clocks_get() + clock;
101
102 if (clk_info->low_power_mode)
103 clk_info->low_power_mode(clock, enable);
104 }
105
106 #ifndef __ZEPHYR__
clock_ms_to_ticks(int clock,uint64_t ms)107 uint64_t clock_ms_to_ticks(int clock, uint64_t ms)
108 {
109 struct clock_info *clk_info = clocks_get() + clock;
110 uint64_t ticks;
111
112 ticks = clk_info->freqs[clk_info->current_freq_idx].ticks_per_msec * ms;
113
114 return ticks;
115 }
116
clock_us_to_ticks(int clock,uint64_t us)117 uint64_t clock_us_to_ticks(int clock, uint64_t us)
118 {
119 struct clock_info *clk_info = clocks_get() + clock;
120 uint64_t ticks;
121
122 ticks = clk_info->freqs[clk_info->current_freq_idx].ticks_per_msec * us / 1000ULL;
123
124 return ticks;
125 }
126
clock_ns_to_ticks(int clock,uint64_t ns)127 uint64_t clock_ns_to_ticks(int clock, uint64_t ns)
128 {
129 struct clock_info *clk_info = clocks_get() + clock;
130
131 return clk_info->freqs[clk_info->current_freq_idx].ticks_per_msec * ns / 1000000ULL;
132 }
133 #endif /* __ZEPHYR__ */
134
clock_ticks_per_sample(int clock,uint32_t sample_rate)135 uint64_t clock_ticks_per_sample(int clock, uint32_t sample_rate)
136 {
137 struct clock_info *clk_info = clocks_get() + clock;
138 uint32_t ticks_per_msec;
139 uint64_t ticks_per_sample;
140
141 platform_shared_get(clk_info, sizeof(*clk_info));
142 ticks_per_msec = clk_info->freqs[clk_info->current_freq_idx].ticks_per_msec;
143 ticks_per_sample = sample_rate ? ticks_per_msec * 1000 / sample_rate : 0;
144
145 return ticks_per_sample;
146 }
147