Skip to content
Merged
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
241 changes: 226 additions & 15 deletions src/cpufreq.c
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,13 @@
#define CLUSTER_PSTATE_DESIRED2 GENMASK(15, 12)
#define CLUSTER_PSTATE_APSC_BUSY BIT(7)
#define CLUSTER_PSTATE_DESIRED1 GENMASK(4, 0)
#define CLUSTER_PSTATE_DESIRED1_S5L8960X GENMASK(24, 22)

#define CLUSTER_SWITCH_TIMEOUT 100
#define PMGR_VOLTAGE_CTL_OFF_S5L8960X 0x20c00
#define PMGR_VOLTAGE_CTL_OFF_T7000 0x23000
#define PMGR_VOLTAGE_CTL_OFF_S8000 0xa0000

#define CLUSTER_SWITCH_TIMEOUT 400

struct cluster_t {
const char *name;
Expand All @@ -39,19 +44,76 @@ struct feat_t {
bool pcluster_only;
};

static u32 pstate_reg_to_pstate(u64 val)
{
switch (chip_id) {
case S5L8960X:
case T7000:
case T7001:
return FIELD_GET(CLUSTER_PSTATE_DESIRED1_S5L8960X, val);
case S8000:
case S8001:
case S8003:
case T8010:
case T8011:
case T8012:
case T8015:
case T8103:
case T6000:
case T6001:
case T6002:
case T8112:
case T6020:
case T6021:
case T6022:
case T6031:
return FIELD_GET(CLUSTER_PSTATE_DESIRED1, val);
default:
printf("cpufreq: Chip 0x%x is unsupported\n", chip_id);
return 0;
}
}

static int set_pstate(const struct cluster_t *cluster, uint32_t pstate)
{
u64 val = read64(cluster->base + CLUSTER_PSTATE);

if (FIELD_GET(CLUSTER_PSTATE_DESIRED1, val) != pstate) {
val &= ~CLUSTER_PSTATE_DESIRED1;
val |= CLUSTER_PSTATE_SET | FIELD_PREP(CLUSTER_PSTATE_DESIRED1, pstate);
if (chip_id == T8103 || chip_id <= T6002) {
val &= ~CLUSTER_PSTATE_DESIRED2;
val |= CLUSTER_PSTATE_SET | FIELD_PREP(CLUSTER_PSTATE_DESIRED2, pstate);
if (pstate_reg_to_pstate(val) != pstate) {
switch (chip_id) {
case S5L8960X:
case T7000:
case T7001:
val &= ~CLUSTER_PSTATE_DESIRED1_S5L8960X;
val |= CLUSTER_PSTATE_SET | FIELD_PREP(CLUSTER_PSTATE_DESIRED1_S5L8960X, pstate);
break;
case S8000:
case S8001:
case S8003:
case T8010:
case T8011:
case T8012:
case T8015:
case T8103:
case T6000:
case T6001:
case T6002:
val &= ~CLUSTER_PSTATE_DESIRED2;
val |= CLUSTER_PSTATE_SET | FIELD_PREP(CLUSTER_PSTATE_DESIRED2, pstate);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please add a // fallthrough comment here

/* fallthrough */
case T8112:
case T6020:
case T6021:
case T6022:
case T6031:
val &= ~CLUSTER_PSTATE_DESIRED1;
val |= CLUSTER_PSTATE_SET | FIELD_PREP(CLUSTER_PSTATE_DESIRED1, pstate);
break;
default:
printf("cpufreq: Chip 0x%x is unsupported\n", chip_id);
return -1;
}
write64(cluster->base + CLUSTER_PSTATE, val);
if (poll32(cluster->base + CLUSTER_PSTATE, CLUSTER_PSTATE_BUSY, 0, CLUSTER_SWITCH_TIMEOUT) <
if (poll64(cluster->base + CLUSTER_PSTATE, CLUSTER_PSTATE_BUSY, 0, CLUSTER_SWITCH_TIMEOUT) <
0) {
printf("cpufreq: Timed out waiting for cluster %s P-State switch\n", cluster->name);
return -1;
Expand Down Expand Up @@ -88,11 +150,60 @@ int cpufreq_init_cluster(const struct cluster_t *cluster, const struct feat_t *f
}
}

/* Unknown */
write64(cluster->base + 0x440f8, 1);
int pmgr_path[8];
u64 pmgr_reg;

if (adt_path_offset_trace(adt, "/arm-io/pmgr", pmgr_path) < 0) {
printf("Error getting /arm-io/pmgr node\n");
return -1;
}

if (adt_get_reg(adt, pmgr_path, "reg", 0, &pmgr_reg, NULL) < 0) {
printf("Error getting /arm-io/pmgr regs\n");
return -1;
}

switch (chip_id) {
case S5L8960X:
write32(pmgr_reg + PMGR_VOLTAGE_CTL_OFF_S5L8960X, 1);
break;
case T7000:
case T7001:
write32(pmgr_reg + PMGR_VOLTAGE_CTL_OFF_T7000, 1);
break;
case S8000:
case S8001:
case S8003:
case T8010:
case T8011:
case T8012:
case T8015:
/*
* On T8015 this will result in the register being written
* two times (for two clusters). However, this is fine.
*/
write32(pmgr_reg + PMGR_VOLTAGE_CTL_OFF_S8000, 1);
break;
case T8103:
case T6000:
case T6001:
case T6002:
case T8112:
case T6020:
case T6021:
case T6022:
case T6031:
/* Unknown */
write64(cluster->base + 0x440f8, 1);

/* Initialize APSC */
set64(cluster->base + 0x200f8, BIT(40));
break;
default:
printf("cpufreq: Chip 0x%x is unsupported\n", chip_id);
break;
}

/* Initialize APSC */
set64(cluster->base + 0x200f8, BIT(40));
switch (chip_id) {
case T8103: {
u64 lo = read64(cluster->base + 0x70000 + cluster->apsc_pstate * 0x20);
Expand Down Expand Up @@ -129,11 +240,15 @@ void cpufreq_fixup_cluster(const struct cluster_t *cluster)
u64 bits = 0;
switch (chip_id) {
case T8103:
case T6000 ... T6002:
case T6000:
case T6001:
case T6002:
bits = CLUSTER_PSTATE_UNK_M1;
break;
case T8112:
case T6020 ... T6022:
case T6020:
case T6021:
case T6022:
bits = CLUSTER_PSTATE_UNK_M2;
break;
default:
Expand All @@ -148,6 +263,44 @@ void cpufreq_fixup_cluster(const struct cluster_t *cluster)
}
}

static const struct cluster_t s5l8960x_clusters[] = {
{"CPU", 0x202200000, false, 2, 6},
{},
};

static const struct cluster_t t7000_clusters[] = {
{"CPU", 0x202200000, false, 2, 5},
{},
};

static const struct cluster_t t7001_clusters[] = {
{"CPU", 0x202200000, false, 2, 7},
{},
};

static const struct cluster_t s8000_clusters[] = {
{"CPU", 0x202200000, false, 2, 7},
{},
};

static const struct cluster_t t8010_clusters[] = {
/* Fused cluster, kernel expects E-core entry */
{"CPU", 0x202f00000, false, 2, 4},
{},
};

static const struct cluster_t t8012_clusters[] = {
/* Fused cluster, kernel expects P-core entry */
{"CPU", 0x202f00000, false, 6, 10},
{},
};

static const struct cluster_t t8015_clusters[] = {
{"ECPU", 0x208e00000, false, 2, 6},
{"PCPU", 0x208e80000, true, 2, 7},
{},
};

static const struct cluster_t t8103_clusters[] = {
{"ECPU", 0x210e00000, false, 1, 5},
{"PCPU", 0x211e00000, true, 1, 7},
Expand Down Expand Up @@ -203,6 +356,23 @@ static const struct cluster_t t6031_clusters[] = {
const struct cluster_t *cpufreq_get_clusters(void)
{
switch (chip_id) {
case S5L8960X:
return s5l8960x_clusters;
case T7000:
return t7000_clusters;
case T7001:
return t7001_clusters;
case S8000:
case S8001:
case S8003:
return s8000_clusters;
case T8010:
case T8011:
return t8010_clusters;
case T8012:
return t8012_clusters;
case T8015:
return t8015_clusters;
case T8103:
return t8103_clusters;
case T6000:
Expand All @@ -225,6 +395,27 @@ const struct cluster_t *cpufreq_get_clusters(void)
}
}

static const struct feat_t s5l8960x_features[] = {
{},
};

static const struct feat_t s8000_features[] = {
{"cpu-apsc", CLUSTER_PSTATE, CLUSTER_PSTATE_M1_APSC_DIS, 0, CLUSTER_PSTATE_APSC_BUSY, false},
{},
};

static const struct feat_t t8010_features[] = {
{"cpu-apsc", CLUSTER_PSTATE, CLUSTER_PSTATE_M1_APSC_DIS, 0, CLUSTER_PSTATE_APSC_BUSY, false},
{},
};

static const struct feat_t t8015_features[] = {
{"cpu-apsc", CLUSTER_PSTATE, CLUSTER_PSTATE_M1_APSC_DIS, 0, CLUSTER_PSTATE_APSC_BUSY, false},
{"cpu-fixed-freq-pll-relock", CLUSTER_PSTATE, 0, CLUSTER_PSTATE_FIXED_FREQ_PLL_RECLOCK, 0,
false},
{},
};

static const struct feat_t t8103_features[] = {
{"cpu-apsc", CLUSTER_PSTATE, CLUSTER_PSTATE_M1_APSC_DIS, 0, CLUSTER_PSTATE_APSC_BUSY, false},
{"ppt-thrtl", 0x48400, 0, BIT(63), 0, false},
Expand Down Expand Up @@ -271,8 +462,24 @@ static const struct feat_t t6031_features[] = {
const struct feat_t *cpufreq_get_features(void)
{
switch (chip_id) {
case S5L8960X:
case T7000:
case T7001:
return s5l8960x_features;
case S8000:
case S8001:
case S8003:
return s8000_features;
case T8010:
case T8011:
case T8012:
return t8010_features;
case T8015:
return t8015_features;
case T8103:
case T6000 ... T6002:
case T6000:
case T6001:
case T6002:
return t8103_features;
case T8112:
return t8112_features;
Expand All @@ -298,6 +505,10 @@ int cpufreq_init(void)
if (!cluster || !features)
return -1;

/* Without this, CLUSTER_PSTATE_BUSY gets stuck */
if (chip_id == T8012 || chip_id == T8015)
pmgr_power_on(0, "SPMI");

bool err = false;
while (cluster->base) {
err |= cpufreq_init_cluster(cluster++, features);
Expand Down
22 changes: 22 additions & 0 deletions src/pmgr.c
Original file line number Diff line number Diff line change
Expand Up @@ -341,6 +341,28 @@ int pmgr_reset(int die, const char *name)
return pmgr_reset_device(die, dev);
}

int pmgr_power_on(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;

uintptr_t addr = pmgr_device_get_addr(die, dev);

if (!addr)
return -1;

return pmgr_set_mode(addr, PMGR_PS_ACTIVE);
}

int pmgr_init(void)
{
int node = adt_path_offset(adt, "/arm-io");
Expand Down
1 change: 1 addition & 0 deletions src/pmgr.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ int pmgr_adt_power_disable_index(const char *path, u32 index);
int pmgr_adt_reset(const char *path);

int pmgr_reset(int die, const char *name);
int pmgr_power_on(int die, const char *name);

int pmgr_set_mode(uintptr_t addr, u8 target_mode);

Expand Down
12 changes: 12 additions & 0 deletions src/utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -454,6 +454,18 @@ static inline int poll32(u64 addr, u32 mask, u32 target, u32 timeout)
return -1;
}

static inline int poll64(u64 addr, u64 mask, u64 target, u32 timeout)
{
while (--timeout > 0) {
u32 value = read64(addr) & mask;
if (value == target)
return 0;
udelay(1);
}

return -1;
}

typedef u64(generic_func)(u64, u64, u64, u64, u64);

struct vector_args {
Expand Down