/*------------------------------------------------------------------------------------------*\
\*------------------------------------------------------------------------------------------*/
#include <linux/config.h>
#include <linux/init.h>
#include <linux/irq.h>
#include <asm/irq.h>
#include <linux/interrupt.h>
#include <linux/kernel_stat.h>

#include <asm/mach-ur8/hw_irq.h>
#include <asm/mach-ur8/hw_pci.h>
#include <asm/mips-boards/ur8.h>
#include <asm/mips-boards/ur8int.h>
#include <asm/mips-boards/ur8_pci.h>


#if CONFIG_PCI_DEBUG
#define DEBUG_PCI(args...) printk(KERN_INFO "[PCI-IRQ]: " args)
#else
#define DEBUG_PCI(args...) /* args */
#endif

union _pci_irq {
    struct __pci_irq {
        volatile unsigned int PM_Change : 1;  /* 0 */
        volatile unsigned int Target_Abort_Det : 1;  /* 1  */
        volatile unsigned int Master_Abort_Det : 1;  /* 2  */
        volatile unsigned int Reserved1: 2;  /* 3,4 */
        volatile unsigned int SERR_Detect : 1;  /* 5  */
        volatile unsigned int PERR_Detect : 1;  /* 6 */
        volatile unsigned int Reserved2 : 9;  /* 7,8,9,10,11,12,13,14,15 */
        volatile unsigned int INTA_in : 1;  /* 16 */
        volatile unsigned int INTB_in : 1;  /* 17 */
        volatile unsigned int INTC_in : 1;  /* 18 */
        volatile unsigned int INTD_in : 1;  /* 19 */
        volatile unsigned int PME_in: 1;  /* 20 */
        volatile unsigned int Reserved3 : 3;  /* 21,22,23 */
        volatile unsigned int Soft_Int0 : 1;  /* 24 */
        volatile unsigned int Soft_Int1 : 1;  /* 25 */
        volatile unsigned int Soft_Int2 : 1;  /* 26 */
        volatile unsigned int Soft_Int3 : 1;  /* 27 */
        volatile unsigned int Reserved4 : 3;  /* 28,29,30 */
        volatile unsigned int v2p_intr : 1;  /* 31 */
    } Bits;
    volatile unsigned int Reg;
};


struct _pci_interrupt_controler {
    volatile unsigned int RevisionID;
    unsigned int Reserved1[3] ; /* 04,08,0c */
    union _pci_irq Status_Set;  /* 10 */
    union _pci_irq Status_Clear;  /* 14 */
    unsigned int Reserved2[2] ; /* 18,1c */
    union _pci_irq HostIrq_EnableSet;  /* 20 */
    union _pci_irq HostIrq_EnableClear;  /* 24 */
    unsigned int Reserved3[2] ; /* 18,1c */
    union _pci_irq BackEnd_Application_IrqEnableSet;  /* 30 */
    union _pci_irq BackEnd_Application_IrqEnableClear;  /* 34 */
};


struct _pci_interrupt_controler *PCI = (struct _pci_interrupt_controler *)0xa8614000;

/*------------------------------------------------------------------------------------------*\
\*------------------------------------------------------------------------------------------*/

unsigned int pci_get_irq(unsigned int instance) {
    unsigned int status;

    if(PCI->Status_Set.Bits.INTA_in)      status = 0;
    else if(PCI->Status_Set.Bits.INTB_in) status = 1;
    else if(PCI->Status_Set.Bits.INTC_in) status = 2;
    else if(PCI->Status_Set.Bits.INTD_in) status = 3;
    else status = (unsigned int)-1; 
    /*--- printk("[pci_get_irq] caught irq: %d\n",status); ---*/
    return status;
}


unsigned int pci_irq_ack(unsigned int instance, unsigned int irq) {
    /*--- printk("[pci_irq_ack], irq=%u\n",irq); ---*/

    PCI->Status_Clear.Reg = (1 << (irq + 16));
    return 0;
}


/*------------------------------------------------------------------------------------------*\
\*------------------------------------------------------------------------------------------*/
irqreturn_t pci_interrupt(int irq, void *dev_id, struct pt_regs *regs) {
    //printk("pci_interrupt\n");

    /*--- Status-Register löschen ---*/
    //pci_irq_ack(0, irq);
    

    return IRQ_HANDLED;
}






/* VERSION von TI

        if ( sw_irq_nr >= AV_PCI_INTA_SWIRQ )
        {       // find the bit number in the interrupt registers 
                hw_irq = map_swirq_to_intr_state( sw_irq_nr );
                AV_PCI_INTR_CLEAR = ( 1 << hw_irq );
                AV_PCI_INTR_DISABLE = ( 1 << hw_irq );
                AV_PCI_BE_INTR_DISABLE = ( 1 << hw_irq );
        }
}
*/
unsigned int pci_irq_disable(unsigned int instance, unsigned int irq) {
        /*--- u32 hw_irq; ---*/
    /*--- printk("[pci_irq_disable] for irq %i\n",irq); ---*/
        
	// find the channel in the pci interrupt register
        // from the irq_nr and disable that 
                // find the bit number in the interrupt registers 
		/*--- hw_irq = irq + UR8_Y_INTA_BIT; // hw_irq = map_swirq_to_intr_state( sw_irq_nr ); ---*/
                /*--- UR8_PCI_INTR_CLEAR = ( 1 << hw_irq ); ---*/
                /*--- UR8_PCI_INTR_DISABLE = ( 1 << hw_irq ); ---*/
                /*--- UR8_PCI_BE_INTR_DISABLE = ( 1 << hw_irq ); ---*/

    PCI->Status_Clear.Reg = (1 << (irq + 16));
    PCI->HostIrq_EnableClear.Reg = (1 << (irq + 16));
    PCI->BackEnd_Application_IrqEnableClear.Reg = (1 << (irq + 16));
	return 0;
}





/* VERSION von TI
void avalanche_pci_enable_irq( unsigned int sw_irq_nr )
{        u32 hw_irq;
        if ( ( sw_irq_nr >= AV_PCI_INTA_SWIRQ ) && ( sw_irq_nr <= (AV_PCI_INTA_SWIRQ + 3 ) ) )
        {       DbgPrint( "Enable PCI IRQ %d\n", sw_irq_nr );
                DbgPrint( "AV_PCI_INTR_ENABLE %d \n",AV_PCI_INTR_ENABLE);
                hw_irq = map_swirq_to_intr_state( sw_irq_nr );
                AV_PCI_INTR_CLEAR = ( 1 << hw_irq );
                AV_PCI_INTR_ENABLE = ( 1 << hw_irq );
                AV_PCI_BE_INTR_ENABLE = ( 1 << hw_irq );
                DbgPrint( "AV_PCI_INTR_ENABLE %x hw_irq %d \n",AV_PCI_INTR_ENABLE,hw_irq);
        } else
        {       if ( sw_irq_nr == AV_PCI_HWIRQ )
                {       DbgPrint( "Enabling Master PCI IRQ\n" );
                        disable_irq( LNXINTNUM( AV_PCI_HWIRQ ) );
                        enable_irq( LNXINTNUM( AV_PCI_HWIRQ ) );
                } else DbgPrint( "Enable PCI IRQ = %d???\n", sw_irq_nr );
        }
}
*/
//unsigned int pci_irq_enable(unsigned int instance, enum _pci_interrupt_types dev_type, unsigned int irq) {
unsigned int pci_irq_enable(unsigned int instance, unsigned int irq) {
    u32 hw_irq;
    /*--- printk("[pci_irq_enable %u]\n", irq); ---*/

    PCI->Status_Clear.Reg = (1 << (irq + 16));
    PCI->HostIrq_EnableClear.Reg = (1 << (irq + 16));
    PCI->BackEnd_Application_IrqEnableClear.Reg = (1 << (irq + 16));

    PCI->HostIrq_EnableSet.Reg = (1 << (irq + 16));
    PCI->BackEnd_Application_IrqEnableSet.Reg = (1 << (irq + 16));

    return 0;
}


static int __init ur8_pci_int_init(void)
{ 	int i = 0;
	int ret_val = 0;
    /*setting the interrupt to pulsed mode. */
    //#define UR8_PCI_INTR_DISABLE		(*(volatile unsigned long *) ( UR8_IRQ_CTRL_BASE + 0x60 ) )
    //UR8_PCI_INTR_DISABLE &= ~(1 << UR8INT_PCI);

    //ur8_intr_type_set(LNXINTNUM(AVALANCHE_PCI), 0); */



    if (request_irq(UR8INT_PCI, pci_interrupt, SA_INTERRUPT, "UR8 Unified PCI", NULL) != 0)
    {   printk(KERN_ERR "Failed to setup handler for unified PCI interrupt\n");
        return -1;
    }
    //enable_irq(UR8INT_PCI);
    printk("[pci_int_init] done\n");
    return 0;
}
late_initcall(ur8_pci_int_init);
