/* Copyright 2013 The ChromiumOS Authors * 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 "builtin/assert.h" #include "common.h" #include "gpio.h" #include "ioexpander.h" #include "registers.h" #include "timer.h" #include "util.h" /* GPIO alternate function structure */ struct gpio_alt_func { /* Module ID (as uint8_t, since enum would be 32-bit) */ uint8_t module_id; /* Alternate function number */ enum gpio_alternate_func func; /* Port base address */ uint32_t port; /* Bitmask on that port (multiple bits allowed) */ uint32_t mask; /* Flags (GPIO_*; see above). */ uint32_t flags; }; /* * Construct the gpio_alt_funcs array. This array is used by gpio_config_module * to enable and disable GPIO alternate functions on a module by module basis. */ #define ALTERNATE(pinmask, function, module, flagz) \ { GPIO_##pinmask, .func = (function), .module_id = (module), \ .flags = (flagz) }, static __const_data const struct gpio_alt_func gpio_alt_funcs[] = { #include "gpio.wrap" }; /* * GPIO_CONFIG_ALL_PORTS signifies a "don't care" for the GPIO port. This is * used in gpio_config_pins(). When the port parameter is set to this, the * pin_mask parameter is ignored. */ #define GPIO_CONFIG_ALL_PORTS 0xFFFFFFFF static int gpio_config_pins(enum module_id id, uint32_t port, uint32_t pin_mask, int enable) { const struct gpio_alt_func *af; int rv = EC_ERROR_INVAL; /* Find pins and set to alternate functions */ for (af = gpio_alt_funcs; af < gpio_alt_funcs + ARRAY_SIZE(gpio_alt_funcs); af++) { if (af->module_id != id) continue; /* Pins for some other module */ /* Check to see if the requested port matches. */ if ((port != GPIO_CONFIG_ALL_PORTS) && (port != af->port)) continue; /* If we don't care which port, enable all applicable pins. */ if (port == GPIO_CONFIG_ALL_PORTS) pin_mask = af->mask; if ((af->mask & pin_mask) == pin_mask) { if (!(af->flags & GPIO_DEFAULT)) gpio_set_flags_by_mask( af->port, (af->mask & pin_mask), enable ? af->flags : GPIO_INPUT); gpio_set_alternate_function( af->port, (af->mask & pin_mask), enable ? af->func : GPIO_ALT_FUNC_NONE); rv = EC_SUCCESS; /* We're done here if we were just setting one port. */ if (port != GPIO_CONFIG_ALL_PORTS) break; } } return rv; } /*****************************************************************************/ /* GPIO API */ int gpio_config_module(enum module_id id, int enable) { /* Set all the alternate functions for this module. */ return gpio_config_pins(id, GPIO_CONFIG_ALL_PORTS, 0, enable); } int gpio_config_pin(enum module_id id, enum gpio_signal signal, int enable) { return gpio_config_pins(id, gpio_list[signal].port, gpio_list[signal].mask, enable); } void gpio_set_flags(enum gpio_signal signal, int flags) { const struct gpio_info *g = gpio_list + signal; gpio_set_flags_by_mask(g->port, g->mask, flags); } int gpio_get_flags(enum gpio_signal signal) { const struct gpio_info *g = gpio_list + signal; if (IS_ENABLED(CONFIG_GPIO_GET_EXTENDED)) return gpio_get_flags_by_mask(g->port, g->mask); else return gpio_get_default_flags(signal); } int gpio_get_default_flags(enum gpio_signal signal) { return gpio_list[signal].flags; } void gpio_reset(enum gpio_signal signal) { const struct gpio_info *g = gpio_list + signal; gpio_set_alternate_function(g->port, g->mask, GPIO_ALT_FUNC_NONE); gpio_set_flags_by_mask(g->port, g->mask, g->flags); } const char *gpio_get_name(enum gpio_signal signal) { return gpio_list[signal].name; } int gpio_is_implemented(enum gpio_signal signal) { return !!gpio_list[signal].mask; } int gpio_get_ternary(enum gpio_signal signal) { int pd, pu; int flags = gpio_get_default_flags(signal); /* Read GPIO with internal pull-down */ gpio_set_flags(signal, GPIO_INPUT | GPIO_PULL_DOWN); pd = gpio_get_level(signal); udelay(100); /* Read GPIO with internal pull-up */ gpio_set_flags(signal, GPIO_INPUT | GPIO_PULL_UP); pu = gpio_get_level(signal); udelay(100); /* Reset GPIO flags */ gpio_set_flags(signal, flags); /* Check PU and PD readings to determine tristate */ return pu && !pd ? 2 : pd; } #ifdef CONFIG_GPIO_POWER_DOWN /* * Power down a group of GPIO pins marked with a module ID * in board/board_name/gpio.inc * Hibernation/sleep entry: * gpio_power_down_module(MODULE_xxxx) * Chip level code will power down all pins in module. * Wake: * Use gpio_config_module to put the module pin(s) * back to enabled state. Chip level code will re-power * and configure the pin(s). * This mechanism does not handle dynamic changing of * pin configuration at run time. */ int gpio_power_down_module(enum module_id id) { const struct gpio_alt_func *af; int rv = EC_ERROR_INVAL; /* Find pins and power down */ for (af = gpio_alt_funcs; af < gpio_alt_funcs + ARRAY_SIZE(gpio_alt_funcs); af++) { if (af->module_id != id) continue; /* Pins for some other module */ gpio_set_flags_by_mask(af->port, af->mask, GPIO_POWER_DOWN); rv = EC_SUCCESS; } return rv; } #endif /* #ifdef CONFIG_GPIO_POWER_DOWN */ void gpio_set_level_verbose(enum console_channel channel, enum gpio_signal signal, int value) { ASSERT(signal_is_gpio(signal)); cprints(channel, "Set %s: %d", gpio_get_name(signal), value); gpio_set_level(signal, value); } void gpio_or_ioex_set_level(int signal, int value) { if (IS_ENABLED(CONFIG_IO_EXPANDER) && signal_is_ioex(signal)) ioex_set_level(signal, value); else gpio_set_level(signal, value); } int gpio_or_ioex_get_level(int signal, int *value) { if (IS_ENABLED(CONFIG_IO_EXPANDER) && signal_is_ioex(signal)) return ioex_get_level(signal, value); *value = gpio_get_level(signal); return EC_SUCCESS; } int signal_is_gpio(int signal) { return ((signal >= GPIO_SIGNAL_START) && (signal < GPIO_SIGNAL_START + GPIO_COUNT)); } __attribute__((weak)) void gpio_set_wakepin(enum gpio_signal signal, uint32_t flags) { /* Some chips may need their own implementations */ } /*****************************************************************************/