summaryrefslogtreecommitdiff
path: root/gxp-pm.h
blob: 217c3df394a24e5420e9f10e9907877f77e404d7 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
/* SPDX-License-Identifier: GPL-2.0 */
/*
 * GXP power management.
 *
 * Copyright (C) 2021 Google LLC
 */
#ifndef __GXP_PM_H__
#define __GXP_PM_H__

#include <soc/google/exynos_pm_qos.h>

#include <gcip/gcip-pm.h>

#include "gxp-internal.h"

#define AUR_DVFS_MIN_RATE AUR_UUD_RATE

enum aur_power_state {
	AUR_OFF = 0,
	AUR_UUD = 1,
	AUR_SUD = 2,
	AUR_UD = 3,
	AUR_NOM = 4,
	AUR_READY = 5,
	AUR_UUD_PLUS = 6,
	AUR_SUD_PLUS = 7,
	AUR_UD_PLUS = 8,
};

static const uint aur_power_state2rate[] = {
	AUR_OFF_RATE,
	AUR_UUD_RATE,
	AUR_SUD_RATE,
	AUR_UD_RATE,
	AUR_NOM_RATE,
	AUR_READY_RATE,
	AUR_UUD_PLUS_RATE,
	AUR_SUD_PLUS_RATE,
	AUR_UD_PLUS_RATE,
};

enum aur_memory_power_state {
	AUR_MEM_UNDEFINED = 0,
	AUR_MEM_MIN = 1,
	AUR_MEM_VERY_LOW = 2,
	AUR_MEM_LOW = 3,
	AUR_MEM_HIGH = 4,
	AUR_MEM_VERY_HIGH = 5,
	AUR_MEM_MAX = 6,
};

enum aur_power_cmu_mux_state {
	AUR_CMU_MUX_LOW = 0,
	AUR_CMU_MUX_NORMAL = 1,
};

#define AUR_NUM_POWER_STATE (AUR_MAX_ALLOW_STATE + 1)
#define AUR_NUM_MEMORY_POWER_STATE (AUR_MAX_ALLOW_MEMORY_STATE + 1)

#define AUR_INIT_DVFS_STATE AUR_UUD

/*
 * These macros mean the maximum valid enum value of aur_power_state and
 * aur_memory_power_state, not necessarily the state with the maximum power
 * level.
 */
#define AUR_MAX_ALLOW_STATE AUR_UD_PLUS
#define AUR_MAX_ALLOW_MEMORY_STATE AUR_MEM_MAX

#define AUR_NUM_POWER_STATE_WORKER 4

struct gxp_pm_device_ops {
	int (*pre_blk_powerup)(struct gxp_dev *gxp);
	int (*post_blk_powerup)(struct gxp_dev *gxp);
	int (*pre_blk_poweroff)(struct gxp_dev *gxp);
	int (*post_blk_poweroff)(struct gxp_dev *gxp);
};

struct gxp_set_acpm_state_work {
	struct work_struct work;
	struct gxp_dev *gxp;
	unsigned long state;
	unsigned long prev_state;
	bool low_clkmux;
	bool prev_low_clkmux;
	bool using;
};

struct gxp_req_pm_qos_work {
	struct work_struct work;
	struct gxp_dev *gxp;
	s32 int_val;
	s32 mif_val;
	bool using;
};

struct gxp_power_states {
	enum aur_power_state power;
	enum aur_memory_power_state memory;
	bool low_clkmux;
};

static const struct gxp_power_states off_states = { AUR_OFF, AUR_MEM_UNDEFINED,
						    false };
static const struct gxp_power_states uud_states = { AUR_UUD, AUR_MEM_UNDEFINED,
						    false };

struct gxp_power_manager {
	struct gxp_dev *gxp;
	struct gcip_pm *pm;
	struct mutex pm_lock;
	uint pwr_state_req_count[AUR_NUM_POWER_STATE];
	uint low_clkmux_pwr_state_req_count[AUR_NUM_POWER_STATE];
	uint mem_pwr_state_req_count[AUR_NUM_MEMORY_POWER_STATE];
	/*
	 * Last set CLKMUX state by asynchronous request handler.
	 * If a core is booting, we shouldn't change clock mux state. This is
	 * the expected state to set after all cores booting are finished.
	 * Otherwise, it's the real state of CLKMUX.
	 */
	bool curr_low_clkmux;
	/* Last requested clock mux state */
	bool last_scheduled_low_clkmux;
	int curr_state;
	int curr_memory_state; /* Note: this state will not be maintained in the MCU mode. */
	struct gxp_pm_device_ops *ops;
	struct gxp_set_acpm_state_work
		set_acpm_state_work[AUR_NUM_POWER_STATE_WORKER];
	/* Serializes searching for an open worker in set_acpm_state_work[] */
	struct mutex set_acpm_state_work_lock;
	uint last_set_acpm_state_worker;
	struct gxp_req_pm_qos_work req_pm_qos_work[AUR_NUM_POWER_STATE_WORKER];
	uint last_req_pm_qos_worker;
	/* Serializes searching for an open worker in req_pm_qos_work[] */
	struct mutex req_pm_qos_work_lock;
	struct workqueue_struct *wq;
	/* INT/MIF requests for memory bandwidth */
	struct exynos_pm_qos_request int_min;
	struct exynos_pm_qos_request mif_min;
	int force_mux_normal_count;
	/* Max frequency that the thermal driver/ACPM will allow in Hz */
	unsigned long thermal_limit;
	u64 blk_switch_count;
	/* PMU AUR_STATUS base address for block status, maybe NULL */
	void __iomem *aur_status;
};

/**
 * gxp_pm_blk_on() - Turn on the power for BLK_AUR
 * @gxp: The GXP device to turn on
 *
 * Note: For most cases you should use gxp_acquire_wakelock() to ensure the
 * device is ready to use, unless you really want to power on the block without
 * setting up the device state.
 *
 * Return:
 * * 0       - BLK ON successfully
 */
int gxp_pm_blk_on(struct gxp_dev *gxp);

/**
 * gxp_pm_blk_off() - Turn off the power for BLK_AUR
 * @gxp: The GXP device to turn off
 *
 * Return:
 * * 0       - BLK OFF successfully
 */
int gxp_pm_blk_off(struct gxp_dev *gxp);

/**
 * gxp_pm_is_blk_down() - Check weather the blk is turned off or not.
 * @gxp: The GXP device to check
 * @timeout_ms: Wait for the block to be turned off for this duration.
 *
 * Return:
 * * true       - blk is turned off.
 */
bool gxp_pm_is_blk_down(struct gxp_dev *gxp, uint timeout_ms);

/**
 * gxp_pm_blk_reboot() - Reboot the blk.
 * @gxp: The GXP device to reboot
 * @timeout_ms: Wait for the block to be turned off for this duration.
 *
 * Return:
 * * 0       - BLK rebooted successfully
 */
int gxp_pm_blk_reboot(struct gxp_dev *gxp, uint timeout_ms);

/**
 * gxp_pm_get_blk_state() - Get the blk power state
 * @gxp: The GXP device to sample state
 *
 * Return:
 * * state   - State number represented in kHZ, or 0 if OFF
 */
int gxp_pm_get_blk_state(struct gxp_dev *gxp);

/**
 * gxp_pm_get_blk_switch_count() - Get the blk switch count number
 * @gxp: The GXP device to switch the blk
 *
 * Return:
 * * count   - Switch count number after the module initialization.
 */
int gxp_pm_get_blk_switch_count(struct gxp_dev *gxp);

/**
 * gxp_pm_core_on() - Turn on a core on GXP device
 * @gxp: The GXP device to operate
 * @core: The core ID to turn on
 * @verbose: A boolean flag to indicate whether to print the log
 *
 * Return:
 * * 0       - Core on process finished successfully
 * * -ETIMEDOUT - Core on process timed-out.
 */
int gxp_pm_core_on(struct gxp_dev *gxp, uint core, bool verbose);

/**
 * gxp_pm_core_off() - Turn off a core on GXP device
 * @gxp: The GXP device to operate
 * @core: The core ID to turn off
 */
void gxp_pm_core_off(struct gxp_dev *gxp, uint core);

/**
 * gxp_pm_init() - API for initialize PM interface for GXP, should only be
 * called once per probe
 * @gxp: The GXP device to operate
 *
 * Return:
 * * 0       - Initialization finished successfully
 * * -ENOMEM - Cannot get memory to finish init.
 */
int gxp_pm_init(struct gxp_dev *gxp);

/**
 * gxp_pm_destroy() - API for removing
 * the power management interface
 * @gxp: The GXP device to operate
 *
 * Return:
 * * 0       - Remove finished successfully
 */
int gxp_pm_destroy(struct gxp_dev *gxp);

/**
 * gxp_pm_blk_set_rate_acpm() - API for setting the block-level DVFS rate.
 * This function can be called at any point after block power on.
 * @gxp: The GXP device to operate
 * @rate: Rate number in khz that need to be set.
 *         Supported rate is in aur_power_state2rate,
 *         if experiment is needed for unsupported rate
 *         please refer to Lassen's ECT table.
 *
 * Return:
 * * 0       - Set finished successfully
 * * Other   - Set rate encounter issue in exynos_acpm_set_rate
 */
int gxp_pm_blk_set_rate_acpm(struct gxp_dev *gxp, unsigned long rate);

/**
 * gxp_pm_blk_get_state_acpm() - API for getting
 * the current DVFS state of the Aurora block.
 * @gxp: The GXP device to operate
 *
 * Return:
 * * State   - State number in Khz from ACPM
 */
int gxp_pm_blk_get_state_acpm(struct gxp_dev *gxp);

/**
 * gxp_pm_update_requested_power_states() - API for a GXP client to vote for a
 * requested power state and a requested memory power state.
 * @gxp: The GXP device to operate.
 * @origin_states: An existing old requested states, will be cleared. If this is
 *                the first vote, pass AUR_OFF and AUR_MEM_UNDEFINED for field
 *                power_state and memory_state. The low_clkmux field will take no
 *                effect if requested state is AUR_OFF.
 * @requested_states: The new requested states.
 *
 * Return:
 * * 0       - Voting registered
 * * -EINVAL - Invalid original state or requested state
 */

int gxp_pm_update_requested_power_states(struct gxp_dev *gxp,
					 struct gxp_power_states origin_states,
					 struct gxp_power_states requested_states);

/**
 * gxp_pm_update_pm_qos() - API for updating the memory power state but passing the values of
 * INT and MIF frequencies directly. This function will ignore the vote ratings and update the
 * frequencies right away.
 * @gxp: The GXP device to operate.
 * @int_val: The value of INT frequency.
 * @mif_val: The value of MIF frequency.
 *
 * Note: This function will not update the @curr_memory_state of gxp_power_manager.
 *
 * Return:
 * * 0       - The memory power state has been changed
 * * -EINVAL - Invalid requested state
 */
int gxp_pm_update_pm_qos(struct gxp_dev *gxp, s32 int_val, s32 mif_val);

/*
 * gxp_pm_force_clkmux_normal() - Force PLL_CON0_NOC_USER and PLL_CON0_PLL_AUR MUX
 * switch to the normal state. This is required to guarantee LPM works when the core
 * is starting the firmware.
 */
void gxp_pm_force_clkmux_normal(struct gxp_dev *gxp);

/*
 * gxp_pm_resume_clkmux() - Check PLL_CON0_NOC_USER and PLL_CON0_PLL_AUR MUX state
 * modified by gxp_pm_force_clkmux_normal(). If the current vote is requested with low
 * frequency CLKMUX flag, should set the MUX state to AUR_CMU_MUX_LOW.
 */
void gxp_pm_resume_clkmux(struct gxp_dev *gxp);

/**
 * gxp_pm_set_thermal_limit() - Notify the power manager of a thermal limit
 * @gxp: The GXP device the limit is set for
 * @thermal_limit: The highest frequency, in Hz, the thermal limit allows
 *
 * The power management code will only use this information for logging.
 */
void gxp_pm_set_thermal_limit(struct gxp_dev *gxp, unsigned long thermal_limit);

#endif /* __GXP_PM_H__ */