1 /**
2  * Copyright © 2017 IBM 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 #include <cassert>
17 #include <fcntl.h>
18 #include <phosphor-logging/elog.hpp>
19 #include <phosphor-logging/elog-errors.hpp>
20 #include <phosphor-logging/log.hpp>
21 #include <sys/ioctl.h>
22 #include <xyz/openbmc_project/Common/error.hpp>
23 #include "gpio.hpp"
24 
25 namespace witherspoon
26 {
27 namespace gpio
28 {
29 
30 using namespace phosphor::logging;
31 
32 using InternalFailure = sdbusplus::xyz::openbmc_project::Common::
33                         Error::InternalFailure;
34 
35 Value GPIO::read()
36 {
37     assert(direction == Direction::input);
38 
39     requestLine();
40 
41     gpiohandle_data data{};
42 
43     auto rc = ioctl(lineFD(),
44                     GPIOHANDLE_GET_LINE_VALUES_IOCTL,
45                     &data);
46 
47     if (rc < 0)
48     {
49         auto e = errno;
50         log<level::ERR>("Failed GET_LINE_VALUES ioctl",
51                         entry("ERRNO=%d", e));
52         elog<InternalFailure>();
53     }
54 
55     return (data.values[0] == 0) ? Value::low : Value::high;
56 }
57 
58 void GPIO::set(Value value)
59 {
60     assert(direction == Direction::output);
61 
62     requestLine(value);
63 
64     gpiohandle_data data{};
65     data.values[0] = static_cast<gpioValue_t>(value);
66 
67     auto rc = ioctl(lineFD(), GPIOHANDLE_SET_LINE_VALUES_IOCTL, &data);
68     if (rc == -1)
69     {
70         auto e = errno;
71         log<level::ERR>("Failed SET_LINE_VALUES ioctl",
72                         entry("ERRNO=%d", e));
73         elog<InternalFailure>();
74     }
75 }
76 
77 void GPIO::requestLine(Value defaultValue)
78 {
79     //Only need to do this once
80     if (lineFD)
81     {
82         return;
83     }
84 
85     power::util::FileDescriptor fd{open(device.c_str(), 0)};
86     if (fd() == -1)
87     {
88         auto e = errno;
89         log<level::ERR>("Failed opening GPIO device",
90                         entry("DEVICE=%s", device.c_str()),
91                         entry("ERRNO=%d", e));
92         elog<InternalFailure>();
93     }
94 
95     //Make an ioctl call to request the GPIO line, which will
96     //return the descriptor to use to access it.
97     gpiohandle_request request{};
98     strncpy(request.consumer_label,
99             "witherspoon-pfault-analysis",
100             sizeof(request.consumer_label));
101 
102     request.flags = (direction == Direction::input) ?
103                     GPIOHANDLE_REQUEST_INPUT : GPIOHANDLE_REQUEST_OUTPUT;
104 
105     request.lineoffsets[0] = gpio;
106     request.lines = 1;
107 
108     if (direction == Direction::output)
109     {
110         request.default_values[0] = static_cast<gpioValue_t>(defaultValue);
111     }
112 
113     auto rc = ioctl(fd(), GPIO_GET_LINEHANDLE_IOCTL, &request);
114     if (rc == -1)
115     {
116         auto e = errno;
117         log<level::ERR>("Failed GET_LINEHANDLE ioctl",
118                         entry("GPIO=%d", gpio),
119                         entry("ERRNO=%d", e));
120         elog<InternalFailure>();
121     }
122 
123     lineFD.set(request.fd);
124 }
125 
126 }
127 }
128