Enforce initialization order for fragments / dummy gpio
I am building a dto for a display using an Ilitek 9806e controller and a Goodix Gt911 touch controller.
These two components share a single reset line, so this reset line should only be asserted once on initialization. As the i2c address of the Goodix controller is determined during reset (depending on the state of a secondary GPIO line) I would like to use the reset code from Goodix driver and skip the reset from the Ilitek driver.
Currently, I have set the reset-gpios of the Ilitek driver to an unused GPIO line. This works and both display and touch controller are initialized correctly.
Two questions:
1) Is there a way to specify a dummy gpio for the Ilitek reset-gpios? The driver requires reset-gpios to be there, but I would prefer not to waste an actual GPIO line for this.
2) For this setup to work, the Goodix driver must be initialized before the Ilitek driver. This appears to work, but may be just dumb luck? Is there a way to enforce this initialization order?
For reference, this is my current dto:
Thanks!
These two components share a single reset line, so this reset line should only be asserted once on initialization. As the i2c address of the Goodix controller is determined during reset (depending on the state of a secondary GPIO line) I would like to use the reset code from Goodix driver and skip the reset from the Ilitek driver.
Currently, I have set the reset-gpios of the Ilitek driver to an unused GPIO line. This works and both display and touch controller are initialized correctly.
Two questions:
1) Is there a way to specify a dummy gpio for the Ilitek reset-gpios? The driver requires reset-gpios to be there, but I would prefer not to waste an actual GPIO line for this.
2) For this setup to work, the Goodix driver must be initialized before the Ilitek driver. This appears to work, but may be just dumb luck? Is there a way to enforce this initialization order?
For reference, this is my current dto:
Code: Select all
/dts-v1/;
/plugin/;
/ {
compatible = "brcm,bcm2835";
i2c_frag: fragment@0 {
target = <&i2c_csi_dsi>;
__overlay__ {
#address-cells = <1>;
#size-cells = <0>;
status = "okay";
gt911: gt911@5d {
compatible = "goodix,gt911";
reg = <0x5d>;
touchscreen-x-mm = <90>;
touchscreen-y-mm = <151>;
reset-gpios = <&gpio 5 1>; // 1 means GPIO_ACTIVE_LOW
irq-gpios = <&gpio 6 0>;
interrupt-parent = <&gpio>;
interrupts = <6 2>; // 2 = IRQ_TYPE_EDGE_FALLING
touchscreen-swapped-x-y=<1>;
touchscreen-inverted-y=<1>;
};
};
};
dsi_frag: fragment@1 {
target = <&dsi1>;
__overlay__ {
#address-cells = <1>;
#size-cells = <0>;
status = "okay";
port {
dsi_out: endpoint {
remote-endpoint = <&dsi_panel_in>;
};
};
panel_disp: panel_disp@0 {
reg = <0>;
// Loads a modified version of the panel-ilitek-ili9806e driver
compatible = "densitron,dmt050wvnmcmi-1a";
// This is an unconnected dummy gpio, the display is reset by the touch controller init
reset-gpios = <&gpio 8 1>; // 1 means GPIO_ACTIVE_LOW
port {
dsi_panel_in: endpoint {
remote-endpoint = <&dsi_out>;
};
};
};
};
};
};
- 6by9
- Raspberry Pi Engineer & Forum Moderator
Raspberry Pi Engineer & Forum Moderator - Posts: 18540
- Joined: Wed Dec 04, 2013 11:27 am
Re: Enforce initialization order for fragments / dummy gpio
You're actually looking to enforce the driver probe order, which is a totally different thing to the overlays that configure the devices.
As you've noted you can't share a GPIO. You can share a regulator though, and the regulator can control the GPIO.
Otherwise you really need to alter the driver so that https://github.com/raspberrypi/linux/bl ... 06e.c#L183
becomes
As you've noted you can't share a GPIO. You can share a regulator though, and the regulator can control the GPIO.
Otherwise you really need to alter the driver so that https://github.com/raspberrypi/linux/bl ... 06e.c#L183
Code: Select all
ctx->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);Code: Select all
ctx->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW);Software Engineer at Raspberry Pi Ltd. Views expressed are still personal views.
I'm not interested in doing contracts for bespoke functionality - please don't ask.
I'm not interested in doing contracts for bespoke functionality - please don't ask.
Re: Enforce initialization order for fragments / dummy gpio
Thank you for the explanation.
I tried to understand the mechanisms controlling probe order, but couldn't find anything that works at the device tree level.
I could modify the Ilitek driver to defer its probing, but the seems to introduce a tight coupling between it and the Goodix driver.
Edit: I have tried to create a phandle reference from the Ilitek to the Goodix driver:
Empirically this seems to result in the correct probe order, but I don't know if this is actually a valid approach. Is the creation of the
"parent" dummy property correct?
Edit2: Nevermind, the phandle reference did nothing for the probe order in general.
I tried to understand the mechanisms controlling probe order, but couldn't find anything that works at the device tree level.
I could modify the Ilitek driver to defer its probing, but the seems to introduce a tight coupling between it and the Goodix driver.
Edit: I have tried to create a phandle reference from the Ilitek to the Goodix driver:
Code: Select all
[...]
panel_disp: panel_disp@0 {
// Ensure touchscreen is probed first to perform reset
parent = < >911 >;
[...]
"parent" dummy property correct?
Edit2: Nevermind, the phandle reference did nothing for the probe order in general.
- PhilE
- Raspberry Pi Engineer & Forum Moderator
Raspberry Pi Engineer & Forum Moderator - Posts: 6888
- Joined: Mon Sep 29, 2014 1:07 pm
Re: Enforce initialization order for fragments / dummy gpio
Linux seems to deliberately avoid/prevent setting probe ordering, except via 2 methods:
1. Some subsystems (clocks, power, ???) have recognised provider/consumer relationships, where the kernel will not attempt to probe the consumer until the provider has probed successfully.
2. Returning -EPROBE_DEFER from the probe function will cause the probe to be retried later, by which time the missing dependency may be available.
1. Some subsystems (clocks, power, ???) have recognised provider/consumer relationships, where the kernel will not attempt to probe the consumer until the provider has probed successfully.
2. Returning -EPROBE_DEFER from the probe function will cause the probe to be retried later, by which time the missing dependency may be available.
Re: Enforce initialization order for fragments / dummy gpio
I am trying to modify the Ilitek driver to defer its probing, but I can't figure out how to determine when the Goodix probe has completed.
I have tried this code:
but parent_dev exists, even if the Goodix probe is not yet complete. Furthermore, introducing this tight coupling to the i2c bus seems really wrong.
I have tried this code:
Code: Select all
static int ili9806e_dsi_probe(struct mipi_dsi_device *dsi)
{
struct device *dev = &dsi->dev;
struct device_node * parent = of_parse_phandle(dev->of_node, "parent", 0);
if (parent)
{
struct device *parent_dev;
parent_dev = bus_find_device_by_of_node(&i2c_bus_type, parent);
Re: Enforce initialization order for fragments / dummy gpio
I have found an approach that actually works, but seems very ugly:
Is this really the correct way of deferring the probe?
Code: Select all
static int ili9806e_dsi_probe(struct mipi_dsi_device *dsi)
{
struct device *dev = &dsi->dev;
struct device_node * parent = of_parse_phandle(dev->of_node, "parent", 0);
if (parent)
{
struct i2c_client * parent_i2c = of_find_i2c_device_by_node(parent);
if (parent_i2c)
{
void * data = dev_get_drvdata(&parent_i2c->dev);
put_device(&parent_i2c->dev);
if (!data)
{
printk(KERN_INFO "ili9806e parent i2c not ready, deferring");
return -EPROBE_DEFER;
}
}
[...]
- PhilE
- Raspberry Pi Engineer & Forum Moderator
Raspberry Pi Engineer & Forum Moderator - Posts: 6888
- Joined: Mon Sep 29, 2014 1:07 pm
Re: Enforce initialization order for fragments / dummy gpio
To recap, you have two devices on a single reset line. There should be only one actual reset, otherwise the device driver that probes first risks having the device state disappear under its feet. You would like to enforce probe ordering so that you can disconnect one of the reset lines and still guarantee that the devices are ready when the drivers run.
Looking back at your original post I noticed this question:
However, you could actually use the FSM capability and create a system with two virtual GPIOs and one real GPIO, where the first user of the virtual reset becomes the owner of the real GPIO, and the other user is effectively ignored. Other than a DT fragment to declare and configure the gpio-fsm instance, all you would need is for the client drivers to return -EPROBE_DEFER if their respective reset GPIO is not available (because the gpio-fsm driver has not been probed yet).
It's almost 5 years since I wrote gpio-fsm, so it will take a little while to refamiliarise myself with the configuration mechanism, but essentially:
1. Declare any real input GPIOs read by the FSM.
2. Declare any real output GPIOs driven by the FSM.
3. Declare how many virtual/software GPIOs to create.
4. Optionally, set how long to wait at shutdown for a "shutdown state" to be reached.
5. Declare all the states in the system, one of which must be the "start state". Each state declares any GPIO states to set on entry to that state, any transitions to other states, and what would trigger those transitions.
In your case I think you need:
1. No real input GPIOs.
2. 1 real real output GPIO - the shared reset line.
3. 2 software GPIOs.
4. I don't think this is relevant - do you care about the state of the reset line as the reboot/power-off happens?
5. Your FSM would need 5 states:
Let me know if this is something you want to try - I'll happily lend a hand.
You can read more about the gpio-fsm driver here:
viewtopic.php?p=1821505#p1821505
There is an example FSM (available as "dtoverlay=fsm-demo") declared here: https://github.com/raspberrypi/linux/bl ... verlay.dts
And a real-world usage ("dtoverlay=ghost-amp") here: https://github.com/raspberrypi/linux/bl ... verlay.dts
Looking back at your original post I noticed this question:
There is a very-non-upstream driver that allows you to create virtual GPIOs and link to them in Device Tree. It's the gpio-fsm driver, where "fsm" is short for Finite State Machine. This would allow you to create a dummy GPIO and save a pin.1) Is there a way to specify a dummy gpio for the Ilitek reset-gpios? The driver requires reset-gpios to be there, but I would prefer not to waste an actual GPIO line for this.
However, you could actually use the FSM capability and create a system with two virtual GPIOs and one real GPIO, where the first user of the virtual reset becomes the owner of the real GPIO, and the other user is effectively ignored. Other than a DT fragment to declare and configure the gpio-fsm instance, all you would need is for the client drivers to return -EPROBE_DEFER if their respective reset GPIO is not available (because the gpio-fsm driver has not been probed yet).
It's almost 5 years since I wrote gpio-fsm, so it will take a little while to refamiliarise myself with the configuration mechanism, but essentially:
1. Declare any real input GPIOs read by the FSM.
2. Declare any real output GPIOs driven by the FSM.
3. Declare how many virtual/software GPIOs to create.
4. Optionally, set how long to wait at shutdown for a "shutdown state" to be reached.
5. Declare all the states in the system, one of which must be the "start state". Each state declares any GPIO states to set on entry to that state, any transitions to other states, and what would trigger those transitions.
In your case I think you need:
1. No real input GPIOs.
2. 1 real real output GPIO - the shared reset line.
3. 2 software GPIOs.
4. I don't think this is relevant - do you care about the state of the reset line as the reboot/power-off happens?
5. Your FSM would need 5 states:
- Idle
This is the start state, with the external reset deasserted.
- reset_0
Move into here from idle or unreset_0 when the software GPIO 0 reset is asserted
On entry assert the real reset.
- unreset_0
Move into here from reset_0 when the software GPIO 0 reset is deasserted
On entry deassert the real reset.
- reset_1
Move into here from idle or unreset_1 when the software GPIO 1 reset is asserted
On entry assert the real reset.
- unreset_1
Move into here from reset_1 when the software GPIO 1 reset is deasserted
On entry deassert the real reset.
Let me know if this is something you want to try - I'll happily lend a hand.
You can read more about the gpio-fsm driver here:
viewtopic.php?p=1821505#p1821505
There is an example FSM (available as "dtoverlay=fsm-demo") declared here: https://github.com/raspberrypi/linux/bl ... verlay.dts
And a real-world usage ("dtoverlay=ghost-amp") here: https://github.com/raspberrypi/linux/bl ... verlay.dts
- PhilE
- Raspberry Pi Engineer & Forum Moderator
Raspberry Pi Engineer & Forum Moderator - Posts: 6888
- Joined: Mon Sep 29, 2014 1:07 pm
Re: Enforce initialization order for fragments / dummy gpio
This shouldn't be far off:
You can add your own fragments, using "<&disp_reset 0 0>" and "<&disp_reset 1 0>" for the resets controlled by the display driver and the touchscreen driver, respectively.
I've extended the state machine so that after both resets - one echoed to the real GPIO, one ignored - it will return to the idle state. The one scenario it won't cope with is the second reset being asserted and deasserted while the first is still being asserted. That would require more states to track.
Code: Select all
/dts-v1/;
/plugin/;
#include <dt-bindings/gpio/gpio-fsm.h>
#define DISP_RESET GF_SW(0)
#define TS_RESET GF_SW(1)
#define SHARED_RESET GF_OP(0) // GPIO7
/{
compatible = "brcm,bcm2835";
fragment@0 {
target-path = "/";
__overlay__ {
disp_reset: disp-reset {
compatible = "rpi,gpio-fsm";
debug = <0>;
gpio-controller;
#gpio-cells = <2>;
num-swgpios = <2>;
gpio-line-names = "disp_reset", "ts_reset";
output-gpios = <&gpio 7 1>; // SHARED_RESET
shutdown-timeout-ms = <2000>;
idle {
start_state;
set = <SHARED_RESET 0>;
reset_0 = <DISP_RESET 1>;
reset_1 = <TS_RESET 1>;
};
reset_0 {
set = <SHARED_RESET 1>;
unreset_0 = <DISP_RESET 0>;
};
unreset_0 {
set = <SHARED_RESET 0>;
skipreset_0a = <DISP_RESET 1>;
};
skipreset_0a {
skipreset_0b = <TS_RESET 1>;
};
skipreset_0b {
idle = <TS_RESET 0>;
};
reset_1 {
set = <SHARED_RESET 1>;
unreset_1 = <TS_RESET 0>;
};
unreset_1 {
set = <SHARED_RESET 0>;
skipreset_1a = <TS_RESET 1>;
};
skipreset_1a {
skipreset_1b = <DISP_RESET 1>;
};
skipreset_1b {
idle = <DISP_RESET 0>;
};
};
};
};
__overrides__ {
fsm_debug = <&disp_reset>,"debug:0";
};
};
I've extended the state machine so that after both resets - one echoed to the real GPIO, one ignored - it will return to the idle state. The one scenario it won't cope with is the second reset being asserted and deasserted while the first is still being asserted. That would require more states to track.
Re: Enforce initialization order for fragments / dummy gpio
Wow, thank you so much for the detailed response! It's very impressive how much functionality can be packed into the dt syntax.
There is unfortunately one extra complication that I only mentioned in passing in the first post: The touchscreen controller needs a second line driven during reset to configure the i2c address, afterwards this line becomes an interrupt. I guess this could also be achieved with the fsm driver, but would make the state machine even more complicated.
The approach of introducing a link between the two drivers and adding a probe deferral in the display driver seems slightly easier and more portable.
Thank you for your help though!
There is unfortunately one extra complication that I only mentioned in passing in the first post: The touchscreen controller needs a second line driven during reset to configure the i2c address, afterwards this line becomes an interrupt. I guess this could also be achieved with the fsm driver, but would make the state machine even more complicated.
The approach of introducing a link between the two drivers and adding a probe deferral in the display driver seems slightly easier and more portable.
Thank you for your help though!
Jump to
- Community
- General discussion
- Announcements
- Other languages
- Deutsch
- Español
- Français
- Italiano
- Nederlands
- 日本語
- Polski
- Português
- Русский
- Türkçe
- User groups and events
- Raspberry Pi Official Magazine
- Using the Raspberry Pi
- Beginners
- Troubleshooting
- Advanced users
- Assistive technology and accessibility
- Education
- Picademy
- Teaching and learning resources
- Staffroom, classroom and projects
- Astro Pi
- Mathematica
- High Altitude Balloon
- Weather station
- Programming
- C/C++
- Java
- Python
- Scratch
- Other programming languages
- Windows 10 for IoT
- Wolfram Language
- Bare metal, Assembly language
- Graphics programming
- OpenGLES
- OpenVG
- OpenMAX
- General programming discussion
- Projects
- Networking and servers
- Automation, sensing and robotics
- Graphics, sound and multimedia
- Other projects
- Gaming
- Media centres
- AIY Projects
- Hardware and peripherals
- Camera board
- Compute Module
- Official Display
- HATs and other add-ons
- Device Tree
- Interfacing (DSI, CSI, I2C, etc.)
- Keyboard computers (400, 500, 500+)
- Raspberry Pi Pico
- General
- SDK
- MicroPython
- Other RP2040 boards
- Zephyr
- Rust
- AI Accelerator
- AI Camera - IMX500
- Hailo
- Software
- Raspberry Pi OS
- Raspberry Pi Connect
- Raspberry Pi Desktop for PC and Mac
- Beta testing
- Other
- Android
- Debian
- FreeBSD
- Gentoo
- Linux Kernel
- NetBSD
- openSUSE
- Plan 9
- Puppy
- Arch
- Pidora / Fedora
- RISCOS
- Ubuntu
- Ye Olde Pi Shoppe
- For sale
- Wanted
- Off topic
- Off topic discussion