xref: /openbmc/phosphor-buttons/src/gpio.cpp (revision 010035ee96cd6b32d1bff77eb5110d030f4e2684)
1a9d39e30SKuiying Wang /*
2a9d39e30SKuiying Wang // Copyright (c) 2018 Intel Corporation
3a9d39e30SKuiying Wang //
4a9d39e30SKuiying Wang // Licensed under the Apache License, Version 2.0 (the "License");
5a9d39e30SKuiying Wang // you may not use this file except in compliance with the License.
6a9d39e30SKuiying Wang // You may obtain a copy of the License at
7a9d39e30SKuiying Wang //
8a9d39e30SKuiying Wang //      http://www.apache.org/licenses/LICENSE-2.0
9a9d39e30SKuiying Wang //
10a9d39e30SKuiying Wang // Unless required by applicable law or agreed to in writing, software
11a9d39e30SKuiying Wang // distributed under the License is distributed on an "AS IS" BASIS,
12a9d39e30SKuiying Wang // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13a9d39e30SKuiying Wang // See the License for the specific language governing permissions and
14a9d39e30SKuiying Wang // limitations under the License.
15a9d39e30SKuiying Wang */
16a9d39e30SKuiying Wang 
17ccd7db05SDelphine CC Chiu #include "button_config.hpp"
1815c60e2fSDelphine CC Chiu #include "config.hpp"
198f2c95a4SMatt Spinler 
209fb15970SGeorge Liu #include <error.h>
21a9d39e30SKuiying Wang #include <fcntl.h>
220d9377d2SPatrick Venture #include <unistd.h>
230d9377d2SPatrick Venture 
248f2c95a4SMatt Spinler #include <gpioplus/utility/aspeed.hpp>
258605bdffSMatt Spinler #include <nlohmann/json.hpp>
269fb15970SGeorge Liu #include <phosphor-logging/lg2.hpp>
27a9d39e30SKuiying Wang 
285b98f4dbSGeorge Liu #include <filesystem>
295b98f4dbSGeorge Liu #include <fstream>
305b98f4dbSGeorge Liu 
318f2c95a4SMatt Spinler const std::string gpioDev = "/sys/class/gpio";
328eca9bb6SPatrick Williams namespace fs = std::filesystem;
33d219fa3cSNaveen Moses std::unordered_map<GpioPolarity, GPIOBufferValue> GpioValueMap = {
34d219fa3cSNaveen Moses     {GpioPolarity::activeLow, {'0', '1'}},
35d219fa3cSNaveen Moses     {GpioPolarity::activeHigh, {'1', '0'}}};
368605bdffSMatt Spinler 
setGpioState(int fd,GpioPolarity polarity,GpioState state)37d219fa3cSNaveen Moses void setGpioState(int fd, GpioPolarity polarity, GpioState state)
38d219fa3cSNaveen Moses {
39d219fa3cSNaveen Moses     char writeBuffer;
40d219fa3cSNaveen Moses 
41d219fa3cSNaveen Moses     if (state == GpioState::assert)
42d219fa3cSNaveen Moses     {
43d219fa3cSNaveen Moses         writeBuffer = GpioValueMap[polarity].assert;
44d219fa3cSNaveen Moses     }
45d219fa3cSNaveen Moses     else
46d219fa3cSNaveen Moses     {
47d219fa3cSNaveen Moses         writeBuffer = GpioValueMap[polarity].deassert;
48d219fa3cSNaveen Moses     }
49d219fa3cSNaveen Moses 
50d219fa3cSNaveen Moses     auto result = ::write(fd, &writeBuffer, sizeof(writeBuffer));
51d219fa3cSNaveen Moses     if (result < 0)
52d219fa3cSNaveen Moses     {
53d219fa3cSNaveen Moses         lg2::error("GPIO write error {GPIOFD} : {ERRORNO}", "GPIOFD", fd,
54d219fa3cSNaveen Moses                    "ERRORNO", errno);
55d219fa3cSNaveen Moses     }
56d219fa3cSNaveen Moses     return;
57d219fa3cSNaveen Moses }
getGpioState(int fd,GpioPolarity polarity)58d219fa3cSNaveen Moses GpioState getGpioState(int fd, GpioPolarity polarity)
59d219fa3cSNaveen Moses {
60d219fa3cSNaveen Moses     int result = -1;
61d219fa3cSNaveen Moses     char readBuffer = '0';
62d219fa3cSNaveen Moses 
63d219fa3cSNaveen Moses     result = ::lseek(fd, 0, SEEK_SET);
64d219fa3cSNaveen Moses 
65d219fa3cSNaveen Moses     if (result < 0)
66d219fa3cSNaveen Moses     {
67d219fa3cSNaveen Moses         lg2::error("GPIO lseek error {GPIOFD}: {ERROR}", "GPIOFD", fd, "ERROR",
68d219fa3cSNaveen Moses                    errno);
69d219fa3cSNaveen Moses         return GpioState::invalid;
70d219fa3cSNaveen Moses     }
71d219fa3cSNaveen Moses 
72d219fa3cSNaveen Moses     result = ::read(fd, &readBuffer, sizeof(readBuffer));
73d219fa3cSNaveen Moses     if (result < 0)
74d219fa3cSNaveen Moses     {
75d219fa3cSNaveen Moses         lg2::error("GPIO read error {GPIOFD}: {ERRORNO}", "GPIOFD", fd,
76d219fa3cSNaveen Moses                    "ERRORNO", errno);
77d219fa3cSNaveen Moses         throw std::runtime_error("GPIO read failed");
78d219fa3cSNaveen Moses     }
79d219fa3cSNaveen Moses     // read the gpio state for the io event received
80d219fa3cSNaveen Moses     GpioState gpioState = (readBuffer == GpioValueMap[polarity].assert)
81d219fa3cSNaveen Moses                               ? (GpioState::assert)
82d219fa3cSNaveen Moses                               : (GpioState::deassert);
83d219fa3cSNaveen Moses     return gpioState;
84d219fa3cSNaveen Moses }
85a9d39e30SKuiying Wang 
getGpioBase()868f2c95a4SMatt Spinler uint32_t getGpioBase()
878f2c95a4SMatt Spinler {
888f2c95a4SMatt Spinler     // Look for a /sys/class/gpio/gpiochip*/label file
898f2c95a4SMatt Spinler     // with a value of GPIO_BASE_LABEL_NAME.  Then read
908f2c95a4SMatt Spinler     // the base value from the 'base' file in that directory.
918f2c95a4SMatt Spinler #ifdef LOOKUP_GPIO_BASE
928f2c95a4SMatt Spinler     for (auto& f : fs::directory_iterator(gpioDev))
938f2c95a4SMatt Spinler     {
948f2c95a4SMatt Spinler         std::string path{f.path()};
958f2c95a4SMatt Spinler         if (path.find("gpiochip") == std::string::npos)
968f2c95a4SMatt Spinler         {
978f2c95a4SMatt Spinler             continue;
988f2c95a4SMatt Spinler         }
998f2c95a4SMatt Spinler 
1008f2c95a4SMatt Spinler         std::ifstream labelStream{path + "/label"};
1018f2c95a4SMatt Spinler         std::string label;
1028f2c95a4SMatt Spinler         labelStream >> label;
1038f2c95a4SMatt Spinler 
1048f2c95a4SMatt Spinler         if (label == GPIO_BASE_LABEL_NAME)
1058f2c95a4SMatt Spinler         {
1068f2c95a4SMatt Spinler             uint32_t base;
1078f2c95a4SMatt Spinler             std::ifstream baseStream{path + "/base"};
1088f2c95a4SMatt Spinler             baseStream >> base;
1098f2c95a4SMatt Spinler             return base;
1108f2c95a4SMatt Spinler         }
1118f2c95a4SMatt Spinler     }
1128f2c95a4SMatt Spinler 
1139fb15970SGeorge Liu     lg2::error("Could not find GPIO base");
1148f2c95a4SMatt Spinler     throw std::runtime_error("Could not find GPIO base!");
1158f2c95a4SMatt Spinler #else
1168f2c95a4SMatt Spinler     return 0;
1178f2c95a4SMatt Spinler #endif
1188f2c95a4SMatt Spinler }
1198f2c95a4SMatt Spinler 
getGpioNum(const std::string & gpioPin)1208f2c95a4SMatt Spinler uint32_t getGpioNum(const std::string& gpioPin)
1218f2c95a4SMatt Spinler {
1228f2c95a4SMatt Spinler     // gpioplus promises that they will figure out how to easily
1238f2c95a4SMatt Spinler     // support multiple BMC vendors when the time comes.
1248f2c95a4SMatt Spinler     auto offset = gpioplus::utility::aspeed::nameToOffset(gpioPin);
1258f2c95a4SMatt Spinler 
1268f2c95a4SMatt Spinler     return getGpioBase() + offset;
1278f2c95a4SMatt Spinler }
1288f2c95a4SMatt Spinler 
configGroupGpio(ButtonConfig & buttonIFConfig)129ccd7db05SDelphine CC Chiu int configGroupGpio(ButtonConfig& buttonIFConfig)
1308605bdffSMatt Spinler {
131dd5495cfSNaveen Moses     int result = 0;
132dd5495cfSNaveen Moses     // iterate the list of gpios from the button interface config
133dd5495cfSNaveen Moses     // and initialize them
134dd5495cfSNaveen Moses     for (auto& gpioCfg : buttonIFConfig.gpios)
1358605bdffSMatt Spinler     {
136ccd7db05SDelphine CC Chiu         result = configGpio(gpioCfg, buttonIFConfig);
137dd5495cfSNaveen Moses         if (result < 0)
1388f2c95a4SMatt Spinler         {
1399fb15970SGeorge Liu             lg2::error("{NAME}: Error configuring gpio-{NUM}: {RESULT}", "NAME",
1409fb15970SGeorge Liu                        buttonIFConfig.formFactorName, "NUM", gpioCfg.number,
1419fb15970SGeorge Liu                        "RESULT", result);
1428f2c95a4SMatt Spinler 
143dd5495cfSNaveen Moses             break;
1448f2c95a4SMatt Spinler         }
1458f2c95a4SMatt Spinler     }
1468f2c95a4SMatt Spinler 
147dd5495cfSNaveen Moses     return result;
148a9d39e30SKuiying Wang }
149a9d39e30SKuiying Wang 
configGpio(GpioInfo & gpioConfig,ButtonConfig & buttonIFConfig)150ccd7db05SDelphine CC Chiu int configGpio(GpioInfo& gpioConfig, ButtonConfig& buttonIFConfig)
151dd5495cfSNaveen Moses {
152dd5495cfSNaveen Moses     auto gpioNum = gpioConfig.number;
153dd5495cfSNaveen Moses     auto gpioDirection = gpioConfig.direction;
154a9d39e30SKuiying Wang 
1558f2c95a4SMatt Spinler     std::string devPath{gpioDev};
156a9d39e30SKuiying Wang 
157a9d39e30SKuiying Wang     std::fstream stream;
158a9d39e30SKuiying Wang 
159a9d39e30SKuiying Wang     stream.exceptions(std::ifstream::failbit | std::ifstream::badbit);
160a9d39e30SKuiying Wang 
1618f2c95a4SMatt Spinler     devPath += "/gpio" + std::to_string(gpioNum) + "/value";
162a9d39e30SKuiying Wang 
1638f2c95a4SMatt Spinler     fs::path fullPath(devPath);
164a9d39e30SKuiying Wang 
1658f2c95a4SMatt Spinler     if (fs::exists(fullPath))
166a9d39e30SKuiying Wang     {
1679fb15970SGeorge Liu         lg2::info("GPIO exported: {PATH}", "PATH", devPath);
168a9d39e30SKuiying Wang     }
169a9d39e30SKuiying Wang     else
170a9d39e30SKuiying Wang     {
171a9d39e30SKuiying Wang         devPath = gpioDev + "/export";
172a9d39e30SKuiying Wang 
173a9d39e30SKuiying Wang         stream.open(devPath, std::fstream::out);
174a9d39e30SKuiying Wang         try
175a9d39e30SKuiying Wang         {
176a9d39e30SKuiying Wang             stream << gpioNum;
177a9d39e30SKuiying Wang             stream.close();
178a9d39e30SKuiying Wang         }
179a9d39e30SKuiying Wang 
180a9d39e30SKuiying Wang         catch (const std::exception& e)
181a9d39e30SKuiying Wang         {
1829fb15970SGeorge Liu             lg2::error("{NUM} error in writing {PATH}: {ERROR}", "NUM", gpioNum,
1839fb15970SGeorge Liu                        "PATH", devPath, "ERROR", e);
184a9d39e30SKuiying Wang             return -1;
185a9d39e30SKuiying Wang         }
186a9d39e30SKuiying Wang     }
187a9d39e30SKuiying Wang 
188a9d39e30SKuiying Wang     if (gpioDirection == "out")
189a9d39e30SKuiying Wang     {
190a9d39e30SKuiying Wang         devPath = gpioDev + "/gpio" + std::to_string(gpioNum) + "/value";
191a9d39e30SKuiying Wang 
192a9d39e30SKuiying Wang         uint32_t currentValue;
193a9d39e30SKuiying Wang 
194a9d39e30SKuiying Wang         stream.open(devPath, std::fstream::in);
195a9d39e30SKuiying Wang         try
196a9d39e30SKuiying Wang         {
197a9d39e30SKuiying Wang             stream >> currentValue;
198a9d39e30SKuiying Wang             stream.close();
199a9d39e30SKuiying Wang         }
200a9d39e30SKuiying Wang 
201a9d39e30SKuiying Wang         catch (const std::exception& e)
202a9d39e30SKuiying Wang         {
2039fb15970SGeorge Liu             lg2::error("Error in reading {PATH}: {ERROR}", "PATH", devPath,
2049fb15970SGeorge Liu                        "ERROR", e);
205a9d39e30SKuiying Wang             return -1;
206a9d39e30SKuiying Wang         }
207a9d39e30SKuiying Wang 
208a9d39e30SKuiying Wang         const char* direction = currentValue ? "high" : "low";
209a9d39e30SKuiying Wang 
210a9d39e30SKuiying Wang         devPath.clear();
211a9d39e30SKuiying Wang         devPath = gpioDev + "/gpio" + std::to_string(gpioNum) + "/direction";
212a9d39e30SKuiying Wang 
213a9d39e30SKuiying Wang         stream.open(devPath, std::fstream::out);
214a9d39e30SKuiying Wang         try
215a9d39e30SKuiying Wang         {
216a9d39e30SKuiying Wang             stream << direction;
217a9d39e30SKuiying Wang             stream.close();
218a9d39e30SKuiying Wang         }
219a9d39e30SKuiying Wang 
220a9d39e30SKuiying Wang         catch (const std::exception& e)
221a9d39e30SKuiying Wang         {
2229fb15970SGeorge Liu             lg2::error("Error in writing: {ERROR}", "ERROR", e);
223a9d39e30SKuiying Wang             return -1;
224a9d39e30SKuiying Wang         }
225a9d39e30SKuiying Wang     }
226a9d39e30SKuiying Wang     else if (gpioDirection == "in")
227a9d39e30SKuiying Wang     {
228a9d39e30SKuiying Wang         devPath = gpioDev + "/gpio" + std::to_string(gpioNum) + "/direction";
229a9d39e30SKuiying Wang 
230a9d39e30SKuiying Wang         stream.open(devPath, std::fstream::out);
231a9d39e30SKuiying Wang         try
232a9d39e30SKuiying Wang         {
233a9d39e30SKuiying Wang             stream << gpioDirection;
234a9d39e30SKuiying Wang             stream.close();
235a9d39e30SKuiying Wang         }
236a9d39e30SKuiying Wang 
237a9d39e30SKuiying Wang         catch (const std::exception& e)
238a9d39e30SKuiying Wang         {
2399fb15970SGeorge Liu             lg2::error("Error in writing: {ERROR}", "ERROR", e);
240a9d39e30SKuiying Wang             return -1;
241a9d39e30SKuiying Wang         }
242a9d39e30SKuiying Wang     }
243a9d39e30SKuiying Wang     else if ((gpioDirection == "both"))
244a9d39e30SKuiying Wang     {
245582b3f02STim Lee         devPath = gpioDev + "/gpio" + std::to_string(gpioNum) + "/direction";
246582b3f02STim Lee 
247582b3f02STim Lee         stream.open(devPath, std::fstream::out);
248582b3f02STim Lee         try
249582b3f02STim Lee         {
25094afa4baSGeorge Liu             // Before set gpio configure as an interrupt pin, need to set
25194afa4baSGeorge Liu             // direction as 'in' or edge can't set as 'rising', 'falling' and
25294afa4baSGeorge Liu             // 'both'
25394afa4baSGeorge Liu             const char* in_direction = "in";
254582b3f02STim Lee             stream << in_direction;
255582b3f02STim Lee             stream.close();
256582b3f02STim Lee         }
257582b3f02STim Lee 
258582b3f02STim Lee         catch (const std::exception& e)
259582b3f02STim Lee         {
2609fb15970SGeorge Liu             lg2::error("Error in writing: {ERROR}", "ERROR", e);
261582b3f02STim Lee             return -1;
262582b3f02STim Lee         }
263582b3f02STim Lee         devPath.clear();
264a9d39e30SKuiying Wang 
265*010035eeSManojkiran Eda         // For gpio configured as ‘both’, it is an interrupt pin and triggered
266*010035eeSManojkiran Eda         // on both rising and falling signals
267a9d39e30SKuiying Wang         devPath = gpioDev + "/gpio" + std::to_string(gpioNum) + "/edge";
268a9d39e30SKuiying Wang 
269a9d39e30SKuiying Wang         stream.open(devPath, std::fstream::out);
270a9d39e30SKuiying Wang         try
271a9d39e30SKuiying Wang         {
272a9d39e30SKuiying Wang             stream << gpioDirection;
273a9d39e30SKuiying Wang             stream.close();
274a9d39e30SKuiying Wang         }
275a9d39e30SKuiying Wang 
276a9d39e30SKuiying Wang         catch (const std::exception& e)
277a9d39e30SKuiying Wang         {
2789fb15970SGeorge Liu             lg2::error("Error in writing: {ERROR}", "ERROR", e);
279a9d39e30SKuiying Wang             return -1;
280a9d39e30SKuiying Wang         }
281a9d39e30SKuiying Wang     }
282a9d39e30SKuiying Wang 
283a9d39e30SKuiying Wang     devPath = gpioDev + "/gpio" + std::to_string(gpioNum) + "/value";
284a9d39e30SKuiying Wang 
285dd5495cfSNaveen Moses     auto fd = ::open(devPath.c_str(), O_RDWR | O_NONBLOCK);
286a9d39e30SKuiying Wang 
287dd5495cfSNaveen Moses     if (fd < 0)
288a9d39e30SKuiying Wang     {
2899fb15970SGeorge Liu         lg2::error("Open {PATH} error: {ERROR}", "PATH", devPath, "ERROR",
2909fb15970SGeorge Liu                    errno);
291a9d39e30SKuiying Wang         return -1;
292a9d39e30SKuiying Wang     }
293a9d39e30SKuiying Wang 
294dd5495cfSNaveen Moses     gpioConfig.fd = fd;
295ccd7db05SDelphine CC Chiu     buttonIFConfig.fds.push_back(fd);
296dd5495cfSNaveen Moses 
297a9d39e30SKuiying Wang     return 0;
298a9d39e30SKuiying Wang }
299