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

#include <arch/ioapic.h>
#include <arch/smp/mpspec.h>
#include <string.h>
#include <southbridge/amd/common/amd_pci_util.h>
#include <southbridge/amd/agesa/hudson/hudson.h>

static void smp_add_mpc_entry(struct mp_config_table *mc, unsigned int length)
{
	mc->mpc_length += length;
	mc->mpc_entry_count++;
}

static void my_smp_write_bus(struct mp_config_table *mc,
			     unsigned char id, const char *bustype)
{
	struct mpc_config_bus *mpc;
	mpc = smp_next_mpc_entry(mc);
	memset(mpc, '\0', sizeof(*mpc));
	mpc->mpc_type = MP_BUS;
	mpc->mpc_busid = id;
	memcpy(mpc->mpc_bustype, bustype, sizeof(mpc->mpc_bustype));
	smp_add_mpc_entry(mc, sizeof(*mpc));
}

static void *smp_write_config_table(void *v)
{
	struct mp_config_table *mc;
	int bus_isa;

	mc = (void *)(((char *)v) + SMP_FLOATING_TABLE_LEN);

	mptable_init(mc);
	memcpy(mc->mpc_oem, "AMD     ", 8);

	smp_write_processors(mc);

	//mptable_write_buses(mc, NULL, &bus_isa);
	my_smp_write_bus(mc, 0, "PCI   ");
	my_smp_write_bus(mc, 1, "PCI   ");
	bus_isa = 0x02;
	my_smp_write_bus(mc, bus_isa, "ISA   ");

	/*
	 * By the time this function gets called, the IOAPIC registers
	 * have been written so they can be read to get the correct
	 * APIC ID and Version
	 */
	u8 ioapic_id = smp_write_ioapic_from_hw(mc, VIO_APIC_VADDR);

	/* I/O Ints:    Type    Polarity    Trigger     Bus ID   IRQ    APIC ID PIN# */
#define IO_LOCAL_INT(type, intr, apicid, pin)				\
	smp_write_lintsrc(mc, (type), MP_IRQ_TRIGGER_EDGE | MP_IRQ_POLARITY_HIGH,\
	bus_isa, (intr), (apicid), (pin))
	mptable_add_isa_interrupts(mc, bus_isa, ioapic_id, 0);

	/* PCI interrupts are level triggered, and are
	 * associated with a specific bus/device/function tuple.
	 */
#define PCI_INT(bus, dev, int_sign, pin)				\
	smp_write_intsrc(mc, mp_INT, MP_IRQ_TRIGGER_LEVEL|MP_IRQ_POLARITY_LOW,\
	(bus), (((dev)<<2)|(int_sign)), ioapic_id, (pin))

	/* IOMMU */
	PCI_INT(0x0, 0x0, 0x0, 0x10);
	PCI_INT(0x0, 0x0, 0x1, 0x11);
	PCI_INT(0x0, 0x0, 0x2, 0x12);
	PCI_INT(0x0, 0x0, 0x3, 0x13);

	/* Internal VGA */
	PCI_INT(0x0, 0x01, 0x0, intr_data_ptr[0x02]);
	PCI_INT(0x0, 0x01, 0x1, intr_data_ptr[0x03]);

	/* SMBUS */
	PCI_INT(0x0, 0x14, 0x0, 0x10);

	/* HD Audio */
	PCI_INT(0x0, 0x14, 0x0, intr_data_ptr[0x13]);

	/* USB */
	PCI_INT(0x0, 0x12, 0x0, intr_data_ptr[0x30]);
	PCI_INT(0x0, 0x12, 0x1, intr_data_ptr[0x31]);
	PCI_INT(0x0, 0x13, 0x0, intr_data_ptr[0x32]);
	PCI_INT(0x0, 0x13, 0x1, intr_data_ptr[0x33]);
	PCI_INT(0x0, 0x16, 0x0, intr_data_ptr[0x34]);
	PCI_INT(0x0, 0x16, 0x1, intr_data_ptr[0x35]);
	PCI_INT(0x0, 0x14, 0x2, intr_data_ptr[0x36]);

	/* sata */
	PCI_INT(0x0, 0x11, 0x0, intr_data_ptr[0x40]);
	PCI_INT(0x0, 0x11, 0x0, intr_data_ptr[0x41]);

	/* on board NIC & Slot PCIE.  */

	/* PCI slots */
	struct device *dev = pcidev_on_root(0x14, 4);
	if (dev && dev->enabled) {
		u8 bus_pci = dev->link_list->secondary;
		/* PCI_SLOT 0. */
		PCI_INT(bus_pci, 0x5, 0x0, 0x14);
		PCI_INT(bus_pci, 0x5, 0x1, 0x15);
		PCI_INT(bus_pci, 0x5, 0x2, 0x16);
		PCI_INT(bus_pci, 0x5, 0x3, 0x17);
	}

	/* PCIe Lan */
	PCI_INT(0x0, 0x06, 0x0, 0x13);

	/* FCH PCIe PortA */
	PCI_INT(0x0, 0x15, 0x0, 0x10);
	/* FCH PCIe PortB */
	PCI_INT(0x0, 0x15, 0x1, 0x11);
	/* FCH PCIe PortC */
	PCI_INT(0x0, 0x15, 0x2, 0x12);
	/* FCH PCIe PortD */
	PCI_INT(0x0, 0x15, 0x3, 0x13);

	/* Local Ints:   Type   IRQ   APIC ID   PIN# */
	IO_LOCAL_INT(mp_ExtINT, 0, MP_APIC_ALL, 0x0);
	IO_LOCAL_INT(mp_NMI, 0, MP_APIC_ALL, 0x1);
	/* There is no extension information... */

	/* Compute the checksums */
	return mptable_finalize(mc);
}

unsigned long write_smp_table(unsigned long addr)
{
	void *v;
	v = smp_write_floating_table(addr, 0);
	return (unsigned long)smp_write_config_table(v);
}