Skip to content

Add MSPI + flash driver for the TI K3 platform #88487

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

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from

Conversation

m-braunschweig
Copy link
Contributor

@m-braunschweig m-braunschweig commented Apr 11, 2025

This PR adds support for MSPI and the Infineon S25H flash on the TI K3 platform. It is tested with the onboard flash of the AM2434 launchpad. (PR for the driver flash driver: #88446). It's now part of this PR.

For running the code on the AM243x Launchpad this branch is used. Until the AM243x Launchpad is generally supported this shouldn't be merged. However I would still like some early review to speed up merging later. Especially on how to potentially handle the timeout parameter better and whether I interpreted the unit being microseconds right.

The driver itself is pretty basic (e.g. no async and XIP support) but enough for running MCUboot on the board (by loading the image into SRAM).

Edit: Mention that this PR also includes the S25H flash driver now

@m-braunschweig m-braunschweig marked this pull request as draft April 11, 2025 08:08
@github-actions github-actions bot added area: MSPI platform: TI K3 Texas Instruments Keystone 3 Processors platform: TI SimpleLink Texas Instruments SimpleLink MCU labels Apr 11, 2025
uint32_t exec_status =
MSPI_TI_K3_REG_READ_MASKED(FLASH_CMD_CTRL, CMD_EXEC_STATUS, base_address);
while (exec_status != 0 &&
k_cyc_to_us_floor32(k_uptime_get() - start_cycles) < req->timeout) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

k_uptime_get is the current uptime in milliseconds, calling k_cyc_to_us_floor32 is probably not what you want.
The timeout parameter is not well documented, but it appears to be in milliseconds (from looking at other drivers)

Copy link
Collaborator

Choose a reason for hiding this comment

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

It is defined in kconfig

int "Completion timeout tolerance (ms)"

Copy link
Collaborator

Choose a reason for hiding this comment

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

Ahh my bad, I'm not familiar with mspi driver API and only looked at the struct definition. It would be helpful to mention it there too

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ok I will update that to use milliseconds everywhere in the MSPI driver. I will also switch over to k_uptime_get so I don't need the conversions anymore

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Also: How are the Kconfig option and timeout field correlated? For me it seems quite unclear since it's used both in flash and MSPI drivers, additionally to the already existing timeout field

Copy link
Collaborator

@swift-tk swift-tk Apr 16, 2025

Choose a reason for hiding this comment

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

Also: How are the Kconfig option and timeout field correlated? For me it seems quite unclear since it's used both in flash and MSPI drivers, additionally to the already existing timeout field

The Kconfig option is offered as a setting to the controllers for its maximum timeout allowence. The timeout field is variable in the transfer struct but should not exceed this Kconfig option.

MSPI_TI_K3_REG_WRITE(0, CONFIG, RESET_PIN, base_addr);

/* general clock cycle delays */
MSPI_TI_K3_REG_WRITE(TI_K3_OSPI_DEFAULT_DELAY, DEV_DELAY, D_NSS, base_addr);
Copy link
Collaborator

Choose a reason for hiding this comment

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

Seems like the chip select delay should come from struct mspi_ce_control

Copy link
Collaborator

Choose a reason for hiding this comment

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

No, not really. struct mspi_ce_control is for software controlled CE. However, I do think that this CE timing maybe peripheral device specific but not transfer specific. NXP have a similar setting as well. We can try add that to struct mspi_dev_cfg
Another option is to slide it in through struct mspi_timing_cfg along with your custom timing parameters.

Copy link
Contributor Author

@m-braunschweig m-braunschweig Apr 15, 2025

Choose a reason for hiding this comment

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

For now I'm going to add it to the mspi_dev_cfg to be read from the devicetree
I'm going to add a custom timing config

#include <zephyr/sys/util.h>
#include <zephyr/sys/util_macro.h>

LOG_MODULE_REGISTER(flash_ti_k3_mspi, CONFIG_MSPI_LOG_LEVEL);
Copy link
Collaborator

Choose a reason for hiding this comment

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

There are a number of mentions of "flash" in this file that should be removed.
This driver may be used for things other than flash memory

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Will update that. They are there because it initially started as a standalone flash driver

int mspi_ti_k3_wait_for_idle(const struct device *controller)
{
const mem_addr_t base_addr = DEVICE_MMIO_GET(controller);
uint32_t idle = MSPI_TI_K3_REG_READ_MASKED(CONFIG, IDLE, base_addr);
Copy link
Collaborator

Choose a reason for hiding this comment

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

Consider using GENMASK/FIELD_PREP/FIELD_GET, the macro concatenations (TI_K3_OSPI_##reg##_##field##_FLD_OFFSET) make it difficult to navigate to the register definitions

#define TI_K3_OSPI_CONFIG_REG_IDLE_MASK GENMASK(31, 31)

uint32_t config_reg = sys_read32(base_addr + TI_K3_OSPI_CONFIG_REG_OFFSET);
uint32_t idle = FIELD_GET(TI_K3_OSPI_CONFIG_REG_IDLE_MASK, config_reg);

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I would prefer to keep the macro since it keeps the code in the mspi_ti_k3.c quite short. I think I will add comments that explains the macros and register definitions

};

struct mspi_ti_k3_data {
struct k_mutex lock;
Copy link
Collaborator

Choose a reason for hiding this comment

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

Add DEVICE_MMIO_RAM; as the first field

Copy link
Collaborator

@swift-tk swift-tk left a comment

Choose a reason for hiding this comment

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

Are you not going to add any soc DTS for the controller?
To ensure compile across PRs, it is best to add the controller to one of the example or testcase.

ce-gpios:
description: |
The used chip select GPIO pins. This property is not used since the driver
only supports the hardware controlled chip select pins.
Copy link
Collaborator

Choose a reason for hiding this comment

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

It is used as part of device_id.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Can you explain what you exactly mean with that?

Currently this driver uses hardware implemented chip select in the OSPI_CONFIG_REG via the PERIPH_CS_LINES_FLD field. There are 4 dedicated pins for that and they are only referenced by the numbers 0..3, which I directly use in the mspi_ti_k3_dev_config function. When I would use ce-gpios with phandle to the GPIO pins I think it would be necassary to do software CS control.

Copy link
Collaborator

@swift-tk swift-tk Apr 16, 2025

Choose a reason for hiding this comment

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

Can you explain what you exactly mean with that?

Currently this driver uses hardware implemented chip select in the OSPI_CONFIG_REG via the PERIPH_CS_LINES_FLD field. There are 4 dedicated pins for that and they are only referenced by the numbers 0..3, which I directly use in the mspi_ti_k3_dev_config function. When I would use ce-gpios with phandle to the GPIO pins I think it would be necassary to do software CS control.

ce-gpios can be used to for software CE control, but it is also used as part of the device id, see struct mspi_dev_id which is filled by MSPI_DEVICE_ID_DT. So you would have to have at least a ce-gpio defined in controller dts. The dev_id should be used to validate the peripheral(child) device within the controller, although some choose not to as no plan to support software-multiperipheral function. i.e. switching between child devices by software under the same controller.
If your controller is capable of switching by hardware CE number, then you can pass that 0~3 number in struct mspi_dev_cfg.ce_num .
I do recognize there might be a compatiblity problem if your CEs are not within one of your GPIO alternate function but dedicated pin. But we will fix it when we get there.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

For now I removed the ce-gpios part from the bindings. However it's still ignored in the driver and added the requirement that GPIO has to be selected in the Kconfig

MSPI_TI_K3_REG_WRITE(0, CONFIG, RESET_PIN, base_addr);

/* general clock cycle delays */
MSPI_TI_K3_REG_WRITE(TI_K3_OSPI_DEFAULT_DELAY, DEV_DELAY, D_NSS, base_addr);
Copy link
Collaborator

Choose a reason for hiding this comment

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

No, not really. struct mspi_ce_control is for software controlled CE. However, I do think that this CE timing maybe peripheral device specific but not transfer specific. NXP have a similar setting as well. We can try add that to struct mspi_dev_cfg
Another option is to slide it in through struct mspi_timing_cfg along with your custom timing parameters.

uint32_t exec_status =
MSPI_TI_K3_REG_READ_MASKED(FLASH_CMD_CTRL, CMD_EXEC_STATUS, base_address);
while (exec_status != 0 &&
k_cyc_to_us_floor32(k_uptime_get() - start_cycles) < req->timeout) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

It is defined in kconfig

int "Completion timeout tolerance (ms)"

@m-braunschweig
Copy link
Contributor Author

Are you not going to add any soc DTS for the controller?
To ensure compile across PRs, it is best to add the controller to one of the example or testcase.

I will add that to this PR when the AM2434 SoC and AM2434 Launchpad are officially supported. The AM2434 SoC support is currently being done in #87321. After that is merged I will add the AM243x LaunchPad board and after that I can also add the MSPI flash test to this PR. Before that I will only add it to the https://github.com/siemens/zephyr/commits/mika/ti/ti-am2434-native-boot/ branch

@m-braunschweig m-braunschweig force-pushed the mika/upstream/add-ti-k3-mspi branch from def3678 to 1602101 Compare April 16, 2025 14:25
@m-braunschweig m-braunschweig changed the title Add MSPI driver for the TI K3 platform Add MSPI + flash driver for the TI K3 platform Apr 16, 2025
@swift-tk
Copy link
Collaborator

@m-braunschweig You should squash the 13 commits into 2, one for the controller driver, one for the flash driver. That is zephyr PR policy.

Add a MSPI driver, used in the TI K3 platform. The driver was tested in
1S-1S-1S and 4S-4S-4S mode with the onboard infineon s25h flash of the
am243x launchpad and a custom driver for the flash using this interface.

The command and dummy cycles are always taken from the xfer request and
never from the devicetree since different commands might have different
latencies.

The driver is somewhat basic for now and lacks e.g. callback
implementation. This is something that can be added in the future. If a
non-supported / invalid request is detected a error code is returned.

Signed-off-by: Mika Braunschweig <[email protected]>
Add a infineon s25h mspi nor flash driver. This driver was tested on the
am243x-lp board.

It assumes the flash is in 1S-1S-1S mode and sends a reset command. After
that it disables the uniform hybrid sector architecture, if activated, and
applies the setting by resetting the flash again.

After that the 4S-4S-4S mode is entered and also 4 byte adressing gets
enabled. During these stepts the JEDEC id is sometimes verified to ensure a
valid connection.

Due to the flash possibily entering continious read mode when undesired
some read operations will read the JEDEC id to prevent this.

Signed-off-by: Mika Braunschweig <[email protected]>
@m-braunschweig m-braunschweig force-pushed the mika/upstream/add-ti-k3-mspi branch from 1602101 to 3b9d09f Compare April 22, 2025 14:12
@m-braunschweig
Copy link
Contributor Author

@swift-tk done.
My original thought for adding extra commits instead of amending them was that this PR is still somewhat WIP / a draft and that it would make reviewing new changes a bit easier. The "final" squash could then have been done when leaving the draft stage.

@m-braunschweig
Copy link
Contributor Author

Also I have some more questions to the MSPI driver API:

  • The TX and RX delay for MSPI devices can be configured both in the mspi_dev_cfg that's used in mspi_dev_config() and it's additionally part of the mspi_xfer struct that's given on every transfer request. In which cases should which value be honored?
  • The Infineon S25H flash has different delays for different commands. How should that be handled in the devicetree since there is only one rx-dummy/tx-dummy field? One option would be to add all of them in the devicetree binding, however the original rx-dummy/tx-dummy comes from the generic MSPI device binding
  • How should read-command and write-command be handled in this case? Should they use the opcode for single or quad mode transfers?

For the devicetree things I was unsure about I currently check during build time that they are unset. However I don't really like that solution and would be happy to hear about ideas to improve it

@swift-tk
Copy link
Collaborator

swift-tk commented Apr 26, 2025

  • The TX and RX delay for MSPI devices can be configured both in the mspi_dev_cfg that's used in mspi_dev_config() and it's additionally part of the mspi_xfer struct that's given on every transfer request. In which cases should which value be honored?

The mspi_dev_cfg contains device specific controller settings. There are cases where a device is initialized with mspi_dev_config() but does not uses mspi_xfer at all(e.g psram or XIP mode) or it needs to use different cmds with different settings when using mspi_xfer. The overlapping members are only the transfer related settings and should not involve changing device state.

The settings given to mspi_xfer takes precedence over mspi_dev_cfg in the context of the peripheral device driver, so tracking the state change with a global mspi_dev_cfg is desirable as the device would have to repeatedly obtain controller access and tries to override controller settings in each device APIs.

  • The Infineon S25H flash has different delays for different commands. How should that be handled in the devicetree since there is only one rx-dummy/tx-dummy field? One option would be to add all of them in the devicetree binding, however the original rx-dummy/tx-dummy comes from the generic MSPI device binding

The device tree properties represent the target configuration that the device will run on most of the time. In the case of flash memory, it represents settings when conducting memory write and read. The default settings for device initialization should be stored as const and possibly used for all register access to maintain maximum compatibility as they should typically be low speed.

  • How should read-command and write-command be handled in this case? Should they use the opcode for single or quad mode transfers?

If your target operating mode is single mode, then swap the cmds in device tree with cmds used in single mode and the same way with quad mode. Mode switching should be done by mspi_dev_config() and device state change should be tracked by the device driver itself if some commands are not available in one mode or the other. IMO, one would only need one target operating condition for a specific device throughout the software program cycle. So intermediate states should be managed by the device driver.

For the devicetree things I was unsure about I currently check during build time that they are unset. However I don't really like that solution and would be happy to hear about ideas to improve it

You have to check it each time when a config is passed from device driver. To reduce unnecessary config from repeated mspi_dev_config() calls and to maintain maximum compatibility, you should use the acquire and release function in the existing flash mspi drivers.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area: MSPI platform: TI K3 Texas Instruments Keystone 3 Processors platform: TI SimpleLink Texas Instruments SimpleLink MCU
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants