xref: /openbmc/phosphor-bmc-code-mgmt/common/src/gpio_controller.cpp (revision e634411ba7c22d18ae01a0b03ce4f7b881c38fcc)
1 #include "gpio_controller.hpp"
2 
3 #include <phosphor-logging/lg2.hpp>
4 
5 #include <fstream>
6 #include <map>
7 
8 PHOSPHOR_LOG2_USING;
9 
GPIOGroup(std::vector<std::string> linesIn,std::vector<bool> polaritiesIn)10 GPIOGroup::GPIOGroup(std::vector<std::string> linesIn,
11                      std::vector<bool> polaritiesIn) :
12     lines(std::move(linesIn)), polarities(std::move(polaritiesIn))
13 {}
14 
~GPIOGroup()15 GPIOGroup::~GPIOGroup()
16 {
17     releaseAll();
18 }
19 
GPIOGroup(GPIOGroup && other)20 GPIOGroup::GPIOGroup(GPIOGroup&& other) noexcept :
21     lines(std::move(other.lines)), polarities(std::move(other.polarities)),
22     activeBulks(std::move(other.activeBulks))
23 {}
24 
operator =(GPIOGroup && other)25 GPIOGroup& GPIOGroup::operator=(GPIOGroup&& other) noexcept
26 {
27     if (this != &other)
28     {
29         releaseAll();
30         lines = std::move(other.lines);
31         polarities = std::move(other.polarities);
32         activeBulks = std::move(other.activeBulks);
33     }
34     return *this;
35 }
36 
muxToBMC()37 bool GPIOGroup::muxToBMC()
38 {
39     return mux(false);
40 }
muxToDevice()41 bool GPIOGroup::muxToDevice()
42 {
43     return mux(true);
44 }
45 
releaseAll()46 void GPIOGroup::releaseAll()
47 {
48     for (auto& b : activeBulks)
49     {
50         if (b)
51         {
52             b->release();
53         }
54     }
55     activeBulks.clear();
56 }
57 
mux(bool inverted)58 bool GPIOGroup::mux(bool inverted)
59 {
60     if (lines.empty())
61     {
62         return true;
63     }
64 
65     activeBulks = requestMuxGPIOs(lines, polarities, inverted);
66     if (activeBulks.empty())
67     {
68         error("Failed to mux GPIOs", "INVERTED", inverted);
69         return false;
70     }
71     return true;
72 }
73 
ScopedBmcMux(GPIOGroup & g)74 ScopedBmcMux::ScopedBmcMux(GPIOGroup& g) : gpioGroup(g)
75 {
76     if (!gpioGroup.muxToBMC())
77     {
78         throw std::runtime_error("Failed to mux GPIOs to BMC");
79     }
80 }
~ScopedBmcMux()81 ScopedBmcMux::~ScopedBmcMux()
82 {
83     gpioGroup.releaseAll();
84     if (!gpioGroup.muxToDevice())
85     {
86         error("Failed to mux GPIOs back to device");
87     }
88     gpioGroup.releaseAll();
89 }
90 
requestMuxGPIOs(const std::vector<std::string> & gpioLines,const std::vector<bool> & gpioPolarities,bool inverted)91 std::vector<std::unique_ptr<::gpiod::line_bulk>> requestMuxGPIOs(
92     const std::vector<std::string>& gpioLines,
93     const std::vector<bool>& gpioPolarities, bool inverted)
94 {
95     std::map<std::string, std::vector<std::string>> groupLineNames;
96     std::map<std::string, std::vector<int>> groupValues;
97 
98     for (size_t i = 0; i < gpioLines.size(); ++i)
99     {
100         auto line = ::gpiod::find_line(gpioLines[i]);
101 
102         if (!line)
103         {
104             error("Failed to find GPIO line: {LINE}", "LINE", gpioLines[i]);
105             return {};
106         }
107 
108         if (line.is_used())
109         {
110             error("GPIO line {LINE} was still used", "LINE", gpioLines[i]);
111             return {};
112         }
113 
114         std::string chipName = line.get_chip().name();
115         groupLineNames[chipName].push_back(gpioLines[i]);
116         groupValues[chipName].push_back(gpioPolarities[i] ^ inverted ? 1 : 0);
117     }
118 
119     std::vector<std::unique_ptr<::gpiod::line_bulk>> lineBulks;
120     ::gpiod::line_request config{"", ::gpiod::line_request::DIRECTION_OUTPUT,
121                                  0};
122 
123     for (auto& [chipName, lineNames] : groupLineNames)
124     {
125         ::gpiod::chip chip(chipName);
126         std::vector<::gpiod::line> lines;
127 
128         for (size_t i = 0; i < lineNames.size(); ++i)
129         {
130             const auto& name = lineNames[i];
131             auto line = chip.find_line(name);
132 
133             if (!line)
134             {
135                 error("Failed to get {LINE} from chip {CHIP}", "LINE", name,
136                       "CHIP", chipName);
137                 return {};
138             }
139 
140             lg2::info("Requesting chip {CHIP}, GPIO line {LINE} to {VALUE}",
141                       "CHIP", chip.name(), "LINE", line.name(), "VALUE",
142                       groupValues[chipName][i]);
143 
144             lines.push_back(std::move(line));
145         }
146 
147         auto lineBulk = std::make_unique<::gpiod::line_bulk>(lines);
148 
149         if (!lineBulk)
150         {
151             error("Failed to create line bulk for chip={CHIP}", "CHIP",
152                   chipName);
153             return {};
154         }
155 
156         lineBulk->request(config, groupValues[chipName]);
157 
158         lineBulks.push_back(std::move(lineBulk));
159     }
160 
161     return lineBulks;
162 }
163