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