/*
 * This file is part of the coreboot project.
 *
 * Copyright (C) 2014 Sage Electronic Engineering, LLC.
 *
 * 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 __ASM_MPSPEC_H
#define __ASM_MPSPEC_H

#include <device/device.h>
#include <cpu/x86/lapic_def.h>

/*
 * Structure definitions for SMP machines following the
 * Intel Multiprocessing Specification 1.1 and 1.4.
 */

/*
 * This tag identifies where the SMP configuration
 * information is.
 */

#define SMP_MAGIC_IDENT	(('_'<<24)|('P'<<16)|('M'<<8)|'_')

/*
 * a maximum of 16 APICs with the current APIC ID architecture.
 */
#define MAX_APICS 16


#define SMP_FLOATING_TABLE_LEN sizeof(struct intel_mp_floating)

struct intel_mp_floating
{
	char mpf_signature[4];	/* "_MP_" */
	u32 mpf_physptr;	/* Configuration table address */
	u8 mpf_length;	/* Our length (paragraphs) */
	u8 mpf_specification;/* Specification version */
	u8 mpf_checksum;	/* Checksum (makes sum 0) */
	u8 mpf_feature1;	/* Predefined or Unique configuration? */
	u8 mpf_feature2;	/* Bit7 set for IMCR/PIC */
#define MP_FEATURE_VIRTUALWIRE (0 << 7)
#define MP_FEATURE_PIC         (1 << 7)
	u8 mpf_feature3;	/* Unused (0) */
	u8 mpf_feature4;	/* Unused (0) */
	u8 mpf_feature5;	/* Unused (0) */
} __attribute__((packed));

struct mp_config_table
{
	char mpc_signature[4];
#define MPC_SIGNATURE "PCMP"
	u16 mpc_length;	/* Size of table */
	u8 mpc_spec;			/* 0x01 */
	u8 mpc_checksum;
	char  mpc_oem[8];
	char  mpc_productid[12];
	u32 mpc_oemptr;	/* 0 if not present */
	u16 mpc_oemsize;	/* 0 if not present */
	u16 mpc_entry_count;
	u32 mpc_lapic;	/* APIC address */
	u16 mpe_length;	/* Extended Table size */
	u8 mpe_checksum;	/* Extended Table checksum */
	u8 reserved;
} __attribute__((packed));

/* Followed by entries */

#define	MP_PROCESSOR	0
#define	MP_BUS		1
#define	MP_IOAPIC	2
#define	MP_INTSRC	3
#define	MP_LINTSRC	4

struct mpc_config_processor
{
	u8 mpc_type;
	u8 mpc_apicid;	/* Local APIC number */
	u8 mpc_apicver;	/* Its versions */
	u8 mpc_cpuflag;
#define MPC_CPU_ENABLED		1	/* Processor is available */
#define MPC_CPU_BOOTPROCESSOR	2	/* Processor is the BP */
	u32 mpc_cpufeature;
#define MPC_CPU_STEPPING_MASK 0x0F
#define MPC_CPU_MODEL_MASK	0xF0
#define MPC_CPU_FAMILY_MASK	0xF00
	u32 mpc_featureflag;	/* CPUID feature value */
	u32 mpc_reserved[2];
} __attribute__((packed));

struct mpc_config_bus
{
	u8 mpc_type;
	u8 mpc_busid;
	u8 mpc_bustype[6];
} __attribute__((packed));

#define BUSTYPE_EISA	"EISA"
#define BUSTYPE_ISA	"ISA"
#define BUSTYPE_INTERN	"INTERN"	/* Internal BUS */
#define BUSTYPE_MCA	"MCA"
#define BUSTYPE_VL	"VL"		/* Local bus */
#define BUSTYPE_PCI	"PCI"
#define BUSTYPE_PCMCIA	"PCMCIA"

struct mpc_config_ioapic
{
	u8 mpc_type;
	u8 mpc_apicid;
	u8 mpc_apicver;
	u8 mpc_flags;
#define MPC_APIC_USABLE		0x01
	void *mpc_apicaddr;
} __attribute__((packed));

struct mpc_config_intsrc
{
	u8 mpc_type;
	u8 mpc_irqtype;
	u16 mpc_irqflag;
	u8 mpc_srcbus;
	u8 mpc_srcbusirq;
	u8 mpc_dstapic;
	u8 mpc_dstirq;
} __attribute__((packed));

enum mp_irq_source_types {
	mp_INT = 0,
	mp_NMI = 1,
	mp_SMI = 2,
	mp_ExtINT = 3
};

#define MP_IRQ_POLARITY_DEFAULT	0x0
#define MP_IRQ_POLARITY_HIGH	0x1
#define MP_IRQ_POLARITY_LOW	0x3
#define MP_IRQ_POLARITY_MASK    0x3
#define MP_IRQ_TRIGGER_DEFAULT	0x0
#define MP_IRQ_TRIGGER_EDGE	0x4
#define MP_IRQ_TRIGGER_LEVEL	0xc
#define MP_IRQ_TRIGGER_MASK     0xc


struct mpc_config_lintsrc
{
	u8 mpc_type;
	u8 mpc_irqtype;
	u16 mpc_irqflag;
	u8 mpc_srcbusid;
	u8 mpc_srcbusirq;
	u8 mpc_destapic;
#define MP_APIC_ALL	0xFF
	u8 mpc_destapiclint;
} __attribute__((packed));

/*
 *	Default configurations
 *
 *	1	2 CPU ISA 82489DX
 *	2	2 CPU EISA 82489DX neither IRQ 0 timer nor IRQ 13 DMA chaining
 *	3	2 CPU EISA 82489DX
 *	4	2 CPU MCA 82489DX
 *	5	2 CPU ISA+PCI
 *	6	2 CPU EISA+PCI
 *	7	2 CPU MCA+PCI
 */

#define MAX_IRQ_SOURCES 128
#define MAX_MP_BUSSES 32
enum mp_bustype {
	MP_BUS_ISA,
	MP_BUS_EISA,
	MP_BUS_PCI,
	MP_BUS_MCA
};

/* Followed by entries */

#define	MPE_SYSTEM_ADDRESS_SPACE	0x80
#define	MPE_BUS_HIERARCHY		0x81
#define	MPE_COMPATIBILITY_ADDRESS_SPACE	0x82

struct mp_exten_config {
	u8 mpe_type;
	u8 mpe_length;
} __attribute__((packed));

typedef struct mp_exten_config *mpe_t;

struct mp_exten_system_address_space {
	u8 mpe_type;
	u8 mpe_length;
	u8 mpe_busid;
	u8 mpe_address_type;
#define ADDRESS_TYPE_IO       0
#define ADDRESS_TYPE_MEM      1
#define ADDRESS_TYPE_PREFETCH 2
	u32 mpe_address_base_low;
	u32 mpe_address_base_high;
	u32 mpe_address_length_low;
	u32 mpe_address_length_high;
} __attribute__((packed));

struct mp_exten_bus_hierarchy {
	u8 mpe_type;
	u8 mpe_length;
	u8 mpe_busid;
	u8 mpe_bus_info;
#define BUS_SUBTRACTIVE_DECODE 1
	u8 mpe_parent_busid;
	u8 reserved[3];
} __attribute__((packed));

struct mp_exten_compatibility_address_space {
	u8 mpe_type;
	u8 mpe_length;
	u8 mpe_busid;
	u8 mpe_address_modifier;
#define ADDRESS_RANGE_SUBTRACT 1
#define ADDRESS_RANGE_ADD      0
	u32 mpe_range_list;
#define RANGE_LIST_IO_ISA	0
	/* X100 - X3FF
	 * X500 - X7FF
	 * X900 - XBFF
	 * XD00 - XFFF
	 */
#define RANGE_LIST_IO_VGA	1
	/* X3B0 - X3BB
	 * X3C0 - X3DF
	 * X7B0 - X7BB
	 * X7C0 - X7DF
	 * XBB0 - XBBB
	 * XBC0 - XBDF
	 * XFB0 - XFBB
	 * XFC0 - XCDF
	 */
} __attribute__((packed));

void mptable_init(struct mp_config_table *mc, u32 lapic_addr);
void *smp_next_mpc_entry(struct mp_config_table *mc);
void *smp_next_mpe_entry(struct mp_config_table *mc);

void smp_write_processor(struct mp_config_table *mc,
	u8 apicid, u8 apicver,
	u8 cpuflag, u32 cpufeature,
	u32 featureflag);
void smp_write_processors(struct mp_config_table *mc);
void smp_write_ioapic(struct mp_config_table *mc,
	u8 id, u8 ver, void *apicaddr);
void smp_write_intsrc(struct mp_config_table *mc,
	u8 irqtype, u16 irqflag, u8 srcbus, u8 srcbusirq,
	u8 dstapic, u8 dstirq);
void smp_write_pci_intsrc(struct mp_config_table *mc,
	u8 irqtype, u8 srcbus, u8 dev, u8 pirq,
	u8 dstapic, u8 dstirq);
void smp_write_intsrc_pci_bridge(struct mp_config_table *mc,
	u8 irqtype, u16 irqflag,
	struct device *dev,
	unsigned char dstapic, unsigned char *dstirq);
void smp_write_lintsrc(struct mp_config_table *mc,
	u8 irqtype, u16 irqflag,
	u8 srcbusid, u8 srcbusirq,
	u8 destapic, u8 destapiclint);
void smp_write_address_space(struct mp_config_table *mc,
	u8 busid, u8 address_type,
	u32 address_base_low, u32 address_base_high,
	u32 address_length_low, u32 address_length_high);
void smp_write_bus_hierarchy(struct mp_config_table *mc,
	u8 busid, u8 bus_info,
	u8 parent_busid);
void smp_write_compatibility_address_space(struct mp_config_table *mc,
	u8 busid, u8 address_modifier,
	u32 range_list);
void *smp_write_floating_table(unsigned long addr, unsigned int virtualwire);
unsigned long write_smp_table(unsigned long addr);

void mptable_lintsrc(struct mp_config_table *mc, unsigned long bus_isa);
void mptable_add_isa_interrupts(struct mp_config_table *mc, unsigned long bus_isa, unsigned long apicid, int external);
void mptable_write_buses(struct mp_config_table *mc, int *max_pci_bus, int *isa_bus);
void *mptable_finalize(struct mp_config_table *mc);

#endif
