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.

PROCESSOR-SDK-AM335X: McSPI interrupts do not occur

Part Number: PROCESSOR-SDK-AM335X

Hello TI support community,

I am trying to modify (drivers/spi/spi-omap2-mcspi.c)
omap4 mcspi drivers in order to use interrupts instead of polling.
I use beaglebone black.
  After I set CS active and enable channel 0 and interrupts,
I receive only two interrupts (in fact a pair):
- tx0_empty and after this I fill the tx buffer by writing in the
mcspi tx reg n-times
and then reset the flag in IRQSTATUS
- as a follow up to the actions taken for tx0_empty,
rx0_full interrupt is risen and I read the mcspi rx reg n-times
and reset the flag in IRQSTATUS.
After that no interrupt comes (neither of tx_empty, rx_full or EOT)
even if interrupts and channel0 are enabled and CS is active,
so I can not transfer remaining data.
  FIFO is enabled and OMAP2_MCSPI_XFERLEVEL is setup accordingly (but similar happens if FIFO is disabled).
  Note that if I want to transfer a message that needs only one pair
of interrupts,
then EOT interrupt comes, which is the expected behavior.
  I used only transfer len less than DMA_MIN_BYTES (160) so dma is not used.
  If I use the polling method to see if something is pending on channel0
everything works well (this is the default behavior in spi-omap2-mcspi.c
omap2_mcspi_txrx_pio()).

Another thing: If I set CS active and enable channel 0, then in IRQSTATUS is set
up TX0_EMPTY and after interrupts are enabled, TX0_EMPTY interrupt will occur.
But if I reset the TX0_EMPTY bit in IRQSTATUS before enabling interrupts
(as the am335x manual says in chapter 24.3.4.1 Interrupt-driven operation:
The interrupt status bit should always be reset after the channel is
enabled and before the event is enabled as an interrupt source) then
no interrupts will occur even if I enable interrupts after that.

  Has anybody used omap4 mcspi in interrupt mode (without dma)?

Thank you and kind regards,
L-C. Duca


  • Hi user6230715,

    user6230715 said:
    I am trying to modify (drivers/spi/spi-omap2-mcspi.c)
    omap4 mcspi drivers in order to use interrupts instead of polling.

    Do you use ti-processor-sdk-linux-am335x-evm-06.01.00.08/board-support/linux-4.19.59/drivers/spi/spi-omap2-mcspi.c driver?


    user6230715 said:
    I use beaglebone black.

    Do you use AM335x MCSPI module as master? Do you attach any chip to BBB AM335x McSPI module? Do you modify linux kernel BBB DTS files or using the default?

    For AM335x McSPI interrupt functionality, I would suggest you to check AM335x TRM ch 24 McSPI


    Check also if below e2e thread will be in help:

    https://e2e.ti.com/support/legacy_forums/embedded/linux/f/354/t/259958

    Regards,
    Pavel

  • Hi Pavel,

    I have tested with ti-processor-sdk-linux-am335x-evm-06.01.00.08/board-support/linux-4.19.59/drivers/spi/spi-omap2-mcspi.c and also with linux vanilla 4.14.71.

    I have tested the following scenarios:

    - spi0 loopback between MOSI-MISO

    - spi1 connected to an SPI FPGA driver that works with omap2_mcspi_txrx_pio()

    In omap2_mcspi_transfer_one() I call omap2_mcspi_txrx_interrupts(spi, t)
    instead of omap2_mcspi_txrx_pio(spi, t).

    The simples code that I've added to spi-omap2-mcspi.c

    DECLARE_WAIT_QUEUE_HEAD(omap_mcspi_wq);
    static unsigned char *rx=NULL;
    static const unsigned char *tx=NULL;
    static int rx_len, tx_len;
    static int interrupt_done;

    static irqreturn_t omap_mcspi_irq_handler(int irq, void *dev_id)
    {    
        struct omap2_mcspi    *mcspi = dev_id;
        int nothing=1;
        u32 l;
        u8 byte;
        
        l = mcspi_read_reg(mcspi->master, OMAP2_MCSPI_IRQSTATUS);

        if(l & OMAP2_MCSPI_IRQSTATUS_TX0_EMPTY) {
            if (tx_len <= 0)
                byte = 0;
            else
                byte = tx ? *tx++ : 0;
            mcspi_write_reg(mcspi->master, OMAP2_MCSPI_TX0, byte);
            tx_len--;
            nothing = 0;
        }

        if(l & OMAP2_MCSPI_IRQSTATUS_RX0_FULL) {
            byte = mcspi_read_reg(mcspi->master, OMAP2_MCSPI_RX0);
            if (rx && (rx_len > 0))
                *rx++ = byte;
            rx_len--;
            nothing = 0;
        }
        
        /* write 1 to OMAP2_MCSPI_IRQSTATUS field to reset it */
        mcspi_write_reg(mcspi->master, OMAP2_MCSPI_IRQSTATUS, l);
        
        if (((tx_len <= 0) && (rx_len <= 0)) || nothing ||
            (l & OMAP2_MCSPI_IRQSTATUS_EOW)) {
            /* disable interrupts */
            l = mcspi_read_reg(mcspi->master, OMAP2_MCSPI_IRQENABLE);
            l &= ~(OMAP2_MCSPI_IRQENABLE_TX0_EMPTY | OMAP2_MCSPI_IRQENABLE_RX0_FULL |
                   OMAP2_MCSPI_IRQENABLE_EOW);
            mcspi_write_reg(mcspi->master, OMAP2_MCSPI_IRQENABLE, l);
            
            interrupt_done = 1;
            wake_up_interruptible(&omap_mcspi_wq);
        }    
        
        return IRQ_HANDLED;
    }

    static unsigned
    omap2_mcspi_txrx_interrupts(struct spi_device *spi, struct spi_transfer *xfer)
    {
        struct omap2_mcspi    *mcspi;
        u32 l;
        mcspi = spi_master_get_devdata(spi->master);

        rx_len = tx_len = xfer->len;
        rx = xfer->rx_buf;
        tx = xfer->tx_buf;    
        
        // CS is active and channel is enabled.
        
        mcspi_write_reg(mcspi->master, OMAP2_MCSPI_IRQSTATUS,
                               OMAP2_MCSPI_IRQSTATUS_TX0_EMPTY | OMAP2_MCSPI_IRQSTATUS_RX0_FULL |
                               OMAP2_MCSPI_IRQSTATUS_EOW);
        l = mcspi_read_reg(mcspi->master, OMAP2_MCSPI_IRQSTATUS);
        dev_warn(&spi->dev, "%s: OMAP2_MCSPI_IRQSTATUS=%x \n",
                __FUNCTION__, l);
        
        /* Enable interrupts last. */
        interrupt_done = 0;
        l = mcspi_read_reg(mcspi->master, OMAP2_MCSPI_IRQENABLE);
        l |= OMAP2_MCSPI_IRQENABLE_TX0_EMPTY | OMAP2_MCSPI_IRQENABLE_RX0_FULL;
                //| OMAP2_MCSPI_IRQENABLE_TX0_UNDERFLOW;
                //| OMAP2_MCSPI_IRQENABLE_EOW;
        dev_warn(&spi->dev, "%s: OMAP2_MCSPI_IRQENABLE <- %x \n",
                __FUNCTION__, l);
        mcspi_write_reg(mcspi->master, OMAP2_MCSPI_IRQENABLE, l);

        /* wait for transfer completion */
        wait_event_interruptible(omap_mcspi_wq, (interrupt_done > 0));
        dev_warn(&spi->dev, "%s: transfer_done: tx_len=%d rx_len=%d rx[0]=%x\n",
                __FUNCTION__, tx_len, rx_len, rx[0]);
        
        return xfer->len - tx_len;
    }


    Anyideas why it does not work? I think no interrupts are coming.

    Thanks,

    L-C.

  • and, yes I use master spi.

  • I know that this simple code requires synchronization (for multiple threads),

    but I've tested it with only one spi-client at a time.

  • user6230715,

    I see you are creating your custom IRQ handler, omap_mcspi_irq_handler. I would suggest you to check and explore the existing IRQ handler, omap2_mcspi_irq_handler, and enlarge its functionality to handle more IRQ events or to create your own handler with devm_request_irq().

    https://patchwork.kernel.org/patch/10641191/

    When you enable McSPI interrupts in IRQENABLE register, at the time when you expect interrupt to be triggered (but it is not), you need to check manually IRQSTATUS, McSPI_TXx, McSPI_RXx, MCSPI_CHxSTAT, MCSPI_XFERLEVEL, MCSPI_DAFTX, MCSPI_DAFRX registers. If values in these McSPI registers ( IRQSTATUS, McSPI_TXx, etc) indicates that IRQ should be triggered by now, then most probably you need to configure Cortex-A8 ARM Interrupt Controller for this MCSPI IRQ.

    Regards,
    Pavel

  • I know omap2_mcspi_irq_handler() - it handles only OMAP2_MCSPI_IRQSTATUS_EOW in slave mode

    (I use master mode)

    I have created my handler with devm_request_irq(),

    I have read the omap2/4 mcspi doc before opening this issue on the forum (bbb uses omap 4),

    I have tested code in vanilla linux and TI linux

    and that is why I posted the code sample.

    Do you know anybody that tested interrupts (OMAP2_MCSPI_IRQSTATUS_TX0_EMPTY, OMAP2_MCSPI_IRQSTATUS_RX0_FULL) in master mode?

    Please excuse me if I'm missing something.

    Thanks

  • user6230715 said:
    Do you know anybody that tested interrupts (OMAP2_MCSPI_IRQSTATUS_TX0_EMPTY, OMAP2_MCSPI_IRQSTATUS_RX0_FULL) in master mode?

    No, I am not aware of ready code for this use case.

    What I can provide you are below e2e threads:

    https://e2e.ti.com/support/processors/f/791/t/776784

    https://e2e.ti.com/support/processors/f/791/t/437886

    Regards,
    Pavel

  • I found the solution with help from Vignesh from TI:

    even if interrupts are enabled (with or without FIFO enabled)
    CPU must write into TX_REG first in order interrupts to arrive:

    Per 24.3.2.2.1 TX_empty

      When FIFO is enabled, no new TX_empty event will be asserted as soon as
      CPU has not performed the
      number of writes into the transmitter register defined by
      MCSPI_XFERLEVEL[AEL]. It is the responsibility
      of CPU to perform the right number of writes.

    Thank you,

    L-C Duca