Based on kernel version 7.2. Page generated on 2026-08-20 08:41 EST.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 |
=================
SPI NOR framework
=================
How to propose a new flash addition
-----------------------------------
Most SPI NOR flashes comply with the JEDEC JESD216
Serial Flash Discoverable Parameter (SFDP) standard. SFDP describes
the functional and feature capabilities of serial flash devices in a
standard set of internal read-only parameter tables.
The SPI NOR driver queries the SFDP tables in order to determine the
flash's parameters and settings. If the flash defines the SFDP tables
it's likely that you won't need a flash entry at all, and instead
rely on the generic flash driver which probes the flash solely based
on its SFDP data. All one has to do is to specify the "jedec,spi-nor"
compatible in the device tree.
There are cases however where you need to define an explicit flash
entry. This typically happens when the flash has settings or support
that is not covered by the SFDP tables (e.g. Block Protection), or
when the flash contains mangled SFDP data. If the later, one needs
to implement the ``spi_nor_fixups`` hooks in order to amend the SFDP
parameters with the correct values.
Minimum testing requirements
-----------------------------
Do all the tests from below and paste them in the commit's comments
section, after the ``---`` marker.
1) Specify the controller that you used to test the flash and specify
the frequency at which the flash was operated, e.g.::
This flash is populated on the X board and was tested at Y
frequency using the Z (put compatible) SPI controller.
2) Dump the sysfs entries and print the md5/sha1/sha256 SFDP checksum::
root@1:~# cat /sys/bus/spi/devices/spi0.0/spi-nor/partname
sst26vf064b
root@1:~# cat /sys/bus/spi/devices/spi0.0/spi-nor/jedec_id
bf2643
root@1:~# cat /sys/bus/spi/devices/spi0.0/spi-nor/manufacturer
sst
root@1:~# xxd -p /sys/bus/spi/devices/spi0.0/spi-nor/sfdp
53464450060102ff00060110300000ff81000106000100ffbf0001180002
0001fffffffffffffffffffffffffffffffffd20f1ffffffff0344eb086b
083b80bbfeffffffffff00ffffff440b0c200dd80fd810d820914824806f
1d81ed0f773830b030b0f7ffffff29c25cfff030c080ffffffffffffffff
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
ffffffffffffffffffffffffffffffffff0004fff37f0000f57f0000f9ff
7d00f57f0000f37f0000ffffffffffffffffffffffffffffffffffffffff
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
ffffbf2643ffb95ffdff30f260f332ff0a122346ff0f19320f1919ffffff
ffffffff00669938ff05013506040232b03072428de89888a585c09faf5a
ffff06ec060c0003080bffffffffff07ffff0202ff060300fdfd040700fc
0300fefe0202070e
root@1:~# sha256sum /sys/bus/spi/devices/spi0.0/spi-nor/sfdp
428f34d0461876f189ac97f93e68a05fa6428c6650b3b7baf736a921e5898ed1 /sys/bus/spi/devices/spi0.0/spi-nor/sfdp
Please dump the SFDP tables using ``xxd -p``. It enables us to do
the reverse operation and convert the hexdump to binary with
``xxd -rp``. Dumping the SFDP data with ``hexdump -Cv`` is accepted,
but less desirable.
3) Dump debugfs data::
root@1:~# cat /sys/kernel/debug/spi-nor/spi0.0/capabilities
Supported read modes by the flash
1S-1S-1S
opcode 0x03
mode cycles 0
dummy cycles 0
1S-1S-1S (fast read)
opcode 0x0b
mode cycles 0
dummy cycles 8
1S-1S-2S
opcode 0x3b
mode cycles 0
dummy cycles 8
1S-2S-2S
opcode 0xbb
mode cycles 4
dummy cycles 0
1S-1S-4S
opcode 0x6b
mode cycles 0
dummy cycles 8
1S-4S-4S
opcode 0xeb
mode cycles 2
dummy cycles 4
4S-4S-4S
opcode 0x0b
mode cycles 2
dummy cycles 4
Supported page program modes by the flash
1S-1S-1S
opcode 0x02
root@1:~# cat /sys/kernel/debug/spi-nor/spi0.0/params
name sst26vf064b
id bf 26 43 bf 26 43
size 8.00 MiB
write size 1
page size 256
address nbytes 3
flags HAS_LOCK | HAS_16BIT_SR | SOFT_RESET | SWP_IS_VOLATILE
opcodes
read 0xeb
dummy cycles 6
erase 0x20
program 0x02
8D extension none
protocols
read 1S-4S-4S
write 1S-1S-1S
register 1S-1S-1S
erase commands
20 (4.00 KiB) [0]
d8 (8.00 KiB) [1]
d8 (32.0 KiB) [2]
d8 (64.0 KiB) [3]
c7 (8.00 MiB)
sector map
region (in hex) | erase mask | flags
------------------+------------+----------
00000000-00007fff | [01 ] |
00008000-0000ffff | [0 2 ] |
00010000-007effff | [0 3] |
007f0000-007f7fff | [0 2 ] |
007f8000-007fffff | [01 ] |
4) Use `mtd-utils <https://git.infradead.org/mtd-utils.git>`__
and verify that erase, read and page program operations work fine::
root@1:~# dd if=/dev/urandom of=./spi_test bs=1M count=2
2+0 records in
2+0 records out
2097152 bytes (2.1 MB, 2.0 MiB) copied, 0.848566 s, 2.5 MB/s
root@1:~# mtd_debug erase /dev/mtd0 0 2097152
Erased 2097152 bytes from address 0x00000000 in flash
root@1:~# mtd_debug read /dev/mtd0 0 2097152 spi_read
Copied 2097152 bytes from address 0x00000000 in flash to spi_read
root@1:~# hexdump spi_read
0000000 ffff ffff ffff ffff ffff ffff ffff ffff
*
0200000
root@1:~# sha256sum spi_read
4bda3a28f4ffe603c0ec1258c0034d65a1a0d35ab7bd523a834608adabf03cc5 spi_read
root@1:~# mtd_debug write /dev/mtd0 0 2097152 spi_test
Copied 2097152 bytes from spi_test to address 0x00000000 in flash
root@1:~# mtd_debug read /dev/mtd0 0 2097152 spi_read
Copied 2097152 bytes from address 0x00000000 in flash to spi_read
root@1:~# sha256sum spi*
c444216a6ba2a4a66cccd60a0dd062bce4b865dd52b200ef5e21838c4b899ac8 spi_read
c444216a6ba2a4a66cccd60a0dd062bce4b865dd52b200ef5e21838c4b899ac8 spi_test
If the flash comes erased by default and the previous erase was ignored,
we won't catch it, thus test the erase again::
root@1:~# mtd_debug erase /dev/mtd0 0 2097152
Erased 2097152 bytes from address 0x00000000 in flash
root@1:~# mtd_debug read /dev/mtd0 0 2097152 spi_read
Copied 2097152 bytes from address 0x00000000 in flash to spi_read
root@1:~# sha256sum spi*
4bda3a28f4ffe603c0ec1258c0034d65a1a0d35ab7bd523a834608adabf03cc5 spi_read
c444216a6ba2a4a66cccd60a0dd062bce4b865dd52b200ef5e21838c4b899ac8 spi_test
Dump some other relevant data::
root@1:~# mtd_debug info /dev/mtd0
mtd.type = MTD_NORFLASH
mtd.flags = MTD_CAP_NORFLASH
mtd.size = 8388608 (8M)
mtd.erasesize = 4096 (4K)
mtd.writesize = 1
mtd.oobsize = 0
regions = 0
5) If your flash supports locking, please go through the following test
procedure to make sure it correctly behaves. The below example
expects the typical situation where eraseblocks and lock sectors have
the same size. In case you enabled MTD_SPI_NOR_USE_4K_SECTORS, you
must adapt `bs` accordingly.
Warning: These tests may hard lock your device! Make sure:
- The device is not hard locked already (#WP strapped to low and
SR_SRWD bit set)
- If you have a WPn pin, you may want to set `no-wp` in your DT for
the time of the test, to only make use of software protection.
Otherwise, clearing the locking state depends on the WPn
signal and if it is tied to low, the flash will be permanently
locked.
Test full chip locking and make sure expectations, the MEMISLOCKED
ioctl output, the debugfs output and experimental results are all
aligned::
root@1:~# alias show_sectors='grep -A4 "locked sectors" /sys/kernel/debug/spi-nor/spi0.0/params'
root@1:~# flash_lock -u /dev/mtd0
root@1:~# flash_lock -i /dev/mtd0
Device: /dev/mtd0
Start: 0
Len: 0x4000000
Lock status: unlocked
Return code: 0
root@1:~# mtd_debug erase /dev/mtd0 0 2097152
Erased 2097152 bytes from address 0x00000000 in flash
root@1:~# mtd_debug write /dev/mtd0 0 2097152 spi_test
Copied 2097152 bytes from spi_test to address 0x00000000 in flash
root@1:~# mtd_debug read /dev/mtd0 0 2097152 spi_read
Copied 2097152 bytes from address 0x00000000 in flash to spi_read
root@1:~# sha256sum spi*
c444216a6ba2a4a66cccd60a0dd062bce4b865dd52b200ef5e21838c4b899ac8 spi_read
c444216a6ba2a4a66cccd60a0dd062bce4b865dd52b200ef5e21838c4b899ac8 spi_test
root@1:~# show_sectors
software locked sectors
region (in hex) | status | #sectors
------------------+----------+---------
00000000-03ffffff | unlocked | 1024
root@1:~# flash_lock -l /dev/mtd0
root@1:~# flash_lock -i /dev/mtd0
Device: /dev/mtd0
Start: 0
Len: 0x4000000
Lock status: locked
Return code: 1
root@1:~# mtd_debug erase /dev/mtd0 0 2097152
Erased 2097152 bytes from address 0x00000000 in flash
root@1:~# mtd_debug read /dev/mtd0 0 2097152 spi_read
Copied 2097152 bytes from address 0x00000000 in flash to spi_read
root@1:~# sha256sum spi*
c444216a6ba2a4a66cccd60a0dd062bce4b865dd52b200ef5e21838c4b899ac8 spi_read
c444216a6ba2a4a66cccd60a0dd062bce4b865dd52b200ef5e21838c4b899ac8 spi_test
root@1:~# dd if=/dev/urandom of=./spi_test2 bs=1M count=2
2+0 records in
2+0 records out
root@1:~# mtd_debug write /dev/mtd0 0 2097152 spi_test2
Copied 2097152 bytes from spi_test2 to address 0x00000000 in flash
root@1:~# mtd_debug read /dev/mtd0 0 2097152 spi_read2
Copied 2097152 bytes from address 0x00000000 in flash to spi_read2
root@1:~# sha256sum spi*
c444216a6ba2a4a66cccd60a0dd062bce4b865dd52b200ef5e21838c4b899ac8 spi_read
c444216a6ba2a4a66cccd60a0dd062bce4b865dd52b200ef5e21838c4b899ac8 spi_read2
c444216a6ba2a4a66cccd60a0dd062bce4b865dd52b200ef5e21838c4b899ac8 spi_test
bea9334df51c620440f86751cba0799214a016329f1736f9456d40cf40efdc88 spi_test2
root@1:~# show_sectors
software locked sectors
region (in hex) | status | #sectors
------------------+----------+---------
00000000-03ffffff | locked | 1024
root@1:~# flash_lock -u /dev/mtd0
Once we trust the debugfs output we can use it to test various
situations. Check top locking/unlocking (end of the device)::
root@1:~# size=$(cat /sys/class/mtd/mtd0/size)
root@1:~# bs=$(cat /sys/class/mtd/mtd0/erasesize)
root@1:~# nsectors=$(grep unlocked /sys/kernel/debug/spi-nor/spi0.0/params | sed -e 's/.*unlocked | //')
root@1:~# ss=$(($size / $nsectors))
root@1:~# bps=$(($ss / $bs))
root@1:~# flash_lock -u /dev/mtd0
root@1:~# flash_lock -l /dev/mtd0 $(($size - (2 * $ss))) $((2 * $bps)) # last two
root@1:~# show_sectors
software locked sectors
region (in hex) | status | #sectors
------------------+----------+---------
00000000-03fdffff | unlocked | 1022
03fe0000-03ffffff | locked | 2
root@1:~# flash_lock -u /dev/mtd0 $(($size - (2 * $ss))) $((1 * $bps)) # last one
root@1:~# show_sectors
software locked sectors
region (in hex) | status | #sectors
------------------+----------+---------
00000000-03feffff | unlocked | 1023
03ff0000-03ffffff | locked | 1
If the flash features 4 block protection bits (BP), we can protect
more than 4MB (typically 128 64kiB-blocks or more), with a finer
grain than locking the entire device::
root@1:~# flash_lock -u /dev/mtd0
root@1:~# flash_lock -l /dev/mtd0 $(($size - (2**7 * $ss))) $((2**7 * $bps))
root@1:~# show_sectors
software locked sectors
region (in hex) | status | #sectors
------------------+----------+---------
00000000-037fffff | unlocked | 896
03800000-03ffffff | locked | 128
If the flash features a Top/Bottom (TB) bit, we can protect the
beginning of the flash::
root@1:~# flash_lock -u /dev/mtd0
root@1:~# flash_lock -l /dev/mtd0 0 $((2 * $bps)) # first two
root@1:~# show_sectors
software locked sectors
region (in hex) | status | #sectors
------------------+----------+---------
00000000-0001ffff | locked | 2
00020000-03ffffff | unlocked | 1022
root@1:~# flash_lock -u /dev/mtd0 $ss $((1 * $bps)) # first one
root@1:~# show_sectors
software locked sectors
region (in hex) | status | #sectors
------------------+----------+---------
00000000-0000ffff | locked | 1
00010000-03ffffff | unlocked | 1023
If the flash features a Complement (CMP) bit, we can protect with
more granularity above half of the capacity. Let's lock all but one
block, then unlock one more block::
root@1:~# all_but_one=$((($size / $bs) - ($ss / $bs)))
root@1:~# flash_lock -u /dev/mtd0
root@1:~# flash_lock -l /dev/mtd0 $ss $all_but_one # all but the first
root@1:~# show_sectors
software locked sectors
region (in hex) | status | #sectors
------------------+----------+---------
00000000-0000ffff | unlocked | 1
00010000-03ffffff | locked | 1023
root@1:~# flash_lock -u /dev/mtd0 $ss $(($ss / $bs)) # all but the two first
root@1:~# show_sectors
software locked sectors
region (in hex) | status | #sectors
------------------+----------+---------
00000000-0001ffff | unlocked | 2
00020000-03ffffff | locked | 1022
root@1:~# flash_lock -u /dev/mtd0
root@1:~# flash_lock -l /dev/mtd0 0 $all_but_one # same from the other side
root@1:~# show_sectors
software locked sectors
region (in hex) | status | #sectors
------------------+----------+---------
00000000-03feffff | locked | 1023
03ff0000-03ffffff | unlocked | 1
root@1:~# flash_lock -u /dev/mtd0 $(($size - (2 * $ss))) $(($ss / $bs)) # all but two
root@1:~# show_sectors
software locked sectors
region (in hex) | status | #sectors
------------------+----------+---------
00000000-03fdffff | locked | 1022
03fe0000-03ffffff | unlocked | 2
|