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.

Linux/AM4376: DMA support on SPI4

Part Number: AM4376

Tool/software: Linux

Hi, 

I try to get DMA support for SPI 4 on the AM437. 

I started adding the following lines on my am437.dts file: 

&spi4 {
    status = "okay";
    pinctrl-names = "default", "sleep";
    pinctrl-0 = <&spi4_pins_default>;
    pinctrl-1 = <&spi4_pins_sleep>;
    ti,spi-num-cs = <1>;
    dmas=<&edma_xbar 55 0 0>, <&edma_xbar 56 0 0>;
    dma-names="tx0","rx0";
    
	spidev4: spi@4 {
		compatible = "rohm,dh2228fv";
		spi-max-frequency = <48000000>;
		reg = <0x0>;
        spi-cpol;
        spi-cpha;
	};
};

Now the output "spi spi2.0: not using DMA for McSPI (-19)" does not appear anymore, but the cpu load stays still very high, when I activate SPI transfer. 

I tried many variations on dts file, but with no success. 

Currently I activate SPI transfer by the following lines: 

static int fd = 0;
static const char *device = "/dev/spidev2.0";
static uint8_t mode = SPI_CPHA | SPI_CPOL;
static uint8_t bits = 8;
static uint32_t speed = 160000;
static uint16_t delay = 0;

	
fd = open(device, O_RDWR); if (fd < 0) printf("can't open device"); ret = ioctl(fd, SPI_IOC_WR_MODE, &mode); if (ret == -1) printf("can't set spi mode"); ret = ioctl(fd, SPI_IOC_WR_BITS_PER_WORD, &bits); if (ret == -1) printf("can't set bits per word"); ret = ioctl(fd, SPI_IOC_WR_MAX_SPEED_HZ, &speed); if (ret == -1) printf("can't set max speed hz"); printf("spi mode: %d\n", mode); printf("bits per word: %d\n", bits); printf("max speed: %.2f MHz\n", (float)speed/1000000); struct spi_ioc_transfer tr = { //.tx_buf = (unsigned long)&cmd, //.rx_buf = (unsigned long)&rx_data, //.len = 1, .delay_usecs = delay, .speed_hz = speed, .bits_per_word = bits, }; ret = ioctl(fd, SPI_IOC_MESSAGE(1), tr); if (ret < 1) { // SPI still busy or failed printf("can't send spi message"); return -1; }

Is there anything wrong with this code? Can I check somehow if dma is used for the transfer?

Best regards
Stefan

  • Stefan,

    You can dump McSPI4 module registers and check if the DMA settings are setup properly. Check the AM437x TRM for the McSPI DMA registers settings.

    You can also add prints or breakpoints into the McSPI driver (spi-omap2-mcspi.c) and check if DMA transfer functions are executed.

    Note also that below 160 elements, the transfer is PIO (not DMA), check the driver:

    /* use PIO for small transfers, avoiding DMA setup/teardown overhead and
    * cache operations; better heuristics consider wordsize and bitrate.
    */
    #define DMA_MIN_BYTES 160


    Regards,
    Pavel
  • The 160 Bytes were a good hint.
    Now I set the the length to 160 byte and the program just stops. So I belive there is still an error in the setup and the program just waits for DMA finish.
    How do I check McSPI4 module register? Is the easiest way some printouts in the driver?

    Regards,
    Stefan
  • So now I had a closer lock. And found, that the command edma_xbar should be ok. 
    The question I have is: 

    1) Is Crossbar mapped Event number 55 and 56 right for SPI4 tx0 and rx0 event for AM437x? 

    2) Where to map this events to? Do I have to map them to an reserved event name or can I chose one? For AM437x can I use Event Number 4 and 5?

    3) In which order I have to use the command edma_xbar in device tree? Is edma_xbar("crossbar event number" 0 "event number to map to") the right command order?

    4) Is the following definition of spi4 correct?

    &spi4 {
    status = "okay";
    pinctrl-names = "default", "sleep";
    pinctrl-0 = <&spi4_pins_default>;
    pinctrl-1 = <&spi4_pins_sleep>;
    ti,spi-num-cs = <1>;
    dmas = <&edma_xbar 55 0 4>,
    <&edma_xbar 56 0 5>;
    dma-names="tx0","rx0";

    spidev4: spi@4 {
    compatible = "rohm,dh2228fv";
    spi-max-frequency = <48000000>;
    reg = <0x0>;
    spi-cpol;
    spi-cpha;
    };
    };

    I tried also 

    dmas = <&edma_xbar 4 0 55>,
    <&edma_xbar 5 0 56>;


    but the program just hang. 

  • Hi,

    Sorry for the delay. Just wanted to let you know that Pavel is OoO until Monday, so his feedback will be delayed.

    Best Regards,
    Yordan
  • Stefan,

    The meaning of parameters of dmas for clients:
    dmas = <&edma_xbar 12 0 1>; where <12> is the DMA request number, <0> is the TC the event should be assigned and <1> is the mux selection for in the crossbar. When mux 0 is used the DMA channel can be requested directly from edma node.

    Check how MMC3 DMA is done in below file:

    linux-kernel/arch/arm/boot/dts/am437x-gp-evm.dts

    &mmc3 {
    status = "okay";
    /* these are on the crossbar and are outlined in the
    xbar-event-map element */
    dmas = <&edma_xbar 30 0 1>,
    <&edma_xbar 31 0 2>;
    dma-names = "tx", "rx";


    Check also below files:

    linux-kernel/Documentation/devicetree/bindings/dma/ti-edma.txt
    linux-kernel/Documentation/devicetree/bindings/dma/ti-dma-crossbar.txt

    Regards,
    Pavel
  • Can you please answer my other questions as well?
  • Stefan,

    I would suggest you to try the below DTS entry:

    dmas = <&edma_xbar 16 0 55>,
    <&edma_xbar 17 0 56>;
    dma-names = "tx", "rx";

    As channels 4/5 are marked as reserved, I think these can not be used. Thus you can use (in example) channels 16/17. With the above code you will have:

    - EDMA channel 16 - SPI4XEVT0 instead of SPI0XEVT0
    - EDMA channel 17 - SPI4REVT0 instead of SPI0REVT0

    If you are using SPI0 also, you need to use another DMA channels for SPI4.

    Parameter <0> means that you will use EDMATC0.

    Check also AM437x TRM, sections:

    10.3.20 EDMA Events
    7.2.2 EDMA Event Multiplexing

    You can dump below register before and after DTS node SPI4 DMA update and check that the value is correctly applied.

    CTRL_TPCC_EVT_MUX_16_19

    [5:0] EVT_MUX_16 = 0 (SPI0XEVT0), = 55 (SPI4XEVT0)
    [13:8] EVT_MUX_17 = 0 (SPI0REVT0), = 56 (SPI4REVT0)

    Regards,
    Pavel
  • Thank you for your reply.
    So I tried it again.

    When I add edma_xbar command in dt and read the register back with the following command
    "devmem2 0x44e10fa0" (because 0x44E1_0000 should be the Control Module and 0xFA0 should be CTRL_TPCC_EVT_MUX_16_19) I get as result "Read at address 0x44E10FA0 (0xb6f1cfa0): 0x00001817". So it looks like writing 55 and 56 failed, but just by the highest bit. Because adding 32 to 18 and 17 are the required 55 and 56.
    Do you have any idea, what went wrong?

    Best regards,
    Stefan
  • I found the bug in "ti-dma-crossbar.c"

    In line 51 in function "ti_am335x_xbar_write" following line must be replaced

    writeb_relaxed(val & 0x1f, iomem + event);

    with 

    writeb_relaxed(val & 0x3f, iomem + event);

    Otherwise the first bit will not used. Now DMA is working fine!

  • Stefan,

    Glad to see you have fix this. Just note that on latest AM437x PSDK Linux 5.x, the ti_am335x_xbar_write function has been updated as below:

    static inline void ti_am335x_xbar_write(void __iomem *iomem, int event, u8 val)
    {
    /*
    * TPCC_EVT_MUX_60_63 register layout is different than the
    * rest, in the sense, that event 63 is mapped to lowest byte
    * and event 60 is mapped to highest, handle it separately.
    */
    if (event >= 60 && event <= 63)
    writeb_relaxed(val, iomem + (63 - event % 4));
    else
    writeb_relaxed(val, iomem + event);
    }


    If you have no more questions related to the subject of the e2e thread, please close/verify/resolve this thread.

    Regards,
    Pavel