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