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 #define DEV_NAME "/dev/gpiochip0" 23 24 cJSON* gpio_json = NULL; 25 26 int gpio_write(GPIO* gpio, uint8_t value) 27 { 28 g_assert (gpio != NULL); 29 struct gpiohandle_data data; 30 memset(&data, 0, sizeof(data)); 31 data.values[0] = value; 32 33 if (gpio->fd <= 0) 34 { 35 return GPIO_ERROR; 36 } 37 38 if (ioctl(gpio->fd, GPIOHANDLE_SET_LINE_VALUES_IOCTL, &data) < 0) 39 { 40 return GPIO_WRITE_ERROR; 41 } 42 43 return GPIO_OK; 44 } 45 46 int gpio_read(GPIO* gpio, uint8_t *value) 47 { 48 g_assert (gpio != NULL); 49 struct gpiohandle_data data; 50 memset(&data, 0, sizeof(data)); 51 52 if (gpio->fd <= 0) 53 { 54 return GPIO_ERROR; 55 } 56 57 if (ioctl(gpio->fd, GPIOHANDLE_GET_LINE_VALUES_IOCTL, &data) < 0) 58 { 59 return GPIO_READ_ERROR; 60 } 61 62 *value = data.values[0]; 63 64 return GPIO_OK; 65 } 66 67 /** 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 */ 77 int get_gpio_base() 78 { 79 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 { 92 93 /* 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; 104 int rc = asprintf(&label_name, "%s/%s/label", 105 GPIO_BASE_PATH, entry->d_name); 106 107 if (!rc) 108 { 109 continue; 110 } 111 112 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; 136 rc = asprintf(&base_name, "%s/%s/base", 137 GPIO_BASE_PATH, entry->d_name); 138 139 if (!rc) 140 { 141 continue; 142 } 143 144 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; 169 } 170 171 /** 172 * 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 */ 184 int convert_gpio_to_num(const char* gpio) 185 { 186 size_t len = strlen(gpio); 187 if (len < 2) 188 { 189 fprintf(stderr, "Invalid GPIO name %s\n", gpio); 190 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 216 return (port * GPIO_PORT_OFFSET) + offset; 217 } 218 219 /** 220 * 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 */ 228 cJSON* 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 */ 262 void 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 */ 279 int 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 { 299 // 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; 329 } 330 else 331 { 332 const cJSON* pin = cJSON_GetObjectItem(def, "pin"); 333 g_assert(pin != NULL); 334 335 int pin_id = convert_gpio_to_num(pin->valuestring); 336 if (pin_id < 0) 337 { 338 return GPIO_LOOKUP_ERROR; 339 } 340 gpio->num = (size_t) pin_id; 341 } 342 // 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; 347 return GPIO_OK; 348 } 349 350 int gpio_open(GPIO* gpio, uint8_t default_value) 351 { 352 g_assert (gpio != NULL); 353 354 char buf[255]; 355 sprintf(buf, "/dev/gpiochip%d", gpio->chip_id); 356 gpio->fd = open(buf, 0); 357 if (gpio->fd == -1) 358 { 359 return GPIO_OPEN_ERROR; 360 } 361 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 392 return GPIO_OK; 393 } 394 395 void gpio_close(GPIO* gpio) 396 { 397 if(gpio->fd < 0) 398 return; 399 400 close(gpio->fd); 401 gpio->fd = -1; 402 } 403