| // 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 <ddk/debug.h> |
| #include <unistd.h> |
| #include <string.h> |
| |
| 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<AmlPwm>(&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 |