Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
227 changes: 177 additions & 50 deletions src/dapf.c
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,12 @@ struct dapf_t8020_config {
u32 r4;
} PACKED;

static int dapf_init_t8020(const char *path, u64 base, int node)
static int dapf_init_t8020(const char *path, u64 base, const void *data, u32 length)
{
u32 length = 0;
const char *prop = "filter-data-instance-0";
const struct dapf_t8020_config *config = adt_getprop(adt, node, prop, &length);
const struct dapf_t8020_config *config = data;

if (!config || !length || (length % sizeof(*config)) != 0) {
printf("dapf: Error getting ADT node %s property %s.\n", path, prop);
if ((length % sizeof(*config)) != 0) {
printf("dapf: Invalid filter data length for %s\n", path);
return -1;
}

Expand Down Expand Up @@ -99,27 +97,8 @@ static int dapf_init_t8110b(u64 base, struct dapf_t8110b_config *config, u32 len
return 0;
}

static int dapf_init_t8110(const char *path, u64 base, int node)
static int dapf_init_t8110(const char *path, u64 base, const void *config, u32 length)
{
u32 length = 0;
char prop[32];
const void *config = NULL;

// The dapf config is not always under instance 0; find the first present
// dapf-instance-N.
for (int i = 0; i < 8; i++) {
snprintf(prop, sizeof(prop), "dapf-instance-%d", i);
length = 0;
config = adt_getprop(adt, node, prop, &length);
if (config && length)
break;
}

if (!config || !length) {
printf("dapf: Error getting ADT node %s dapf-instance property.\n", path);
return -1;
}

// The least common multiple of 52 and 56 is 728 which is in the range of
// the observed lengthe for "dapf-instance-0". The 52 byte variant is more
// common and prefering that works so far.
Expand All @@ -128,11 +107,59 @@ static int dapf_init_t8110(const char *path, u64 base, int node)
} else if (length % sizeof(struct dapf_t8110b_config) == 0) {
return dapf_init_t8110b(base, (struct dapf_t8110b_config *)config, length);
} else {
printf("dapf: Invalid length for %s property %s\n", path, prop);
printf("dapf: Invalid filter data length for %s\n", path);
return -1;
}
}

/* dart,t8110 keeps its filter data in the first dapf-instance-N present. */
static const void *dapf_filter_data(int node, u32 *length)
{
if (!adt_is_compatible(adt, node, "dart,t8110"))
return adt_getprop(adt, node, "filter-data-instance-0", length);

for (int i = 0; i < 8; i++) {
char prop[32];
snprintf(prop, sizeof(prop), "dapf-instance-%d", i);
const void *data = adt_getprop(adt, node, prop, length);
if (data && *length)
return data;
}
return NULL;
}

static bool dapf_has_clock_gates(const char *path)
{
int node = adt_path_offset(adt, path);
u32 len = 0;

/* mtp and aop on T8132 list an empty set. */
return node >= 0 && adt_getprop(adt, node, "clock-gates", &len) && len;
}

/*
* The DAPF is gated with the device behind its DART, whose clock gates are on
* the DART or on the device named without "dart-".
*/
static bool dapf_power_path(const char *path, char *buf, size_t size)
{
const char *name = strrchr(path, '/');

if (!name || strlen(path) >= size)
return false;

strcpy(buf, path);
if (dapf_has_clock_gates(buf))
return true;

name++;
if (strncmp(name, "dart-", 5))
return false;
strcpy(buf + (name - path), name + 5);

return dapf_has_clock_gates(buf);
}

int dapf_init(const char *path, int index)
{
int ret;
Expand All @@ -143,38 +170,59 @@ int dapf_init(const char *path, int index)
return -1;
}

u32 pwr;
if (!adt_getprop(adt, node, "clock-gates", &pwr))
pwr = 0;
if (pwr && (pmgr_adt_power_enable(path) < 0))
return -1;

u64 base;
if (adt_get_reg(adt, dart_path, "reg", index, &base, NULL) < 0) {
printf("dapf: Error getting DAPF %s base address.\n", path);
return -1;
}

if (adt_is_compatible(adt, node, "dart,t8020")) {
ret = dapf_init_t8020(path, base, node);
} else if (adt_is_compatible(adt, node, "dart,t6000")) {
ret = dapf_init_t8020(path, base, node);
} else if (adt_is_compatible(adt, node, "dart,t8110")) {
ret = dapf_init_t8110(path, base, node);
} else {
bool t8110 = adt_is_compatible(adt, node, "dart,t8110");
if (!t8110 && !adt_is_compatible(adt, node, "dart,t8020") &&
!adt_is_compatible(adt, node, "dart,t6000")) {
printf("dapf: DAPF %s at 0x%lx is of an unknown type\n", path, base);
return -1;
}

if (pwr)
pmgr_adt_power_disable(path);
/* M4-generation SoCs list no filter data for dart-ave's DAPF. */
u32 length = 0;
const void *data = dapf_filter_data(node, &length);
if (!data || !length) {
printf("dapf: %s has no filter data\n", path);
return 0;
}

/*
* Power the device if it is off, and power it down again after. If its
* state is unknown, power it and leave it on.
*/
char power[64];
int active = 1;
if (dapf_power_path(path, power, sizeof(power))) {
active = pmgr_adt_power_is_active(power);
if (active != 1 && pmgr_adt_power_enable(power) < 0) {
printf("dapf: Error powering %s\n", power);
return -1;
}
}

if (t8110)
ret = dapf_init_t8110(path, base, data, length);
else
ret = dapf_init_t8020(path, base, data, length);

if (active == 0)
pmgr_adt_power_disable(power);

if (!ret)
printf("dapf: Initialized %s\n", path);

return ret;
}

#define DAPF_REG_NONE -1
#define DAPF_REG_UNKNOWN -2

/* DAPF reg indices for DARTs whose ADT has no "instance" list. */
struct entry {
const char *path;
int index;
Expand All @@ -188,21 +236,100 @@ struct entry dapf_entries[] = {
{"/arm-io/dart-ave", 3}, {NULL, -1},
};

static int dapf_table_index(const char *path)
{
for (struct entry *entry = dapf_entries; entry->path; entry++) {
if (!strcmp(entry->path, path))
return entry->index;
}
return DAPF_REG_NONE;
}

/*
* "instance" types a DART's reg ranges by fourcc, as {type, unit, name[8]} for
* every range, or as {type, name[8]} for the DART ranges only, where an entry
* of type DAPF puts the DAPF in the range after them.
*/
struct dapf_instance_short {
u32 type;
char name[8];
} PACKED;

struct dapf_instance_long {
u32 type;
u32 unit;
char name[8];
} PACKED;

#define DAPF_INSTANCE_TYPE 0x44415046 /* DAPF */

static int dapf_reg_index(const char *path)
{
int dart_path[8];
int node = adt_path_offset_trace(adt, path, dart_path);
if (node < 0)
return DAPF_REG_UNKNOWN;

u32 len = 0;
const void *instance = adt_getprop(adt, node, "instance", &len);
if (!instance || !len)
return DAPF_REG_UNKNOWN;

u32 regs = 0;
while (!adt_get_reg(adt, dart_path, "reg", regs, NULL, NULL))
regs++;

if (len == regs * sizeof(struct dapf_instance_long)) {
const struct dapf_instance_long *inst = instance;

for (u32 i = 0; i < regs; i++) {
if (inst[i].type == DAPF_INSTANCE_TYPE)
return i;
}
return DAPF_REG_NONE;
}

if (len % sizeof(struct dapf_instance_short) == 0) {
const struct dapf_instance_short *inst = instance;
u32 count = len / sizeof(*inst);

if (count + 1 != regs)
return count == regs ? DAPF_REG_NONE : DAPF_REG_UNKNOWN;
for (u32 i = 0; i < count; i++) {
if (inst[i].type == DAPF_INSTANCE_TYPE)
return count;
}
return DAPF_REG_NONE;
}

return DAPF_REG_UNKNOWN;
}

int dapf_init_all(void)
{
int ret = 0;
int count = 0;
struct entry *entry = dapf_entries;
int node = adt_path_offset(adt, "/arm-io");
if (node < 0)
return -1;

while (entry->path != NULL) {
if (adt_path_offset(adt, entry->path) < 0) {
entry++;
ADT_FOREACH_CHILD(adt, node)
{
const char *type = adt_getprop(adt, node, "device_type", NULL);
if (!type || strcmp(type, "dart"))
continue;
}
if (dapf_init(entry->path, entry->index) < 0) {

char path[64];
snprintf(path, sizeof(path), "/arm-io/%s", adt_get_name(adt, node));

int index = dapf_reg_index(path);
if (index == DAPF_REG_UNKNOWN)
index = dapf_table_index(path);
if (index == DAPF_REG_NONE)
continue;

if (dapf_init(path, index) < 0)
ret = -1;
}
entry++;
count += 1;
}
return ret ? ret : count;
Expand Down
4 changes: 1 addition & 3 deletions src/kboot.c
Original file line number Diff line number Diff line change
Expand Up @@ -2883,9 +2883,7 @@ int kboot_boot(void *kernel)

usb_init();
pcie_init();
/* HACK: DAPF init raises an SError on T8132. */
if (chip_id != T8132)
dapf_init_all();
dapf_init_all();

printf("Setting SMP mode to WFE...\n");
smp_set_wfe_mode(true);
Expand Down
47 changes: 47 additions & 0 deletions src/pmgr.c
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,53 @@ int pmgr_adt_power_disable_index(const char *path, u32 index)
return pmgr_adt_device_set_mode(path, index, PMGR_PS_PWRGATE, false);
}

/* A device is active when it and its parents are. A virtual device has no state of its own. */
static int pmgr_is_active_recursive(u8 die, u16 id)
{
const struct pmgr_device *device;

if (pmgr_find_device(id, &device))
return -1;

if (!(device->flags & PMGR_FLAG_VIRTUAL)) {
uintptr_t addr = pmgr_device_get_addr(die, device);
if (!addr)
return -1;
if (FIELD_GET(PMGR_PS_ACTUAL, read32(addr)) != PMGR_PS_ACTIVE)
return 0;
}

u16 parents[2];
pmgr_adt_get_parents(device, parents);
for (int i = 0; i < 2; i++) {
if (parents[i]) {
int ret = pmgr_is_active_recursive(die, parents[i]);
if (ret <= 0)
return ret;
}
}

return 1;
}

int pmgr_adt_power_is_active(const char *path)
{
const u32 *devices;
u32 n_devices;

if (pmgr_adt_find_devices(path, &devices, &n_devices) < 0)
return -1;

for (u32 i = 0; i < n_devices; ++i) {
int ret = pmgr_is_active_recursive(FIELD_GET(PMGR_DIE_ID, devices[i]),
FIELD_GET(PMGR_DEVICE_ID, devices[i]));
if (ret <= 0)
return ret;
}

return 1;
}

static int pmgr_reset_device(int die, const struct pmgr_device *dev)
{
if (die < 0 || die > 16) {
Expand Down
1 change: 1 addition & 0 deletions src/pmgr.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ int pmgr_adt_power_enable(const char *path);
int pmgr_adt_power_disable(const char *path);
int pmgr_adt_power_enable_index(const char *path, u32 index);
int pmgr_adt_power_disable_index(const char *path, u32 index);
int pmgr_adt_power_is_active(const char *path);
int pmgr_adt_reset(const char *path);

int pmgr_reset(int die, const char *name);
Expand Down