xref: /openbmc/skeleton/libopenbmc_intf/gpio.c (revision fd84bfa3)
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 #include <sys/ioctl.h>
18 #include <linux/gpio.h>
19 
20 #define GPIO_PORT_OFFSET 8
21 #define GPIO_BASE_PATH "/sys/class/gpio"
22 #define DEV_NAME "/dev/gpiochip0"
23 
24 cJSON* gpio_json = NULL;
25 
gpio_write(GPIO * gpio,uint8_t value)26 int gpio_write(GPIO* gpio, uint8_t value)
27 {
28 	g_assert (gpio != NULL);
29 	struct gpiohandle_data data;
30 	memset(&data, 0, sizeof(data));
31 	data.values[0] = value;
32 
33 	if (gpio->fd <= 0)
34 	{
35 		return GPIO_ERROR;
36 	}
37 
38 	if (ioctl(gpio->fd, GPIOHANDLE_SET_LINE_VALUES_IOCTL, &data) < 0)
39 	{
40 		return GPIO_WRITE_ERROR;
41 	}
42 
43 	return GPIO_OK;
44 }
45 
gpio_read(GPIO * gpio,uint8_t * value)46 int gpio_read(GPIO* gpio, uint8_t *value)
47 {
48 	g_assert (gpio != NULL);
49 	struct gpiohandle_data data;
50 	memset(&data, 0, sizeof(data));
51 
52 	if (gpio->fd <= 0)
53 	{
54 		return GPIO_ERROR;
55 	}
56 
57 	if (ioctl(gpio->fd, GPIOHANDLE_GET_LINE_VALUES_IOCTL, &data) < 0)
58 	{
59 		return GPIO_READ_ERROR;
60 	}
61 
62 	*value = data.values[0];
63 
64 	return GPIO_OK;
65 }
66 
67 /**
68  * Determine the GPIO base number for the system.  It is found in
69  * the 'base' file in the /sys/class/gpio/gpiochipX/ directory where the
70  * /sys/class/gpio/gpiochipX/label file has a '1e780000.gpio' in it.
71  *
72  * Note: This method is ASPEED specific.  Could add support for
73  * additional SOCs in the future.
74  *
75  * @return int - the GPIO base number, or < 0 if not found
76  */
get_gpio_base()77 int get_gpio_base()
78 {
79 	int gpio_base = -1;
80 
81 	DIR* dir = opendir(GPIO_BASE_PATH);
82 	if (dir == NULL)
83 	{
84 		fprintf(stderr, "Unable to open directory %s\n",
85 				GPIO_BASE_PATH);
86 		return -1;
87 	}
88 
89 	struct dirent* entry;
90 	while ((entry = readdir(dir)) != NULL)
91 	{
92 
93 		/* Look in the gpiochip<X> directories for a file called 'label' */
94 		/* that contains '1e780000.gpio', then in that directory read */
95 		/* the GPIO base out of the 'base' file. */
96 
97 		if (strncmp(entry->d_name, "gpiochip", 8) != 0)
98 		{
99 			continue;
100 		}
101 
102 		gboolean is_bmc = FALSE;
103 		char* label_name;
104 		int rc = asprintf(&label_name, "%s/%s/label",
105 				GPIO_BASE_PATH, entry->d_name);
106 
107 		if (!rc)
108 		{
109 			continue;
110 		}
111 
112 		FILE* fd = fopen(label_name, "r");
113 		free(label_name);
114 
115 		if (!fd)
116 		{
117 			continue;
118 		}
119 
120 		char label[14];
121 		if (fgets(label, 14, fd) != NULL)
122 		{
123 			if (strcmp(label, "1e780000.gpio") == 0)
124 			{
125 				is_bmc = TRUE;
126 			}
127 		}
128 		fclose(fd);
129 
130 		if (!is_bmc)
131 		{
132 			continue;
133 		}
134 
135 		char* base_name;
136 		rc = asprintf(&base_name, "%s/%s/base",
137 				GPIO_BASE_PATH, entry->d_name);
138 
139 		if (!rc)
140 		{
141 			continue;
142 		}
143 
144 		fd = fopen(base_name, "r");
145 		free(base_name);
146 
147 		if (!fd)
148 		{
149 			continue;
150 		}
151 
152 		if (fscanf(fd, "%d", &gpio_base) != 1)
153 		{
154 			gpio_base = -1;
155 		}
156 		fclose(fd);
157 
158 		/* We found the right file. No need to continue. */
159 		break;
160 	}
161 	closedir(dir);
162 
163 	if (gpio_base == -1)
164 	{
165 		fprintf(stderr, "Could not find GPIO base\n");
166 	}
167 
168 	return gpio_base;
169 }
170 
171 /**
172  * Converts the GPIO port/offset nomenclature value
173  * to a number.  Supports the ASPEED method where
174  * num = base + (port * 8) + offset, and the port is an
175  * A-Z character that converts to 0 to 25.  The base
176  * is obtained form the hardware.
177  *
178  * For example: "A5" -> 5,  "Z7" -> 207
179  *
180  * @param[in] gpio - the GPIO name
181  *
182  * @return int - the GPIO number or < 0 if not found
183  */
convert_gpio_to_num(const char * gpio)184 int convert_gpio_to_num(const char* gpio)
185 {
186 	size_t len = strlen(gpio);
187 	if (len < 2)
188 	{
189 		fprintf(stderr, "Invalid GPIO name %s\n", gpio);
190 		return -1;
191 	}
192 
193 	/* Read the offset from the last character */
194 	if (!isdigit(gpio[len-1]))
195 	{
196 		fprintf(stderr, "Invalid GPIO offset in GPIO %s\n", gpio);
197 		return -1;
198 	}
199 
200 	int offset = gpio[len-1] - '0';
201 
202 	/* Read the port from the second to last character */
203 	if (!isalpha(gpio[len-2]))
204 	{
205 		fprintf(stderr, "Invalid GPIO port in GPIO %s\n", gpio);
206 		return -1;
207 	}
208 	int port = toupper(gpio[len-2]) - 'A';
209 
210 	/* Check for a 2 character port, like AA */
211 	if ((len == 3) && isalpha(gpio[len-3]))
212 	{
213 		port += 26 * (toupper(gpio[len-3]) - 'A' + 1);
214 	}
215 
216 	return (port * GPIO_PORT_OFFSET) + offset;
217 }
218 
219 /**
220  * Returns the cJSON pointer to the GPIO definition
221  * for the GPIO passed in.
222  *
223  * @param[in] gpio_name - the GPIO name, like BMC_POWER_UP
224  *
225  * @return cJSON* - pointer to the cJSON object or NULL
226  *				  if not found.
227  */
get_gpio_def(const char * gpio_name)228 cJSON* get_gpio_def(const char* gpio_name)
229 {
230 	if (gpio_json == NULL)
231 	{
232 		gpio_json = load_json();
233 		if (gpio_json == NULL)
234 		{
235 			return NULL;
236 		}
237 	}
238 
239 	cJSON* gpio_defs = cJSON_GetObjectItem(gpio_json, "gpio_definitions");
240 	g_assert(gpio_defs != NULL);
241 
242 	cJSON* def;
243 	cJSON_ArrayForEach(def, gpio_defs)
244 	{
245 		cJSON* name = cJSON_GetObjectItem(def, "name");
246 		g_assert(name != NULL);
247 
248 		if (strcmp(name->valuestring, gpio_name) == 0)
249 		{
250 			return def;
251 		}
252 	}
253 	return NULL;
254 }
255 
256 /**
257  * Frees the gpio_json memory
258  *
259  * Can be called once when callers are done calling making calls
260  * to gpio_init() so that the JSON only needs to be loaded once.
261  */
gpio_inits_done()262 void gpio_inits_done()
263 {
264 	if (gpio_json != NULL)
265 	{
266 		cJSON_Delete(gpio_json);
267 		gpio_json = NULL;
268 	}
269 }
270 
271 /**
272  * Fills in the dev, direction, and num elements in
273  * the GPIO structure.
274  *
275  * @param gpio - the GPIO structure to fill in
276  *
277  * @return GPIO_OK if successful
278  */
gpio_get_params(GPIO * gpio)279 int gpio_get_params(GPIO* gpio)
280 {
281 	gpio->dev = g_strdup(GPIO_BASE_PATH);
282 
283 	const cJSON* def = get_gpio_def(gpio->name);
284 	if (def == NULL)
285 	{
286 		fprintf(stderr, "Unable to find GPIO %s in the JSON\n",
287 				gpio->name);
288 		return GPIO_LOOKUP_ERROR;
289 	}
290 
291 	const cJSON* dir = cJSON_GetObjectItem(def, "direction");
292 	g_assert(dir != NULL);
293 	gpio->direction = g_strdup(dir->valuestring);
294 
295 	/* Must use either 'num', like 87, or 'pin', like "A5" */
296 	const cJSON* num = cJSON_GetObjectItem(def, "num");
297 	if ((num != NULL) && cJSON_IsNumber(num))
298 	{
299 		// When the "num" key is used, we will assume
300 		// that the system has gpiochip0 and that each
301 		// bank has the same amount of pins
302 
303 		struct gpiochip_info info;
304 		int fd, ret;
305 		// Open the device
306 		fd = open(DEV_NAME, O_RDONLY);
307 		if (fd < 0)
308 		{
309 			fprintf(stderr, "Unable to open %s: %s\n",
310 					DEV_NAME, strerror(errno));
311 			return GPIO_LOOKUP_ERROR;
312 		}
313 
314 		// Query GPIO chip info
315 		ret = ioctl(fd, GPIO_GET_CHIPINFO_IOCTL, &info);
316 		if (ret == -1)
317 		{
318 			fprintf(stderr, "Unable to get chip info from ioctl: %s\n",
319 					strerror(errno));
320 			close(fd);
321 			return GPIO_LOOKUP_ERROR;
322 		}
323 
324 		close(fd);
325 		printf("Number of pins per bank: %d\n", info.lines);
326 
327 		gpio->num = (num->valueint) % info.lines;
328 		gpio->chip_id = (num->valueint) / info.lines;
329 	}
330 	else
331 	{
332 		const cJSON* pin = cJSON_GetObjectItem(def, "pin");
333 		g_assert(pin != NULL);
334 
335 		int pin_id = convert_gpio_to_num(pin->valuestring);
336 		if (pin_id < 0)
337 		{
338 			return GPIO_LOOKUP_ERROR;
339 		}
340 		gpio->num = (size_t) pin_id;
341 	}
342 	// TODO: For the purposes of skeleton and the projects that use it,
343 	// it should be safe to assume this will always be 0. Eventually skeleton
344 	// should be going away, but if the need arises before then this may need
345 	// to be updated to handle non-zero cases.
346 	gpio->chip_id = 0;
347 	return GPIO_OK;
348 }
349 
gpio_open(GPIO * gpio,uint8_t default_value)350 int gpio_open(GPIO* gpio, uint8_t default_value)
351 {
352 	g_assert (gpio != NULL);
353 
354 	char buf[255];
355 	sprintf(buf, "/dev/gpiochip%d", gpio->chip_id);
356 	gpio->fd = open(buf, 0);
357 	if (gpio->fd == -1)
358 	{
359 		return GPIO_OPEN_ERROR;
360 	}
361 
362 	struct gpiohandle_request req;
363 	memset(&req, 0, sizeof(req));
364 	strncpy(req.consumer_label, "skeleton-gpio",  sizeof(req.consumer_label));
365 
366 	// open gpio for writing or reading
367 	if (gpio->direction == NULL)
368 	{
369 		gpio_close(gpio);
370 		return GPIO_OPEN_ERROR;
371 	}
372 	req.flags = (strcmp(gpio->direction,"in") == 0) ? GPIOHANDLE_REQUEST_INPUT
373 								: GPIOHANDLE_REQUEST_OUTPUT;
374 
375 	req.lineoffsets[0] = gpio->num;
376 	req.lines = 1;
377 
378 	if (strcmp(gpio->direction,"out") == 0)
379 	{
380 		req.default_values[0] = default_value;
381 	}
382 
383 	int rc = ioctl(gpio->fd, GPIO_GET_LINEHANDLE_IOCTL, &req);
384 	if (rc < 0)
385 	{
386 		gpio_close(gpio);
387 		return GPIO_OPEN_ERROR;
388 	}
389 	gpio_close(gpio);
390 	gpio->fd = req.fd;
391 
392 	return GPIO_OK;
393 }
394 
gpio_close(GPIO * gpio)395 void gpio_close(GPIO* gpio)
396 {
397 	if(gpio->fd < 0)
398 		return;
399 
400 	close(gpio->fd);
401 	gpio->fd = -1;
402 }
403