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