blob: 0a16ce502e25c1354fb8abe54cf5421fee5c3f15 [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";
Patrick Williams8eca9bb2022-06-16 17:11:51 -050033namespace fs = std::filesystem;
Naveen Mosesd219fa32022-07-20 00:01:46 +053034std::unordered_map<GpioPolarity, GPIOBufferValue> GpioValueMap = {
35 {GpioPolarity::activeLow, {'0', '1'}},
36 {GpioPolarity::activeHigh, {'1', '0'}}};
Matt Spinler8605bdf2018-11-05 14:55:46 -060037
Naveen Mosesd219fa32022-07-20 00:01:46 +053038void setGpioState(int fd, GpioPolarity polarity, GpioState state)
39{
40 char writeBuffer;
41
42 if (state == GpioState::assert)
43 {
44 writeBuffer = GpioValueMap[polarity].assert;
45 }
46 else
47 {
48 writeBuffer = GpioValueMap[polarity].deassert;
49 }
50
51 auto result = ::write(fd, &writeBuffer, sizeof(writeBuffer));
52 if (result < 0)
53 {
54 lg2::error("GPIO write error {GPIOFD} : {ERRORNO}", "GPIOFD", fd,
55 "ERRORNO", errno);
56 }
57 return;
58}
59GpioState getGpioState(int fd, GpioPolarity polarity)
60{
61 int result = -1;
62 char readBuffer = '0';
63
64 result = ::lseek(fd, 0, SEEK_SET);
65
66 if (result < 0)
67 {
68 lg2::error("GPIO lseek error {GPIOFD}: {ERROR}", "GPIOFD", fd, "ERROR",
69 errno);
70 return GpioState::invalid;
71 }
72
73 result = ::read(fd, &readBuffer, sizeof(readBuffer));
74 if (result < 0)
75 {
76 lg2::error("GPIO read error {GPIOFD}: {ERRORNO}", "GPIOFD", fd,
77 "ERRORNO", errno);
78 throw std::runtime_error("GPIO read failed");
79 }
80 // read the gpio state for the io event received
81 GpioState gpioState = (readBuffer == GpioValueMap[polarity].assert)
82 ? (GpioState::assert)
83 : (GpioState::deassert);
84 return gpioState;
85}
Kuiying Wanga9d39e32018-08-14 13:47:32 +080086void closeGpio(int fd)
87{
88 if (fd > 0)
89 {
90 ::close(fd);
91 }
92}
93
Matt Spinler8f2c95a2018-11-05 15:17:29 -060094uint32_t getGpioBase()
95{
96 // Look for a /sys/class/gpio/gpiochip*/label file
97 // with a value of GPIO_BASE_LABEL_NAME. Then read
98 // the base value from the 'base' file in that directory.
99#ifdef LOOKUP_GPIO_BASE
100 for (auto& f : fs::directory_iterator(gpioDev))
101 {
102 std::string path{f.path()};
103 if (path.find("gpiochip") == std::string::npos)
104 {
105 continue;
106 }
107
108 std::ifstream labelStream{path + "/label"};
109 std::string label;
110 labelStream >> label;
111
112 if (label == GPIO_BASE_LABEL_NAME)
113 {
114 uint32_t base;
115 std::ifstream baseStream{path + "/base"};
116 baseStream >> base;
117 return base;
118 }
119 }
120
George Liu9fb15972022-06-20 14:54:38 +0800121 lg2::error("Could not find GPIO base");
Matt Spinler8f2c95a2018-11-05 15:17:29 -0600122 throw std::runtime_error("Could not find GPIO base!");
123#else
124 return 0;
125#endif
126}
127
128uint32_t getGpioNum(const std::string& gpioPin)
129{
130 // gpioplus promises that they will figure out how to easily
131 // support multiple BMC vendors when the time comes.
132 auto offset = gpioplus::utility::aspeed::nameToOffset(gpioPin);
133
134 return getGpioBase() + offset;
135}
136
Naveen Mosesa1af3292021-12-15 11:47:01 +0530137int configGroupGpio(buttonConfig& buttonIFConfig)
Matt Spinler8605bdf2018-11-05 14:55:46 -0600138{
Naveen Mosesdd5495c2021-12-03 22:40:46 +0530139 int result = 0;
140 // iterate the list of gpios from the button interface config
141 // and initialize them
142 for (auto& gpioCfg : buttonIFConfig.gpios)
Matt Spinler8605bdf2018-11-05 14:55:46 -0600143 {
Naveen Mosesa1af3292021-12-15 11:47:01 +0530144 result = configGpio(gpioCfg);
Naveen Mosesdd5495c2021-12-03 22:40:46 +0530145 if (result < 0)
Matt Spinler8605bdf2018-11-05 14:55:46 -0600146 {
George Liu9fb15972022-06-20 14:54:38 +0800147 lg2::error("{NAME}: Error configuring gpio-{NUM}: {RESULT}", "NAME",
148 buttonIFConfig.formFactorName, "NUM", gpioCfg.number,
149 "RESULT", result);
Naveen Mosesdd5495c2021-12-03 22:40:46 +0530150
151 break;
Matt Spinler8605bdf2018-11-05 14:55:46 -0600152 }
153 }
Naveen Mosesdd5495c2021-12-03 22:40:46 +0530154
155 return result;
Matt Spinler8605bdf2018-11-05 14:55:46 -0600156}
157
Naveen Mosesa1af3292021-12-15 11:47:01 +0530158int configGpio(gpioInfo& gpioConfig)
Matt Spinler8f2c95a2018-11-05 15:17:29 -0600159{
Naveen Mosesdd5495c2021-12-03 22:40:46 +0530160 auto gpioNum = gpioConfig.number;
161 auto gpioDirection = gpioConfig.direction;
Kuiying Wanga9d39e32018-08-14 13:47:32 +0800162
Matt Spinler8f2c95a2018-11-05 15:17:29 -0600163 std::string devPath{gpioDev};
Kuiying Wanga9d39e32018-08-14 13:47:32 +0800164
165 std::fstream stream;
166
167 stream.exceptions(std::ifstream::failbit | std::ifstream::badbit);
168
Matt Spinler8f2c95a2018-11-05 15:17:29 -0600169 devPath += "/gpio" + std::to_string(gpioNum) + "/value";
Kuiying Wanga9d39e32018-08-14 13:47:32 +0800170
Matt Spinler8f2c95a2018-11-05 15:17:29 -0600171 fs::path fullPath(devPath);
Kuiying Wanga9d39e32018-08-14 13:47:32 +0800172
Matt Spinler8f2c95a2018-11-05 15:17:29 -0600173 if (fs::exists(fullPath))
Kuiying Wanga9d39e32018-08-14 13:47:32 +0800174 {
George Liu9fb15972022-06-20 14:54:38 +0800175 lg2::info("GPIO exported: {PATH}", "PATH", devPath);
Kuiying Wanga9d39e32018-08-14 13:47:32 +0800176 }
177 else
178 {
Kuiying Wanga9d39e32018-08-14 13:47:32 +0800179 devPath = gpioDev + "/export";
180
181 stream.open(devPath, std::fstream::out);
182 try
183 {
184 stream << gpioNum;
185 stream.close();
186 }
187
Patrick Venture0d9377d2018-11-01 19:34:59 -0700188 catch (const std::exception& e)
Kuiying Wanga9d39e32018-08-14 13:47:32 +0800189 {
George Liu9fb15972022-06-20 14:54:38 +0800190 lg2::error("{NUM} error in writing {PATH}: {ERROR}", "NUM", gpioNum,
191 "PATH", devPath, "ERROR", e);
Kuiying Wanga9d39e32018-08-14 13:47:32 +0800192 return -1;
193 }
194 }
195
196 if (gpioDirection == "out")
197 {
Kuiying Wanga9d39e32018-08-14 13:47:32 +0800198 devPath = gpioDev + "/gpio" + std::to_string(gpioNum) + "/value";
199
200 uint32_t currentValue;
201
202 stream.open(devPath, std::fstream::in);
203 try
204 {
205 stream >> currentValue;
206 stream.close();
207 }
208
209 catch (const std::exception& e)
210 {
George Liu9fb15972022-06-20 14:54:38 +0800211 lg2::error("Error in reading {PATH}: {ERROR}", "PATH", devPath,
212 "ERROR", e);
Kuiying Wanga9d39e32018-08-14 13:47:32 +0800213 return -1;
214 }
215
216 const char* direction = currentValue ? "high" : "low";
217
218 devPath.clear();
219 devPath = gpioDev + "/gpio" + std::to_string(gpioNum) + "/direction";
220
221 stream.open(devPath, std::fstream::out);
222 try
223 {
224 stream << direction;
225 stream.close();
226 }
227
228 catch (const std::exception& e)
229 {
George Liu9fb15972022-06-20 14:54:38 +0800230 lg2::error("Error in writing: {ERROR}", "ERROR", e);
Kuiying Wanga9d39e32018-08-14 13:47:32 +0800231 return -1;
232 }
233 }
234 else if (gpioDirection == "in")
235 {
Kuiying Wanga9d39e32018-08-14 13:47:32 +0800236 devPath = gpioDev + "/gpio" + std::to_string(gpioNum) + "/direction";
237
238 stream.open(devPath, std::fstream::out);
239 try
240 {
241 stream << gpioDirection;
242 stream.close();
243 }
244
245 catch (const std::exception& e)
246 {
George Liu9fb15972022-06-20 14:54:38 +0800247 lg2::error("Error in writing: {ERROR}", "ERROR", e);
Kuiying Wanga9d39e32018-08-14 13:47:32 +0800248 return -1;
249 }
250 }
251 else if ((gpioDirection == "both"))
252 {
Tim Lee582b3f02019-01-03 15:39:27 +0800253 devPath = gpioDev + "/gpio" + std::to_string(gpioNum) + "/direction";
254
255 stream.open(devPath, std::fstream::out);
256 try
257 {
George Liu94afa4b2022-06-20 13:36:43 +0800258 // Before set gpio configure as an interrupt pin, need to set
259 // direction as 'in' or edge can't set as 'rising', 'falling' and
260 // 'both'
261 const char* in_direction = "in";
Tim Lee582b3f02019-01-03 15:39:27 +0800262 stream << in_direction;
263 stream.close();
264 }
265
266 catch (const std::exception& e)
267 {
George Liu9fb15972022-06-20 14:54:38 +0800268 lg2::error("Error in writing: {ERROR}", "ERROR", e);
Tim Lee582b3f02019-01-03 15:39:27 +0800269 return -1;
270 }
271 devPath.clear();
Kuiying Wanga9d39e32018-08-14 13:47:32 +0800272
273 // For gpio configured as ‘both’, it is an interrupt pin and trigged on
274 // both rising and falling signals
Kuiying Wanga9d39e32018-08-14 13:47:32 +0800275 devPath = gpioDev + "/gpio" + std::to_string(gpioNum) + "/edge";
276
277 stream.open(devPath, std::fstream::out);
278 try
279 {
280 stream << gpioDirection;
281 stream.close();
282 }
283
284 catch (const std::exception& e)
285 {
George Liu9fb15972022-06-20 14:54:38 +0800286 lg2::error("Error in writing: {ERROR}", "ERROR", e);
Kuiying Wanga9d39e32018-08-14 13:47:32 +0800287 return -1;
288 }
289 }
290
Kuiying Wanga9d39e32018-08-14 13:47:32 +0800291 devPath = gpioDev + "/gpio" + std::to_string(gpioNum) + "/value";
292
Naveen Mosesdd5495c2021-12-03 22:40:46 +0530293 auto fd = ::open(devPath.c_str(), O_RDWR | O_NONBLOCK);
Kuiying Wanga9d39e32018-08-14 13:47:32 +0800294
Naveen Mosesdd5495c2021-12-03 22:40:46 +0530295 if (fd < 0)
Kuiying Wanga9d39e32018-08-14 13:47:32 +0800296 {
George Liu9fb15972022-06-20 14:54:38 +0800297 lg2::error("Open {PATH} error: {ERROR}", "PATH", devPath, "ERROR",
298 errno);
Kuiying Wanga9d39e32018-08-14 13:47:32 +0800299 return -1;
300 }
301
Naveen Mosesdd5495c2021-12-03 22:40:46 +0530302 gpioConfig.fd = fd;
303
Kuiying Wanga9d39e32018-08-14 13:47:32 +0800304 return 0;
305}