xref: /openbmc/skeleton/libopenbmc_intf/gpio.c (revision c6fa1ddd)
1 #define _GNU_SOURCE
2 
3 #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>
12 #include <dirent.h>
13 #include "openbmc_intf.h"
14 #include "gpio.h"
15 #include "gpio_json.h"
16 
17 #define GPIO_PORT_OFFSET 8
18 #define GPIO_BASE_PATH "/sys/class/gpio"
19 
20 cJSON* gpio_json = NULL;
21 
22 int gpio_writec(GPIO* gpio, char value)
23 {
24 	g_assert (gpio != NULL);
25 	int rc = GPIO_OK;
26 	char buf[1];
27 	buf[0] = value;
28 
29 	if (lseek(gpio->fd, 0, SEEK_SET) == -1)
30 	{
31 		return GPIO_ERROR;
32 	}
33 
34 	if (write(gpio->fd, buf, 1) != 1)
35 	{
36 		rc = GPIO_WRITE_ERROR;
37 	}
38 	return rc;
39 }
40 
41 int gpio_write(GPIO* gpio, uint8_t value)
42 {
43 	g_assert (gpio != NULL);
44 	int rc = GPIO_OK;
45 	char buf[1];
46 	buf[0] = '0';
47 	if (value==1)
48 	{
49 		buf[0]='1';
50 	}
51 
52 	if (lseek(gpio->fd, 0, SEEK_SET) == -1)
53 	{
54 		return GPIO_ERROR;
55 	}
56 
57 	if (write(gpio->fd, buf, 1) != 1)
58 	{
59 		rc = GPIO_WRITE_ERROR;
60 	}
61 	return rc;
62 }
63 
64 int gpio_read(GPIO* gpio, uint8_t *value)
65 {
66 	g_assert (gpio != NULL);
67 	char buf[1];
68 	int r = GPIO_OK;
69 	if (gpio->fd <= 0)
70 	{
71 		r = GPIO_ERROR;
72 	}
73 	else
74 	{
75 		if (lseek(gpio->fd, 0, SEEK_SET) == -1)
76 		{
77 			return GPIO_ERROR;
78 		}
79 
80 		if (read(gpio->fd,&buf,1) != 1)
81 		{
82 			r = GPIO_READ_ERROR;
83 		} else {
84 			if (buf[0]=='1') {
85 				*value = 1;
86 			} else {
87 				*value = 0;
88 			}
89 		}
90 	}
91 	return r;
92 }
93 int gpio_clock_cycle(GPIO* gpio, int num_clks) {
94 	g_assert (gpio != NULL);
95         int i=0;
96 	int r=GPIO_OK;
97         for (i=0;i<num_clks;i++) {
98                 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 		}
106         }
107 	return r;
108 }
109 
110 /**
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  */
120 int get_gpio_base()
121 {
122 	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;
201 }
202 
203 /**
204  * 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  */
216 int 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 /**
262  * 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  */
270 cJSON* 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  */
304 void 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  */
321 int 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 
357 // Gets the gpio device path from gpio manager object
358 int gpio_init(GPIO* gpio)
359 {
360 	int rc = gpio_get_params(gpio);
361 	if (rc != GPIO_OK)
362 	{
363 	    return rc;
364 	}
365 
366 	g_print("GPIO Lookup:  %s = %d,%s\n",gpio->name,gpio->num,gpio->direction);
367 
368 	//export and set direction
369 	char dev[254];
370 	char data[4];
371 	int fd;
372 	do {
373 		struct stat st;
374 
375 		sprintf(dev,"%s/gpio%d/value",gpio->dev,gpio->num);
376 		//check if gpio is exported, if not export
377     		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;
385 			}
386 			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 			}
393 		}
394 		const char* file = "edge";
395 		const char* direction = gpio->direction;
396 		if (strcmp(direction, "in") == 0)
397 		{
398 			file = "direction";
399 		}
400 		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 		}
417 		sprintf(dev,"%s/gpio%d/%s",gpio->dev,gpio->num,file);
418 		fd = open(dev,O_WRONLY);
419 		if (fd == GPIO_ERROR) {
420 			rc = GPIO_WRITE_ERROR;
421 			break;
422 		}
423 		rc = write(fd,direction,strlen(direction));
424 		if (rc != strlen(direction)) {
425 			rc = GPIO_WRITE_ERROR;
426 			break;
427 		}
428 
429 		close(fd);
430 		rc = GPIO_OK;
431 	} while(0);
432 
433 	return rc;
434 }
435 
436 
437 
438 
439 char* get_gpio_dev(GPIO* gpio)
440 {
441 	char* buf;
442 	asprintf(&buf, "%s/gpio%d/value", gpio->dev, gpio->num);
443 	return buf;
444 }
445 
446 int gpio_open_interrupt(GPIO* gpio, GIOFunc func, gpointer user_data)
447 {
448 	int rc = GPIO_OK;
449 	char buf[255];
450 	sprintf(buf, "%s/gpio%d/value", gpio->dev, gpio->num);
451 	gpio->fd = open(buf, O_RDONLY | O_NONBLOCK );
452 	gpio->irq_inited = false;
453 	if (gpio->fd == -1)
454 	{
455 		rc = GPIO_OPEN_ERROR;
456 	}
457 	else
458 	{
459 		GIOChannel* channel = g_io_channel_unix_new( gpio->fd);
460 		guint id = g_io_add_watch( channel, G_IO_PRI, func, user_data );
461 	}
462 	return rc;
463 }
464 
465 int gpio_open(GPIO* gpio)
466 {
467 	g_assert (gpio != NULL);
468 	// open gpio for writing or reading
469 	char buf[254];
470 	int rc = 0;
471 	gpio->fd = -1;
472 	if (gpio->direction == NULL) {
473 		return GPIO_OPEN_ERROR;
474 	}
475 	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);
483 		gpio->fd = open(buf, O_RDWR);
484 
485 	}
486 	if (gpio->fd == -1) {
487 		return GPIO_OPEN_ERROR;
488 	}
489 	return GPIO_OK;
490 }
491 
492 void gpio_close(GPIO* gpio)
493 {
494 	close(gpio->fd);
495 }
496