/*  ============================================================================
 *     Copyright (C) 2013 Texas Instruments Incorporated.       *
 *
 *   Use of this software is controlled by the terms and conditions found in the
 *   license agreement under which this software has been supplied.
 *   ===========================================================================
 */
/** ============================================================================
Interrupts configuration and ISR for PCIE
 * =============================================================================
 *  Revision History
 *  ===============
 *  17-July-2013 Thomas Yang file created
 *  August 22, 2013 Brighton Feng,  update
 * =============================================================================
 */

#include <stdio.h>
#include <csl_pscAux.h>
#include "K2_common.h"
#include "K2_PCIE_init_drv.h"

volatile Uint32 PCIE_IntCCNT; //CCNT value when PCIE MSI interrupt happens
volatile Uint32 gudInterruptFlag = 0;
void PCIE_MSI_ISR(Uint32 uiIAR_value, Uint32 uiIntAddress)
{
	int i, j;
	Uint32 uiINT_ID;
	Uint32 MSI_IRQ_STATUS, MSI_number;
	CSL_Pciess_appRegs * PCIE_app_regs;
	
	uiINT_ID= GIC_GET_INT_ID(uiIAR_value)-32;
	
#if defined(DEVICE_K2H) || defined(DEVICE_K2K)
	PCIE_app_regs = gpPCIE_app_regs;
#else //for K2L and K2E
	if((CSL_ARM_GIC_PCIE_INT4<=uiINT_ID)&&(uiINT_ID<CSL_ARM_GIC_PCIE_INT12))
		PCIE_app_regs = gpPCIE_app_regs[0];
	else
		PCIE_app_regs = gpPCIE_app_regs[1];
#endif

	PCIE_IntCCNT= CP15_read_CCNT();
	
	for(j=0; j<8; j++)
	{
		MSI_IRQ_STATUS= PCIE_app_regs->MSIX_IRQ[j].MSI_IRQ_STATUS;

		//clear the interrupt
		PCIE_app_regs->MSIX_IRQ[j].MSI_IRQ_STATUS= MSI_IRQ_STATUS;

		for(i=0; i<4; i++)
		{
			if((MSI_IRQ_STATUS>>i)&1)
			{
				MSI_number= j+i*8;

				printf("PCIE MSI%d interrupt generated!\n", MSI_number);
			}
		}

		//indicate end-of-interrupt in IRQ_EOI register
		PCIE_app_regs->IRQ_EOI = j+4;
	}
	gudInterruptFlag = 1;
}

char * PCIE_ERR_Str[]=
{
	"PCIE System error (OR of fatal, nonfatal, correctable errors) (RC mode only)",
	"PCIe fatal error (RC mode only)",
	"PCIe non-fatal error (RC mode only)",
	"PCIe correctable error (RC mode only)",
	"PCIE AXI Error due to fatal condition in AXI bridge (EP/RC modes)           ",
	"PCIe advanced error (RC mode only)"
};

char * PCIE_PMRST_Str[]=
{
	"PCIE Power management turn-off message interrupt (EP mode only)",
	"PCIE Power management ack message interrupt (RC mode only)",
	"PCIE Power management event interrupt (RC mode only)",
	"PCIE Link request reset interrupt (hot reset or link down) (RC mode only)"
};

void PCIE_ERR_handler(Uint32 uiPCIE_instant_num)
{
	int i;
	Uint32 IRQ_STATUS;
#if defined(DEVICE_K2H) || defined(DEVICE_K2K)
	CSL_Pciess_appRegs * PCIE_app_regs = gpPCIE_app_regs;
#else //for K2L and K2E
	CSL_Pciess_appRegs * PCIE_app_regs = gpPCIE_app_regs[uiPCIE_instant_num];
#endif
	
	IRQ_STATUS= PCIE_app_regs->ERR_IRQ_STATUS;

	//clear the interrupt
	PCIE_app_regs->ERR_IRQ_STATUS= IRQ_STATUS;

	for(i=0;i<6;i++)
	{
		if((IRQ_STATUS>>i)&1)
			printf("%s\n", PCIE_ERR_Str[i]);
	}
	
	//indicate end-of-interrupt in IRQ_EOI register
	PCIE_app_regs->IRQ_EOI = 12;
}

void PCIE_PMRST_handler(Uint32 uiPCIE_instant_num)
{
	int i;
	Uint32 IRQ_STATUS;
#if defined(DEVICE_K2H) || defined(DEVICE_K2K)
	CSL_Pciess_appRegs * PCIE_app_regs = gpPCIE_app_regs;
#else //for K2L and K2E
	CSL_Pciess_appRegs * PCIE_app_regs = gpPCIE_app_regs[uiPCIE_instant_num];
#endif
	
	IRQ_STATUS= PCIE_app_regs->PMRST_IRQ_STATUS;

	//clear the interrupt
	PCIE_app_regs->PMRST_IRQ_STATUS= IRQ_STATUS;

	for(i=0;i<4;i++)
	{
		if((IRQ_STATUS>>i)&1)
			printf("%s\n", PCIE_PMRST_Str[i]);
	}
	
	//indicate end-of-interrupt in IRQ_EOI register
	PCIE_app_regs->IRQ_EOI = 13;
}

/*ISR for PCIE error, power management, reset*/
void PCIE_ERR_PMRST_ISR(Uint32 uiIAR_value, Uint32 uiIntAddress)
{
	Uint32 uiINT_ID;

	uiINT_ID= GIC_GET_INT_ID(uiIAR_value)-32;

	if(CSL_ARM_GIC_PCIE_INT12==uiINT_ID)
		PCIE_ERR_handler(0);
	if(CSL_ARM_GIC_PCIE_INT13==uiINT_ID)
		PCIE_PMRST_handler(0);
#ifdef CSL_ARM_GIC_PCIE_1_INT12 	//for K2E
	if(CSL_ARM_GIC_PCIE_1_INT12==uiINT_ID)
		PCIE_ERR_handler(1);
	if(CSL_ARM_GIC_PCIE_1_INT13==uiINT_ID)
		PCIE_PMRST_handler(1);
#endif
}

void PCIE_Interrupts_Init(void)
{
	int i;
	GIC_INT_Config int_cfg;

	int_cfg.trigger_type= GIC_TRIGGER_TYPE_EDGE;
	int_cfg.ucGroupNum= 1; //route QMSS interrupt to group 1, IRQ
	int_cfg.ucPriority= GIC400_PRIORITY_LOWER;

	GIC_interrupt_hook(GIC_CONVERT_SPI_ID(CSL_ARM_GIC_PCIE_INT12), 
		&int_cfg, PCIE_ERR_PMRST_ISR);
	GIC_interrupt_hook(GIC_CONVERT_SPI_ID(CSL_ARM_GIC_PCIE_INT13), 
		&int_cfg, PCIE_ERR_PMRST_ISR);
	for(i=0; i<8; i++)
		GIC_interrupt_hook(GIC_CONVERT_SPI_ID(CSL_ARM_GIC_PCIE_INT4+i), 
			&int_cfg, PCIE_MSI_ISR);
#ifdef CSL_ARM_GIC_PCIE_1_INT12 	//for K2E
	GIC_interrupt_hook(GIC_CONVERT_SPI_ID(CSL_ARM_GIC_PCIE_1_INT12), 
		&int_cfg, PCIE_ERR_PMRST_ISR);
	GIC_interrupt_hook(GIC_CONVERT_SPI_ID(CSL_ARM_GIC_PCIE_1_INT13), 
		&int_cfg, PCIE_ERR_PMRST_ISR);
	for(i=0; i<8; i++)
		GIC_interrupt_hook(GIC_CONVERT_SPI_ID(CSL_ARM_GIC_PCIE_1_INT4+i), 
			&int_cfg, PCIE_MSI_ISR);
#endif
}

