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