blob: 9f8b98d2e1bec69ee611b967695d15df872c3dea [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
George Liu9fb15972022-06-20 14:54:38 +080021#include <error.h>
Kuiying Wanga9d39e32018-08-14 13:47:32 +080022#include <fcntl.h>
Patrick Venture0d9377d2018-11-01 19:34:59 -070023#include <unistd.h>
24
Matt Spinler8f2c95a2018-11-05 15:17:29 -060025#include <gpioplus/utility/aspeed.hpp>
Matt Spinler8605bdf2018-11-05 14:55:46 -060026#include <nlohmann/json.hpp>
George Liu9fb15972022-06-20 14:54:38 +080027#include <phosphor-logging/lg2.hpp>
Kuiying Wanga9d39e32018-08-14 13:47:32 +080028
George Liu5b98f4d2022-06-20 13:31:14 +080029#include <filesystem>
30#include <fstream>
31
Matt Spinler8f2c95a2018-11-05 15:17:29 -060032const std::string gpioDev = "/sys/class/gpio";
Matt Spinler8605bdf2018-11-05 14:55:46 -060033
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
George Liu9fb15972022-06-20 14:54:38 +080071 lg2::error("Could not find GPIO base");
Matt Spinler8f2c95a2018-11-05 15:17:29 -060072 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 {
George Liu9fb15972022-06-20 14:54:38 +080097 lg2::error("{NAME}: Error configuring gpio-{NUM}: {RESULT}", "NAME",
98 buttonIFConfig.formFactorName, "NUM", gpioCfg.number,
99 "RESULT", result);
Naveen Mosesdd5495c2021-12-03 22:40:46 +0530100
101 break;
Matt Spinler8605bdf2018-11-05 14:55:46 -0600102 }
103 }
Naveen Mosesdd5495c2021-12-03 22:40:46 +0530104
105 return result;
Matt Spinler8605bdf2018-11-05 14:55:46 -0600106}
107
Naveen Mosesa1af3292021-12-15 11:47:01 +0530108int configGpio(gpioInfo& gpioConfig)
Matt Spinler8f2c95a2018-11-05 15:17:29 -0600109{
Naveen Mosesdd5495c2021-12-03 22:40:46 +0530110 auto gpioNum = gpioConfig.number;
111 auto gpioDirection = gpioConfig.direction;
Kuiying Wanga9d39e32018-08-14 13:47:32 +0800112
Matt Spinler8f2c95a2018-11-05 15:17:29 -0600113 std::string devPath{gpioDev};
Kuiying Wanga9d39e32018-08-14 13:47:32 +0800114
115 std::fstream stream;
116
117 stream.exceptions(std::ifstream::failbit | std::ifstream::badbit);
118
Matt Spinler8f2c95a2018-11-05 15:17:29 -0600119 devPath += "/gpio" + std::to_string(gpioNum) + "/value";
Kuiying Wanga9d39e32018-08-14 13:47:32 +0800120
Matt Spinler8f2c95a2018-11-05 15:17:29 -0600121 fs::path fullPath(devPath);
Kuiying Wanga9d39e32018-08-14 13:47:32 +0800122
Matt Spinler8f2c95a2018-11-05 15:17:29 -0600123 if (fs::exists(fullPath))
Kuiying Wanga9d39e32018-08-14 13:47:32 +0800124 {
George Liu9fb15972022-06-20 14:54:38 +0800125 lg2::info("GPIO exported: {PATH}", "PATH", devPath);
Kuiying Wanga9d39e32018-08-14 13:47:32 +0800126 }
127 else
128 {
Kuiying Wanga9d39e32018-08-14 13:47:32 +0800129 devPath = gpioDev + "/export";
130
131 stream.open(devPath, std::fstream::out);
132 try
133 {
134 stream << gpioNum;
135 stream.close();
136 }
137
Patrick Venture0d9377d2018-11-01 19:34:59 -0700138 catch (const std::exception& e)
Kuiying Wanga9d39e32018-08-14 13:47:32 +0800139 {
George Liu9fb15972022-06-20 14:54:38 +0800140 lg2::error("{NUM} error in writing {PATH}: {ERROR}", "NUM", gpioNum,
141 "PATH", devPath, "ERROR", e);
Kuiying Wanga9d39e32018-08-14 13:47:32 +0800142 return -1;
143 }
144 }
145
146 if (gpioDirection == "out")
147 {
Kuiying Wanga9d39e32018-08-14 13:47:32 +0800148 devPath = gpioDev + "/gpio" + std::to_string(gpioNum) + "/value";
149
150 uint32_t currentValue;
151
152 stream.open(devPath, std::fstream::in);
153 try
154 {
155 stream >> currentValue;
156 stream.close();
157 }
158
159 catch (const std::exception& e)
160 {
George Liu9fb15972022-06-20 14:54:38 +0800161 lg2::error("Error in reading {PATH}: {ERROR}", "PATH", devPath,
162 "ERROR", e);
Kuiying Wanga9d39e32018-08-14 13:47:32 +0800163 return -1;
164 }
165
166 const char* direction = currentValue ? "high" : "low";
167
168 devPath.clear();
169 devPath = gpioDev + "/gpio" + std::to_string(gpioNum) + "/direction";
170
171 stream.open(devPath, std::fstream::out);
172 try
173 {
174 stream << direction;
175 stream.close();
176 }
177
178 catch (const std::exception& e)
179 {
George Liu9fb15972022-06-20 14:54:38 +0800180 lg2::error("Error in writing: {ERROR}", "ERROR", e);
Kuiying Wanga9d39e32018-08-14 13:47:32 +0800181 return -1;
182 }
183 }
184 else if (gpioDirection == "in")
185 {
Kuiying Wanga9d39e32018-08-14 13:47:32 +0800186 devPath = gpioDev + "/gpio" + std::to_string(gpioNum) + "/direction";
187
188 stream.open(devPath, std::fstream::out);
189 try
190 {
191 stream << gpioDirection;
192 stream.close();
193 }
194
195 catch (const std::exception& e)
196 {
George Liu9fb15972022-06-20 14:54:38 +0800197 lg2::error("Error in writing: {ERROR}", "ERROR", e);
Kuiying Wanga9d39e32018-08-14 13:47:32 +0800198 return -1;
199 }
200 }
201 else if ((gpioDirection == "both"))
202 {
Tim Lee582b3f02019-01-03 15:39:27 +0800203 devPath = gpioDev + "/gpio" + std::to_string(gpioNum) + "/direction";
204
205 stream.open(devPath, std::fstream::out);
206 try
207 {
George Liu94afa4b2022-06-20 13:36:43 +0800208 // Before set gpio configure as an interrupt pin, need to set
209 // direction as 'in' or edge can't set as 'rising', 'falling' and
210 // 'both'
211 const char* in_direction = "in";
Tim Lee582b3f02019-01-03 15:39:27 +0800212 stream << in_direction;
213 stream.close();
214 }
215
216 catch (const std::exception& e)
217 {
George Liu9fb15972022-06-20 14:54:38 +0800218 lg2::error("Error in writing: {ERROR}", "ERROR", e);
Tim Lee582b3f02019-01-03 15:39:27 +0800219 return -1;
220 }
221 devPath.clear();
Kuiying Wanga9d39e32018-08-14 13:47:32 +0800222
223 // For gpio configured as ‘both’, it is an interrupt pin and trigged on
224 // both rising and falling signals
Kuiying Wanga9d39e32018-08-14 13:47:32 +0800225 devPath = gpioDev + "/gpio" + std::to_string(gpioNum) + "/edge";
226
227 stream.open(devPath, std::fstream::out);
228 try
229 {
230 stream << gpioDirection;
231 stream.close();
232 }
233
234 catch (const std::exception& e)
235 {
George Liu9fb15972022-06-20 14:54:38 +0800236 lg2::error("Error in writing: {ERROR}", "ERROR", e);
Kuiying Wanga9d39e32018-08-14 13:47:32 +0800237 return -1;
238 }
239 }
240
Kuiying Wanga9d39e32018-08-14 13:47:32 +0800241 devPath = gpioDev + "/gpio" + std::to_string(gpioNum) + "/value";
242
Naveen Mosesdd5495c2021-12-03 22:40:46 +0530243 auto fd = ::open(devPath.c_str(), O_RDWR | O_NONBLOCK);
Kuiying Wanga9d39e32018-08-14 13:47:32 +0800244
Naveen Mosesdd5495c2021-12-03 22:40:46 +0530245 if (fd < 0)
Kuiying Wanga9d39e32018-08-14 13:47:32 +0800246 {
George Liu9fb15972022-06-20 14:54:38 +0800247 lg2::error("Open {PATH} error: {ERROR}", "PATH", devPath, "ERROR",
248 errno);
Kuiying Wanga9d39e32018-08-14 13:47:32 +0800249 return -1;
250 }
251
Naveen Mosesdd5495c2021-12-03 22:40:46 +0530252 gpioConfig.fd = fd;
253
Kuiying Wanga9d39e32018-08-14 13:47:32 +0800254 return 0;
255}