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 #define GPIO_PORT_OFFSET 8 18 #define GPIO_BASE_PATH "/sys/class/gpio" 19 20 cJSON* gpio_json = NULL; 21 22 int gpio_writec(GPIO* gpio, char value) 23 { 24 g_assert (gpio != NULL); 25 int rc = GPIO_OK; 26 char buf[1]; 27 buf[0] = value; 28 29 if (lseek(gpio->fd, 0, SEEK_SET) == -1) 30 { 31 return GPIO_ERROR; 32 } 33 34 if (write(gpio->fd, buf, 1) != 1) 35 { 36 rc = GPIO_WRITE_ERROR; 37 } 38 return rc; 39 } 40 41 int gpio_write(GPIO* gpio, uint8_t value) 42 { 43 g_assert (gpio != NULL); 44 int rc = GPIO_OK; 45 char buf[1]; 46 buf[0] = '0'; 47 if (value==1) 48 { 49 buf[0]='1'; 50 } 51 52 if (lseek(gpio->fd, 0, SEEK_SET) == -1) 53 { 54 return GPIO_ERROR; 55 } 56 57 if (write(gpio->fd, buf, 1) != 1) 58 { 59 rc = GPIO_WRITE_ERROR; 60 } 61 return rc; 62 } 63 64 int gpio_read(GPIO* gpio, uint8_t *value) 65 { 66 g_assert (gpio != NULL); 67 char buf[1]; 68 int r = GPIO_OK; 69 if (gpio->fd <= 0) 70 { 71 r = GPIO_ERROR; 72 } 73 else 74 { 75 if (lseek(gpio->fd, 0, SEEK_SET) == -1) 76 { 77 return GPIO_ERROR; 78 } 79 80 if (read(gpio->fd,&buf,1) != 1) 81 { 82 r = GPIO_READ_ERROR; 83 } else { 84 if (buf[0]=='1') { 85 *value = 1; 86 } else { 87 *value = 0; 88 } 89 } 90 } 91 return r; 92 } 93 int gpio_clock_cycle(GPIO* gpio, int num_clks) { 94 g_assert (gpio != NULL); 95 int i=0; 96 int r=GPIO_OK; 97 for (i=0;i<num_clks;i++) { 98 if (gpio_writec(gpio,'0') == -1) { 99 r = GPIO_WRITE_ERROR; 100 break; 101 } 102 if (gpio_writec(gpio,'1') == -1) { 103 r = GPIO_WRITE_ERROR; 104 break; 105 } 106 } 107 return r; 108 } 109 110 /** 111 * Determine the GPIO base number for the system. It is found in 112 * the 'base' file in the /sys/class/gpio/gpiochipX/ directory where the 113 * /sys/class/gpio/gpiochipX/label file has a '1e780000.gpio' in it. 114 * 115 * Note: This method is ASPEED specific. Could add support for 116 * additional SOCs in the future. 117 * 118 * @return int - the GPIO base number, or < 0 if not found 119 */ 120 int get_gpio_base() 121 { 122 int gpio_base = -1; 123 124 DIR* dir = opendir(GPIO_BASE_PATH); 125 if (dir == NULL) 126 { 127 fprintf(stderr, "Unable to open directory %s\n", 128 GPIO_BASE_PATH); 129 return -1; 130 } 131 132 struct dirent* entry; 133 while ((entry = readdir(dir)) != NULL) 134 { 135 /* Look in the gpiochip<X> directories for a file called 'label' */ 136 /* that contains '1e780000.gpio', then in that directory read */ 137 /* the GPIO base out of the 'base' file. */ 138 139 if (strncmp(entry->d_name, "gpiochip", 8) != 0) 140 { 141 continue; 142 } 143 144 gboolean is_bmc = FALSE; 145 char* label_name; 146 asprintf(&label_name, "%s/%s/label", 147 GPIO_BASE_PATH, entry->d_name); 148 149 FILE* fd = fopen(label_name, "r"); 150 free(label_name); 151 152 if (!fd) 153 { 154 continue; 155 } 156 157 char label[14]; 158 if (fgets(label, 14, fd) != NULL) 159 { 160 if (strcmp(label, "1e780000.gpio") == 0) 161 { 162 is_bmc = TRUE; 163 } 164 } 165 fclose(fd); 166 167 if (!is_bmc) 168 { 169 continue; 170 } 171 172 char* base_name; 173 asprintf(&base_name, "%s/%s/base", 174 GPIO_BASE_PATH, entry->d_name); 175 176 fd = fopen(base_name, "r"); 177 free(base_name); 178 179 if (!fd) 180 { 181 continue; 182 } 183 184 if (fscanf(fd, "%d", &gpio_base) != 1) 185 { 186 gpio_base = -1; 187 } 188 fclose(fd); 189 190 /* We found the right file. No need to continue. */ 191 break; 192 } 193 closedir(dir); 194 195 if (gpio_base == -1) 196 { 197 fprintf(stderr, "Could not find GPIO base\n"); 198 } 199 200 return gpio_base; 201 } 202 203 /** 204 * Converts the GPIO port/offset nomenclature value 205 * to a number. Supports the ASPEED method where 206 * num = base + (port * 8) + offset, and the port is an 207 * A-Z character that converts to 0 to 25. The base 208 * is obtained form the hardware. 209 * 210 * For example: "A5" -> 5, "Z7" -> 207 211 * 212 * @param[in] gpio - the GPIO name 213 * 214 * @return int - the GPIO number or < 0 if not found 215 */ 216 int convert_gpio_to_num(const char* gpio) 217 { 218 static int gpio_base = -1; 219 if (gpio_base == -1) 220 { 221 gpio_base = get_gpio_base(); 222 if (gpio_base < 0) 223 { 224 return gpio_base; 225 } 226 } 227 228 size_t len = strlen(gpio); 229 if (len < 2) 230 { 231 fprintf(stderr, ("Invalid GPIO name %s\n", gpio)); 232 return -1; 233 } 234 235 /* Read the offset from the last character */ 236 if (!isdigit(gpio[len-1])) 237 { 238 fprintf(stderr, "Invalid GPIO offset in GPIO %s\n", gpio); 239 return -1; 240 } 241 242 int offset = gpio[len-1] - '0'; 243 244 /* Read the port from the second to last character */ 245 if (!isalpha(gpio[len-2])) 246 { 247 fprintf(stderr, "Invalid GPIO port in GPIO %s\n", gpio); 248 return -1; 249 } 250 int port = toupper(gpio[len-2]) - 'A'; 251 252 /* Check for a 2 character port, like AA */ 253 if ((len == 3) && isalpha(gpio[len-3])) 254 { 255 port += 26 * (toupper(gpio[len-3]) - 'A' + 1); 256 } 257 258 return gpio_base + (port * GPIO_PORT_OFFSET) + offset; 259 } 260 261 /** 262 * Returns the cJSON pointer to the GPIO definition 263 * for the GPIO passed in. 264 * 265 * @param[in] gpio_name - the GPIO name, like BMC_POWER_UP 266 * 267 * @return cJSON* - pointer to the cJSON object or NULL 268 * if not found. 269 */ 270 cJSON* get_gpio_def(const char* gpio_name) 271 { 272 if (gpio_json == NULL) 273 { 274 gpio_json = load_json(); 275 if (gpio_json == NULL) 276 { 277 return NULL; 278 } 279 } 280 281 cJSON* gpio_defs = cJSON_GetObjectItem(gpio_json, "gpio_definitions"); 282 g_assert(gpio_defs != NULL); 283 284 cJSON* def; 285 cJSON_ArrayForEach(def, gpio_defs) 286 { 287 cJSON* name = cJSON_GetObjectItem(def, "name"); 288 g_assert(name != NULL); 289 290 if (strcmp(name->valuestring, gpio_name) == 0) 291 { 292 return def; 293 } 294 } 295 return NULL; 296 } 297 298 /** 299 * Frees the gpio_json memory 300 * 301 * Can be called once when callers are done calling making calls 302 * to gpio_init() so that the JSON only needs to be loaded once. 303 */ 304 void gpio_inits_done() 305 { 306 if (gpio_json != NULL) 307 { 308 cJSON_Delete(gpio_json); 309 gpio_json = NULL; 310 } 311 } 312 313 /** 314 * Fills in the dev, direction, and num elements in 315 * the GPIO structure. 316 * 317 * @param gpio - the GPIO structure to fill in 318 * 319 * @return GPIO_OK if successful 320 */ 321 int gpio_get_params(GPIO* gpio) 322 { 323 gpio->dev = g_strdup(GPIO_BASE_PATH); 324 325 const cJSON* def = get_gpio_def(gpio->name); 326 if (def == NULL) 327 { 328 fprintf(stderr, "Unable to find GPIO %s in the JSON\n", 329 gpio->name); 330 return GPIO_LOOKUP_ERROR; 331 } 332 333 const cJSON* dir = cJSON_GetObjectItem(def, "direction"); 334 g_assert(dir != NULL); 335 gpio->direction = g_strdup(dir->valuestring); 336 337 /* Must use either 'num', like 87, or 'pin', like "A5" */ 338 const cJSON* num = cJSON_GetObjectItem(def, "num"); 339 if ((num != NULL) && cJSON_IsNumber(num)) 340 { 341 gpio->num = num->valueint; 342 } 343 else 344 { 345 const cJSON* pin = cJSON_GetObjectItem(def, "pin"); 346 g_assert(pin != NULL); 347 348 gpio->num = convert_gpio_to_num(pin->valuestring); 349 if (gpio->num < 0) 350 { 351 return GPIO_LOOKUP_ERROR; 352 } 353 } 354 return GPIO_OK; 355 } 356 357 // Gets the gpio device path from gpio manager object 358 int gpio_init(GPIO* gpio) 359 { 360 int rc = gpio_get_params(gpio); 361 if (rc != GPIO_OK) 362 { 363 return rc; 364 } 365 366 g_print("GPIO Lookup: %s = %d,%s\n",gpio->name,gpio->num,gpio->direction); 367 368 //export and set direction 369 char dev[254]; 370 char data[4]; 371 int fd; 372 do { 373 struct stat st; 374 375 sprintf(dev,"%s/gpio%d/value",gpio->dev,gpio->num); 376 //check if gpio is exported, if not export 377 int result = stat(dev, &st); 378 if (result) 379 { 380 sprintf(dev,"%s/export",gpio->dev); 381 fd = open(dev, O_WRONLY); 382 if (fd == GPIO_ERROR) { 383 rc = GPIO_OPEN_ERROR; 384 break; 385 } 386 sprintf(data,"%d",gpio->num); 387 rc = write(fd,data,strlen(data)); 388 close(fd); 389 if (rc != strlen(data)) { 390 rc = GPIO_WRITE_ERROR; 391 break; 392 } 393 } 394 const char* file = "edge"; 395 const char* direction = gpio->direction; 396 if (strcmp(direction, "in") == 0) 397 { 398 file = "direction"; 399 } 400 else if (strcmp(direction, "out") == 0) 401 { 402 file = "direction"; 403 404 // Read current value, so we can set 'high' or 'low'. 405 // Setting direction directly to 'out' is the same as 406 // setting to 'low' which can change the value in the 407 // GPIO. 408 uint8_t value = 0; 409 rc = gpio_open(gpio); 410 if (rc) break; 411 rc = gpio_read(gpio, &value); 412 if (rc) break; 413 gpio_close(gpio); 414 415 direction = (value ? "high" : "low"); 416 } 417 sprintf(dev,"%s/gpio%d/%s",gpio->dev,gpio->num,file); 418 fd = open(dev,O_WRONLY); 419 if (fd == GPIO_ERROR) { 420 rc = GPIO_WRITE_ERROR; 421 break; 422 } 423 rc = write(fd,direction,strlen(direction)); 424 if (rc != strlen(direction)) { 425 rc = GPIO_WRITE_ERROR; 426 break; 427 } 428 429 close(fd); 430 rc = GPIO_OK; 431 } while(0); 432 433 return rc; 434 } 435 436 437 438 439 char* get_gpio_dev(GPIO* gpio) 440 { 441 char* buf; 442 asprintf(&buf, "%s/gpio%d/value", gpio->dev, gpio->num); 443 return buf; 444 } 445 446 int gpio_open_interrupt(GPIO* gpio, GIOFunc func, gpointer user_data) 447 { 448 int rc = GPIO_OK; 449 char buf[255]; 450 sprintf(buf, "%s/gpio%d/value", gpio->dev, gpio->num); 451 gpio->fd = open(buf, O_RDONLY | O_NONBLOCK ); 452 gpio->irq_inited = false; 453 if (gpio->fd == -1) 454 { 455 rc = GPIO_OPEN_ERROR; 456 } 457 else 458 { 459 GIOChannel* channel = g_io_channel_unix_new( gpio->fd); 460 guint id = g_io_add_watch( channel, G_IO_PRI, func, user_data ); 461 } 462 return rc; 463 } 464 465 int gpio_open(GPIO* gpio) 466 { 467 g_assert (gpio != NULL); 468 // open gpio for writing or reading 469 char buf[254]; 470 int rc = 0; 471 gpio->fd = -1; 472 if (gpio->direction == NULL) { 473 return GPIO_OPEN_ERROR; 474 } 475 if (strcmp(gpio->direction,"in")==0) 476 { 477 sprintf(buf, "%s/gpio%d/value", gpio->dev, gpio->num); 478 gpio->fd = open(buf, O_RDONLY); 479 } 480 else 481 { 482 sprintf(buf, "%s/gpio%d/value", gpio->dev, gpio->num); 483 gpio->fd = open(buf, O_RDWR); 484 485 } 486 if (gpio->fd == -1) { 487 return GPIO_OPEN_ERROR; 488 } 489 return GPIO_OK; 490 } 491 492 void gpio_close(GPIO* gpio) 493 { 494 close(gpio->fd); 495 } 496