blob: faf56397a9915bff6207173a552b2c896b697bce [file] [log] [blame]
Kuiying Wanga9d39e32018-08-14 13:47:32 +08001/*
2// Copyright (c) 2018 Intel 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
George Liud6a1bae2022-06-20 13:47:31 +080017#include "config.h"
Patrick Venture0d9377d2018-11-01 19:34:59 -070018
George Liud6a1bae2022-06-20 13:47:31 +080019#include "gpio.hpp"
Matt Spinler8f2c95a2018-11-05 15:17:29 -060020
Kuiying Wanga9d39e32018-08-14 13:47:32 +080021#include <fcntl.h>
Patrick Venture0d9377d2018-11-01 19:34:59 -070022#include <unistd.h>
23
Matt Spinler8f2c95a2018-11-05 15:17:29 -060024#include <gpioplus/utility/aspeed.hpp>
Matt Spinler8605bdf2018-11-05 14:55:46 -060025#include <nlohmann/json.hpp>
Matt Spinler8605bdf2018-11-05 14:55:46 -060026#include <phosphor-logging/log.hpp>
Kuiying Wanga9d39e32018-08-14 13:47:32 +080027
George Liu5b98f4d2022-06-20 13:31:14 +080028#include <filesystem>
29#include <fstream>
30
Matt Spinler8f2c95a2018-11-05 15:17:29 -060031const std::string gpioDev = "/sys/class/gpio";
Matt Spinler8605bdf2018-11-05 14:55:46 -060032
33using namespace phosphor::logging;
Patrick Williams8eca9bb2022-06-16 17:11:51 -050034namespace fs = std::filesystem;
Matt Spinler8605bdf2018-11-05 14:55:46 -060035
Kuiying Wanga9d39e32018-08-14 13:47:32 +080036void closeGpio(int fd)
37{
38 if (fd > 0)
39 {
40 ::close(fd);
41 }
42}
43
Matt Spinler8f2c95a2018-11-05 15:17:29 -060044uint32_t getGpioBase()
45{
46 // Look for a /sys/class/gpio/gpiochip*/label file
47 // with a value of GPIO_BASE_LABEL_NAME. Then read
48 // the base value from the 'base' file in that directory.
49#ifdef LOOKUP_GPIO_BASE
50 for (auto& f : fs::directory_iterator(gpioDev))
51 {
52 std::string path{f.path()};
53 if (path.find("gpiochip") == std::string::npos)
54 {
55 continue;
56 }
57
58 std::ifstream labelStream{path + "/label"};
59 std::string label;
60 labelStream >> label;
61
62 if (label == GPIO_BASE_LABEL_NAME)
63 {
64 uint32_t base;
65 std::ifstream baseStream{path + "/base"};
66 baseStream >> base;
67 return base;
68 }
69 }
70
71 log<level::ERR>("Could not find GPIO base");
72 throw std::runtime_error("Could not find GPIO base!");
73#else
74 return 0;
75#endif
76}
77
78uint32_t getGpioNum(const std::string& gpioPin)
79{
80 // gpioplus promises that they will figure out how to easily
81 // support multiple BMC vendors when the time comes.
82 auto offset = gpioplus::utility::aspeed::nameToOffset(gpioPin);
83
84 return getGpioBase() + offset;
85}
86
Naveen Mosesa1af3292021-12-15 11:47:01 +053087int configGroupGpio(buttonConfig& buttonIFConfig)
Matt Spinler8605bdf2018-11-05 14:55:46 -060088{
Naveen Mosesdd5495c2021-12-03 22:40:46 +053089 int result = 0;
90 // iterate the list of gpios from the button interface config
91 // and initialize them
92 for (auto& gpioCfg : buttonIFConfig.gpios)
Matt Spinler8605bdf2018-11-05 14:55:46 -060093 {
Naveen Mosesa1af3292021-12-15 11:47:01 +053094 result = configGpio(gpioCfg);
Naveen Mosesdd5495c2021-12-03 22:40:46 +053095 if (result < 0)
Matt Spinler8605bdf2018-11-05 14:55:46 -060096 {
Naveen Mosesdd5495c2021-12-03 22:40:46 +053097 std::string errorMsg =
98 "Error configuring gpio: GPIO_NUM=" +
99 std::to_string(gpioCfg.number) +
100 ",BUTTON_NAME=" + buttonIFConfig.formFactorName;
101 log<level::ERR>(errorMsg.c_str());
102
103 break;
Matt Spinler8605bdf2018-11-05 14:55:46 -0600104 }
105 }
Naveen Mosesdd5495c2021-12-03 22:40:46 +0530106
107 return result;
Matt Spinler8605bdf2018-11-05 14:55:46 -0600108}
109
Naveen Mosesa1af3292021-12-15 11:47:01 +0530110int configGpio(gpioInfo& gpioConfig)
Matt Spinler8f2c95a2018-11-05 15:17:29 -0600111{
Naveen Mosesdd5495c2021-12-03 22:40:46 +0530112 auto gpioNum = gpioConfig.number;
113 auto gpioDirection = gpioConfig.direction;
Kuiying Wanga9d39e32018-08-14 13:47:32 +0800114
Matt Spinler8f2c95a2018-11-05 15:17:29 -0600115 std::string devPath{gpioDev};
Kuiying Wanga9d39e32018-08-14 13:47:32 +0800116
117 std::fstream stream;
118
119 stream.exceptions(std::ifstream::failbit | std::ifstream::badbit);
120
Matt Spinler8f2c95a2018-11-05 15:17:29 -0600121 devPath += "/gpio" + std::to_string(gpioNum) + "/value";
Kuiying Wanga9d39e32018-08-14 13:47:32 +0800122
Matt Spinler8f2c95a2018-11-05 15:17:29 -0600123 fs::path fullPath(devPath);
Kuiying Wanga9d39e32018-08-14 13:47:32 +0800124
Matt Spinler8f2c95a2018-11-05 15:17:29 -0600125 if (fs::exists(fullPath))
Kuiying Wanga9d39e32018-08-14 13:47:32 +0800126 {
Matt Spinler8605bdf2018-11-05 14:55:46 -0600127 log<level::INFO>("GPIO exported", entry("PATH=%s", devPath.c_str()));
Kuiying Wanga9d39e32018-08-14 13:47:32 +0800128 }
129 else
130 {
Kuiying Wanga9d39e32018-08-14 13:47:32 +0800131 devPath = gpioDev + "/export";
132
133 stream.open(devPath, std::fstream::out);
134 try
135 {
136 stream << gpioNum;
137 stream.close();
138 }
139
Patrick Venture0d9377d2018-11-01 19:34:59 -0700140 catch (const std::exception& e)
Kuiying Wanga9d39e32018-08-14 13:47:32 +0800141 {
Matt Spinler8605bdf2018-11-05 14:55:46 -0600142 log<level::ERR>("Error in writing!",
143 entry("PATH=%s", devPath.c_str()),
144 entry("NUM=%d", gpioNum));
Kuiying Wanga9d39e32018-08-14 13:47:32 +0800145 return -1;
146 }
147 }
148
149 if (gpioDirection == "out")
150 {
Kuiying Wanga9d39e32018-08-14 13:47:32 +0800151 devPath = gpioDev + "/gpio" + std::to_string(gpioNum) + "/value";
152
153 uint32_t currentValue;
154
155 stream.open(devPath, std::fstream::in);
156 try
157 {
158 stream >> currentValue;
159 stream.close();
160 }
161
162 catch (const std::exception& e)
163 {
Matt Spinler8605bdf2018-11-05 14:55:46 -0600164 log<level::ERR>("Error in reading!",
165 entry("PATH=%s", devPath.c_str()));
Kuiying Wanga9d39e32018-08-14 13:47:32 +0800166 return -1;
167 }
168
169 const char* direction = currentValue ? "high" : "low";
170
171 devPath.clear();
172 devPath = gpioDev + "/gpio" + std::to_string(gpioNum) + "/direction";
173
174 stream.open(devPath, std::fstream::out);
175 try
176 {
177 stream << direction;
178 stream.close();
179 }
180
181 catch (const std::exception& e)
182 {
Matt Spinler8605bdf2018-11-05 14:55:46 -0600183 log<level::ERR>("Error in writing!");
Kuiying Wanga9d39e32018-08-14 13:47:32 +0800184 return -1;
185 }
186 }
187 else if (gpioDirection == "in")
188 {
Kuiying Wanga9d39e32018-08-14 13:47:32 +0800189 devPath = gpioDev + "/gpio" + std::to_string(gpioNum) + "/direction";
190
191 stream.open(devPath, std::fstream::out);
192 try
193 {
194 stream << gpioDirection;
195 stream.close();
196 }
197
198 catch (const std::exception& e)
199 {
Matt Spinler8605bdf2018-11-05 14:55:46 -0600200 log<level::ERR>("Error in writing!");
Kuiying Wanga9d39e32018-08-14 13:47:32 +0800201 return -1;
202 }
203 }
204 else if ((gpioDirection == "both"))
205 {
Tim Lee582b3f02019-01-03 15:39:27 +0800206 devPath = gpioDev + "/gpio" + std::to_string(gpioNum) + "/direction";
207
208 stream.open(devPath, std::fstream::out);
209 try
210 {
George Liu94afa4b2022-06-20 13:36:43 +0800211 // Before set gpio configure as an interrupt pin, need to set
212 // direction as 'in' or edge can't set as 'rising', 'falling' and
213 // 'both'
214 const char* in_direction = "in";
Tim Lee582b3f02019-01-03 15:39:27 +0800215 stream << in_direction;
216 stream.close();
217 }
218
219 catch (const std::exception& e)
220 {
221 log<level::ERR>("Error in writing!");
222 return -1;
223 }
224 devPath.clear();
Kuiying Wanga9d39e32018-08-14 13:47:32 +0800225
226 // For gpio configured as ‘both’, it is an interrupt pin and trigged on
227 // both rising and falling signals
Kuiying Wanga9d39e32018-08-14 13:47:32 +0800228 devPath = gpioDev + "/gpio" + std::to_string(gpioNum) + "/edge";
229
230 stream.open(devPath, std::fstream::out);
231 try
232 {
233 stream << gpioDirection;
234 stream.close();
235 }
236
237 catch (const std::exception& e)
238 {
Matt Spinler8605bdf2018-11-05 14:55:46 -0600239 log<level::ERR>("Error in writing!");
Kuiying Wanga9d39e32018-08-14 13:47:32 +0800240 return -1;
241 }
242 }
243
Kuiying Wanga9d39e32018-08-14 13:47:32 +0800244 devPath = gpioDev + "/gpio" + std::to_string(gpioNum) + "/value";
245
Naveen Mosesdd5495c2021-12-03 22:40:46 +0530246 auto fd = ::open(devPath.c_str(), O_RDWR | O_NONBLOCK);
Kuiying Wanga9d39e32018-08-14 13:47:32 +0800247
Naveen Mosesdd5495c2021-12-03 22:40:46 +0530248 if (fd < 0)
Kuiying Wanga9d39e32018-08-14 13:47:32 +0800249 {
Matt Spinler8605bdf2018-11-05 14:55:46 -0600250 log<level::ERR>("open error!");
Kuiying Wanga9d39e32018-08-14 13:47:32 +0800251 return -1;
252 }
253
Naveen Mosesdd5495c2021-12-03 22:40:46 +0530254 gpioConfig.fd = fd;
255
Kuiying Wanga9d39e32018-08-14 13:47:32 +0800256 return 0;
257}