[PATCH 0/2] dm: add support for stubbing optional devices

As U-Boot works to align itself with upstream devicetrees, there are some common issues we start to run into, that of hardware blocks which might be important for an OS like Linux, but which aren't useful in U-Boot.
To offer an example: Qualcomm platforms feature a Resource Power Manager (RPM(h)) co-processor, it can opportunistically ramp down rails and clocks based on usage. This necessitates placing votes on these resources for every peripheral that uses them, and as a result means that many peripherals that we want to use in U-Boot contain references to the RPMh clock controller. However, we don't actually need to do this in the context of U-Boot, this is mostly something an OS would care about.
With CONFIG_OF_LIVE it would be possible to dynamically remove such references (we could for example add a "bootph-os" property to indicate that the device should be ignored by U-Boot). However OF_LIVE is not available on all boards, and is only available post-relocation. This approach would also require adding a new DT property for every arch/platform/board that we want to ignore.
We could instead try to handle this at the driver level, but this becomes intractable when trying to scale across all of the drivers and for all of the supported hardware generations. It also complicates thing if we actually want/need to use this previously optional resource in the future.
== The stub approach ==
The approach taken with this series is instead to allow for a stub driver to be defined per-uclass, with the clock subsystem being the first user. This stub driver is used as a fall back in the case where a driver requests a device of a specific type (such as a clock controller) and where no other driver binds to the node.
This solution allows us to immediately solve this issue for Qualcomm platforms, and for any other architectures with similar issues. An obvious next step is to do the same for optional power-domain controllers.
Another benefit to this approach is that it is easy to introspect, the stub drivers will show up in "dm tree". So the case of forgetting to enable a driver will be much more obvious than if we tried to find the clock, failed, and just did nothing.
== Future expansion ==
The exact scope of the stub driver (when it should match, how it should behave) obviously depends heavily on the uclass, as such I think it should be up to the subsystem maintainers.
An obvious next choice would be the power domain uclass. The current architecture of U-Boot requires either disabling CONFIG_POWER_DOMAIN, supporting every single power domain controller on your platform, or adding DM_FLAG_DEFAULT_PD_CTRL_OFF to a bunch of drivers.
--- Caleb Connolly (2): dm: core: add support for fallback drivers clk: introduce a fallback stub driver
drivers/clk/Makefile | 1 + drivers/clk/clk-fallback.c | 39 +++++++++++++++++++++++++++++++++++++++ drivers/clk/clk-uclass.c | 4 ++++ drivers/core/Kconfig | 20 ++++++++++++++++++++ drivers/core/uclass.c | 24 +++++++++++++++++++++++- include/dm/uclass.h | 3 +++ 6 files changed, 90 insertions(+), 1 deletion(-) --- change-id: 20240410-b4-stub-drivers-1c1565bb06a6 base-commit: 56c4a3aa2ed15b64eabd067a10be2091d28a5f2f
// Caleb (they/them)

Introduce support for a uclass to provide a fallback/stub driver which can be used when no device is found for a given node. This might be useful for handling non-essential clock controllers like the RPMh on Qualcomm platforms, or during early bringup to get UART output before a real clock driver has been created.
Signed-off-by: Caleb Connolly caleb.connolly@linaro.org --- drivers/core/Kconfig | 10 ++++++++++ drivers/core/uclass.c | 24 +++++++++++++++++++++++- include/dm/uclass.h | 3 +++ 3 files changed, 36 insertions(+), 1 deletion(-)
diff --git a/drivers/core/Kconfig b/drivers/core/Kconfig index 1081d61fcf01..09075b9b7a15 100644 --- a/drivers/core/Kconfig +++ b/drivers/core/Kconfig @@ -466,5 +466,15 @@ config BOUNCE_BUFFER
A second possible use of bounce buffers is their ability to provide aligned buffers for DMA operations.
+menuconfig FALLBACK_DRIVERS + bool "Enable per-uclass fallback drivers" + depends on DM + help + If a driver requests a resource (like a clock) from a node which + isn't bound to a driver, the driver model will look for a fallback + driver to "stub" the resource. These stubs usually do nothing and + are therefore only suitable in instances where the resource is not + required. + endmenu diff --git a/drivers/core/uclass.c b/drivers/core/uclass.c index e46d5717aa62..91d3a48d77b8 100644 --- a/drivers/core/uclass.c +++ b/drivers/core/uclass.c @@ -378,8 +378,26 @@ int uclass_find_device_by_of_offset(enum uclass_id id, int node,
return -ENODEV; }
+static int uclass_bind_fallback(struct uclass *uc, ofnode node, struct udevice **devp) +{ + struct driver *drv; + + log(LOGC_DM, LOGL_ERR, " - binding fallback '%s' driver '%s'\n", + uc->uc_drv->name, uc->uc_drv->fallback_drv_name); + + drv = lists_driver_lookup_name(uc->uc_drv->fallback_drv_name); + if (!drv) { + log(LOGC_DM, LOGL_DEBUG, " - Can't find stub driver '%s' for uclass '%s'\n", + uc->uc_drv->fallback_drv_name, uc->uc_drv->name); + return -ENOENT; + } + + return device_bind_with_driver_data(gd->dm_root, drv, + ofnode_get_name(node), 0, node, devp); +} + int uclass_find_device_by_ofnode(enum uclass_id id, ofnode node, struct udevice **devp) { struct uclass *uc; @@ -401,9 +419,13 @@ int uclass_find_device_by_ofnode(enum uclass_id id, ofnode node, *devp = dev; goto done; } } - ret = -ENODEV; + + if (CONFIG_IS_ENABLED(FALLBACK_DRIVERS) && uc->uc_drv->fallback_drv_name) + ret = uclass_bind_fallback(uc, node, devp); + else + ret = -ENODEV;
done: log(LOGC_DM, LOGL_DEBUG, " - result for %s: %s (ret=%d)\n", ofnode_get_name(node), *devp ? (*devp)->name : "(none)", ret); diff --git a/include/dm/uclass.h b/include/dm/uclass.h index 456eef7f2f31..b99e36485bc5 100644 --- a/include/dm/uclass.h +++ b/include/dm/uclass.h @@ -67,8 +67,10 @@ struct udevice; * @child_pre_probe: Called before a child in this uclass is probed * @child_post_probe: Called after a child in this uclass is probed * @init: Called to set up the uclass * @destroy: Called to destroy the uclass + * @stub_drv_name: Name of a stub driver to use for devices that are not + * supported by any other driver. * @priv_auto: If non-zero this is the size of the private data * to be allocated in the uclass's ->priv pointer. If zero, then the uclass * driver is responsible for allocating any data required. * @per_device_auto: Each device can hold private data owned @@ -98,8 +100,9 @@ struct uclass_driver { int (*child_pre_probe)(struct udevice *dev); int (*child_post_probe)(struct udevice *dev); int (*init)(struct uclass *class); int (*destroy)(struct uclass *class); + const char *fallback_drv_name; int priv_auto; int per_device_auto; int per_device_plat_auto; int per_child_auto;

Am 10. April 2024 19:06:57 MESZ schrieb Caleb Connolly caleb.connolly@linaro.org:
Introduce support for a uclass to provide a fallback/stub driver which can be used when no device is found for a given node. This might be useful for handling non-essential clock controllers like the RPMh on Qualcomm platforms, or during early bringup to get UART output before a real clock driver has been created.
Signed-off-by: Caleb Connolly caleb.connolly@linaro.org
drivers/core/Kconfig | 10 ++++++++++ drivers/core/uclass.c | 24 +++++++++++++++++++++++- include/dm/uclass.h | 3 +++ 3 files changed, 36 insertions(+), 1 deletion(-)
diff --git a/drivers/core/Kconfig b/drivers/core/Kconfig index 1081d61fcf01..09075b9b7a15 100644 --- a/drivers/core/Kconfig +++ b/drivers/core/Kconfig @@ -466,5 +466,15 @@ config BOUNCE_BUFFER
A second possible use of bounce buffers is their ability to provide aligned buffers for DMA operations.
+menuconfig FALLBACK_DRIVERS
Wouldn't it be preferable to mark individual drivers as fallback drivers in their declaration?
This would allow alternative fallback drivers and would not require any definitions at uclass level.
Just by building a fallback driver you would enable the fallback behavior for its uclass.
Best regards
Heinrich
- bool "Enable per-uclass fallback drivers"
- depends on DM
- help
If a driver requests a resource (like a clock) from a node which
isn't bound to a driver, the driver model will look for a fallback
driver to "stub" the resource. These stubs usually do nothing and
are therefore only suitable in instances where the resource is not
required.
endmenu diff --git a/drivers/core/uclass.c b/drivers/core/uclass.c index e46d5717aa62..91d3a48d77b8 100644 --- a/drivers/core/uclass.c +++ b/drivers/core/uclass.c @@ -378,8 +378,26 @@ int uclass_find_device_by_of_offset(enum uclass_id id, int node,
return -ENODEV; }
+static int uclass_bind_fallback(struct uclass *uc, ofnode node, struct udevice **devp) +{
- struct driver *drv;
- log(LOGC_DM, LOGL_ERR, " - binding fallback '%s' driver '%s'\n",
uc->uc_drv->name, uc->uc_drv->fallback_drv_name);
- drv = lists_driver_lookup_name(uc->uc_drv->fallback_drv_name);
- if (!drv) {
log(LOGC_DM, LOGL_DEBUG, " - Can't find stub driver '%s' for uclass '%s'\n",
uc->uc_drv->fallback_drv_name, uc->uc_drv->name);
return -ENOENT;
- }
- return device_bind_with_driver_data(gd->dm_root, drv,
ofnode_get_name(node), 0, node, devp);
+}
int uclass_find_device_by_ofnode(enum uclass_id id, ofnode node, struct udevice **devp) { struct uclass *uc; @@ -401,9 +419,13 @@ int uclass_find_device_by_ofnode(enum uclass_id id, ofnode node, *devp = dev; goto done; } }
- ret = -ENODEV;
- if (CONFIG_IS_ENABLED(FALLBACK_DRIVERS) && uc->uc_drv->fallback_drv_name)
ret = uclass_bind_fallback(uc, node, devp);
- else
ret = -ENODEV;
done: log(LOGC_DM, LOGL_DEBUG, " - result for %s: %s (ret=%d)\n", ofnode_get_name(node), *devp ? (*devp)->name : "(none)", ret); diff --git a/include/dm/uclass.h b/include/dm/uclass.h index 456eef7f2f31..b99e36485bc5 100644 --- a/include/dm/uclass.h +++ b/include/dm/uclass.h @@ -67,8 +67,10 @@ struct udevice;
- @child_pre_probe: Called before a child in this uclass is probed
- @child_post_probe: Called after a child in this uclass is probed
- @init: Called to set up the uclass
- @destroy: Called to destroy the uclass
- @stub_drv_name: Name of a stub driver to use for devices that are not
- supported by any other driver.
- @priv_auto: If non-zero this is the size of the private data
- to be allocated in the uclass's ->priv pointer. If zero, then the uclass
- driver is responsible for allocating any data required.
- @per_device_auto: Each device can hold private data owned
@@ -98,8 +100,9 @@ struct uclass_driver { int (*child_pre_probe)(struct udevice *dev); int (*child_post_probe)(struct udevice *dev); int (*init)(struct uclass *class); int (*destroy)(struct uclass *class);
- const char *fallback_drv_name; int priv_auto; int per_device_auto; int per_device_plat_auto; int per_child_auto;

On Wed, Apr 10, 2024 at 07:27:17PM +0200, Heinrich Schuchardt wrote:
Am 10. April 2024 19:06:57 MESZ schrieb Caleb Connolly caleb.connolly@linaro.org:
Introduce support for a uclass to provide a fallback/stub driver which can be used when no device is found for a given node. This might be useful for handling non-essential clock controllers like the RPMh on Qualcomm platforms, or during early bringup to get UART output before a real clock driver has been created.
Signed-off-by: Caleb Connolly caleb.connolly@linaro.org
drivers/core/Kconfig | 10 ++++++++++ drivers/core/uclass.c | 24 +++++++++++++++++++++++- include/dm/uclass.h | 3 +++ 3 files changed, 36 insertions(+), 1 deletion(-)
diff --git a/drivers/core/Kconfig b/drivers/core/Kconfig index 1081d61fcf01..09075b9b7a15 100644 --- a/drivers/core/Kconfig +++ b/drivers/core/Kconfig @@ -466,5 +466,15 @@ config BOUNCE_BUFFER
A second possible use of bounce buffers is their ability to provide aligned buffers for DMA operations.
+menuconfig FALLBACK_DRIVERS
Wouldn't it be preferable to mark individual drivers as fallback drivers in their declaration?
This would allow alternative fallback drivers and would not require any definitions at uclass level.
Just by building a fallback driver you would enable the fallback behavior for its uclass.
I think some of this is addressed in the cover letter. My concern / questions come down to I think just a matter of naming. Both "fallback" and "stub" are used at times. As a concept, we have some areas where we need a no-op driver because whereas the DT describes a relationship in the hardware, here in U-Boot we can just accept things as configured. To me "fallback" implies more functionality of the driver when it's really just a generic no-op driver to fill in the dependencies within the tree. Can we rename things a bit along those lines?

On 4/10/24 15:44, Tom Rini wrote:
On Wed, Apr 10, 2024 at 07:27:17PM +0200, Heinrich Schuchardt wrote:
Am 10. April 2024 19:06:57 MESZ schrieb Caleb Connolly caleb.connolly@linaro.org:
Introduce support for a uclass to provide a fallback/stub driver which can be used when no device is found for a given node. This might be useful for handling non-essential clock controllers like the RPMh on Qualcomm platforms, or during early bringup to get UART output before a real clock driver has been created.
Signed-off-by: Caleb Connolly caleb.connolly@linaro.org
drivers/core/Kconfig | 10 ++++++++++ drivers/core/uclass.c | 24 +++++++++++++++++++++++- include/dm/uclass.h | 3 +++ 3 files changed, 36 insertions(+), 1 deletion(-)
diff --git a/drivers/core/Kconfig b/drivers/core/Kconfig index 1081d61fcf01..09075b9b7a15 100644 --- a/drivers/core/Kconfig +++ b/drivers/core/Kconfig @@ -466,5 +466,15 @@ config BOUNCE_BUFFER
A second possible use of bounce buffers is their ability to provide aligned buffers for DMA operations.
+menuconfig FALLBACK_DRIVERS
Wouldn't it be preferable to mark individual drivers as fallback drivers in their declaration?
This would allow alternative fallback drivers and would not require any definitions at uclass level.
Just by building a fallback driver you would enable the fallback behavior for its uclass.
I think some of this is addressed in the cover letter. My concern / questions come down to I think just a matter of naming. Both "fallback" and "stub" are used at times. As a concept, we have some areas where we need a no-op driver because whereas the DT describes a relationship in the hardware, here in U-Boot we can just accept things as configured. To me "fallback" implies more functionality of the driver when it's really just a generic no-op driver to fill in the dependencies within the tree. Can we rename things a bit along those lines?
I would rather just have a stub driver with compatibles for all clocks we want it to match. I think having a generic fallback driver could cause issues in the future if we need to switch over to using a real driver.
--Sean

Hi all,
Sorry for the slow follow-up.
On 11/04/2024 04:37, Sean Anderson wrote:
On 4/10/24 15:44, Tom Rini wrote:
On Wed, Apr 10, 2024 at 07:27:17PM +0200, Heinrich Schuchardt wrote:
Am 10. April 2024 19:06:57 MESZ schrieb Caleb Connolly caleb.connolly@linaro.org:
Introduce support for a uclass to provide a fallback/stub driver which can be used when no device is found for a given node. This might be useful for handling non-essential clock controllers like the RPMh on Qualcomm platforms, or during early bringup to get UART output before a real clock driver has been created.
Signed-off-by: Caleb Connolly caleb.connolly@linaro.org
drivers/core/Kconfig | 10 ++++++++++ drivers/core/uclass.c | 24 +++++++++++++++++++++++- include/dm/uclass.h | 3 +++ 3 files changed, 36 insertions(+), 1 deletion(-)
diff --git a/drivers/core/Kconfig b/drivers/core/Kconfig index 1081d61fcf01..09075b9b7a15 100644 --- a/drivers/core/Kconfig +++ b/drivers/core/Kconfig @@ -466,5 +466,15 @@ config BOUNCE_BUFFER
A second possible use of bounce buffers is their ability to provide aligned buffers for DMA operations.
+menuconfig FALLBACK_DRIVERS
Wouldn't it be preferable to mark individual drivers as fallback drivers in their declaration?
This would allow alternative fallback drivers and would not require any definitions at uclass level.
Just by building a fallback driver you would enable the fallback behavior for its uclass.
I think some of this is addressed in the cover letter. My concern / questions come down to I think just a matter of naming. Both "fallback" and "stub" are used at times. As a concept, we have some areas where we need a no-op driver because whereas the DT describes a relationship in the hardware, here in U-Boot we can just accept things as configured. To me "fallback" implies more functionality of the driver when it's really just a generic no-op driver to fill in the dependencies within the tree. Can we rename things a bit along those lines?
Yes, will do. I originally used "stub" but then decided "fallback" was better, but I think you're right that stub is the right way to go here. As I agree that "fallback" implies some kind of real implementation.
I would rather just have a stub driver with compatibles for all clocks we want it to match. I think having a generic fallback driver could cause issues in the future if we need to switch over to using a real driver.
I don't think either approach is significantly better from a developer perspective. With my patches if there a driver with a matching compatible available then you will NEVER bind the stub, even if probe fails on the real driver. With your proposal we'd have to remember to remove the compatible from the stub driver or risk the race condition when binding drivers.
There is another subtle but important difference if we were to use a compatible list in the stub. It turns out that CONFIG_OF_LIVE affects the bind behaviour - U-Boot won't try to bind the children of a node with no driver when using the livetree. This is arguably more "correct", as usually child devices depend on their parent, so we save some cycles by not binding unsupported devices.
But, the RPM clock controller which I originally implemented this feature for is such a case, this is the DT (simplified for brevity):
rpm: remoteproc { compatible = "qcom,sm6115-rpm-proc", "qcom,rpm-proc";
glink-edge { compatible = "qcom,glink-rpm";
rpm_requests: rpm-requests { compatible = "qcom,rpm-sm6115";
rpmcc: clock-controller { compatible = "qcom,rpmcc-sm6115", "qcom,rpmcc"; }; }; };
With livetree (which we use on qcom) we would need to stub not just the rpmcc but also the rpm-requests, the glink bus, and the remoteproc (yeah these platforms are a bit silly, I know XD).
However with the fallback solution as implemented in these patches, we totally sidestep this issue by directly binding the node on-demand, something we can only safely do for a stub driver as we don't need the parent devices.
So I can either:
1) Make OF_LIVE behave like flattree and descend into all nodes (I don't know if this would cause any issues, but I think the livetree behaviour is more "correct" tbh). 2) Add stubs for all 3 of the parent nodes (and manually maintain a table of compatibles for clock drivers we want to stub). 3) Stick with this approach of binding the stub as a last resort.
Obviously I've already put the time into 3, and that would still be my preferred approach, but I'm open to other ideas.
--Sean

On Fri, May 03, 2024 at 05:53:11PM +0200, Caleb Connolly wrote:
Hi all,
Sorry for the slow follow-up.
On 11/04/2024 04:37, Sean Anderson wrote:
On 4/10/24 15:44, Tom Rini wrote:
On Wed, Apr 10, 2024 at 07:27:17PM +0200, Heinrich Schuchardt wrote:
Am 10. April 2024 19:06:57 MESZ schrieb Caleb Connolly caleb.connolly@linaro.org:
Introduce support for a uclass to provide a fallback/stub driver which can be used when no device is found for a given node. This might be useful for handling non-essential clock controllers like the RPMh on Qualcomm platforms, or during early bringup to get UART output before a real clock driver has been created.
Signed-off-by: Caleb Connolly caleb.connolly@linaro.org
drivers/core/Kconfig | 10 ++++++++++ drivers/core/uclass.c | 24 +++++++++++++++++++++++- include/dm/uclass.h | 3 +++ 3 files changed, 36 insertions(+), 1 deletion(-)
diff --git a/drivers/core/Kconfig b/drivers/core/Kconfig index 1081d61fcf01..09075b9b7a15 100644 --- a/drivers/core/Kconfig +++ b/drivers/core/Kconfig @@ -466,5 +466,15 @@ config BOUNCE_BUFFER
A second possible use of bounce buffers is their ability to provide aligned buffers for DMA operations.
+menuconfig FALLBACK_DRIVERS
Wouldn't it be preferable to mark individual drivers as fallback drivers in their declaration?
This would allow alternative fallback drivers and would not require any definitions at uclass level.
Just by building a fallback driver you would enable the fallback behavior for its uclass.
I think some of this is addressed in the cover letter. My concern / questions come down to I think just a matter of naming. Both "fallback" and "stub" are used at times. As a concept, we have some areas where we need a no-op driver because whereas the DT describes a relationship in the hardware, here in U-Boot we can just accept things as configured. To me "fallback" implies more functionality of the driver when it's really just a generic no-op driver to fill in the dependencies within the tree. Can we rename things a bit along those lines?
Yes, will do. I originally used "stub" but then decided "fallback" was better, but I think you're right that stub is the right way to go here. As I agree that "fallback" implies some kind of real implementation.
I would rather just have a stub driver with compatibles for all clocks we want it to match. I think having a generic fallback driver could cause issues in the future if we need to switch over to using a real driver.
I don't think either approach is significantly better from a developer perspective. With my patches if there a driver with a matching compatible available then you will NEVER bind the stub, even if probe fails on the real driver. With your proposal we'd have to remember to remove the compatible from the stub driver or risk the race condition when binding drivers.
There is another subtle but important difference if we were to use a compatible list in the stub. It turns out that CONFIG_OF_LIVE affects the bind behaviour - U-Boot won't try to bind the children of a node with no driver when using the livetree. This is arguably more "correct", as usually child devices depend on their parent, so we save some cycles by not binding unsupported devices.
But, the RPM clock controller which I originally implemented this feature for is such a case, this is the DT (simplified for brevity):
rpm: remoteproc { compatible = "qcom,sm6115-rpm-proc", "qcom,rpm-proc";
glink-edge { compatible = "qcom,glink-rpm"; rpm_requests: rpm-requests { compatible = "qcom,rpm-sm6115"; rpmcc: clock-controller { compatible = "qcom,rpmcc-sm6115", "qcom,rpmcc"; }; };
};
With livetree (which we use on qcom) we would need to stub not just the rpmcc but also the rpm-requests, the glink bus, and the remoteproc (yeah these platforms are a bit silly, I know XD).
However with the fallback solution as implemented in these patches, we totally sidestep this issue by directly binding the node on-demand, something we can only safely do for a stub driver as we don't need the parent devices.
So I can either:
- Make OF_LIVE behave like flattree and descend into all nodes (I don't
know if this would cause any issues, but I think the livetree behaviour is more "correct" tbh). 2) Add stubs for all 3 of the parent nodes (and manually maintain a table of compatibles for clock drivers we want to stub). 3) Stick with this approach of binding the stub as a last resort.
Obviously I've already put the time into 3, and that would still be my preferred approach, but I'm open to other ideas.
I don't like option 1 either. But I don't see why option 2 is worrisome? Yes, if adding a real driver for something we had stubbed out, we will need to remove the compatible string for the stub. But that shouldn't happen often? And we can put in a debug level or similar print so that if someone is at the point of adding a new driver that had been stubbed out and didn't remove the compatible stub to start with it should show up pretty quick in looking at logs?

On 10/04/2024 19:27, Heinrich Schuchardt wrote:
Am 10. April 2024 19:06:57 MESZ schrieb Caleb Connolly caleb.connolly@linaro.org:
Introduce support for a uclass to provide a fallback/stub driver which can be used when no device is found for a given node. This might be useful for handling non-essential clock controllers like the RPMh on Qualcomm platforms, or during early bringup to get UART output before a real clock driver has been created.
Signed-off-by: Caleb Connolly caleb.connolly@linaro.org
drivers/core/Kconfig | 10 ++++++++++ drivers/core/uclass.c | 24 +++++++++++++++++++++++- include/dm/uclass.h | 3 +++ 3 files changed, 36 insertions(+), 1 deletion(-)
diff --git a/drivers/core/Kconfig b/drivers/core/Kconfig index 1081d61fcf01..09075b9b7a15 100644 --- a/drivers/core/Kconfig +++ b/drivers/core/Kconfig @@ -466,5 +466,15 @@ config BOUNCE_BUFFER
A second possible use of bounce buffers is their ability to provide aligned buffers for DMA operations.
+menuconfig FALLBACK_DRIVERS
Wouldn't it be preferable to mark individual drivers as fallback drivers in their declaration?
This would allow alternative fallback drivers and would not require any definitions at uclass level.
I don't see an obvious usecase for having multiple stub drivers for a given uclass. Maybe as a result of that, I have no idea how we'd go about choosing which stub to use if multiple were available.
I don't think it would be particularly hard to switch over if at some point in the future we have such a need, but I don't see a benefit to this added complexity now.
Just by building a fallback driver you would enable the fallback behavior for its uclass.
I could drop the CONFIG_FALLBACK_DRIVERS option, it's just there to make it possible to opt in/out of all stub drivers at once, but maybe this isn't very useful.
Kind Regards,
Best regards
Heinrich
- bool "Enable per-uclass fallback drivers"
- depends on DM
- help
If a driver requests a resource (like a clock) from a node which
isn't bound to a driver, the driver model will look for a fallback
driver to "stub" the resource. These stubs usually do nothing and
are therefore only suitable in instances where the resource is not
required.
endmenu diff --git a/drivers/core/uclass.c b/drivers/core/uclass.c index e46d5717aa62..91d3a48d77b8 100644 --- a/drivers/core/uclass.c +++ b/drivers/core/uclass.c @@ -378,8 +378,26 @@ int uclass_find_device_by_of_offset(enum uclass_id id, int node,
return -ENODEV; }
+static int uclass_bind_fallback(struct uclass *uc, ofnode node, struct udevice **devp) +{
- struct driver *drv;
- log(LOGC_DM, LOGL_ERR, " - binding fallback '%s' driver '%s'\n",
uc->uc_drv->name, uc->uc_drv->fallback_drv_name);
- drv = lists_driver_lookup_name(uc->uc_drv->fallback_drv_name);
- if (!drv) {
log(LOGC_DM, LOGL_DEBUG, " - Can't find stub driver '%s' for uclass '%s'\n",
uc->uc_drv->fallback_drv_name, uc->uc_drv->name);
return -ENOENT;
- }
- return device_bind_with_driver_data(gd->dm_root, drv,
ofnode_get_name(node), 0, node, devp);
+}
int uclass_find_device_by_ofnode(enum uclass_id id, ofnode node, struct udevice **devp) { struct uclass *uc; @@ -401,9 +419,13 @@ int uclass_find_device_by_ofnode(enum uclass_id id, ofnode node, *devp = dev; goto done; } }
- ret = -ENODEV;
- if (CONFIG_IS_ENABLED(FALLBACK_DRIVERS) && uc->uc_drv->fallback_drv_name)
ret = uclass_bind_fallback(uc, node, devp);
- else
ret = -ENODEV;
done: log(LOGC_DM, LOGL_DEBUG, " - result for %s: %s (ret=%d)\n", ofnode_get_name(node), *devp ? (*devp)->name : "(none)", ret); diff --git a/include/dm/uclass.h b/include/dm/uclass.h index 456eef7f2f31..b99e36485bc5 100644 --- a/include/dm/uclass.h +++ b/include/dm/uclass.h @@ -67,8 +67,10 @@ struct udevice;
- @child_pre_probe: Called before a child in this uclass is probed
- @child_post_probe: Called after a child in this uclass is probed
- @init: Called to set up the uclass
- @destroy: Called to destroy the uclass
- @stub_drv_name: Name of a stub driver to use for devices that are not
- supported by any other driver.
- @priv_auto: If non-zero this is the size of the private data
- to be allocated in the uclass's ->priv pointer. If zero, then the uclass
- driver is responsible for allocating any data required.
- @per_device_auto: Each device can hold private data owned
@@ -98,8 +100,9 @@ struct uclass_driver { int (*child_pre_probe)(struct udevice *dev); int (*child_post_probe)(struct udevice *dev); int (*init)(struct uclass *class); int (*destroy)(struct uclass *class);
- const char *fallback_drv_name; int priv_auto; int per_device_auto; int per_device_plat_auto; int per_child_auto;

Add a stub_clk driver which does absolutely nothing and is configured as the fallback stub for UCLASS_CLK. If there is a dependency on a clock device which is not supported by any existing driver, and CONFIG_CLK_STUB is enabled, then the stub driver will kick in.
This is intended to be useful during early bringup (e.g. if the serial port is already configured for a default rate) or for clock controllers that simply aren't vital for a platform to work.
Signed-off-by: Caleb Connolly caleb.connolly@linaro.org --- drivers/clk/Makefile | 1 + drivers/clk/clk-fallback.c | 39 +++++++++++++++++++++++++++++++++++++++ drivers/clk/clk-uclass.c | 4 ++++ drivers/core/Kconfig | 10 ++++++++++ 4 files changed, 54 insertions(+)
diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index 638ad04baeb0..dd57f2fd0397 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -4,8 +4,9 @@ # Wolfgang Denk, DENX Software Engineering, wd@denx.de. #
obj-$(CONFIG_$(SPL_TPL_)CLK) += clk-uclass.o +obj-$(CONFIG_CLK_FALLBACK) += clk-fallback.o obj-$(CONFIG_$(SPL_TPL_)CLK) += clk_fixed_rate.o obj-$(CONFIG_$(SPL_TPL_)CLK) += clk_fixed_factor.o obj-$(CONFIG_$(SPL_TPL_)CLK_CCF) += clk.o clk-divider.o clk-mux.o clk-gate.o obj-$(CONFIG_$(SPL_TPL_)CLK_CCF) += clk-fixed-factor.o diff --git a/drivers/clk/clk-fallback.c b/drivers/clk/clk-fallback.c new file mode 100644 index 000000000000..f5733821e998 --- /dev/null +++ b/drivers/clk/clk-fallback.c @@ -0,0 +1,39 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2024 Linaro Ltd. + * + * Stub clock driver for non-vital clocks. + */ + +#include <clk.h> +#include <clk-uclass.h> +#include <dm.h> + +static ulong fallback_clk_set_rate(struct clk *clk, ulong rate) +{ + return (clk->rate = rate); +} + +static ulong fallback_clk_get_rate(struct clk *clk) +{ + return clk->rate; +} + +static int fallback_clk_nop(struct clk *clk) +{ + return 0; +}; + +static struct clk_ops fallback_clk_ops = { + .set_rate = fallback_clk_set_rate, + .get_rate = fallback_clk_get_rate, + .enable = fallback_clk_nop, + .disable = fallback_clk_nop, +}; + +U_BOOT_DRIVER(clk_fallback) = { + .name = "clk_fallback", + .id = UCLASS_CLK, + .ops = &fallback_clk_ops, + .flags = DM_FLAG_DEFAULT_PD_CTRL_OFF, +}; diff --git a/drivers/clk/clk-uclass.c b/drivers/clk/clk-uclass.c index ed6e60bc4841..c0cdb3cf529c 100644 --- a/drivers/clk/clk-uclass.c +++ b/drivers/clk/clk-uclass.c @@ -11,8 +11,9 @@ #include <common.h> #include <clk.h> #include <clk-uclass.h> #include <dm.h> +#include <dm/lists.h> #include <dt-structs.h> #include <errno.h> #include <log.h> #include <malloc.h> @@ -801,6 +802,9 @@ int clk_uclass_post_probe(struct udevice *dev)
UCLASS_DRIVER(clk) = { .id = UCLASS_CLK, .name = "clk", +#if CONFIG_IS_ENABLED(CLK_FALLBACK) + .fallback_drv_name = "clk_fallback", +#endif .post_probe = clk_uclass_post_probe, }; diff --git a/drivers/core/Kconfig b/drivers/core/Kconfig index 09075b9b7a15..1cf78b4303ca 100644 --- a/drivers/core/Kconfig +++ b/drivers/core/Kconfig @@ -476,5 +476,15 @@ menuconfig FALLBACK_DRIVERS driver to "stub" the resource. These stubs usually do nothing and are therefore only suitable in instances where the resource is not required.
+config CLK_FALLBACK + bool "Enable the clock fallback driver" + depends on FALLBACK_DRIVERS && CLK + help + This provides a no-op clock driver that can be used as a fallback + when no other driver is available. This is useful for systems with + clock controllers that are not actually required for peripherals to + work correctly (for example the RPMh clock controller on Qualcomm + platforms). + endmenu
participants (4)
-
Caleb Connolly
-
Heinrich Schuchardt
-
Sean Anderson
-
Tom Rini