blob: 1785e792c2ac1a4450488add8cfdb4e7d35ffbb9 [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 {
Patrick Williams0d259e32024-10-04 08:35:52 -040091
Matt Spinlere2cd39d2018-08-07 14:32:58 -050092 /* Look in the gpiochip<X> directories for a file called 'label' */
93 /* that contains '1e780000.gpio', then in that directory read */
94 /* the GPIO base out of the 'base' file. */
95
96 if (strncmp(entry->d_name, "gpiochip", 8) != 0)
97 {
98 continue;
99 }
100
101 gboolean is_bmc = FALSE;
102 char* label_name;
Patrick Williams0d259e32024-10-04 08:35:52 -0400103 int rc = asprintf(&label_name, "%s/%s/label",
Matt Spinlere2cd39d2018-08-07 14:32:58 -0500104 GPIO_BASE_PATH, entry->d_name);
105
Patrick Williams0d259e32024-10-04 08:35:52 -0400106 if (!rc)
107 {
108 continue;
109 }
110
Matt Spinlere2cd39d2018-08-07 14:32:58 -0500111 FILE* fd = fopen(label_name, "r");
112 free(label_name);
113
114 if (!fd)
115 {
116 continue;
117 }
118
119 char label[14];
120 if (fgets(label, 14, fd) != NULL)
121 {
122 if (strcmp(label, "1e780000.gpio") == 0)
123 {
124 is_bmc = TRUE;
125 }
126 }
127 fclose(fd);
128
129 if (!is_bmc)
130 {
131 continue;
132 }
133
134 char* base_name;
Patrick Williams0d259e32024-10-04 08:35:52 -0400135 rc = asprintf(&base_name, "%s/%s/base",
Matt Spinlere2cd39d2018-08-07 14:32:58 -0500136 GPIO_BASE_PATH, entry->d_name);
137
Patrick Williams0d259e32024-10-04 08:35:52 -0400138 if (!rc)
139 {
140 continue;
141 }
142
Matt Spinlere2cd39d2018-08-07 14:32:58 -0500143 fd = fopen(base_name, "r");
144 free(base_name);
145
146 if (!fd)
147 {
148 continue;
149 }
150
151 if (fscanf(fd, "%d", &gpio_base) != 1)
152 {
153 gpio_base = -1;
154 }
155 fclose(fd);
156
157 /* We found the right file. No need to continue. */
158 break;
159 }
160 closedir(dir);
161
162 if (gpio_base == -1)
163 {
164 fprintf(stderr, "Could not find GPIO base\n");
165 }
166
167 return gpio_base;
Matt Spinler3a70e932018-08-07 14:16:47 -0500168}
169
170/**
Matt Spinler8428d442018-08-07 14:28:35 -0500171 * Converts the GPIO port/offset nomenclature value
172 * to a number. Supports the ASPEED method where
173 * num = base + (port * 8) + offset, and the port is an
174 * A-Z character that converts to 0 to 25. The base
175 * is obtained form the hardware.
176 *
177 * For example: "A5" -> 5, "Z7" -> 207
178 *
179 * @param[in] gpio - the GPIO name
180 *
181 * @return int - the GPIO number or < 0 if not found
182 */
183int convert_gpio_to_num(const char* gpio)
184{
Matt Spinler8428d442018-08-07 14:28:35 -0500185 size_t len = strlen(gpio);
186 if (len < 2)
187 {
Ed Tanous36171642021-01-08 15:40:19 -0800188 fprintf(stderr, "Invalid GPIO name %s\n", gpio);
Matt Spinler8428d442018-08-07 14:28:35 -0500189 return -1;
190 }
191
192 /* Read the offset from the last character */
193 if (!isdigit(gpio[len-1]))
194 {
195 fprintf(stderr, "Invalid GPIO offset in GPIO %s\n", gpio);
196 return -1;
197 }
198
199 int offset = gpio[len-1] - '0';
200
201 /* Read the port from the second to last character */
202 if (!isalpha(gpio[len-2]))
203 {
204 fprintf(stderr, "Invalid GPIO port in GPIO %s\n", gpio);
205 return -1;
206 }
207 int port = toupper(gpio[len-2]) - 'A';
208
209 /* Check for a 2 character port, like AA */
210 if ((len == 3) && isalpha(gpio[len-3]))
211 {
212 port += 26 * (toupper(gpio[len-3]) - 'A' + 1);
213 }
214
Anthony Wilsonc0c74e72019-03-06 10:54:58 -0600215 return (port * GPIO_PORT_OFFSET) + offset;
Matt Spinler8428d442018-08-07 14:28:35 -0500216}
217
218/**
Matt Spinler3a70e932018-08-07 14:16:47 -0500219 * Returns the cJSON pointer to the GPIO definition
220 * for the GPIO passed in.
221 *
222 * @param[in] gpio_name - the GPIO name, like BMC_POWER_UP
223 *
224 * @return cJSON* - pointer to the cJSON object or NULL
225 * if not found.
226 */
227cJSON* get_gpio_def(const char* gpio_name)
228{
229 if (gpio_json == NULL)
230 {
231 gpio_json = load_json();
232 if (gpio_json == NULL)
233 {
234 return NULL;
235 }
236 }
237
238 cJSON* gpio_defs = cJSON_GetObjectItem(gpio_json, "gpio_definitions");
239 g_assert(gpio_defs != NULL);
240
241 cJSON* def;
242 cJSON_ArrayForEach(def, gpio_defs)
243 {
244 cJSON* name = cJSON_GetObjectItem(def, "name");
245 g_assert(name != NULL);
246
247 if (strcmp(name->valuestring, gpio_name) == 0)
248 {
249 return def;
250 }
251 }
252 return NULL;
253}
254
255/**
256 * Frees the gpio_json memory
257 *
258 * Can be called once when callers are done calling making calls
259 * to gpio_init() so that the JSON only needs to be loaded once.
260 */
261void gpio_inits_done()
262{
263 if (gpio_json != NULL)
264 {
265 cJSON_Delete(gpio_json);
266 gpio_json = NULL;
267 }
268}
269
270/**
271 * Fills in the dev, direction, and num elements in
272 * the GPIO structure.
273 *
274 * @param gpio - the GPIO structure to fill in
275 *
276 * @return GPIO_OK if successful
277 */
278int gpio_get_params(GPIO* gpio)
279{
280 gpio->dev = g_strdup(GPIO_BASE_PATH);
281
282 const cJSON* def = get_gpio_def(gpio->name);
283 if (def == NULL)
284 {
285 fprintf(stderr, "Unable to find GPIO %s in the JSON\n",
286 gpio->name);
287 return GPIO_LOOKUP_ERROR;
288 }
289
290 const cJSON* dir = cJSON_GetObjectItem(def, "direction");
291 g_assert(dir != NULL);
292 gpio->direction = g_strdup(dir->valuestring);
293
294 /* Must use either 'num', like 87, or 'pin', like "A5" */
295 const cJSON* num = cJSON_GetObjectItem(def, "num");
296 if ((num != NULL) && cJSON_IsNumber(num))
297 {
298 gpio->num = num->valueint;
299 }
300 else
301 {
302 const cJSON* pin = cJSON_GetObjectItem(def, "pin");
303 g_assert(pin != NULL);
304
Patrick Williams0d259e32024-10-04 08:35:52 -0400305 int pin_id = convert_gpio_to_num(pin->valuestring);
306 if (pin_id < 0)
Matt Spinler3a70e932018-08-07 14:16:47 -0500307 {
308 return GPIO_LOOKUP_ERROR;
309 }
Patrick Williams0d259e32024-10-04 08:35:52 -0400310 gpio->num = (size_t) pin_id;
Matt Spinler3a70e932018-08-07 14:16:47 -0500311 }
Anthony Wilsonc0c74e72019-03-06 10:54:58 -0600312 // TODO: For the purposes of skeleton and the projects that use it,
313 // it should be safe to assume this will always be 0. Eventually skeleton
314 // should be going away, but if the need arises before then this may need
315 // to be updated to handle non-zero cases.
316 gpio->chip_id = 0;
Matt Spinler3a70e932018-08-07 14:16:47 -0500317 return GPIO_OK;
318}
319
Anthony Wilsonc0c74e72019-03-06 10:54:58 -0600320int gpio_open(GPIO* gpio, uint8_t default_value)
Norman Jamese7594922015-08-27 14:25:24 -0500321{
Norman James65a295a2015-09-26 22:21:10 -0500322 g_assert (gpio != NULL);
Norman Jamese7594922015-08-27 14:25:24 -0500323
Anthony Wilsonc0c74e72019-03-06 10:54:58 -0600324 char buf[255];
325 sprintf(buf, "/dev/gpiochip%d", gpio->chip_id);
326 gpio->fd = open(buf, 0);
327 if (gpio->fd == -1)
328 {
Norman James88872672015-09-21 16:51:35 -0500329 return GPIO_OPEN_ERROR;
330 }
Anthony Wilsonc0c74e72019-03-06 10:54:58 -0600331
332 struct gpiohandle_request req;
333 memset(&req, 0, sizeof(req));
334 strncpy(req.consumer_label, "skeleton-gpio", sizeof(req.consumer_label));
335
336 // open gpio for writing or reading
337 if (gpio->direction == NULL)
338 {
339 gpio_close(gpio);
340 return GPIO_OPEN_ERROR;
341 }
342 req.flags = (strcmp(gpio->direction,"in") == 0) ? GPIOHANDLE_REQUEST_INPUT
343 : GPIOHANDLE_REQUEST_OUTPUT;
344
345 req.lineoffsets[0] = gpio->num;
346 req.lines = 1;
347
348 if (strcmp(gpio->direction,"out") == 0)
349 {
350 req.default_values[0] = default_value;
351 }
352
353 int rc = ioctl(gpio->fd, GPIO_GET_LINEHANDLE_IOCTL, &req);
354 if (rc < 0)
355 {
356 gpio_close(gpio);
357 return GPIO_OPEN_ERROR;
358 }
359 gpio_close(gpio);
360 gpio->fd = req.fd;
361
Norman James88872672015-09-21 16:51:35 -0500362 return GPIO_OK;
Norman Jamese7594922015-08-27 14:25:24 -0500363}
364
365void gpio_close(GPIO* gpio)
366{
Anthony Wilsonc0c74e72019-03-06 10:54:58 -0600367 if(gpio->fd < 0)
368 return;
369
Norman Jamese7594922015-08-27 14:25:24 -0500370 close(gpio->fd);
Anthony Wilsonc0c74e72019-03-06 10:54:58 -0600371 gpio->fd = -1;
Norman Jamese7594922015-08-27 14:25:24 -0500372}