Skip to content

mtd_spi_nor: Use SFDP for SPI NOR flash parameter autodiscovery #15617

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 7 commits into
base: master
Choose a base branch
from

Conversation

bergzand
Copy link
Member

Contribution description

JEDEC has a standardized device information tables for serial flash chips. This PR adds limited support for reading these tables and configure the flash parameters based on these tables. The number of address bytes, the chip size, and supported erase sizes with wait times are discovered from the flash.

A flag is added to disable this to allow for manual configuration.

A downside of this change is that most SPI NOR flash parameters have to reside in the MCU memory.

Testing procedure

This can be tested using examples/filesystem, the board should behave as before this PR, except that less compile time configuration is required.

Some SPI NOR flash chips, such as Winbond chips, do not support this. I've added a flag so that manual configuration is still possible. This is also the case for the Mulle board.

Issues/PRs references

None.

@bergzand bergzand added Type: new feature The issue requests / The PR implemements a new feature for RIOT Area: drivers Area: Device drivers labels Dec 11, 2020
@bergzand bergzand force-pushed the pr/mtd_spi_nor/sfdp branch 3 times, most recently from b83ec2f to 290843d Compare December 11, 2020 16:19
@benpicco benpicco requested a review from vincent-d December 11, 2020 17:23
@bergzand bergzand force-pushed the pr/mtd_spi_nor/sfdp branch from a9e0601 to a7d2273 Compare December 11, 2020 17:48
@bergzand bergzand added CI: ready for build If set, CI server will compile all applications for all available boards for the labeled PR Process: API change Integration Process: PR contains or issue proposes an API change. Should be handled with care. labels Dec 11, 2020
@stale
Copy link

stale bot commented Jun 16, 2021

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. If you want me to ignore this issue, please mark it with the "State: don't stale" label. Thank you for your contributions.

@stale stale bot added the State: stale State: The issue / PR has no activity for >185 days label Jun 16, 2021
@stale stale bot closed this Jul 20, 2021
@bergzand bergzand added State: don't stale State: Tell state-bot to ignore this issue and removed State: stale State: The issue / PR has no activity for >185 days labels Nov 15, 2021
@bergzand bergzand reopened this Nov 15, 2021
@github-actions github-actions bot added the Area: boards Area: Board ports label Nov 15, 2021
@benpicco
Copy link
Contributor

benpicco commented Feb 6, 2022

I would still like to have this!
Want to give the PR a rebase?

JEDEC has a standardized device information tables for serial flash
chips. This commit adds limited support for reading these tables and
configure the flash parameters based on these tables. A flag is added to
disable this to allow for manual configuration. A downside of this
change is that most SPI NOR flash parameters have to reside in the
MCU memory.
@bergzand bergzand force-pushed the pr/mtd_spi_nor/sfdp branch from a7d2273 to 3bed1e0 Compare February 7, 2022 12:15
@bergzand
Copy link
Member Author

bergzand commented Feb 7, 2022

rebased!

@benpicco benpicco self-requested a review February 7, 2022 12:19
Copy link
Contributor

@crasbe crasbe left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some very small things I found looking through the code, otherwise everything looks good :)

mtd_jedec_id_t jedec_id; /**< JEDEC ID of the chip */

uint8_t addr_width; /**< Number of bytes in addresses, usually 3 for small devices */
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a duplicate of the addr_width member in line 168.

Comment on lines +443 to +451
/* Read the useful parameters from the basic SPI flash param table */

_read_sfdp_access(dev, param);

/* Size of the flash */
_read_sfdp_size(dev, param);

/* Erase timings */

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the /* Erase timings */ comment should be above the _read_sfdp_access() function, at least that is where the erase timings are written to the device structure.

mtd_dev_t *mtd = &dev->base;
mtd->sector_count = size / (mtd->pages_per_sector * mtd->page_size);

DEBUG("Full size: %"PRIu32", sector_count = %"PRIu32"\n", size, mtd->sector_count);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be good to add mtd_spi_sfdp: in front of this debug message as well for consistency.

Comment on lines +383 to +384
DEBUG("Erase type %u: %"PRIu32"B, inst: 0x%.2x, erase time: %"PRIu32"ms\n",
i + 1, 1LU << (access_info.erase[i].size), access_info.erase[i].instruction, _sfdp_sector_erase_timing(access_info.erase_time >> (4 + 7 * i)));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be good to add mtd_spi_sfdp: in front of this debug message as well for consistency.

size_t size_exp = access_info.erase[i].size;

if (!size_exp) {
DEBUG("Empty erase time record, skipping\n");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be good to add mtd_spi_sfdp: in front of this debug message as well for consistency.

@crasbe
Copy link
Contributor

crasbe commented Nov 21, 2024

I gave this a test with my nRF52840DK and the SFDP functionality seems to work with the on board Macronix MX25R6432F chip:

It correctly detected that it is 8MB big and has 4096 byte sectors.

Log with mtd_spi_nor debug messages enabled:
2024-11-21 15:47:26,681 # mount /nvm0
2024-11-21 15:47:26,684 # mtd_spi_nor_init: 0x20000254
2024-11-21 15:47:26,688 # mtd_spi_nor_init: -> spi: 1, cs: 11, opcodes: 0xb264
2024-11-21 15:47:26,690 # mtd_spi_nor_init: init pins
2024-11-21 15:47:26,695 # mtd_spi_nor_init: power up MTD devicemtd_spi_cmd: 0x20000254, ab
2024-11-21 15:47:26,698 # mtd_spi_read_jedec_id: rdid=0x9f
2024-11-21 15:47:26,703 # mtd_spi_cmd_read: 0x20000254, 9f, 0x20000890, 13
2024-11-21 15:47:26,706 # mtd_spi_read_jedec_id: bank=1 manuf=0xc2
2024-11-21 15:47:26,710 # mtd_spi_read_jedec_id: device=0x28, 0x17
2024-11-21 15:47:26,715 # mtd_spi_nor_init: Found chip with ID: (1, 0xc2, 0x28, 0x17)
2024-11-21 15:47:26,719 # mtd_spi_sfdp: signature: 50444653
2024-11-21 15:47:26,721 # mtd_spi_sfdp: revision 1.6, 3 headers 
2024-11-21 15:47:26,728 # mtd_spi_sfdp: param ID: ff00 revision 1.6, 64 bytes length, offset 000030
2024-11-21 15:47:26,732 # Erase type 1: 4096B, inst: 0x20, erase time: 48ms
2024-11-21 15:47:26,737 # Erase type 2: 32768B, inst: 0x52, erase time: 240ms
2024-11-21 15:47:26,742 # Erase type 3: 65536B, inst: 0xd8, erase time: 480ms
2024-11-21 15:47:26,746 # Erase type 4: 1B, inst: 0xff, erase time: 1ms
2024-11-21 15:47:26,749 # Empty erase time record, skipping
2024-11-21 15:47:26,752 # Full size: 8388608, sector_count = 2048
2024-11-21 15:47:26,759 # mtd_spi_sfdp: param ID: ffc2 revision 1.0, 16 bytes length, offset 000110
2024-11-21 15:47:26,765 # mtd_spi_sfdp: param ID: ff84 revision 1.0, 8 bytes length, offset 0000c0
2024-11-21 15:47:26,775 # mtd_spi_nor_init: 8388608 bytes (2048 sectors, 4096 bytes/sector, 32768 pages, 16 pages/sector, 256 bytes/page)
2024-11-21 15:47:26,778 # mtd_spi_nor_init: Using 3 byte addresses
2024-11-21 15:47:26,784 # mtd_spi_cmd_read: 0x20000254, 05, 0x20000890, 1
2024-11-21 15:47:26,787 # mtd_spi_nor_init: device status = 0x02
2024-11-21 15:47:26,789 # mtd_spi_cmd: 0x20000254, 06
2024-11-21 15:47:26,791 # mtd_spi_cmd: 0x20000254, 98
2024-11-21 15:47:26,797 # mtd_spi_nor_init: page_addr_mask = 0xffffff00, page_addr_shift = 8
2024-11-21 15:47:26,803 # mtd_spi_nor_init: sec_addr_mask = 0xfffff000, sec_addr_shift = 12
2024-11-21 15:47:26,807 # mtd_spi_nor_read: 0x20000254, 0x20001d50, 0x0, 0x100
2024-11-21 15:47:26,813 # mtd_spi_cmd_addr_read: 0x20000254, 03, (000000), 0x20001d50, 256
2024-11-21 15:47:26,817 # mtd_spi_cmd_addr_read: addr: 00 00 00
2024-11-21 15:47:26,821 # mtd_spi_nor_read: 0x20000254, 0x20001d50, 0x1000, 0x100
2024-11-21 15:47:26,827 # mtd_spi_cmd_addr_read: 0x20000254, 03, (001000), 0x20001d50, 256
2024-11-21 15:47:26,831 # mtd_spi_cmd_addr_read: addr: 00 10 00
2024-11-21 15:47:26,836 # mtd_spi_nor_read: 0x20000254, 0x20001d50, 0x1100, 0x100
2024-11-21 15:47:26,842 # mtd_spi_cmd_addr_read: 0x20000254, 03, (001100), 0x20001d50, 256
2024-11-21 15:47:26,845 # mtd_spi_cmd_addr_read: addr: 00 11 00
2024-11-21 15:47:26,850 # mtd_spi_nor_read: 0x20000254, 0x20001d50, 0x1000, 0x100
2024-11-21 15:47:26,856 # mtd_spi_cmd_addr_read: 0x20000254, 03, (001000), 0x20001d50, 256
2024-11-21 15:47:26,859 # mtd_spi_cmd_addr_read: addr: 00 10 00
2024-11-21 15:47:26,862 # /sda successfully mounted

However the vfs df command shows the flash size to be 2048. I think this might be due to the old RIOT version this is based on, later versions show the flash size in bytes. I created some files and the Used size didn't increase, so it's probably sectors.

Log with mtd_spi_nor debug messages enabled:
> vfs df
2024-11-21 15:52:06,972 # vfs df
2024-11-21 15:52:06,977 # Mountpoint              Total         Used    Available     Capacity
2024-11-21 15:52:06,981 # mtd_spi_nor_read: 0x20000254, 0x20001d50, 0x0, 0x100
2024-11-21 15:52:06,987 # mtd_spi_cmd_addr_read: 0x20000254, 03, (000000), 0x20001d50, 256
2024-11-21 15:52:06,990 # mtd_spi_cmd_addr_read: addr: 00 00 00
2024-11-21 15:52:06,996 # mtd_spi_nor_read: 0x20000254, 0x20001d50, 0x1000, 0x100
2024-11-21 15:52:07,001 # mtd_spi_cmd_addr_read: 0x20000254, 03, (001000), 0x20001d50, 256
2024-11-21 15:52:07,005 # mtd_spi_cmd_addr_read: addr: 00 10 00
2024-11-21 15:52:07,010 # mtd_spi_nor_read: 0x20000254, 0x20001d50, 0x1100, 0x100
2024-11-21 15:52:07,016 # mtd_spi_cmd_addr_read: 0x20000254, 03, (001100), 0x20001d50, 256
2024-11-21 15:52:07,019 # mtd_spi_cmd_addr_read: addr: 00 11 00
2024-11-21 15:52:07,024 # mtd_spi_nor_read: 0x20000254, 0x20001d50, 0x1000, 0x100
2024-11-21 15:52:07,030 # mtd_spi_cmd_addr_read: 0x20000254, 03, (001000), 0x20001d50, 256
2024-11-21 15:52:07,033 # mtd_spi_cmd_addr_read: addr: 00 10 00
2024-11-21 15:52:07,039 # /sda                     2048            2         2046       0%
2024-11-21 15:52:07,046 # /const                     27           27            0     100%

Then I tested it with the IS25LE01G, which was also successful. Likewise, it shows the occupation in sectors.

Log with mtd_spi_nor debug messages enabled:
mount /nvm0
2024-11-21 15:57:11,221 # mount /nvm0
2024-11-21 15:57:11,225 # mtd_spi_nor_init: 0x20000254
2024-11-21 15:57:11,228 # mtd_spi_nor_init: -> spi: 0, cs: 26, opcodes: 0xb264
2024-11-21 15:57:11,231 # mtd_spi_nor_init: init pins
2024-11-21 15:57:11,237 # mtd_spi_nor_init: power up MTD devicemtd_spi_cmd: 0x20000254, ab
2024-11-21 15:57:11,240 # mtd_spi_cmd: 0x20000254, 06
2024-11-21 15:57:11,242 # mtd_spi_cmd: 0x20000254, b7
2024-11-21 15:57:11,244 # mtd_spi_read_jedec_id: rdid=0x9f
2024-11-21 15:57:11,249 # mtd_spi_cmd_read: 0x20000254, 9f, 0x20000890, 13
2024-11-21 15:57:11,252 # mtd_spi_read_jedec_id: bank=1 manuf=0x9d
2024-11-21 15:57:11,256 # mtd_spi_read_jedec_id: device=0x60, 0x1b
2024-11-21 15:57:11,261 # mtd_spi_nor_init: Found chip with ID: (1, 0x9d, 0x60, 0x1b)
2024-11-21 15:57:11,264 # mtd_spi_sfdp: signature: 50444653
2024-11-21 15:57:11,267 # mtd_spi_sfdp: revision 1.6, 2 headers 
2024-11-21 15:57:11,274 # mtd_spi_sfdp: param ID: ff00 revision 1.6, 64 bytes length, offset 000030
2024-11-21 15:57:11,279 # Erase type 1: 4096B, inst: 0x20, erase time: 112ms
2024-11-21 15:57:11,283 # Erase type 2: 32768B, inst: 0x52, erase time: 144ms
2024-11-21 15:57:11,288 # Erase type 3: 65536B, inst: 0xd8, erase time: 176ms
2024-11-21 15:57:11,292 # Erase type 4: 1B, inst: 0xff, erase time: 1ms
2024-11-21 15:57:11,295 # Empty erase time record, skipping
2024-11-21 15:57:11,298 # Full size: 134217728, sector_count = 32768
2024-11-21 15:57:11,305 # mtd_spi_sfdp: param ID: ff84 revision 1.0, 8 bytes length, offset 000080
2024-11-21 15:57:11,315 # mtd_spi_nor_init: 134217728 bytes (32768 sectors, 4096 bytes/sector, 524288 pages, 16 pages/sector, 256 bytes/page)
2024-11-21 15:57:11,319 # mtd_spi_nor_init: Using 4 byte addresses
2024-11-21 15:57:11,323 # mtd_spi_cmd_read: 0x20000254, 05, 0x20000890, 1
2024-11-21 15:57:11,326 # mtd_spi_nor_init: device status = 0x02
2024-11-21 15:57:11,329 # mtd_spi_cmd: 0x20000254, 06
2024-11-21 15:57:11,331 # mtd_spi_cmd: 0x20000254, b7
2024-11-21 15:57:11,334 # mtd_spi_cmd: 0x20000254, 06
2024-11-21 15:57:11,336 # mtd_spi_cmd: 0x20000254, 98
2024-11-21 15:57:11,342 # mtd_spi_nor_init: page_addr_mask = 0xffffff00, page_addr_shift = 8
2024-11-21 15:57:11,348 # mtd_spi_nor_init: sec_addr_mask = 0xfffff000, sec_addr_shift = 12
2024-11-21 15:57:11,352 # mtd_spi_nor_read: 0x20000254, 0x20001e58, 0x0, 0x100
2024-11-21 15:57:11,358 # mtd_spi_cmd_addr_read: 0x20000254, 03, (000000), 0x20001e58, 256
2024-11-21 15:57:11,362 # mtd_spi_cmd_addr_read: addr: 00 00 00 00
2024-11-21 15:57:11,367 # mtd_spi_nor_read: 0x20000254, 0x20001e58, 0x1000, 0x100
2024-11-21 15:57:11,372 # mtd_spi_cmd_addr_read: 0x20000254, 03, (001000), 0x20001e58, 256
2024-11-21 15:57:11,376 # mtd_spi_cmd_addr_read: addr: 00 00 10 00
2024-11-21 15:57:11,381 # mtd_spi_nor_read: 0x20000254, 0x20001e58, 0x1100, 0x100
2024-11-21 15:57:11,387 # mtd_spi_cmd_addr_read: 0x20000254, 03, (001100), 0x20001e58, 256
2024-11-21 15:57:11,391 # mtd_spi_cmd_addr_read: addr: 00 00 11 00
2024-11-21 15:57:11,396 # mtd_spi_nor_read: 0x20000254, 0x20001e58, 0x1000, 0x100
2024-11-21 15:57:11,402 # mtd_spi_cmd_addr_read: 0x20000254, 03, (001000), 0x20001e58, 256
2024-11-21 15:57:11,405 # mtd_spi_cmd_addr_read: addr: 00 00 10 00
2024-11-21 15:57:11,408 # /sda successfully mounted

The IS25LP128 does not seem to work. The size was detected correctly, but the formatting failed. I

Log with mtd_spi_nor debug messages enabled:
> format /nvm0
2024-11-21 15:59:25,352 # format /nvm0
2024-11-21 15:59:25,355 # mtd_spi_nor_init: 0x20000254
2024-11-21 15:59:25,360 # mtd_spi_nor_init: -> spi: 0, cs: 26, opcodes: 0xb264
2024-11-21 15:59:25,362 # mtd_spi_nor_init: init pins
2024-11-21 15:59:25,368 # mtd_spi_nor_init: power up MTD devicemtd_spi_cmd: 0x20000254, ab
2024-11-21 15:59:25,370 # mtd_spi_cmd: 0x20000254, 06
2024-11-21 15:59:25,373 # mtd_spi_cmd: 0x20000254, b7
2024-11-21 15:59:25,376 # mtd_spi_read_jedec_id: rdid=0x9f
2024-11-21 15:59:25,380 # mtd_spi_cmd_read: 0x20000254, 9f, 0x200008a0, 13
2024-11-21 15:59:25,383 # mtd_spi_read_jedec_id: bank=1 manuf=0x9d
2024-11-21 15:59:25,387 # mtd_spi_read_jedec_id: device=0x60, 0x18
2024-11-21 15:59:25,392 # mtd_spi_nor_init: Found chip with ID: (1, 0x9d, 0x60, 0x18)
2024-11-21 15:59:25,395 # mtd_spi_sfdp: signature: 50444653
2024-11-21 15:59:25,399 # mtd_spi_sfdp: revision 1.6, 2 headers 
2024-11-21 15:59:25,405 # mtd_spi_sfdp: param ID: ff00 revision 1.6, 64 bytes length, offset 000030
2024-11-21 15:59:25,410 # Erase type 1: 4096B, inst: 0x20, erase time: 48ms
2024-11-21 15:59:25,414 # Erase type 2: 32768B, inst: 0x52, erase time: 160ms
2024-11-21 15:59:25,419 # Erase type 3: 65536B, inst: 0xd8, erase time: 304ms
2024-11-21 15:59:25,423 # Erase type 4: 1B, inst: 0xff, erase time: 1ms
2024-11-21 15:59:25,426 # Empty erase time record, skipping
2024-11-21 15:59:25,429 # Full size: 16777216, sector_count = 4096
2024-11-21 15:59:25,436 # mtd_spi_sfdp: param ID: 029d revision 1.5, 12 bytes length, offset 000080
2024-11-21 15:59:25,446 # mtd_spi_nor_init: 16777216 bytes (4096 sectors, 4096 bytes/sector, 65536 pages, 16 pages/sector, 256 bytes/page)
2024-11-21 15:59:25,449 # mtd_spi_nor_init: Using 4 byte addresses
2024-11-21 15:59:25,454 # mtd_spi_cmd_read: 0x20000254, 05, 0x200008a0, 1
2024-11-21 15:59:25,457 # mtd_spi_nor_init: device status = 0x02
2024-11-21 15:59:25,459 # mtd_spi_cmd: 0x20000254, 06
2024-11-21 15:59:25,462 # mtd_spi_cmd: 0x20000254, b7
2024-11-21 15:59:25,464 # mtd_spi_cmd: 0x20000254, 06
2024-11-21 15:59:25,467 # mtd_spi_cmd: 0x20000254, 98
2024-11-21 15:59:25,473 # mtd_spi_nor_init: page_addr_mask = 0xffffff00, page_addr_shift = 8
2024-11-21 15:59:25,478 # mtd_spi_nor_init: sec_addr_mask = 0xfffff000, sec_addr_shift = 12
2024-11-21 15:59:25,483 # mtd_spi_nor_read: 0x20000254, 0x20001e58, 0x1000, 0x100
2024-11-21 15:59:25,489 # mtd_spi_cmd_addr_read: 0x20000254, 03, (001000), 0x20001e58, 256
2024-11-21 15:59:25,493 # mtd_spi_cmd_addr_read: addr: 00 00 10 00
2024-11-21 15:59:25,497 # mtd_spi_nor_erase: 0x20000254, 0x0, 0x1000
2024-11-21 15:59:25,499 # mtd_spi_cmd: 0x20000254, 06
2024-11-21 15:59:25,504 # mtd_spi_cmd_addr_write: 0x20000254, 20, (000000), 0, 0
2024-11-21 15:59:25,508 # mtd_spi_cmd_addr_write: addr: 00 00 00 00
2024-11-21 15:59:25,512 # mtd_spi_cmd_read: 0x20000254, 05, 0x200006f7, 1
2024-11-21 15:59:25,515 # mtd_spi_nor: wait device status = 0x02
2024-11-21 15:59:25,518 # wait loop 0 times, yield 0 times
2024-11-21 15:59:25,523 # mtd_spi_nor_read: 0x20000254, 0x20001e58, 0x100, 0x100
2024-11-21 15:59:25,529 # mtd_spi_cmd_addr_read: 0x20000254, 03, (000100), 0x20001e58, 256
2024-11-21 15:59:25,532 # mtd_spi_cmd_addr_read: addr: 00 00 01 00
2024-11-21 15:59:25,538 # mtd_spi_nor_write_page: 0x20000254, 0x20001d50, 0x0, 0x0, 0x100
2024-11-21 15:59:25,541 # mtd_spi_cmd: 0x20000254, 06
2024-11-21 15:59:25,547 # mtd_spi_cmd_addr_write: 0x20000254, 02, (000000), 0x20001d50, 256
2024-11-21 15:59:25,550 # mtd_spi_cmd_addr_write: addr: 00 00 00 00
2024-11-21 15:59:25,555 # mtd_spi_cmd_read: 0x20000254, 05, 0x2000063f, 1
2024-11-21 15:59:25,558 # mtd_spi_nor: wait device status = 0x00
2024-11-21 15:59:25,561 # wait loop 0 times, yield 0 times
2024-11-21 15:59:25,566 # mtd_spi_nor_read: 0x20000254, 0x20001e58, 0x0, 0x100
2024-11-21 15:59:25,571 # mtd_spi_cmd_addr_read: 0x20000254, 03, (000000), 0x20001e58, 256
2024-11-21 15:59:25,575 # mtd_spi_cmd_addr_read: addr: 00 00 00 00
2024-11-21 15:59:25,578 # Error while formatting /sda

The formatting does not seem to work with the MX25L12873F either:

Log with mtd_spi_nor debug messages enabled:
format /nvm0
2024-11-21 16:06:40,796 # format /nvm0
2024-11-21 16:06:40,799 # mtd_spi_nor_init: 0x20000254
2024-11-21 16:06:40,802 # mtd_spi_nor_init: -> spi: 0, cs: 25, opcodes: 0xb264
2024-11-21 16:06:40,805 # mtd_spi_nor_init: init pins
2024-11-21 16:06:40,810 # mtd_spi_nor_init: power up MTD devicemtd_spi_cmd: 0x20000254, ab
2024-11-21 16:06:40,814 # mtd_spi_read_jedec_id: rdid=0x9f
2024-11-21 16:06:40,817 # mtd_spi_cmd_read: 0x20000254, 9f, 0x200008a0, 13
2024-11-21 16:06:40,821 # mtd_spi_read_jedec_id: bank=1 manuf=0xc2
2024-11-21 16:06:40,825 # mtd_spi_read_jedec_id: device=0x20, 0x18
2024-11-21 16:06:40,830 # mtd_spi_nor_init: Found chip with ID: (1, 0xc2, 0x20, 0x18)
2024-11-21 16:06:40,833 # mtd_spi_sfdp: signature: 50444653
2024-11-21 16:06:40,837 # mtd_spi_sfdp: revision 1.6, 3 headers 
2024-11-21 16:06:40,844 # mtd_spi_sfdp: param ID: ff00 revision 1.6, 64 bytes length, offset 000030
2024-11-21 16:06:40,847 # Erase type 1: 4096B, inst: 0x20, erase time: 25ms
2024-11-21 16:06:40,852 # Erase type 2: 32768B, inst: 0x52, erase time: 144ms
2024-11-21 16:06:40,856 # Erase type 3: 65536B, inst: 0xd8, erase time: 256ms
2024-11-21 16:06:40,860 # Erase type 4: 1B, inst: 0xff, erase time: 1ms
2024-11-21 16:06:40,864 # Empty erase time record, skipping
2024-11-21 16:06:40,867 # Full size: 16777216, sector_count = 4096
2024-11-21 16:06:40,874 # mtd_spi_sfdp: param ID: ffc2 revision 1.0, 16 bytes length, offset 000110
2024-11-21 16:06:40,881 # mtd_spi_sfdp: param ID: ff84 revision 1.0, 8 bytes length, offset 0000c0
2024-11-21 16:06:40,890 # mtd_spi_nor_init: 16777216 bytes (4096 sectors, 4096 bytes/sector, 65536 pages, 16 pages/sector, 256 bytes/page)
2024-11-21 16:06:40,894 # mtd_spi_nor_init: Using 4 byte addresses
2024-11-21 16:06:40,899 # mtd_spi_cmd_read: 0x20000254, 05, 0x200008a0, 1
2024-11-21 16:06:40,902 # mtd_spi_nor_init: device status = 0x40
2024-11-21 16:06:40,904 # mtd_spi_cmd: 0x20000254, 06
2024-11-21 16:06:40,906 # mtd_spi_cmd: 0x20000254, b7
2024-11-21 16:06:40,909 # mtd_spi_cmd: 0x20000254, 06
2024-11-21 16:06:40,911 # mtd_spi_cmd: 0x20000254, 98
2024-11-21 16:06:40,917 # mtd_spi_nor_init: page_addr_mask = 0xffffff00, page_addr_shift = 8
2024-11-21 16:06:40,922 # mtd_spi_nor_init: sec_addr_mask = 0xfffff000, sec_addr_shift = 12
2024-11-21 16:06:40,927 # mtd_spi_nor_read: 0x20000254, 0x20001d50, 0x1000, 0x100
2024-11-21 16:06:40,933 # mtd_spi_cmd_addr_read: 0x20000254, 03, (001000), 0x20001d50, 256
2024-11-21 16:06:40,937 # mtd_spi_cmd_addr_read: addr: 00 00 10 00
2024-11-21 16:06:40,942 # mtd_spi_nor_erase: 0x20000254, 0x0, 0x1000
2024-11-21 16:06:40,944 # mtd_spi_cmd: 0x20000254, 06
2024-11-21 16:06:40,948 # mtd_spi_cmd_addr_write: 0x20000254, 20, (000000), 0, 0
2024-11-21 16:06:40,952 # mtd_spi_cmd_addr_write: addr: 00 00 00 00
2024-11-21 16:06:40,956 # mtd_spi_cmd_read: 0x20000254, 05, 0x200006f7, 1
2024-11-21 16:06:40,959 # mtd_spi_nor: wait device status = 0x42
2024-11-21 16:06:40,963 # wait loop 0 times, yield 0 times
2024-11-21 16:06:40,968 # mtd_spi_nor_read: 0x20000254, 0x20001d50, 0x100, 0x100
2024-11-21 16:06:40,974 # mtd_spi_cmd_addr_read: 0x20000254, 03, (000100), 0x20001d50, 256
2024-11-21 16:06:40,976 # mtd_spi_cmd_addr_read: addr: 00 00 01 00
2024-11-21 16:06:40,982 # mtd_spi_nor_write_page: 0x20000254, 0x20001e58, 0x0, 0x0, 0x100
2024-11-21 16:06:40,985 # mtd_spi_cmd: 0x20000254, 06
2024-11-21 16:06:40,991 # mtd_spi_cmd_addr_write: 0x20000254, 02, (000000), 0x20001e58, 256
2024-11-21 16:06:40,994 # mtd_spi_cmd_addr_write: addr: 00 00 00 00
2024-11-21 16:06:40,999 # mtd_spi_cmd_read: 0x20000254, 05, 0x2000063f, 1
2024-11-21 16:06:41,002 # mtd_spi_nor: wait device status = 0x40
2024-11-21 16:06:41,005 # wait loop 0 times, yield 0 times
2024-11-21 16:06:41,010 # mtd_spi_nor_read: 0x20000254, 0x20001d50, 0x0, 0x100
2024-11-21 16:06:41,015 # mtd_spi_cmd_addr_read: 0x20000254, 03, (000000), 0x20001d50, 256
2024-11-21 16:06:41,019 # mtd_spi_cmd_addr_read: addr: 00 00 00 00
2024-11-21 16:06:41,024 # mtd_spi_nor_erase: 0x20000254, 0x1000, 0x1000
2024-11-21 16:06:41,026 # mtd_spi_cmd: 0x20000254, 06
2024-11-21 16:06:41,031 # mtd_spi_cmd_addr_write: 0x20000254, 20, (001000), 0, 0
2024-11-21 16:06:41,034 # mtd_spi_cmd_addr_write: addr: 00 00 10 00
2024-11-21 16:06:41,039 # mtd_spi_cmd_read: 0x20000254, 05, 0x200006f7, 1
2024-11-21 16:06:41,042 # mtd_spi_nor: wait device status = 0x42
2024-11-21 16:06:41,045 # wait loop 0 times, yield 0 times
2024-11-21 16:06:41,050 # mtd_spi_nor_read: 0x20000254, 0x20001d50, 0x1100, 0x100
2024-11-21 16:06:41,056 # mtd_spi_cmd_addr_read: 0x20000254, 03, (001100), 0x20001d50, 256
2024-11-21 16:06:41,059 # mtd_spi_cmd_addr_read: addr: 00 00 11 00
2024-11-21 16:06:41,065 # mtd_spi_nor_write_page: 0x20000254, 0x20001e58, 0x10, 0x0, 0x100
2024-11-21 16:06:41,068 # mtd_spi_cmd: 0x20000254, 06
2024-11-21 16:06:41,074 # mtd_spi_cmd_addr_write: 0x20000254, 02, (001000), 0x20001e58, 256
2024-11-21 16:06:41,077 # mtd_spi_cmd_addr_write: addr: 00 00 10 00
2024-11-21 16:06:41,082 # mtd_spi_cmd_read: 0x20000254, 05, 0x2000063f, 1
2024-11-21 16:06:41,086 # mtd_spi_nor: wait device status = 0x40
2024-11-21 16:06:41,088 # wait loop 0 times, yield 0 times
2024-11-21 16:06:41,094 # mtd_spi_nor_read: 0x20000254, 0x20001d50, 0x1000, 0x100
2024-11-21 16:06:41,099 # mtd_spi_cmd_addr_read: 0x20000254, 03, (001000), 0x20001d50, 256
2024-11-21 16:06:41,103 # mtd_spi_cmd_addr_read: addr: 00 00 10 00
2024-11-21 16:06:41,105 # Error while formatting /sda

HOWEVER, the same errors happen with the master branch version that your addition is based on. I reset it with git reset --hard 4e007d3f3e987ddf1ae0cb542685ad7439e23147

In the current master, both flash chips work as they should, so I guess something changed in the meantime and a rebase would fix it. I did not check yet how many things changed in the meantime and how hard a rebase would be.

I agree with @benpicco that this is an awesome addition to the mtd_spi_nor driver.

.wait_32k_erase = 20LU * US_PER_MS,
.wait_chip_wake_up = 1LU * US_PER_MS,
.flag = SPI_NOR_F_NO_SFDP,
.addr_width = 3,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
.addr_width = 3,

This should not be necessary and will be overwritten by _set_addr_width in mtd_spi_nor_init anyway.

@crasbe
Copy link
Contributor

crasbe commented Jan 16, 2025

This PR can actually still be rebased to the current master with limited changes and it solves the aforementioned issues.
I resolved the merge conflicts locally and removed the second addr_width

The patch was created with the following command:

RIOT$ git diff 3bed1e0e2abe91d710990d0b875ea5bb81ca78d9:drivers/mtd_spi_nor/mtd_spi_nor.c drivers/mtd_spi_nor/mtd_spi_nor.c
RIOT$ git diff 3bed1e0e2abe91d710990d0b875ea5bb81ca78d9:drivers/include/mtd_spi_nor.h drivers/include/mtd_spi_nor.h
Patch for mtd_spi_nor.c
diff --git a/drivers/mtd_spi_nor/mtd_spi_nor.c b/drivers/mtd_spi_nor/mtd_spi_nor.c
index 7ba3007132..96d217a8b8 100644
--- a/drivers/mtd_spi_nor/mtd_spi_nor.c
+++ b/drivers/mtd_spi_nor/mtd_spi_nor.c
@@ -25,15 +25,22 @@
 #include <string.h>
 #include <errno.h>
 
-#include "macros/units.h"
-#include "mtd.h"
-#include "xtimer.h"
-#include "timex.h"
-#include "thread.h"
+#include "busy_wait.h"
 #include "byteorder.h"
-#include "bitarithm.h"
+#include "kernel_defines.h"
+#include "macros/math.h"
+#include "macros/utils.h"
+#include "mtd.h"
 #include "mtd_spi_nor.h"
 #include "mtd_spi_nor/sfdp.h"
+#include "time_units.h"
+#include "thread.h"
+
+#if IS_USED(MODULE_ZTIMER)
+#include "ztimer.h"
+#elif IS_USED(MODULE_XTIMER)
+#include "xtimer.h"
+#endif
 
 #define ENABLE_DEBUG    0
 #include "debug.h"
@@ -60,8 +67,6 @@
 
 #define MBIT_AS_BYTES       ((1024 * 1024) / 8)
 
-#define MIN(a, b) ((a) > (b) ? (b) : (a))
-
 /**
  * @brief   JEDEC memory manufacturer ID codes.
  *
@@ -392,15 +397,15 @@ static void _read_sfdp_access(mtd_spi_nor_t *dev,
         size_t size = 1LU << size_exp;
         uint32_t timing = _sfdp_sector_erase_timing(access_info.erase_time >> (4 + 7 * i));
         switch (size) {
-            case KiB(4):
+            case 4096:
                 dev->wait_sector_erase = timing;
                 dev->flag |= SPI_NOR_F_SECT_4K;
                 break;
-            case KiB(32):
+            case 32768:
                 dev->wait_32k_erase = timing;
                 dev->flag |= SPI_NOR_F_SECT_32K;
                 break;
-            case KiB(64):
+            case 65536:
                 dev->wait_64k_erase = timing;
                 dev->flag |= SPI_NOR_F_SECT_64K;
                 break;
@@ -514,7 +519,8 @@ static uint32_t mtd_spi_nor_get_size(const mtd_jedec_id_t *id)
     if (mtd_spi_manuf_match(id, SPI_NOR_JEDEC_ATMEL) &&
         /* ID 2 is used to encode the product version, usually 1 or 2 */
         (id->device[1] & ~0x3) == 0) {
-        return (0x1F & id->device[0]) * MBIT_AS_BYTES;
+        /* capacity encoded as power of 32k sectors */
+        return (32 * 1024) << (0x1F & id->device[0]);
     }
     if (mtd_spi_manuf_match(id, SPI_NOR_JEDEC_MICROCHIP)) {
         switch (id->device[1]) {
@@ -543,14 +549,29 @@ static uint32_t mtd_spi_nor_get_size(const mtd_jedec_id_t *id)
     return 1 << id->device[1];
 }
 
+static void delay_us(unsigned us)
+{
+#if defined(MODULE_ZTIMER_USEC)
+    ztimer_sleep(ZTIMER_USEC, us);
+#elif defined(MODULE_ZTIMER_MSEC)
+    ztimer_sleep(ZTIMER_MSEC, DIV_ROUND_UP(us, US_PER_MS));
+#else
+    busy_wait_us(us);
+#endif
+}
+
 static inline void wait_for_write_complete(const mtd_spi_nor_t *dev, uint32_t us)
 {
     unsigned i = 0, j = 0;
-    uint32_t div = 2;
+    uint32_t div = 1; /* first wait one full interval */
+#if IS_ACTIVE(ENABLE_DEBUG)
     uint32_t diff = 0;
-    if (IS_ACTIVE(ENABLE_DEBUG) && IS_USED(MODULE_XTIMER)) {
-        diff = xtimer_now_usec();
-    }
+#endif
+#if IS_ACTIVE(ENABLE_DEBUG) && IS_USED(MODULE_ZTIMER_USEC)
+    diff = ztimer_now(ZTIMER_USEC);
+#elif IS_ACTIVE(ENABLE_DEBUG) && IS_USED(MODULE_XTIMER)
+    diff = xtimer_now_usec();
+#endif
     do {
         uint8_t status;
         mtd_spi_cmd_read(dev, dev->params->opcode->rdsr, &status, sizeof(status));
@@ -560,34 +581,31 @@ static inline void wait_for_write_complete(const mtd_spi_nor_t *dev, uint32_t us
             break;
         }
         i++;
-#if MODULE_XTIMER
         if (us) {
-            xtimer_usleep(us);
+            uint32_t wait_us = us / div;
+            uint32_t wait_min = 2;
+
+            wait_us = wait_us > wait_min ? wait_us : wait_min;
+
+            delay_us(wait_us);
             /* reduce the waiting time quickly if the estimate was too short,
              * but still avoid busy (yield) waiting */
-            if (us > 2 * XTIMER_BACKOFF) {
-                us -= (us / div);
-                div++;
-            }
-            else {
-                us = 2 * XTIMER_BACKOFF;
-            }
+            div++;
         }
         else {
             j++;
             thread_yield();
         }
-#else
-        (void)div;
-        (void) us;
-        thread_yield();
-#endif
     } while (1);
     DEBUG("wait loop %u times, yield %u times", i, j);
-    if (IS_ACTIVE(ENABLE_DEBUG) && IS_ACTIVE(MODULE_XTIMER)) {
-        diff = xtimer_now_usec() - diff;
-        DEBUG(", total wait %"PRIu32"us", diff);
-    }
+#if IS_ACTIVE(ENABLE_DEBUG)
+#if IS_USED(MODULE_ZTIMER_USEC)
+    diff = ztimer_now(ZTIMER_USEC) - diff;
+#elif IS_USED(MODULE_XTIMER)
+    diff = xtimer_now_usec() - diff;
+#endif
+    DEBUG(", total wait %"PRIu32"us", diff);
+#endif
     DEBUG("\n");
 }
 
@@ -625,20 +643,22 @@ static int mtd_spi_nor_power(mtd_dev_t *mtd, enum mtd_power_state power)
     switch (power) {
         case MTD_POWER_UP:
             mtd_spi_cmd(dev, dev->params->opcode->wake);
-#if defined(MODULE_XTIMER)
-            /* No sense in trying multiple times if no xtimer to wait between
-               reads */
-            uint8_t retries = 0;
+
+            /* fall back to polling if no timer is used */
+            unsigned retries = MTD_POWER_UP_WAIT_FOR_ID;
+            if (!IS_USED(MODULE_ZTIMER) && !IS_USED(MODULE_XTIMER)) {
+                retries *= dev->wait_chip_wake_up * 1000;
+            }
+
             int res = 0;
             do {
-                xtimer_usleep(dev->wait_chip_wake_up);
+                delay_us(dev->wait_chip_wake_up);
                 res = mtd_spi_read_jedec_id(dev, &dev->jedec_id);
-                retries++;
-            } while (res < 0 && retries < MTD_POWER_UP_WAIT_FOR_ID);
+            } while (res < 0 && --retries);
             if (res < 0) {
+                mtd_spi_release(dev);
                 return -EIO;
             }
-#endif
             /* enable 32 bit address mode */
             if (dev->addr_width == 4) {
                 _enable_32bit_addr(dev);
@@ -661,7 +681,7 @@ static void _set_addr_width(mtd_dev_t *mtd)
     uint32_t flash_size = mtd->pages_per_sector * mtd->page_size
                         * mtd->sector_count;
 
-    if (flash_size > 0xFFFFFF) {
+    if (flash_size > (0x1UL << 24)) {
         dev->addr_width = 4;
     } else {
         dev->addr_width = 3;
@@ -680,9 +700,9 @@ static int mtd_spi_nor_init(mtd_dev_t *mtd)
     _init_pins(dev);
 
     /* power up the MTD device*/
-    DEBUG("mtd_spi_nor_init: power up MTD device");
+    DEBUG_PUTS("mtd_spi_nor_init: power up MTD device");
     if (mtd_spi_nor_power(mtd, MTD_POWER_UP)) {
-        DEBUG("mtd_spi_nor_init: failed to power up MTD device");
+        DEBUG_PUTS("mtd_spi_nor_init: failed to power up MTD device");
         return -EIO;
     }
 
@@ -703,6 +723,9 @@ static int mtd_spi_nor_init(mtd_dev_t *mtd)
         mtd->sector_count = mtd_spi_nor_get_size(&dev->jedec_id)
                           / (mtd->pages_per_sector * mtd->page_size);
     }
+    /* SPI NOR is byte addressable; instances don't need to configure that */
+    assert(mtd->write_size <= 1);
+    mtd->write_size = 1;
     _set_addr_width(mtd);
 
     /* verify configuration */
@@ -795,45 +818,6 @@ static int mtd_spi_nor_read(mtd_dev_t *mtd, void *dest, uint32_t addr, uint32_t
     return 0;
 }
 
-static int mtd_spi_nor_write(mtd_dev_t *mtd, const void *src, uint32_t addr, uint32_t size)
-{
-    uint32_t total_size = mtd->page_size * mtd->pages_per_sector * mtd->sector_count;
-
-    DEBUG("mtd_spi_nor_write: %p, %p, 0x%" PRIx32 ", 0x%" PRIx32 "\n",
-          (void *)mtd, src, addr, size);
-    if (size == 0) {
-        return 0;
-    }
-    const mtd_spi_nor_t *dev = (mtd_spi_nor_t *)mtd;
-    if (size > mtd->page_size) {
-        DEBUG("mtd_spi_nor_write: ERR: page program >1 page (%" PRIu32 ")!\n", mtd->page_size);
-        return -EOVERFLOW;
-    }
-    if (dev->page_addr_mask &&
-        ((addr & dev->page_addr_mask) != ((addr + size - 1) & dev->page_addr_mask))) {
-        DEBUG("mtd_spi_nor_write: ERR: page program spans page boundary!\n");
-        return -EOVERFLOW;
-    }
-    if (addr + size > total_size) {
-        return -EOVERFLOW;
-    }
-
-    mtd_spi_acquire(dev);
-
-    /* write enable */
-    mtd_spi_cmd(dev, dev->params->opcode->wren);
-
-    /* Page program */
-    mtd_spi_cmd_addr_write(dev, dev->params->opcode->page_program, addr, src, size);
-
-    /* waiting for the command to complete before returning */
-    wait_for_write_complete(dev, 0);
-
-    mtd_spi_release(dev);
-
-    return 0;
-}
-
 static int mtd_spi_nor_write_page(mtd_dev_t *mtd, const void *src, uint32_t page, uint32_t offset,
                                   uint32_t size)
 {
@@ -942,7 +926,6 @@ static int mtd_spi_nor_erase(mtd_dev_t *mtd, uint32_t addr, uint32_t size)
 const mtd_desc_t mtd_spi_nor_driver = {
     .init = mtd_spi_nor_init,
     .read = mtd_spi_nor_read,
-    .write = mtd_spi_nor_write,
     .write_page = mtd_spi_nor_write_page,
     .erase = mtd_spi_nor_erase,
     .power = mtd_spi_nor_power,
Patch for mtd_spi_nor.h
diff --git a/drivers/include/mtd_spi_nor.h b/drivers/include/mtd_spi_nor.h
index 81b1cd9412..5b97b5626c 100644
--- a/drivers/include/mtd_spi_nor.h
+++ b/drivers/include/mtd_spi_nor.h
@@ -134,8 +134,6 @@ typedef struct {
 
     mtd_jedec_id_t jedec_id; /**< JEDEC ID of the chip */
 
-    uint8_t addr_width;      /**< Number of bytes in addresses, usually 3 for small devices */
-
     /**
      * @brief   bitmask to corresponding to the page address
      *

However, as this PR moves the timing information from the mtd_spi_nor_params_t structure (which is const) to the mtd_spi_nor_dev_t structure, additional boards have to be modified:
iotlab-m3, samr34-xpro, same54-xpro, adafruit-metro-m4-express, adafruit-pybadge, adafruit-itsybitsy-m4, adafruit-grand-central-m4-express, qn9080dk, nrf5340dk-app (and maybe more? The Seeed Xiao nRF52840 will be on the list as well, as soon as it's merged).

Unfortunately I have none of these boards, so I couldn't test whether the SFDP feature would work here, but moving the timing parameters from one structure to another and setting the SPI_NOR_F_NO_SFDP flag would be a low risk to break things.

I could create patches for the boards as well, but it would be good to get some feedback about what's the best approach before that.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Area: boards Area: Board ports Area: drivers Area: Device drivers CI: ready for build If set, CI server will compile all applications for all available boards for the labeled PR Process: API change Integration Process: PR contains or issue proposes an API change. Should be handled with care. State: don't stale State: Tell state-bot to ignore this issue Type: new feature The issue requests / The PR implemements a new feature for RIOT
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants