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 <sof/drivers/timer.h>
11 #include <sof/lib/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 <sof/spinlock.h>
17 #include <sof/trace/trace.h>
18 #include <user/trace.h>
19 #include <stdint.h>
20 
21 /* 8890ea76-0df9-44ae-87e6-994f4c15e9fa */
22 DECLARE_SOF_UUID("clock", clock_uuid, 0x8890ea76, 0x0df9, 0x44ae,
23 		 0x87, 0xe6, 0x99, 0x4f, 0x4c, 0x15, 0xe9, 0xfa);
24 
25 DECLARE_TR_CTX(clock_tr, SOF_UUID(clock_uuid), LOG_LEVEL_INFO);
26 
27 struct clock_notify_data clk_notify_data;
28 
clock_get_nearest_freq_idx(const struct freq_table * tab,uint32_t size,uint32_t hz)29 static inline uint32_t clock_get_nearest_freq_idx(const struct freq_table *tab,
30 						  uint32_t size, uint32_t hz)
31 {
32 	uint32_t i;
33 
34 	/* find lowest available frequency that is >= requested hz */
35 	for (i = 0; i < size; i++) {
36 		if (hz <= tab[i].freq)
37 			return i;
38 	}
39 
40 	/* not found, so return max frequency */
41 	return size - 1;
42 }
43 
clock_get_freq(int clock)44 uint32_t clock_get_freq(int clock)
45 {
46 	struct clock_info *clk_info = clocks_get() + clock;
47 	uint32_t freq = clk_info->freqs[clk_info->current_freq_idx].freq;
48 
49 	return freq;
50 }
51 
clock_set_freq(int clock,uint32_t hz)52 void clock_set_freq(int clock, uint32_t hz)
53 {
54 	struct clock_info *clk_info = clocks_get() + clock;
55 	uint32_t idx;
56 	uint32_t flags;
57 
58 	clk_notify_data.old_freq =
59 		clk_info->freqs[clk_info->current_freq_idx].freq;
60 	clk_notify_data.old_ticks_per_msec =
61 		clk_info->freqs[clk_info->current_freq_idx].ticks_per_msec;
62 
63 	/* atomic context for changing clocks */
64 	spin_lock_irq(&clk_info->lock, flags);
65 
66 	/* get nearest frequency that is >= requested Hz */
67 	idx = clock_get_nearest_freq_idx(clk_info->freqs, clk_info->freqs_num,
68 					 hz);
69 	clk_notify_data.freq = clk_info->freqs[idx].freq;
70 
71 	tr_info(&clock_tr, "clock %d set freq %dHz freq_idx %d",
72 		clock, hz, idx);
73 
74 	/* tell anyone interested we are about to change freq */
75 	clk_notify_data.message = CLOCK_NOTIFY_PRE;
76 	notifier_event(clk_info, clk_info->notification_id,
77 		       clk_info->notification_mask, &clk_notify_data,
78 		       sizeof(clk_notify_data));
79 
80 	if (!clk_info->set_freq ||
81 	    clk_info->set_freq(clock, idx) == 0)
82 		/* update clock frequency */
83 		clk_info->current_freq_idx = idx;
84 
85 	/* tell anyone interested we have now changed freq */
86 	clk_notify_data.message = CLOCK_NOTIFY_POST;
87 	notifier_event(clk_info, clk_info->notification_id,
88 		       clk_info->notification_mask, &clk_notify_data,
89 		       sizeof(clk_notify_data));
90 
91 	spin_unlock_irq(&clk_info->lock, flags);
92 }
93 
clock_low_power_mode(int clock,bool enable)94 void clock_low_power_mode(int clock, bool enable)
95 {
96 	struct clock_info *clk_info = clocks_get() + clock;
97 
98 	if (clk_info->low_power_mode)
99 		clk_info->low_power_mode(clock, enable);
100 }
101 
clock_ms_to_ticks(int clock,uint64_t ms)102 uint64_t clock_ms_to_ticks(int clock, uint64_t ms)
103 {
104 	struct clock_info *clk_info = clocks_get() + clock;
105 	uint64_t ticks;
106 
107 	ticks = clk_info->freqs[clk_info->current_freq_idx].ticks_per_msec * ms;
108 
109 	return ticks;
110 }
111 
clock_us_to_ticks(int clock,uint64_t us)112 uint64_t clock_us_to_ticks(int clock, uint64_t us)
113 {
114 	struct clock_info *clk_info = clocks_get() + clock;
115 	uint64_t ticks;
116 
117 	ticks = clk_info->freqs[clk_info->current_freq_idx].ticks_per_msec * us / 1000ULL;
118 
119 	return ticks;
120 }
121 
clock_ticks_per_sample(int clock,uint32_t sample_rate)122 uint64_t clock_ticks_per_sample(int clock, uint32_t sample_rate)
123 {
124 	struct clock_info *clk_info = clocks_get() + clock;
125 	uint32_t ticks_per_msec;
126 	uint64_t ticks_per_sample;
127 
128 	platform_shared_get(clk_info, sizeof(*clk_info));
129 	ticks_per_msec = clk_info->freqs[clk_info->current_freq_idx].ticks_per_msec;
130 	ticks_per_sample = sample_rate ? ticks_per_msec * 1000 / sample_rate : 0;
131 
132 	return ticks_per_sample;
133 }
134 
platform_timer_set_delta(struct timer * timer,uint64_t ns)135 void platform_timer_set_delta(struct timer *timer, uint64_t ns)
136 {
137 	struct clock_info *clk_info = clocks_get() + PLATFORM_DEFAULT_CLOCK;
138 	uint32_t ticks_per_msec =
139 		clk_info->freqs[clk_info->current_freq_idx].ticks_per_msec;
140 	uint64_t ticks;
141 
142 	ticks = ticks_per_msec * ns / 1000000;
143 	timer->delta = ticks - platform_timer_get(timer);
144 
145 }
146