/
pmgr.c
406 lines (321 loc) · 10.5 KB
/
pmgr.c
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
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
/* SPDX-License-Identifier: MIT */
#include "pmgr.h"
#include "adt.h"
#include "string.h"
#include "types.h"
#include "utils.h"
#define PMGR_RESET BIT(31)
#define PMGR_AUTO_ENABLE BIT(28)
#define PMGR_PS_AUTO GENMASK(27, 24)
#define PMGR_PARENT_OFF BIT(11)
#define PMGR_DEV_DISABLE BIT(10)
#define PMGR_WAS_CLKGATED BIT(9)
#define PMGR_WAS_PWRGATED BIT(8)
#define PMGR_PS_ACTUAL GENMASK(7, 4)
#define PMGR_PS_TARGET GENMASK(3, 0)
#define PMGR_POLL_TIMEOUT 10000
#define PMGR_FLAG_VIRTUAL 0x10
struct pmgr_device {
u32 flags;
u16 parent[2];
u8 unk1[2];
u8 addr_offset;
u8 psreg_idx;
u8 unk2[14];
u16 id;
u8 unk3[4];
const char name[0x10];
} PACKED;
static int pmgr_initialized = 0;
static int pmgr_path[8];
static int pmgr_offset;
static int pmgr_dies;
static const u32 *pmgr_ps_regs = NULL;
static u32 pmgr_ps_regs_len = 0;
static const struct pmgr_device *pmgr_devices = NULL;
static u32 pmgr_devices_len = 0;
static uintptr_t pmgr_get_psreg(u8 idx)
{
if (idx * 12 >= pmgr_ps_regs_len) {
printf("pmgr: Index %d is out of bounds for ps-regs\n", idx);
return 0;
}
u32 reg_idx = pmgr_ps_regs[3 * idx];
u32 reg_offset = pmgr_ps_regs[3 * idx + 1];
u64 pmgr_reg;
if (adt_get_reg(adt, pmgr_path, "reg", reg_idx, &pmgr_reg, NULL) < 0) {
printf("pmgr: Error getting /arm-io/pmgr regs\n");
return 0;
}
return pmgr_reg + reg_offset;
}
int pmgr_set_mode(uintptr_t addr, u8 target_mode)
{
mask32(addr, PMGR_PS_TARGET, FIELD_PREP(PMGR_PS_TARGET, target_mode));
if (poll32(addr, PMGR_PS_ACTUAL, FIELD_PREP(PMGR_PS_ACTUAL, target_mode), PMGR_POLL_TIMEOUT) <
0) {
printf("pmgr: timeout while trying to set mode %x for device at 0x%lx: %x\n", target_mode,
addr, read32(addr));
return -1;
}
return 0;
}
static int pmgr_find_device(u16 id, const struct pmgr_device **device)
{
for (size_t i = 0; i < pmgr_devices_len; ++i) {
const struct pmgr_device *i_device = &pmgr_devices[i];
if (i_device->id != id)
continue;
*device = i_device;
return 0;
}
return -1;
}
static uintptr_t pmgr_device_get_addr(u8 die, const struct pmgr_device *device)
{
uintptr_t addr = pmgr_get_psreg(device->psreg_idx);
if (addr == 0)
return 0;
addr += PMGR_DIE_OFFSET * die;
addr += (device->addr_offset << 3);
return addr;
}
static int pmgr_set_mode_recursive(u8 die, u16 id, u8 target_mode, bool recurse)
{
if (!pmgr_initialized) {
printf("pmgr: pmgr_set_mode_recursive() called before successful pmgr_init()\n");
return -1;
}
if (id == 0)
return -1;
const struct pmgr_device *device;
if (pmgr_find_device(id, &device))
return -1;
if (target_mode == 0 && !(device->flags & PMGR_FLAG_VIRTUAL)) {
uintptr_t addr = pmgr_device_get_addr(die, device);
if (!addr)
return -1;
if (pmgr_set_mode(addr, target_mode))
return -1;
}
if (recurse)
for (int i = 0; i < 2; i++) {
if (device->parent[i]) {
u16 parent = FIELD_GET(PMGR_DEVICE_ID, device->parent[i]);
int ret = pmgr_set_mode_recursive(die, parent, target_mode, true);
if (ret < 0)
return ret;
}
}
if (target_mode != 0 && !(device->flags & PMGR_FLAG_VIRTUAL)) {
uintptr_t addr = pmgr_device_get_addr(die, device);
if (!addr)
return -1;
if (pmgr_set_mode(addr, target_mode))
return -1;
}
return 0;
}
int pmgr_power_enable(u32 id)
{
u16 device = FIELD_GET(PMGR_DEVICE_ID, id);
u8 die = FIELD_GET(PMGR_DIE_ID, id);
return pmgr_set_mode_recursive(die, device, PMGR_PS_ACTIVE, true);
}
int pmgr_power_disable(u32 id)
{
u16 device = FIELD_GET(PMGR_DEVICE_ID, id);
u8 die = FIELD_GET(PMGR_DIE_ID, id);
return pmgr_set_mode_recursive(die, device, PMGR_PS_PWRGATE, false);
}
static int pmgr_adt_find_devices(const char *path, const u32 **devices, u32 *n_devices)
{
int node_offset = adt_path_offset(adt, path);
if (node_offset < 0) {
printf("pmgr: Error getting node %s\n", path);
return -1;
}
*devices = adt_getprop(adt, node_offset, "clock-gates", n_devices);
if (*devices == NULL || *n_devices == 0) {
printf("pmgr: Error getting %s clock-gates.\n", path);
return -1;
}
*n_devices /= 4;
return 0;
}
static int pmgr_adt_devices_set_mode(const char *path, u8 target_mode, int recurse)
{
const u32 *devices;
u32 n_devices;
int ret = 0;
if (pmgr_adt_find_devices(path, &devices, &n_devices) < 0)
return -1;
for (u32 i = 0; i < n_devices; ++i) {
u16 device = FIELD_GET(PMGR_DEVICE_ID, devices[i]);
u8 die = FIELD_GET(PMGR_DIE_ID, devices[i]);
if (pmgr_set_mode_recursive(die, device, target_mode, recurse))
ret = -1;
}
return ret;
}
static int pmgr_adt_device_set_mode(const char *path, u32 index, u8 target_mode, int recurse)
{
const u32 *devices;
u32 n_devices;
int ret = 0;
if (pmgr_adt_find_devices(path, &devices, &n_devices) < 0)
return -1;
if (index >= n_devices)
return -1;
u16 device = FIELD_GET(PMGR_DEVICE_ID, devices[index]);
u8 die = FIELD_GET(PMGR_DIE_ID, devices[index]);
if (pmgr_set_mode_recursive(die, device, target_mode, recurse))
ret = -1;
return ret;
}
int pmgr_adt_power_enable(const char *path)
{
int ret = pmgr_adt_devices_set_mode(path, PMGR_PS_ACTIVE, true);
return ret;
}
int pmgr_adt_power_disable(const char *path)
{
return pmgr_adt_devices_set_mode(path, PMGR_PS_PWRGATE, false);
}
int pmgr_adt_power_enable_index(const char *path, u32 index)
{
int ret = pmgr_adt_device_set_mode(path, index, PMGR_PS_ACTIVE, true);
return ret;
}
int pmgr_adt_power_disable_index(const char *path, u32 index)
{
return pmgr_adt_device_set_mode(path, index, PMGR_PS_PWRGATE, false);
}
static int pmgr_reset_device(int die, const struct pmgr_device *dev)
{
if (die < 0 || die > 16) {
printf("pmgr: invalid die id %d for device %s\n", die, dev->name);
return -1;
}
uintptr_t addr = pmgr_device_get_addr(die, dev);
u32 reg = read32(addr);
if (FIELD_GET(PMGR_PS_ACTUAL, reg) != PMGR_PS_ACTIVE) {
printf("pmgr: will not reset disabled device %d.%s\n", die, dev->name);
return -1;
}
printf("pmgr: resetting device %d.%s\n", die, dev->name);
set32(addr, PMGR_DEV_DISABLE);
set32(addr, PMGR_RESET);
udelay(10);
clear32(addr, PMGR_RESET);
clear32(addr, PMGR_DEV_DISABLE);
return 0;
}
int pmgr_adt_reset(const char *path)
{
const u32 *devices;
u32 n_devices;
int ret = 0;
if (pmgr_adt_find_devices(path, &devices, &n_devices) < 0)
return -1;
for (u32 i = 0; i < n_devices; ++i) {
const struct pmgr_device *device;
u16 id = FIELD_GET(PMGR_DEVICE_ID, devices[i]);
u8 die = FIELD_GET(PMGR_DIE_ID, devices[i]);
if (pmgr_find_device(id, &device)) {
ret = -1;
continue;
}
if (pmgr_reset_device(die, device))
ret = -1;
}
return ret;
}
int pmgr_reset(int die, const char *name)
{
const struct pmgr_device *dev = NULL;
for (unsigned int i = 0; i < pmgr_devices_len; ++i) {
if (strncmp(pmgr_devices[i].name, name, 0x10) == 0) {
dev = &pmgr_devices[i];
break;
}
}
if (!dev)
return -1;
return pmgr_reset_device(die, dev);
}
int pmgr_init(void)
{
int node = adt_path_offset(adt, "/arm-io");
if (node < 0) {
printf("pmgr: Error getting /arm-io node\n");
return -1;
}
if (ADT_GETPROP(adt, node, "die-count", &pmgr_dies) < 0)
pmgr_dies = 1;
pmgr_offset = adt_path_offset_trace(adt, "/arm-io/pmgr", pmgr_path);
if (pmgr_offset < 0) {
printf("pmgr: Error getting /arm-io/pmgr node\n");
return -1;
}
pmgr_ps_regs = adt_getprop(adt, pmgr_offset, "ps-regs", &pmgr_ps_regs_len);
if (pmgr_ps_regs == NULL || pmgr_ps_regs_len == 0) {
printf("pmgr: Error getting /arm-io/pmgr ps-regs\n.");
return -1;
}
pmgr_devices = adt_getprop(adt, pmgr_offset, "devices", &pmgr_devices_len);
if (pmgr_devices == NULL || pmgr_devices_len == 0) {
printf("pmgr: Error getting /arm-io/pmgr devices.\n");
return -1;
}
pmgr_devices_len /= sizeof(*pmgr_devices);
pmgr_initialized = 1;
printf("pmgr: Cleaning up device states...\n");
for (u8 die = 0; die < pmgr_dies; ++die) {
for (size_t i = 0; i < pmgr_devices_len; ++i) {
const struct pmgr_device *device = &pmgr_devices[i];
if ((device->flags & PMGR_FLAG_VIRTUAL))
continue;
uintptr_t addr = pmgr_device_get_addr(die, device);
if (!addr)
continue;
u32 reg = read32(addr);
if (reg & PMGR_AUTO_ENABLE || FIELD_GET(PMGR_PS_TARGET, reg) == PMGR_PS_ACTIVE) {
for (int j = 0; j < 2; j++) {
if (device->parent[j]) {
const struct pmgr_device *pdevice;
if (pmgr_find_device(device->parent[j], &pdevice)) {
printf("pmgr: Failed to find parent #%d for %s\n", device->parent[j],
device->name);
continue;
}
if ((pdevice->flags & PMGR_FLAG_VIRTUAL))
continue;
addr = pmgr_device_get_addr(die, pdevice);
if (!addr)
continue;
reg = read32(addr);
if (!(reg & PMGR_AUTO_ENABLE) &&
FIELD_GET(PMGR_PS_TARGET, reg) != PMGR_PS_ACTIVE) {
printf("pmgr: Enabling %d.%s, parent of active device %s\n", die,
pdevice->name, device->name);
pmgr_set_mode(addr, PMGR_PS_ACTIVE);
}
}
}
}
}
}
printf("pmgr: initialized, %d devices on %u dies found.\n", pmgr_devices_len, pmgr_dies);
return 0;
}
u32 pmgr_get_feature(const char *name)
{
u32 val = 0;
int node = adt_path_offset(adt, "/arm-io/pmgr");
if (node < 0)
return 0;
if (ADT_GETPROP(adt, node, name, &val) < 0)
return 0;
return val;
}