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