// Copyright 2018 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 "aml-voltage.h" #include #include #include namespace thermal { namespace { // Sleep for 200 microseconds inorder to let the voltage change // take effect. Source: Amlogic SDK. constexpr uint32_t kSleep = 200; // Step up or down 3 steps in the voltage table while changing // voltage and not directly. Source: Amlogic SDK constexpr int kSteps = 3; // Invalid index in the voltage-table constexpr int kInvalidIndex = -1; } // namespace zx_status_t AmlVoltageRegulator::Init(zx_device_t* parent, opp_info_t* opp_info) { ZX_DEBUG_ASSERT(opp_info); // Create a PWM period = 1250, hwpwm - 1 to signify using PWM_D from PWM_C/D. // Source: Amlogic SDK. fbl::AllocChecker ac; pwm_ = fbl::make_unique_checked(&ac, 1250, 1); if (!ac.check()) { return ZX_ERR_NO_MEMORY; } // Initialize the PWM. zx_status_t status = pwm_->Init(parent); if (status != ZX_OK) { zxlogf(ERROR, "aml-voltage: Could not inititalize PWM: %d\n", status); return status; } // Get the voltage-table metadata. memcpy(&opp_info_, opp_info, sizeof(opp_info_t)); current_voltage_index_ = kInvalidIndex; // Set the voltage to maximum to start with // TODO(braval): Figure out a better way to set initialize //. voltage. status = SetVoltage(981000); return ZX_OK; } zx_status_t AmlVoltageRegulator::SetVoltage(uint32_t microvolt) { // Find the entry in the voltage-table. int target_index; for (target_index = 0; target_index < MAX_VOLTAGE_TABLE; target_index++) { if (opp_info_.voltage_table[target_index].microvolt == microvolt) { break; } } // Invalid voltage request. if (target_index == MAX_VOLTAGE_TABLE) { return ZX_ERR_INVALID_ARGS; } zx_status_t status; // If this is the first time we are setting up the voltage // we directly set it. if (current_voltage_index_ < 0) { // Update new duty cycle. status = pwm_->Configure(opp_info_.voltage_table[target_index].duty_cycle); if (status != ZX_OK) { zxlogf(ERROR, "aml-voltage: Failed to set bew duty_cycle %d\n", status); return status; } usleep(kSleep); current_voltage_index_ = target_index; return ZX_OK; } // Otherwise we adjust to the target voltage step by step. while (current_voltage_index_ != target_index) { if (current_voltage_index_ < target_index) { if (current_voltage_index_ < target_index - kSteps) { // Step up by 3 in the voltage table. current_voltage_index_ += kSteps; } else { current_voltage_index_ = target_index; } } else { if (current_voltage_index_ > target_index + kSteps) { // Step down by 3 in the voltage table. current_voltage_index_ -= kSteps; } else { current_voltage_index_ = target_index; } } // Update new duty cycle. status = pwm_->Configure(opp_info_.voltage_table[target_index].duty_cycle); if (status != ZX_OK) { zxlogf(ERROR, "aml-voltage: Failed to set bew duty_cycle %d\n", status); return status; } usleep(kSleep); } // Update the current voltage index. current_voltage_index_ = target_index; return ZX_OK; } uint32_t AmlVoltageRegulator::GetVoltage() { ZX_DEBUG_ASSERT(current_voltage_index_ != kInvalidIndex); return opp_info_.voltage_table[current_voltage_index_].microvolt; } } // namespace thermal