/*
 * This file is part of the coreboot project.
 *
 * Copyright (C) 2017 Siemens AG
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; version 2 of the License.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 */
#ifndef FSP_BROADWELL_DE_GPIO_H_
#define FSP_BROADWELL_DE_GPIO_H_

#include <stdint.h>
#include <compiler.h>

/* Chipset owned GPIO configuration registers */
#define GPIO_1_USE_SEL		0x00
#define GPIO_1_IO_SEL		0x04
#define GPIO_1_LVL		0x0c
#define GPIO_1_BLINK		0x18
#define GPIO_1_NMI_EN		0x28
#define GPIO_1_INVERT		0x2c
#define GPIO_2_USE_SEL		0x30
#define GPIO_2_IO_SEL		0x34
#define GPIO_2_LVL		0x38
#define GPIO_2_NMI_EN		0x3c
#define GPIO_3_USE_SEL		0x40
#define GPIO_3_IO_SEL		0x44
#define GPIO_3_LVL		0x48
#define GPIO_3_NMI_EN		0x50
#define REG_INVALID		0xff

/* The pin can either be a GPIO or connected to the native function. */
#define GPIO_MODE_NATIVE	0
#define GPIO_MODE_GPIO		1
/* Once configured as GPIO the pin can be an input or an output. */
#define GPIO_OUTPUT		0
#define GPIO_INPUT		1
#define GPIO_NMI_EN		1
/* For output GPIO mode the pin can either drive high or low level. */
#define GPIO_OUT_LEVEL_LOW	0
#define GPIO_OUT_LEVEL_HIGH	1
/* The following functions are only valid for GPIO bank 1. */
#define GPIO_OUT_BLINK		1
#define GPIO_IN_INVERT		1

#define GPIO_NUM_BANKS		3
#define MAX_GPIO_NUM		75	/* 0 based GPIO number */
#define GPIO_LIST_END		0xff

/* Define possible GPIO configurations. */
#define PCH_GPIO_END \
	{ .use_sel = GPIO_LIST_END }

#define PCH_GPIO_NATIVE(gpio) { \
	 .num = (gpio), \
	 .use_sel = GPIO_MODE_NATIVE }

#define PCH_GPIO_INPUT(gpio) { \
	.num = (gpio), \
	.use_sel = GPIO_MODE_GPIO, \
	.io_sel = GPIO_INPUT }

#define PCH_GPIO_INPUT_INVERT(gpio) { \
	.num = (gpio), \
	.use_sel = GPIO_MODE_GPIO, \
	.io_sel = GPIO_INPUT, \
	.invert_input = GPIO_IN_INVERT }

#define PCH_GPIO_INPUT_NMI(gpio) { \
	.num = (gpio), \
	.use_sel = GPIO_MODE_GPIO, \
	.io_sel = GPIO_INPUT, \
	.nmi_en = GPIO_NMI_EN }

#define PCH_GPIO_OUT_LOW(gpio) { \
	.num = (gpio), \
	.use_sel = GPIO_MODE_GPIO, \
	.io_sel = GPIO_OUTPUT, \
	.level = GPIO_OUT_LEVEL_LOW }

#define PCH_GPIO_OUT_HIGH(gpio) { \
	.num = (gpio), \
	.use_sel = GPIO_MODE_GPIO, \
	.io_sel = GPIO_OUTPUT, \
	.level = GPIO_OUT_LEVEL_HIGH }

#define PCH_GPIO_OUT_BLINK(gpio) { \
	.num = (gpio), \
	.use_sel = GPIO_MODE_GPIO, \
	.io_sel = GPIO_OUTPUT, \
	.blink_en = GPIO_OUT_BLINK }

struct gpio_config {
	uint8_t num;
	uint8_t use_sel;
	uint8_t io_sel;
	uint8_t level;
	uint8_t blink_en;
	uint8_t nmi_en;
	uint8_t invert_input;
} __packed;

/* Unfortunately the register layout is not linear between different GPIO banks.
 * In addition not every bank has all the functions so that some registers might
 * be missing on a particular bank. To make the code better readable introduce a
 * wrapper structure for the register addresses for every bank.
 */
struct gpio_config_regs {
	uint8_t use_sel;
	uint8_t io_sel;
	uint8_t level;
	uint8_t nmi_en;
	uint8_t blink_en;
	uint8_t invert_input;
};

/* Define gpio_t here to be able to use src/include/gpio.h for gpio_set() and
   gpio_get().*/
typedef uint8_t gpio_t;

/* Configure GPIOs with mainboard provided settings */
void init_gpios(const struct gpio_config config[]);

#endif /* FSP_BROADWELL_DE_GPIO_H_ */
