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

#include <acpi/acpigen.h>
#include <acpi/acpigen_pci.h>
#include <device/pci_def.h>
#include <device/pci_ops.h>
#include <string.h>

#include "acpi_pirq_gen.h"

static void gen_apic_route(const struct slot_pin_irq_map *pin_irq_map,
			   unsigned int map_count)
{
	for (unsigned int i = 0; i < map_count; i++)
		/*
		 * The reason for subtracting PCI_INT_A from the pin given is
		 * that PCI defines pins as 1-4, and _PRT uses 0-3.
		 */
		acpigen_write_PRT_GSI_entry(pin_irq_map[i].slot,
					    pin_irq_map[i].pin - PCI_INT_A,
					    pin_irq_map[i].apic_gsi);
}

static void gen_pic_route(const struct slot_pin_irq_map *pin_irq_map,
			  unsigned int map_count,
			  const struct pic_pirq_map *pirq_map)
{
	for (unsigned int i = 0; i < map_count; i++) {
		const enum pirq pirq = pin_irq_map[i].pic_pirq;
		const unsigned int pin = pin_irq_map[i].pin - PCI_INT_A;
		if (pirq == PIRQ_INVALID)
			continue;

		const size_t pirq_index = pirq_idx(pirq);
		if (pirq_map->type == PIRQ_GSI)
			acpigen_write_PRT_GSI_entry(pin_irq_map[i].slot,
						    pin,
						    pirq_map->gsi[pirq_index]);
		else
			acpigen_write_PRT_source_entry(pin_irq_map[i].slot,
						       pin,
						       pirq_map->source_path[pirq_index],
						       0);
	}
}

void intel_write_pci0_PRT(const struct slot_pin_irq_map *pin_irq_map,
			  unsigned int map_count,
			  const struct pic_pirq_map *pirq_map)
{
	/* \_SB.PCI0._PRT */
	acpigen_write_scope("\\_SB.PCI0");
	acpigen_write_method("_PRT", 0);
	acpigen_write_if();
	acpigen_emit_namestring("PICM");
	acpigen_emit_byte(RETURN_OP);
	acpigen_write_package(map_count);
	gen_apic_route(pin_irq_map, map_count);
	acpigen_pop_len(); /* package */
	acpigen_write_else();
	acpigen_emit_byte(RETURN_OP);
	acpigen_write_package(map_count);
	gen_pic_route(pin_irq_map, map_count, pirq_map);
	acpigen_pop_len(); /* package */
	acpigen_pop_len(); /* else PICM */
	acpigen_pop_len(); /* _PRT */
	acpigen_pop_len(); /* \_SB */
}

bool is_slot_pin_assigned(const struct slot_pin_irq_map *pin_irq_map,
			  unsigned int map_count, unsigned int slot,
			  enum pci_pin pin)
{
	for (size_t i = 0; i < map_count; i++) {
		if (pin_irq_map[i].slot == slot && pin_irq_map[i].pin == pin)
			return true;
	}

	return false;
}
