| // 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"); |