/* SPDX-License-Identifier: GPL-2.0-only */

#include <bootblock_common.h>
#include <arch/io.h>
#include <baseboard/variants.h>
#include <device/pci_ops.h>
#include <device/pnp_ops.h>
#include <intelpch/espi.h>
#include <soc/pci_devs.h>
#include <superio/ite/common/ite.h>
#include <superio/ite/it8613e/it8613e.h>

#define it8613e_index		(0x2e)
#define it8613e_data		(0x2f)
#define it8613e_ecdata_base	(0xa40)

#define EC_DEV		PNP_DEV(it8613e_index, IT8613E_EC)
#define UART_DEV	PNP_DEV(it8613e_index, IT8613E_SP1)

/*
 * IT8613E/LX Super I/O Chip Initialization Settings
 */
struct initdata {
	u16 reg;
	u8  OpAnd;
	u8  OpOr;
};

static const struct initdata init_values[] = {
	/* Entry ITE SIO configuration */
	{ it8613e_index, 0x00, 0x87 },
	{ it8613e_index, 0x00, 0x01 },
	{ it8613e_index, 0x00, 0x55 },
	{ it8613e_index, 0x00, 0x55 },
	/* Start IT8613E/LX config */
	{ it8613e_index, 0x00, 0x23 },
	{ it8613e_data,  0x1f, 0x40 },
	/* LDN: 04, EC */
	{ it8613e_index, 0x00, 0x07 },
	{ it8613e_data,  0x00, 0x04 },
	{ it8613e_index, 0x00, 0xf0 },
	{ it8613e_data,  0x00, 0x00 },
	{ it8613e_index, 0x00, 0xf1 },
	{ it8613e_data,  0x00, 0xbf },
	{ it8613e_index, 0x00, 0xf2 },
	{ it8613e_data,  0x00, 0x00 },
	{ it8613e_index, 0x00, 0xf3 },
	{ it8613e_data,  0x00, 0x00 },
	{ it8613e_index, 0x00, 0xf4 },
	{ it8613e_data,  0x00, 0x60 },
	{ it8613e_index, 0x00, 0xf5 },
	{ it8613e_data,  0x3f, 0x00 },
	{ it8613e_index, 0x00, 0xfa },
	{ it8613e_data,  0x00, 0x00 },
	{ it8613e_index, 0x00, 0xfb },
	{ it8613e_data,  0xf3, 0x0c },
	/* LDN: 03, Unknown Device */
	{ it8613e_index, 0x00, 0x07 },
	{ it8613e_data,  0x00, 0x03 },
	{ it8613e_index, 0x00, 0xf0 },
	{ it8613e_data,  0xf7, 0x08 },
	{ it8613e_index, 0x00, 0x07 },
	{ it8613e_data,  0x00, 0x07 },
	{ it8613e_index, 0x00, 0x28 },
	{ it8613e_data,  0xbf, 0x00 },
	{ it8613e_index, 0x00, 0x72 },
	{ it8613e_data,  0xff, 0x00 },
	{ it8613e_index, 0x00, 0x2c },
	{ it8613e_data,  0xbf, 0x40 },
	/* LDN: 04, EC */
	{ it8613e_index, 0x00, 0x07 },
	{ it8613e_data,  0x00, 0x04 },
	/* Set IO1 to 0xa40 */
	{ it8613e_index, 0x00, 0x61 },
	{ it8613e_data,  0x00, 0x40 },
	{ it8613e_index, 0x00, 0x60 },
	{ it8613e_data,  0x00, 0x0a },
	/* Set IO2 to 0xa30 */
	{ it8613e_index, 0x00, 0x62 },
	{ it8613e_data,  0x00, 0x0a },
	{ it8613e_index, 0x00, 0x63 },
	{ it8613e_data,  0x00, 0x30 },
	/* Enable the EC Device */
	{ it8613e_index, 0x00, 0x30 },
	{ it8613e_data,  0x00, 0x01 },
	/* for Environment Controller */
	{ 0x0a45, 0x00, 0x50 },
	{ 0x0a46, 0x00, 0xff },
	{ 0x0a45, 0x00, 0x51 },
	{ 0x0a46, 0x00, 0x38 },
	{ 0x0a45, 0x00, 0x00 },
	{ 0x0a46, 0x00, 0x01 },
	{ 0x0a45, 0x00, 0x0a },
	{ 0x0a46, 0x00, 0x64 },
	{ 0x0a45, 0x00, 0x8e },
	{ 0x0a46, 0x3f, 0xc0 },
	{ 0x0a45, 0x00, 0x00 },
	{ 0x0a46, 0x00, 0x40 },
	/* */
	{ it8613e_index, 0x00, 0x23 },
	{ it8613e_data,  0xfe, 0x00 },
	/* LDN: 07, GPIO */
	{ it8613e_index, 0x00, 0x07 },
	{ it8613e_data,  0x00, 0x07 },
	{ it8613e_index, 0x00, 0x25 },
	{ it8613e_data,  0x08, 0x00 },
	{ it8613e_index, 0x00, 0x26 },
	{ it8613e_data,  0x00, 0x00 },
	{ it8613e_index, 0x00, 0x27 },
	{ it8613e_data,  0x00, 0x00 },
	{ it8613e_index, 0x00, 0x28 },
	{ it8613e_data,  0x40, 0x00 },
	{ it8613e_index, 0x00, 0x29 },
	{ it8613e_data,  0x00, 0x00 },
	{ it8613e_index, 0x00, 0x71 },
	{ it8613e_data,  0x00, 0x00 },
	{ it8613e_index, 0x00, 0x72 },
	{ it8613e_data,  0x00, 0x90 },
	{ it8613e_index, 0x00, 0x73 },
	{ it8613e_data,  0x00, 0x00 },
	{ it8613e_index, 0x00, 0x2a },
	{ it8613e_data,  0xdf, 0x00 },
	{ it8613e_index, 0x00, 0x2b },
	{ it8613e_data,  0x0f, 0x00 },
	{ it8613e_index, 0x00, 0x2c },
	{ it8613e_data,  0x62, 0x00 },
	/* LDN: 05, Keyboard */
	{ it8613e_index, 0x00, 0x07 },
	{ it8613e_data,  0x00, 0x05 },
	/*  Disable the Device */
	{ it8613e_index, 0x00, 0x30 },
	{ it8613e_data,  0x00, 0x00 },
	/* LDN: 06, Mouse */
	{ it8613e_index, 0x00, 0x07 },
	{ it8613e_data,  0x00, 0x06 },
	/*  Disable the Device */
	{ it8613e_index, 0x00, 0x30 },
	{ it8613e_data,  0x00, 0x00 },
	/* LDN: 05, Keyboard */
	{ it8613e_index, 0x00, 0x07 },
	{ it8613e_data,  0x00, 0x05 },
	{ it8613e_index, 0x00, 0xf0 },
	{ it8613e_data,  0xf7, 0x08 },
	/* LDN: 04, EC */
	{ it8613e_index, 0x00, 0x07 },
	{ it8613e_data,  0x00, 0x04 },
	{ it8613e_index, 0x00, 0xf4 },
	{ it8613e_data,  0x7f, 0x80 },
	{ it8613e_index, 0x00, 0x70 },
	{ it8613e_data,  0x00, 0x00 },
	/* exit ITE config */
	{ it8613e_index, 0x00, 0x02 },
	{ it8613e_data,  0x00, 0x02 },
};

static void sio_init(const struct initdata *table, const size_t count)
{
	u8 val;
	for (size_t i = 0; i < count; i++) {
		if (table[i].OpAnd != 0) {
			val = inb(table->reg) & table[i].OpAnd;
			val |= table[i].OpOr;
		} else {
			val = table[i].OpOr;
		}

		outb(val, table[i].reg);
	}
}

static const struct initdata it8613e_initdata[] = {
	{ 0x0007, 0x00, IT8613E_GPIO },	/* LDN GPIO */
	{ 0x0071, 0xf7, 0x00 },		/* ESPI_CLOCK */
	{ 0x0023, 0xf6, 0x00 },
	{ 0x002d, 0xfb, (1 << 1) },
	{ 0x0007, 0x00, IT8613E_CIR },
	{ 0x0030, 0x00, 0x00 },
	{ 0x0060, 0x00, 0x00 },
	{ 0x0061, 0x00, 0x00 },
	{ 0x0070, 0x00, 0x00 },
	{ 0x0007, 0x00, IT8613E_EC },	/* LDN EC */
	{ 0x00f0, 0x00, 0x00 },
	{ 0x00f1, 0x00, 0xbf },
	{ 0x00f2, 0x00, 0x00 },
	{ 0x00f3, 0x00, 0x00 },
	{ 0x00f4, 0x9f, 0x60 },
	{ 0x00f5, 0x3f, 0x00 },
	{ 0x00fa, 0x00, 0x00 },
	{ 0x00fb, 0xf3, 0x0c },
	{ 0x0007, 0x00, IT8613E_GPIO },	/* LDN GPIO */
	{ 0x0026, 0x00, 0x00 },
	{ 0x0029, 0x00, 0x00 },
	{ 0x002a, 0x7f, 0x80 },
	{ 0x002b, 0xbf, 0x00 },
};

static const struct initdata it8613e_ecdata[] = {
	{ 0x0050, 0x00, 0xff },
	{ 0x0051, 0x00, 0x15 },
	{ 0x000a, 0x8f, 0x68 },
	{ 0x000b, 0x00, 0xc9 },
	{ 0x000c, 0xf8, 0x07 },
	{ 0x0013, 0x07, 0x70 },
	{ 0x0014, 0x7f, 0xc0 },
	{ 0x005c, 0x7f, 0x80 },
	{ 0x0056, 0x00, 0x68 },
	{ 0x0057, 0x00, 0x05 },
	{ 0x0059, 0x00, 0x00 },
	{ 0x005c, 0x7f, 0x00 },
	{ 0x0065, 0x60, 0x04 },
	{ 0x006d, 0x60, 0x04 },
	{ 0x0075, 0x60, 0x04 },
	{ 0x0089, 0x00, 0x30 },
	{ 0x008a, 0x00, 0x01 },
	{ 0x008b, 0x00, 0x02 },
	{ 0x008c, 0x00, 0x01 },
	{ 0x008e, 0x00, 0x20 },
	{ 0x0006, 0x00, 0x40 },
	{ 0x001d, 0x00, 0x14 },
	{ 0x001e, 0x00, 0x04 },
	{ 0x0006, 0x00, 0x00 },
	{ 0x0055, 0x7f, 0x80 },
	{ 0x0000, 0xbe, 0x41 },
	{ 0x0000, 0x00, 0x00 },
	{ 0x0000, 0x00, 0x00 },
};

static void it8613e_init(const u16 p_idx, const u16 p_dat, const struct initdata *table, const size_t count)
{
	u8 val;

	for (size_t i = 0; i < count; i++) {
		outb(table[i].reg, p_idx);

		if (table[i].OpAnd == 0) {
			val = table[i].OpOr;
		} else {
			val = ((inb(p_dat) & table[i].OpAnd) |
				table[i].OpOr);
		}

		outb(val, p_dat);
	}
}

void bootblock_mainboard_early_init(void)
{
	/* fixed io decode of espi */
	pci_write_config32(PCH_DEV_ESPI, ESPI_IO_DEC, 0x3c030070);
	variant_configure_early_gpio_pads();

	/* initial IT8613e/LX from table */
	sio_init(init_values, ARRAY_SIZE(init_values));

	pnp_enter_conf_state(EC_DEV);
	it8613e_init(it8613e_index, it8613e_data,
		it8613e_initdata, ARRAY_SIZE(it8613e_initdata));
	pnp_exit_conf_state(EC_DEV);

	/* Environment Controller */
	it8613e_init(it8613e_ecdata_base + 5, it8613e_ecdata_base + 6,
		it8613e_ecdata, ARRAY_SIZE(it8613e_ecdata));

	/* 5VSB_CTRL# disable */
	ite_reg_write(EC_DEV, 0xfa, 0);
	ite_disable_pme_out(EC_DEV);
	ite_ac_resume_southbridge(EC_DEV);

	ite_enable_serial(UART_DEV, CONFIG_TTYS0_BASE);
}