This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

C6678 PCIe MSI only fires once

I'm having a problem receiving more than one PCIe MSI on a C6678 DSP that is configured as an EP.  I know the PCIe spec does not support MSI from the RC to an EP, but the C6678 documentation says it does support it by enabling a RC to write directly to the C6678's MSI_IRQ register at address BAR0+0x54.  Below is a small test app I wrote to verify all that's needed to setup and handle MSI interrupts to be received on the C6678.  It works as expected when the RC writes to the C6678's MSI_IRQ, but it only works on the first try.  It does not work again after that. 

Here's a summary of my test app below.  On the C6678, "primary" system event 17 is the PCIe MSI.  My understanding is that primary system events go directly to the CorePac INTC, bypassing the upstream chip level INTCs that secondary system events go through first.  I've mapped PCIe MSI (primary system event 17) to hardware interrupt 6.  When the RC writes 0,8,16,or 24 to the MSI_IRQ register the first time, my Isr() gets invoked on core 0 as expected.  Isr() posts a semaphore which InterruptHandler() is waiting for.  InterruptHandler() wakes up and reads the MSI_IRQ_STATUS for core 0 to determine which of the MSI interrupts (0,8,16,and/or 24) has occurred.  Then it simply clears the interrupts and loops back to wait on the next interrupt signal from Isr().  However, repeated writes to MSI_IRQ from RC do not re-invoke Isr().  I assume I'm not clearing the interrupt correctly or there is another unknown register that needs clearing.  Can anyone tell me what I'm doing wrong?

#define INTC_REGS_ADDR        0x01800000    // address of Interrupt Controller registers
#define PCIE_MSI_EVENT_ID    17          // system event for PCIe MSI
#define INT6                6            // arbitrarily chose CPU INT6 for IPC events

// available core 0 MSI interrupts
#define MSI_0_INT_0        0x01
#define MSI_0_INT_8        0x02
#define MSI_0_INT_16    0x04
#define MSI_0_INT_24    0x08
                                        // after confirming its not already used
Void Isr(UArg arg)
{
    Semaphore_Handle hIsrSem = (Semaphore_Handle)arg;
    Semaphore_post(hIsrSem);
}

Void InterruptHandler(UArg arg1, UArg arg2)
{
    Semaphore_Params          semParams;
    Semaphore_Struct          IsrSem;
    Semaphore_Handle          hIsrSem;
    Hwi_Params                  hwiParams;
    Hwi_Struct                  ipcHwi;
    CSL_IntcRegsOvly          pIntcRegs;
    Pcie_Handle                 hPCIe;
    pcieRegisters_t             pcieRegs;
    pcieIrqEOIReg_t          irqEOI;
    pcieMsiIrqStatusReg_t     msiIrqStatus;
    pcieMsiIrqEnableSetReg_t msiIrqEnableSet;

    // create semaphore for Isr to signal us when interrupt occurs
    Semaphore_Params_init(&semParams);
    semParams.mode = Semaphore_Mode_COUNTING;
    Semaphore_construct(&IsrSem, 0, &semParams);
    hIsrSem = Semaphore_handle(&IsrSem);

    // create ISR for PCIe MSI events
    Hwi_Params_init(&hwiParams);
    hwiParams.eventId = PCIE_MSI_EVENT_ID;
    hwiParams.arg     = (UArg)hIsrSem;
    //hwiParams.priority = ???;
    Hwi_construct(&ipcHwi, INT6, Isr, &hwiParams, NULL);

    // enable MSI 0, which remote proc will signal us with for IPC events
    Pcie_open(0, &hPCIe);
    memset(&pcieRegs, 0, sizeof(pcieRegs));
    pcieRegs.msiIrqEnableSet[0] = &msiIrqEnableSet; // [0] => core 0
    msiIrqEnableSet.raw = 0;
    msiIrqEnableSet.msiIrqEnSet = MSI_0_INT_0;
    Pcie_writeRegs(hPCIe, pcie_LOCATION_LOCAL, &pcieRegs);

    pcieRegs.msiIrqEnableSet[0] = 0;  // don't need to read or write msiIrqEnableSet[0] anymore
    pcieRegs.msiIrqStatus[0] = &msiIrqStatus; // will be reading msiIrqStatus[0] on every interrupt
    irqEOI.EOI = INT6;    // will always set EOI on our interrupt after processing it
    pIntcRegs = (CSL_IntcRegsOvly)INTC_REGS_ADDR; // ptr to interrupt controller registers
    while(1)
    {
        if(Semaphore_pend(hIsrSem, BIOS_WAIT_FOREVER)) // Isr will post this semaphore
        {
            msiIrqStatus.raw = 0;
            pcieRegs.irqEOI = 0; // don't need to read irqEOI, just msiIrqStatus[0]
            // read msiIrqStatus[0] register
            Pcie_readRegs(hPCIe, pcie_LOCATION_LOCAL, &pcieRegs);
            if(msiIrqStatus.msiIrqStatus & MSI_0_INT_0)
            {
                // process MSI_0_INT_0 interrupt
            }
            if(msiIrqStatus.msiIrqStatus & MSI_0_INT_8)
            {
                // process MSI_0_INT_8 interrupt
            }
            if(msiIrqStatus.msiIrqStatus & MSI_0_INT_16)
            {
                // process MSI_0_INT_16 interrupt
            }
            if(msiIrqStatus.msiIrqStatus & MSI_0_INT_24)
            {
                // process MSI_0_INT_24 interrupt
            }
            // clear this interrupt (@ msiIrqStatus[0] & irqEOI registers)
            pcieRegs.irqEOI = &irqEOI; // irqEOI already initialized to INT6
            Pcie_writeRegs(hPCIe, pcie_LOCATION_LOCAL, &pcieRegs);
            // clear the PCIe MSI event
            pIntcRegs->EVTCLR[0] = CSL_INTC_EVTCLR_EC17_MASK;
        }
    }
    Pcie_close(hPCIe);
    Hwi_destruct(&ipcHwi);
    Semaphore_destruct(&IsrSem);
}

int main()
{
    Task_Params tskParams;

    Task_Params_init(&tskParams);
    tskParams.stackSize = 4*1024;
    tskParams.priority = 14;
    tskParams.instance->name = "InterruptHandler";
    Task_create((Task_FuncPtr) InterruptHandler, &tskParams, NULL);

    BIOS_start();
}