1 /**
2  * Copyright © 2017 IBM Corporation
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 #include <phosphor-logging/log.hpp>
17 #include <phosphor-logging/elog.hpp>
18 #include <xyz/openbmc_project/Sensor/Device/error.hpp>
19 #include <xyz/openbmc_project/Control/Device/error.hpp>
20 #include <xyz/openbmc_project/Power/Fault/error.hpp>
21 #include "elog-errors.hpp"
22 #include "names_values.hpp"
23 #include "power_supply.hpp"
24 #include "pmbus.hpp"
25 #include "utility.hpp"
26 
27 using namespace phosphor::logging;
28 using namespace sdbusplus::xyz::openbmc_project::Control::Device::Error;
29 using namespace sdbusplus::xyz::openbmc_project::Sensor::Device::Error;
30 using namespace sdbusplus::xyz::openbmc_project::Power::Fault::Error;
31 
32 namespace witherspoon
33 {
34 namespace power
35 {
36 namespace psu
37 {
38 
39 constexpr auto INVENTORY_OBJ_PATH = "/xyz/openbmc_project/inventory";
40 constexpr auto INVENTORY_INTERFACE = "xyz.openbmc_project.Inventory.Item";
41 constexpr auto PRESENT_PROP = "Present";
42 
43 PowerSupply::PowerSupply(const std::string& name, size_t inst,
44                          const std::string& objpath, const std::string& invpath,
45                          sdbusplus::bus::bus& bus)
46     : Device(name, inst), monitorPath(objpath), inventoryPath(invpath),
47       bus(bus), pmbusIntf(objpath)
48 {
49     updatePresence();
50 
51     using namespace sdbusplus::bus;
52     auto present_obj_path = INVENTORY_OBJ_PATH + inventoryPath;
53     presentMatch = std::make_unique<match_t>(bus,
54                                              match::rules::propertiesChanged(
55                                                      present_obj_path,
56                                                      INVENTORY_INTERFACE),
57                                              [this](auto& msg)
58     {
59         this->inventoryChanged(msg);
60     });
61 }
62 
63 void PowerSupply::analyze()
64 {
65     using namespace witherspoon::pmbus;
66 
67     try
68     {
69         if (present)
70         {
71             auto curUVFault = pmbusIntf.readBit(VIN_UV_FAULT, Type::Hwmon);
72             //TODO: 3 consecutive reads should be performed.
73             // If 3 consecutive reads are seen, log the fault.
74             // Driver gives cached value, read once a second.
75             // increment for fault on, decrement for fault off, to deglitch.
76             // If count reaches 3, we have fault. If count reaches 0, fault is
77             // cleared.
78 
79             //TODO: INPUT FAULT or WARNING bit to check from STATUS_WORD
80             // pmbus-core update to read high byte of STATUS_WORD?
81 
82             if ((curUVFault != vinUVFault) || inputFault)
83             {
84 
85                 if (curUVFault)
86                 {
87                     std::uint16_t statusWord = 0;
88                     statusWord = pmbusIntf.read(STATUS_WORD, Type::Debug);
89 
90                     util::NamesValues nv;
91                     nv.add("STATUS_WORD", statusWord);
92 
93                     using metadata = xyz::openbmc_project::Power::Fault::
94                             PowerSupplyUnderVoltageFault;
95 
96                     report<PowerSupplyUnderVoltageFault>(
97                             metadata::RAW_STATUS(nv.get().c_str()));
98 
99                     vinUVFault = true;
100                 }
101                 else
102                 {
103                     log<level::INFO>("VIN_UV_FAULT cleared",
104                                      entry("POWERSUPPLY=%s",
105                                            inventoryPath.c_str()));
106                     vinUVFault = false;
107                 }
108             }
109         }
110     }
111     catch (ReadFailure& e)
112     {
113         if (!readFailLogged)
114         {
115             commit<ReadFailure>();
116             readFailLogged = true;
117             // TODO - Need to reset that to false at start of power on, or
118             // presence change.
119         }
120     }
121 
122     return;
123 }
124 
125 void PowerSupply::inventoryChanged(sdbusplus::message::message& msg)
126 {
127     std::string msgSensor;
128     std::map<std::string, sdbusplus::message::variant<uint32_t, bool>> msgData;
129     msg.read(msgSensor, msgData);
130 
131     // Check if it was the Present property that changed.
132     auto valPropMap = msgData.find(PRESENT_PROP);
133     if (valPropMap != msgData.end())
134     {
135         present = sdbusplus::message::variant_ns::get<bool>(valPropMap->second);
136 
137         if (present)
138         {
139             readFailLogged = false;
140             vinUVFault = false;
141         }
142     }
143 
144     return;
145 }
146 
147 void PowerSupply::updatePresence()
148 {
149     // Use getProperty utility function to get presence status.
150     std::string path = INVENTORY_OBJ_PATH + inventoryPath;
151     std::string service = "xyz.openbmc_project.Inventory.Manager";
152     util::getProperty(INVENTORY_INTERFACE, PRESENT_PROP, path,
153                       service, bus, this->present);
154 }
155 
156 void PowerSupply::clearFaults()
157 {
158     //TODO - Clear faults at pre-poweron. openbmc/openbmc#1736
159     return;
160 }
161 
162 }
163 }
164 }
165