/*------------------------------------------------------------------------------------------*\
\*------------------------------------------------------------------------------------------*/
#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-ohio/hw_irq.h>
#include <asm/mach-ohio/hw_vlynq.h>
#include <asm/mips-boards/ohio.h>
#include <asm/mips-boards/ohioint.h>
#include <asm/mips-boards/ohio_vlynq.h>

#ifdef DEBUGVLYNQ
#define DEBUG_VLYNQ(args...) printk(KERN_INFO "[VLYNQ-IRQ]: " args)
#else
#define DEBUG_VLYNQ(args...) /* args */
#endif
/*------------------------------------------------------------------------------------------*\
\*------------------------------------------------------------------------------------------*/
unsigned int vlynq_get_irq(unsigned int instance) {

    if((instance >= MAX_VLYNQ_DEVICES) || (vlynq_config[instance] == NULL))
        return (unsigned int)-1;
    if(vlynq_config[instance]->vlynq->local.Interrupt_Priority.Bits.nointpend)
        return (unsigned int)-2;
    return vlynq_config[instance]->vlynq->local.Interrupt_Priority.Bits.intstat;
}
/*------------------------------------------------------------------------------------------*\
\*------------------------------------------------------------------------------------------*/
unsigned int vlynq_irq_status(unsigned int instance) {
    if((instance >= MAX_VLYNQ_DEVICES) || (vlynq_config[instance] == NULL))
        return (unsigned int)-1;
    
    return vlynq_config[instance]->vlynq->local.Interrupt_Status;
}

/*------------------------------------------------------------------------------------------*\
\*------------------------------------------------------------------------------------------*/
unsigned int vlynq_irq_enable_mask(unsigned int instance) {
    if((instance >= MAX_VLYNQ_DEVICES) || (vlynq_config[instance] == NULL))
        return (unsigned int)-1;
    return vlynq_config[instance]->vlynq->local.Interrupt_Status;
}

/*------------------------------------------------------------------------------------------*\
\*------------------------------------------------------------------------------------------*/
unsigned int vlynq_irq_ack(unsigned int instance, unsigned int irq) {
    if((instance >= MAX_VLYNQ_DEVICES) || (vlynq_config[instance] == NULL))
        return (unsigned int)-1;
    vlynq_config[instance]->vlynq->local.Interrupt_Status = 1 << irq;

    return 0;
}

/*------------------------------------------------------------------------------------------*\
\*------------------------------------------------------------------------------------------*/
unsigned int vlynq_irq_to_vector(unsigned int instance, unsigned int irq) {

    if ((irq < OHIO_INT_START_VIRTUAL) && (irq > MAX_VLYNQ_INT_VECTORS) && (instance >= MAX_VLYNQ_DEVICES))
        return (unsigned int)-1;

    return irq - OHIO_INT_START_VIRTUAL - (instance * 32);
}

/*------------------------------------------------------------------------------------------*\
\*------------------------------------------------------------------------------------------*/
unsigned int vlynq_vector_to_irq(unsigned int instance, unsigned int vector) {
    
    if ((vector > MAX_VLYNQ_INT_VECTORS) && (instance >= MAX_VLYNQ_DEVICES))
        return (unsigned int)-1;

    return OHIO_INT_START_VIRTUAL + (instance * 32) + vector;
}

/*------------------------------------------------------------------------------------------*\
\*------------------------------------------------------------------------------------------*/
/*unsigned int vlynq_irq_vector_setup(unsigned int instance, unsigned int irq, 
                                    enum _vlynq_vector_typ type, enum _vlynq_vector_polarity polarity) */
unsigned int vlynq_irq_vector_setup(unsigned int instance, unsigned int irq, unsigned int map_vector, enum _vlynq_interrupt_types dev_type, 
                                    enum _vlynq_vector_typ type, enum _vlynq_vector_polarity polarity)
{
    volatile union __vlynq_Interrupt_Vector *Int_Vector;
    volatile struct _vlynq_registers_half* vlynq_dev_registers;
    unsigned int value;
    unsigned int vector;
    
    DEBUG_VLYNQ("%s(%d, %d, %d, %d, %d)\n",__FUNCTION__, instance, irq, map_vector, dev_type, type, polarity);
    
    if((instance >= MAX_VLYNQ_DEVICES) || (vlynq_config[instance] == NULL) || map_vector >= VLYNQ_NUM_INT_BITS)
        return (unsigned int)-1;
        
    if(dev_type == VLYNQ_INT_LOCAL)
    	vector=vlynq_irq_to_vector(instance, irq); //Der IRQ-Vector(Vlynq) muss einem Interrupt im System(Linux) zugeordnet werden.
    else
    	vector=irq; // Fuer Remote handelt es sich bereits um den korrekten IRQ.
    	
    if(vector >= MAX_VLYNQ_INT_VECTORS)	/* Es werden momentan maximal 8 [0..7] Vectoren in Vlynq unterstuetzt. */
    	return (unsigned int)-2;

	/* Soll Remote oder Local konfiguriert werden?*/
	if ( dev_type == VLYNQ_INT_LOCAL )
		vlynq_dev_registers = &(vlynq_config[instance]->vlynq->local);
	else
		vlynq_dev_registers = &(vlynq_config[instance]->vlynq->remote);
		
    if (vector > 3)
        Int_Vector = &(vlynq_dev_registers->Interrupt_Vector_2);
    else
        Int_Vector = &(vlynq_dev_registers->Interrupt_Vector_1);

    value = (map_vector & 31) | (type<<OHIO_VLYNQ_TYPE_SHIFT) | (polarity<<OHIO_VLYNQ_POLARITY_SHIFT);
    
    DEBUG_VLYNQ("Vector_Setup: Int_Vec=%08x\n",vlynq_dev_registers->Interrupt_Vector_1);
    DEBUG_VLYNQ("Vector_Setup: Int_Vec=%08x\n",vlynq_dev_registers->Interrupt_Vector_2);

    Int_Vector->Register &= (~(0xff << ((vector % 4) << 3)));   /*--- löschen bitfeld---*/
    Int_Vector->Register |= ((value << ((vector % 4) << 3)));   /*--- setzen bitfeld---*/

    DEBUG_VLYNQ("Vector_Setup: Int_Vec=%08x\n",vlynq_dev_registers->Interrupt_Vector_1);
    DEBUG_VLYNQ("Vector_Setup: Int_Vec=%08x\n",vlynq_dev_registers->Interrupt_Vector_2);

	
    return 0;
}

/*------------------------------------------------------------------------------------------*\
\*------------------------------------------------------------------------------------------*/
unsigned int vlynq_irq_enable(unsigned int instance, enum _vlynq_interrupt_types dev_type, unsigned int irq) {

    volatile union __vlynq_Interrupt_Vector *Int_Vector;
    volatile struct _vlynq_registers_half* vlynq_dev_registers;
    unsigned int vector;

    if((instance >= MAX_VLYNQ_DEVICES) || (vlynq_config[instance] == NULL) || dev_type == VLYNQ_INT_OFF || dev_type == VLYNQ_INT_ROOT_ISR )
        return (unsigned int)-1;
        
    if( dev_type == VLYNQ_INT_LOCAL )
    	vector = vlynq_irq_to_vector(instance, irq); /*Handelt sich um den SystemIRQ. Muss auf den Vector abgebildet werden.*/
    else
    	vector = irq; /* Handelt sich um den ModulIRQ. Muss nicht umgewandelt werden. */
    	
    if(vector >= MAX_VLYNQ_INT_VECTORS)	/* Es werden momentan maximal 8 [0..7] Vectoren in Vlynq unterstuetzt. */
    	return (unsigned int)-2;

	/* Soll Remote oder Local konfiguriert werden?*/
	if ( dev_type == VLYNQ_INT_LOCAL )
		vlynq_dev_registers = &vlynq_config[instance]->vlynq->local;
	else
		vlynq_dev_registers = &vlynq_config[instance]->vlynq->remote;
		
    if (vector > 3)
        Int_Vector = &(vlynq_dev_registers->Interrupt_Vector_2);
    else
        Int_Vector = &(vlynq_dev_registers->Interrupt_Vector_1);

    Int_Vector->Register = Int_Vector->Register | ((1<<OHIO_VLYNQ_INT_SHIFT) << ((vector % 4) << 3));

    return 0;
}

/*------------------------------------------------------------------------------------------*\
\*------------------------------------------------------------------------------------------*/
unsigned int vlynq_irq_disable(unsigned int instance,  enum _vlynq_interrupt_types dev_type, unsigned int irq) {

    volatile union __vlynq_Interrupt_Vector *Int_Vector;
    volatile struct _vlynq_registers_half* vlynq_dev_registers;
    unsigned int vector;

    if((instance >= MAX_VLYNQ_DEVICES) || (vlynq_config[instance] == NULL) || dev_type == VLYNQ_INT_OFF || dev_type == VLYNQ_INT_ROOT_ISR )
        return (unsigned int)-1;
        
    if( dev_type == VLYNQ_INT_LOCAL )
    	vector = vlynq_irq_to_vector(instance, irq); /*Handelt sich um den SystemIRQ. Muss auf den Vector abgebildet werden.*/
    else
    	vector = irq; /* Handelt sich um den ModulIRQ. Muss nicht umgewandelt werden. */
    	
    if(vector >= MAX_VLYNQ_INT_VECTORS)	/* Es werden momentan maximal 8 [0..7] Vectoren in Vlynq unterstuetzt. */
    	return (unsigned int)-2;

    /* Soll Remote oder Local konfiguriert werden?*/
	if ( dev_type == VLYNQ_INT_LOCAL )
		vlynq_dev_registers = &vlynq_config[instance]->vlynq->local;
	else
		vlynq_dev_registers = &vlynq_config[instance]->vlynq->remote;
		
    if (vector > 3)
        Int_Vector = &(vlynq_dev_registers->Interrupt_Vector_2);
    else
        Int_Vector = &(vlynq_dev_registers->Interrupt_Vector_1);

    Int_Vector->Register = Int_Vector->Register & ~((1<<OHIO_VLYNQ_INT_SHIFT) << ((vector % 4) << 3));

    return 0;
}

/*------------------------------------------------------------------------------------------*\
\*------------------------------------------------------------------------------------------*/
irqreturn_t vlynq_interrupt(int irq, void *dev_id, struct pt_regs *regs) {

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

    return IRQ_HANDLED;
}
