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.

AM62x: Linux UART driver with DMA support not working correctly when transmitter sends more than 64 characters

Other Parts Discussed in Thread: SK-AM62, TM4C1292NCPDT

Hi Bin Liu,

Based on our previous offline discussion, I modified the "/drivers/tty/serial/8250/8250_omap.c" to direct the UART RX data into the buffer setup to be directly accessed by the application instead of the TTY buffer.

https://e2e.ti.com/support/processors-group/processors/f/processors-forum/1303959/am62x-linux-uart-driver-with-dma-support

/* Must be called while priv->rx_dma_lock is held */
static void __dma_rx_do_complete(struct uart_8250_port *p)
{
	struct uart_8250_dma    *dma = p->dma;
	struct tty_port         *tty_port = &p->port.state->port;
	struct omap8250_priv	*priv = p->port.private_data;
	struct dma_chan		*rxchan = dma->rxchan;
	dma_cookie_t		cookie;
	struct dma_tx_state     state;
	int                     count;
	int			ret;
	u32			reg;

	if (!dma->rx_running)
		goto out;

	cookie = dma->rx_cookie;
	dma->rx_running = 0;

	/* Re-enable RX FIFO interrupt now that transfer is complete */
	if (priv->habit & UART_HAS_RHR_IT_DIS) {
		reg = serial_in(p, UART_OMAP_IER2);
		reg &= ~UART_OMAP_IER2_RHR_IT_DIS;
		serial_out(p, UART_OMAP_IER2, UART_OMAP_IER2_RHR_IT_DIS);
	}

	dmaengine_tx_status(rxchan, cookie, &state);

	count = dma->rx_size - state.residue + state.in_flight_bytes;

	if (count < dma->rx_size) {
		dmaengine_terminate_async(rxchan);

		/*
		 * Poll for teardown to complete which guarantees in
		 * flight data is drained.
		 */
		if (state.in_flight_bytes) {
			int poll_count = 25;

			while (dmaengine_tx_status(rxchan, cookie, NULL) &&
			       poll_count--)
				cpu_relax();

			if (poll_count == -1)
				dev_err(p->port.dev, "teardown incomplete\n");
		}
	}
	if (!count)
		goto out;
	
	if ( IRQ_NO == p->port.irq )
	{
		write_to_dmac_buffer(dma->rx_buf, count);
	}
	else
	{
		ret = tty_insert_flip_string(tty_port, dma->rx_buf, count);

		p->port.icount.rx += ret;
		p->port.icount.buf_overrun += count - ret;
	}
out:

	tty_flip_buffer_push(tty_port);
}

It works as expected when the transmitter sends data equal or less than 64 bytes.

I use below as the sample test data, which is 72 characters length.

const char *pBuf = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ9876543210";

The test will iterate to send 1 character, then 2 character, then 3 character until the full length. Then, the next iteration will start with the full length, and reducing continuously until only sending 1 character.

Below is the print out for the RX data. You can see after it reaches more than 64 characters, it starts to not work correctly. Most of the time, the extra characters will just lost, but sometimes it will receive partial of it.

It becomes intermittent and unreliable to receive data with length more than 64 characters.

01 0
02 01
03 012
04 0123
05 01234
06 012345
07 0123456
08 01234567
09 012345678
10 0123456789
11 0123456789a
12 0123456789ab
13 0123456789abc
14 0123456789abcd
15 0123456789abcde
16 0123456789abcdef
17 0123456789abcdefg
18 0123456789abcdefgh
19 0123456789abcdefghi
20 0123456789abcdefghij
21 0123456789abcdefghijk
22 0123456789abcdefghijkl
23 0123456789abcdefghijklm
24 0123456789abcdefghijklmn
25 0123456789abcdefghijklmno
26 0123456789abcdefghijklmnop
27 0123456789abcdefghijklmnopq
28 0123456789abcdefghijklmnopqr
29 0123456789abcdefghijklmnopqrs
30 0123456789abcdefghijklmnopqrst
31 0123456789abcdefghijklmnopqrstu
32 0123456789abcdefghijklmnopqrstuv
33 0123456789abcdefghijklmnopqrstuvw
34 0123456789abcdefghijklmnopqrstuvwx
35 0123456789abcdefghijklmnopqrstuvwxy
36 0123456789abcdefghijklmnopqrstuvwxyz
37 0123456789abcdefghijklmnopqrstuvwxyzA
38 0123456789abcdefghijklmnopqrstuvwxyzAB
39 0123456789abcdefghijklmnopqrstuvwxyzABC
40 0123456789abcdefghijklmnopqrstuvwxyzABCD
41 0123456789abcdefghijklmnopqrstuvwxyzABCDE
42 0123456789abcdefghijklmnopqrstuvwxyzABCDEF
43 0123456789abcdefghijklmnopqrstuvwxyzABCDEFG
44 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGH
45 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHI
46 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJ
47 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJK
48 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKL
49 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLM
50 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMN
51 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNO
52 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOP
53 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQ
54 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQR
55 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRS
56 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRST
57 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTU
58 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUV
59 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVW
60 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWX
61 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXY
62 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
63 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ9
64 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ98
65 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ98
66 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ98
67 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ98
68 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ98
69 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ98
70 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ98
71 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ98
72 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ98
72 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ98
71 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ98321
70 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ98
69 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ98
68 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ98
67 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ98
66 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ98
65 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ98
64 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ98
63 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ9
62 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
61 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXY
60 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWX
59 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVW
58 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUV
57 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTU
56 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRST
55 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRS
54 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQR
53 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQ
52 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOP
51 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNO
50 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMN
49 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLM
48 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKL
47 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJK
46 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJ
45 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHI
44 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGH
43 0123456789abcdefghijklmnopqrstuvwxyzABCDEFG
42 0123456789abcdefghijklmnopqrstuvwxyzABCDEF
41 0123456789abcdefghijklmnopqrstuvwxyzABCDE
40 0123456789abcdefghijklmnopqrstuvwxyzABCD
39 0123456789abcdefghijklmnopqrstuvwxyzABC
38 0123456789abcdefghijklmnopqrstuvwxyzAB
37 0123456789abcdefghijklmnopqrstuvwxyzA
36 0123456789abcdefghijklmnopqrstuvwxyz
35 0123456789abcdefghijklmnopqrstuvwxy
34 0123456789abcdefghijklmnopqrstuvwx
33 0123456789abcdefghijklmnopqrstuvw
32 0123456789abcdefghijklmnopqrstuv
31 0123456789abcdefghijklmnopqrstu
30 0123456789abcdefghijklmnopqrst
29 0123456789abcdefghijklmnopqrs
28 0123456789abcdefghijklmnopqr
27 0123456789abcdefghijklmnopq
26 0123456789abcdefghijklmnop
25 0123456789abcdefghijklmno
24 0123456789abcdefghijklmn
23 0123456789abcdefghijklm
22 0123456789abcdefghijkl
21 0123456789abcdefghijk
20 0123456789abcdefghij
19 0123456789abcdefghi
18 0123456789abcdefgh
17 0123456789abcdefg
16 0123456789abcdef
15 0123456789abcde
14 0123456789abcd
13 0123456789abc
12 0123456789ab
11 0123456789a
10 0123456789
09 012345678
08 01234567
07 0123456
06 012345
05 01234
04 0123
03 012
02 01
01 0

Any idea what is wrong here ? If this the same issue that the development team is fixing for the next TI SDK release ?

rgds,

kc Wong

  • Hi KC,

    Based on our previous offline discussion, I modified the "/drivers/tty/serial/8250/8250_omap.c" to direct the UART RX data into the buffer setup to be directly accessed by the application instead of the TTY buffer.

    Can you please share the changes as a patch? I cannot easily tell what change it is from the code snipet.

    Below is the print out for the RX data. You can see after it reaches more than 64 characters, it starts to not work correctly. Most of the time, the extra characters will just lost, but sometimes it will receive partial of it.

    Is this with or without DMA channels configured in kernel device tree?

    Anyway, I thought the first test you are going to do is to use the standard 8250_omap driver with the QUIRK patch I sent in the previous e2e thread to see if it works in your application, isn't it? Debugging a custom driver is not really in our support scope.

  • Hi Bin Liu,

    Below are the changes. The change is simple, it just redirects the UART RX data to another buffer.

    diff --git a/drivers/tty/serial/8250/8250_omap.c b/drivers/tty/serial/8250/8250_omap.c
    index 537bee8d2258..ab4ae6fd1b68 100644
    --- a/drivers/tty/serial/8250/8250_omap.c
    +++ b/drivers/tty/serial/8250/8250_omap.c
    @@ -31,6 +31,8 @@
     
     #include "8250.h"
     
    +#include "/home/keysight/sambashare/projects/torreys/dmms/Torreys/software/firmware/lkm/dmac_driver.c"
    +
     #define DEFAULT_CLK_SPEED	48000000
     
     #define UART_ERRATA_i202_MDR1_ACCESS	(1 << 0)
    @@ -855,10 +857,16 @@ static void __dma_rx_do_complete(struct uart_8250_port *p)
     	}
     	if (!count)
     		goto out;
    +	
    +
    +	if ( IRQ_NO != p->port.irq ||
    +		write_to_dmac_buffer(dma->rx_buf, count) < 0 )
    +	{
     	ret = tty_insert_flip_string(tty_port, dma->rx_buf, count);
     
     	p->port.icount.rx += ret;
     	p->port.icount.buf_overrun += count - ret;
    +	}
     out:
     
     	tty_flip_buffer_push(tty_port);

    And I believe the issue is nothing to do with the custom driver here. You may just enable the DMA and duplicate the issue with the standard cat and echo utility as shown below.

    root@p550:~#
    root@p550:~# stty -cread -F /dev/ttyS3
    root@p550:~# stty -echo -F /dev/ttyS3
    root@p550:~# cat /dev/ttyS3 &
    [1] 286
    root@p550:~#
    root@p550:~# echo "a" > /dev/ttyS3
    a
    root@p550:~# echo "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ9876543210" > /dev/ttyS3
    root@p550:~# 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ98
    root@p550:~# echo "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ9876543210" > /dev/ttyS3
    root@p550:~# 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ98
    
    root@p550:~# echo "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ9876543210" > /dev/ttyS3
    root@p550:~# 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ98
    root@p550:~# echo "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ9876543210" > /dev/ttyS3
    root@p550:~# 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ98
    root@p550:~# echo "a" > /dev/ttyS3
    root@p550:~# a
    
    root@p550:~# echo "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ98" > /dev/ttyS3
    root@p550:~# 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ98
    root@p550:~# echo "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" > /dev/ttyS3
    root@p550:~# 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
    
    root@p550:~# echo "a" > /dev/ttyS3
    a
    root@p550:~# echo "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ9876543210" > /dev/ttyS3
    root@p550:~# 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ98
    root@p550:~#
    

    In case you are interested, I also attached the dmac_driver.c below where write_to_dmac_buffer() function is defined.

    rgds,

    kc Wong

    #include <linux/module.h>
    #include <linux/kernel.h>
    #include <linux/fs.h>
    #include <linux/ioctl.h>
    #include <linux/miscdevice.h>
    
    #include <linux/dma-mapping.h>
    #include <linux/dmaengine.h>
    
    #include <linux/completion.h>
    
    #include "dmac_driver.h"
    
    #define DRIVER_NAME "dmac_driver"
    #define IRQ_NO 19
    
    /* --- debugfs implementation --- */
    #ifdef CONFIG_DEBUG_FS
    #include <linux/debugfs.h>
    
    static struct dentry *rootdir;
    
    static int dmac_uart_register_show(struct seq_file *s, void *data)
    {
        seq_printf(s, "TODO:\n");
        return 0;
    }
    DEFINE_SHOW_ATTRIBUTE(dmac_uart_register);
    
    static int rx_fifo = 0;
    static int rx_timeout = 0;
    static int dmac_dma_error_show(struct seq_file *s, void *data)
    {
        seq_printf(s, "rx_fifo:   %d\n", rx_fifo);
        seq_printf(s, "rx_timeout:  %d\n", rx_timeout);
    
        // reset after read
        rx_fifo = 0;
        rx_timeout = 0;
    
        return 0;
    }
    DEFINE_SHOW_ATTRIBUTE(dmac_dma_error);
    
    static void __init dmac_debugfs_init(void)
    {
    	rootdir = debugfs_create_dir(DRIVER_NAME, NULL);
    
    	/* /sys/kernel/debug/dmac_driver/uart_register */
    	debugfs_create_file("uart_register", 0444, rootdir, NULL,
    			    &dmac_uart_register_fops);
    
    	/* /sys/kernel/debug/dmac_driver/dma_error */
    	debugfs_create_file("dma_error", 0444, rootdir, NULL,
    			    &dmac_dma_error_fops);
    }
    
    static void __exit dmac_debugfs_exit(void)
    {
        debugfs_remove_recursive(rootdir);
        rootdir = NULL;
    }
    #endif	/* DEBUG_FS */
    
    typedef struct dmac
    {
        struct device *dev;
    
        // Memory allocation
        dma_addr_t rx_addr;
        void *rx_buf;
        size_t rx_size;
    	size_t readOffset;
    	size_t writeOffset;
    
        int data_length;
    
        // Synchronization
        struct completion complete;
    } dmac_t;
    
    static dmac_t *p_dmac_global = NULL;
    
    static int __init dmac_init(void);
    static void __exit dmac_exit(void);
    
    static int dmac_open(struct inode *inode, struct file *file);
    static int dmac_release(struct inode *inode, struct file *file);
    static ssize_t dmac_read(struct file *filp, char __user *buf, size_t len,loff_t * off);
    static ssize_t dmac_write(struct file *filp, const char *buf, size_t len, loff_t * off);
    static long dmac_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
    static int dmac_mmap(struct file *file_p, struct vm_area_struct *vma);
    
    static struct file_operations dmac_fops =
    {
        .owner      = THIS_MODULE,
        .read       = dmac_read,
        .write      = dmac_write,
        .open       = dmac_open,
        .release    = dmac_release,
        .unlocked_ioctl = dmac_ioctl,
        .mmap	= dmac_mmap,
    };
    
    static struct miscdevice dmac_misc_device = {
        .minor = MISC_DYNAMIC_MINOR,
        .name = "dmac",
        .fops = &dmac_fops,
    };
    
    static int __init dmac_init(void)
    {
        int error;
    
        pr_info("misc dmac: init\n");
    
        error = misc_register(&dmac_misc_device);
        if (error) {
            pr_err("misc dmac: Failed to  register misc device\n");
            return error;
        }
    
        dmac_debugfs_init();
        
        return 0;
    }
    
    static void __exit dmac_exit(void)
    {
        pr_info("misc dmac: exit\n");
    
        misc_deregister(&dmac_misc_device);
    
        dmac_debugfs_exit();
    }
    
    static int dmac_open(struct inode *inode, struct file *file)
    {
        struct device *dev = dmac_misc_device.this_device;
        dma_cap_mask_t mask;
    
        dmac_t *p_dmac = (dmac_t *) devm_kzalloc(dev, sizeof(dmac_t ), GFP_KERNEL);
        if (!p_dmac)
        {
            dev_err(p_dmac->dev, "Failed to allocate memory for dmac_t\n");
            return -1;
        }
    
        p_dmac->dev = dev;
    
        file->private_data = p_dmac;
    
        dma_cap_zero(mask);
        dma_cap_set(DMA_SLAVE | DMA_CYCLIC, mask);
    
        dma_set_coherent_mask(dev, DMA_BIT_MASK(32));
    
        init_completion (&p_dmac->complete);
    
        dev_info(p_dmac->dev, "open\n");
        return 0;
    }
    
    static int dmac_release(struct inode *inode, struct file *file)
    {
        dmac_t *p_dmac = (dmac_t *) file->private_data;
        dev_info(p_dmac->dev, "release\n");  
        return 0;
    }
    
    static ssize_t dmac_read(struct file *file, char __user *buf, size_t len, loff_t *off)
    {
        dmac_t *p_dmac = (dmac_t *) file->private_data;
        
        if (!p_dmac->rx_buf || !p_dmac->rx_addr)
        {
            dev_err(p_dmac->dev, "Memory is not allocated\n");
            return -ENOMEM;
        }
    
        len = p_dmac->data_length;
        if ( copy_to_user(buf, p_dmac->rx_buf, len) )
        {
            dev_err(p_dmac->dev, "Failed copy to user\n");
            return -EFAULT;
        }
    
        dev_dbg(p_dmac->dev, "read\n");
        return len;
    }
    
    static ssize_t dmac_write(struct file *file, const char __user *buf, size_t len, loff_t *off)
    {
        dmac_t *p_dmac = (dmac_t *) file->private_data;
        
        if (!p_dmac->rx_buf || !p_dmac->rx_addr)
        {
            dev_err(p_dmac->dev, "Memory is not allocated\n");
            return -ENOMEM;
        }
    
        if ( copy_from_user(p_dmac->rx_buf, buf, len) )
        {
            dev_err(p_dmac->dev, "Failed copy from user\n");
            return -EFAULT; 
        }
        p_dmac->data_length = len;
    
        dev_dbg(p_dmac->dev, "write\n");
        return len;
    }
    
    static long dmac_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
    {
        dmac_t *p_dmac = (dmac_t *) file->private_data;
        
        switch (cmd)
        {
        case DMAC_ALLOCATE_MEMORY:
            {
                allocate_memory_arg_t k_arg;
    
                if ( copy_from_user(&k_arg , (allocate_memory_arg_t *)arg, sizeof(k_arg)) )
                {
                    dev_err(p_dmac->dev, "Failed copy from user\n");
                    return -EFAULT;
                }
    
                p_dmac->rx_size = k_arg.i_size;
                p_dmac->rx_buf = dma_alloc_coherent(p_dmac->dev, p_dmac->rx_size, &p_dmac->rx_addr, GFP_KERNEL);
    
                k_arg.o_kernel_virtual_address = (uint64_t) p_dmac->rx_buf;
                k_arg.o_physical_address = (uint64_t) p_dmac->rx_addr;
    
                dev_info(p_dmac->dev, "DMAC_ALLOCATE_MEMORY, size = %ld virtual = 0x%llx physical = 0x%llx\n",
                        k_arg.i_size,
                        k_arg.o_kernel_virtual_address,
                        k_arg.o_physical_address);
    
                if ( copy_to_user((allocate_memory_arg_t *)arg, &k_arg, sizeof(k_arg)) )
                {
                    dev_err(p_dmac->dev, "Failed copy to user\n");
                    return -EFAULT;
                }
            }
            break;
    
        case DMAC_FREE_MEMORY:
            {
                free_memory_arg_t k_arg;
    
                if ( copy_from_user(&k_arg , (free_memory_arg_t *)arg, sizeof(k_arg)) )
                {
                    dev_err(p_dmac->dev, "Failed copy from user\n");
                    return -EFAULT;
                }
    
                dev_info(p_dmac->dev, "DMAC_FREE_MEMORY, size = %ld virtual = 0x%llx physical = 0x%llx\n",
                        k_arg.i_size,
                        k_arg.i_kernel_virtual_address,
                        k_arg.i_physical_address);
                
                dma_free_coherent(p_dmac->dev, k_arg.i_size, (void *) k_arg.i_kernel_virtual_address, (dma_addr_t) k_arg.i_physical_address);
            }
            break;
    
        case DMAC_START_DMA:
            {
                start_dma_arg_t k_arg;
    
                if ( copy_from_user(&k_arg , (start_dma_arg_t *)arg, sizeof(k_arg)) )
                {
                    dev_err(p_dmac->dev, "Failed copy from user\n");
                    return -EFAULT;
                }
    
                dev_dbg(p_dmac->dev, "DMAC_START_DMA, size = %ld block_size = %ld physical = 0x%llx\n",
                        k_arg.i_size,
                        k_arg.i_block_size,
                        k_arg.i_physical_address);
    
                if (!p_dmac->rx_buf || !p_dmac->rx_addr)
                {
                    dev_err(p_dmac->dev, "Memory is not allocated\n");
                    return -ENOMEM;
                }
    
                p_dmac_global = p_dmac; // I don't have a better way to get this pointer.
    
                if ( copy_to_user((start_dma_arg_t *)arg, &k_arg, sizeof(k_arg)) )
                {
                    dev_err(p_dmac->dev, "Failed copy to user\n");
                    return -EFAULT;
                }
            }
            break;
    
        case DMAC_WAIT_DMA_COMPLETE:
            {
                 wait_dma_complete_arg_t k_arg;
    
                 uint32_t timeout;
    
                if ( copy_from_user(&k_arg , (wait_dma_complete_arg_t *)arg, sizeof(k_arg)) )
                {
                    dev_err(p_dmac->dev, "Failed copy from user\n");
                    return -EFAULT;
                }
    
                timeout = msecs_to_jiffies(k_arg.i_millisec_wait);
                timeout = wait_for_completion_timeout(&p_dmac->complete, timeout);
    
                k_arg.o_completed_event = false;
                k_arg.o_timeout_event = false;
    
                if (timeout > 0)
                {
                    k_arg.o_completed_event = true;
                    reinit_completion(&p_dmac->complete);
                }
                else if (timeout == 0)
                {
                    k_arg.o_timeout_event = true;
                }
    
                dev_dbg(p_dmac->dev, "DMAC_WAIT_DMA_COMPLETE, millisec_wait = %d completed = %d timeout = %d\n",
                        k_arg.i_millisec_wait,
                        k_arg.o_completed_event,
                        k_arg.o_timeout_event);
    
                if ( copy_to_user((wait_dma_complete_arg_t *)arg, &k_arg, sizeof(k_arg)) )
                {
                    dev_err(p_dmac->dev, "Failed copy to user\n");
                    return -EFAULT;
                }
            }
            break;
    
        case DMAC_DISABLE_DMA:
            {
                dev_dbg(p_dmac->dev, "DMAC_DISABLE_DMA\n");
    
                p_dmac_global = NULL;
            }
            break;
    
        case DMAC_ABORT_DMA:
            {
                dev_dbg(p_dmac->dev, "DMAC_ABORT_DMA\n");
    
                p_dmac_global = NULL;
            }
            break;
    
        case DMAC_GET_DMA_STATUS:
            {
                get_dma_status_arg_t k_arg;
    
                k_arg.o_destination_address = p_dmac->rx_addr + p_dmac->writeOffset;
    
                dev_dbg(p_dmac->dev, "DMAC_GET_DMA_STATUS, destination address = %ld\n", k_arg.o_destination_address);
    
                if ( copy_to_user((get_dma_status_arg_t *)arg, &k_arg, sizeof(k_arg)) )
                {
                    dev_err(p_dmac->dev, "Failed copy to user\n");
                    return -EFAULT;
                }
            }
            break;
    
        default:
            dev_err(p_dmac->dev, "unknown ioctl command\n");
            return -ENOTTY;
        }
        return 0;
    }
    
    static int dmac_mmap(struct file *file, struct vm_area_struct *vma)
    {
        dmac_t *p_dmac = (dmac_t *) file->private_data;
    
        if (!p_dmac->rx_buf || !p_dmac->rx_addr)
        {
            dev_err(p_dmac->dev, "Memory is not allocated\n");
            return -ENOMEM;
        }
        
        dev_dbg(p_dmac->dev, "mmap, virtual = 0x%llx physical = 0x%llx\n",
                (uint64_t) p_dmac->rx_buf,
                (uint64_t) p_dmac->rx_addr);
    
        return dma_mmap_coherent(p_dmac->dev, vma,
                                 p_dmac->rx_buf, p_dmac->rx_addr,
                                 vma->vm_end - vma->vm_start);
    }
    
    static void dmac_rx_callback(void *data)
    {
        dmac_t *p_dmac = (dmac_t *)data;
        
        dev_info(p_dmac->dev, "rx_callback\n");
    
    	/* Indicate the DMA transaction completed to allow the other
    	 * thread of control to finish processing
    	 */
    	complete(&p_dmac->complete);
    }
    
    
    // To avoid the empty condition (readOffset == writeOffset),
    static const size_t IMPLEMENTATION_OVERHEAD = 1;
    
    static size_t rngNBytes(dmac_t *p_dmac)
    {
    #if 1
        int nBytes = p_dmac->writeOffset - p_dmac->readOffset;
    
        if (nBytes < 0) nBytes += p_dmac->rx_size;
    #else // less optimized implementation
        size_t nBytes;
        
        if (p_dmac->writeOffset > p_dmac->readOffset)
        {
        	nBytes = p_dmac->writeOffset - p_dmac->readOffset;
        }
        else if (p_dmac->writeOffset < p_dmac->readOffset)
        {
        	nBytes = p_dmac->writeOffset + (p_dmac->rx_size - p_dmac->readOffset);
        }
        else // p_dmac->writeOffset == p_dmac->readOffset
        {
        	nBytes = 0;
        }
    #endif
        return nBytes;
    }
    
    static size_t rngFreeBytes(dmac_t *p_dmac)
    {
       return (p_dmac->rx_size - rngNBytes(p_dmac)) - IMPLEMENTATION_OVERHEAD;
    }
    
    __maybe_unused static bool rngIsEmpty(dmac_t *p_dmac)
    {  
        return (p_dmac->readOffset == p_dmac->writeOffset);
    }
    
    __maybe_unused static bool rngIsFull(dmac_t *p_dmac)
    {
        return (0 == rngFreeBytes(p_dmac));
    }
    
    __maybe_unused static ssize_t write_to_dmac_buffer(const char *buf, size_t len)
    {
    //    size_t spaceAvailable, capacity, nBytesToEnd;
        size_t capacity, nBytesToEnd;
        
        dmac_t *p_dmac = p_dmac_global;    
        if (p_dmac == NULL) return -1;
        
        if (!p_dmac->rx_buf || !p_dmac->rx_addr)
        {
            dev_err(p_dmac->dev, "Memory is not allocated\n");
            return -ENOMEM;
        }
    #if 0 // Only make sense if the read offset is also moving.
        spaceAvailable = rngFreeBytes(p_dmac);
    
        // write to available space
        if (len > spaceAvailable) len = spaceAvailable;
    #endif
        capacity = p_dmac->rx_size;
        
        nBytesToEnd = capacity - p_dmac->writeOffset;
        
        if (len <= nBytesToEnd)
        {
            memcpy(p_dmac->rx_buf + p_dmac->writeOffset, buf, len);
        
        	p_dmac->writeOffset = p_dmac->writeOffset + len;
        	if (p_dmac->writeOffset >= capacity) p_dmac->writeOffset = 0;
        }
        else /* wrap around */
        {
            size_t remaining = len - nBytesToEnd;
            
            memcpy(p_dmac->rx_buf + p_dmac->writeOffset, buf, nBytesToEnd);
        
            memcpy(p_dmac->rx_buf, buf + nBytesToEnd, remaining);
        
        	p_dmac->writeOffset = remaining;
        	//if (p_dmac->writeOffset >= capacity) p_dmac->writeOffset = 0;
        }
    
        dmac_rx_callback(p_dmac);
    
        dev_info(p_dmac->dev, "write_to_dmac_buffer: %ld\n", len);
    
        return len;
    }
    
    module_init(dmac_init);
    module_exit(dmac_exit);
     
    MODULE_LICENSE("GPL");
    MODULE_AUTHOR("Keysight Technologies");
    MODULE_DESCRIPTION("DMAC driver");
    
    MODULE_VERSION("1:0.0");

  • Hi KC,

    And I believe the issue is nothing to do with the custom driver here. You may just enable the DMA and duplicate the issue with the standard cat and echo utility as shown below.

    This is without your write_to_dmac_buffer() patch, right?

    I think there is a known issue with DMA that the 65th byte could be dropped, but it should only drop one byte, but in your case it seems multiple bytes are dropped.

    I will have to reproduce the issue on my board and take a look. However due to other work that I am doing now, I won't be able to get on this in a couple weeks.

  • Hi Bin Liu,

    That's right. I just enabled the DMA in the device tree. And of course, without write_to_dmac_buffer() patch.

    But of course, you need to loopback the TX and RX line for this test either physically or set the loopback enable register bit (UART_MCR_LOOP).

    I loopback by setting the register bit.

    root@p550:~# echo "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ9876543210" > /dev/ttyS3
    root@p550:~# 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ98
    
    root@p550:~# echo "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ9876543210" > /dev/ttyS3
    root@p550:~# 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ98
    
    root@p550:~# echo "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ9876543210-----0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ9876543210" > /dev/ttyS3
    root@p550:~# 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ98-----0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ9876543210
    
    root@p550:~# echo "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ9876543210-----0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ9876543210" > /dev/ttyS3
    root@p550:~# 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ98---0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ9876543210
    
    root@p550:~# echo "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ9876543210-----0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ9876543210" > /dev/ttyS3
    root@p550:~# 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ98----0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ9876543210
    
    root@p550:~# echo "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ9876543210" > /dev/ttyS3
    root@p550:~# 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ98
    
    root@p550:~# echo "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ9876543210" > /dev/ttyS3
    root@p550:~# 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ98
    
    root@p550:~#
    

    I think you are right about dropping one byte, but it is true at lower baudrate like 9600. We are running at 3Mbps baudrate.

    root@p550:~# stty -F /dev/ttyS3 speed
    3000000
    root@p550:~# echo "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ9876543210" > /dev/ttyS3
    root@p550:~# 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ98
    
    root@p550:~# stty -F /dev/ttyS3 speed
    9600
    root@p550:~# echo "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ9876543210" > /dev/ttyS3
    root@p550:~# 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ986543210
    

    rgds,

    kc Wong

  • Hi KC,

    Please focus on the non-DMA mode discussed in another thread.

    I will double check the DMA mode, but I won't be able to get into it in a couple weeks.

  • Good day Bin Liu,

    Just to check any update on the 64bytes limit for DMA? 

    For the data byte drop at 65th bytes any plan from TI to have the fix? or it is a design limitation?

  • Sorry, I haven't got time for it yet. But I should start on this early next week.

  • Good day Bin Liu,

    Allow me to follow up with you on this, any update form your side?

  • Hi Sau Swang,

    Sorry for the delay. I have been working on another customer escalation, but it has been wrapped up today, so I will start to work on this case from tomorrow.

  • Thank you very much, looking forward for you good news.

  • Good day Bin Liu,

    Any updates?

  • Hi Sau Swang,

    I was able to reproduce the problem on SK-AM62 EVM. I see there are mainly two problems when sending more than 64 bytes:

    - one or more bytes from byte 65 would got lost

    - the receiving is obviously slow, more than a few msec delayed

    After discussed the problems with our sw dev team, this is a known issue on UART with DMA enabled, it happens on multiple platforms, not only AM62x. The sw dev team plans to restrucure the DMA flow in the UART driver to fix the issue, it is planned for the 2nd half of this year.

    Can you please remind me what is the reason that your project cannot use the UART without DMA?

  • Using the UART without DMA has RX overrun problem.

  • Does the UART device on the other end support hardware flow control? hw flow control would prevent RX overrun.

  • The other end does not support hardware flow control.

    And basically, the other end cannot support hardware flow control as it needs to stream measurement data continuously.

  • Hi Bin Liu, 

    Any chance the fix can be in earlier? or any mitigation plan we can used while waiting for the fix? 2nd half is too late for our side.

  • Hi Sau Swang,

    I am sorry for the delay. But the developer is busy for the current SDK9.2 release and SDK10.0 development which are already planned. He can only start working on this UART issue after he is freed up from the SDK10.0.

    Meanwhile, I keep working on this issue to see if I can come out with a quick fix, but it is not guaranteed.

  • Hi Sau Swang,

    Does your UART application mainly receive data stream at 3Mbps, and barely sending data out?

    In my testing, I disabled TX DMA, and RX issues seem go away.

  • Can you please test with the kernel patch below to see if the UART issue still exists?

    diff --git a/drivers/tty/serial/8250/8250_omap.c b/drivers/tty/serial/8250/8250_omap.c
    index 0b9c47172bc8..0952f08622c9 100644
    --- a/drivers/tty/serial/8250/8250_omap.c
    +++ b/drivers/tty/serial/8250/8250_omap.c
    @@ -1042,6 +1042,10 @@ static int omap_8250_tx_dma(struct uart_8250_port *p)
            unsigned int    skip_byte = 0;
            int ret;
     
    +       /* HACK: do not use DMA for TX */
    +       ret = -EINVAL;
    +       goto err;
    +
            if (dma->tx_running)
                    return 0;
            if (uart_tx_stopped(&p->port) || uart_circ_empty(xmit)) {

  • Thanks Bin Liu.

    I am out of office this week.

    Will test the patch when I go back office next week.

  • Hi Bin Liu,

    The issue is no longer able to duplicate with the standard cat and echo utility after applying the kernel patch.

    root@p550:~# stty -cread -F /dev/ttyS3
    root@p550:~# stty -echo -F /dev/ttyS3
    root@p550:~# cat /dev/ttyS3 &
    [1] 277
    root@p550:~# echo "a" > /dev/ttyS3
    
    
    root@p550:~# echo "a" > /dev/ttyS3
    root@p550:~# /tmp/loopback
    root@p550:~# echo "a" > /dev/ttyS3
    root@p550:~# a
    
    root@p550:~# stty -F /dev/ttyS3 speed
    0
    root@p550:~# echo "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ9876543210-----0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ9876543210" > /dev/ttyS3
    root@p550:~# 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ9876543210-----0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ9876543210
    
    root@p550:~# echo "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ9876543210-----0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ9876543210" > /dev/ttyS3
    root@p550:~# 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ9876543210-----0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ9876543210
    
    root@p550:~# echo "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ9876543210-----0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ9876543210" > /dev/ttyS3
    root@p550:~# 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ9876543210-----0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ9876543210
    

    Maybe a little bit explanation why TX DMA will affect the receiving and has to be disabled.

    Will try to do more tests with this kernel patch.

    rgds,

    kc Wong

  • Hi KC,

    Thanks for the testing, and I am glad you don't see the RX issue anymore with TX DMA disabled.

    I have not got enough time to understand why and how TX with DMA affects RX. (and I am out of office this week too.) But in the UART ISR, RX is handled first then followed by TX handling. I suspect the issue is that TX with DMA takes too much time sometimes which delays the RX handling for the next interrupt iteration, but I don't have debug analysis data to back this up.

  • Hi Bin Liu,

     

    We also try the same kernel patch with our application. But unfortunately, we still observe data missing with our application after applying the kernel patch.

    We are trying to explore an idea to make use the M4F core in this SOC to do the UART TX and RX exclusively. Not sure if it is feasible???

    What I am thinking is we will not let Linux to handle this UART with the A53 core as it is now. But dedicate the M4F core to handle that UART. The data will be in the shared memory between the A53 core and M4F core. 

    I believe the M4F core is currently not being used for anything.

    rgds,

    kc Wong

  • Hi KC,

    But unfortunately, we still observe data missing with our application after applying the kernel patch.

    When data missing happened, does it miss just one byte or multiple bytes? You still have the following UART driver patch applied, right?

    diff --git a/drivers/tty/serial/8250/8250_omap.c b/drivers/tty/serial/8250/8250_omap.c
    index 2bc6b982238f..842f3c19192e 100644
    --- a/drivers/tty/serial/8250/8250_omap.c
    +++ b/drivers/tty/serial/8250/8250_omap.c
    @@ -1337,7 +1337,7 @@ static struct omap8250_dma_params am33xx_dma = {
     static struct omap8250_platdata am654_platdata = {
            .dma_params     = &am654_dma,
            .habit          = UART_HAS_EFR2 | UART_HAS_RHR_IT_DIS |
    -                         UART_RX_TIMEOUT_QUIRK | UART_HAS_NATIVE_RS485,
    +                         UART_HAS_NATIVE_RS485,
     };
     
     static struct omap8250_platdata am33xx_platdata = {

    We are trying to explore an idea to make use the M4F core in this SOC to do the UART TX and RX exclusively. Not sure if it is feasible???

    I asked our MCU expert, yes, this should be feasible, using M4F to operate the UART and using RPMsg to pass the shared memory address to A53 Linux. However the MCU SDK which runs on M4F doesn't have DMA support on UART. I believe your application requires DMA due to high data throughput on UART, right?

  • TI's shared memory example is here:
    https://git.ti.com/cgit/rpmsg/rpmsg_char_zerocopy/

    Future readers:
    Please note that the current version of the rpmsg_char_zerocopy example is intended for Linux kernel 6.1. When we update the example for Linux SDK 10.0, the new commit will break backwards compatibility with Linux kernel 6.1. So if you are using SDK 9.x, please check that you are using the commits from 2023, not 2024.

    Regards,

    Nick

  • Hi Bin Liu,

    Yes, the patch that removes the UART_RX_TIMEOUT_QUIRK is still there. When data missing happened, it misses multiple bytes.

    As A53 core is running Linux and a lot of other stuffs, it is not dedicated to only do the UART operation, thus DMA is needed to do UART operation exclusively when using A53 core.

    But if M4F core is dedicated to do the UART operation exclusively, it should have enough processing power even without DMA support. I remember the document mentioned this M4F core can operate up to 400 MHz.

    Actually at the other end of the UART is Tiva C Series TM4C1292NCPDT, it is also M4F core but only 120 MHz.

    Thus, I hope the M4F core can be used for UART operation in interrupt mode. Hope I am right in this matter.

    rgds,

    kc Wong

  • Hi Nick,

    I believe our Linux kernel is on ti-linux-6.1.y branch. So, which SDK version should I be using?

    Actually, I have no development experience on M4F core. How should I start?


    rgds,

    kc Wong

  • Hi KC,

    Thus, I hope the M4F core can be used for UART operation in interrupt mode. Hope I am right in this matter.

    When accessed from M4F, only the MCU_UART supports interrupt mode. The MAIN domain UART do not have their interrupt events routed to MCU domain, so M4F can only operate MAIN domain UART in polling mode.

  • Hello KC,

    For more details on Bin's comment, refer to the MCU+ SDK document "Accessing main and wakeup domain peripherals from MCU domain"
    https://software-dl.ti.com/mcu-plus-sdk/esd/AM62X/09_01_00_39/exports/docs/api_guide_am62x/MAIN_DOMAIN_PERIPHERAL_FROM_MCU.html

    Linux kernel 6.1 is associated with the SDK 9.x releases.

    For getting started on M4F development, I would follow the Getting Started documentation in the MCU+ SDK:
    https://software-dl.ti.com/mcu-plus-sdk/esd/AM62X/09_01_00_39/exports/docs/api_guide_am62x/GETTING_STARTED.html

    The AM62x Academy is a great resource for learning how to develop on the part.

    For getting started on developing an M4F application to run alongside Linux A53, please follow
    AM62x Academy > Multicore module > Application Development on Remote Cores
    https://dev.ti.com/tirex/explore/node?node=A__AVn3JGT9fqm0PbS.pegO-g__AM62-ACADEMY__uiYMDcq__LATEST

    Finally, we do NOT yet have a dedicated MCU section in the AM62x academy. In the meantime, you can reference the documents in the AM62Ax academy, MCU section: 
    https://dev.ti.com/tirex/explore/node?node=A__AJi7DQYi3ubqRUvJuSJ60w__AM62A-ACADEMY__WeZ9SsL__LATEST 

    If you have specific questions during your M4F development, feel free to create a new e2e thread to discuss.

    Regards,

    Nick

  • Thanks Nick for the URLs.

    Will create a new e2e thread for further questions about the M4F core development.