Skip to content
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

Enable multiple ICE40 on the same bus #77980

Conversation

benediktibk
Copy link
Collaborator

@benediktibk benediktibk commented Sep 4, 2024

At the moment the build fails if multiple ICE40 are configured on the same SPI master with CONFIG_PINCTRL=y due to duplicate instantiation of the parent pinctrl-config.

This PR introduces a new SPI API which allows reapplying the default pin state, which in turn removes the necessity to have the parent pinctrl-config in the ICE40 driver.

So far I came up with only one alternative: Macrobatics in the ICE40 driver which loops over all SPI masters and creates one pinctrl-config instance for each SPI master with at least one ICE40 instance on it. This didn't seem to be a really good option to me.

Fixes #77983.

@benediktibk benediktibk force-pushed the add_multipe_ice40_same_bus branch 2 times, most recently from 5cd2e92 to 100a947 Compare September 4, 2024 09:29
@benediktibk benediktibk marked this pull request as ready for review September 4, 2024 09:32
@zephyrbot zephyrbot added area: SPI SPI bus platform: STM32 ST Micro STM32 area: FPGA Field-Programmable Gate Array (FPGA) labels Sep 4, 2024
@benediktibk benediktibk added the DNM This PR should not be merged (Do Not Merge) label Sep 4, 2024
@benediktibk
Copy link
Collaborator Author

benediktibk commented Sep 4, 2024

I've added the DNM as I think it might be a good idea to implement this API not only for STM32, if it is overall an acceptable approach.

@tbursztyka Could you please take a look at this one? I assume there is some room for different opinions on how to solve this issue.

Copy link
Member

@cfriedt cfriedt left a comment

Choose a reason for hiding this comment

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

@benediktibk Looks good to me. If you're looking for additional SPI drivers to implement this on, spi_esp32_spim would be fantastic (the ice40 FPGA driver was added for icev_wireless originally)

@benediktibk
Copy link
Collaborator Author

@benediktibk Looks good to me. If you're looking for additional SPI drivers to implement this on, spi_esp32_spim would be fantastic (the ice40 FPGA driver was added for icev_wireless originally)

Done

@zephyrbot zephyrbot added the platform: ESP32 Espressif ESP32 label Sep 5, 2024
@benediktibk benediktibk changed the title Enable multiple ICE40 on the same bus. Enable multiple ICE40 on the same bus Sep 5, 2024
@benediktibk
Copy link
Collaborator Author

The changes are tested only on STM32, as it is the only platform which I have available at the moment.

@cfriedt
Copy link
Member

cfriedt commented Sep 5, 2024

It the pinctrl config was always at a known offset (e.g. first) in all drivers config struct, it just occurred to me that this could be done in a generic way for all spi devices - e.g. as a static inline function in the spi.h header (would be a no-op if CONFIG_PINCTRL=n).

Thoughts?

sylvioalves
sylvioalves previously approved these changes Sep 5, 2024
Copy link
Collaborator

@sylvioalves sylvioalves left a comment

Choose a reason for hiding this comment

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

+1 regarding ESP32 changes.

@erwango
Copy link
Member

erwango commented Sep 5, 2024

Would be valuable to have SPI maintainers approval here. @tbursztyka, @teburd

This introduces a SPI API which allows applying the default pin state. This
might be necessary in some cases, if they have been reconfigured to a
different setup, for instance to do some bitbanging.

Signed-off-by: Benedikt Schmidt <[email protected]>
Implement the apply default pin state API for the STM32.

Signed-off-by: Benedikt Schmidt <[email protected]>
This gets rid of the instantation of the parent pinctrl config, which was
used to reapply the default pin state for the parent SPI bus. It uses
instead the newly introduced SPI API to do so.
This enables multiple instances of an ICE40 on the same SPI bus with
CONFIG_PINCTRL=y.

Signed-off-by: Benedikt Schmidt <[email protected]>
Implement the apply default pin state API for the ESP32.

Signed-off-by: Benedikt Schmidt <[email protected]>
@benediktibk benediktibk dismissed stale reviews from sylvioalves and cfriedt via e7391af September 5, 2024 18:21
@benediktibk benediktibk force-pushed the add_multipe_ice40_same_bus branch from 08436b2 to e7391af Compare September 5, 2024 18:21
@benediktibk
Copy link
Collaborator Author

Rebased to resolve merge conflicts.

@cfriedt
Copy link
Member

cfriedt commented Sep 6, 2024

For instance spi_emul doesn't even use pinctrl, so I think we can't use this approach.

It might be possible to overcome this issue fairly easily if it's just made an optional SPI driver API call (which is NULL in the api structure).

So drivers that do not have it could return -ENOTSUP from the static inline wrapper. The testsuite could just ignore that error.

That would make this solution work and scale quite well, generically.

@benediktibk
Copy link
Collaborator Author

For instance spi_emul doesn't even use pinctrl, so I think we can't use this approach.

It might be possible to overcome this issue fairly easily if it's just made an optional SPI driver API call (which is NULL in the api structure).

So drivers that do not have it could return -ENOTSUP from the static inline wrapper. The testsuite could just ignore that error.

That would make this solution work and scale quite well, generically.

Besides using ENOSYS instead of ENOTSUP, isn't this exactly what I have implemented? Or have I misunderstood you somehow?

@cfriedt
Copy link
Member

cfriedt commented Sep 6, 2024

That would make this solution work and scale quite well, generically.

Besides using ENOSYS instead of ENOTSUP, isn't this exactly what I have implemented? Or have I misunderstood you somehow?

Sorry, minor confusion.

I was still trying to think of a way to do it generically with pinctrl config at a fixed offset. Actually, if the config is just a read-only part of the API, then I think it can be done in a generic way, without functions required for every driver.

@benediktibk
Copy link
Collaborator Author

That would make this solution work and scale quite well, generically.

Besides using ENOSYS instead of ENOTSUP, isn't this exactly what I have implemented? Or have I misunderstood you somehow?

Sorry, minor confusion.

I was still trying to think of a way to do it generically with pinctrl config at a fixed offset. Actually, if the config is just a read-only part of the API, then I think it can be done in a generic way, without functions required for every driver.

Considering that we also have drivers without any pinctrl config at all we would additionally required a flag at a fixed offset which defines if the pinctrl config is available at another fixed offset. This would of course spare us the implementation of the function in every driver, but then each driver has to set at least the flag.

I'm not sure if this is in total better? Or am I missing something?

@cfriedt
Copy link
Member

cfriedt commented Sep 9, 2024

I'm not sure if this is in total better? Or am I missing something?

It's more uniform, probably easier to maintain, and scales to all SPI drivers just about immediately, without any specific customization.

The current solution would still need an additional, custom, function pointer even if the function pointer is NULL for every SPI device.

Not blocking, but more for the SPI maintainers consideration.

Copy link
Collaborator

@tbursztyka tbursztyka left a comment

Choose a reason for hiding this comment

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

@gmarull as you are pinctrl maintainer, I think we will need your input on this one.

@@ -1293,6 +1300,48 @@ static inline int spi_release_dt(const struct spi_dt_spec *spec)
return spi_release(spec->bus, &spec->config);
}


/**
* @brief Apply the default pin state
Copy link
Collaborator

@tbursztyka tbursztyka Sep 9, 2024

Choose a reason for hiding this comment

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

I have a hard time to see how this is SPI specific.
What if tomorrow the same use case appear with an i²c peripheral or a CAN one etc... would we expend each and every APIs with such a workaround? Because it looks to me as a workaround so far.

Perhaps I am missing something, I never had such use-case at hand myself.
To me, it seems that the issue is in pinctrl side. PINCTRL_DT_DEFINE() should be called only once, and by the instance that is related to it.
struct pinctrl_dev_config declared by PINCTRL _DT_DEFINE() is not static, so at this stage: any user of that cfg (like ice40 driver via FPGA_ICE40_PINCTRL_CONFIG() should be good to go.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

There is actually PINCTRL_DT_DEV_CONFIG_DECLARE which would reference the one instance via extern. But then I still have got the issue, that I can declare this only once. And I really can't get my head around a Macro-Solution which declares the variable within fpga_ice40.c only once per SPI bus master.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Perhaps auto-generated files could help: a header calling PINCTRL_DT_DEV_CONFIG_DECLARE for each valid pinctrl instance? Something like that, that would be automatically included at the end of pinctrl.h. If necessary even a .c file calling PINCTRL_DT_DEFINE() only once in a central place.

I don't know much pinctrl actually, but I am pretty sure there is room for improvements here. Let's see what @gmarull has to say.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I could also create a copy of PINCTRL_DT_DEFINE, e.g. PINCTRL _DT_DEFINE_CUSTOM_NAME, which has as an additional parameter the desired name of the variable. I would of course have to use this custom name then also for the other on the fly created variables (states, ...), but this might actually work. Although not really beautiful as well.

@cfriedt
Copy link
Member

cfriedt commented Sep 9, 2024

@tbursztyka - you're right in that it isn't necessarily a SPI-specific issue. Technically, it could be useful for other buses that require temporary pinctrl mode-change from bitbanged gpio back to the default setting.

In this case, bit-banging is required because the FPGA needs a specific handshake that is outside of the normal SPI protocol. In Linux, this is probably handled via some "quirk".

Effectively, Benedikt's change gets the device to remember it's default pinctrl assignment, so that users can tell the bus to reset back to the default pinctrl, without explicitly knowing what that default is, and that works well for multi-instance.

Also, yeah, I would agree @gmarull should take a look.

Copy link
Member

@gmarull gmarull left a comment

Choose a reason for hiding this comment

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

I'm not sure if I follow what this PR is doing, but doesn't seem like an adequate solution by any means. Only a single pinctrl config instance is allowed per DT node. If you have multiple ICE40 instances, I assume you'll also have N DT nodes, so you should be able to have N pinctrl config instances. Maybe some code snippets will help to understand the problem better.

@benediktibk
Copy link
Collaborator Author

I'm not sure if I follow what this PR is doing, but doesn't seem like an adequate solution by any means. Only a single pinctrl config instance is allowed per DT node. If you have multiple ICE40 instances, I assume you'll also have N DT nodes, so you should be able to have N pinctrl config instances. Maybe some code snippets will help to understand the problem better.

The issue is that the ICE40 instances have to reset the pinctrl of the parent, the SPI device. Therefore there are N ICE40 nodes, but only 1 pinctrl config instance.

@gmarull
Copy link
Member

gmarull commented Sep 10, 2024

I'm not sure if I follow what this PR is doing, but doesn't seem like an adequate solution by any means. Only a single pinctrl config instance is allowed per DT node. If you have multiple ICE40 instances, I assume you'll also have N DT nodes, so you should be able to have N pinctrl config instances. Maybe some code snippets will help to understand the problem better.

The issue is that the ICE40 instances have to reset the pinctrl of the parent, the SPI device. Therefore there are N ICE40 nodes, but only 1 pinctrl config instance.

Ok, got it. TBH, sounds a bit difficult to generalize, and extending SPI just for such cases feels like an overkill. Maybe an option can be that devices select PINCTRL_NON_STATIC, and then use PINCTRL_DT_DEV_CONFIG_DECLARE. However, I'd consider this potentially unsafe, as you'd be taking control of the pinctrl state out of the parent driver's internal state. This may not be compatible with all parent drivers...

@benediktibk
Copy link
Collaborator Author

Ok, got it. TBH, sounds a bit difficult to generalize, and extending SPI just for such cases feels like an overkill. Maybe an option can be that devices select PINCTRL_NON_STATIC, and then use PINCTRL_DT_DEV_CONFIG_DECLARE. However, I'd consider this potentially unsafe, as you'd be taking control of the pinctrl state out of the parent driver's internal state. This may not be compatible with all parent drivers...

Actually not all SPI masters even use PINCTRL, therefore I would like to get away from referencing something external which might does not even exist.

@gmarull
Copy link
Member

gmarull commented Sep 10, 2024

Ok, got it. TBH, sounds a bit difficult to generalize, and extending SPI just for such cases feels like an overkill. Maybe an option can be that devices select PINCTRL_NON_STATIC, and then use PINCTRL_DT_DEV_CONFIG_DECLARE. However, I'd consider this potentially unsafe, as you'd be taking control of the pinctrl state out of the parent driver's internal state. This may not be compatible with all parent drivers...

Actually not all SPI masters even use PINCTRL, therefore I would like to get away from referencing something external which might does not even exist.

I guess what we need then is a mechanism to de-initialize devices, and re-initialize them again. This way you could safely take ownership of SPI resources, and give them back. This has actually been discussed a few times, and this may be one valid use case to add to the list.

@benediktibk
Copy link
Collaborator Author

Ok, got it. TBH, sounds a bit difficult to generalize, and extending SPI just for such cases feels like an overkill. Maybe an option can be that devices select PINCTRL_NON_STATIC, and then use PINCTRL_DT_DEV_CONFIG_DECLARE. However, I'd consider this potentially unsafe, as you'd be taking control of the pinctrl state out of the parent driver's internal state. This may not be compatible with all parent drivers...

Actually not all SPI masters even use PINCTRL, therefore I would like to get away from referencing something external which might does not even exist.

I guess what we need then is a mechanism to de-initialize devices, and re-initialize them again. This way you could safely take ownership of SPI resources, and give them back. This has actually been discussed a few times, and this may be one valid use case to add to the list.

I agree, deinitializing and reinitializing the SPI device would do the trick.

@benediktibk benediktibk added the dev-review To be discussed in dev-review meeting label Oct 14, 2024
@benediktibk
Copy link
Collaborator Author

Another approach would be to have a Kconfig for each load modes (GPIO and SPI). This way one could solve the issue for cases, where it is possible to use the SPI load mode. But for smaller MCUs the issue would still exist.

Or the SPI driver model could be extended. At least according to the comment in fpga_ice40.c this might be a solution:

/*
 * Note: When loading a bitstream, the iCE40 has a 'quirk' in that the CS
 * polarity must be inverted during the 'leading clocks' phase and
 * 'trailing clocks' phase. While the bitstream is being transmitted, the
 * CS polarity is normal (active low). Zephyr's SPI driver model currently
 * does not handle these types of quirks (in contrast to e.g. Linux).
 *
 * The logical alternative would be to put the CS into GPIO mode, perform 3
 * separate SPI transfers (inverting CS polarity as necessary) and then
 * restore the default pinctrl settings. On some higher-end microcontrollers
 * and microprocessors, it's possible to do that without breaking the iCE40
 * timing requirements.
 *
 * However, on lower-end microcontrollers, the amount of time that elapses
 * between SPI transfers does break the iCE40 timing requirements. That
 * leaves us with the bitbanging option. Of course, on lower-end
 * microcontrollers, the amount of time required to execute something
 * like gpio_pin_configure_dt() dwarfs the 2*500 nanoseconds needed to
 * achieve the minimum 1 MHz clock rate for loading the iCE40 bistream. So
 * in order to bitbang on lower-end microcontrollers, we actually require
 * direct register access to the set and clear registers.
 *
 * With that, this driver is left with 2 possible modes of operation which
 * are:
 * - FPGA_ICE40_LOAD_MODE_SPI (for higher-end microcontrollers)
 * - FPGA_ICE40_LOAD_MODE_GPIO (for lower-end microcontrollers)
 */

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area: FPGA Field-Programmable Gate Array (FPGA) area: SPI SPI bus platform: ESP32 Espressif ESP32 platform: STM32 ST Micro STM32
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Multiple ICE40 instances on the same SPI master
8 participants