xref: /openbmc/skeleton/libopenbmc_intf/gpio.c (revision 0d259e38)
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 
23 cJSON* gpio_json = NULL;
24 
gpio_write(GPIO * gpio,uint8_t value)25 int gpio_write(GPIO* gpio, uint8_t value)
26 {
27 	g_assert (gpio != NULL);
28 	struct gpiohandle_data data;
29 	memset(&data, 0, sizeof(data));
30 	data.values[0] = value;
31 
32 	if (gpio->fd <= 0)
33 	{
34 		return GPIO_ERROR;
35 	}
36 
37 	if (ioctl(gpio->fd, GPIOHANDLE_SET_LINE_VALUES_IOCTL, &data) < 0)
38 	{
39 		return GPIO_WRITE_ERROR;
40 	}
41 
42 	return GPIO_OK;
43 }
44 
gpio_read(GPIO * gpio,uint8_t * value)45 int gpio_read(GPIO* gpio, uint8_t *value)
46 {
47 	g_assert (gpio != NULL);
48 	struct gpiohandle_data data;
49 	memset(&data, 0, sizeof(data));
50 
51 	if (gpio->fd <= 0)
52 	{
53 		return GPIO_ERROR;
54 	}
55 
56 	if (ioctl(gpio->fd, GPIOHANDLE_GET_LINE_VALUES_IOCTL, &data) < 0)
57 	{
58 		return GPIO_READ_ERROR;
59 	}
60 
61 	*value = data.values[0];
62 
63 	return GPIO_OK;
64 }
65 
66 /**
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  */
get_gpio_base()76 int get_gpio_base()
77 {
78 	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 
92 		/* 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;
103 		int rc = asprintf(&label_name, "%s/%s/label",
104 				GPIO_BASE_PATH, entry->d_name);
105 
106 		if (!rc)
107 		{
108 			continue;
109 		}
110 
111 		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;
135 		rc = asprintf(&base_name, "%s/%s/base",
136 				GPIO_BASE_PATH, entry->d_name);
137 
138 		if (!rc)
139 		{
140 			continue;
141 		}
142 
143 		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;
168 }
169 
170 /**
171  * 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  */
convert_gpio_to_num(const char * gpio)183 int convert_gpio_to_num(const char* gpio)
184 {
185 	size_t len = strlen(gpio);
186 	if (len < 2)
187 	{
188 		fprintf(stderr, "Invalid GPIO name %s\n", gpio);
189 		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 
215 	return (port * GPIO_PORT_OFFSET) + offset;
216 }
217 
218 /**
219  * 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  */
get_gpio_def(const char * gpio_name)227 cJSON* 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  */
gpio_inits_done()261 void 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  */
gpio_get_params(GPIO * gpio)278 int 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 
305 		int pin_id = convert_gpio_to_num(pin->valuestring);
306 		if (pin_id < 0)
307 		{
308 			return GPIO_LOOKUP_ERROR;
309 		}
310 		gpio->num = (size_t) pin_id;
311 	}
312 	// 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;
317 	return GPIO_OK;
318 }
319 
gpio_open(GPIO * gpio,uint8_t default_value)320 int gpio_open(GPIO* gpio, uint8_t default_value)
321 {
322 	g_assert (gpio != NULL);
323 
324 	char buf[255];
325 	sprintf(buf, "/dev/gpiochip%d", gpio->chip_id);
326 	gpio->fd = open(buf, 0);
327 	if (gpio->fd == -1)
328 	{
329 		return GPIO_OPEN_ERROR;
330 	}
331 
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 
362 	return GPIO_OK;
363 }
364 
gpio_close(GPIO * gpio)365 void gpio_close(GPIO* gpio)
366 {
367 	if(gpio->fd < 0)
368 		return;
369 
370 	close(gpio->fd);
371 	gpio->fd = -1;
372 }
373