blob: 33a9ffe970d7e668709260493ffdeb8056f51db5 [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
Matt Spinler8428d442018-08-07 14:28:35 -050017#define GPIO_PORT_OFFSET 8
Matt Spinler3a70e932018-08-07 14:16:47 -050018#define GPIO_BASE_PATH "/sys/class/gpio"
19
20cJSON* gpio_json = NULL;
Norman Jamese7594922015-08-27 14:25:24 -050021
Norman James32e74e22015-09-15 21:28:06 -050022int gpio_writec(GPIO* gpio, char value)
Norman Jamese7594922015-08-27 14:25:24 -050023{
Norman James65a295a2015-09-26 22:21:10 -050024 g_assert (gpio != NULL);
Norman James32e74e22015-09-15 21:28:06 -050025 int rc = GPIO_OK;
Norman Jamese7594922015-08-27 14:25:24 -050026 char buf[1];
27 buf[0] = value;
Yong Li86101ea2017-11-17 14:34:18 +080028
29 if (lseek(gpio->fd, 0, SEEK_SET) == -1)
30 {
31 return GPIO_ERROR;
32 }
33
Norman Jamese7594922015-08-27 14:25:24 -050034 if (write(gpio->fd, buf, 1) != 1)
35 {
Norman James32e74e22015-09-15 21:28:06 -050036 rc = GPIO_WRITE_ERROR;
37 }
38 return rc;
Norman Jamese7594922015-08-27 14:25:24 -050039}
40
Norman James32e74e22015-09-15 21:28:06 -050041int gpio_write(GPIO* gpio, uint8_t value)
Norman Jamese7594922015-08-27 14:25:24 -050042{
Norman James65a295a2015-09-26 22:21:10 -050043 g_assert (gpio != NULL);
Norman James32e74e22015-09-15 21:28:06 -050044 int rc = GPIO_OK;
Norman Jamese7594922015-08-27 14:25:24 -050045 char buf[1];
46 buf[0] = '0';
47 if (value==1)
48 {
49 buf[0]='1';
Patrick Williams3a8fa6e2016-08-24 14:04:22 -050050 }
Yong Li86101ea2017-11-17 14:34:18 +080051
52 if (lseek(gpio->fd, 0, SEEK_SET) == -1)
53 {
54 return GPIO_ERROR;
55 }
56
Norman Jamese7594922015-08-27 14:25:24 -050057 if (write(gpio->fd, buf, 1) != 1)
58 {
Norman James32e74e22015-09-15 21:28:06 -050059 rc = GPIO_WRITE_ERROR;
60 }
61 return rc;
Norman Jamese7594922015-08-27 14:25:24 -050062}
63
Norman James88872672015-09-21 16:51:35 -050064int gpio_read(GPIO* gpio, uint8_t *value)
Norman Jamese7594922015-08-27 14:25:24 -050065{
Norman James65a295a2015-09-26 22:21:10 -050066 g_assert (gpio != NULL);
Norman Jamese7594922015-08-27 14:25:24 -050067 char buf[1];
Norman James32e74e22015-09-15 21:28:06 -050068 int r = GPIO_OK;
Norman James8abb50c2015-09-16 10:58:16 -050069 if (gpio->fd <= 0)
Norman Jamese7594922015-08-27 14:25:24 -050070 {
Patrick Williams3a8fa6e2016-08-24 14:04:22 -050071 r = GPIO_ERROR;
Norman James8abb50c2015-09-16 10:58:16 -050072 }
73 else
74 {
Yong Li86101ea2017-11-17 14:34:18 +080075 if (lseek(gpio->fd, 0, SEEK_SET) == -1)
76 {
77 return GPIO_ERROR;
78 }
79
Norman James8abb50c2015-09-16 10:58:16 -050080 if (read(gpio->fd,&buf,1) != 1)
81 {
Norman James8abb50c2015-09-16 10:58:16 -050082 r = GPIO_READ_ERROR;
Norman James32e74e22015-09-15 21:28:06 -050083 } else {
Norman James8abb50c2015-09-16 10:58:16 -050084 if (buf[0]=='1') {
85 *value = 1;
86 } else {
87 *value = 0;
88 }
Norman James32e74e22015-09-15 21:28:06 -050089 }
Norman Jamese7594922015-08-27 14:25:24 -050090 }
Norman James32e74e22015-09-15 21:28:06 -050091 return r;
Norman Jamese7594922015-08-27 14:25:24 -050092}
Norman James32e74e22015-09-15 21:28:06 -050093int gpio_clock_cycle(GPIO* gpio, int num_clks) {
Norman James65a295a2015-09-26 22:21:10 -050094 g_assert (gpio != NULL);
Norman Jamese7594922015-08-27 14:25:24 -050095 int i=0;
Norman James32e74e22015-09-15 21:28:06 -050096 int r=GPIO_OK;
Norman Jamese7594922015-08-27 14:25:24 -050097 for (i=0;i<num_clks;i++) {
Norman James32e74e22015-09-15 21:28:06 -050098 if (gpio_writec(gpio,'0') == -1) {
99 r = GPIO_WRITE_ERROR;
100 break;
101 }
102 if (gpio_writec(gpio,'1') == -1) {
103 r = GPIO_WRITE_ERROR;
104 break;
105 }
Norman Jamese7594922015-08-27 14:25:24 -0500106 }
Norman James32e74e22015-09-15 21:28:06 -0500107 return r;
Norman Jamese7594922015-08-27 14:25:24 -0500108}
109
Matt Spinlere2cd39d2018-08-07 14:32:58 -0500110/**
111 * Determine the GPIO base number for the system. It is found in
112 * the 'base' file in the /sys/class/gpio/gpiochipX/ directory where the
113 * /sys/class/gpio/gpiochipX/label file has a '1e780000.gpio' in it.
114 *
115 * Note: This method is ASPEED specific. Could add support for
116 * additional SOCs in the future.
117 *
118 * @return int - the GPIO base number, or < 0 if not found
119 */
Matt Spinler8428d442018-08-07 14:28:35 -0500120int get_gpio_base()
Matt Spinler3a70e932018-08-07 14:16:47 -0500121{
Matt Spinlere2cd39d2018-08-07 14:32:58 -0500122 int gpio_base = -1;
123
124 DIR* dir = opendir(GPIO_BASE_PATH);
125 if (dir == NULL)
126 {
127 fprintf(stderr, "Unable to open directory %s\n",
128 GPIO_BASE_PATH);
129 return -1;
130 }
131
132 struct dirent* entry;
133 while ((entry = readdir(dir)) != NULL)
134 {
135 /* Look in the gpiochip<X> directories for a file called 'label' */
136 /* that contains '1e780000.gpio', then in that directory read */
137 /* the GPIO base out of the 'base' file. */
138
139 if (strncmp(entry->d_name, "gpiochip", 8) != 0)
140 {
141 continue;
142 }
143
144 gboolean is_bmc = FALSE;
145 char* label_name;
146 asprintf(&label_name, "%s/%s/label",
147 GPIO_BASE_PATH, entry->d_name);
148
149 FILE* fd = fopen(label_name, "r");
150 free(label_name);
151
152 if (!fd)
153 {
154 continue;
155 }
156
157 char label[14];
158 if (fgets(label, 14, fd) != NULL)
159 {
160 if (strcmp(label, "1e780000.gpio") == 0)
161 {
162 is_bmc = TRUE;
163 }
164 }
165 fclose(fd);
166
167 if (!is_bmc)
168 {
169 continue;
170 }
171
172 char* base_name;
173 asprintf(&base_name, "%s/%s/base",
174 GPIO_BASE_PATH, entry->d_name);
175
176 fd = fopen(base_name, "r");
177 free(base_name);
178
179 if (!fd)
180 {
181 continue;
182 }
183
184 if (fscanf(fd, "%d", &gpio_base) != 1)
185 {
186 gpio_base = -1;
187 }
188 fclose(fd);
189
190 /* We found the right file. No need to continue. */
191 break;
192 }
193 closedir(dir);
194
195 if (gpio_base == -1)
196 {
197 fprintf(stderr, "Could not find GPIO base\n");
198 }
199
200 return gpio_base;
Matt Spinler3a70e932018-08-07 14:16:47 -0500201}
202
203/**
Matt Spinler8428d442018-08-07 14:28:35 -0500204 * Converts the GPIO port/offset nomenclature value
205 * to a number. Supports the ASPEED method where
206 * num = base + (port * 8) + offset, and the port is an
207 * A-Z character that converts to 0 to 25. The base
208 * is obtained form the hardware.
209 *
210 * For example: "A5" -> 5, "Z7" -> 207
211 *
212 * @param[in] gpio - the GPIO name
213 *
214 * @return int - the GPIO number or < 0 if not found
215 */
216int convert_gpio_to_num(const char* gpio)
217{
218 static int gpio_base = -1;
219 if (gpio_base == -1)
220 {
221 gpio_base = get_gpio_base();
222 if (gpio_base < 0)
223 {
224 return gpio_base;
225 }
226 }
227
228 size_t len = strlen(gpio);
229 if (len < 2)
230 {
231 fprintf(stderr, ("Invalid GPIO name %s\n", gpio));
232 return -1;
233 }
234
235 /* Read the offset from the last character */
236 if (!isdigit(gpio[len-1]))
237 {
238 fprintf(stderr, "Invalid GPIO offset in GPIO %s\n", gpio);
239 return -1;
240 }
241
242 int offset = gpio[len-1] - '0';
243
244 /* Read the port from the second to last character */
245 if (!isalpha(gpio[len-2]))
246 {
247 fprintf(stderr, "Invalid GPIO port in GPIO %s\n", gpio);
248 return -1;
249 }
250 int port = toupper(gpio[len-2]) - 'A';
251
252 /* Check for a 2 character port, like AA */
253 if ((len == 3) && isalpha(gpio[len-3]))
254 {
255 port += 26 * (toupper(gpio[len-3]) - 'A' + 1);
256 }
257
258 return gpio_base + (port * GPIO_PORT_OFFSET) + offset;
259}
260
261/**
Matt Spinler3a70e932018-08-07 14:16:47 -0500262 * Returns the cJSON pointer to the GPIO definition
263 * for the GPIO passed in.
264 *
265 * @param[in] gpio_name - the GPIO name, like BMC_POWER_UP
266 *
267 * @return cJSON* - pointer to the cJSON object or NULL
268 * if not found.
269 */
270cJSON* get_gpio_def(const char* gpio_name)
271{
272 if (gpio_json == NULL)
273 {
274 gpio_json = load_json();
275 if (gpio_json == NULL)
276 {
277 return NULL;
278 }
279 }
280
281 cJSON* gpio_defs = cJSON_GetObjectItem(gpio_json, "gpio_definitions");
282 g_assert(gpio_defs != NULL);
283
284 cJSON* def;
285 cJSON_ArrayForEach(def, gpio_defs)
286 {
287 cJSON* name = cJSON_GetObjectItem(def, "name");
288 g_assert(name != NULL);
289
290 if (strcmp(name->valuestring, gpio_name) == 0)
291 {
292 return def;
293 }
294 }
295 return NULL;
296}
297
298/**
299 * Frees the gpio_json memory
300 *
301 * Can be called once when callers are done calling making calls
302 * to gpio_init() so that the JSON only needs to be loaded once.
303 */
304void gpio_inits_done()
305{
306 if (gpio_json != NULL)
307 {
308 cJSON_Delete(gpio_json);
309 gpio_json = NULL;
310 }
311}
312
313/**
314 * Fills in the dev, direction, and num elements in
315 * the GPIO structure.
316 *
317 * @param gpio - the GPIO structure to fill in
318 *
319 * @return GPIO_OK if successful
320 */
321int gpio_get_params(GPIO* gpio)
322{
323 gpio->dev = g_strdup(GPIO_BASE_PATH);
324
325 const cJSON* def = get_gpio_def(gpio->name);
326 if (def == NULL)
327 {
328 fprintf(stderr, "Unable to find GPIO %s in the JSON\n",
329 gpio->name);
330 return GPIO_LOOKUP_ERROR;
331 }
332
333 const cJSON* dir = cJSON_GetObjectItem(def, "direction");
334 g_assert(dir != NULL);
335 gpio->direction = g_strdup(dir->valuestring);
336
337 /* Must use either 'num', like 87, or 'pin', like "A5" */
338 const cJSON* num = cJSON_GetObjectItem(def, "num");
339 if ((num != NULL) && cJSON_IsNumber(num))
340 {
341 gpio->num = num->valueint;
342 }
343 else
344 {
345 const cJSON* pin = cJSON_GetObjectItem(def, "pin");
346 g_assert(pin != NULL);
347
348 gpio->num = convert_gpio_to_num(pin->valuestring);
349 if (gpio->num < 0)
350 {
351 return GPIO_LOOKUP_ERROR;
352 }
353 }
354 return GPIO_OK;
355}
356
Norman Jamese7594922015-08-27 14:25:24 -0500357// Gets the gpio device path from gpio manager object
Matt Spinler0f3fd5a2018-08-08 11:15:26 -0500358int gpio_init(GPIO* gpio)
Norman Jamese7594922015-08-27 14:25:24 -0500359{
Matt Spinler3a70e932018-08-07 14:16:47 -0500360 int rc = gpio_get_params(gpio);
361 if (rc != GPIO_OK)
362 {
363 return rc;
Norman James32e74e22015-09-15 21:28:06 -0500364 }
Norman Jamese7594922015-08-27 14:25:24 -0500365
Norman Jamese7594922015-08-27 14:25:24 -0500366 g_print("GPIO Lookup: %s = %d,%s\n",gpio->name,gpio->num,gpio->direction);
Patrick Williams3a8fa6e2016-08-24 14:04:22 -0500367
Norman Jamese7594922015-08-27 14:25:24 -0500368 //export and set direction
369 char dev[254];
370 char data[4];
Norman James88872672015-09-21 16:51:35 -0500371 int fd;
Norman James32e74e22015-09-15 21:28:06 -0500372 do {
Norman James88872672015-09-21 16:51:35 -0500373 struct stat st;
Patrick Williams3a8fa6e2016-08-24 14:04:22 -0500374
Norman James5a7cc8d2015-10-06 12:31:06 -0500375 sprintf(dev,"%s/gpio%d/value",gpio->dev,gpio->num);
376 //check if gpio is exported, if not export
Norman James88872672015-09-21 16:51:35 -0500377 int result = stat(dev, &st);
378 if (result)
379 {
380 sprintf(dev,"%s/export",gpio->dev);
381 fd = open(dev, O_WRONLY);
382 if (fd == GPIO_ERROR) {
383 rc = GPIO_OPEN_ERROR;
384 break;
Patrick Williams3a8fa6e2016-08-24 14:04:22 -0500385 }
Norman James88872672015-09-21 16:51:35 -0500386 sprintf(data,"%d",gpio->num);
387 rc = write(fd,data,strlen(data));
388 close(fd);
389 if (rc != strlen(data)) {
390 rc = GPIO_WRITE_ERROR;
391 break;
392 }
Norman James32e74e22015-09-15 21:28:06 -0500393 }
Norman James5a7cc8d2015-10-06 12:31:06 -0500394 const char* file = "edge";
Patrick Williamsed1368d2016-08-24 14:23:45 -0500395 const char* direction = gpio->direction;
396 if (strcmp(direction, "in") == 0)
Norman James5a7cc8d2015-10-06 12:31:06 -0500397 {
398 file = "direction";
399 }
Patrick Williamsed1368d2016-08-24 14:23:45 -0500400 else if (strcmp(direction, "out") == 0)
401 {
402 file = "direction";
403
404 // Read current value, so we can set 'high' or 'low'.
405 // Setting direction directly to 'out' is the same as
406 // setting to 'low' which can change the value in the
407 // GPIO.
408 uint8_t value = 0;
409 rc = gpio_open(gpio);
410 if (rc) break;
411 rc = gpio_read(gpio, &value);
412 if (rc) break;
413 gpio_close(gpio);
414
415 direction = (value ? "high" : "low");
416 }
Norman James5a7cc8d2015-10-06 12:31:06 -0500417 sprintf(dev,"%s/gpio%d/%s",gpio->dev,gpio->num,file);
Norman James32e74e22015-09-15 21:28:06 -0500418 fd = open(dev,O_WRONLY);
419 if (fd == GPIO_ERROR) {
420 rc = GPIO_WRITE_ERROR;
421 break;
422 }
Patrick Williamsed1368d2016-08-24 14:23:45 -0500423 rc = write(fd,direction,strlen(direction));
424 if (rc != strlen(direction)) {
Norman James88872672015-09-21 16:51:35 -0500425 rc = GPIO_WRITE_ERROR;
426 break;
427 }
428
Norman James32e74e22015-09-15 21:28:06 -0500429 close(fd);
Norman James5a7cc8d2015-10-06 12:31:06 -0500430 rc = GPIO_OK;
Norman James32e74e22015-09-15 21:28:06 -0500431 } while(0);
Norman Jamese7594922015-08-27 14:25:24 -0500432
Norman James32e74e22015-09-15 21:28:06 -0500433 return rc;
Norman Jamese7594922015-08-27 14:25:24 -0500434}
Norman James5a7cc8d2015-10-06 12:31:06 -0500435
436
437
438
Norman James471ab592015-08-30 22:29:40 -0500439char* get_gpio_dev(GPIO* gpio)
440{
441 char* buf;
Patrick Williamsd6baab92016-08-24 14:05:55 -0500442 asprintf(&buf, "%s/gpio%d/value", gpio->dev, gpio->num);
Norman James471ab592015-08-30 22:29:40 -0500443 return buf;
444}
445
Norman James5a7cc8d2015-10-06 12:31:06 -0500446int gpio_open_interrupt(GPIO* gpio, GIOFunc func, gpointer user_data)
447{
448 int rc = GPIO_OK;
Norman James02b77f32015-10-28 18:59:29 -0500449 char buf[255];
Norman James5a7cc8d2015-10-06 12:31:06 -0500450 sprintf(buf, "%s/gpio%d/value", gpio->dev, gpio->num);
451 gpio->fd = open(buf, O_RDONLY | O_NONBLOCK );
Norman James02b77f32015-10-28 18:59:29 -0500452 gpio->irq_inited = false;
Norman James5a7cc8d2015-10-06 12:31:06 -0500453 if (gpio->fd == -1)
454 {
455 rc = GPIO_OPEN_ERROR;
456 }
457 else
458 {
Norman James02b77f32015-10-28 18:59:29 -0500459 GIOChannel* channel = g_io_channel_unix_new( gpio->fd);
Norman James5a7cc8d2015-10-06 12:31:06 -0500460 guint id = g_io_add_watch( channel, G_IO_PRI, func, user_data );
461 }
462 return rc;
463}
464
Norman Jamese7594922015-08-27 14:25:24 -0500465int gpio_open(GPIO* gpio)
466{
Norman James65a295a2015-09-26 22:21:10 -0500467 g_assert (gpio != NULL);
Norman Jamese7594922015-08-27 14:25:24 -0500468 // open gpio for writing or reading
469 char buf[254];
Norman James32e74e22015-09-15 21:28:06 -0500470 int rc = 0;
Norman James5a7cc8d2015-10-06 12:31:06 -0500471 gpio->fd = -1;
Norman James65a295a2015-09-26 22:21:10 -0500472 if (gpio->direction == NULL) {
473 return GPIO_OPEN_ERROR;
474 }
Norman Jamese7594922015-08-27 14:25:24 -0500475 if (strcmp(gpio->direction,"in")==0)
476 {
477 sprintf(buf, "%s/gpio%d/value", gpio->dev, gpio->num);
478 gpio->fd = open(buf, O_RDONLY);
479 }
480 else
481 {
482 sprintf(buf, "%s/gpio%d/value", gpio->dev, gpio->num);
Norman James25571112015-12-15 09:36:28 -0600483 gpio->fd = open(buf, O_RDWR);
Norman Jamese7594922015-08-27 14:25:24 -0500484
485 }
Norman James88872672015-09-21 16:51:35 -0500486 if (gpio->fd == -1) {
487 return GPIO_OPEN_ERROR;
488 }
489 return GPIO_OK;
Norman Jamese7594922015-08-27 14:25:24 -0500490}
491
492void gpio_close(GPIO* gpio)
493{
494 close(gpio->fd);
495}