/* * Copyright (C) 2021 Denis 'GNUtoo' Carikli * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #include #include "exynos4412_gpios_data.h" /* * Generic functions */ static int get_2bit_field_value(uint32_t register_value, int gpio_offset) { return ((register_value >> (2*gpio_offset)) & 3); } /* * CON */ static char *gpio_direction_str(int value) { /* The GPCON field size is 4 bits. So the maximum value it can take * is 0xf. So to convert that to string, if we add the trailing \0, the * size of the the string is 4. */ static char result[4] = { 0 }; switch (value) { case GPIO_INPUT: return "GPIO_INPUT"; case GPIO_OUTPUT: return "GPIO_OUTPUT"; default: snprintf(result, sizeof(result), "0x%x", value); return (char*)result; } } static int get_con_value(uint32_t register_value, int gpio_offset) { return ((register_value >> (gpio_offset * 4)) & 0xf); } /* * DAT */ static char *gpio_value_str(int value) { /* The GPDAT field size is 1 bit. But when the pin is not input nor * output, its value is undefined. */ switch (value) { case GPIO_VALUE_LOW: return "GPIO_VALUE_LOW"; case GPIO_VALUE_HIGH: return "GPIO_VALUE_HIGH"; case GPIO_VALUE_UNDEFINED: return "GPIO_VALUE_UNDEFINED"; } assert(false); /* This should normally not be reached */ return ""; /* Avoid -Werror=return-type */ } static int get_dat_value(uint32_t register_value, int gpio_offset) { return ((register_value >> (gpio_offset)) & 1); } /* * DRV */ static char *gpio_drive_str(int value) { /* The GPDRV field size is 2 bit. */ switch (value) { case GPIO_DRIVE_1X: return "GPIO_DRIVE_1X"; case GPIO_DRIVE_2X: return "GPIO_DRIVE_2X"; case GPIO_DRIVE_3X: return "GPIO_DRIVE_3X"; case GPIO_DRIVE_4X: return "GPIO_DRIVE_4X"; } assert(false); /* This should normally not be reached */ return ""; /* Avoid -Werror=return-type */ } /* * PUD */ static char *gpio_resistor_str(int value) { /* The GPPUD field size is 2 bit. */ switch (value) { case GPIO_RESISTORS_PULLDOWN_DISABLE: return "GPIO_RESISTORS_PULLDOWN_DISABLE"; case GPIO_RESISTORS_ENABLE_PULL_DOWN: return "GPIO_RESISTORS_ENABLE_PULL_DOWN"; case GPIO_RESISTORS_RESERVED: return "GPIO_RESISTORS_RESERVED"; case GPIO_RESISTORS_ENABLE_PULL_UP: return "GPIO_RESISTORS_ENABLE_PULL_UP"; } assert(false); /* This should normally not be reached */ return ""; /* Avoid -Werror=return-type */ } /* * CONPDN */ static char *gpio_power_down_str(int value) { /* The GPCON field size is 2 bits. */ switch (value) { case GPIO_POWER_DOWN_OUTPUT_LOW: return "GPIO_POWER_DOWN_OUTPUT_LOW"; case GPIO_POWER_DOWN_OUTPUT_HIGH: return "GPIO_POWER_DOWN_OUTPUT_HIGH"; case GPIO_POWER_DOWN_INPUT: return "GPIO_POWER_DOWN_INPUT"; case GPIO_POWER_DOWN_PREVIOUS_STATE: return "GPIO_POWER_DOWN_PREVIOUS_STATE"; } assert(false); /* This should normally not be reached */ return ""; /* Avoid -Werror=return-type */ } struct gpio_bank_data gpio_banks_data[] = { { .name = "gpf0", .nr_gpios = 8, .base = 0x11400000, .offset = 0x0180, }, { .name = "gpf1", .nr_gpios = 8, .base = 0x11400000, .offset = 0x01a0, }, { .name = "gpf2", .nr_gpios = 8, .base = 0x11400000, .offset = 0x01c0, }, { .name = "gpf3", .nr_gpios = 6, .base = 0x11400000, .offset = 0x01e0, }, { .name = "gpj0", .nr_gpios = 8, .base = 0x11400000, .offset = 0x0240, }, { .name = "gpj1", .nr_gpios = 5, .base = 0x11400000, .offset = 0x0260, }, { .name = "gpl0", .nr_gpios = 7, .base = 0x11000000, .offset = 0x00c0, }, { .name = "gpl1", .nr_gpios = 2, .base = 0x11000000, .offset = 0x00e0, }, { .name = "gpl2", .nr_gpios = 8, .base = 0x11000000, .offset = 0x0100, }, { .name = "gpm0", .nr_gpios = 8, .base = 0x11000000, .offset = 0x0260, }, { .name = "gpm1", .nr_gpios = 7, .base = 0x11000000, .offset = 0x0280, }, { .name = "gpm2", .nr_gpios = 5, .base = 0x11000000, .offset = 0x02a0, }, { .name = "gpm3", .nr_gpios = 8, .base = 0x11000000, .offset = 0x02c0, }, { .name = "gpm4", .nr_gpios = 8, .base = 0x11000000, .offset = 0x02e0, }, { .name = "gpx0", .nr_gpios = 8, .base = 0x11000000, .offset = 0x0c00, }, { .name = "gpx1", .nr_gpios = 8, .base = 0x11000000, .offset = 0x0c20, }, { .name = "gpx2", .nr_gpios = 8, .base = 0x11000000, .offset = 0x0c40, }, { .name = "gpx3", .nr_gpios = 8, .base = 0x11000000, .offset = 0x0c60, }, { .name = "gpy0", .nr_gpios = 6, .base = 0x11000000, .offset = 0x0120, }, { .name = "gpy1", .nr_gpios = 4, .base = 0x11000000, .offset = 0x0140, }, { .name = "gpy2", .nr_gpios = 6, .base = 0x11000000, .offset = 0x0160, }, { /* Sentinel */ }, }; struct gpio_register_data gpio_registers_data[] = { { .name = "con", .get_value = get_con_value, .offset = 0x00, .value_str = gpio_direction_str, }, { .name = "dat", .get_value = get_dat_value, .offset = 0x04, .value_str = gpio_value_str, }, { .name = "pud", .get_value = get_2bit_field_value, .offset = 0x08, .value_str = gpio_resistor_str, }, { .name = "drv", .get_value = get_2bit_field_value, .offset = 0x0c, .value_str = gpio_drive_str, }, { .name = "con-pdn", .get_value = get_2bit_field_value, .offset = 0x10, .value_str = gpio_power_down_str, }, { .name = "pud-pdn", .get_value = get_2bit_field_value, .offset = 0x14, .value_str = gpio_resistor_str, }, }; struct gpio_bank_data *get_gpio_bank_data(char *bank) { int i = 0; while (1) { if (gpio_banks_data[i].name == NULL) break; if (!strcmp(gpio_banks_data[i].name, bank)) return &(gpio_banks_data[i]); i++; } return NULL; } struct gpio_register_data *get_gpio_register_data(char *register_name) { int i = 0; while (1) { if (gpio_registers_data[i].name == NULL) break; if (!strcmp(gpio_registers_data[i].name, register_name)) return &(gpio_registers_data[i]); i++; } return NULL; } void print_gpio_banks_data(void) { int i = 0; while (1) { if (gpio_banks_data[i].name == NULL) break; printf("%s[%d] {\n", gpio_banks_data[i].name, gpio_banks_data[i].nr_gpios); printf("\toffset: 0x%x\n", gpio_banks_data[i].offset); printf("}\n"); i++; } } int decode_gpio_data(int debug, char *bank, uint32_t gpio_offset, char* gpio_register_name, uint32_t *virt_addr) { struct gpio_bank_data *gpio_bank_data; struct gpio_register_data *gpio_register_data; uint32_t *addr; uint32_t register_value; gpio_bank_data = get_gpio_bank_data(bank); if (gpio_bank_data == NULL) { errno = EINVAL; return -1; } gpio_register_data = get_gpio_register_data(gpio_register_name); if (gpio_register_data == NULL) { errno = EINVAL; return -1; } addr = virt_addr + gpio_bank_data->offset + gpio_register_data->offset; register_value = *virt_addr; if (debug) printf("%s: %s: offset 0x%x value: 0x%x\n", __func__, bank, addr - virt_addr, register_value); return gpio_register_data->get_value(register_value, gpio_offset); } char *gpio_data_str(char* gpio_register_name, uint32_t register_value) { struct gpio_register_data *gpio_register_data; gpio_register_data = get_gpio_register_data(gpio_register_name); if (gpio_register_data == NULL) { errno = EINVAL; return NULL; } return gpio_register_data->value_str(register_value); } off_t get_gpio_register_offset(int debug, char *gpio_bank_name, char *register_name) { struct gpio_bank_data *gpio_bank_data; struct gpio_register_data *gpio_register_data; off_t gpio_hardware_block_offset; off_t offset = 0; gpio_bank_data = get_gpio_bank_data(gpio_bank_name); if (gpio_bank_data == NULL) { errno = EINVAL; return -1; } gpio_register_data = get_gpio_register_data(register_name); if (gpio_register_data == NULL) { errno = EINVAL; return -1; } if (gpio_bank_data->base == 0x11400000) gpio_hardware_block_offset = 0x400000; offset = gpio_hardware_block_offset + gpio_bank_data->offset + gpio_register_data->offset; return offset; } int get_bank_gpio_numbers(int debug, char *gpio_bank_name) { struct gpio_bank_data *gpio_bank_data; gpio_bank_data = get_gpio_bank_data(gpio_bank_name); if (gpio_bank_data == NULL) { errno = EINVAL; return -1; } return gpio_bank_data->nr_gpios; }