blob: 0226ced541497146f64a459532b6df2022940bac [file] [log] [blame]
Patrick Williamsd6baab92016-08-24 14:05:55 -05001#define _GNU_SOURCE
2
Norman Jamese7594922015-08-27 14:25:24 -05003#include <stdint.h>
4#include <stdio.h>
5#include <stdlib.h>
6#include <string.h>
7#include <fcntl.h>
8#include <unistd.h>
9#include <argp.h>
10#include <sys/stat.h>
11#include <sys/mman.h>
Matt Spinlere2cd39d2018-08-07 14:32:58 -050012#include <dirent.h>
Brad Bishopf6c85682016-06-27 11:56:39 -040013#include "openbmc_intf.h"
Norman Jamese7594922015-08-27 14:25:24 -050014#include "gpio.h"
Matt Spinler3a70e932018-08-07 14:16:47 -050015#include "gpio_json.h"
Norman Jamese7594922015-08-27 14:25:24 -050016
Anthony Wilsonc0c74e72019-03-06 10:54:58 -060017#include <sys/ioctl.h>
18#include <linux/gpio.h>
19
Matt Spinler8428d442018-08-07 14:28:35 -050020#define GPIO_PORT_OFFSET 8
Matt Spinler3a70e932018-08-07 14:16:47 -050021#define GPIO_BASE_PATH "/sys/class/gpio"
22
23cJSON* gpio_json = NULL;
Norman Jamese7594922015-08-27 14:25:24 -050024
Norman James32e74e22015-09-15 21:28:06 -050025int gpio_write(GPIO* gpio, uint8_t value)
Norman Jamese7594922015-08-27 14:25:24 -050026{
Norman James65a295a2015-09-26 22:21:10 -050027 g_assert (gpio != NULL);
Anthony Wilsonc0c74e72019-03-06 10:54:58 -060028 struct gpiohandle_data data;
29 memset(&data, 0, sizeof(data));
30 data.values[0] = value;
Yong Li86101ea2017-11-17 14:34:18 +080031
Anthony Wilsonc0c74e72019-03-06 10:54:58 -060032 if (gpio->fd <= 0)
Yong Li86101ea2017-11-17 14:34:18 +080033 {
34 return GPIO_ERROR;
35 }
36
Anthony Wilsonc0c74e72019-03-06 10:54:58 -060037 if (ioctl(gpio->fd, GPIOHANDLE_SET_LINE_VALUES_IOCTL, &data) < 0)
Norman Jamese7594922015-08-27 14:25:24 -050038 {
Anthony Wilsonc0c74e72019-03-06 10:54:58 -060039 return GPIO_WRITE_ERROR;
Norman James32e74e22015-09-15 21:28:06 -050040 }
Anthony Wilsonc0c74e72019-03-06 10:54:58 -060041
42 return GPIO_OK;
Norman Jamese7594922015-08-27 14:25:24 -050043}
44
Norman James88872672015-09-21 16:51:35 -050045int gpio_read(GPIO* gpio, uint8_t *value)
Norman Jamese7594922015-08-27 14:25:24 -050046{
Norman James65a295a2015-09-26 22:21:10 -050047 g_assert (gpio != NULL);
Anthony Wilsonc0c74e72019-03-06 10:54:58 -060048 struct gpiohandle_data data;
49 memset(&data, 0, sizeof(data));
50
Norman James8abb50c2015-09-16 10:58:16 -050051 if (gpio->fd <= 0)
Norman Jamese7594922015-08-27 14:25:24 -050052 {
Anthony Wilsonc0c74e72019-03-06 10:54:58 -060053 return GPIO_ERROR;
Norman James8abb50c2015-09-16 10:58:16 -050054 }
Yong Li86101ea2017-11-17 14:34:18 +080055
Anthony Wilsonc0c74e72019-03-06 10:54:58 -060056 if (ioctl(gpio->fd, GPIOHANDLE_GET_LINE_VALUES_IOCTL, &data) < 0)
57 {
58 return GPIO_READ_ERROR;
Norman Jamese7594922015-08-27 14:25:24 -050059 }
Anthony Wilsonc0c74e72019-03-06 10:54:58 -060060
61 *value = data.values[0];
62
63 return GPIO_OK;
Norman Jamese7594922015-08-27 14:25:24 -050064}
65
Matt Spinlere2cd39d2018-08-07 14:32:58 -050066/**
67 * Determine the GPIO base number for the system. It is found in
68 * the 'base' file in the /sys/class/gpio/gpiochipX/ directory where the
69 * /sys/class/gpio/gpiochipX/label file has a '1e780000.gpio' in it.
70 *
71 * Note: This method is ASPEED specific. Could add support for
72 * additional SOCs in the future.
73 *
74 * @return int - the GPIO base number, or < 0 if not found
75 */
Matt Spinler8428d442018-08-07 14:28:35 -050076int get_gpio_base()
Matt Spinler3a70e932018-08-07 14:16:47 -050077{
Matt Spinlere2cd39d2018-08-07 14:32:58 -050078 int gpio_base = -1;
79
80 DIR* dir = opendir(GPIO_BASE_PATH);
81 if (dir == NULL)
82 {
83 fprintf(stderr, "Unable to open directory %s\n",
84 GPIO_BASE_PATH);
85 return -1;
86 }
87
88 struct dirent* entry;
89 while ((entry = readdir(dir)) != NULL)
90 {
91 /* Look in the gpiochip<X> directories for a file called 'label' */
92 /* that contains '1e780000.gpio', then in that directory read */
93 /* the GPIO base out of the 'base' file. */
94
95 if (strncmp(entry->d_name, "gpiochip", 8) != 0)
96 {
97 continue;
98 }
99
100 gboolean is_bmc = FALSE;
101 char* label_name;
102 asprintf(&label_name, "%s/%s/label",
103 GPIO_BASE_PATH, entry->d_name);
104
105 FILE* fd = fopen(label_name, "r");
106 free(label_name);
107
108 if (!fd)
109 {
110 continue;
111 }
112
113 char label[14];
114 if (fgets(label, 14, fd) != NULL)
115 {
116 if (strcmp(label, "1e780000.gpio") == 0)
117 {
118 is_bmc = TRUE;
119 }
120 }
121 fclose(fd);
122
123 if (!is_bmc)
124 {
125 continue;
126 }
127
128 char* base_name;
129 asprintf(&base_name, "%s/%s/base",
130 GPIO_BASE_PATH, entry->d_name);
131
132 fd = fopen(base_name, "r");
133 free(base_name);
134
135 if (!fd)
136 {
137 continue;
138 }
139
140 if (fscanf(fd, "%d", &gpio_base) != 1)
141 {
142 gpio_base = -1;
143 }
144 fclose(fd);
145
146 /* We found the right file. No need to continue. */
147 break;
148 }
149 closedir(dir);
150
151 if (gpio_base == -1)
152 {
153 fprintf(stderr, "Could not find GPIO base\n");
154 }
155
156 return gpio_base;
Matt Spinler3a70e932018-08-07 14:16:47 -0500157}
158
159/**
Matt Spinler8428d442018-08-07 14:28:35 -0500160 * Converts the GPIO port/offset nomenclature value
161 * to a number. Supports the ASPEED method where
162 * num = base + (port * 8) + offset, and the port is an
163 * A-Z character that converts to 0 to 25. The base
164 * is obtained form the hardware.
165 *
166 * For example: "A5" -> 5, "Z7" -> 207
167 *
168 * @param[in] gpio - the GPIO name
169 *
170 * @return int - the GPIO number or < 0 if not found
171 */
172int convert_gpio_to_num(const char* gpio)
173{
Matt Spinler8428d442018-08-07 14:28:35 -0500174 size_t len = strlen(gpio);
175 if (len < 2)
176 {
Ed Tanous36171642021-01-08 15:40:19 -0800177 fprintf(stderr, "Invalid GPIO name %s\n", gpio);
Matt Spinler8428d442018-08-07 14:28:35 -0500178 return -1;
179 }
180
181 /* Read the offset from the last character */
182 if (!isdigit(gpio[len-1]))
183 {
184 fprintf(stderr, "Invalid GPIO offset in GPIO %s\n", gpio);
185 return -1;
186 }
187
188 int offset = gpio[len-1] - '0';
189
190 /* Read the port from the second to last character */
191 if (!isalpha(gpio[len-2]))
192 {
193 fprintf(stderr, "Invalid GPIO port in GPIO %s\n", gpio);
194 return -1;
195 }
196 int port = toupper(gpio[len-2]) - 'A';
197
198 /* Check for a 2 character port, like AA */
199 if ((len == 3) && isalpha(gpio[len-3]))
200 {
201 port += 26 * (toupper(gpio[len-3]) - 'A' + 1);
202 }
203
Anthony Wilsonc0c74e72019-03-06 10:54:58 -0600204 return (port * GPIO_PORT_OFFSET) + offset;
Matt Spinler8428d442018-08-07 14:28:35 -0500205}
206
207/**
Matt Spinler3a70e932018-08-07 14:16:47 -0500208 * Returns the cJSON pointer to the GPIO definition
209 * for the GPIO passed in.
210 *
211 * @param[in] gpio_name - the GPIO name, like BMC_POWER_UP
212 *
213 * @return cJSON* - pointer to the cJSON object or NULL
214 * if not found.
215 */
216cJSON* get_gpio_def(const char* gpio_name)
217{
218 if (gpio_json == NULL)
219 {
220 gpio_json = load_json();
221 if (gpio_json == NULL)
222 {
223 return NULL;
224 }
225 }
226
227 cJSON* gpio_defs = cJSON_GetObjectItem(gpio_json, "gpio_definitions");
228 g_assert(gpio_defs != NULL);
229
230 cJSON* def;
231 cJSON_ArrayForEach(def, gpio_defs)
232 {
233 cJSON* name = cJSON_GetObjectItem(def, "name");
234 g_assert(name != NULL);
235
236 if (strcmp(name->valuestring, gpio_name) == 0)
237 {
238 return def;
239 }
240 }
241 return NULL;
242}
243
244/**
245 * Frees the gpio_json memory
246 *
247 * Can be called once when callers are done calling making calls
248 * to gpio_init() so that the JSON only needs to be loaded once.
249 */
250void gpio_inits_done()
251{
252 if (gpio_json != NULL)
253 {
254 cJSON_Delete(gpio_json);
255 gpio_json = NULL;
256 }
257}
258
259/**
260 * Fills in the dev, direction, and num elements in
261 * the GPIO structure.
262 *
263 * @param gpio - the GPIO structure to fill in
264 *
265 * @return GPIO_OK if successful
266 */
267int gpio_get_params(GPIO* gpio)
268{
269 gpio->dev = g_strdup(GPIO_BASE_PATH);
270
271 const cJSON* def = get_gpio_def(gpio->name);
272 if (def == NULL)
273 {
274 fprintf(stderr, "Unable to find GPIO %s in the JSON\n",
275 gpio->name);
276 return GPIO_LOOKUP_ERROR;
277 }
278
279 const cJSON* dir = cJSON_GetObjectItem(def, "direction");
280 g_assert(dir != NULL);
281 gpio->direction = g_strdup(dir->valuestring);
282
283 /* Must use either 'num', like 87, or 'pin', like "A5" */
284 const cJSON* num = cJSON_GetObjectItem(def, "num");
285 if ((num != NULL) && cJSON_IsNumber(num))
286 {
287 gpio->num = num->valueint;
288 }
289 else
290 {
291 const cJSON* pin = cJSON_GetObjectItem(def, "pin");
292 g_assert(pin != NULL);
293
294 gpio->num = convert_gpio_to_num(pin->valuestring);
295 if (gpio->num < 0)
296 {
297 return GPIO_LOOKUP_ERROR;
298 }
299 }
Anthony Wilsonc0c74e72019-03-06 10:54:58 -0600300 // TODO: For the purposes of skeleton and the projects that use it,
301 // it should be safe to assume this will always be 0. Eventually skeleton
302 // should be going away, but if the need arises before then this may need
303 // to be updated to handle non-zero cases.
304 gpio->chip_id = 0;
Matt Spinler3a70e932018-08-07 14:16:47 -0500305 return GPIO_OK;
306}
307
Anthony Wilsonc0c74e72019-03-06 10:54:58 -0600308int gpio_open(GPIO* gpio, uint8_t default_value)
Norman Jamese7594922015-08-27 14:25:24 -0500309{
Norman James65a295a2015-09-26 22:21:10 -0500310 g_assert (gpio != NULL);
Norman Jamese7594922015-08-27 14:25:24 -0500311
Anthony Wilsonc0c74e72019-03-06 10:54:58 -0600312 char buf[255];
313 sprintf(buf, "/dev/gpiochip%d", gpio->chip_id);
314 gpio->fd = open(buf, 0);
315 if (gpio->fd == -1)
316 {
Norman James88872672015-09-21 16:51:35 -0500317 return GPIO_OPEN_ERROR;
318 }
Anthony Wilsonc0c74e72019-03-06 10:54:58 -0600319
320 struct gpiohandle_request req;
321 memset(&req, 0, sizeof(req));
322 strncpy(req.consumer_label, "skeleton-gpio", sizeof(req.consumer_label));
323
324 // open gpio for writing or reading
325 if (gpio->direction == NULL)
326 {
327 gpio_close(gpio);
328 return GPIO_OPEN_ERROR;
329 }
330 req.flags = (strcmp(gpio->direction,"in") == 0) ? GPIOHANDLE_REQUEST_INPUT
331 : GPIOHANDLE_REQUEST_OUTPUT;
332
333 req.lineoffsets[0] = gpio->num;
334 req.lines = 1;
335
336 if (strcmp(gpio->direction,"out") == 0)
337 {
338 req.default_values[0] = default_value;
339 }
340
341 int rc = ioctl(gpio->fd, GPIO_GET_LINEHANDLE_IOCTL, &req);
342 if (rc < 0)
343 {
344 gpio_close(gpio);
345 return GPIO_OPEN_ERROR;
346 }
347 gpio_close(gpio);
348 gpio->fd = req.fd;
349
Norman James88872672015-09-21 16:51:35 -0500350 return GPIO_OK;
Norman Jamese7594922015-08-27 14:25:24 -0500351}
352
353void gpio_close(GPIO* gpio)
354{
Anthony Wilsonc0c74e72019-03-06 10:54:58 -0600355 if(gpio->fd < 0)
356 return;
357
Norman Jamese7594922015-08-27 14:25:24 -0500358 close(gpio->fd);
Anthony Wilsonc0c74e72019-03-06 10:54:58 -0600359 gpio->fd = -1;
Norman Jamese7594922015-08-27 14:25:24 -0500360}