Compare commits

...

189 Commits

Author SHA1 Message Date
Heinrich Toews 305190aba7 arch: arm64: dts: ti: Enable slb9670 TPM on k3-am623-pfc-750-8400
TPM support is required for secure key storage and platform integrity
features. Enabling the slb9670 node ensures the TPM is available for
system use on this board variant.

Signed-off-by: Heinrich Toews <ht@twx-software.de>
2026-06-16 17:53:42 +02:00
Heinrich Toews 5fd796f64e arm64: dts: ti: Move default UART4 RTS GPIO to common board DTSI
When an interaction overlay is loaded after a base overlay, its
inclusion of k3-am623-pfc-rs485.dtsi overrides the baseboard-specific
UART4 rts-gpios config back to GPIO0_38. Moving the default rts-gpios to
the base common DTSI prevents this override, letting the active baseboard
overlay (like p0004224 which uses GPIO0_37) cleanly configure its
specific RTS routing.

Signed-off-by: Heinrich Toews <ht@twx-software.de>
2026-06-11 10:36:05 +02:00
Heinrich Toews 4988fa5c86 arm64: boot: fitImage: Add missing config for base-p0004224
U-Boot fails to load the baseboard overlay for PCB p0004224 at boot
because the FIT image configuration node is missing. Adding this entry
resolves the configuration name correctly and lets the bootloader
apply the appropriate base overlay.

Signed-off-by: Heinrich Toews <ht@twx-software.de>
2026-06-11 10:28:50 +02:00
Heinrich Toews c3ed670c91 arm64: dts: ti: Fix UART4 pinmux reference in base overlay p0004224
The omap8250 driver fails to register main_uart4 because the base
devicetree's phandle reference for 'pinctrl-0' is invalidated when
the overlay redefines 'main_uart4_pins_default'. Explicitly assigning
the pinctrl-0 property in the overlay ensures that the resolver
updates the phandle to the newly overlay-defined node.

Signed-off-by: Heinrich Toews <ht@twx-software.de>
2026-06-11 09:52:59 +02:00
Heinrich Toews 4d1f5a13ae arch: arm64: dts: ti: Add overlay for 750-8400 p0004224 RS485 DE fix
Board revision p0004224 has a hardware bug with RS485 DE routed to the
wrong pin (RS.DE instead of RS-2.DE). This overlay applies a workaround
to enable correct RS485 operation on this revision. Future revision
p0004305 resolves the issue in hardware, making this workaround
unnecessary.

Signed-off-by: Heinrich Toews <ht@twx-software.de>
2026-06-10 15:59:42 +02:00
Heinrich Toews e0657990ef Revert "arm64: dts: ti: pfc400-rev3: Use rs-2.de for RS-485 direction control"
This reverts commit 9698958e83.
2026-06-10 15:45:11 +02:00
Heinrich Toews 37580c48f9 arm64: dts: ti: Move single LED mode to specific baseboard overlay
The newer p0004305 baseboard utilizes a dual-LED configuration on its
Ethernet ports for link and activity signaling, making the global
single-led-mode property counter-productive.

DTC compiles separate label overrides inside DTBOs into disjoint
fragments, causing dynamic compile-time /delete-property/ directives
not to propagate across fragment boundaries. Decouple the single-led
configuration from the shared dtsi by allocating it exclusively inside
the legacy p0003964 baseboard overlay.

Signed-off-by: Heinrich Toews <ht@twx-software.de>
2026-06-09 17:16:32 +02:00
Heinrich Toews 9698958e83 arm64: dts: ti: pfc400-rev3: Use rs-2.de for RS-485 direction control
PFC400 hardware revision 3 has a misconfiguration where the
RS-485-2 Driver Enable signal (RS-2.DE) is falsely wired to the RS-1 instance line
instead of RS-485-1 (RS-1.DE).

Override rts-gpios in the revision-specific overlay to select
the rs-2.de GPIO as a workaround for this board errata.

Signed-off-by: Heinrich Toews <ht@twx-software.de>
2026-06-09 16:29:40 +02:00
Heinrich Toews 1bbcd3bd76 arm64: dts: ti: Update board IDs for PFC400 revision 2 overlays
To match the updated hardware identification numbers assigned to the
revision 2 of the PFC400 board prototypes, the overlay filenames and
their respective configurations in the Makefile and fitImage.its need
to be updated. This ensures consistency between physical revision 2
boards and the compiled device-tree binaries.

Signed-off-by: Heinrich Toews <ht@twx-software.de>
2026-06-09 13:14:52 +02:00
Heinrich Toews 5781a66491 arm64: dts: ti: k3-am623-pfc-750-84xx: fix switch port labels (dirty)
Ethernet switch port labels were incorrectly assigned, causing
mismatch between physical port positions and their logical names.
Swap labels to reflect the correct port-to-interface mapping:
port@0->ethX1, port@2->ethX3, port@3->ethX4, port@4->ethEM.

Signed-off-by: Heinrich Toews <ht@twx-software.de>
2026-05-28 18:04:37 +02:00
Heinrich Toews e3fd89f943 arm64: dts: ti: shorten FIT config nodes and add OMS poll interval
The U-Boot extension string generation for overlay configurations
exceeded the libfdt 31-character limit, causing failures during boot.
Shorten the configuration node names in the FIT image to match the
updated U-Boot logic. Additionally, add a poll interval to the OMS
switch overlay to work around missing MCU GPIO interrupt routing.

Signed-off-by: Heinrich Toews <ht@twx-software.de>
2026-05-28 17:39:37 +02:00
Heinrich Toews fbb5aac346 arm64: dts: ti: k3-am623-pfc-750-84xx: Fix VDD_CORE min voltage
The minimum voltage for VDD_CORE was set to 750mV, which is below
the actual operating range. Align it with the maximum of 850mV to
reflect the correct and fixed voltage level for this regulator.

Signed-off-by: Heinrich Toews <ht@twx-software.de>
2026-05-28 09:54:45 +02:00
Heinrich Toews 59f1d87a2c arch: arm64: dts: ti: Remove duplicate TPS65219 PMIC node from wosm
The TPS65219 PMIC node at address 0x30 is already defined in the
base board DTS. Having it redefined in the wosm variant causes
device tree overlay conflicts and redundant regulator registrations.

Signed-off-by: Heinrich Toews <ht@twx-software.de>
2026-05-28 09:54:45 +02:00
Heinrich Toews 7c8e5ded71 arch/arm64/boot/dts/ti: Fix OPP voltage for AM623 PFC 750-8xxx
Correct the operating performance point voltages from 750mV to
850mV across all frequency points. The previous 750mV setting was
insufficient for stable operation at the defined frequencies.

Signed-off-by: Heinrich Toews <ht@twx-software.de>
2026-05-28 09:54:45 +02:00
Heinrich Toews ad4a4f96a3 arm64: dts: ti: k3-am62x-pfc: Fix VDD_CORE voltage for 1.25 GHz OPP
The 1.25 GHz operating performance point (OPP) requests a core voltage
of 0.75V, violating the SoC specifications which require 0.85V. Under
heavy load, the insufficient voltage causes core brownouts, leading to
data aborts, undefined instruction exceptions, and severe memory
corruption. Increase opp-microvolt to 850000 to stabilize the system
at peak frequencies.

Signed-off-by: Heinrich Toews <ht@twx-software.de>
2026-05-28 09:54:45 +02:00
Heinrich Toews 3bbeb76f8d arch: arm64: boot: dts: ti: Add missing address/size-cells
Add missing `#address-cells` and `#size-cells` properties to root
node, `fss`, `ospi0`, and `main_i2c0` nodes in the PFC 750-84xx
WOSM device tree include file.

Without these properties, DT validation warns about missing
address/size specifiers, and child node addresses cannot be
correctly parsed by the kernel.

Signed-off-by: Heinrich Toews <ht@twx-software.de>
2026-05-28 09:54:45 +02:00
Heinrich Toews 1c9d167f39 arm64: dts: ti: k3-am623: fix missing address and size cells
Relying on implicit default values for #address-cells and #size-cells
triggers avoid_default_addr_size and reg_format warnings during device
tree compilation. These unresolved properties create cascading failures
when parsing reg properties and resolving DTBO prerequisites.

Explicitly defining bus cell constraints for I2C and SPI overlay targets
silences these warnings and satisfies strict boundary requirements for
robust overlay application.

Signed-off-by: Heinrich Toews <ht@twx-software.de>
2026-05-28 09:54:45 +02:00
Heinrich Toews c2f1a0dbf7 arm64: dts: ti: Remove static overlays from FIT image config
Foundational hardware definitions are now integrated into the base
device tree via a base .dtsi file, resolving previous root node
patching issues. Default FIT configuration no longer requires static
overlays for CPU voltages, RAM, and PMIC regulators. Removing these
entries allows U-Boot to dynamically load hardware-specific overlays
based on EEPROM values and simplifies runtime application.

Signed-off-by: Heinrich Toews <ht@twx-software.de>
2026-05-28 09:54:45 +02:00
Heinrich Toews f40a482cfa arm64: dts: ti: Extract WOSM core into base dtb and support new overlays
Applying fundamental System-On-Module components like the PMIC, memory
capacity, and CPU voltages via a device tree overlay using the root
node ('&{/}') causes unpredictable structural and dtc compilation
issues (such as register storms on the SDHCI controller).

To bypass this problem, separate the immutable baseline hardware of the
WOSM into a new 'k3-am623-pfc-750-84xx-wosm-base.dtsi' and include it
statically in the main 'k3-am623-pfc-750-8400.dts'. The original
'wosm.dtsi' is now cleanly stripped of root manipulation and serves
only as a framework for true peripheral overlays.

Additionally, introduce the missing new hardware revisions (wosm
p0004263, base p0004305, localbus p0004067, interaction p0004065)
into the Makefile and update the flattened device tree layout inside
'fitImage.its' to assemble them correctly.

Signed-off-by: Heinrich Toews <ht@twx-software.de>
2026-05-28 09:54:45 +02:00
Heinrich Toews 5a450e9f83 arm64: dts: ti: Add expansion resets, RS485 and CAN pinmux for p0004263
Hardware revision p0004263 introduces the following routing changes:
- nRST-EM2 moves from pad B6 to E18
- nRST-EM3 moves from pad A6 to B18

In addition, the WOSM module pins for a second RS485 interface (UART3)
and a CAN interface (MCAN0) are now explicitly defined for future use.

Define the 'em_reset_pins_default', 'rs485_2_pins_default', and
'can_interface_pins_default' in the p0004263 configuration overlay.

Signed-off-by: Heinrich Toews <ht@twx-software.de>
2026-05-28 09:54:45 +02:00
Heinrich Toews fc1cce5f76 arm64: dts: ti: Update OMS buttons for p0004067 revision
The newer hardware revision p0004067 removes the combined nSTOP/RUN
switch from pad D24. Instead, it introduces two distinct buttons:
nRUN-BAS on pad A8 (MCU_I2C0_SCL) and nSTOP-BAS on pad D10
(MCU_I2C0_SDA).

Update the localbus p0004067 overlay to reflect these changes by
disabling the old "oms_stop_run" GPIO keys node, omitting pad D24 from
the "oms_pins_default" node, and defining the new individual RUN and
STOP buttons using the MCU GPIO pins. Finally, include the new overlay
in the build system.

Signed-off-by: Heinrich Toews <ht@twx-software.de>
2026-05-28 09:54:45 +02:00
Heinrich Toews af7d0d54df mtd: spi-nor: core: Restrict blind MRAM reset to mram nodes
The spi_nor_everspin_reboot_fix() was executing unconditionally for any
SPI NOR device lacking a dedicated reset GPIO. This caused unintended
8-8-8 soft reset sequences and an unnecessary boot delay for unrelated
flashes.

While the new WOSM p0004263 hardware revision routes the MRAM nRST to
a GPIO, older revisions (e.g. p0000042) still rely on nPORz and therefore
need this fallback to escape Octal-STR mode during a warm reboot.

Limit the blind reset strictly to nodes named "mram" to avoid breaking
standard SPI NOR flash initializations.

Signed-off-by: Heinrich Toews <ht@twx-software.de>
2026-05-28 09:54:45 +02:00
Heinrich Toews a6e86cb411 arm64: dts: ti: Add MRAM reset for WOSM p0004263 revision
The new AM6234 WOSM hardware revision (p0004263) disconnects the OSPI
MRAM nRST from nPORz and routes it to pad G25 (GPIO0_1). Define the
pinctrl for G25 and assign it to the MRAM node via the reset-gpios
property to ensure proper initialization. Add the new overlay to the
build system.

Signed-off-by: Heinrich Toews <ht@twx-software.de>
2026-05-28 09:54:45 +02:00
Heinrich Toews dd08a46be6 arm64: dts: ti: align fitImage configurations with u-boot board names
Update the FIT image base configuration name from 'conf-750-8400'
to 'conf-PFC-750-840x' to correctly map against the runtime U-Boot
evaluation of the '${board_name}' environment variable.

Signed-off-by: Heinrich Toews <ht@twx-software.de>
2026-05-28 09:54:44 +02:00
Heinrich Toews 8c4152a66a arm64: dts: ti: fix missing root overlay nodes and FIT config
When overlay dtsi files specify root nodes using the standard '/ {'
syntax within a '/plugin/' context, the dtc compiler orphans these
nodes. At runtime, the applied overlays lose critical bindings like
voltage regulators and CPU configuration, leading to SDHCI register
storms and clock initialization failures.

Target the root node explicitly via '&{/} {' to ensure correct overlay
application. Additionally, add all board-specific overlays to the
default FIT configuration to streamline automatic booting.

Signed-off-by: Heinrich Toews <ht@twx-software.de>
2026-05-28 09:54:44 +02:00
Heinrich Toews dc4f8882fe arch: arm64: dts: ti: add M4 LED wrapper for PFC 750-8400
The PFC 750-8400 board uses an M4 coprocessor to drive system
LEDs. Add the wago,m4-led-wrapper node to expose sys, run, io,
em and four user LEDs via the remoteproc firmware interface.
The sys LED defaults to an orange timer trigger on boot.

Signed-off-by: Heinrich Toews <ht@twx-software.de>
2026-05-28 09:54:44 +02:00
Heinrich Toews f99c43a2d2 arm64: dts: ti: Extract WAGO board components to overlays
last working

U-Boot@PFC-750-840x:/ setenv boot_conf "conf-750-8400#overlay-750-8400-wosm-p0000042#overlay-750-8400-base-p0000017#overlay-750-8400-localbus-p0000007#overlay-750-8400-interaction-p0000031"
U-Boot@PFC-750-840x:/ setenv fdt_high
U-Boot@PFC-750-840x:/ setenv bootm_size 0x10000000
U-Boot@PFC-750-840x:/ ext4load mmc 1:2 ${loadaddr} /boot/fitImage
23880192 bytes read in 1048 ms (21.7 MiB/s)
U-Boot@PFC-750-840x:/ setenv bootargs "console=ttyS2,115200 root=/dev/mmcblk0p2 rw rootwait"
U-Boot@PFC-750-840x:/ bootm ${loadaddr}#${boot_conf}

Signed-off-by: Heinrich Toews <ht@twx-software.de>
2026-05-28 09:54:44 +02:00
Heinrich Toews e4dd81bf6d tools: leds: add m4-led-perf-test.sh
Bash/sh script (BusyBox compatible) to stress-test the WAGO M4 RGB LED
strip driver by activating kernel LED triggers on all ten strip LEDs
(sys, run, io, em, u1-u6).

Three trigger modes selectable via --trigger=<mode>:

  pattern   (default)
    Phase 1 - FADE:       staggered smooth brightness ramp (200 ms
              offset per LED) using the pattern trigger
    Phase 2 - FAST FLASH: 50 ms on/off in distinct colours per LED
    Phase 3 - SLOW FLASH: 400 ms on/off with colour cycling

  timer
    Simple on/off blink with staggered delay_on/delay_off values
    (50 ms .. 500 ms) and per-LED colours.

  heartbeat
    Kernel heartbeat trigger, all LEDs white.

Additional options:
  --duration=<sec>   Total test duration in seconds (default: 30)
  --version          Print version string
  --help             Print usage

Cleanup handler (trap INT/TERM) restores trigger=none + brightness=0
on all LEDs when the test ends or is interrupted.

Signed-off-by: Heinrich Toews <ht@twx-software.de>
2026-05-28 09:54:44 +02:00
Heinrich Toews da7fae1b03 arm64: defconfig: enable DYNAMIC_DEBUG for am6xxx
Tracing complex interactions between the application and remote
processors (e.g. RPMsg channels, state synchronizations) requires
comprehensive log granularity without flooding the console by default.

CONFIG_DYNAMIC_DEBUG: Allows selectively enabling or disabling dev_dbg()
  and pr_debug() messages at runtime via the debugfs control file.
  This ensures low-overhead operations normally while permitting in-depth
  event tracing when analyzing boot delays or firmware communication.

Signed-off-by: Heinrich Toews <ht@twx-software.de>
2026-05-28 09:54:44 +02:00
Heinrich Toews cce6dbfb08 arm64: defconfig: enable WAGO M4 RGB LED wrapper support
Operating the RGB LED strips on WAGO PFC AM6xxx-based platforms requires
the wrapper driver alongside specific LED subsystem features to properly
drive colors and sequences.

CONFIG_LEDS_CLASS_MULTICOLOR: Provides the multicolor LED class
  framework, essential to represent the aggregated RGB channels.
CONFIG_LEDS_WAGO_M4_WRAPPER: Enables the dedicated driver translating
  standard LED commands to RPMsg messages for the M4 firmware.
CONFIG_LEDS_WAGO_M4_WRAPPER_SYSFS_PASSTHROUGH: Serves as a back-door to
  the M4 to execute predefined commands directly, bypassing the Linux LED
  subsystem.
CONFIG_LEDS_TRIGGER_PATTERN: Provides the kernel framework to parse
  and execute complex timing sequences (patterns) for the LEDs.

Signed-off-by: Heinrich Toews <ht@twx-software.de>
2026-05-28 09:54:44 +02:00
Heinrich Toews 9778b2faa4 arm64: dts: ti: k3-am623-pfc: disable M4F auto-boot
The M4F remote processor lifecycle requires explicit management by the
kernel remoteproc framework to ensure proper synchronization with Linux
drivers. Automatic booting bypasses necessary initialization sequences
and prevents controlled RPMsg channel establishment.

Signed-off-by: Heinrich Toews <ht@twx-software.de>
2026-05-28 09:54:44 +02:00
Heinrich Toews 32b1b0da35 arm64: dts: ti: k3-am62x-pfc: reserve U-Boot IPC sidecar memory
U-Boot establishes an IPC sidecar mechanism utilizing shared memory
regions that must remain untouched by the kernel. Unrestricted access
corrupts the pre-established communication channels.

Signed-off-by: Heinrich Toews <ht@twx-software.de>
2026-05-28 09:54:44 +02:00
Heinrich Toews f28267ee58 leds: rgb: add WAGO M4 RGB LED wrapper driver
The WAGO PFC controllers use a dedicated M4 remote processor to manage
RGB LED strips. Communication with the M4 core is handled via a binary
RPMsg protocol. A specialized wrapper is required to translate standard
Linux LED class and pattern trigger events into RPMsg commands understood
by the remote processor firmware.

Support includes device tree based LED naming, initial default intensity
configuration, and dynamic RGB pattern triggers. The driver registers
LED devices at probe time for immediate trigger availability and polls
the remote processor to minimize boot delays. An optional sysfs
passthrough mechanism allows direct command execution for advanced
diagnostics.

Signed-off-by: Heinrich Toews <ht@twx-software.de>
2026-05-28 09:54:44 +02:00
Heinrich Toews 44e5bd9b8c remoteproc: ti_k3_m4: add ti,no-auto-boot DT property support
Add a new optional boolean device tree property 'ti,no-auto-boot' for
the TI K3 M4F remoteproc driver. When present, this property disables
automatic firmware loading and booting at probe time by setting the
rproc->auto_boot flag to false.

This is useful in scenarios where the M4F firmware depends on resources
that are only available after the filesystem is mounted (e.g. firmware
files in /lib/firmware). In such cases, an external driver such as
'wago,m4-led-wrapper' can explicitly call rproc_boot() once all
dependencies are satisfied.

The corresponding YAML binding documentation is updated to describe the
new property and its intended use case.

Signed-off-by: Heinrich Toews <ht@twx-software.de>
2026-05-28 09:54:44 +02:00
Maxim Laschinksy 0d9d7d4c5a leds: trigger: pattern: Add per-tuple RGB color
- Extend pattern trigger to parse optional RGB per tuple
- Plumb color data through include/linux/leds.h
- Preserve existing tuple semantics when color omitted

Signed-off-by: Maxim Laschinsky <maxim.laschinsky@wago.com>
2026-05-28 09:54:44 +02:00
Maxim Laschinksy 13361187d2 leds: led-class-multicolor: Accept trailing RGB components
- Allow shorter writes for <=3 sub-LEDs (e.g. "R,G")
- Improve sysfs usability for RGB patterns
- Maintain compatibility with full-length writes

Signed-off-by: Maxim Laschinsky <maxim.laschinsky@wago.com>
2026-05-28 09:54:44 +02:00
Maxim Laschinksy 67e0e9c205 leds: group-multicolor: Respect default-state KEEP
- Skip initial brightness write when DT specifies KEEP
- Use LED core led_init_default_state_get() for defaults
- Add optional mc-allow-subled-writes to keep sub-LED sysfs writable
- Preserve intensity scaling and suspend/resume behavior

Signed-off-by: Maxim Laschinsky <maxim.laschinsky@wago.com>
2026-05-28 09:54:44 +02:00
Peter GALKA 267524ab51 Merge pull request #433 from BU-Automation/topic/gape/fix-gpio-dp_trig_reset
wsysinit: fix gpio 'dp_trig_reset' handling
2026-05-28 09:05:39 +02:00
Peter Galka e93894be4a wsysinit: fix gpio 'dp_trig_reset' handling
Signed-off-by: Peter Galka <peter.galka@wago.com>
2026-05-26 16:19:51 +02:00
Oleg KARFICH 036060f93d Merge pull request #428 from BU-Automation/topic/ht/pfc400/mram-em008lxo-mtd-ram
MTD: Enable mmap() support and diagnostic tooling for MRAM devices
2026-05-15 11:12:59 +02:00
Oleg KARFICH abeb97feae Merge branch 'master' into topic/ht/pfc400/mram-em008lxo-mtd-ram 2026-05-13 14:45:04 +02:00
Oleg KARFICH 38da587d35 Merge pull request #432 from BU-Automation/topic/ok/release_w05.13.00
add new version release_w05.13.00
2026-05-13 14:34:17 +02:00
Oleg Karfich dd0c614012 add new version release_w05.13.00
Signed-off-by: Oleg Karfich <oleg.karfich@wago.com>
2026-05-13 09:36:54 +02:00
Oleg KARFICH fd81267439 Merge branch 'master' into topic/ht/pfc400/mram-em008lxo-mtd-ram 2026-05-12 15:51:54 +02:00
Andrej UNRAU 527d216fa5 Merge pull request #431 from BU-Automation/topic/AUn/revert-mram-optimize-timings
Revert "arm: dts: pfc-750-84xx-wosm: mram: optimize timings"
2026-05-12 12:09:36 +02:00
Andrej Unrau 5c81f0089b Revert "arm: dts: pfc-750-84xx-wosm: mram: optimize timings"
This reverts commit 49f86c67e217f5bc745c203db5ba3c2783f53076.

The optimized MRAM timings are not stable on all devices.

Signed-off-by: Andrej Unrau <andrej.unrau@wago.com>
2026-05-11 10:48:36 +02:00
Oleg KARFICH 03ce0e95a4 Merge branch 'master' into topic/ht/pfc400/mram-em008lxo-mtd-ram 2026-05-07 16:47:03 +02:00
Oleg KARFICH 69818ae138 Merge pull request #430 from BU-Automation/topic/ht/pfc400/pr-ksz
Revert "arm64: configs: am6xxx: enable NET_DSA_MICROCHIP_KSZ9477_SYSFS"
2026-05-07 16:46:45 +02:00
Heinrich Toews 50c4d42a31 Revert "arm64: configs: am6xxx: enable NET_DSA_MICROCHIP_KSZ9477_SYSFS"
This reverts commit f7bef5c22d.
2026-05-06 12:40:02 +02:00
Heinrich Toews e9ad8bc142 tools: mtd: add mram_io byte-wise read/write tool for MTD_RAM devices
mram_io is a small user-space utility that maps a MTD_RAM device
(e.g. Everspin MRAM on Cadence OSPI in DAC mode) into its address
space via mmap() and performs byte-granular reads or writes without
any kernel copy overhead.

Usage:
  mram_io --device <device> --read --offset <off> --length <n>
  mram_io --device <device> --write --offset <off> --data <b0> [b1 b2 ...]

Options:
  --device / -D <n>  MTD character device, e.g. /dev/mtd0
  --offset / -o <n>  Byte offset (hex or decimal)
  --length / -l <n>  Number of bytes to read
  --data   / -d      Remaining arguments are bytes to write

Read output is a classic hex-dump with offset, hex columns (split
8+8) and an ASCII column.

Write mode logs each byte as it is written, then performs an
immediate read-back and reports OK / MISMATCH per byte.

Bytes are written as volatile stores to prevent the compiler from
optimising away individual byte accesses; the mmap region is mapped
noncached by the kernel (pgprot_noncached) so every store goes
straight to the hardware.

Argument parsing uses getopt_long and utilizes the device path as a
named option (--device / -D) to eliminate positional ambiguity when
passing data bytes.

Signed-off-by: Heinrich Toews <ht@twx-software.de>
2026-04-23 12:26:09 +02:00
Heinrich Toews 26cea97562 mtd: spi-nor: log MTD_RAM type and physical address at probe time
The late_init fixup in everspin.c sets mtd->priv to a map_info with
the AHB window's physical base address, but mtd->type/flags were
previously assigned redundantly in the fixup and then overwritten by
spi_nor_set_mtd_info() which always hardcodes MTD_NORFLASH.

Fix the ordering properly:

- spi_nor_set_mtd_info() in core.c now checks mtd->priv before
  assigning type/flags. If a fixup hook has already placed a
  struct map_info there, it uses MTD_RAM / MTD_CAP_RAM and emits
  a dev_info() with the physical base address:

    spi-nor: MTD type set to MTD_RAM (mmap enabled), phys base: 0x60000000

  This makes it easy to verify in the kernel log that the device
  came up correctly and which physical address to use for mmap().

- The now-redundant mtd->type / mtd->flags assignments are removed
  from everspin_mram_late_init().

- core.c gains #include <linux/mtd/map.h> for the struct map_info cast.

Signed-off-by: Heinrich Toews <ht@twx-software.de>
2026-04-23 12:17:29 +02:00
Heinrich Toews 98b840c366 mtd: spi-nor: everspin: enable mmap() for MRAM via Cadence OSPI DAC window
The Cadence OSPI controller (ti,am654-ospi) provides a Direct Access
Controller (DAC) mode: the flash/MRAM address space is visible as a
contiguous window in the CPU's physical address space via the AHB bus
(resource[1] in the platform device, base address ~0x60000000 on AM62x).

When DAC mode is active the driver already uses memcpy_fromio() /
memcpy_toio() against this window for all data transfers. There is
therefore a 1:1 mapping between MTD offsets and physical addresses:

  phys_addr = res_ahb->start + mtd_offset

Wire this up to allow user-space mmap() on /dev/mtd0:

1. Allocate a struct everspin_mram_priv (contains an embedded
   struct map_info) via devm_kzalloc() in the late_init fixup hook.

2. Read the AHB window physical base address from the controller's
   platform_device resource[1] using platform_get_resource().
   This is the same resource that cqspi_probe() uses for ioremap,
   so it is always valid when DAC mode is active.

3. Fill in map_info.phys and map_info.size, store it in nor->priv
   and point mtd->priv at it.

4. Set mtd->type = MTD_RAM and mtd->flags = MTD_CAP_RAM so that
   mtdchar_mmap() takes the vm_iomap_memory() path introduced in the
   companion patch 'mtd: mtdchar: enable mmap() for MTD_RAM devices
   with a physical map'.

If the AHB resource is not found (e.g. non-Cadence controller) the
function logs a warning and continues without mmap support; all other
functionality is unaffected.

After this change user-space can do:

  int fd = open("/dev/mtd0", O_RDWR | O_SYNC);
  void *p = mmap(NULL, size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);

and get direct access to the MRAM without any kernel copy overhead.

Tested on AM6232 (PFC-750-8xxx) with Everspin EM008LXO in Octal-STR
(8-8-8) mode at 200 MHz.

Signed-off-by: Heinrich Toews <ht@twx-software.de>
2026-04-22 14:01:11 +02:00
Heinrich Toews 1259dde287 mtd: mtdchar: enable mmap() for MTD_RAM devices with a physical map
The mmap() path in mtdchar has been intentionally dead since commit
9fdca4d ('mtd: kill dead code in mtdchar_mmap') with the comment
'This is broken because it assumes the MTD device is map-based'.

Re-enable mmap() for MTD_RAM devices that explicitly set mtd->priv to
a valid struct map_info with a known physical address (map->phys !=
NO_XIP). The NULL and NO_XIP guards prevent the broken assumptions
that caused the original removal from applying to any other device.

All other MTD types continue to return -ENODEV as before.

This is a prerequisite for Everspin MRAM on Cadence OSPI (DAC mode)
which exposes the memory as a directly CPU-addressable AHB window.

Signed-off-by: Heinrich Toews <ht@twx-software.de>
2026-04-22 14:01:11 +02:00
Christian HOHNSTÄDT c02ca98757 Merge pull request #427 from BU-Automation/topic/cho/YBP-12364-rs485-term
YBP-12364 - RS485: termination-resistor via WDx
2026-04-16 10:53:07 +02:00
Christian Hohnstaedt 83f91a7720 arm64: dts: k3-am623-pfc-rs485: remove term-gpio
Disconnect the gpio from rs485-ioctl

This way the BIAS network and termination-resistor configuration
work the same way via "gpioset" from the commandline.

Now the resistor may be configured without accessing the serial device,
which may be blocked by an other user.

Signed-off-by: Christian Hohnstaedt <christian.hohnstaedt@wago.com>
2026-04-15 15:05:36 +02:00
Oleg KARFICH 8aaf0bc4dd Merge pull request #402 from BU-Automation/topic/ht/pfc400/next
Add additional support for PFC400 peripherals based on 1st Redesign
2026-04-02 09:50:55 +02:00
Oleg KARFICH b6bd016ca4 Merge branch 'master' into topic/ht/pfc400/next 2026-04-01 12:08:54 +02:00
Oleg Karfich e4b0c7a3f3 add new version release_w05.12.08
Signed-off-by: Oleg Karfich <oleg.karfich@wago.com>
2026-03-24 11:15:09 +01:00
Heinrich Toews ee6bbf0acb drivers: remoteproc: ti_k3_m4: use memremap for no-map reserved memory
Replace devm_ioremap_wc() with memremap(MEMREMAP_WB) for no-map
reserved-memory regions that have struct page backing.

Using ioremap_wc() on a region backed by struct pages violates
ARM64 memory aliasing rules by creating conflicting cached and
write-combining mappings of the same physical pages. This can
cause SIGBUS faults in unrelated userspace processes.

Add a devm cleanup action to call memunmap() on the mapped
region when the device is released.

Signed-off-by: Heinrich Toews <ht@twx-software.de>
2026-03-18 17:32:17 +01:00
Heinrich Toews fb6487598d arm64: dts: k3-am623-pfc-750-8xxx: add no-map and gap reservations for AM623 PFC
Add missing no-map property to ramoops region to prevent Linux
from managing it as regular memory.

Reserve gap regions adjacent to ioremap_wc-mapped M4 memory to
avoid ARM64 cache aliasing issues caused by Linux allocating
pages next to these regions.

Also reserve a gap between r5f-dma-memory and TF-A region to
prevent potential memory conflicts.

Signed-off-by: Heinrich Toews <ht@twx-software.de>
2026-03-18 17:29:27 +01:00
Heinrich Toews 84f6b6b2b4 dsa: microchip: ksz9477_sysfs: move to regmap
Signed-off-by: Heinrich Toews <ht@twx-software.de>
2026-03-17 17:54:09 +01:00
Heinrich Toews c6b83a8a18 arm64: dts: k3-am623-pfc-750-8xxx: fix padconf for SPI2_D1
Signed-off-by: Heinrich Toews <ht@twx-software.de>
2026-03-17 17:54:09 +01:00
Heinrich Toews 440b2636a6 arm64: dts: k3-am62x-pfc: add spidev definition
Signed-off-by: Heinrich Toews <ht@twx-software.de>
2026-03-17 17:54:09 +01:00
Heinrich Toews 8fbc497d02 driver: spi: kbus: fix: add contextualization to max_speed_hz validation
Signed-off-by: Heinrich Toews <ht@twx-software.de>
2026-03-17 17:54:09 +01:00
Heinrich Toews 364bafa89b arm64: dts: k3-am62x-pfc: tpm: fix: reference names should always be in lower case
Signed-off-by: Heinrich Toews <ht@twx-software.de>
2026-03-17 17:54:09 +01:00
Heinrich Toews 973133fb07 arm64: dts: k3-am623-pfc-750-84xx: ksz: enable single led mode
Signed-off-by: Heinrich Toews <ht@twx-software.de>
2026-03-17 17:54:09 +01:00
Heinrich Toews e9a7a7db80 net: dsa: microchip: ksz_common: add single led mode support for ksz9477
Signed-off-by: Heinrich Toews <ht@twx-software.de>
2026-03-17 17:54:08 +01:00
Heinrich Toews f7bef5c22d arm64: configs: am6xxx: enable NET_DSA_MICROCHIP_KSZ9477_SYSFS
Signed-off-by: Heinrich Toews <ht@twx-software.de>
2026-03-17 17:54:08 +01:00
Heinrich Toews 07add97641 net: dsa: microchip: ksz9477: add sysfs reg access support
This commit adds sysfs support for ksz9477 register acccess in
userspace. Although access is possible through the use of regmap it
showed to be uncomfortable to work with whole register dumps and also
or security reasons write access is prohibited by deault.

Do a 16-bit read access:

    echo 16 0x040C > ksz_read

A 32-bit wirte access:

    echo 32 0x0120 0x0115  > ksz_write

Results will be logged in the kernel buffer.

Signed-off-by: Heinrich Toews <ht@twx-software.de>
2026-03-17 17:54:08 +01:00
Heinrich Toews 008d7df4af arm64: configs: am6xxx: enable RPMSG configs
Signed-off-by: Heinrich Toews <ht@twx-software.de>
2026-03-17 17:54:08 +01:00
Heinrich Toews c4cf89b09f configs: am6xxx: enable REMOTEPROC and TI_K3_M4_REMOTEPROC
Signed-off-by: Heinrich Toews <ht@twx-software.de>
2026-03-17 17:54:08 +01:00
Heinrich Toews c037b08a85 arm64: dts: k3-am623-pfc-750-8xxx: add remoteproc mcu_m4fss nodes
Signed-off-by: Heinrich Toews <ht@twx-software.de>
2026-03-17 17:54:08 +01:00
Hari Nagalla beb102e610 remoteproc: Introduce mailbox messages for graceful shutdown
Two mailbox messages are added to facilitate handshake between the
remote processors to ensure graceful stop of the remote processors.

Signed-off-by: Hari Nagalla <hnagalla@ti.com>
2026-03-17 17:54:08 +01:00
Markus Schneider-Pargmann 750daf2748 remoteproc: k3-m4: Move suspend to suspend_late
The remote core has to be able to query the DM for the next system mode
in the suspend path. To support this, ti_sci.c has to send the
prepare_sleep command before the suspend message is sent to the remote
core. This patch moves the suspend to suspend_late to be executed after
ti_sci's suspend call.

Signed-off-by: Markus Schneider-Pargmann <msp@baylibre.com>
2026-03-17 17:54:08 +01:00
Hari Nagalla 1f4ef45bc2 remoteproc: k3-m4: Introduce PM suspend/resume handlers
Introduce suspend/resume handling to m4 remoteproc driver. The driver
registers for pm notifications. And on SUSPEND_PREPARE event a i
RP_MBOX_SUSPEND_SYSTEM mailbox message is sent to the remote core.
a) If the remote core does n't respond to the SUSPEND message, the
driver sets a device on constraint and returns.
b) If the remote core responds with 'RP_MBOX_SUSPEND_ACK', the driver
initiates a stop of the remote core and returns from the suspend
handler.
c) On the other hand, if the remote core responds with
RP_MBOX_SUSPEND_AUTO or RP_MBOX_SUSPEND_CANCEL, the driver simply
returns.

On the Resume path, driver queries DM to determine the state of the
remote core and if it is Off, turns on the remote core.

Signed-off-by: Hari Nagalla <hnagalla@ti.com>
Signed-off-by: Markus Schneider-Pargmann <msp@baylibre.com>
2026-03-17 17:54:08 +01:00
Hari Nagalla 0436682951 remoteproc: k3-m4: support for graceful shutdown of remote cores
Upon a stop request, K3-M4 remote proc driver sends a RP_MBOX_SHUTDOWN
mailbox message to the remote R5 core.

The remote core is expected to:
- relinquish all the resources acquired through Device Manager (DM)
- disable its interrupts
- send back a mailbox acknowledgment RP_MBOX_SHUDOWN_ACK
- enter WFI state.

Meanwhile, the K3-M4 remote proc driver does:
- wait for the RP_MBOX_SHUTDOWN_ACK from the remote core
- wait for the remote proc to enter WFI state
- reset the remote core through device manager

Signed-off-by: Hari Nagalla <hnagalla@ti.com>
2026-03-17 17:54:08 +01:00
Martyn Welch 8811cb9cf5 remoteproc: k3-m4: Add a remoteproc driver for M4F subsystem
The AM62x and AM64x SoCs of the TI K3 family has a Cortex M4F core in
the MCU domain. This core is typically used for safety applications in a
stand alone mode. However, some application (non safety related) may
want to use the M4F core as a generic remote processor with IPC to the
host processor. The M4F core has internal IRAM and DRAM memories and are
exposed to the system bus for code and data loading.

A remote processor driver is added to support this subsystem, including
being able to load and boot the M4F core. Loading includes to M4F
internal memories and predefined external code/data memories. The
carve outs for external contiguous memory is defined in the M4F device
node and should match with the external memory declarations in the M4F
image binary. The M4F subsystem has two resets. One reset is for the
entire subsystem i.e including the internal memories and the other, a
local reset is only for the M4F processing core. When loading the image,
the driver first releases the subsystem reset, loads the firmware image
and then releases the local reset to let the M4F processing core run.

Signed-off-by: Martyn Welch <martyn.welch@collabora.com>
Signed-off-by: Andrew Davis <afd@ti.com>
Signed-off-by: Hari Nagalla <hnagalla@ti.com>
2026-03-17 17:54:08 +01:00
Hari Nagalla 4535aed8be dt-bindings: remoteproc: k3-m4f: Add K3 AM64x SoCs
K3 AM64x SoC has a Cortex M4F subsystem in the MCU voltage domain.
The remote processor's life cycle management and IPC mechanisms are
similar across the R5F and M4F cores from remote processor driver
point of view. However, there are subtle differences in image loading
and starting the M4F subsystems.

The YAML binding document provides the various node properties to be
configured by the consumers of the M4F subsystem.

Signed-off-by: Martyn Welch <martyn.welch@collabora.com>
Signed-off-by: Andrew Davis <afd@ti.com>
Reviewed-by: Conor Dooley <conor.dooley@microchip.com>
Signed-off-by: Hari Nagalla <hnagalla@ti.com>
2026-03-17 17:54:08 +01:00
Heinrich Toews 45a6f8a5b2 arm64: configs: am6xxx: enable NFC_TAG_ST25DV
Signed-off-by: Heinrich Toews <ht@twx-software.de>
2026-03-17 17:54:08 +01:00
Heinrich Toews 835d1cd4c5 nfc: tag: add NFC tag device st25dvxxk
This driver is actually an external kernel module that was imported
into the kernel tree.

You can find the original driver of Boban Loïc at

https://github.com/2pecshy/eeprom-ST25DV-linux-driver.git

Changes done by Fa. WAGO:

- refactor the probe and detect functions to align with
  modern Linux kernel best practices:

- Switch to Managed Resources: Replaced standard kmalloc/kzalloc with
  devm_kmalloc/devm_kzalloc for all data structures and buffers.
  This simplifies the probe function and eliminates the need for
  most memory-related goto labels and manual kfree calls.
- Automatic Device Management: Integrated devm_add_action_or_reset
  to handle the automatic unregistering of the dummy I2C client,
  ensuring safer resource release during error paths and removal.
- Modernized Logging: Replaced generic pr_warn/pr_info calls with
  device-specific dev_err, dev_info, and dev_dbg for better
  traceability in systems with multiple I2C devices.
- Improved String Handling: Replaced the deprecated strlcpy with
  the safer strscpy in the detection phase.
- Enhanced Status Reporting: Added detailed dev_info upon successful
  probe, including the specific chip model name and detected memory
  capacity.
- Coding Style Fixes: Standardized indentation, removed unnecessary
  braces for single-statement blocks, and structured area-specific
  initializations to improve readability and maintainability.

The driver was tested and seems to be working fine.

Signed-off-by: Heinrich Toews <ht@twx-software.de>
2026-03-17 17:54:08 +01:00
Sergej WIEBE 4f6af6494f Merge pull request #425 from BU-Automation/topic/sw/add_initramfs_support
arm: configs: am3xxx_pfc_generic: enable initramfs support
2026-03-16 17:12:43 +01:00
Sergej Wiebe b1cdb250c5 arm: configs: am3xxx_pfc_generic: enable initramfs support
Enable CONFIG_BLK_DEV_INITRD to allow booting an external initramfs
passed by the bootloader via the Android boot image format.

Without this option the kernel ignores any ramdisk provided through
ATAGs or the devicetree linux,initrd-start/end properties and hangs
indefinitely at:

  Waiting for root device /dev/ram0...

Signed-off-by: Sergej Wiebe <sergej.wiebe@wago.com>
2026-03-16 11:52:21 +01:00
Andrej UNRAU d20b58cb7c Merge pull request #424 from BU-Automation/fix/dts-rename-eb-led-to-em
arm64: dts: ti: k3-am623-pfc-750-84xx: fix LED name 'eb' to 'em'
2026-03-10 15:57:18 +01:00
Andrej Unrau 1f75fdeac0 arm64: dts: ti: k3-am623-pfc-750-84xx: fix LED name 'eb' to 'em'
The LED on the PFC400 interaction board was incorrectly named 'eb'.
Rename all occurrences (node names, labels, and multicolor group) to the
correct name 'em'.

Signed-off-by: Andrej Unrau <andrej.unrau@wago.com>
2026-03-10 14:55:14 +01:00
Oliver SCHILDAN b661f3fdcc Merge pull request #423 from BU-Automation/topic/osch/release_w05.11.08
add new version release_w05.11.08
2026-02-27 14:56:39 +01:00
Oliver Schildan d14d5d186e add new version release_w05.11.08 2026-02-27 12:49:22 +01:00
Tobias BIEHL 0e6597e783 Merge pull request #422 from BU-Automation/topic/tbi/kbus-spi-waitqueue-issue
drivers: spi: kbus: remove re-initialization of workqueue in renesas_isr
2026-02-27 11:13:11 +01:00
Tobias Biehl 238366a634 drivers: spi: kbus: remove re-initialization of workqueue in renesas_isr
The init_workqueue_head() call during usage leads to hanging ioctl()
calls in userspace. There is no need to re-init the workqueue, so remove
it.

Signed-off-by: Tobias Biehl <tobias.biehl@wago.com>
2026-02-27 10:30:09 +01:00
Oleg Karfich 7d25e1159b add new version release_w05.11.07
Signed-off-by: Oleg Karfich <oleg.karfich@wago.com>
2026-02-26 16:08:16 +01:00
Heinrich Toews 1dde1a6b0f arm: dts: pfc-750-84xx-wosm: mram: optimize timings
set cdns,tsd2d-ns to 30 ~ D_NSS_FLD   (Bit 31:24)
set cdns,tshsl-ns to 30 ~ D_BTWN_FLD  (Bit 23:16)
set cdns,tchsh-ns to 10 ~ D_AFTER_FLD (Bit 15:8)
set cdns,tslch-ns to 10 ~ D_INIT_FLD  (Bit 7:0)

OSPI0_DEV_DELAY_REG Register in AM62xx TRM.

Calculation with Ref-Clk (400 MHz): 2.5ns * FLD-value.

Can be accessed through devmem:

    devmem 0xfc4000c 32
    0x0C0C0404

Tested with mram-test3.sh:

root@PFC400-DEV-R3:~ ./mram_test3.sh --timeout 600 --debug
Starting Stress Test on /dev/mtd0 (1024 KB Range, Duration: 600s)
[00:03:57] [Iter 5670] Pos:  748 / 1024 KB | Offset: 0x000bb200^[[A
[00:09:59] [Iter 14383] Pos:  217 / 1024 KB | Offset: 0x00036700

========================================
         MRAM TEST RESULTS
========================================
 Final Status:     PASSED
 Total Runtime:    00:10:00
 Total Iterations: 14383
 Total Data Moved: 57532 KB
 Config Timeout:   600s
========================================

Signed-off-by: Heinrich Toews <ht@twx-software.de>
2026-02-26 16:08:16 +01:00
Heinrich Toews 58bbb53e99 arm: dts: pfc-750-84xx-wosm: mram: remove cdns,phy-mode
The device-tree bindnig cdns,phy-mode does not exist in mainline
kernels.

Signed-off-by: Heinrich Toews <ht@twx-software.de>
2026-02-26 16:08:16 +01:00
Oleg KARFICH eeaa1fa415 Merge pull request #419 from BU-Automation/topic/ht/pfc400/mram-em008lxo-rebase
Add full MRAM STR (8s-8s-8s) Support with 200 Mhz Clock Speed
2026-02-25 15:21:40 +01:00
Heinrich Toews 4e58c348b2 spi: cadence-qspi: add logging for clk configuration changes
Signed-off-by: Heinrich Toews <ht@twx-software.de>
2026-02-25 10:00:36 +01:00
Heinrich Toews f3d8580745 mtd: spi-nor: everspin: implement 200MHz Octal-STR mode for EM008LX
This patch enables high-speed Octal-STR (8s-8s-8s) operation at 200MHz
for the Everspin EM008LX MRAM series.

To maintain reliable boot-up, the device is identified at a safe
frequency. Upon successful JEDEC ID verification, the driver escalates
the clock frequency to 200MHz and synchronizes the dummy cycles.

Key Improvements:
- Added frequency escalation in params->setup() hook to 200MHz.
- Implemented Octal register access (0x85/0x81) for configuration.
- Synchronized dummy cycles to 20, as required for 200MHz operation.
- Validated with Cadence OSPI controller using a 400MHz reference clock
  and a read-capture delay of 3-5 cycles.

The resulting configuration achieves the maximum specified throughput
of the Everspin MRAM while maintaining data integrity.

Signed-off-by: Heinrich Toews <ht@twx-software.de>
2026-02-25 10:00:08 +01:00
Heinrich Toews 1d2ed385ff arm: dts: pfc-750-84xx-wosm: mram: change total capacity to 1MB
Signed-off-by: Heinrich Toews <ht@twx-software.de>
2026-02-24 11:23:02 +01:00
Heinrich Toews 7587fa5670 arm: dts: pfc-750-84xx-wosm: mram: adjust timings for 80 Mhz 8s-8s-8s mode
Signed-off-by: Heinrich Toews <ht@twx-software.de>
2026-02-24 11:23:02 +01:00
Heinrich Toews 0918552295 mtd: spi-nor: core: add blind octal reset fix for Everspin MRAM
Everspin Octal-STR MRAMs (like EM008LXO) stay in Octal mode after a
warm reboot if the hardware reset pin is not toggled (e.g., when
connected to nPORZ-MCU). This causes the subsequent JEDEC ID read
to fail, as the controller restarts in Single-SPI mode while the
device still expects Octal-STR commands.

This patch introduces spi_nor_everspin_reboot_fix(), which is called
within spi_nor_hw_reset() as a fallback if no dedicated reset GPIO
is defined in the Device Tree.

The fix sends a "blind" Software Reset sequence (0x66 -> 0x99) using
8-8-8 protocol. If the device is in Octal-STR mode, it will recover to
Single-SPI. If it is already in Single-SPI mode, the 8-lane command is
ignored by the device, making this a safe recovery path for warm
reboots.

Signed-off-by: Heinrich Toews <ht@twx-software.de>
2026-02-24 11:23:02 +01:00
Heinrich Toews 133e49bfc4 mtd: spi-nor: everspin: refactor initialization and enable Octal STR mode
Refactor the Everspin MRAM initialization sequence to support transition
into Octal STR (8s-0-8s) mode.

The following changes are implemented:
- Add everspin_mram_software_reset() to ensure the device is in a known
  state (Single-SPI) before configuration.
- Update register addresses and opcodes to align with the datasheet and
  legacy driver requirements (e.g., using 0x81 for WRAR).
- Refactor everspin_mram_write_reg() to use SNOR_PROTO_1_1_1 for initial
  configuration and ensure Write Enable (WREN) is issued.
- Implement Octal STR mode activation by configuring CFR1V (Dummy Cycles)
  and CFR0V (Mode).
- Add verification steps by reading the Status Register and ID in
  SNOR_PROTO_8_8_8 mode with appropriate dummy cycles.

This ensures the MRAM is correctly detected and switched to high-speed
octal operation during the probe phase.

Signed-off-by: Heinrich Toews <ht@twx-software.de>
2026-02-24 11:23:02 +01:00
Oleg KARFICH 2b10a5afd4 Merge pull request #416 from BU-Automation/topic/ok/pfc400/redesign-2
Topic/ok/pfc400/redesign 2
2026-02-20 07:55:54 +01:00
Oleg Karfich 4ae31418d2 add new version release_w05.11.06
Signed-off-by: Oleg Karfich <oleg.karfich@wago.com>
2026-02-19 15:49:56 +01:00
Oleg Karfich 6441f580b5 arm64: dts: k3-am623-pfc-750-84xx-baseboard: move KSZ9477 reset pin to MCU GPIO
Move the reset GPIO pin for the KSZ9477 Ethernet switch from main_gpio1
pin 10 to mcu_gpio0 pin 11 to match the actual hardware configuration.

This change:
- Removes the reset pin definition from main_pmx0 pinctrl group
- Adds a new mcu_pmx0 pinctrl group for the MCU GPIO configuration
- Updates the ksz9477 device node to reference both pinctrl groups
- Changes reset-gpios from main_gpio1 10 to mcu_gpio0 11

Signed-off-by: Oleg Karfich <oleg.karfich@wago.com>
2026-02-19 15:16:31 +01:00
Oleg Karfich 9e6fd10aa1 add new version release_w05.11.05
Signed-off-by: Oleg Karfich <oleg.karfich@wago.com>
2026-02-18 10:34:56 +01:00
Oleg KARFICH d3d097fd35 Merge pull request #417 from BU-Automation/topic/rg/cc100_can
arm: dts: cc100 dali change PTN5150 configuration
2026-02-18 08:42:20 +01:00
Ralf Gliese 2c0c1870ce arm: dts: EC300: change PTN5150 configuration
add new hardware Version AHX(instead HHX) of PTN5150 with different I2C address

Signed-off-by: Ralf Gliese <ralf.gliese@elrest.de>
2026-02-17 10:58:29 +01:00
Ralf Gliese 6f8cbb8517 arm: dts: cc100 dali change PTN5150 configuration
add new hardware Version AHX (old HX) of PTN5150 with diffrent I2C address

Signed-off-by: Ralf Gliese <ralf.gliese@elrest.de>
2026-02-17 07:50:59 +01:00
Oleg KARFICH 5a06c5102f Merge pull request #414 from BU-Automation/topic/rg/cc100_can
dts: CC100_CAN: change adc configuration
2026-02-13 15:30:23 +01:00
Oleg KARFICH f44b178c4f Merge branch 'master' into topic/rg/cc100_can 2026-02-13 08:51:58 +01:00
Oleg Karfich b5d708e333 add new version release_w05.11.04
Signed-off-by: Oleg Karfich <oleg.karfich@wago.com>
2026-02-12 12:36:52 +01:00
Oleg KARFICH c80c840ed1 Merge pull request #413 from BU-Automation/topic/sw/revert_2GiB_fix
Revert "arm64: dts: ti: k3-am62: Limit PFC400 memory to 2 GiB"
2026-02-11 13:16:18 +01:00
Ralf Gliese 7879a50ae7 dts: CC100_CAN: change adc configuration
update adc configuration for adc1 and adc2

Signed-off-by: Ralf Gliese <ralf.gliese@elrest.de>
2026-02-11 09:54:42 +01:00
Sergej Wiebe f33151c644 Revert "arm64: dts: ti: k3-am62: Limit PFC400 memory to 2 GiB"
This reverts commit 8419103477.
2026-02-11 09:14:10 +01:00
Oleg Karfich 6d2e7187d3 add new version w05.11.03
Signed-off-by: Oleg Karfich <oleg.karfich@wago.com>
2026-02-09 14:06:11 +01:00
Oleg Karfich 7ca5d28515 arm64: configs: am6xxx_defconfig: enable bridge netfilter as built-in
Change CONFIG_BRIDGE_NETFILTER from module (m) to built-in (y) in
am6xxx_defconfig to ensure bridge netfilter functionality is always
available without requiring module loading.

Signed-off-by: Oleg Karfich <oleg.karfich@wago.com>
2026-02-09 14:06:11 +01:00
Sergej Wiebe de4884efa2 add new version w05.11.02
Signed-off-by: Sergej Wiebe <sergej.wiebe@wago.com>
2026-02-09 08:40:44 +01:00
Sergej Wiebe 8419103477 arm64: dts: ti: k3-am62: Limit PFC400 memory to 2 GiB
Reduce the memory configuration for PFC-750-8400 from 4 GiB to 2 GiB
as a workaround for kernel instability with non-contiguous memory banks.

The AM62x SoC supports 4 GiB LPDDR4 RAM configured as two 2 GiB banks:
- Bank 0: 0x80000000 - 0xFFFFFFFF (2 GiB)
- Bank 1: 0x880000000 - 0x8FFFFFFFF (2 GiB, above 4 GB physical address)

The kernel currently cannot properly handle the large memory gap between
these banks. When both banks are configured, the system experiences:
- Kernel panics during memory allocation
- System instability and unexpected reboots
- Memory access failures in high address regions

This patch limits the device tree to expose only the first 2 GiB bank
until proper kernel support for non-contiguous memory regions with large
address gaps is implemented for AM62x platforms.

Memory configuration change:
  before: reg = <0x00 0x80000000 0x00 0x80000000>,
               <0x08 0x80000000 0x00 0x80000000>;
  after:  reg = <0x00 0x80000000 0x00 0x80000000>;

Fixes: WAT-38639
Signed-off-by: Sergej Wiebe <sergej.wiebe@wago.com>
2026-02-09 08:40:44 +01:00
Oleg Karfich 475455de8c add new version w05.11.01
Signed-off-by: Oleg Karfich <oleg.karfich@wago.com>
2026-02-04 09:49:55 +01:00
Oleg Karfich 2d16ee0d5d arm64: dts: ti: add vendor-specific compatible strings for am623 pfcs
Add WAGO vendor prefix and product-specific compatible strings to
AM623 PFC device trees for proper device identification:
- PFC300-750-8302: add "wago,am623-pfc-750_8302-100c"
- PFC400-750-8400: add "wago,am623-pfc-750_8400-100d"

Both entries now include the generic "wago,am623-pfc" fallback
before the TI-specific compatible string, following the device tree
convention of listing compatible strings from most specific to most
generic.

Signed-off-by: Oleg Karfich <oleg.karfich@wago.com>
2026-02-04 09:49:55 +01:00
Oleg Karfich f02bdd750e add new version w05.11.00
Signed-off-by: Oleg Karfich <oleg.karfich@wago.com>
2026-01-30 14:15:43 +01:00
Oleg Karfich cf55b087d3 labgrid.basesoftware: update submodule
* labgrid.basesoftware 9b26e60...ac38584 (2):
  > feat(pfcimagedriver): remove pdfjs before rsync install
  > Merge pull request #293 from BU-Automation/topic/ok/refactor-dip-test-env

Signed-off-by: Oleg Karfich <oleg.karfich@wago.com>
2026-01-30 14:15:43 +01:00
Heinrich Toews aae12aa053 arm64: dts: pfc-750-84xx-wosm: add uio device UIO_OSPI_MRAM
Signed-off-by: Heinrich Toews <ht@twx-software.de>
2026-01-30 14:15:43 +01:00
Heinrich Toews f2a9a9d82a arm64: dts: pfc-750-84xx-wosm: adjust ospi0 configuration for mram
We need to set the compatible here to "ti,am654-ospi" in order to use
DAC mode. If used with 'cdns,qspi-nor' DAC mode is going to be disabled.

The configuration was tested with 25 Mhz and seems to be stable but
higher speeds could be possible.

Create also a partition for the mtd nor device.

Signed-off-by: Heinrich Toews <ht@twx-software.de>
2026-01-30 14:15:43 +01:00
Heinrich Toews e8bb94fc8b arm64: configs: am6xxx: enable CONFIG_MTD_SPI_NOR
Signed-off-by: Heinrich Toews <ht@twx-software.de>
2026-01-30 14:15:43 +01:00
Heinrich Toews 859e24dc88 spi: cadence-qspi: add dbg output for DAC mode
Signed-off-by: Heinrich Toews <ht@twx-software.de>
2026-01-30 14:15:43 +01:00
Heinrich Toews 262c9d47f4 spi: cadence-qspi: always use direct mode on reads if enabled
Signed-off-by: Heinrich Toews <ht@twx-software.de>
2026-01-30 14:15:43 +01:00
Heinrich Toews 8a42262612 spi: cadence-qspi: add an info message for DAC enabled
Signed-off-by: Heinrich Toews <ht@twx-software.de>
2026-01-30 14:15:43 +01:00
Heinrich Toews 23a3f5b2c9 spi: cadence-qspi: fix a wrong DTR/wr_completion handling
In case of a DTR (double transfer rate) the controller seems to have a
problem to handle dummy bytes correctly while do a busy wait for write
completion. The code up to this point was disabling direct-mode if write
completion is enabled and is assuming that DTR is enabled. During wirtes
direct mode is only triggered if DTR is disabled. This is configuration
mismatch in case of STR and wr_completion enabled.

This commit fixes this behaviour and disables direct-mode writes only if
both DTR and wr_completion is enabled.

Signed-off-by: Heinrich Toews <ht@twx-software.de>
2026-01-30 14:15:43 +01:00
Heinrich Toews f33e4f76fa mtd: spi-nor: add support for everspin mram flashes
Signed-off-by: Heinrich Toews <ht@twx-software.de>
2026-01-30 14:15:43 +01:00
Oleg Karfich bf7f63945b add new version w05.10.02
Signed-off-by: Oleg Karfich <oleg.karfich@wago.com>
2026-01-28 11:41:24 +01:00
Oleg KARFICH 57aa8e8fec Merge pull request #405 from BU-Automation/topic/maxim/dts_add_support_leds-group-multicolor
Topic/maxim/dts add support leds group multicolor
2026-01-28 10:28:38 +01:00
Oleg KARFICH f6e5a4a6d2 Merge branch 'master' into topic/maxim/dts_add_support_leds-group-multicolor 2026-01-28 09:41:32 +01:00
Andrej UNRAU 496de54955 Merge pull request #404 from BU-Automation/topic/aun/pfc400-cpu-freq-fix-1.4ghz
arm64: dts: ti: k3-am623-pfc-750-8400: Enable 1.4GHz CPU frequency
2026-01-27 17:44:59 +01:00
Maxim Laschinksy 1c0d6b699b arm: dts: stm32mp153-cc100: Align LED boot policy
- Set non-SYS LEDs/groups to OFF for clean boot
- Keep sys-red/sys-green at KEEP with timer trigger
- Add mc-allow-subled-writes to sys_led group
- Preserve led-mmc default trigger unchanged

Signed-off-by: Maxim Laschinsky <maxim.laschinsky@wago.com>
2026-01-26 19:18:35 +01:00
Maxim Laschinksy 18b3801143 arm64: dts: am623-pfc400: RGB LEDs and boot policy
- Group PCA9552 channels into multicolor sys/run/io/em/u1..u6
- Set sys-red/sys-green to KEEP with timer trigger for early blink
- Set non-SYS LEDs and groups to OFF for deterministic startup
- Enable mc-allow-subled-writes on sys_led for legacy control
- Set sys-overwrite default-state to ON (static control)

Signed-off-by: Maxim Laschinsky <maxim.laschinsky@wago.com>
2026-01-26 19:18:35 +01:00
Andrej Unrau 306ad02f8e arm64: dts: ti: k3-am623-pfc-750-8400: Enable 1.4GHz CPU frequency
The PFC400 is capable of supplying 0.85V to the VDD_CORE which allows
the Cortex-A53s to operate at 1.4GHz according to the AM62x SoC data
sheet[0].

The CPU voltage regulator (buck1_30_reg) was configured with a fixed
maximum voltage of 750mV, preventing the CPU from utilizing the
available 1.4GHz OPP. Increase the maximum voltage to 850mV to allow
the regulator to supply the required voltage for the opp-1400000000
entry from the common OPP table.

[0] https://www.ti.com/lit/ds/symlink/am625.pdf

Signed-off-by: Andrej Unrau <andrej.unrau@wago.com>
2026-01-26 11:51:41 +01:00
Oleg KARFICH 0fb5a75a40 Merge pull request #400 from BU-Automation/topic/ok/activate-dip-test
labgrid.basesoftware: update submodule
2026-01-08 14:24:33 +01:00
Oleg Karfich b5c0b5cbad labgrid.basesoftware: update submodule
* labgrid.basesoftware 6ff6e59...9b26e60 (6):
  > test(config): activate DIP test for 750-8111 devices
  > feat(config): update DIP test configs for 750-8110/8111 and 750-8101
  > refactor(tests): improve DIP switch test with parametrization
  > refactor(driver): update DIP driver bindings and remove resource
  > test: linux: controls: use dip-pattern for dip-switch test
  > Merge pull request #290 from BU-Automation/topic/tbi/kbus-update-testapp

Signed-off-by: Oleg Karfich <oleg.karfich@wago.com>
2026-01-08 07:53:40 +01:00
Oleg KARFICH 9ab5ef989e Merge pull request #399 from BU-Automation/topic/ok/pfc400-fix-model-naming
arm64: dts: k3-am623-pfc-750-8400.dts: update model and compatible
2026-01-06 09:42:28 +01:00
Oleg Karfich 8937f1936f arm64: dts: k3-am623-pfc-750-8400.dts: update model and compatible
- Set 'compatible' and 'model' properties in k3-am623-pfc-750-8400.dts
  to properly identify the board.
- Remove redundant 'model' property from k3-am623-pfc-750-84xx-wosm.dtsi
  to avoid duplication and ensure correct board description.

Signed-off-by: Oleg Karfich <oleg.karfich@wago.com>
2026-01-05 14:12:48 +01:00
Oleg Karfich 030a46b301 add new version w05.10.01
Signed-off-by: Oleg Karfich <oleg.karfich@wago.com>
2026-01-05 13:38:30 +01:00
Oleg Karfich dde9f8c022 arm64: dts: k3-am623-pfc-750-84xx-wosm.dtsi: set ethernet1 alias for cpsw_port1 for consistency
Set the ethernet1 alias to cpsw_port1 to ensure consistency with included
k3-am623-pfc-750-8xxx-common.dtsi DTS file, which defines both ethernet aliases
in reverse order. Without this change, ethernet0 and ethernet1 would both point
to cpsw_port2, leading to incorrect alias mapping and incorrect mac
address fixup in bootloader.

Signed-off-by: Oleg Karfich <oleg.karfich@wago.com>
2026-01-05 13:38:30 +01:00
Oleg KARFICH 2786b333ab Merge pull request #397 from BU-Automation/topic/ok/fix-kbus-tests
labgrid.basesoftware: update submodule
2025-12-22 13:51:34 +01:00
Oleg Karfich a5c2fdfd06 labgrid.basesoftware: update submodule
* labgrid.basesoftware 69aa8fc...6ff6e59 (3):
  > Revert "test(kbus): mark kbus tests as xfail"
  > tests: linux: kbus: update testkbus applications
  > Merge pull request #292 from BU-Automation/topic/ok/xfail-kbus-on-pfcg2

Signed-off-by: Oleg Karfich <oleg.karfich@wago.com>
2025-12-22 12:34:58 +01:00
Oleg Karfich d6f1d86ee7 add new version w05.10.00
Signed-off-by: Oleg Karfich <oleg.karfich@wago.com>
2025-12-19 17:08:55 +01:00
Oleg KARFICH 5cf072de1a Merge pull request #394 from BU-Automation/topic/js/ksz-refactor
net: dsa: microchip: refactor ethtool and devlink
2025-12-19 14:29:31 +01:00
Jan Sondhauss fdab488aa1 net: dsa: microchip: refactor ethtool and devlink
- extract common regdump function
- conistently use ksz9477 instead of ksz9
- fix integer constants in code

Signed-off-by: Jan Sondhauss <jan.sondhauss@wago.com>
2025-12-19 13:06:40 +01:00
Oleg KARFICH 1e1dcb7b51 Merge pull request #395 from BU-Automation/topic/sw/kbus-fix
labgrid.basesoftware: update submodule due to kbus testapp update
2025-12-19 13:05:46 +01:00
Sergej Wiebe ccd677e99c labgrid.basesoftware: update submodule due to kbus testapp update
Signed-off-by: Sergej Wiebe <sergej.wiebe@wago.com>
2025-12-19 08:11:57 +01:00
Sergej Wiebe e3feca2671 chore(jenkins): update backup U-Boot version to w01.09.00_01
Update Jenkins pipeline to use U-Boot release w01.09.00_01 as backup
bootloader for test deployment:
- Update buildPathUBoot to release_w01.09.00_01 branch
- Update artifactoryBuildUBootFastboot to w01.09.00_01 release
- Used as LG_U_BOOT_BACKUP_AM6XXX for test environment deployment

Signed-off-by: Sergej Wiebe <sergej.wiebe@wago.com>
2025-12-19 08:11:56 +01:00
Oleg KARFICH 28b30bfdd1 Merge pull request #360 from BU-Automation/topic/js/ksz9477-broadcast-rate-limit
Topic/js/ksz9477 broadcast rate limit
2025-12-05 12:55:48 +01:00
Jan Sondhauss f613e4249f net: dsa: ksz9477: add ethtool regdump functionality
This allows for the switches registers to be dumped via the ethtool
interface. Compared to the ksz8 implemention only the registers are
exposed. Dumping of the fdb and stat mac table can be added once the
need arises.

The reg-dump uses the reg_map of the driver to select which registers to
dump.

Signed-off-by: Jan Sondhauss <jan.sondhauss@wago.com>
2025-12-04 11:18:15 +01:00
Jan Sondhauss 2d6b9d3c39 net: dsa: ksz9477: add devlink parameter interface
The devlink interface exposes the switches broadcast and multicast
protection feature to the userspace.
The handling is similiar to that of the ksz8863 but with different
registers and values.

Signed-off-by: Jan Sondhauss <jan.sondhauss@wago.com>
2025-12-04 11:18:11 +01:00
Jan Sondhauss e1dfc25514 arm: configs: imx6_vtpctp_defconfig: enable vdso
VDSO allows to avoid syscalls when getting the timeofday
aka posix clock_gettime

Signed-off-by: Jan Sondhauss <jan.sondhauss@wago.com>
2025-12-01 11:53:04 +01:00
Jan Sondhauss e11d0146b8 arm: configs: am3xxx_pfc_generic: enable vdso
VDSO allows to avoid syscalls when getting the timeofday
aka posix clock_gettime

Signed-off-by: Jan Sondhauss <jan.sondhauss@wago.com>
2025-12-01 11:53:04 +01:00
Thomas Unterberg aa4373798b labgrid.basesoftware: verify vdso support 2025-12-01 11:53:04 +01:00
Oleg KARFICH e86ede0ad3 Merge pull request #392 from BU-Automation/topc/ok/fix-model
arm64: dts: : k3-am623-pfc: update model strings for PFC300 and PFC400
2025-11-27 08:42:10 +01:00
Sergej Wiebe 8c50523c43 arm: dts: sync PFC device tree files from u-boot repo
Synchronize PFC 750-8xxx device tree files with u-boot repository
changes including:

- Fix indentation and formatting consistency (spaces to tabs)
- Remove unused includes and aliases for cleaner code
- Update comments and code organization

This sync brings the Linux DTS files in alignment with the
corresponding u-boot device tree definitions for PFC300/400
variants.

Signed-off-by: Sergej Wiebe <sergej.wiebe@wago.com>
2025-11-25 14:47:58 +01:00
Heinrich Toews 41dee5f42a arm: dts: pfc-750-84xx-interaction: add 2nd PCA led-driver
Signed-off-by: Heinrich Toews <ht@twx-software.de>
2025-11-25 12:25:01 +01:00
Heinrich Toews af5488e2ac arm: dts: pfc-750-84xx-wosm: set VSEL_SD by gpio pin
Signed-off-by: Heinrich Toews <ht@twx-software.de>
2025-11-25 12:24:45 +01:00
Oleg Karfich c14dfeb637 arm64: dts: : k3-am623-pfc: update model strings for PFC300 and PFC400
Changed the 'model' property in device tree source files for PFC300-750-8302
and PFC400-750-8400 to remove the "WAGO" prefix and use a consistent
format. This improves clarity and aligns with naming conventions.

Signed-off-by: Oleg Karfich <oleg.karfich@wago.com>
2025-11-24 16:06:27 +01:00
Peter GALKA 12bea14158 Merge pull request #386 from BU-Automation/topic/gape/enable-tracing-cc100
arch: configs: stm32mp1: enable tracing
2025-11-21 07:16:08 +01:00
Peter Galka b05b572baf add new version w05.09.00
Signed-off-by: Peter Galka <peter.galka@wago.com>
2025-11-20 09:30:44 +01:00
Peter Galka 23acecfb00 arch: configs: stm32mp1: enable tracing
Signed-off-by: Peter Galka <peter.galka@wago.com>
2025-11-19 15:47:19 +01:00
Jan Sondhauss e2ee500d29 Revert "net: phy: phy_device: repeat reading of the phy-state when state is down"
Repeated reading of the phy-state does not help in preventing spurious
link down.

This reverts commit 621a26e5dd.

Signed-off-by: Jan Sondhauss <jan.sondhauss@wago.com>
2025-11-19 06:56:18 +01:00
Oleg KARFICH 5c511313fa Merge pull request #359 from BU-Automation/topic/ht/pfc400/dsa-ksz9477-rebase
Introduce PFC400 Plattform-Support incl. working KSZ DSA network switch
2025-11-18 11:01:38 +01:00
Oleg Karfich a105e63ad5 labgrid.basesoftware: update submodule
* labgrid.basesoftware 32a1f83...bdfb3ac (1):
  > Merge pull request #285 from BU-Automation/topic/ht/ok/pfc400/dsa-ksz9477-rebase-review/alltests

Signed-off-by: Oleg Karfich <oleg.karfich@wago.com>
2025-11-17 15:09:35 +01:00
Oleg Karfich 4b9e09800c arm64: dts: k3-am623-pfc-750-8302.dts: switch gpmc pinctrl from mcu_pmx0 to main_pmx0
Change gpmc-data-default-pins node to use main_pmx0 instead of mcu_pmx0
in the device tree for k3-am623-pfc-750-8302. This aligns pinmux
configuration with hardware requirements

Signed-off-by: Oleg Karfich <oleg.karfich@wago.com>
2025-11-17 15:09:35 +01:00
Heinrich Toews f75bd1e792 arm64: dts: k3-am623-pfc-750-84xx: add working ksz9477 config
Signed-off-by: Heinrich Toews <ht@twx-software.de>
2025-11-17 15:09:35 +01:00
Heinrich Toews f7e25870a7 driver: spi: kbus: fix: add missing contextualization of KBUS-specific code
The driver is lacking of a consistent contextualization
of KBUS-specific code paths.

The problem showed up while trying to probe the micrel KSZ switch
driver by utilizing the spi bus.

This patch contextualizes the problematic code paths by checking first
if we are in a KBUS-driver context.

Signed-off-by: Heinrich Toews <ht@twx-software.de>
2025-11-17 15:09:35 +01:00
Heinrich Toews bbcb939022 arm64: dts: pfc-750-84xx: interaction: add led reset pin config
Signed-off-by: Heinrich Toews <ht@twx-software.de>
2025-11-17 15:09:35 +01:00
Heinrich Toews f8bab57b5d arm64: dts: k3-am62x-pfc-kbus: increase max spi freqency to 25 mhz
Signed-off-by: Heinrich Toews <ht@twx-software.de>
2025-11-17 15:09:34 +01:00
Heinrich Toews 4299c425a7 arm64: dts: k3-am623-pfc-750-8xxx: disable SD write protection
Due to EMV related challenges we decided to move from normal SD
to uSD cards. Here we have no WP pin. So disable it.

Signed-off-by: Heinrich Toews <ht@twx-software.de>
2025-11-17 15:09:34 +01:00
Heinrich Toews f60906a0ec arm64: dts: k3-am623-pfc-750-84xx-kbus: enable mmc0 sd gpio-led function
Signed-off-by: Heinrich Toews <ht@twx-software.de>
2025-11-17 15:09:34 +01:00
Heinrich Toews 68369bf6dd arm64: configs: am6xxx: turn on CONFIG_LEDS_GPIO
Signed-off-by: Heinrich Toews <ht@twx-software.de>
2025-11-17 15:09:34 +01:00
Heinrich Toews 8516594844 arm64: dts: k3-am623-pfc-750-8xxx: add eeprom_typelabel alias
Signed-off-by: Heinrich Toews <ht@twx-software.de>
2025-11-17 15:09:34 +01:00
Heinrich Toews 28f8298a30 arm64: dts: k3-am623-pfc-750-84xx: disable HW-ID eeproms due to firmware incompatability
Disable for now all HW-ID eeprom devices due to incapability of the
firmware of handling several eeprom devices at the same time.

/dev/eeprom needs to be set to the specific typelabel eeprom device.

See /etc/init.d/link_devices for the details.

Signed-off-by: Heinrich Toews <ht@twx-software.de>
2025-11-17 15:09:34 +01:00
Heinrich Toews 8ef41904b6 configs: am6xxx: add pfc400 related configs
Signed-off-by: Heinrich Toews <ht@twx-software.de>
2025-11-17 15:09:34 +01:00
Heinrich Toews 50b3642e6d arm64: dts: k3-am62x-pfc: introduce the pfc400 platform
NOTICE

    - TPS configuration relies on a due NVM/u-boot preperation
    - Micrel-Switch for now is only setup in 'switched-mode'
    - SDHC HS SDR104 support was heavely tested and seems to work fine
    - USB-Host needs a due DEFCONFIG configuration

TODO

    - Micrel DSA seperated mode configuration
    - OneWire
    - MRAM QSPI Setup & Test
    - tpm, etc.

Signed-off-by: Heinrich Toews <ht@twx-software.de>
2025-11-17 15:09:34 +01:00
Heinrich Toews e056f60bdb arm64: dts: k3-am62x-pfc: rework oftree structure
As a preparation for the new pfc400 platform we need
to rework the structure of am623-pfc device trees.

Signed-off-by: Heinrich Toews <ht@twx-software.de>
2025-11-17 15:09:34 +01:00
Oleg Karfich 67307e3d9c drivers: misc: ti_sn74lv165a: refactor driver and split dip-switch gpios
Refactor device tree and driver to use separate named GPIO properties
(dip-clk-gpios, dip-output-gpios, dip-load-gpios) for the SN74LV165A
shift register instead of a single 'gpios' array. Update driver to
match new bindings and simplify GPIO handling. Also improve sysfs
attribute implementation and error handling.

Signed-off-by: Oleg Karfich <oleg.karfich@wago.com>
2025-11-17 12:32:57 +01:00
Oleg Karfich 90d3dae72a add new version w05.08.02
Signed-off-by: Oleg Karfich <oleg.karfich@wago.com>
2025-11-17 12:32:57 +01:00
Oleg Karfich 1386f06618 drivers: misc: ti_sn74lv165a: fix active_low handling
Remove active_low field and related logic from the sn74_gpio struct and
associated functions. GPIO polarity is handled directly by the gpiod API.

Signed-off-by: Oleg Karfich <oleg.karfich@wago.com>
2025-11-17 12:32:57 +01:00
Peter GALKA 0f005f4102 Merge pull request #382 from BU-Automation/topic/gape/pfc300-watchdog-timeout
drivers: watchdog: rti_wdt:
2025-11-14 12:18:54 +01:00
Peter GALKA de5ef70f30 Merge branch 'master' into topic/gape/pfc300-watchdog-timeout 2025-11-14 09:21:48 +01:00
Oleg KARFICH 91ff52486f Merge pull request #383 from BU-Automation/topic/dje/jenkins-library-release
Added exclude for non-existing files in deploy stage
2025-11-13 07:47:21 +01:00
Oleg KARFICH e831417a68 Merge branch 'master' into topic/dje/jenkins-library-release 2025-11-12 17:24:27 +01:00
Dennis JEINSEN e5afcd44d7 jenkins: exclude non-existing build link in modules folder in deploy stage
When deploying all kernel artefacts the modules folder includes dead
links to the build folder. This breaks a pipeline update. Fix this by
excluding the dead link when deploying to artifactory.
2025-11-12 17:17:37 +01:00
Peter Galka 36118fe405 drivers: watchdog: rti_wdt: allow device specific timeout configuration
The source of the timeout value for an inactive watchdog is now evaluated
in the following order:
1. "timeout-sec" from the Device Tree (per-watchdog)
2. Module parameter (global)
3. Default value (60 s) (global)

This reordering ensures that device-specific configuration takes precedence
over global defaults, improving flexibility for systems with multiple watchdogs.

Signed-off-by: Peter Galka <peter.galka@wago.com>
2025-11-05 10:42:11 +01:00
Oleg KARFICH 413223af09 Merge pull request #380 from BU-Automation/topic/ok/update-labgrid-basesoftware
labgrid.basesoftware: update submodule
2025-11-03 14:50:47 +01:00
Oleg Karfich a4c7b78584 labgrid.basesoftware: update submodule
* labgrid.basesoftware f1a3c1f...32a1f83 (2):
  > Merge pull request #266 from BU-Automation/topic/tbi/kbus-add-benchmark-test
  > Merge pull request #284 from BU-Automation/topic/ok/update-submodules

Signed-off-by: Oleg Karfich <oleg.karfich@wago.com>
2025-11-03 13:20:30 +01:00
94 changed files with 7498 additions and 1284 deletions
@@ -0,0 +1,60 @@
# 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 "rpmsg-tty" channel using
ASCII text commands (CMD-WLED / CMD-IDL). Each LED is exposed as a Linux LED
multiclass (RGB) device under /sys/class/leds/m4-led<N>/.
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";
};
@@ -0,0 +1,133 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/remoteproc/ti,k3-m4f-rproc.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: TI K3 M4F processor subsystems
maintainers:
- Hari Nagalla <hnagalla@ti.com>
- Mathieu Poirier <mathieu.poirier@linaro.org>
description: |
Some K3 family SoCs have Arm Cortex M4F cores. AM64x is a SoC in K3
family with a M4F core. Typically safety oriented applications may use
the M4F core in isolation without an IPC. Where as some industrial and
home automation applications, may use the M4F core as a remote processor
with IPC communications.
$ref: /schemas/arm/keystone/ti,k3-sci-common.yaml#
properties:
compatible:
enum:
- ti,am64-m4fss
power-domains:
maxItems: 1
"#address-cells":
const: 2
"#size-cells":
const: 2
reg:
items:
- description: IRAM internal memory region
- description: DRAM internal memory region
reg-names:
items:
- const: iram
- const: dram
resets:
maxItems: 1
firmware-name:
maxItems: 1
description: Name of firmware to load for the M4F core
ti,no-auto-boot:
type: boolean
description:
If present, the remoteproc will not automatically load and boot the
M4F firmware at probe time. Firmware loading is then delegated to an
external driver that calls rproc_boot() explicitly once the
filesystem is available.
mboxes:
description:
OMAP Mailbox specifier denoting the sub-mailbox, to be used for
communication with the remote processor. This property should match
with the sub-mailbox node used in the firmware image.
maxItems: 1
memory-region:
description:
phandle to the reserved memory nodes to be associated with the
remoteproc device. Optional memory regions available for firmware
specific purposes.
(see reserved-memory/reserved-memory.yaml in dtschema project)
maxItems: 8
items:
- description: regions used for DMA allocations like vrings, vring buffers
and memory dedicated to firmware's specific purposes.
additionalItems: true
required:
- compatible
- reg
- reg-names
- ti,sci
- ti,sci-dev-id
- ti,sci-proc-ids
- resets
- firmware-name
unevaluatedProperties: false
examples:
- |
reserved-memory {
#address-cells = <2>;
#size-cells = <2>;
mcu_m4fss_dma_memory_region: m4f-dma-memory@9cb00000 {
compatible = "shared-dma-pool";
reg = <0x00 0x9cb00000 0x00 0x100000>;
no-map;
};
mcu_m4fss_memory_region: m4f-memory@9cc00000 {
compatible = "shared-dma-pool";
reg = <0x00 0x9cc00000 0x00 0xe00000>;
no-map;
};
};
soc {
#address-cells = <2>;
#size-cells = <2>;
mailbox0_cluster0: mailbox-0 {
#mbox-cells = <1>;
};
remoteproc@5000000 {
compatible = "ti,am64-m4fss";
reg = <0x00 0x5000000 0x00 0x30000>,
<0x00 0x5040000 0x00 0x10000>;
reg-names = "iram", "dram";
resets = <&k3_reset 9 1>;
firmware-name = "am62-mcu-m4f0_0-fw";
mboxes = <&mailbox0_cluster0>, <&mbox_m4_0>;
memory-region = <&mcu_m4fss_dma_memory_region>,
<&mcu_m4fss_memory_region>;
ti,sci = <&dmsc>;
ti,sci-dev-id = <9>;
ti,sci-proc-ids = <0x18 0xff>;
};
};
@@ -29,3 +29,7 @@
&cc_logic_usb_c {
status = "okay";
};
&cc_logic_usb_c_1 {
status = "okay";
};
+8
View File
@@ -485,6 +485,14 @@
pinctrl-0 = <&ptn5150_pins>;
};
cc_logic_usb_c_1: cclogic@1d {
compatible = "nxp,ptn5150";
reg = <0x1d>;
int-gpio = <&gpio1 7 GPIO_ACTIVE_HIGH>;
vbus-gpio = <&gpio1 24 GPIO_ACTIVE_HIGH>;
pinctrl-names = "default";
pinctrl-0 = <&ptn5150_pins>;
};
};
&i2c3 {
+30 -6
View File
@@ -272,17 +272,41 @@
status = "okay";
adc1: adc@0 {
st,adc-channels = <1 5 13 15>;
st,min-sample-time-nsecs = <10000>;
assigned-resolution-bits = <16>;
status = "okay";
channel@1 {
reg = <1>;
st,min-sample-time-ns = <10000>;
assigned-resolution-bits = <16>;
};
channel@5 {
reg = <5>;
st,min-sample-time-ns = <10000>;
assigned-resolution-bits = <16>;
};
channel@13 {
reg = <13>;
st,min-sample-time-ns = <10000>;
assigned-resolution-bits = <16>;
};
channel@15 {
reg = <15>;
st,min-sample-time-ns = <10000>;
assigned-resolution-bits = <16>;
};
};
adc2: adc@100 {
st,adc-channels = <0 3>;
st,min-sample-time-nsecs = <10000>;
assigned-resolution-bits = <16>;
status = "okay";
channel@0 {
reg = <0>;
st,min-sample-time-ns = <10000>;
assigned-resolution-bits = <16>;
};
channel@3 {
reg = <3>;
st,min-sample-time-ns = <10000>;
assigned-resolution-bits = <16>;
};
};
};
@@ -145,3 +145,18 @@ din_spi@0 {
spi-max-frequency = <5000000>;
};
};
&i2c4 {
cc_logic_usb_c_1: cclogic@1d {
compatible = "nxp,ptn5150";
reg = <0x1d>;
int-gpio = <&gpioh 3 GPIO_ACTIVE_LOW>;
vbus-supply = <&vbus_otg>;
port {
con_usbotg_hs_ep_1: endpoint {
remote-endpoint = <&usbotg_hs_ep>;
};
};
};
};
+40 -12
View File
@@ -10,6 +10,7 @@
#include <dt-bindings/mfd/st,stpmic1.h>
#include <dt-bindings/input/gpio-keys.h>
#include <dt-bindings/input/input.h>
#include <dt-bindings/leds/common.h>
/ {
aliases {
@@ -85,31 +86,45 @@
led_i10: u1-red {
label = "u1-red";
gpios = <&gpioi 10 GPIO_ACTIVE_HIGH>;
color = <LED_COLOR_ID_RED>;
max-brightness = <255>;
default-state = "off";
};
led_j1: u1-green {
label = "u1-green";
gpios = <&gpioj 1 GPIO_ACTIVE_HIGH>;
color = <LED_COLOR_ID_GREEN>;
max-brightness = <255>;
default-state = "off";
};
led_a13: sys-red {
label = "sys-red";
gpios = <&gpioa 13 GPIO_ACTIVE_LOW>;
color = <LED_COLOR_ID_RED>;
max-brightness = <255>;
default-state = "keep";
linux,default-trigger = "timer";
};
led_a14: sys-green {
label = "sys-green";
gpios = <&gpioa 14 GPIO_ACTIVE_LOW>;
color = <LED_COLOR_ID_GREEN>;
max-brightness = <255>;
default-state = "keep";
linux,default-trigger = "timer";
};
led_b12: run-red {
label = "run-red";
gpios = <&gpiob 12 GPIO_ACTIVE_HIGH>;
color = <LED_COLOR_ID_RED>;
max-brightness = <255>;
default-state = "off";
};
led_b0: run-green {
label = "run-green";
gpios = <&gpiob 0 GPIO_ACTIVE_HIGH>;
color = <LED_COLOR_ID_GREEN>;
max-brightness = <255>;
default-state = "off";
};
led_g10: led-mmc {
@@ -671,19 +686,32 @@
};
/ {
multi_color_sys: mc_sys {
compatible = "multi_led";
label = "sys";
leds = <&led_a13 &led_a14>;
};
multi_color_run: mc_run {
compatible = "multi_led";
label = "run";
leds = <&led_b12 &led_b0>;
};
multi_color_u1: mc_u1 {
compatible = "multi_led";
u1_led: u1-led {
compatible = "leds-group-multicolor";
label = "u1";
color = <LED_COLOR_ID_RGB>;
function = LED_FUNCTION_INDICATOR;
default-state = "off";
leds = <&led_i10 &led_j1>;
max-brightness = <255>;
};
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 = <&led_a13 &led_a14>;
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 = <&led_b12 &led_b0>;
max-brightness = <255>;
};
};
@@ -11,15 +11,16 @@
model = "WAGO PFC100 750-8110/8111";
compatible = "wago,am335x-pfc-750_811x-100a", "wago,am335x-pfc", "ti,am33xx";
dip_switch: dip-switch {
/* ti,sn74lv165a 8-bit shifit register */
dip-switch {
pinctrl-names = "default";
pinctrl-0 = <&uart1_dip_pins>;
compatible = "ti,sn74lv165a";
status = "okay";
gpios = <&gpio0 15 GPIO_ACTIVE_HIGH /* clk */
&gpio0 14 GPIO_ACTIVE_LOW /* output */
&gpio0 13 GPIO_ACTIVE_LOW /* load */
>;
dip-clk-gpios = <&gpio0 15 GPIO_ACTIVE_HIGH>;
dip-output-gpios = <&gpio0 14 GPIO_ACTIVE_LOW>;
dip-load-gpios = <&gpio0 13 GPIO_ACTIVE_LOW>;
clk,period = <2>;
};
@@ -98,9 +99,6 @@
};
&am33xx_pinmux {
pinctrl-names = "default";
pinctrl-0 = <&uart1_dip_pins>;
uart1_dip_pins: pinmux_uart1_dip_pins {
pinctrl-single,pins = <
AM33XX_UART1_TXD_GPIO0_15(PIN_OUTPUT_PULLDOWN | SLEWCTRL_SLOW)
@@ -8,6 +8,7 @@ CONFIG_BPF_JIT=y
CONFIG_PREEMPT_RT=y
CONFIG_IKCONFIG=y
CONFIG_IKCONFIG_PROC=y
CONFIG_BLK_DEV_INITRD=y
CONFIG_CGROUPS=y
CONFIG_MEMCG=y
CONFIG_BLK_CGROUP=y
@@ -32,6 +33,7 @@ CONFIG_WAGO_SYSTEM_BASED_STARTUP=y
# CONFIG_SOC_TI81XX is not set
CONFIG_ARM_THUMBEE=y
CONFIG_SWP_EMULATE=y
CONFIG_VDSO=y
CONFIG_ARCH_FORCE_MAX_ORDER=12
CONFIG_UACCESS_WITH_MEMCPY=y
# CONFIG_ATAGS is not set
+1
View File
@@ -29,6 +29,7 @@ CONFIG_SOC_IMX6SL=y
CONFIG_SOC_IMX6SX=y
CONFIG_WAGO_SYSTEM_BASED_STARTUP=y
CONFIG_ARM_THUMBEE=y
CONFIG_VDSO=y
# CONFIG_ARM_ERRATA_643719 is not set
CONFIG_SMP=y
CONFIG_VMSPLIT_2G=y
+1 -2
View File
@@ -568,8 +568,7 @@ CONFIG_DEBUG_FS=y
# CONFIG_SCHED_DEBUG is not set
CONFIG_SCHEDSTATS=y
CONFIG_DEBUG_PREEMPT=y
CONFIG_STACKTRACE=y
# CONFIG_FTRACE is not set
CONFIG_FUNCTION_TRACER=y
CONFIG_UNWINDER_FRAME_POINTER=y
CONFIG_DEBUG_LL=y
CONFIG_EARLY_PRINTK=y
+1 -1
View File
@@ -89,7 +89,7 @@ static int wsysinit_init_probe_dt(struct platform_device *pdev)
struct device_node *np = pdev->dev.of_node;
struct __kernel_timex tmx = {0};
if (of_property_present(np, "dp,rst")) {
if (of_property_present(np, "dp,rst-gpios")) {
wsysinitset.dp_gpio_rst = devm_gpiod_get(&pdev->dev, "dp,rst", GPIOD_OUT_HIGH);
if (IS_ERR(wsysinitset.dp_gpio_rst)) {
dev_err(&pdev->dev,
+12 -1
View File
@@ -24,7 +24,17 @@ dtb-$(CONFIG_ARCH_K3) += k3-am62-lp-sk.dtb
dtb-$(CONFIG_ARCH_K3) += k3-am625-sk-hdmi-audio.dtb
dtb-$(CONFIG_ARCH_K3) += k3-am62-lp-sk-hdmi-audio.dtb
dtb-$(CONFIG_ARCH_K3) += k3-am623-pfc-750-8302.dtb
dtb-$(CONFIG_ARCH_K3) += k3-am623-pfc-ems.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) += 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
@@ -85,3 +95,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 += -@
+165
View File
@@ -0,0 +1,165 @@
/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";
};
};
};
+13
View File
@@ -171,4 +171,17 @@
bosch,mram-cfg = <0x0 128 64 64 64 64 32 32>;
status = "disabled";
};
mcu_m4fss: m4fss@5000000 {
compatible = "ti,am64-m4fss";
reg = <0x00 0x5000000 0x00 0x30000>,
<0x00 0x5040000 0x00 0x10000>;
reg-names = "iram", "dram";
resets = <&k3_reset 9 1>;
firmware-name = "am62-mcu-m4f0_0-fw";
ti,sci = <&dmsc>;
ti,sci-dev-id = <9>;
ti,sci-proc-ids = <0x18 0xff>;
status = "disabled";
};
};
+284 -19
View File
@@ -6,35 +6,232 @@
* Copyright (C) 2024 WAGO GmbH & Co. KG - https://www.wago.com/
*/
#include "k3-am62x-pfc.dtsi"
#include "k3-am623-pfc-750-8xxx-common.dtsi"
#include "k3-am623-pfc-750-830x-common.dtsi"
#include "k3-am623-pfc-kbus.dtsi"
#include "k3-am623-pfc-rs485.dtsi"
#include "k3-am623-pfc-tpm.dtsi"
#include "k3-am623-pfc-rtc.dtsi"
/ {
model = "WAGO PFC300 750-8302";
compatible = "ti,am623-pfc", "ti,am625";
model = "PFC300-750-8302";
compatible = "wago,am623-pfc-750_8302-100c", "wago,am623-pfc", "ti,am625";
cpus {
cpu-map {
cluster0 {
/delete-node/ core2;
/delete-node/ core3;
};
};
aliases {
ethernet0 = &cpsw_port1;
ethernet1 = &cpsw_port2;
};
cpu@0 {
cpu-supply = <&buck1_30_reg>;
};
cpus {
cpu-map {
cluster0 {
/delete-node/ core2;
/delete-node/ core3;
};
};
cpu@1 {
cpu-supply = <&buck1_30_reg>;
};
cpu@0 {
cpu-supply = <&buck1_30_reg>;
};
/delete-node/ cpu@2;
/delete-node/ cpu@3;
};
cpu@1 {
cpu-supply = <&buck1_30_reg>;
};
/delete-node/ cpu@2;
/delete-node/ cpu@3;
};
memory@80000000 {
device_type = "memory";
/* 2G RAM */
reg = <0x00000000 0x80000000 0x00000000 0x80000000>;
};
/* 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
* need to adapt this configuration
*/
oms: PAC-Operating-Mode-Switch {
compatible = "gpio-keys";
pinctrl-names = "default";
pinctrl-0 = <&oms_pins_default>;
#address-cells = <1>;
#size-cells = <0>;
autorepeat;
oms_run: run {
label = "RUN";
gpios = <&main_gpio0 65 GPIO_ACTIVE_LOW>;
linux,code = <1>;
linux,input-type = <5>;
debounce-interval = <1>;
};
oms_stop: stop {
label = "STOP";
gpios = <&main_gpio1 10 GPIO_ACTIVE_LOW>;
linux,code = <2>;
linux,input-type = <5>;
debounce-interval = <1>;
};
oms_reset: reset {
label = "RESET";
gpios = <&main_gpio1 12 GPIO_ACTIVE_LOW>;
linux,code = <3>;
linux,input-type = <1>;
debounce-interval = <1>;
};
oms_reset_all: reset_all {
label = "RESET_ALL";
gpios = <&main_gpio1 11 GPIO_ACTIVE_HIGH>;
linux,code = <4>;
linux,input-type = <1>;
debounce-interval = <1>;
};
};
uio_nvram: nvram@51000000 {
compatible = "uio_pdrv_genirq";
linux,uio-name = "UIO_NVRAM";
reg = <0 0x51000000 0 0x00020000>; /* 128k 16bit NVRAM */
};
vdd_mmc1: regulator-3 {
compatible = "regulator-fixed";
pinctrl-names = "default";
pinctrl-0 = <&vdd_sd_dv_pins_default>;
regulator-name = "vdd_mmc1";
regulator-min-microvolt = <3300000>;
regulator-max-microvolt = <3300000>;
regulator-boot-on;
vin-supply = <&vcc_3v3_sys>;
gpio = <&main_gpio0 31 GPIO_ACTIVE_HIGH>;
enable-active-high;
startup-delay-us = <5000>;
};
};
&main_pmx0 {
gpmc_data_pins_default: gpmc-data-default-pins {
pinctrl-single,pins = <
AM62X_IOPAD(0x00bc, PIN_OUTPUT, 1) /* (V24) VOUT0_DATA1.GPMC0_A1 */
AM62X_IOPAD(0x00c0, PIN_OUTPUT, 1) /* (W25) VOUT0_DATA2.GPMC0_A2 */
AM62X_IOPAD(0x00c4, PIN_OUTPUT, 1) /* (W24) VOUT0_DATA3.GPMC0_A3 */
AM62X_IOPAD(0x00c8, PIN_OUTPUT, 1) /* (Y25) VOUT0_DATA4.GPMC0_A4 */
AM62X_IOPAD(0x00cc, PIN_OUTPUT, 1) /* (Y24) VOUT0_DATA5.GPMC0_A5 */
AM62X_IOPAD(0x00d0, PIN_OUTPUT, 1) /* (Y23) VOUT0_DATA6.GPMC0_A6 */
AM62X_IOPAD(0x00d4, PIN_OUTPUT, 1) /* (AA25) VOUT0_DATA7.GPMC0_A7 */
AM62X_IOPAD(0x00d8, PIN_OUTPUT, 1) /* (V21) VOUT0_DATA8.GPMC0_A8 */
AM62X_IOPAD(0x00dc, PIN_OUTPUT, 1) /* (W21) VOUT0_DATA9.GPMC0_A9 */
AM62X_IOPAD(0x00e0, PIN_OUTPUT, 1) /* (V20) VOUT0_DATA10.GPMC0_A10 */
AM62X_IOPAD(0x00e4, PIN_OUTPUT, 1) /* (AA23) VOUT0_DATA11.GPMC0_A11 */
AM62X_IOPAD(0x00e8, PIN_OUTPUT, 1) /* (AB25) VOUT0_DATA12.GPMC0_A12 */
AM62X_IOPAD(0x00ec, PIN_OUTPUT, 1) /* (AA24) VOUT0_DATA13.GPMC0_A13 */
AM62X_IOPAD(0x00f0, PIN_OUTPUT, 1) /* (Y22) VOUT0_DATA14.GPMC0_A14 */
AM62X_IOPAD(0x00f4, PIN_OUTPUT, 1) /* (AA21) VOUT0_DATA15.GPMC0_A15 */
AM62X_IOPAD(0x00f8, PIN_OUTPUT, 1) /* (AB24) VOUT0_HSYNC.GPMC0_A16 */
>;
};
gpmc_addr_pins_default: gpmc-addr-default-pins {
pinctrl-single,pins = <
AM62X_IOPAD(0x003c, PIN_INPUT, 0) /* (M25) GPMC0_AD0 */
AM62X_IOPAD(0x0040, PIN_INPUT, 0) /* (N23) GPMC0_AD1 */
AM62X_IOPAD(0x0044, PIN_INPUT, 0) /* (N24) GPMC0_AD2 */
AM62X_IOPAD(0x0048, PIN_INPUT, 0) /* (N25) GPMC0_AD3 */
AM62X_IOPAD(0x004c, PIN_INPUT, 0) /* (P24) GPMC0_AD4 */
AM62X_IOPAD(0x0050, PIN_INPUT, 0) /* (P22) GPMC0_AD5 */
AM62X_IOPAD(0x0054, PIN_INPUT, 0) /* (P21) GPMC0_AD6 */
AM62X_IOPAD(0x0058, PIN_INPUT, 0) /* (R23) GPMC0_AD7 */
AM62X_IOPAD(0x005c, PIN_INPUT, 0) /* (R24) GPMC0_AD8 */
AM62X_IOPAD(0x0060, PIN_INPUT, 0) /* (R25) GPMC0_AD9 */
AM62X_IOPAD(0x0064, PIN_INPUT, 0) /* (T25) GPMC0_AD10 */
AM62X_IOPAD(0x0068, PIN_INPUT, 0) /* (R21) GPMC0_AD11 */
AM62X_IOPAD(0x006c, PIN_INPUT, 0) /* (T22) GPMC0_AD12 */
AM62X_IOPAD(0x0070, PIN_INPUT, 0) /* (T24) GPMC0_AD13 */
AM62X_IOPAD(0x0074, PIN_INPUT, 0) /* (U25) GPMC0_AD14 */
AM62X_IOPAD(0x0078, PIN_INPUT, 0) /* (U24) GPMC0_AD15 */
>;
};
gpmc_ctrl_pins_default: gpmc-ctrl-default-pins {
pinctrl-single,pins = <
AM62X_IOPAD(0x0094, PIN_OUTPUT, 0) /* (N20) GPMC0_BE1n */
AM62X_IOPAD(0x00a8, PIN_OUTPUT, 0) /* (M21) GPMC0_CSn0 */
AM62X_IOPAD(0x0088, PIN_OUTPUT, 0) /* (L24) GPMC0_OEn_REn */
AM62X_IOPAD(0x008c, PIN_OUTPUT, 0) /* (L25) GPMC0_WEn */
AM62X_IOPAD(0x0090, PIN_OUTPUT, 0) /* (M24) GPMC0_BE0n_CLE */
>;
};
main_mdio1_pins_default: main-mdio1-default-pins {
pinctrl-single,pins = <
AM62X_IOPAD(0x160, PIN_OUTPUT, 0) /* (AD24) MDIO0_MDC */
AM62X_IOPAD(0x15c, PIN_INPUT_PULLUP, 0) /* (AB22) MDIO0_MDIO */
AM62X_IOPAD(0x019c, PIN_OUTPUT, 7) /* (B18) MCASP0_AXR1.GPIO1_9 */
>;
};
main_rgmii1_pins_default: main-rgmii1-default-pins {
pinctrl-single,pins = <
AM62X_IOPAD(0x14c, PIN_INPUT, 0) /* (AB17) RGMII1_RD0 */
AM62X_IOPAD(0x150, PIN_INPUT, 0) /* (AC17) RGMII1_RD1 */
AM62X_IOPAD(0x154, PIN_INPUT, 0) /* (AB16) RGMII1_RD2 */
AM62X_IOPAD(0x158, PIN_INPUT, 0) /* (AA15) RGMII1_RD3 */
AM62X_IOPAD(0x148, PIN_INPUT, 0) /* (AD17) RGMII1_RXC */
AM62X_IOPAD(0x144, PIN_INPUT, 0) /* (AE17) RGMII1_RX_CTL */
AM62X_IOPAD(0x134, PIN_OUTPUT, 0) /* (AE20) RGMII1_TD0 */
AM62X_IOPAD(0x138, PIN_OUTPUT, 0) /* (AD20) RGMII1_TD1 */
AM62X_IOPAD(0x13c, PIN_OUTPUT, 0) /* (AE18) RGMII1_TD2 */
AM62X_IOPAD(0x140, PIN_OUTPUT, 0) /* (AD18) RGMII1_TD3 */
AM62X_IOPAD(0x130, PIN_OUTPUT, 0) /* (AE19) RGMII1_TXC */
AM62X_IOPAD(0x12c, PIN_OUTPUT, 0) /* (AD19) RGMII1_TX_CTL */
AM62X_IOPAD(0x0030, PIN_INPUT, 7) /* (G21) OSPI0_CSn1.GPIO0_12 nINT-RGMII1 */
>;
};
main_rgmii2_pins_default: main-rgmii2-default-pins {
pinctrl-single,pins = <
AM62X_IOPAD(0x184, PIN_INPUT, 0) /* (AE23) RGMII2_RD0 */
AM62X_IOPAD(0x188, PIN_INPUT, 0) /* (AB20) RGMII2_RD1 */
AM62X_IOPAD(0x18c, PIN_INPUT, 0) /* (AC21) RGMII2_RD2 */
AM62X_IOPAD(0x190, PIN_INPUT, 0) /* (AE22) RGMII2_RD3 */
AM62X_IOPAD(0x180, PIN_INPUT, 0) /* (AD23) RGMII2_RXC */
AM62X_IOPAD(0x17c, PIN_INPUT, 0) /* (AD22) RGMII2_RX_CTL */
AM62X_IOPAD(0x16c, PIN_OUTPUT, 0) /* (Y18) RGMII2_TD0 */
AM62X_IOPAD(0x170, PIN_OUTPUT, 0) /* (AA18) RGMII2_TD1 */
AM62X_IOPAD(0x174, PIN_OUTPUT, 0) /* (AD21) RGMII2_TD2 */
AM62X_IOPAD(0x178, PIN_OUTPUT, 0) /* (AC20) RGMII2_TD3 */
AM62X_IOPAD(0x168, PIN_OUTPUT, 0) /* (AE21) RGMII2_TXC */
AM62X_IOPAD(0x164, PIN_OUTPUT, 0) /* (AA19) RGMII2_TX_CTL */
AM62X_IOPAD(0x0034, PIN_INPUT, 7) /* (H21) OSPI0_CSn2.GPIO0_13 */
>;
};
vdd_sd_dv_pins_default: vdd-sd-dv-default-pins {
pinctrl-single,pins = <
AM62X_IOPAD(0x07c, PIN_OUTPUT, 7) /* (P25) GPMC0_CLK.GPIO0_31 */
>;
};
oms_pins_default: oms-default-pins {
pinctrl-single,pins = <
AM62X_IOPAD(0x0108, PIN_INPUT, 7) /* (D24) MMC2_DAT3.GPIO0_65 */
AM62X_IOPAD(0x01a0, PIN_INPUT, 7) /* (E18) MCASP0_AXR0.GPIO1_10 */
AM62X_IOPAD(0x01a8, PIN_INPUT, 7) /* (D20) MCASP0_AFSX.GPIO1_12 */
AM62X_IOPAD(0x01a4, PIN_INPUT, 7) /* (B20) MCASP0_ACLKX.GPIO1_11 */
>;
};
};
&wsysinit {
status = "okay";
board,variant = "pfc300";
};
&oms {
@@ -51,6 +248,42 @@
&gpmc0 {
status = "okay";
pinctrl-names = "default";
pinctrl-0 = <&gpmc_data_pins_default>, <&gpmc_addr_pins_default>, <&gpmc_ctrl_pins_default>;
num-cs = <4>;
num-waitpins = <4>;
ranges = <
0 0 0x00 0x51000000 0x01000000 /* CS0: NVRAM, 16M */
>;
nvram: nor@0,0 { /* NVRAM Device 128k */
reg = <0 0 0x01000000>;
bank-width = <2>; /* 1: 8bit, 2: 16bit */
gpmc,sync-clk-ps = <0>;
gpmc,cs-on-ns = <0>;
gpmc,cs-rd-off-ns = <50>;
gpmc,cs-wr-off-ns = <30>;
gpmc,adv-on-ns = <0>;
gpmc,adv-rd-off-ns = <0>;
gpmc,adv-wr-off-ns = <0>;
gpmc,oe-on-ns = <0>;
gpmc,oe-off-ns = <50>;
gpmc,we-on-ns = <0>;
gpmc,we-off-ns = <30>;
gpmc,rd-cycle-ns = <50>;
gpmc,wr-cycle-ns = <30>;
gpmc,access-ns = <40>;
gpmc,wr-access-ns = <10>;
gpmc,wr-data-mux-bus-ns = <0>;
gpmc,cycle2cycle-delay-ns = <10>;
gpmc,cycle2cycle-samecsen;
gpmc,cycle2cycle-diffcsen;
#address-cells = <1>;
#size-cells = <1>;
};
};
&main_uart0 {
@@ -87,22 +320,54 @@
&sdhci1 { /* SD */
status = "okay";
vmmc-supply = <&vdd_mmc1>;
cap-sd-highspeed;
no-1-8-v;
};
&cpsw3g {
status = "okay";
pinctrl-names = "default";
pinctrl-0 = <&main_mdio1_pins_default
&main_rgmii1_pins_default
&main_rgmii2_pins_default>;
};
&cpsw_port1 {
status = "okay";
phy-mode = "rgmii-rxid";
phy-handle = <&cpsw3g_phy0>;
};
&cpsw_port2 {
status = "okay";
phy-mode = "rgmii-rxid";
phy-handle = <&cpsw3g_phy1>;
};
&cpsw3g_mdio {
status = "okay";
reset-gpios = <&main_gpio1 9 GPIO_ACTIVE_LOW>;
reset-delay-us = <10>;
reset-post-delay-us = <10000>;
cpsw3g_phy0: ethernet-phy@0 {
// ADIN1300
compatible = "ethernet-phy-id0283.bc30";
reg = <0>;
adi,rx-internal-delay-ps = <2000>;
};
cpsw3g_phy1: ethernet-phy@1 {
// ADIN1300
compatible = "ethernet-phy-id0283.bc30";
reg = <1>;
adi,rx-internal-delay-ps = <2000>;
};
};
&usbss0 {
@@ -0,0 +1,68 @@
// SPDX-License-Identifier: GPL-2.0
&main_i2c0 {
#include "k3-am623-pfc-750-830x-leds.dtsi"
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_3v3_sys>;
ldo3-supply = <&vcc_5v0>;
ldo4-supply = <&vcc_5v0>;
pinctrl-names = "default";
pinctrl-0 = <&pmic_irq_pins_default>;
interrupt-parent = <&main_gpio0>;
interrupts = <68 IRQ_TYPE_EDGE_FALLING>;
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 = "VCC1V8_SYS";
regulator-min-microvolt = <1800000>;
regulator-max-microvolt = <1800000>;
regulator-boot-on;
regulator-always-on;
};
buck3_30_reg: buck3 {
regulator-name = "VCC3V3_SYS";
regulator-min-microvolt = <3300000>;
regulator-max-microvolt = <3300000>;
regulator-boot-on;
regulator-always-on;
};
ldo1_30_reg: ldo1 {
regulator-name = "VCC3V3_SDC";
regulator-min-microvolt = <3300000>;
regulator-max-microvolt = <3300000>;
};
ldo2_30_reg: ldo2 {
regulator-name = "VDDA_1V8";
regulator-min-microvolt = <1800000>;
regulator-max-microvolt = <1800000>;
regulator-boot-on;
regulator-always-on;
};
};
};
};
@@ -6,7 +6,8 @@
* Copyright (C) 2024 WAGO GmbH & Co. KG - https://www.wago.com/
*/
#include "k3-am62x-pfc.dtsi"
#include "k3-am623-pfc-750-8xxx-common.dtsi"
#include "k3-am623-pfc-750-830x-common.dtsi"
/ {
model = "WAGO AM623 PFC EMS";
@@ -460,14 +461,6 @@
status = "okay";
};
&oms {
status = "disabled";
};
&nvram {
status = "okay";
};
&watchdog_ext {
status = "okay";
};
@@ -509,9 +502,6 @@
status = "disabled";
};
/delete-node/ &vdd_mmc1;
/delete-node/ &vdd_sd_dv_pins_default;
&cpsw3g {
status = "disabled";
};
@@ -0,0 +1,143 @@
// SPDX-License-Identifier: GPL-2.0
led_bar60: pca9552@60 {
compatible = "nxp,pca9552";
#address-cells = <1>;
#size-cells = <0>;
reg = <0x60>;
lb60_0: sys-red@0 {
label = "sys-red";
reg = <0>;
linux,default-trigger = "timer";
};
lb60_1: sys-green@1 {
label = "sys-green";
reg = <1>;
linux,default-trigger = "timer";
};
lb60_2: run-red@2 {
label = "run-red";
reg = <2>;
};
lb60_3: run-green@3 {
label = "run-green";
reg = <3>;
};
lb60_4: io-red@4 {
label = "io-red";
reg = <4>;
};
lb60_5: io-green@5 {
label = "io-green";
reg = <5>;
};
lb60_6: ms-red@6 {
label = "ms-red";
reg = <6>;
};
lb60_7: ms-green@7 {
label = "ms-green";
reg = <7>;
};
lb60_8: ns-red@8 {
label = "ns-red";
reg = <8>;
};
lb60_9: ns-green@9 {
label = "ns-green";
reg = <9>;
};
lb60_10: usr_r@10 {
label = "trm-red";
reg = <10>;
};
lb60_11: usr_g@11 {
label = "trm-green";
reg = <11>;
};
lb60_15: sys-overwrite@15 {
label = "sys-overwrite";
reg = <15>;
linux,default-trigger = "default-on";
};
};
led_bar61: pca9552@61 {
compatible = "nxp,pca9552";
#address-cells = <1>;
#size-cells = <0>;
reg = <0x61>;
lb61_0: u6-red@0 {
label = "u6-red";
reg = <0>;
};
lb61_1: u6-green@1 {
label = "u6-green";
reg = <1>;
};
lb61_2: u5-red@2 {
label = "u5-red";
reg = <2>;
};
lb61_3: u5-green@3 {
label = "u5-green";
reg = <3>;
};
lb61_4: u4-red@4 {
label = "u4-red";
reg = <4>;
};
lb61_5: u4-green@5 {
label = "u4-green";
reg = <5>;
};
lb61_6: u3-red@6 {
label = "u3-red";
reg = <6>;
};
lb61_7: u3-green@7 {
label = "u3-green";
reg = <7>;
};
lb61_8: u2-red@8 {
label = "u2-red";
reg = <8>;
};
lb61_9: u2-green@9 {
label = "u2-green";
reg = <9>;
};
lb61_10: u1-red@10 {
label = "u1-red";
reg = <10>;
};
lb61_11: u1-green@11 {
label = "u1-green";
reg = <11>;
};
};
@@ -0,0 +1,96 @@
// SPDX-License-Identifier: GPL-2.0
/*
* PFC400 - AM62x based WAGO device
*
* Copyright (C) 2021-2022 Texas Instruments Incorporated - https://www.ti.com/
* Copyright (C) 2025 WAGO GmbH & Co. KG - https://www.wago.com/
*/
/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 {
compatible = "wago,m4-led-wrapper";
remoteproc = <&mcu_m4fss>;
firmware-name = "wago-led-server-app.elf";
wago,reset-on-init;
#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";
};
};
};
&wsysinit {
status = "okay";
board,variant = "pfc400";
};
&slb9670 {
status = "okay";
};
&main_spi2 {
ti,spi-num-cs = <4>;
spidev@3 {
compatible = "rohm,dh2228fv";
reg = <3>;
spi-max-frequency = <50000000>;
};
};
@@ -0,0 +1,103 @@
// SPDX-License-Identifier: GPL-2.0
/*
* PFC400 baseboard dtsi for AM62x based WAGO devices
*
* Copyright (C) 2021-2022 Texas Instruments Incorporated - https://www.ti.com/
* Copyright (C) 2025 WAGO GmbH & Co. KG - https://www.wago.com/
*/
&main_pmx0 {
main_ksz9477_pins: main-ksz9477-pins {
pinctrl-single,pins = <
AM62X_IOPAD(0x0034, PIN_INPUT, 7) /* (H21) OSPI0_CSn2.GPIO0_13 nINT-RGMII2 / nINT-ETHSW(BB) */
>;
};
};
&mcu_pmx0 {
mcu_ksz9477_pins: mcu-ksz9477-pins {
pinctrl-single,pins = <
AM62X_MCU_IOPAD(0x002c, PIN_OUTPUT, 7) /* (C6) WKUP_UART0_CTSn.MCU_GPIO0_11 nRST-RGMII2 / nRST-ETHSW(BB) */
>;
};
};
&mcu_spi0 {
#address-cells = <1>;
#size-cells = <0>;
ksz9477@0 {
compatible = "microchip,ksz9477";
pinctrl-names = "default";
pinctrl-0 = <&main_ksz9477_pins>, <&mcu_ksz9477_pins>;
reg = <0>;
spi-max-frequency = <1000000>;
reset-gpios = <&mcu_gpio0 11 GPIO_ACTIVE_LOW>;
dsa,member = <0 0>;
interrupt-parent = <&main_gpio0>;
interrupts = <13 IRQ_TYPE_EDGE_FALLING>;
ports {
#address-cells = <1>;
#size-cells = <0>;
port@0 {
reg = <0>;
label = "ethX1";
phy-mode = "internal";
};
port@1 {
reg = <1>;
label = "ethX2";
phy-mode = "internal";
};
port@2 {
reg = <2>;
label = "ethX3";
phy-mode = "internal";
};
port@3 {
reg = <3>;
label = "ethX4";
phy-mode = "internal";
};
port@4 {
reg = <4>;
label = "ethEM";
phy-mode = "internal";
};
port@5 {
reg = <5>;
label = "cpu";
ethernet = <&cpsw3g>; /* &eth0 */
phy-mode = "rgmii-txid";
tx-internal-delay-ps = <2000>;
fixed-link {
speed = <1000>;
full-duplex;
};
};
};
};
};
&main_i2c1 {
#address-cells = <1>;
#size-cells = <0>;
eeprom_hw_id_bb: at24c02@54 {
status = "disabled";
compatible = "atmel,24c02";
reg = <0x51>;
pagesize = <16>;
};
};
@@ -0,0 +1,398 @@
// SPDX-License-Identifier: GPL-2.0
/*
* PFC400 interaction board dtsi for AM62x based WAGO devices
*
* Copyright (C) 2021-2022 Texas Instruments Incorporated - https://www.ti.com/
* Copyright (C) 2025 WAGO GmbH & Co. KG - https://www.wago.com/
*/
#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 {
status = "disabled";
compatible = "atmel,24c02";
reg = <0x54>;
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
*
* This driver is not yet upstream but there is a kernel driver available at:
*
* https://github.com/2pecshy/eeprom-ST25DV-linux-driver/tree/master
*
*/
st25dv04k: st25dv04k@53 {
status = "okay";
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>;
};
};
@@ -0,0 +1,83 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Common dtsi for AM62x WAGO based devices
*
* Copyright (C) 2021-2022 Texas Instruments Incorporated - https://www.ti.com/
* Copyright (C) 2025 WAGO GmbH & Co. KG - https://www.wago.com/
*/
#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
* need to adapt this configuration
*/
oms: PAC-Operating-Mode-Switch {
status = "okay";
compatible = "gpio-keys";
pinctrl-names = "default";
pinctrl-0 = <&oms_pins_default>;
#address-cells = <1>;
#size-cells = <0>;
autorepeat;
oms_stop_run: stop_run {
label = "RUN";
gpios = <&main_gpio0 65 GPIO_ACTIVE_LOW>;
linux,code = <1>;
linux,input-type = <5>;
debounce-interval = <1>;
};
oms_reset_all: reset_all {
label = "RESET_ALL";
gpios = <&main_gpio1 11 GPIO_ACTIVE_HIGH>;
linux,code = <4>;
linux,input-type = <1>;
debounce-interval = <1>;
};
};
leds {
compatible = "gpio-leds";
pinctrl-0 = <&sd_activity_led_pin>;
pinctrl-names = "default";
led-0 {
function = LED_FUNCTION_SD;
color = <LED_COLOR_ID_ORANGE>;
gpios = <&main_gpio0 31 GPIO_ACTIVE_LOW>;
linux,default-trigger = "mmc0";
default-state = "on";
};
};
};
&main_pmx0 {
oms_pins_default: oms-default-pins {
pinctrl-single,pins = <
AM62X_IOPAD(0x0108, PIN_INPUT, 7) /* (D24) MMC2_DAT3.GPIO0_65 nSTOP-RUN */
AM62X_IOPAD(0x01a4, PIN_INPUT, 7) /* (B20) MCASP0_ACLKX.GPIO1_11 IO-RAB */
>;
};
sd_activity_led_pin: sd-activity-led-pin {
pinctrl-single,pins = <
AM62X_IOPAD(0x07c, PIN_OUTPUT, 7) /* (P25) GPMC0_CLK.GPIO0_31 (MMC1.PWR / SDC.LED) */
>;
};
};
&main_i2c1 {
#address-cells = <1>;
#size-cells = <0>;
eeprom_hw_id_lb: at24c02@50 {
status = "disabled";
compatible = "atmel,24c02";
reg = <0x50>;
pagesize = <16>;
};
};
@@ -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;
};
};
};
};
@@ -0,0 +1,294 @@
// SPDX-License-Identifier: GPL-2.0
/*
* PFC400 WOSM dtsi for AM62x based WAGO devices
*
* Copyright (C) 2021-2022 Texas Instruments Incorporated - https://www.ti.com/
* Copyright (C) 2025 WAGO GmbH & Co. KG - https://www.wago.com/
*/
&{/} {
compatible = "ti,am623-pfc", "ti,am625";
#address-cells = <2>;
#size-cells = <2>;
cpus {
cpu@0 {
cpu-supply = <&buck1_30_reg>;
};
cpu@1 {
cpu-supply = <&buck1_30_reg>;
};
cpu@2 {
cpu-supply = <&buck1_30_reg>;
};
cpu@3 {
cpu-supply = <&buck1_30_reg>;
};
};
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>;
};
};
&fss {
#address-cells = <2>;
#size-cells = <2>;
uio_mram: uio@500000000 {
compatible = "uio_pdrv_genirq";
linux,uio-name = "UIO_OSPI_MRAM";
/*
* FSS Memory Region: External Memory Space (Region 3)
* OSPI EVERSPIN MRAM em008lxo (8Mb)
*
*/
reg = <0x05 0x00000000 0x00 0x00100000>;
};
};
&mcu_pmx0 {
mcu_spi0_pins: mcu-spi0-pins {
pinctrl-single,pins = <
AM64X_MCU_IOPAD(0x008, PIN_OUTPUT, 0) /* (A7) MCU_SPI0_CLK SCLK */
AM64X_MCU_IOPAD(0x000, PIN_OUTPUT, 0) /* (E8) MCU_SPI0_CS0 nCS0 */
AM64X_MCU_IOPAD(0x00c, PIN_INPUT, 0) /* (D9) MCU_SPI0_D0 MISO */
AM64X_MCU_IOPAD(0x010, PIN_OUTPUT, 0) /* (C9) MCU_SPI0_D1 MOSI */
>;
};
};
&main_pmx0 {
main_mdio1_pins_default: main-mdio1-default-pins {
pinctrl-single,pins = <
AM62X_IOPAD(0x160, PIN_OUTPUT, 0) /* (AD24) MDIO0_MDC not used, just for testing */
AM62X_IOPAD(0x15c, PIN_INPUT_PULLUP, 0) /* (AB22) MDIO0_MDIO not used, just for testing */
>;
};
main_rgmii2_pins_default: main-rgmii2-default-pins {
pinctrl-single,pins = <
AM62X_IOPAD(0x184, PIN_INPUT, 0) /* (AE23) RGMII2_RD0 */
AM62X_IOPAD(0x188, PIN_INPUT, 0) /* (AB20) RGMII2_RD1 */
AM62X_IOPAD(0x18c, PIN_INPUT, 0) /* (AC21) RGMII2_RD2 */
AM62X_IOPAD(0x190, PIN_INPUT, 0) /* (AE22) RGMII2_RD3 */
AM62X_IOPAD(0x180, PIN_INPUT, 0) /* (AD23) RGMII2_RXC */
AM62X_IOPAD(0x17c, PIN_INPUT, 0) /* (AD22) RGMII2_RX_CTL */
AM62X_IOPAD(0x16c, PIN_OUTPUT, 0) /* (Y18) RGMII2_TD0 */
AM62X_IOPAD(0x170, PIN_OUTPUT, 0) /* (AA18) RGMII2_TD1 */
AM62X_IOPAD(0x174, PIN_OUTPUT, 0) /* (AD21) RGMII2_TD2 */
AM62X_IOPAD(0x178, PIN_OUTPUT, 0) /* (AC20) RGMII2_TD3 */
AM62X_IOPAD(0x168, PIN_OUTPUT, 0) /* (AE21) RGMII2_TXC */
AM62X_IOPAD(0x164, PIN_OUTPUT, 0) /* (AA19) RGMII2_TX_CTL */
>;
};
usb1_pins_default: usb1-default-pins {
pinctrl-single,pins = <
AM62X_IOPAD(0x0258, PIN_INPUT, 0) /* (F18) USB1_DRVVBUS */
>;
};
mram_irq_pins_default: mram-irq-default-pins {
pinctrl-single,pins = <
AM62X_IOPAD(0x0038, PIN_INPUT, 7) /* (E24) OSPI0_CSn3.GPIO0_14 (nINT-nVMEM) */
>;
};
ospi0_pins_default: ospi0-default-pins {
pinctrl-single,pins = <
AM62X_IOPAD(0x000, PIN_OUTPUT, 0) /* (H24) OSPI0_CLK */
AM62X_IOPAD(0x02c, PIN_OUTPUT, 0) /* (F23) OSPI0_CSn0 */
AM62X_IOPAD(0x00c, PIN_INPUT, 0) /* (E25) OSPI0_D0 */
AM62X_IOPAD(0x010, PIN_INPUT, 0) /* (G24) OSPI0_D1 */
AM62X_IOPAD(0x014, PIN_INPUT, 0) /* (F25) OSPI0_D2 */
AM62X_IOPAD(0x018, PIN_INPUT, 0) /* (F24) OSPI0_D3 */
AM62X_IOPAD(0x01c, PIN_INPUT, 0) /* (J23) OSPI0_D4 */
AM62X_IOPAD(0x020, PIN_INPUT, 0) /* (J25) OSPI0_D5 */
AM62X_IOPAD(0x024, PIN_INPUT, 0) /* (H25) OSPI0_D6 */
AM62X_IOPAD(0x028, PIN_INPUT, 0) /* (J22) OSPI0_D7 */
AM62X_IOPAD(0x008, PIN_INPUT, 0) /* (J24) OSPI0_DQS */
>;
};
};
&watchdog_ext {
status = "okay";
};
&gpmc0 {
status = "disabled";
};
&mcu_spi0 {
status = "okay";
pinctrl-names = "default";
pinctrl-0 = <&mcu_spi0_pins>;
};
&main_spi0 {
status = "okay";
};
&main_spi2 {
status = "okay";
};
&ospi0 {
#address-cells = <1>;
#size-cells = <0>;
compatible = "ti,am654-ospi";
pinctrl-names = "default";
pinctrl-0 = <&ospi0_pins_default>;
status = "okay";
/* Setup the clock for 200 MHz (400 MHz Ref / 2) */
assigned-clock-rates = <400000000>;
/* Everspin Tech. EM008LXO
* Order-No EM008LXOAB320IS1R
*/
mram: mram@0 {
pinctrl-names = "default";
pinctrl-0 = <&mram_irq_pins_default>;
interrupt-parent = <&main_gpio0>;
interrupts = <14 IRQ_TYPE_EDGE_FALLING>;
compatible = "jedec,spi-nor";
reg = <0x0>;
/* 50 MHz for Startup */
spi-max-frequency = <50000000>;
/* 200 MHz needs a very precise sampling point.
* Start with 4 or 5 since 125MHz already needed 3.
*/
cdns,read-delay = <5>;
/* Tighten timings for 200 MHz (T=5ns) */
cdns,tshsl-ns = <200>; /* CS# high pulse width (increased for stability) */
cdns,tsd2d-ns = <200>; /* Delay between back-to-back transfers */
cdns,tchsh-ns = <3>; /* CS# hold time */
cdns,tslch-ns = <3>; /* CS# setup time */
spi-rx-bus-width = <8>;
spi-tx-bus-width = <8>;
partitions {
compatible = "fixed-partitions";
#address-cells = <1>;
#size-cells = <1>;
partition@0 {
label = "ospi.mram.mem";
reg = <0x0 0x100000>; /* 1MB total capacity */
};
};
};
};
&main_i2c0 {
#address-cells = <1>;
#size-cells = <0>;
status = "okay";
eeprom_hw_id_wosm: at24c02@50 {
status = "disabled";
compatible = "atmel,24c02";
reg = <0x50>;
pagesize = <16>;
};
};
&main_i2c1 {
status = "okay";
};
&sdhci0 { /* eMMC */
status = "okay";
};
&sdhci1 { /* SD */
status = "okay";
vmmc-supply = <&vcc_3v3_sys>;
vqmmc-supply = <&ldo1_sd_1v8_reg>;
};
&cpsw3g {
status = "okay";
pinctrl-names = "default";
pinctrl-0 = <&main_mdio1_pins_default
&main_rgmii2_pins_default>;
};
&cpsw_port1 {
status = "disabled";
};
/*
* Use a fixed phy for emac2.
* that's rgmii2
*/
&cpsw_port2 {
status = "okay";
phy-mode = "rgmii-rxid";
fixed-link {
speed = <1000>;
full-duplex;
};
};
&cpsw3g_mdio {
status = "disabled";
};
&usbss1 {
status = "okay";
ti,vbus-divider;
};
&usb1 {
status = "okay";
dr_mode = "host";
pinctrl-names = "default";
pinctrl-0 = <&usb1_pins_default>;
};
@@ -0,0 +1,411 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Common dtsi for AM62x based WAGO devices
*
* Copyright (C) 2021-2022 Texas Instruments Incorporated - https://www.ti.com/
*
* Copyright (C) 2025 WAGO GmbH & Co. KG <http://global.wago.com>
*/
#include <dt-bindings/leds/common.h>
#include <dt-bindings/gpio/gpio.h>
#include "k3-am625.dtsi"
/ {
aliases {
serial2 = &main_uart0;
serial1 = &main_uart2;
serial4 = &main_uart4;
mmc0 = &sdhci1;
mmc1 = &sdhci0;
ethernet0 = &cpsw_port1;
ethernet1 = &cpsw_port2;
usb0 = &usb0;
watchdog0 = &main_rti0;
watchdog1 = &watchdog_ext;
watchdog2 = &main_rti1;
};
opp-table {
opp-200000000 {
opp-microvolt = <850000>;
};
opp-400000000 {
opp-microvolt = <850000>;
};
opp-600000000 {
opp-microvolt = <850000>;
};
opp-800000000 {
opp-microvolt = <850000>;
};
opp-1000000000 {
opp-microvolt = <850000>;
};
opp-1250000000 {
opp-microvolt = <850000>;
};
opp-1400000000 {
opp-hz = /bits/ 64 <1400000000>;
opp-microvolt = <850000>;
opp-supported-hw = <0x01 0x0004>;
clock-latency-ns = <6000000>;
};
};
chosen {
stdout-path = "serial2:115200n8";
};
reserved-memory {
#address-cells = <2>;
#size-cells = <2>;
ranges;
ramoops@9ca00000 {
compatible = "ramoops";
reg = <0x00 0x9ca00000 0x00 0x00100000>;
no-map;
record-size = <0x8000>;
console-size = <0x8000>;
ftrace-size = <0x00>;
pmsg-size = <0x8000>;
};
secure_tfa_ddr: tfa@9e780000 {
reg = <0x00 0x9e780000 0x00 0x80000>;
alignment = <0x1000>;
no-map;
};
secure_ddr: optee@9e800000 {
reg = <0x00 0x9e800000 0x00 0x01800000>; /* for OP-TEE */
alignment = <0x1000>;
no-map;
};
/* Gap between m4f-memory and r5f-dma-memory: must be reserved
* to prevent Linux from allocating pages that are adjacent to
* ioremap_wc-mapped M4 regions (ARM64 cache aliasing). */
m4f_gap_memory: m4f-gap@9da00000 {
reg = <0x00 0x9da00000 0x00 0x100000>;
no-map;
};
wkup_r5fss0_core0_dma_memory_region: r5f-dma-memory@9db00000 {
compatible = "shared-dma-pool";
reg = <0x00 0x9db00000 0x00 0xc00000>;
no-map;
};
/* Gap between r5f-dma-memory and tfa */
r5f_gap_memory: r5f-gap@9e700000 {
reg = <0x00 0x9e700000 0x00 0x80000>;
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>;
no-map;
};
mcu_m4fss_memory_region: m4f-memory@9cc00000 {
compatible = "shared-dma-pool";
reg = <0x00 0x9cc00000 0x00 0xe00000>;
no-map;
};
};
vmain_pd: regulator-0 {
/* TPS6522005 Dual PMIC */
compatible = "regulator-fixed";
regulator-name = "vmain_pd";
regulator-min-microvolt = <5000000>;
regulator-max-microvolt = <5000000>;
regulator-always-on;
regulator-boot-on;
};
vcc_5v0: regulator-1 {
compatible = "regulator-fixed";
regulator-name = "vcc_5v0";
regulator-min-microvolt = <5000000>;
regulator-max-microvolt = <5000000>;
vin-supply = <&vmain_pd>;
regulator-always-on;
regulator-boot-on;
};
vcc_3v3_sys: regulator-2 {
compatible = "regulator-fixed";
regulator-name = "vcc_3v3_sys";
regulator-min-microvolt = <3300000>;
regulator-max-microvolt = <3300000>;
vin-supply = <&vmain_pd>;
regulator-always-on;
regulator-boot-on;
};
watchdog_ext: watchdog {
compatible = "linux,wdt-gpio";
pinctrl-names = "default";
pinctrl-0 = <&wdg_ext_pins_default>;
hw_algo = "toggle";
hw_margin_ms = <1120>; /* STWD100xY: min. 1120, typ. 1600 */
en-gpios = <&main_gpio0 42 GPIO_ACTIVE_LOW>;
gpios = <&main_gpio1 30 GPIO_ACTIVE_LOW>;
};
wsysinit: wsysinit_init {
compatible = "wago,sysinit";
tty,rs232-485 = "ttyS0";
};
};
&main_pmx0 {
main_uart0_pins_default: main-uart0-default-pins {
pinctrl-single,pins = <
AM62X_IOPAD(0x1c8, PIN_INPUT, 0) /* (D14) UART0_RXD */
AM62X_IOPAD(0x1cc, PIN_OUTPUT, 0) /* (E14) UART0_TXD */
>;
};
main_uart2_pins_default: main-uart2-default-pins {
pinctrl-single,pins = <
AM62X_IOPAD(0x01d0, PIN_INPUT, 3) /* (A15) UART0_CTSn.UART2_RXD */
AM62X_IOPAD(0x01d4, PIN_OUTPUT, 3) /* (B15) UART0_RTSn.UART2_TXD */
>;
};
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(0x09c, PIN_OUTPUT_PULLDOWN, 7) /* (V25) GPIO0_38 (RS-DE) */
>;
};
main_i2c0_pins_default: main-i2c0-default-pins {
pinctrl-single,pins = <
AM62X_IOPAD(0x1e0, PIN_INPUT_PULLUP, 0) /* (B16) I2C0_SCL */
AM62X_IOPAD(0x1e4, PIN_INPUT_PULLUP, 0) /* (A16) I2C0_SDA */
>;
};
main_i2c1_pins_default: main-i2c1-default-pins {
pinctrl-single,pins = <
AM62X_IOPAD(0x1e8, PIN_INPUT_PULLUP, 0) /* (B17) I2C1_SCL */
AM62X_IOPAD(0x1ec, PIN_INPUT_PULLUP, 0) /* (A17) I2C1_SDA */
>;
};
main_mmc0_pins_default: main-mmc0-default-pins {
pinctrl-single,pins = <
AM62X_IOPAD(0x220, PIN_INPUT, 0) /* (Y3) MMC0_CMD */
AM62X_IOPAD(0x218, PIN_OUTPUT, 0) /* (AB1) MMC0_CLK */
AM62X_IOPAD(0x214, PIN_INPUT, 0) /* (AA2) MMC0_DAT0 */
AM62X_IOPAD(0x210, PIN_INPUT, 0) /* (AA1) MMC0_DAT1 */
AM62X_IOPAD(0x20c, PIN_INPUT, 0) /* (AA3) MMC0_DAT2 */
AM62X_IOPAD(0x208, PIN_INPUT, 0) /* (Y4) MMC0_DAT3 */
AM62X_IOPAD(0x204, PIN_INPUT, 0) /* (AB2) MMC0_DAT4 */
AM62X_IOPAD(0x200, PIN_INPUT, 0) /* (AC1) MMC0_DAT5 */
AM62X_IOPAD(0x1fc, PIN_INPUT, 0) /* (AD2) MMC0_DAT6 */
AM62X_IOPAD(0x1f8, PIN_INPUT, 0) /* (AC2) MMC0_DAT7 */
>;
};
main_mmc1_pins_default: main-mmc1-default-pins {
pinctrl-single,pins = <
AM62X_IOPAD(0x23c, PIN_INPUT, 0) /* (A21) MMC1_CMD */
AM62X_IOPAD(0x234, PIN_OUTPUT, 0) /* (B22) MMC1_CLK */
AM62X_IOPAD(0x230, PIN_INPUT, 0) /* (A22) MMC1_DAT0 */
AM62X_IOPAD(0x22c, PIN_INPUT, 0) /* (B21) MMC1_DAT1 */
AM62X_IOPAD(0x228, PIN_INPUT, 0) /* (C21) MMC1_DAT2 */
AM62X_IOPAD(0x224, PIN_INPUT, 0) /* (D22) MMC1_DAT3 */
AM62X_IOPAD(0x240, PIN_INPUT_PULLUP, 0) /* (D17) MMC1_SDCD */
AM62X_IOPAD(0x244, PIN_INPUT_PULLUP, 0) /* (C17) MMC1_SDWP */
>;
};
main_spi0_pins_default: main-spi0-default-pins {
pinctrl-single,pins = <
AM62X_IOPAD(0x01bc, PIN_OUTPUT, 0) /* (A14) SPI0_CLK */
AM62X_IOPAD(0x01c0, PIN_INPUT, 0) /* (B13) SPI0_D0 */
AM62X_IOPAD(0x01c4, PIN_OUTPUT, 0) /* (B14) SPI0_D1 */
AM62X_IOPAD(0x01b4, PIN_OUTPUT, 0) /* (A13) SPI0_CS0 */
>;
};
main_spi2_pins_default: main-spi2-default-pins {
pinctrl-single,pins = <
AM62X_IOPAD(0x01b0, PIN_OUTPUT, 1) /* (A20) MCASP0_ACLKR.SPI2_CLK */
AM62X_IOPAD(0x0194, PIN_INPUT, 1) /* (B19) MCASP0_AXR3.SPI2_D0 */
AM62X_IOPAD(0x0198, PIN_OUTPUT, 1) /* (A19) MCASP0_AXR2.SPI2_D1 */
AM62X_IOPAD(0x01ac, PIN_OUTPUT, 1) /* (E19) MCASP0_AFSR.SPI2_CS0 */
AM62X_IOPAD(0x01a8, PIN_OUTPUT, 1) /* (D20) MCASP0_AFSX.SPI2_CS3 */
>;
};
wdg_ext_pins_default: wdg-ext-default-pins {
pinctrl-single,pins = <
AM62X_IOPAD(0x00ac, PIN_OUTPUT_PULLUP | INPUT_EN, 7) /* (L21) GPMC0_CSn1.GPIO0_42 */
AM62X_IOPAD(0x01f0, PIN_OUTPUT, 7) /* (A18) EXT_REFCLK1.GPIO1_30 */
>;
};
pmic_irq_pins_default: pmic-irq-default-pins {
pinctrl-single,pins = <
AM62X_IOPAD(0x0114, PIN_INPUT, 7) /* (B24) MMC2_DAT0.GPIO0_68 */
>;
};
};
&main_uart0 { /* DGB UART */
status = "okay";
pinctrl-names = "default";
pinctrl-0 = <&main_uart0_pins_default>;
};
&main_uart2 { /* KBUS Firmware download */
status = "okay";
pinctrl-names = "default";
pinctrl-0 = <&main_uart2_pins_default>;
};
&main_uart4 { /* RS485 */
status = "okay";
pinctrl-names = "default";
pinctrl-0 = <&main_uart4_pins_default>;
rts-gpios = <&main_gpio0 38 GPIO_ACTIVE_HIGH>;
};
&main_i2c0 { /* type label */
pinctrl-names = "default";
pinctrl-0 = <&main_i2c0_pins_default>;
clock-frequency = <400000>;
eeprom_type_label: at24c512@54 {
compatible = "atmel,24c512";
reg = <0x54>;
pagesize = <128>;
};
};
&main_spi0 {
pinctrl-names = "default";
pinctrl-0 = <&main_spi0_pins_default>;
dmas = <&main_pktdma 0xc300 0>, <&main_pktdma 0x4300 0>;
dma-names = "tx0", "rx0";
/*
* When this option is set, the McSPI EOW Interrupt will be used
* to signal when a DMA Transfer is done. This is needed for this
* platform to prevent unpredictable delays resulting from TX/RX
* completions within the UDMA driver.
*/
use-eow-interrupt;
};
&main_spi2 {
pinctrl-names = "default";
pinctrl-0 = <&main_spi2_pins_default>;
};
&main_i2c1 {
pinctrl-names = "default";
pinctrl-0 = <&main_i2c1_pins_default>;
clock-frequency = <400000>;
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.
use-goldcap-with-higher-voltage;
};
};
&sdhci0 { /* eMMC */
pinctrl-names = "default";
pinctrl-0 = <&main_mmc0_pins_default>;
ti,driver-strength-ohm = <33>;
disable-wp;
};
&sdhci1 { /* SD */
pinctrl-names = "default";
pinctrl-0 = <&main_mmc1_pins_default>;
ti,driver-strength-ohm = <33>;
disable-wp;
};
&mailbox0_cluster0 {
mbox_m4_0: mbox-m4-0 {
ti,mbox-rx = <0 0 0>;
ti,mbox-tx = <1 0 0>;
};
};
/*
* This system watchdog is reserved for internal use and is not user-configurable.
* It must not be modified or deleted to ensure consistent behavior!
*/
&main_rti1 { // watchdog@e010000
timeout-sec = <120>;
};
/*
* AM6232 does not have main_rti2, main_rti3, and main_rti15
* https://e2e.ti.com/support/processors-group/processors/f/processors-forum/1218039/am623-stuck-in-sd-card-after-boot-up
*/
&main_rti2 { // watchdog@e020000
status = "disabled";
};
&main_rti3 { // watchdog@e030000
status = "disabled";
};
&main_rti15 { // watchdog@e0f0000
status = "disabled";
};
&usbss0 {
status = "okay";
ti,vbus-divider;
};
&usb0 {
status = "okay";
dr_mode = "peripheral";
};
&mcu_m4fss {
mboxes = <&mailbox0_cluster0 &mbox_m4_0>;
memory-region = <&mcu_m4fss_dma_memory_region>,
<&mcu_m4fss_memory_region>;
ti,no-auto-boot;
status = "okay";
};
@@ -0,0 +1,61 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Common dtsi for AM62x WAGO based devices
*
* Copyright (C) 2021-2022 Texas Instruments Incorporated - https://www.ti.com/
* Copyright (C) 2025 WAGO GmbH & Co. KG - https://www.wago.com/
*/
&main_pmx0 {
kbus_pins_default: kbus-default-pins {
pinctrl-single,pins = <
AM62X_IOPAD(0x0100, PIN_INPUT_PULLUP, 7) /* (AC25) VOUT0_VSYNC.GPIO0_63 (kbus,nsync) */
AM62X_IOPAD(0x0128, PIN_OUTPUT_PULLDOWN, 7) /* (B23) MMC2_SDWP.GPIO0_72 (kbus,nrst) */
AM62X_IOPAD(0x0118, PIN_OUTPUT_PULLUP, 7) /* (D25) MMC2_CLK.GPIO0_69 (kbus,cmdsel) */
AM62X_IOPAD(0x0104, PIN_OUTPUT_PULLUP, 7) /* (AC24) VOUT0_PCLK.GPIO0_64 (kbus,nirq) */
AM62X_IOPAD(0x0120, PIN_INPUT_PULLUP, 7) /* (C24) MMC2_CMD.GPIO0_70 (kbus,nerr) */
AM62X_IOPAD(0x0124, PIN_INPUT_PULLUP, 7) /* (A23) MMC2_SDCD.GPIO0_71 (kbus,nrdy) */
>;
};
};
&main_spi0 {
#address-cells = <1>;
#size-cells = <0>;
spi-rt;
spi-rt-prio = <81>;
kbus@0 {
pinctrl-names = "default";
pinctrl-0 = <&kbus_pins_default>;
compatible = "wago,spi-kbus";
reg = <0>;
spi-max-frequency = <25000000>;
kbus,reset-on-boot;
kbus,renesas;
/*
* kbus,cmdsel-gpio is used to signal the need for an update
* while the KBUS slave cpu is being reset. Its low level
* forces an update.
*/
kbus,force-update-detection;
/*
* announce used tty-device in /sys/class/wago/system.
* is needed to link this device to /dev/ttyKbus
*/
/* TODO: check serial device name in user-space */
kbus,tty-device = "ttyS1"; /* corresponds to &uart2*/
/* some should be active low, keep all to high for compatibility reasons */
kbus,nrst-gpio = <&main_gpio0 72 GPIO_ACTIVE_LOW>;
kbus,nsync-gpio = <&main_gpio0 63 GPIO_ACTIVE_HIGH>;
kbus,cmdsel-gpio = <&main_gpio0 69 GPIO_ACTIVE_HIGH>;
kbus,nirq-gpio = <&main_gpio0 64 GPIO_ACTIVE_HIGH>;
kbus,nerr-gpio = <&main_gpio0 70 GPIO_ACTIVE_HIGH>;
kbus,nrdy-gpio = <&main_gpio0 71 GPIO_ACTIVE_HIGH>;
};
};
@@ -0,0 +1,38 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Common dtsi for AM62x WAGO based devices
*
* Copyright (C) 2021-2022 Texas Instruments Incorporated - https://www.ti.com/
* Copyright (C) 2025 WAGO GmbH & Co. KG - https://www.wago.com/
*/
&main_i2c1 {
#address-cells = <1>;
#size-cells = <0>;
/*
* PCA9538, 8-Bit I/O Expander
*/
io_exp_8bit: io-exp-8bit@71 {
compatible = "nxp,pca9538";
reg = <0x71>;
gpio-controller;
#gpio-cells = <2>;
gpio-line-names = "RS.TERM-EN", "RS.SLR",
"+5V0-FB-ENA", "IO3",
"BIAS.EN", "BIAS.HIR",
"IO6", "IO7";
rs485-5v { /* +5V0-FB-ENA */
gpio-hog;
gpios = <2 GPIO_ACTIVE_HIGH>;
output-high;
};
};
};
&main_uart4 { /* RS485 Interface */
/* RS485 termination is controlled via GPIO userspace tools */
rs485-rts-active-high;
linux,rs485-enabled-at-boot-time;
};
@@ -0,0 +1,24 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Common Real-Time-Clock dtsi for AM62x WAGO based devices
*
* Copyright (C) 2021-2022 Texas Instruments Incorporated - https://www.ti.com/
* Copyright (C) 2025 WAGO GmbH & Co. KG - https://www.wago.com/
*/
&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.
*/
use-goldcap-with-higher-voltage;
};
};
@@ -0,0 +1,32 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Common Trusted Platform Module dtsi for AM62x based WAGO devices
*
* Copyright (C) 2021-2022 Texas Instruments Incorporated - https://www.ti.com/
* Copyright (C) 2025 WAGO GmbH & Co. KG - https://www.wago.com/
*/
&main_pmx0 {
tpm_pins_default: tpm-pins-default-pins {
pinctrl-single,pins = <
AM62X_IOPAD(0x010c, PIN_INPUT, 7) /* (E23) MMC2_DAT2.GPIO0_66 */
>;
};
};
&main_spi2 {
slb9670: SLB9670@0 {
compatible = "infineon,slb9670", "tis,tpm2-spi", "tcg,tpm_tis-spi";
reg = <0>;
#gpio-cells = <2>;
#interrupt-cells = <2>;
pinctrl-names = "default";
pinctrl-0 = <&tpm_pins_default>;
interrupt-parent = <&main_gpio0>;
interrupts = <66 IRQ_TYPE_EDGE_FALLING>;
#address-cells = <1>;
#size-cells = <0>;
spi-max-frequency = <43000000>;
};
};
-911
View File
@@ -1,911 +0,0 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Common dtsi for AM62x WAGO based devices
*
* Copyright (C) 2021-2022 Texas Instruments Incorporated - https://www.ti.com/
*
* Copyright (C) 2024 WAGO GmbH & Co. KG <http://global.wago.com>
* Oleg Karfich <oleg.karfich@wago.com>
* Andrej Unrau <andrej.unrau@wago.com>
*/
#include <dt-bindings/leds/common.h>
#include <dt-bindings/gpio/gpio.h>
#include <dt-bindings/net/ti-dp83867.h>
#include "k3-am625.dtsi"
/ {
aliases {
serial2 = &main_uart0;
serial1 = &main_uart2;
serial4 = &main_uart4;
mmc0 = &sdhci1;
mmc1 = &sdhci0;
ethernet0 = &cpsw_port1;
ethernet1 = &cpsw_port2;
usb0 = &usb0;
watchdog0 = &main_rti0;
watchdog1 = &watchdog_ext;
watchdog2 = &main_rti1;
};
opp-table {
opp-200000000 {
opp-microvolt = <750000>;
};
opp-400000000 {
opp-microvolt = <750000>;
};
opp-600000000 {
opp-microvolt = <750000>;
};
opp-800000000 {
opp-microvolt = <750000>;
};
opp-1000000000 {
opp-microvolt = <750000>;
};
opp-1250000000 {
opp-microvolt = <750000>;
};
opp-1400000000 {
opp-hz = /bits/ 64 <1400000000>;
opp-microvolt = <850000>;
opp-supported-hw = <0x01 0x0004>;
clock-latency-ns = <6000000>;
};
};
chosen {
stdout-path = "serial2:115200n8";
};
memory@80000000 {
device_type = "memory";
/* 2G RAM */
reg = <0x00000000 0x80000000 0x00000000 0x80000000>;
};
reserved-memory {
#address-cells = <2>;
#size-cells = <2>;
ranges;
ramoops@9ca00000 {
compatible = "ramoops";
reg = <0x00 0x9ca00000 0x00 0x00100000>;
record-size = <0x8000>;
console-size = <0x8000>;
ftrace-size = <0x00>;
pmsg-size = <0x8000>;
};
secure_tfa_ddr: tfa@9e780000 {
reg = <0x00 0x9e780000 0x00 0x80000>;
alignment = <0x1000>;
no-map;
};
secure_ddr: optee@9e800000 {
reg = <0x00 0x9e800000 0x00 0x01800000>; /* for OP-TEE */
alignment = <0x1000>;
no-map;
};
wkup_r5fss0_core0_dma_memory_region: r5f-dma-memory@9db00000 {
compatible = "shared-dma-pool";
reg = <0x00 0x9db00000 0x00 0xc00000>;
no-map;
};
};
wsysinit: wsysinit_init {
compatible = "wago,sysinit";
tty,rs232-485 = "ttyS0";
board,variant = "pfc300";
};
/* 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
* need to adapt this configuration
*/
oms: PAC-Operating-Mode-Switch {
compatible = "gpio-keys";
pinctrl-names = "default";
pinctrl-0 = <&oms_pins_default>;
#address-cells = <1>;
#size-cells = <0>;
autorepeat;
oms_run: run {
label = "RUN";
gpios = <&main_gpio0 65 GPIO_ACTIVE_LOW>;
linux,code = <1>;
linux,input-type = <5>;
debounce-interval = <1>;
};
oms_stop: stop {
label = "STOP";
gpios = <&main_gpio1 10 GPIO_ACTIVE_LOW>;
linux,code = <2>;
linux,input-type = <5>;
debounce-interval = <1>;
};
oms_reset: reset {
label = "RESET";
gpios = <&main_gpio1 12 GPIO_ACTIVE_LOW>;
linux,code = <3>;
linux,input-type = <1>;
debounce-interval = <1>;
};
oms_reset_all: reset_all {
label = "RESET_ALL";
gpios = <&main_gpio1 11 GPIO_ACTIVE_HIGH>;
linux,code = <4>;
linux,input-type = <1>;
debounce-interval = <1>;
};
};
uio_nvram: nvram@51000000 {
compatible = "uio_pdrv_genirq";
linux,uio-name = "UIO_NVRAM";
reg = <0 0x51000000 0 0x00020000>; /* 128k 16bit NVRAM */
};
vmain_pd: regulator-0 {
/* TPS6522005 Dual PMIC */
compatible = "regulator-fixed";
regulator-name = "vmain_pd";
regulator-min-microvolt = <5000000>;
regulator-max-microvolt = <5000000>;
regulator-always-on;
regulator-boot-on;
};
vcc_5v0: regulator-1 {
compatible = "regulator-fixed";
regulator-name = "vcc_5v0";
regulator-min-microvolt = <5000000>;
regulator-max-microvolt = <5000000>;
vin-supply = <&vmain_pd>;
regulator-always-on;
regulator-boot-on;
};
vcc_3v3_sys: regulator-2 {
compatible = "regulator-fixed";
regulator-name = "vcc_3v3_sys";
regulator-min-microvolt = <3300000>;
regulator-max-microvolt = <3300000>;
vin-supply = <&vmain_pd>;
regulator-always-on;
regulator-boot-on;
};
vdd_mmc1: regulator-3 {
compatible = "regulator-fixed";
pinctrl-names = "default";
pinctrl-0 = <&vdd_sd_dv_pins_default>;
regulator-name = "vdd_mmc1";
regulator-min-microvolt = <3300000>;
regulator-max-microvolt = <3300000>;
regulator-boot-on;
vin-supply = <&vcc_3v3_sys>;
gpio = <&main_gpio0 31 GPIO_ACTIVE_LOW>;
enable-active-high;
startup-delay-us = <5000>;
};
watchdog_ext: watchdog {
compatible = "linux,wdt-gpio";
pinctrl-names = "default";
pinctrl-0 = <&wdg_ext_pins_default>;
hw_algo = "toggle";
hw_margin_ms = <1120>; /* STWD100xY: min. 1120, typ. 1600 */
en-gpios = <&main_gpio0 42 GPIO_ACTIVE_LOW>;
gpios = <&main_gpio1 30 GPIO_ACTIVE_LOW>;
};
};
&main_pmx0 {
main_uart0_pins_default: main-uart0-default-pins {
pinctrl-single,pins = <
AM62X_IOPAD(0x1c8, PIN_INPUT, 0) /* (D14) UART0_RXD */
AM62X_IOPAD(0x1cc, PIN_OUTPUT, 0) /* (E14) UART0_TXD */
>;
};
main_uart2_pins_default: main-uart2-default-pins {
pinctrl-single,pins = <
AM62X_IOPAD(0x01d0, PIN_INPUT, 3) /* (A15) UART0_CTSn.UART2_RXD */
AM62X_IOPAD(0x01d4, PIN_OUTPUT, 3) /* (B15) UART0_RTSn.UART2_TXD */
>;
};
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(0x09c, PIN_OUTPUT_PULLDOWN, 7) /* (V25) GPIO0_38 (RS-DE) */
>;
};
main_i2c0_pins_default: main-i2c0-default-pins {
pinctrl-single,pins = <
AM62X_IOPAD(0x1e0, PIN_INPUT_PULLUP, 0) /* (B16) I2C0_SCL */
AM62X_IOPAD(0x1e4, PIN_INPUT_PULLUP, 0) /* (A16) I2C0_SDA */
>;
};
main_i2c1_pins_default: main-i2c1-default-pins {
pinctrl-single,pins = <
AM62X_IOPAD(0x1e8, PIN_INPUT_PULLUP, 0) /* (B17) I2C1_SCL */
AM62X_IOPAD(0x1ec, PIN_INPUT_PULLUP, 0) /* (A17) I2C1_SDA */
>;
};
main_mmc0_pins_default: main-mmc0-default-pins {
pinctrl-single,pins = <
AM62X_IOPAD(0x220, PIN_INPUT, 0) /* (Y3) MMC0_CMD */
AM62X_IOPAD(0x218, PIN_OUTPUT, 0) /* (AB1) MMC0_CLK */
AM62X_IOPAD(0x214, PIN_INPUT, 0) /* (AA2) MMC0_DAT0 */
AM62X_IOPAD(0x210, PIN_INPUT, 0) /* (AA1) MMC0_DAT1 */
AM62X_IOPAD(0x20c, PIN_INPUT, 0) /* (AA3) MMC0_DAT2 */
AM62X_IOPAD(0x208, PIN_INPUT, 0) /* (Y4) MMC0_DAT3 */
AM62X_IOPAD(0x204, PIN_INPUT, 0) /* (AB2) MMC0_DAT4 */
AM62X_IOPAD(0x200, PIN_INPUT, 0) /* (AC1) MMC0_DAT5 */
AM62X_IOPAD(0x1fc, PIN_INPUT, 0) /* (AD2) MMC0_DAT6 */
AM62X_IOPAD(0x1f8, PIN_INPUT, 0) /* (AC2) MMC0_DAT7 */
>;
};
main_mmc1_pins_default: main-mmc1-default-pins {
pinctrl-single,pins = <
AM62X_IOPAD(0x23c, PIN_INPUT, 0) /* (A21) MMC1_CMD */
AM62X_IOPAD(0x234, PIN_OUTPUT, 0) /* (B22) MMC1_CLK */
AM62X_IOPAD(0x230, PIN_INPUT, 0) /* (A22) MMC1_DAT0 */
AM62X_IOPAD(0x22c, PIN_INPUT, 0) /* (B21) MMC1_DAT1 */
AM62X_IOPAD(0x228, PIN_INPUT, 0) /* (C21) MMC1_DAT2 */
AM62X_IOPAD(0x224, PIN_INPUT, 0) /* (D22) MMC1_DAT3 */
AM62X_IOPAD(0x240, PIN_INPUT_PULLUP, 0) /* (D17) MMC1_SDCD */
AM62X_IOPAD(0x244, PIN_INPUT_PULLUP, 0) /* (C17) MMC1_SDWP */
>;
};
main_mdio1_pins_default: main-mdio1-default-pins {
pinctrl-single,pins = <
AM62X_IOPAD(0x160, PIN_OUTPUT, 0) /* (AD24) MDIO0_MDC */
AM62X_IOPAD(0x15c, PIN_INPUT_PULLUP, 0) /* (AB22) MDIO0_MDIO */
AM62X_IOPAD(0x019c, PIN_OUTPUT, 7) /* (B18) MCASP0_AXR1.GPIO1_9 */
>;
};
main_rgmii1_pins_default: main-rgmii1-default-pins {
pinctrl-single,pins = <
AM62X_IOPAD(0x14c, PIN_INPUT, 0) /* (AB17) RGMII1_RD0 */
AM62X_IOPAD(0x150, PIN_INPUT, 0) /* (AC17) RGMII1_RD1 */
AM62X_IOPAD(0x154, PIN_INPUT, 0) /* (AB16) RGMII1_RD2 */
AM62X_IOPAD(0x158, PIN_INPUT, 0) /* (AA15) RGMII1_RD3 */
AM62X_IOPAD(0x148, PIN_INPUT, 0) /* (AD17) RGMII1_RXC */
AM62X_IOPAD(0x144, PIN_INPUT, 0) /* (AE17) RGMII1_RX_CTL */
AM62X_IOPAD(0x134, PIN_OUTPUT, 0) /* (AE20) RGMII1_TD0 */
AM62X_IOPAD(0x138, PIN_OUTPUT, 0) /* (AD20) RGMII1_TD1 */
AM62X_IOPAD(0x13c, PIN_OUTPUT, 0) /* (AE18) RGMII1_TD2 */
AM62X_IOPAD(0x140, PIN_OUTPUT, 0) /* (AD18) RGMII1_TD3 */
AM62X_IOPAD(0x130, PIN_OUTPUT, 0) /* (AE19) RGMII1_TXC */
AM62X_IOPAD(0x12c, PIN_OUTPUT, 0) /* (AD19) RGMII1_TX_CTL */
AM62X_IOPAD(0x0030, PIN_INPUT, 7) /* (G21) OSPI0_CSn1.GPIO0_12 */
>;
};
main_rgmii2_pins_default: main-rgmii2-default-pins {
pinctrl-single,pins = <
AM62X_IOPAD(0x184, PIN_INPUT, 0) /* (AE23) RGMII2_RD0 */
AM62X_IOPAD(0x188, PIN_INPUT, 0) /* (AB20) RGMII2_RD1 */
AM62X_IOPAD(0x18c, PIN_INPUT, 0) /* (AC21) RGMII2_RD2 */
AM62X_IOPAD(0x190, PIN_INPUT, 0) /* (AE22) RGMII2_RD3 */
AM62X_IOPAD(0x180, PIN_INPUT, 0) /* (AD23) RGMII2_RXC */
AM62X_IOPAD(0x17c, PIN_INPUT, 0) /* (AD22) RGMII2_RX_CTL */
AM62X_IOPAD(0x16c, PIN_OUTPUT, 0) /* (Y18) RGMII2_TD0 */
AM62X_IOPAD(0x170, PIN_OUTPUT, 0) /* (AA18) RGMII2_TD1 */
AM62X_IOPAD(0x174, PIN_OUTPUT, 0) /* (AD21) RGMII2_TD2 */
AM62X_IOPAD(0x178, PIN_OUTPUT, 0) /* (AC20) RGMII2_TD3 */
AM62X_IOPAD(0x168, PIN_OUTPUT, 0) /* (AE21) RGMII2_TXC */
AM62X_IOPAD(0x164, PIN_OUTPUT, 0) /* (AA19) RGMII2_TX_CTL */
AM62X_IOPAD(0x0034, PIN_INPUT, 7) /* (H21) OSPI0_CSn2.GPIO0_13 */
>;
};
vdd_sd_dv_pins_default: vdd-sd-dv-default-pins {
pinctrl-single,pins = <
AM62X_IOPAD(0x07c, PIN_OUTPUT, 7) /* (P25) GPMC0_CLK.GPIO0_31 */
>;
};
gpmc_data_pins_default: gpmc-data-default-pins {
pinctrl-single,pins = <
AM62X_IOPAD(0x00bc, PIN_OUTPUT, 1) /* (V24) VOUT0_DATA1.GPMC0_A1 */
AM62X_IOPAD(0x00c0, PIN_OUTPUT, 1) /* (W25) VOUT0_DATA2.GPMC0_A2 */
AM62X_IOPAD(0x00c4, PIN_OUTPUT, 1) /* (W24) VOUT0_DATA3.GPMC0_A3 */
AM62X_IOPAD(0x00c8, PIN_OUTPUT, 1) /* (Y25) VOUT0_DATA4.GPMC0_A4 */
AM62X_IOPAD(0x00cc, PIN_OUTPUT, 1) /* (Y24) VOUT0_DATA5.GPMC0_A5 */
AM62X_IOPAD(0x00d0, PIN_OUTPUT, 1) /* (Y23) VOUT0_DATA6.GPMC0_A6 */
AM62X_IOPAD(0x00d4, PIN_OUTPUT, 1) /* (AA25) VOUT0_DATA7.GPMC0_A7 */
AM62X_IOPAD(0x00d8, PIN_OUTPUT, 1) /* (V21) VOUT0_DATA8.GPMC0_A8 */
AM62X_IOPAD(0x00dc, PIN_OUTPUT, 1) /* (W21) VOUT0_DATA9.GPMC0_A9 */
AM62X_IOPAD(0x00e0, PIN_OUTPUT, 1) /* (V20) VOUT0_DATA10.GPMC0_A10 */
AM62X_IOPAD(0x00e4, PIN_OUTPUT, 1) /* (AA23) VOUT0_DATA11.GPMC0_A11 */
AM62X_IOPAD(0x00e8, PIN_OUTPUT, 1) /* (AB25) VOUT0_DATA12.GPMC0_A12 */
AM62X_IOPAD(0x00ec, PIN_OUTPUT, 1) /* (AA24) VOUT0_DATA13.GPMC0_A13 */
AM62X_IOPAD(0x00f0, PIN_OUTPUT, 1) /* (Y22) VOUT0_DATA14.GPMC0_A14 */
AM62X_IOPAD(0x00f4, PIN_OUTPUT, 1) /* (AA21) VOUT0_DATA15.GPMC0_A15 */
AM62X_IOPAD(0x00f8, PIN_OUTPUT, 1) /* (AB24) VOUT0_HSYNC.GPMC0_A16 */
>;
};
gpmc_addr_pins_default: gpmc-addr-default-pins {
pinctrl-single,pins = <
AM62X_IOPAD(0x003c, PIN_INPUT, 0) /* (M25) GPMC0_AD0 */
AM62X_IOPAD(0x0040, PIN_INPUT, 0) /* (N23) GPMC0_AD1 */
AM62X_IOPAD(0x0044, PIN_INPUT, 0) /* (N24) GPMC0_AD2 */
AM62X_IOPAD(0x0048, PIN_INPUT, 0) /* (N25) GPMC0_AD3 */
AM62X_IOPAD(0x004c, PIN_INPUT, 0) /* (P24) GPMC0_AD4 */
AM62X_IOPAD(0x0050, PIN_INPUT, 0) /* (P22) GPMC0_AD5 */
AM62X_IOPAD(0x0054, PIN_INPUT, 0) /* (P21) GPMC0_AD6 */
AM62X_IOPAD(0x0058, PIN_INPUT, 0) /* (R23) GPMC0_AD7 */
AM62X_IOPAD(0x005c, PIN_INPUT, 0) /* (R24) GPMC0_AD8 */
AM62X_IOPAD(0x0060, PIN_INPUT, 0) /* (R25) GPMC0_AD9 */
AM62X_IOPAD(0x0064, PIN_INPUT, 0) /* (T25) GPMC0_AD10 */
AM62X_IOPAD(0x0068, PIN_INPUT, 0) /* (R21) GPMC0_AD11 */
AM62X_IOPAD(0x006c, PIN_INPUT, 0) /* (T22) GPMC0_AD12 */
AM62X_IOPAD(0x0070, PIN_INPUT, 0) /* (T24) GPMC0_AD13 */
AM62X_IOPAD(0x0074, PIN_INPUT, 0) /* (U25) GPMC0_AD14 */
AM62X_IOPAD(0x0078, PIN_INPUT, 0) /* (U24) GPMC0_AD15 */
>;
};
gpmc_ctrl_pins_default: gpmc-ctrl-default-pins {
pinctrl-single,pins = <
AM62X_IOPAD(0x0094, PIN_OUTPUT, 0) /* (N20) GPMC0_BE1n */
AM62X_IOPAD(0x00a8, PIN_OUTPUT, 0) /* (M21) GPMC0_CSn0 */
AM62X_IOPAD(0x0088, PIN_OUTPUT, 0) /* (L24) GPMC0_OEn_REn */
AM62X_IOPAD(0x008c, PIN_OUTPUT, 0) /* (L25) GPMC0_WEn */
AM62X_IOPAD(0x0090, PIN_OUTPUT, 0) /* (M24) GPMC0_BE0n_CLE */
>;
};
oms_pins_default: oms-default-pins {
pinctrl-single,pins = <
AM62X_IOPAD(0x0108, PIN_INPUT, 7) /* (D24) MMC2_DAT3.GPIO0_65 */
AM62X_IOPAD(0x01a0, PIN_INPUT, 7) /* (E18) MCASP0_AXR0.GPIO1_10 */
AM62X_IOPAD(0x01a8, PIN_INPUT, 7) /* (D20) MCASP0_AFSX.GPIO1_12 */
AM62X_IOPAD(0x01a4, PIN_INPUT, 7) /* (B20) MCASP0_ACLKX.GPIO1_11 */
>;
};
main_spi0_pins_default: main-spi0-default-pins {
pinctrl-single,pins = <
AM62X_IOPAD(0x01bc, PIN_OUTPUT, 0) /* (A14) SPI0_CLK */
AM62X_IOPAD(0x01c0, PIN_INPUT, 0) /* (B13) SPI0_D0 */
AM62X_IOPAD(0x01c4, PIN_OUTPUT, 0) /* (B14) SPI0_D1 */
AM62X_IOPAD(0x01b4, PIN_OUTPUT, 0) /* (A13) SPI0_CS0 */
>;
};
main_spi2_pins_default: main-spi2-default-pins {
pinctrl-single,pins = <
AM62X_IOPAD(0x01b0, PIN_OUTPUT, 1) /* (A20) MCASP0_ACLKR.SPI2_CLK */
AM62X_IOPAD(0x0194, PIN_INPUT, 1) /* (B19) MCASP0_AXR3.SPI2_D0 */
AM62X_IOPAD(0x0198, PIN_INPUT, 1) /* (A19) MCASP0_AXR2.SPI2_D1 */
AM62X_IOPAD(0x01ac, PIN_OUTPUT, 1) /* (E19) MCASP0_AFSR.SPI2_CS0 */
>;
};
tpm_pins_default: tpm-pins-default-pins {
pinctrl-single,pins = <
AM62X_IOPAD(0x010c, PIN_INPUT, 7) /* (E23) MMC2_DAT2.GPIO0_66 */
>;
};
kbus_pins_default: kbus-default-pins {
pinctrl-single,pins = <
AM62X_IOPAD(0x0100, PIN_INPUT_PULLUP, 7) /* (AC25) VOUT0_VSYNC.GPIO0_63 (kbus,nsync) */
AM62X_IOPAD(0x0128, PIN_OUTPUT_PULLDOWN, 7) /* (B23) MMC2_SDWP.GPIO0_72 (kbus,nrst) */
AM62X_IOPAD(0x0118, PIN_OUTPUT_PULLUP, 7) /* (D25) MMC2_CLK.GPIO0_69 (kbus,cmdsel) */
AM62X_IOPAD(0x0104, PIN_OUTPUT_PULLUP, 7) /* (AC24) VOUT0_PCLK.GPIO0_64 (kbus,nirq) */
AM62X_IOPAD(0x0120, PIN_INPUT_PULLUP, 7) /* (C24) MMC2_CMD.GPIO0_70 (kbus,nerr) */
AM62X_IOPAD(0x0124, PIN_INPUT_PULLUP, 7) /* (A23) MMC2_SDCD.GPIO0_71 (kbus,nrdy) */
>;
};
wdg_ext_pins_default: wdg-ext-default-pins {
pinctrl-single,pins = <
AM62X_IOPAD(0x00ac, PIN_OUTPUT_PULLUP | INPUT_EN, 7) /* (L21) GPMC0_CSn1.GPIO0_42 */
AM62X_IOPAD(0x01f0, PIN_OUTPUT, 7) /* (A18) EXT_REFCLK1.GPIO1_30 */
>;
};
pmic_irq_pins_default: pmic-irq-default-pins {
pinctrl-single,pins = <
AM62X_IOPAD(0x0114, PIN_INPUT, 7) /* (B24) MMC2_DAT0.GPIO0_68 */
>;
};
};
&gpmc0 {
pinctrl-names = "default";
pinctrl-0 = <&gpmc_data_pins_default>, <&gpmc_addr_pins_default>, <&gpmc_ctrl_pins_default>;
num-cs = <4>;
num-waitpins = <4>;
ranges = <
0 0 0x00 0x51000000 0x01000000 /* CS0: NVRAM, 16M */
>;
nvram: nor@0,0 { /* NVRAM Device 128k */
reg = <0 0 0x01000000>;
bank-width = <2>; /* 1: 8bit, 2: 16bit */
gpmc,sync-clk-ps = <0>;
gpmc,cs-on-ns = <0>;
gpmc,cs-rd-off-ns = <50>;
gpmc,cs-wr-off-ns = <30>;
gpmc,adv-on-ns = <0>;
gpmc,adv-rd-off-ns = <0>;
gpmc,adv-wr-off-ns = <0>;
gpmc,oe-on-ns = <0>;
gpmc,oe-off-ns = <50>;
gpmc,we-on-ns = <0>;
gpmc,we-off-ns = <30>;
gpmc,rd-cycle-ns = <50>;
gpmc,wr-cycle-ns = <30>;
gpmc,access-ns = <40>;
gpmc,wr-access-ns = <10>;
gpmc,wr-data-mux-bus-ns = <0>;
gpmc,cycle2cycle-delay-ns = <10>;
gpmc,cycle2cycle-samecsen;
gpmc,cycle2cycle-diffcsen;
#address-cells = <1>;
#size-cells = <1>;
};
};
&main_uart0 {
pinctrl-names = "default";
pinctrl-0 = <&main_uart0_pins_default>;
};
&main_uart2 { /* KBUS Firmware download */
pinctrl-names = "default";
pinctrl-0 = <&main_uart2_pins_default>;
};
&main_uart4 { /* RS485 Interface */
pinctrl-names = "default";
pinctrl-0 = <&main_uart4_pins_default>;
rts-gpios = <&main_gpio0 38 GPIO_ACTIVE_HIGH>;
rs485-term-gpios = <&rs485 0 GPIO_ACTIVE_HIGH>;
rs485-rts-active-high;
linux,rs485-enabled-at-boot-time;
};
&main_i2c0 {
pinctrl-names = "default";
pinctrl-0 = <&main_i2c0_pins_default>;
clock-frequency = <400000>;
eeprom: at24c512@54 {
compatible = "atmel,24c512";
reg = <0x54>;
pagesize = <128>;
};
led_bar60: pca9552@60 {
compatible = "nxp,pca9552";
#address-cells = <1>;
#size-cells = <0>;
reg = <0x60>;
lb60_0: sys-red@0 {
label = "sys-red";
reg = <0>;
linux,default-trigger = "timer";
};
lb60_1: sys-green@1 {
label = "sys-green";
reg = <1>;
linux,default-trigger = "timer";
};
lb60_2: run-red@2 {
label = "run-red";
reg = <2>;
};
lb60_3: run-green@3 {
label = "run-green";
reg = <3>;
};
lb60_4: io-red@4 {
label = "io-red";
reg = <4>;
};
lb60_5: io-green@5 {
label = "io-green";
reg = <5>;
};
lb60_6: ms-red@6 {
label = "ms-red";
reg = <6>;
};
lb60_7: ms-green@7 {
label = "ms-green";
reg = <7>;
};
lb60_8: ns-red@8 {
label = "ns-red";
reg = <8>;
};
lb60_9: ns-green@9 {
label = "ns-green";
reg = <9>;
};
lb60_10: usr_r@10 {
label = "trm-red";
reg = <10>;
};
lb60_11: usr_g@11 {
label = "trm-green";
reg = <11>;
};
lb60_15: sys-over-red@15 {
label = "sys-over-red";
reg = <15>;
linux,default-trigger = "default-on";
};
};
led_bar61: pca9552@61 {
compatible = "nxp,pca9552";
#address-cells = <1>;
#size-cells = <0>;
reg = <0x61>;
lb61_0: u6-red@0 {
label = "u6-red";
reg = <0>;
};
lb61_1: u6-green@1 {
label = "u6-green";
reg = <1>;
};
lb61_2: u5-red@2 {
label = "u5-red";
reg = <2>;
};
lb61_3: u5-green@3 {
label = "u5-green";
reg = <3>;
};
lb61_4: u4-red@4 {
label = "u4-red";
reg = <4>;
};
lb61_5: u4-green@5 {
label = "u4-green";
reg = <5>;
};
lb61_6: u3-red@6 {
label = "u3-red";
reg = <6>;
};
lb61_7: u3-green@7 {
label = "u3-green";
reg = <7>;
};
lb61_8: u2-red@8 {
label = "u2-red";
reg = <8>;
};
lb61_9: u2-green@9 {
label = "u2-green";
reg = <9>;
};
lb61_10: u1-red@10 {
label = "u1-red";
reg = <10>;
};
lb61_11: u1-green@11 {
label = "u1-green";
reg = <11>;
};
};
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_3v3_sys>;
ldo3-supply = <&vcc_5v0>;
ldo4-supply = <&vcc_5v0>;
pinctrl-names = "default";
pinctrl-0 = <&pmic_irq_pins_default>;
interrupt-parent = <&main_gpio0>;
interrupts = <68 IRQ_TYPE_EDGE_FALLING>;
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 = "VCC1V8_SYS";
regulator-min-microvolt = <1800000>;
regulator-max-microvolt = <1800000>;
regulator-boot-on;
regulator-always-on;
};
buck3_30_reg: buck3 {
regulator-name = "VCC3V3_SYS";
regulator-min-microvolt = <3300000>;
regulator-max-microvolt = <3300000>;
regulator-boot-on;
regulator-always-on;
};
ldo1_30_reg: ldo1 {
regulator-name = "VCC3V3_SDC";
regulator-min-microvolt = <3300000>;
regulator-max-microvolt = <3300000>;
};
ldo2_30_reg: ldo2 {
regulator-name = "VDDA_1V8";
regulator-min-microvolt = <1800000>;
regulator-max-microvolt = <1800000>;
regulator-boot-on;
regulator-always-on;
};
};
};
};
&main_spi0 {
pinctrl-names = "default";
pinctrl-0 = <&main_spi0_pins_default>;
spi-rt;
spi-rt-prio = <81>;
dmas = <&main_pktdma 0xc300 0>, <&main_pktdma 0x4300 0>;
dma-names = "tx0", "rx0";
/*
* When this option is set, the McSPI EOW Interrupt will be used
* to signal when a DMA Transfer is done. This is needed for this
* platform to prevent unpredictable delays resulting from TX/RX
* completions within the UDMA driver.
*/
use-eow-interrupt;
kbus@0 {
pinctrl-names = "default";
pinctrl-0 = <&kbus_pins_default>;
compatible = "wago,spi-kbus";
reg = <0>;
spi-max-frequency = <10000000>;
kbus,reset-on-boot;
kbus,renesas;
/*
* kbus,cmdsel-gpio is used to signal the need for an update
* while the KBUS slave cpu is being reset. Its low level
* forces an update.
*/
kbus,force-update-detection;
/*
* announce used tty-device in /sys/class/wago/system.
* is needed to link this device to /dev/ttyKbus
*/
/* TODO: check serial device name in user-space */
kbus,tty-device = "ttyS1"; /* corresponds to &uart2*/
/* some should be active low, keep all to high for compatibility reasons */
kbus,nrst-gpio = <&main_gpio0 72 GPIO_ACTIVE_LOW>;
kbus,nsync-gpio = <&main_gpio0 63 GPIO_ACTIVE_HIGH>;
kbus,cmdsel-gpio = <&main_gpio0 69 GPIO_ACTIVE_HIGH>;
kbus,nirq-gpio = <&main_gpio0 64 GPIO_ACTIVE_HIGH>;
kbus,nerr-gpio = <&main_gpio0 70 GPIO_ACTIVE_HIGH>;
kbus,nrdy-gpio = <&main_gpio0 71 GPIO_ACTIVE_HIGH>;
};
};
&main_spi2 {
pinctrl-names = "default";
pinctrl-0 = <&main_spi2_pins_default>;
SLB9670: SLB9670@0 {
compatible = "infineon,slb9670", "tis,tpm2-spi", "tcg,tpm_tis-spi";
reg = <0>;
#gpio-cells = <2>;
#interrupt-cells = <2>;
pinctrl-names = "default";
pinctrl-0 = <&tpm_pins_default>;
interrupt-parent = <&main_gpio0>;
interrupts = <66 IRQ_TYPE_EDGE_FALLING>;
#address-cells = <1>;
#size-cells = <0>;
spi-max-frequency = <43000000>;
};
};
&main_i2c1 {
pinctrl-names = "default";
pinctrl-0 = <&main_i2c1_pins_default>;
clock-frequency = <400000>;
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.
use-goldcap-with-higher-voltage;
};
rs485: rs485@71 {
compatible = "nxp,pca9538";
reg = <0x71>;
gpio-controller;
#gpio-cells = <2>;
gpio-line-names = "RS.TERM-EN", "RS.SLR",
"+5V0-FB-ENA", "MP200",
"BIAS.EN", "BIAS.HIR",
"MP201", "MP202";
rs485-5v {
gpio-hog;
gpios = <2 GPIO_ACTIVE_HIGH>;
output-high;
};
};
};
&sdhci0 {
/* eMMC */
pinctrl-names = "default";
pinctrl-0 = <&main_mmc0_pins_default>;
ti,driver-strength-ohm = <33>;
disable-wp;
};
&sdhci1 {
/* SD */
vmmc-supply = <&vdd_mmc1>;
pinctrl-names = "default";
pinctrl-0 = <&main_mmc1_pins_default>;
ti,driver-strength-ohm = <33>;
cap-sd-highspeed;
no-1-8-v;
};
&cpsw3g {
pinctrl-names = "default";
pinctrl-0 = <&main_mdio1_pins_default
&main_rgmii1_pins_default
&main_rgmii2_pins_default>;
};
&cpsw_port1 {
phy-mode = "rgmii-rxid";
phy-handle = <&cpsw3g_phy0>;
};
&cpsw_port2 {
phy-mode = "rgmii-rxid";
phy-handle = <&cpsw3g_phy1>;
};
&cpsw3g_mdio {
reset-gpios = <&main_gpio1 9 GPIO_ACTIVE_LOW>;
reset-delay-us = <10>;
reset-post-delay-us = <10000>;
cpsw3g_phy0: ethernet-phy@0 {
// ADIN1300
compatible = "ethernet-phy-id0283.bc30";
reg = <0>;
adi,rx-internal-delay-ps = <2000>;
};
cpsw3g_phy1: ethernet-phy@1 {
// ADIN1300
compatible = "ethernet-phy-id0283.bc30";
reg = <1>;
adi,rx-internal-delay-ps = <2000>;
};
};
&mailbox0_cluster0 {
mbox_m4_0: mbox-m4-0 {
ti,mbox-rx = <0 0 0>;
ti,mbox-tx = <1 0 0>;
};
};
/*
* AM6232 does not have main_rti2, main_rti3, and main_rti15
* https://e2e.ti.com/support/processors-group/processors/f/processors-forum/1218039/am623-stuck-in-sd-card-after-boot-up
*/
&main_rti2 { // watchdog@e020000
status = "disabled";
};
&main_rti3 { // watchdog@e030000
status = "disabled";
};
&main_rti15 { // watchdog@e0f0000
status = "disabled";
};
&usbss0 {
ti,vbus-divider;
};
&usb0 {
dr_mode = "peripheral";
};
@@ -0,0 +1,15 @@
/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,24 @@
/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,9 @@
/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"
@@ -0,0 +1,9 @@
/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"
@@ -0,0 +1,9 @@
/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"
@@ -0,0 +1,9 @@
/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,49 @@
/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,9 @@
/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,44 @@
/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>;
};
+34 -8
View File
@@ -86,7 +86,7 @@ CONFIG_INET_ESPINTCP=y
CONFIG_IPV6_OPTIMISTIC_DAD=y
CONFIG_IPV6_SIT=m
CONFIG_NETFILTER=y
CONFIG_BRIDGE_NETFILTER=m
CONFIG_BRIDGE_NETFILTER=y
CONFIG_NF_CONNTRACK=m
CONFIG_NF_CONNTRACK_EVENTS=y
CONFIG_NF_CONNTRACK_FTP=m
@@ -131,6 +131,7 @@ CONFIG_BRIDGE_EBT_T_FILTER=m
CONFIG_BRIDGE_EBT_T_NAT=m
CONFIG_BRIDGE=y
CONFIG_BRIDGE_VLAN_FILTERING=y
CONFIG_NET_DSA=y
CONFIG_VLAN_8021Q=m
CONFIG_VLAN_8021Q_GVRP=y
CONFIG_NET_SCHED=y
@@ -149,9 +150,10 @@ CONFIG_NET_ACT_POLICE=m
CONFIG_NET_ACT_GACT=m
CONFIG_NET_ACT_MIRRED=m
CONFIG_NET_ACT_GATE=m
CONFIG_NET_SWITCHDEV=y
CONFIG_CGROUP_NET_PRIO=y
# CONFIG_WIRELESS is not set
CONFIG_NFC=y
CONFIG_NFC_TAG_ST25DV=y
CONFIG_PAGE_POOL_STATS=y
CONFIG_DEVTMPFS=y
CONFIG_DEVTMPFS_MOUNT=y
@@ -161,10 +163,12 @@ CONFIG_VEXPRESS_CONFIG=y
CONFIG_MHI_BUS=m
CONFIG_ARM_SCMI_PROTOCOL=y
CONFIG_ARM_SCPI_PROTOCOL=y
CONFIG_MTD=m
CONFIG_MTD=y
# CONFIG_MTD_OF_PARTS is not set
CONFIG_MTD_PHYSMAP=m
CONFIG_MTD_PHYSMAP_OF=y
CONFIG_MTD_PLATRAM=m
CONFIG_MTD_SPI_NOR=y
CONFIG_BLK_DEV_LOOP=y
CONFIG_BLK_DEV_NBD=m
CONFIG_SRAM=y
@@ -172,6 +176,7 @@ CONFIG_SRAM=y
CONFIG_EEPROM_AT24=y
CONFIG_SCSI=y
# CONFIG_SCSI_PROC_FS is not set
CONFIG_BLK_DEV_SD=y
# CONFIG_BLK_DEV_BSG is not set
CONFIG_SCSI_SAS_LIBSAS=y
# CONFIG_SCSI_LOWLEVEL is not set
@@ -182,6 +187,8 @@ CONFIG_IPVLAN=m
CONFIG_VXLAN=m
CONFIG_TUN=y
CONFIG_VETH=m
CONFIG_NET_DSA_MICROCHIP_KSZ_COMMON=y
CONFIG_NET_DSA_MICROCHIP_KSZ_SPI=y
# CONFIG_NET_VENDOR_ALACRITECH is not set
# CONFIG_NET_VENDOR_AMAZON is not set
# CONFIG_NET_VENDOR_AMD is not set
@@ -252,6 +259,7 @@ CONFIG_SERIAL_8250_DW=y
CONFIG_SERIAL_8250_OMAP=y
CONFIG_SERIAL_OF_PLATFORM=y
# CONFIG_SERIAL_OMAP_MODBUS is not set
CONFIG_RPMSG_TTY=y
CONFIG_HW_RANDOM=y
CONFIG_TCG_TPM=y
CONFIG_TCG_TIS=y
@@ -297,14 +305,20 @@ CONFIG_REGULATOR_TPS65219=y
CONFIG_USB=y
CONFIG_USB_ANNOUNCE_NEW_DEVICES=y
# CONFIG_USB_DEFAULT_PERSIST is not set
CONFIG_USB_OTG=y
CONFIG_USB_XHCI_HCD=y
CONFIG_USB_EHCI_HCD=y
CONFIG_USB_EHCI_ROOT_HUB_TT=y
CONFIG_USB_EHCI_HCD_PLATFORM=y
CONFIG_USB_OHCI_HCD=y
CONFIG_USB_OHCI_HCD_PLATFORM=y
CONFIG_USB_STORAGE=y
CONFIG_USB_MUSB_HDRC=y
CONFIG_USB_DWC3=y
CONFIG_USB_DWC3_GADGET=y
# CONFIG_USB_DWC3_KEYSTONE is not set
CONFIG_NOP_USB_XCEIV=y
CONFIG_USB_ULPI=y
CONFIG_USB_GADGET=y
CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS=32
CONFIG_USB_CONFIGFS=m
CONFIG_USB_CONFIGFS_SERIAL=y
CONFIG_USB_CONFIGFS_ACM=y
@@ -317,6 +331,7 @@ CONFIG_USB_CONFIGFS_EEM=y
CONFIG_USB_CONFIGFS_MASS_STORAGE=y
CONFIG_USB_CONFIGFS_F_FS=y
CONFIG_USB_FUNCTIONFS=m
CONFIG_USB_MASS_STORAGE=y
CONFIG_MMC=y
CONFIG_MMC_BLOCK_MINORS=32
CONFIG_MMC_ARMMMCI=y
@@ -326,12 +341,17 @@ 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
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
@@ -356,6 +376,14 @@ CONFIG_IOMMU_IO_PGTABLE_ARMV7S=y
CONFIG_IOMMU_IO_PGTABLE_DART=y
CONFIG_ARM_SMMU=y
CONFIG_ARM_SMMU_V3=y
CONFIG_REMOTEPROC=y
CONFIG_REMOTEPROC_CDEV=y
CONFIG_TI_K3_DSP_REMOTEPROC=y
CONFIG_TI_K3_M4_REMOTEPROC=y
CONFIG_TI_K3_R5_REMOTEPROC=y
CONFIG_RPMSG_CHAR=y
CONFIG_RPMSG_CTRL=y
CONFIG_RPMSG_VIRTIO=y
CONFIG_SOC_BRCMSTB=y
CONFIG_TI_SCI_PM_DOMAINS=y
CONFIG_PM_DEVFREQ=y
@@ -387,7 +415,6 @@ CONFIG_NTFS3_FS=m
CONFIG_TMPFS=y
CONFIG_TMPFS_POSIX_ACL=y
CONFIG_HUGETLBFS=y
CONFIG_CONFIGFS_FS=y
CONFIG_SQUASHFS=y
CONFIG_PSTORE=y
CONFIG_PSTORE_CONSOLE=y
@@ -436,8 +463,6 @@ CONFIG_SYSTEM_TRUSTED_KEYRING=y
CONFIG_PACKING=y
CONFIG_INDIRECT_PIO=y
CONFIG_CRC_CCITT=m
CONFIG_CRC_T10DIF=y
CONFIG_CRC64_ROCKSOFT=y
CONFIG_CRC_ITU_T=y
CONFIG_CRC7=y
CONFIG_CRC8=y
@@ -445,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
+10 -5
View File
@@ -42,22 +42,27 @@ static ssize_t multi_intensity_store(struct device *dev,
mutex_lock(&led_cdev->led_access);
for (i = 0; i < mcled_cdev->num_colors; i++) {
for (i = 0; i < LED_COLOR_ID_MAX; i++) {
ret = sscanf(buf + offset, "%i%n",
&intensity_value[i], &nrchars);
if (ret != 1) {
ret = -EINVAL;
goto err_out;
}
if (ret != 1)
break;
offset += nrchars;
}
/* Expect newline; reject surplus trailing chars */
offset++;
if (offset < size) {
ret = -EINVAL;
goto err_out;
}
/* Require at least the physical number of colors */
if (i < mcled_cdev->num_colors) {
ret = -EINVAL;
goto err_out;
}
for (i = 0; i < mcled_cdev->num_colors; i++)
mcled_cdev->subled_info[i].intensity = intensity_value[i];
+36
View File
@@ -51,4 +51,40 @@ 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-tty/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" channel using ASCII text commands and exposes each LED
as a Linux LED multiclass (RGB) device under
/sys/class/leds/m4-led<N>/.
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
+1
View File
@@ -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
+24 -12
View File
@@ -70,6 +70,7 @@ static int leds_gmc_probe(struct platform_device *pdev)
struct leds_multicolor *priv;
unsigned int max_brightness = 0;
int i, ret, count = 0;
bool allow_subled_writes = false;
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
@@ -121,28 +122,39 @@ static int leds_gmc_probe(struct platform_device *pdev)
priv->mc_cdev.num_colors = count;
init_data.fwnode = dev_fwnode(dev);
allow_subled_writes = fwnode_property_present(init_data.fwnode, "mc-allow-subled-writes");
ret = devm_led_classdev_multicolor_register_ext(dev, &priv->mc_cdev, &init_data);
if (ret)
return dev_err_probe(dev, ret, "failed to register multicolor LED for %s.\n",
cdev->name);
ret = leds_gmc_set(cdev, cdev->brightness);
if (ret)
return dev_err_probe(dev, ret, "failed to set LED value for %s.", cdev->name);
/* Honor DT default-state using LED core helper */
{
int def = led_init_default_state_get(dev_fwnode(dev));
if (def != LEDS_DEFSTATE_KEEP) {
ret = leds_gmc_set(cdev, cdev->brightness);
if (ret)
return dev_err_probe(dev, ret, "failed to set LED value for %s.", cdev->name);
}
}
for (i = 0; i < count; i++) {
struct led_classdev *led_cdev = priv->monochromatics[i];
/*
* Make the individual LED sysfs interface read-only to prevent the user
* to change the brightness of the individual LEDs of the group.
*/
mutex_lock(&led_cdev->led_access);
led_sysfs_disable(led_cdev);
mutex_unlock(&led_cdev->led_access);
/* Optionally keep sub-LED sysfs writable for legacy userspace */
if (!allow_subled_writes) {
/*
* Make the individual LED sysfs interface read-only to prevent the user
* to change the brightness of the individual LEDs of the group.
*/
mutex_lock(&led_cdev->led_access);
led_sysfs_disable(led_cdev);
mutex_unlock(&led_cdev->led_access);
/* Restore the write access to the LED sysfs when the group is destroyed */
devm_add_action_or_reset(dev, restore_sysfs_write_access, led_cdev);
/* Restore the write access to the LED sysfs when the group is destroyed */
devm_add_action_or_reset(dev, restore_sysfs_write_access, led_cdev);
}
}
return 0;
+84
View File
@@ -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 */
+900
View File
@@ -0,0 +1,900 @@
// 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 13 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_RETRY_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"
#define WAGO_LED_NUM_LEDS 10
#define WAGO_LED_NUM_CHANNELS 3 /* R, G, B */
/* 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 number of boot attempts before giving up */
#define WAGO_BOOT_MAX_RETRIES 150
/*
* 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: number of boot 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;
/* 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 void 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);
}
/* -------------------------------------------------------------------------
* 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 = 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", &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)
priv->rpdev = NULL;
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) {
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 v2");
MODULE_ALIAS("platform:" DRIVER_NAME);
+114 -15
View File
@@ -13,6 +13,9 @@
#include <linux/mutex.h>
#include <linux/slab.h>
#include <linux/timer.h>
#include <linux/ctype.h>
#include <linux/string.h>
#include <linux/led-class-multicolor.h>
#define MAX_PATTERNS 1024
/*
@@ -34,8 +37,56 @@ struct pattern_trig_data {
bool is_indefinite;
bool is_hw_pattern;
struct timer_list timer;
#if IS_ENABLED(CONFIG_LEDS_CLASS_MULTICOLOR)
struct led_classdev_mc *mcled;
#endif
};
#if IS_ENABLED(CONFIG_LEDS_CLASS_MULTICOLOR)
static void pattern_trig_apply_color(struct pattern_trig_data *data,
const struct led_pattern *pat)
{
int i;
unsigned int max_intensity;
bool has_red = false, has_green = false, has_blue = false;
if (!data->mcled || !pat->has_color)
return;
max_intensity = data->mcled->led_cdev.max_brightness;
for (i = 0; i < data->mcled->num_colors; i++) {
switch (data->mcled->subled_info[i].color_index) {
case LED_COLOR_ID_RED:
data->mcled->subled_info[i].intensity =
DIV_ROUND_CLOSEST(pat->red * max_intensity, 255);
has_red = true;
break;
case LED_COLOR_ID_GREEN:
data->mcled->subled_info[i].intensity =
DIV_ROUND_CLOSEST(pat->green * max_intensity, 255);
has_green = true;
break;
case LED_COLOR_ID_BLUE:
data->mcled->subled_info[i].intensity =
DIV_ROUND_CLOSEST(pat->blue * max_intensity, 255);
has_blue = true;
break;
default:
break;
}
}
if ((!has_red && pat->red) || (!has_green && pat->green) ||
(!has_blue && pat->blue))
dev_warn_once(data->led_cdev->dev,
"pattern: color component set for absent channel\n");
}
#else
static inline void pattern_trig_apply_color(struct pattern_trig_data *data,
const struct led_pattern *pat) { }
#endif
static void pattern_trig_update_patterns(struct pattern_trig_data *data)
{
data->curr = data->next;
@@ -81,6 +132,7 @@ static void pattern_trig_timer_function(struct timer_list *t)
if (data->curr->brightness == data->next->brightness) {
/* Step change of brightness */
pattern_trig_apply_color(data, data->curr);
led_set_brightness(data->led_cdev,
data->curr->brightness);
mod_timer(&data->timer,
@@ -104,6 +156,8 @@ static void pattern_trig_timer_function(struct timer_list *t)
continue;
}
if (data->delta_t == 0)
pattern_trig_apply_color(data, data->curr);
led_set_brightness(data->led_cdev,
pattern_trig_compute_brightness(data));
mod_timer(&data->timer,
@@ -207,10 +261,18 @@ static ssize_t pattern_trig_show_patterns(struct pattern_trig_data *data,
goto out;
for (i = 0; i < data->npatterns; i++) {
count += scnprintf(buf + count, PAGE_SIZE - count,
"%d %u ",
data->patterns[i].brightness,
data->patterns[i].delta_t);
const struct led_pattern *p = &data->patterns[i];
if (!hw_pattern && p->has_color)
count += scnprintf(buf + count, PAGE_SIZE - count,
"%d:%02x%02x%02x %u ",
p->brightness, p->red, p->green,
p->blue, p->delta_t);
else
count += scnprintf(buf + count, PAGE_SIZE - count,
"%d %u ",
p->brightness,
p->delta_t);
}
buf[count - 1] = '\n';
@@ -223,22 +285,52 @@ out:
static int pattern_trig_store_patterns_string(struct pattern_trig_data *data,
const char *buf, size_t count)
{
int ccount, cr, offset = 0;
int consumed, ccount;
unsigned int brightness, delta_t;
size_t offset = 0;
u8 r, g, b;
while (offset < count - 1 && data->npatterns < MAX_PATTERNS) {
cr = 0;
ccount = sscanf(buf + offset, "%u %u %n",
&data->patterns[data->npatterns].brightness,
&data->patterns[data->npatterns].delta_t, &cr);
while (offset < count && data->npatterns < MAX_PATTERNS) {
while (offset < count && isspace(buf[offset]))
offset++;
if (offset >= count)
break;
if (ccount != 2 ||
data->patterns[data->npatterns].brightness > data->led_cdev->max_brightness) {
if (!data->is_hw_pattern) {
consumed = 0;
ccount = sscanf(buf + offset,
"%u:%2hhx%2hhx%2hhx %u %n",
&brightness, &r, &g, &b,
&delta_t, &consumed);
if (ccount == 5) {
if (brightness > data->led_cdev->max_brightness) {
data->npatterns = 0;
return -EINVAL;
}
data->patterns[data->npatterns].brightness = brightness;
data->patterns[data->npatterns].delta_t = delta_t;
data->patterns[data->npatterns].has_color = true;
data->patterns[data->npatterns].red = r;
data->patterns[data->npatterns].green = g;
data->patterns[data->npatterns].blue = b;
offset += consumed;
data->npatterns++;
continue;
}
}
consumed = 0;
ccount = sscanf(buf + offset, "%u %u %n", &brightness, &delta_t, &consumed);
if (ccount != 2 || brightness > data->led_cdev->max_brightness) {
data->npatterns = 0;
return -EINVAL;
}
offset += cr;
data->patterns[data->npatterns].brightness = brightness;
data->patterns[data->npatterns].delta_t = delta_t;
data->npatterns++;
if (consumed == 0)
break;
offset += consumed;
}
return 0;
@@ -274,6 +366,8 @@ static ssize_t pattern_trig_store_patterns(struct led_classdev *led_cdev,
data->is_hw_pattern = hw_pattern;
data->npatterns = 0;
/* Zero all pattern entries so per-tuple color field clearing is unnecessary */
memset(data->patterns, 0, sizeof(data->patterns));
if (buf)
err = pattern_trig_store_patterns_string(data, buf, count);
@@ -404,6 +498,11 @@ static int pattern_trig_activate(struct led_classdev *led_cdev)
data->last_repeat = -1;
mutex_init(&data->lock);
data->led_cdev = led_cdev;
#if IS_ENABLED(CONFIG_LEDS_CLASS_MULTICOLOR)
if (led_cdev->color == LED_COLOR_ID_MULTI ||
led_cdev->color == LED_COLOR_ID_RGB)
data->mcled = lcdev_to_mccdev(led_cdev);
#endif
led_set_trigger_data(led_cdev, data);
timer_setup(&data->timer, pattern_trig_timer_function, 0);
led_cdev->activated = true;
@@ -459,5 +558,5 @@ module_exit(pattern_trig_exit);
MODULE_AUTHOR("Raphael Teysseyre <rteysseyre@gmail.com>");
MODULE_AUTHOR("Baolin Wang <baolin.wang@linaro.org>");
MODULE_DESCRIPTION("LED Pattern trigger");
MODULE_DESCRIPTION("LED Pattern trigger (optional per-node RGB)");
MODULE_LICENSE("GPL v2");
+36 -89
View File
@@ -20,101 +20,79 @@
#define DEFAULT_PERIOD 4 /* milliseconds */
#define NUM_BITS 8
struct sn74_gpio {
struct gpio_desc *gpiod;
bool active_low;
};
struct sn74_gpio_info {
struct sn74_gpio clk;
struct sn74_gpio load;
struct sn74_gpio input;
struct gpio_desc *clk;
struct gpio_desc *load;
struct gpio_desc *output;
};
struct sn74_platform_data {
struct sn74_gpio_info *ctrl_gpios;
struct sn74_gpio_info ctrl_gpios;
unsigned long period;
unsigned int bits;
};
static int sn74_get_input(struct sn74_gpio *gpio)
{
return (gpiod_get_value_cansleep(gpio->gpiod) ^ gpio->active_low);
}
static void sn74_set_ctrl_gpio(struct sn74_gpio *gpio, int what)
{
gpiod_set_value_cansleep(gpio->gpiod, what ^ gpio->active_low);
}
static ssize_t sn74_input_value_show(struct device *dev,
struct device_attribute *attr, char *buf)
static ssize_t value_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct sn74_platform_data *pdata;
struct sn74_gpio_info *ctrl_gpios;
unsigned long half_period;
int i, value = 0;
pr_debug("## %s:%d\n", __func__, __LINE__);
pdata = dev_get_drvdata(dev);
if (!pdata) {
dev_warn(dev, "%s:%d > could not get platform-data\n", __func__,
__LINE__);
return -1;
return -EINVAL;
}
ctrl_gpios = pdata->ctrl_gpios;
half_period = (pdata->period / 2);
ctrl_gpios = &pdata->ctrl_gpios;
half_period = pdata->period / 2;
/* load register */
sn74_set_ctrl_gpio(&ctrl_gpios->load, 1);
/* pulse load register to latch inputs */
gpiod_set_value_cansleep(ctrl_gpios->load, 1);
ndelay(half_period);
sn74_set_ctrl_gpio(&ctrl_gpios->load, 0);
gpiod_set_value_cansleep(ctrl_gpios->load, 0);
for (i = 0; i < pdata->bits; i++) {
value <<= 1;
value |= sn74_get_input(&ctrl_gpios->input);
value = (value << 1) |
gpiod_get_value_cansleep(ctrl_gpios->output);
/* do one clk cycle */
sn74_set_ctrl_gpio(&ctrl_gpios->clk, 1);
gpiod_set_value_cansleep(ctrl_gpios->clk, 1);
ndelay(half_period);
sn74_set_ctrl_gpio(&ctrl_gpios->clk, 0);
gpiod_set_value_cansleep(ctrl_gpios->clk, 0);
ndelay(half_period);
}
return sprintf(buf, "0x%02x\n", value);
return sysfs_emit(buf, "0x%02x\n", value);
}
/*
* ATTRIBUTES:
*
* /sys/devices/<dts-node-name>/value
* /sys/devices/platform/<dts-node-name>/value
*
*/
static DEVICE_ATTR(value, 0444, sn74_input_value_show, NULL);
static DEVICE_ATTR_RO(value);
static struct attribute *sn74_attrs[] = {
&dev_attr_value.attr,
NULL,
};
static struct attribute_group sn74_attr_group = {
static const struct attribute_group sn74_attr_group = {
.attrs = sn74_attrs,
};
#ifdef CONFIG_OF
static struct sn74_platform_data *sn74_get_devtree_pdata(struct device *dev)
{
struct device_node *node;
struct sn74_platform_data *pdata = NULL;
struct sn74_gpio_info *ctrl_gpios;
struct sn74_platform_data *pdata;
struct gpio_desc *gpiod;
int period, ret;
dev_dbg(dev, "## %s:%d\n", __func__, __LINE__);
node = dev->of_node;
if (!node) {
ret = -ENODEV;
@@ -128,54 +106,38 @@ static struct sn74_platform_data *sn74_get_devtree_pdata(struct device *dev)
goto err_free;
}
ctrl_gpios = devm_kzalloc(dev, sizeof(*ctrl_gpios), GFP_KERNEL);
if (!ctrl_gpios) {
ret = -ENOMEM;
goto err_free;
}
pdata->ctrl_gpios = ctrl_gpios;
if (!of_find_property(node, "gpios", NULL)) {
dev_err(dev, "failed to get control gpios\n");
ret = -ENODEV;
goto err_free;
}
gpiod = devm_gpiod_get_index(dev, NULL, 0, GPIOD_OUT_LOW);
if(IS_ERR(gpiod)) {
gpiod = devm_gpiod_get(dev, "dip-clk", GPIOD_OUT_LOW);
if (IS_ERR(gpiod)) {
dev_err(dev, "failed to get dip-clk gpio\n");
ret = PTR_ERR(gpiod);
goto err_free;
}
gpiod_set_consumer_name(gpiod, "sn74_clk");
pdata->ctrl_gpios->clk.gpiod = gpiod;
pdata->ctrl_gpios->clk.active_low = gpiod_is_active_low(gpiod);
pdata->ctrl_gpios.clk = gpiod;
gpiod = devm_gpiod_get_index(dev, NULL, 1, GPIOD_IN);
if(IS_ERR(gpiod)) {
gpiod = devm_gpiod_get(dev, "dip-output", GPIOD_IN);
if (IS_ERR(gpiod)) {
dev_err(dev, "failed to get dip-output gpio\n");
ret = PTR_ERR(gpiod);
goto err_free;
}
gpiod_set_consumer_name(gpiod, "sn74_input");
pdata->ctrl_gpios->input.gpiod = gpiod;
pdata->ctrl_gpios->input.active_low = gpiod_is_active_low(gpiod);
pdata->ctrl_gpios.output = gpiod;
gpiod = devm_gpiod_get_index(dev, NULL, 2, GPIOD_OUT_LOW);
if(IS_ERR(gpiod)) {
gpiod = devm_gpiod_get(dev, "dip-load", GPIOD_OUT_LOW);
if (IS_ERR(gpiod)) {
dev_err(dev, "failed to get dip-load gpio\n");
ret = PTR_ERR(gpiod);
goto err_free;
}
gpiod_set_consumer_name(gpiod, "sn74_load");
pdata->ctrl_gpios->load.gpiod = gpiod;
pdata->ctrl_gpios->load.active_low = gpiod_is_active_low(gpiod);
pdata->ctrl_gpios.load = gpiod;
if (of_property_read_u32(node, "clk,period", &period)) {
period = DEFAULT_PERIOD;
dev_warn(dev, "not freq value found, using default value: %d\n",
period);
}
pdata->period = (((unsigned long)period) *
1000000); /* convert to nano-seconds for udelay */
/* convert to nano-seconds for udelay */
pdata->period = (((unsigned long)period) * 1000000);
dev_dbg(dev, "clk period : %ld ns\n", pdata->period);
@@ -183,11 +145,9 @@ static struct sn74_platform_data *sn74_get_devtree_pdata(struct device *dev)
err_free:
devm_kfree(dev, pdata);
devm_kfree(dev, ctrl_gpios);
err_out:
return ERR_PTR(ret);
}
static const struct of_device_id sn74_of_match[] = {
@@ -198,23 +158,12 @@ static const struct of_device_id sn74_of_match[] = {
};
MODULE_DEVICE_TABLE(of, sn74_of_match);
#else
static struct sn74_platform_data *sn74_get_devtree_pdata(struct device *dev)
{
return ERR_PTR(-ENODEV);
}
#endif
static int sn74_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct sn74_platform_data *pdata;
int ret;
dev_dbg(dev, "## %s:%d\n", __func__, __LINE__);
pdata = dev_get_platdata(dev);
if (IS_ERR_OR_NULL(pdata)) {
pdata = sn74_get_devtree_pdata(dev);
@@ -239,8 +188,6 @@ static int sn74_remove(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
dev_dbg(dev, "## %s:%d\n", __func__, __LINE__);
sysfs_remove_group(&dev->kobj, &sn74_attr_group);
return 0;
+6 -5
View File
@@ -1381,11 +1381,12 @@ static int mtdchar_mmap(struct file *file, struct vm_area_struct *vma)
struct mtd_info *mtd = mfi->mtd;
struct map_info *map = mtd->priv;
/* This is broken because it assumes the MTD device is map-based
and that mtd->priv is a valid struct map_info. It should be
replaced with something that uses the mtd_get_unmapped_area()
operation properly. */
if (0 /*mtd->type == MTD_RAM || mtd->type == MTD_ROM*/) {
/*
* Only MTD_RAM devices that have a valid map_info with a physical
* address (e.g. MRAM on a Cadence OSPI DAC window) support mmap().
* All other MTD types return -ENODEV as before.
*/
if (mtd->type == MTD_RAM && map && map->phys != NO_XIP) {
#ifdef pgprot_noncached
if (file->f_flags & O_DSYNC || map->phys >= __pa(high_memory))
vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+90 -4
View File
@@ -14,6 +14,7 @@
#include <linux/math64.h>
#include <linux/module.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/map.h>
#include <linux/mtd/spi-nor.h>
#include <linux/mutex.h>
#include <linux/of_platform.h>
@@ -2003,6 +2004,7 @@ static const struct spi_nor_manufacturer *manufacturers[] = {
&spi_nor_eon,
&spi_nor_esmt,
&spi_nor_everspin,
&spi_mram_everspin,
&spi_nor_fujitsu,
&spi_nor_gigadevice,
&spi_nor_intel,
@@ -3419,8 +3421,24 @@ static void spi_nor_set_mtd_info(struct spi_nor *nor)
mtd->dev.parent = dev;
if (!mtd->name)
mtd->name = dev_name(dev);
mtd->type = MTD_NORFLASH;
mtd->flags = MTD_CAP_NORFLASH;
/*
* If a fixup hook has already populated mtd->priv with a struct
* map_info (e.g. Everspin MRAM on Cadence OSPI DAC window), honour
* that and advertise the device as byte-addressable RAM so that
* mtdchar_mmap() can call vm_iomap_memory() for zero-copy access.
* Otherwise fall back to the standard NOR-flash type.
*/
if (mtd->priv) {
mtd->type = MTD_RAM;
mtd->flags = MTD_CAP_RAM;
dev_info(dev,
"MTD type set to MTD_RAM (mmap enabled), "
"phys base: %pa\n",
&((struct map_info *)mtd->priv)->phys);
} else {
mtd->type = MTD_NORFLASH;
mtd->flags = MTD_CAP_NORFLASH;
}
/* Unset BIT_WRITEABLE to enable JFFS2 write buffer for ECC'd NOR */
if (nor->flags & SNOR_F_ECC)
mtd->flags &= ~MTD_BIT_WRITEABLE;
@@ -3441,13 +3459,81 @@ static void spi_nor_set_mtd_info(struct spi_nor *nor)
mtd->_put_device = spi_nor_put_device;
}
/**
* spi_nor_everspin_reboot_fix - Forces Everspin MRAM out of Octal mode
* @nor: pointer to 'struct spi_nor'
*
* This function sends a "blind" Software Reset sequence in Octal-STR (8-8-8).
* It is required for warm reboots because the MRAM stays in Octal mode
* while the controller restarts in Single-SPI mode.
*/
/**
* spi_nor_everspin_reboot_fix - Blind reset for MRAM Octal-STR escape
* @nor: pointer to 'struct spi_nor'
*/
static void spi_nor_everspin_reboot_fix(struct spi_nor *nor)
{
struct spi_mem_op op =
SPI_MEM_OP(SPI_MEM_OP_CMD(0x66, 8),
SPI_MEM_OP_NO_ADDR,
SPI_MEM_OP_NO_DUMMY,
SPI_MEM_OP_NO_DATA);
int ret;
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.
*/
dev_info(nor->dev, "MRAM Reboot-Fix: Sending blind Octal-STR reset (0x66->0x99)...\n");
/* 1. Reset Enable (0x66) in Octal-STR */
ret = spi_mem_exec_op(nor->spimem, &op);
/* 2. Reset (0x99) in Octal-STR */
op.cmd.opcode = 0x99;
ret |= spi_mem_exec_op(nor->spimem, &op);
if (ret) {
/*
* Note: A failure here is expected if the controller is
* not yet ready for 8-lane commands or if the chip
* is already in Single-SPI mode.
*/
dev_dbg(nor->dev, "MRAM Reboot-Fix: Octal reset op returned %d (normal for Single-SPI)\n", ret);
}
/* Essential delay: allow MRAM internal state machine to recover */
usleep_range(1000, 2000);
dev_info(nor->dev, "MRAM Reboot-Fix: Completed. Chip should be in Single-SPI mode now.\n");
}
static int spi_nor_hw_reset(struct spi_nor *nor)
{
struct gpio_desc *reset;
reset = devm_gpiod_get_optional(nor->dev, "reset", GPIOD_OUT_LOW);
if (IS_ERR_OR_NULL(reset))
return PTR_ERR_OR_ZERO(reset);
if (IS_ERR(reset))
return PTR_ERR(reset);
if (!reset) {
/*
* FALLBACK: No dedicated reset-pin found in DT.
* Execute the blind Octal-STR reset to recover from warm reboots.
*/
spi_nor_everspin_reboot_fix(nor);
return 0;
}
/*
* Experimental delay values by looking at different flash device
+1
View File
@@ -635,6 +635,7 @@ extern const struct spi_nor_manufacturer spi_nor_catalyst;
extern const struct spi_nor_manufacturer spi_nor_eon;
extern const struct spi_nor_manufacturer spi_nor_esmt;
extern const struct spi_nor_manufacturer spi_nor_everspin;
extern const struct spi_nor_manufacturer spi_mram_everspin;
extern const struct spi_nor_manufacturer spi_nor_fujitsu;
extern const struct spi_nor_manufacturer spi_nor_gigadevice;
extern const struct spi_nor_manufacturer spi_nor_intel;
+420 -1
View File
@@ -5,9 +5,394 @@
*/
#include <linux/mtd/spi-nor.h>
#include <linux/mtd/map.h>
#include <linux/delay.h>
#include <linux/platform_device.h>
#include <linux/spi/spi.h>
#include <linux/spi/spi-mem.h>
#include "core.h"
/* Private data stored in nor->priv for MRAM mmap support */
struct everspin_mram_priv {
struct map_info map;
};
/* Optimization for 200 MHz: 20 Dummy Cycles are typically required */
#define EVERSPIN_MRAM_DUMMY_CYCLES 8
#define EVERSPIN_MRAM_DUMMY_CYCLES_FAST 20
#define EVERSPIN_MRAM_SPEED_MHZ 200000000
#define SPINOR_OP_MT_RD_ANY_REG 0x85 /* Read volatile configuration register */
#define SPINOR_OP_MT_WR_ANY_REG 0x81 /* Write volatile configuration register */
#define SPINOR_REG_MT_CFR0V 0x00 /* Address for Mode Configuration */
#define SPINOR_REG_MT_CFR1V 0x01 /* Address for Dummy Cycle Configuration */
#define SPINOR_MT_OCT_STR 0xB7 /* Enable Octal STR mode with DS */
#define SPINOR_OP_EV_OCTAL_FAST_READ 0xCB /* Everspin Octal Fast Read (8-8-8) */
#define SPINOR_OP_EV_OCTAL_PAGE_PROG 0x82 /* Everspin Octal Page Program (8-8-8) */
/**
* everspin_mram_software_reset - Software Reset in Single-SPI mode
*/
static int everspin_mram_software_reset(struct spi_nor *nor)
{
struct spi_mem_op op;
int ret;
/* Software Reset Enable (SPINOR_OP_SRSTEN: 0x66) */
op = (struct spi_mem_op)SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_SRSTEN, 1),
SPI_MEM_OP_NO_ADDR,
SPI_MEM_OP_NO_DUMMY,
SPI_MEM_OP_NO_DATA);
ret = spi_nor_write_any_volatile_reg(nor, &op, nor->reg_proto);
if (ret)
return ret;
/* Software Reset (SPINOR_OP_SRST: 0x99) */
op = (struct spi_mem_op)SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_SRST, 1),
SPI_MEM_OP_NO_ADDR,
SPI_MEM_OP_NO_DUMMY,
SPI_MEM_OP_NO_DATA);
ret = spi_nor_write_any_volatile_reg(nor, &op, nor->reg_proto);
if (ret)
return ret;
udelay(100);
return 0;
}
/**
* everspin_mram_write_reg - Writes to configuration registers (4-byte addr)
*/
static int everspin_mram_write_reg(struct spi_nor *nor, u32 addr, u8 val)
{
struct spi_mem_op op =
SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_MT_WR_ANY_REG, 1),
SPI_MEM_OP_ADDR(3, addr, 1),
SPI_MEM_OP_NO_DUMMY,
SPI_MEM_OP_DATA_OUT(1, nor->bouncebuf, 1));
nor->bouncebuf[0] = val;
spi_nor_write_enable(nor);
return spi_nor_write_any_volatile_reg(nor, &op, SNOR_PROTO_1_1_1);
}
/**
* everspin_mram_read_reg_octal - Reads a register in 8s-8s-8s mode
* Uses Opcode 0x85, 3-byte address, and 8 dummy cycles.
*/
static int everspin_mram_read_reg_octal(struct spi_nor *nor, u32 addr, u8 *val)
{
struct spi_mem_op op =
SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_MT_RD_ANY_REG, 8),
SPI_MEM_OP_ADDR(3, addr, 8),
SPI_MEM_OP_DUMMY(8, 8), /* Fixed 8 dummies for reg read */
SPI_MEM_OP_DATA_IN(1, nor->bouncebuf, 8));
int ret;
ret = spi_nor_read_any_reg(nor, &op, SNOR_PROTO_8_8_8);
if (ret)
return ret;
*val = nor->bouncebuf[0];
return 0;
}
/**
* everspin_mram_write_reg_octal - Writes a register in 8s-8s-8s mode
* Uses Opcode 0x81, 3-byte address, and 0 dummy cycles.
*/
static int everspin_mram_write_reg_octal(struct spi_nor *nor, u32 addr, u8 val)
{
struct spi_mem_op op =
SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_MT_WR_ANY_REG, 8),
SPI_MEM_OP_ADDR(3, addr, 8),
SPI_MEM_OP_NO_DUMMY,
SPI_MEM_OP_DATA_OUT(1, nor->bouncebuf, 8));
nor->bouncebuf[0] = val;
/* WREN is required before writing to volatile registers */
spi_nor_write_enable(nor);
return spi_nor_write_any_volatile_reg(nor, &op, SNOR_PROTO_8_8_8);
}
/**
* everspin_mram_unlock - Clears Block Protection bits
*/
static int everspin_mram_unlock(struct spi_nor *nor)
{
struct spi_mem_op op;
spi_nor_write_enable(nor);
nor->bouncebuf[0] = 0x00;
op = (struct spi_mem_op)SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_WRSR, 1),
SPI_MEM_OP_NO_ADDR,
SPI_MEM_OP_NO_DUMMY,
SPI_MEM_OP_DATA_OUT(1, nor->bouncebuf, 1));
return spi_nor_write_any_volatile_reg(nor, &op, nor->reg_proto);
}
static void everspin_mram_default_init(struct spi_nor *nor)
{
struct spi_mem_op op;
struct spi_mem_op op_ri; /* for read id */
int ret;
/* Do a Software Reset to get sure we are in a clean state.
* Not necessary if a hardware reset is already done in NOR layer.
*/
everspin_mram_software_reset(nor);
dev_info(nor->dev, "Starting Everspin MRAM initialization (Octal-STR, %d Dummies) ...\n",
EVERSPIN_MRAM_DUMMY_CYCLES);
/* Initial SR1 Check */
op = (struct spi_mem_op)SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_RDSR, 1),
SPI_MEM_OP_NO_ADDR,
SPI_MEM_OP_NO_DUMMY,
SPI_MEM_OP_DATA_IN(1, nor->bouncebuf, 1));
ret = spi_nor_read_any_reg(nor, &op, SNOR_PROTO_1_1_1);
if (!ret) {
u8 sr1 = nor->bouncebuf[0];
dev_info(nor->dev, "Initial SR1: 0x%02x (BP-Bits: 0x%x)\n", sr1, (sr1 & 0x3c) >> 2);
if (sr1 & GENMASK(5, 2))
everspin_mram_unlock(nor);
}
/* Configuration: Set Dummy Cycles in CFR1V */
everspin_mram_write_reg(nor, SPINOR_REG_MT_CFR1V, EVERSPIN_MRAM_DUMMY_CYCLES);
/* Verify WEL */
ret = spi_nor_read_any_reg(nor, &op, SNOR_PROTO_1_1_1);
if (!ret)
dev_info(nor->dev, "SR1 after config: 0x%02x (WEL should be 0x00)\n", nor->bouncebuf[0]);
/* Setup Octal-STR with DS */
everspin_mram_write_reg(nor, SPINOR_REG_MT_CFR0V, SPINOR_MT_OCT_STR);
/* Status Register Read (05h) in Octal STR Mode (8s-0-8s) with 8 Dummies */
struct spi_mem_op op_rsr = SPI_MEM_OP(
/* Command: Opcode 05h, sent on 8 lanes */
SPI_MEM_OP_CMD(SPINOR_OP_RDSR, 8),
/* Address: None */
SPI_MEM_OP_NO_ADDR,
/* EVERSPIN_MRAM_DUMMY_CYCLES cycles, sent on 8 lanes */
SPI_MEM_OP_DUMMY(EVERSPIN_MRAM_DUMMY_CYCLES, 8),
/* Data: 1 byte (Status), received on 8 lanes */
SPI_MEM_OP_DATA_IN(1, nor->bouncebuf, 8)
);
/* Execute using the framework helper with Octal-STR protocol */
ret = spi_nor_read_any_reg(nor, &op_rsr, SNOR_PROTO_8_8_8);
if (!ret) {
/* Access the result from the DMA-safe bounce buffer */
u8 status = nor->bouncebuf[0];
dev_info(nor->dev, "MRAM Status Register (8s-0-8s): 0x%02x\n", status);
}
dev_info(nor->dev, "Going to access Read ID (0x9f) in 8s-0-8s Mode ...\n");
/* Read ID (0x9f) access in Octal-STR mode (8s-0-8s) with 8 Dummy Bytes */
op_ri = (struct spi_mem_op)SPI_MEM_OP(
/* Opcode 0x9F, 1 Byte long, sent on 8 lanes */
SPI_MEM_OP_CMD(SPINOR_OP_RDID, 8),
/* No address phase */
SPI_MEM_OP_NO_ADDR,
/* EVERSPIN_MRAM_DUMMY_CYCLES Dummy Cycles, sent on 8 lanes */
SPI_MEM_OP_DUMMY(EVERSPIN_MRAM_DUMMY_CYCLES, 8),
/* Read 3 bytes of ID data, received on 8 lanes */
SPI_MEM_OP_DATA_IN(3, nor->bouncebuf, 8)
);
/* Protokoll 8-8-8 für 8s-0-8s (Octal STR) */
ret = spi_nor_read_any_reg(nor, &op_ri, SNOR_PROTO_8_8_8);
if (!ret) {
/* Access the 3 bytes from bouncebuf */
u8 manufacturer_id = nor->bouncebuf[0];
u8 memory_type = nor->bouncebuf[1];
u8 capacity = nor->bouncebuf[2];
/* Log the result in hex format */
dev_info(nor->dev, "MRAM INFO: Octal Read ID: %02x %02x %02x.\n",
manufacturer_id, memory_type, capacity);
} else {
dev_info(nor->dev, "Read ID Access in 8s-0-8s mode FAILED!\n");
}
}
/**
* everspin_mram_ready_noop - MRAM is always ready, no polling needed
*/
static int everspin_mram_ready_noop(struct spi_nor *nor)
{
return 1;
}
/**
* everspin_mram_setup - Finalizes the SPI setup and escalates frequency
* @nor: pointer to 'struct spi_nor'
* @hwcaps: pointer to 'struct spi_nor_hwcaps'
*
* This hook is called after default_init/late_init but before the first
* data transfer. It is the ideal place to switch from safe scan frequency
* to high-performance operational frequency.
*/
static int everspin_mram_setup(struct spi_nor *nor,
const struct spi_nor_hwcaps *hwcaps)
{
struct spi_nor_flash_parameter *params = nor->params;
struct spi_device *spi = nor->spimem->spi;
int ret;
u8 val = 0;
/*
* Request high speed frequency.
*/
spi->max_speed_hz = EVERSPIN_MRAM_SPEED_MHZ;
dev_info(nor->dev, "Escalating speed to %u Hz in setup hook\n",
spi->max_speed_hz);
/* Update Dummy Bytes to serve the higher rate EVERSPIN_MRAM_SPEED_MHZ */
nor->read_dummy = EVERSPIN_MRAM_DUMMY_CYCLES_FAST;
spi_nor_set_read_settings(&params->reads[SNOR_CMD_READ], 0,
EVERSPIN_MRAM_DUMMY_CYCLES_FAST,
SPINOR_OP_EV_OCTAL_FAST_READ, SNOR_PROTO_8_8_8);
everspin_mram_write_reg_octal(nor, SPINOR_REG_MT_CFR1V,
EVERSPIN_MRAM_DUMMY_CYCLES_FAST);
everspin_mram_read_reg_octal(nor, SPINOR_REG_MT_CFR1V, &val);
dev_info(nor->dev, "Updating Dummy Bytes to %d (read: %d)\n", nor->read_dummy, val);
/*
* Force the controller driver (cadence-qspi) to re-run
* cqspi_config_baudrate_div() and update hardware registers.
*/
ret = spi_setup(spi);
if (ret) {
dev_err(nor->dev, "Failed to update SPI setup (%d)\n", ret);
return ret;
}
return 0;
}
/**
* everspin_mram_late_init - Final 8-8-8 STR configuration for EM008LXO
*/
static int everspin_mram_late_init(struct spi_nor *nor)
{
struct spi_nor_flash_parameter *params = nor->params;
struct platform_device *ctlr_pdev;
struct everspin_mram_priv *priv;
struct resource *res_ahb;
dev_info(nor->dev, "Finalizing 8s-8s-8s STR: Write/Read fully functional.\n");
/* Bypass core hwcaps restrictions.
* We need this as a workaround.
*/
params->hwcaps.mask |= SNOR_HWCAPS_READ | SNOR_HWCAPS_PP;
/* Global Octal STR Protocol Settings */
nor->read_proto = SNOR_PROTO_8_8_8;
nor->write_proto = SNOR_PROTO_8_8_8;
nor->reg_proto = SNOR_PROTO_8_8_8;
nor->read_opcode = SPINOR_OP_EV_OCTAL_FAST_READ;
/* Set global read dummy cycles */
nor->read_dummy = EVERSPIN_MRAM_DUMMY_CYCLES;
nor->program_opcode = SPINOR_OP_EV_OCTAL_PAGE_PROG;
nor->addr_nbytes = 3;
params->addr_nbytes = 3;
/* Disable WIP Polling (Fixes the 40s Timeout) */
params->ready = everspin_mram_ready_noop;
/* Align Page Size for Controller Stability */
/* Cadence OSPI handles 256-byte pages more reliably in Octal mode */
params->page_size = 256;
/* Map Opcodes to standard slots for MTD core */
spi_nor_set_read_settings(&params->reads[SNOR_CMD_READ], 0,
EVERSPIN_MRAM_DUMMY_CYCLES,
SPINOR_OP_EV_OCTAL_FAST_READ, SNOR_PROTO_8_8_8);
spi_nor_set_pp_settings(&params->page_programs[SNOR_CMD_PP],
SPINOR_OP_EV_OCTAL_PAGE_PROG, SNOR_PROTO_8_8_8);
nor->params->setup = everspin_mram_setup;
/*
* Set up MTD_RAM so that /dev/mtd0 supports mmap().
*
* The Cadence OSPI controller exposes the flash memory as a directly
* accessible window in the CPU address space (DAC / AHB mode). We
* read the physical base address of that window from the controller's
* platform_device resource[1] the same resource that cqspi_probe()
* uses for ioremap. This lets mtdchar_mmap() call vm_iomap_memory()
* and hand the physical pages directly to user-space.
*/
priv = devm_kzalloc(nor->dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
/*
* nor->spimem->spi->controller->dev.parent is the platform_device
* of the Cadence QSPI controller. Its IORESOURCE_MEM[1] is the AHB
* (DAC) window index 0 is the APB register block.
*/
ctlr_pdev = to_platform_device(nor->spimem->spi->controller->dev.parent);
res_ahb = platform_get_resource(ctlr_pdev, IORESOURCE_MEM, 1);
if (!res_ahb) {
dev_warn(nor->dev,
"MRAM: cannot find AHB resource mmap() disabled\n");
devm_kfree(nor->dev, priv);
return 0;
}
priv->map.name = nor->mtd.name;
priv->map.phys = res_ahb->start;
priv->map.size = resource_size(res_ahb);
priv->map.virt = NULL; /* not needed for mmap path */
nor->priv = priv;
/*
* mtd->type and mtd->flags will be set to MTD_RAM / MTD_CAP_RAM
* by spi_nor_set_mtd_info() in core.c once it sees mtd->priv != NULL.
* mtdchar_mmap() will then call vm_iomap_memory() via map->phys.
*/
nor->mtd.priv = &priv->map;
dev_info(nor->dev,
"MRAM: AHB window at %pa size %pa mmap() enabled\n",
&res_ahb->start, &priv->map.size);
return 0;
}
static const struct spi_nor_fixups everspin_mram_fixups = {
.default_init = everspin_mram_default_init,
.late_init = everspin_mram_late_init,
};
static const struct flash_info everspin_nor_parts[] = {
/* Everspin */
{ "mr25h128", CAT25_INFO(16 * 1024, 1, 256, 2) },
@@ -16,8 +401,42 @@ static const struct flash_info everspin_nor_parts[] = {
{ "mr25h40", CAT25_INFO(512 * 1024, 1, 256, 3) },
};
static const struct flash_info everspin_mram_parts[] = {
/* Everspin */
{ "em256lx", INFO(0x6bbb19, 0, 32 * 1024 * 1024, 1)
FLAGS(SPI_NOR_NO_ERASE)
.fixups = &everspin_mram_fixups,
},
{ "em128lx", INFO(0x6bbb18, 0, 16 * 1024 * 1024, 1)
FLAGS(SPI_NOR_NO_ERASE)
.fixups = &everspin_mram_fixups,
},
{ "em064lx", INFO(0x6bbb17, 0, 8 * 1024 * 1024, 1)
FLAGS(SPI_NOR_NO_ERASE)
.fixups = &everspin_mram_fixups,
},
{ "em032lx", INFO(0x6bbb16, 0, 4 * 1024 * 1024, 1)
FLAGS(SPI_NOR_NO_ERASE)
.fixups = &everspin_mram_fixups,
},
{ "em016lx", INFO(0x6bbb15, 0, 2 * 1024 * 1024, 1)
FLAGS(SPI_NOR_NO_ERASE)
.fixups = &everspin_mram_fixups,
},
{ "em008lx", INFO(0x6bbb14, 0, 1 * 1024 * 1024, 1)
FLAGS(SPI_NOR_NO_ERASE)
.fixups = &everspin_mram_fixups,
},
};
const struct spi_nor_manufacturer spi_nor_everspin = {
.name = "everspin",
.parts = everspin_nor_parts,
.nparts = ARRAY_SIZE(everspin_nor_parts),
};
const struct spi_nor_manufacturer spi_mram_everspin = {
.name = "everspin_mram",
.parts = everspin_mram_parts,
.nparts = ARRAY_SIZE(everspin_mram_parts),
};
+7
View File
@@ -39,3 +39,10 @@ config NET_DSA_MICROCHIP_KSZ8863_SMI
help
Select to enable support for registering switches configured through
Microchip SMI. It supports the KSZ8863 and KSZ8873 switch.
config NET_DSA_MICROCHIP_KSZ9477_SYSFS
tristate "SYSFS interface for userspace KSZ register access"
depends on NET_DSA_MICROCHIP_KSZ_COMMON
help
Select to enable support for accessing KSZ registers in userspace
through the sysfs interface.
+2
View File
@@ -10,7 +10,9 @@ ksz_switch-objs += ksz_ptp.o
endif
ksz_switch-objs += ksz8_ethtool.o
ksz_switch-objs += ksz9477_ethtool.o
ksz_switch-objs += ksz_devlink.o
obj-$(CONFIG_NET_DSA_MICROCHIP_KSZ9477_I2C) += ksz9477_i2c.o
obj-$(CONFIG_NET_DSA_MICROCHIP_KSZ_SPI) += ksz_spi.o
obj-$(CONFIG_NET_DSA_MICROCHIP_KSZ8863_SMI) += ksz8863_smi.o
obj-$(CONFIG_NET_DSA_MICROCHIP_KSZ9477_SYSFS) += ksz9477_sysfs.o
+42 -86
View File
@@ -2,64 +2,17 @@
#include "ksz8_ethtool.h"
#include "ksz8.h"
enum {
KSZ8_REGDUMP_ID_GLOBAL = 0x1100,
KSZ8_REGDUMP_ID_PORT = 0x1200,
KSZ8_REGDUMP_ID_STAT_MAC_TBL = 0x2000,
KSZ8_REGDUMP_ID_FDB = 0x2100,
};
struct ksz_regdump_hdr {
u32 module_id;
u32 len;
};
struct ksz_regdump_record {
struct ksz_regdump_hdr hdr;
u32 start_off;
u32 end_off;
};
// creates a regdump record entry
// note we use u32 to transport register values, despite their size in
// the device
#define KSZ_REGDUMP_ENTRY(id, start, end) { \
.hdr.module_id = (id), \
.hdr.len = (((end) - (start)) * sizeof(u32)) * 2 \
+ sizeof(struct ksz_regdump_hdr), \
.start_off = (start), \
.end_off = (end), \
}
#define SIZE_IS_MULTIPLE_OF(T, T2) (sizeof(T) == (DIV_ROUND_UP(sizeof(T), sizeof(T2))* sizeof(T2)))
// sizeof(ksz8_dyn_mac_entry) == 12
struct ksz8_dyn_mac_entry {
/*entry 1*/
u32 : 24;
u8 timestamp;
/* entry 2 & 3 */
u8 fid;
u8 src_port;
u8 mac[ETH_ALEN];
};
#define KSZ8_REGDUMP_MAC_ENTRY(id, entry_size, max_entries) { \
.hdr.module_id = (id), \
.hdr.len = 2 * sizeof(u32) + (entry_size) * (max_entries) \
+ sizeof(struct ksz_regdump_hdr), \
.start_off = 0, \
.end_off = 0, \
}
#include "ksz_ethtool.h"
static const struct ksz_regdump_record ksz8_regdump_records[] = {
KSZ_REGDUMP_ENTRY(KSZ8_REGDUMP_ID_GLOBAL, 0x00, 0x10),
KSZ_REGDUMP_ENTRY(KSZ8_REGDUMP_ID_PORT, 0x10, 0x20),
KSZ_REGDUMP_ENTRY(KSZ8_REGDUMP_ID_PORT + 1, 0x20, 0x30),
KSZ_REGDUMP_ENTRY(KSZ8_REGDUMP_ID_PORT + 2, 0x30, 0x40),
KSZ8_REGDUMP_MAC_ENTRY(KSZ8_REGDUMP_ID_STAT_MAC_TBL, sizeof(struct alu_struct), 8),
KSZ8_REGDUMP_MAC_ENTRY(KSZ8_REGDUMP_ID_FDB, sizeof(struct ksz8_dyn_mac_entry), 1024),
KSZ_REGDUMP_ENTRY(KSZ_REGDUMP_ID_GLOBAL, 0x00, 0x10),
KSZ_REGDUMP_ENTRY(KSZ_REGDUMP_ID_PORT, 0x10, 0x20),
KSZ_REGDUMP_ENTRY(KSZ_REGDUMP_ID_PORT + 1, 0x20, 0x30),
KSZ_REGDUMP_ENTRY(KSZ_REGDUMP_ID_PORT + 2, 0x30, 0x40),
KSZ8_REGDUMP_MAC_ENTRY(KSZ_REGDUMP_ID_STAT_MAC_TBL,
sizeof(struct alu_struct), 8),
KSZ8_REGDUMP_MAC_ENTRY(KSZ_REGDUMP_ID_FDB,
sizeof(struct ksz8_dyn_mac_entry), 1024),
};
int ksz8_get_regs_len(struct ksz_device *dev, int port)
@@ -72,20 +25,22 @@ int ksz8_get_regs_len(struct ksz_device *dev, int port)
return length;
}
static void ksz8_get_regs_dump_static_mac_table(struct ksz_device *dev, u32 *reg, u32 *pos)
static void ksz8_get_regs_dump_static_mac_table(struct ksz_device *dev,
u32 *reg, u32 *pos)
{
BUILD_BUG_ON(!SIZE_IS_MULTIPLE_OF(struct alu_struct, u32));
int index, ret;
u32 cnt = *pos;
reg[cnt++] = KSZ8_REGDUMP_ID_STAT_MAC_TBL;
struct ksz_regdump_hdr *hdr = (struct ksz_regdump_hdr *)&reg[cnt];
const u32 pos_length = cnt++;
reg[pos_length] = sizeof(struct ksz_regdump_hdr);
hdr->module_id = KSZ_REGDUMP_ID_STAT_MAC_TBL;
hdr->len = sizeof(struct ksz_regdump_hdr);
cnt += 2;
for (index = 0; index < dev->info->num_statics; index++) {
bool valid;
struct alu_struct* alu = (struct alu_struct*)&reg[cnt];
struct alu_struct *alu = (struct alu_struct *)&reg[cnt];
ret = ksz8_r_sta_mac_table(dev, index, alu, &valid);
if (ret)
@@ -94,7 +49,7 @@ static void ksz8_get_regs_dump_static_mac_table(struct ksz_device *dev, u32 *reg
if (!valid)
continue;
reg[pos_length] += sizeof(struct alu_struct);
hdr->len += sizeof(struct alu_struct);
cnt += sizeof(struct alu_struct) / sizeof(u32);
}
*pos = cnt;
@@ -107,55 +62,56 @@ static void ksz8_get_regs_dump_fdb(struct ksz_device *dev, u32 *reg, u32 *pos)
u16 entries = 0;
u32 cnt = *pos;
reg[cnt++] = KSZ8_REGDUMP_ID_FDB;
struct ksz_regdump_hdr *hdr = (struct ksz_regdump_hdr *)&reg[cnt];
const u32 pos_length = cnt++;
reg[pos_length] = sizeof(struct ksz_regdump_hdr);
hdr->module_id = KSZ_REGDUMP_ID_FDB;
hdr->len = sizeof(struct ksz_regdump_hdr);
cnt += 2;
do {
struct ksz8_dyn_mac_entry *entry = (struct ksz8_dyn_mac_entry*) &reg[cnt];
int ret = ksz8_r_dyn_mac_table(dev, i, entry->mac, &entry->fid, &entry->src_port,
&entry->timestamp, &entries);
struct ksz8_dyn_mac_entry *entry =
(struct ksz8_dyn_mac_entry *)&reg[cnt];
int ret = ksz8_r_dyn_mac_table(dev, i, entry->mac, &entry->fid,
&entry->src_port,
&entry->timestamp, &entries);
if (!ret) {
reg[pos_length] += sizeof(struct ksz8_dyn_mac_entry);
cnt += DIV_ROUND_UP(sizeof(struct ksz8_dyn_mac_entry), sizeof(u32));
hdr->len += sizeof(struct ksz8_dyn_mac_entry);
cnt += DIV_ROUND_UP(sizeof(struct ksz8_dyn_mac_entry),
sizeof(u32));
}
i++;
} while (i < entries);
*pos = cnt;
}
void ksz8_get_regs(struct ksz_device *dev, int port,
struct ethtool_regs *regs, void *p)
void ksz8_get_regs(struct ksz_device *dev, int port, struct ethtool_regs *regs,
void *p)
{
int i, j;
int i;
u32 pos = 0;
u32 *reg = p;
for (i = 0; i < ARRAY_SIZE(ksz8_regdump_records); i++) {
struct ksz_regdump_record rec = ksz8_regdump_records[i];
struct ksz_regdump_hdr *hdr =
(struct ksz_regdump_hdr *)&reg[pos];
if (rec.hdr.module_id == KSZ8_REGDUMP_ID_STAT_MAC_TBL) {
if (rec.hdr.module_id == KSZ_REGDUMP_ID_STAT_MAC_TBL) {
ksz8_get_regs_dump_static_mac_table(dev, reg, &pos);
continue;
}
if (rec.hdr.module_id == KSZ8_REGDUMP_ID_FDB) {
if (rec.hdr.module_id == KSZ_REGDUMP_ID_FDB) {
ksz8_get_regs_dump_fdb(dev, reg, &pos);
continue;
}
reg[pos++] = rec.hdr.module_id;
reg[pos++] = rec.hdr.len;
for (j = rec.start_off;
j < rec.end_off; j++) {
u8 val;
reg[pos++] = j;
ksz_read8(dev, j, &val);
reg[pos++] = val;
}
pos += 2;
hdr->module_id = rec.hdr.module_id;
hdr->len = pos;
ksz_dump_regmap_range(dev, reg, &pos, rec.start_off,
rec.end_off);
hdr->len = (pos - hdr->len + 2) * sizeof(u32);
}
}
+2
View File
@@ -4,8 +4,10 @@
#define __KSZ8XXX_ETHTOOL_H
#include <linux/types.h>
#include "ksz_ethtool.h"
#include "ksz_common.h"
int ksz8_get_regs_len(struct ksz_device *dev, int port);
void ksz8_get_regs(struct ksz_device *dev, int port,
struct ethtool_regs *regs, void *p);
+5
View File
@@ -18,6 +18,7 @@
#include "ksz9477_reg.h"
#include "ksz_common.h"
#include "ksz9477.h"
#include "ksz9477_sysfs.h"
static void ksz_cfg(struct ksz_device *dev, u32 addr, u8 bits, bool set)
{
@@ -1185,6 +1186,10 @@ int ksz9477_switch_init(struct ksz_device *dev)
if (ret)
return ret;
#ifdef CONFIG_NET_DSA_MICROCHIP_KSZ9477_SYSFS
ksz_sysfs_register(dev);
#endif
return 0;
}
@@ -0,0 +1,61 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#include "ksz9477_ethtool.h"
#include "ksz_ethtool.h"
static const struct ksz_regdump_record ksz9477_regdump_records[] = {
KSZ_REGDUMP_ENTRY(KSZ_REGDUMP_ID_GLOBAL, 0x00, 0x700),
KSZ_REGDUMP_ENTRY(KSZ_REGDUMP_ID_PORT + 0, 0x1000, 0x1fff),
KSZ_REGDUMP_ENTRY(KSZ_REGDUMP_ID_PORT + 1, 0x2000, 0x2fff),
KSZ_REGDUMP_ENTRY(KSZ_REGDUMP_ID_PORT + 2, 0x3000, 0x3fff),
KSZ_REGDUMP_ENTRY(KSZ_REGDUMP_ID_PORT + 3, 0x4000, 0x4fff),
KSZ_REGDUMP_ENTRY(KSZ_REGDUMP_ID_PORT + 4, 0x5000, 0x5fff),
KSZ_REGDUMP_ENTRY(KSZ_REGDUMP_ID_PORT + 5, 0x6000, 0x6fff),
KSZ_REGDUMP_ENTRY(KSZ_REGDUMP_ID_PORT + 6, 0x7000, 0x7fff),
};
int ksz9477_get_regs_len(struct ksz_device *dev, int port)
{
int i, length = 0;
const struct regmap_range *yes_ranges = dev->info->rd_table->yes_ranges;
for (i = 0; i < dev->info->rd_table->n_yes_ranges; i++) {
const struct regmap_range range = yes_ranges[i];
length += (range.range_max - range.range_min + 1) *
sizeof(u32) * 2;
}
// ports + global + stat + fdb
length += (7 + 1 + 1 + 1) * sizeof(struct ksz_regdump_hdr);
return length;
}
void ksz9477_get_regs(struct ksz_device *dev, int port,
struct ethtool_regs *regs, void *p)
{
int i;
u32 pos = 0;
u32 *reg = p;
for (i = 0; i < ARRAY_SIZE(ksz9477_regdump_records); i++) {
struct ksz_regdump_record rec = ksz9477_regdump_records[i];
struct ksz_regdump_hdr *hdr =
(struct ksz_regdump_hdr *)&reg[pos];
hdr->module_id = rec.hdr.module_id;
pos += 2;
if (rec.hdr.module_id == KSZ_REGDUMP_ID_STAT_MAC_TBL) {
continue;
}
if (rec.hdr.module_id == KSZ_REGDUMP_ID_FDB) {
continue;
}
hdr->len = pos;
ksz_dump_regmap_range(dev, reg, &pos, rec.start_off,
rec.end_off);
hdr->len = (pos - hdr->len + 2) * sizeof(u32);
}
}
@@ -0,0 +1,14 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#ifndef __KSZ9477_ETHTOOL_H
#define __KSZ9477_ETHTOOL_H
#include <linux/types.h>
#include "ksz_ethtool.h"
#include "ksz_common.h"
int ksz9477_get_regs_len(struct ksz_device *dev, int port);
void ksz9477_get_regs(struct ksz_device *dev, int port,
struct ethtool_regs *regs, void *p);
#endif
+128
View File
@@ -0,0 +1,128 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* KSZ9477 Sysfs Register Access (Bypassing Regmap Whitelist)
*/
#include <linux/device.h>
#include <linux/regmap.h> // NOTE: Required for direct regmap access
#include "ksz_common.h"
#include "ksz9477_reg.h"
#include "ksz9477_sysfs.h"
#define to_kszdev(d) container_of(d, struct ksz_device, dev)
struct ksz_device *g_kdev = NULL;
static ssize_t ksz_read_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
u32 reg, value = 0;
int ret, bit;
char end;
if (!g_kdev || !g_kdev->regmap)
return -EINVAL;
ret = sscanf(buf, "%d %x%c", &bit, &reg, &end);
if (ret < 1) return -EINVAL;
/*
* Use regmap_bulk_read to bypass the kernel's internal address
* validation. regmap_read would immediately return -EIO for
* 'illegal' addresses.
*/
switch(bit) {
case 8:
ret = regmap_bulk_read(g_kdev->regmap[0], reg, (u8*)&value, 1);
break;
case 16:
/* KSZ expects big-endian on the bus, regmap bulk handles that */
ret = regmap_bulk_read(g_kdev->regmap[1], reg, (u16*)&value, 1);
break;
case 32:
ret = regmap_bulk_read(g_kdev->regmap[2], reg, &value, 1);
break;
default:
return -EINVAL;
}
if (ret < 0) {
dev_err(dev, "Read failed at 0x%x with error %d\n", reg, ret);
return ret;
}
dev_info(dev, "%d-bit read: reg 0x%04x: 0x%08x.\n", bit, reg, value);
return count;
}
static DEVICE_ATTR_WO(ksz_read);
static ssize_t ksz_write_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
u32 reg, value;
int ret, bit;
char end;
if (!g_kdev || !g_kdev->regmap)
return -EINVAL;
ret = sscanf(buf, "%d %x %x%c", &bit, &reg, &value, &end);
if (ret < 1) return -EINVAL;
switch(bit) {
case 8:
ret = regmap_bulk_write(g_kdev->regmap[0], reg, (u8*)&value, 1);
break;
case 16:
ret = regmap_bulk_write(g_kdev->regmap[1], reg, (u16*)&value, 1);
break;
case 32:
ret = regmap_bulk_write(g_kdev->regmap[2], reg, &value, 1);
break;
default:
return -EINVAL;
}
if (ret < 0) {
dev_err(dev, "Write failed at 0x%x\n", reg);
return ret;
}
dev_info(dev, "%d-bit write: reg 0x%04x value 0x%08x.\n", bit, reg, value);
return count;
}
static DEVICE_ATTR_WO(ksz_write);
int ksz_sysfs_register(struct ksz_device *kdev)
{
struct device *dev = kdev->dev;
int ret;
if (!kdev)
return -EINVAL;
ret = device_create_file(dev, &dev_attr_ksz_read);
if (ret)
dev_err(dev, "Could not create <%s> attribute.\n", "ksz_read");
ret = device_create_file(dev, &dev_attr_ksz_write);
if (ret)
dev_err(dev, "Could not create <%s> attribute.\n", "ksz_write");
g_kdev = kdev;
return ret;
}
void ksz_sysfs_unregister(struct ksz_device *kdev)
{
struct device *dev = kdev->dev;
g_kdev = NULL;
device_remove_file(dev, &dev_attr_ksz_read);
device_remove_file(dev, &dev_attr_ksz_write);
}
@@ -0,0 +1,8 @@
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef KSZ9477_SYSFS_H_
#define KSZ9477_SYSFS_H_
int ksz_sysfs_register(struct ksz_device *kdev);
void ksz_sysfs_unregister(struct ksz_device *kdev);
#endif /* KSZ9477_SYSFS_H_ */
+58 -9
View File
@@ -33,6 +33,7 @@
#include "lan937x.h"
#include "ksz_devlink.h"
#include "ksz8_ethtool.h"
#include "ksz9477_ethtool.h"
#define MIB_COUNTER_NUM 0x20
@@ -218,9 +219,9 @@ static const struct ksz_dev_ops ksz8_dev_ops = {
.change_mtu = ksz8_change_mtu,
.devlink_param_setup = ksz8_setup_devlink_params,
.devlink_param_teardown = ksz8_teardown_devlink_params,
.devlink_param_get = ksz8_devlink_param_get,
.devlink_param_set = ksz8_devlink_param_set,
.devlink_info_get= ksz8_devlink_info_get,
.devlink_param_get = ksz_devlink_param_get,
.devlink_param_set = ksz_devlink_param_set,
.devlink_info_get = ksz_devlink_info_get,
.get_regs = ksz8_get_regs,
.get_regs_len = ksz8_get_regs_len,
};
@@ -262,9 +263,16 @@ static const struct ksz_dev_ops ksz9477_dev_ops = {
.config_cpu_port = ksz9477_config_cpu_port,
.tc_cbs_set_cinc = ksz9477_tc_cbs_set_cinc,
.enable_stp_addr = ksz9477_enable_stp_addr,
.devlink_info_get = ksz_devlink_info_get,
.devlink_param_get = ksz_devlink_param_get,
.devlink_param_set = ksz_devlink_param_set,
.devlink_param_setup = ksz9477_setup_devlink_params,
.devlink_param_teardown = ksz9477_teardown_devlink_params,
.reset = ksz9477_reset_switch,
.init = ksz9477_switch_init,
.exit = ksz9477_switch_exit,
.get_regs = ksz9477_get_regs,
.get_regs_len = ksz9477_get_regs_len,
};
static const struct ksz_dev_ops lan937x_dev_ops = {
@@ -439,6 +447,9 @@ static const u16 ksz9477_regs[] = {
[S_MULTICAST_CTRL] = 0x0331,
[P_XMII_CTRL_0] = 0x0300,
[P_XMII_CTRL_1] = 0x0301,
[P_PHY_MMD_SETUP] = 0x011A,
[P_PHY_MMD_DATA] = 0x011C,
[P_PHY_DIGITAL_DEBUG_3] = 0x013C,
};
static const u32 ksz9477_masks[] = {
@@ -2947,6 +2958,39 @@ static void ksz_set_gbit(struct ksz_device *dev, int port, bool gbit)
ksz_pwrite8(dev, port, regs[P_XMII_CTRL_1], data8);
}
static void ksz_enable_single_led_mode(struct ksz_device *dev, int port)
{
const u16 *regs = dev->info->regs;
u16 data16;
dev_info(dev->dev, "port-%d: enable single-led mode.\n", port);
/* Set up register address for MMD */
data16 = 0x0002;
ksz_pwrite16(dev, port, regs[P_PHY_MMD_SETUP], data16);
/* Select Register 00h of MMD */
data16 = 0x0000;
ksz_pwrite16(dev, port, regs[P_PHY_MMD_DATA], data16);
/* Select register data for MMD */
data16 = 0x4002;
ksz_pwrite16(dev, port, regs[P_PHY_MMD_SETUP], data16);
/* Write value 0010h to MMD */
data16 = 0x0011;
ksz_pwrite16(dev, port, regs[P_PHY_MMD_DATA], data16);
/* Due to Errata Module 19 this workaround is needed to
* enable single-led mode. It has also to be a 32bit
* write.
*/
dev_info(dev->dev, "port-%d: applying errata (Module 19) " \
"workaround.\n", port);
data16 = 0xfa00;
ksz_pwrite16(dev, port, regs[P_PHY_DIGITAL_DEBUG_3], data16);
}
static void ksz_set_100_10mbit(struct ksz_device *dev, int port, int speed)
{
const u8 *bitval = dev->info->xmii_ctrl0;
@@ -3012,6 +3056,9 @@ static void ksz9477_phylink_mac_link_up(struct ksz_device *dev, int port,
{
struct ksz_port *p;
if (dev->single_led_mode && port != dev->cpu_port)
ksz_enable_single_led_mode(dev, port);
p = &dev->ports[port];
/* Internal PHYs */
@@ -3442,7 +3489,7 @@ static int ksz_devlink_param_setup(struct dsa_switch *ds)
return 0;
}
static int ksz_devlink_info_get(struct dsa_switch *ds, struct devlink_info_req *req, struct netlink_ext_ack *extack) {
static int ksz_get_devlink_info(struct dsa_switch *ds, struct devlink_info_req *req, struct netlink_ext_ack *extack) {
struct ksz_device* dev = ds->priv;
@@ -3452,7 +3499,7 @@ static int ksz_devlink_info_get(struct dsa_switch *ds, struct devlink_info_req *
return -EOPNOTSUPP;
}
static int ksz_devlink_param_get(struct dsa_switch *ds, u32 id,
static int ksz_get_devlink_param(struct dsa_switch *ds, u32 id,
struct devlink_param_gset_ctx *ctx)
{
struct ksz_device *dev = ds->priv;
@@ -3463,7 +3510,7 @@ static int ksz_devlink_param_get(struct dsa_switch *ds, u32 id,
return -EOPNOTSUPP;
}
static int ksz_devlink_param_set(struct dsa_switch *ds, u32 id,
static int ksz_set_devlink_param(struct dsa_switch *ds, u32 id,
struct devlink_param_gset_ctx *ctx)
{
struct ksz_device *dev = ds->priv;
@@ -3538,9 +3585,9 @@ static const struct dsa_switch_ops ksz_switch_ops = {
.port_setup_tc = ksz_setup_tc,
.get_mac_eee = ksz_get_mac_eee,
.set_mac_eee = ksz_set_mac_eee,
.devlink_param_get = ksz_devlink_param_get,
.devlink_param_set = ksz_devlink_param_set,
.devlink_info_get = ksz_devlink_info_get,
.devlink_param_get = ksz_get_devlink_param,
.devlink_param_set = ksz_set_devlink_param,
.devlink_info_get = ksz_get_devlink_info,
.get_regs = ksz_get_regs,
.get_regs_len = ksz_get_regs_len,
};
@@ -3736,6 +3783,8 @@ int ksz_switch_register(struct ksz_device *dev)
ret = of_property_read_u8(dev->dev->of_node, "microchip,led-mode", &dev->led_mode);
if (ret)
dev->led_mode = 0;
dev->single_led_mode = of_property_read_bool(dev->dev->of_node,
"microchip,single-led-mode");
}
ret = dsa_register_switch(dev->ds);
+4
View File
@@ -150,6 +150,7 @@ struct ksz_device {
struct gpio_desc *reset_gpio; /* Optional reset GPIO */
bool disable_internal_ldo; /* Disable internal 1.8V LDO */
u8 led_mode;
bool single_led_mode; /* Enable Single LED Mode */
/* chip specific data */
u32 chip_id;
u8 chip_rev;
@@ -232,6 +233,9 @@ enum ksz_regs {
S_MULTICAST_CTRL,
P_XMII_CTRL_0,
P_XMII_CTRL_1,
P_PHY_MMD_SETUP,
P_PHY_MMD_DATA,
P_PHY_DIGITAL_DEBUG_3,
};
enum ksz_masks {
+96 -19
View File
@@ -7,13 +7,20 @@
#include <net/devlink.h>
#include <net/dsa.h>
#include "ksz8795_reg.h"
#include "ksz_common.h"
#define KSZ8_P_BCAST_STORM_CTRL 0
#define KSZ8_PORT_BROADCAST_STORM BIT(7)
#define KSZ9477_P_BCAST_STORM_CTRL 0x400
#define KSZ9477_PORT_BROADCAST_STORM BIT(1)
static void ksz_port_cfg(struct ksz_device *dev, int port, int offset, u8 bits,
bool set)
{
regmap_update_bits(ksz_regmap_8(dev), PORT_CTRL_ADDR(port, offset),
regmap_update_bits(ksz_regmap_8(dev), dev->dev_ops->get_port_addr(port, offset),
bits, set ? bits : 0);
}
@@ -23,9 +30,11 @@ enum ksz_devlink_param_id {
KSZ_DEVLINK_PARAM_ID_MCAST_STORM_RATE_DISABLE,
KSZ_DEVLINK_PARAM_ID_PORT_BASE,
KSZ_DEVLINK_PARAM_ID_PORT_MAX = KSZ_DEVLINK_PARAM_ID_PORT_BASE + 3,
KSZ9477_DEVLINK_PARAM_ID_PORT_BASE,
KSZ9477_DEVLINK_PARAM_ID_PORT_MAX = KSZ9477_DEVLINK_PARAM_ID_PORT_BASE + 8,
};
static const struct devlink_param ksz_devlink_params[] = {
static const struct devlink_param ksz8_devlink_params[] = {
DSA_DEVLINK_PARAM_DRIVER(KSZ_DEVLINK_PARAM_ID_BROADCAST_STORM_RATE,
"bcast storm rate", DEVLINK_PARAM_TYPE_U16,
BIT(DEVLINK_PARAM_CMODE_RUNTIME)),
@@ -48,19 +57,73 @@ static const struct devlink_param ksz_devlink_params[] = {
};
static const struct devlink_param ksz9477_devlink_params[] = {
DSA_DEVLINK_PARAM_DRIVER(KSZ_DEVLINK_PARAM_ID_BROADCAST_STORM_RATE,
"bcast storm rate", DEVLINK_PARAM_TYPE_U16,
BIT(DEVLINK_PARAM_CMODE_RUNTIME)),
DSA_DEVLINK_PARAM_DRIVER(KSZ_DEVLINK_PARAM_ID_MCAST_STORM_RATE_DISABLE,
"mcast storm rate disable",
DEVLINK_PARAM_TYPE_BOOL,
BIT(DEVLINK_PARAM_CMODE_RUNTIME)),
DSA_DEVLINK_PARAM_DRIVER(KSZ9477_DEVLINK_PARAM_ID_PORT_BASE,
"bcast storm protection enable port 0",
DEVLINK_PARAM_TYPE_BOOL,
BIT(DEVLINK_PARAM_CMODE_RUNTIME)),
DSA_DEVLINK_PARAM_DRIVER(KSZ9477_DEVLINK_PARAM_ID_PORT_BASE + 1,
"bcast storm protection enable port 1",
DEVLINK_PARAM_TYPE_BOOL,
BIT(DEVLINK_PARAM_CMODE_RUNTIME)),
DSA_DEVLINK_PARAM_DRIVER(KSZ9477_DEVLINK_PARAM_ID_PORT_BASE + 2,
"bcast storm protection enable port 2",
DEVLINK_PARAM_TYPE_BOOL,
BIT(DEVLINK_PARAM_CMODE_RUNTIME)),
DSA_DEVLINK_PARAM_DRIVER(KSZ9477_DEVLINK_PARAM_ID_PORT_BASE + 3,
"bcast storm protection enable port 3",
DEVLINK_PARAM_TYPE_BOOL,
BIT(DEVLINK_PARAM_CMODE_RUNTIME)),
DSA_DEVLINK_PARAM_DRIVER(KSZ9477_DEVLINK_PARAM_ID_PORT_BASE + 4,
"bcast storm protection enable port 4",
DEVLINK_PARAM_TYPE_BOOL,
BIT(DEVLINK_PARAM_CMODE_RUNTIME)),
DSA_DEVLINK_PARAM_DRIVER(KSZ9477_DEVLINK_PARAM_ID_PORT_BASE + 5,
"bcast storm protection enable port 5",
DEVLINK_PARAM_TYPE_BOOL,
BIT(DEVLINK_PARAM_CMODE_RUNTIME)),
DSA_DEVLINK_PARAM_DRIVER(KSZ9477_DEVLINK_PARAM_ID_PORT_BASE + 6,
"bcast storm protection enable port 6",
DEVLINK_PARAM_TYPE_BOOL,
BIT(DEVLINK_PARAM_CMODE_RUNTIME)),
DSA_DEVLINK_PARAM_DRIVER(KSZ9477_DEVLINK_PARAM_ID_PORT_BASE + 7,
"bcast storm protection enable port 7",
DEVLINK_PARAM_TYPE_BOOL,
BIT(DEVLINK_PARAM_CMODE_RUNTIME)),
};
int ksz8_setup_devlink_params(struct dsa_switch *ds)
{
return dsa_devlink_params_register(ds, ksz_devlink_params,
ARRAY_SIZE(ksz_devlink_params));
return dsa_devlink_params_register(ds, ksz8_devlink_params,
ARRAY_SIZE(ksz8_devlink_params));
}
void ksz8_teardown_devlink_params(struct dsa_switch *ds)
{
dsa_devlink_params_unregister(ds, ksz_devlink_params,
ARRAY_SIZE(ksz_devlink_params));
dsa_devlink_params_unregister(ds, ksz8_devlink_params,
ARRAY_SIZE(ksz8_devlink_params));
}
int ksz8_devlink_param_get(struct ksz_device *dev, u32 id,
int ksz9477_setup_devlink_params(struct dsa_switch *ds)
{
return dsa_devlink_params_register(ds, ksz9477_devlink_params,
ARRAY_SIZE(ksz9477_devlink_params));
}
void ksz9477_teardown_devlink_params(struct dsa_switch *ds)
{
dsa_devlink_params_unregister(ds, ksz9477_devlink_params,
ARRAY_SIZE(ksz9477_devlink_params));
}
int ksz_devlink_param_get(struct ksz_device *dev, u32 id,
struct devlink_param_gset_ctx *ctx)
{
const u16 *regs = dev->info->regs;
@@ -84,9 +147,19 @@ int ksz8_devlink_param_get(struct ksz_device *dev, u32 id,
int port = id - KSZ_DEVLINK_PARAM_ID_PORT_BASE;
u8 val;
ksz_pread8(dev, port, P_BCAST_STORM_CTRL, &val);
ctx->val.vbool = (val & PORT_BROADCAST_STORM) ==
PORT_BROADCAST_STORM;
ksz_pread8(dev, port, KSZ8_P_BCAST_STORM_CTRL, &val);
ctx->val.vbool = (val & KSZ8_PORT_BROADCAST_STORM) ==
KSZ8_PORT_BROADCAST_STORM;
} break;
case KSZ9477_DEVLINK_PARAM_ID_PORT_BASE ... KSZ9477_DEVLINK_PARAM_ID_PORT_MAX: {
int port = id - KSZ9477_DEVLINK_PARAM_ID_PORT_BASE;
u8 val;
ksz_pread8(dev, port, KSZ9477_P_BCAST_STORM_CTRL, &val);
ctx->val.vbool = (val & KSZ9477_PORT_BROADCAST_STORM) ==
KSZ9477_PORT_BROADCAST_STORM;
} break;
default:
@@ -96,7 +169,7 @@ int ksz8_devlink_param_get(struct ksz_device *dev, u32 id,
return 0;
}
int ksz8_devlink_param_set(struct ksz_device *dev, u32 id,
int ksz_devlink_param_set(struct ksz_device *dev, u32 id,
struct devlink_param_gset_ctx *ctx)
{
const u16 *regs;
@@ -128,8 +201,15 @@ int ksz8_devlink_param_set(struct ksz_device *dev, u32 id,
case KSZ_DEVLINK_PARAM_ID_PORT_BASE ... KSZ_DEVLINK_PARAM_ID_PORT_MAX: {
int port = id - KSZ_DEVLINK_PARAM_ID_PORT_BASE;
ksz_port_cfg(dev, port, P_BCAST_STORM_CTRL,
PORT_BROADCAST_STORM, ctx->val.vbool);
ksz_port_cfg(dev, port, KSZ8_P_BCAST_STORM_CTRL,
KSZ8_PORT_BROADCAST_STORM, ctx->val.vbool);
} break;
case KSZ9477_DEVLINK_PARAM_ID_PORT_BASE ... KSZ9477_DEVLINK_PARAM_ID_PORT_MAX: {
int port = id - KSZ9477_DEVLINK_PARAM_ID_PORT_BASE;
ksz_port_cfg(dev, port, KSZ9477_P_BCAST_STORM_CTRL,
KSZ9477_PORT_BROADCAST_STORM, ctx->val.vbool);
} break;
default:
@@ -139,12 +219,9 @@ int ksz8_devlink_param_set(struct ksz_device *dev, u32 id,
return 0;
}
int ksz8_devlink_info_get(struct ksz_device *dev, struct devlink_info_req *req,
int ksz_devlink_info_get(struct ksz_device *dev, struct devlink_info_req *req,
struct netlink_ext_ack *extack)
{
return devlink_info_version_fixed_put(
req, DEVLINK_INFO_VERSION_GENERIC_ASIC_ID,
dev->info->dev_name);
req, DEVLINK_INFO_VERSION_GENERIC_ASIC_ID, dev->info->dev_name);
}
+6 -3
View File
@@ -13,12 +13,15 @@
int ksz8_setup_devlink_params(struct dsa_switch *ds);
void ksz8_teardown_devlink_params(struct dsa_switch *ds);
int ksz8_devlink_param_get(struct ksz_device *dev, u32 id,
int ksz9477_setup_devlink_params(struct dsa_switch *ds);
void ksz9477_teardown_devlink_params(struct dsa_switch *ds);
int ksz_devlink_param_get(struct ksz_device *dev, u32 id,
struct devlink_param_gset_ctx *ctx);
int ksz8_devlink_param_set(struct ksz_device *dev, u32 id,
int ksz_devlink_param_set(struct ksz_device *dev, u32 id,
struct devlink_param_gset_ctx *ctx);
int ksz8_devlink_info_get(struct ksz_device *dev, struct devlink_info_req *req,
int ksz_devlink_info_get(struct ksz_device *dev, struct devlink_info_req *req,
struct netlink_ext_ack *extack);
#endif
+88
View File
@@ -0,0 +1,88 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#ifndef __KSZ_ETHTOOL_H
#define __KSZ_ETHTOOL_H
#include <linux/types.h>
#include <linux/if_ether.h>
#include <linux/regmap.h>
#include "ksz_common.h"
// sizeof(ksz8_dyn_mac_entry) == 12
struct ksz8_dyn_mac_entry {
/*entry 1*/
u32 : 24;
u8 timestamp;
/* entry 2 & 3 */
u8 fid;
u8 src_port;
u8 mac[ETH_ALEN];
};
struct ksz_regdump_hdr {
u32 module_id;
u32 len;
};
struct ksz_regdump_record {
struct ksz_regdump_hdr hdr;
u32 start_off;
u32 end_off;
};
enum {
KSZ_REGDUMP_ID_GLOBAL = 0x1100,
KSZ_REGDUMP_ID_PORT = 0x1200,
KSZ_REGDUMP_ID_STAT_MAC_TBL = 0x2000,
KSZ_REGDUMP_ID_FDB = 0x2100,
};
// creates a regdump record entry
// note we use u32 to transport register values, despite their size in
// the device
#define KSZ_REGDUMP_ENTRY(id, start, end) { \
.hdr.module_id = (id), \
.hdr.len = (((end) - (start)) * sizeof(u32)) * 2 \
+ sizeof(struct ksz_regdump_hdr), \
.start_off = (start), \
.end_off = (end), \
}
#define SIZE_IS_MULTIPLE_OF(T, T2) (sizeof(T) == (DIV_ROUND_UP(sizeof(T), sizeof(T2))* sizeof(T2)))
#define KSZ8_REGDUMP_MAC_ENTRY(id, entry_size, max_entries) { \
.hdr.module_id = (id), \
.hdr.len = 2 * sizeof(u32) + (entry_size) * (max_entries) \
+ sizeof(struct ksz_regdump_hdr), \
.start_off = 0, \
.end_off = 0, \
}
static inline void ksz_dump_regmap_range(struct ksz_device *dev, u32 *regs, u32 *pos,
u32 start, u32 end)
{
int i, length = *pos;
const struct regmap_range *yes_ranges = dev->info->rd_table->yes_ranges;
for (i = 0; i < dev->info->rd_table->n_yes_ranges; i++) {
const struct regmap_range range = yes_ranges[i];
u32 reg;
if (range.range_max < start || range.range_min >= end)
continue;
for (reg = range.range_min; reg <= range.range_max; reg++) {
u8 val;
ksz_read8(dev, reg, &val);
regs[length++] = reg;
regs[length++] = val;
}
}
*pos = length;
}
#endif
+4 -15
View File
@@ -2343,13 +2343,12 @@ EXPORT_SYMBOL(genphy_aneg_done);
*/
int genphy_update_link(struct phy_device *phydev)
{
int status = 0, bmcr, i;
int status = 0, bmcr;
bmcr = phy_read(phydev, MII_BMCR);
if (bmcr < 0)
return bmcr;
/* Autoneg is being started, therefore disregard BMSR value and
* report link as down.
*/
@@ -2370,19 +2369,9 @@ int genphy_update_link(struct phy_device *phydev)
}
/* Read link and autonegotiation status */
for (i = 0; i < 20; i++) {
status = phy_read(phydev, MII_BMSR);
if (status < 0)
return status;
if (status & BMSR_LSTATUS)
break;
cpu_relax();
}
if (status & BMSR_LSTATUS)
if (i >= 1)
phydev_info(phydev, "link up after multiple reads: %di\n", i);
status = phy_read(phydev, MII_BMSR);
if (status < 0)
return status;
done:
phydev->link = status & BMSR_LSTATUS ? 1 : 0;
phydev->autoneg_complete = status & BMSR_ANEGCOMPLETE ? 1 : 0;
+1
View File
@@ -70,4 +70,5 @@ source "drivers/nfc/st-nci/Kconfig"
source "drivers/nfc/nxp-nci/Kconfig"
source "drivers/nfc/s3fwrn5/Kconfig"
source "drivers/nfc/st95hf/Kconfig"
source "drivers/nfc/tag/Kconfig"
endmenu
+1
View File
@@ -18,3 +18,4 @@ obj-$(CONFIG_NFC_NXP_NCI) += nxp-nci/
obj-$(CONFIG_NFC_S3FWRN5) += s3fwrn5/
obj-$(CONFIG_NFC_ST95HF) += st95hf/
obj-$(CONFIG_NFC_VIRTUAL_NCI) += virtual_ncidev.o
obj-y += tag/
+5
View File
@@ -0,0 +1,5 @@
menu "NFC Tag devices"
depends on NFC
source "drivers/nfc/tag/st25dv/Kconfig"
endmenu
+6
View File
@@ -0,0 +1,6 @@
# SPDX-License-Identifier: GPL-2.0
#
# Makefile for nfc tag devices
#
obj-$(CONFIG_NFC_TAG_ST25DV) += st25dv/
+6
View File
@@ -0,0 +1,6 @@
# SPDX-License-Identifier: GPL-2.0-only
config NFC_TAG_ST25DV
tristate "STMicroelectronics ST25DVxxK nfc tag driver"
depends on I2C
help
This is the STMicroelectronics NFC Tag driver for the ST25DVxxK devices.
+7
View File
@@ -0,0 +1,7 @@
# SPDX-License-Identifier: GPL-2.0-only
#
# Makefile for ST25DV NFC driver
#
# st25dv-y += st25dv.o
obj-$(CONFIG_NFC_TAG_ST25DV) += st25dv.o
+443
View File
@@ -0,0 +1,443 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2018 Loïc Boban <loic.boban@gmail.com>
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/device.h>
#include <linux/i2c.h>
#include <linux/uaccess.h>
#include <linux/delay.h>
#include <linux/mutex.h>
#define DYN_REG_SIZE 0x8
#define SYS_MEM_SIZE 0x24
#define USER_MEM_SIZE 0x200
#define MAILBOX_MEM_SIZE 0x100
#define SYS_ADDR 0x57
#define USER_ADDR 0x53
#define PWD_OFF 0x0900
#define DYN_REG_OFF 0x2000
#define MAILBOX_OFF 0x2008
#define MAX_TRY 10
#define PWD_REQ_SIZE 0x13
#define PWD_SIZE 0x08
#define CMD_PRESENT_PWD 0x09
#define CMD_WRITE_PWD 0x07
#define PWD_CMD_POS 10
#define PWD1_POS 2
#define PWD2_POS 11
#define MEM_04K 512
#define MEM_16K 2000
#define MEM_64K 8000
static const struct bin_attribute st25dv_p_pwd_attr;
enum area_type {
USER_AREA = 0,
SYS_AREA = 1,
DYN_REG_AREA = 2,
MAILBOX_AREA = 3,
};
/*
* The ST25DV EEPROM has two areas: the user area and the system
* area to manage read/write protection for the NFC interface and
* I2C interface. To drive the system area, a dummy i2c_client is used.
*/
static int mem_config[] = {MEM_04K, MEM_04K, MEM_16K, MEM_64K};
static int area_off[] = {0, 0, DYN_REG_OFF, MAILBOX_OFF};
/* one struct is used for each area */
struct st25dv_data {
u8 *data; /* area data */
enum area_type type;
struct bin_attribute bin_attr;
struct i2c_client *client;
struct mutex *update_lock; /* protect for concurrent updates */
struct st25dv_data *next;
};
/* Addresses to scan */
static const unsigned short normal_i2c[] = { USER_ADDR, SYS_ADDR, I2C_CLIENT_END };
static ssize_t st25dv_read(struct file *filp, struct kobject *kobj,
struct bin_attribute *bin_attr, char *buf, loff_t off, size_t count)
{
int r_size, nack;
struct i2c_client *client = to_i2c_client(kobj_to_dev(kobj));
struct st25dv_data *data = i2c_get_clientdata(client);
u16 cur_off;
while (&data->bin_attr != bin_attr)
data = data->next;
off += area_off[data->type];
mutex_lock(data->update_lock);
for (cur_off = off; cur_off-off < count; cur_off++) {
nack = 0;
retry_:
if (nack > MAX_TRY) {
mutex_unlock(data->update_lock);
return r_size;
}
r_size = i2c_smbus_write_byte_data(client, (cur_off >> 8) & 0x0ff, cur_off & 0x0ff);
if (r_size < 0) {
nack++;
udelay(150);
goto retry_;
}
r_size = i2c_smbus_read_byte(client);
if (r_size < 0) {
nack++;
udelay(150);
goto retry_;
}
data->data[cur_off] = r_size;
}
memcpy(buf, &data->data[off], count);
mutex_unlock(data->update_lock);
dev_dbg(&client->dev, "%zu bytes read\n", count);
return count;
}
/*
* I2C_SMBUS_BLOCK_MAX = 9 page writes
* MAX tw = 9 * 5ms
* write_block is faster than write single byte but not supported by some adapter
*/
static ssize_t st25dv_write_block(struct file *filp, struct kobject *kobj,
struct bin_attribute *bin_attr, char *buf, loff_t off, size_t count)
{
int r_size, to_write, not_write;
struct i2c_client *client = to_i2c_client(kobj_to_dev(kobj));
struct st25dv_data *data = i2c_get_clientdata(client);
u16 cur_off, cur_buf_off;
u8 nack, tmp[I2C_SMBUS_BLOCK_MAX];
while (&data->bin_attr != bin_attr)
data = data->next;
off += area_off[data->type];
mutex_lock(data->update_lock);
memcpy(data->data + off, buf, count);
not_write = count;
cur_off = off;
cur_buf_off = 0;
while (not_write) {
nack = 0;
to_write = not_write > I2C_SMBUS_BLOCK_MAX-2 ? I2C_SMBUS_BLOCK_MAX-2 : not_write;
not_write -= to_write;
tmp[1] = cur_off;
tmp[0] = cur_off >> 8;
memcpy(tmp + 2, buf + cur_buf_off, to_write);
retry_:
if (nack > MAX_TRY) {
mutex_unlock(data->update_lock);
return r_size;
}
r_size = i2c_master_send(client, tmp, to_write+2);
if (r_size < 0) {
nack++;
mdelay(5);
goto retry_;
}
mdelay(20);
cur_off += to_write;
cur_buf_off += to_write;
}
mutex_unlock(data->update_lock);
dev_dbg(&client->dev, "%zu bytes written\n", count);
return count;
}
static ssize_t st25dv_send_pwd_req(struct file *filp, struct kobject *kobj,
struct bin_attribute *bin_attr,
char *buf, loff_t off, size_t count)
{
u8 pwd_req[PWD_REQ_SIZE], cmd, *pwd_ptr1, *pwd_ptr2;
int r_size, off_tmp, nack;
struct i2c_client *client = to_i2c_client(kobj_to_dev(kobj));
struct st25dv_data *data = i2c_get_clientdata(client);
cmd = bin_attr == &st25dv_p_pwd_attr ? CMD_PRESENT_PWD : CMD_WRITE_PWD;
if (count != PWD_SIZE) {
dev_err(&client->dev, "send pwd cmd fail: count=%d\n", (int)count);
return count;
}
nack = 0;
pwd_req[0] = 0x09;
pwd_req[1] = 0x00;
pwd_ptr1 = &pwd_req[PWD1_POS];
pwd_ptr2 = &pwd_req[PWD2_POS];
pwd_req[PWD_CMD_POS] = cmd;
for (off_tmp = PWD_SIZE-1; off_tmp >= 0; off_tmp--) {
*pwd_ptr1 = buf[off_tmp];
*pwd_ptr2 = buf[off_tmp];
pwd_ptr1++;
pwd_ptr2++;
}
mutex_lock(data->update_lock);
retry_:
if (nack > MAX_TRY) {
mutex_unlock(data->update_lock);
return r_size;
}
r_size = i2c_master_send(client, pwd_req, PWD_REQ_SIZE);
if (r_size < 0) {
nack++;
mdelay(5);
goto retry_;
}
mutex_unlock(data->update_lock);
dev_info(&client->dev, "%s password command successful\n",
(cmd == CMD_PRESENT_PWD) ? "Presentation" : "Write");
return count;
}
static struct bin_attribute st25dv_user_attr = {
.attr = { .name = "st25dv_user", .mode = 0666 },
.size = USER_MEM_SIZE,
.read = st25dv_read,
.write = st25dv_write_block,
};
static const struct bin_attribute st25dv_sys_attr = {
.attr = { .name = "st25dv_sys", .mode = 0644 },
.size = SYS_MEM_SIZE,
.read = st25dv_read,
.write = st25dv_write_block,
};
static const struct bin_attribute st25dv_dyn_reg_attr = {
.attr = { .name = "st25dv_dyn_reg", .mode = 0644 },
.size = DYN_REG_SIZE,
.read = st25dv_read,
.write = st25dv_write_block,
};
static const struct bin_attribute st25dv_mailbox_attr = {
.attr = { .name = "st25dv_mailbox", .mode = 0666 },
.size = MAILBOX_MEM_SIZE,
.read = st25dv_read,
.write = st25dv_write_block,
};
static const struct bin_attribute st25dv_w_pwd_attr = {
.attr = { .name = "st25dv_write_pwd", .mode = 0200 },
.size = PWD_SIZE,
.write = st25dv_send_pwd_req,
};
static const struct bin_attribute st25dv_p_pwd_attr = {
.attr = { .name = "st25dv_present_pwd", .mode = 0200 },
.size = PWD_SIZE,
.write = st25dv_send_pwd_req,
};
/* Return 0 if detection is successful, -ENODEV otherwise */
static int st25dv_detect(struct i2c_client *client, struct i2c_board_info *info)
{
struct i2c_adapter *adapter = client->adapter;
if (!(adapter->class & I2C_CLASS_SPD) && (client->addr != USER_ADDR))
return -ENODEV;
if (!i2c_new_dummy_device(client->adapter, SYS_ADDR))
return -ENODEV;
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_READ_WORD_DATA)
&& !i2c_check_functionality(adapter, I2C_FUNC_SMBUS_READ_I2C_BLOCK)) {
dev_err(&client->dev, "ST25DV detected at 0x%02x but lacks required functionality\n",
client->addr);
return -ENODEV;
}
dev_info(&client->dev, "ST25DV EEPROM detected at 0x%02x\n", client->addr);
strscpy(info->type, "st25dv", I2C_NAME_SIZE);
return 0;
}
static int st25dv_probe(struct i2c_client *client)
{
const struct i2c_device_id *id = i2c_client_get_device_id(client);
struct st25dv_data *data, *sys_data, *dyn_reg_data, *mailbox_data;
struct i2c_client *client_sys_area;
struct mutex *st25dv_lock;
int status;
/* Dummy client for the system area at addr 0x57 */
client_sys_area = i2c_new_dummy_device(client->adapter, SYS_ADDR);
if (!client_sys_area) {
dev_err(&client->dev, "st25dv sys eeprom not detected\n");
return -ENODEV;
}
dev_set_name(&client_sys_area->dev, "st25dv-sys-%d-%04x",
i2c_adapter_id(client->adapter), client_sys_area->addr);
/* Register action to unregister dummy device automatically */
status = devm_add_action_or_reset(&client->dev,
(void(*)(void *))i2c_unregister_device,
client_sys_area);
if (status)
return status;
/* Allocate lock and data structures */
st25dv_lock = devm_kzalloc(&client->dev, sizeof(*st25dv_lock), GFP_KERNEL);
data = devm_kmalloc(&client->dev, sizeof(*data), GFP_KERNEL);
sys_data = devm_kmalloc(&client_sys_area->dev, sizeof(*sys_data), GFP_KERNEL);
dyn_reg_data = devm_kmalloc(&client->dev, sizeof(*dyn_reg_data), GFP_KERNEL);
mailbox_data = devm_kmalloc(&client->dev, sizeof(*mailbox_data), GFP_KERNEL);
if (!st25dv_lock || !data || !sys_data || !dyn_reg_data || !mailbox_data)
return -ENOMEM;
mutex_init(st25dv_lock);
/* Allocate and initialize buffers with 0xff */
data->data = devm_kmalloc(&client->dev, mem_config[id->driver_data], GFP_KERNEL);
sys_data->data = devm_kmalloc(&client_sys_area->dev, SYS_MEM_SIZE, GFP_KERNEL);
dyn_reg_data->data = devm_kmalloc(&client->dev, DYN_REG_SIZE, GFP_KERNEL);
mailbox_data->data = devm_kmalloc(&client->dev, MAILBOX_MEM_SIZE, GFP_KERNEL);
if (!data->data || !sys_data->data || !dyn_reg_data->data || !mailbox_data->data)
return -ENOMEM;
memset(data->data, 0xff, mem_config[id->driver_data]);
memset(sys_data->data, 0xff, SYS_MEM_SIZE);
memset(dyn_reg_data->data, 0xff, DYN_REG_SIZE);
memset(mailbox_data->data, 0xff, MAILBOX_MEM_SIZE);
/* Setup User Area */
data->client = client;
data->update_lock = st25dv_lock;
data->next = dyn_reg_data;
data->type = USER_AREA;
memcpy(&data->bin_attr, &st25dv_user_attr, sizeof(struct bin_attribute));
data->bin_attr.size = mem_config[id->driver_data];
/* Setup Dynamic Registers Area */
dyn_reg_data->client = client;
dyn_reg_data->update_lock = st25dv_lock;
dyn_reg_data->next = mailbox_data;
dyn_reg_data->type = DYN_REG_AREA;
memcpy(&dyn_reg_data->bin_attr, &st25dv_dyn_reg_attr, sizeof(struct bin_attribute));
/* Setup Mailbox Area */
mailbox_data->client = client;
mailbox_data->update_lock = st25dv_lock;
mailbox_data->next = sys_data;
mailbox_data->type = MAILBOX_AREA;
memcpy(&mailbox_data->bin_attr, &st25dv_mailbox_attr, sizeof(struct bin_attribute));
/* Setup System Area */
sys_data->client = client_sys_area;
sys_data->update_lock = st25dv_lock;
sys_data->next = data;
sys_data->type = SYS_AREA;
memcpy(&sys_data->bin_attr, &st25dv_sys_attr, sizeof(struct bin_attribute));
i2c_set_clientdata(client, data);
i2c_set_clientdata(client_sys_area, sys_data);
/* Create sysfs files - labels only for sysfs cleanup */
status = sysfs_create_bin_file(&client->dev.kobj, &data->bin_attr);
if (status < 0) goto err_sysfs;
status = sysfs_create_bin_file(&client_sys_area->dev.kobj, &sys_data->bin_attr);
if (status < 0) goto err_sysfs4;
status = sysfs_create_bin_file(&client->dev.kobj, &dyn_reg_data->bin_attr);
if (status < 0) goto err_sysfs3;
status = sysfs_create_bin_file(&client_sys_area->dev.kobj, &st25dv_w_pwd_attr);
if (status < 0) goto err_sysfs2;
status = sysfs_create_bin_file(&client_sys_area->dev.kobj, &st25dv_p_pwd_attr);
if (status < 0) goto err_sysfs1;
status = sysfs_create_bin_file(&client->dev.kobj, &mailbox_data->bin_attr);
if (status < 0) goto err_sysfs0;
dev_info(&client->dev, "ST25DV %s detected, %d bytes user memory\n",
id->name, mem_config[id->driver_data]);
return 0;
err_sysfs0:
sysfs_remove_bin_file(&client_sys_area->dev.kobj, &st25dv_p_pwd_attr);
err_sysfs1:
sysfs_remove_bin_file(&client_sys_area->dev.kobj, &st25dv_w_pwd_attr);
err_sysfs2:
sysfs_remove_bin_file(&client->dev.kobj, &dyn_reg_data->bin_attr);
err_sysfs3:
sysfs_remove_bin_file(&client_sys_area->dev.kobj, &sys_data->bin_attr);
err_sysfs4:
sysfs_remove_bin_file(&client->dev.kobj, &data->bin_attr);
err_sysfs:
dev_err(&client->dev, "failed to create bin file: %d\n", status);
return status;
}
static void st25dv_remove(struct i2c_client *client)
{
struct st25dv_data *data = i2c_get_clientdata(client);
struct st25dv_data *tmp = data;
do {
if (tmp->type == SYS_AREA) {
sysfs_remove_bin_file(&tmp->client->dev.kobj, &st25dv_p_pwd_attr);
sysfs_remove_bin_file(&tmp->client->dev.kobj, &st25dv_w_pwd_attr);
}
sysfs_remove_bin_file(&tmp->client->dev.kobj, &tmp->bin_attr);
tmp = tmp->next;
} while (tmp != data);
}
static const struct i2c_device_id st25dv_id[] = {
{ "st25dv", 0 },
{ "st25dv04k", 1 },
{ "st25dv16k", 2 },
{ "st25dv64k", 3 },
{ }
};
static struct i2c_driver st25dv_driver = {
.driver = {
.name = "st25dv",
},
.probe = st25dv_probe,
.remove = st25dv_remove,
.id_table = st25dv_id,
.class = I2C_CLASS_DDC | I2C_CLASS_SPD,
.detect = st25dv_detect,
.address_list = normal_i2c,
};
static int __init st25dv_i2c_init_driver(void)
{
return i2c_add_driver(&st25dv_driver);
}
static void __exit st25dv_i2c_exit_driver(void)
{
i2c_del_driver(&st25dv_driver);
}
module_init(st25dv_i2c_init_driver);
module_exit(st25dv_i2c_exit_driver);
MODULE_INFO(intree, "y");
MODULE_AUTHOR("Loïc Boban <loic.boban@gmail.com>");
MODULE_DESCRIPTION("nfc/i2c eeprom st25dv driver");
MODULE_LICENSE("GPL");
+13
View File
@@ -339,6 +339,19 @@ config TI_K3_DSP_REMOTEPROC
It's safe to say N here if you're not interested in utilizing
the DSP slave processors.
config TI_K3_M4_REMOTEPROC
tristate "TI K3 M4 remoteproc support"
depends on ARCH_K3 || COMPILE_TEST
select MAILBOX
select OMAP2PLUS_MBOX
help
Say m here to support TI's M4 remote processor subsystems
on various TI K3 family of SoCs through the remote processor
framework.
It's safe to say N here if you're not interested in utilizing
a remote processor.
config TI_K3_R5_REMOTEPROC
tristate "TI K3 R5 remoteproc support"
depends on ARCH_K3
+1
View File
@@ -37,5 +37,6 @@ obj-$(CONFIG_ST_REMOTEPROC) += st_remoteproc.o
obj-$(CONFIG_ST_SLIM_REMOTEPROC) += st_slim_rproc.o
obj-$(CONFIG_STM32_RPROC) += stm32_rproc.o
obj-$(CONFIG_TI_K3_DSP_REMOTEPROC) += ti_k3_dsp_remoteproc.o
obj-$(CONFIG_TI_K3_M4_REMOTEPROC) += ti_k3_m4_remoteproc.o
obj-$(CONFIG_TI_K3_R5_REMOTEPROC) += ti_k3_r5_remoteproc.o
obj-$(CONFIG_XLNX_R5_REMOTEPROC) += xlnx_r5_remoteproc.o
+3 -1
View File
@@ -59,7 +59,9 @@ enum omap_rp_mbox_messages {
RP_MBOX_SUSPEND_SYSTEM = 0xFFFFFF11,
RP_MBOX_SUSPEND_ACK = 0xFFFFFF12,
RP_MBOX_SUSPEND_CANCEL = 0xFFFFFF13,
RP_MBOX_END_MSG = 0xFFFFFF14,
RP_MBOX_SHUTDOWN = 0xFFFFFF14,
RP_MBOX_SHUTDOWN_ACK = 0xFFFFFF15,
RP_MBOX_END_MSG = 0xFFFFFF16,
};
#endif /* _OMAP_RPMSG_H */
File diff suppressed because it is too large Load Diff
+37 -2
View File
@@ -290,6 +290,15 @@ struct cqspi_driver_platdata {
#define CQSPI_REG_VERSAL_DMA_VAL 0x602
bool cqspi_dac_enabled(struct cqspi_st *cqspi)
{
u32 reg;
reg = readl(cqspi->iobase + CQSPI_REG_CONFIG);
return (reg & CQSPI_REG_CONFIG_ENB_DIR_ACC_CTRL) ? true : false;
}
static int cqspi_wait_for_bit(void __iomem *reg, const u32 mask, bool clr)
{
u32 val;
@@ -992,6 +1001,9 @@ static int cqspi_write_setup(struct cqspi_flash_pdata *f_pdata,
reg = readl(reg_base + CQSPI_REG_WR_COMPLETION_CTRL);
reg |= CQSPI_REG_WR_DISABLE_AUTO_POLL;
writel(reg, reg_base + CQSPI_REG_WR_COMPLETION_CTRL);
}
if (op->cmd.dtr && cqspi->wr_completion) {
/*
* DAC mode require auto polling as flash needs to be polled
* for write completion in case of bubble in SPI transaction
@@ -1183,6 +1195,7 @@ static void cqspi_config_baudrate_div(struct cqspi_st *cqspi)
const unsigned int ref_clk_hz = cqspi->master_ref_clk_hz;
void __iomem *reg_base = cqspi->iobase;
u32 reg, div;
u32 actual_clk;
/* Recalculate the baudrate divisor based on QSPI specification. */
div = DIV_ROUND_UP(ref_clk_hz, 2 * cqspi->sclk) - 1;
@@ -1195,10 +1208,25 @@ static void cqspi_config_baudrate_div(struct cqspi_st *cqspi)
cqspi->sclk, ref_clk_hz/((div+1)*2));
}
/* Calculate actual clock for logging purposes */
actual_clk = ref_clk_hz / (2 * (div + 1));
dev_info(&cqspi->pdev->dev,
"CQSPI Clock Config: Ref=%u Hz, Target=%u Hz, Divisor=%u, Result=%u Hz\n",
ref_clk_hz, cqspi->sclk, div, actual_clk);
reg = readl(reg_base + CQSPI_REG_CONFIG);
/* Log old register value for deep debugging */
dev_info(&cqspi->pdev->dev, "Old CONFIG_REG: 0x%08x\n", reg);
reg &= ~(CQSPI_REG_CONFIG_BAUD_MASK << CQSPI_REG_CONFIG_BAUD_LSB);
reg |= (div & CQSPI_REG_CONFIG_BAUD_MASK) << CQSPI_REG_CONFIG_BAUD_LSB;
writel(reg, reg_base + CQSPI_REG_CONFIG);
dev_info(&cqspi->pdev->dev, "New CONFIG_REG: 0x%08x (BaudDiv field updated)\n",
readl(reg_base + CQSPI_REG_CONFIG));
}
static void cqspi_readdata_capture(struct cqspi_st *cqspi,
@@ -1257,6 +1285,7 @@ static ssize_t cqspi_write(struct cqspi_flash_pdata *f_pdata,
const struct spi_mem_op *op)
{
struct cqspi_st *cqspi = f_pdata->cqspi;
struct device *dev = &cqspi->pdev->dev;
loff_t to = op->addr.val;
size_t len = op->data.nbytes;
const u_char *buf = op->data.buf.out;
@@ -1274,8 +1303,9 @@ static ssize_t cqspi_write(struct cqspi_flash_pdata *f_pdata,
* mode. So, we can not use direct mode when in DTR mode for writing
* data.
*/
if (!op->cmd.dtr && cqspi->use_direct_mode &&
if (cqspi->use_direct_mode &&
cqspi->use_direct_mode_wr && ((to + len) <= cqspi->ahb_size)) {
dev_dbg(dev, "%s: using DAC mode.", __func__);
memcpy_toio(cqspi->ahb_base + to, buf, len);
return cqspi_wait_idle(cqspi);
}
@@ -1303,7 +1333,9 @@ static int cqspi_direct_read_execute(struct cqspi_flash_pdata *f_pdata,
dma_addr_t dma_dst;
struct device *ddev;
if (!cqspi->rx_chan || !virt_addr_valid(buf)) {
if (cqspi->use_direct_mode || !cqspi->rx_chan ||
!virt_addr_valid(buf)) {
dev_dbg(dev, "%s: using DAC mode.", __func__);
memcpy_fromio(buf, cqspi->ahb_base + from, len);
return 0;
}
@@ -1874,6 +1906,9 @@ static int cqspi_probe(struct platform_device *pdev)
goto probe_setup_failed;
}
dev_info(dev, "Direct Access Controller (DAC) is %s.\n",
cqspi_dac_enabled(cqspi) ? "enabled" : "disabled");
return 0;
probe_setup_failed:
cqspi_controller_enable(cqspi, 0);
+20 -14
View File
@@ -54,7 +54,7 @@ static struct cdev kbus_cdev;
/* sysfs */
extern struct class *wsysinit_sysfs_class;
extern struct device *wsysinit_sysfs_device;
static struct device *kbus_device;
static struct device *kbus_device = NULL;
int kbus_wait_for_gpio(int gpio)
{
@@ -132,7 +132,6 @@ static irqreturn_t kbus_renesas_isr(int irq, void *dev)
if (unlikely(kdrvdata->cmdsel)) {
if (kdrvdata->kbus_ign_reset_irq) {
kdrvdata->kbus_ign_reset_irq = 0;
init_waitqueue_head(&kdrvdata->kbus_irq_wq);
// trace for command 0xA3 find in libkbusfwload library
trace_pxc_kbus(__func__, "kbus-irq: edge falling after command\
SCPU_COMMAND_START_UPDATE [0xa3]: ignore irq");
@@ -223,19 +222,26 @@ static struct task_struct *find_dma_task(void)
void kbus_boost_dma_task(u8 enable)
{
struct kbus_drv_data *kdrvdata = dev_get_drvdata(kbus_device);
struct sched_param boost_param = {
.sched_priority = enable ? kdrvdata->kbus_dma_boost_prio :
kdrvdata->kbus_dma_normal_prio
};
struct kbus_drv_data *kdrvdata = NULL;
struct sched_param boost_param;
if (kdrvdata->dma_task) {
if ((enable && !kdrvdata->kbus_dma_boost_en) ||
(!enable && kdrvdata->kbus_dma_boost_en)) {
kdrvdata->kbus_dma_boost_en =
!kdrvdata->kbus_dma_boost_en;
sched_setscheduler(kdrvdata->dma_task, SCHED_FIFO,
&boost_param);
if (kbus_device) {
kdrvdata = dev_get_drvdata(kbus_device);
if (kdrvdata) {
boost_param.sched_priority = enable ?
kdrvdata->kbus_dma_boost_prio :
kdrvdata->kbus_dma_normal_prio;
if (kdrvdata->dma_task) {
if ((enable && !kdrvdata->kbus_dma_boost_en) ||
(!enable && kdrvdata->kbus_dma_boost_en)) {
kdrvdata->kbus_dma_boost_en =
!kdrvdata->kbus_dma_boost_en;
sched_setscheduler(kdrvdata->dma_task, SCHED_FIFO,
&boost_param);
}
}
}
}
}
+18 -10
View File
@@ -657,9 +657,11 @@ omap2_mcspi_txrx_dma(struct spi_device *spi, struct spi_transfer *xfer)
int wait_res;
#ifdef CONFIG_SPI_KBUS_OMAP_EXTENSION
trace_pxc_kbus(__func__, "enter");
if (spi->dev.driver == &kbus_driver.driver) {
trace_pxc_kbus(__func__, "enter");
kbus_boost_dma_task(1);
kbus_boost_dma_task(1);
}
#endif
mcspi = spi_controller_get_devdata(spi->controller);
@@ -777,9 +779,11 @@ omap2_mcspi_txrx_dma_eow(struct spi_device *spi, struct spi_transfer *xfer)
int ret;
#ifdef CONFIG_SPI_KBUS_OMAP_EXTENSION
trace_pxc_kbus(__func__, "enter");
if (spi->dev.driver == &kbus_driver.driver) {
trace_pxc_kbus(__func__, "enter");
kbus_boost_dma_task(1);
kbus_boost_dma_task(1);
}
#endif
mcspi = spi_controller_get_devdata(spi->controller);
@@ -866,10 +870,12 @@ omap2_mcspi_txrx_pio(struct spi_device *spi, struct spi_transfer *xfer)
word_len = cs->word_len;
#ifdef CONFIG_SPI_KBUS_OMAP_EXTENSION
trace_pxc_kbus(__func__, "enter");
kbus_dbg("%s[%d]: count: %d\n", __func__,__LINE__, count);
if (spi->dev.driver == &kbus_driver.driver) {
trace_pxc_kbus(__func__, "enter");
kbus_dbg("%s[%d]: count: %d\n", __func__,__LINE__, count);
kbus_boost_dma_task(0);
kbus_boost_dma_task(0);
}
#endif
l = mcspi_cached_chconf0(spi);
@@ -1304,9 +1310,11 @@ static int omap2_mcspi_setup(struct spi_device *spi)
struct omap2_mcspi_cs *cs = spi->controller_state;
#ifdef CONFIG_SPI_KBUS_OMAP_EXTENSION
if (spi->max_speed_hz < (OMAP2_MCSPI_MAX_FREQ >> 15) ||
spi->max_speed_hz > OMAP2_MCSPI_MAX_FREQ)
return -EINVAL;
if (spi->dev.driver == &kbus_driver.driver) {
if (spi->max_speed_hz < (OMAP2_MCSPI_MAX_FREQ >> 15) ||
spi->max_speed_hz > OMAP2_MCSPI_MAX_FREQ)
return -EINVAL;
}
#endif
if (!cs) {
+3 -1
View File
@@ -335,7 +335,9 @@ static int rti_wdt_probe(struct platform_device *pdev)
memunmap(vaddr);
}
watchdog_init_timeout(wdd, heartbeat, dev);
// grab timeout from DTS first
wdd->timeout = heartbeat;
watchdog_init_timeout(wdd, 0, dev);
ret = watchdog_register_device(wdd);
if (ret) {
+14 -3
View File
@@ -706,13 +706,24 @@ static inline void led_classdev_notify_brightness_hw_changed(
#endif
/**
* struct led_pattern - pattern interval settings
* @delta_t: pattern interval delay, in milliseconds
* @brightness: pattern interval brightness
* struct led_pattern - pattern interval settings (optional RGB per node)
* @delta_t: pattern interval delay (ms)
* @brightness: pattern interval brightness (0..max_brightness)
* @has_color: true if this node specifies new RGB intensities
* @red: red component (0..255, valid if has_color)
* @green: green component (0..255, valid if has_color)
* @blue: blue component (0..255, valid if has_color)
*
* If has_color is false the previously applied color (if any) remains
* unchanged; only brightness/duration are processed.
*/
struct led_pattern {
u32 delta_t;
int brightness;
bool has_color;
u8 red;
u8 green;
u8 blue;
};
enum led_audio {
+1 -1
View File
@@ -139,7 +139,7 @@ makefileProject {
flattenFiles = false
uploadToArtifactory("kernel-generic-dev-local/wago/linux-kernel/${this.channel}/${defConfigName}/${defConfigName}${version}${env.SNAPSHOT_SUFFIX}/") {
includes(['.build/deploy/**/*'])
excludes(['.build/deploy/modules/*/build'])
promote(this.release)
}
}
+3 -3
View File
@@ -11,14 +11,14 @@ def toolchain = Toolchains.LINARO_11_3_2022_08_AARCH64_LINUX_GNU
// e.g. "firmware :: pfc :: trunk :: pfc-adv"
String artifactoryBuildPFC300 = ''
String artifactoryBuildUBoot = ''
String artifactoryBuildUBootFastboot = 'firmware :: pfc :: u-boot :: am6xxx_pfc300 :: release_w01.07.01_01'
String artifactoryBuildUBootFastboot = 'firmware :: pfc :: u-boot :: am6xxx_pfc300 :: release_w01.09.00_01'
String artifactoryBuildKernel = ''
String buildPathPFC300 = 'firmware/pfc/pfc-300/main'
String buildNumberPFC300 = ''
// The complete path to a barebox build job. The build job must exists.
String buildPathUBoot = 'firmware/pfc/u-boot/am6xxx_pfc300/release_w01.07.01_01'
String buildPathUBoot = 'firmware/pfc/u-boot/am6xxx_pfc300/release_w01.09.00_01'
// if empty then the jenkins plug in tries to find the build number of the last succeed build
String buildNumberUBoot = ''
String buildNumberUBootFastboot = 'latest'
@@ -120,7 +120,7 @@ makefileProject {
flattenFiles = false
uploadToArtifactory("kernel-generic-dev-local/wago/linux-kernel/${this.channel}/${defConfigName}/${defConfigName}${version}${env.SNAPSHOT_SUFFIX}/") {
includes(['.build/deploy/**/*'])
excludes(['.build/deploy/modules/*/build'])
promote(this.release)
}
}
+1 -1
View File
@@ -120,7 +120,7 @@ makefileProject {
flattenFiles = false
uploadToArtifactory("kernel-generic-dev-local/wago/linux-kernel/${this.channel}/${defConfigName}/${defConfigName}${version}${env.SNAPSHOT_SUFFIX}/") {
includes(['.build/deploy/**/*'])
excludes(['.build/deploy/modules/*/build'])
promote(this.release)
}
}
+1 -1
View File
@@ -125,7 +125,7 @@ makefileProject {
flattenFiles = false
uploadToArtifactory("kernel-generic-dev-local/wago/linux-kernel/${this.channel}/${defConfigName}/${defConfigName}${version}${env.SNAPSHOT_SUFFIX}/") {
includes(['.build/deploy/**/*'])
excludes(['.build/deploy/modules/*/build'])
promote(this.release)
}
}
+1 -1
View File
@@ -122,7 +122,7 @@ makefileProject {
flattenFiles = false
uploadToArtifactory("kernel-generic-dev-local/wago/linux-kernel/${this.channel}/${defConfigName}/${defConfigName}${version}${env.SNAPSHOT_SUFFIX}/") {
includes(['.build/deploy/**/*'])
excludes(['.build/deploy/modules/*/build'])
promote(this.release)
}
}
+1 -1
View File
@@ -1 +1 @@
-w05.08.01
-w05.13.01
+383
View File
@@ -0,0 +1,383 @@
#!/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 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
+33
View File
@@ -0,0 +1,33 @@
# SPDX-License-Identifier: GPL-2.0-only
include ../scripts/Makefile.include
bindir ?= /usr/sbin
ifeq ($(srctree),)
srctree := $(patsubst %/,%,$(dir $(CURDIR)))
srctree := $(patsubst %/,%,$(dir $(srctree)))
endif
# Do not use make's built-in rules
# (this improves performance and avoids hard-to-debug behaviour);
MAKEFLAGS += -r
CFLAGS += -O2 -Wall -Wextra -g -D_GNU_SOURCE
ALL_TARGETS := mram_io
ALL_PROGRAMS := $(patsubst %,$(OUTPUT)%,$(ALL_TARGETS))
all: $(ALL_PROGRAMS)
export srctree OUTPUT CC LD CFLAGS
include $(srctree)/tools/build/Makefile.include
$(OUTPUT)mram_io: mram_io.c
$(CC) $(CFLAGS) -o $@ $<
clean:
$(RM) $(ALL_PROGRAMS)
install: all
install -d $(DESTDIR)$(bindir)
install -m 755 $(ALL_PROGRAMS) $(DESTDIR)$(bindir)
+334
View File
@@ -0,0 +1,334 @@
// SPDX-License-Identifier: GPL-2.0
/*
* mram_io - Byte-wise read/write tool for MTD_RAM devices via mmap()
*
* Requires a MTD device that supports mmap() (e.g. Everspin MRAM on
* Cadence OSPI in DAC mode, registered as MTD_RAM).
*
* Usage:
* mram_io --read --device <dev> --offset <offset> --length <n>
* mram_io --write --device <dev> --offset <offset> --data <b0> [b1 b2 ...]
*
* Examples:
* mram_io --read --device /dev/mtd0 --offset 0x100 --length 16
* mram_io --write --device /dev/mtd0 --offset 0x100 --data 0xde 0xad 0xbe 0xef
*/
#include <errno.h>
#include <fcntl.h>
#include <getopt.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <unistd.h>
/* ------------------------------------------------------------------ */
/* Helpers */
/* ------------------------------------------------------------------ */
static void usage(const char *prog)
{
fprintf(stderr,
"Usage:\n"
" %s --read --device <dev> --offset <off> --length <n>\n"
" %s --write --device <dev> --offset <off> --data <b0> [b1 ...]\n"
"\n"
"Options:\n"
" --device, -D <path> MTD character device, e.g. /dev/mtd0\n"
" --offset, -o <n> Byte offset into the device (hex or dec)\n"
" --length, -l <n> Number of bytes to read\n"
" --data, -d Following args are bytes to write (hex or dec)\n"
" --read, -r Read mode\n"
" --write, -w Write mode\n"
" --help, -h Show this help\n"
"\n"
"Output (read): hex-dump with offset, hex columns and ASCII column.\n",
prog, prog);
}
static unsigned long parse_num(const char *s)
{
char *end;
unsigned long v = strtoul(s, &end, 0);
if (*end != '\0') {
fprintf(stderr, "Invalid number: '%s'\n", s);
exit(EXIT_FAILURE);
}
return v;
}
/* Returns the size of the MTD device via lseek(). */
static off_t mtd_size(int fd)
{
off_t sz = lseek(fd, 0, SEEK_END);
if (sz < 0) {
perror("lseek (SEEK_END)");
exit(EXIT_FAILURE);
}
lseek(fd, 0, SEEK_SET);
return sz;
}
/* ------------------------------------------------------------------ */
/* main */
/* ------------------------------------------------------------------ */
enum mode { MODE_NONE, MODE_READ, MODE_WRITE };
int main(int argc, char *argv[])
{
static const struct option long_opts[] = {
{ "read", no_argument, NULL, 'r' },
{ "write", no_argument, NULL, 'w' },
{ "device", required_argument, NULL, 'D' },
{ "offset", required_argument, NULL, 'o' },
{ "length", required_argument, NULL, 'l' },
{ "data", no_argument, NULL, 'd' },
{ "help", no_argument, NULL, 'h' },
{ NULL, 0, NULL, 0 },
};
enum mode mode = MODE_NONE;
const char *device = NULL;
unsigned long offset = 0;
unsigned long length = 0;
int have_offset = 0;
int have_length = 0;
int data_mode = 0;
int opt;
int opt_idx = 0;
/*
* Parse all named options first. Stop at --data/-d: everything
* after it is a raw byte value for --write, not an option.
*/
while (!data_mode &&
(opt = getopt_long(argc, argv, "rwD:o:l:dh",
long_opts, &opt_idx)) != -1) {
switch (opt) {
case 'r':
mode = MODE_READ;
break;
case 'w':
mode = MODE_WRITE;
break;
case 'D':
device = optarg;
break;
case 'o':
offset = parse_num(optarg);
have_offset = 1;
break;
case 'l':
length = parse_num(optarg);
have_length = 1;
break;
case 'd':
data_mode = 1;
break;
case 'h':
usage(argv[0]);
return EXIT_SUCCESS;
default:
usage(argv[0]);
return EXIT_FAILURE;
}
}
/* --- Validate ------------------------------------------------- */
if (!device) {
fprintf(stderr, "Error: --device <path> is required.\n\n");
usage(argv[0]);
return EXIT_FAILURE;
}
if (mode == MODE_NONE) {
fprintf(stderr, "Error: specify --read or --write.\n\n");
usage(argv[0]);
return EXIT_FAILURE;
}
if (!have_offset) {
fprintf(stderr, "Error: --offset is required.\n\n");
usage(argv[0]);
return EXIT_FAILURE;
}
/* Collect write bytes from remaining argv (after --data) */
uint8_t *write_buf = NULL;
size_t write_cnt = 0;
if (mode == MODE_WRITE) {
if (!data_mode) {
fprintf(stderr,
"Error: --write requires --data <b0> [b1 ...].\n\n");
usage(argv[0]);
return EXIT_FAILURE;
}
write_cnt = (size_t)(argc - optind);
if (write_cnt == 0) {
fprintf(stderr, "Error: no data bytes given after --data.\n\n");
usage(argv[0]);
return EXIT_FAILURE;
}
write_buf = malloc(write_cnt);
if (!write_buf) {
perror("malloc");
return EXIT_FAILURE;
}
for (size_t i = 0; i < write_cnt; i++) {
unsigned long v = parse_num(argv[optind + i]);
if (v > 0xff) {
fprintf(stderr,
"Error: byte value 0x%lx out of range.\n", v);
free(write_buf);
return EXIT_FAILURE;
}
write_buf[i] = (uint8_t)v;
}
length = write_cnt;
}
if (mode == MODE_READ && !have_length) {
fprintf(stderr, "Error: --read requires --length.\n\n");
usage(argv[0]);
return EXIT_FAILURE;
}
/* --- Open device ---------------------------------------------- */
int flags = (mode == MODE_WRITE) ? (O_RDWR | O_SYNC) : O_RDONLY;
int fd = open(device, flags);
if (fd < 0) {
fprintf(stderr, "open(%s): %s\n", device, strerror(errno));
free(write_buf);
return EXIT_FAILURE;
}
off_t dev_size = mtd_size(fd);
if ((unsigned long)dev_size == 0) {
fprintf(stderr, "Error: device size is 0.\n");
goto err_close;
}
if (offset >= (unsigned long)dev_size) {
fprintf(stderr,
"Error: offset 0x%lx is beyond device size 0x%lx.\n",
offset, (unsigned long)dev_size);
goto err_close;
}
if (offset + length > (unsigned long)dev_size) {
fprintf(stderr,
"Warning: clamping length from %lu to %lu (end of device).\n",
length, (unsigned long)dev_size - offset);
length = (unsigned long)dev_size - offset;
}
/* --- mmap ----------------------------------------------------- */
/*
* mmap() the whole device. The kernel (mtdchar_mmap) maps the
* physical AHB window directly into our address space; accesses
* go straight to the MRAM without any kernel copy.
*/
int prot = PROT_READ | ((mode == MODE_WRITE) ? PROT_WRITE : 0);
void *map = mmap(NULL, (size_t)dev_size, prot, MAP_SHARED, fd, 0);
if (map == MAP_FAILED) {
fprintf(stderr, "mmap(%s): %s\n", device, strerror(errno));
fprintf(stderr,
"Note: device must be of type MTD_RAM with a physical "
"AHB window (e.g. Everspin MRAM on Cadence OSPI).\n");
goto err_close;
}
uint8_t *base = (uint8_t *)map + offset;
/* --- Read ----------------------------------------------------- */
if (mode == MODE_READ) {
printf("Reading %lu byte(s) from %s at offset 0x%lx:\n\n",
length, device, offset);
printf(" Offset 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f"
" ASCII\n");
printf(" -------- -----------------------------------------------"
" ----------------\n");
for (unsigned long i = 0; i < length; ) {
unsigned long row_off = offset + i;
unsigned long row_start = i;
/* Offset column */
printf(" %08lx ", row_off);
/* Hex bytes: 16 per row, split 8+8 */
for (int col = 0; col < 16; col++) {
if (col == 8)
printf(" ");
if (row_start + col < length)
printf("%02x ", base[row_start + col]);
else
printf(" ");
}
/* ASCII column */
printf(" |");
for (int col = 0; col < 16 && row_start + col < length; col++) {
uint8_t c = base[row_start + col];
printf("%c", (c >= 0x20 && c < 0x7f) ? c : '.');
}
printf("|\n");
i += 16;
}
printf("\n");
}
/* --- Write ---------------------------------------------------- */
if (mode == MODE_WRITE) {
printf("Writing %lu byte(s) to %s at offset 0x%lx:\n",
length, device, offset);
for (size_t i = 0; i < write_cnt; i++) {
/*
* Volatile write: ensure each byte reaches the hardware
* window. The mmap region is pgprot_noncached, but being
* explicit prevents compiler from merging byte stores.
*/
volatile uint8_t *dst = (volatile uint8_t *)base + i;
*dst = write_buf[i];
printf(" [0x%08lx] <- 0x%02x\n",
offset + i, write_buf[i]);
}
/* Read back for verification */
printf("\nVerification (read-back):\n");
for (size_t i = 0; i < write_cnt; i++) {
uint8_t got = *((volatile uint8_t *)base + i);
const char *status = (got == write_buf[i]) ? "OK" : "MISMATCH";
printf(" [0x%08lx] wrote 0x%02x read 0x%02x %s\n",
offset + i, write_buf[i], got, status);
}
}
munmap(map, (size_t)dev_size);
free(write_buf);
close(fd);
return EXIT_SUCCESS;
err_close:
free(write_buf);
close(fd);
return EXIT_FAILURE;
}