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 "intel-i915.h"
6 #include "macros.h"
7 #include "power.h"
8 #include "registers.h"
9
10 namespace i915 {
11
PowerWellRef()12 PowerWellRef::PowerWellRef() {}
13
PowerWellRef(Power * power,PowerWell power_well)14 PowerWellRef::PowerWellRef(Power* power, PowerWell power_well)
15 : power_(power), power_well_(power_well) {
16 if (power_->power_well1_refs_++ == 0) {
17 power_->SetPowerWell1Enable(true);
18 }
19 if (power_well_ == PowerWell2 && power_->power_well2_refs_++ == 0) {
20 power_->SetPowerWell2Enable(true);
21 }
22 }
23
PowerWellRef(PowerWellRef && o)24 PowerWellRef::PowerWellRef(PowerWellRef&& o) : power_(o.power_), power_well_(o.power_well_) {
25 o.power_ = nullptr;
26 }
27
operator =(PowerWellRef && o)28 PowerWellRef& PowerWellRef::operator=(PowerWellRef&& o) {
29 power_ = o.power_;
30 power_well_ = o.power_well_;
31 o.power_ = nullptr;
32 return *this;
33 }
34
~PowerWellRef()35 PowerWellRef::~PowerWellRef() {
36 if (power_ == nullptr) {
37 return;
38 }
39 if (power_well_ == PowerWell2 && --power_->power_well2_refs_ == 0) {
40 power_->SetPowerWell2Enable(false);
41 }
42 if (--power_->power_well1_refs_ == 0) {
43 power_->SetPowerWell1Enable(false);
44 }
45 }
46
Power(Controller * controller)47 Power::Power(Controller* controller) : controller_(controller) {}
48
Resume()49 void Power::Resume() {
50 if (power_well1_refs_ > 0) {
51 SetPowerWell1Enable(true);
52 }
53 if (power_well2_refs_ > 0) {
54 SetPowerWell2Enable(true);
55 }
56 }
57
GetCdClockPowerWellRef()58 PowerWellRef Power::GetCdClockPowerWellRef() {
59 return PowerWellRef(this, PowerWell1);
60 }
61
GetPipePowerWellRef(registers::Pipe pipe)62 PowerWellRef Power::GetPipePowerWellRef(registers::Pipe pipe) {
63 return PowerWellRef(this, pipe == registers::PIPE_A ? PowerWell1 : PowerWell2);
64 }
65
GetDdiPowerWellRef(registers::Ddi ddi)66 PowerWellRef Power::GetDdiPowerWellRef(registers::Ddi ddi) {
67 return PowerWellRef(this, ddi == registers::DDI_A ? PowerWell1 : PowerWell2);
68 }
69
SetPowerWell1Enable(bool enable)70 void Power::SetPowerWell1Enable(bool enable) {
71 auto power_well = registers::PowerWellControl2::Get().ReadFrom(controller_->mmio_space());
72 power_well.set_power_well_1_request(enable);
73 power_well.set_misc_io_power_state(enable);
74 power_well.WriteTo(controller_->mmio_space());
75
76 if (enable) {
77 if (!WAIT_ON_US(registers::PowerWellControl2
78 ::Get().ReadFrom(controller_->mmio_space()).power_well_1_state(), 10)) {
79 LOG_ERROR("Power Well 1 failed to enable\n");
80 return;
81 }
82 if (!WAIT_ON_US(registers::PowerWellControl2
83 ::Get().ReadFrom(controller_->mmio_space()).misc_io_power_state(), 10)) {
84 LOG_ERROR("Misc IO power failed to enable\n");
85 return;
86 }
87 if (!WAIT_ON_US(registers::FuseStatus
88 ::Get().ReadFrom(controller_->mmio_space()).pg1_dist_status(), 5)) {
89 LOG_ERROR("Power Well 1 distribution failed\n");
90 return;
91 }
92 } else {
93 // Unconditionally sleep when disabling power well 1, as per the docs
94 zx_nanosleep(zx_deadline_after(ZX_MSEC(10)));
95 }
96 }
97
SetPowerWell2Enable(bool enable)98 void Power::SetPowerWell2Enable(bool enable) {
99 auto power_well = registers::PowerWellControl2::Get().ReadFrom(controller_->mmio_space());
100 power_well.set_power_well_2_request(enable);
101 power_well.WriteTo(controller_->mmio_space());
102
103 if (enable) {
104 power_well.ReadFrom(controller_->mmio_space());
105 if (!WAIT_ON_US(registers::PowerWellControl2
106 ::Get().ReadFrom(controller_->mmio_space()).power_well_2_state(), 20)) {
107 LOG_ERROR("Failed to enable Power Well 2\n");
108 return;
109 }
110 if (!WAIT_ON_US(registers::FuseStatus
111 ::Get().ReadFrom(controller_->mmio_space()).pg2_dist_status(), 1)) {
112 LOG_ERROR("Power Well 2 distribution failed\n");
113 return;
114 }
115 }
116 }
117 } // namespace i915
118