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