| /* Copyright (c) 2013 The Chromium OS Authors. All rights reserved. |
| * Use of this source code is governed by a BSD-style license that can be |
| * found in the LICENSE file. |
| */ |
| |
| /* GPIO common functionality for Chrome EC */ |
| |
| #include "common.h" |
| #include "console.h" |
| #include "gpio.h" |
| #include "host_command.h" |
| #include "system.h" |
| #include "util.h" |
| |
| static uint8_t last_val[(GPIO_COUNT + 7) / 8]; |
| |
| /** |
| * Find a GPIO signal by name. |
| * |
| * @param name Signal name to find |
| * |
| * @return the signal index, or GPIO_COUNT if no match. |
| */ |
| static enum gpio_signal find_signal_by_name(const char *name) |
| { |
| int i; |
| |
| if (!name || !*name) |
| return GPIO_COUNT; |
| |
| for (i = 0; i < GPIO_COUNT; i++) |
| if (gpio_is_implemented(i) && |
| !strcasecmp(name, gpio_get_name(i))) |
| return i; |
| |
| return GPIO_COUNT; |
| } |
| |
| /** |
| * Update last_val |
| * |
| * @param i Index of last_val[] to update |
| * @param v New value for last_val[i] |
| * |
| * @return 1 if last_val[i] was updated, 0 if last_val[i]==v. |
| */ |
| static int last_val_changed(int i, int v) |
| { |
| if (v && !(last_val[i / 8] & (1 << (i % 8)))) { |
| last_val[i / 8] |= 1 << (i % 8); |
| return 1; |
| } else if (!v && last_val[i / 8] & (1 << (i % 8))) { |
| last_val[i / 8] &= ~(1 << (i % 8)); |
| return 1; |
| } else { |
| return 0; |
| } |
| } |
| |
| static enum ec_error_list set(const char *name, int value) |
| { |
| enum gpio_signal signal = find_signal_by_name(name); |
| |
| if (signal == GPIO_COUNT) |
| return EC_ERROR_INVAL; |
| |
| if (!gpio_is_implemented(signal)) |
| return EC_ERROR_INVAL; |
| |
| if (!(gpio_get_default_flags(signal) & GPIO_OUTPUT)) |
| return EC_ERROR_INVAL; |
| |
| gpio_set_level(signal, value); |
| |
| return EC_SUCCESS; |
| } |
| /*****************************************************************************/ |
| /* Console commands */ |
| |
| static void print_gpio_info(int gpio) |
| { |
| int changed, v, flags; |
| |
| if (!gpio_is_implemented(gpio)) |
| return; /* Skip unsupported signals */ |
| |
| v = gpio_get_level(gpio); |
| #ifdef CONFIG_CMD_GPIO_EXTENDED |
| flags = gpio_get_flags(gpio); |
| #else |
| flags = 0; |
| #endif |
| changed = last_val_changed(gpio, v); |
| |
| ccprintf(" %d%c %s%s%s%s%s%s%s%s%s%s\n", v, (changed ? '*' : ' '), |
| (flags & GPIO_INPUT ? "I " : ""), |
| (flags & GPIO_OUTPUT ? "O " : ""), |
| (flags & GPIO_LOW ? "L " : ""), |
| (flags & GPIO_HIGH ? "H " : ""), |
| (flags & GPIO_ANALOG ? "A " : ""), |
| (flags & GPIO_OPEN_DRAIN ? "ODR " : ""), |
| (flags & GPIO_PULL_UP ? "PU " : ""), |
| (flags & GPIO_PULL_DOWN ? "PD " : ""), |
| (flags & GPIO_ALTERNATE ? "ALT " : ""), |
| gpio_get_name(gpio)); |
| |
| /* Flush console to avoid truncating output */ |
| cflush(); |
| } |
| |
| static int command_gpio_get(int argc, char **argv) |
| { |
| int i; |
| |
| /* If a signal is specified, print only that one */ |
| if (argc == 2) { |
| i = find_signal_by_name(argv[1]); |
| if (i == GPIO_COUNT) |
| return EC_ERROR_PARAM1; |
| print_gpio_info(i); |
| |
| return EC_SUCCESS; |
| } |
| |
| /* Otherwise print them all */ |
| for (i = 0; i < GPIO_COUNT; i++) { |
| if (!gpio_is_implemented(i)) |
| continue; /* Skip unsupported signals */ |
| |
| print_gpio_info(i); |
| } |
| |
| return EC_SUCCESS; |
| } |
| DECLARE_SAFE_CONSOLE_COMMAND(gpioget, command_gpio_get, |
| "[name]", |
| "Read GPIO value(s)"); |
| |
| static int command_gpio_set(int argc, char **argv) |
| { |
| #ifdef CONFIG_CMD_GPIO_EXTENDED |
| int gpio; |
| int flags = 0; |
| int af = -1; |
| char *e; |
| |
| if (argc < 3) |
| return EC_ERROR_PARAM_COUNT; |
| |
| gpio = find_signal_by_name(argv[1]); |
| if (gpio == GPIO_COUNT) |
| return EC_ERROR_PARAM1; |
| |
| if (strcasecmp(argv[2], "IN") == 0) |
| flags = GPIO_INPUT; |
| else if (strcasecmp(argv[2], "1") == 0) |
| flags = GPIO_OUT_HIGH; |
| else if (strcasecmp(argv[2], "0") == 0) |
| flags = GPIO_OUT_LOW; |
| else if (strcasecmp(argv[2], "A") == 0) |
| flags = GPIO_ANALOG; |
| else if (strcasecmp(argv[2], "ALT") == 0) { |
| if (argc >= 4) { |
| af = strtoi(argv[3], &e, 0); |
| if (*e || af < 0 || af > 5) |
| return EC_ERROR_PARAM2; |
| } |
| flags = GPIO_ALTERNATE; |
| } else |
| return EC_ERROR_PARAM2; |
| |
| /* Update alt function if requested. */ |
| if (af >= 0) { |
| const struct gpio_info *g = gpio_list + gpio; |
| |
| gpio_set_alternate_function(g->port, g->mask, af); |
| } |
| /* Update GPIO flags. */ |
| gpio_set_flags(gpio, flags); |
| #else |
| char *e; |
| int v; |
| |
| if (argc < 3) |
| return EC_ERROR_PARAM_COUNT; |
| |
| v = strtoi(argv[2], &e, 0); |
| if (*e) |
| return EC_ERROR_PARAM2; |
| |
| if (set(argv[1], v) != EC_SUCCESS) |
| return EC_ERROR_PARAM1; |
| #endif |
| return EC_SUCCESS; |
| } |
| DECLARE_CONSOLE_COMMAND(gpioset, command_gpio_set, |
| #ifdef CONFIG_CMD_GPIO_EXTENDED |
| "name <0 | 1 | IN | A | ALT [func]>", |
| #else |
| "name <0 | 1>", |
| #endif |
| "Set a GPIO"); |
| |
| /*****************************************************************************/ |
| /* Host commands */ |
| |
| static int gpio_command_get(struct host_cmd_handler_args *args) |
| { |
| const struct ec_params_gpio_get_v1 *p_v1 = args->params; |
| struct ec_response_gpio_get_v1 *r_v1 = args->response; |
| int i, len; |
| |
| if (args->version == 0) { |
| const struct ec_params_gpio_get *p = args->params; |
| struct ec_response_gpio_get *r = args->response; |
| |
| i = find_signal_by_name(p->name); |
| if (i == GPIO_COUNT) |
| return EC_RES_ERROR; |
| |
| r->val = gpio_get_level(i); |
| args->response_size = sizeof(struct ec_response_gpio_get); |
| return EC_RES_SUCCESS; |
| } |
| |
| switch (p_v1->subcmd) { |
| case EC_GPIO_GET_BY_NAME: |
| i = find_signal_by_name(p_v1->get_value_by_name.name); |
| if (i == GPIO_COUNT) |
| return EC_RES_ERROR; |
| |
| r_v1->get_value_by_name.val = gpio_get_level(i); |
| args->response_size = sizeof(r_v1->get_value_by_name); |
| break; |
| case EC_GPIO_GET_COUNT: |
| r_v1->get_count.val = GPIO_COUNT; |
| args->response_size = sizeof(r_v1->get_count); |
| break; |
| case EC_GPIO_GET_INFO: |
| if (p_v1->get_info.index >= GPIO_COUNT) |
| return EC_RES_ERROR; |
| |
| i = p_v1->get_info.index; |
| len = strlen(gpio_get_name(i)); |
| memcpy(r_v1->get_info.name, gpio_get_name(i), len+1); |
| r_v1->get_info.val = gpio_get_level(i); |
| r_v1->get_info.flags = gpio_get_default_flags(i); |
| args->response_size = sizeof(r_v1->get_info); |
| break; |
| default: |
| return EC_RES_INVALID_PARAM; |
| } |
| |
| return EC_RES_SUCCESS; |
| |
| } |
| DECLARE_HOST_COMMAND(EC_CMD_GPIO_GET, gpio_command_get, |
| EC_VER_MASK(0) | EC_VER_MASK(1)); |
| |
| static int gpio_command_set(struct host_cmd_handler_args *args) |
| { |
| const struct ec_params_gpio_set *p = args->params; |
| |
| if (system_is_locked()) |
| return EC_RES_ACCESS_DENIED; |
| |
| if (set(p->name, p->val) != EC_SUCCESS) |
| return EC_RES_ERROR; |
| |
| return EC_RES_SUCCESS; |
| } |
| DECLARE_HOST_COMMAND(EC_CMD_GPIO_SET, gpio_command_set, EC_VER_MASK(0)); |