Search code examples
linuxlinux-kernelembedded-linuxkernel-moduleflash-memory

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:

  • MTD devices activated
  • SPI activated
  • SPI device driver activated

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.


Solution

  • 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