blob: c59de3142aee456bcd71bf7159168428699de9df [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
Anthony Wilsonc0c74e72019-03-06 10:54:58 -060017#include <sys/ioctl.h>
18#include <linux/gpio.h>
19
Matt Spinler8428d442018-08-07 14:28:35 -050020#define GPIO_PORT_OFFSET 8
Matt Spinler3a70e932018-08-07 14:16:47 -050021#define GPIO_BASE_PATH "/sys/class/gpio"
Jonico Eustaquiofd84bfa2024-01-17 10:02:15 -060022#define DEV_NAME "/dev/gpiochip0"
Matt Spinler3a70e932018-08-07 14:16:47 -050023
24cJSON* gpio_json = NULL;
Norman Jamese7594922015-08-27 14:25:24 -050025
Norman James32e74e22015-09-15 21:28:06 -050026int gpio_write(GPIO* gpio, uint8_t value)
Norman Jamese7594922015-08-27 14:25:24 -050027{
Norman James65a295a2015-09-26 22:21:10 -050028 g_assert (gpio != NULL);
Anthony Wilsonc0c74e72019-03-06 10:54:58 -060029 struct gpiohandle_data data;
30 memset(&data, 0, sizeof(data));
31 data.values[0] = value;
Yong Li86101ea2017-11-17 14:34:18 +080032
Anthony Wilsonc0c74e72019-03-06 10:54:58 -060033 if (gpio->fd <= 0)
Yong Li86101ea2017-11-17 14:34:18 +080034 {
35 return GPIO_ERROR;
36 }
37
Anthony Wilsonc0c74e72019-03-06 10:54:58 -060038 if (ioctl(gpio->fd, GPIOHANDLE_SET_LINE_VALUES_IOCTL, &data) < 0)
Norman Jamese7594922015-08-27 14:25:24 -050039 {
Anthony Wilsonc0c74e72019-03-06 10:54:58 -060040 return GPIO_WRITE_ERROR;
Norman James32e74e22015-09-15 21:28:06 -050041 }
Anthony Wilsonc0c74e72019-03-06 10:54:58 -060042
43 return GPIO_OK;
Norman Jamese7594922015-08-27 14:25:24 -050044}
45
Norman James88872672015-09-21 16:51:35 -050046int gpio_read(GPIO* gpio, uint8_t *value)
Norman Jamese7594922015-08-27 14:25:24 -050047{
Norman James65a295a2015-09-26 22:21:10 -050048 g_assert (gpio != NULL);
Anthony Wilsonc0c74e72019-03-06 10:54:58 -060049 struct gpiohandle_data data;
50 memset(&data, 0, sizeof(data));
51
Norman James8abb50c2015-09-16 10:58:16 -050052 if (gpio->fd <= 0)
Norman Jamese7594922015-08-27 14:25:24 -050053 {
Anthony Wilsonc0c74e72019-03-06 10:54:58 -060054 return GPIO_ERROR;
Norman James8abb50c2015-09-16 10:58:16 -050055 }
Yong Li86101ea2017-11-17 14:34:18 +080056
Anthony Wilsonc0c74e72019-03-06 10:54:58 -060057 if (ioctl(gpio->fd, GPIOHANDLE_GET_LINE_VALUES_IOCTL, &data) < 0)
58 {
59 return GPIO_READ_ERROR;
Norman Jamese7594922015-08-27 14:25:24 -050060 }
Anthony Wilsonc0c74e72019-03-06 10:54:58 -060061
62 *value = data.values[0];
63
64 return GPIO_OK;
Norman Jamese7594922015-08-27 14:25:24 -050065}
66
Matt Spinlere2cd39d2018-08-07 14:32:58 -050067/**
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 */
Matt Spinler8428d442018-08-07 14:28:35 -050077int get_gpio_base()
Matt Spinler3a70e932018-08-07 14:16:47 -050078{
Matt Spinlere2cd39d2018-08-07 14:32:58 -050079 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 {
Patrick Williams0d259e32024-10-04 08:35:52 -040092
Matt Spinlere2cd39d2018-08-07 14:32:58 -050093 /* 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;
Patrick Williams0d259e32024-10-04 08:35:52 -0400104 int rc = asprintf(&label_name, "%s/%s/label",
Matt Spinlere2cd39d2018-08-07 14:32:58 -0500105 GPIO_BASE_PATH, entry->d_name);
106
Patrick Williams0d259e32024-10-04 08:35:52 -0400107 if (!rc)
108 {
109 continue;
110 }
111
Matt Spinlere2cd39d2018-08-07 14:32:58 -0500112 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;
Patrick Williams0d259e32024-10-04 08:35:52 -0400136 rc = asprintf(&base_name, "%s/%s/base",
Matt Spinlere2cd39d2018-08-07 14:32:58 -0500137 GPIO_BASE_PATH, entry->d_name);
138
Patrick Williams0d259e32024-10-04 08:35:52 -0400139 if (!rc)
140 {
141 continue;
142 }
143
Matt Spinlere2cd39d2018-08-07 14:32:58 -0500144 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;
Matt Spinler3a70e932018-08-07 14:16:47 -0500169}
170
171/**
Matt Spinler8428d442018-08-07 14:28:35 -0500172 * 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 */
184int convert_gpio_to_num(const char* gpio)
185{
Matt Spinler8428d442018-08-07 14:28:35 -0500186 size_t len = strlen(gpio);
187 if (len < 2)
188 {
Ed Tanous36171642021-01-08 15:40:19 -0800189 fprintf(stderr, "Invalid GPIO name %s\n", gpio);
Matt Spinler8428d442018-08-07 14:28:35 -0500190 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
Anthony Wilsonc0c74e72019-03-06 10:54:58 -0600216 return (port * GPIO_PORT_OFFSET) + offset;
Matt Spinler8428d442018-08-07 14:28:35 -0500217}
218
219/**
Matt Spinler3a70e932018-08-07 14:16:47 -0500220 * 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 */
228cJSON* 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 */
262void 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 */
279int 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 {
Jonico Eustaquiofd84bfa2024-01-17 10:02:15 -0600299 // 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;
Matt Spinler3a70e932018-08-07 14:16:47 -0500329 }
330 else
331 {
332 const cJSON* pin = cJSON_GetObjectItem(def, "pin");
333 g_assert(pin != NULL);
334
Patrick Williams0d259e32024-10-04 08:35:52 -0400335 int pin_id = convert_gpio_to_num(pin->valuestring);
336 if (pin_id < 0)
Matt Spinler3a70e932018-08-07 14:16:47 -0500337 {
338 return GPIO_LOOKUP_ERROR;
339 }
Patrick Williams0d259e32024-10-04 08:35:52 -0400340 gpio->num = (size_t) pin_id;
Matt Spinler3a70e932018-08-07 14:16:47 -0500341 }
Anthony Wilsonc0c74e72019-03-06 10:54:58 -0600342 // 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;
Matt Spinler3a70e932018-08-07 14:16:47 -0500347 return GPIO_OK;
348}
349
Anthony Wilsonc0c74e72019-03-06 10:54:58 -0600350int gpio_open(GPIO* gpio, uint8_t default_value)
Norman Jamese7594922015-08-27 14:25:24 -0500351{
Norman James65a295a2015-09-26 22:21:10 -0500352 g_assert (gpio != NULL);
Norman Jamese7594922015-08-27 14:25:24 -0500353
Anthony Wilsonc0c74e72019-03-06 10:54:58 -0600354 char buf[255];
355 sprintf(buf, "/dev/gpiochip%d", gpio->chip_id);
356 gpio->fd = open(buf, 0);
357 if (gpio->fd == -1)
358 {
Norman James88872672015-09-21 16:51:35 -0500359 return GPIO_OPEN_ERROR;
360 }
Anthony Wilsonc0c74e72019-03-06 10:54:58 -0600361
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
Norman James88872672015-09-21 16:51:35 -0500392 return GPIO_OK;
Norman Jamese7594922015-08-27 14:25:24 -0500393}
394
395void gpio_close(GPIO* gpio)
396{
Anthony Wilsonc0c74e72019-03-06 10:54:58 -0600397 if(gpio->fd < 0)
398 return;
399
Norman Jamese7594922015-08-27 14:25:24 -0500400 close(gpio->fd);
Anthony Wilsonc0c74e72019-03-06 10:54:58 -0600401 gpio->fd = -1;
Norman Jamese7594922015-08-27 14:25:24 -0500402}