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();
}