1 // SPDX-License-Identifier: BSD-3-Clause
2 //
3 // Copyright(c) 2018 Intel Corporation. All rights reserved.
4 //
5 // Author: Tomasz Lauda <tomasz.lauda@linux.intel.com>
6
7 /**
8 * \file
9 * \brief Runtime power management implementation
10 * \author Tomasz Lauda <tomasz.lauda@linux.intel.com>
11 */
12
13 #include <sof/drivers/timer.h>
14 #include <sof/lib/alloc.h>
15 #include <sof/lib/memory.h>
16 #include <sof/lib/pm_runtime.h>
17 #include <sof/lib/uuid.h>
18 #include <sof/sof.h>
19 #include <sof/spinlock.h>
20 #include <ipc/topology.h>
21 #include <stdint.h>
22
23 /* d7f6712d-131c-45a7-82ed-6aa9dc2291ea */
24 DECLARE_SOF_UUID("pm-runtime", pm_runtime_uuid, 0xd7f6712d, 0x131c, 0x45a7,
25 0x82, 0xed, 0x6a, 0xa9, 0xdc, 0x22, 0x91, 0xea);
26
27 DECLARE_TR_CTX(pm_tr, SOF_UUID(pm_runtime_uuid), LOG_LEVEL_INFO);
28
pm_runtime_init(struct sof * sof)29 void pm_runtime_init(struct sof *sof)
30 {
31 sof->prd = rzalloc(SOF_MEM_ZONE_SYS_SHARED, 0, SOF_MEM_CAPS_RAM, sizeof(*sof->prd));
32 spinlock_init(&sof->prd->lock);
33
34 platform_pm_runtime_init(sof->prd);
35
36 }
37
38 /* Warning: the terms in this API (enable, active,... ) apply sometimes
39 * to power _management_ and other times to _power_ which can be
40 * confusing. They are however consistent with
41 * https://www.kernel.org/doc/Documentation/power/runtime_pm.txt and the
42 * long tradition of double and triple negations in power management.
43 */
44
45 /** Increase the usage counter of some PM context. In general this
46 * blocks low power state but there are exception(s), for instance the
47 * context for the PM_RUNTIME_HOST_DMA_L1 is reversed: usage blocks high
48 * power state.
49 *
50 * Warning: some (all?) platforms don't really implement any counter, in
51 * other words the "counter" is silently maxed at 1.
52 */
pm_runtime_get(enum pm_runtime_context context,uint32_t index)53 void pm_runtime_get(enum pm_runtime_context context, uint32_t index)
54 {
55 tr_dbg(&pm_tr, "pm_runtime_get() context %d index %d", context, index);
56
57 switch (context) {
58 default:
59 platform_pm_runtime_get(context, index, RPM_ASYNC);
60 break;
61 }
62 }
63
pm_runtime_get_sync(enum pm_runtime_context context,uint32_t index)64 void pm_runtime_get_sync(enum pm_runtime_context context, uint32_t index)
65 {
66 tr_dbg(&pm_tr, "pm_runtime_get_sync() context %d index %d", context,
67 index);
68
69 switch (context) {
70 default:
71 platform_pm_runtime_get(context, index, 0);
72 break;
73 }
74 }
75
76 /** Decreases the usage counter of some PM context, see pm_runtime_get()
77 */
pm_runtime_put(enum pm_runtime_context context,uint32_t index)78 void pm_runtime_put(enum pm_runtime_context context, uint32_t index)
79 {
80 tr_dbg(&pm_tr, "pm_runtime_put() context %d index %d", context, index);
81
82 switch (context) {
83 default:
84 platform_pm_runtime_put(context, index, RPM_ASYNC);
85 break;
86 }
87 }
88
pm_runtime_put_sync(enum pm_runtime_context context,uint32_t index)89 void pm_runtime_put_sync(enum pm_runtime_context context, uint32_t index)
90 {
91 tr_dbg(&pm_tr, "pm_runtime_put_sync() context %d index %d", context,
92 index);
93
94 switch (context) {
95 default:
96 platform_pm_runtime_put(context, index, 0);
97 break;
98 }
99 }
100
101 /** Enables power _management_. The management, not the power. */
pm_runtime_enable(enum pm_runtime_context context,uint32_t index)102 void pm_runtime_enable(enum pm_runtime_context context, uint32_t index)
103 {
104 tr_dbg(&pm_tr, "pm_runtime_enable() context %d index %d", context,
105 index);
106
107 switch (context) {
108 default:
109 platform_pm_runtime_enable(context, index);
110 break;
111 }
112 }
113
114 /** Disables power _management_. The management, not the power. */
pm_runtime_disable(enum pm_runtime_context context,uint32_t index)115 void pm_runtime_disable(enum pm_runtime_context context, uint32_t index)
116 {
117 tr_dbg(&pm_tr, "pm_runtime_disable() context %d index %d", context,
118 index);
119
120 switch (context) {
121 default:
122 platform_pm_runtime_disable(context, index);
123 break;
124 }
125 }
126
127 /** Is the _power_ active. The power, not its management. */
pm_runtime_is_active(enum pm_runtime_context context,uint32_t index)128 bool pm_runtime_is_active(enum pm_runtime_context context, uint32_t index)
129 {
130 tr_dbg(&pm_tr, "pm_runtime_is_active() context %d index %d", context,
131 index);
132
133 switch (context) {
134 default:
135 return platform_pm_runtime_is_active(context, index);
136 }
137 }
138
139 #if CONFIG_DSP_RESIDENCY_COUNTERS
init_dsp_r_state(enum dsp_r_state r_state)140 void init_dsp_r_state(enum dsp_r_state r_state)
141 {
142 struct pm_runtime_data *prd = pm_runtime_data_get();
143 struct r_counters_data *r_counters;
144
145 r_counters = rzalloc(SOF_MEM_ZONE_SYS_SHARED, 0, SOF_MEM_CAPS_RAM, sizeof(*r_counters));
146 prd->r_counters = r_counters;
147
148 r_counters->ts = platform_timer_get(timer_get());
149 r_counters->cur_r_state = r_state;
150 }
151
report_dsp_r_state(enum dsp_r_state r_state)152 void report_dsp_r_state(enum dsp_r_state r_state)
153 {
154 struct r_counters_data *r_counters = pm_runtime_data_get()->r_counters;
155 uint64_t ts, delta;
156
157 /* It is possible to call report_dsp_r_state in early platform init from
158 * pm_runtime_disable so a safe check for r_counters is required
159 */
160 if (!r_counters || r_counters->cur_r_state == r_state)
161 return;
162
163 ts = platform_timer_get(timer_get());
164 delta = ts - r_counters->ts;
165
166 delta += mailbox_sw_reg_read64(SRAM_REG_R_STATE_TRACE_BASE +
167 r_counters->cur_r_state *
168 sizeof(uint64_t));
169
170 mailbox_sw_reg_write64(SRAM_REG_R_STATE_TRACE_BASE + r_counters->cur_r_state
171 * sizeof(uint64_t), delta);
172
173 r_counters->cur_r_state = r_state;
174 r_counters->ts = ts;
175 }
176
get_dsp_r_state(void)177 enum dsp_r_state get_dsp_r_state(void)
178 {
179 struct r_counters_data *r_counters = pm_runtime_data_get()->r_counters;
180
181 return r_counters ? r_counters->cur_r_state : 0;
182 }
183 #endif
184