Compare commits
13 Commits
8b8a65bcaf
...
ea18e83e94
| Author | SHA1 | Date | |
|---|---|---|---|
| ea18e83e94 | |||
| 869a9bb515 | |||
| 7766560288 | |||
| e7771f5d84 | |||
| fede4fbf28 | |||
| f54cd18f2f | |||
| be90ea6381 | |||
| 6373fa5baf | |||
| db380cc82d | |||
| 3168361da4 | |||
| 0fc48cf1ad | |||
| a938aad692 | |||
| e9cdbb93f3 |
@@ -0,0 +1,62 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/leds/wago,m4-led-wrapper.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: WAGO M4 RGB LED Strip Wrapper
|
||||
|
||||
maintainers:
|
||||
- WAGO GmbH & Co. KG <linux@wago.com>
|
||||
|
||||
description: |
|
||||
Driver for a WS2812 RGB LED strip (10 LEDs) controlled by the TI AM62x M4
|
||||
coprocessor running the Zephyr wago-led-server-app.
|
||||
|
||||
The driver communicates with the M4 via the RPMsg "wago-led" endpoint using
|
||||
a binary protocol of packed byte frames (command ID 0x01–0x04 defined in
|
||||
wago-m4-led-protocol.h). Each LED is exposed as a Linux LED multicolor (RGB)
|
||||
device. ASCII command injection is available only through the optional sysfs
|
||||
passthrough (CONFIG_LEDS_WAGO_M4_WRAPPER_SYSFS_PASSTHROUGH).
|
||||
|
||||
The M4 firmware may either be pre-loaded by the bootloader — in which case
|
||||
the driver attaches to the already-running core via remoteproc — or it may be
|
||||
loaded by the driver itself from /lib/firmware.
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: wago,m4-led-wrapper
|
||||
|
||||
remoteproc:
|
||||
$ref: /schemas/types.yaml#/definitions/phandle
|
||||
description:
|
||||
Phandle to the remoteproc node that manages the M4 coprocessor.
|
||||
|
||||
firmware-name:
|
||||
$ref: /schemas/types.yaml#/definitions/string
|
||||
description: |
|
||||
File name of the M4 ELF firmware image, looked up in /lib/firmware.
|
||||
Only required when the bootloader has not already started the M4.
|
||||
default: "wago-led-server.elf"
|
||||
|
||||
wago,reset-on-init:
|
||||
type: boolean
|
||||
description: |
|
||||
If present, the driver will reset (turn off) all LEDs when the RPMsg
|
||||
channel is first established. This is useful for clearing any colors
|
||||
inherited from the bootloader.
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- remoteproc
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
/* Board .dts snippet */
|
||||
leds-m4 {
|
||||
compatible = "wago,m4-led-wrapper";
|
||||
remoteproc = <&mcu_m4fss>;
|
||||
firmware-name = "wago-led-server.elf";
|
||||
};
|
||||
@@ -27,6 +27,15 @@ dtb-$(CONFIG_ARCH_K3) += k3-am623-pfc-750-8302.dtb
|
||||
dtb-$(CONFIG_ARCH_K3) += k3-am623-pfc-750-830x-ems.dtb
|
||||
dtb-$(CONFIG_ARCH_K3) += k3-am623-pfc-750-8400.dtb
|
||||
dtb-$(CONFIG_ARCH_K3) += k3-am623-spehvac.dtb
|
||||
dtb-$(CONFIG_ARCH_K3) += overlay-750-8400-wosm-p0004037.dtbo \
|
||||
overlay-750-8400-wosm-p0004263.dtbo \
|
||||
overlay-750-8400-base-p0003964.dtbo \
|
||||
overlay-750-8400-base-p0004305.dtbo \
|
||||
overlay-750-8400-base-p0004224.dtbo \
|
||||
overlay-750-8400-localbus-p0003978.dtbo \
|
||||
overlay-750-8400-localbus-p0004067.dtbo \
|
||||
overlay-750-8400-interaction-p0003965.dtbo \
|
||||
overlay-750-8400-interaction-p0004065.dtbo
|
||||
|
||||
# Boards with AM62Ax SoC
|
||||
dtb-$(CONFIG_ARCH_K3) += k3-am62a7-sk.dtb
|
||||
@@ -87,3 +96,4 @@ DTC_FLAGS_k3-am6548-iot2050-advanced-m2 += -@
|
||||
DTC_FLAGS_k3-j721e-common-proc-board += -@
|
||||
DTC_FLAGS_k3-j721s2-common-proc-board += -@
|
||||
DTC_FLAGS_k3-am623-pfc-750-8302 += -@
|
||||
DTC_FLAGS_k3-am623-pfc-750-8400 += -@
|
||||
|
||||
@@ -0,0 +1,166 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/dts-v1/;
|
||||
|
||||
/ {
|
||||
description = "WAGO AM623 PFC FIT Image";
|
||||
#address-cells = <1>;
|
||||
|
||||
images {
|
||||
kernel {
|
||||
description = "Linux Kernel";
|
||||
data = /incbin/("../../Image");
|
||||
type = "kernel";
|
||||
arch = "arm64";
|
||||
os = "linux";
|
||||
compression = "none";
|
||||
load = <0x80080000>;
|
||||
entry = <0x80080000>;
|
||||
hash-1 {
|
||||
algo = "sha256";
|
||||
};
|
||||
};
|
||||
|
||||
fdt-PFC-750-840x {
|
||||
description = "Flattened Device Tree blob";
|
||||
data = /incbin/("k3-am623-pfc-750-8400.dtb");
|
||||
type = "flat_dt";
|
||||
arch = "arm64";
|
||||
compression = "none";
|
||||
load = <0x88000000>;
|
||||
|
||||
hash-1 {
|
||||
algo = "sha256";
|
||||
};
|
||||
};
|
||||
|
||||
img-overlay-PFC-750-840x-wosm-p0004037 {
|
||||
description = "WOSM Overlay";
|
||||
data = /incbin/("overlay-750-8400-wosm-p0004037.dtbo");
|
||||
type = "flat_dt";
|
||||
arch = "arm64";
|
||||
compression = "none";
|
||||
|
||||
};
|
||||
|
||||
img-overlay-PFC-750-840x-wosm-p0004263 {
|
||||
description = "WOSM Overlay p0004263";
|
||||
data = /incbin/("overlay-750-8400-wosm-p0004263.dtbo");
|
||||
type = "flat_dt";
|
||||
arch = "arm64";
|
||||
compression = "none";
|
||||
};
|
||||
|
||||
img-overlay-PFC-750-840x-base-p0003964 {
|
||||
description = "Base Overlay";
|
||||
data = /incbin/("overlay-750-8400-base-p0003964.dtbo");
|
||||
type = "flat_dt";
|
||||
arch = "arm64";
|
||||
compression = "none";
|
||||
|
||||
};
|
||||
|
||||
img-overlay-PFC-750-840x-base-p0004224 {
|
||||
description = "Base Overlay p0004224";
|
||||
data = /incbin/("overlay-750-8400-base-p0004224.dtbo");
|
||||
type = "flat_dt";
|
||||
arch = "arm64";
|
||||
compression = "none";
|
||||
};
|
||||
|
||||
img-overlay-PFC-750-840x-base-p0004305 {
|
||||
description = "Base Overlay p0004305";
|
||||
data = /incbin/("overlay-750-8400-base-p0004305.dtbo");
|
||||
type = "flat_dt";
|
||||
arch = "arm64";
|
||||
compression = "none";
|
||||
};
|
||||
|
||||
img-overlay-PFC-750-840x-localbus-p0003978 {
|
||||
description = "Localbus Overlay";
|
||||
data = /incbin/("overlay-750-8400-localbus-p0003978.dtbo");
|
||||
type = "flat_dt";
|
||||
arch = "arm64";
|
||||
compression = "none";
|
||||
|
||||
};
|
||||
|
||||
img-overlay-PFC-750-840x-localbus-p0004067 {
|
||||
description = "Localbus Overlay p0004067";
|
||||
data = /incbin/("overlay-750-8400-localbus-p0004067.dtbo");
|
||||
type = "flat_dt";
|
||||
arch = "arm64";
|
||||
compression = "none";
|
||||
};
|
||||
|
||||
img-overlay-PFC-750-840x-interaction-p0003965 {
|
||||
description = "Interaction Overlay";
|
||||
data = /incbin/("overlay-750-8400-interaction-p0003965.dtbo");
|
||||
type = "flat_dt";
|
||||
arch = "arm64";
|
||||
compression = "none";
|
||||
|
||||
};
|
||||
|
||||
img-overlay-PFC-750-840x-interaction-p0004065 {
|
||||
description = "Interaction Overlay p0004065";
|
||||
data = /incbin/("overlay-750-8400-interaction-p0004065.dtbo");
|
||||
type = "flat_dt";
|
||||
arch = "arm64";
|
||||
compression = "none";
|
||||
};
|
||||
};
|
||||
|
||||
configurations {
|
||||
default = "conf-PFC-750-840x";
|
||||
conf-PFC-750-840x {
|
||||
description = "Boot Linux kernel with base FDT and overlays";
|
||||
kernel = "kernel";
|
||||
fdt = "fdt-PFC-750-840x";
|
||||
};
|
||||
|
||||
ovl-750-840x-wosm-p0004037 {
|
||||
description = "WOSM Overlay config";
|
||||
fdt = "img-overlay-PFC-750-840x-wosm-p0004037";
|
||||
};
|
||||
|
||||
ovl-750-840x-wosm-p0004263 {
|
||||
description = "WOSM Overlay p0004263 config";
|
||||
fdt = "img-overlay-PFC-750-840x-wosm-p0004263";
|
||||
};
|
||||
|
||||
ovl-750-840x-base-p0003964 {
|
||||
description = "Base Overlay config";
|
||||
fdt = "img-overlay-PFC-750-840x-base-p0003964";
|
||||
};
|
||||
|
||||
ovl-750-840x-base-p0004224 {
|
||||
description = "Base Overlay p0004224 config";
|
||||
fdt = "img-overlay-PFC-750-840x-base-p0004224";
|
||||
};
|
||||
|
||||
ovl-750-840x-base-p0004305 {
|
||||
description = "Base Overlay p0004305 config";
|
||||
fdt = "img-overlay-PFC-750-840x-base-p0004305";
|
||||
};
|
||||
|
||||
ovl-750-840x-localbus-p0003978 {
|
||||
description = "Localbus Overlay config";
|
||||
fdt = "img-overlay-PFC-750-840x-localbus-p0003978";
|
||||
};
|
||||
|
||||
ovl-750-840x-localbus-p0004067 {
|
||||
description = "Localbus Overlay p0004067 config";
|
||||
fdt = "img-overlay-PFC-750-840x-localbus-p0004067";
|
||||
};
|
||||
|
||||
ovl-750-840x-interaction-p0003965 {
|
||||
description = "Interaction Overlay config";
|
||||
fdt = "img-overlay-PFC-750-840x-interaction-p0003965";
|
||||
};
|
||||
|
||||
ovl-750-840x-interaction-p0004065 {
|
||||
description = "Interaction Overlay p0004065 config";
|
||||
fdt = "img-overlay-PFC-750-840x-interaction-p0004065";
|
||||
};
|
||||
};
|
||||
};
|
||||
@@ -6,14 +6,29 @@
|
||||
* Copyright (C) 2025 WAGO GmbH & Co. KG - https://www.wago.com/
|
||||
*/
|
||||
|
||||
#include "k3-am623-pfc-750-84xx-wosm.dtsi"
|
||||
#include "k3-am623-pfc-750-84xx-baseboard.dtsi"
|
||||
#include "k3-am623-pfc-750-84xx-kbus.dtsi"
|
||||
#include "k3-am623-pfc-750-84xx-interaction.dtsi"
|
||||
/dts-v1/;
|
||||
|
||||
#include "k3-am623-pfc-750-8xxx-common.dtsi"
|
||||
#include "k3-am623-pfc-750-84xx-wosm-base.dtsi"
|
||||
#include "k3-am623-pfc-tpm.dtsi"
|
||||
|
||||
/ {
|
||||
compatible = "wago,am623-pfc-750_8400-100d", "wago,am623-pfc", "ti,am625";
|
||||
model = "PFC400-750-8400";
|
||||
|
||||
aliases {
|
||||
usb1 = &usb1;
|
||||
ethernet0 = &cpsw_port2;
|
||||
ethernet1 = &cpsw_port1;
|
||||
};
|
||||
|
||||
leds_m4: leds-m4 {
|
||||
compatible = "wago,m4-led-wrapper";
|
||||
remoteproc = <&mcu_m4fss>;
|
||||
firmware-name = "wago-led-server-app.elf";
|
||||
wago,reset-on-init;
|
||||
status = "disabled";
|
||||
};
|
||||
};
|
||||
|
||||
&wsysinit {
|
||||
@@ -21,6 +36,10 @@
|
||||
board,variant = "pfc400";
|
||||
};
|
||||
|
||||
&slb9670 {
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
&main_spi2 {
|
||||
ti,spi-num-cs = <4>;
|
||||
|
||||
|
||||
@@ -23,6 +23,9 @@
|
||||
};
|
||||
|
||||
&mcu_spi0 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
ksz9477@0 {
|
||||
compatible = "microchip,ksz9477";
|
||||
pinctrl-names = "default";
|
||||
@@ -37,8 +40,6 @@
|
||||
interrupt-parent = <&main_gpio0>;
|
||||
interrupts = <13 IRQ_TYPE_EDGE_FALLING>;
|
||||
|
||||
microchip,single-led-mode;
|
||||
|
||||
ports {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
@@ -90,6 +91,9 @@
|
||||
};
|
||||
|
||||
&main_i2c1 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
eeprom_hw_id_bb: at24c02@54 {
|
||||
status = "disabled";
|
||||
compatible = "atmel,24c02";
|
||||
|
||||
@@ -9,15 +9,10 @@
|
||||
#include "k3-am623-pfc-rs485.dtsi"
|
||||
#include "k3-am623-pfc-rtc.dtsi"
|
||||
|
||||
&mcu_pmx0 {
|
||||
led_reset_pins_default: led-reset-pins-default {
|
||||
pinctrl-single,pins = <
|
||||
AM62X_MCU_IOPAD(0x050, PIN_OUTPUT | INPUT_EN, 7) /* (A9) WKUP_I2C0_SDA.GPIO0_20 nRST-LED-DISP */
|
||||
>;
|
||||
};
|
||||
};
|
||||
|
||||
&main_i2c1 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
status = "okay";
|
||||
|
||||
eeprom_hw_id_ib: at24c02@54 {
|
||||
@@ -27,250 +22,6 @@
|
||||
pagesize = <16>;
|
||||
};
|
||||
|
||||
/*
|
||||
* NXP PCA9552BS
|
||||
* 16-Bit I²C-Bus LED Driver
|
||||
*/
|
||||
led_bar60: pca9552@60 {
|
||||
compatible = "nxp,pca9552";
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&led_reset_pins_default>;
|
||||
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
reg = <0x60>;
|
||||
|
||||
lb60_0: sys-red@0 {
|
||||
label = "sys-red";
|
||||
reg = <0>;
|
||||
color = <LED_COLOR_ID_RED>;
|
||||
default-state = "keep";
|
||||
linux,default-trigger = "timer";
|
||||
};
|
||||
|
||||
lb60_1: sys-green@1 {
|
||||
label = "sys-green";
|
||||
reg = <1>;
|
||||
color = <LED_COLOR_ID_GREEN>;
|
||||
default-state = "keep";
|
||||
linux,default-trigger = "timer";
|
||||
};
|
||||
|
||||
lb60_2: sys-blue@2 {
|
||||
label = "sys-blue";
|
||||
reg = <2>;
|
||||
color = <LED_COLOR_ID_BLUE>;
|
||||
default-state = "off";
|
||||
};
|
||||
|
||||
lb60_3: run-red@3 {
|
||||
label = "run-red";
|
||||
reg = <3>;
|
||||
color = <LED_COLOR_ID_RED>;
|
||||
default-state = "off";
|
||||
};
|
||||
|
||||
lb60_4: run-green@4 {
|
||||
label = "run-green";
|
||||
reg = <4>;
|
||||
color = <LED_COLOR_ID_GREEN>;
|
||||
default-state = "off";
|
||||
};
|
||||
|
||||
lb60_5: run-blue@5 {
|
||||
label = "run-blue";
|
||||
reg = <5>;
|
||||
color = <LED_COLOR_ID_BLUE>;
|
||||
default-state = "off";
|
||||
};
|
||||
|
||||
lb60_6: io-red@6 {
|
||||
label = "io-red";
|
||||
reg = <6>;
|
||||
color = <LED_COLOR_ID_RED>;
|
||||
default-state = "off";
|
||||
};
|
||||
|
||||
lb60_7: io-green@7 {
|
||||
label = "io-green";
|
||||
reg = <7>;
|
||||
color = <LED_COLOR_ID_GREEN>;
|
||||
default-state = "off";
|
||||
};
|
||||
|
||||
lb60_8: io-blue@8 {
|
||||
label = "io-blue";
|
||||
reg = <8>;
|
||||
color = <LED_COLOR_ID_BLUE>;
|
||||
default-state = "off";
|
||||
};
|
||||
|
||||
lb60_9: em-red@9 {
|
||||
label = "em-red";
|
||||
reg = <9>;
|
||||
color = <LED_COLOR_ID_RED>;
|
||||
default-state = "off";
|
||||
};
|
||||
|
||||
lb60_10: em-green@10 {
|
||||
label = "em-green";
|
||||
reg = <10>;
|
||||
color = <LED_COLOR_ID_GREEN>;
|
||||
default-state = "off";
|
||||
};
|
||||
|
||||
lb60_11: em-blue@11 {
|
||||
label = "em-blue";
|
||||
reg = <11>;
|
||||
color = <LED_COLOR_ID_BLUE>;
|
||||
default-state = "off";
|
||||
};
|
||||
|
||||
lb60_12: u1-red@12 {
|
||||
label = "u1-red";
|
||||
reg = <12>;
|
||||
color = <LED_COLOR_ID_RED>;
|
||||
default-state = "off";
|
||||
};
|
||||
|
||||
lb60_13: u1-green@13 {
|
||||
label = "u1-green";
|
||||
reg = <13>;
|
||||
color = <LED_COLOR_ID_GREEN>;
|
||||
default-state = "off";
|
||||
};
|
||||
|
||||
lb60_14: u1-blue@14 {
|
||||
label = "u1-blue";
|
||||
reg = <14>;
|
||||
color = <LED_COLOR_ID_BLUE>;
|
||||
default-state = "off";
|
||||
};
|
||||
|
||||
lb60_15: sys-overwrite@15 {
|
||||
label = "sys-overwrite";
|
||||
reg = <15>;
|
||||
default-state = "on";
|
||||
};
|
||||
};
|
||||
|
||||
/*
|
||||
* NXP PCA9552BS
|
||||
* 16-Bit I²C-Bus LED Driver
|
||||
*/
|
||||
led_bar61: pca9552@61 {
|
||||
compatible = "nxp,pca9552";
|
||||
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
reg = <0x61>;
|
||||
|
||||
lb61_0: u2-red@0 {
|
||||
label = "u2-red";
|
||||
reg = <0>;
|
||||
color = <LED_COLOR_ID_RED>;
|
||||
default-state = "off";
|
||||
};
|
||||
|
||||
lb61_1: u2-green@1 {
|
||||
label = "u2-green";
|
||||
reg = <1>;
|
||||
color = <LED_COLOR_ID_GREEN>;
|
||||
default-state = "off";
|
||||
};
|
||||
|
||||
lb61_2: u2-blue@2 {
|
||||
label = "u2-blue";
|
||||
reg = <2>;
|
||||
color = <LED_COLOR_ID_BLUE>;
|
||||
default-state = "off";
|
||||
};
|
||||
|
||||
lb61_3: u3-red@3 {
|
||||
label = "u3-red";
|
||||
reg = <3>;
|
||||
color = <LED_COLOR_ID_RED>;
|
||||
default-state = "off";
|
||||
};
|
||||
|
||||
lb61_4: u3-green@4 {
|
||||
label = "u3-green";
|
||||
reg = <4>;
|
||||
color = <LED_COLOR_ID_GREEN>;
|
||||
default-state = "off";
|
||||
};
|
||||
|
||||
lb61_5: u3-blue@5 {
|
||||
label = "u3-blue";
|
||||
reg = <5>;
|
||||
color = <LED_COLOR_ID_BLUE>;
|
||||
default-state = "off";
|
||||
};
|
||||
|
||||
lb61_6: u4-red@6 {
|
||||
label = "u4-red";
|
||||
reg = <6>;
|
||||
color = <LED_COLOR_ID_RED>;
|
||||
default-state = "off";
|
||||
};
|
||||
|
||||
lb61_7: u4-green@7 {
|
||||
label = "u4-green";
|
||||
reg = <7>;
|
||||
color = <LED_COLOR_ID_GREEN>;
|
||||
default-state = "off";
|
||||
};
|
||||
|
||||
lb61_8: u4-blue@8 {
|
||||
label = "u4-blue";
|
||||
reg = <8>;
|
||||
color = <LED_COLOR_ID_BLUE>;
|
||||
default-state = "off";
|
||||
};
|
||||
|
||||
lb61_9: u5-red@9 {
|
||||
label = "u5-red";
|
||||
reg = <9>;
|
||||
color = <LED_COLOR_ID_RED>;
|
||||
default-state = "off";
|
||||
};
|
||||
|
||||
lb61_10: u5-green@10 {
|
||||
label = "u5-green";
|
||||
reg = <10>;
|
||||
color = <LED_COLOR_ID_GREEN>;
|
||||
default-state = "off";
|
||||
};
|
||||
|
||||
lb61_11: u5-blue@11 {
|
||||
label = "u5-blue";
|
||||
reg = <11>;
|
||||
color = <LED_COLOR_ID_BLUE>;
|
||||
default-state = "off";
|
||||
};
|
||||
|
||||
lb61_12: u6_r@12 {
|
||||
label = "u6-red";
|
||||
reg = <12>;
|
||||
color = <LED_COLOR_ID_RED>;
|
||||
default-state = "off";
|
||||
};
|
||||
|
||||
lb61_13: u6_g@13 {
|
||||
label = "u6-green";
|
||||
reg = <13>;
|
||||
color = <LED_COLOR_ID_GREEN>;
|
||||
default-state = "off";
|
||||
};
|
||||
|
||||
lb61_14: u6_b@14 {
|
||||
label = "u6-blue";
|
||||
reg = <14>;
|
||||
color = <LED_COLOR_ID_BLUE>;
|
||||
default-state = "off";
|
||||
};
|
||||
};
|
||||
|
||||
/*
|
||||
* Dynamic NFC/RFID tag IC with 4-Kbit EEPROM
|
||||
*
|
||||
@@ -284,112 +35,4 @@
|
||||
compatible = "st25dv,st25dv04k";
|
||||
reg = <0x53>;
|
||||
};
|
||||
};
|
||||
|
||||
/*
|
||||
* Multicolor LED groups for PFC400 (RGB)
|
||||
* Group triplets from PCA9552 into multicolor LEDs to enable
|
||||
* led-class-multicolor and pattern trigger RGB support.
|
||||
*/
|
||||
/ {
|
||||
sys_led: sys-led {
|
||||
compatible = "leds-group-multicolor";
|
||||
label = "sys";
|
||||
color = <LED_COLOR_ID_RGB>;
|
||||
function = LED_FUNCTION_INDICATOR;
|
||||
default-state = "keep";
|
||||
mc-allow-subled-writes;
|
||||
leds = <&lb60_0 &lb60_1 &lb60_2>;
|
||||
max-brightness = <255>;
|
||||
};
|
||||
|
||||
run_led: run-led {
|
||||
compatible = "leds-group-multicolor";
|
||||
label = "run";
|
||||
color = <LED_COLOR_ID_RGB>;
|
||||
function = LED_FUNCTION_INDICATOR;
|
||||
default-state = "off";
|
||||
leds = <&lb60_3 &lb60_4 &lb60_5>;
|
||||
max-brightness = <255>;
|
||||
};
|
||||
|
||||
io_led: io-led {
|
||||
compatible = "leds-group-multicolor";
|
||||
label = "io";
|
||||
color = <LED_COLOR_ID_RGB>;
|
||||
function = LED_FUNCTION_INDICATOR;
|
||||
default-state = "off";
|
||||
leds = <&lb60_6 &lb60_7 &lb60_8>;
|
||||
max-brightness = <255>;
|
||||
};
|
||||
|
||||
em_led: em-led {
|
||||
compatible = "leds-group-multicolor";
|
||||
label = "em";
|
||||
color = <LED_COLOR_ID_RGB>;
|
||||
function = LED_FUNCTION_INDICATOR;
|
||||
default-state = "off";
|
||||
leds = <&lb60_9 &lb60_10 &lb60_11>;
|
||||
max-brightness = <255>;
|
||||
};
|
||||
|
||||
u1_led: u1-led {
|
||||
compatible = "leds-group-multicolor";
|
||||
label = "u1";
|
||||
color = <LED_COLOR_ID_RGB>;
|
||||
function = LED_FUNCTION_INDICATOR;
|
||||
default-state = "off";
|
||||
leds = <&lb60_12 &lb60_13 &lb60_14>;
|
||||
max-brightness = <255>;
|
||||
};
|
||||
|
||||
u2_led: u2-led {
|
||||
compatible = "leds-group-multicolor";
|
||||
label = "u2";
|
||||
color = <LED_COLOR_ID_RGB>;
|
||||
function = LED_FUNCTION_INDICATOR;
|
||||
default-state = "off";
|
||||
leds = <&lb61_0 &lb61_1 &lb61_2>;
|
||||
max-brightness = <255>;
|
||||
};
|
||||
|
||||
u3_led: u3-led {
|
||||
compatible = "leds-group-multicolor";
|
||||
label = "u3";
|
||||
color = <LED_COLOR_ID_RGB>;
|
||||
function = LED_FUNCTION_INDICATOR;
|
||||
default-state = "off";
|
||||
leds = <&lb61_3 &lb61_4 &lb61_5>;
|
||||
max-brightness = <255>;
|
||||
};
|
||||
|
||||
u4_led: u4-led {
|
||||
compatible = "leds-group-multicolor";
|
||||
label = "u4";
|
||||
color = <LED_COLOR_ID_RGB>;
|
||||
function = LED_FUNCTION_INDICATOR;
|
||||
default-state = "off";
|
||||
leds = <&lb61_6 &lb61_7 &lb61_8>;
|
||||
max-brightness = <255>;
|
||||
};
|
||||
|
||||
u5_led: u5-led {
|
||||
compatible = "leds-group-multicolor";
|
||||
label = "u5";
|
||||
color = <LED_COLOR_ID_RGB>;
|
||||
function = LED_FUNCTION_INDICATOR;
|
||||
default-state = "off";
|
||||
leds = <&lb61_9 &lb61_10 &lb61_11>;
|
||||
max-brightness = <255>;
|
||||
};
|
||||
|
||||
u6_led: u6-led {
|
||||
compatible = "leds-group-multicolor";
|
||||
label = "u6";
|
||||
color = <LED_COLOR_ID_RGB>;
|
||||
function = LED_FUNCTION_INDICATOR;
|
||||
default-state = "off";
|
||||
leds = <&lb61_12 &lb61_13 &lb61_14>;
|
||||
max-brightness = <255>;
|
||||
};
|
||||
};
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
#include "k3-am623-pfc-kbus.dtsi"
|
||||
|
||||
/ {
|
||||
&{/} {
|
||||
/* this name of the gpio-keys device is a
|
||||
* historical heritage from 3.6.11 kernel.
|
||||
* the device-name is checked in omsd. So we
|
||||
@@ -71,6 +71,9 @@
|
||||
};
|
||||
|
||||
&main_i2c1 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
eeprom_hw_id_lb: at24c02@50 {
|
||||
status = "disabled";
|
||||
compatible = "atmel,24c02";
|
||||
|
||||
@@ -0,0 +1,144 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
/ {
|
||||
compatible = "ti,am623-pfc", "ti,am625";
|
||||
|
||||
memory@80000000 {
|
||||
device_type = "memory";
|
||||
|
||||
/* 4 GiB RAM */
|
||||
reg = <0x00000000 0x80000000 0x00000000 0x80000000>,
|
||||
<0x00000008 0x80000000 0x00000000 0x80000000>;
|
||||
};
|
||||
|
||||
vcc_1v8_sys: regulator-4 {
|
||||
compatible = "regulator-fixed";
|
||||
regulator-name = "vcc_1v8_sys";
|
||||
regulator-min-microvolt = <1800000>;
|
||||
regulator-max-microvolt = <1800000>;
|
||||
vin-supply = <&vmain_pd>;
|
||||
regulator-always-on;
|
||||
regulator-boot-on;
|
||||
};
|
||||
|
||||
ldo1_sd_1v8_reg: ldo1-sd-1v8-reg {
|
||||
/* Output VLDO1 of tps65219 */
|
||||
compatible = "regulator-gpio";
|
||||
regulator-name = "VDDSHV_SD_1V8_IO_PMIC_GPIO";
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&mcu_sd_vsel_pin>;
|
||||
regulator-min-microvolt = <1800000>;
|
||||
regulator-max-microvolt = <3300000>;
|
||||
regulator-boot-on;
|
||||
vin-supply = <&vcc_5v0>;
|
||||
gpios = <&mcu_gpio0 10 GPIO_ACTIVE_HIGH>;
|
||||
states = <1800000 0x0>,
|
||||
<3300000 0x1>;
|
||||
};
|
||||
};
|
||||
|
||||
&mcu_pmx0 {
|
||||
mcu_sd_vsel_pin: mcu-sd-vsel-pins {
|
||||
pinctrl-single,pins = <
|
||||
AM64X_MCU_IOPAD(0x028, PIN_OUTPUT, 7) /* (C5) WKUP_UART0_TXD.MCU_GPIO0_10 */
|
||||
>;
|
||||
};
|
||||
};
|
||||
|
||||
&cpu0 {
|
||||
cpu-supply = <&buck1_30_reg>;
|
||||
};
|
||||
|
||||
&cpu1 {
|
||||
cpu-supply = <&buck1_30_reg>;
|
||||
};
|
||||
|
||||
&cpu2 {
|
||||
cpu-supply = <&buck1_30_reg>;
|
||||
};
|
||||
|
||||
&cpu3 {
|
||||
cpu-supply = <&buck1_30_reg>;
|
||||
};
|
||||
|
||||
&main_i2c0 {
|
||||
tps65219_30: pmic@30 {
|
||||
compatible = "ti,tps65219";
|
||||
reg = <0x30>;
|
||||
system-power-controller;
|
||||
|
||||
buck1-supply = <&vcc_5v0>;
|
||||
buck2-supply = <&vcc_5v0>;
|
||||
buck3-supply = <&vcc_5v0>;
|
||||
ldo1-supply = <&vcc_3v3_sys>;
|
||||
ldo2-supply = <&vcc_1v8_sys>;
|
||||
ldo3-supply = <&vcc_3v3_sys>;
|
||||
ldo4-supply = <&vcc_3v3_sys>;
|
||||
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&pmic_irq_pins_default>;
|
||||
|
||||
interrupt-parent = <&main_gpio0>;
|
||||
interrupts = <68 IRQ_TYPE_EDGE_FALLING>;
|
||||
|
||||
/* NOTICE:
|
||||
*
|
||||
* We could use "tps65219-gpio" here with GPO1 (EN-DCDC-1V8)
|
||||
* and GPO2 (CARRIER_PWR_EN for SD-Card and I2C1 devices) enabled,
|
||||
* but will skip it for now and trust the bootloader or TPS-NVM for the
|
||||
* correct settings correctly.
|
||||
*
|
||||
* For now we declare vcc_1v8_sys as being fixed.
|
||||
*
|
||||
*/
|
||||
regulators {
|
||||
buck1_30_reg: buck1 {
|
||||
regulator-name = "VDD_CORE";
|
||||
regulator-min-microvolt = <850000>;
|
||||
regulator-max-microvolt = <850000>;
|
||||
regulator-boot-on;
|
||||
regulator-always-on;
|
||||
};
|
||||
|
||||
buck2_30_reg: buck2 {
|
||||
regulator-name = "VCC3V3";
|
||||
regulator-min-microvolt = <3300000>;
|
||||
regulator-max-microvolt = <3300000>;
|
||||
regulator-boot-on;
|
||||
regulator-always-on;
|
||||
};
|
||||
|
||||
buck3_30_reg: buck3 {
|
||||
regulator-name = "VDD_LPDDR4";
|
||||
regulator-min-microvolt = <1200000>;
|
||||
regulator-max-microvolt = <1200000>;
|
||||
regulator-boot-on;
|
||||
regulator-always-on;
|
||||
};
|
||||
|
||||
ldo2_30_reg: ldo2 {
|
||||
regulator-name = "VDDAR_CORE";
|
||||
regulator-min-microvolt = <850000>;
|
||||
regulator-max-microvolt = <850000>;
|
||||
regulator-boot-on;
|
||||
regulator-always-on;
|
||||
};
|
||||
|
||||
ldo3_30_reg: ldo3 {
|
||||
regulator-name = "VDDA_1V8";
|
||||
regulator-min-microvolt = <1800000>;
|
||||
regulator-max-microvolt = <1800000>;
|
||||
regulator-boot-on;
|
||||
regulator-always-on;
|
||||
};
|
||||
|
||||
ldo4_30_reg: ldo4 {
|
||||
regulator-name = "VDDA_2V5";
|
||||
regulator-min-microvolt = <2500000>;
|
||||
regulator-max-microvolt = <2500000>;
|
||||
regulator-boot-on;
|
||||
regulator-always-on;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
@@ -6,17 +6,12 @@
|
||||
* Copyright (C) 2025 WAGO GmbH & Co. KG - https://www.wago.com/
|
||||
*/
|
||||
|
||||
#include "k3-am623-pfc-750-8xxx-common.dtsi"
|
||||
#include "k3-am623-pfc-tpm.dtsi"
|
||||
|
||||
/ {
|
||||
|
||||
&{/} {
|
||||
compatible = "ti,am623-pfc", "ti,am625";
|
||||
|
||||
aliases {
|
||||
usb1 = &usb1;
|
||||
ethernet0 = &cpsw_port2;
|
||||
ethernet1 = &cpsw_port1;
|
||||
};
|
||||
#address-cells = <2>;
|
||||
#size-cells = <2>;
|
||||
|
||||
cpus {
|
||||
cpu@0 {
|
||||
@@ -71,6 +66,9 @@
|
||||
};
|
||||
|
||||
&fss {
|
||||
#address-cells = <2>;
|
||||
#size-cells = <2>;
|
||||
|
||||
uio_mram: uio@500000000 {
|
||||
compatible = "uio_pdrv_genirq";
|
||||
linux,uio-name = "UIO_OSPI_MRAM";
|
||||
@@ -93,12 +91,6 @@
|
||||
AM64X_MCU_IOPAD(0x010, PIN_OUTPUT, 0) /* (C9) MCU_SPI0_D1 MOSI */
|
||||
>;
|
||||
};
|
||||
|
||||
mcu_sd_vsel_pin: mcu-sd-vsel-pins {
|
||||
pinctrl-single,pins = <
|
||||
AM64X_MCU_IOPAD(0x028, PIN_OUTPUT, 7) /* (C5) WKUP_UART0_TXD.MCU_GPIO0_10 */
|
||||
>;
|
||||
};
|
||||
};
|
||||
|
||||
&main_pmx0 {
|
||||
@@ -190,6 +182,8 @@
|
||||
};
|
||||
|
||||
&ospi0 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
compatible = "ti,am654-ospi";
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&ospi0_pins_default>;
|
||||
@@ -242,6 +236,8 @@
|
||||
};
|
||||
|
||||
&main_i2c0 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
status = "okay";
|
||||
|
||||
eeprom_hw_id_wosm: at24c02@50 {
|
||||
@@ -250,86 +246,6 @@
|
||||
reg = <0x50>;
|
||||
pagesize = <16>;
|
||||
};
|
||||
|
||||
tps65219_30: pmic@30 {
|
||||
compatible = "ti,tps65219";
|
||||
reg = <0x30>;
|
||||
system-power-controller;
|
||||
|
||||
buck1-supply = <&vcc_5v0>;
|
||||
buck2-supply = <&vcc_5v0>;
|
||||
buck3-supply = <&vcc_5v0>;
|
||||
ldo1-supply = <&vcc_3v3_sys>;
|
||||
ldo2-supply = <&vcc_1v8_sys>;
|
||||
ldo3-supply = <&vcc_3v3_sys>;
|
||||
ldo4-supply = <&vcc_3v3_sys>;
|
||||
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&pmic_irq_pins_default>;
|
||||
|
||||
interrupt-parent = <&main_gpio0>;
|
||||
interrupts = <68 IRQ_TYPE_EDGE_FALLING>;
|
||||
|
||||
/* NOTICE:
|
||||
*
|
||||
* We could use "tps65219-gpio" here with GPO1 (EN-DCDC-1V8)
|
||||
* and GPO2 (CARRIER_PWR_EN for SD-Card and I2C1 devices) enabled,
|
||||
* but will skip it for now and trust the bootloader or TPS-NVM for the
|
||||
* correct settings correctly.
|
||||
*
|
||||
* For now we declare vcc_1v8_sys as being fixed.
|
||||
*
|
||||
*/
|
||||
regulators {
|
||||
buck1_30_reg: buck1 {
|
||||
regulator-name = "VDD_CORE";
|
||||
regulator-min-microvolt = <750000>;
|
||||
regulator-max-microvolt = <850000>;
|
||||
regulator-boot-on;
|
||||
regulator-always-on;
|
||||
};
|
||||
|
||||
buck2_30_reg: buck2 {
|
||||
regulator-name = "VCC3V3";
|
||||
regulator-min-microvolt = <3300000>;
|
||||
regulator-max-microvolt = <3300000>;
|
||||
regulator-boot-on;
|
||||
regulator-always-on;
|
||||
};
|
||||
|
||||
buck3_30_reg: buck3 {
|
||||
regulator-name = "VDD_LPDDR4";
|
||||
regulator-min-microvolt = <1200000>;
|
||||
regulator-max-microvolt = <1200000>;
|
||||
regulator-boot-on;
|
||||
regulator-always-on;
|
||||
};
|
||||
|
||||
ldo2_30_reg: ldo2 {
|
||||
regulator-name = "VDDAR_CORE";
|
||||
regulator-min-microvolt = <850000>;
|
||||
regulator-max-microvolt = <850000>;
|
||||
regulator-boot-on;
|
||||
regulator-always-on;
|
||||
};
|
||||
|
||||
ldo3_30_reg: ldo3 {
|
||||
regulator-name = "VDDA_1V8";
|
||||
regulator-min-microvolt = <1800000>;
|
||||
regulator-max-microvolt = <1800000>;
|
||||
regulator-boot-on;
|
||||
regulator-always-on;
|
||||
};
|
||||
|
||||
ldo4_30_reg: ldo4 {
|
||||
regulator-name = "VDDA_2V5";
|
||||
regulator-min-microvolt = <2500000>;
|
||||
regulator-max-microvolt = <2500000>;
|
||||
regulator-boot-on;
|
||||
regulator-always-on;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
&main_i2c1 {
|
||||
|
||||
@@ -28,27 +28,27 @@
|
||||
|
||||
opp-table {
|
||||
opp-200000000 {
|
||||
opp-microvolt = <750000>;
|
||||
opp-microvolt = <850000>;
|
||||
};
|
||||
|
||||
opp-400000000 {
|
||||
opp-microvolt = <750000>;
|
||||
opp-microvolt = <850000>;
|
||||
};
|
||||
|
||||
opp-600000000 {
|
||||
opp-microvolt = <750000>;
|
||||
opp-microvolt = <850000>;
|
||||
};
|
||||
|
||||
opp-800000000 {
|
||||
opp-microvolt = <750000>;
|
||||
opp-microvolt = <850000>;
|
||||
};
|
||||
|
||||
opp-1000000000 {
|
||||
opp-microvolt = <750000>;
|
||||
opp-microvolt = <850000>;
|
||||
};
|
||||
|
||||
opp-1250000000 {
|
||||
opp-microvolt = <750000>;
|
||||
opp-microvolt = <850000>;
|
||||
};
|
||||
|
||||
opp-1400000000 {
|
||||
@@ -110,9 +110,20 @@
|
||||
no-map;
|
||||
};
|
||||
|
||||
/*
|
||||
* U-Boot IPC side-door shared memory.
|
||||
* Written by U-Boot (A53) to send LED commands to the M4.
|
||||
* Must not be touched by the Linux kernel.
|
||||
* See UBOOT_IPC_SHMEM_PHYS in wago_m4_led_protocol.h.
|
||||
*/
|
||||
uboot_ipc_shm: uboot-ipc-shm@9cbff000 {
|
||||
reg = <0x00 0x9cbff000 0x00 0x1000>;
|
||||
no-map;
|
||||
};
|
||||
|
||||
mcu_m4fss_dma_memory_region: m4f-dma-memory@9cb00000 {
|
||||
compatible = "shared-dma-pool";
|
||||
reg = <0x00 0x9cb00000 0x00 0x100000>;
|
||||
reg = <0x00 0x9cb00000 0x00 0xff000>;
|
||||
no-map;
|
||||
};
|
||||
|
||||
@@ -275,6 +286,7 @@
|
||||
status = "okay";
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&main_uart4_pins_default>;
|
||||
rts-gpios = <&main_gpio0 38 GPIO_ACTIVE_HIGH>;
|
||||
};
|
||||
|
||||
&main_i2c0 { /* type label */
|
||||
@@ -379,5 +391,6 @@
|
||||
mboxes = <&mailbox0_cluster0 &mbox_m4_0>;
|
||||
memory-region = <&mcu_m4fss_dma_memory_region>,
|
||||
<&mcu_m4fss_memory_region>;
|
||||
ti,no-auto-boot;
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
@@ -20,6 +20,9 @@
|
||||
};
|
||||
|
||||
&main_spi0 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
spi-rt;
|
||||
spi-rt-prio = <81>;
|
||||
|
||||
|
||||
@@ -7,6 +7,9 @@
|
||||
*/
|
||||
|
||||
&main_i2c1 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
/*
|
||||
* PCA9538, 8-Bit I/O Expander
|
||||
*/
|
||||
@@ -29,7 +32,6 @@
|
||||
};
|
||||
|
||||
&main_uart4 { /* RS485 Interface */
|
||||
rts-gpios = <&main_gpio0 38 GPIO_ACTIVE_HIGH>;
|
||||
/* RS485 termination is controlled via GPIO userspace tools */
|
||||
rs485-rts-active-high;
|
||||
linux,rs485-enabled-at-boot-time;
|
||||
|
||||
@@ -7,11 +7,14 @@
|
||||
*/
|
||||
|
||||
&main_i2c1 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
rtc@52 {
|
||||
compatible = "microcrystal,rv3028";
|
||||
reg = <0x52>;
|
||||
|
||||
/*
|
||||
/*
|
||||
* The backup goldcap is supplied with a higher voltage (+5V)
|
||||
* then the RTC (+3,3) itself. It requires a special setup
|
||||
* to avoid hardware failure.
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/dts-v1/;
|
||||
/plugin/;
|
||||
|
||||
#include "k3-pinctrl.h"
|
||||
#include <dt-bindings/gpio/gpio.h>
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
#include <dt-bindings/leds/common.h>
|
||||
|
||||
#include "k3-am623-pfc-750-84xx-baseboard.dtsi"
|
||||
|
||||
&mcu_spi0 {
|
||||
ksz9477@0 {
|
||||
microchip,single-led-mode;
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,25 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/dts-v1/;
|
||||
/plugin/;
|
||||
|
||||
#include "k3-pinctrl.h"
|
||||
#include <dt-bindings/gpio/gpio.h>
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
#include <dt-bindings/leds/common.h>
|
||||
|
||||
#include "k3-am623-pfc-750-84xx-baseboard.dtsi"
|
||||
|
||||
&main_pmx0 {
|
||||
main_uart4_pins_default: main-uart4-default-pins {
|
||||
pinctrl-single,pins = <
|
||||
AM62X_IOPAD(0x0b0, PIN_INPUT, 3) /* (K22) UART4_RXD */
|
||||
AM62X_IOPAD(0x0b4, PIN_OUTPUT, 3) /* (K24) UART4_TXD */
|
||||
AM62X_IOPAD(0x0098, PIN_OUTPUT_PULLDOWN, 7) /* (U23) GPIO0_37 (RS-DE) */
|
||||
>;
|
||||
};
|
||||
};
|
||||
|
||||
&main_uart4 {
|
||||
pinctrl-0 = <&main_uart4_pins_default>;
|
||||
rts-gpios = <&main_gpio0 37 GPIO_ACTIVE_HIGH>;
|
||||
};
|
||||
@@ -0,0 +1,36 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/dts-v1/;
|
||||
/plugin/;
|
||||
|
||||
#include "k3-pinctrl.h"
|
||||
#include <dt-bindings/gpio/gpio.h>
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
#include <dt-bindings/leds/common.h>
|
||||
|
||||
#include "k3-am623-pfc-750-84xx-baseboard.dtsi"
|
||||
|
||||
/*
|
||||
* Rev.3 hardware has a different KSZ9477 port-to-PHY routing compared
|
||||
* to Rev.2. Override the port labels accordingly.
|
||||
*/
|
||||
&mcu_spi0 {
|
||||
ksz9477@0 {
|
||||
ports {
|
||||
port@0 {
|
||||
label = "ethX1";
|
||||
};
|
||||
|
||||
port@2 {
|
||||
label = "ethX3";
|
||||
};
|
||||
|
||||
port@3 {
|
||||
label = "ethX4";
|
||||
};
|
||||
|
||||
port@4 {
|
||||
label = "ethEM";
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,375 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/dts-v1/;
|
||||
/plugin/;
|
||||
|
||||
#include "k3-pinctrl.h"
|
||||
#include <dt-bindings/gpio/gpio.h>
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
#include <dt-bindings/leds/common.h>
|
||||
|
||||
#include "k3-am623-pfc-750-84xx-interaction.dtsi"
|
||||
|
||||
&mcu_pmx0 {
|
||||
led_reset_pins_default: led-reset-pins-default {
|
||||
pinctrl-single,pins = <
|
||||
AM62X_MCU_IOPAD(0x050, PIN_OUTPUT | INPUT_EN, 7) /* (A9) WKUP_I2C0_SDA.GPIO0_20 nRST-LED-DISP */
|
||||
>;
|
||||
};
|
||||
};
|
||||
|
||||
&main_i2c1 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
/*
|
||||
* NXP PCA9552BS
|
||||
* 16-Bit I2C-Bus LED Driver
|
||||
*/
|
||||
led_bar60: pca9552@60 {
|
||||
compatible = "nxp,pca9552";
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&led_reset_pins_default>;
|
||||
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
reg = <0x60>;
|
||||
|
||||
lb60_0: sys-red@0 {
|
||||
label = "sys-red";
|
||||
reg = <0>;
|
||||
color = <LED_COLOR_ID_RED>;
|
||||
default-state = "keep";
|
||||
linux,default-trigger = "timer";
|
||||
};
|
||||
|
||||
lb60_1: sys-green@1 {
|
||||
label = "sys-green";
|
||||
reg = <1>;
|
||||
color = <LED_COLOR_ID_GREEN>;
|
||||
default-state = "keep";
|
||||
linux,default-trigger = "timer";
|
||||
};
|
||||
|
||||
lb60_2: sys-blue@2 {
|
||||
label = "sys-blue";
|
||||
reg = <2>;
|
||||
color = <LED_COLOR_ID_BLUE>;
|
||||
default-state = "off";
|
||||
};
|
||||
|
||||
lb60_3: run-red@3 {
|
||||
label = "run-red";
|
||||
reg = <3>;
|
||||
color = <LED_COLOR_ID_RED>;
|
||||
default-state = "off";
|
||||
};
|
||||
|
||||
lb60_4: run-green@4 {
|
||||
label = "run-green";
|
||||
reg = <4>;
|
||||
color = <LED_COLOR_ID_GREEN>;
|
||||
default-state = "off";
|
||||
};
|
||||
|
||||
lb60_5: run-blue@5 {
|
||||
label = "run-blue";
|
||||
reg = <5>;
|
||||
color = <LED_COLOR_ID_BLUE>;
|
||||
default-state = "off";
|
||||
};
|
||||
|
||||
lb60_6: io-red@6 {
|
||||
label = "io-red";
|
||||
reg = <6>;
|
||||
color = <LED_COLOR_ID_RED>;
|
||||
default-state = "off";
|
||||
};
|
||||
|
||||
lb60_7: io-green@7 {
|
||||
label = "io-green";
|
||||
reg = <7>;
|
||||
color = <LED_COLOR_ID_GREEN>;
|
||||
default-state = "off";
|
||||
};
|
||||
|
||||
lb60_8: io-blue@8 {
|
||||
label = "io-blue";
|
||||
reg = <8>;
|
||||
color = <LED_COLOR_ID_BLUE>;
|
||||
default-state = "off";
|
||||
};
|
||||
|
||||
lb60_9: em-red@9 {
|
||||
label = "em-red";
|
||||
reg = <9>;
|
||||
color = <LED_COLOR_ID_RED>;
|
||||
default-state = "off";
|
||||
};
|
||||
|
||||
lb60_10: em-green@10 {
|
||||
label = "em-green";
|
||||
reg = <10>;
|
||||
color = <LED_COLOR_ID_GREEN>;
|
||||
default-state = "off";
|
||||
};
|
||||
|
||||
lb60_11: em-blue@11 {
|
||||
label = "em-blue";
|
||||
reg = <11>;
|
||||
color = <LED_COLOR_ID_BLUE>;
|
||||
default-state = "off";
|
||||
};
|
||||
|
||||
lb60_12: u1-red@12 {
|
||||
label = "u1-red";
|
||||
reg = <12>;
|
||||
color = <LED_COLOR_ID_RED>;
|
||||
default-state = "off";
|
||||
};
|
||||
|
||||
lb60_13: u1-green@13 {
|
||||
label = "u1-green";
|
||||
reg = <13>;
|
||||
color = <LED_COLOR_ID_GREEN>;
|
||||
default-state = "off";
|
||||
};
|
||||
|
||||
lb60_14: u1-blue@14 {
|
||||
label = "u1-blue";
|
||||
reg = <14>;
|
||||
color = <LED_COLOR_ID_BLUE>;
|
||||
default-state = "off";
|
||||
};
|
||||
|
||||
lb60_15: sys-overwrite@15 {
|
||||
label = "sys-overwrite";
|
||||
reg = <15>;
|
||||
default-state = "on";
|
||||
};
|
||||
};
|
||||
|
||||
/*
|
||||
* NXP PCA9552BS
|
||||
* 16-Bit I2C-Bus LED Driver
|
||||
*/
|
||||
led_bar61: pca9552@61 {
|
||||
compatible = "nxp,pca9552";
|
||||
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
reg = <0x61>;
|
||||
|
||||
lb61_0: u2-red@0 {
|
||||
label = "u2-red";
|
||||
reg = <0>;
|
||||
color = <LED_COLOR_ID_RED>;
|
||||
default-state = "off";
|
||||
};
|
||||
|
||||
lb61_1: u2-green@1 {
|
||||
label = "u2-green";
|
||||
reg = <1>;
|
||||
color = <LED_COLOR_ID_GREEN>;
|
||||
default-state = "off";
|
||||
};
|
||||
|
||||
lb61_2: u2-blue@2 {
|
||||
label = "u2-blue";
|
||||
reg = <2>;
|
||||
color = <LED_COLOR_ID_BLUE>;
|
||||
default-state = "off";
|
||||
};
|
||||
|
||||
lb61_3: u3-red@3 {
|
||||
label = "u3-red";
|
||||
reg = <3>;
|
||||
color = <LED_COLOR_ID_RED>;
|
||||
default-state = "off";
|
||||
};
|
||||
|
||||
lb61_4: u3-green@4 {
|
||||
label = "u3-green";
|
||||
reg = <4>;
|
||||
color = <LED_COLOR_ID_GREEN>;
|
||||
default-state = "off";
|
||||
};
|
||||
|
||||
lb61_5: u3-blue@5 {
|
||||
label = "u3-blue";
|
||||
reg = <5>;
|
||||
color = <LED_COLOR_ID_BLUE>;
|
||||
default-state = "off";
|
||||
};
|
||||
|
||||
lb61_6: u4-red@6 {
|
||||
label = "u4-red";
|
||||
reg = <6>;
|
||||
color = <LED_COLOR_ID_RED>;
|
||||
default-state = "off";
|
||||
};
|
||||
|
||||
lb61_7: u4-green@7 {
|
||||
label = "u4-green";
|
||||
reg = <7>;
|
||||
color = <LED_COLOR_ID_GREEN>;
|
||||
default-state = "off";
|
||||
};
|
||||
|
||||
lb61_8: u4-blue@8 {
|
||||
label = "u4-blue";
|
||||
reg = <8>;
|
||||
color = <LED_COLOR_ID_BLUE>;
|
||||
default-state = "off";
|
||||
};
|
||||
|
||||
lb61_9: u5-red@9 {
|
||||
label = "u5-red";
|
||||
reg = <9>;
|
||||
color = <LED_COLOR_ID_RED>;
|
||||
default-state = "off";
|
||||
};
|
||||
|
||||
lb61_10: u5-green@10 {
|
||||
label = "u5-green";
|
||||
reg = <10>;
|
||||
color = <LED_COLOR_ID_GREEN>;
|
||||
default-state = "off";
|
||||
};
|
||||
|
||||
lb61_11: u5-blue@11 {
|
||||
label = "u5-blue";
|
||||
reg = <11>;
|
||||
color = <LED_COLOR_ID_BLUE>;
|
||||
default-state = "off";
|
||||
};
|
||||
|
||||
lb61_12: u6_r@12 {
|
||||
label = "u6-red";
|
||||
reg = <12>;
|
||||
color = <LED_COLOR_ID_RED>;
|
||||
default-state = "off";
|
||||
};
|
||||
|
||||
lb61_13: u6_g@13 {
|
||||
label = "u6-green";
|
||||
reg = <13>;
|
||||
color = <LED_COLOR_ID_GREEN>;
|
||||
default-state = "off";
|
||||
};
|
||||
|
||||
lb61_14: u6_b@14 {
|
||||
label = "u6-blue";
|
||||
reg = <14>;
|
||||
color = <LED_COLOR_ID_BLUE>;
|
||||
default-state = "off";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
/*
|
||||
* Multicolor LED groups for PFC400 (RGB)
|
||||
* Group triplets from PCA9552 into multicolor LEDs to enable
|
||||
* led-class-multicolor and pattern trigger RGB support.
|
||||
*/
|
||||
&{/} {
|
||||
sys_led: sys-led {
|
||||
compatible = "leds-group-multicolor";
|
||||
label = "sys";
|
||||
color = <LED_COLOR_ID_RGB>;
|
||||
function = LED_FUNCTION_INDICATOR;
|
||||
default-state = "keep";
|
||||
mc-allow-subled-writes;
|
||||
leds = <&lb60_0 &lb60_1 &lb60_2>;
|
||||
max-brightness = <255>;
|
||||
};
|
||||
|
||||
run_led: run-led {
|
||||
compatible = "leds-group-multicolor";
|
||||
label = "run";
|
||||
color = <LED_COLOR_ID_RGB>;
|
||||
function = LED_FUNCTION_INDICATOR;
|
||||
default-state = "off";
|
||||
leds = <&lb60_3 &lb60_4 &lb60_5>;
|
||||
max-brightness = <255>;
|
||||
};
|
||||
|
||||
io_led: io-led {
|
||||
compatible = "leds-group-multicolor";
|
||||
label = "io";
|
||||
color = <LED_COLOR_ID_RGB>;
|
||||
function = LED_FUNCTION_INDICATOR;
|
||||
default-state = "off";
|
||||
leds = <&lb60_6 &lb60_7 &lb60_8>;
|
||||
max-brightness = <255>;
|
||||
};
|
||||
|
||||
em_led: em-led {
|
||||
compatible = "leds-group-multicolor";
|
||||
label = "em";
|
||||
color = <LED_COLOR_ID_RGB>;
|
||||
function = LED_FUNCTION_INDICATOR;
|
||||
default-state = "off";
|
||||
leds = <&lb60_9 &lb60_10 &lb60_11>;
|
||||
max-brightness = <255>;
|
||||
};
|
||||
|
||||
u1_led: u1-led {
|
||||
compatible = "leds-group-multicolor";
|
||||
label = "u1";
|
||||
color = <LED_COLOR_ID_RGB>;
|
||||
function = LED_FUNCTION_INDICATOR;
|
||||
default-state = "off";
|
||||
leds = <&lb60_12 &lb60_13 &lb60_14>;
|
||||
max-brightness = <255>;
|
||||
};
|
||||
|
||||
u2_led: u2-led {
|
||||
compatible = "leds-group-multicolor";
|
||||
label = "u2";
|
||||
color = <LED_COLOR_ID_RGB>;
|
||||
function = LED_FUNCTION_INDICATOR;
|
||||
default-state = "off";
|
||||
leds = <&lb61_0 &lb61_1 &lb61_2>;
|
||||
max-brightness = <255>;
|
||||
};
|
||||
|
||||
u3_led: u3-led {
|
||||
compatible = "leds-group-multicolor";
|
||||
label = "u3";
|
||||
color = <LED_COLOR_ID_RGB>;
|
||||
function = LED_FUNCTION_INDICATOR;
|
||||
default-state = "off";
|
||||
leds = <&lb61_3 &lb61_4 &lb61_5>;
|
||||
max-brightness = <255>;
|
||||
};
|
||||
|
||||
u4_led: u4-led {
|
||||
compatible = "leds-group-multicolor";
|
||||
label = "u4";
|
||||
color = <LED_COLOR_ID_RGB>;
|
||||
function = LED_FUNCTION_INDICATOR;
|
||||
default-state = "off";
|
||||
leds = <&lb61_6 &lb61_7 &lb61_8>;
|
||||
max-brightness = <255>;
|
||||
};
|
||||
|
||||
u5_led: u5-led {
|
||||
compatible = "leds-group-multicolor";
|
||||
label = "u5";
|
||||
color = <LED_COLOR_ID_RGB>;
|
||||
function = LED_FUNCTION_INDICATOR;
|
||||
default-state = "off";
|
||||
leds = <&lb61_9 &lb61_10 &lb61_11>;
|
||||
max-brightness = <255>;
|
||||
};
|
||||
|
||||
u6_led: u6-led {
|
||||
compatible = "leds-group-multicolor";
|
||||
label = "u6";
|
||||
color = <LED_COLOR_ID_RGB>;
|
||||
function = LED_FUNCTION_INDICATOR;
|
||||
default-state = "off";
|
||||
leds = <&lb61_12 &lb61_13 &lb61_14>;
|
||||
max-brightness = <255>;
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,58 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/dts-v1/;
|
||||
/plugin/;
|
||||
|
||||
#include "k3-pinctrl.h"
|
||||
#include <dt-bindings/gpio/gpio.h>
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
#include <dt-bindings/leds/common.h>
|
||||
|
||||
#include "k3-am623-pfc-750-84xx-interaction.dtsi"
|
||||
|
||||
&leds_m4 {
|
||||
status = "okay";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
led@0 {
|
||||
reg = <0>;
|
||||
label = "sys";
|
||||
linux,default-trigger = "timer";
|
||||
led-default-intensity = <255 80 0 64>; /* Orange, brightness 64/255 */
|
||||
};
|
||||
|
||||
led@1 {
|
||||
reg = <1>;
|
||||
label = "run";
|
||||
};
|
||||
|
||||
led@2 {
|
||||
reg = <2>;
|
||||
label = "io";
|
||||
};
|
||||
|
||||
led@3 {
|
||||
reg = <3>;
|
||||
label = "em";
|
||||
};
|
||||
|
||||
led@4 {
|
||||
reg = <4>;
|
||||
label = "u1";
|
||||
};
|
||||
|
||||
led@5 {
|
||||
reg = <5>;
|
||||
label = "u2";
|
||||
};
|
||||
|
||||
led@6 {
|
||||
reg = <6>;
|
||||
label = "u3";
|
||||
};
|
||||
|
||||
led@7 {
|
||||
reg = <7>;
|
||||
label = "u4";
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,10 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/dts-v1/;
|
||||
/plugin/;
|
||||
|
||||
#include "k3-pinctrl.h"
|
||||
#include <dt-bindings/gpio/gpio.h>
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
#include <dt-bindings/leds/common.h>
|
||||
|
||||
#include "k3-am623-pfc-750-84xx-kbus.dtsi"
|
||||
@@ -0,0 +1,50 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/dts-v1/;
|
||||
/plugin/;
|
||||
|
||||
#include "k3-pinctrl.h"
|
||||
#include <dt-bindings/gpio/gpio.h>
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
#include <dt-bindings/leds/common.h>
|
||||
|
||||
#include "k3-am623-pfc-750-84xx-kbus.dtsi"
|
||||
|
||||
&mcu_pmx0 {
|
||||
oms_mcu_pins_default: oms-mcu-default-pins {
|
||||
pinctrl-single,pins = <
|
||||
AM62X_MCU_IOPAD(0x0044, PIN_INPUT, 7) /* (A8) MCU_I2C0_SCL.MCU_GPIO0_17 nRUN-BAS */
|
||||
AM62X_MCU_IOPAD(0x0048, PIN_INPUT, 7) /* (D10) MCU_I2C0_SDA.MCU_GPIO0_18 nSTOP-BAS */
|
||||
>;
|
||||
};
|
||||
};
|
||||
|
||||
&oms_pins_default {
|
||||
pinctrl-single,pins = <
|
||||
AM62X_IOPAD(0x01a4, PIN_INPUT, 7) /* (B20) MCASP0_ACLKX.GPIO1_11 IO-RAB */
|
||||
>;
|
||||
};
|
||||
|
||||
&oms {
|
||||
pinctrl-0 = <&oms_pins_default &oms_mcu_pins_default>;
|
||||
poll-interval = <100>;
|
||||
|
||||
oms_run: run {
|
||||
label = "RUN";
|
||||
gpios = <&mcu_gpio0 17 GPIO_ACTIVE_LOW>;
|
||||
linux,code = <1>;
|
||||
linux,input-type = <5>;
|
||||
debounce-interval = <1>;
|
||||
};
|
||||
|
||||
oms_stop: stop {
|
||||
label = "STOP";
|
||||
gpios = <&mcu_gpio0 18 GPIO_ACTIVE_LOW>;
|
||||
linux,code = <2>;
|
||||
linux,input-type = <5>;
|
||||
debounce-interval = <1>;
|
||||
};
|
||||
};
|
||||
|
||||
&oms_stop_run {
|
||||
status = "disabled";
|
||||
};
|
||||
@@ -0,0 +1,10 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/dts-v1/;
|
||||
/plugin/;
|
||||
|
||||
#include "k3-pinctrl.h"
|
||||
#include <dt-bindings/gpio/gpio.h>
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
#include <dt-bindings/leds/common.h>
|
||||
|
||||
#include "k3-am623-pfc-750-84xx-wosm.dtsi"
|
||||
@@ -0,0 +1,45 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/dts-v1/;
|
||||
/plugin/;
|
||||
|
||||
#include "k3-pinctrl.h"
|
||||
#include <dt-bindings/gpio/gpio.h>
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
#include <dt-bindings/leds/common.h>
|
||||
|
||||
#include "k3-am623-pfc-750-84xx-wosm.dtsi"
|
||||
|
||||
&main_pmx0 {
|
||||
mram_reset_pins_default: mram-reset-default-pins {
|
||||
pinctrl-single,pins = <
|
||||
AM62X_IOPAD(0x0004, PIN_OUTPUT, 7) /* (G25) OSPI0_LBCLKO.GPIO0_1 (nRST) */
|
||||
>;
|
||||
};
|
||||
|
||||
em_reset_pins_default: em-reset-default-pins {
|
||||
pinctrl-single,pins = <
|
||||
AM62X_IOPAD(0x01a0, PIN_OUTPUT, 7) /* (E18) MCASP0_AXR0.GPIO1_10 (nRST-EM2) */
|
||||
AM62X_IOPAD(0x019c, PIN_OUTPUT, 7) /* (B18) MCASP0_AXR1.GPIO1_9 (nRST-EM3) */
|
||||
>;
|
||||
};
|
||||
|
||||
rs485_2_pins_default: rs485-2-default-pins {
|
||||
pinctrl-single,pins = <
|
||||
AM62X_IOPAD(0x0098, PIN_OUTPUT, 7) /* (U23) GPMC0_WAIT0.GPIO0_38 (RS-2.DE) */
|
||||
AM62X_IOPAD(0x00c0, PIN_INPUT, 4) /* (W25) VOUT0_DATA2.UART3_RXD (RS-2.RXD) */
|
||||
AM62X_IOPAD(0x00c4, PIN_OUTPUT, 4) /* (W24) VOUT0_DATA3.UART3_TXD (RS-2.TXD) */
|
||||
>;
|
||||
};
|
||||
|
||||
can_interface_pins_default: can-interface-default-pins {
|
||||
pinctrl-single,pins = <
|
||||
AM62X_IOPAD(0x01dc, PIN_INPUT, 0) /* (E15) MCAN0_RX (CAN.RXD) */
|
||||
AM62X_IOPAD(0x01d8, PIN_OUTPUT, 0) /* (C15) MCAN0_TX (CAN.TXD) */
|
||||
>;
|
||||
};
|
||||
};
|
||||
|
||||
&mram {
|
||||
pinctrl-0 = <&mram_irq_pins_default &mram_reset_pins_default>;
|
||||
reset-gpios = <&main_gpio0 1 GPIO_ACTIVE_LOW>;
|
||||
};
|
||||
@@ -341,6 +341,9 @@ CONFIG_MMC_SDHCI_PLTFM=y
|
||||
CONFIG_MMC_SDHCI_AM654=y
|
||||
CONFIG_NEW_LEDS=y
|
||||
CONFIG_LEDS_CLASS=y
|
||||
CONFIG_LEDS_CLASS_MULTICOLOR=y
|
||||
CONFIG_LEDS_WAGO_M4_WRAPPER=y
|
||||
CONFIG_LEDS_WAGO_M4_WRAPPER_SYSFS_PASSTHROUGH=y
|
||||
CONFIG_LEDS_GPIO=y
|
||||
CONFIG_LEDS_PCA955X=y
|
||||
CONFIG_LEDS_TRIGGERS=y
|
||||
@@ -348,6 +351,7 @@ CONFIG_LEDS_TRIGGER_TIMER=y
|
||||
CONFIG_LEDS_TRIGGER_HEARTBEAT=y
|
||||
CONFIG_LEDS_TRIGGER_DEFAULT_ON=y
|
||||
CONFIG_LEDS_TRIGGER_PANIC=y
|
||||
CONFIG_LEDS_TRIGGER_PATTERN=y
|
||||
CONFIG_EDAC=y
|
||||
CONFIG_RTC_CLASS=y
|
||||
CONFIG_RTC_DRV_RV3028=y
|
||||
@@ -466,6 +470,7 @@ CONFIG_DMA_CMA=y
|
||||
CONFIG_CMA_SIZE_MBYTES=32
|
||||
CONFIG_IRQ_POLL=y
|
||||
CONFIG_PRINTK_TIME=y
|
||||
CONFIG_DYNAMIC_DEBUG=y
|
||||
CONFIG_DEBUG_INFO_DWARF_TOOLCHAIN_DEFAULT=y
|
||||
CONFIG_MAGIC_SYSRQ=y
|
||||
CONFIG_DEBUG_FS=y
|
||||
|
||||
@@ -51,4 +51,39 @@ config LEDS_MT6370_RGB
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called "leds-mt6370-rgb".
|
||||
|
||||
config LEDS_WAGO_M4_WRAPPER
|
||||
tristate "WAGO M4 RGB LED strip wrapper (RPMsg/remoteproc)"
|
||||
depends on RPMSG
|
||||
depends on REMOTEPROC
|
||||
depends on OF
|
||||
help
|
||||
Driver for a 10-LED WS2812 RGB strip on the TI AM62x M4 coprocessor
|
||||
running the Zephyr wago-led-server-app. Communicates over the RPMsg
|
||||
"wago-led" endpoint using a binary packed-frame protocol and exposes
|
||||
each LED as a Linux LED multicolor (RGB) device.
|
||||
|
||||
The M4 firmware can be pre-loaded by the bootloader (the driver
|
||||
will attach) or loaded by this driver from /lib/firmware.
|
||||
|
||||
Say M or Y for WAGO boards with an M4-driven WS2812 LED strip.
|
||||
|
||||
config LEDS_WAGO_M4_WRAPPER_SYSFS_PASSTHROUGH
|
||||
bool "WAGO M4 LED wrapper: sysfs raw command passthrough"
|
||||
depends on LEDS_WAGO_M4_WRAPPER
|
||||
default n
|
||||
help
|
||||
Adds a wago_led_cmd sysfs attribute to the platform device that
|
||||
forwards raw ASCII commands directly to the M4 via RPMsg, bypassing
|
||||
the LED multiclass framework.
|
||||
|
||||
Useful for development, testing and triggering M4-side effects
|
||||
(animations, blink patterns) that have no LED class equivalent:
|
||||
|
||||
echo "CMD-IDL" > /sys/.../leds-m4/wago_led_cmd
|
||||
echo "CMD-CYC-50-128" > /sys/.../leds-m4/wago_led_cmd
|
||||
echo "CMD-BLK-0-R-200-255" > /sys/.../leds-m4/wago_led_cmd
|
||||
|
||||
Say N for production builds where direct M4 access should be
|
||||
restricted to the LED class interface only.
|
||||
|
||||
endif # LEDS_CLASS_MULTICOLOR
|
||||
|
||||
@@ -4,3 +4,4 @@ obj-$(CONFIG_LEDS_GROUP_MULTICOLOR) += leds-group-multicolor.o
|
||||
obj-$(CONFIG_LEDS_PWM_MULTICOLOR) += leds-pwm-multicolor.o
|
||||
obj-$(CONFIG_LEDS_QCOM_LPG) += leds-qcom-lpg.o
|
||||
obj-$(CONFIG_LEDS_MT6370_RGB) += leds-mt6370-rgb.o
|
||||
obj-$(CONFIG_LEDS_WAGO_M4_WRAPPER) += wago-m4-led-wrapper.o
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* WAGO M4 LED Wrapper — IPC Protocol Definition
|
||||
*
|
||||
* Binary protocol between the Linux wago-m4-led-wrapper driver and the
|
||||
* Zephyr wago-led-server-app running on the TI AM62x M4 coprocessor.
|
||||
*
|
||||
* !! Must match the command IDs and frame layouts in the Zephyr app !!
|
||||
*
|
||||
* Author: WAGO GmbH & Co. KG
|
||||
*/
|
||||
|
||||
#ifndef _WAGO_M4_LED_PROTOCOL_H
|
||||
#define _WAGO_M4_LED_PROTOCOL_H
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
#define WAGO_LED_NUM_LEDS 10
|
||||
#define WAGO_LED_NUM_CHANNELS 3 /* R, G, B */
|
||||
|
||||
/**
|
||||
* enum wago_led_cmd - Binary command IDs (first byte of every RPMsg frame)
|
||||
*
|
||||
* @WAGO_CMD_SET_LED: Set a single LED, all others unchanged. 5 bytes total.
|
||||
* @WAGO_CMD_SET_STRIP: Set all LEDs atomically with a master brightness.
|
||||
* 2 + WAGO_LED_NUM_LEDS * 3 bytes total.
|
||||
* @WAGO_CMD_SET_ALL: Set every LED to the same color. 4 bytes total.
|
||||
* @WAGO_CMD_IDL: Turn off all LEDs. 1 byte total.
|
||||
*/
|
||||
enum wago_led_cmd {
|
||||
WAGO_CMD_SET_LED = 0x01,
|
||||
WAGO_CMD_SET_STRIP = 0x02,
|
||||
WAGO_CMD_SET_ALL = 0x03,
|
||||
WAGO_CMD_IDL = 0x04,
|
||||
};
|
||||
|
||||
/**
|
||||
* struct wago_msg_set_led - WAGO_CMD_SET_LED frame
|
||||
* @cmd: WAGO_CMD_SET_LED
|
||||
* @led_idx: LED index [0 .. WAGO_LED_NUM_LEDS-1]
|
||||
* @r: Red intensity [0..255]
|
||||
* @g: Green intensity [0..255]
|
||||
* @b: Blue intensity [0..255]
|
||||
*/
|
||||
struct wago_msg_set_led {
|
||||
__u8 cmd;
|
||||
__u8 led_idx;
|
||||
__u8 r;
|
||||
__u8 g;
|
||||
__u8 b;
|
||||
} __packed;
|
||||
|
||||
/**
|
||||
* struct wago_msg_set_strip - WAGO_CMD_SET_STRIP frame
|
||||
* @cmd: WAGO_CMD_SET_STRIP
|
||||
* @brightness: Master brightness scale [0..255], applied to every channel
|
||||
* @leds: RGB values for all LEDs; each channel scaled by brightness
|
||||
* on the M4 side: out = (in * brightness) / 255
|
||||
*/
|
||||
struct wago_msg_set_strip {
|
||||
__u8 cmd;
|
||||
__u8 brightness;
|
||||
struct {
|
||||
__u8 r;
|
||||
__u8 g;
|
||||
__u8 b;
|
||||
} leds[WAGO_LED_NUM_LEDS];
|
||||
} __packed;
|
||||
|
||||
/**
|
||||
* struct wago_msg_set_all - WAGO_CMD_SET_ALL frame
|
||||
* @cmd: WAGO_CMD_SET_ALL
|
||||
* @r: Red intensity [0..255]
|
||||
* @g: Green intensity [0..255]
|
||||
* @b: Blue intensity [0..255]
|
||||
*/
|
||||
struct wago_msg_set_all {
|
||||
__u8 cmd;
|
||||
__u8 r;
|
||||
__u8 g;
|
||||
__u8 b;
|
||||
} __packed;
|
||||
|
||||
#endif /* _WAGO_M4_LED_PROTOCOL_H */
|
||||
@@ -0,0 +1,929 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* WAGO M4 LED Wrapper Driver
|
||||
*
|
||||
* Controls a WS2812 RGB LED strip (10 LEDs) running on the TI AM62x M4
|
||||
* coprocessor via the Zephyr wago-led-server-app. Exposes each LED as a
|
||||
* Linux LED multiclass (RGB) device.
|
||||
*
|
||||
* Protocol
|
||||
* --------
|
||||
* Communication uses the "wago-led" RPMsg endpoint announced by the Zephyr app.
|
||||
* The driver sends binary frames fire-and-forget style — ACK replies from the
|
||||
* M4 are intentionally ignored.
|
||||
*
|
||||
* Binary frame formats (little-endian, packed):
|
||||
*
|
||||
* CMD_SET_LED (0x01) [u8 cmd][u8 idx][u8 r][u8 g][u8 b] 5 bytes
|
||||
* CMD_SET_STRIP (0x02) [u8 cmd][u8 bri][r0][g0][b0]...[r9][g9][b9] 32 bytes
|
||||
* CMD_SET_ALL (0x03) [u8 cmd][u8 r][u8 g][u8 b] 4 bytes
|
||||
* CMD_IDL (0x04) [u8 cmd] 1 byte
|
||||
*
|
||||
* The LED multiclass brightness_set callback uses CMD_SET_LED to update
|
||||
* only the addressed LED without disturbing others on the strip.
|
||||
*
|
||||
* Fire-and-forget rationale
|
||||
* -------------------------
|
||||
* Waiting for an ACK after every command costs 1–3 ms per roundtrip (IPM
|
||||
* interrupt + Zephyr thread wake-up). LED strip updates are idempotent — a
|
||||
* missed frame is harmless. If the vring TX ring is full, rpmsg_trysend()
|
||||
* returns -ENOMEM; the driver retries up to WAGO_SEND_RETRIES times.
|
||||
*
|
||||
* Firmware loading and boot sequencing
|
||||
* -------------------------------------
|
||||
* 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:
|
||||
* 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 -> RPMsg channel appears, wago_rpmsg_probe() sets rpdev
|
||||
* -ENOENT -> filesystem not ready, reschedule after WAGO_BOOT_INITIAL_DELAY_MS
|
||||
* other -> fatal, stop retrying
|
||||
*
|
||||
* Sysfs example
|
||||
* -------------
|
||||
* 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):
|
||||
* echo "CMD-IDL" > /sys/bus/platform/devices/leds-m4/wago_led_cmd
|
||||
* echo "CMD-CYC-50-128" > /sys/bus/platform/devices/leds-m4/wago_led_cmd
|
||||
*
|
||||
* Author: WAGO GmbH & Co. KG
|
||||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/led-class-multicolor.h>
|
||||
#include <linux/leds.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/property.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/remoteproc.h>
|
||||
#include <linux/rpmsg.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/workqueue.h>
|
||||
|
||||
#include "wago-m4-led-protocol.h"
|
||||
|
||||
#define DRIVER_NAME "wago-m4-led-wrapper"
|
||||
|
||||
/* Endpoint name announced by the Zephyr app via RPMsg name-service.
|
||||
* Must match RPMSG_TTY_NAME in the Zephyr wago-led-server-app/src/main.c
|
||||
*/
|
||||
#define WAGO_RPMSG_EPT_NAME "wago-led"
|
||||
|
||||
/* Maximum ASCII command length for sysfs passthrough */
|
||||
#define WAGO_CMD_MAX_LEN 32
|
||||
|
||||
/* First boot attempt this many ms after probe() — Linux-boot path only,
|
||||
* gives the rootfs time to mount before loading firmware from /lib/firmware.
|
||||
* Not used in the SPL path (M4 already running).
|
||||
*/
|
||||
#define WAGO_BOOT_INITIAL_DELAY_MS 5000
|
||||
/* Retry interval when rproc is not yet registered or firmware not found */
|
||||
#define WAGO_BOOT_RETRY_MS 200
|
||||
/* Maximum rproc-handle acquisition attempts before giving up (~30 s) */
|
||||
#define WAGO_BOOT_MAX_RETRIES 150
|
||||
/* Maximum filesystem-polling attempts when firmware file is not found.
|
||||
* At WAGO_BOOT_INITIAL_DELAY_MS per attempt this bounds the wait to ~15 s.
|
||||
*/
|
||||
#define WAGO_BOOT_FS_MAX_RETRIES 3
|
||||
|
||||
/*
|
||||
* vring TX ring retry policy for rpmsg_trysend().
|
||||
* Total worst-case wait: WAGO_SEND_RETRIES x WAGO_SEND_RETRY_US = 1 ms
|
||||
*/
|
||||
#define WAGO_SEND_RETRIES 5
|
||||
#define WAGO_SEND_RETRY_US 200
|
||||
|
||||
/* colour channel indices matching subled_info[] order */
|
||||
#define CH_RED 0
|
||||
#define CH_GREEN 1
|
||||
#define CH_BLUE 2
|
||||
|
||||
/* Maximum binary frame size for CMD_SET_STRIP:
|
||||
* 1 (cmd) + 1 (brightness) + WAGO_LED_NUM_LEDS * 3 (RGB)
|
||||
*/
|
||||
#define WAGO_SET_STRIP_LEN \
|
||||
(2 + WAGO_LED_NUM_LEDS * WAGO_LED_NUM_CHANNELS)
|
||||
|
||||
/* -------------------------------------------------------------------------
|
||||
* Data structures
|
||||
* ----------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
struct wago_m4_led_priv;
|
||||
|
||||
/**
|
||||
* struct wago_led - per-LED state
|
||||
* @mc_cdev: multicolor LED class device (wraps led_classdev)
|
||||
* @subled_info: R/G/B sub-LED descriptors
|
||||
* @priv: back-pointer to driver private data
|
||||
* @index: LED index on the strip [0 .. WAGO_LED_NUM_LEDS-1]
|
||||
*/
|
||||
struct wago_led {
|
||||
struct led_classdev_mc mc_cdev;
|
||||
struct mc_subled subled_info[WAGO_LED_NUM_CHANNELS];
|
||||
struct wago_m4_led_priv *priv;
|
||||
unsigned int index;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct wago_m4_led_priv - driver private data
|
||||
* @dev: underlying platform device
|
||||
* @rproc: remoteproc handle for the M4 core
|
||||
* @rproc_booted_by_us: true when we called rproc_boot() ourselves
|
||||
* @fw_name: firmware filename (from DT or default)
|
||||
* @boot_work: delayed work for filesystem-ready retry loop
|
||||
* @boot_retries: rproc handle acquisition attempts made so far
|
||||
* @fs_retries: firmware filesystem polling attempts made so far
|
||||
* @rpdev: RPMsg device (populated when the channel appears)
|
||||
* @send_lock: serialises concurrent rpmsg_trysend() calls
|
||||
* @leds: per-LED state array
|
||||
*/
|
||||
struct wago_m4_led_priv {
|
||||
struct device *dev;
|
||||
|
||||
/* remoteproc */
|
||||
struct rproc *rproc;
|
||||
bool rproc_booted_by_us;
|
||||
bool reset_on_init;
|
||||
const char *fw_name;
|
||||
|
||||
/* boot retry workqueue */
|
||||
struct delayed_work boot_work;
|
||||
int boot_retries;
|
||||
int fs_retries;
|
||||
|
||||
/* RPMsg */
|
||||
struct rpmsg_device *rpdev;
|
||||
|
||||
/* serialises concurrent send calls */
|
||||
struct mutex send_lock;
|
||||
|
||||
/* LED strip */
|
||||
struct wago_led leds[WAGO_LED_NUM_LEDS];
|
||||
};
|
||||
|
||||
/* -------------------------------------------------------------------------
|
||||
* RPMsg callback: M4 -> Linux
|
||||
*
|
||||
* No ACKs in binary protocol — callback registered to satisfy rpmsg core.
|
||||
* ----------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
static int wago_rpmsg_cb(struct rpmsg_device *rpdev, void *data,
|
||||
int len, void *priv_data, u32 src)
|
||||
{
|
||||
dev_dbg(&rpdev->dev, "rx (ignored, %d bytes)\n", len);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------------
|
||||
* IPC helper: fire-and-forget binary send
|
||||
*
|
||||
* Uses rpmsg_trysend() to avoid blocking. Retries up to WAGO_SEND_RETRIES
|
||||
* times with a short udelay back-off when the vring TX ring is full.
|
||||
* ----------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
static int wago_send(struct wago_m4_led_priv *priv,
|
||||
const void *msg, size_t len)
|
||||
{
|
||||
int ret, tries;
|
||||
|
||||
if (!priv->rpdev) {
|
||||
dev_dbg(priv->dev, "RPMsg channel not yet available, dropping frame\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
for (tries = 0; tries < WAGO_SEND_RETRIES; tries++) {
|
||||
ret = rpmsg_trysend(priv->rpdev->ept, (void *)msg, len);
|
||||
if (ret != -ENOMEM)
|
||||
break;
|
||||
udelay(WAGO_SEND_RETRY_US);
|
||||
}
|
||||
|
||||
if (ret)
|
||||
dev_warn_ratelimited(priv->dev,
|
||||
"rpmsg_trysend failed after %d tries: %d\n",
|
||||
tries, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Thin wrapper for sysfs passthrough (ASCII strings) */
|
||||
static int wago_send_ascii(struct wago_m4_led_priv *priv, const char *cmd)
|
||||
{
|
||||
dev_dbg(priv->dev, "tx ascii: %s", cmd);
|
||||
return wago_send(priv, cmd, strlen(cmd));
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------------
|
||||
* Sysfs attribute: wago_led_cmd
|
||||
* Only compiled in when CONFIG_LEDS_WAGO_M4_WRAPPER_SYSFS_PASSTHROUGH=y
|
||||
*
|
||||
* Allows sending raw ASCII commands to the M4 directly from the shell.
|
||||
* The command string is forwarded as-is via RPMsg (fire-and-forget).
|
||||
* Note: only legacy animation commands are handled as ASCII by the M4;
|
||||
* LED colour updates from the kernel use the binary protocol.
|
||||
*
|
||||
* Usage:
|
||||
* echo "CMD-IDL" > /sys/bus/platform/devices/leds-m4/wago_led_cmd
|
||||
* echo "CMD-CYC-50-128" > /sys/bus/platform/devices/leds-m4/wago_led_cmd
|
||||
* echo "CMD-BLK-0-R-200-255" > /sys/bus/platform/devices/leds-m4/wago_led_cmd
|
||||
* echo "CMD-FAD-0-G-20-5" > /sys/bus/platform/devices/leds-m4/wago_led_cmd
|
||||
* ----------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#ifdef CONFIG_LEDS_WAGO_M4_WRAPPER_SYSFS_PASSTHROUGH
|
||||
|
||||
/*
|
||||
* wago_all_leds_off - set brightness=0 and cancel triggers on all LEDs.
|
||||
*
|
||||
* Called when CMD-IDL is sent via sysfs passthrough. Without this, any
|
||||
* active kernel trigger (e.g. "heartbeat") would keep calling
|
||||
* brightness_set() and immediately re-light LEDs that CMD-IDL just cleared.
|
||||
*/
|
||||
static void wago_all_leds_off(struct wago_m4_led_priv *priv)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < WAGO_LED_NUM_LEDS; i++) {
|
||||
struct led_classdev *lcdev =
|
||||
&priv->leds[i].mc_cdev.led_cdev;
|
||||
|
||||
/* led_set_brightness() cancels any active trigger and sets
|
||||
* brightness=0 through the normal LED core path.
|
||||
*/
|
||||
led_set_brightness(lcdev, LED_OFF);
|
||||
}
|
||||
}
|
||||
|
||||
static ssize_t wago_led_cmd_store(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct wago_m4_led_priv *priv = dev_get_drvdata(dev);
|
||||
char cmd[WAGO_CMD_MAX_LEN];
|
||||
size_t len;
|
||||
int ret;
|
||||
|
||||
/* Strip trailing newline added by echo and copy into local buffer */
|
||||
len = min(count, sizeof(cmd) - 2);
|
||||
memcpy(cmd, buf, len);
|
||||
|
||||
/* Remove trailing whitespace / newline */
|
||||
while (len > 0 && (cmd[len - 1] == '\n' ||
|
||||
cmd[len - 1] == '\r' ||
|
||||
cmd[len - 1] == ' '))
|
||||
len--;
|
||||
|
||||
/* Re-add a single newline — the Zephyr app expects it */
|
||||
cmd[len++] = '\n';
|
||||
cmd[len] = '\0';
|
||||
|
||||
/* If this is CMD-IDL, silence all kernel-side triggers first.
|
||||
* Otherwise an active trigger (e.g. "heartbeat") would keep calling
|
||||
* brightness_set() and immediately re-light the strip after the M4
|
||||
* has cleared it.
|
||||
*/
|
||||
if (strncmp(cmd, "CMD-IDL", 7) == 0)
|
||||
wago_all_leds_off(priv);
|
||||
|
||||
mutex_lock(&priv->send_lock);
|
||||
ret = wago_send_ascii(priv, cmd);
|
||||
mutex_unlock(&priv->send_lock);
|
||||
|
||||
return ret ? ret : count;
|
||||
}
|
||||
|
||||
static ssize_t wago_led_cmd_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct wago_m4_led_priv *priv = dev_get_drvdata(dev);
|
||||
|
||||
return sysfs_emit(buf, "%s\n",
|
||||
priv->rpdev ? "online" : "offline");
|
||||
}
|
||||
|
||||
static DEVICE_ATTR_RW(wago_led_cmd);
|
||||
|
||||
static struct attribute *wago_led_attrs[] = {
|
||||
&dev_attr_wago_led_cmd.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static const struct attribute_group wago_led_attr_group = {
|
||||
.attrs = wago_led_attrs,
|
||||
};
|
||||
|
||||
#endif /* CONFIG_LEDS_WAGO_M4_WRAPPER_SYSFS_PASSTHROUGH */
|
||||
|
||||
static int wago_led_set(struct led_classdev *led_cdev,
|
||||
enum led_brightness brightness)
|
||||
{
|
||||
struct led_classdev_mc *mc_cdev = lcdev_to_mccdev(led_cdev);
|
||||
struct wago_led *led = container_of(mc_cdev, struct wago_led, mc_cdev);
|
||||
struct wago_m4_led_priv *priv = led->priv;
|
||||
struct wago_msg_set_led msg;
|
||||
|
||||
/*
|
||||
* The pattern trigger updates the 'intensity' values in mc_cdev->subled_info
|
||||
* directly (via pattern_trig_apply_color). We must use these current
|
||||
* intensities instead of recalculating them from the static multi_intensity.
|
||||
*
|
||||
* Note: when the pattern trigger does gradual dimming, it updates the
|
||||
* brightness of the led_cdev while keeping the sub-led intensities constant
|
||||
* (at the target color). This correctly results in a fading effect.
|
||||
*/
|
||||
int i;
|
||||
u8 components[WAGO_LED_NUM_CHANNELS] = {0};
|
||||
|
||||
for (i = 0; i < mc_cdev->num_colors && i < WAGO_LED_NUM_CHANNELS; i++)
|
||||
components[i] = (u8)((brightness * mc_cdev->subled_info[i].intensity) / LED_FULL);
|
||||
|
||||
dev_dbg(priv->dev, "LED%d: bri=%d R=%d G=%d B=%d (ints: %d %d %d)\n",
|
||||
led->index, brightness,
|
||||
components[CH_RED], components[CH_GREEN], components[CH_BLUE],
|
||||
mc_cdev->subled_info[CH_RED].intensity,
|
||||
mc_cdev->subled_info[CH_GREEN].intensity,
|
||||
mc_cdev->subled_info[CH_BLUE].intensity);
|
||||
|
||||
/*
|
||||
* Use CMD_SET_LED to update only this LED without touching the
|
||||
* others. The M4 maintains the full pixel[] buffer internally.
|
||||
*/
|
||||
msg.cmd = WAGO_CMD_SET_LED;
|
||||
msg.led_idx = (u8)led->index;
|
||||
msg.r = components[CH_RED];
|
||||
msg.g = components[CH_GREEN];
|
||||
msg.b = components[CH_BLUE];
|
||||
|
||||
mutex_lock(&priv->send_lock);
|
||||
wago_send(priv, &msg, sizeof(msg));
|
||||
mutex_unlock(&priv->send_lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------------
|
||||
* LED registration
|
||||
* ----------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
/*
|
||||
* wago_led_init_subled - initialise sub-LED descriptors common to every strip LED.
|
||||
*/
|
||||
static void wago_led_init_subled(struct wago_led *led)
|
||||
{
|
||||
led->subled_info[CH_RED].color_index = LED_COLOR_ID_RED;
|
||||
led->subled_info[CH_RED].intensity = 255;
|
||||
led->subled_info[CH_RED].channel = CH_RED;
|
||||
|
||||
led->subled_info[CH_GREEN].color_index = LED_COLOR_ID_GREEN;
|
||||
led->subled_info[CH_GREEN].intensity = 255;
|
||||
led->subled_info[CH_GREEN].channel = CH_GREEN;
|
||||
|
||||
led->subled_info[CH_BLUE].color_index = LED_COLOR_ID_BLUE;
|
||||
led->subled_info[CH_BLUE].intensity = 255;
|
||||
led->subled_info[CH_BLUE].channel = CH_BLUE;
|
||||
|
||||
led->mc_cdev.subled_info = led->subled_info;
|
||||
led->mc_cdev.num_colors = WAGO_LED_NUM_CHANNELS;
|
||||
|
||||
led->mc_cdev.led_cdev.brightness_set_blocking = wago_led_set;
|
||||
led->mc_cdev.led_cdev.max_brightness = LED_FULL;
|
||||
led->mc_cdev.led_cdev.flags = LED_CORE_SUSPENDRESUME;
|
||||
led->mc_cdev.led_cdev.color = LED_COLOR_ID_MULTI;
|
||||
}
|
||||
|
||||
/*
|
||||
* wago_led_apply_default_intensity - read optional 'led-default-intensity'
|
||||
* DT property and apply R/G/B values and optional brightness to the LED.
|
||||
*
|
||||
* Property format: <R G B> (three u32 values, each 0-255)
|
||||
* or: <R G B brightness> (four u32 values, each 0-255)
|
||||
* If the property is absent or malformed the subled defaults (255/255/255)
|
||||
* set by wago_led_init_subled() are kept unchanged.
|
||||
*/
|
||||
static void wago_led_apply_default_intensity(struct device *dev,
|
||||
struct wago_led *led,
|
||||
struct fwnode_handle *fwnode)
|
||||
{
|
||||
u32 rgba[4] = { 255, 255, 255, LED_FULL };
|
||||
int count;
|
||||
|
||||
/* Accept both 3-cell (R G B) and 4-cell (R G B brightness) */
|
||||
count = fwnode_property_count_u32(fwnode, "led-default-intensity");
|
||||
if (count != 3 && count != 4)
|
||||
return; /* property absent or wrong size — keep defaults */
|
||||
|
||||
if (fwnode_property_read_u32_array(fwnode, "led-default-intensity",
|
||||
rgba, count))
|
||||
return;
|
||||
|
||||
led->subled_info[CH_RED].intensity = clamp_val(rgba[0], 0, 255);
|
||||
led->subled_info[CH_GREEN].intensity = clamp_val(rgba[1], 0, 255);
|
||||
led->subled_info[CH_BLUE].intensity = clamp_val(rgba[2], 0, 255);
|
||||
|
||||
if (count == 4)
|
||||
led->mc_cdev.led_cdev.brightness = clamp_val(rgba[3], 0, LED_FULL);
|
||||
|
||||
dev_dbg(dev, "LED %u: default intensity RGB(%u,%u,%u) brightness=%u from DT\n",
|
||||
led->index, rgba[0], rgba[1], rgba[2],
|
||||
led->mc_cdev.led_cdev.brightness);
|
||||
}
|
||||
|
||||
/*
|
||||
* wago_led_register_leds - register all strip LEDs with the LED core.
|
||||
*
|
||||
* When the DT node contains child nodes (led@N with a 'reg' property),
|
||||
* they are used to drive naming via led_init_data.fwnode — the LED core
|
||||
* will read the 'label' and 'linux,default-trigger' properties from the
|
||||
* child node automatically.
|
||||
*
|
||||
* When no child nodes are present the driver falls back to the legacy
|
||||
* hard-coded names "m4-led0" … "m4-led9".
|
||||
*/
|
||||
static int wago_led_register_leds(struct wago_m4_led_priv *priv)
|
||||
{
|
||||
struct device *dev = priv->dev;
|
||||
struct fwnode_handle *child;
|
||||
char default_label[16];
|
||||
int count, ret;
|
||||
|
||||
count = device_get_child_node_count(dev);
|
||||
|
||||
if (count > 0) {
|
||||
/*
|
||||
* DT child-node path: iterate child nodes ordered by their
|
||||
* 'reg' property which encodes the LED index [0..9].
|
||||
*/
|
||||
device_for_each_child_node(dev, child) {
|
||||
struct led_init_data init_data = {};
|
||||
struct wago_led *led;
|
||||
u32 reg;
|
||||
|
||||
if (fwnode_property_read_u32(child, "reg", ®)) {
|
||||
dev_warn(dev,
|
||||
"LED child node missing 'reg', skipping\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (reg >= WAGO_LED_NUM_LEDS) {
|
||||
dev_warn(dev,
|
||||
"LED child reg %u out of range [0..%d], skipping\n",
|
||||
reg, WAGO_LED_NUM_LEDS - 1);
|
||||
continue;
|
||||
}
|
||||
|
||||
led = &priv->leds[reg];
|
||||
led->priv = priv;
|
||||
led->index = reg;
|
||||
wago_led_init_subled(led);
|
||||
wago_led_apply_default_intensity(dev, led, child);
|
||||
|
||||
init_data.fwnode = child;
|
||||
init_data.devicename = NULL;
|
||||
init_data.devname_mandatory = false;
|
||||
|
||||
ret = devm_led_classdev_multicolor_register_ext(dev,
|
||||
&led->mc_cdev,
|
||||
&init_data);
|
||||
if (ret) {
|
||||
dev_err(dev,
|
||||
"Failed to register LED %u: %d\n",
|
||||
reg, ret);
|
||||
fwnode_handle_put(child);
|
||||
return ret;
|
||||
}
|
||||
|
||||
dev_dbg(dev, "Registered LED %u from DT child node\n", reg);
|
||||
}
|
||||
} else {
|
||||
/*
|
||||
* Fallback: no child nodes — use legacy "m4-ledN" names.
|
||||
*/
|
||||
int i;
|
||||
|
||||
for (i = 0; i < WAGO_LED_NUM_LEDS; i++) {
|
||||
struct led_init_data init_data = {};
|
||||
struct wago_led *led = &priv->leds[i];
|
||||
|
||||
led->priv = priv;
|
||||
led->index = i;
|
||||
wago_led_init_subled(led);
|
||||
|
||||
snprintf(default_label, sizeof(default_label), "m4-led%d", i);
|
||||
init_data.fwnode = NULL;
|
||||
init_data.devicename = DRIVER_NAME;
|
||||
init_data.default_label = default_label;
|
||||
init_data.devname_mandatory = false;
|
||||
|
||||
ret = devm_led_classdev_multicolor_register_ext(dev,
|
||||
&led->mc_cdev,
|
||||
&init_data);
|
||||
if (ret) {
|
||||
dev_err(dev,
|
||||
"Failed to register LED %d: %d\n",
|
||||
i, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
dev_dbg(dev, "Registered %s (fallback name)\n",
|
||||
led->mc_cdev.led_cdev.name);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------------
|
||||
* RPMsg driver — probe / remove
|
||||
* Called by the rpmsg core when the M4 announces the "rpmsg-tty" service.
|
||||
* ----------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
static int wago_rpmsg_probe(struct rpmsg_device *rpdev)
|
||||
{
|
||||
/*
|
||||
* Walk up the device parent chain to find our priv pointer.
|
||||
*
|
||||
* The chain built by the remoteproc / virtio stack is:
|
||||
*
|
||||
* rpdev->dev rpmsg device (this device)
|
||||
* .parent virtio_device (vrp->vdev)
|
||||
* .parent rproc_vdev platform_device (rvdev->pdev)
|
||||
* .parent rproc->dev <- dev_set_drvdata set here
|
||||
*
|
||||
* We set dev_set_drvdata on rproc->dev in wago_boot_work() as soon
|
||||
* as we obtained the rproc handle, so three levels up is correct.
|
||||
*/
|
||||
struct device *rproc_dev = rpdev->dev.parent->parent->parent;
|
||||
struct wago_m4_led_priv *priv;
|
||||
|
||||
/* Try three levels up first, then four (rproc->dev.parent layout
|
||||
* may differ across kernel versions).
|
||||
*/
|
||||
priv = dev_get_drvdata(rproc_dev);
|
||||
if (!priv) {
|
||||
/* One more level up: rproc->dev.parent = ti_k3_m4 pdev */
|
||||
priv = dev_get_drvdata(rproc_dev->parent);
|
||||
}
|
||||
|
||||
if (!priv) {
|
||||
dev_err(&rpdev->dev,
|
||||
"No driver private data found in parent chain\n");
|
||||
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);
|
||||
|
||||
if (priv->reset_on_init) {
|
||||
struct wago_msg_set_all msg = {
|
||||
.cmd = WAGO_CMD_SET_ALL,
|
||||
.r = 0, .g = 0, .b = 0
|
||||
};
|
||||
|
||||
dev_info(priv->dev, "Resetting all LEDs to black (reset-on-init)\n");
|
||||
mutex_lock(&priv->send_lock);
|
||||
wago_send(priv, &msg, sizeof(msg));
|
||||
mutex_unlock(&priv->send_lock);
|
||||
}
|
||||
|
||||
dev_info(&rpdev->dev,
|
||||
"WAGO M4 RPMsg channel up — LED strip active (%d LEDs)\n",
|
||||
WAGO_LED_NUM_LEDS);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void wago_rpmsg_remove(struct rpmsg_device *rpdev)
|
||||
{
|
||||
struct wago_m4_led_priv *priv = dev_get_drvdata(&rpdev->dev);
|
||||
|
||||
if (priv) {
|
||||
mutex_lock(&priv->send_lock);
|
||||
priv->rpdev = NULL;
|
||||
mutex_unlock(&priv->send_lock);
|
||||
}
|
||||
|
||||
dev_info(&rpdev->dev, "WAGO M4 LED RPMsg channel removed\n");
|
||||
}
|
||||
|
||||
static const struct rpmsg_device_id wago_rpmsg_id_table[] = {
|
||||
{ .name = WAGO_RPMSG_EPT_NAME },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(rpmsg, wago_rpmsg_id_table);
|
||||
|
||||
static struct rpmsg_driver wago_rpmsg_driver = {
|
||||
.drv = {
|
||||
.name = DRIVER_NAME "-rpmsg",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.id_table = wago_rpmsg_id_table,
|
||||
.probe = wago_rpmsg_probe,
|
||||
.callback = wago_rpmsg_cb,
|
||||
.remove = wago_rpmsg_remove,
|
||||
};
|
||||
|
||||
/* -------------------------------------------------------------------------
|
||||
* remoteproc boot (delayed-work retry loop)
|
||||
* ----------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
/*
|
||||
* 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)
|
||||
{
|
||||
struct wago_m4_led_priv *priv =
|
||||
container_of(work, struct wago_m4_led_priv, boot_work.work);
|
||||
struct device *dev = priv->dev;
|
||||
int ret;
|
||||
|
||||
/* Lazily obtain the rproc handle on the first run */
|
||||
if (!priv->rproc) {
|
||||
struct device_node *np = dev->of_node;
|
||||
struct device_node *rproc_np;
|
||||
|
||||
rproc_np = of_parse_phandle(np, "remoteproc", 0);
|
||||
if (!rproc_np) {
|
||||
dev_err(dev, "Missing 'remoteproc' phandle in DT\n");
|
||||
return;
|
||||
}
|
||||
|
||||
priv->rproc = rproc_get_by_phandle(rproc_np->phandle);
|
||||
of_node_put(rproc_np);
|
||||
|
||||
if (IS_ERR_OR_NULL(priv->rproc)) {
|
||||
priv->rproc = NULL;
|
||||
dev_warn(dev, "rproc not yet available, retrying...\n");
|
||||
goto retry;
|
||||
}
|
||||
|
||||
/*
|
||||
* Store priv on the rproc device so wago_rpmsg_probe() can
|
||||
* retrieve it by walking up the parent chain from the rpmsg
|
||||
* device. This must be done before rproc_boot() triggers the
|
||||
* virtio / RPMsg stack.
|
||||
*/
|
||||
dev_set_drvdata(&priv->rproc->dev, priv);
|
||||
}
|
||||
|
||||
/*
|
||||
* 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 (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, RPMsg channel expected shortly\n");
|
||||
return;
|
||||
}
|
||||
|
||||
/* 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);
|
||||
goto fatal;
|
||||
}
|
||||
|
||||
dev_dbg(dev, "Boot attempt %d/%d: loading %s\n",
|
||||
priv->boot_retries + 1, WAGO_BOOT_MAX_RETRIES, priv->fw_name);
|
||||
|
||||
ret = rproc_boot(priv->rproc);
|
||||
if (ret == 0) {
|
||||
priv->rproc_booted_by_us = true;
|
||||
dev_info(dev, "M4 booted with %s, waiting for RPMsg channel\n",
|
||||
priv->fw_name);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* -ENOENT: firmware file not found — rootfs not yet mounted.
|
||||
* Switch to the longer retry interval for filesystem polling.
|
||||
*/
|
||||
if (ret == -ENOENT) {
|
||||
if (++priv->fs_retries >= WAGO_BOOT_FS_MAX_RETRIES) {
|
||||
dev_err(dev,
|
||||
"Firmware '%s' not found after %d attempts, giving up\n",
|
||||
priv->fw_name, WAGO_BOOT_FS_MAX_RETRIES);
|
||||
goto fatal;
|
||||
}
|
||||
dev_dbg(dev, "Firmware not found yet, retrying in %d ms\n",
|
||||
WAGO_BOOT_INITIAL_DELAY_MS);
|
||||
schedule_delayed_work(&priv->boot_work,
|
||||
msecs_to_jiffies(WAGO_BOOT_INITIAL_DELAY_MS));
|
||||
return;
|
||||
}
|
||||
|
||||
dev_err(dev, "rproc_boot failed: %d\n", ret);
|
||||
|
||||
fatal:
|
||||
rproc_put(priv->rproc);
|
||||
priv->rproc = NULL;
|
||||
return;
|
||||
|
||||
retry:
|
||||
if (++priv->boot_retries >= WAGO_BOOT_MAX_RETRIES) {
|
||||
dev_err(dev,
|
||||
"Firmware '%s' not found after %d attempts, giving up\n",
|
||||
priv->fw_name, WAGO_BOOT_MAX_RETRIES);
|
||||
if (priv->rproc) {
|
||||
rproc_put(priv->rproc);
|
||||
priv->rproc = NULL;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
schedule_delayed_work(&priv->boot_work,
|
||||
msecs_to_jiffies(WAGO_BOOT_RETRY_MS));
|
||||
}
|
||||
|
||||
static void wago_rproc_stop(struct wago_m4_led_priv *priv)
|
||||
{
|
||||
if (!priv->rproc)
|
||||
return;
|
||||
|
||||
if (priv->rproc_booted_by_us) {
|
||||
rproc_shutdown(priv->rproc);
|
||||
priv->rproc_booted_by_us = false;
|
||||
}
|
||||
|
||||
rproc_put(priv->rproc);
|
||||
priv->rproc = NULL;
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------------
|
||||
* Platform driver — probe / remove
|
||||
* ----------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
static int wago_m4_led_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct wago_m4_led_priv *priv;
|
||||
const char *fw_name;
|
||||
int ret;
|
||||
|
||||
priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
|
||||
priv->dev = &pdev->dev;
|
||||
mutex_init(&priv->send_lock);
|
||||
|
||||
/* Read firmware name from DT, fall back to default */
|
||||
if (of_property_read_string(pdev->dev.of_node, "firmware-name", &fw_name))
|
||||
fw_name = "wago-led-server.elf";
|
||||
priv->fw_name = fw_name;
|
||||
|
||||
priv->reset_on_init = device_property_read_bool(&pdev->dev, "wago,reset-on-init");
|
||||
|
||||
platform_set_drvdata(pdev, priv);
|
||||
|
||||
/*
|
||||
* 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) {
|
||||
dev_err(&pdev->dev,
|
||||
"Failed to register rpmsg driver: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_LEDS_WAGO_M4_WRAPPER_SYSFS_PASSTHROUGH
|
||||
ret = sysfs_create_group(&pdev->dev.kobj, &wago_led_attr_group);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev,
|
||||
"Failed to create sysfs group: %d\n", ret);
|
||||
unregister_rpmsg_driver(&wago_rpmsg_driver);
|
||||
return ret;
|
||||
}
|
||||
#endif /* CONFIG_LEDS_WAGO_M4_WRAPPER_SYSFS_PASSTHROUGH */
|
||||
|
||||
/*
|
||||
* Schedule boot_work immediately (delay=0).
|
||||
*
|
||||
* boot_work polls for the rproc handle with WAGO_BOOT_RETRY_MS
|
||||
* intervals (200 ms). Once the rproc is registered by the remoteproc
|
||||
* subsystem it checks the state:
|
||||
* RPROC_DETACHED -> attach right away (SPL path, ~1 retry needed)
|
||||
* otherwise -> load firmware from /lib/firmware (Linux-boot path,
|
||||
* retries until rootfs is mounted)
|
||||
*/
|
||||
INIT_DELAYED_WORK(&priv->boot_work, wago_boot_work);
|
||||
schedule_delayed_work(&priv->boot_work, 0);
|
||||
|
||||
dev_info(&pdev->dev,
|
||||
"WAGO M4 LED wrapper probed, %d LEDs active\n",
|
||||
WAGO_LED_NUM_LEDS);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int wago_m4_led_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct wago_m4_led_priv *priv = platform_get_drvdata(pdev);
|
||||
|
||||
/* Cancel any pending boot retry before tearing down */
|
||||
cancel_delayed_work_sync(&priv->boot_work);
|
||||
|
||||
#ifdef CONFIG_LEDS_WAGO_M4_WRAPPER_SYSFS_PASSTHROUGH
|
||||
sysfs_remove_group(&pdev->dev.kobj, &wago_led_attr_group);
|
||||
#endif
|
||||
unregister_rpmsg_driver(&wago_rpmsg_driver);
|
||||
wago_rproc_stop(priv);
|
||||
|
||||
dev_info(&pdev->dev, "WAGO M4 LED wrapper removed\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------------
|
||||
* Device Tree match table / module boilerplate
|
||||
* ----------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
static const struct of_device_id wago_m4_led_of_match[] = {
|
||||
{ .compatible = "wago,m4-led-wrapper" },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, wago_m4_led_of_match);
|
||||
|
||||
static struct platform_driver wago_m4_led_driver = {
|
||||
.probe = wago_m4_led_probe,
|
||||
.remove = wago_m4_led_remove,
|
||||
.driver = {
|
||||
.name = DRIVER_NAME,
|
||||
.of_match_table = wago_m4_led_of_match,
|
||||
},
|
||||
};
|
||||
module_platform_driver(wago_m4_led_driver);
|
||||
|
||||
MODULE_DESCRIPTION("WAGO M4 RGB LED strip wrapper (rpmsg-tty / remoteproc, fire-and-forget)");
|
||||
MODULE_AUTHOR("WAGO GmbH & Co. KG");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS("platform:" DRIVER_NAME);
|
||||
@@ -3483,6 +3483,13 @@ static void spi_nor_everspin_reboot_fix(struct spi_nor *nor)
|
||||
if (!nor->spimem || !nor->dev)
|
||||
return;
|
||||
|
||||
/*
|
||||
* Limit this hack to nodes explicitly named "mram" to prevent
|
||||
* sending blind 8-8-8 resets to unrelated SPI NOR flashes.
|
||||
*/
|
||||
if (!nor->dev->of_node || !of_node_name_eq(nor->dev->of_node, "mram"))
|
||||
return;
|
||||
|
||||
/*
|
||||
* Logging as dev_info so it appears in dmesg during boot.
|
||||
* This helps verify if the fix is being executed.
|
||||
|
||||
Executable
+384
@@ -0,0 +1,384 @@
|
||||
#!/bin/sh
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
#
|
||||
# m4-led-perf-test.sh - Performance / stress test for WAGO M4 RGB LED strip
|
||||
#
|
||||
# Exercises all ten M4 strip LEDs (sys, run, io, em, u1-u6) by activating
|
||||
# kernel LED triggers at various frequencies and patterns. Designed to run
|
||||
# on a BusyBox-based target (ash/sh compatible, no bashisms).
|
||||
#
|
||||
# Triggers supported:
|
||||
# pattern - (default) three phases: fade, flash-fast, flash-slow
|
||||
# timer - simple on/off blink at staggered frequencies per LED
|
||||
# heartbeat - kernel heartbeat trigger (frequency fixed by kernel)
|
||||
#
|
||||
# Usage:
|
||||
# m4-led-perf-test.sh [--trigger=<pattern|timer|heartbeat>]
|
||||
# [--duration=<seconds>]
|
||||
# [--help] [--version]
|
||||
#
|
||||
# Author: WAGO GmbH & Co. KG
|
||||
|
||||
VERSION="1.0.0"
|
||||
LED_SYSFS="/sys/class/leds"
|
||||
|
||||
# All M4 strip LED names (must match DT 'label' properties)
|
||||
LEDS="sys run io em u1 u2 u3 u4 u5 u6"
|
||||
|
||||
# Default options
|
||||
OPT_TRIGGER="pattern"
|
||||
OPT_DURATION=30
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Logging helpers
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
log() { echo "[ m4-led-test ] $*"; }
|
||||
info() { log "INFO $*"; }
|
||||
warn() { log "WARN $*"; }
|
||||
err() { log "ERROR $*" >&2; }
|
||||
die() { err "$*"; cleanup; exit 1; }
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Usage / version
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
usage() {
|
||||
cat <<EOF
|
||||
Usage: $(basename "$0") [OPTIONS]
|
||||
|
||||
Performance / stress test for the WAGO M4 RGB LED strip.
|
||||
Activates kernel LED triggers on all ten strip LEDs at various
|
||||
frequencies to stress the RPMsg / M4 communication path.
|
||||
|
||||
Options:
|
||||
--trigger=<mode> Trigger mode to use (default: pattern)
|
||||
pattern - three phases: fade in/out, fast flash,
|
||||
slow flash with colour cycling
|
||||
timer - simple on/off blink at staggered
|
||||
frequencies (10 ms .. 500 ms)
|
||||
heartbeat - kernel heartbeat trigger
|
||||
--duration=<sec> How long to run the test in seconds (default: 30)
|
||||
--version Print version and exit
|
||||
--help Print this help and exit
|
||||
|
||||
Examples:
|
||||
$(basename "$0")
|
||||
$(basename "$0") --trigger=timer --duration=60
|
||||
$(basename "$0") --trigger=heartbeat
|
||||
|
||||
EOF
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Argument parsing (BusyBox-compatible, no getopt)
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
for arg in "$@"; do
|
||||
case "$arg" in
|
||||
--help)
|
||||
usage
|
||||
exit 0
|
||||
;;
|
||||
--version)
|
||||
echo "$(basename "$0") version $VERSION"
|
||||
exit 0
|
||||
;;
|
||||
--trigger=*)
|
||||
OPT_TRIGGER="${arg#--trigger=}"
|
||||
;;
|
||||
--duration=*)
|
||||
OPT_DURATION="${arg#--duration=}"
|
||||
;;
|
||||
*)
|
||||
err "Unknown option: $arg"
|
||||
usage >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# Validate trigger
|
||||
case "$OPT_TRIGGER" in
|
||||
pattern|timer|heartbeat) ;;
|
||||
*) die "Invalid trigger '$OPT_TRIGGER'. Choose: pattern, timer, heartbeat" ;;
|
||||
esac
|
||||
|
||||
# Validate duration (must be a positive integer)
|
||||
case "$OPT_DURATION" in
|
||||
''|*[!0-9]*) die "Invalid duration '$OPT_DURATION': must be a positive integer" ;;
|
||||
esac
|
||||
[ "$OPT_DURATION" -gt 0 ] || die "Duration must be > 0"
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# LED sysfs helpers
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
# led_path <name> -> /sys/class/leds/<name>
|
||||
led_path() { echo "${LED_SYSFS}/$1"; }
|
||||
|
||||
# led_write <name> <file> <value>
|
||||
led_write() {
|
||||
local path
|
||||
path="$(led_path "$1")/$2"
|
||||
if [ ! -w "$path" ]; then
|
||||
warn "Not writable: $path — skipping"
|
||||
return 1
|
||||
fi
|
||||
printf '%s' "$3" > "$path" 2>/dev/null || warn "Write failed: $path <- $3"
|
||||
}
|
||||
|
||||
# led_set_trigger <name> <trigger>
|
||||
led_set_trigger() { led_write "$1" trigger "$2"; }
|
||||
|
||||
# led_set_brightness <name> <value 0-255>
|
||||
led_set_brightness() { led_write "$1" brightness "$2"; }
|
||||
|
||||
# led_set_multi_intensity <name> <R> <G> <B>
|
||||
led_set_multi_intensity() { led_write "$1" multi_intensity "$2 $3 $4"; }
|
||||
|
||||
# led_set_pattern <name> <pattern-string>
|
||||
led_set_pattern() { led_write "$1" pattern "$2"; }
|
||||
|
||||
# Check which LEDs are actually present in sysfs
|
||||
check_leds() {
|
||||
local found=0 missing=0 name
|
||||
for name in $LEDS; do
|
||||
if [ -d "$(led_path "$name")" ]; then
|
||||
found=$((found + 1))
|
||||
else
|
||||
warn "LED '$name' not found in $LED_SYSFS — skipping"
|
||||
missing=$((missing + 1))
|
||||
fi
|
||||
done
|
||||
info "Found $found / $(echo $LEDS | wc -w) expected LEDs"
|
||||
[ "$found" -gt 0 ] || die "No M4 LEDs found in $LED_SYSFS. Is the driver loaded?"
|
||||
}
|
||||
|
||||
# Return only the LEDs that exist in sysfs
|
||||
present_leds() {
|
||||
local name
|
||||
for name in $LEDS; do
|
||||
[ -d "$(led_path "$name")" ] && printf '%s ' "$name"
|
||||
done
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Cleanup: restore all LEDs to 'none' trigger and brightness=0
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
cleanup() {
|
||||
info "Restoring all LEDs (trigger=none, brightness=0) ..."
|
||||
local name
|
||||
for name in $(present_leds); do
|
||||
led_set_trigger "$name" "none"
|
||||
led_set_brightness "$name" 0
|
||||
done
|
||||
info "Cleanup done."
|
||||
}
|
||||
|
||||
# Trap SIGINT / SIGTERM so Ctrl-C always restores LEDs
|
||||
trap 'info "Interrupted — cleaning up ..."; cleanup; exit 130' INT TERM
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Trigger implementations
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
# --- HEARTBEAT --------------------------------------------------------------
|
||||
|
||||
run_heartbeat() {
|
||||
info "Setting trigger=heartbeat on all LEDs ..."
|
||||
local name
|
||||
for name in $(present_leds); do
|
||||
# White at full brightness
|
||||
led_set_multi_intensity "$name" 255 255 255
|
||||
led_set_brightness "$name" 255
|
||||
led_set_trigger "$name" heartbeat
|
||||
info " $name -> heartbeat"
|
||||
done
|
||||
}
|
||||
|
||||
# --- TIMER ------------------------------------------------------------------
|
||||
#
|
||||
# Stagger delay_on / delay_off across LEDs to create a "running" effect.
|
||||
# Pairs (on_ms, off_ms) cycle through a range from fast (50/50) to slow
|
||||
# (500/500) and back.
|
||||
|
||||
run_timer() {
|
||||
info "Setting trigger=timer on all LEDs with staggered frequencies ..."
|
||||
|
||||
# Pre-defined (on_ms off_ms R G B) tuples — one per LED
|
||||
# Colours: red, green, blue, cyan, magenta, yellow, white, orange, lime, teal
|
||||
set -- \
|
||||
"50 50 255 0 0" \
|
||||
"100 100 0 255 0" \
|
||||
"200 200 0 0 255" \
|
||||
"300 300 0 255 255" \
|
||||
"400 400 255 0 255" \
|
||||
"500 500 255 255 0" \
|
||||
"150 350 255 255 255" \
|
||||
"80 420 255 128 0" \
|
||||
"250 250 128 255 0" \
|
||||
"350 150 0 128 128"
|
||||
|
||||
local name idx on off r g b
|
||||
idx=1
|
||||
for name in $(present_leds); do
|
||||
# Extract the idx-th tuple from positional params
|
||||
eval "tuple=\$$idx"
|
||||
on=$(echo "$tuple" | awk '{print $1}')
|
||||
off=$(echo "$tuple" | awk '{print $2}')
|
||||
r=$(echo "$tuple" | awk '{print $3}')
|
||||
g=$(echo "$tuple" | awk '{print $4}')
|
||||
b=$(echo "$tuple" | awk '{print $5}')
|
||||
|
||||
led_set_multi_intensity "$name" "$r" "$g" "$b"
|
||||
led_set_brightness "$name" 255
|
||||
led_set_trigger "$name" timer
|
||||
led_write "$name" delay_on "$on"
|
||||
led_write "$name" delay_off "$off"
|
||||
|
||||
info " $name -> timer on=${on}ms off=${off}ms RGB($r,$g,$b)"
|
||||
idx=$((idx + 1))
|
||||
done
|
||||
}
|
||||
|
||||
# --- PATTERN ----------------------------------------------------------------
|
||||
#
|
||||
# Three phases, each lasting 1/3 of the total duration:
|
||||
#
|
||||
# Phase 1 — FADE: smooth brightness ramp up/down (pattern trigger)
|
||||
# Phase 2 — FAST FLASH: rapid on/off (50 ms) in different colours per LED
|
||||
# Phase 3 — SLOW FLASH: slow pulse (400 ms) with colour cycling
|
||||
#
|
||||
# Pattern format: "<brightness> <duration_ms> <brightness> <duration_ms> ..."
|
||||
# brightness 0-255, duration in ms. The pattern trigger loops the sequence.
|
||||
|
||||
# Smooth fade: 0->255 in steps then 255->0, total ~2 s per cycle
|
||||
FADE_PATTERN="0 100 32 100 64 100 96 100 128 100 160 100 192 100 224 100"
|
||||
FADE_PATTERN+=" 255 100 224 100 192 100 160 100 128 100 96 100 64 100 32 100"
|
||||
|
||||
# Fast flash: full on 50 ms, off 50 ms
|
||||
FLASH_FAST_PATTERN="255 50 0 50"
|
||||
|
||||
# Slow flash: full on 400 ms, off 400 ms
|
||||
FLASH_SLOW_PATTERN="255 400 0 400"
|
||||
|
||||
run_pattern_phase() {
|
||||
local phase_name="$1"
|
||||
local pattern="$2"
|
||||
shift 2
|
||||
# Remaining args: "name R G B" tuples
|
||||
info " Phase: $phase_name"
|
||||
local name r g b
|
||||
for name in $(present_leds); do
|
||||
# Consume next R G B from positional args
|
||||
r="$1"; g="$2"; b="$3"; shift 3 2>/dev/null || true
|
||||
led_set_trigger "$name" none
|
||||
led_set_multi_intensity "$name" "$r" "$g" "$b"
|
||||
led_set_brightness "$name" 255
|
||||
led_set_trigger "$name" pattern
|
||||
led_set_pattern "$name" "$pattern"
|
||||
info " $name -> pattern='$phase_name' RGB($r,$g,$b)"
|
||||
done
|
||||
}
|
||||
|
||||
run_pattern() {
|
||||
local phase_dur total_leds phase_sleep
|
||||
total_leds=$(present_leds | wc -w)
|
||||
phase_dur=$(( OPT_DURATION / 3 ))
|
||||
[ "$phase_dur" -lt 2 ] && phase_dur=2
|
||||
|
||||
info "Pattern test: 3 phases x ~${phase_dur}s (${total_leds} LEDs)"
|
||||
|
||||
# --- Phase 1: FADE (all white, staggered start via offset pattern) ------
|
||||
# Give each LED a slightly rotated version of the fade pattern so they
|
||||
# don't all pulse in sync — we pre-build per-LED patterns with an initial
|
||||
# offset silence of (index * 200 ms).
|
||||
info "--- Phase 1/3: FADE ---"
|
||||
local idx=0 name offset_pattern prefix
|
||||
for name in $(present_leds); do
|
||||
offset_pattern=""
|
||||
if [ "$idx" -gt 0 ]; then
|
||||
# Insert a leading "0 <offset_ms>" step to stagger each LED
|
||||
prefix=$((idx * 200))
|
||||
offset_pattern="0 ${prefix} "
|
||||
fi
|
||||
led_set_trigger "$name" none
|
||||
led_set_multi_intensity "$name" 255 255 255
|
||||
led_set_brightness "$name" 255
|
||||
led_set_trigger "$name" pattern
|
||||
led_set_pattern "$name" "${offset_pattern}${FADE_PATTERN}"
|
||||
info " $name -> fade (offset ${idx}x200ms)"
|
||||
idx=$((idx + 1))
|
||||
done
|
||||
sleep "$phase_dur"
|
||||
|
||||
# --- Phase 2: FAST FLASH — different colour per LED --------------------
|
||||
info "--- Phase 2/3: FAST FLASH ---"
|
||||
run_pattern_phase "fast-flash" "$FLASH_FAST_PATTERN" \
|
||||
255 0 0 \
|
||||
0 255 0 \
|
||||
0 0 255 \
|
||||
255 255 0 \
|
||||
0 255 255 \
|
||||
255 0 255 \
|
||||
255 128 0 \
|
||||
128 0 255 \
|
||||
0 255 128 \
|
||||
255 255 255
|
||||
sleep "$phase_dur"
|
||||
|
||||
# --- Phase 3: SLOW FLASH — colour cycling (shift colours between LEDs) --
|
||||
info "--- Phase 3/3: SLOW FLASH ---"
|
||||
run_pattern_phase "slow-flash" "$FLASH_SLOW_PATTERN" \
|
||||
0 255 255 \
|
||||
255 0 255 \
|
||||
255 255 0 \
|
||||
0 0 255 \
|
||||
0 255 0 \
|
||||
255 0 0 \
|
||||
128 255 128 \
|
||||
255 128 128 \
|
||||
128 128 255 \
|
||||
200 200 200
|
||||
# Let the last phase run for the remainder of the duration
|
||||
local elapsed=$(( phase_dur * 2 ))
|
||||
local remaining=$(( OPT_DURATION - elapsed ))
|
||||
[ "$remaining" -lt 1 ] && remaining=1
|
||||
sleep "$remaining"
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Main
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
info "============================================"
|
||||
info " WAGO M4 LED Performance Test v${VERSION}"
|
||||
info "============================================"
|
||||
info "Trigger : $OPT_TRIGGER"
|
||||
info "Duration : ${OPT_DURATION}s"
|
||||
info "LED sysfs: $LED_SYSFS"
|
||||
info "--------------------------------------------"
|
||||
|
||||
check_leds
|
||||
|
||||
case "$OPT_TRIGGER" in
|
||||
pattern)
|
||||
run_pattern
|
||||
;;
|
||||
timer)
|
||||
run_timer
|
||||
info "Running for ${OPT_DURATION}s ..."
|
||||
sleep "$OPT_DURATION"
|
||||
;;
|
||||
heartbeat)
|
||||
run_heartbeat
|
||||
info "Running for ${OPT_DURATION}s ..."
|
||||
sleep "$OPT_DURATION"
|
||||
;;
|
||||
esac
|
||||
|
||||
cleanup
|
||||
info "Test complete."
|
||||
exit 0
|
||||
Reference in New Issue
Block a user