blob: 1f1f226848ad0ff09109e766681cee398dc0fa0f [file] [log] [blame]
/*
* Copyright (c) 2011 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.
*
* Alternatively, this software may be distributed under the terms of the
* GNU General Public License ("GPL") version 2 as published by the Free
* Software Foundation.
*/
/* Implementation of vboot flag accessor from GPIO hardware */
#include <common.h>
#include <dm.h>
#include <errno.h>
#include <fdtdec.h>
#include <asm-generic/gpio.h>
#include <asm/global_data.h>
#include <asm/gpio.h>
#include <cros/common.h>
#include <cros/vboot_flag.h>
DECLARE_GLOBAL_DATA_PTR;
/* Default empty implementation */
static int __vboot_flag_setup_gpio_arch(enum vboot_flag_id id,
struct vboot_flag_context *context)
{
return 0;
}
int vboot_flag_setup_gpio_arch(enum vboot_flag_id id,
struct vboot_flag_context *context)
__attribute__((weak, alias("__vboot_flag_setup_gpio_arch")));
static int vboot_flag_setup_gpio(enum vboot_flag_id id,
struct vboot_flag_context *context)
{
const void *blob = gd->fdt_blob;
unsigned long delay_time;
int ret;
ret = gpio_request_by_name_nodev(blob, context->node, "gpio", 0,
&context->gpio_state, GPIOD_IS_IN);
if (ret && ret != ENOENT && ret != -EBUSY) {
VBDEBUG("failed to decode GPIO state: %s\n",
vboot_flag_node_name(id));
return -1;
}
if (!ret) {
if (vboot_flag_setup_gpio_arch(id, context)) {
VBDEBUG("arch specific setup failed: %s\n",
vboot_flag_node_name(id));
return -1;
}
}
/*
* In theory we have to insert a delay here for charging the input
* gate capacitance. Consider a 200K ohms series resister and 10
* picofarads gate capacitance.
*
* RC time constant is
* 200 K ohms * 10 picofarads = 2 microseconds
*
* Then 10-90% rise time is
* 2 microseconds * 2.2 = 4.4 microseconds
*
* Thus, 10 microseconds gives us a 50% margin.
*/
delay_time = fdtdec_get_int(blob, context->config_node,
"cros-gpio-input-charging-delay", 0);
if (delay_time)
context->gpio_valid_time = timer_get_us() + delay_time;
#ifdef CONFIG_SANDBOX
int value;
struct udevice *dev = context->gpio_state.dev;
value = fdtdec_get_int(blob, context->node, "sandbox-value", -1);
if (value != -1)
sandbox_gpio_set_value(dev, context->gpio_state.offset, value);
printf("Sandbox gpio %s/%d = %d\n", dev->name,
context->gpio_state.offset, value);
#endif
context->initialized = 1;
return 0;
}
static int vboot_flag_fetch_gpio(enum vboot_flag_id id,
struct vboot_flag_context *context,
struct vboot_flag_details *details)
{
int p, valid_time;
if (!context->initialized) {
VBDEBUG("gpio state is not initialized\n");
return -1;
}
details->port = gpio_get_number(&context->gpio_state);
details->active_high = (context->gpio_state.flags &
GPIOD_ACTIVE_LOW) ? 0 : 1;
p = details->active_high ? 0 : 1;
valid_time = context->gpio_valid_time;
if (valid_time) {
/* We can only read GPIO after valid_time */
while (timer_get_us() < valid_time)
udelay(10);
}
details->value = p ^ gpio_get_value(details->port);
return 0;
}
CROS_VBOOT_FLAG_DRIVER(gpio) = {
.name = "gpio",
.compat = COMPAT_GOOGLE_GPIO_FLAG,
.setup = vboot_flag_setup_gpio,
.fetch = vboot_flag_fetch_gpio,
};