/*
 * This file is part of the coreboot project.
 *
 * Copyright (C) 2012 Advanced Micro Devices, Inc.
 *
 * 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.
 */

#include <stdint.h>
#include <console/console.h>
#include <device/device.h>
#include <arch/ioapic.h>
#include <device/pci.h>
#include <device/pci_ids.h>
#include "NbPlatform.h"
#include "nb_cimx.h"
#include "rd890_cfg.h"


/**
 * Global RD890 CIMX Configuration structure
 */
static NB_CONFIG nb_cfg[MAX_NB_COUNT];
static HT_CONFIG ht_cfg[MAX_NB_COUNT];
static PCIE_CONFIG pcie_cfg[MAX_NB_COUNT];
static AMD_NB_CONFIG_BLOCK gConfig;


/**
 * Reset PCIE Cores, Training the Ports selected by port_enable of devicetree
 * After this call EP are fully operational on particular NB
 */
void nb_Pcie_Early_Init(void)
{
	LibSystemApiCall(AmdPcieEarlyInit, &gConfig); //AmdPcieEarlyInit(&gConfig);
}

void nb_Pcie_Late_Init(void)
{
	LibSystemApiCall(AmdPcieLateInit, &gConfig);
}

void nb_Early_Post_Init(void)
{
	LibSystemApiCall(AmdEarlyPostInit, &gConfig);
}

void nb_Mid_Post_Init(void)
{
	LibSystemApiCall(AmdMidPostInit, &gConfig);
}

void nb_Late_Post_Init(void)
{
	LibSystemApiCall(AmdLatePostInit, &gConfig);
}

static void rd890_enable(device_t dev)
{
	u32 address = 0;
	u32 devfn;
	AMD_NB_CONFIG *NbConfigPtr = NULL;

	u8 nb_index = 0; /* The first IO Hub, TODO: other NBs */
	address = MAKE_SBDFO(0, 0x0, 0x0, 0x0, 0x0);
	NbConfigPtr = &(gConfig.Northbridges[nb_index]);

	devfn = dev->path.pci.devfn;
	printk(BIOS_INFO, "rd890_enable  ");
	printk(BIOS_INFO, "Bus-%x Dev-%X Fun-%X, enable=%x\n",
			0, (devfn >> 3), (devfn & 0x07), dev->enabled);

	/* we only do this once */
	if (devfn == 0) {
		/* CIMX configuration defualt initialize */
		rd890_cimx_config(&gConfig, &nb_cfg[0], &ht_cfg[0], &pcie_cfg[0]);
		if (gConfig.StandardHeader.CalloutPtr != NULL) {
			gConfig.StandardHeader.CalloutPtr(CB_AmdSetPcieEarlyConfig,
					(uintptr_t)dev, (VOID*)NbConfigPtr);
		}
		/* Reset PCIE Cores, Training the Ports selected by port_enable of devicetree
		 * After this call EP are fully operational on particular NB
		 */
		nb_Pcie_Early_Init();

		if (gConfig.StandardHeader.CalloutPtr != NULL) {
			gConfig.StandardHeader.CalloutPtr(CB_AmdSetEarlyPostConfig, 0, (VOID*)NbConfigPtr);
		}
		nb_Early_Post_Init();

		if (gConfig.StandardHeader.CalloutPtr != NULL) {
			gConfig.StandardHeader.CalloutPtr(CB_AmdSetMidPostConfig, 0, (VOID*)NbConfigPtr);
		}
		nb_Mid_Post_Init();
		nb_Pcie_Late_Init();

		if (gConfig.StandardHeader.CalloutPtr != NULL) {
			gConfig.StandardHeader.CalloutPtr(CB_AmdSetLatePostConfig, 0, (VOID*)NbConfigPtr);
		}
		nb_Late_Post_Init();
	}
}

struct chip_operations northbridge_amd_cimx_rd890_ops = {
	CHIP_NAME("ATI rd890")
	.enable_dev = rd890_enable,
};


static void ioapic_init(struct device *dev)
{
	void *ioapic_base;

	pci_write_config32(dev, 0xF8, 0x1);
	ioapic_base = (void *)(uintptr_t)(pci_read_config32(dev, 0xFC) & 0xfffffff0);
	clear_ioapic(ioapic_base);
	setup_ioapic(ioapic_base, 1);
}

static void rd890_read_resource(struct device *dev)
{
	pci_dev_read_resources(dev);

	/* rpr6.2.(1). Write the Base Address Register (BAR) */
	pci_write_config32(dev, 0xF8, 0x1); /* set IOAPIC's index as 1 and make sure no one changes it. */
	pci_get_resource(dev, 0xFC); /* APIC located in sr5690 */

	compact_resources(dev);
}

/* If IOAPIC's index changes, we should replace the pci_dev_set_resource(). */
static void rd890_set_resources(struct device *dev)
{
	pci_write_config32(dev, 0xF8, 0x1); /* set IOAPIC's index as 1 and make sure no one changes it. */
	pci_dev_set_resources(dev);
}

static struct pci_operations lops_pci = {
	.set_subsystem = pci_dev_set_subsystem,
};

static struct device_operations ht_ops = {
	.read_resources = rd890_read_resource,
	.set_resources = rd890_set_resources,
	.enable_resources = pci_dev_enable_resources,
	.init = ioapic_init,
	.scan_bus = 0,
	.ops_pci = &lops_pci,
};

static const unsigned short driver_ids[] = {
	PCI_DEVICE_ID_AMD_SR5690_HT,
	PCI_DEVICE_ID_AMD_SR5670_HT,
	PCI_DEVICE_ID_AMD_SR5650_HT,
	PCI_DEVICE_ID_AMD_RD890TV_HT,
	PCI_DEVICE_ID_AMD_RD890_HT,
	PCI_DEVICE_ID_AMD_990FX_HT,
	0
};

static const struct pci_driver ht_driver_sr5690 __pci_driver = {
	.ops = &ht_ops,
	.vendor = PCI_VENDOR_ID_ATI,
	.devices= driver_ids,
};
