/* SPDX-License-Identifier: GPL-2.0-or-later */

#include "superiotool.h"

#define DEVICE_ID_MSB   0x1f
#define DEVICE_ID_LSB   0x1e
#define DEVICE_SUBID    0x1d
#define DEVICE_REVISION 0x1c

// Always reads 0xfe with recent parts
#define DEVICE_ID_REG_LEGACY 0x20


static const struct {
	uint32_t devid_subdevid;
	const char *name;
} chip_names[] = {
	// MEC152x-Data-Sheet-DS00003427C.pdf, Table 1-1 "MEC152X FEATURE LIST BY PACKAGE"
	{0x0023b4, "MEC1523-144 WFBGA"},
	{0x002334, "MEC1521-144 WFBGA"},
	{0x002374, "MEC1527-144 WFBGA"},
	{0x002333, "MEC1521-128 VTQFP/WFBGA"},
	{0x002373, "MEC1527-128 WFBGA"},

	// MEC142x data sheet (00002343C.pdf), Table 7-3 "DEVICE IDENTIFICATION PER PRODUCT"
	{0x001110, "MEC1424"},
	{0x001310, "MEC1426"},
	{0x001510, "MEC1428"},

	// MEC172x-Data-Sheet-DS00003583D.pdf, Table 1-1 "MEC172X FEATURE LIST"
	{0x002234, "MEC1723N B0-I/SZ"},
	{0x002274, "MEC1727N-B0-I/SZ"},
	{0x002227, "MEC1721N-B0-I/LJ"},
	{0x002237, "MEC1723N-B0-I/LJ"},
	{0x002277, "MEC1727N-B0-I/LJ"},

	{EOT}
};

static const char *lookup_chip_name(uint16_t device_id, uint8_t subdevice_id)
{
	const uint32_t id = (((uint32_t)device_id) << 8) | subdevice_id;
	for (size_t i = 0; chip_names[i].devid_subdevid != EOT; i++) {
		if (chip_names[i].devid_subdevid == id)
			return chip_names[i].name;
	}
	return "UNKNOWN";
}

// .superio_id holds only the device id, which indicates the family (e.g.
// MEC152x or MEC172x or ...)
// We do parse the subdevice id (which indicates the part, e.g. MEC1523 vs
// MEC1521) later. See also `chip_names`.
// We don't list the TEST and Reserved registers, because there are just too many of them.
static const struct superio_registers reg_table[] = {
	{0x0023, "MEC152x", { /* Default I/O Address: 0x2e/0x2f */
		{NOLDN, NULL,
			{0x1c,0x1d,0x1e,0x1f,0x20,EOT},
			{MISC,MISC,0x23,0x00,0xfe,EOT}},
		// refer to "MEC152x System BIOS Porting Guide" AN3622-Application-Note-DS00003622A.pdf
		// Table 5-1 "HOST LOGICAL DEVICES ON MEC152X ESPI"
		// or to MEC152x-Data-Sheet-DS00003427C.pdf, Table 9-6
		// (note that the BIOS Porting Guide seems to be wrong about LDN 2f / 32 byte Test Block)
		{0x0d, "eSPI I/O Component",
			{0x30,
			                 0x34,0x36,0x37, 0x38,0x3a,0x3b, 0x3c,0x3e,0x3f, // ldn  _  d  e  0
			 0x40,0x42,0x43, 0x44,0x46,0x47, 0x48,0x4a,0x4b, 0x4c,0x4e,0x4f, // ldn  1  2  3  4
			 0x50,0x52,0x53,                 0x58,0x5a,0x5b, 0x5c,0x5e,0x5f, // ldn  5  _  7  8
			 0x60,0x62,0x63, 0x64,0x66,0x67, 0x68,0x6a,0x6b, 0x6c,0x6e,0x6f, // ldn  9  a 10 11
			                 0x74,0x76,0x77, 0x78,0x7a,0x7b, 0x7c,0x7e,0x7f, // ldn  _ 20 21 14
			                 0x84,0x86,0x87, 0x88,0x8a,0x8b,                 // ldn  _ 2f  b  _
			 0xac,0xad,0xae,0xaf,0xb0,0xb1,0xb2,0xb3,0xb4,0xb5,
			 0xb6,0xb7,0xb8,0xb9,0xba,0xbb,0xbc,0xbd,0xbe,0xbf,
			 EOT},
			{0x00,
			                 0x00,0x2e,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00,
			 0x00,0x60,0x00, 0x00,0x62,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00,
			 0x00,0x00,0x00,                 0x00,0x00,0x00, 0x00,0x92,0x00,
			 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00,
			                 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00,
			                 0x00,0x00,0x00, 0x00,0x00,0x00,
			 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,RSVD,0xff,
			 0xff,0xff,0xff,0xff,0xff,RSVD,RSVD,0xff,0xff,0xff,
			 EOT}},
		{0x0e, "eSPI Memory Component", {0x30,EOT}, {0x00,EOT}},
		{0x00, "Mailbox", {0x30,EOT}, {0x00,EOT}},
		{0x01, "8042 Emulated Keyboard Controller", {0x30,EOT}, {0x00,EOT}},
		{0x02, "ACPI EC0", {0x30,EOT}, {0x00,EOT}},
		{0x03, "ACPI EC1", {0x30,EOT}, {0x00,EOT}},
		{0x04, "ACPI EC2", {0x30,EOT}, {0x00,EOT}},
		{0x05, "ACPI EC3", {0x30,EOT}, {0x00,EOT}},
		{0x07, "ACPI PM1", {0x30,EOT}, {0x00,EOT}},
		{0x08, "Port 92-Legacy (Fast Keyboard)", {0x30,EOT}, {0x00,EOT}},
		{0x09, "UART 0", {0x30,0xf0,EOT}, {0x00,0x02,EOT}}, // c.f. Section 16 (UART)
		{0x0a, "UART 1", {0x30,0xf0,EOT}, {0x00,0x02,EOT}}, // subsection 16.11
		{0x0b, "UART 2", {0x30,0xf0,EOT}, {0x00,0x02,EOT}}, // ("Configuration Registers")
		{0x10, "EMI 0", {0x30,EOT}, {0x00,EOT}},
		{0x11, "EMI 1", {0x30,EOT}, {0x00,EOT}},
		{0x14, "Real Time Clock", {0x30,EOT}, {0x00,EOT}},
		{0x20, "Port 80 BIOS Debug Port 0", {0x30,EOT}, {0x00,EOT}},
		{0x21, "Port 80 BIOS Debug Port 1", {0x30,EOT}, {0x00,EOT}},
		{0x2f, "32 Byte Test Block", {0x30,EOT}, {0x00,EOT}},
		{EOT}}},

	{0x0022, "MEC172x", {
		{NOLDN, NULL,
			{0x1c,0x1d,0x1e,0x1f,0x20,0x24,0x25,0x26,0x27,EOT},
			{MISC,MISC,0x22,0x00,0xfe,MISC,MISC,MISC,MISC,EOT}},
		// refer to "MEC172x System BIOS Porting Guide" AN3642-Application-Note-DS00003642A.pdf
		// Table 5-1 "HOST LOGICAL DEVICES ON MEC172X ESPI"
		// or to MEC152x-Data-Sheet-DS00003427C.pdf, Table 9-6
		// (note that the BIOS Porting Guide seems to be wrong about LDN 2f / 32 byte Test Block)
		{0x0d, "eSPI I/O Component",
			{0x30,
			                 0x34,0x36,0x37, 0x38,0x3a,0x3b, 0x3c,0x3e,0x3f, // ldn  _  d  e  0
			 0x40,0x42,0x43, 0x44,0x46,0x47, 0x48,0x4a,0x4b, 0x4c,0x4e,0x4f, // ldn  1  2  3  4
			 0x50,0x52,0x53, 0x54,0x56,0x57, 0x58,0x5a,0x5b, 0x5c,0x5e,0x5f, // ldn  5  6  7  8
			 0x60,0x62,0x63, 0x64,0x66,0x67, 0x68,0x6a,0x6b, 0x6c,0x6e,0x6f, // ldn  9  a 10 11
			 0x70,0x72,0x73, 0x74,0x76,0x77, 0x78,0x7a,0x7b, 0x7c,0x7e,0x7f, // ldn 12 20 21 14
			                                 0x88,0x8a,0x8b, 0x8c,0x8e,0x8f, // ldn  _ 2f  b  _
			 0xac,0xad,0xae,0xaf,0xb0,0xb1,0xb2,0xb3,0xb4,0xb5,
			 0xb6,0xb7,0xb8,0xb9,0xba,0xbb,0xbc,0xbd,0xbe,
			 EOT},
			{0x00,
			                 0x00,0x2e,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00,
			 0x00,0x60,0x00, 0x00,0x62,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00,
			 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x92,0x00,
			 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00,
			 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00,
			                                 0x00,0x00,0x00, 0x00,0x00,0x00,
			 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
			 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
			 EOT}},
		{0x0e, "eSPI Memory Component", {0x30,EOT}, {0x00,EOT}},
		{0x00, "Mailbox", {0x30,EOT}, {0x00,EOT}},
		{0x01, "8042 Emulated Keyboard Controller", {0x30,EOT}, {0x00,EOT}},
		{0x02, "ACPI EC0", {0x30,EOT}, {0x00,EOT}},
		{0x03, "ACPI EC1", {0x30,EOT}, {0x00,EOT}},
		{0x04, "ACPI EC2", {0x30,EOT}, {0x00,EOT}},
		{0x05, "ACPI EC3", {0x30,EOT}, {0x00,EOT}},
		{0x06, "ACPI EC4", {0x30,EOT}, {0x00,EOT}},
		{0x07, "ACPI PM1", {0x30,EOT}, {0x00,EOT}},
		{0x08, "Port 92-Legacy (Fast Keyboard)", {0x30,EOT}, {0x00,EOT}},
		{0x09, "UART 0", {0x30,0xf0,EOT}, {0x00,0x02,EOT}}, // c.f. Section 18 (UART)
		{0x0a, "UART 1", {0x30,0xf0,EOT}, {0x00,0x02,EOT}}, // 18.11 ("Configuration Registers")
		{0x10, "EMI 0", {0x30,EOT}, {0x00,EOT}},
		{0x11, "EMI 1", {0x30,EOT}, {0x00,EOT}},
		{0x12, "EMI 2", {0x30,EOT}, {0x00,EOT}},
		{0x14, "Real Time Clock", {0x30,EOT}, {0x00,EOT}},
		{0x20, "Port 80 BIOS Debug Port 0", {0x30,EOT}, {0x00,EOT}},
		{0x21, "Port 80 BIOS Debug Port 1", {0x30,EOT}, {0x00,EOT}},
		{0x2f, "32 Byte Test Block", {0x30,EOT}, {0x00,EOT}},
		{0x0f, "glue", {0x30,EOT}, {0x00,EOT}},
		{EOT}}},

	/* Note: There's some guesswork going on for MEC142x based on MEC152x and MEC172x.
	   This is only a "better-than-nothing" entry! */
	{0x0011, "MEC142x (missing registers!)", { /* Note: 0x0011, 0x0013 and 0x0015 all belong to this family. */
		{NOLDN, NULL,
			{0x1c,0x1d,0x1e,0x1f,0x20,EOT},
			{MISC,MISC,0x11,0x00,0xfe,EOT}},
		// refer to MEC142x data sheet 00002343C.pdf Table 7-4
		// and Table 4-14 "I/O BASE ADDRESS REGISTERS"
		{0x10, "eSPI I/O Component",
			{0x30,
			                 0x34,0x36,0x37, 0x38,0x3a,0x3b, 0x3c,0x3e,0x3f, // ldn  _ 10 12  9
			 0x40,0x42,0x43, 0x44,0x46,0x47, 0x48,0x4a,0x4b, 0x4c,0x4e,0x4f, // ldn  1  3  4  a
			 0x50,0x52,0x53, 0x54,0x56,0x57, 0x58,0x5a,0x5b, 0x5c,0x5e,0x5f, // ldn  b  5  7  6
			 0x60,0x62,0x63, 0x64,0x66,0x67, 0x68,0x6a,0x6b, 0x6c,0x6e,0x6f, // ldn  0 15 16 14
			 EOT},
			{0x00,
			                 0x00,0x2e,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00,
			 0x00,0x60,0x00, 0x00,0x62,0x00, 0x00,0x66,0x00, 0x00,0x00,0x00,
			 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x92,0x00,
			 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00,
			 EOT}},
		{0x12, "eSPI Memory Component", {0x30,EOT}, {0x00,EOT}},
		{0x0c, "LPC Interface", {0x30,EOT}, {0x00,EOT}},
		{0x09, "Mailbox", {0x30,EOT}, {0x00,EOT}},
		{0x01, "8042 Emulated Keyboard Controller", {0x30,EOT}, {0x00,EOT}},
		{0x03, "ACPI EC0", {0x30,EOT}, {0x00,EOT}},
		{0x04, "ACPI EC1", {0x30,EOT}, {0x00,EOT}},
		{0x0a, "ACPI EC2", {0x30,EOT}, {0x00,EOT}},
		{0x0b, "ACPI EC3", {0x30,EOT}, {0x00,EOT}},
		{0x05, "ACPI PM1", {0x30,EOT}, {0x00,EOT}},
		{0x07, "UART 0", {0x30,0xf0,EOT}, {0x00,0x02,EOT}},
		{0x06, "Port 92-Legacy (Fast Keyboard)", {0x30,EOT}, {0x00,EOT}},
		{0x00, "EMI 0", {0x30,EOT}, {0x00,EOT}},
		{0x15, "Port 80 BIOS Debug Port 0", {0x30,EOT}, {0x00,EOT}},
		{0x16, "Port 80 BIOS Debug Port 1", {0x30,EOT}, {0x00,EOT}},
		{0x14, "32 Byte Test Block", {0x30,EOT}, {0x00,EOT}},
		{EOT}}},
	{EOT}
};

static void enter_conf_mode_microchip(uint16_t port)
{
	OUTB(0x55, port);
}

static void exit_conf_mode_microchip(uint16_t port)
{
	OUTB(0xaa, port);
}

void probe_idregs_microchip(uint16_t port)
{
	probing_for("Microchip", "", port);

	enter_conf_mode_microchip(port);

	uint8_t legacy_id = regval(port, DEVICE_ID_REG_LEGACY);
	if (legacy_id != 0xfe) {
		if (verbose)
			printf(NOTFOUND "legacy_id=0x%02x, expected 0xfe\n", legacy_id);
		exit_conf_mode_microchip(port);
		return;
	}

	uint16_t id = (regval(port, DEVICE_ID_MSB) << 8) | regval(port, DEVICE_ID_LSB);
	uint8_t subid = regval(port, DEVICE_SUBID);
	uint8_t revision = regval(port, DEVICE_REVISION);

	uint16_t id_for_reg_table = id;
	uint32_t id_subid = (id << 8) | subid;
	// MEC142x parts use different numbers despite same family.
	if (id_subid == 0x001310 || id_subid == 0x001510)
		id_for_reg_table = 0x0011; // cheat a bit, they're compatible

	if (superio_unknown(reg_table, id_for_reg_table)) {
		if (verbose)
			printf(NOTFOUND "id=0x%04x, subid=0x%02x, rev=0x%02x\n", id, subid, revision);
		exit_conf_mode_microchip(port);
		return;
	}

	printf("Found %s %s (id=0x%04x, subid=0x%02x, rev=0x%02x) at 0x%x\n",
	       get_superio_name(reg_table, id_for_reg_table), lookup_chip_name(id, subid),
	       id, subid, revision, port);
	chip_found = 1;

	dump_superio("Microchip", reg_table, port, id_for_reg_table, LDN_SEL);

	exit_conf_mode_microchip(port);
}

void print_microchip_chips(void)
{
	print_vendor_chips("Microchip", reg_table);
}
