blob: a398d04627c9aabd04bf9fcaf1cf427e4ba59724 [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
Patrick Venture0d9377d2018-11-01 19:34:59 -070017#include "gpio.hpp"
18
Matt Spinler8f2c95a2018-11-05 15:17:29 -060019#include "settings.hpp"
20
Kuiying Wanga9d39e32018-08-14 13:47:32 +080021#include <fcntl.h>
Patrick Venture0d9377d2018-11-01 19:34:59 -070022#include <unistd.h>
23
24#include <experimental/filesystem>
Kuiying Wanga9d39e32018-08-14 13:47:32 +080025#include <fstream>
Matt Spinler8f2c95a2018-11-05 15:17:29 -060026#include <gpioplus/utility/aspeed.hpp>
Matt Spinler8605bdf2018-11-05 14:55:46 -060027#include <nlohmann/json.hpp>
Matt Spinler8f2c95a2018-11-05 15:17:29 -060028#include <optional>
Matt Spinler8605bdf2018-11-05 14:55:46 -060029#include <phosphor-logging/log.hpp>
Matt Spinler8f2c95a2018-11-05 15:17:29 -060030#include <tuple>
Kuiying Wanga9d39e32018-08-14 13:47:32 +080031
Matt Spinler8f2c95a2018-11-05 15:17:29 -060032const std::string gpioDev = "/sys/class/gpio";
Matt Spinler8605bdf2018-11-05 14:55:46 -060033static constexpr auto gpioDefs = "/etc/default/obmc/gpio/gpio_defs.json";
34
35using namespace phosphor::logging;
Matt Spinler8f2c95a2018-11-05 15:17:29 -060036namespace fs = std::experimental::filesystem;
Matt Spinler8605bdf2018-11-05 14:55:46 -060037
Kuiying Wanga9d39e32018-08-14 13:47:32 +080038void closeGpio(int fd)
39{
40 if (fd > 0)
41 {
42 ::close(fd);
43 }
44}
45
Matt Spinler8f2c95a2018-11-05 15:17:29 -060046uint32_t getGpioBase()
47{
48 // Look for a /sys/class/gpio/gpiochip*/label file
49 // with a value of GPIO_BASE_LABEL_NAME. Then read
50 // the base value from the 'base' file in that directory.
51#ifdef LOOKUP_GPIO_BASE
52 for (auto& f : fs::directory_iterator(gpioDev))
53 {
54 std::string path{f.path()};
55 if (path.find("gpiochip") == std::string::npos)
56 {
57 continue;
58 }
59
60 std::ifstream labelStream{path + "/label"};
61 std::string label;
62 labelStream >> label;
63
64 if (label == GPIO_BASE_LABEL_NAME)
65 {
66 uint32_t base;
67 std::ifstream baseStream{path + "/base"};
68 baseStream >> base;
69 return base;
70 }
71 }
72
73 log<level::ERR>("Could not find GPIO base");
74 throw std::runtime_error("Could not find GPIO base!");
75#else
76 return 0;
77#endif
78}
79
80uint32_t getGpioNum(const std::string& gpioPin)
81{
82 // gpioplus promises that they will figure out how to easily
83 // support multiple BMC vendors when the time comes.
84 auto offset = gpioplus::utility::aspeed::nameToOffset(gpioPin);
85
86 return getGpioBase() + offset;
87}
88
Matt Spinler8605bdf2018-11-05 14:55:46 -060089bool gpioDefined(const std::string& gpioName)
90{
91 try
92 {
93 std::ifstream gpios{gpioDefs};
94 auto json = nlohmann::json::parse(gpios, nullptr, true);
95 auto defs = json["gpio_definitions"];
96
97 auto gpio =
98 std::find_if(defs.begin(), defs.end(), [&gpioName](const auto g) {
99 return gpioName == g["name"];
100 });
101
102 if (gpio != defs.end())
103 {
104 return true;
105 }
106 }
107 catch (std::exception& e)
108 {
109 log<level::ERR>("Error parsing GPIO JSON", entry("ERROR=%s", e.what()),
110 entry("GPIO_NAME=%s", gpioName.c_str()));
111 }
112 return false;
113}
114
Matt Spinler8f2c95a2018-11-05 15:17:29 -0600115std::optional<std::tuple<int, std::string>>
116 getGpioConfig(const std::string& gpioName)
117{
118
119 try
120 {
121 std::ifstream gpios{gpioDefs};
122 auto json = nlohmann::json::parse(gpios, nullptr, true);
123 auto defs = json["gpio_definitions"];
124
125 auto gpio =
126 std::find_if(defs.begin(), defs.end(), [&gpioName](const auto g) {
127 return gpioName == g["name"];
128 });
129
130 if (gpio != defs.end())
131 {
132 return std::make_tuple(getGpioNum((*gpio)["pin"]),
133 (*gpio)["direction"]);
134 }
135 else
136 {
137 log<level::ERR>("Unable to find GPIO in the definitions",
138 entry("GPIO_NAME=%s", gpioName.c_str()));
139 }
140 }
141 catch (std::exception& e)
142 {
143 log<level::ERR>("Error parsing GPIO JSON", entry("ERROR=%s", e.what()),
144 entry("GPIO_NAME=%s", gpioName.c_str()));
145 }
146 return {};
147}
148
Kuiying Wanga9d39e32018-08-14 13:47:32 +0800149int configGpio(const char* gpioName, int* fd, sdbusplus::bus::bus& bus)
150{
Matt Spinler8f2c95a2018-11-05 15:17:29 -0600151 auto config = getGpioConfig(gpioName);
152 if (!config)
Kuiying Wanga9d39e32018-08-14 13:47:32 +0800153 {
Kuiying Wanga9d39e32018-08-14 13:47:32 +0800154 return -1;
155 }
156
Matt Spinler8f2c95a2018-11-05 15:17:29 -0600157 auto [gpioNum, gpioDirection] = *config;
Kuiying Wanga9d39e32018-08-14 13:47:32 +0800158
Matt Spinler8f2c95a2018-11-05 15:17:29 -0600159 std::string devPath{gpioDev};
Kuiying Wanga9d39e32018-08-14 13:47:32 +0800160
161 std::fstream stream;
162
163 stream.exceptions(std::ifstream::failbit | std::ifstream::badbit);
164
Matt Spinler8f2c95a2018-11-05 15:17:29 -0600165 devPath += "/gpio" + std::to_string(gpioNum) + "/value";
Kuiying Wanga9d39e32018-08-14 13:47:32 +0800166
Matt Spinler8f2c95a2018-11-05 15:17:29 -0600167 fs::path fullPath(devPath);
Kuiying Wanga9d39e32018-08-14 13:47:32 +0800168
Matt Spinler8f2c95a2018-11-05 15:17:29 -0600169 if (fs::exists(fullPath))
Kuiying Wanga9d39e32018-08-14 13:47:32 +0800170 {
Matt Spinler8605bdf2018-11-05 14:55:46 -0600171 log<level::INFO>("GPIO exported", entry("PATH=%s", devPath.c_str()));
Kuiying Wanga9d39e32018-08-14 13:47:32 +0800172 }
173 else
174 {
Kuiying Wanga9d39e32018-08-14 13:47:32 +0800175 devPath = gpioDev + "/export";
176
177 stream.open(devPath, std::fstream::out);
178 try
179 {
180 stream << gpioNum;
181 stream.close();
182 }
183
Patrick Venture0d9377d2018-11-01 19:34:59 -0700184 catch (const std::exception& e)
Kuiying Wanga9d39e32018-08-14 13:47:32 +0800185 {
Matt Spinler8605bdf2018-11-05 14:55:46 -0600186 log<level::ERR>("Error in writing!",
187 entry("PATH=%s", devPath.c_str()),
188 entry("NUM=%d", gpioNum));
Kuiying Wanga9d39e32018-08-14 13:47:32 +0800189 return -1;
190 }
191 }
192
193 if (gpioDirection == "out")
194 {
Kuiying Wanga9d39e32018-08-14 13:47:32 +0800195 devPath = gpioDev + "/gpio" + std::to_string(gpioNum) + "/value";
196
197 uint32_t currentValue;
198
199 stream.open(devPath, std::fstream::in);
200 try
201 {
202 stream >> currentValue;
203 stream.close();
204 }
205
206 catch (const std::exception& e)
207 {
Matt Spinler8605bdf2018-11-05 14:55:46 -0600208 log<level::ERR>("Error in reading!",
209 entry("PATH=%s", devPath.c_str()));
Kuiying Wanga9d39e32018-08-14 13:47:32 +0800210 return -1;
211 }
212
213 const char* direction = currentValue ? "high" : "low";
214
215 devPath.clear();
216 devPath = gpioDev + "/gpio" + std::to_string(gpioNum) + "/direction";
217
218 stream.open(devPath, std::fstream::out);
219 try
220 {
221 stream << direction;
222 stream.close();
223 }
224
225 catch (const std::exception& e)
226 {
Matt Spinler8605bdf2018-11-05 14:55:46 -0600227 log<level::ERR>("Error in writing!");
Kuiying Wanga9d39e32018-08-14 13:47:32 +0800228 return -1;
229 }
230 }
231 else if (gpioDirection == "in")
232 {
Kuiying Wanga9d39e32018-08-14 13:47:32 +0800233 devPath = gpioDev + "/gpio" + std::to_string(gpioNum) + "/direction";
234
235 stream.open(devPath, std::fstream::out);
236 try
237 {
238 stream << gpioDirection;
239 stream.close();
240 }
241
242 catch (const std::exception& e)
243 {
Matt Spinler8605bdf2018-11-05 14:55:46 -0600244 log<level::ERR>("Error in writing!");
Kuiying Wanga9d39e32018-08-14 13:47:32 +0800245 return -1;
246 }
247 }
248 else if ((gpioDirection == "both"))
249 {
250
251 // For gpio configured as ‘both’, it is an interrupt pin and trigged on
252 // both rising and falling signals
Kuiying Wanga9d39e32018-08-14 13:47:32 +0800253 devPath = gpioDev + "/gpio" + std::to_string(gpioNum) + "/edge";
254
255 stream.open(devPath, std::fstream::out);
256 try
257 {
258 stream << gpioDirection;
259 stream.close();
260 }
261
262 catch (const std::exception& e)
263 {
Matt Spinler8605bdf2018-11-05 14:55:46 -0600264 log<level::ERR>("Error in writing!");
Kuiying Wanga9d39e32018-08-14 13:47:32 +0800265 return -1;
266 }
267 }
268
Kuiying Wanga9d39e32018-08-14 13:47:32 +0800269 devPath = gpioDev + "/gpio" + std::to_string(gpioNum) + "/value";
270
271 *fd = ::open(devPath.c_str(), O_RDWR | O_NONBLOCK);
272
273 if (*fd < 0)
274 {
Matt Spinler8605bdf2018-11-05 14:55:46 -0600275 log<level::ERR>("open error!");
Kuiying Wanga9d39e32018-08-14 13:47:32 +0800276 return -1;
277 }
278
279 return 0;
280}