Stefan Jaritz
Stefan Jaritz

Reputation: 2299

How to read/write from an Flash connected through SPI in an embedded Linux (ARM)?

I am using Yocto and meta-atmel to build an embedded Linux(4.4.19). On my board is an Flash which is connected through SPI. I tried several ways to write on it. But they all failed. How to read/write data into it?

Some info:

Flashtype 4Mbit:
s25fl164k (http://www.farnell.com/datasheets/1756778.pdf)

Included through Device Tree:

    spi1: spi@f8008000 {
        cs-gpios = <&pioC 25 GPIO_ACTIVE_HIGH>;
        status = "okay";

        m25p80@0 {
            compatible = "spansion,s25fl164k";
            spi-max-frequency = <50000000>;
            reg = <0>;
        };
    };

Kernel config:

dmesg print at startup:

[    2.630000] Creating 8 MTD partitions on "atmel_nand":
[    2.640000] 0x000000000000-0x000000040000 : "bootstrap"
[    2.640000] 0x000000040000-0x0000000c0000 : "uboot"
[    2.650000] 0x0000000c0000-0x000000100000 : "env"
[    2.660000] 0x000000100000-0x000000140000 : "env_redundant"
[    2.660000] 0x000000140000-0x000000180000 : "spare"
[    2.670000] 0x000000180000-0x000000200000 : "dtb"
[    2.670000] 0x000000200000-0x000000800000 : "kernel"
[    2.680000] 0x000000800000-0x000010000000 : "rootfs"
[    2.690000] atmel_spi f0004000.spi: version: 0x213
[    2.690000] atmel_spi f0004000.spi: DMA TX channel not available, SPI unable to use DMA
[    2.700000] atmel_spi f0004000.spi: Atmel SPI Controller using PIO only
[    2.700000] atmel_spi f0004000.spi: Atmel SPI Controller at 0xf0004000 (irq 25)
[    2.710000] m25p80 spi32766.0: at25df321a (4096 Kbytes)

fdisk print (look at mtdblock8):

root@sama5d3xek:~# fdisk -l
Disk /dev/ram0: 8 MiB, 8388608 bytes, 16384 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 4096 bytes
I/O size (minimum/optimal): 4096 bytes / 4096 bytes


Disk /dev/ram1: 8 MiB, 8388608 bytes, 16384 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 4096 bytes
I/O size (minimum/optimal): 4096 bytes / 4096 bytes


Disk /dev/ram2: 8 MiB, 8388608 bytes, 16384 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 4096 bytes
I/O size (minimum/optimal): 4096 bytes / 4096 bytes


Disk /dev/ram3: 8 MiB, 8388608 bytes, 16384 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 4096 bytes
I/O size (minimum/optimal): 4096 bytes / 4096 bytes


Disk /dev/mtdblock0: 256 KiB, 262144 bytes, 512 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes


Disk /dev/mtdblock1: 512 KiB, 524288 bytes, 1024 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes


Disk /dev/mtdblock2: 256 KiB, 262144 bytes, 512 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes


Disk /dev/mtdblock3: 256 KiB, 262144 bytes, 512 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes


Disk /dev/mtdblock4: 256 KiB, 262144 bytes, 512 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes


Disk /dev/mtdblock5: 512 KiB, 524288 bytes, 1024 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes


Disk /dev/mtdblock6: 6 MiB, 6291456 bytes, 12288 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes


Disk /dev/mtdblock7: 248 MiB, 260046848 bytes, 507904 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes


Disk /dev/mtdblock8: 4 MiB, 4194304 bytes, 8192 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes


Disk /dev/mmcblk0: 7.4 GiB, 7985954816 bytes, 15597568 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: dos
Disk identifier: 0x00000000

Device         Boot Start      End  Sectors  Size Id Type
/dev/mmcblk0p1       8192 15597567 15589376  7.4G  b W95 FAT32

Reading/writing test:

cat /dev/mtdblock8
echo "hello" > /dev/mtdblock8
cat /dev/mtdblock8

I don't get any results/errors.

Mounting:

mkdir /tmp/abc
mount -t jffs2 /dev/mtdblock8 /tmp/abc
mount: /dev/mtdblock8: can't read superblock

Any ideas?

I like to do a demo. Let's say write "hello Linux" on position 12345 at the SPI flash.

Upvotes: 3

Views: 26584

Answers (7)

Dražen Grašovec
Dražen Grašovec

Reputation: 802

There is a lot of info missing to be sure what is happening in your system.

You say you want to write to your SPI FLASH device

[    2.630000] Creating 8 MTD partitions on "atmel_nand":
[    2.640000] 0x000000000000-0x000000040000 : "bootstrap"
[    2.640000] 0x000000040000-0x0000000c0000 : "uboot"
[    2.650000] 0x0000000c0000-0x000000100000 : "env"
[    2.660000] 0x000000100000-0x000000140000 : "env_redundant"
[    2.660000] 0x000000140000-0x000000180000 : "spare"
[    2.670000] 0x000000180000-0x000000200000 : "dtb"
[    2.670000] 0x000000200000-0x000000800000 : "kernel"
[    2.680000] 0x000000800000-0x000010000000 : "rootfs"

cat /dev/mtdblock8
echo "hello" > /dev/mtdblock8
cat /dev/mtdblock8

But those 8 MTD partitions looks like belong to NAND FLASH and m25c80 is device driver for SPI NOR FLASH device.

In order to bind device to spi-nor kernel driver you need a "jedec,spi-nor" in your compatible device tree property.

Also /dev/mtdblock8 is a block device, useful for storing a filesystem. You cannot write just a couple of bytes. You can read and write in multiple og blocks, which are e.g. 512 bytes. If you want to write a string or array of bytes you should better use character device /dev/mtd8 representation instead:

The second interface is a /dev/mtdblockX block device, handled by the mtdblock driver. This device is mostly used to mount MTD filesystems, such as JFFS2 and YAFFS2, because the mount command primarily works with block devices. You may be tempted to use this device to write to the MTD device, but the corresponding driver isn’t elaborate enough for use in production. When you attempt to write to a given block, the previous contents are copied to RAM, the MTD block is erased, and the updated contents are written to the block. As you can see, there is no wear leveling of any sort, as a series of writes to the same part of the block device could very quickly damage the corresponding erase blocks.

mount -t jffs2 /dev/mtdblock8 /tmp/abc

Also if your partition was not formatted, you have to blank your filesystem before mounting it, because a blank JFFS2 filesystem consists entirely of free blocks.

flash_eraseall -j /dev/mtd8

Upvotes: 1

Stefan Jaritz
Stefan Jaritz

Reputation: 2299

There was an error in my device tree table file. The spi1, the image sensor interface(isi1) and the i2c(i2c1) where using the same pins. When compiling the kernel + dts there are no error shown. In general the combination of fdisk + mtd_debug was very useful to check the driver and the hardware at low level. In my case SAMA5D35 + own mainboard @ dts:

ahb {
    apb {
        spi1: spi@f8008000 {
            cs-gpios = <&pioC 25 GPIO_ACTIVE_LOW>;
            status = "okay";
            m25p80@0 {
                compatible = "spansion,s25fl132k", "jedec,spi-nor";
                spi-max-frequency = <108000000>;
                reg = <0>;
                m25p,fast-read;
            };
        };

        // conflicts with spi1 
        i2c1: i2c@f0018000 {
            status = "disabled";
        };
        // confilcts with spi1 (pin PC27 periph B TWCK1 pin, conflicts with SPI1_NPCS2, ISI_D10)
        isi: isi@f0034000 {
            status = "disabled";
        };
};

now it works fine

Upvotes: 2

Joe Kul
Joe Kul

Reputation: 2534

Perhaps the device is locked by U-Boot, and ioctl UNLOCK is not implemented in your kernel's m25p80 driver. I've seen that before, refer to erasing-flash-nor-ioctlmemunlock-return-status.

Upvotes: 3

Aivars
Aivars

Reputation: 93

You can use mtd_debug command tool. With this tool is possible to test read/write one byte. This is very convenient to localize problem.

Upvotes: 1

Aivars
Aivars

Reputation: 93

You can try lower clock frequency, this solve my problems with SPI FLASH:

spi-max-frequency = <10000000>

Upvotes: 0

Ezequiel Garcia
Ezequiel Garcia

Reputation: 1057

So, let's go step by step. Your SPI NOR flash is described in the devicetree, and it seems you've managed to configure your kernel correctly (that is, add the relevant drivers). This is confirmed by your log:

[    2.710000] m25p80 spi32766.0: at25df321a (4096 Kbytes)

It would also seem true that /dev/mtd8 is the MTD device associated with that device (from your size analysis). You should be able to confirm it by inspecting /sys/class/mtd.

Now, in order to program the device you need to 1) erase the sectors you want to write, 2) write those sectors, and finally read back and confirm. To write, you can use a write() syscall (i.e. cat somefile > /dev/mtd8). To erase you need an ioctl syscall, i.e. flash_erase command.

The MTD website has some relevant information:

http://www.linux-mtd.infradead.org/index.html

The Free-Electrons post mentioned in hashdefine reply is fine too.

Upvotes: 2

hashdefine
hashdefine

Reputation: 344

You can use Memory technology Devices (MTD) subsystem for erase/write/read operation on flash partitions. The SPI flash is mounted to mtdblock8 in your case.Use the below command to see all the existing partitions

cat /proc/mtd

To write to the mtd device, use nandwrite command. It is available with busybox.

For mounting try

mount -t jffs2 /dev/block/mtdblock8 /tmp/abc

Details on MTD: http://free-electrons.com/blog/managing-flash-storage-with-linux/

Details on mtd utils: http://processors.wiki.ti.com/index.php/Mtdutils

Upvotes: 1

Related Questions