blob: 653ad4cb5bf3aef07ec8d5e1010b29dcb3f8850c [file] [log] [blame]
// Copyright 2019 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "mtk-power.h"
#include <lib/device-protocol/pdev.h>
#include <ddk/debug.h>
#include <ddk/platform-defs.h>
#include <ddk/protocol/platform/bus.h>
#include <ddk/protocol/platform/device.h>
#include <soc/mt8167/mt8167-power-regs.h>
#include <soc/mt8167/mt8167-power.h>
#include "src/devices/power/drivers/mtk-power/mtk-power-bind.h"
namespace power {
const fbl::Vector<uint32_t> kSupportedVoltageList1{1800000, 1900000, 2000000, 2200000};
const fbl::Vector<uint32_t> kSupportedVoltageList2{3300000, 3400000, 3500000, 3600000};
const fbl::Vector<uint32_t> kSupportedVoltageList3{1800000, 3300000};
const fbl::Vector<uint32_t> kSupportedVoltageList4{3000000, 3300000};
const fbl::Vector<uint32_t> kSupportedVoltageList5{1200000, 1300000, 1500000, 1800000,
2000000, 2800000, 3000000, 3300000};
const fbl::Vector<uint32_t> kSupportedVoltageList6{1240000, 1390000};
const fbl::Vector<uint32_t> kSupportedVoltageList7{1200000, 1300000, 1500000, 1800000};
const fbl::Vector<uint32_t> kSupportedVoltageList8{1800000, 2000000};
void MtkRegulator::WaitForIdle() {
while (PmicWacs2RData::Get().ReadFrom(&pmic_mmio_).wacs2_fsm() != PmicWacs2RData::kFsmStateIdle) {
}
}
void MtkRegulator::WaitForValidClear() {
while (PmicWacs2RData::Get().ReadFrom(&pmic_mmio_).wacs2_fsm() !=
PmicWacs2RData::kFsmStateWfVldClear) {
}
}
zx_status_t MtkRegulator::ReadPMICReg(uint32_t reg_addr, uint32_t* reg_value) {
WaitForIdle();
PmicWacs2Cmd::Get()
.FromValue(0)
.set_wacs2_write(0)
.set_wacs2_addr(reg_addr >> 1)
.WriteTo(&pmic_mmio_);
// Wait for data to be available.
WaitForValidClear();
*reg_value = PmicWacs2RData::Get().ReadFrom(&pmic_mmio_).wacs2_rdata();
// Data is read. clear the valid flag.
PmicWacs2VldClear::Get().ReadFrom(&pmic_mmio_).set_wacs2_vldclr(1).WriteTo(&pmic_mmio_);
return ZX_OK;
}
zx_status_t MtkRegulator::WritePMICReg(uint32_t reg_addr, uint32_t value) {
WaitForIdle();
PmicWacs2Cmd::Get()
.FromValue(0)
.set_wacs2_write(1)
.set_wacs2_addr(reg_addr >> 1)
.set_wacs2_data(value)
.WriteTo(&pmic_mmio_);
return ZX_OK;
}
zx_status_t MtkRegulator::Enable() {
if (enabled_) {
return ZX_OK;
}
uint32_t cur_val;
zx_status_t status = ReadPMICReg(enable_register_, &cur_val);
if (status != ZX_OK) {
zxlogf(ERROR, "%s Reading PMIC reg failed: %d", __FUNCTION__, status);
return status;
}
status = WritePMICReg(enable_register_, (cur_val | 1 << enable_bit_));
if (status != ZX_OK) {
zxlogf(ERROR, "%s Writing PMIC reg failed: %d", __FUNCTION__, status);
return status;
}
enabled_ = true;
return ZX_OK;
}
zx_status_t MtkRegulator::Disable() {
if (!enabled_) {
return ZX_ERR_BAD_STATE;
}
uint32_t cur_val;
zx_status_t status = ReadPMICReg(enable_register_, &cur_val);
if (status != ZX_OK) {
zxlogf(ERROR, "%s Reading PMIC reg failed: %d", __FUNCTION__, status);
return status;
}
status = WritePMICReg(enable_register_, (cur_val &= ~(1 << enable_bit_)));
if (status != ZX_OK) {
zxlogf(ERROR, "%s Writing PMIC reg failed: %d", __FUNCTION__, status);
return status;
}
enabled_ = false;
return ZX_OK;
}
zx_status_t MtkBuckRegulator::SetVoltageSelReg() {
uint32_t ctrl_reg_val;
zx_status_t status = ReadPMICReg(buck_voltage_ctrl_reg_, &ctrl_reg_val);
if (status != ZX_OK) {
zxlogf(ERROR, "%s Reading PMIC reg failed: %d", __FUNCTION__, status);
return status;
}
if (ctrl_reg_val & (1 << 1)) {
voltage_sel_reg_ = buck_voltage_on_reg_;
}
return ZX_OK;
}
zx_status_t MtkBuckRegulator::GetVoltageSelector(uint32_t set_voltage, uint32_t* actual_voltage,
uint16_t* selector) {
if (!step_size_) {
return ZX_ERR_BAD_STATE;
}
if (set_voltage < min_voltage_) {
zxlogf(ERROR, "%s Voltage :%x is not a supported voltage", __FUNCTION__, set_voltage);
return ZX_ERR_NOT_SUPPORTED;
}
if (set_voltage > max_voltage_) {
zxlogf(ERROR, "%s Voltage :%x is not a supported voltage", __FUNCTION__, set_voltage);
return ZX_ERR_NOT_SUPPORTED;
}
uint16_t sel = static_cast<uint16_t>((set_voltage - min_voltage_) / step_size_);
*actual_voltage = min_voltage_ + (sel * step_size_);
*selector = sel;
return ZX_OK;
}
zx_status_t MtkBuckRegulator::RequestVoltage(uint32_t voltage, uint32_t* actual_voltage) {
uint16_t selector = 0;
zx_status_t status = GetVoltageSelector(voltage, actual_voltage, &selector);
if (status != ZX_OK) {
return status;
}
if (cur_voltage_ == *actual_voltage) {
return ZX_OK;
}
uint32_t cur_val;
status = ReadPMICReg(voltage_sel_reg_, &cur_val);
if (status != ZX_OK) {
zxlogf(ERROR, "%s Reading PMIC reg failed: %d", __FUNCTION__, status);
return status;
}
cur_val &= ~voltage_sel_mask_;
cur_val |= (selector & voltage_sel_mask_);
status = WritePMICReg(voltage_sel_reg_, cur_val);
if (status != ZX_OK) {
zxlogf(ERROR, "%s Writing PMIC reg failed: %d", __FUNCTION__, status);
return status;
}
cur_voltage_ = *actual_voltage;
return ZX_OK;
}
zx_status_t MtkLdoRegulator::GetVoltageSelector(uint32_t set_voltage, uint32_t* actual_voltage,
uint16_t* selector) {
size_t num_voltages = supported_voltages_.size();
if (num_voltages == 0) {
return ZX_ERR_BAD_STATE;
}
if (set_voltage < supported_voltages_[0] || set_voltage > supported_voltages_[num_voltages - 1]) {
zxlogf(ERROR, "%s Voltage :%x is not a supported voltage", __FUNCTION__, set_voltage);
return ZX_ERR_NOT_SUPPORTED;
}
for (size_t i = 0; i < num_voltages; i++) {
uint32_t voltage = supported_voltages_[i];
if (voltage == set_voltage) {
*selector = static_cast<uint16_t>(i);
*actual_voltage = voltage;
return ZX_OK;
}
if (set_voltage > voltage && set_voltage < supported_voltages_[i + 1]) {
*selector = static_cast<uint16_t>(i);
*actual_voltage = voltage;
return ZX_OK;
}
}
return ZX_ERR_BAD_STATE;
}
zx_status_t MtkLdoRegulator::RequestVoltage(uint32_t voltage, uint32_t* actual_voltage) {
uint16_t selector = 0;
zx_status_t status = GetVoltageSelector(voltage, actual_voltage, &selector);
if (status != ZX_OK) {
return status;
}
if (cur_voltage_ == *actual_voltage) {
return ZX_OK;
}
uint32_t cur_val;
status = ReadPMICReg(voltage_sel_reg_, &cur_val);
if (status != ZX_OK) {
zxlogf(ERROR, "%s Reading PMIC reg failed: %d", __FUNCTION__, status);
return status;
}
cur_val &= ~voltage_sel_mask_;
cur_val |= ((selector << voltage_sel_shift_) & voltage_sel_mask_);
status = WritePMICReg(voltage_sel_reg_, cur_val);
if (status != ZX_OK) {
zxlogf(ERROR, "%s Writing PMIC reg failed: %d", __FUNCTION__, status);
return status;
}
cur_voltage_ = *actual_voltage;
return ZX_OK;
}
zx_status_t MtkPower::PowerImplGetCurrentVoltage(uint32_t index, uint32_t* current_voltage) {
if (index >= kMt8167NumPowerDomains) {
return ZX_ERR_OUT_OF_RANGE;
}
*current_voltage = power_domains_[index]->cur_voltage();
return ZX_OK;
}
zx_status_t MtkPower::PowerImplDisablePowerDomain(uint32_t index) {
if (index >= kMt8167NumPowerDomains) {
return ZX_ERR_OUT_OF_RANGE;
}
zx_status_t status = power_domains_[index]->Disable();
if (status != ZX_OK) {
zxlogf(ERROR, "%s Disable power domain %d failed. Status: %d", __FUNCTION__, index, status);
return status;
}
return ZX_OK;
}
zx_status_t MtkPower::PowerImplEnablePowerDomain(uint32_t index) {
if (index >= kMt8167NumPowerDomains) {
return ZX_ERR_OUT_OF_RANGE;
}
return power_domains_[index]->Enable();
}
zx_status_t MtkPower::PowerImplGetPowerDomainStatus(uint32_t index,
power_domain_status_t* out_status) {
if (index >= kMt8167NumPowerDomains) {
return ZX_ERR_OUT_OF_RANGE;
}
*out_status =
power_domains_[index]->enabled() ? POWER_DOMAIN_STATUS_ENABLED : POWER_DOMAIN_STATUS_DISABLED;
return ZX_OK;
}
zx_status_t MtkPower::PowerImplGetSupportedVoltageRange(uint32_t index, uint32_t* min_voltage,
uint32_t* max_voltage) {
if (index >= kMt8167NumPowerDomains) {
return ZX_ERR_OUT_OF_RANGE;
}
return power_domains_[index]->GetSupportedVoltageRange(min_voltage, max_voltage);
}
zx_status_t MtkPower::PowerImplRequestVoltage(uint32_t index, uint32_t voltage,
uint32_t* actual_voltage) {
if (index >= kMt8167NumPowerDomains) {
return ZX_ERR_OUT_OF_RANGE;
}
return power_domains_[index]->RequestVoltage(voltage, actual_voltage);
}
zx_status_t MtkPower::PowerImplWritePmicCtrlReg(uint32_t index, uint32_t reg_addr, uint32_t value) {
return ZX_ERR_NOT_SUPPORTED;
}
zx_status_t MtkPower::PowerImplReadPmicCtrlReg(uint32_t index, uint32_t addr, uint32_t* value) {
return ZX_ERR_NOT_SUPPORTED;
}
void MtkPower::DdkRelease() { delete this; }
void MtkPower::DdkUnbind(ddk::UnbindTxn txn) { txn.Reply(); }
enum MtkRegulatorType { BUCK = 1, LDO, FIXED };
struct MtkRegulatorParams {
uint8_t type;
uint32_t enable_register = 0;
uint8_t enable_bit = 0;
uint32_t select_register = 0;
uint32_t select_mask = 0;
uint32_t select_shift = 0;
uint32_t buck_voltage_control_register = 0;
uint32_t buck_voltage_on_register = 0;
uint32_t min_voltage = 0;
uint32_t max_voltage = 0;
uint32_t default_voltage = 0;
uint32_t step_size = 0;
const fbl::Vector<uint32_t>& supported_voltage = kSupportedVoltageList1;
};
MtkRegulatorParams kMtkRegulatorParams[] = {
[kBuckVProc] = {.type = BUCK,
.enable_register = kPmicVprocCon7,
.enable_bit = 1,
.select_register = kPmicVprocCon9,
.select_mask = 0x7f,
.select_shift = 0,
.buck_voltage_control_register = kPmicVprocCon5,
.buck_voltage_on_register = kPmicVprocCon10,
.min_voltage = 700000,
.max_voltage = 1493750,
.step_size = 6250},
[kBuckVCore] = {.type = BUCK,
.enable_register = kPmicVcoreCon7,
.enable_bit = 1,
.select_register = kPmicVcoreCon9,
.select_mask = 0x7f,
.select_shift = 0,
.buck_voltage_control_register = kPmicVcoreCon5,
.buck_voltage_on_register = kPmicVcoreCon10,
.min_voltage = 700000,
.max_voltage = 1493750,
.step_size = 6250},
[kBuckVSys] = {.type = BUCK,
.enable_register = kPmicVsysCon7,
.enable_bit = 1,
.select_register = kPmicVsysCon9,
.select_mask = 0x7f,
.select_shift = 0,
.buck_voltage_control_register = kPmicVsysCon5,
.buck_voltage_on_register = kPmicVsysCon10,
.min_voltage = 1400000,
.max_voltage = 2987500,
.step_size = 12500},
[kALdoVAud28] =
{
.type = FIXED,
.enable_register = kPmicAnaLdoCon23,
.enable_bit = 14,
.default_voltage = 2800000,
},
[kALdoVAud22] =
{
.type = LDO,
.enable_register = kPmicAnaLdoCon2,
.enable_bit = 14,
.select_register = kPmicAnaLdoCon8,
.select_mask = 0x60,
.select_shift = 5,
.supported_voltage = kSupportedVoltageList1,
},
[kALdoVAdc18] =
{
.type = FIXED,
.enable_register = kPmicAnaLdoCon25,
.enable_bit = 14,
.default_voltage = 1800000,
},
[kALdoVXo22] =
{
.type = FIXED,
.enable_register = kPmicAnaLdoCon1,
.enable_bit = 10,
.default_voltage = 2800000,
},
[kALdoVCamA] =
{
.type = FIXED,
.enable_register = kPmicAnaLdoCon4,
.enable_bit = 15,
.default_voltage = 2800000,
},
[kVSysLdoVm] =
{
.type = LDO,
.enable_register = kPmicDigLdoCon47,
.enable_bit = 14,
.select_register = kPmicDigLdoCon48,
.select_mask = 0x30,
.select_shift = 4,
.supported_voltage = kSupportedVoltageList6,
},
[kVSysLdoVcn18] =
{
.type = FIXED,
.enable_register = kPmicDigLdoCon11,
.enable_bit = 14,
.default_voltage = 1800000,
},
[kVSysLdoVio18] =
{
.type = FIXED,
.enable_register = kPmicDigLdoCon49,
.enable_bit = 14,
.default_voltage = 1800000,
},
[kVSysLdoVCamIo] =
{
.type = FIXED,
.enable_register = kPmicDigLdoCon53,
.enable_bit = 14,
.default_voltage = 1800000,
},
[kVSysLdoVCamD] =
{
.type = LDO,
.enable_register = kPmicDigLdoCon51,
.enable_bit = 14,
.select_register = kPmicDigLdoCon52,
.select_mask = 0x60,
.select_shift = 5,
.supported_voltage = kSupportedVoltageList7,
},
[kVDLdoVcn35] = {.type = LDO,
.enable_register = kPmicAnaLdoCon21,
.enable_bit = 12,
.select_register = kPmicAnaLdoCon16,
.select_mask = 0xC,
.select_shift = 6,
.supported_voltage = kSupportedVoltageList2},
[kVDLdoVio28] =
{
.type = FIXED,
.enable_register = kPmicDigLdoCon0,
.enable_bit = 14,
.default_voltage = 2800000,
},
[kVDLdoVemc33] = {.type = LDO,
.enable_register = kPmicDigLdoCon6,
.enable_bit = 14,
.select_register = kPmicDigLdoCon27,
.select_mask = 0x80,
.select_shift = 7,
.supported_voltage = kSupportedVoltageList4},
[kVDLdoVmc] = {.type = LDO,
.enable_register = kPmicDigLdoCon3,
.enable_bit = 12,
.select_register = kPmicDigLdoCon24,
.select_mask = 0x10,
.select_shift = 4,
.supported_voltage = kSupportedVoltageList3},
[kVDLdoVmch] = {.type = LDO,
.enable_register = kPmicDigLdoCon5,
.enable_bit = 14,
.select_register = kPmicDigLdoCon26,
.select_mask = 0x80,
.select_shift = 7,
.supported_voltage = kSupportedVoltageList4},
[kVDLdoVUsb33] =
{
.type = FIXED,
.enable_register = kPmicDigLdoCon2,
.enable_bit = 14,
.default_voltage = 3300000,
},
[kVDLdoVGp1] = {.type = LDO,
.enable_register = kPmicDigLdoCon7,
.enable_bit = 15,
.select_register = kPmicDigLdoCon28,
.select_mask = 0xE0,
.select_shift = 5,
.supported_voltage = kSupportedVoltageList5},
[kVDLdoVM25] =
{
.type = FIXED,
.enable_register = kPmicDigLdoCon55,
.enable_bit = 14,
.default_voltage = 2500000,
},
[kVDLdoVGp2] = {.type = LDO,
.enable_register = kPmicDigLdoCon8,
.enable_bit = 15,
.select_register = kPmicDigLdoCon29,
.select_mask = 0xE0,
.select_shift = 5,
.supported_voltage = kSupportedVoltageList5},
[kVDLdoVCamAf] = {.type = LDO,
.enable_register = kPmicDigLdoCon31,
.enable_bit = 15,
.select_register = kPmicDigLdoCon32,
.select_mask = 0xE0,
.select_shift = 5,
.supported_voltage = kSupportedVoltageList5},
};
void MtkPower::InitializePowerDomains() {
for (size_t i = 0; i < kMt8167NumPowerDomains; i++) {
auto& reg_params = kMtkRegulatorParams[i];
if (reg_params.type == BUCK) {
power_domains_[i] = std::make_unique<MtkBuckRegulator>(
pmic_mmio_.View(0), reg_params.enable_register, reg_params.enable_bit,
reg_params.select_register, reg_params.select_mask, reg_params.select_shift,
reg_params.buck_voltage_control_register, reg_params.buck_voltage_on_register,
reg_params.min_voltage, reg_params.max_voltage, reg_params.step_size);
MtkBuckRegulator* buck = static_cast<MtkBuckRegulator*>(power_domains_[i].get());
buck->SetVoltageSelReg();
} else if (reg_params.type == FIXED) {
power_domains_[i] =
std::make_unique<MtkFixedRegulator>(pmic_mmio_.View(0), reg_params.default_voltage,
reg_params.enable_register, reg_params.enable_bit);
} else if (reg_params.type == LDO) {
power_domains_[i] = std::make_unique<MtkLdoRegulator>(
pmic_mmio_.View(0), reg_params.enable_register, reg_params.enable_bit,
reg_params.select_register, reg_params.select_mask, reg_params.select_shift,
reg_params.supported_voltage);
}
}
}
zx_status_t MtkPower::Init() {
// TODO(ravoorir): Check if bootloader did not init the PMIC and
// do the needful.
InitializePowerDomains();
return ZX_OK;
}
zx_status_t MtkPower::Bind() {
pbus_protocol_t pbus;
zx_status_t status = device_get_protocol(parent(), ZX_PROTOCOL_PBUS, &pbus);
if (status != ZX_OK) {
zxlogf(ERROR, "%s failed to get ZX_PROTOCOL_PBUS, %d", __FUNCTION__, status);
return status;
}
power_impl_protocol_t power_proto = {
.ops = &power_impl_protocol_ops_,
.ctx = this,
};
status = pbus_register_protocol(&pbus, ZX_PROTOCOL_POWER_IMPL, &power_proto, sizeof(power_proto));
if (status != ZX_OK) {
zxlogf(ERROR, "%s pbus_register_protocol failed: %d", __FUNCTION__, status);
return status;
}
status = DdkAdd("mtk-power", DEVICE_ADD_ALLOW_MULTI_COMPOSITE);
if (status != ZX_OK) {
zxlogf(ERROR, "%s DdkAdd failed: %d", __FUNCTION__, status);
}
return status;
}
zx_status_t MtkPower::Create(void* ctx, zx_device_t* parent) {
zx_status_t status = ZX_OK;
ddk::PDev pdev(parent);
if (!pdev.is_valid()) {
zxlogf(ERROR, "%s Could not get pdev: %d", __FUNCTION__, status);
return ZX_ERR_NO_RESOURCES;
}
std::optional<ddk::MmioBuffer> mmio;
status = pdev.MapMmio(0, &mmio);
if (status != ZX_OK) {
zxlogf(ERROR, "%s Failed to get mmio: %d", __FUNCTION__, status);
return status;
}
auto dev = std::make_unique<MtkPower>(parent, *std::move(mmio));
if ((status = dev->Init()) != ZX_OK) {
return status;
}
if ((status = dev->Bind()) != ZX_OK) {
return status;
}
// devmgr is now in charge of the device.
__UNUSED auto* dummy = dev.release();
return ZX_OK;
}
static constexpr zx_driver_ops_t mtk_power_driver_ops = []() {
zx_driver_ops_t driver_ops = {};
driver_ops.version = DRIVER_OPS_VERSION;
driver_ops.bind = MtkPower::Create;
return driver_ops;
}();
} // namespace power
ZIRCON_DRIVER(mtk_power, power::mtk_power_driver_ops, "zircon", "0.1");