blob: 2fe14219c68e4f248c1b57b1e53026ac01eaed25 [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>
Brad Bishopf6c85682016-06-27 11:56:39 -040012#include "openbmc_intf.h"
Norman Jamese7594922015-08-27 14:25:24 -050013#include "gpio.h"
Matt Spinler3a70e932018-08-07 14:16:47 -050014#include "gpio_json.h"
Norman Jamese7594922015-08-27 14:25:24 -050015
Matt Spinler8428d442018-08-07 14:28:35 -050016#define GPIO_PORT_OFFSET 8
Matt Spinler3a70e932018-08-07 14:16:47 -050017#define GPIO_BASE_PATH "/sys/class/gpio"
18
19cJSON* gpio_json = NULL;
Norman Jamese7594922015-08-27 14:25:24 -050020
Norman James32e74e22015-09-15 21:28:06 -050021int gpio_writec(GPIO* gpio, char value)
Norman Jamese7594922015-08-27 14:25:24 -050022{
Norman James65a295a2015-09-26 22:21:10 -050023 g_assert (gpio != NULL);
Norman James32e74e22015-09-15 21:28:06 -050024 int rc = GPIO_OK;
Norman Jamese7594922015-08-27 14:25:24 -050025 char buf[1];
26 buf[0] = value;
Yong Li86101ea2017-11-17 14:34:18 +080027
28 if (lseek(gpio->fd, 0, SEEK_SET) == -1)
29 {
30 return GPIO_ERROR;
31 }
32
Norman Jamese7594922015-08-27 14:25:24 -050033 if (write(gpio->fd, buf, 1) != 1)
34 {
Norman James32e74e22015-09-15 21:28:06 -050035 rc = GPIO_WRITE_ERROR;
36 }
37 return rc;
Norman Jamese7594922015-08-27 14:25:24 -050038}
39
Norman James32e74e22015-09-15 21:28:06 -050040int gpio_write(GPIO* gpio, uint8_t value)
Norman Jamese7594922015-08-27 14:25:24 -050041{
Norman James65a295a2015-09-26 22:21:10 -050042 g_assert (gpio != NULL);
Norman James32e74e22015-09-15 21:28:06 -050043 int rc = GPIO_OK;
Norman Jamese7594922015-08-27 14:25:24 -050044 char buf[1];
45 buf[0] = '0';
46 if (value==1)
47 {
48 buf[0]='1';
Patrick Williams3a8fa6e2016-08-24 14:04:22 -050049 }
Yong Li86101ea2017-11-17 14:34:18 +080050
51 if (lseek(gpio->fd, 0, SEEK_SET) == -1)
52 {
53 return GPIO_ERROR;
54 }
55
Norman Jamese7594922015-08-27 14:25:24 -050056 if (write(gpio->fd, buf, 1) != 1)
57 {
Norman James32e74e22015-09-15 21:28:06 -050058 rc = GPIO_WRITE_ERROR;
59 }
60 return rc;
Norman Jamese7594922015-08-27 14:25:24 -050061}
62
Norman James88872672015-09-21 16:51:35 -050063int gpio_read(GPIO* gpio, uint8_t *value)
Norman Jamese7594922015-08-27 14:25:24 -050064{
Norman James65a295a2015-09-26 22:21:10 -050065 g_assert (gpio != NULL);
Norman Jamese7594922015-08-27 14:25:24 -050066 char buf[1];
Norman James32e74e22015-09-15 21:28:06 -050067 int r = GPIO_OK;
Norman James8abb50c2015-09-16 10:58:16 -050068 if (gpio->fd <= 0)
Norman Jamese7594922015-08-27 14:25:24 -050069 {
Patrick Williams3a8fa6e2016-08-24 14:04:22 -050070 r = GPIO_ERROR;
Norman James8abb50c2015-09-16 10:58:16 -050071 }
72 else
73 {
Yong Li86101ea2017-11-17 14:34:18 +080074 if (lseek(gpio->fd, 0, SEEK_SET) == -1)
75 {
76 return GPIO_ERROR;
77 }
78
Norman James8abb50c2015-09-16 10:58:16 -050079 if (read(gpio->fd,&buf,1) != 1)
80 {
Norman James8abb50c2015-09-16 10:58:16 -050081 r = GPIO_READ_ERROR;
Norman James32e74e22015-09-15 21:28:06 -050082 } else {
Norman James8abb50c2015-09-16 10:58:16 -050083 if (buf[0]=='1') {
84 *value = 1;
85 } else {
86 *value = 0;
87 }
Norman James32e74e22015-09-15 21:28:06 -050088 }
Norman Jamese7594922015-08-27 14:25:24 -050089 }
Norman James32e74e22015-09-15 21:28:06 -050090 return r;
Norman Jamese7594922015-08-27 14:25:24 -050091}
Norman James32e74e22015-09-15 21:28:06 -050092int gpio_clock_cycle(GPIO* gpio, int num_clks) {
Norman James65a295a2015-09-26 22:21:10 -050093 g_assert (gpio != NULL);
Norman Jamese7594922015-08-27 14:25:24 -050094 int i=0;
Norman James32e74e22015-09-15 21:28:06 -050095 int r=GPIO_OK;
Norman Jamese7594922015-08-27 14:25:24 -050096 for (i=0;i<num_clks;i++) {
Norman James32e74e22015-09-15 21:28:06 -050097 if (gpio_writec(gpio,'0') == -1) {
98 r = GPIO_WRITE_ERROR;
99 break;
100 }
101 if (gpio_writec(gpio,'1') == -1) {
102 r = GPIO_WRITE_ERROR;
103 break;
104 }
Norman Jamese7594922015-08-27 14:25:24 -0500105 }
Norman James32e74e22015-09-15 21:28:06 -0500106 return r;
Norman Jamese7594922015-08-27 14:25:24 -0500107}
108
Matt Spinler8428d442018-08-07 14:28:35 -0500109int get_gpio_base()
Matt Spinler3a70e932018-08-07 14:16:47 -0500110{
111 /* TODO */
112 return 0;
113}
114
115/**
Matt Spinler8428d442018-08-07 14:28:35 -0500116 * Converts the GPIO port/offset nomenclature value
117 * to a number. Supports the ASPEED method where
118 * num = base + (port * 8) + offset, and the port is an
119 * A-Z character that converts to 0 to 25. The base
120 * is obtained form the hardware.
121 *
122 * For example: "A5" -> 5, "Z7" -> 207
123 *
124 * @param[in] gpio - the GPIO name
125 *
126 * @return int - the GPIO number or < 0 if not found
127 */
128int convert_gpio_to_num(const char* gpio)
129{
130 static int gpio_base = -1;
131 if (gpio_base == -1)
132 {
133 gpio_base = get_gpio_base();
134 if (gpio_base < 0)
135 {
136 return gpio_base;
137 }
138 }
139
140 size_t len = strlen(gpio);
141 if (len < 2)
142 {
143 fprintf(stderr, ("Invalid GPIO name %s\n", gpio));
144 return -1;
145 }
146
147 /* Read the offset from the last character */
148 if (!isdigit(gpio[len-1]))
149 {
150 fprintf(stderr, "Invalid GPIO offset in GPIO %s\n", gpio);
151 return -1;
152 }
153
154 int offset = gpio[len-1] - '0';
155
156 /* Read the port from the second to last character */
157 if (!isalpha(gpio[len-2]))
158 {
159 fprintf(stderr, "Invalid GPIO port in GPIO %s\n", gpio);
160 return -1;
161 }
162 int port = toupper(gpio[len-2]) - 'A';
163
164 /* Check for a 2 character port, like AA */
165 if ((len == 3) && isalpha(gpio[len-3]))
166 {
167 port += 26 * (toupper(gpio[len-3]) - 'A' + 1);
168 }
169
170 return gpio_base + (port * GPIO_PORT_OFFSET) + offset;
171}
172
173/**
Matt Spinler3a70e932018-08-07 14:16:47 -0500174 * Returns the cJSON pointer to the GPIO definition
175 * for the GPIO passed in.
176 *
177 * @param[in] gpio_name - the GPIO name, like BMC_POWER_UP
178 *
179 * @return cJSON* - pointer to the cJSON object or NULL
180 * if not found.
181 */
182cJSON* get_gpio_def(const char* gpio_name)
183{
184 if (gpio_json == NULL)
185 {
186 gpio_json = load_json();
187 if (gpio_json == NULL)
188 {
189 return NULL;
190 }
191 }
192
193 cJSON* gpio_defs = cJSON_GetObjectItem(gpio_json, "gpio_definitions");
194 g_assert(gpio_defs != NULL);
195
196 cJSON* def;
197 cJSON_ArrayForEach(def, gpio_defs)
198 {
199 cJSON* name = cJSON_GetObjectItem(def, "name");
200 g_assert(name != NULL);
201
202 if (strcmp(name->valuestring, gpio_name) == 0)
203 {
204 return def;
205 }
206 }
207 return NULL;
208}
209
210/**
211 * Frees the gpio_json memory
212 *
213 * Can be called once when callers are done calling making calls
214 * to gpio_init() so that the JSON only needs to be loaded once.
215 */
216void gpio_inits_done()
217{
218 if (gpio_json != NULL)
219 {
220 cJSON_Delete(gpio_json);
221 gpio_json = NULL;
222 }
223}
224
225/**
226 * Fills in the dev, direction, and num elements in
227 * the GPIO structure.
228 *
229 * @param gpio - the GPIO structure to fill in
230 *
231 * @return GPIO_OK if successful
232 */
233int gpio_get_params(GPIO* gpio)
234{
235 gpio->dev = g_strdup(GPIO_BASE_PATH);
236
237 const cJSON* def = get_gpio_def(gpio->name);
238 if (def == NULL)
239 {
240 fprintf(stderr, "Unable to find GPIO %s in the JSON\n",
241 gpio->name);
242 return GPIO_LOOKUP_ERROR;
243 }
244
245 const cJSON* dir = cJSON_GetObjectItem(def, "direction");
246 g_assert(dir != NULL);
247 gpio->direction = g_strdup(dir->valuestring);
248
249 /* Must use either 'num', like 87, or 'pin', like "A5" */
250 const cJSON* num = cJSON_GetObjectItem(def, "num");
251 if ((num != NULL) && cJSON_IsNumber(num))
252 {
253 gpio->num = num->valueint;
254 }
255 else
256 {
257 const cJSON* pin = cJSON_GetObjectItem(def, "pin");
258 g_assert(pin != NULL);
259
260 gpio->num = convert_gpio_to_num(pin->valuestring);
261 if (gpio->num < 0)
262 {
263 return GPIO_LOOKUP_ERROR;
264 }
265 }
266 return GPIO_OK;
267}
268
Norman Jamese7594922015-08-27 14:25:24 -0500269// Gets the gpio device path from gpio manager object
Norman James32e74e22015-09-15 21:28:06 -0500270int gpio_init(GDBusConnection *connection, GPIO* gpio)
Norman Jamese7594922015-08-27 14:25:24 -0500271{
Matt Spinler3a70e932018-08-07 14:16:47 -0500272 int rc = gpio_get_params(gpio);
273 if (rc != GPIO_OK)
274 {
275 return rc;
Norman James32e74e22015-09-15 21:28:06 -0500276 }
Norman Jamese7594922015-08-27 14:25:24 -0500277
Norman Jamese7594922015-08-27 14:25:24 -0500278 g_print("GPIO Lookup: %s = %d,%s\n",gpio->name,gpio->num,gpio->direction);
Patrick Williams3a8fa6e2016-08-24 14:04:22 -0500279
Norman Jamese7594922015-08-27 14:25:24 -0500280 //export and set direction
281 char dev[254];
282 char data[4];
Norman James88872672015-09-21 16:51:35 -0500283 int fd;
Norman James32e74e22015-09-15 21:28:06 -0500284 do {
Norman James88872672015-09-21 16:51:35 -0500285 struct stat st;
Patrick Williams3a8fa6e2016-08-24 14:04:22 -0500286
Norman James5a7cc8d2015-10-06 12:31:06 -0500287 sprintf(dev,"%s/gpio%d/value",gpio->dev,gpio->num);
288 //check if gpio is exported, if not export
Norman James88872672015-09-21 16:51:35 -0500289 int result = stat(dev, &st);
290 if (result)
291 {
292 sprintf(dev,"%s/export",gpio->dev);
293 fd = open(dev, O_WRONLY);
294 if (fd == GPIO_ERROR) {
295 rc = GPIO_OPEN_ERROR;
296 break;
Patrick Williams3a8fa6e2016-08-24 14:04:22 -0500297 }
Norman James88872672015-09-21 16:51:35 -0500298 sprintf(data,"%d",gpio->num);
299 rc = write(fd,data,strlen(data));
300 close(fd);
301 if (rc != strlen(data)) {
302 rc = GPIO_WRITE_ERROR;
303 break;
304 }
Norman James32e74e22015-09-15 21:28:06 -0500305 }
Norman James5a7cc8d2015-10-06 12:31:06 -0500306 const char* file = "edge";
Patrick Williamsed1368d2016-08-24 14:23:45 -0500307 const char* direction = gpio->direction;
308 if (strcmp(direction, "in") == 0)
Norman James5a7cc8d2015-10-06 12:31:06 -0500309 {
310 file = "direction";
311 }
Patrick Williamsed1368d2016-08-24 14:23:45 -0500312 else if (strcmp(direction, "out") == 0)
313 {
314 file = "direction";
315
316 // Read current value, so we can set 'high' or 'low'.
317 // Setting direction directly to 'out' is the same as
318 // setting to 'low' which can change the value in the
319 // GPIO.
320 uint8_t value = 0;
321 rc = gpio_open(gpio);
322 if (rc) break;
323 rc = gpio_read(gpio, &value);
324 if (rc) break;
325 gpio_close(gpio);
326
327 direction = (value ? "high" : "low");
328 }
Norman James5a7cc8d2015-10-06 12:31:06 -0500329 sprintf(dev,"%s/gpio%d/%s",gpio->dev,gpio->num,file);
Norman James32e74e22015-09-15 21:28:06 -0500330 fd = open(dev,O_WRONLY);
331 if (fd == GPIO_ERROR) {
332 rc = GPIO_WRITE_ERROR;
333 break;
334 }
Patrick Williamsed1368d2016-08-24 14:23:45 -0500335 rc = write(fd,direction,strlen(direction));
336 if (rc != strlen(direction)) {
Norman James88872672015-09-21 16:51:35 -0500337 rc = GPIO_WRITE_ERROR;
338 break;
339 }
340
Norman James32e74e22015-09-15 21:28:06 -0500341 close(fd);
Norman James5a7cc8d2015-10-06 12:31:06 -0500342 rc = GPIO_OK;
Norman James32e74e22015-09-15 21:28:06 -0500343 } while(0);
Norman Jamese7594922015-08-27 14:25:24 -0500344
Norman James32e74e22015-09-15 21:28:06 -0500345 return rc;
Norman Jamese7594922015-08-27 14:25:24 -0500346}
Norman James5a7cc8d2015-10-06 12:31:06 -0500347
348
349
350
Norman James471ab592015-08-30 22:29:40 -0500351char* get_gpio_dev(GPIO* gpio)
352{
353 char* buf;
Patrick Williamsd6baab92016-08-24 14:05:55 -0500354 asprintf(&buf, "%s/gpio%d/value", gpio->dev, gpio->num);
Norman James471ab592015-08-30 22:29:40 -0500355 return buf;
356}
357
Norman James5a7cc8d2015-10-06 12:31:06 -0500358int gpio_open_interrupt(GPIO* gpio, GIOFunc func, gpointer user_data)
359{
360 int rc = GPIO_OK;
Norman James02b77f32015-10-28 18:59:29 -0500361 char buf[255];
Norman James5a7cc8d2015-10-06 12:31:06 -0500362 sprintf(buf, "%s/gpio%d/value", gpio->dev, gpio->num);
363 gpio->fd = open(buf, O_RDONLY | O_NONBLOCK );
Norman James02b77f32015-10-28 18:59:29 -0500364 gpio->irq_inited = false;
Norman James5a7cc8d2015-10-06 12:31:06 -0500365 if (gpio->fd == -1)
366 {
367 rc = GPIO_OPEN_ERROR;
368 }
369 else
370 {
Norman James02b77f32015-10-28 18:59:29 -0500371 GIOChannel* channel = g_io_channel_unix_new( gpio->fd);
Norman James5a7cc8d2015-10-06 12:31:06 -0500372 guint id = g_io_add_watch( channel, G_IO_PRI, func, user_data );
373 }
374 return rc;
375}
376
Norman Jamese7594922015-08-27 14:25:24 -0500377int gpio_open(GPIO* gpio)
378{
Norman James65a295a2015-09-26 22:21:10 -0500379 g_assert (gpio != NULL);
Norman Jamese7594922015-08-27 14:25:24 -0500380 // open gpio for writing or reading
381 char buf[254];
Norman James32e74e22015-09-15 21:28:06 -0500382 int rc = 0;
Norman James5a7cc8d2015-10-06 12:31:06 -0500383 gpio->fd = -1;
Norman James65a295a2015-09-26 22:21:10 -0500384 if (gpio->direction == NULL) {
385 return GPIO_OPEN_ERROR;
386 }
Norman Jamese7594922015-08-27 14:25:24 -0500387 if (strcmp(gpio->direction,"in")==0)
388 {
389 sprintf(buf, "%s/gpio%d/value", gpio->dev, gpio->num);
390 gpio->fd = open(buf, O_RDONLY);
391 }
392 else
393 {
394 sprintf(buf, "%s/gpio%d/value", gpio->dev, gpio->num);
Norman James25571112015-12-15 09:36:28 -0600395 gpio->fd = open(buf, O_RDWR);
Norman Jamese7594922015-08-27 14:25:24 -0500396
397 }
Norman James88872672015-09-21 16:51:35 -0500398 if (gpio->fd == -1) {
399 return GPIO_OPEN_ERROR;
400 }
401 return GPIO_OK;
Norman Jamese7594922015-08-27 14:25:24 -0500402}
403
404void gpio_close(GPIO* gpio)
405{
406 close(gpio->fd);
407}