1 // Copyright 2018 The Fuchsia Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "aml-voltage.h"
6 #include <ddk/debug.h>
7 #include <unistd.h>
8 #include <string.h>
9
10 namespace thermal {
11
12 namespace {
13
14 // Sleep for 200 microseconds inorder to let the voltage change
15 // take effect. Source: Amlogic SDK.
16 constexpr uint32_t kSleep = 200;
17 // Step up or down 3 steps in the voltage table while changing
18 // voltage and not directly. Source: Amlogic SDK
19 constexpr int kSteps = 3;
20 // Invalid index in the voltage-table
21 constexpr int kInvalidIndex = -1;
22
23 } // namespace
24
Init(zx_device_t * parent,opp_info_t * opp_info)25 zx_status_t AmlVoltageRegulator::Init(zx_device_t* parent, opp_info_t* opp_info) {
26 ZX_DEBUG_ASSERT(opp_info);
27
28 // Create a PWM period = 1250, hwpwm - 1 to signify using PWM_D from PWM_C/D.
29 // Source: Amlogic SDK.
30 fbl::AllocChecker ac;
31 pwm_ = fbl::make_unique_checked<AmlPwm>(&ac, 1250, 1);
32 if (!ac.check()) {
33 return ZX_ERR_NO_MEMORY;
34 }
35
36 // Initialize the PWM.
37 zx_status_t status = pwm_->Init(parent);
38 if (status != ZX_OK) {
39 zxlogf(ERROR, "aml-voltage: Could not inititalize PWM: %d\n", status);
40 return status;
41 }
42
43 // Get the voltage-table metadata.
44 memcpy(&opp_info_, opp_info, sizeof(opp_info_t));
45
46 current_voltage_index_ = kInvalidIndex;
47
48 // Set the voltage to maximum to start with
49 // TODO(braval): Figure out a better way to set initialize
50 //. voltage.
51 status = SetVoltage(981000);
52 return ZX_OK;
53 }
54
SetVoltage(uint32_t microvolt)55 zx_status_t AmlVoltageRegulator::SetVoltage(uint32_t microvolt) {
56 // Find the entry in the voltage-table.
57 int target_index;
58 for (target_index = 0; target_index < MAX_VOLTAGE_TABLE; target_index++) {
59 if (opp_info_.voltage_table[target_index].microvolt == microvolt) {
60 break;
61 }
62 }
63
64 // Invalid voltage request.
65 if (target_index == MAX_VOLTAGE_TABLE) {
66 return ZX_ERR_INVALID_ARGS;
67 }
68
69 zx_status_t status;
70 // If this is the first time we are setting up the voltage
71 // we directly set it.
72 if (current_voltage_index_ < 0) {
73 // Update new duty cycle.
74 status = pwm_->Configure(opp_info_.voltage_table[target_index].duty_cycle);
75 if (status != ZX_OK) {
76 zxlogf(ERROR, "aml-voltage: Failed to set bew duty_cycle %d\n", status);
77 return status;
78 }
79 usleep(kSleep);
80 current_voltage_index_ = target_index;
81 return ZX_OK;
82 }
83
84 // Otherwise we adjust to the target voltage step by step.
85 while (current_voltage_index_ != target_index) {
86 if (current_voltage_index_ < target_index) {
87 if (current_voltage_index_ < target_index - kSteps) {
88 // Step up by 3 in the voltage table.
89 current_voltage_index_ += kSteps;
90 } else {
91 current_voltage_index_ = target_index;
92 }
93 } else {
94 if (current_voltage_index_ > target_index + kSteps) {
95 // Step down by 3 in the voltage table.
96 current_voltage_index_ -= kSteps;
97 } else {
98 current_voltage_index_ = target_index;
99 }
100 }
101 // Update new duty cycle.
102 status = pwm_->Configure(opp_info_.voltage_table[target_index].duty_cycle);
103 if (status != ZX_OK) {
104 zxlogf(ERROR, "aml-voltage: Failed to set bew duty_cycle %d\n", status);
105 return status;
106 }
107 usleep(kSleep);
108 }
109
110 // Update the current voltage index.
111 current_voltage_index_ = target_index;
112 return ZX_OK;
113 }
114
GetVoltage()115 uint32_t AmlVoltageRegulator::GetVoltage() {
116 ZX_DEBUG_ASSERT(current_voltage_index_ != kInvalidIndex);
117 return opp_info_.voltage_table[current_voltage_index_].microvolt;
118 }
119 } // namespace thermal
120