blob: 60d4973d61bbd115aa5b1bfd77d4ec9b812109d2 [file] [log] [blame]
/* 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 "gpio.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 */
uint8_t 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 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 : -1);
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);
}
#ifdef CONFIG_CMD_GPIO_EXTENDED
int gpio_get_flags(enum gpio_signal signal)
{
const struct gpio_info *g = gpio_list + signal;
return gpio_get_flags_by_mask(g->port, g->mask);
}
#endif
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_flags_by_mask(g->port, g->mask, g->flags);
gpio_set_alternate_function(g->port, g->mask, -1);
}
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 */
/*****************************************************************************/