iBoot/platform/generic/pmgr/pmgr_binning.c

295 lines
8.9 KiB
C

/*
* Copyright (C) 2015 Apple Inc. All rights reserved.
*
* This document is the property of Apple Inc.
* It is considered confidential and proprietary.
*
* This document may not be reproduced or transmitted in any form,
* in whole or in part, without the express written permission of
* Apple Inc.
*/
#include <platform.h>
#include <platform/pmgr.h>
#include <debug.h>
static uint64_t pmgr_binning_read_register(uint64_t address, uint32_t low, uint32_t high, bool reg64)
{
uint64_t result = 0;
do {
uint64_t current_low;
uint64_t offset;
uint64_t register_value;
if (reg64) {
offset = high/64;
current_low = offset * 64;
register_value = ((uint64_t *)(address))[offset];
} else {
offset = high/32;
current_low = offset * 32;
register_value = ((uint32_t *)(address))[offset];
}
// Mask useful bit : [current_low:high]
register_value &= (1ULL << (high - current_low + 1)) - 1;
if (current_low <= low) {
result |= register_value >> (low - current_low);
break;
}
result |= register_value << (current_low - low);
high = current_low - 1;
} while (1);
return result;
}
static uint64_t pmgr_binning_read(const struct pmgr_binning_fuse_to_register *fuse_to_register, uint32_t binning_low, uint32_t binning_high)
{
uint32_t result = 0;
while (1) {
uint32_t i = 0;
while (fuse_to_register[i].register_address != 0) {
if ((fuse_to_register[i].binning_high >= binning_high) && (fuse_to_register[i].binning_low <= binning_high)) {
break;
}
i++;
}
if (fuse_to_register[i].register_address == 0) {
return PMGR_BINNING_NOTFOUND;
}
if ((fuse_to_register[i].binning_high >= binning_high) && (fuse_to_register[i].binning_low <= binning_low)) {
result |= pmgr_binning_read_register(fuse_to_register[i].register_address,
binning_low - fuse_to_register[i].binning_low + fuse_to_register[i].register_low,
binning_high - fuse_to_register[i].binning_low + fuse_to_register[i].register_low,
fuse_to_register[i].register_64bit);
break;
}
result |= pmgr_binning_read_register(fuse_to_register[i].register_address,
fuse_to_register[i].register_low,
binning_high - fuse_to_register[i].binning_low + fuse_to_register[i].register_low,
fuse_to_register[i].register_64bit) << (fuse_to_register[i].binning_low - binning_low);
binning_high = fuse_to_register[i].binning_low - 1;
}
return result;
}
static uint32_t pmgr_binning_from_const_data(enum pmgr_binning_type_t type, uint32_t mode, bool use_binning)
{
uint32_t i;
for (i = 0; i < pmgr_binning_mode_to_const_data_size; i++) {
if (pmgr_binning_mode_to_const_data[i].type != type) {
continue;
}
if (pmgr_binning_mode_to_const_data[i].mode != mode) {
continue;
}
if ((use_binning) && pmgr_binning_mode_to_const_data[i].bingroup != pmgr_binning_get_group() && pmgr_binning_mode_to_const_data[i].bingroup != PMGR_BINNING_GROUP_ALL) {
continue;
}
if ((use_binning) && (pmgr_binning_get_revision() < pmgr_binning_mode_to_const_data[i].fuse_revision_minimum)) {
continue;
}
if ((!use_binning) && (pmgr_binning_mode_to_const_data[i].fuse_revision_minimum != 0)) {
continue;
}
return pmgr_binning_mode_to_const_data[i].mv;
}
return PMGR_BINNING_NOTFOUND;
}
uint32_t pmgr_binning_from_config_data(uint32_t voltage_index, const struct pmgr_binning_voltage_index_to_config **config_data)
{
uint32_t i;
for (i = 0; i < pmgr_binning_voltage_index_to_config_data_size; i++) {
if (pmgr_binning_voltage_index_to_config_data[i].voltage_index != voltage_index) {
continue;
}
if (pmgr_binning_voltage_index_to_config_data[i].bingroup != pmgr_binning_get_group() && pmgr_binning_voltage_index_to_config_data[i].bingroup != PMGR_BINNING_GROUP_ALL) {
continue;
}
if (pmgr_binning_get_revision() < pmgr_binning_voltage_index_to_config_data[i].fuse_revision_minimum) {
continue;
}
*config_data = &pmgr_binning_voltage_index_to_config_data[i];
return pmgr_binning_voltage_index_to_config_data[i].safe_voltage;
}
return PMGR_BINNING_NOTFOUND;
}
int32_t pmgr_binning_get_voltage_offset(uint32_t voltage_index, enum pmgr_binning_type_t type)
{
uint32_t i;
uint32_t size = 0;
const struct pmgr_binning_voltage_index_to_offset *voltage_index_to_offset;
uint32_t board_id = platform_get_board_id();
for (i = 0; i < pmgr_binning_board_id_to_offsets_data_size; i++) {
if (pmgr_binning_board_id_to_offsets_data[i].board_id != board_id) {
continue;
}
if (pmgr_binning_board_id_to_offsets_data[i].type != type) {
continue;
}
if (platform_get_chip_revision() < pmgr_binning_board_id_to_offsets_data[i].chip_rev_min) {
continue;
}
if (pmgr_binning_board_id_to_offsets_data[i].bingroup != pmgr_binning_get_group() && pmgr_binning_board_id_to_offsets_data[i].bingroup != PMGR_BINNING_GROUP_ALL) {
continue;
}
voltage_index_to_offset = pmgr_binning_board_id_to_offsets_data[i].voltage_offsets;
size = pmgr_binning_board_id_to_offsets_data[i].size;
break;
}
for (i = 0; i < size; i++) {
if (voltage_index_to_offset[i].voltage_index != voltage_index) {
continue;
}
if (pmgr_binning_get_revision() < voltage_index_to_offset[i].fuse_revision_minimum) {
continue;
}
return voltage_index_to_offset[i].offset;
}
return 0;
}
uint32_t pmgr_binning_mode_get_value(enum pmgr_binning_type_t type, uint32_t mode)
{
uint32_t i = 0;
do {
if (pmgr_binning_mode_to_fuse_data[i].type == PMGR_BINNING_END_OF_LIST) {
return PMGR_BINNING_NOTFOUND;
}
if (pmgr_binning_mode_to_fuse_data[i].type != type) {
i++;
continue;
}
if (pmgr_binning_mode_to_fuse_data[i].mode != mode) {
i++;
continue;
}
if (type != PMGR_BINNING_GROUP && pmgr_binning_mode_to_fuse_data[i].bingroup != pmgr_binning_get_group() && pmgr_binning_mode_to_fuse_data[i].bingroup != PMGR_BINNING_GROUP_ALL) {
i++;
continue;
}
break;
} while (1);
if (pmgr_binning_mode_to_fuse_data[i].low > pmgr_binning_mode_to_fuse_data[i].high) {
panic("Invalid low (%u) and high (%u) mode_to_fuse_data for type %u mode %u\n", pmgr_binning_mode_to_fuse_data[i].low, pmgr_binning_mode_to_fuse_data[i].high, type, mode);
}
uint32_t result = pmgr_binning_read(pmgr_binning_mode_to_fuse_data[i].fuse_to_register, pmgr_binning_mode_to_fuse_data[i].low, pmgr_binning_mode_to_fuse_data[i].high);
return result;
}
uint32_t pmgr_binning_get_base(void)
{
static uint32_t base = 0;
if (base == 0) {
base = pmgr_binning_mode_get_value(PMGR_BINNING_BASE, 0);
}
return base;
}
uint32_t pmgr_binning_get_group(void)
{
static uint32_t group = 0;
if (group == 0) {
group =pmgr_binning_mode_get_value(PMGR_BINNING_GROUP, 0);
}
return group;
}
uint32_t pmgr_binning_get_revision(void)
{
static uint32_t revision = 0;
if (revision == 0) {
revision = pmgr_binning_mode_get_value(PMGR_BINNING_REV, 0);
}
return revision;
}
uint32_t pmgr_binning_mode_get_mv(enum pmgr_binning_type_t type, uint32_t mode)
{
uint32_t value = pmgr_binning_mode_get_value(type, mode);
if ((value == PMGR_BINNING_NOTFOUND) || (value == 0)) {
return PMGR_BINNING_NOTFOUND;
}
return 25 * (pmgr_binning_get_base() + 1) + value * 5;
}
uint32_t pmgr_binning_get_mv(uint32_t voltage_index, bool sram, bool use_binning)
{
uint32_t voltage;
int32_t voltage_offset;
uint32_t mode;
enum pmgr_binning_type_t type;
if (sram) {
if (voltage_index >= pmgr_binning_voltage_config_sram_data_size) {
return PMGR_BINNING_NOTFOUND;
}
const struct pmgr_binning_voltage_config *binning_voltage_config;
binning_voltage_config = &pmgr_binning_voltage_config_sram_data[voltage_index];
voltage = binning_voltage_config->safe_voltage;
type = binning_voltage_config->type;
mode = binning_voltage_config->mode;
if ((voltage == 0) && (type != PMGR_BINNING_NONE)){
return pmgr_binning_from_const_data(type, mode, use_binning);
}
} else {
const struct pmgr_binning_voltage_index_to_config *config_data;
if (pmgr_binning_from_config_data(voltage_index, &config_data) == PMGR_BINNING_NOTFOUND) {
return PMGR_BINNING_NOTFOUND;
}
voltage = config_data->safe_voltage;
type = config_data->type;
mode = config_data->mode;
if (use_binning) {
uint32_t voltage_binning = pmgr_binning_mode_get_mv(type, mode);
if (voltage_binning != PMGR_BINNING_NOTFOUND) {
voltage = voltage_binning;
}
}
}
voltage_offset = pmgr_binning_get_voltage_offset(voltage_index, type);
if ((int32_t)voltage + voltage_offset < 0) {
panic("Invalid voltage offset");
}
return voltage + voltage_offset;
}
const struct pmgr_binning_vol_adj *pmgr_binning_get_vol_adj(uint32_t chipdid, uint32_t chip_rev, uint32_t voltage_index)
{
uint32_t fuse_rev = pmgr_binning_get_revision();
for (size_t i = 0; i < pmgr_binning_voltadj_entry_data_size; i++) {
if (voltage_index != pmgr_binning_voltadj_entry_data[i].voltage_index) {
continue;
}
if ((chip_rev < pmgr_binning_voltadj_entry_data[i].chip_rev_min)) {
continue;
}
if ((fuse_rev < pmgr_binning_voltadj_entry_data[i].fuse_rev_min)) {
continue;
}
return &pmgr_binning_voltadj_entry_data[i].voltages;
}
return NULL;
}