"SPI bug" in at91 linux 4.9.xxx

This forum is for users of Microchip MPUs and who are interested in using Linux OS.

Moderator: nferre

Stefan J
Posts: 12
Joined: Thu Sep 15, 2016 10:07 am

"SPI bug" in at91 linux 4.9.xxx

Tue Jul 04, 2017 6:35 pm

There is a problem reading large amount of data form flash which is connected to a SAMA3D35 via SPI.


Code: Select all

[    1.420000] atmel_spi f8008000.spi: version: 0x213
[    1.420000] atmel_spi f8008000.spi: Using dma1chan0 (tx) and dma1chan1 (rx) for DMA transfers
[    1.430000] m25p80 spi32766.0: s25fl164k (8192 Kbytes)
[    1.440000] atmel_spi f8008000.spi: Atmel SPI Controller at 0xf8008000 (irq 22)
[   44.240000] ------------[ cut here ]------------
[   44.240000] WARNING: CPU: 0 PID: 629 at /home/user/myTC/poky/sek4/tmp/work-shared/sama5d3xek/kernel-source/drivers/spi/spi-atmel.c:1280 atmel_spi_transfer_one_message+0x444/0x9b8
[   44.240000] Modules linked in: sek4bsp(O)
[   44.240000] CPU: 0 PID: 629 Comm: RTSLoader Tainted: G           O    4.9.30-linux4sam_5.6-rc6 #1
[   44.240000] Hardware name: Atmel SAMA5
[   44.240000] [<c010cbfc>] (unwind_backtrace) from [<c010a71c>] (show_stack+0x10/0x14)
[   44.240000] [<c010a71c>] (show_stack) from [<c0115920>] (__warn+0xe4/0xfc)
[   44.240000] [<c0115920>] (__warn) from [<c01159e8>] (warn_slowpath_null+0x20/0x28)
[   44.240000] [<c01159e8>] (warn_slowpath_null) from [<c03eb3c8>] (atmel_spi_transfer_one_message+0x444/0x9b8)
[   44.240000] [<c03eb3c8>] (atmel_spi_transfer_one_message) from [<c03ea124>] (__spi_pump_messages+0x304/0x454)
[   44.240000] [<c03ea124>] (__spi_pump_messages) from [<c03ea3dc>] (__spi_sync+0x15c/0x16c)
[   44.240000] [<c03ea3dc>] (__spi_sync) from [<c03ea410>] (spi_sync+0x24/0x3c)
[   44.240000] [<c03ea410>] (spi_sync) from [<c03c3c4c>] (m25p80_read+0x2e4/0x370)
[   44.240000] [<c03c3c4c>] (m25p80_read) from [<c03cff80>] (spi_nor_read+0x80/0xf4)
[   44.240000] [<c03cff80>] (spi_nor_read) from [<c03ba974>] (mtd_read+0x78/0xa8)
[   44.240000] [<c03ba974>] (mtd_read) from [<c03bfc70>] (mtdchar_read+0xf8/0x230)
[   44.240000] [<c03bfc70>] (mtdchar_read) from [<c01a4af4>] (__vfs_read+0x1c/0x10c)
[   44.240000] [<c01a4af4>] (__vfs_read) from [<c01a5858>] (vfs_read+0x8c/0x118)
[   44.240000] [<c01a5858>] (vfs_read) from [<c01a6644>] (SyS_read+0x3c/0x90)
[   44.240000] [<c01a6644>] (SyS_read) from [<c01073e0>] (ret_fast_syscall+0x0/0x1c)
[   44.240000] ---[ end trace 028eaee5cd77d987 ]---
[   44.240000] m25p80 spi32766.0: spi transfer timeout
A hopefully amost working c code snipet for that:

Code: Select all

/* open source file */
fd = open("/dev/mtd8", O_NOCTTY);
if (-1 == fd)
	printf("open failed", errno);
	fd = 0;
	goto JL_FAILED;

/* detect size of source file */
mtd_info_t fd_info;
if (-1 == ioctl(fd, MEMGETINFO, &fd_info))
	printf("ioctrl[MEMGETINFO] failed", errno);
	goto JL_FAILED;
size_t flashSize = (size_t) fd_info.size;
if (0 == flashSize)
	printf("Database is empty!");
	goto JL_FAILED;
printf("EsaSystemInfo: Database is %d bytes in size. Reading from flash in %d Bytes chunks.", fd_info.size, fd_info.erasesize);

/* create buffer */
uint8_t * InternalMemPtr__ = calloc(flashSize, sizeof(uint8_t));

/* read data form device */
size_t bytesRemaining = flashSize;
uint8_t *pWritePtr = (uint8_t *)pBuffer;

	const ssize_t bytesRead = read(fd, pWritePtr, bytesRemaining > fd_info.erasesize ? fd_info.erasesize : bytesRemaining);
	if (-1 == bytesRead)
		printf("Failed to read from device '%s'! %u Bytes remaining. Error: %s", "/dev/mtd8", (unsigned int)bytesRemaining, strerror(errno));
		goto JL_FAILED;
	else if (0 == bytesRead)
		printf("Encountered EoF while trying to read %lu bytes. This may lead to errors.", bytesRemaining);
		bytesRemaining -= bytesRead;
		pWritePtr += bytesRead;
When trying to read the memory in one flush with:

Code: Select all

const ssize_t bytesRead = read(fd, pWritePtr, bytesRemaining);
I can produce the timeout warning.
In the older 4.4.xxx kernel it worked without the timeout.
I checked it for the 4.9.30,4.9.33, 4.9.34 and 4.9.35 kernels. Same error.

Hope that helps if someone has a similar problem.
Posts: 17
Joined: Thu Jan 26, 2017 12:48 pm

Re: "SPI bug" in at91 linux 4.9.xxx

Wed Jul 12, 2017 1:26 pm


interesting topic as I'm having the same issue when reading a large buffer on a peripheral connected via spi2 on sama5d4 using kernel 4.4.26-linux4sam_5.5.
Posts: 23
Joined: Fri Jan 23, 2015 3:29 am

Re: "SPI bug" in at91 linux 4.9.xxx

Wed Nov 29, 2017 8:38 pm

I think I may be experiencing the same issue with the at91sam9x5 (9G25,9G45) devices. My issue is that MTD flash reads of SPI attached devices are really slow with 4.9 compared to 4.4 and 3.2 kernels. Example:

Code: Select all

time dd if=/dev/mtdblock5 of=/tmp/uboot-env1.bin 
264+0 records in
264+0 records out
real    0m 3.20s
user    0m 0.01s
sys     0m 0.01s
This command only takes about 0.39 seconds with the 4.4 kernel. It is about 100 times slower with the 4.9 kernel.
I have an application that also reads an MTD dataflash partition (via SPI) and it now times out. It does read in smaller blocks, though, not in one go.

Anyone figure this one out?
Posts: 23
Joined: Fri Jan 23, 2015 3:29 am

Re: "SPI bug" in at91 linux 4.9.xxx

Thu Nov 30, 2017 7:50 pm

Using a logic analyzer, I have observed the following:

With the previous kernels, the SPI clock is fairly symmetrical. With the 4.9.52 kernel, it is highly asymmetrical. With the 4.9.x kernel, the high portion of the clock pulse is about the same as that in the previous kernels, but the low portion of the clock pulse is much longer (~ 100 times longer). Something has gotten buggered with the SPI clock. With some luck, I might track this down.
Posts: 1
Joined: Wed Dec 27, 2017 10:39 am

Re: "SPI bug" in at91 linux 4.9.xxx

Wed Dec 27, 2017 2:17 pm


Are you able to find the root cause/solution for this issue.
Posts: 23
Joined: Fri Jan 23, 2015 3:29 am

Re: "SPI bug" in at91 linux 4.9.xxx

Tue Jan 02, 2018 11:29 pm

No resolution yet. I have raised the issue with the Atmel FAE.
Posts: 23
Joined: Fri Jan 23, 2015 3:29 am

Re: "SPI bug" in at91 linux 4.9.xxx

Fri Feb 02, 2018 8:55 pm

I think I have found the change in the 4.9 kernel that causes this issue. It turns out there is nothing wrong with the SPI clock. Thanks to the Atmel FAE for pointing me in the right direction.

The following change was made to the Atmel SPI driver (drivers/spi/spi-atmel.c):

Code: Select all

     as->caps.is_spi2 = version > 0x121;
     as->caps.has_wdrbt = version >= 0x210;
+#ifdef CONFIG_SOC_SAM_V4_V5
+    /*
+     * Atmel SoCs based on ARM9 (SAM9x) cores should not use spi_map_buf()
+     * since this later function tries to map buffers with dma_map_sg()
+     * even if they have not been allocated inside DMA-safe areas.
+     * On SoCs based on Cortex A5 (SAMA5Dx), it works anyway because for
+     * those ARM cores, the data cache follows the PIPT model.
+     * Also the L2 cache controller of SAMA5D2 uses the PIPT model too.
+     * In case of PIPT caches, there cannot be cache aliases.
+     * However on ARM9 cores, the data cache follows the VIVT model, hence
+     * the cache aliases issue can occur when buffers are allocated from
+     * DMA-unsafe areas, by vmalloc() for instance, where cache coherency is
+     * not taken into account or at least not handled completely (cache
+     * lines of aliases are not invalidated).
+     * This is not a theorical issue: it was reproduced when trying to mount
+     * a UBI file-system on a at91sam9g35ek board.
+     */
+    as->caps.has_dma_support = false;
     as->caps.has_dma_support = version >= 0x212;
+    as->caps.has_pdc_support = version < 0x212;
This change disables DMA for SPI on the G25, G45, etc. SoCs. In non DMA mode, there is a delay between each byte in a transfer. I measured this delay between 25 and 45 us. This is the reason the transfers are much slower. If the above change is removed (as a test), the slow down goes away.

It looks like this DMA issue was encountered on our products using the G25 and G45 SoC before, but was solved in a different way (still using DMA). The work around in the Atmel SPI driver that was applied to the code I am using adjusts the data buffer in atmel_spi_dma_map_xfer() to remap any vmalloc()ed addresses to their corresponding kmapped address before handing it to dma_map_single(). I cannot state that this is a valid approach, but it does seem to work.

Code: Select all

static void *adjust_buffer_location(struct device *dev, const void *buf)
    void *ptr;

    if (likely(!is_vmalloc_addr(buf))) {
        return (void *)buf;
    } else {
        struct page *pg;

        pg = vmalloc_to_page(buf);
        if (pg == 0) {
            dev_err(dev, "failed to vmalloc_to_page\n");
            return NULL;
        ptr = page_address(pg) + ((size_t)buf & ~PAGE_MASK);
        dev_dbg(dev, "adjust_buffer_location: %p => %p\n", buf, ptr);
        return ptr;
Perhaps this will be useful.

Return to “LINUX”

Who is online

Users browsing this forum: Bing [Bot] and 2 guests