/* Copyright 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 module for MEC1322 */

#include "common.h"
#include "gpio.h"
#include "hooks.h"
#include "registers.h"
#include "system.h"
#include "task.h"
#include "timer.h"
#include "util.h"

struct gpio_int_mapping {
	int8_t girq_id;
	int8_t port_offset;
};

/* Mapping from GPIO port to GIRQ info */
static const struct gpio_int_mapping int_map[22] = {
	{11, 0}, {11, 0}, {11, 0}, {11, 0},
	{10, 4}, {10, 4}, {10, 4}, {-1, -1},
	{-1, -1}, {-1, -1}, {9, 10}, {9, 10},
	{9, 10}, {9, 10}, {8, 14}, {8, 14},
	{8, 14}, {-1, -1}, {-1, -1}, {-1, -1},
	{20, 20}, {20, 20}
};

void gpio_set_alternate_function(uint32_t port, uint32_t mask,
				enum gpio_alternate_func func)
{
	int i;
	uint32_t val;

	while (mask) {
		i = __builtin_ffs(mask) - 1;
		val = MEC1322_GPIO_CTL(port, i);
		val &= ~(BIT(12) | BIT(13));
		/* mux_control = DEFAULT, indicates GPIO */
		if (func > GPIO_ALT_FUNC_DEFAULT)
			val |= (func & 0x3) << 12;
		MEC1322_GPIO_CTL(port, i) = val;
		mask &= ~BIT(i);
	}
}

test_mockable int gpio_get_level(enum gpio_signal signal)
{
	uint32_t mask = gpio_list[signal].mask;
	int i;
	uint32_t val;

	if (mask == 0)
		return 0;
	i = GPIO_MASK_TO_NUM(mask);
	val = MEC1322_GPIO_CTL(gpio_list[signal].port, i);

	return (val & BIT(24)) ? 1 : 0;
}

void gpio_set_level(enum gpio_signal signal, int value)
{
	uint32_t mask = gpio_list[signal].mask;
	int i;

	if (mask == 0)
		return;
	i = GPIO_MASK_TO_NUM(mask);

	if (value)
		MEC1322_GPIO_CTL(gpio_list[signal].port, i) |= BIT(16);
	else
		MEC1322_GPIO_CTL(gpio_list[signal].port, i) &= ~BIT(16);
}

void gpio_set_flags_by_mask(uint32_t port, uint32_t mask, uint32_t flags)
{
	int i;
	uint32_t val;
	while (mask) {
		i = GPIO_MASK_TO_NUM(mask);
		mask &= ~BIT(i);
		val = MEC1322_GPIO_CTL(port, i);

		/*
		 * Select open drain first, so that we don't glitch the signal
		 * when changing the line to an output.
		 */
		if (flags & GPIO_OPEN_DRAIN)
			val |= BIT(8);
		else
			val &= ~BIT(8);

		if (flags & GPIO_OUTPUT) {
			val |= BIT(9);
			val &= ~BIT(10);
		} else {
			val &= ~BIT(9);
			val |= BIT(10);
		}

		/* Handle pullup / pulldown */
		if (flags & GPIO_PULL_UP)
			val = (val & ~0x3) | 0x1;
		else if (flags & GPIO_PULL_DOWN)
			val = (val & ~0x3) | 0x2;
		else
			val &= ~0x3;

		/* Set up interrupt */
		if (flags & (GPIO_INT_F_RISING | GPIO_INT_F_FALLING))
			val |= BIT(7);
		else
			val &= ~BIT(7);

		val &= ~(0x7 << 4);

		if ((flags & GPIO_INT_F_RISING) && (flags & GPIO_INT_F_FALLING))
			val |= 0x7 << 4;
		else if (flags & GPIO_INT_F_RISING)
			val |= 0x5 << 4;
		else if (flags & GPIO_INT_F_FALLING)
			val |= 0x6 << 4;
		else if (flags & GPIO_INT_F_HIGH)
			val |= 0x1 << 4;
		else if (!(flags & GPIO_INT_F_LOW)) /* No interrupt flag set */
			val |= 0x4 << 4;

		/* Set up level */
		if (flags & GPIO_HIGH)
			val |= BIT(16);
		else if (flags & GPIO_LOW)
			val &= ~BIT(16);

		MEC1322_GPIO_CTL(port, i) = val;
	}
}

int gpio_enable_interrupt(enum gpio_signal signal)
{
	int i, port, girq_id, bit_id;

	if (gpio_list[signal].mask == 0)
		return EC_SUCCESS;

	i = GPIO_MASK_TO_NUM(gpio_list[signal].mask);
	port = gpio_list[signal].port;
	girq_id = int_map[port].girq_id;
	bit_id = (port - int_map[port].port_offset) * 8 + i;

	MEC1322_INT_ENABLE(girq_id) |= BIT(bit_id);
	MEC1322_INT_BLK_EN |= BIT(girq_id);

	return EC_SUCCESS;
}

int gpio_disable_interrupt(enum gpio_signal signal)
{
	int i, port, girq_id, bit_id;

	if (gpio_list[signal].mask == 0)
		return EC_SUCCESS;

	i = GPIO_MASK_TO_NUM(gpio_list[signal].mask);
	port = gpio_list[signal].port;
	girq_id = int_map[port].girq_id;
	bit_id = (port - int_map[port].port_offset) * 8 + i;

	MEC1322_INT_DISABLE(girq_id) = BIT(bit_id);

	return EC_SUCCESS;
}

int gpio_clear_pending_interrupt(enum gpio_signal signal)
{
	int i, port, girq_id, bit_id;

	if (gpio_list[signal].mask == 0)
		return EC_SUCCESS;

	i = GPIO_MASK_TO_NUM(gpio_list[signal].mask);
	port = gpio_list[signal].port;
	girq_id = int_map[port].girq_id;
	bit_id = (port - int_map[port].port_offset) * 8 + i;

	/* Clear interrupt source sticky status bit even if not enabled */
	MEC1322_INT_SOURCE(girq_id) |= 1 << bit_id;

	return EC_SUCCESS;
}

void gpio_pre_init(void)
{
	int i;
	int flags;
	int is_warm = system_is_reboot_warm();
	const struct gpio_info *g = gpio_list;


	for (i = 0; i < GPIO_COUNT; i++, g++) {
		flags = g->flags;

		if (flags & GPIO_DEFAULT)
			continue;

		/*
		 * If this is a warm reboot, don't set the output levels or
		 * we'll shut off the AP.
		 */
		if (is_warm)
			flags &= ~(GPIO_LOW | GPIO_HIGH);

		gpio_set_flags_by_mask(g->port, g->mask, flags);

		/* Use as GPIO, not alternate function */
		gpio_set_alternate_function(g->port, g->mask,
					GPIO_ALT_FUNC_NONE);
	}
}

/* Clear any interrupt flags before enabling GPIO interrupt */
#define ENABLE_GPIO_GIRQ(x) \
	do { \
		MEC1322_INT_SOURCE(x) |= MEC1322_INT_RESULT(x); \
		task_enable_irq(MEC1322_IRQ_GIRQ ## x); \
	} while (0)

static void gpio_init(void)
{
	ENABLE_GPIO_GIRQ(8);
	ENABLE_GPIO_GIRQ(9);
	ENABLE_GPIO_GIRQ(10);
	ENABLE_GPIO_GIRQ(11);
	ENABLE_GPIO_GIRQ(20);
}
DECLARE_HOOK(HOOK_INIT, gpio_init, HOOK_PRIO_DEFAULT);

/*****************************************************************************/
/* Interrupt handlers */


/**
 * Handler for each GIRQ interrupt. This reads and clears the interrupt bits for
 * the GIRQ interrupt, then finds and calls the corresponding GPIO interrupt
 * handlers.
 *
 * @param girq		GIRQ index
 * @param port_offset	GPIO port offset for the given GIRQ
 */
static void gpio_interrupt(int girq, int port_offset)
{
	int i, bit;
	const struct gpio_info *g = gpio_list;
	uint32_t sts = MEC1322_INT_RESULT(girq);

	MEC1322_INT_SOURCE(girq) |= sts;

	for (i = 0; i < GPIO_IH_COUNT && sts; ++i, ++g) {
		bit = (g->port - port_offset) * 8 + __builtin_ffs(g->mask) - 1;
		if (sts & BIT(bit))
			gpio_irq_handlers[i](i);
		sts &= ~BIT(bit);
	}
}

#define GPIO_IRQ_FUNC(irqfunc, girq, port_offset)  \
	void irqfunc(void)                         \
	{                                          \
		gpio_interrupt(girq, port_offset); \
	}

GPIO_IRQ_FUNC(__girq_8_interrupt, 8, 14);
GPIO_IRQ_FUNC(__girq_9_interrupt, 9, 10);
GPIO_IRQ_FUNC(__girq_10_interrupt, 10, 4);
GPIO_IRQ_FUNC(__girq_11_interrupt, 11, 0);
GPIO_IRQ_FUNC(__girq_20_interrupt, 20, 20);

#undef GPIO_IRQ_FUNC

/*
 * Declare IRQs.  Nesting this macro inside the GPIO_IRQ_FUNC macro works
 * poorly because DECLARE_IRQ() stringizes its inputs.
 */
DECLARE_IRQ(MEC1322_IRQ_GIRQ8, __girq_8_interrupt, 1);
DECLARE_IRQ(MEC1322_IRQ_GIRQ9, __girq_9_interrupt, 1);
DECLARE_IRQ(MEC1322_IRQ_GIRQ10, __girq_10_interrupt, 1);
DECLARE_IRQ(MEC1322_IRQ_GIRQ11, __girq_11_interrupt, 1);
DECLARE_IRQ(MEC1322_IRQ_GIRQ20, __girq_20_interrupt, 1);
