/* Copyright 2017 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.
 */

#include "console.h"
#include "gpio.h"
#include "hooks.h"
#include "physical_presence.h"
#include "rbox.h"
#include "registers.h"
#include "system.h"
#include "system_chip.h"
#include "task.h"
#include "timer.h"

#define CPRINTS(format, args...) cprints(CC_RBOX, format, ## args)
#define CPRINTF(format, args...) cprintf(CC_RBOX, format, ## args)

/**
 * Enable/disable power button interrupt.
 *
 * @param enable	Enable (!=0) or disable (==0)
 */
static void power_button_enable_interrupt(int enable)
{
	if (enable) {
		/* Clear any leftover power button interrupts */
		GWRITE_FIELD(RBOX, INT_STATE, INTR_PWRB_IN_FED, 1);

		/* Enable power button interrupt */
		GWRITE_FIELD(RBOX, INT_ENABLE, INTR_PWRB_IN_FED, 1);
		task_enable_irq(GC_IRQNUM_RBOX0_INTR_PWRB_IN_FED_INT);
	} else {
		GWRITE_FIELD(RBOX, INT_ENABLE, INTR_PWRB_IN_FED, 0);
		task_disable_irq(GC_IRQNUM_RBOX0_INTR_PWRB_IN_FED_INT);
	}
}

static void power_button_handler(void)
{
	CPRINTS("power button pressed");

	if (physical_detect_press() != EC_SUCCESS) {
		/* Not consumed by physical detect */
#ifdef CONFIG_U2F
		/* Track last power button press for U2F */
		power_button_record();
#endif
	}

	GWRITE_FIELD(RBOX, INT_STATE, INTR_PWRB_IN_FED, 1);
}
DECLARE_IRQ(GC_IRQNUM_RBOX0_INTR_PWRB_IN_FED_INT, power_button_handler, 1);

#ifdef CONFIG_U2F
static void power_button_init(void)
{
	/*
	 * Enable power button interrupts all the time for U2F.
	 *
	 * Ideally U2F should only enable physical presence after the start of
	 * a U2F request (using atomic operations for the PP enable mask so it
	 * plays nicely with CCD config), but that doesn't happen yet.
	 */
	power_button_enable_interrupt(1);
}
DECLARE_HOOK(HOOK_INIT, power_button_init, HOOK_PRIO_DEFAULT);
#endif  /* CONFIG_U2F */

void board_physical_presence_enable(int enable)
{
#ifndef CONFIG_U2F
	/* Enable/disable power button interrupts */
	power_button_enable_interrupt(enable);
#endif

	/* Stay awake while we're doing this, just in case. */
	if (enable)
		disable_sleep(SLEEP_MASK_PHYSICAL_PRESENCE);
	else
		enable_sleep(SLEEP_MASK_PHYSICAL_PRESENCE);
}

static int command_powerbtn(int argc, char **argv)
{
	char *e;
	int ms = 200;

	if (argc > 1) {
		if (!strcasecmp("pulse", argv[1])) {
			if (argc == 3) {
				ms = strtoi(argv[2], &e, 0);
				if (*e)
					return EC_ERROR_PARAM2;
			}

			ccprintf("Force %dms power button press\n", ms);

			rbox_powerbtn_press();
			msleep(ms);
			rbox_powerbtn_release();
		} else if (!strcasecmp("press", argv[1])) {
			rbox_powerbtn_press();
		} else if (!strcasecmp("release", argv[1])) {
			rbox_powerbtn_release();
		} else
			return EC_ERROR_PARAM1;
	}

	ccprintf("powerbtn: %s\n",
		 rbox_powerbtn_override_is_enabled() ? "forced press" :
		 rbox_powerbtn_is_pressed() ? "pressed\n" : "released\n");
	return EC_SUCCESS;
}
DECLARE_CONSOLE_COMMAND(powerbtn, command_powerbtn,
			"[pulse [ms] | press | release]",
			"get/set the state of the power button");
