From: Stefan Kalscheuer Date: Sat, 31 May 2025 10:09:38 +0000 (+0200) Subject: mvebu: 6.12: refresh patches X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=1738e87632ca8176ba7936bb4180b3b591e205f5;p=thirdparty%2Fopenwrt.git mvebu: 6.12: refresh patches Drop upstreamed patches: 0001-v6.16-pinctrl-armada-37xx-use-correct-OUTPUT_VAL-register-.patch 0002-v6.16-pinctrl-armada-37xx-set-GPIO-output-value-before-set.patch 820-v6.11-01-dt-bindings-firmware-add-cznic-turris-omnia-mcu-bind.patch 820-v6.11-02-platform-cznic-Add-preliminary-support-for-Turris-Om.patch 820-v6.11-03-platform-cznic-turris-omnia-mcu-Add-support-for-MCU-.patch 820-v6.11-04-platform-cznic-turris-omnia-mcu-Add-support-for-powe.patch 820-v6.11-05-platform-cznic-turris-omnia-mcu-Add-support-for-MCU-.patch 820-v6.11-06-platform-cznic-turris-omnia-mcu-Add-support-for-MCU-.patch 820-v6.11-07-ARM-dts-turris-omnia-Add-MCU-system-controller-node.patch 820-v6.11-08-ARM-dts-turris-omnia-Add-GPIO-key-node-for-front-but.patch 820-v6.11-09-platform-cznic-turris-omnia-mcu-Depend-on-OF.patch 820-v6.11-10-platform-cznic-turris-omnia-mcu-Depend-on-WATCHDOG.patch 820-v6.11-11-platform-cznic-turris-omnia-mcu-fix-Kconfig-dependen.patch Manually refreshed: 310-linksys-use-eth0-as-cpu-port.patch 350-drivers-thermal-step_wise-add-support-for-hysteresis.patch 901-dt-bindings-Add-IEI-vendor-prefix-and-IEI-WT61P803-P.patch 902-drivers-mfd-Add-a-driver-for-IEI-WT61P803-PUZZLE-MCU.patch 910-drivers-leds-wt61p803-puzzle-improvements.patch All other patches automatically refreshed. Signed-off-by: Stefan Kalscheuer Link: https://github.com/openwrt/openwrt/pull/18975 Signed-off-by: Hauke Mehrtens --- diff --git a/target/linux/mvebu/patches-6.12/0001-v6.16-pinctrl-armada-37xx-use-correct-OUTPUT_VAL-register-.patch b/target/linux/mvebu/patches-6.12/0001-v6.16-pinctrl-armada-37xx-use-correct-OUTPUT_VAL-register-.patch deleted file mode 100644 index 0d594a20bf1..00000000000 --- a/target/linux/mvebu/patches-6.12/0001-v6.16-pinctrl-armada-37xx-use-correct-OUTPUT_VAL-register-.patch +++ /dev/null @@ -1,44 +0,0 @@ -From 947c93eb29c2a581c0b0b6d5f21af3c2b7ff6d25 Mon Sep 17 00:00:00 2001 -From: Gabor Juhos -Date: Wed, 14 May 2025 21:18:32 +0200 -Subject: [PATCH 1/7] pinctrl: armada-37xx: use correct OUTPUT_VAL register for - GPIOs > 31 - -The controller has two consecutive OUTPUT_VAL registers and both -holds output value for 32 GPIOs. Due to a missing adjustment, the -current code always uses the first register while setting the -output value whereas it should use the second one for GPIOs > 31. - -Add the missing armada_37xx_update_reg() call to adjust the register -according to the 'offset' parameter of the function to fix the issue. - -Cc: stable@vger.kernel.org -Fixes: 6702abb3bf23 ("pinctrl: armada-37xx: Fix direction_output() callback behavior") -Signed-off-by: Imre Kaloz -Reviewed-by: Andrew Lunn -Signed-off-by: Gabor Juhos -Link: https://lore.kernel.org/20250514-pinctrl-a37xx-fixes-v2-1-07e9ac1ab737@gmail.com -Signed-off-by: Linus Walleij ---- - drivers/pinctrl/mvebu/pinctrl-armada-37xx.c | 3 +++ - 1 file changed, 3 insertions(+) - ---- a/drivers/pinctrl/mvebu/pinctrl-armada-37xx.c -+++ b/drivers/pinctrl/mvebu/pinctrl-armada-37xx.c -@@ -417,6 +417,7 @@ static int armada_37xx_gpio_direction_ou - unsigned int offset, int value) - { - struct armada_37xx_pinctrl *info = gpiochip_get_data(chip); -+ unsigned int val_offset = offset; - unsigned int reg = OUTPUT_EN; - unsigned int mask, val, ret; - -@@ -429,6 +430,8 @@ static int armada_37xx_gpio_direction_ou - return ret; - - reg = OUTPUT_VAL; -+ armada_37xx_update_reg(®, &val_offset); -+ - val = value ? mask : 0; - regmap_update_bits(info->regmap, reg, mask, val); - diff --git a/target/linux/mvebu/patches-6.12/0002-v6.16-pinctrl-armada-37xx-set-GPIO-output-value-before-set.patch b/target/linux/mvebu/patches-6.12/0002-v6.16-pinctrl-armada-37xx-set-GPIO-output-value-before-set.patch deleted file mode 100644 index 02f42c03246..00000000000 --- a/target/linux/mvebu/patches-6.12/0002-v6.16-pinctrl-armada-37xx-set-GPIO-output-value-before-set.patch +++ /dev/null @@ -1,58 +0,0 @@ -From e6ebd4942981f8ad37189bbb36a3c8495e21ef4c Mon Sep 17 00:00:00 2001 -From: Gabor Juhos -Date: Wed, 14 May 2025 21:18:33 +0200 -Subject: [PATCH 2/7] pinctrl: armada-37xx: set GPIO output value before - setting direction - -Changing the direction before updating the output value in the -OUTPUT_VAL register may result in a glitch on the output line -if the previous value in the OUTPUT_VAL register is different -from the one we want to set. - -In order to avoid that, update the output value before changing -the direction. - -Cc: stable@vger.kernel.org -Fixes: 6702abb3bf23 ("pinctrl: armada-37xx: Fix direction_output() callback behavior") -Signed-off-by: Imre Kaloz -Reviewed-by: Andrew Lunn -Signed-off-by: Gabor Juhos -Link: https://lore.kernel.org/20250514-pinctrl-a37xx-fixes-v2-2-07e9ac1ab737@gmail.com -Signed-off-by: Linus Walleij ---- - drivers/pinctrl/mvebu/pinctrl-armada-37xx.c | 15 +++++++-------- - 1 file changed, 7 insertions(+), 8 deletions(-) - ---- a/drivers/pinctrl/mvebu/pinctrl-armada-37xx.c -+++ b/drivers/pinctrl/mvebu/pinctrl-armada-37xx.c -@@ -417,23 +417,22 @@ static int armada_37xx_gpio_direction_ou - unsigned int offset, int value) - { - struct armada_37xx_pinctrl *info = gpiochip_get_data(chip); -- unsigned int val_offset = offset; -- unsigned int reg = OUTPUT_EN; -+ unsigned int en_offset = offset; -+ unsigned int reg = OUTPUT_VAL; - unsigned int mask, val, ret; - - armada_37xx_update_reg(®, &offset); - mask = BIT(offset); -+ val = value ? mask : 0; - -- ret = regmap_update_bits(info->regmap, reg, mask, mask); -- -+ ret = regmap_update_bits(info->regmap, reg, mask, val); - if (ret) - return ret; - -- reg = OUTPUT_VAL; -- armada_37xx_update_reg(®, &val_offset); -+ reg = OUTPUT_EN; -+ armada_37xx_update_reg(®, &en_offset); - -- val = value ? mask : 0; -- regmap_update_bits(info->regmap, reg, mask, val); -+ regmap_update_bits(info->regmap, reg, mask, mask); - - return 0; - } diff --git a/target/linux/mvebu/patches-6.12/100-aardvark-workaround-PCIe.patch b/target/linux/mvebu/patches-6.12/100-aardvark-workaround-PCIe.patch index 4936f6ad16c..b2282a3684f 100644 --- a/target/linux/mvebu/patches-6.12/100-aardvark-workaround-PCIe.patch +++ b/target/linux/mvebu/patches-6.12/100-aardvark-workaround-PCIe.patch @@ -48,7 +48,7 @@ Cc: stable@vger.kernel.org --- a/drivers/pci/controller/pci-aardvark.c +++ b/drivers/pci/controller/pci-aardvark.c -@@ -212,6 +212,8 @@ enum { +@@ -211,6 +211,8 @@ enum { }; #define VENDOR_ID_REG (LMI_BASE_ADDR + 0x44) @@ -57,7 +57,7 @@ Cc: stable@vger.kernel.org /* PCIe core controller registers */ #define CTRL_CORE_BASE_ADDR 0x18000 -@@ -560,6 +562,11 @@ static void advk_pcie_setup_hw(struct ad +@@ -559,6 +561,11 @@ static void advk_pcie_setup_hw(struct ad PCIE_CORE_CTRL2_TD_ENABLE; advk_writel(pcie, reg, PCIE_CORE_CTRL2_REG); @@ -69,7 +69,7 @@ Cc: stable@vger.kernel.org /* Set lane X1 */ reg = advk_readl(pcie, PCIE_CORE_CTRL0_REG); reg &= ~LANE_CNT_MSK; -@@ -1661,6 +1668,9 @@ static irqreturn_t advk_pcie_irq_handler +@@ -1654,6 +1661,9 @@ static irqreturn_t advk_pcie_irq_handler struct advk_pcie *pcie = arg; u32 status; diff --git a/target/linux/mvebu/patches-6.12/300-mvebu-Mangle-bootloader-s-kernel-arguments.patch b/target/linux/mvebu/patches-6.12/300-mvebu-Mangle-bootloader-s-kernel-arguments.patch index 8f5b9dee405..2d01b48f69d 100644 --- a/target/linux/mvebu/patches-6.12/300-mvebu-Mangle-bootloader-s-kernel-arguments.patch +++ b/target/linux/mvebu/patches-6.12/300-mvebu-Mangle-bootloader-s-kernel-arguments.patch @@ -28,7 +28,7 @@ Signed-off-by: Michael Gray --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig -@@ -1566,6 +1566,17 @@ config ARM_ATAG_DTB_COMPAT_CMDLINE_EXTEN +@@ -1503,6 +1503,17 @@ config ARM_ATAG_DTB_COMPAT_CMDLINE_EXTEN The command-line arguments provided by the boot loader will be appended to the the device tree bootargs property. @@ -247,7 +247,7 @@ Signed-off-by: Michael Gray } --- a/init/main.c +++ b/init/main.c -@@ -112,6 +112,10 @@ +@@ -114,6 +114,10 @@ #include @@ -258,7 +258,7 @@ Signed-off-by: Michael Gray static int kernel_init(void *); /* -@@ -929,6 +933,18 @@ void start_kernel(void) +@@ -960,6 +964,18 @@ void start_kernel(void) boot_cpu_hotplug_init(); pr_notice("Kernel command line: %s\n", saved_command_line); @@ -275,5 +275,5 @@ Signed-off-by: Michael Gray +#endif + /* parameters may set static keys */ - jump_label_init(); parse_early_param(); + after_dashes = parse_args("Booting kernel", diff --git a/target/linux/mvebu/patches-6.12/302-add_powertables.patch b/target/linux/mvebu/patches-6.12/302-add_powertables.patch index 36117d0cd12..19e6a5132e6 100644 --- a/target/linux/mvebu/patches-6.12/302-add_powertables.patch +++ b/target/linux/mvebu/patches-6.12/302-add_powertables.patch @@ -1,6 +1,6 @@ --- a/arch/arm/boot/dts/marvell/armada-385-linksys.dtsi +++ b/arch/arm/boot/dts/marvell/armada-385-linksys.dtsi -@@ -214,11 +214,19 @@ +@@ -212,11 +212,19 @@ &pcie1 { /* Marvell 88W8864, 5GHz-only */ status = "okay"; diff --git a/target/linux/mvebu/patches-6.12/307-armada-xp-linksys-mamba-broken-idle.patch b/target/linux/mvebu/patches-6.12/307-armada-xp-linksys-mamba-broken-idle.patch index b801b54435a..fd329d41ce1 100644 --- a/target/linux/mvebu/patches-6.12/307-armada-xp-linksys-mamba-broken-idle.patch +++ b/target/linux/mvebu/patches-6.12/307-armada-xp-linksys-mamba-broken-idle.patch @@ -1,6 +1,6 @@ --- a/arch/arm/boot/dts/marvell/armada-xp-linksys-mamba.dts +++ b/arch/arm/boot/dts/marvell/armada-xp-linksys-mamba.dts -@@ -483,3 +483,7 @@ +@@ -481,3 +481,7 @@ }; }; }; diff --git a/target/linux/mvebu/patches-6.12/308-armada-xp-linksys-mamba-wan.patch b/target/linux/mvebu/patches-6.12/308-armada-xp-linksys-mamba-wan.patch index 5d8c5811b62..c1f47ee15a4 100644 --- a/target/linux/mvebu/patches-6.12/308-armada-xp-linksys-mamba-wan.patch +++ b/target/linux/mvebu/patches-6.12/308-armada-xp-linksys-mamba-wan.patch @@ -1,11 +1,11 @@ --- a/arch/arm/boot/dts/marvell/armada-xp-linksys-mamba.dts +++ b/arch/arm/boot/dts/marvell/armada-xp-linksys-mamba.dts -@@ -385,7 +385,7 @@ +@@ -383,7 +383,7 @@ - port@4 { + ethernet-port@4 { reg = <4>; - label = "internet"; + label = "wan"; }; - port@5 { + ethernet-port@5 { diff --git a/target/linux/mvebu/patches-6.12/310-linksys-use-eth0-as-cpu-port.patch b/target/linux/mvebu/patches-6.12/310-linksys-use-eth0-as-cpu-port.patch index 683a5ebe51a..925149a3f4b 100644 --- a/target/linux/mvebu/patches-6.12/310-linksys-use-eth0-as-cpu-port.patch +++ b/target/linux/mvebu/patches-6.12/310-linksys-use-eth0-as-cpu-port.patch @@ -9,13 +9,13 @@ phy-mode = "sgmii"; buffer-manager = <&bm>; bm,pool-long = <2>; -@@ -200,10 +200,10 @@ +@@ -198,10 +198,10 @@ label = "wan"; }; -- port@5 { +- ethernet-port@5 { - reg = <5>; -+ port@6 { ++ ethernet-port@6 { + reg = <6>; phy-mode = "sgmii"; - ethernet = <ð2>; diff --git a/target/linux/mvebu/patches-6.12/315-armada-xp-linksys-mamba-resize-kernel.patch b/target/linux/mvebu/patches-6.12/315-armada-xp-linksys-mamba-resize-kernel.patch index d36fe7441cc..21b0dd10a5c 100644 --- a/target/linux/mvebu/patches-6.12/315-armada-xp-linksys-mamba-resize-kernel.patch +++ b/target/linux/mvebu/patches-6.12/315-armada-xp-linksys-mamba-resize-kernel.patch @@ -11,7 +11,7 @@ Signed-off-by: Tad Davanzo --- a/arch/arm/boot/dts/marvell/armada-xp-linksys-mamba.dts +++ b/arch/arm/boot/dts/marvell/armada-xp-linksys-mamba.dts -@@ -454,9 +454,9 @@ +@@ -452,9 +452,9 @@ reg = <0xa00000 0x2800000>; /* 40MB */ }; @@ -23,7 +23,7 @@ Signed-off-by: Tad Davanzo }; /* kernel2 overlaps with rootfs2 by design */ -@@ -465,9 +465,9 @@ +@@ -463,9 +463,9 @@ reg = <0x3200000 0x2800000>; /* 40MB */ }; diff --git a/target/linux/mvebu/patches-6.12/350-drivers-thermal-step_wise-add-support-for-hysteresis.patch b/target/linux/mvebu/patches-6.12/350-drivers-thermal-step_wise-add-support-for-hysteresis.patch index e7332b6df01..b0db0450f15 100644 --- a/target/linux/mvebu/patches-6.12/350-drivers-thermal-step_wise-add-support-for-hysteresis.patch +++ b/target/linux/mvebu/patches-6.12/350-drivers-thermal-step_wise-add-support-for-hysteresis.patch @@ -24,28 +24,25 @@ Signed-off-by: Daniel Golle --- a/drivers/thermal/gov_step_wise.c +++ b/drivers/thermal/gov_step_wise.c -@@ -86,22 +86,31 @@ static void thermal_zone_trip_update(str +@@ -74,19 +74,28 @@ static void thermal_zone_trip_update(str + int trip_id = thermal_zone_trip_id(tz, trip); struct thermal_instance *instance; bool throttle = false; - int old_target; + int hyst_temp; - trend = get_tz_trend(tz, trip_id); - -- if (tz->temperature >= trip->temperature) { +- if (tz->temperature >= trip_threshold) { - throttle = true; - trace_thermal_zone_trip(tz, trip_id, trip->type); - } - - dev_dbg(&tz->device, "Trip%d[type=%d,temp=%d]:trend=%d,throttle=%d\n", -- trip_id, trip->type, trip->temperature, trend, throttle); +- trip_id, trip->type, trip_threshold, trend, throttle); + hyst_temp = trip->temperature - trip->hysteresis; + dev_dbg(&tz->device, "Trip%d[type=%d,temp=%d,hyst=%d]:trend=%d,throttle=%d\n", + trip_id, trip->type, trip->temperature, hyst_temp, trend, throttle); - list_for_each_entry(instance, &tz->thermal_instances, tz_node) { - if (instance->trip != trip) - continue; + list_for_each_entry(instance, &td->thermal_instances, trip_node) { + int old_target; old_target = instance->target; + throttle = false; @@ -61,5 +58,5 @@ Signed-off-by: Daniel Golle + } + instance->target = get_target_state(instance, trend, throttle); - dev_dbg(&instance->cdev->device, "old_target=%d, target=%d\n", - old_target, (int)instance->target); + + dev_dbg(&instance->cdev->device, "old_target=%d, target=%ld\n", diff --git a/target/linux/mvebu/patches-6.12/400-find_active_root.patch b/target/linux/mvebu/patches-6.12/400-find_active_root.patch index 90164adcd43..bda65064943 100644 --- a/target/linux/mvebu/patches-6.12/400-find_active_root.patch +++ b/target/linux/mvebu/patches-6.12/400-find_active_root.patch @@ -38,7 +38,7 @@ Signed-off-by: Imre Kaloz + } parts[i].name = partname; - if (of_get_property(pp, "read-only", &len)) + if (of_property_read_bool(pp, "read-only")) @@ -271,6 +278,18 @@ static int __init ofpart_parser_init(voi return 0; } diff --git a/target/linux/mvebu/patches-6.12/700-mvneta-tx-queue-workaround.patch b/target/linux/mvebu/patches-6.12/700-mvneta-tx-queue-workaround.patch index 099281e048a..28aadad1ffe 100644 --- a/target/linux/mvebu/patches-6.12/700-mvneta-tx-queue-workaround.patch +++ b/target/linux/mvebu/patches-6.12/700-mvneta-tx-queue-workaround.patch @@ -14,7 +14,7 @@ Signed-off-by: Felix Fietkau --- --- a/drivers/net/ethernet/marvell/mvneta.c +++ b/drivers/net/ethernet/marvell/mvneta.c -@@ -5294,6 +5294,16 @@ static int mvneta_setup_tc(struct net_de +@@ -5296,6 +5296,16 @@ static int mvneta_setup_tc(struct net_de } } @@ -31,7 +31,7 @@ Signed-off-by: Felix Fietkau static const struct net_device_ops mvneta_netdev_ops = { .ndo_open = mvneta_open, .ndo_stop = mvneta_stop, -@@ -5304,6 +5314,9 @@ static const struct net_device_ops mvnet +@@ -5306,6 +5316,9 @@ static const struct net_device_ops mvnet .ndo_fix_features = mvneta_fix_features, .ndo_get_stats64 = mvneta_get_stats64, .ndo_eth_ioctl = mvneta_ioctl, diff --git a/target/linux/mvebu/patches-6.12/701-mvpp2-read-mac-address-from-nvmem.patch b/target/linux/mvebu/patches-6.12/701-mvpp2-read-mac-address-from-nvmem.patch index 69ee10c3f2e..ebfa19db085 100644 --- a/target/linux/mvebu/patches-6.12/701-mvpp2-read-mac-address-from-nvmem.patch +++ b/target/linux/mvebu/patches-6.12/701-mvpp2-read-mac-address-from-nvmem.patch @@ -12,7 +12,7 @@ Signed-off-by: Tobias Schramm --- --- a/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c +++ b/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c -@@ -6156,6 +6156,12 @@ static int mvpp2_port_copy_mac_addr(stru +@@ -6179,6 +6179,12 @@ static int mvpp2_port_copy_mac_addr(stru return 0; } diff --git a/target/linux/mvebu/patches-6.12/820-v6.11-01-dt-bindings-firmware-add-cznic-turris-omnia-mcu-bind.patch b/target/linux/mvebu/patches-6.12/820-v6.11-01-dt-bindings-firmware-add-cznic-turris-omnia-mcu-bind.patch deleted file mode 100644 index 4d6d8e82c2e..00000000000 --- a/target/linux/mvebu/patches-6.12/820-v6.11-01-dt-bindings-firmware-add-cznic-turris-omnia-mcu-bind.patch +++ /dev/null @@ -1,125 +0,0 @@ -From cdfed4d05780450817ef96567e2cd8d355ca9e70 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Marek=20Beh=C3=BAn?= -Date: Mon, 1 Jul 2024 13:30:03 +0200 -Subject: [PATCH 01/11] dt-bindings: firmware: add cznic,turris-omnia-mcu - binding -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -Add binding for cznic,turris-omnia-mcu, the device-tree node -representing the system-controller features provided by the MCU on the -Turris Omnia router. - -Signed-off-by: Marek Behún -Reviewed-by: Krzysztof Kozlowski -Reviewed-by: Conor Dooley -Reviewed-by: Andy Shevchenko -Acked-by: Bartosz Golaszewski -Link: https://lore.kernel.org/r/20240701113010.16447-2-kabel@kernel.org -Signed-off-by: Arnd Bergmann ---- - .../firmware/cznic,turris-omnia-mcu.yaml | 86 +++++++++++++++++++ - MAINTAINERS | 1 + - 2 files changed, 87 insertions(+) - create mode 100644 Documentation/devicetree/bindings/firmware/cznic,turris-omnia-mcu.yaml - ---- /dev/null -+++ b/Documentation/devicetree/bindings/firmware/cznic,turris-omnia-mcu.yaml -@@ -0,0 +1,86 @@ -+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) -+%YAML 1.2 -+--- -+$id: http://devicetree.org/schemas/firmware/cznic,turris-omnia-mcu.yaml# -+$schema: http://devicetree.org/meta-schemas/core.yaml# -+ -+title: CZ.NIC's Turris Omnia MCU -+ -+maintainers: -+ - Marek Behún -+ -+description: -+ The MCU on Turris Omnia acts as a system controller providing additional -+ GPIOs, interrupts, watchdog, system power off and wakeup configuration. -+ -+properties: -+ compatible: -+ const: cznic,turris-omnia-mcu -+ -+ reg: -+ description: MCU I2C slave address -+ maxItems: 1 -+ -+ interrupts: -+ maxItems: 1 -+ -+ interrupt-controller: true -+ -+ '#interrupt-cells': -+ const: 2 -+ description: | -+ The first cell specifies the interrupt number (0 to 63), the second cell -+ specifies interrupt type (which can be one of IRQ_TYPE_EDGE_RISING, -+ IRQ_TYPE_EDGE_FALLING or IRQ_TYPE_EDGE_BOTH). -+ The interrupt numbers correspond sequentially to GPIO numbers, taking the -+ GPIO banks into account: -+ IRQ number GPIO bank GPIO pin within bank -+ 0 - 15 0 0 - 15 -+ 16 - 47 1 0 - 31 -+ 48 - 63 2 0 - 15 -+ There are several exceptions: -+ IRQ number meaning -+ 11 LED panel brightness changed by button press -+ 13 TRNG entropy ready -+ 14 ECDSA message signature computation done -+ -+ gpio-controller: true -+ -+ '#gpio-cells': -+ const: 3 -+ description: -+ The first cell is bank number (0, 1 or 2), the second cell is pin number -+ within the bank (0 to 15 for banks 0 and 2, 0 to 31 for bank 1), and the -+ third cell specifies consumer flags. -+ -+required: -+ - compatible -+ - reg -+ - interrupts -+ - interrupt-controller -+ - gpio-controller -+ -+additionalProperties: false -+ -+examples: -+ - | -+ #include -+ -+ i2c { -+ #address-cells = <1>; -+ #size-cells = <0>; -+ -+ system-controller@2a { -+ compatible = "cznic,turris-omnia-mcu"; -+ reg = <0x2a>; -+ -+ interrupt-parent = <&gpio1>; -+ interrupts = <11 IRQ_TYPE_NONE>; -+ -+ gpio-controller; -+ #gpio-cells = <3>; -+ -+ interrupt-controller; -+ #interrupt-cells = <2>; -+ }; -+ }; ---- a/MAINTAINERS -+++ b/MAINTAINERS -@@ -2104,6 +2104,7 @@ F: Documentation/ABI/testing/sysfs-bus-m - F: Documentation/ABI/testing/sysfs-firmware-turris-mox-rwtm - F: Documentation/devicetree/bindings/bus/moxtet.txt - F: Documentation/devicetree/bindings/firmware/cznic,turris-mox-rwtm.txt -+F: Documentation/devicetree/bindings/firmware/cznic,turris-omnia-mcu.yaml - F: Documentation/devicetree/bindings/gpio/gpio-moxtet.txt - F: Documentation/devicetree/bindings/leds/cznic,turris-omnia-leds.yaml - F: Documentation/devicetree/bindings/watchdog/armada-37xx-wdt.txt diff --git a/target/linux/mvebu/patches-6.12/820-v6.11-02-platform-cznic-Add-preliminary-support-for-Turris-Om.patch b/target/linux/mvebu/patches-6.12/820-v6.11-02-platform-cznic-Add-preliminary-support-for-Turris-Om.patch deleted file mode 100644 index 72bd18d144b..00000000000 --- a/target/linux/mvebu/patches-6.12/820-v6.11-02-platform-cznic-Add-preliminary-support-for-Turris-Om.patch +++ /dev/null @@ -1,922 +0,0 @@ -From 4a63f684c8badfc43f384df2291ed2566909a3bc Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Marek=20Beh=C3=BAn?= -Date: Mon, 1 Jul 2024 13:30:04 +0200 -Subject: [PATCH 02/11] platform: cznic: Add preliminary support for Turris - Omnia MCU -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -Add the basic skeleton for a new platform driver for the microcontroller -found on the Turris Omnia board. - -Signed-off-by: Marek Behún -Reviewed-by: Andy Shevchenko -Acked-by: Bartosz Golaszewski -Link: https://lore.kernel.org/r/20240701113010.16447-3-kabel@kernel.org -Signed-off-by: Arnd Bergmann ---- - .../sysfs-bus-i2c-devices-turris-omnia-mcu | 81 ++++ - MAINTAINERS | 3 + - drivers/platform/Kconfig | 2 + - drivers/platform/Makefile | 1 + - drivers/platform/cznic/Kconfig | 25 ++ - drivers/platform/cznic/Makefile | 4 + - .../platform/cznic/turris-omnia-mcu-base.c | 393 ++++++++++++++++++ - drivers/platform/cznic/turris-omnia-mcu.h | 74 ++++ - include/linux/turris-omnia-mcu-interface.h | 249 +++++++++++ - 9 files changed, 832 insertions(+) - create mode 100644 Documentation/ABI/testing/sysfs-bus-i2c-devices-turris-omnia-mcu - create mode 100644 drivers/platform/cznic/Kconfig - create mode 100644 drivers/platform/cznic/Makefile - create mode 100644 drivers/platform/cznic/turris-omnia-mcu-base.c - create mode 100644 drivers/platform/cznic/turris-omnia-mcu.h - create mode 100644 include/linux/turris-omnia-mcu-interface.h - ---- /dev/null -+++ b/Documentation/ABI/testing/sysfs-bus-i2c-devices-turris-omnia-mcu -@@ -0,0 +1,81 @@ -+What: /sys/bus/i2c/devices//board_revision -+Date: September 2024 -+KernelVersion: 6.11 -+Contact: Marek Behún -+Description: (RO) Contains board revision number. -+ -+ Only available if board information is burned in the MCU (older -+ revisions have board information burned in the ATSHA204-A chip). -+ -+ Format: %u. -+ -+What: /sys/bus/i2c/devices//first_mac_address -+Date: September 2024 -+KernelVersion: 6.11 -+Contact: Marek Behún -+Description: (RO) Contains device first MAC address. Each Turris Omnia is -+ allocated 3 MAC addresses. The two additional addresses are -+ computed from the first one by incrementing it. -+ -+ Only available if board information is burned in the MCU (older -+ revisions have board information burned in the ATSHA204-A chip). -+ -+ Format: %pM. -+ -+What: /sys/bus/i2c/devices//fw_features -+Date: September 2024 -+KernelVersion: 6.11 -+Contact: Marek Behún -+Description: (RO) Newer versions of the microcontroller firmware report the -+ features they support. These can be read from this file. If the -+ MCU firmware is too old, this file reads 0x0. -+ -+ Format: 0x%x. -+ -+What: /sys/bus/i2c/devices//fw_version_hash_application -+Date: September 2024 -+KernelVersion: 6.11 -+Contact: Marek Behún -+Description: (RO) Contains the version hash (commit hash) of the application -+ part of the microcontroller firmware. -+ -+ Format: %s. -+ -+What: /sys/bus/i2c/devices//fw_version_hash_bootloader -+Date: September 2024 -+KernelVersion: 6.11 -+Contact: Marek Behún -+Description: (RO) Contains the version hash (commit hash) of the bootloader -+ part of the microcontroller firmware. -+ -+ Format: %s. -+ -+What: /sys/bus/i2c/devices//mcu_type -+Date: September 2024 -+KernelVersion: 6.11 -+Contact: Marek Behún -+Description: (RO) Contains the microcontroller type (STM32, GD32, MKL). -+ -+ Format: %s. -+ -+What: /sys/bus/i2c/devices//reset_selector -+Date: September 2024 -+KernelVersion: 6.11 -+Contact: Marek Behún -+Description: (RO) Contains the selected factory reset level, determined by -+ how long the rear reset button was held by the user during board -+ reset. -+ -+ Format: %i. -+ -+What: /sys/bus/i2c/devices//serial_number -+Date: September 2024 -+KernelVersion: 6.11 -+Contact: Marek Behún -+Description: (RO) Contains the 64-bit board serial number in hexadecimal -+ format. -+ -+ Only available if board information is burned in the MCU (older -+ revisions have board information burned in the ATSHA204-A chip). -+ -+ Format: %016X. ---- a/MAINTAINERS -+++ b/MAINTAINERS -@@ -2100,6 +2100,7 @@ M: Marek Behún - S: Maintained - W: https://www.turris.cz/ - F: Documentation/ABI/testing/debugfs-moxtet -+F: Documentation/ABI/testing/sysfs-bus-i2c-devices-turris-omnia-mcu - F: Documentation/ABI/testing/sysfs-bus-moxtet-devices - F: Documentation/ABI/testing/sysfs-firmware-turris-mox-rwtm - F: Documentation/devicetree/bindings/bus/moxtet.txt -@@ -2113,10 +2114,12 @@ F: drivers/firmware/turris-mox-rwtm.c - F: drivers/gpio/gpio-moxtet.c - F: drivers/leds/leds-turris-omnia.c - F: drivers/mailbox/armada-37xx-rwtm-mailbox.c -+F: drivers/platform/cznic/ - F: drivers/watchdog/armada_37xx_wdt.c - F: include/dt-bindings/bus/moxtet.h - F: include/linux/armada-37xx-rwtm-mailbox.h - F: include/linux/moxtet.h -+F: include/linux/turris-omnia-mcu-interface.h - - ARM/FARADAY FA526 PORT - M: Hans Ulli Kroll ---- a/drivers/platform/Kconfig -+++ b/drivers/platform/Kconfig -@@ -7,6 +7,8 @@ source "drivers/platform/goldfish/Kconfi - - source "drivers/platform/chrome/Kconfig" - -+source "drivers/platform/cznic/Kconfig" -+ - source "drivers/platform/mellanox/Kconfig" - - source "drivers/platform/olpc/Kconfig" ---- a/drivers/platform/Makefile -+++ b/drivers/platform/Makefile -@@ -10,5 +10,6 @@ obj-$(CONFIG_MIPS) += mips/ - obj-$(CONFIG_OLPC_EC) += olpc/ - obj-$(CONFIG_GOLDFISH) += goldfish/ - obj-$(CONFIG_CHROME_PLATFORMS) += chrome/ -+obj-$(CONFIG_CZNIC_PLATFORMS) += cznic/ - obj-$(CONFIG_SURFACE_PLATFORMS) += surface/ - obj-$(CONFIG_MIKROTIK) += mikrotik/ ---- /dev/null -+++ b/drivers/platform/cznic/Kconfig -@@ -0,0 +1,25 @@ -+# SPDX-License-Identifier: GPL-2.0-only -+# -+# For a description of the syntax of this configuration file, -+# see Documentation/kbuild/kconfig-language.rst. -+# -+ -+menuconfig CZNIC_PLATFORMS -+ bool "Platform support for CZ.NIC's Turris hardware" -+ help -+ Say Y here to be able to choose driver support for CZ.NIC's Turris -+ devices. This option alone does not add any kernel code. -+ -+if CZNIC_PLATFORMS -+ -+config TURRIS_OMNIA_MCU -+ tristate "Turris Omnia MCU driver" -+ depends on MACH_ARMADA_38X || COMPILE_TEST -+ depends on I2C -+ help -+ Say Y here to add support for the features implemented by the -+ microcontroller on the CZ.NIC's Turris Omnia SOHO router. -+ To compile this driver as a module, choose M here; the module will be -+ called turris-omnia-mcu. -+ -+endif # CZNIC_PLATFORMS ---- /dev/null -+++ b/drivers/platform/cznic/Makefile -@@ -0,0 +1,4 @@ -+# SPDX-License-Identifier: GPL-2.0-only -+ -+obj-$(CONFIG_TURRIS_OMNIA_MCU) += turris-omnia-mcu.o -+turris-omnia-mcu-y := turris-omnia-mcu-base.o ---- /dev/null -+++ b/drivers/platform/cznic/turris-omnia-mcu-base.c -@@ -0,0 +1,393 @@ -+// SPDX-License-Identifier: GPL-2.0 -+/* -+ * CZ.NIC's Turris Omnia MCU driver -+ * -+ * 2024 by Marek Behún -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include -+#include "turris-omnia-mcu.h" -+ -+#define OMNIA_FW_VERSION_LEN 20 -+#define OMNIA_FW_VERSION_HEX_LEN (2 * OMNIA_FW_VERSION_LEN + 1) -+#define OMNIA_BOARD_INFO_LEN 16 -+ -+int omnia_cmd_write_read(const struct i2c_client *client, -+ void *cmd, unsigned int cmd_len, -+ void *reply, unsigned int reply_len) -+{ -+ struct i2c_msg msgs[2]; -+ int ret, num; -+ -+ msgs[0].addr = client->addr; -+ msgs[0].flags = 0; -+ msgs[0].len = cmd_len; -+ msgs[0].buf = cmd; -+ num = 1; -+ -+ if (reply_len) { -+ msgs[1].addr = client->addr; -+ msgs[1].flags = I2C_M_RD; -+ msgs[1].len = reply_len; -+ msgs[1].buf = reply; -+ num++; -+ } -+ -+ ret = i2c_transfer(client->adapter, msgs, num); -+ if (ret < 0) -+ return ret; -+ if (ret != num) -+ return -EIO; -+ -+ return 0; -+} -+ -+static int omnia_get_version_hash(struct omnia_mcu *mcu, bool bootloader, -+ char version[static OMNIA_FW_VERSION_HEX_LEN]) -+{ -+ u8 reply[OMNIA_FW_VERSION_LEN]; -+ char *p; -+ int err; -+ -+ err = omnia_cmd_read(mcu->client, -+ bootloader ? OMNIA_CMD_GET_FW_VERSION_BOOT -+ : OMNIA_CMD_GET_FW_VERSION_APP, -+ reply, sizeof(reply)); -+ if (err) -+ return err; -+ -+ p = bin2hex(version, reply, OMNIA_FW_VERSION_LEN); -+ *p = '\0'; -+ -+ return 0; -+} -+ -+static ssize_t fw_version_hash_show(struct device *dev, char *buf, -+ bool bootloader) -+{ -+ struct omnia_mcu *mcu = dev_get_drvdata(dev); -+ char version[OMNIA_FW_VERSION_HEX_LEN]; -+ int err; -+ -+ err = omnia_get_version_hash(mcu, bootloader, version); -+ if (err) -+ return err; -+ -+ return sysfs_emit(buf, "%s\n", version); -+} -+ -+static ssize_t fw_version_hash_application_show(struct device *dev, -+ struct device_attribute *a, -+ char *buf) -+{ -+ return fw_version_hash_show(dev, buf, false); -+} -+static DEVICE_ATTR_RO(fw_version_hash_application); -+ -+static ssize_t fw_version_hash_bootloader_show(struct device *dev, -+ struct device_attribute *a, -+ char *buf) -+{ -+ return fw_version_hash_show(dev, buf, true); -+} -+static DEVICE_ATTR_RO(fw_version_hash_bootloader); -+ -+static ssize_t fw_features_show(struct device *dev, struct device_attribute *a, -+ char *buf) -+{ -+ struct omnia_mcu *mcu = dev_get_drvdata(dev); -+ -+ return sysfs_emit(buf, "0x%x\n", mcu->features); -+} -+static DEVICE_ATTR_RO(fw_features); -+ -+static ssize_t mcu_type_show(struct device *dev, struct device_attribute *a, -+ char *buf) -+{ -+ struct omnia_mcu *mcu = dev_get_drvdata(dev); -+ -+ return sysfs_emit(buf, "%s\n", mcu->type); -+} -+static DEVICE_ATTR_RO(mcu_type); -+ -+static ssize_t reset_selector_show(struct device *dev, -+ struct device_attribute *a, char *buf) -+{ -+ u8 reply; -+ int err; -+ -+ err = omnia_cmd_read_u8(to_i2c_client(dev), OMNIA_CMD_GET_RESET, -+ &reply); -+ if (err) -+ return err; -+ -+ return sysfs_emit(buf, "%d\n", reply); -+} -+static DEVICE_ATTR_RO(reset_selector); -+ -+static ssize_t serial_number_show(struct device *dev, -+ struct device_attribute *a, char *buf) -+{ -+ struct omnia_mcu *mcu = dev_get_drvdata(dev); -+ -+ return sysfs_emit(buf, "%016llX\n", mcu->board_serial_number); -+} -+static DEVICE_ATTR_RO(serial_number); -+ -+static ssize_t first_mac_address_show(struct device *dev, -+ struct device_attribute *a, char *buf) -+{ -+ struct omnia_mcu *mcu = dev_get_drvdata(dev); -+ -+ return sysfs_emit(buf, "%pM\n", mcu->board_first_mac); -+} -+static DEVICE_ATTR_RO(first_mac_address); -+ -+static ssize_t board_revision_show(struct device *dev, -+ struct device_attribute *a, char *buf) -+{ -+ struct omnia_mcu *mcu = dev_get_drvdata(dev); -+ -+ return sysfs_emit(buf, "%u\n", mcu->board_revision); -+} -+static DEVICE_ATTR_RO(board_revision); -+ -+static struct attribute *omnia_mcu_base_attrs[] = { -+ &dev_attr_fw_version_hash_application.attr, -+ &dev_attr_fw_version_hash_bootloader.attr, -+ &dev_attr_fw_features.attr, -+ &dev_attr_mcu_type.attr, -+ &dev_attr_reset_selector.attr, -+ &dev_attr_serial_number.attr, -+ &dev_attr_first_mac_address.attr, -+ &dev_attr_board_revision.attr, -+ NULL -+}; -+ -+static umode_t omnia_mcu_base_attrs_visible(struct kobject *kobj, -+ struct attribute *a, int n) -+{ -+ struct device *dev = kobj_to_dev(kobj); -+ struct omnia_mcu *mcu = dev_get_drvdata(dev); -+ -+ if ((a == &dev_attr_serial_number.attr || -+ a == &dev_attr_first_mac_address.attr || -+ a == &dev_attr_board_revision.attr) && -+ !(mcu->features & OMNIA_FEAT_BOARD_INFO)) -+ return 0; -+ -+ return a->mode; -+} -+ -+static const struct attribute_group omnia_mcu_base_group = { -+ .attrs = omnia_mcu_base_attrs, -+ .is_visible = omnia_mcu_base_attrs_visible, -+}; -+ -+static const struct attribute_group *omnia_mcu_groups[] = { -+ &omnia_mcu_base_group, -+ NULL -+}; -+ -+static void omnia_mcu_print_version_hash(struct omnia_mcu *mcu, bool bootloader) -+{ -+ const char *type = bootloader ? "bootloader" : "application"; -+ struct device *dev = &mcu->client->dev; -+ char version[OMNIA_FW_VERSION_HEX_LEN]; -+ int err; -+ -+ err = omnia_get_version_hash(mcu, bootloader, version); -+ if (err) { -+ dev_err(dev, "Cannot read MCU %s firmware version: %d\n", -+ type, err); -+ return; -+ } -+ -+ dev_info(dev, "MCU %s firmware version hash: %s\n", type, version); -+} -+ -+static const char *omnia_status_to_mcu_type(u16 status) -+{ -+ switch (status & OMNIA_STS_MCU_TYPE_MASK) { -+ case OMNIA_STS_MCU_TYPE_STM32: -+ return "STM32"; -+ case OMNIA_STS_MCU_TYPE_GD32: -+ return "GD32"; -+ case OMNIA_STS_MCU_TYPE_MKL: -+ return "MKL"; -+ default: -+ return "unknown"; -+ } -+} -+ -+static void omnia_info_missing_feature(struct device *dev, const char *feature) -+{ -+ dev_info(dev, -+ "Your board's MCU firmware does not support the %s feature.\n", -+ feature); -+} -+ -+static int omnia_mcu_read_features(struct omnia_mcu *mcu) -+{ -+ static const struct { -+ u16 mask; -+ const char *name; -+ } features[] = { -+#define _DEF_FEAT(_n, _m) { OMNIA_FEAT_ ## _n, _m } -+ _DEF_FEAT(EXT_CMDS, "extended control and status"), -+ _DEF_FEAT(WDT_PING, "watchdog pinging"), -+ _DEF_FEAT(LED_STATE_EXT_MASK, "peripheral LED pins reading"), -+ _DEF_FEAT(NEW_INT_API, "new interrupt API"), -+ _DEF_FEAT(POWEROFF_WAKEUP, "poweroff and wakeup"), -+ _DEF_FEAT(TRNG, "true random number generator"), -+#undef _DEF_FEAT -+ }; -+ struct i2c_client *client = mcu->client; -+ struct device *dev = &client->dev; -+ bool suggest_fw_upgrade = false; -+ u16 status; -+ int err; -+ -+ /* status word holds MCU type, which we need below */ -+ err = omnia_cmd_read_u16(client, OMNIA_CMD_GET_STATUS_WORD, &status); -+ if (err) -+ return err; -+ -+ /* -+ * Check whether MCU firmware supports the OMNIA_CMD_GET_FEATURES -+ * command. -+ */ -+ if (status & OMNIA_STS_FEATURES_SUPPORTED) { -+ /* try read 32-bit features */ -+ err = omnia_cmd_read_u32(client, OMNIA_CMD_GET_FEATURES, -+ &mcu->features); -+ if (err) { -+ /* try read 16-bit features */ -+ u16 features16; -+ -+ err = omnia_cmd_read_u16(client, OMNIA_CMD_GET_FEATURES, -+ &features16); -+ if (err) -+ return err; -+ -+ mcu->features = features16; -+ } else { -+ if (mcu->features & OMNIA_FEAT_FROM_BIT_16_INVALID) -+ mcu->features &= GENMASK(15, 0); -+ } -+ } else { -+ dev_info(dev, -+ "Your board's MCU firmware does not support feature reading.\n"); -+ suggest_fw_upgrade = true; -+ } -+ -+ mcu->type = omnia_status_to_mcu_type(status); -+ dev_info(dev, "MCU type %s%s\n", mcu->type, -+ (mcu->features & OMNIA_FEAT_PERIPH_MCU) ? -+ ", with peripheral resets wired" : ""); -+ -+ omnia_mcu_print_version_hash(mcu, true); -+ -+ if (mcu->features & OMNIA_FEAT_BOOTLOADER) -+ dev_warn(dev, -+ "MCU is running bootloader firmware. Was firmware upgrade interrupted?\n"); -+ else -+ omnia_mcu_print_version_hash(mcu, false); -+ -+ for (unsigned int i = 0; i < ARRAY_SIZE(features); i++) { -+ if (mcu->features & features[i].mask) -+ continue; -+ -+ omnia_info_missing_feature(dev, features[i].name); -+ suggest_fw_upgrade = true; -+ } -+ -+ if (suggest_fw_upgrade) -+ dev_info(dev, -+ "Consider upgrading MCU firmware with the omnia-mcutool utility.\n"); -+ -+ return 0; -+} -+ -+static int omnia_mcu_read_board_info(struct omnia_mcu *mcu) -+{ -+ u8 reply[1 + OMNIA_BOARD_INFO_LEN]; -+ int err; -+ -+ err = omnia_cmd_read(mcu->client, OMNIA_CMD_BOARD_INFO_GET, reply, -+ sizeof(reply)); -+ if (err) -+ return err; -+ -+ if (reply[0] != OMNIA_BOARD_INFO_LEN) -+ return -EIO; -+ -+ mcu->board_serial_number = get_unaligned_le64(&reply[1]); -+ -+ /* we can't use ether_addr_copy() because reply is not u16-aligned */ -+ memcpy(mcu->board_first_mac, &reply[9], sizeof(mcu->board_first_mac)); -+ -+ mcu->board_revision = reply[15]; -+ -+ return 0; -+} -+ -+static int omnia_mcu_probe(struct i2c_client *client) -+{ -+ struct device *dev = &client->dev; -+ struct omnia_mcu *mcu; -+ int err; -+ -+ if (!client->irq) -+ return dev_err_probe(dev, -EINVAL, "IRQ resource not found\n"); -+ -+ mcu = devm_kzalloc(dev, sizeof(*mcu), GFP_KERNEL); -+ if (!mcu) -+ return -ENOMEM; -+ -+ mcu->client = client; -+ i2c_set_clientdata(client, mcu); -+ -+ err = omnia_mcu_read_features(mcu); -+ if (err) -+ return dev_err_probe(dev, err, -+ "Cannot determine MCU supported features\n"); -+ -+ if (mcu->features & OMNIA_FEAT_BOARD_INFO) { -+ err = omnia_mcu_read_board_info(mcu); -+ if (err) -+ return dev_err_probe(dev, err, -+ "Cannot read board info\n"); -+ } -+ -+ return 0; -+} -+ -+static const struct of_device_id of_omnia_mcu_match[] = { -+ { .compatible = "cznic,turris-omnia-mcu" }, -+ {} -+}; -+ -+static struct i2c_driver omnia_mcu_driver = { -+ .probe = omnia_mcu_probe, -+ .driver = { -+ .name = "turris-omnia-mcu", -+ .of_match_table = of_omnia_mcu_match, -+ .dev_groups = omnia_mcu_groups, -+ }, -+}; -+module_i2c_driver(omnia_mcu_driver); -+ -+MODULE_AUTHOR("Marek Behun "); -+MODULE_DESCRIPTION("CZ.NIC's Turris Omnia MCU"); -+MODULE_LICENSE("GPL"); ---- /dev/null -+++ b/drivers/platform/cznic/turris-omnia-mcu.h -@@ -0,0 +1,74 @@ -+/* SPDX-License-Identifier: GPL-2.0 */ -+/* -+ * CZ.NIC's Turris Omnia MCU driver -+ * -+ * 2024 by Marek Behún -+ */ -+ -+#ifndef __TURRIS_OMNIA_MCU_H -+#define __TURRIS_OMNIA_MCU_H -+ -+#include -+#include -+#include -+ -+struct i2c_client; -+ -+struct omnia_mcu { -+ struct i2c_client *client; -+ const char *type; -+ u32 features; -+ -+ /* board information */ -+ u64 board_serial_number; -+ u8 board_first_mac[ETH_ALEN]; -+ u8 board_revision; -+}; -+ -+int omnia_cmd_write_read(const struct i2c_client *client, -+ void *cmd, unsigned int cmd_len, -+ void *reply, unsigned int reply_len); -+ -+static inline int omnia_cmd_read(const struct i2c_client *client, u8 cmd, -+ void *reply, unsigned int len) -+{ -+ return omnia_cmd_write_read(client, &cmd, 1, reply, len); -+} -+ -+static inline int omnia_cmd_read_u32(const struct i2c_client *client, u8 cmd, -+ u32 *dst) -+{ -+ __le32 reply; -+ int err; -+ -+ err = omnia_cmd_read(client, cmd, &reply, sizeof(reply)); -+ if (err) -+ return err; -+ -+ *dst = le32_to_cpu(reply); -+ -+ return 0; -+} -+ -+static inline int omnia_cmd_read_u16(const struct i2c_client *client, u8 cmd, -+ u16 *dst) -+{ -+ __le16 reply; -+ int err; -+ -+ err = omnia_cmd_read(client, cmd, &reply, sizeof(reply)); -+ if (err) -+ return err; -+ -+ *dst = le16_to_cpu(reply); -+ -+ return 0; -+} -+ -+static inline int omnia_cmd_read_u8(const struct i2c_client *client, u8 cmd, -+ u8 *reply) -+{ -+ return omnia_cmd_read(client, cmd, reply, sizeof(*reply)); -+} -+ -+#endif /* __TURRIS_OMNIA_MCU_H */ ---- /dev/null -+++ b/include/linux/turris-omnia-mcu-interface.h -@@ -0,0 +1,249 @@ -+/* SPDX-License-Identifier: GPL-2.0 */ -+/* -+ * CZ.NIC's Turris Omnia MCU I2C interface commands definitions -+ * -+ * 2024 by Marek Behún -+ */ -+ -+#ifndef __TURRIS_OMNIA_MCU_INTERFACE_H -+#define __TURRIS_OMNIA_MCU_INTERFACE_H -+ -+#include -+#include -+ -+enum omnia_commands_e { -+ OMNIA_CMD_GET_STATUS_WORD = 0x01, /* slave sends status word back */ -+ OMNIA_CMD_GENERAL_CONTROL = 0x02, -+ OMNIA_CMD_LED_MODE = 0x03, /* default/user */ -+ OMNIA_CMD_LED_STATE = 0x04, /* LED on/off */ -+ OMNIA_CMD_LED_COLOR = 0x05, /* LED number + RED + GREEN + BLUE */ -+ OMNIA_CMD_USER_VOLTAGE = 0x06, -+ OMNIA_CMD_SET_BRIGHTNESS = 0x07, -+ OMNIA_CMD_GET_BRIGHTNESS = 0x08, -+ OMNIA_CMD_GET_RESET = 0x09, -+ OMNIA_CMD_GET_FW_VERSION_APP = 0x0A, /* 20B git hash number */ -+ OMNIA_CMD_SET_WATCHDOG_STATE = 0x0B, /* 0 - disable -+ * 1 - enable / ping -+ * after boot watchdog is started -+ * with 2 minutes timeout -+ */ -+ -+ /* OMNIA_CMD_WATCHDOG_STATUS = 0x0C, not implemented anymore */ -+ -+ OMNIA_CMD_GET_WATCHDOG_STATE = 0x0D, -+ OMNIA_CMD_GET_FW_VERSION_BOOT = 0x0E, /* 20B Git hash number */ -+ OMNIA_CMD_GET_FW_CHECKSUM = 0x0F, /* 4B length, 4B checksum */ -+ -+ /* available if FEATURES_SUPPORTED bit set in status word */ -+ OMNIA_CMD_GET_FEATURES = 0x10, -+ -+ /* available if EXT_CMD bit set in features */ -+ OMNIA_CMD_GET_EXT_STATUS_DWORD = 0x11, -+ OMNIA_CMD_EXT_CONTROL = 0x12, -+ OMNIA_CMD_GET_EXT_CONTROL_STATUS = 0x13, -+ -+ /* available if NEW_INT_API bit set in features */ -+ OMNIA_CMD_GET_INT_AND_CLEAR = 0x14, -+ OMNIA_CMD_GET_INT_MASK = 0x15, -+ OMNIA_CMD_SET_INT_MASK = 0x16, -+ -+ /* available if FLASHING bit set in features */ -+ OMNIA_CMD_FLASH = 0x19, -+ -+ /* available if WDT_PING bit set in features */ -+ OMNIA_CMD_SET_WDT_TIMEOUT = 0x20, -+ OMNIA_CMD_GET_WDT_TIMELEFT = 0x21, -+ -+ /* available if POWEROFF_WAKEUP bit set in features */ -+ OMNIA_CMD_SET_WAKEUP = 0x22, -+ OMNIA_CMD_GET_UPTIME_AND_WAKEUP = 0x23, -+ OMNIA_CMD_POWER_OFF = 0x24, -+ -+ /* available if USB_OVC_PROT_SETTING bit set in features */ -+ OMNIA_CMD_SET_USB_OVC_PROT = 0x25, -+ OMNIA_CMD_GET_USB_OVC_PROT = 0x26, -+ -+ /* available if TRNG bit set in features */ -+ OMNIA_CMD_TRNG_COLLECT_ENTROPY = 0x28, -+ -+ /* available if CRYPTO bit set in features */ -+ OMNIA_CMD_CRYPTO_GET_PUBLIC_KEY = 0x29, -+ OMNIA_CMD_CRYPTO_SIGN_MESSAGE = 0x2A, -+ OMNIA_CMD_CRYPTO_COLLECT_SIGNATURE = 0x2B, -+ -+ /* available if BOARD_INFO it set in features */ -+ OMNIA_CMD_BOARD_INFO_GET = 0x2C, -+ OMNIA_CMD_BOARD_INFO_BURN = 0x2D, -+ -+ /* available only at address 0x2b (LED-controller) */ -+ /* available only if LED_GAMMA_CORRECTION bit set in features */ -+ OMNIA_CMD_SET_GAMMA_CORRECTION = 0x30, -+ OMNIA_CMD_GET_GAMMA_CORRECTION = 0x31, -+ -+ /* available only at address 0x2b (LED-controller) */ -+ /* available only if PER_LED_CORRECTION bit set in features */ -+ /* available only if FROM_BIT_16_INVALID bit NOT set in features */ -+ OMNIA_CMD_SET_LED_CORRECTIONS = 0x32, -+ OMNIA_CMD_GET_LED_CORRECTIONS = 0x33, -+}; -+ -+enum omnia_flashing_commands_e { -+ OMNIA_FLASH_CMD_UNLOCK = 0x01, -+ OMNIA_FLASH_CMD_SIZE_AND_CSUM = 0x02, -+ OMNIA_FLASH_CMD_PROGRAM = 0x03, -+ OMNIA_FLASH_CMD_RESET = 0x04, -+}; -+ -+enum omnia_sts_word_e { -+ OMNIA_STS_MCU_TYPE_MASK = GENMASK(1, 0), -+ OMNIA_STS_MCU_TYPE_STM32 = FIELD_PREP_CONST(OMNIA_STS_MCU_TYPE_MASK, 0), -+ OMNIA_STS_MCU_TYPE_GD32 = FIELD_PREP_CONST(OMNIA_STS_MCU_TYPE_MASK, 1), -+ OMNIA_STS_MCU_TYPE_MKL = FIELD_PREP_CONST(OMNIA_STS_MCU_TYPE_MASK, 2), -+ OMNIA_STS_FEATURES_SUPPORTED = BIT(2), -+ OMNIA_STS_USER_REGULATOR_NOT_SUPPORTED = BIT(3), -+ OMNIA_STS_CARD_DET = BIT(4), -+ OMNIA_STS_MSATA_IND = BIT(5), -+ OMNIA_STS_USB30_OVC = BIT(6), -+ OMNIA_STS_USB31_OVC = BIT(7), -+ OMNIA_STS_USB30_PWRON = BIT(8), -+ OMNIA_STS_USB31_PWRON = BIT(9), -+ OMNIA_STS_ENABLE_4V5 = BIT(10), -+ OMNIA_STS_BUTTON_MODE = BIT(11), -+ OMNIA_STS_BUTTON_PRESSED = BIT(12), -+ OMNIA_STS_BUTTON_COUNTER_MASK = GENMASK(15, 13), -+}; -+ -+enum omnia_ctl_byte_e { -+ OMNIA_CTL_LIGHT_RST = BIT(0), -+ OMNIA_CTL_HARD_RST = BIT(1), -+ /* BIT(2) is currently reserved */ -+ OMNIA_CTL_USB30_PWRON = BIT(3), -+ OMNIA_CTL_USB31_PWRON = BIT(4), -+ OMNIA_CTL_ENABLE_4V5 = BIT(5), -+ OMNIA_CTL_BUTTON_MODE = BIT(6), -+ OMNIA_CTL_BOOTLOADER = BIT(7), -+}; -+ -+enum omnia_features_e { -+ OMNIA_FEAT_PERIPH_MCU = BIT(0), -+ OMNIA_FEAT_EXT_CMDS = BIT(1), -+ OMNIA_FEAT_WDT_PING = BIT(2), -+ OMNIA_FEAT_LED_STATE_EXT_MASK = GENMASK(4, 3), -+ OMNIA_FEAT_LED_STATE_EXT = FIELD_PREP_CONST(OMNIA_FEAT_LED_STATE_EXT_MASK, 1), -+ OMNIA_FEAT_LED_STATE_EXT_V32 = FIELD_PREP_CONST(OMNIA_FEAT_LED_STATE_EXT_MASK, 2), -+ OMNIA_FEAT_LED_GAMMA_CORRECTION = BIT(5), -+ OMNIA_FEAT_NEW_INT_API = BIT(6), -+ OMNIA_FEAT_BOOTLOADER = BIT(7), -+ OMNIA_FEAT_FLASHING = BIT(8), -+ OMNIA_FEAT_NEW_MESSAGE_API = BIT(9), -+ OMNIA_FEAT_BRIGHTNESS_INT = BIT(10), -+ OMNIA_FEAT_POWEROFF_WAKEUP = BIT(11), -+ OMNIA_FEAT_CAN_OLD_MESSAGE_API = BIT(12), -+ OMNIA_FEAT_TRNG = BIT(13), -+ OMNIA_FEAT_CRYPTO = BIT(14), -+ OMNIA_FEAT_BOARD_INFO = BIT(15), -+ -+ /* -+ * Orginally the features command replied only 16 bits. If more were -+ * read, either the I2C transaction failed or 0xff bytes were sent. -+ * Therefore to consider bits 16 - 31 valid, one bit (20) was reserved -+ * to be zero. -+ */ -+ -+ /* Bits 16 - 19 correspond to bits 0 - 3 of status word */ -+ OMNIA_FEAT_MCU_TYPE_MASK = GENMASK(17, 16), -+ OMNIA_FEAT_MCU_TYPE_STM32 = FIELD_PREP_CONST(OMNIA_FEAT_MCU_TYPE_MASK, 0), -+ OMNIA_FEAT_MCU_TYPE_GD32 = FIELD_PREP_CONST(OMNIA_FEAT_MCU_TYPE_MASK, 1), -+ OMNIA_FEAT_MCU_TYPE_MKL = FIELD_PREP_CONST(OMNIA_FEAT_MCU_TYPE_MASK, 2), -+ OMNIA_FEAT_FEATURES_SUPPORTED = BIT(18), -+ OMNIA_FEAT_USER_REGULATOR_NOT_SUPPORTED = BIT(19), -+ -+ /* must not be set */ -+ OMNIA_FEAT_FROM_BIT_16_INVALID = BIT(20), -+ -+ OMNIA_FEAT_PER_LED_CORRECTION = BIT(21), -+ OMNIA_FEAT_USB_OVC_PROT_SETTING = BIT(22), -+}; -+ -+enum omnia_ext_sts_dword_e { -+ OMNIA_EXT_STS_SFP_nDET = BIT(0), -+ OMNIA_EXT_STS_LED_STATES_MASK = GENMASK(31, 12), -+ OMNIA_EXT_STS_WLAN0_MSATA_LED = BIT(12), -+ OMNIA_EXT_STS_WLAN1_LED = BIT(13), -+ OMNIA_EXT_STS_WLAN2_LED = BIT(14), -+ OMNIA_EXT_STS_WPAN0_LED = BIT(15), -+ OMNIA_EXT_STS_WPAN1_LED = BIT(16), -+ OMNIA_EXT_STS_WPAN2_LED = BIT(17), -+ OMNIA_EXT_STS_WAN_LED0 = BIT(18), -+ OMNIA_EXT_STS_WAN_LED1 = BIT(19), -+ OMNIA_EXT_STS_LAN0_LED0 = BIT(20), -+ OMNIA_EXT_STS_LAN0_LED1 = BIT(21), -+ OMNIA_EXT_STS_LAN1_LED0 = BIT(22), -+ OMNIA_EXT_STS_LAN1_LED1 = BIT(23), -+ OMNIA_EXT_STS_LAN2_LED0 = BIT(24), -+ OMNIA_EXT_STS_LAN2_LED1 = BIT(25), -+ OMNIA_EXT_STS_LAN3_LED0 = BIT(26), -+ OMNIA_EXT_STS_LAN3_LED1 = BIT(27), -+ OMNIA_EXT_STS_LAN4_LED0 = BIT(28), -+ OMNIA_EXT_STS_LAN4_LED1 = BIT(29), -+ OMNIA_EXT_STS_LAN5_LED0 = BIT(30), -+ OMNIA_EXT_STS_LAN5_LED1 = BIT(31), -+}; -+ -+enum omnia_ext_ctl_e { -+ OMNIA_EXT_CTL_nRES_MMC = BIT(0), -+ OMNIA_EXT_CTL_nRES_LAN = BIT(1), -+ OMNIA_EXT_CTL_nRES_PHY = BIT(2), -+ OMNIA_EXT_CTL_nPERST0 = BIT(3), -+ OMNIA_EXT_CTL_nPERST1 = BIT(4), -+ OMNIA_EXT_CTL_nPERST2 = BIT(5), -+ OMNIA_EXT_CTL_PHY_SFP = BIT(6), -+ OMNIA_EXT_CTL_PHY_SFP_AUTO = BIT(7), -+ OMNIA_EXT_CTL_nVHV_CTRL = BIT(8), -+}; -+ -+enum omnia_int_e { -+ OMNIA_INT_CARD_DET = BIT(0), -+ OMNIA_INT_MSATA_IND = BIT(1), -+ OMNIA_INT_USB30_OVC = BIT(2), -+ OMNIA_INT_USB31_OVC = BIT(3), -+ OMNIA_INT_BUTTON_PRESSED = BIT(4), -+ OMNIA_INT_SFP_nDET = BIT(5), -+ OMNIA_INT_BRIGHTNESS_CHANGED = BIT(6), -+ OMNIA_INT_TRNG = BIT(7), -+ OMNIA_INT_MESSAGE_SIGNED = BIT(8), -+ -+ OMNIA_INT_LED_STATES_MASK = GENMASK(31, 12), -+ OMNIA_INT_WLAN0_MSATA_LED = BIT(12), -+ OMNIA_INT_WLAN1_LED = BIT(13), -+ OMNIA_INT_WLAN2_LED = BIT(14), -+ OMNIA_INT_WPAN0_LED = BIT(15), -+ OMNIA_INT_WPAN1_LED = BIT(16), -+ OMNIA_INT_WPAN2_LED = BIT(17), -+ OMNIA_INT_WAN_LED0 = BIT(18), -+ OMNIA_INT_WAN_LED1 = BIT(19), -+ OMNIA_INT_LAN0_LED0 = BIT(20), -+ OMNIA_INT_LAN0_LED1 = BIT(21), -+ OMNIA_INT_LAN1_LED0 = BIT(22), -+ OMNIA_INT_LAN1_LED1 = BIT(23), -+ OMNIA_INT_LAN2_LED0 = BIT(24), -+ OMNIA_INT_LAN2_LED1 = BIT(25), -+ OMNIA_INT_LAN3_LED0 = BIT(26), -+ OMNIA_INT_LAN3_LED1 = BIT(27), -+ OMNIA_INT_LAN4_LED0 = BIT(28), -+ OMNIA_INT_LAN4_LED1 = BIT(29), -+ OMNIA_INT_LAN5_LED0 = BIT(30), -+ OMNIA_INT_LAN5_LED1 = BIT(31), -+}; -+ -+enum omnia_cmd_poweroff_e { -+ OMNIA_CMD_POWER_OFF_POWERON_BUTTON = BIT(0), -+ OMNIA_CMD_POWER_OFF_MAGIC = 0xdead, -+}; -+ -+enum omnia_cmd_usb_ovc_prot_e { -+ OMNIA_CMD_xET_USB_OVC_PROT_PORT_MASK = GENMASK(3, 0), -+ OMNIA_CMD_xET_USB_OVC_PROT_ENABLE = BIT(4), -+}; -+ -+#endif /* __TURRIS_OMNIA_MCU_INTERFACE_H */ diff --git a/target/linux/mvebu/patches-6.12/820-v6.11-03-platform-cznic-turris-omnia-mcu-Add-support-for-MCU-.patch b/target/linux/mvebu/patches-6.12/820-v6.11-03-platform-cznic-turris-omnia-mcu-Add-support-for-MCU-.patch deleted file mode 100644 index 3309a773a97..00000000000 --- a/target/linux/mvebu/patches-6.12/820-v6.11-03-platform-cznic-turris-omnia-mcu-Add-support-for-MCU-.patch +++ /dev/null @@ -1,1311 +0,0 @@ -From 7f4f2744f9788312e12940b516b51a0a466b137e Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Marek=20Beh=C3=BAn?= -Date: Mon, 1 Jul 2024 13:30:05 +0200 -Subject: [PATCH 03/11] platform: cznic: turris-omnia-mcu: Add support for MCU - connected GPIOs -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -Add support for GPIOs connected to the MCU on the Turris Omnia board. - -This includes: -- front button pin -- enable pins for USB regulators -- MiniPCIe / mSATA card presence pins in MiniPCIe port 0 -- LED output pins from WAN ethernet PHY, LAN switch and MiniPCIe ports -- on board revisions 32+ also various peripheral resets and another - voltage regulator enable pin - -Signed-off-by: Marek Behún -Acked-by: Bartosz Golaszewski -Link: https://lore.kernel.org/r/20240701113010.16447-4-kabel@kernel.org -Signed-off-by: Arnd Bergmann ---- - .../sysfs-bus-i2c-devices-turris-omnia-mcu | 16 + - drivers/platform/cznic/Kconfig | 15 + - drivers/platform/cznic/Makefile | 1 + - .../platform/cznic/turris-omnia-mcu-base.c | 3 +- - .../platform/cznic/turris-omnia-mcu-gpio.c | 1094 +++++++++++++++++ - drivers/platform/cznic/turris-omnia-mcu.h | 68 + - 6 files changed, 1196 insertions(+), 1 deletion(-) - create mode 100644 drivers/platform/cznic/turris-omnia-mcu-gpio.c - ---- a/Documentation/ABI/testing/sysfs-bus-i2c-devices-turris-omnia-mcu -+++ b/Documentation/ABI/testing/sysfs-bus-i2c-devices-turris-omnia-mcu -@@ -22,6 +22,22 @@ Description: (RO) Contains device first - - Format: %pM. - -+What: /sys/bus/i2c/devices//front_button_mode -+Date: September 2024 -+KernelVersion: 6.11 -+Contact: Marek Behún -+Description: (RW) The front button on the Turris Omnia router can be -+ configured either to change the intensity of all the LEDs on the -+ front panel, or to send the press event to the CPU as an -+ interrupt. -+ -+ This file switches between these two modes: -+ - "mcu" makes the button press event be handled by the MCU to -+ change the LEDs panel intensity. -+ - "cpu" makes the button press event be handled by the CPU. -+ -+ Format: %s. -+ - What: /sys/bus/i2c/devices//fw_features - Date: September 2024 - KernelVersion: 6.11 ---- a/drivers/platform/cznic/Kconfig -+++ b/drivers/platform/cznic/Kconfig -@@ -16,9 +16,24 @@ config TURRIS_OMNIA_MCU - tristate "Turris Omnia MCU driver" - depends on MACH_ARMADA_38X || COMPILE_TEST - depends on I2C -+ select GPIOLIB -+ select GPIOLIB_IRQCHIP - help - Say Y here to add support for the features implemented by the - microcontroller on the CZ.NIC's Turris Omnia SOHO router. -+ The features include: -+ - GPIO pins -+ - to get front button press events (the front button can be -+ configured either to generate press events to the CPU or to change -+ front LEDs panel brightness) -+ - to enable / disable USB port voltage regulators and to detect -+ USB overcurrent -+ - to detect MiniPCIe / mSATA card presence in MiniPCIe port 0 -+ - to configure resets of various peripherals on board revisions 32+ -+ - to enable / disable the VHV voltage regulator to the SOC in order -+ to be able to program SOC's OTP on board revisions 32+ -+ - to get input from the LED output pins of the WAN ethernet PHY, LAN -+ switch and MiniPCIe ports - To compile this driver as a module, choose M here; the module will be - called turris-omnia-mcu. - ---- a/drivers/platform/cznic/Makefile -+++ b/drivers/platform/cznic/Makefile -@@ -2,3 +2,4 @@ - - obj-$(CONFIG_TURRIS_OMNIA_MCU) += turris-omnia-mcu.o - turris-omnia-mcu-y := turris-omnia-mcu-base.o -+turris-omnia-mcu-y += turris-omnia-mcu-gpio.o ---- a/drivers/platform/cznic/turris-omnia-mcu-base.c -+++ b/drivers/platform/cznic/turris-omnia-mcu-base.c -@@ -196,6 +196,7 @@ static const struct attribute_group omni - - static const struct attribute_group *omnia_mcu_groups[] = { - &omnia_mcu_base_group, -+ &omnia_mcu_gpio_group, - NULL - }; - -@@ -370,7 +371,7 @@ static int omnia_mcu_probe(struct i2c_cl - "Cannot read board info\n"); - } - -- return 0; -+ return omnia_mcu_register_gpiochip(mcu); - } - - static const struct of_device_id of_omnia_mcu_match[] = { ---- /dev/null -+++ b/drivers/platform/cznic/turris-omnia-mcu-gpio.c -@@ -0,0 +1,1094 @@ -+// SPDX-License-Identifier: GPL-2.0 -+/* -+ * CZ.NIC's Turris Omnia MCU GPIO and IRQ driver -+ * -+ * 2024 by Marek Behún -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include -+#include "turris-omnia-mcu.h" -+ -+#define OMNIA_CMD_INT_ARG_LEN 8 -+#define FRONT_BUTTON_RELEASE_DELAY_MS 50 -+ -+static const char * const omnia_mcu_gpio_templates[64] = { -+ /* GPIOs with value read from the 16-bit wide status */ -+ [4] = "MiniPCIe0 Card Detect", -+ [5] = "MiniPCIe0 mSATA Indicator", -+ [6] = "Front USB3 port over-current", -+ [7] = "Rear USB3 port over-current", -+ [8] = "Front USB3 port power", -+ [9] = "Rear USB3 port power", -+ [12] = "Front Button", -+ -+ /* GPIOs with value read from the 32-bit wide extended status */ -+ [16] = "SFP nDET", -+ [28] = "MiniPCIe0 LED", -+ [29] = "MiniPCIe1 LED", -+ [30] = "MiniPCIe2 LED", -+ [31] = "MiniPCIe0 PAN LED", -+ [32] = "MiniPCIe1 PAN LED", -+ [33] = "MiniPCIe2 PAN LED", -+ [34] = "WAN PHY LED0", -+ [35] = "WAN PHY LED1", -+ [36] = "LAN switch p0 LED0", -+ [37] = "LAN switch p0 LED1", -+ [38] = "LAN switch p1 LED0", -+ [39] = "LAN switch p1 LED1", -+ [40] = "LAN switch p2 LED0", -+ [41] = "LAN switch p2 LED1", -+ [42] = "LAN switch p3 LED0", -+ [43] = "LAN switch p3 LED1", -+ [44] = "LAN switch p4 LED0", -+ [45] = "LAN switch p4 LED1", -+ [46] = "LAN switch p5 LED0", -+ [47] = "LAN switch p5 LED1", -+ -+ /* GPIOs with value read from the 16-bit wide extended control status */ -+ [48] = "eMMC nRESET", -+ [49] = "LAN switch nRESET", -+ [50] = "WAN PHY nRESET", -+ [51] = "MiniPCIe0 nPERST", -+ [52] = "MiniPCIe1 nPERST", -+ [53] = "MiniPCIe2 nPERST", -+ [54] = "WAN PHY SFP mux", -+ [56] = "VHV power disable", -+}; -+ -+struct omnia_gpio { -+ u8 cmd; -+ u8 ctl_cmd; -+ u8 bit; -+ u8 ctl_bit; -+ u8 int_bit; -+ u16 feat; -+ u16 feat_mask; -+}; -+ -+#define OMNIA_GPIO_INVALID_INT_BIT 0xff -+ -+#define _DEF_GPIO(_cmd, _ctl_cmd, _bit, _ctl_bit, _int_bit, _feat, _feat_mask) \ -+ { \ -+ .cmd = _cmd, \ -+ .ctl_cmd = _ctl_cmd, \ -+ .bit = _bit, \ -+ .ctl_bit = _ctl_bit, \ -+ .int_bit = (_int_bit) < 0 ? OMNIA_GPIO_INVALID_INT_BIT \ -+ : (_int_bit), \ -+ .feat = _feat, \ -+ .feat_mask = _feat_mask, \ -+ } -+ -+#define _DEF_GPIO_STS(_name) \ -+ _DEF_GPIO(OMNIA_CMD_GET_STATUS_WORD, 0, __bf_shf(OMNIA_STS_ ## _name), \ -+ 0, __bf_shf(OMNIA_INT_ ## _name), 0, 0) -+ -+#define _DEF_GPIO_CTL(_name) \ -+ _DEF_GPIO(OMNIA_CMD_GET_STATUS_WORD, OMNIA_CMD_GENERAL_CONTROL, \ -+ __bf_shf(OMNIA_STS_ ## _name), __bf_shf(OMNIA_CTL_ ## _name), \ -+ -1, 0, 0) -+ -+#define _DEF_GPIO_EXT_STS(_name, _feat) \ -+ _DEF_GPIO(OMNIA_CMD_GET_EXT_STATUS_DWORD, 0, \ -+ __bf_shf(OMNIA_EXT_STS_ ## _name), 0, \ -+ __bf_shf(OMNIA_INT_ ## _name), \ -+ OMNIA_FEAT_ ## _feat | OMNIA_FEAT_EXT_CMDS, \ -+ OMNIA_FEAT_ ## _feat | OMNIA_FEAT_EXT_CMDS) -+ -+#define _DEF_GPIO_EXT_STS_LED(_name, _ledext) \ -+ _DEF_GPIO(OMNIA_CMD_GET_EXT_STATUS_DWORD, 0, \ -+ __bf_shf(OMNIA_EXT_STS_ ## _name), 0, \ -+ __bf_shf(OMNIA_INT_ ## _name), \ -+ OMNIA_FEAT_LED_STATE_ ## _ledext, \ -+ OMNIA_FEAT_LED_STATE_EXT_MASK) -+ -+#define _DEF_GPIO_EXT_STS_LEDALL(_name) \ -+ _DEF_GPIO(OMNIA_CMD_GET_EXT_STATUS_DWORD, 0, \ -+ __bf_shf(OMNIA_EXT_STS_ ## _name), 0, \ -+ __bf_shf(OMNIA_INT_ ## _name), \ -+ OMNIA_FEAT_LED_STATE_EXT_MASK, 0) -+ -+#define _DEF_GPIO_EXT_CTL(_name, _feat) \ -+ _DEF_GPIO(OMNIA_CMD_GET_EXT_CONTROL_STATUS, OMNIA_CMD_EXT_CONTROL, \ -+ __bf_shf(OMNIA_EXT_CTL_ ## _name), \ -+ __bf_shf(OMNIA_EXT_CTL_ ## _name), -1, \ -+ OMNIA_FEAT_ ## _feat | OMNIA_FEAT_EXT_CMDS, \ -+ OMNIA_FEAT_ ## _feat | OMNIA_FEAT_EXT_CMDS) -+ -+#define _DEF_INT(_name) \ -+ _DEF_GPIO(0, 0, 0, 0, __bf_shf(OMNIA_INT_ ## _name), 0, 0) -+ -+static inline bool is_int_bit_valid(const struct omnia_gpio *gpio) -+{ -+ return gpio->int_bit != OMNIA_GPIO_INVALID_INT_BIT; -+} -+ -+static const struct omnia_gpio omnia_gpios[64] = { -+ /* GPIOs with value read from the 16-bit wide status */ -+ [4] = _DEF_GPIO_STS(CARD_DET), -+ [5] = _DEF_GPIO_STS(MSATA_IND), -+ [6] = _DEF_GPIO_STS(USB30_OVC), -+ [7] = _DEF_GPIO_STS(USB31_OVC), -+ [8] = _DEF_GPIO_CTL(USB30_PWRON), -+ [9] = _DEF_GPIO_CTL(USB31_PWRON), -+ -+ /* brightness changed interrupt, no GPIO */ -+ [11] = _DEF_INT(BRIGHTNESS_CHANGED), -+ -+ [12] = _DEF_GPIO_STS(BUTTON_PRESSED), -+ -+ /* TRNG interrupt, no GPIO */ -+ [13] = _DEF_INT(TRNG), -+ -+ /* MESSAGE_SIGNED interrupt, no GPIO */ -+ [14] = _DEF_INT(MESSAGE_SIGNED), -+ -+ /* GPIOs with value read from the 32-bit wide extended status */ -+ [16] = _DEF_GPIO_EXT_STS(SFP_nDET, PERIPH_MCU), -+ [28] = _DEF_GPIO_EXT_STS_LEDALL(WLAN0_MSATA_LED), -+ [29] = _DEF_GPIO_EXT_STS_LEDALL(WLAN1_LED), -+ [30] = _DEF_GPIO_EXT_STS_LEDALL(WLAN2_LED), -+ [31] = _DEF_GPIO_EXT_STS_LED(WPAN0_LED, EXT), -+ [32] = _DEF_GPIO_EXT_STS_LED(WPAN1_LED, EXT), -+ [33] = _DEF_GPIO_EXT_STS_LED(WPAN2_LED, EXT), -+ [34] = _DEF_GPIO_EXT_STS_LEDALL(WAN_LED0), -+ [35] = _DEF_GPIO_EXT_STS_LED(WAN_LED1, EXT_V32), -+ [36] = _DEF_GPIO_EXT_STS_LEDALL(LAN0_LED0), -+ [37] = _DEF_GPIO_EXT_STS_LEDALL(LAN0_LED1), -+ [38] = _DEF_GPIO_EXT_STS_LEDALL(LAN1_LED0), -+ [39] = _DEF_GPIO_EXT_STS_LEDALL(LAN1_LED1), -+ [40] = _DEF_GPIO_EXT_STS_LEDALL(LAN2_LED0), -+ [41] = _DEF_GPIO_EXT_STS_LEDALL(LAN2_LED1), -+ [42] = _DEF_GPIO_EXT_STS_LEDALL(LAN3_LED0), -+ [43] = _DEF_GPIO_EXT_STS_LEDALL(LAN3_LED1), -+ [44] = _DEF_GPIO_EXT_STS_LEDALL(LAN4_LED0), -+ [45] = _DEF_GPIO_EXT_STS_LEDALL(LAN4_LED1), -+ [46] = _DEF_GPIO_EXT_STS_LEDALL(LAN5_LED0), -+ [47] = _DEF_GPIO_EXT_STS_LEDALL(LAN5_LED1), -+ -+ /* GPIOs with value read from the 16-bit wide extended control status */ -+ [48] = _DEF_GPIO_EXT_CTL(nRES_MMC, PERIPH_MCU), -+ [49] = _DEF_GPIO_EXT_CTL(nRES_LAN, PERIPH_MCU), -+ [50] = _DEF_GPIO_EXT_CTL(nRES_PHY, PERIPH_MCU), -+ [51] = _DEF_GPIO_EXT_CTL(nPERST0, PERIPH_MCU), -+ [52] = _DEF_GPIO_EXT_CTL(nPERST1, PERIPH_MCU), -+ [53] = _DEF_GPIO_EXT_CTL(nPERST2, PERIPH_MCU), -+ [54] = _DEF_GPIO_EXT_CTL(PHY_SFP, PERIPH_MCU), -+ [56] = _DEF_GPIO_EXT_CTL(nVHV_CTRL, PERIPH_MCU), -+}; -+ -+/* mapping from interrupts to indexes of GPIOs in the omnia_gpios array */ -+static const u8 omnia_int_to_gpio_idx[32] = { -+ [__bf_shf(OMNIA_INT_CARD_DET)] = 4, -+ [__bf_shf(OMNIA_INT_MSATA_IND)] = 5, -+ [__bf_shf(OMNIA_INT_USB30_OVC)] = 6, -+ [__bf_shf(OMNIA_INT_USB31_OVC)] = 7, -+ [__bf_shf(OMNIA_INT_BUTTON_PRESSED)] = 12, -+ [__bf_shf(OMNIA_INT_TRNG)] = 13, -+ [__bf_shf(OMNIA_INT_MESSAGE_SIGNED)] = 14, -+ [__bf_shf(OMNIA_INT_SFP_nDET)] = 16, -+ [__bf_shf(OMNIA_INT_BRIGHTNESS_CHANGED)] = 11, -+ [__bf_shf(OMNIA_INT_WLAN0_MSATA_LED)] = 28, -+ [__bf_shf(OMNIA_INT_WLAN1_LED)] = 29, -+ [__bf_shf(OMNIA_INT_WLAN2_LED)] = 30, -+ [__bf_shf(OMNIA_INT_WPAN0_LED)] = 31, -+ [__bf_shf(OMNIA_INT_WPAN1_LED)] = 32, -+ [__bf_shf(OMNIA_INT_WPAN2_LED)] = 33, -+ [__bf_shf(OMNIA_INT_WAN_LED0)] = 34, -+ [__bf_shf(OMNIA_INT_WAN_LED1)] = 35, -+ [__bf_shf(OMNIA_INT_LAN0_LED0)] = 36, -+ [__bf_shf(OMNIA_INT_LAN0_LED1)] = 37, -+ [__bf_shf(OMNIA_INT_LAN1_LED0)] = 38, -+ [__bf_shf(OMNIA_INT_LAN1_LED1)] = 39, -+ [__bf_shf(OMNIA_INT_LAN2_LED0)] = 40, -+ [__bf_shf(OMNIA_INT_LAN2_LED1)] = 41, -+ [__bf_shf(OMNIA_INT_LAN3_LED0)] = 42, -+ [__bf_shf(OMNIA_INT_LAN3_LED1)] = 43, -+ [__bf_shf(OMNIA_INT_LAN4_LED0)] = 44, -+ [__bf_shf(OMNIA_INT_LAN4_LED1)] = 45, -+ [__bf_shf(OMNIA_INT_LAN5_LED0)] = 46, -+ [__bf_shf(OMNIA_INT_LAN5_LED1)] = 47, -+}; -+ -+/* index of PHY_SFP GPIO in the omnia_gpios array */ -+#define OMNIA_GPIO_PHY_SFP_OFFSET 54 -+ -+static int omnia_ctl_cmd_locked(struct omnia_mcu *mcu, u8 cmd, u16 val, u16 mask) -+{ -+ unsigned int len; -+ u8 buf[5]; -+ -+ buf[0] = cmd; -+ -+ switch (cmd) { -+ case OMNIA_CMD_GENERAL_CONTROL: -+ buf[1] = val; -+ buf[2] = mask; -+ len = 3; -+ break; -+ -+ case OMNIA_CMD_EXT_CONTROL: -+ put_unaligned_le16(val, &buf[1]); -+ put_unaligned_le16(mask, &buf[3]); -+ len = 5; -+ break; -+ -+ default: -+ BUG(); -+ } -+ -+ return omnia_cmd_write(mcu->client, buf, len); -+} -+ -+static int omnia_ctl_cmd(struct omnia_mcu *mcu, u8 cmd, u16 val, u16 mask) -+{ -+ guard(mutex)(&mcu->lock); -+ -+ return omnia_ctl_cmd_locked(mcu, cmd, val, mask); -+} -+ -+static int omnia_gpio_request(struct gpio_chip *gc, unsigned int offset) -+{ -+ if (!omnia_gpios[offset].cmd) -+ return -EINVAL; -+ -+ return 0; -+} -+ -+static int omnia_gpio_get_direction(struct gpio_chip *gc, unsigned int offset) -+{ -+ struct omnia_mcu *mcu = gpiochip_get_data(gc); -+ -+ if (offset == OMNIA_GPIO_PHY_SFP_OFFSET) { -+ int val; -+ -+ scoped_guard(mutex, &mcu->lock) { -+ val = omnia_cmd_read_bit(mcu->client, -+ OMNIA_CMD_GET_EXT_CONTROL_STATUS, -+ OMNIA_EXT_CTL_PHY_SFP_AUTO); -+ if (val < 0) -+ return val; -+ } -+ -+ if (val) -+ return GPIO_LINE_DIRECTION_IN; -+ -+ return GPIO_LINE_DIRECTION_OUT; -+ } -+ -+ if (omnia_gpios[offset].ctl_cmd) -+ return GPIO_LINE_DIRECTION_OUT; -+ -+ return GPIO_LINE_DIRECTION_IN; -+} -+ -+static int omnia_gpio_direction_input(struct gpio_chip *gc, unsigned int offset) -+{ -+ const struct omnia_gpio *gpio = &omnia_gpios[offset]; -+ struct omnia_mcu *mcu = gpiochip_get_data(gc); -+ -+ if (offset == OMNIA_GPIO_PHY_SFP_OFFSET) -+ return omnia_ctl_cmd(mcu, OMNIA_CMD_EXT_CONTROL, -+ OMNIA_EXT_CTL_PHY_SFP_AUTO, -+ OMNIA_EXT_CTL_PHY_SFP_AUTO); -+ -+ if (gpio->ctl_cmd) -+ return -ENOTSUPP; -+ -+ return 0; -+} -+ -+static int omnia_gpio_direction_output(struct gpio_chip *gc, -+ unsigned int offset, int value) -+{ -+ const struct omnia_gpio *gpio = &omnia_gpios[offset]; -+ struct omnia_mcu *mcu = gpiochip_get_data(gc); -+ u16 val, mask; -+ -+ if (!gpio->ctl_cmd) -+ return -ENOTSUPP; -+ -+ mask = BIT(gpio->ctl_bit); -+ val = value ? mask : 0; -+ -+ if (offset == OMNIA_GPIO_PHY_SFP_OFFSET) -+ mask |= OMNIA_EXT_CTL_PHY_SFP_AUTO; -+ -+ return omnia_ctl_cmd(mcu, gpio->ctl_cmd, val, mask); -+} -+ -+static int omnia_gpio_get(struct gpio_chip *gc, unsigned int offset) -+{ -+ const struct omnia_gpio *gpio = &omnia_gpios[offset]; -+ struct omnia_mcu *mcu = gpiochip_get_data(gc); -+ -+ /* -+ * If firmware does not support the new interrupt API, we are informed -+ * of every change of the status word by an interrupt from MCU and save -+ * its value in the interrupt service routine. Simply return the saved -+ * value. -+ */ -+ if (gpio->cmd == OMNIA_CMD_GET_STATUS_WORD && -+ !(mcu->features & OMNIA_FEAT_NEW_INT_API)) -+ return test_bit(gpio->bit, &mcu->last_status); -+ -+ guard(mutex)(&mcu->lock); -+ -+ /* -+ * If firmware does support the new interrupt API, we may have cached -+ * the value of a GPIO in the interrupt service routine. If not, read -+ * the relevant bit now. -+ */ -+ if (is_int_bit_valid(gpio) && test_bit(gpio->int_bit, &mcu->is_cached)) -+ return test_bit(gpio->int_bit, &mcu->cached); -+ -+ return omnia_cmd_read_bit(mcu->client, gpio->cmd, BIT(gpio->bit)); -+} -+ -+static unsigned long * -+_relevant_field_for_sts_cmd(u8 cmd, unsigned long *sts, unsigned long *ext_sts, -+ unsigned long *ext_ctl) -+{ -+ switch (cmd) { -+ case OMNIA_CMD_GET_STATUS_WORD: -+ return sts; -+ case OMNIA_CMD_GET_EXT_STATUS_DWORD: -+ return ext_sts; -+ case OMNIA_CMD_GET_EXT_CONTROL_STATUS: -+ return ext_ctl; -+ default: -+ return NULL; -+ } -+} -+ -+static int omnia_gpio_get_multiple(struct gpio_chip *gc, unsigned long *mask, -+ unsigned long *bits) -+{ -+ unsigned long sts = 0, ext_sts = 0, ext_ctl = 0, *field; -+ struct omnia_mcu *mcu = gpiochip_get_data(gc); -+ struct i2c_client *client = mcu->client; -+ unsigned int i; -+ int err; -+ -+ /* determine which bits to read from the 3 possible commands */ -+ for_each_set_bit(i, mask, ARRAY_SIZE(omnia_gpios)) { -+ field = _relevant_field_for_sts_cmd(omnia_gpios[i].cmd, -+ &sts, &ext_sts, &ext_ctl); -+ if (!field) -+ continue; -+ -+ __set_bit(omnia_gpios[i].bit, field); -+ } -+ -+ guard(mutex)(&mcu->lock); -+ -+ if (mcu->features & OMNIA_FEAT_NEW_INT_API) { -+ /* read relevant bits from status */ -+ err = omnia_cmd_read_bits(client, OMNIA_CMD_GET_STATUS_WORD, -+ sts, &sts); -+ if (err) -+ return err; -+ } else { -+ /* -+ * Use status word value cached in the interrupt service routine -+ * if firmware does not support the new interrupt API. -+ */ -+ sts = mcu->last_status; -+ } -+ -+ /* read relevant bits from extended status */ -+ err = omnia_cmd_read_bits(client, OMNIA_CMD_GET_EXT_STATUS_DWORD, -+ ext_sts, &ext_sts); -+ if (err) -+ return err; -+ -+ /* read relevant bits from extended control */ -+ err = omnia_cmd_read_bits(client, OMNIA_CMD_GET_EXT_CONTROL_STATUS, -+ ext_ctl, &ext_ctl); -+ if (err) -+ return err; -+ -+ /* assign relevant bits in result */ -+ for_each_set_bit(i, mask, ARRAY_SIZE(omnia_gpios)) { -+ field = _relevant_field_for_sts_cmd(omnia_gpios[i].cmd, -+ &sts, &ext_sts, &ext_ctl); -+ if (!field) -+ continue; -+ -+ __assign_bit(i, bits, test_bit(omnia_gpios[i].bit, field)); -+ } -+ -+ return 0; -+} -+ -+static void omnia_gpio_set(struct gpio_chip *gc, unsigned int offset, int value) -+{ -+ const struct omnia_gpio *gpio = &omnia_gpios[offset]; -+ struct omnia_mcu *mcu = gpiochip_get_data(gc); -+ u16 val, mask; -+ -+ if (!gpio->ctl_cmd) -+ return; -+ -+ mask = BIT(gpio->ctl_bit); -+ val = value ? mask : 0; -+ -+ omnia_ctl_cmd(mcu, gpio->ctl_cmd, val, mask); -+} -+ -+static void omnia_gpio_set_multiple(struct gpio_chip *gc, unsigned long *mask, -+ unsigned long *bits) -+{ -+ unsigned long ctl = 0, ctl_mask = 0, ext_ctl = 0, ext_ctl_mask = 0; -+ struct omnia_mcu *mcu = gpiochip_get_data(gc); -+ unsigned int i; -+ -+ for_each_set_bit(i, mask, ARRAY_SIZE(omnia_gpios)) { -+ unsigned long *field, *field_mask; -+ u8 bit = omnia_gpios[i].ctl_bit; -+ -+ switch (omnia_gpios[i].ctl_cmd) { -+ case OMNIA_CMD_GENERAL_CONTROL: -+ field = &ctl; -+ field_mask = &ctl_mask; -+ break; -+ case OMNIA_CMD_EXT_CONTROL: -+ field = &ext_ctl; -+ field_mask = &ext_ctl_mask; -+ break; -+ default: -+ field = field_mask = NULL; -+ break; -+ } -+ -+ if (!field) -+ continue; -+ -+ __set_bit(bit, field_mask); -+ __assign_bit(bit, field, test_bit(i, bits)); -+ } -+ -+ guard(mutex)(&mcu->lock); -+ -+ if (ctl_mask) -+ omnia_ctl_cmd_locked(mcu, OMNIA_CMD_GENERAL_CONTROL, -+ ctl, ctl_mask); -+ -+ if (ext_ctl_mask) -+ omnia_ctl_cmd_locked(mcu, OMNIA_CMD_EXT_CONTROL, -+ ext_ctl, ext_ctl_mask); -+} -+ -+static bool omnia_gpio_available(struct omnia_mcu *mcu, -+ const struct omnia_gpio *gpio) -+{ -+ if (gpio->feat_mask) -+ return (mcu->features & gpio->feat_mask) == gpio->feat; -+ -+ if (gpio->feat) -+ return mcu->features & gpio->feat; -+ -+ return true; -+} -+ -+static int omnia_gpio_init_valid_mask(struct gpio_chip *gc, -+ unsigned long *valid_mask, -+ unsigned int ngpios) -+{ -+ struct omnia_mcu *mcu = gpiochip_get_data(gc); -+ -+ for (unsigned int i = 0; i < ngpios; i++) { -+ const struct omnia_gpio *gpio = &omnia_gpios[i]; -+ -+ if (gpio->cmd || is_int_bit_valid(gpio)) -+ __assign_bit(i, valid_mask, -+ omnia_gpio_available(mcu, gpio)); -+ else -+ __clear_bit(i, valid_mask); -+ } -+ -+ return 0; -+} -+ -+static int omnia_gpio_of_xlate(struct gpio_chip *gc, -+ const struct of_phandle_args *gpiospec, -+ u32 *flags) -+{ -+ u32 bank, gpio; -+ -+ if (WARN_ON(gpiospec->args_count != 3)) -+ return -EINVAL; -+ -+ if (flags) -+ *flags = gpiospec->args[2]; -+ -+ bank = gpiospec->args[0]; -+ gpio = gpiospec->args[1]; -+ -+ switch (bank) { -+ case 0: -+ return gpio < 16 ? gpio : -EINVAL; -+ case 1: -+ return gpio < 32 ? 16 + gpio : -EINVAL; -+ case 2: -+ return gpio < 16 ? 48 + gpio : -EINVAL; -+ default: -+ return -EINVAL; -+ } -+} -+ -+static void omnia_irq_shutdown(struct irq_data *d) -+{ -+ struct gpio_chip *gc = irq_data_get_irq_chip_data(d); -+ struct omnia_mcu *mcu = gpiochip_get_data(gc); -+ irq_hw_number_t hwirq = irqd_to_hwirq(d); -+ u8 bit = omnia_gpios[hwirq].int_bit; -+ -+ __clear_bit(bit, &mcu->rising); -+ __clear_bit(bit, &mcu->falling); -+} -+ -+static void omnia_irq_mask(struct irq_data *d) -+{ -+ struct gpio_chip *gc = irq_data_get_irq_chip_data(d); -+ struct omnia_mcu *mcu = gpiochip_get_data(gc); -+ irq_hw_number_t hwirq = irqd_to_hwirq(d); -+ u8 bit = omnia_gpios[hwirq].int_bit; -+ -+ if (!omnia_gpios[hwirq].cmd) -+ __clear_bit(bit, &mcu->rising); -+ __clear_bit(bit, &mcu->mask); -+ gpiochip_disable_irq(gc, hwirq); -+} -+ -+static void omnia_irq_unmask(struct irq_data *d) -+{ -+ struct gpio_chip *gc = irq_data_get_irq_chip_data(d); -+ struct omnia_mcu *mcu = gpiochip_get_data(gc); -+ irq_hw_number_t hwirq = irqd_to_hwirq(d); -+ u8 bit = omnia_gpios[hwirq].int_bit; -+ -+ gpiochip_enable_irq(gc, hwirq); -+ __set_bit(bit, &mcu->mask); -+ if (!omnia_gpios[hwirq].cmd) -+ __set_bit(bit, &mcu->rising); -+} -+ -+static int omnia_irq_set_type(struct irq_data *d, unsigned int type) -+{ -+ struct gpio_chip *gc = irq_data_get_irq_chip_data(d); -+ struct omnia_mcu *mcu = gpiochip_get_data(gc); -+ irq_hw_number_t hwirq = irqd_to_hwirq(d); -+ struct device *dev = &mcu->client->dev; -+ u8 bit = omnia_gpios[hwirq].int_bit; -+ -+ if (!(type & IRQ_TYPE_EDGE_BOTH)) { -+ dev_err(dev, "irq %u: unsupported type %u\n", d->irq, type); -+ return -EINVAL; -+ } -+ -+ __assign_bit(bit, &mcu->rising, type & IRQ_TYPE_EDGE_RISING); -+ __assign_bit(bit, &mcu->falling, type & IRQ_TYPE_EDGE_FALLING); -+ -+ return 0; -+} -+ -+static void omnia_irq_bus_lock(struct irq_data *d) -+{ -+ struct gpio_chip *gc = irq_data_get_irq_chip_data(d); -+ struct omnia_mcu *mcu = gpiochip_get_data(gc); -+ -+ /* nothing to do if MCU firmware does not support new interrupt API */ -+ if (!(mcu->features & OMNIA_FEAT_NEW_INT_API)) -+ return; -+ -+ mutex_lock(&mcu->lock); -+} -+ -+/** -+ * omnia_mask_interleave - Interleaves the bytes from @rising and @falling -+ * @dst: the destination u8 array of interleaved bytes -+ * @rising: rising mask -+ * @falling: falling mask -+ * -+ * Interleaves the little-endian bytes from @rising and @falling words. -+ * -+ * If @rising = (r0, r1, r2, r3) and @falling = (f0, f1, f2, f3), the result is -+ * @dst = (r0, f0, r1, f1, r2, f2, r3, f3). -+ * -+ * The MCU receives an interrupt mask and reports a pending interrupt bitmap in -+ * this interleaved format. The rationale behind this is that the low-indexed -+ * bits are more important - in many cases, the user will be interested only in -+ * interrupts with indexes 0 to 7, and so the system can stop reading after -+ * first 2 bytes (r0, f0), to save time on the slow I2C bus. -+ * -+ * Feel free to remove this function and its inverse, omnia_mask_deinterleave, -+ * and use an appropriate bitmap_*() function once such a function exists. -+ */ -+static void -+omnia_mask_interleave(u8 *dst, unsigned long rising, unsigned long falling) -+{ -+ for (unsigned int i = 0; i < sizeof(u32); i++) { -+ dst[2 * i] = rising >> (8 * i); -+ dst[2 * i + 1] = falling >> (8 * i); -+ } -+} -+ -+/** -+ * omnia_mask_deinterleave - Deinterleaves the bytes into @rising and @falling -+ * @src: the source u8 array containing the interleaved bytes -+ * @rising: pointer where to store the rising mask gathered from @src -+ * @falling: pointer where to store the falling mask gathered from @src -+ * -+ * This is the inverse function to omnia_mask_interleave. -+ */ -+static void omnia_mask_deinterleave(const u8 *src, unsigned long *rising, -+ unsigned long *falling) -+{ -+ *rising = *falling = 0; -+ -+ for (unsigned int i = 0; i < sizeof(u32); i++) { -+ *rising |= src[2 * i] << (8 * i); -+ *falling |= src[2 * i + 1] << (8 * i); -+ } -+} -+ -+static void omnia_irq_bus_sync_unlock(struct irq_data *d) -+{ -+ struct gpio_chip *gc = irq_data_get_irq_chip_data(d); -+ struct omnia_mcu *mcu = gpiochip_get_data(gc); -+ struct device *dev = &mcu->client->dev; -+ u8 cmd[1 + OMNIA_CMD_INT_ARG_LEN]; -+ unsigned long rising, falling; -+ int err; -+ -+ /* nothing to do if MCU firmware does not support new interrupt API */ -+ if (!(mcu->features & OMNIA_FEAT_NEW_INT_API)) -+ return; -+ -+ cmd[0] = OMNIA_CMD_SET_INT_MASK; -+ -+ rising = mcu->rising & mcu->mask; -+ falling = mcu->falling & mcu->mask; -+ -+ /* interleave the rising and falling bytes into the command arguments */ -+ omnia_mask_interleave(&cmd[1], rising, falling); -+ -+ dev_dbg(dev, "set int mask %8ph\n", &cmd[1]); -+ -+ err = omnia_cmd_write(mcu->client, cmd, sizeof(cmd)); -+ if (err) { -+ dev_err(dev, "Cannot set mask: %d\n", err); -+ goto unlock; -+ } -+ -+ /* -+ * Remember which GPIOs have both rising and falling interrupts enabled. -+ * For those we will cache their value so that .get() method is faster. -+ * We also need to forget cached values of GPIOs that aren't cached -+ * anymore. -+ */ -+ mcu->both = rising & falling; -+ mcu->is_cached &= mcu->both; -+ -+unlock: -+ mutex_unlock(&mcu->lock); -+} -+ -+static const struct irq_chip omnia_mcu_irq_chip = { -+ .name = "Turris Omnia MCU interrupts", -+ .irq_shutdown = omnia_irq_shutdown, -+ .irq_mask = omnia_irq_mask, -+ .irq_unmask = omnia_irq_unmask, -+ .irq_set_type = omnia_irq_set_type, -+ .irq_bus_lock = omnia_irq_bus_lock, -+ .irq_bus_sync_unlock = omnia_irq_bus_sync_unlock, -+ .flags = IRQCHIP_IMMUTABLE, -+ GPIOCHIP_IRQ_RESOURCE_HELPERS, -+}; -+ -+static void omnia_irq_init_valid_mask(struct gpio_chip *gc, -+ unsigned long *valid_mask, -+ unsigned int ngpios) -+{ -+ struct omnia_mcu *mcu = gpiochip_get_data(gc); -+ -+ for (unsigned int i = 0; i < ngpios; i++) { -+ const struct omnia_gpio *gpio = &omnia_gpios[i]; -+ -+ if (is_int_bit_valid(gpio)) -+ __assign_bit(i, valid_mask, -+ omnia_gpio_available(mcu, gpio)); -+ else -+ __clear_bit(i, valid_mask); -+ } -+} -+ -+static int omnia_irq_init_hw(struct gpio_chip *gc) -+{ -+ struct omnia_mcu *mcu = gpiochip_get_data(gc); -+ u8 cmd[1 + OMNIA_CMD_INT_ARG_LEN] = {}; -+ -+ cmd[0] = OMNIA_CMD_SET_INT_MASK; -+ -+ return omnia_cmd_write(mcu->client, cmd, sizeof(cmd)); -+} -+ -+/* -+ * Determine how many bytes we need to read from the reply to the -+ * OMNIA_CMD_GET_INT_AND_CLEAR command in order to retrieve all unmasked -+ * interrupts. -+ */ -+static unsigned int -+omnia_irq_compute_pending_length(unsigned long rising, unsigned long falling) -+{ -+ return max(omnia_compute_reply_length(rising, true, 0), -+ omnia_compute_reply_length(falling, true, 1)); -+} -+ -+static bool omnia_irq_read_pending_new(struct omnia_mcu *mcu, -+ unsigned long *pending) -+{ -+ struct device *dev = &mcu->client->dev; -+ u8 reply[OMNIA_CMD_INT_ARG_LEN] = {}; -+ unsigned long rising, falling; -+ unsigned int len; -+ int err; -+ -+ len = omnia_irq_compute_pending_length(mcu->rising & mcu->mask, -+ mcu->falling & mcu->mask); -+ if (!len) -+ return false; -+ -+ guard(mutex)(&mcu->lock); -+ -+ err = omnia_cmd_read(mcu->client, OMNIA_CMD_GET_INT_AND_CLEAR, reply, -+ len); -+ if (err) { -+ dev_err(dev, "Cannot read pending IRQs: %d\n", err); -+ return false; -+ } -+ -+ /* deinterleave the reply bytes into rising and falling */ -+ omnia_mask_deinterleave(reply, &rising, &falling); -+ -+ rising &= mcu->mask; -+ falling &= mcu->mask; -+ *pending = rising | falling; -+ -+ /* cache values for GPIOs that have both edges enabled */ -+ mcu->is_cached &= ~(rising & falling); -+ mcu->is_cached |= mcu->both & (rising ^ falling); -+ mcu->cached = (mcu->cached | rising) & ~falling; -+ -+ return true; -+} -+ -+static int omnia_read_status_word_old_fw(struct omnia_mcu *mcu, -+ unsigned long *status) -+{ -+ u16 raw_status; -+ int err; -+ -+ err = omnia_cmd_read_u16(mcu->client, OMNIA_CMD_GET_STATUS_WORD, -+ &raw_status); -+ if (err) -+ return err; -+ -+ /* -+ * Old firmware has a bug wherein it never resets the USB port -+ * overcurrent bits back to zero. Ignore them. -+ */ -+ *status = raw_status & ~(OMNIA_STS_USB30_OVC | OMNIA_STS_USB31_OVC); -+ -+ return 0; -+} -+ -+static void button_release_emul_fn(struct work_struct *work) -+{ -+ struct omnia_mcu *mcu = container_of(to_delayed_work(work), -+ struct omnia_mcu, -+ button_release_emul_work); -+ -+ mcu->button_pressed_emul = false; -+ generic_handle_irq_safe(mcu->client->irq); -+} -+ -+static void -+fill_int_from_sts(unsigned long *rising, unsigned long *falling, -+ unsigned long rising_sts, unsigned long falling_sts, -+ unsigned long sts_bit, unsigned long int_bit) -+{ -+ if (rising_sts & sts_bit) -+ *rising |= int_bit; -+ if (falling_sts & sts_bit) -+ *falling |= int_bit; -+} -+ -+static bool omnia_irq_read_pending_old(struct omnia_mcu *mcu, -+ unsigned long *pending) -+{ -+ unsigned long status, rising_sts, falling_sts, rising, falling; -+ struct device *dev = &mcu->client->dev; -+ int err; -+ -+ guard(mutex)(&mcu->lock); -+ -+ err = omnia_read_status_word_old_fw(mcu, &status); -+ if (err) { -+ dev_err(dev, "Cannot read pending IRQs: %d\n", err); -+ return false; -+ } -+ -+ /* -+ * The old firmware triggers an interrupt whenever status word changes, -+ * but does not inform about which bits rose or fell. We need to compute -+ * this here by comparing with the last status word value. -+ * -+ * The OMNIA_STS_BUTTON_PRESSED bit needs special handling, because the -+ * old firmware clears the OMNIA_STS_BUTTON_PRESSED bit on successful -+ * completion of the OMNIA_CMD_GET_STATUS_WORD command, resulting in -+ * another interrupt: -+ * - first we get an interrupt, we read the status word where -+ * OMNIA_STS_BUTTON_PRESSED is present, -+ * - MCU clears the OMNIA_STS_BUTTON_PRESSED bit because we read the -+ * status word, -+ * - we get another interrupt because the status word changed again -+ * (the OMNIA_STS_BUTTON_PRESSED bit was cleared). -+ * -+ * The gpiolib-cdev, gpiolib-sysfs and gpio-keys input driver all call -+ * the gpiochip's .get() method after an edge event on a requested GPIO -+ * occurs. -+ * -+ * We ensure that the .get() method reads 1 for the button GPIO for some -+ * time. -+ */ -+ -+ if (status & OMNIA_STS_BUTTON_PRESSED) { -+ mcu->button_pressed_emul = true; -+ mod_delayed_work(system_wq, &mcu->button_release_emul_work, -+ msecs_to_jiffies(FRONT_BUTTON_RELEASE_DELAY_MS)); -+ } else if (mcu->button_pressed_emul) { -+ status |= OMNIA_STS_BUTTON_PRESSED; -+ } -+ -+ rising_sts = ~mcu->last_status & status; -+ falling_sts = mcu->last_status & ~status; -+ -+ mcu->last_status = status; -+ -+ /* -+ * Fill in the relevant interrupt bits from status bits for CARD_DET, -+ * MSATA_IND and BUTTON_PRESSED. -+ */ -+ rising = 0; -+ falling = 0; -+ fill_int_from_sts(&rising, &falling, rising_sts, falling_sts, -+ OMNIA_STS_CARD_DET, OMNIA_INT_CARD_DET); -+ fill_int_from_sts(&rising, &falling, rising_sts, falling_sts, -+ OMNIA_STS_MSATA_IND, OMNIA_INT_MSATA_IND); -+ fill_int_from_sts(&rising, &falling, rising_sts, falling_sts, -+ OMNIA_STS_BUTTON_PRESSED, OMNIA_INT_BUTTON_PRESSED); -+ -+ /* Use only bits that are enabled */ -+ rising &= mcu->rising & mcu->mask; -+ falling &= mcu->falling & mcu->mask; -+ *pending = rising | falling; -+ -+ return true; -+} -+ -+static bool omnia_irq_read_pending(struct omnia_mcu *mcu, -+ unsigned long *pending) -+{ -+ if (mcu->features & OMNIA_FEAT_NEW_INT_API) -+ return omnia_irq_read_pending_new(mcu, pending); -+ else -+ return omnia_irq_read_pending_old(mcu, pending); -+} -+ -+static irqreturn_t omnia_irq_thread_handler(int irq, void *dev_id) -+{ -+ struct omnia_mcu *mcu = dev_id; -+ struct irq_domain *domain; -+ unsigned long pending; -+ unsigned int i; -+ -+ if (!omnia_irq_read_pending(mcu, &pending)) -+ return IRQ_NONE; -+ -+ domain = mcu->gc.irq.domain; -+ -+ for_each_set_bit(i, &pending, 32) { -+ unsigned int nested_irq; -+ -+ nested_irq = irq_find_mapping(domain, omnia_int_to_gpio_idx[i]); -+ -+ handle_nested_irq(nested_irq); -+ } -+ -+ return IRQ_RETVAL(pending); -+} -+ -+static const char * const front_button_modes[] = { "mcu", "cpu" }; -+ -+static ssize_t front_button_mode_show(struct device *dev, -+ struct device_attribute *a, char *buf) -+{ -+ struct omnia_mcu *mcu = dev_get_drvdata(dev); -+ int val; -+ -+ if (mcu->features & OMNIA_FEAT_NEW_INT_API) { -+ val = omnia_cmd_read_bit(mcu->client, OMNIA_CMD_GET_STATUS_WORD, -+ OMNIA_STS_BUTTON_MODE); -+ if (val < 0) -+ return val; -+ } else { -+ val = !!(mcu->last_status & OMNIA_STS_BUTTON_MODE); -+ } -+ -+ return sysfs_emit(buf, "%s\n", front_button_modes[val]); -+} -+ -+static ssize_t front_button_mode_store(struct device *dev, -+ struct device_attribute *a, -+ const char *buf, size_t count) -+{ -+ struct omnia_mcu *mcu = dev_get_drvdata(dev); -+ int err, i; -+ -+ i = sysfs_match_string(front_button_modes, buf); -+ if (i < 0) -+ return i; -+ -+ err = omnia_ctl_cmd_locked(mcu, OMNIA_CMD_GENERAL_CONTROL, -+ i ? OMNIA_CTL_BUTTON_MODE : 0, -+ OMNIA_CTL_BUTTON_MODE); -+ if (err) -+ return err; -+ -+ return count; -+} -+static DEVICE_ATTR_RW(front_button_mode); -+ -+static struct attribute *omnia_mcu_gpio_attrs[] = { -+ &dev_attr_front_button_mode.attr, -+ NULL -+}; -+ -+const struct attribute_group omnia_mcu_gpio_group = { -+ .attrs = omnia_mcu_gpio_attrs, -+}; -+ -+int omnia_mcu_register_gpiochip(struct omnia_mcu *mcu) -+{ -+ bool new_api = mcu->features & OMNIA_FEAT_NEW_INT_API; -+ struct device *dev = &mcu->client->dev; -+ unsigned long irqflags; -+ int err; -+ -+ err = devm_mutex_init(dev, &mcu->lock); -+ if (err) -+ return err; -+ -+ mcu->gc.request = omnia_gpio_request; -+ mcu->gc.get_direction = omnia_gpio_get_direction; -+ mcu->gc.direction_input = omnia_gpio_direction_input; -+ mcu->gc.direction_output = omnia_gpio_direction_output; -+ mcu->gc.get = omnia_gpio_get; -+ mcu->gc.get_multiple = omnia_gpio_get_multiple; -+ mcu->gc.set = omnia_gpio_set; -+ mcu->gc.set_multiple = omnia_gpio_set_multiple; -+ mcu->gc.init_valid_mask = omnia_gpio_init_valid_mask; -+ mcu->gc.can_sleep = true; -+ mcu->gc.names = omnia_mcu_gpio_templates; -+ mcu->gc.base = -1; -+ mcu->gc.ngpio = ARRAY_SIZE(omnia_gpios); -+ mcu->gc.label = "Turris Omnia MCU GPIOs"; -+ mcu->gc.parent = dev; -+ mcu->gc.owner = THIS_MODULE; -+ mcu->gc.of_gpio_n_cells = 3; -+ mcu->gc.of_xlate = omnia_gpio_of_xlate; -+ -+ gpio_irq_chip_set_chip(&mcu->gc.irq, &omnia_mcu_irq_chip); -+ /* This will let us handle the parent IRQ in the driver */ -+ mcu->gc.irq.parent_handler = NULL; -+ mcu->gc.irq.num_parents = 0; -+ mcu->gc.irq.parents = NULL; -+ mcu->gc.irq.default_type = IRQ_TYPE_NONE; -+ mcu->gc.irq.handler = handle_bad_irq; -+ mcu->gc.irq.threaded = true; -+ if (new_api) -+ mcu->gc.irq.init_hw = omnia_irq_init_hw; -+ mcu->gc.irq.init_valid_mask = omnia_irq_init_valid_mask; -+ -+ err = devm_gpiochip_add_data(dev, &mcu->gc, mcu); -+ if (err) -+ return dev_err_probe(dev, err, "Cannot add GPIO chip\n"); -+ -+ /* -+ * Before requesting the interrupt, if firmware does not support the new -+ * interrupt API, we need to cache the value of the status word, so that -+ * when it changes, we may compare the new value with the cached one in -+ * the interrupt handler. -+ */ -+ if (!new_api) { -+ err = omnia_read_status_word_old_fw(mcu, &mcu->last_status); -+ if (err) -+ return dev_err_probe(dev, err, -+ "Cannot read status word\n"); -+ -+ INIT_DELAYED_WORK(&mcu->button_release_emul_work, -+ button_release_emul_fn); -+ } -+ -+ irqflags = IRQF_ONESHOT; -+ if (new_api) -+ irqflags |= IRQF_TRIGGER_LOW; -+ else -+ irqflags |= IRQF_TRIGGER_FALLING; -+ -+ err = devm_request_threaded_irq(dev, mcu->client->irq, NULL, -+ omnia_irq_thread_handler, irqflags, -+ "turris-omnia-mcu", mcu); -+ if (err) -+ return dev_err_probe(dev, err, "Cannot request IRQ\n"); -+ -+ if (!new_api) { -+ /* -+ * The button_release_emul_work has to be initialized before the -+ * thread is requested, and on driver remove it needs to be -+ * canceled before the thread is freed. Therefore we can't use -+ * devm_delayed_work_autocancel() directly, because the order -+ * devm_delayed_work_autocancel(); -+ * devm_request_threaded_irq(); -+ * would cause improper release order: -+ * free_irq(); -+ * cancel_delayed_work_sync(); -+ * Instead we first initialize the work above, and only now -+ * after IRQ is requested we add the work devm action. -+ */ -+ err = devm_add_action(dev, devm_delayed_work_drop, -+ &mcu->button_release_emul_work); -+ if (err) -+ return err; -+ } -+ -+ return 0; -+} ---- a/drivers/platform/cznic/turris-omnia-mcu.h -+++ b/drivers/platform/cznic/turris-omnia-mcu.h -@@ -8,8 +8,12 @@ - #ifndef __TURRIS_OMNIA_MCU_H - #define __TURRIS_OMNIA_MCU_H - -+#include -+#include - #include -+#include - #include -+#include - #include - - struct i2c_client; -@@ -23,18 +27,78 @@ struct omnia_mcu { - u64 board_serial_number; - u8 board_first_mac[ETH_ALEN]; - u8 board_revision; -+ -+ /* GPIO chip */ -+ struct gpio_chip gc; -+ struct mutex lock; -+ unsigned long mask, rising, falling, both, cached, is_cached; -+ /* Old MCU firmware handling needs the following */ -+ struct delayed_work button_release_emul_work; -+ unsigned long last_status; -+ bool button_pressed_emul; - }; - - int omnia_cmd_write_read(const struct i2c_client *client, - void *cmd, unsigned int cmd_len, - void *reply, unsigned int reply_len); - -+static inline int omnia_cmd_write(const struct i2c_client *client, void *cmd, -+ unsigned int len) -+{ -+ return omnia_cmd_write_read(client, cmd, len, NULL, 0); -+} -+ - static inline int omnia_cmd_read(const struct i2c_client *client, u8 cmd, - void *reply, unsigned int len) - { - return omnia_cmd_write_read(client, &cmd, 1, reply, len); - } - -+static inline unsigned int -+omnia_compute_reply_length(unsigned long mask, bool interleaved, -+ unsigned int offset) -+{ -+ if (!mask) -+ return 0; -+ -+ return ((__fls(mask) >> 3) << interleaved) + 1 + offset; -+} -+ -+/* Returns 0 on success */ -+static inline int omnia_cmd_read_bits(const struct i2c_client *client, u8 cmd, -+ unsigned long bits, unsigned long *dst) -+{ -+ __le32 reply; -+ int err; -+ -+ if (!bits) { -+ *dst = 0; -+ return 0; -+ } -+ -+ err = omnia_cmd_read(client, cmd, &reply, -+ omnia_compute_reply_length(bits, false, 0)); -+ if (err) -+ return err; -+ -+ *dst = le32_to_cpu(reply) & bits; -+ -+ return 0; -+} -+ -+static inline int omnia_cmd_read_bit(const struct i2c_client *client, u8 cmd, -+ unsigned long bit) -+{ -+ unsigned long reply; -+ int err; -+ -+ err = omnia_cmd_read_bits(client, cmd, bit, &reply); -+ if (err) -+ return err; -+ -+ return !!reply; -+} -+ - static inline int omnia_cmd_read_u32(const struct i2c_client *client, u8 cmd, - u32 *dst) - { -@@ -71,4 +135,8 @@ static inline int omnia_cmd_read_u8(cons - return omnia_cmd_read(client, cmd, reply, sizeof(*reply)); - } - -+extern const struct attribute_group omnia_mcu_gpio_group; -+ -+int omnia_mcu_register_gpiochip(struct omnia_mcu *mcu); -+ - #endif /* __TURRIS_OMNIA_MCU_H */ diff --git a/target/linux/mvebu/patches-6.12/820-v6.11-04-platform-cznic-turris-omnia-mcu-Add-support-for-powe.patch b/target/linux/mvebu/patches-6.12/820-v6.11-04-platform-cznic-turris-omnia-mcu-Add-support-for-powe.patch deleted file mode 100644 index 2ef6242d707..00000000000 --- a/target/linux/mvebu/patches-6.12/820-v6.11-04-platform-cznic-turris-omnia-mcu-Add-support-for-powe.patch +++ /dev/null @@ -1,415 +0,0 @@ -From f69e0a731ab471f3a57c48258ad2d9990820c173 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Marek=20Beh=C3=BAn?= -Date: Mon, 1 Jul 2024 13:30:06 +0200 -Subject: [PATCH 04/11] platform: cznic: turris-omnia-mcu: Add support for - poweroff and wakeup -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -Add support for true board poweroff (MCU can disable all unnecessary -voltage regulators) and wakeup at a specified time, implemented via a -RTC driver so that the rtcwake utility can be used to configure it. - -Signed-off-by: Marek Behún -Reviewed-by: Andy Shevchenko -Acked-by: Alexandre Belloni -Acked-by: Bartosz Golaszewski -Link: https://lore.kernel.org/r/20240701113010.16447-5-kabel@kernel.org -Signed-off-by: Arnd Bergmann ---- - .../sysfs-bus-i2c-devices-turris-omnia-mcu | 16 ++ - drivers/platform/cznic/Kconfig | 4 + - drivers/platform/cznic/Makefile | 1 + - .../platform/cznic/turris-omnia-mcu-base.c | 5 + - .../cznic/turris-omnia-mcu-sys-off-wakeup.c | 260 ++++++++++++++++++ - drivers/platform/cznic/turris-omnia-mcu.h | 20 ++ - 6 files changed, 306 insertions(+) - create mode 100644 drivers/platform/cznic/turris-omnia-mcu-sys-off-wakeup.c - ---- a/Documentation/ABI/testing/sysfs-bus-i2c-devices-turris-omnia-mcu -+++ b/Documentation/ABI/testing/sysfs-bus-i2c-devices-turris-omnia-mcu -@@ -38,6 +38,22 @@ Description: (RW) The front button on th - - Format: %s. - -+What: /sys/bus/i2c/devices//front_button_poweron -+Date: September 2024 -+KernelVersion: 6.11 -+Contact: Marek Behún -+Description: (RW) Newer versions of the microcontroller firmware of the -+ Turris Omnia router support powering off the router into true -+ low power mode. The router can be powered on by pressing the -+ front button. -+ -+ This file configures whether front button power on is enabled. -+ -+ This file is present only if the power off feature is supported -+ by the firmware. -+ -+ Format: %i. -+ - What: /sys/bus/i2c/devices//fw_features - Date: September 2024 - KernelVersion: 6.11 ---- a/drivers/platform/cznic/Kconfig -+++ b/drivers/platform/cznic/Kconfig -@@ -18,10 +18,14 @@ config TURRIS_OMNIA_MCU - depends on I2C - select GPIOLIB - select GPIOLIB_IRQCHIP -+ select RTC_CLASS - help - Say Y here to add support for the features implemented by the - microcontroller on the CZ.NIC's Turris Omnia SOHO router. - The features include: -+ - board poweroff into true low power mode (with voltage regulators -+ disabled) and the ability to configure wake up from this mode (via -+ rtcwake) - - GPIO pins - - to get front button press events (the front button can be - configured either to generate press events to the CPU or to change ---- a/drivers/platform/cznic/Makefile -+++ b/drivers/platform/cznic/Makefile -@@ -3,3 +3,4 @@ - obj-$(CONFIG_TURRIS_OMNIA_MCU) += turris-omnia-mcu.o - turris-omnia-mcu-y := turris-omnia-mcu-base.o - turris-omnia-mcu-y += turris-omnia-mcu-gpio.o -+turris-omnia-mcu-y += turris-omnia-mcu-sys-off-wakeup.o ---- a/drivers/platform/cznic/turris-omnia-mcu-base.c -+++ b/drivers/platform/cznic/turris-omnia-mcu-base.c -@@ -197,6 +197,7 @@ static const struct attribute_group omni - static const struct attribute_group *omnia_mcu_groups[] = { - &omnia_mcu_base_group, - &omnia_mcu_gpio_group, -+ &omnia_mcu_poweroff_group, - NULL - }; - -@@ -371,6 +372,10 @@ static int omnia_mcu_probe(struct i2c_cl - "Cannot read board info\n"); - } - -+ err = omnia_mcu_register_sys_off_and_wakeup(mcu); -+ if (err) -+ return err; -+ - return omnia_mcu_register_gpiochip(mcu); - } - ---- /dev/null -+++ b/drivers/platform/cznic/turris-omnia-mcu-sys-off-wakeup.c -@@ -0,0 +1,260 @@ -+// SPDX-License-Identifier: GPL-2.0 -+/* -+ * CZ.NIC's Turris Omnia MCU system off and RTC wakeup driver -+ * -+ * This is not a true RTC driver (in the sense that it does not provide a -+ * real-time clock), rather the MCU implements a wakeup from powered off state -+ * at a specified time relative to MCU boot, and we expose this feature via RTC -+ * alarm, so that it can be used via the rtcwake command, which is the standard -+ * Linux command for this. -+ * -+ * 2024 by Marek Behún -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include -+#include "turris-omnia-mcu.h" -+ -+static int omnia_get_uptime_wakeup(const struct i2c_client *client, u32 *uptime, -+ u32 *wakeup) -+{ -+ __le32 reply[2]; -+ int err; -+ -+ err = omnia_cmd_read(client, OMNIA_CMD_GET_UPTIME_AND_WAKEUP, reply, -+ sizeof(reply)); -+ if (err) -+ return err; -+ -+ if (uptime) -+ *uptime = le32_to_cpu(reply[0]); -+ -+ if (wakeup) -+ *wakeup = le32_to_cpu(reply[1]); -+ -+ return 0; -+} -+ -+static int omnia_read_time(struct device *dev, struct rtc_time *tm) -+{ -+ u32 uptime; -+ int err; -+ -+ err = omnia_get_uptime_wakeup(to_i2c_client(dev), &uptime, NULL); -+ if (err) -+ return err; -+ -+ rtc_time64_to_tm(uptime, tm); -+ -+ return 0; -+} -+ -+static int omnia_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) -+{ -+ struct i2c_client *client = to_i2c_client(dev); -+ struct omnia_mcu *mcu = i2c_get_clientdata(client); -+ u32 wakeup; -+ int err; -+ -+ err = omnia_get_uptime_wakeup(client, NULL, &wakeup); -+ if (err) -+ return err; -+ -+ alrm->enabled = !!wakeup; -+ rtc_time64_to_tm(wakeup ?: mcu->rtc_alarm, &alrm->time); -+ -+ return 0; -+} -+ -+static int omnia_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) -+{ -+ struct i2c_client *client = to_i2c_client(dev); -+ struct omnia_mcu *mcu = i2c_get_clientdata(client); -+ -+ mcu->rtc_alarm = rtc_tm_to_time64(&alrm->time); -+ -+ if (alrm->enabled) -+ return omnia_cmd_write_u32(client, OMNIA_CMD_SET_WAKEUP, -+ mcu->rtc_alarm); -+ -+ return 0; -+} -+ -+static int omnia_alarm_irq_enable(struct device *dev, unsigned int enabled) -+{ -+ struct i2c_client *client = to_i2c_client(dev); -+ struct omnia_mcu *mcu = i2c_get_clientdata(client); -+ -+ return omnia_cmd_write_u32(client, OMNIA_CMD_SET_WAKEUP, -+ enabled ? mcu->rtc_alarm : 0); -+} -+ -+static const struct rtc_class_ops omnia_rtc_ops = { -+ .read_time = omnia_read_time, -+ .read_alarm = omnia_read_alarm, -+ .set_alarm = omnia_set_alarm, -+ .alarm_irq_enable = omnia_alarm_irq_enable, -+}; -+ -+static int omnia_power_off(struct sys_off_data *data) -+{ -+ struct omnia_mcu *mcu = data->cb_data; -+ __be32 tmp; -+ u8 cmd[9]; -+ u16 arg; -+ int err; -+ -+ if (mcu->front_button_poweron) -+ arg = OMNIA_CMD_POWER_OFF_POWERON_BUTTON; -+ else -+ arg = 0; -+ -+ cmd[0] = OMNIA_CMD_POWER_OFF; -+ put_unaligned_le16(OMNIA_CMD_POWER_OFF_MAGIC, &cmd[1]); -+ put_unaligned_le16(arg, &cmd[3]); -+ -+ /* -+ * Although all values from and to MCU are passed in little-endian, the -+ * MCU's CRC unit uses big-endian CRC32 polynomial (0x04c11db7), so we -+ * need to use crc32_be() here. -+ */ -+ tmp = cpu_to_be32(get_unaligned_le32(&cmd[1])); -+ put_unaligned_le32(crc32_be(~0, (void *)&tmp, sizeof(tmp)), &cmd[5]); -+ -+ err = omnia_cmd_write(mcu->client, cmd, sizeof(cmd)); -+ if (err) -+ dev_err(&mcu->client->dev, -+ "Unable to send the poweroff command: %d\n", err); -+ -+ return NOTIFY_DONE; -+} -+ -+static int omnia_restart(struct sys_off_data *data) -+{ -+ struct omnia_mcu *mcu = data->cb_data; -+ u8 cmd[3]; -+ int err; -+ -+ cmd[0] = OMNIA_CMD_GENERAL_CONTROL; -+ -+ if (reboot_mode == REBOOT_HARD) -+ cmd[1] = cmd[2] = OMNIA_CTL_HARD_RST; -+ else -+ cmd[1] = cmd[2] = OMNIA_CTL_LIGHT_RST; -+ -+ err = omnia_cmd_write(mcu->client, cmd, sizeof(cmd)); -+ if (err) -+ dev_err(&mcu->client->dev, -+ "Unable to send the restart command: %d\n", err); -+ -+ /* -+ * MCU needs a little bit to process the I2C command, otherwise it will -+ * do a light reset based on SOC SYSRES_OUT pin. -+ */ -+ mdelay(1); -+ -+ return NOTIFY_DONE; -+} -+ -+static ssize_t front_button_poweron_show(struct device *dev, -+ struct device_attribute *a, char *buf) -+{ -+ struct omnia_mcu *mcu = dev_get_drvdata(dev); -+ -+ return sysfs_emit(buf, "%d\n", mcu->front_button_poweron); -+} -+ -+static ssize_t front_button_poweron_store(struct device *dev, -+ struct device_attribute *a, -+ const char *buf, size_t count) -+{ -+ struct omnia_mcu *mcu = dev_get_drvdata(dev); -+ bool val; -+ int err; -+ -+ err = kstrtobool(buf, &val); -+ if (err) -+ return err; -+ -+ mcu->front_button_poweron = val; -+ -+ return count; -+} -+static DEVICE_ATTR_RW(front_button_poweron); -+ -+static struct attribute *omnia_mcu_poweroff_attrs[] = { -+ &dev_attr_front_button_poweron.attr, -+ NULL -+}; -+ -+static umode_t poweroff_attrs_visible(struct kobject *kobj, struct attribute *a, -+ int n) -+{ -+ struct device *dev = kobj_to_dev(kobj); -+ struct omnia_mcu *mcu = dev_get_drvdata(dev); -+ -+ if (mcu->features & OMNIA_FEAT_POWEROFF_WAKEUP) -+ return a->mode; -+ -+ return 0; -+} -+ -+const struct attribute_group omnia_mcu_poweroff_group = { -+ .attrs = omnia_mcu_poweroff_attrs, -+ .is_visible = poweroff_attrs_visible, -+}; -+ -+int omnia_mcu_register_sys_off_and_wakeup(struct omnia_mcu *mcu) -+{ -+ struct device *dev = &mcu->client->dev; -+ int err; -+ -+ /* MCU restart is always available */ -+ err = devm_register_sys_off_handler(dev, SYS_OFF_MODE_RESTART, -+ SYS_OFF_PRIO_FIRMWARE, -+ omnia_restart, mcu); -+ if (err) -+ return dev_err_probe(dev, err, -+ "Cannot register system restart handler\n"); -+ -+ /* -+ * Poweroff and wakeup are available only if POWEROFF_WAKEUP feature is -+ * present. -+ */ -+ if (!(mcu->features & OMNIA_FEAT_POWEROFF_WAKEUP)) -+ return 0; -+ -+ err = devm_register_sys_off_handler(dev, SYS_OFF_MODE_POWER_OFF, -+ SYS_OFF_PRIO_FIRMWARE, -+ omnia_power_off, mcu); -+ if (err) -+ return dev_err_probe(dev, err, -+ "Cannot register system power off handler\n"); -+ -+ mcu->rtcdev = devm_rtc_allocate_device(dev); -+ if (IS_ERR(mcu->rtcdev)) -+ return dev_err_probe(dev, PTR_ERR(mcu->rtcdev), -+ "Cannot allocate RTC device\n"); -+ -+ mcu->rtcdev->ops = &omnia_rtc_ops; -+ mcu->rtcdev->range_max = U32_MAX; -+ set_bit(RTC_FEATURE_ALARM_WAKEUP_ONLY, mcu->rtcdev->features); -+ -+ err = devm_rtc_register_device(mcu->rtcdev); -+ if (err) -+ return dev_err_probe(dev, err, "Cannot register RTC device\n"); -+ -+ mcu->front_button_poweron = true; -+ -+ return 0; -+} ---- a/drivers/platform/cznic/turris-omnia-mcu.h -+++ b/drivers/platform/cznic/turris-omnia-mcu.h -@@ -15,8 +15,10 @@ - #include - #include - #include -+#include - - struct i2c_client; -+struct rtc_device; - - struct omnia_mcu { - struct i2c_client *client; -@@ -36,6 +38,11 @@ struct omnia_mcu { - struct delayed_work button_release_emul_work; - unsigned long last_status; - bool button_pressed_emul; -+ -+ /* RTC device for configuring wake-up */ -+ struct rtc_device *rtcdev; -+ u32 rtc_alarm; -+ bool front_button_poweron; - }; - - int omnia_cmd_write_read(const struct i2c_client *client, -@@ -48,6 +55,17 @@ static inline int omnia_cmd_write(const - return omnia_cmd_write_read(client, cmd, len, NULL, 0); - } - -+static inline int omnia_cmd_write_u32(const struct i2c_client *client, u8 cmd, -+ u32 val) -+{ -+ u8 buf[5]; -+ -+ buf[0] = cmd; -+ put_unaligned_le32(val, &buf[1]); -+ -+ return omnia_cmd_write(client, buf, sizeof(buf)); -+} -+ - static inline int omnia_cmd_read(const struct i2c_client *client, u8 cmd, - void *reply, unsigned int len) - { -@@ -136,7 +154,9 @@ static inline int omnia_cmd_read_u8(cons - } - - extern const struct attribute_group omnia_mcu_gpio_group; -+extern const struct attribute_group omnia_mcu_poweroff_group; - - int omnia_mcu_register_gpiochip(struct omnia_mcu *mcu); -+int omnia_mcu_register_sys_off_and_wakeup(struct omnia_mcu *mcu); - - #endif /* __TURRIS_OMNIA_MCU_H */ diff --git a/target/linux/mvebu/patches-6.12/820-v6.11-05-platform-cznic-turris-omnia-mcu-Add-support-for-MCU-.patch b/target/linux/mvebu/patches-6.12/820-v6.11-05-platform-cznic-turris-omnia-mcu-Add-support-for-MCU-.patch deleted file mode 100644 index cf3f88bfcfc..00000000000 --- a/target/linux/mvebu/patches-6.12/820-v6.11-05-platform-cznic-turris-omnia-mcu-Add-support-for-MCU-.patch +++ /dev/null @@ -1,250 +0,0 @@ -From 33ae4e4c86bc6ff298489fb8b743e2743dd0af6d Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Marek=20Beh=C3=BAn?= -Date: Mon, 1 Jul 2024 13:30:07 +0200 -Subject: [PATCH 05/11] platform: cznic: turris-omnia-mcu: Add support for MCU - watchdog -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -Add support for the watchdog mechanism provided by the MCU. - -Signed-off-by: Marek Behún -Reviewed-by: Andy Shevchenko -Reviewed-by: Guenter Roeck -Acked-by: Bartosz Golaszewski -Link: https://lore.kernel.org/r/20240701113010.16447-6-kabel@kernel.org -Signed-off-by: Arnd Bergmann ---- - drivers/platform/cznic/Kconfig | 2 + - drivers/platform/cznic/Makefile | 1 + - .../platform/cznic/turris-omnia-mcu-base.c | 4 + - .../cznic/turris-omnia-mcu-watchdog.c | 130 ++++++++++++++++++ - drivers/platform/cznic/turris-omnia-mcu.h | 24 ++++ - 5 files changed, 161 insertions(+) - create mode 100644 drivers/platform/cznic/turris-omnia-mcu-watchdog.c - ---- a/drivers/platform/cznic/Kconfig -+++ b/drivers/platform/cznic/Kconfig -@@ -19,6 +19,7 @@ config TURRIS_OMNIA_MCU - select GPIOLIB - select GPIOLIB_IRQCHIP - select RTC_CLASS -+ select WATCHDOG_CORE - help - Say Y here to add support for the features implemented by the - microcontroller on the CZ.NIC's Turris Omnia SOHO router. -@@ -26,6 +27,7 @@ config TURRIS_OMNIA_MCU - - board poweroff into true low power mode (with voltage regulators - disabled) and the ability to configure wake up from this mode (via - rtcwake) -+ - MCU watchdog - - GPIO pins - - to get front button press events (the front button can be - configured either to generate press events to the CPU or to change ---- a/drivers/platform/cznic/Makefile -+++ b/drivers/platform/cznic/Makefile -@@ -4,3 +4,4 @@ obj-$(CONFIG_TURRIS_OMNIA_MCU) += turris - turris-omnia-mcu-y := turris-omnia-mcu-base.o - turris-omnia-mcu-y += turris-omnia-mcu-gpio.o - turris-omnia-mcu-y += turris-omnia-mcu-sys-off-wakeup.o -+turris-omnia-mcu-y += turris-omnia-mcu-watchdog.o ---- a/drivers/platform/cznic/turris-omnia-mcu-base.c -+++ b/drivers/platform/cznic/turris-omnia-mcu-base.c -@@ -376,6 +376,10 @@ static int omnia_mcu_probe(struct i2c_cl - if (err) - return err; - -+ err = omnia_mcu_register_watchdog(mcu); -+ if (err) -+ return err; -+ - return omnia_mcu_register_gpiochip(mcu); - } - ---- /dev/null -+++ b/drivers/platform/cznic/turris-omnia-mcu-watchdog.c -@@ -0,0 +1,130 @@ -+// SPDX-License-Identifier: GPL-2.0 -+/* -+ * CZ.NIC's Turris Omnia MCU watchdog driver -+ * -+ * 2024 by Marek Behún -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include -+#include "turris-omnia-mcu.h" -+ -+#define WATCHDOG_TIMEOUT 120 -+ -+static unsigned int timeout; -+module_param(timeout, int, 0); -+MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds"); -+ -+static bool nowayout = WATCHDOG_NOWAYOUT; -+module_param(nowayout, bool, 0); -+MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" -+ __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); -+ -+static int omnia_wdt_start(struct watchdog_device *wdt) -+{ -+ struct omnia_mcu *mcu = watchdog_get_drvdata(wdt); -+ -+ return omnia_cmd_write_u8(mcu->client, OMNIA_CMD_SET_WATCHDOG_STATE, 1); -+} -+ -+static int omnia_wdt_stop(struct watchdog_device *wdt) -+{ -+ struct omnia_mcu *mcu = watchdog_get_drvdata(wdt); -+ -+ return omnia_cmd_write_u8(mcu->client, OMNIA_CMD_SET_WATCHDOG_STATE, 0); -+} -+ -+static int omnia_wdt_ping(struct watchdog_device *wdt) -+{ -+ struct omnia_mcu *mcu = watchdog_get_drvdata(wdt); -+ -+ return omnia_cmd_write_u8(mcu->client, OMNIA_CMD_SET_WATCHDOG_STATE, 1); -+} -+ -+static int omnia_wdt_set_timeout(struct watchdog_device *wdt, -+ unsigned int timeout) -+{ -+ struct omnia_mcu *mcu = watchdog_get_drvdata(wdt); -+ -+ return omnia_cmd_write_u16(mcu->client, OMNIA_CMD_SET_WDT_TIMEOUT, -+ timeout * DECI); -+} -+ -+static unsigned int omnia_wdt_get_timeleft(struct watchdog_device *wdt) -+{ -+ struct omnia_mcu *mcu = watchdog_get_drvdata(wdt); -+ u16 timeleft; -+ int err; -+ -+ err = omnia_cmd_read_u16(mcu->client, OMNIA_CMD_GET_WDT_TIMELEFT, -+ &timeleft); -+ if (err) { -+ dev_err(&mcu->client->dev, "Cannot get watchdog timeleft: %d\n", -+ err); -+ return 0; -+ } -+ -+ return timeleft / DECI; -+} -+ -+static const struct watchdog_info omnia_wdt_info = { -+ .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE, -+ .identity = "Turris Omnia MCU Watchdog", -+}; -+ -+static const struct watchdog_ops omnia_wdt_ops = { -+ .owner = THIS_MODULE, -+ .start = omnia_wdt_start, -+ .stop = omnia_wdt_stop, -+ .ping = omnia_wdt_ping, -+ .set_timeout = omnia_wdt_set_timeout, -+ .get_timeleft = omnia_wdt_get_timeleft, -+}; -+ -+int omnia_mcu_register_watchdog(struct omnia_mcu *mcu) -+{ -+ struct device *dev = &mcu->client->dev; -+ u8 state; -+ int err; -+ -+ if (!(mcu->features & OMNIA_FEAT_WDT_PING)) -+ return 0; -+ -+ mcu->wdt.info = &omnia_wdt_info; -+ mcu->wdt.ops = &omnia_wdt_ops; -+ mcu->wdt.parent = dev; -+ mcu->wdt.min_timeout = 1; -+ mcu->wdt.max_timeout = 65535 / DECI; -+ -+ mcu->wdt.timeout = WATCHDOG_TIMEOUT; -+ watchdog_init_timeout(&mcu->wdt, timeout, dev); -+ -+ watchdog_set_drvdata(&mcu->wdt, mcu); -+ -+ omnia_wdt_set_timeout(&mcu->wdt, mcu->wdt.timeout); -+ -+ err = omnia_cmd_read_u8(mcu->client, OMNIA_CMD_GET_WATCHDOG_STATE, -+ &state); -+ if (err) -+ return dev_err_probe(dev, err, -+ "Cannot get MCU watchdog state\n"); -+ -+ if (state) -+ set_bit(WDOG_HW_RUNNING, &mcu->wdt.status); -+ -+ watchdog_set_nowayout(&mcu->wdt, nowayout); -+ watchdog_stop_on_reboot(&mcu->wdt); -+ err = devm_watchdog_register_device(dev, &mcu->wdt); -+ if (err) -+ return dev_err_probe(dev, err, -+ "Cannot register MCU watchdog\n"); -+ -+ return 0; -+} ---- a/drivers/platform/cznic/turris-omnia-mcu.h -+++ b/drivers/platform/cznic/turris-omnia-mcu.h -@@ -13,6 +13,7 @@ - #include - #include - #include -+#include - #include - #include - #include -@@ -43,6 +44,9 @@ struct omnia_mcu { - struct rtc_device *rtcdev; - u32 rtc_alarm; - bool front_button_poweron; -+ -+ /* MCU watchdog */ -+ struct watchdog_device wdt; - }; - - int omnia_cmd_write_read(const struct i2c_client *client, -@@ -55,6 +59,25 @@ static inline int omnia_cmd_write(const - return omnia_cmd_write_read(client, cmd, len, NULL, 0); - } - -+static inline int omnia_cmd_write_u8(const struct i2c_client *client, u8 cmd, -+ u8 val) -+{ -+ u8 buf[2] = { cmd, val }; -+ -+ return omnia_cmd_write(client, buf, sizeof(buf)); -+} -+ -+static inline int omnia_cmd_write_u16(const struct i2c_client *client, u8 cmd, -+ u16 val) -+{ -+ u8 buf[3]; -+ -+ buf[0] = cmd; -+ put_unaligned_le16(val, &buf[1]); -+ -+ return omnia_cmd_write(client, buf, sizeof(buf)); -+} -+ - static inline int omnia_cmd_write_u32(const struct i2c_client *client, u8 cmd, - u32 val) - { -@@ -158,5 +181,6 @@ extern const struct attribute_group omni - - int omnia_mcu_register_gpiochip(struct omnia_mcu *mcu); - int omnia_mcu_register_sys_off_and_wakeup(struct omnia_mcu *mcu); -+int omnia_mcu_register_watchdog(struct omnia_mcu *mcu); - - #endif /* __TURRIS_OMNIA_MCU_H */ diff --git a/target/linux/mvebu/patches-6.12/820-v6.11-06-platform-cznic-turris-omnia-mcu-Add-support-for-MCU-.patch b/target/linux/mvebu/patches-6.12/820-v6.11-06-platform-cznic-turris-omnia-mcu-Add-support-for-MCU-.patch deleted file mode 100644 index 35387e34c7d..00000000000 --- a/target/linux/mvebu/patches-6.12/820-v6.11-06-platform-cznic-turris-omnia-mcu-Add-support-for-MCU-.patch +++ /dev/null @@ -1,225 +0,0 @@ -From b3ed8645c45567b598bef0868dca166f8ed166a0 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Marek=20Beh=C3=BAn?= -Date: Mon, 1 Jul 2024 13:30:08 +0200 -Subject: [PATCH 06/11] platform: cznic: turris-omnia-mcu: Add support for MCU - provided TRNG -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -Add support for true random number generator provided by the MCU. -New Omnia boards come without the Atmel SHA204-A chip. Instead the -crypto functionality is provided by new microcontroller, which has -a TRNG peripheral. - -Signed-off-by: Marek Behún -Acked-by: Bartosz Golaszewski -Link: https://lore.kernel.org/r/20240701113010.16447-7-kabel@kernel.org -Signed-off-by: Arnd Bergmann ---- - drivers/platform/cznic/Kconfig | 2 + - drivers/platform/cznic/Makefile | 1 + - .../platform/cznic/turris-omnia-mcu-base.c | 6 +- - .../platform/cznic/turris-omnia-mcu-gpio.c | 2 +- - .../platform/cznic/turris-omnia-mcu-trng.c | 105 ++++++++++++++++++ - drivers/platform/cznic/turris-omnia-mcu.h | 8 ++ - 6 files changed, 122 insertions(+), 2 deletions(-) - create mode 100644 drivers/platform/cznic/turris-omnia-mcu-trng.c - ---- a/drivers/platform/cznic/Kconfig -+++ b/drivers/platform/cznic/Kconfig -@@ -18,6 +18,7 @@ config TURRIS_OMNIA_MCU - depends on I2C - select GPIOLIB - select GPIOLIB_IRQCHIP -+ select HW_RANDOM - select RTC_CLASS - select WATCHDOG_CORE - help -@@ -27,6 +28,7 @@ config TURRIS_OMNIA_MCU - - board poweroff into true low power mode (with voltage regulators - disabled) and the ability to configure wake up from this mode (via - rtcwake) -+ - true random number generator (if available on the MCU) - - MCU watchdog - - GPIO pins - - to get front button press events (the front button can be ---- a/drivers/platform/cznic/Makefile -+++ b/drivers/platform/cznic/Makefile -@@ -4,4 +4,5 @@ obj-$(CONFIG_TURRIS_OMNIA_MCU) += turris - turris-omnia-mcu-y := turris-omnia-mcu-base.o - turris-omnia-mcu-y += turris-omnia-mcu-gpio.o - turris-omnia-mcu-y += turris-omnia-mcu-sys-off-wakeup.o -+turris-omnia-mcu-y += turris-omnia-mcu-trng.o - turris-omnia-mcu-y += turris-omnia-mcu-watchdog.o ---- a/drivers/platform/cznic/turris-omnia-mcu-base.c -+++ b/drivers/platform/cznic/turris-omnia-mcu-base.c -@@ -380,7 +380,11 @@ static int omnia_mcu_probe(struct i2c_cl - if (err) - return err; - -- return omnia_mcu_register_gpiochip(mcu); -+ err = omnia_mcu_register_gpiochip(mcu); -+ if (err) -+ return err; -+ -+ return omnia_mcu_register_trng(mcu); - } - - static const struct of_device_id of_omnia_mcu_match[] = { ---- a/drivers/platform/cznic/turris-omnia-mcu-gpio.c -+++ b/drivers/platform/cznic/turris-omnia-mcu-gpio.c -@@ -194,7 +194,7 @@ static const struct omnia_gpio omnia_gpi - }; - - /* mapping from interrupts to indexes of GPIOs in the omnia_gpios array */ --static const u8 omnia_int_to_gpio_idx[32] = { -+const u8 omnia_int_to_gpio_idx[32] = { - [__bf_shf(OMNIA_INT_CARD_DET)] = 4, - [__bf_shf(OMNIA_INT_MSATA_IND)] = 5, - [__bf_shf(OMNIA_INT_USB30_OVC)] = 6, ---- /dev/null -+++ b/drivers/platform/cznic/turris-omnia-mcu-trng.c -@@ -0,0 +1,105 @@ -+// SPDX-License-Identifier: GPL-2.0 -+/* -+ * CZ.NIC's Turris Omnia MCU TRNG driver -+ * -+ * 2024 by Marek Behún -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "../../gpio/gpiolib.h" -+ -+#include -+#include "turris-omnia-mcu.h" -+ -+#define OMNIA_CMD_TRNG_MAX_ENTROPY_LEN 64 -+ -+static irqreturn_t omnia_trng_irq_handler(int irq, void *dev_id) -+{ -+ struct omnia_mcu *mcu = dev_id; -+ -+ complete(&mcu->trng_entropy_ready); -+ -+ return IRQ_HANDLED; -+} -+ -+static int omnia_trng_read(struct hwrng *rng, void *data, size_t max, bool wait) -+{ -+ struct omnia_mcu *mcu = container_of(rng, struct omnia_mcu, trng); -+ u8 reply[1 + OMNIA_CMD_TRNG_MAX_ENTROPY_LEN]; -+ int err, bytes; -+ -+ if (!wait && !completion_done(&mcu->trng_entropy_ready)) -+ return 0; -+ -+ do { -+ if (wait_for_completion_interruptible(&mcu->trng_entropy_ready)) -+ return -ERESTARTSYS; -+ -+ err = omnia_cmd_read(mcu->client, -+ OMNIA_CMD_TRNG_COLLECT_ENTROPY, -+ reply, sizeof(reply)); -+ if (err) -+ return err; -+ -+ bytes = min3(reply[0], max, OMNIA_CMD_TRNG_MAX_ENTROPY_LEN); -+ } while (wait && !bytes); -+ -+ memcpy(data, &reply[1], bytes); -+ -+ return bytes; -+} -+ -+int omnia_mcu_register_trng(struct omnia_mcu *mcu) -+{ -+ struct device *dev = &mcu->client->dev; -+ u8 irq_idx, dummy; -+ int irq, err; -+ -+ if (!(mcu->features & OMNIA_FEAT_TRNG)) -+ return 0; -+ -+ irq_idx = omnia_int_to_gpio_idx[__bf_shf(OMNIA_INT_TRNG)]; -+ irq = gpiod_to_irq(gpiochip_get_desc(&mcu->gc, irq_idx)); -+ if (!irq) -+ return dev_err_probe(dev, -ENXIO, "Cannot get TRNG IRQ\n"); -+ -+ /* -+ * If someone else cleared the TRNG interrupt but did not read the -+ * entropy, a new interrupt won't be generated, and entropy collection -+ * will be stuck. Ensure an interrupt will be generated by executing -+ * the collect entropy command (and discarding the result). -+ */ -+ err = omnia_cmd_read(mcu->client, OMNIA_CMD_TRNG_COLLECT_ENTROPY, -+ &dummy, 1); -+ if (err) -+ return err; -+ -+ init_completion(&mcu->trng_entropy_ready); -+ -+ err = devm_request_threaded_irq(dev, irq, NULL, omnia_trng_irq_handler, -+ IRQF_ONESHOT, "turris-omnia-mcu-trng", -+ mcu); -+ if (err) -+ return dev_err_probe(dev, err, "Cannot request TRNG IRQ\n"); -+ -+ mcu->trng.name = "turris-omnia-mcu-trng"; -+ mcu->trng.read = omnia_trng_read; -+ -+ err = devm_hwrng_register(dev, &mcu->trng); -+ if (err) -+ return dev_err_probe(dev, err, "Cannot register TRNG\n"); -+ -+ return 0; -+} ---- a/drivers/platform/cznic/turris-omnia-mcu.h -+++ b/drivers/platform/cznic/turris-omnia-mcu.h -@@ -9,7 +9,9 @@ - #define __TURRIS_OMNIA_MCU_H - - #include -+#include - #include -+#include - #include - #include - #include -@@ -47,6 +49,10 @@ struct omnia_mcu { - - /* MCU watchdog */ - struct watchdog_device wdt; -+ -+ /* true random number generator */ -+ struct hwrng trng; -+ struct completion trng_entropy_ready; - }; - - int omnia_cmd_write_read(const struct i2c_client *client, -@@ -176,11 +182,13 @@ static inline int omnia_cmd_read_u8(cons - return omnia_cmd_read(client, cmd, reply, sizeof(*reply)); - } - -+extern const u8 omnia_int_to_gpio_idx[32]; - extern const struct attribute_group omnia_mcu_gpio_group; - extern const struct attribute_group omnia_mcu_poweroff_group; - - int omnia_mcu_register_gpiochip(struct omnia_mcu *mcu); - int omnia_mcu_register_sys_off_and_wakeup(struct omnia_mcu *mcu); -+int omnia_mcu_register_trng(struct omnia_mcu *mcu); - int omnia_mcu_register_watchdog(struct omnia_mcu *mcu); - - #endif /* __TURRIS_OMNIA_MCU_H */ diff --git a/target/linux/mvebu/patches-6.12/820-v6.11-07-ARM-dts-turris-omnia-Add-MCU-system-controller-node.patch b/target/linux/mvebu/patches-6.12/820-v6.11-07-ARM-dts-turris-omnia-Add-MCU-system-controller-node.patch deleted file mode 100644 index ad65de2f523..00000000000 --- a/target/linux/mvebu/patches-6.12/820-v6.11-07-ARM-dts-turris-omnia-Add-MCU-system-controller-node.patch +++ /dev/null @@ -1,69 +0,0 @@ -From 4f11095a4ae00b2fe4cebb21e36ee37cc62f5e1a Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Marek=20Beh=C3=BAn?= -Date: Mon, 1 Jul 2024 13:30:09 +0200 -Subject: [PATCH 07/11] ARM: dts: turris-omnia: Add MCU system-controller node -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -Turris Omnia's MCU provides various features that can be configured over -I2C at address 0x2a. Add device-tree node. - -This does not carry a Fixes tag - we do not want this to get backported -to stable kernels for the following reason: U-Boot since v2022.10 -inserts a phy-reset-gpio property into the WAN ethernet node pointing to -the MCU node if it finds the MCU node with a cznic,turris-omnia-mcu -compatible. Thus if this change got backported to a stable kernel, the -WAN interface driver would defer probe indefinitely (since it would wait -for the turris-omnia-mcu driver which would not be present). - -Signed-off-by: Marek Behún -Reviewed-by: Andrew Lunn -Reviewed-by: Andy Shevchenko -Reviewed-by: Conor Dooley -Acked-by: Bartosz Golaszewski -Acked-by: Alexandre Belloni -Link: https://lore.kernel.org/r/20240701113010.16447-8-kabel@kernel.org -Signed-off-by: Arnd Bergmann ---- - .../dts/marvell/armada-385-turris-omnia.dts | 22 ++++++++++++++++++- - 1 file changed, 21 insertions(+), 1 deletion(-) - ---- a/arch/arm/boot/dts/marvell/armada-385-turris-omnia.dts -+++ b/arch/arm/boot/dts/marvell/armada-385-turris-omnia.dts -@@ -218,7 +218,22 @@ - #size-cells = <0>; - reg = <0>; - -- /* STM32F0 command interface at address 0x2a */ -+ mcu: system-controller@2a { -+ compatible = "cznic,turris-omnia-mcu"; -+ reg = <0x2a>; -+ -+ pinctrl-names = "default"; -+ pinctrl-0 = <&mcu_pins>; -+ -+ interrupt-parent = <&gpio1>; -+ interrupts = <11 IRQ_TYPE_NONE>; -+ -+ gpio-controller; -+ #gpio-cells = <3>; -+ -+ interrupt-controller; -+ #interrupt-cells = <2>; -+ }; - - led-controller@2b { - compatible = "cznic,turris-omnia-leds"; -@@ -503,6 +518,11 @@ - }; - - &pinctrl { -+ mcu_pins: mcu-pins { -+ marvell,pins = "mpp43"; -+ marvell,function = "gpio"; -+ }; -+ - pcawan_pins: pcawan-pins { - marvell,pins = "mpp46"; - marvell,function = "gpio"; diff --git a/target/linux/mvebu/patches-6.12/820-v6.11-08-ARM-dts-turris-omnia-Add-GPIO-key-node-for-front-but.patch b/target/linux/mvebu/patches-6.12/820-v6.11-08-ARM-dts-turris-omnia-Add-GPIO-key-node-for-front-but.patch deleted file mode 100644 index 4b36167d668..00000000000 --- a/target/linux/mvebu/patches-6.12/820-v6.11-08-ARM-dts-turris-omnia-Add-GPIO-key-node-for-front-but.patch +++ /dev/null @@ -1,46 +0,0 @@ -From c3eeabe0b8d22d7c869278cc0cb35b83512fbed5 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Marek=20Beh=C3=BAn?= -Date: Mon, 1 Jul 2024 13:30:10 +0200 -Subject: [PATCH 08/11] ARM: dts: turris-omnia: Add GPIO key node for front - button -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -Now that we have the MCU device-tree node, which acts as a GPIO -controller, add GPIO key node for the front button. - -Signed-off-by: Marek Behún -Reviewed-by: Andrew Lunn -Reviewed-by: Andy Shevchenko -Reviewed-by: Conor Dooley -Acked-by: Bartosz Golaszewski -Acked-by: Alexandre Belloni -Link: https://lore.kernel.org/r/20240701113010.16447-9-kabel@kernel.org -Signed-off-by: Arnd Bergmann ---- - .../boot/dts/marvell/armada-385-turris-omnia.dts | 13 +++++++++++++ - 1 file changed, 13 insertions(+) - ---- a/arch/arm/boot/dts/marvell/armada-385-turris-omnia.dts -+++ b/arch/arm/boot/dts/marvell/armada-385-turris-omnia.dts -@@ -112,6 +112,19 @@ - status = "disabled"; - }; - -+ gpio-keys { -+ compatible = "gpio-keys"; -+ -+ front-button { -+ label = "Front Button"; -+ linux,code = ; -+ linux,can-disable; -+ gpios = <&mcu 0 12 GPIO_ACTIVE_HIGH>; -+ /* debouncing is done by the microcontroller */ -+ debounce-interval = <0>; -+ }; -+ }; -+ - sound { - compatible = "simple-audio-card"; - simple-audio-card,name = "SPDIF"; diff --git a/target/linux/mvebu/patches-6.12/820-v6.11-09-platform-cznic-turris-omnia-mcu-Depend-on-OF.patch b/target/linux/mvebu/patches-6.12/820-v6.11-09-platform-cznic-turris-omnia-mcu-Depend-on-OF.patch deleted file mode 100644 index 80c9e1a3cc2..00000000000 --- a/target/linux/mvebu/patches-6.12/820-v6.11-09-platform-cznic-turris-omnia-mcu-Depend-on-OF.patch +++ /dev/null @@ -1,32 +0,0 @@ -From 08838657bbc35494276c7ba4ef53f30a9816f8c9 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Marek=20Beh=C3=BAn?= -Date: Mon, 8 Jul 2024 13:40:01 +0200 -Subject: [PATCH 09/11] platform: cznic: turris-omnia-mcu: Depend on OF -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -Add depend on OF, otherwise the compilation fails with - error: no member named 'of_gpio_n_cells' in 'struct gpio_chip' - error: no member named 'of_xlate' in 'struct gpio_chip' - -Fixes: dfa556e45ae9 ("platform: cznic: turris-omnia-mcu: Add support for MCU connected GPIOs") -Reported-by: kernel test robot -Closes: https://lore.kernel.org/oe-kbuild-all/202407031646.trNSwajF-lkp@intel.com/ -Signed-off-by: Marek Behún -Link: https://lore.kernel.org/r/20240708114002.4285-2-kabel@kernel.org -Signed-off-by: Arnd Bergmann ---- - drivers/platform/cznic/Kconfig | 1 + - 1 file changed, 1 insertion(+) - ---- a/drivers/platform/cznic/Kconfig -+++ b/drivers/platform/cznic/Kconfig -@@ -16,6 +16,7 @@ config TURRIS_OMNIA_MCU - tristate "Turris Omnia MCU driver" - depends on MACH_ARMADA_38X || COMPILE_TEST - depends on I2C -+ depends on OF - select GPIOLIB - select GPIOLIB_IRQCHIP - select HW_RANDOM diff --git a/target/linux/mvebu/patches-6.12/820-v6.11-10-platform-cznic-turris-omnia-mcu-Depend-on-WATCHDOG.patch b/target/linux/mvebu/patches-6.12/820-v6.11-10-platform-cznic-turris-omnia-mcu-Depend-on-WATCHDOG.patch deleted file mode 100644 index 50e028752af..00000000000 --- a/target/linux/mvebu/patches-6.12/820-v6.11-10-platform-cznic-turris-omnia-mcu-Depend-on-WATCHDOG.patch +++ /dev/null @@ -1,32 +0,0 @@ -From 5e425e6eca155c162da58d4e58e896ed4109c7fd Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Marek=20Beh=C3=BAn?= -Date: Mon, 8 Jul 2024 13:40:02 +0200 -Subject: [PATCH 10/11] platform: cznic: turris-omnia-mcu: Depend on WATCHDOG -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -Add depend on WATCHDOG, otherwise modpost fails with - ERROR: modpost: "watchdog_init_timeout" [drivers/platform/cznic/turris-omnia-mcu.ko] undefined! - ERROR: modpost: "devm_watchdog_register_device" [drivers/platform/cznic/turris-omnia-mcu.ko] undefined! - -Fixes: ab89fb5fb92c ("platform: cznic: turris-omnia-mcu: Add support for MCU watchdog") -Reported-by: kernel test robot -Closes: https://lore.kernel.org/oe-kbuild-all/202407040711.g19y3cWq-lkp@intel.com/ -Signed-off-by: Marek Behún -Link: https://lore.kernel.org/r/20240708114002.4285-3-kabel@kernel.org -Signed-off-by: Arnd Bergmann ---- - drivers/platform/cznic/Kconfig | 1 + - 1 file changed, 1 insertion(+) - ---- a/drivers/platform/cznic/Kconfig -+++ b/drivers/platform/cznic/Kconfig -@@ -17,6 +17,7 @@ config TURRIS_OMNIA_MCU - depends on MACH_ARMADA_38X || COMPILE_TEST - depends on I2C - depends on OF -+ depends on WATCHDOG - select GPIOLIB - select GPIOLIB_IRQCHIP - select HW_RANDOM diff --git a/target/linux/mvebu/patches-6.12/820-v6.11-11-platform-cznic-turris-omnia-mcu-fix-Kconfig-dependen.patch b/target/linux/mvebu/patches-6.12/820-v6.11-11-platform-cznic-turris-omnia-mcu-fix-Kconfig-dependen.patch deleted file mode 100644 index 30a476586ee..00000000000 --- a/target/linux/mvebu/patches-6.12/820-v6.11-11-platform-cznic-turris-omnia-mcu-fix-Kconfig-dependen.patch +++ /dev/null @@ -1,45 +0,0 @@ -From 24c68c2525de5fcd0f3b16b2ad1028fb13b53393 Mon Sep 17 00:00:00 2001 -From: Arnd Bergmann -Date: Mon, 15 Jul 2024 08:02:30 +0200 -Subject: [PATCH 11/11] platform: cznic: turris-omnia-mcu: fix Kconfig - dependencies - -The newly added driver causes a Kconfig warning: - -WARNING: unmet direct dependencies detected for RTC_CLASS - Depends on [n]: !S390 [=y] - Selected by [m]: - - TURRIS_OMNIA_MCU [=m] && CZNIC_PLATFORMS [=y] && (MACH_ARMADA_38X || COMPILE_TEST [=y]) && I2C [=m] && OF [=y] && WATCHDOG [=y] - -The problem here is that it selects entire subsystems, which normal -device drivers should not do. Changes all of these to 'depends on' -instead. - -Fixes: dfa556e45ae9e ("platform: cznic: turris-omnia-mcu: Add support for MCU connected GPIOs") -Fixes: 90e700fd12b61 ("platform: cznic: turris-omnia-mcu: Add support for poweroff and wakeup") -Fixes: ab89fb5fb92c7 ("platform: cznic: turris-omnia-mcu: Add support for MCU watchdog") -Fixes: 41bb142a40289 ("platform: cznic: turris-omnia-mcu: Add support for MCU provided TRNG") -Reported-by: Nathan Chancellor -Signed-off-by: Arnd Bergmann ---- - drivers/platform/cznic/Kconfig | 8 ++++---- - 1 file changed, 4 insertions(+), 4 deletions(-) - ---- a/drivers/platform/cznic/Kconfig -+++ b/drivers/platform/cznic/Kconfig -@@ -18,11 +18,11 @@ config TURRIS_OMNIA_MCU - depends on I2C - depends on OF - depends on WATCHDOG -- select GPIOLIB -+ depends on GPIOLIB -+ depends on HW_RANDOM -+ depends on RTC_CLASS -+ depends on WATCHDOG_CORE - select GPIOLIB_IRQCHIP -- select HW_RANDOM -- select RTC_CLASS -- select WATCHDOG_CORE - help - Say Y here to add support for the features implemented by the - microcontroller on the CZ.NIC's Turris Omnia SOHO router. diff --git a/target/linux/mvebu/patches-6.12/830-01-i2c-add-init_recovery-callback.patch b/target/linux/mvebu/patches-6.12/830-01-i2c-add-init_recovery-callback.patch index de46f339dec..b82bb24a08e 100644 --- a/target/linux/mvebu/patches-6.12/830-01-i2c-add-init_recovery-callback.patch +++ b/target/linux/mvebu/patches-6.12/830-01-i2c-add-init_recovery-callback.patch @@ -169,7 +169,7 @@ Reviewed-by: Linus Walleij err_str = "no suitable method provided"; --- a/include/linux/i2c.h +++ b/include/linux/i2c.h -@@ -622,6 +622,9 @@ struct i2c_timings { +@@ -635,6 +635,9 @@ struct i2c_timings { * for generic GPIO recovery. * @get_bus_free: Returns the bus free state as seen from the IP core in case it * has a more complex internal logic than just reading SDA. Optional. @@ -179,7 +179,7 @@ Reviewed-by: Linus Walleij * @prepare_recovery: This will be called before starting recovery. Platform may * configure padmux here for SDA/SCL line or something else they want. * @unprepare_recovery: This will be called after completing recovery. Platform -@@ -646,6 +649,7 @@ struct i2c_bus_recovery_info { +@@ -659,6 +662,7 @@ struct i2c_bus_recovery_info { void (*set_sda)(struct i2c_adapter *adap, int val); int (*get_bus_free)(struct i2c_adapter *adap); diff --git a/target/linux/mvebu/patches-6.12/830-02-i2c-pxa-prevent-calling-of-the-generic-recovery-init-code.patch b/target/linux/mvebu/patches-6.12/830-02-i2c-pxa-prevent-calling-of-the-generic-recovery-init-code.patch index a05cedcefea..a67ac4cdfdd 100644 --- a/target/linux/mvebu/patches-6.12/830-02-i2c-pxa-prevent-calling-of-the-generic-recovery-init-code.patch +++ b/target/linux/mvebu/patches-6.12/830-02-i2c-pxa-prevent-calling-of-the-generic-recovery-init-code.patch @@ -201,7 +201,7 @@ Signed-off-by: Imre Kaloz --- a/drivers/i2c/busses/i2c-pxa.c +++ b/drivers/i2c/busses/i2c-pxa.c -@@ -1331,6 +1331,12 @@ static void i2c_pxa_unprepare_recovery(s +@@ -1330,6 +1330,12 @@ static void i2c_pxa_unprepare_recovery(s i2c_pxa_enable(i2c); } @@ -214,7 +214,7 @@ Signed-off-by: Imre Kaloz static int i2c_pxa_init_recovery(struct pxa_i2c *i2c) { struct i2c_bus_recovery_info *bri = &i2c->recovery; -@@ -1399,6 +1405,7 @@ static int i2c_pxa_init_recovery(struct +@@ -1398,6 +1404,7 @@ static int i2c_pxa_init_recovery(struct return 0; } diff --git a/target/linux/mvebu/patches-6.12/830-03-i2c-pxa-handle-Early-Bus-Busy-condition-on-Armada-3700.patch b/target/linux/mvebu/patches-6.12/830-03-i2c-pxa-handle-Early-Bus-Busy-condition-on-Armada-3700.patch index 52ccb8cf08c..77c3a2c24c8 100644 --- a/target/linux/mvebu/patches-6.12/830-03-i2c-pxa-handle-Early-Bus-Busy-condition-on-Armada-3700.patch +++ b/target/linux/mvebu/patches-6.12/830-03-i2c-pxa-handle-Early-Bus-Busy-condition-on-Armada-3700.patch @@ -226,7 +226,7 @@ the controller from the bad state described in the previous patch. --- a/drivers/i2c/busses/i2c-pxa.c +++ b/drivers/i2c/busses/i2c-pxa.c -@@ -70,6 +70,7 @@ +@@ -71,6 +71,7 @@ #define ISR_GCAD (1 << 8) /* general call address detected */ #define ISR_SAD (1 << 9) /* slave address detected */ #define ISR_BED (1 << 10) /* bus error no ACK/NAK */ @@ -234,7 +234,7 @@ the controller from the bad state described in the previous patch. #define ILCR_SLV_SHIFT 0 #define ILCR_SLV_MASK (0x1FF << ILCR_SLV_SHIFT) -@@ -262,6 +263,7 @@ struct pxa_i2c { +@@ -263,6 +264,7 @@ struct pxa_i2c { bool highmode_enter; u32 fm_mask; u32 hs_mask; @@ -242,7 +242,7 @@ the controller from the bad state described in the previous patch. struct i2c_bus_recovery_info recovery; struct pinctrl *pinctrl; -@@ -428,7 +430,7 @@ static int i2c_pxa_wait_bus_not_busy(str +@@ -429,7 +431,7 @@ static int i2c_pxa_wait_bus_not_busy(str while (1) { isr = readl(_ISR(i2c)); @@ -251,7 +251,7 @@ the controller from the bad state described in the previous patch. return 0; if (isr & ISR_SAD) -@@ -465,7 +467,7 @@ static int i2c_pxa_wait_master(struct px +@@ -466,7 +468,7 @@ static int i2c_pxa_wait_master(struct px * quick check of the i2c lines themselves to ensure they've * gone high... */ @@ -260,7 +260,7 @@ the controller from the bad state described in the previous patch. readl(_IBMR(i2c)) == (IBMR_SCLS | IBMR_SDAS)) { if (i2c_debug > 0) dev_dbg(&i2c->adap.dev, "%s: done\n", __func__); -@@ -486,7 +488,7 @@ static int i2c_pxa_set_master(struct pxa +@@ -487,7 +489,7 @@ static int i2c_pxa_set_master(struct pxa if (i2c_debug) dev_dbg(&i2c->adap.dev, "setting to bus master\n"); @@ -269,7 +269,7 @@ the controller from the bad state described in the previous patch. dev_dbg(&i2c->adap.dev, "%s: unit is busy\n", __func__); if (!i2c_pxa_wait_master(i2c)) { dev_dbg(&i2c->adap.dev, "%s: error: unit busy\n", __func__); -@@ -512,7 +514,7 @@ static int i2c_pxa_wait_slave(struct pxa +@@ -513,7 +515,7 @@ static int i2c_pxa_wait_slave(struct pxa dev_dbg(&i2c->adap.dev, "%s: %ld: ISR=%08x, ICR=%08x, IBMR=%02x\n", __func__, (long)jiffies, readl(_ISR(i2c)), readl(_ICR(i2c)), readl(_IBMR(i2c))); @@ -278,7 +278,7 @@ the controller from the bad state described in the previous patch. (readl(_ISR(i2c)) & ISR_SAD) != 0 || (readl(_ICR(i2c)) & ICR_SCLE) == 0) { if (i2c_debug > 1) -@@ -1170,7 +1172,7 @@ static int i2c_pxa_pio_set_master(struct +@@ -1171,7 +1173,7 @@ static int i2c_pxa_pio_set_master(struct /* * Wait for the bus to become free. */ @@ -287,7 +287,7 @@ the controller from the bad state described in the previous patch. udelay(1000); if (timeout < 0) { -@@ -1317,7 +1319,7 @@ static void i2c_pxa_unprepare_recovery(s +@@ -1316,7 +1318,7 @@ static void i2c_pxa_unprepare_recovery(s * handing control of the bus back to avoid the bus changing state. */ isr = readl(_ISR(i2c)); @@ -296,7 +296,7 @@ the controller from the bad state described in the previous patch. dev_dbg(&i2c->adap.dev, "recovery: resetting controller, ISR=0x%08x\n", isr); i2c_pxa_do_reset(i2c); -@@ -1481,6 +1483,10 @@ static int i2c_pxa_probe(struct platform +@@ -1480,6 +1482,10 @@ static int i2c_pxa_probe(struct platform i2c->fm_mask = pxa_reg_layout[i2c_type].fm; i2c->hs_mask = pxa_reg_layout[i2c_type].hs; diff --git a/target/linux/mvebu/patches-6.12/901-dt-bindings-Add-IEI-vendor-prefix-and-IEI-WT61P803-P.patch b/target/linux/mvebu/patches-6.12/901-dt-bindings-Add-IEI-vendor-prefix-and-IEI-WT61P803-P.patch index 2f7361c93ed..90ca8ee3c41 100644 --- a/target/linux/mvebu/patches-6.12/901-dt-bindings-Add-IEI-vendor-prefix-and-IEI-WT61P803-P.patch +++ b/target/linux/mvebu/patches-6.12/901-dt-bindings-Add-IEI-vendor-prefix-and-IEI-WT61P803-P.patch @@ -16,8 +16,7 @@ Cc: Robert Marko .../hwmon/iei,wt61p803-puzzle-hwmon.yaml | 53 ++++++++++++ .../leds/iei,wt61p803-puzzle-leds.yaml | 39 +++++++++ .../bindings/mfd/iei,wt61p803-puzzle.yaml | 82 +++++++++++++++++++ - .../devicetree/bindings/vendor-prefixes.yaml | 2 + - 4 files changed, 176 insertions(+) + 3 files changed, 174 insertions(+) create mode 100644 Documentation/devicetree/bindings/hwmon/iei,wt61p803-puzzle-hwmon.yaml create mode 100644 Documentation/devicetree/bindings/leds/iei,wt61p803-puzzle-leds.yaml create mode 100644 Documentation/devicetree/bindings/mfd/iei,wt61p803-puzzle.yaml @@ -205,14 +204,3 @@ Cc: Robert Marko + }; + }; + }; ---- a/Documentation/devicetree/bindings/vendor-prefixes.yaml -+++ b/Documentation/devicetree/bindings/vendor-prefixes.yaml -@@ -611,6 +611,8 @@ patternProperties: - description: IC Plus Corp. - "^idt,.*": - description: Integrated Device Technologies, Inc. -+ "^iei,.*": -+ description: IEI Integration Corp. - "^ifi,.*": - description: Ingenieurburo Fur Ic-Technologie (I/F/I) - "^ilitek,.*": diff --git a/target/linux/mvebu/patches-6.12/902-drivers-mfd-Add-a-driver-for-IEI-WT61P803-PUZZLE-MCU.patch b/target/linux/mvebu/patches-6.12/902-drivers-mfd-Add-a-driver-for-IEI-WT61P803-PUZZLE-MCU.patch index 2aac515eb19..6f99497c36d 100644 --- a/target/linux/mvebu/patches-6.12/902-drivers-mfd-Add-a-driver-for-IEI-WT61P803-PUZZLE-MCU.patch +++ b/target/linux/mvebu/patches-6.12/902-drivers-mfd-Add-a-driver-for-IEI-WT61P803-PUZZLE-MCU.patch @@ -20,13 +20,13 @@ Cc: Robert Marko drivers/mfd/Makefile | 1 + drivers/mfd/iei-wt61p803-puzzle.c | 908 ++++++++++++++++++++++++ include/linux/mfd/iei-wt61p803-puzzle.h | 66 ++ - 4 files changed, 984 insertions(+) + 4 files changed, 981 insertions(+) create mode 100644 drivers/mfd/iei-wt61p803-puzzle.c create mode 100644 include/linux/mfd/iei-wt61p803-puzzle.h --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig -@@ -2274,6 +2274,15 @@ config SGI_MFD_IOC3 +@@ -2343,6 +2343,15 @@ config SGI_MFD_IOC3 If you have an SGI Origin, Octane, or a PCI IOC3 card, then say Y. Otherwise say N. @@ -44,7 +44,7 @@ Cc: Robert Marko select MFD_CORE --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile -@@ -244,6 +244,7 @@ obj-$(CONFIG_MFD_RT4831) += rt4831.o +@@ -250,6 +250,7 @@ obj-$(CONFIG_MFD_RT4831) += rt4831.o obj-$(CONFIG_MFD_RT5033) += rt5033.o obj-$(CONFIG_MFD_RT5120) += rt5120.o obj-$(CONFIG_MFD_SKY81452) += sky81452.o @@ -54,7 +54,7 @@ Cc: Robert Marko obj-$(CONFIG_INTEL_SOC_PMIC_BXTWC) += intel_soc_pmic_bxtwc.o --- /dev/null +++ b/drivers/mfd/iei-wt61p803-puzzle.c -@@ -0,0 +1,912 @@ +@@ -0,0 +1,909 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* IEI WT61P803 PUZZLE MCU Driver + * System management microcontroller for fan control, temperature sensor reading, @@ -79,7 +79,7 @@ Cc: Robert Marko +#include +#include +#include -+#include ++#include + +/* start, payload and XOR checksum at end */ +#define IEI_WT61P803_PUZZLE_MAX_COMMAND_LENGTH (1 + 20 + 1) @@ -190,8 +190,8 @@ Cc: Robert Marko + return checksum; +} + -+static int iei_wt61p803_puzzle_process_resp(struct iei_wt61p803_puzzle *mcu, -+ const unsigned char *raw_resp_data, size_t size) ++static size_t iei_wt61p803_puzzle_process_resp(struct iei_wt61p803_puzzle *mcu, ++ const u8 *raw_resp_data, size_t size) +{ + unsigned char checksum; + @@ -227,11 +227,11 @@ Cc: Robert Marko + return size; +} + -+static int iei_wt61p803_puzzle_recv_buf(struct serdev_device *serdev, -+ const unsigned char *data, size_t size) ++static size_t iei_wt61p803_puzzle_recv_buf(struct serdev_device *serdev, ++ const u8 *data, size_t size) +{ + struct iei_wt61p803_puzzle *mcu = serdev_device_get_drvdata(serdev); -+ int ret; ++ size_t ret; + + ret = iei_wt61p803_puzzle_process_resp(mcu, data, size); + /* Return the number of processed bytes if function returns error, @@ -514,8 +514,7 @@ Cc: Robert Marko + goto err; + } + -+ sprintf(mcu->version.serial_number, "%.*s", -+ IEI_WT61P803_PUZZLE_SN_LENGTH, resp_buf + 4); ++ memcpy(mcu->version.serial_number, resp_buf + 4, IEI_WT61P803_PUZZLE_SN_LENGTH); +err: + mutex_unlock(&mcu->lock); + return ret; @@ -561,8 +560,7 @@ Cc: Robert Marko + } + } + -+ sprintf(mcu->version.serial_number, "%.*s", -+ IEI_WT61P803_PUZZLE_SN_LENGTH, serial_number); ++ memcpy(mcu->version.serial_number, serial_number, IEI_WT61P803_PUZZLE_SN_LENGTH); +err: + mutex_unlock(&mcu->lock); + return ret; @@ -594,8 +592,7 @@ Cc: Robert Marko + goto err; + } + -+ sprintf(mcu->version.mac_address[index], "%.*s", -+ IEI_WT61P803_PUZZLE_MAC_LENGTH, resp_buf + 4); ++ memcpy(mcu->version.mac_address[index], resp_buf + 4, IEI_WT61P803_PUZZLE_MAC_LENGTH); +err: + mutex_unlock(&mcu->lock); + return ret; @@ -644,8 +641,8 @@ Cc: Robert Marko + goto err; + } + -+ sprintf(mcu->version.mac_address[mac_address_idx], "%.*s", -+ IEI_WT61P803_PUZZLE_MAC_LENGTH, mac_address); ++ memcpy(mcu->version.mac_address[mac_address_idx], mac_address, ++ IEI_WT61P803_PUZZLE_MAC_LENGTH); +err: + mutex_unlock(&mcu->lock); + return ret; diff --git a/target/linux/mvebu/patches-6.12/903-drivers-hwmon-Add-the-IEI-WT61P803-PUZZLE-HWMON-driv.patch b/target/linux/mvebu/patches-6.12/903-drivers-hwmon-Add-the-IEI-WT61P803-PUZZLE-HWMON-driv.patch index eca2bb81e48..d83683363cd 100644 --- a/target/linux/mvebu/patches-6.12/903-drivers-hwmon-Add-the-IEI-WT61P803-PUZZLE-HWMON-driv.patch +++ b/target/linux/mvebu/patches-6.12/903-drivers-hwmon-Add-the-IEI-WT61P803-PUZZLE-HWMON-driv.patch @@ -26,7 +26,7 @@ Cc: Robert Marko --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig -@@ -785,6 +785,14 @@ config SENSORS_IBMPOWERNV +@@ -828,6 +828,14 @@ config SENSORS_IBMPOWERNV This driver can also be built as a module. If so, the module will be called ibmpowernv. @@ -43,7 +43,7 @@ Cc: Robert Marko depends on IIO --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile -@@ -90,6 +90,7 @@ obj-$(CONFIG_SENSORS_HS3001) += hs3001.o +@@ -94,6 +94,7 @@ obj-$(CONFIG_SENSORS_HS3001) += hs3001.o obj-$(CONFIG_SENSORS_ULTRA45) += ultra45_env.o obj-$(CONFIG_SENSORS_I5500) += i5500_temp.o obj-$(CONFIG_SENSORS_I5K_AMB) += i5k_amb.o diff --git a/target/linux/mvebu/patches-6.12/904-drivers-leds-Add-the-IEI-WT61P803-PUZZLE-LED-driver.patch b/target/linux/mvebu/patches-6.12/904-drivers-leds-Add-the-IEI-WT61P803-PUZZLE-LED-driver.patch index a9cb46bbd84..ae492919982 100644 --- a/target/linux/mvebu/patches-6.12/904-drivers-leds-Add-the-IEI-WT61P803-PUZZLE-LED-driver.patch +++ b/target/linux/mvebu/patches-6.12/904-drivers-leds-Add-the-IEI-WT61P803-PUZZLE-LED-driver.patch @@ -30,7 +30,7 @@ Cc: Robert Marko --- a/drivers/leds/Kconfig +++ b/drivers/leds/Kconfig -@@ -316,6 +316,14 @@ config LEDS_IPAQ_MICRO +@@ -354,6 +354,14 @@ config LEDS_IPAQ_MICRO Choose this option if you want to use the notification LED on Compaq/HP iPAQ h3100 and h3600. @@ -47,7 +47,7 @@ Cc: Robert Marko depends on LEDS_CLASS --- a/drivers/leds/Makefile +++ b/drivers/leds/Makefile -@@ -34,6 +34,7 @@ obj-$(CONFIG_LEDS_HP6XX) += leds-hp6xx. +@@ -35,6 +35,7 @@ obj-$(CONFIG_LEDS_HP6XX) += leds-hp6xx. obj-$(CONFIG_LEDS_INTEL_SS4200) += leds-ss4200.o obj-$(CONFIG_LEDS_IP30) += leds-ip30.o obj-$(CONFIG_LEDS_IPAQ_MICRO) += leds-ipaq-micro.o diff --git a/target/linux/mvebu/patches-6.12/906-Documentation-hwmon-Add-iei-wt61p803-puzzle-hwmon-dr.patch b/target/linux/mvebu/patches-6.12/906-Documentation-hwmon-Add-iei-wt61p803-puzzle-hwmon-dr.patch index 77e9fa298e7..63a7552357a 100644 --- a/target/linux/mvebu/patches-6.12/906-Documentation-hwmon-Add-iei-wt61p803-puzzle-hwmon-dr.patch +++ b/target/linux/mvebu/patches-6.12/906-Documentation-hwmon-Add-iei-wt61p803-puzzle-hwmon-dr.patch @@ -64,7 +64,7 @@ Cc: Robert Marko +================= == ===================================================== --- a/Documentation/hwmon/index.rst +++ b/Documentation/hwmon/index.rst -@@ -82,6 +82,7 @@ Hardware Monitoring Kernel Drivers +@@ -87,6 +87,7 @@ Hardware Monitoring Kernel Drivers ibmaem ibm-cffps ibmpowernv diff --git a/target/linux/mvebu/patches-6.12/907-MAINTAINERS-Add-an-entry-for-the-IEI-WT61P803-PUZZLE.patch b/target/linux/mvebu/patches-6.12/907-MAINTAINERS-Add-an-entry-for-the-IEI-WT61P803-PUZZLE.patch index d70bfe9a1af..5e343ef7c33 100644 --- a/target/linux/mvebu/patches-6.12/907-MAINTAINERS-Add-an-entry-for-the-IEI-WT61P803-PUZZLE.patch +++ b/target/linux/mvebu/patches-6.12/907-MAINTAINERS-Add-an-entry-for-the-IEI-WT61P803-PUZZLE.patch @@ -16,8 +16,8 @@ Cc: Robert Marko --- a/MAINTAINERS +++ b/MAINTAINERS -@@ -10143,6 +10143,22 @@ IFCVF VIRTIO DATA PATH ACCELERATOR - R: Zhu Lingshan +@@ -10965,6 +10965,22 @@ L: virtualization@lists.linux.dev + S: Supported F: drivers/vdpa/ifcvf/ +IEI WT61P803 M801 MFD DRIVER diff --git a/target/linux/mvebu/patches-6.12/910-drivers-leds-wt61p803-puzzle-improvements.patch b/target/linux/mvebu/patches-6.12/910-drivers-leds-wt61p803-puzzle-improvements.patch index 8de403773a0..c35ee4f8847 100644 --- a/target/linux/mvebu/patches-6.12/910-drivers-leds-wt61p803-puzzle-improvements.patch +++ b/target/linux/mvebu/patches-6.12/910-drivers-leds-wt61p803-puzzle-improvements.patch @@ -251,9 +251,9 @@ #define IEI_WT61P803_PUZZLE_CMD_TEMP_ALL 0x41 /* A */ --- a/drivers/mfd/iei-wt61p803-puzzle.c +++ b/drivers/mfd/iei-wt61p803-puzzle.c -@@ -176,6 +176,9 @@ static int iei_wt61p803_puzzle_recv_buf( +@@ -176,6 +176,9 @@ static size_t iei_wt61p803_puzzle_recv_b struct iei_wt61p803_puzzle *mcu = serdev_device_get_drvdata(serdev); - int ret; + size_t ret; + print_hex_dump_debug("puzzle-mcu rx: ", DUMP_PREFIX_NONE, + 16, 1, data, size, false);