leds: wago-m4: register LEDs at probe time for immediate trigger activation
Previously LED class devices were registered inside wago_rpmsg_probe(), which is called only after the RPMsg channel is announced by the M4. With the M4 firmware loaded by U-Boot/SPL the channel still takes a few hundred ms to appear, and before that no LED trigger could run. This change makes the driver register all LED class devices directly in wago_m4_led_probe() so that kernel triggers (timer, pattern, ...) start working immediately at probe time, independently of the RPMsg channel state. wago_led_set() already drops frames silently when rpdev == NULL, so the trigger runs without error until the channel comes up and real hardware updates start flowing. Additionally, probe() now peeks at the rproc state via a temporary rproc_get_by_phandle() call: when the rproc is already in RPROC_DETACHED state (U-Boot/SPL loaded the firmware), boot_work is scheduled with delay=0 instead of WAGO_BOOT_INITIAL_DELAY_MS so that the RPMsg attach happens as fast as possible. Summary of changes: - wago_m4_led_probe(): call wago_led_register_leds() before the rpmsg driver registration; detect RPROC_DETACHED and set boot_delay_ms=0. - wago_rpmsg_probe(): remove wago_led_register_leds() call; only store rpdev so wago_led_set() can begin sending real frames. - Update header comment to document both boot paths. Signed-off-by: Heinrich Toews <ht@twx-software.de>
This commit is contained in:
@@ -31,27 +31,47 @@
|
||||
*
|
||||
* Firmware loading and boot sequencing
|
||||
* -------------------------------------
|
||||
* The M4 firmware must come from the filesystem (/lib/firmware). At probe()
|
||||
* time the rootfs may not yet be mounted, so the driver uses a
|
||||
* delayed_work retry loop:
|
||||
* Two boot paths are supported:
|
||||
*
|
||||
* a) U-Boot / SPL path (default for production):
|
||||
* The M4 firmware is loaded by SPL before Linux starts. The rproc is
|
||||
* already in RPROC_DETACHED state when the driver probes.
|
||||
*
|
||||
* In this path the driver registers all LED class devices immediately
|
||||
* at probe() time so that kernel LED triggers (e.g. timer, pattern)
|
||||
* start working without any delay. wago_led_set() silently drops
|
||||
* frames while rpdev == NULL; once the RPMsg channel is announced
|
||||
* (typically within a few hundred ms) real hardware updates flow.
|
||||
*
|
||||
* The boot_work is scheduled with zero delay to call rproc_boot()
|
||||
* (attach) as early as possible.
|
||||
*
|
||||
* b) Linux-boot path (development / fallback):
|
||||
* The driver loads the firmware from /lib/firmware itself via rproc_boot().
|
||||
* Because the rootfs may not yet be mounted at probe() time, boot_work
|
||||
* retries with WAGO_BOOT_RETRY_MS until the file appears.
|
||||
*
|
||||
* In this path LED class devices are also registered at probe() time
|
||||
* so triggers work immediately, but the M4 will not actually render
|
||||
* colours until rproc_boot() completes and the RPMsg channel appears.
|
||||
*
|
||||
* probe()
|
||||
* -> register LED class devices immediately (triggers start at once)
|
||||
* -> register RPMsg driver (waits for M4 channel announcement)
|
||||
* -> schedule wago_boot_work (first attempt after WAGO_BOOT_INITIAL_DELAY)
|
||||
* -> schedule wago_boot_work:
|
||||
* RPROC_DETACHED -> delay=0 (attach right away)
|
||||
* otherwise -> delay=WAGO_BOOT_INITIAL_DELAY_MS
|
||||
*
|
||||
* wago_boot_work
|
||||
* -> rproc_get + rproc_set_firmware + rproc_boot
|
||||
* success -> done (LEDs appear when RPMsg channel is announced)
|
||||
* success -> RPMsg channel appears, wago_rpmsg_probe() sets rpdev
|
||||
* -ENOENT -> filesystem not ready, reschedule after WAGO_BOOT_RETRY_MS
|
||||
* other -> fatal, stop retrying
|
||||
*
|
||||
* If the M4 is already running (RPROC_DETACHED, bootloader path), the
|
||||
* work attaches immediately without touching the filesystem.
|
||||
*
|
||||
* Sysfs example
|
||||
* -------------
|
||||
* echo "0 128 128" > /sys/class/leds/m4-led3/multi_intensity
|
||||
* echo 255 > /sys/class/leds/m4-led3/brightness
|
||||
* echo "0 128 128" > /sys/class/leds/sys/multi_intensity
|
||||
* echo 255 > /sys/class/leds/sys/brightness
|
||||
*
|
||||
* # Send raw commands directly to the M4 for testing
|
||||
* # (requires CONFIG_LEDS_WAGO_M4_WRAPPER_SYSFS_PASSTHROUGH=y):
|
||||
@@ -553,7 +573,6 @@ static int wago_rpmsg_probe(struct rpmsg_device *rpdev)
|
||||
*/
|
||||
struct device *rproc_dev = rpdev->dev.parent->parent->parent;
|
||||
struct wago_m4_led_priv *priv;
|
||||
int ret;
|
||||
|
||||
/* Try three levels up first, then four (rproc->dev.parent layout
|
||||
* may differ across kernel versions). */
|
||||
@@ -569,17 +588,16 @@ static int wago_rpmsg_probe(struct rpmsg_device *rpdev)
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/*
|
||||
* RPMsg channel is up — LED class devices are already registered
|
||||
* (done at platform probe time). Just store the channel handle so
|
||||
* that wago_led_set() can start sending real frames to the M4.
|
||||
*/
|
||||
priv->rpdev = rpdev;
|
||||
dev_set_drvdata(&rpdev->dev, priv);
|
||||
|
||||
ret = wago_led_register_leds(priv);
|
||||
if (ret) {
|
||||
priv->rpdev = NULL;
|
||||
return ret;
|
||||
}
|
||||
|
||||
dev_info(&rpdev->dev,
|
||||
"WAGO M4 LED wrapper ready — %d RGB LEDs on strip\n",
|
||||
"WAGO M4 RPMsg channel up — LED strip active (%d LEDs)\n",
|
||||
WAGO_LED_NUM_LEDS);
|
||||
return 0;
|
||||
}
|
||||
@@ -616,11 +634,15 @@ static struct rpmsg_driver wago_rpmsg_driver = {
|
||||
* ---------------------------------------------------------------------- */
|
||||
|
||||
/*
|
||||
* wago_boot_work - try to get the rproc handle and boot the M4.
|
||||
* wago_boot_work - try to get the rproc handle and attach/boot the M4.
|
||||
*
|
||||
* Called from a workqueue, so blocking operations are safe.
|
||||
* Reschedules itself with WAGO_BOOT_RETRY_MS if the firmware file is not
|
||||
* yet available (-ENOENT / -EAGAIN), giving the rootfs time to appear.
|
||||
*
|
||||
* When the M4 is already running (RPROC_DETACHED, U-Boot/SPL path) this
|
||||
* work is scheduled with zero delay from probe() so the RPMsg channel
|
||||
* comes up as fast as possible.
|
||||
*/
|
||||
static void wago_boot_work(struct work_struct *work)
|
||||
{
|
||||
@@ -659,22 +681,22 @@ static void wago_boot_work(struct work_struct *work)
|
||||
}
|
||||
|
||||
/*
|
||||
* If the M4 is already powered (RPROC_DETACHED = bootloader path),
|
||||
* just attach — no firmware file needed.
|
||||
* U-Boot / SPL path: M4 is already powered (RPROC_DETACHED).
|
||||
* Just attach — no firmware file needed.
|
||||
*/
|
||||
if (priv->rproc->state == RPROC_DETACHED) {
|
||||
dev_info(dev, "M4 already running — attaching\n");
|
||||
dev_info(dev, "M4 already running (SPL path) — attaching\n");
|
||||
ret = rproc_boot(priv->rproc);
|
||||
if (ret) {
|
||||
dev_err(dev, "rproc attach failed: %d\n", ret);
|
||||
goto fatal;
|
||||
}
|
||||
priv->rproc_booted_by_us = true;
|
||||
dev_info(dev, "M4 attached, waiting for RPMsg channel\n");
|
||||
dev_info(dev, "M4 attached, RPMsg channel expected shortly\n");
|
||||
return;
|
||||
}
|
||||
|
||||
/* Set the firmware name before each attempt (idempotent) */
|
||||
/* Linux-boot path: load firmware from filesystem */
|
||||
ret = rproc_set_firmware(priv->rproc, priv->fw_name);
|
||||
if (ret) {
|
||||
dev_err(dev, "rproc_set_firmware failed: %d\n", ret);
|
||||
@@ -746,7 +768,9 @@ static void wago_rproc_stop(struct wago_m4_led_priv *priv)
|
||||
static int wago_m4_led_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct wago_m4_led_priv *priv;
|
||||
struct device_node *rproc_np;
|
||||
const char *fw_name;
|
||||
unsigned long boot_delay_ms;
|
||||
int ret;
|
||||
|
||||
priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
|
||||
@@ -764,9 +788,21 @@ static int wago_m4_led_probe(struct platform_device *pdev)
|
||||
platform_set_drvdata(pdev, priv);
|
||||
|
||||
/*
|
||||
* Register the RPMsg driver first. The rpmsg core will call
|
||||
* wago_rpmsg_probe() once the M4 announces the "rpmsg-tty" channel,
|
||||
* regardless of whether the boot was done by us or the bootloader.
|
||||
* Register LED class devices immediately so that kernel triggers
|
||||
* (timer, pattern, ...) start working right away regardless of
|
||||
* whether the RPMsg channel is up yet.
|
||||
* wago_led_set() drops frames silently while rpdev == NULL.
|
||||
*/
|
||||
ret = wago_led_register_leds(priv);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "Failed to register LEDs: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Register the RPMsg driver. The rpmsg core will call
|
||||
* wago_rpmsg_probe() once the M4 announces the endpoint,
|
||||
* regardless of whether the boot was done by SPL or by us.
|
||||
*/
|
||||
ret = register_rpmsg_driver(&wago_rpmsg_driver);
|
||||
if (ret) {
|
||||
@@ -786,18 +822,41 @@ static int wago_m4_led_probe(struct platform_device *pdev)
|
||||
#endif /* CONFIG_LEDS_WAGO_M4_WRAPPER_SYSFS_PASSTHROUGH */
|
||||
|
||||
/*
|
||||
* Schedule the boot work. The first attempt fires after
|
||||
* WAGO_BOOT_INITIAL_DELAY_MS, giving the rootfs a head start.
|
||||
* If the M4 is already running (bootloader path), the work will
|
||||
* attach immediately without loading any firmware.
|
||||
* Determine boot delay:
|
||||
*
|
||||
* If the rproc is already available and in RPROC_DETACHED state
|
||||
* (U-Boot / SPL loaded the firmware), schedule boot_work immediately
|
||||
* so the RPMsg channel comes up as fast as possible.
|
||||
*
|
||||
* Otherwise use WAGO_BOOT_INITIAL_DELAY_MS to give the rootfs time
|
||||
* to mount before we try to load the firmware file.
|
||||
*/
|
||||
boot_delay_ms = WAGO_BOOT_INITIAL_DELAY_MS;
|
||||
rproc_np = of_parse_phandle(pdev->dev.of_node, "remoteproc", 0);
|
||||
if (rproc_np) {
|
||||
struct rproc *rp = rproc_get_by_phandle(rproc_np->phandle);
|
||||
|
||||
of_node_put(rproc_np);
|
||||
if (!IS_ERR_OR_NULL(rp)) {
|
||||
if (rp->state == RPROC_DETACHED) {
|
||||
boot_delay_ms = 0;
|
||||
dev_info(&pdev->dev,
|
||||
"M4 already running (SPL path), "
|
||||
"attaching immediately\n");
|
||||
}
|
||||
/* rproc_boot_work will re-acquire it lazily */
|
||||
rproc_put(rp);
|
||||
}
|
||||
}
|
||||
|
||||
INIT_DELAYED_WORK(&priv->boot_work, wago_boot_work);
|
||||
schedule_delayed_work(&priv->boot_work,
|
||||
msecs_to_jiffies(WAGO_BOOT_INITIAL_DELAY_MS));
|
||||
msecs_to_jiffies(boot_delay_ms));
|
||||
|
||||
dev_info(&pdev->dev,
|
||||
"WAGO M4 LED wrapper probed, boot in %d ms\n",
|
||||
WAGO_BOOT_INITIAL_DELAY_MS);
|
||||
"WAGO M4 LED wrapper probed, %d LEDs active, "
|
||||
"RPMsg attach in %lu ms\n",
|
||||
WAGO_LED_NUM_LEDS, boot_delay_ms);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user