PROCESSOR-SDK-AM62A: How to set up GPIO Auto Trigger a DMA transfer in Linux?

Part Number: PROCESSOR-SDK-AM62A

Tool/software:

Hello,

I almost got my driver and app to successfully use DMA to transfer data from my ASIC via a QSPI interface. I still have two issues, one is a data corruption which is being looked at in another post and the other issue is that my system has too much interrupt latency for the GPIO IRQ handler to reliably start the DMA transfer.  I am hoping that using GPIO auto trigger to initiate the DMA transfer will fix the problem.   I saw the other post that explains the process for QNX but I am using LInux for my OS.  Can someone give me an example on how to implement this in Linux?

I assume that a change to the .dts file is in order.  Below is the node for the driver.  What changes are needed to change that GPIO interrupt to be auto triggered and also in the driver code?

Thanks,

Victor

&ospi0 {
                status = "okay";
                pinctrl-names = "default";
                pinctrl-0 = <&main_ospi0_pins_default>;
                compatible = "slq,qspi-nor";
                reg = <0x00 0x0fc40000 0x00 0x100>,
                      <0x05 0x00000000 0x01 0x00000000>;

                clock-names = "slq-clk";

                memory-region = <&slq_rx_region>;

                interrupt-names = "slq-irq";
                interrupt-parent = <&main_gpio0>;
                interrupts = <62 IRQ_TYPE_EDGE_FALLING>;
                gpio-irq = <&main_gpio0 62 GPIO_ACTIVE_LOW>;

                maxwell0: am62x@0 {
                        status = "okay";
                        is-dual = <0>;
                        num-cs = <1>;
                        spi-tx-bus-width = <1>;
                        spi-rx-bus-width = <4>;
                        compatible = "trimble,maxwell-slq0", "jedec,spi-nor";
                        reg = <0x0>;
                        spi-max-frequency = <50000000>;
                        cdns,read-delay = <50>;
                        cdns,tshsl-ns = <4>;
                        cdns,tsd2d-ns = <4>;
                        cdns,tchsh-ns = <4>;
                        cdns,tslch-ns = <4>;
                };

};

  • Hi Victor,

    Triggering DMA by GPIO directly requires configuring GPIO interrupt in L2G module, but kernel doesn't have a driver for this function, so currently it is possible to use GPIO directly trigger DMA in Linux.

  • Hello,

    What functions that is related to the L2G module are available?  Maybe I can hack something that will work just for me.  Any assistance such as examples or code for this L2G module will be very helpful.

    I recognize your name.  You helped someone else did the GPMC DMA work a few years ago.  I took that work and was able to get it working on my system.   The only issue that I have with that capability is a infrequent bit of corrupted data will pop up.  I am trying to tracking that down.

    Thanks,

    Victor

  • Hi Victor,

    What functions that is related to the L2G module are available?  Maybe I can hack something that will work just for me.  Any assistance such as examples or code for this L2G module will be very helpful.

    Yes, it is "hackable". Let's come back on this later.

    I recognize your name.  You helped someone else did the GPMC DMA work a few years ago.  I took that work and was able to get it working on my system. 

    Are you refer to the following e2e thread?

    https://e2e.ti.com/support/processors-group/processors/f/processors-forum/1337943/am62a3-linux-dma-identifiers-for-gpmc-device-register-to-memory-transfer

    The only issue that I have with that capability is a infrequent bit of corrupted data will pop up.  I am trying to tracking that down.

    I'd like to know more details of data corruption problem to see if we can resolve it instead of hack L2G for GPIO directly triggering DMA.

    Does the problem seem to be like DMA transfer is scheduled late occasionally which caused the data source overflow? If so, have you tried to use RT-kernel with cpu isolation and irq affinity? I used this method solved a DMA transfer scheduling problem for a different customer.

  • Hello,

    Yes, I took over the work for Craig Boardman and I was able to get it working.  However, the need to use GPIO trigger and the GPMC DMA work are two different issues.

    I need the GPIO trigger due to this issue:

    https://e2e.ti.com/support/processors-group/processors/f/processors-forum/1476789/sk-am62-dma-has-corrupt-data-reading-using-ospi-controller/5671149#5671149

    Due to lack of GPIO core affinity, there are times that when the GPIO triggers an interrupt, the CPU cannot start the DMA transfer in a timely manner due to other interrupts running on core 0.  I am hoping that this auto trigger will fix the problem.   My code now sets up the DMA transfer way before the GPIO triggers. Once the GPIO triggers, the IRQ handler only calls dma_async_issue_pending() to start the DMA transfer.  I believe that function needs to be moved to where the DMA transfer is set up and changed to wait for the GPIO trigger event.  I also need to know on how to hack the GPIO trigger event to the L2G.

    As for the corrupted data that I am seeing, it has nothing to do with the CPU not being able to start the DMA process in time.  I believe the corrupted data may be related to the GPMC DMA issue.

    The GPMC DMA data corruption is not related to data overflow.  In this application, software requests for a certain amount of data and our ASIC generates that amount of data and stops.  There is no problem when not using DMA.  We are now planning to test this application using a FPGA but add test code onto the FPGA to generate a known pattern so we can verify if the data is getting corrupted via DMA.

    BTW, there is another issue that you worked on in the past that I also posted about.   You have answered a post that DMA should work in cyclic mode.  I posted a request middle of last year on how to get the DMA to work in cyclic mode for a SPI transfer. Someone answered but never follow through.  I have got the DMA to work but the problem is for every transfer, I have to start the DMA which is a complete waste of time.  I don't know why TI never got back to me on implementing DMA cyclic mode.  Can you provide some insight on how DMA can be set up to read data from a SPI port using cyclic mode?  Currently, the kernel will error out if I try to set up cyclic mode.

    Thanks,

    Victor

  • Hi Victor,

    Sorry for the late response. I am in the middle of a work to be delivered in a couple days and didn't have enough time look into your questions.

    OSPI interface is not in my support scope, and I am not familiar with it, but it seems its kernel driver uses a different UDMA function from that is used in GPMC driver I did for the other customer, so I am not sure how easily or possible to enable cyclic mode. I would also need to look into the UDMA driver to see how much work or if it is possible to enable GPIO direct triggering for the OSPI DMA request. Hopefully I can have time for this next week if not on Friday.

  • Hello,

    I would appreciate any help with getting the GPIO trigger working in Linux first.  If you can show me how to hack the code, that will be enough for now.

    I haven't heard back on the other post regarding the corrupted data with the DMA transfer.  I am working on that to see if this problem is more widespread.  Anything I find will be put on that post.

    As for cyclic mode on SPI DMA, that is on the bottom of the list because it is working but not efficient.

    Thanks,

    Victor

  • Hi Victor,

    before I get time to get dive to this, please check the following e2e thread in which I helped a customer enabling gpio triggering DMA. The thread is very log and might take you some time to digest.

    One of the challenges in enabling gpio directly triggering DMA is to map the gpio bank interrupt to the DMA channel in the L2G module. The mapping is not documented in the TRM I believe.

    https://e2e.ti.com/support/processors-group/processors/f/processors-forum/1200987/am625-rerouting-interrupt-to-dma-event-and-dma_dev_to_mem-channel-selection

  • Hello,

    There are some similarities with what you did with the GPMC interface but there are some differences that I have questions. My data transfer starts with an interrupt line asserting low when a FIFO in my ASIC goes over a threshold and it de asserts when enough data is read from the FIFO. Data is read from the FIFO using QSPI.

    The low level driver works by first setting up the DMA transfer as follows:

    tx = dmaengine_prep_dma_memcpy(rx_chan, dma_dst, dma_src, buffer_len, flags);
    if (!tx) {
        dev_err(dev, "device_prep_dma_memcpy error\n");
    }

    tx->callback = slq_rx_dma_callback;
    tx->callback_param = cqspi;
    cookie = tx->tx_submit(tx);
    reinit_completion(&rx_dma_complete);

    ret = dma_submit_error(cookie);
    if (ret) {
        dev_err(dev, "dma_submit_error %d\n", cookie);
    }

    Software then suspend until the interrupt asserts and in the IRQ handler the DMA transfer is started by calling dma_async_issue_pending(rx_chan).

    Here are the questions that I have:

    1) My interrupt is a falling edge. Bit 31 of the DMASS0_INTAGGR_0 register only accept a pulsed event of a rising edge.  Do I need to insert a flip flop on my interrupt line in order to generate a rising edge?
    2) Do I use the same BCHAN_EVT_OFFSET of 0xc400?  If not, what do I use?
    3) I am using GPIO0_12.  I am also using GPIO0_13 and GPIO0_14 as inputs but not as interrupts. So, the same GPIO bank interrupt can be used as in your previous post.
    4) Do I use the same U-Boot patch?  I also don't see the file rm-cfg.yam1 in my uboot directory. If I need to use that patch,  I need to figure out where that patch go.
    5) I believe uc->config.tr_trigger_type needs to be set to 1 or 2. But I am unable to set it.  I added the dmas = <&main_bcdma 1 0 0>; in OSPI node in my device tree. But when I print out the value of uc->config.tr_trigger_type, it is still 0.  Below is my OSPI node in the device tree.  Do I need to comment out interrupt-names, interrupt-parent,        interrupts or gpio-irq below?
     

    &ospi0 {
            status = "okay";
            pinctrl-names = "default";
            pinctrl-0 = <&main_ospi0_pins_default>;
            compatible = "slq,qspi-nor";
            reg = <0x00 0x0fc40000 0x00 0x100>,
                  <0x05 0x00000000 0x01 0x00000000>;

            clock-names = "slq-clk";

            memory-region = <&slq_rx_region>;

            dmas = <&main_bcdma 1 0 0>;
            dma-names = "rx";
            interrupt-names = "slq-irq";
            interrupt-parent = <&main_gpio0>;
            interrupts = <12 IRQ_TYPE_EDGE_FALLING>;
            gpio-irq = <&main_gpio0 12 GPIO_ACTIVE_LOW>;

            maxwell0: am62x@0 {
                    status = "okay";
                    is-dual = <0>;
                    num-cs = <1>;
                    spi-tx-bus-width = <1>;
                    spi-rx-bus-width = <4>;
                    compatible = "trimble,maxwell-slq0", "jedec,spi-nor";
                    reg = <0x0>;
                    spi-max-frequency = <50000000>;
                    cdns,read-delay = <50>;
                    cdns,tshsl-ns = <4>;
                    cdns,tsd2d-ns = <4>;
                    cdns,tchsh-ns = <4>;
                    cdns,tslch-ns = <4>;
            };
    };


    That is all the questions I have for now.  I am sure I will have more as I proceed further.

    Thanks,

    Victor

  • Hi Victor,

    I spent some time today looked deeper on this. I am not really confident that you can get the work done without technical details of the DMA controller or close technical support from TI. But none of the options is available - we don't have any other public DMA documentation other than the TRM and we don't provide support on software functions/features which are not provided in the SDK.

    Besides the 5 questions you asked about the GPIO interrupt routing, the first problem is the DMA function udma_prep_dma_memcpy(), which will be called indirectly by dmaengine_prep_dma_memcpy() in the OSPI driver, doesn't support external trigger. So it won't be blocked until the GPIO interrupt event.

  • Hi Victor,

    In an offline email, the requirement for this item is "GPIO trigger OSPI cyclic mode". I'd like to confirm if this is what you ask. This requirement include two irrelevant tasks, adding DMA cyclic mode for OSPI, and enabling GPIO triggering DMA.

    If this is what you need, I'd like to ask for details of your OSPI ASIC usecase, so that I can port it to my EVM setup to implement the cyclic mode and GPIO trigger.

    You mentioned you don't use the driver spi-cadence-quadspi.c but have your own driver talking to the ASIC. Can you please share the driver code? The spi-cadence-quaspi.c is in the spi framework and really cannot use the DMA cyclic mode.

  • I used spi-cadence-quadspi.c to generate my own driver.   It is basically a simplified version where it only has to handle one message.

    I will email you my driver privately.

    To get you to where I am right now, you need to somehow generate a 10Hz GPIO interrupt which will kick off this DMA transfer.  You don't need to worry about sending in data.  The length of the data frame can be 1024 clocks.  Once the OSPI controller sends out data frame, you should get a RX completion interrupt.  Since you don't have to worry about the incoming data, you can just set up the DMA transfer for the next GPIO interrupt.

    Yes, I need to change the current GPIO interrupt waking up a IRQ handler to start a DMA transfer to a GPIO trigger automatically start the GPIO transfer.   Due to multiple interrupts on CPU0, the interrupt latency for this handler is all over the place.

    I also need the cyclic mode because without it, I would have to set up the next transfer during the RX completion interrupt.  I hope to avoid this needless work.

    Hopefully, when you are all done, there will be no irq handler and the call to dmaengine...() to start a DMA transfer will only be done once at initialization.

    Thanks,

    Victor