[openwrt/openwrt] mvebu: copy 5.15 patches to 6.1
LEDE Commits
lede-commits at lists.infradead.org
Wed Aug 2 07:25:15 PDT 2023
lynxis pushed a commit to openwrt/openwrt.git, branch master:
https://git.openwrt.org/04f6d674f633398e56141097e9e7fa7c53bf461a
commit 04f6d674f633398e56141097e9e7fa7c53bf461a
Author: Stefan Kalscheuer <stefan at stklcode.de>
AuthorDate: Sun Jun 18 16:55:50 2023 +0200
mvebu: copy 5.15 patches to 6.1
To start migration to Kernel 6.1 we copy all existing patches from 5.15.
Signed-off-by: Stefan Kalscheuer <stefan at stklcode.de>
---
...ris-omnia-configure-LED-0-pin-function-to.patch | 38 +
.../patches-6.1/100-aardvark-workaround-PCIe.patch | 81 ++
...s-turris-omnia-enable-LED-controller-node.patch | 48 +
...-omnia-support-HW-controlled-mode-via-pri.patch | 118 +++
...-omnia-initialize-multi-intensity-to-full.patch | 33 +
...-omnia-change-max-brightness-from-255-to-.patch | 31 +
...er-reset-linkstation-poweroff-add-ls220de.patch | 15 +
...vebu-Mangle-bootloader-s-kernel-arguments.patch | 279 ++++++
.../301-mvebu-armada-38x-enable-libata-leds.patch | 10 +
.../mvebu/patches-6.1/302-add_powertables.patch | 770 +++++++++++++++
.../mvebu/patches-6.1/304-revert_i2c_delay.patch | 15 +
.../305-armada-385-rd-mtd-partitions.patch | 19 +
.../306-ARM-mvebu-385-ap-Add-partitions.patch | 35 +
.../307-armada-xp-linksys-mamba-broken-idle.patch | 10 +
.../308-armada-xp-linksys-mamba-wan.patch | 11 +
.../mvebu/patches-6.1/309-linksys-status-led.patch | 50 +
.../310-linksys-use-eth0-as-cpu-port.patch | 25 +
.../311-adjust-compatible-for-linksys.patch | 68 ++
...-armada388-clearfog-emmc-on-clearfog-base.patch | 87 ++
.../313-helios4-dts-status-led-alias.patch | 28 +
...315-armada-xp-linksys-mamba-resize-kernel.patch | 37 +
.../316-armada-370-dts-fix-crypto-engine.patch | 29 +
.../mvebu/patches-6.1/400-find_active_root.patch | 60 ++
.../700-mvneta-tx-queue-workaround.patch | 38 +
...next-ethernet-marvell-mvnetaMQPrioOffload.patch | 66 ++
...et-next-ethernet-marvell-mvnetaMQPrioFlag.patch | 30 +
...t-next-ethernet-marvell-mvnetaMQPrioQueue.patch | 97 ++
...xt-ethernet-marvell-mvnetaMQPrioTCOffload.patch | 182 ++++
...-phy-mvebu-a3700-comphy-Reset-COMPHY-regi.patch | 50 +
...bu-indicate-failure-to-enter-deeper-sleep.patch | 40 +
.../801-pci-mvebu-time-out-reset-on-link-up.patch | 60 ++
...-Add-IEI-vendor-prefix-and-IEI-WT61P803-P.patch | 218 +++++
...-Add-a-driver-for-IEI-WT61P803-PUZZLE-MCU.patch | 1034 ++++++++++++++++++++
...on-Add-the-IEI-WT61P803-PUZZLE-HWMON-driv.patch | 501 ++++++++++
...ds-Add-the-IEI-WT61P803-PUZZLE-LED-driver.patch | 207 ++++
...on-ABI-Add-iei-wt61p803-puzzle-driver-sys.patch | 82 ++
...on-hwmon-Add-iei-wt61p803-puzzle-hwmon-dr.patch | 74 ++
...-Add-an-entry-for-the-IEI-WT61P803-PUZZLE.patch | 41 +
...drivers-leds-wt61p803-puzzle-improvements.patch | 271 +++++
...11-drivers-leds-wt61p803-puzzle-mcu-retry.patch | 63 ++
40 files changed, 4951 insertions(+)
diff --git a/target/linux/mvebu/patches-6.1/100-ARM-dts-turris-omnia-configure-LED-0-pin-function-to.patch b/target/linux/mvebu/patches-6.1/100-ARM-dts-turris-omnia-configure-LED-0-pin-function-to.patch
new file mode 100644
index 0000000000..67beb06541
--- /dev/null
+++ b/target/linux/mvebu/patches-6.1/100-ARM-dts-turris-omnia-configure-LED-0-pin-function-to.patch
@@ -0,0 +1,38 @@
+From 81c0004a6433ff90fa6129418802c3c367e453c2 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Marek=20Beh=C3=BAn?= <kabel at kernel.org>
+Date: Mon, 4 Jul 2022 13:36:21 +0200
+Subject: [PATCH 1/5] ARM: dts: turris-omnia: configure LED[0] pin function to
+ link/activity
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+The marvell PHY driver changes the LED[0] pin function to "On - 1000
+Mbps Link, Off - Else".
+
+Turris Omnia expects that the function is "On - Link, Blink - Activity,
+Off - No link".
+
+Use the `marvell,reg-init` DT property to change the function.
+
+In the future, once netdev trigger will support HW offloading, we will
+be able to have this configured via the combination of PHY driver and
+leds-turris-omnia driver.
+
+Signed-off-by: Marek Behún <kabel at kernel.org>
+---
+ arch/arm/boot/dts/armada-385-turris-omnia.dts | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+--- a/arch/arm/boot/dts/armada-385-turris-omnia.dts
++++ b/arch/arm/boot/dts/armada-385-turris-omnia.dts
+@@ -396,7 +396,8 @@
+ phy1: ethernet-phy at 1 {
+ compatible = "ethernet-phy-ieee802.3-c22";
+ reg = <1>;
+- marvell,reg-init = <3 18 0 0x4985>;
++ marvell,reg-init = <3 18 0 0x4985>,
++ <3 16 0xfff0 0x0001>;
+
+ /* irq is connected to &pcawan pin 7 */
+ };
diff --git a/target/linux/mvebu/patches-6.1/100-aardvark-workaround-PCIe.patch b/target/linux/mvebu/patches-6.1/100-aardvark-workaround-PCIe.patch
new file mode 100644
index 0000000000..975eadb80e
--- /dev/null
+++ b/target/linux/mvebu/patches-6.1/100-aardvark-workaround-PCIe.patch
@@ -0,0 +1,81 @@
+Subject: [PATCH v2] PCI: aardvark: Implement workaround for PCIe Completion Timeout
+Date: Tue, 2 Aug 2022 14:38:16 +0200
+Message-Id: <20220802123816.21817-1-pali at kernel.org>
+X-Mailer: git-send-email 2.20.1
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+Precedence: bulk
+List-ID: <linux-pci.vger.kernel.org>
+X-Mailing-List: linux-pci at vger.kernel.org
+
+Marvell Armada 3700 Functional Errata, Guidelines, and Restrictions
+document describes in erratum 3.12 PCIe Completion Timeout (Ref #: 251),
+that PCIe IP does not support a strong-ordered model for inbound posted vs.
+outbound completion.
+
+As a workaround for this erratum, DIS_ORD_CHK flag in Debug Mux Control
+register must be set. It disables the ordering check in the core between
+Completions and Posted requests received from the link.
+
+Marvell also suggests to do full memory barrier at the beginning of
+aardvark summary interrupt handler before calling interrupt handlers of
+endpoint drivers in order to minimize the risk for the race condition
+documented in the Erratum between the DMA done status reading and the
+completion of writing to the host memory.
+
+More details about this issue and suggested workarounds are in discussion:
+https://lore.kernel.org/linux-pci/BN9PR18MB425154FE5019DCAF2028A1D5DB8D9@BN9PR18MB4251.namprd18.prod.outlook.com/t/#u
+
+It was reported that enabling this workaround fixes instability issues and
+"Unhandled fault" errors when using 60 GHz WiFi 802.11ad card with Qualcomm
+QCA6335 chip under significant load which were caused by interrupt status
+stuck in the outbound CMPLT queue traced back to this erratum.
+
+This workaround fixes also kernel panic triggered after some minutes of
+usage 5 GHz WiFi 802.11ax card with Mediatek MT7915 chip:
+
+ Internal error: synchronous external abort: 96000210 [#1] SMP
+ Kernel panic - not syncing: Fatal exception in interrupt
+
+Signed-off-by: Thomas Petazzoni <thomas.petazzoni at bootlin.com>
+Signed-off-by: Pali Rohár <pali at kernel.org>
+Fixes: 8c39d710363c ("PCI: aardvark: Add Aardvark PCI host controller driver")
+Cc: stable at vger.kernel.org
+---
+ drivers/pci/controller/pci-aardvark.c | 10 ++++++++++
+ 1 file changed, 10 insertions(+)
+
+--- a/drivers/pci/controller/pci-aardvark.c
++++ b/drivers/pci/controller/pci-aardvark.c
+@@ -210,6 +210,8 @@ enum {
+ };
+
+ #define VENDOR_ID_REG (LMI_BASE_ADDR + 0x44)
++#define DEBUG_MUX_CTRL_REG (LMI_BASE_ADDR + 0x208)
++#define DIS_ORD_CHK BIT(30)
+
+ /* PCIe core controller registers */
+ #define CTRL_CORE_BASE_ADDR 0x18000
+@@ -558,6 +560,11 @@ static void advk_pcie_setup_hw(struct ad
+ PCIE_CORE_CTRL2_TD_ENABLE;
+ advk_writel(pcie, reg, PCIE_CORE_CTRL2_REG);
+
++ /* Disable ordering checks, workaround for erratum 3.12 "PCIe completion timeout" */
++ reg = advk_readl(pcie, DEBUG_MUX_CTRL_REG);
++ reg |= DIS_ORD_CHK;
++ advk_writel(pcie, reg, DEBUG_MUX_CTRL_REG);
++
+ /* Set lane X1 */
+ reg = advk_readl(pcie, PCIE_CORE_CTRL0_REG);
+ reg &= ~LANE_CNT_MSK;
+@@ -1580,6 +1587,9 @@ static irqreturn_t advk_pcie_irq_handler
+ struct advk_pcie *pcie = arg;
+ u32 status;
+
++ /* Full memory barrier (ARM dsb sy), workaround for erratum 3.12 "PCIe completion timeout" */
++ mb();
++
+ status = advk_readl(pcie, HOST_CTRL_INT_STATUS_REG);
+ if (!(status & PCIE_IRQ_CORE_INT))
+ return IRQ_NONE;
diff --git a/target/linux/mvebu/patches-6.1/101-ARM-dts-turris-omnia-enable-LED-controller-node.patch b/target/linux/mvebu/patches-6.1/101-ARM-dts-turris-omnia-enable-LED-controller-node.patch
new file mode 100644
index 0000000000..ecfb43553f
--- /dev/null
+++ b/target/linux/mvebu/patches-6.1/101-ARM-dts-turris-omnia-enable-LED-controller-node.patch
@@ -0,0 +1,48 @@
+From fed7cef5e4f2df8c6a79bebf5da1fdd3783ff6f3 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Marek=20Beh=C3=BAn?= <kabel at kernel.org>
+Date: Mon, 4 Jul 2022 13:36:22 +0200
+Subject: [PATCH] ARM: dts: turris-omnia: enable LED controller node
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+The LED controller node is disabled because the leds-turris-omnia driver
+does not support setting the LED blinking to be controlled by the MCU.
+
+The patches for that have now been sent [1], so let's enable the node.
+
+[1] https://lore.kernel.org/linux-leds/20220704105955.15474-1-kabel@kernel.org/T/
+
+Signed-off-by: Marek Behún <kabel at kernel.org>
+---
+ arch/arm/boot/dts/armada-385-turris-omnia.dts | 7 ++-----
+ 1 file changed, 2 insertions(+), 5 deletions(-)
+
+--- a/arch/arm/boot/dts/armada-385-turris-omnia.dts
++++ b/arch/arm/boot/dts/armada-385-turris-omnia.dts
+@@ -194,15 +194,13 @@
+ reg = <0x2b>;
+ #address-cells = <1>;
+ #size-cells = <0>;
++ status = "okay";
+
+ /*
+ * LEDs are controlled by MCU (STM32F0) at
+ * address 0x2b.
+ *
+- * The driver does not support HW control mode
+- * for the LEDs yet. Disable the LEDs for now.
+- *
+- * Also LED functions are not stable yet:
++ * LED functions are not stable yet:
+ * - there are 3 LEDs connected via MCU to PCIe
+ * ports. One of these ports supports mSATA.
+ * There is no mSATA nor PCIe function.
+@@ -213,7 +211,6 @@
+ * B. Again there is no such function defined.
+ * For now we use LED_FUNCTION_INDICATOR
+ */
+- status = "disabled";
+
+ multi-led at 0 {
+ reg = <0x0>;
diff --git a/target/linux/mvebu/patches-6.1/102-leds-turris-omnia-support-HW-controlled-mode-via-pri.patch b/target/linux/mvebu/patches-6.1/102-leds-turris-omnia-support-HW-controlled-mode-via-pri.patch
new file mode 100644
index 0000000000..c14469460a
--- /dev/null
+++ b/target/linux/mvebu/patches-6.1/102-leds-turris-omnia-support-HW-controlled-mode-via-pri.patch
@@ -0,0 +1,118 @@
+From 80e643510cb14f116f687e992210c0008a09d869 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Marek=20Beh=C3=BAn?= <kabel at kernel.org>
+Date: Mon, 4 Jul 2022 12:59:53 +0200
+Subject: [PATCH] leds: turris-omnia: support HW controlled mode via
+ private trigger
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Add support for enabling MCU controlled mode of the Turris Omnia LEDs
+via a LED private trigger called "omnia-mcu".
+
+When in MCU controlled mode, the user can still set LED color, but the
+blinking is done by MCU, which does different things for various LEDs:
+- WAN LED is blinked according to the LED[0] pin of the WAN PHY
+- LAN LEDs are blinked according to the LED[0] output of corresponding
+ port of the LAN switch
+- PCIe LEDs are blinked according to the logical OR of the MiniPCIe port
+ LED pins
+
+For a long time I wanted to actually do this differently: I wanted to
+make the netdev trigger to transparently offload the blinking to the HW
+if user set compatible settings for the netdev trigger.
+There was some work on this, and hopefully we will be able to complete
+it sometime, but since there are various complications, it will probably
+not be soon.
+
+In the meantime let's support HW controlled mode via this private LED
+trigger. If, in the future, we manage to complete the netdev trigger
+offloading, we can still keep this private trigger for backwards
+compatiblity, if needed.
+
+We also set "omnia-mcu" to cdev->default_trigger, so that the MCU keeps
+control until the user first wants to take over it. If a different
+default trigger is specified in device-tree via the
+`linux,default-trigger` property, LED class will overwrite
+cdev->default_trigger, and so the DT property will be respected.
+
+Signed-off-by: Marek Behún <kabel at kernel.org>
+---
+ drivers/leds/Kconfig | 1 +
+ drivers/leds/leds-turris-omnia.c | 41 ++++++++++++++++++++++++++++++++
+ 2 files changed, 42 insertions(+)
+
+--- a/drivers/leds/Kconfig
++++ b/drivers/leds/Kconfig
+@@ -163,6 +163,7 @@ config LEDS_TURRIS_OMNIA
+ depends on I2C
+ depends on MACH_ARMADA_38X || COMPILE_TEST
+ depends on OF
++ select LEDS_TRIGGERS
+ help
+ This option enables basic support for the LEDs found on the front
+ side of CZ.NIC's Turris Omnia router. There are 12 RGB LEDs on the
+--- a/drivers/leds/leds-turris-omnia.c
++++ b/drivers/leds/leds-turris-omnia.c
+@@ -41,6 +41,39 @@ struct omnia_leds {
+ struct omnia_led leds[];
+ };
+
++static struct led_hw_trigger_type omnia_hw_trigger_type;
++
++static int omnia_hwtrig_activate(struct led_classdev *cdev)
++{
++ struct omnia_leds *leds = dev_get_drvdata(cdev->dev->parent);
++ struct omnia_led *led = to_omnia_led(lcdev_to_mccdev(cdev));
++
++ /* put the LED into MCU controlled mode */
++ return i2c_smbus_write_byte_data(leds->client, CMD_LED_MODE,
++ CMD_LED_MODE_LED(led->reg));
++}
++
++static void omnia_hwtrig_deactivate(struct led_classdev *cdev)
++{
++ struct omnia_leds *leds = dev_get_drvdata(cdev->dev->parent);
++ struct omnia_led *led = to_omnia_led(lcdev_to_mccdev(cdev));
++ int ret;
++
++ /* put the LED into software mode */
++ ret = i2c_smbus_write_byte_data(leds->client, CMD_LED_MODE,
++ CMD_LED_MODE_LED(led->reg) |
++ CMD_LED_MODE_USER);
++ if (ret < 0)
++ dev_err(cdev->dev, "Cannot put to software mode: %i\n", ret);
++}
++
++static struct led_trigger omnia_hw_trigger = {
++ .name = "omnia-mcu",
++ .activate = omnia_hwtrig_activate,
++ .deactivate = omnia_hwtrig_deactivate,
++ .trigger_type = &omnia_hw_trigger_type,
++};
++
+ static int omnia_led_brightness_set_blocking(struct led_classdev *cdev,
+ enum led_brightness brightness)
+ {
+@@ -112,6 +145,8 @@ static int omnia_led_register(struct i2c
+ cdev = &led->mc_cdev.led_cdev;
+ cdev->max_brightness = 255;
+ cdev->brightness_set_blocking = omnia_led_brightness_set_blocking;
++ cdev->trigger_type = &omnia_hw_trigger_type;
++ cdev->default_trigger = omnia_hw_trigger.name;
+
+ /* put the LED into software mode */
+ ret = i2c_smbus_write_byte_data(client, CMD_LED_MODE,
+@@ -228,6 +263,12 @@ static int omnia_leds_probe(struct i2c_c
+
+ mutex_init(&leds->lock);
+
++ ret = devm_led_trigger_register(dev, &omnia_hw_trigger);
++ if (ret < 0) {
++ dev_err(dev, "Cannot register private LED trigger: %d\n", ret);
++ return ret;
++ }
++
+ led = &leds->leds[0];
+ for_each_available_child_of_node(np, child) {
+ ret = omnia_led_register(client, led, child);
diff --git a/target/linux/mvebu/patches-6.1/103-leds-turris-omnia-initialize-multi-intensity-to-full.patch b/target/linux/mvebu/patches-6.1/103-leds-turris-omnia-initialize-multi-intensity-to-full.patch
new file mode 100644
index 0000000000..1fe76b8f2c
--- /dev/null
+++ b/target/linux/mvebu/patches-6.1/103-leds-turris-omnia-initialize-multi-intensity-to-full.patch
@@ -0,0 +1,33 @@
+From bda176cceb735b9b46c1900658b6486c34e13ae6 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Marek=20Beh=C3=BAn?= <kabel at kernel.org>
+Date: Mon, 4 Jul 2022 12:59:54 +0200
+Subject: [PATCH] leds: turris-omnia: initialize multi-intensity to full
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+The default color of each LED before driver probe (255, 255, 255).
+Initialize multi_intensity to this value, so that it corresponds to the
+reality.
+
+Signed-off-by: Marek Behún <kabel at kernel.org>
+---
+ drivers/leds/leds-turris-omnia.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+--- a/drivers/leds/leds-turris-omnia.c
++++ b/drivers/leds/leds-turris-omnia.c
+@@ -131,10 +131,13 @@ static int omnia_led_register(struct i2c
+ }
+
+ led->subled_info[0].color_index = LED_COLOR_ID_RED;
++ led->subled_info[0].intensity = 255;
+ led->subled_info[0].channel = 0;
+ led->subled_info[1].color_index = LED_COLOR_ID_GREEN;
++ led->subled_info[1].intensity = 255;
+ led->subled_info[1].channel = 1;
+ led->subled_info[2].color_index = LED_COLOR_ID_BLUE;
++ led->subled_info[2].intensity = 255;
+ led->subled_info[2].channel = 2;
+
+ led->mc_cdev.subled_info = led->subled_info;
diff --git a/target/linux/mvebu/patches-6.1/104-leds-turris-omnia-change-max-brightness-from-255-to-.patch b/target/linux/mvebu/patches-6.1/104-leds-turris-omnia-change-max-brightness-from-255-to-.patch
new file mode 100644
index 0000000000..fb8864dfd1
--- /dev/null
+++ b/target/linux/mvebu/patches-6.1/104-leds-turris-omnia-change-max-brightness-from-255-to-.patch
@@ -0,0 +1,31 @@
+From 349cbe949b9622cc05b148ecfa6268cbbae35b45 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Marek=20Beh=C3=BAn?= <kabel at kernel.org>
+Date: Mon, 4 Jul 2022 12:59:55 +0200
+Subject: [PATCH] leds: turris-omnia: change max brightness from 255 to 1
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Using binary brightness makes more sense for this controller, because
+internally in the MCU it works that way: the LED has a color, and a
+state whether it is ON or OFF.
+
+The resulting brightness computation with led_mc_calc_color_components()
+will now always result in either (0, 0, 0) or the multi_intensity value.
+
+Signed-off-by: Marek Behún <kabel at kernel.org>
+---
+ drivers/leds/leds-turris-omnia.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/leds/leds-turris-omnia.c
++++ b/drivers/leds/leds-turris-omnia.c
+@@ -146,7 +146,7 @@ static int omnia_led_register(struct i2c
+ init_data.fwnode = &np->fwnode;
+
+ cdev = &led->mc_cdev.led_cdev;
+- cdev->max_brightness = 255;
++ cdev->max_brightness = 1;
+ cdev->brightness_set_blocking = omnia_led_brightness_set_blocking;
+ cdev->trigger_type = &omnia_hw_trigger_type;
+ cdev->default_trigger = omnia_hw_trigger.name;
diff --git a/target/linux/mvebu/patches-6.1/105-power-reset-linkstation-poweroff-add-ls220de.patch b/target/linux/mvebu/patches-6.1/105-power-reset-linkstation-poweroff-add-ls220de.patch
new file mode 100644
index 0000000000..3223861234
--- /dev/null
+++ b/target/linux/mvebu/patches-6.1/105-power-reset-linkstation-poweroff-add-ls220de.patch
@@ -0,0 +1,15 @@
+--- a/drivers/power/reset/linkstation-poweroff.c
++++ b/drivers/power/reset/linkstation-poweroff.c
+@@ -142,6 +142,12 @@ static void linkstation_poweroff(void)
+ }
+
+ static const struct of_device_id ls_poweroff_of_match[] = {
++ { .compatible = "buffalo,ls220d",
++ .data = &linkstation_power_off_cfg,
++ },
++ { .compatible = "buffalo,ls220de",
++ .data = &linkstation_power_off_cfg,
++ },
+ { .compatible = "buffalo,ls421d",
+ .data = &linkstation_power_off_cfg,
+ },
diff --git a/target/linux/mvebu/patches-6.1/300-mvebu-Mangle-bootloader-s-kernel-arguments.patch b/target/linux/mvebu/patches-6.1/300-mvebu-Mangle-bootloader-s-kernel-arguments.patch
new file mode 100644
index 0000000000..9069de1614
--- /dev/null
+++ b/target/linux/mvebu/patches-6.1/300-mvebu-Mangle-bootloader-s-kernel-arguments.patch
@@ -0,0 +1,279 @@
+From 71270226b14733a4b1f2cde58ea9265caa50b38d Mon Sep 17 00:00:00 2001
+From: Adrian Panella <ianchi74 at outlook.com>
+Date: Thu, 9 Mar 2017 09:37:17 +0100
+Subject: [PATCH 67/69] generic: Mangle bootloader's kernel arguments
+
+The command-line arguments provided by the boot loader will be
+appended to a new device tree property: bootloader-args.
+If there is a property "append-rootblock" in DT under /chosen
+and a root= option in bootloaders command line it will be parsed
+and added to DT bootargs with the form: <append-rootblock>XX.
+Only command line ATAG will be processed, the rest of the ATAGs
+sent by bootloader will be ignored.
+This is usefull in dual boot systems, to get the current root partition
+without afecting the rest of the system.
+
+Signed-off-by: Adrian Panella <ianchi74 at outlook.com>
+
+This patch has been modified to be mvebu specific. The original patch
+did not pass the bootloader cmdline on if no append-rootblock stanza
+was found, resulting in blank cmdline and failure to boot.
+
+Signed-off-by: Michael Gray <michael.gray at lantisproject.com>
+---
+ arch/arm/Kconfig | 11 ++++
+ arch/arm/boot/compressed/atags_to_fdt.c | 85 ++++++++++++++++++++++++-
+ init/main.c | 16 +++++
+ 3 files changed, 111 insertions(+), 1 deletion(-)
+
+--- a/arch/arm/Kconfig
++++ b/arch/arm/Kconfig
+@@ -1727,6 +1727,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.
+
++config ARM_ATAG_DTB_COMPAT_CMDLINE_MANGLE
++ bool "Append rootblock parsing bootloader's kernel arguments"
++ help
++ The command-line arguments provided by the boot loader will be
++ appended to a new device tree property: bootloader-args.
++ If there is a property "append-rootblock" in DT under /chosen
++ and a root= option in bootloaders command line it will be parsed
++ and added to DT bootargs with the form: <append-rootblock>XX.
++ Only command line ATAG will be processed, the rest of the ATAGs
++ sent by bootloader will be ignored.
++
+ endchoice
+
+ config CMDLINE
+--- a/arch/arm/boot/compressed/atags_to_fdt.c
++++ b/arch/arm/boot/compressed/atags_to_fdt.c
+@@ -5,6 +5,8 @@
+
+ #if defined(CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_EXTEND)
+ #define do_extend_cmdline 1
++#elif defined(CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_MANGLE)
++#define do_extend_cmdline 1
+ #else
+ #define do_extend_cmdline 0
+ #endif
+@@ -20,6 +22,7 @@ static int node_offset(void *fdt, const
+ return offset;
+ }
+
++#ifndef CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_MANGLE
+ static int setprop(void *fdt, const char *node_path, const char *property,
+ void *val_array, int size)
+ {
+@@ -28,6 +31,7 @@ static int setprop(void *fdt, const char
+ return offset;
+ return fdt_setprop(fdt, offset, property, val_array, size);
+ }
++#endif
+
+ static int setprop_string(void *fdt, const char *node_path,
+ const char *property, const char *string)
+@@ -38,6 +42,7 @@ static int setprop_string(void *fdt, con
+ return fdt_setprop_string(fdt, offset, property, string);
+ }
+
++#ifndef CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_MANGLE
+ static int setprop_cell(void *fdt, const char *node_path,
+ const char *property, uint32_t val)
+ {
+@@ -46,6 +51,7 @@ static int setprop_cell(void *fdt, const
+ return offset;
+ return fdt_setprop_cell(fdt, offset, property, val);
+ }
++#endif
+
+ static const void *getprop(const void *fdt, const char *node_path,
+ const char *property, int *len)
+@@ -58,6 +64,7 @@ static const void *getprop(const void *f
+ return fdt_getprop(fdt, offset, property, len);
+ }
+
++#ifndef CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_MANGLE
+ static uint32_t get_cell_size(const void *fdt)
+ {
+ int len;
+@@ -69,6 +76,74 @@ static uint32_t get_cell_size(const void
+ return cell_size;
+ }
+
++#endif
++
++#if defined(CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_MANGLE)
++
++static char *append_rootblock(char *dest, const char *str, int len, void *fdt)
++{
++ const char *ptr, *end;
++ const char *root="root=";
++ int i, l;
++ const char *rootblock;
++
++ //ARM doesn't have __HAVE_ARCH_STRSTR, so search manually
++ ptr = str - 1;
++
++ do {
++ //first find an 'r' at the begining or after a space
++ do {
++ ptr++;
++ ptr = strchr(ptr, 'r');
++ if (!ptr)
++ goto no_append;
++
++ } while (ptr != str && *(ptr-1) != ' ');
++
++ //then check for the rest
++ for(i = 1; i <= 4; i++)
++ if(*(ptr+i) != *(root+i)) break;
++
++ } while (i != 5);
++
++ end = strchr(ptr, ' ');
++ end = end ? (end - 1) : (strchr(ptr, 0) - 1);
++
++ //find partition number (assumes format root=/dev/mtdXX | /dev/mtdblockXX | yy:XX )
++ for( i = 0; end >= ptr && *end >= '0' && *end <= '9'; end--, i++);
++ ptr = end + 1;
++
++ /* if append-rootblock property is set use it to append to command line */
++ rootblock = getprop(fdt, "/chosen", "append-rootblock", &l);
++ if (rootblock == NULL)
++ goto no_append;
++
++ if (*dest != ' ') {
++ *dest = ' ';
++ dest++;
++ len++;
++ }
++
++ if (len + l + i <= COMMAND_LINE_SIZE) {
++ memcpy(dest, rootblock, l);
++ dest += l - 1;
++ memcpy(dest, ptr, i);
++ dest += i;
++ }
++
++ return dest;
++
++no_append:
++ len = strlen(str);
++ if (len + 1 < COMMAND_LINE_SIZE) {
++ memcpy(dest, str, len);
++ dest += len;
++ }
++
++ return dest;
++}
++#endif
++
+ static void merge_fdt_bootargs(void *fdt, const char *fdt_cmdline)
+ {
+ char cmdline[COMMAND_LINE_SIZE];
+@@ -88,18 +163,28 @@ static void merge_fdt_bootargs(void *fdt
+
+ /* and append the ATAG_CMDLINE */
+ if (fdt_cmdline) {
++
++#if defined(CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_MANGLE)
++ //save original bootloader args
++ //and append ubi.mtd with root partition number to current cmdline
++ setprop_string(fdt, "/chosen", "bootloader-args", fdt_cmdline);
++ ptr = append_rootblock(ptr, fdt_cmdline, len, fdt);
++
++#else
+ len = strlen(fdt_cmdline);
+ if (ptr - cmdline + len + 2 < COMMAND_LINE_SIZE) {
+ *ptr++ = ' ';
+ memcpy(ptr, fdt_cmdline, len);
+ ptr += len;
+ }
++#endif
+ }
+ *ptr = '\0';
+
+ setprop_string(fdt, "/chosen", "bootargs", cmdline);
+ }
+
++#ifndef CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_MANGLE
+ static void hex_str(char *out, uint32_t value)
+ {
+ uint32_t digit;
+@@ -117,6 +202,7 @@ static void hex_str(char *out, uint32_t
+ }
+ *out = '\0';
+ }
++#endif
+
+ /*
+ * Convert and fold provided ATAGs into the provided FDT.
+@@ -131,9 +217,11 @@ int atags_to_fdt(void *atag_list, void *
+ struct tag *atag = atag_list;
+ /* In the case of 64 bits memory size, need to reserve 2 cells for
+ * address and size for each bank */
++#ifndef CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_MANGLE
+ __be32 mem_reg_property[2 * 2 * NR_BANKS];
+- int memcount = 0;
+- int ret, memsize;
++ int memsize, memcount = 0;
++#endif
++ int ret;
+
+ /* make sure we've got an aligned pointer */
+ if ((u32)atag_list & 0x3)
+@@ -168,7 +256,9 @@ int atags_to_fdt(void *atag_list, void *
+ else
+ setprop_string(fdt, "/chosen", "bootargs",
+ atag->u.cmdline.cmdline);
+- } else if (atag->hdr.tag == ATAG_MEM) {
++ }
++#ifndef CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_MANGLE
++ else if (atag->hdr.tag == ATAG_MEM) {
+ if (memcount >= sizeof(mem_reg_property)/4)
+ continue;
+ if (!atag->u.mem.size)
+@@ -212,6 +302,10 @@ int atags_to_fdt(void *atag_list, void *
+ setprop(fdt, "/memory", "reg", mem_reg_property,
+ 4 * memcount * memsize);
+ }
++#else
++
++ }
++#endif
+
+ return fdt_pack(fdt);
+ }
+--- a/init/main.c
++++ b/init/main.c
+@@ -114,6 +114,10 @@
+
+ #include <kunit/test.h>
+
++#if defined(CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_MANGLE)
++#include <linux/of.h>
++#endif
++
+ static int kernel_init(void *);
+
+ extern void init_IRQ(void);
+@@ -991,6 +995,18 @@ asmlinkage __visible void __init __no_sa
+ page_alloc_init();
+
+ pr_notice("Kernel command line: %s\n", saved_command_line);
++
++#if defined(CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_MANGLE)
++ //Show bootloader's original command line for reference
++ if(of_chosen) {
++ const char *prop = of_get_property(of_chosen, "bootloader-args", NULL);
++ if(prop)
++ pr_notice("Bootloader command line (ignored): %s\n", prop);
++ else
++ pr_notice("Bootloader command line not present\n");
++ }
++#endif
++
+ /* parameters may set static keys */
+ jump_label_init();
+ parse_early_param();
diff --git a/target/linux/mvebu/patches-6.1/301-mvebu-armada-38x-enable-libata-leds.patch b/target/linux/mvebu/patches-6.1/301-mvebu-armada-38x-enable-libata-leds.patch
new file mode 100644
index 0000000000..615caac24f
--- /dev/null
+++ b/target/linux/mvebu/patches-6.1/301-mvebu-armada-38x-enable-libata-leds.patch
@@ -0,0 +1,10 @@
+--- a/arch/arm/mach-mvebu/Kconfig
++++ b/arch/arm/mach-mvebu/Kconfig
+@@ -67,6 +67,7 @@ config MACH_ARMADA_38X
+ select HAVE_ARM_TWD if SMP
+ select MACH_MVEBU_V7
+ select PINCTRL_ARMADA_38X
++ select ARCH_WANT_LIBATA_LEDS
+ help
+ Say 'Y' here if you want your kernel to support boards based
+ on the Marvell Armada 380/385 SoC with device tree.
diff --git a/target/linux/mvebu/patches-6.1/302-add_powertables.patch b/target/linux/mvebu/patches-6.1/302-add_powertables.patch
new file mode 100644
index 0000000000..93ad5de2cc
--- /dev/null
+++ b/target/linux/mvebu/patches-6.1/302-add_powertables.patch
@@ -0,0 +1,770 @@
+--- a/arch/arm/boot/dts/armada-385-linksys.dtsi
++++ b/arch/arm/boot/dts/armada-385-linksys.dtsi
+@@ -214,11 +214,19 @@
+ &pcie1 {
+ /* Marvell 88W8864, 5GHz-only */
+ status = "okay";
++
++ mwlwifi {
++ marvell,2ghz = <0>;
++ };
+ };
+
+ &pcie2 {
+ /* Marvell 88W8864, 2GHz-only */
+ status = "okay";
++
++ mwlwifi {
++ marvell,5ghz = <0>;
++ };
+ };
+
+ &pinctrl {
+--- a/arch/arm/boot/dts/armada-385-linksys-caiman.dts
++++ b/arch/arm/boot/dts/armada-385-linksys-caiman.dts
+@@ -142,3 +142,205 @@
+ };
+ };
+ };
++
++&pcie1 {
++ mwlwifi {
++ marvell,chainmask = <2 2>;
++ marvell,powertable {
++ AU =
++ <36 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>,
++ <40 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>,
++ <44 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>,
++ <48 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>,
++ <52 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>,
++ <56 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>,
++ <60 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>,
++ <64 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>,
++ <100 0 0x17 0x17 0x17 0x17 0x17 0x17 0x17 0x15 0x17 0x17 0x17 0x14 0x17 0x17 0x17 0x14 0 0xf>,
++ <104 0 0x17 0x17 0x17 0x17 0x17 0x17 0x17 0x15 0x17 0x17 0x17 0x14 0x17 0x17 0x17 0x14 0 0xf>,
++ <108 0 0x17 0x17 0x17 0x17 0x17 0x17 0x17 0x15 0x17 0x17 0x17 0x14 0x17 0x17 0x17 0x14 0 0xf>,
++ <112 0 0x17 0x17 0x17 0x17 0x17 0x17 0x17 0x15 0x17 0x17 0x17 0x14 0x17 0x17 0x17 0x14 0 0xf>,
++ <116 0 0x17 0x17 0x17 0x17 0x17 0x17 0x17 0x15 0x17 0x17 0x17 0x14 0x17 0x17 0x17 0x14 0 0xf>,
++ <120 0 0x17 0x17 0x17 0x17 0x17 0x17 0x17 0x15 0x17 0x17 0x17 0x14 0x17 0x17 0x17 0x14 0 0xf>,
++ <124 0 0x17 0x17 0x17 0x17 0x17 0x17 0x17 0x15 0x17 0x17 0x17 0x14 0x17 0x17 0x17 0x14 0 0xf>,
++ <128 0 0x17 0x17 0x17 0x17 0x17 0x17 0x17 0x15 0x17 0x17 0x17 0x14 0x17 0x17 0x17 0x14 0 0xf>,
++ <132 0 0x17 0x17 0x17 0x17 0x17 0x17 0x17 0x15 0x17 0x17 0x17 0x14 0x17 0x17 0x17 0x14 0 0xf>,
++ <136 0 0x17 0x17 0x17 0x17 0x17 0x17 0x17 0x15 0x17 0x17 0x17 0x14 0x17 0x17 0x17 0x14 0 0xf>,
++ <140 0 0x17 0x17 0x17 0x17 0x17 0x17 0x17 0x15 0x17 0x17 0x17 0x14 0x17 0x17 0x17 0x14 0 0xf>,
++ <149 0 0x1a 0x1a 0x18 0x17 0x1a 0x1a 0x17 0x15 0x1a 0x1a 0x17 0x14 0x1a 0x1a 0x17 0x14 0 0xf>,
++ <153 0 0x1a 0x1a 0x18 0x17 0x1a 0x1a 0x17 0x15 0x1a 0x1a 0x17 0x14 0x1a 0x1a 0x17 0x14 0 0xf>,
++ <157 0 0x1a 0x1a 0x18 0x17 0x1a 0x1a 0x17 0x15 0x1a 0x1a 0x17 0x14 0x1a 0x1a 0x17 0x14 0 0xf>,
++ <161 0 0x1a 0x1a 0x18 0x17 0x1a 0x1a 0x17 0x15 0x1a 0x1a 0x17 0x14 0x1a 0x1a 0x17 0x14 0 0xf>,
++ <165 0 0x1a 0x1a 0x18 0x17 0x1a 0x1a 0x17 0x15 0x1a 0x1a 0x17 0x14 0x1a 0x1a 0x17 0x14 0 0xf>;
++ CA =
++ <36 0 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0 0xf>,
++ <40 0 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0 0xf>,
++ <44 0 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0 0xf>,
++ <48 0 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0 0xf>,
++ <52 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0 0xf>,
++ <56 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0 0xf>,
++ <60 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0 0xf>,
++ <64 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0 0xf>,
++ <100 0 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0 0xf>,
++ <104 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0 0xf>,
++ <108 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>,
++ <112 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>,
++ <116 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>,
++ <120 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>,
++ <124 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>,
++ <128 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>,
++ <132 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>,
++ <136 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>,
++ <140 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>,
++ <149 0 0x1a 0x1a 0x18 0x17 0x19 0x19 0x17 0x15 0x18 0x18 0x17 0x14 0x15 0x15 0x15 0x14 0 0xf>,
++ <153 0 0x1a 0x1a 0x18 0x17 0x1a 0x1a 0x17 0x15 0x1a 0x1a 0x17 0x14 0x15 0x15 0x15 0x14 0 0xf>,
++ <157 0 0x1a 0x1a 0x18 0x17 0x1a 0x1a 0x17 0x15 0x1a 0x1a 0x17 0x14 0x15 0x15 0x15 0x14 0 0xf>,
++ <161 0 0x1a 0x1a 0x18 0x17 0x1a 0x1a 0x17 0x15 0x1a 0x1a 0x17 0x14 0x15 0x15 0x15 0x14 0 0xf>,
++ <165 0 0x1a 0x1a 0x18 0x17 0x1a 0x1a 0x17 0x15 0x1a 0x1a 0x17 0x14 0x15 0x15 0x15 0x14 0 0xf>;
++ CN =
++ <36 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>,
++ <40 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>,
++ <44 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>,
++ <48 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>,
++ <52 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>,
++ <56 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>,
++ <60 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>,
++ <64 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>,
++ <100 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>,
++ <104 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>,
++ <108 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>,
++ <112 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>,
++ <116 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>,
++ <120 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>,
++ <124 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>,
++ <128 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>,
++ <132 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>,
++ <136 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>,
++ <140 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>,
++ <149 0 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x14 0x14 0x14 0x14 0x11 0x11 0x11 0x11 0 0xf>,
++ <153 0 0x1a 0x1a 0x18 0x17 0x1a 0x1a 0x17 0x15 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0 0xf>,
++ <157 0 0x1a 0x1a 0x18 0x17 0x1a 0x1a 0x17 0x15 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0 0xf>,
++ <161 0 0x1a 0x1a 0x18 0x17 0x1a 0x1a 0x17 0x15 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0 0xf>,
++ <165 0 0x15 0x15 0x15 0x15 0x16 0x16 0x16 0x15 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0 0xf>;
++ ETSI =
++ <36 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>,
++ <40 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>,
++ <44 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>,
++ <48 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>,
++ <52 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>,
++ <56 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>,
++ <60 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>,
++ <64 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>,
++ <100 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>,
++ <104 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>,
++ <108 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>,
++ <112 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>,
++ <116 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>,
++ <120 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>,
++ <124 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>,
++ <128 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>,
++ <132 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>,
++ <136 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>,
++ <140 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>,
++ <149 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>;
++ FCC =
++ <36 0 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0 0xf>,
++ <40 0 0x19 0x19 0x18 0x17 0x19 0x19 0x17 0x15 0x17 0x17 0x17 0x14 0x10 0x10 0x10 0x10 0 0xf>,
++ <44 0 0x19 0x19 0x18 0x17 0x19 0x19 0x17 0x15 0x17 0x17 0x17 0x14 0x10 0x10 0x10 0x10 0 0xf>,
++ <48 0 0x1a 0x1a 0x18 0x17 0x1a 0x1a 0x17 0x15 0x17 0x17 0x17 0x14 0x10 0x10 0x10 0x10 0 0xf>,
++ <52 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0 0xf>,
++ <56 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0 0xf>,
++ <60 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0 0xf>,
++ <64 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0 0xf>,
++ <100 0 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0 0xf>,
++ <104 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0 0xf>,
++ <108 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>,
++ <112 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>,
++ <116 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>,
++ <120 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>,
++ <124 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>,
++ <128 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>,
++ <132 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>,
++ <136 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>,
++ <140 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>,
++ <149 0 0x1a 0x1a 0x18 0x17 0x19 0x19 0x17 0x15 0x18 0x18 0x17 0x14 0x15 0x15 0x15 0x14 0 0xf>,
++ <153 0 0x1a 0x1a 0x18 0x17 0x1a 0x1a 0x17 0x15 0x1a 0x1a 0x17 0x14 0x15 0x15 0x15 0x14 0 0xf>,
++ <157 0 0x1a 0x1a 0x18 0x17 0x1a 0x1a 0x17 0x15 0x1a 0x1a 0x17 0x14 0x15 0x15 0x15 0x14 0 0xf>,
++ <161 0 0x1a 0x1a 0x18 0x17 0x1a 0x1a 0x17 0x15 0x1a 0x1a 0x17 0x14 0x15 0x15 0x15 0x14 0 0xf>,
++ <165 0 0x1a 0x1a 0x18 0x17 0x1a 0x1a 0x17 0x15 0x1a 0x1a 0x17 0x14 0x15 0x15 0x15 0x14 0 0xf>;
++ };
++ };
++};
++
++&pcie2 {
++ mwlwifi {
++ marvell,chainmask = <2 2>;
++ marvell,powertable {
++ AU =
++ <1 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0x0 0x0 0x0 0x0 0 0xf>,
++ <2 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0x0 0x0 0x0 0x0 0 0xf>,
++ <3 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0x0 0x0 0x0 0x0 0 0xf>,
++ <4 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0x0 0x0 0x0 0x0 0 0xf>,
++ <5 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0x0 0x0 0x0 0x0 0 0xf>,
++ <6 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0x0 0x0 0x0 0x0 0 0xf>,
++ <7 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0x0 0x0 0x0 0x0 0 0xf>,
++ <8 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0x0 0x0 0x0 0x0 0 0xf>,
++ <9 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0x0 0x0 0x0 0x0 0 0xf>,
++ <10 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0x0 0x0 0x0 0x0 0 0xf>,
++ <11 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0x0 0x0 0x0 0x0 0 0xf>;
++ CA =
++ <1 0 0x19 0x14 0x14 0x14 0x13 0x13 0x13 0x13 0x10 0x10 0x10 0x10 0x00 0x00 0x00 0x00 0 0xf>,
++ <2 0 0x1a 0x19 0x18 0x17 0x19 0x19 0x17 0x16 0x14 0x14 0x14 0x14 0x00 0x00 0x00 0x00 0 0xf>,
++ <3 0 0x1a 0x19 0x18 0x17 0x19 0x19 0x17 0x16 0x14 0x14 0x14 0x14 0x00 0x00 0x00 0x00 0 0xf>,
++ <4 0 0x1a 0x19 0x18 0x17 0x19 0x19 0x17 0x16 0x14 0x14 0x14 0x14 0x00 0x00 0x00 0x00 0 0xf>,
++ <5 0 0x1a 0x19 0x18 0x17 0x19 0x19 0x17 0x16 0x14 0x14 0x14 0x14 0x00 0x00 0x00 0x00 0 0xf>,
++ <6 0 0x1a 0x19 0x18 0x17 0x19 0x19 0x17 0x16 0x14 0x14 0x14 0x14 0x00 0x00 0x00 0x00 0 0xf>,
++ <7 0 0x1a 0x19 0x18 0x17 0x19 0x19 0x17 0x16 0x14 0x14 0x14 0x14 0x00 0x00 0x00 0x00 0 0xf>,
++ <8 0 0x1a 0x19 0x18 0x17 0x19 0x19 0x17 0x16 0x14 0x14 0x14 0x14 0x00 0x00 0x00 0x00 0 0xf>,
++ <9 0 0x1a 0x19 0x18 0x17 0x19 0x19 0x17 0x16 0x14 0x14 0x14 0x14 0x00 0x00 0x00 0x00 0 0xf>,
++ <10 0 0x1a 0x19 0x18 0x17 0x19 0x19 0x17 0x16 0x14 0x14 0x14 0x14 0x00 0x00 0x00 0x00 0 0xf>,
++ <11 0 0x19 0x15 0x15 0x15 0x14 0x14 0x14 0x14 0x13 0x13 0x13 0x13 0x00 0x00 0x00 0x00 0 0xf>;
++ CN =
++ <1 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0x0 0x0 0x0 0x0 0 0xf>,
++ <2 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0x0 0x0 0x0 0x0 0 0xf>,
++ <3 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0x0 0x0 0x0 0x0 0 0xf>,
++ <4 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0x0 0x0 0x0 0x0 0 0xf>,
++ <5 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0x0 0x0 0x0 0x0 0 0xf>,
++ <6 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0x0 0x0 0x0 0x0 0 0xf>,
++ <7 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0x0 0x0 0x0 0x0 0 0xf>,
++ <8 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0x0 0x0 0x0 0x0 0 0xf>,
++ <9 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0x0 0x0 0x0 0x0 0 0xf>,
++ <10 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0x0 0x0 0x0 0x0 0 0xf>,
++ <11 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0x0 0x0 0x0 0x0 0 0xf>,
++ <12 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0x0 0x0 0x0 0x0 0 0xf>,
++ <13 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0x0 0x0 0x0 0x0 0 0xf>,
++ <14 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0x0 0x0 0x0 0x0 0 0xf>;
++ ETSI =
++ <1 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0x0 0x0 0x0 0x0 0 0xf>,
++ <2 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0x0 0x0 0x0 0x0 0 0xf>,
++ <3 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0x0 0x0 0x0 0x0 0 0xf>,
++ <4 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0x0 0x0 0x0 0x0 0 0xf>,
++ <5 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0x0 0x0 0x0 0x0 0 0xf>,
++ <6 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0x0 0x0 0x0 0x0 0 0xf>,
++ <7 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0x0 0x0 0x0 0x0 0 0xf>,
++ <8 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0x0 0x0 0x0 0x0 0 0xf>,
++ <9 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0x0 0x0 0x0 0x0 0 0xf>,
++ <10 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0x0 0x0 0x0 0x0 0 0xf>,
++ <11 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0x0 0x0 0x0 0x0 0 0xf>,
++ <12 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0x0 0x0 0x0 0x0 0 0xf>,
++ <13 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0x0 0x0 0x0 0x0 0 0xf>;
++ FCC =
++ <1 0 0x19 0x14 0x14 0x14 0x13 0x13 0x13 0x13 0x10 0x10 0x10 0x10 0x0 0x0 0x0 0x0 0 0xf>,
++ <2 0 0x1a 0x19 0x18 0x17 0x19 0x19 0x17 0x16 0x14 0x14 0x14 0x14 0x0 0x0 0x0 0x0 0 0xf>,
++ <3 0 0x1a 0x19 0x18 0x17 0x19 0x19 0x17 0x16 0x14 0x14 0x14 0x14 0x0 0x0 0x0 0x0 0 0xf>,
++ <4 0 0x1a 0x19 0x18 0x17 0x19 0x19 0x17 0x16 0x14 0x14 0x14 0x14 0x0 0x0 0x0 0x0 0 0xf>,
++ <5 0 0x1a 0x19 0x18 0x17 0x19 0x19 0x17 0x16 0x14 0x14 0x14 0x14 0x0 0x0 0x0 0x0 0 0xf>,
++ <6 0 0x1a 0x19 0x18 0x17 0x19 0x19 0x17 0x16 0x14 0x14 0x14 0x14 0x0 0x0 0x0 0x0 0 0xf>,
++ <7 0 0x1a 0x19 0x18 0x17 0x19 0x19 0x17 0x16 0x14 0x14 0x14 0x14 0x0 0x0 0x0 0x0 0 0xf>,
++ <8 0 0x1a 0x19 0x18 0x17 0x19 0x19 0x17 0x16 0x14 0x14 0x14 0x14 0x0 0x0 0x0 0x0 0 0xf>,
++ <9 0 0x1a 0x19 0x18 0x17 0x19 0x19 0x17 0x16 0x14 0x14 0x14 0x14 0x0 0x0 0x0 0x0 0 0xf>,
++ <10 0 0x1a 0x19 0x18 0x17 0x19 0x19 0x17 0x16 0x14 0x14 0x14 0x14 0x0 0x0 0x0 0x0 0 0xf>,
++ <11 0 0x19 0x15 0x15 0x15 0x14 0x14 0x14 0x14 0x13 0x13 0x13 0x13 0x0 0x0 0x0 0x0 0 0xf>;
++ };
++ };
++};
+--- a/arch/arm/boot/dts/armada-385-linksys-cobra.dts
++++ b/arch/arm/boot/dts/armada-385-linksys-cobra.dts
+@@ -142,3 +142,205 @@
+ };
+ };
+ };
++
++&pcie1 {
++ mwlwifi {
++ marvell,chainmask = <4 4>;
++ marvell,powertable {
++ AU =
++ <36 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++ <40 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++ <44 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++ <48 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++ <52 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++ <56 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++ <60 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++ <64 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++ <100 0 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0 0xf>,
++ <104 0 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0 0xf>,
++ <108 0 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0 0xf>,
++ <112 0 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0 0xf>,
++ <116 0 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0 0xf>,
++ <120 0 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0 0xf>,
++ <124 0 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0 0xf>,
++ <128 0 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0 0xf>,
++ <132 0 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0 0xf>,
++ <136 0 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0 0xf>,
++ <140 0 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0 0xf>,
++ <149 0 0x19 0x19 0x19 0x17 0x19 0x19 0x16 0x15 0x19 0x19 0x16 0x15 0x19 0x19 0x16 0x15 0 0xf>,
++ <153 0 0x19 0x19 0x19 0x17 0x19 0x19 0x16 0x15 0x19 0x19 0x16 0x15 0x19 0x19 0x16 0x15 0 0xf>,
++ <157 0 0x19 0x19 0x19 0x17 0x19 0x19 0x16 0x15 0x19 0x19 0x16 0x15 0x19 0x19 0x16 0x15 0 0xf>,
++ <161 0 0x19 0x19 0x19 0x17 0x19 0x19 0x16 0x15 0x19 0x19 0x16 0x15 0x19 0x19 0x16 0x15 0 0xf>,
++ <165 0 0x19 0x19 0x19 0x17 0x19 0x19 0x16 0x15 0x19 0x19 0x16 0x15 0x19 0x19 0x16 0x15 0 0xf>;
++ CA =
++ <36 0 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0 0xf>,
++ <40 0 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0 0xf>,
++ <44 0 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0 0xf>,
++ <48 0 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0 0xf>,
++ <52 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0 0xf>,
++ <56 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0 0xf>,
++ <60 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0 0xf>,
++ <64 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0 0xf>,
++ <100 0 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0 0xf>,
++ <104 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0 0xf>,
++ <108 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>,
++ <112 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>,
++ <116 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>,
++ <120 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>,
++ <124 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>,
++ <128 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>,
++ <132 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>,
++ <136 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>,
++ <140 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>,
++ <149 0 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0 0xf>,
++ <153 0 0x16 0x16 0x16 0x16 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0 0xf>,
++ <157 0 0x16 0x16 0x16 0x16 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0 0xf>,
++ <161 0 0x16 0x16 0x16 0x16 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0 0xf>,
++ <165 0 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0 0xf>;
++ CN =
++ <36 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++ <40 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++ <44 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++ <48 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++ <52 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++ <56 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++ <60 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++ <64 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++ <100 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++ <104 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++ <108 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++ <112 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++ <116 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++ <120 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++ <124 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++ <128 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++ <132 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++ <136 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++ <140 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++ <149 0 0x14 0x14 0x14 0x14 0x13 0x13 0x13 0x13 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>,
++ <153 0 0x16 0x16 0x16 0x16 0x15 0x15 0x15 0x15 0x14 0x14 0x14 0x14 0x10 0x10 0x10 0x10 0 0xf>,
++ <157 0 0x16 0x16 0x16 0x16 0x15 0x15 0x15 0x15 0x14 0x14 0x14 0x14 0x10 0x10 0x10 0x10 0 0xf>,
++ <161 0 0x16 0x16 0x16 0x16 0x15 0x15 0x15 0x15 0x14 0x14 0x14 0x14 0x10 0x10 0x10 0x10 0 0xf>,
++ <165 0 0x13 0x13 0x13 0x13 0x13 0x13 0x13 0x13 0x14 0x14 0x14 0x14 0x10 0x10 0x10 0x10 0 0xf>;
++ ETSI =
++ <36 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++ <40 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++ <44 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++ <48 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++ <52 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++ <56 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++ <60 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++ <64 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++ <100 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++ <104 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++ <108 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++ <112 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++ <116 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++ <120 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++ <124 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++ <128 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++ <132 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++ <136 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++ <140 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++ <149 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>;
++ FCC =
++ <36 0 0x12 0x12 0x12 0x12 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0xf 0xf 0xf 0xf 0 0xf>,
++ <40 0 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0xf 0xf 0xf 0xf 0 0xf>,
++ <44 0 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0xf 0xf 0xf 0xf 0 0xf>,
++ <48 0 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0xf 0xf 0xf 0xf 0 0xf>,
++ <52 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0 0xf>,
++ <56 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0 0xf>,
++ <60 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0 0xf>,
++ <64 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0 0xf>,
++ <100 0 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0 0xf>,
++ <104 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0 0xf>,
++ <108 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>,
++ <112 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>,
++ <116 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>,
++ <120 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>,
++ <124 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>,
++ <128 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>,
++ <132 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>,
++ <136 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>,
++ <140 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>,
++ <149 0 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0 0xf>,
++ <153 0 0x16 0x16 0x16 0x16 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0 0xf>,
++ <157 0 0x16 0x16 0x16 0x16 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0 0xf>,
++ <161 0 0x16 0x16 0x16 0x16 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0 0xf>,
++ <165 0 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0 0xf>;
++ };
++ };
++};
++
++&pcie2 {
++ mwlwifi {
++ marvell,chainmask = <4 4>;
++ marvell,powertable {
++ AU =
++ <1 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++ <2 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++ <3 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++ <4 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++ <5 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++ <6 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++ <7 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++ <8 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++ <9 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++ <10 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++ <11 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>;
++ CA =
++ <1 0 0x17 0x10 0x10 0x10 0xf 0xf 0xf 0xf 0xe 0xe 0xe 0xe 0x0 0x0 0x0 0x0 0 0xf>,
++ <2 0 0x18 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0x0 0x0 0x0 0x0 0 0xf>,
++ <3 0 0x18 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0x0 0x0 0x0 0x0 0 0xf>,
++ <4 0 0x18 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0x0 0x0 0x0 0x0 0 0xf>,
++ <5 0 0x18 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0x0 0x0 0x0 0x0 0 0xf>,
++ <6 0 0x18 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0x0 0x0 0x0 0x0 0 0xf>,
++ <7 0 0x18 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0x0 0x0 0x0 0x0 0 0xf>,
++ <8 0 0x18 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0x0 0x0 0x0 0x0 0 0xf>,
++ <9 0 0x18 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0x0 0x0 0x0 0x0 0 0xf>,
++ <10 0 0x18 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0x0 0x0 0x0 0x0 0 0xf>,
++ <11 0 0x17 0x12 0x12 0x12 0x13 0x13 0x13 0x13 0xf 0xf 0xf 0xf 0x0 0x0 0x0 0x0 0 0xf>;
++ CN =
++ <1 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++ <2 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++ <3 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++ <4 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++ <5 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++ <6 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++ <7 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++ <8 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++ <9 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++ <10 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++ <11 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++ <12 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++ <13 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++ <14 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>;
++ ETSI =
++ <1 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++ <2 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++ <3 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++ <4 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++ <5 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++ <6 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++ <7 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++ <8 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++ <9 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++ <10 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++ <11 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++ <12 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++ <13 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>;
++ FCC =
++ <1 0 0x17 0x10 0x10 0x10 0xf 0xf 0xf 0xf 0xe 0xe 0xe 0xe 0x0 0x0 0x0 0x0 0 0xf>,
++ <2 0 0x18 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0x0 0x0 0x0 0x0 0 0xf>,
++ <3 0 0x18 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0x0 0x0 0x0 0x0 0 0xf>,
++ <4 0 0x18 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0x0 0x0 0x0 0x0 0 0xf>,
++ <5 0 0x18 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0x0 0x0 0x0 0x0 0 0xf>,
++ <6 0 0x18 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0x0 0x0 0x0 0x0 0 0xf>,
++ <7 0 0x18 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0x0 0x0 0x0 0x0 0 0xf>,
++ <8 0 0x18 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0x0 0x0 0x0 0x0 0 0xf>,
++ <9 0 0x18 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0x0 0x0 0x0 0x0 0 0xf>,
++ <10 0 0x18 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0x0 0x0 0x0 0x0 0 0xf>,
++ <11 0 0x17 0x12 0x12 0x12 0x13 0x13 0x13 0x13 0xf 0xf 0xf 0xf 0x0 0x0 0x0 0x0 0 0xf>;
++ };
++ };
++};
+--- a/arch/arm/boot/dts/armada-385-linksys-shelby.dts
++++ b/arch/arm/boot/dts/armada-385-linksys-shelby.dts
+@@ -142,3 +142,205 @@
+ };
+ };
+ };
++
++&pcie1 {
++ mwlwifi {
++ marvell,chainmask = <4 4>;
++ marvell,powertable {
++ AU =
++ <36 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++ <40 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++ <44 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++ <48 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++ <52 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++ <56 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++ <60 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++ <64 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++ <100 0 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0 0xf>,
++ <104 0 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0 0xf>,
++ <108 0 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0 0xf>,
++ <112 0 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0 0xf>,
++ <116 0 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0 0xf>,
++ <120 0 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0 0xf>,
++ <124 0 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0 0xf>,
++ <128 0 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0 0xf>,
++ <132 0 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0 0xf>,
++ <136 0 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0 0xf>,
++ <140 0 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0 0xf>,
++ <149 0 0x19 0x19 0x19 0x17 0x19 0x19 0x16 0x15 0x19 0x19 0x16 0x15 0x19 0x19 0x16 0x15 0 0xf>,
++ <153 0 0x19 0x19 0x19 0x17 0x19 0x19 0x16 0x15 0x19 0x19 0x16 0x15 0x19 0x19 0x16 0x15 0 0xf>,
++ <157 0 0x19 0x19 0x19 0x17 0x19 0x19 0x16 0x15 0x19 0x19 0x16 0x15 0x19 0x19 0x16 0x15 0 0xf>,
++ <161 0 0x19 0x19 0x19 0x17 0x19 0x19 0x16 0x15 0x19 0x19 0x16 0x15 0x19 0x19 0x16 0x15 0 0xf>,
++ <165 0 0x19 0x19 0x19 0x17 0x19 0x19 0x16 0x15 0x19 0x19 0x16 0x15 0x19 0x19 0x16 0x15 0 0xf>;
++ CA =
++ <36 0 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0 0xf>,
++ <40 0 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0 0xf>,
++ <44 0 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0 0xf>,
++ <48 0 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0 0xf>,
++ <52 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0 0xf>,
++ <56 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0 0xf>,
++ <60 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0 0xf>,
++ <64 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0 0xf>,
++ <100 0 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0 0xf>,
++ <104 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0 0xf>,
++ <108 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>,
++ <112 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>,
++ <116 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>,
++ <120 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>,
++ <124 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>,
++ <128 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>,
++ <132 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>,
++ <136 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>,
++ <140 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>,
++ <149 0 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0 0xf>,
++ <153 0 0x16 0x16 0x16 0x16 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0 0xf>,
++ <157 0 0x16 0x16 0x16 0x16 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0 0xf>,
++ <161 0 0x16 0x16 0x16 0x16 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0 0xf>,
++ <165 0 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0 0xf>;
++ CN =
++ <36 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++ <40 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++ <44 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++ <48 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++ <52 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++ <56 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++ <60 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++ <64 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++ <100 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++ <104 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++ <108 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++ <112 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++ <116 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++ <120 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++ <124 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++ <128 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++ <132 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++ <136 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++ <140 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++ <149 0 0x14 0x14 0x14 0x14 0x13 0x13 0x13 0x13 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>,
++ <153 0 0x16 0x16 0x16 0x16 0x15 0x15 0x15 0x15 0x14 0x14 0x14 0x14 0x10 0x10 0x10 0x10 0 0xf>,
++ <157 0 0x16 0x16 0x16 0x16 0x15 0x15 0x15 0x15 0x14 0x14 0x14 0x14 0x10 0x10 0x10 0x10 0 0xf>,
++ <161 0 0x16 0x16 0x16 0x16 0x15 0x15 0x15 0x15 0x14 0x14 0x14 0x14 0x10 0x10 0x10 0x10 0 0xf>,
++ <165 0 0x13 0x13 0x13 0x13 0x13 0x13 0x13 0x13 0x14 0x14 0x14 0x14 0x10 0x10 0x10 0x10 0 0xf>;
++ ETSI =
++ <36 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++ <40 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++ <44 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++ <48 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++ <52 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++ <56 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++ <60 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++ <64 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++ <100 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++ <104 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++ <108 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++ <112 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++ <116 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++ <120 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++ <124 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++ <128 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++ <132 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++ <136 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++ <140 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++ <149 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>;
++ FCC =
++ <36 0 0x12 0x12 0x12 0x12 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0xf 0xf 0xf 0xf 0 0xf>,
++ <40 0 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0xf 0xf 0xf 0xf 0 0xf>,
++ <44 0 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0xf 0xf 0xf 0xf 0 0xf>,
++ <48 0 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0xf 0xf 0xf 0xf 0 0xf>,
++ <52 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0 0xf>,
++ <56 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0 0xf>,
++ <60 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0 0xf>,
++ <64 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0 0xf>,
++ <100 0 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0 0xf>,
++ <104 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0 0xf>,
++ <108 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>,
++ <112 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>,
++ <116 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>,
++ <120 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>,
++ <124 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>,
++ <128 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>,
++ <132 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>,
++ <136 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>,
++ <140 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>,
++ <149 0 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0 0xf>,
++ <153 0 0x16 0x16 0x16 0x16 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0 0xf>,
++ <157 0 0x16 0x16 0x16 0x16 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0 0xf>,
++ <161 0 0x16 0x16 0x16 0x16 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0 0xf>,
++ <165 0 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0 0xf>;
++ };
++ };
++};
++
++&pcie2 {
++ mwlwifi {
++ marvell,chainmask = <4 4>;
++ marvell,powertable {
++ AU =
++ <1 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++ <2 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++ <3 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++ <4 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++ <5 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++ <6 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++ <7 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++ <8 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++ <9 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++ <10 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++ <11 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>;
++ CA =
++ <1 0 0x17 0x10 0x10 0x10 0xf 0xf 0xf 0xf 0xe 0xe 0xe 0xe 0x0 0x0 0x0 0x0 0 0xf>,
++ <2 0 0x18 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0x0 0x0 0x0 0x0 0 0xf>,
++ <3 0 0x18 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0x0 0x0 0x0 0x0 0 0xf>,
++ <4 0 0x18 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0x0 0x0 0x0 0x0 0 0xf>,
++ <5 0 0x18 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0x0 0x0 0x0 0x0 0 0xf>,
++ <6 0 0x18 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0x0 0x0 0x0 0x0 0 0xf>,
++ <7 0 0x18 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0x0 0x0 0x0 0x0 0 0xf>,
++ <8 0 0x18 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0x0 0x0 0x0 0x0 0 0xf>,
++ <9 0 0x18 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0x0 0x0 0x0 0x0 0 0xf>,
++ <10 0 0x18 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0x0 0x0 0x0 0x0 0 0xf>,
++ <11 0 0x17 0x12 0x12 0x12 0x13 0x13 0x13 0x13 0xf 0xf 0xf 0xf 0x0 0x0 0x0 0x0 0 0xf>;
++ CN =
++ <1 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++ <2 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++ <3 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++ <4 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++ <5 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++ <6 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++ <7 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++ <8 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++ <9 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++ <10 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++ <11 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++ <12 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++ <13 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++ <14 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>;
++ ETSI =
++ <1 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++ <2 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++ <3 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++ <4 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++ <5 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++ <6 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++ <7 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++ <8 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++ <9 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++ <10 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++ <11 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++ <12 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++ <13 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>;
++ FCC =
++ <1 0 0x17 0x10 0x10 0x10 0xf 0xf 0xf 0xf 0xe 0xe 0xe 0xe 0x0 0x0 0x0 0x0 0 0xf>,
++ <2 0 0x18 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0x0 0x0 0x0 0x0 0 0xf>,
++ <3 0 0x18 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0x0 0x0 0x0 0x0 0 0xf>,
++ <4 0 0x18 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0x0 0x0 0x0 0x0 0 0xf>,
++ <5 0 0x18 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0x0 0x0 0x0 0x0 0 0xf>,
++ <6 0 0x18 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0x0 0x0 0x0 0x0 0 0xf>,
++ <7 0 0x18 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0x0 0x0 0x0 0x0 0 0xf>,
++ <8 0 0x18 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0x0 0x0 0x0 0x0 0 0xf>,
++ <9 0 0x18 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0x0 0x0 0x0 0x0 0 0xf>,
++ <10 0 0x18 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0x0 0x0 0x0 0x0 0 0xf>,
++ <11 0 0x17 0x12 0x12 0x12 0x13 0x13 0x13 0x13 0xf 0xf 0xf 0xf 0x0 0x0 0x0 0x0 0 0xf>;
++ };
++ };
++};
+--- a/arch/arm/boot/dts/armada-385-linksys-rango.dts
++++ b/arch/arm/boot/dts/armada-385-linksys-rango.dts
+@@ -157,6 +157,18 @@
+ };
+ };
+
++&pcie1 {
++ mwlwifi {
++ marvell,chainmask = <4 4>;
++ };
++};
++
++&pcie2 {
++ mwlwifi {
++ marvell,chainmask = <4 4>;
++ };
++};
++
+ &sdhci {
+ pinctrl-names = "default";
+ pinctrl-0 = <&sdhci_pins>;
+--- a/arch/arm/boot/dts/armada-xp-linksys-mamba.dts
++++ b/arch/arm/boot/dts/armada-xp-linksys-mamba.dts
+@@ -225,12 +225,100 @@
+ pcie at 2,0 {
+ /* Port 0, Lane 1 */
+ status = "okay";
++
++ mwlwifi {
++ marvell,5ghz = <0>;
++ marvell,chainmask = <4 4>;
++ marvell,powertable {
++ FCC =
++ <1 0 0x17 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0xf 0xf 0xf 0xf 0x0 0x0 0x0 0x0 0 0xf>,
++ <2 0 0x17 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x10 0x10 0x10 0x10 0x0 0x0 0x0 0x0 0 0xf>,
++ <3 0 0x17 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x10 0x10 0x10 0x10 0x0 0x0 0x0 0x0 0 0xf>,
++ <4 0 0x17 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x10 0x10 0x10 0x10 0x0 0x0 0x0 0x0 0 0xf>,
++ <5 0 0x17 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x10 0x10 0x10 0x10 0x0 0x0 0x0 0x0 0 0xf>,
++ <6 0 0x17 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x10 0x10 0x10 0x10 0x0 0x0 0x0 0x0 0 0xf>,
++ <7 0 0x17 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x10 0x10 0x10 0x10 0x0 0x0 0x0 0x0 0 0xf>,
++ <8 0 0x17 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x10 0x10 0x10 0x10 0x0 0x0 0x0 0x0 0 0xf>,
++ <9 0 0x17 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x10 0x10 0x10 0x10 0x0 0x0 0x0 0x0 0 0xf>,
++ <10 0 0x17 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x10 0x10 0x10 0x10 0x0 0x0 0x0 0x0 0 0xf>,
++ <11 0 0x17 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x0 0x0 0x0 0x0 0 0xf>;
++
++ ETSI =
++ <1 0 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0x0 0x0 0x0 0x0 0 0xf>,
++ <2 0 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0x0 0x0 0x0 0x0 0 0xf>,
++ <3 0 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0x0 0x0 0x0 0x0 0 0xf>,
++ <4 0 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0x0 0x0 0x0 0x0 0 0xf>,
++ <5 0 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0x0 0x0 0x0 0x0 0 0xf>,
++ <6 0 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0x0 0x0 0x0 0x0 0 0xf>,
++ <7 0 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0x0 0x0 0x0 0x0 0 0xf>,
++ <8 0 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0x0 0x0 0x0 0x0 0 0xf>,
++ <9 0 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0x0 0x0 0x0 0x0 0 0xf>,
++ <10 0 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0x0 0x0 0x0 0x0 0 0xf>,
++ <11 0 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0x0 0x0 0x0 0x0 0 0xf>,
++ <12 0 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0x0 0x0 0x0 0x0 0 0xf>,
++ <13 0 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0x0 0x0 0x0 0x0 0 0xf>;
++ };
++ };
+ };
+
+ /* Second mini-PCIe port */
+ pcie at 3,0 {
+ /* Port 0, Lane 3 */
+ status = "okay";
++
++ mwlwifi {
++ marvell,2ghz = <0>;
++ marvell,chainmask = <4 4>;
++ marvell,powertable {
++ FCC =
++ <36 0 0x8 0x8 0x8 0x8 0x8 0x8 0x8 0x8 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0 0xf>,
++ <40 0 0x8 0x8 0x8 0x8 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0 0xf>,
++ <44 0 0x8 0x8 0x8 0x8 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0 0xf>,
++ <48 0 0x8 0x8 0x8 0x8 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0 0xf>,
++ <52 0 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0x12 0x12 0x12 0x12 0x12 0x12 0x12 0x12 0 0xf>,
++ <56 0 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0x12 0x12 0x12 0x12 0x12 0x12 0x12 0x12 0 0xf>,
++ <60 0 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0x12 0x12 0x12 0x12 0x12 0x12 0x12 0x12 0 0xf>,
++ <64 0 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0x12 0x12 0x12 0x12 0x12 0x12 0x12 0x12 0 0xf>,
++ <100 0 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0 0xf>,
++ <104 0 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0 0xf>,
++ <108 0 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0 0xf>,
++ <112 0 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0 0xf>,
++ <116 0 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0 0xf>,
++ <120 0 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0 0xf>,
++ <124 0 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0 0xf>,
++ <128 0 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0 0xf>,
++ <132 0 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0 0xf>,
++ <136 0 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0 0xf>,
++ <140 0 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0 0xf>,
++ <149 0 0x16 0x16 0x16 0x16 0x14 0x14 0x14 0x14 0x15 0x15 0x15 0x15 0x14 0x14 0x14 0x14 0 0xf>,
++ <153 0 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x14 0x14 0x14 0x14 0 0xf>,
++ <157 0 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x14 0x14 0x14 0x14 0 0xf>,
++ <161 0 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x14 0x14 0x14 0x14 0 0xf>,
++ <165 0 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x15 0x15 0x15 0x15 0x14 0x14 0x14 0x14 0 0xf>;
++
++ ETSI =
++ <36 0 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xd 0xd 0xd 0xd 0xc 0xc 0xc 0xc 0 0xf>,
++ <40 0 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xd 0xd 0xd 0xd 0xc 0xc 0xc 0xc 0 0xf>,
++ <44 0 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xd 0xd 0xd 0xd 0xc 0xc 0xc 0xc 0 0xf>,
++ <48 0 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xd 0xd 0xd 0xd 0xc 0xc 0xc 0xc 0 0xf>,
++ <52 0 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xd 0xd 0xd 0xd 0xc 0xc 0xc 0xc 0 0xf>,
++ <56 0 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xd 0xd 0xd 0xd 0xc 0xc 0xc 0xc 0 0xf>,
++ <60 0 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xd 0xd 0xd 0xd 0xc 0xc 0xc 0xc 0 0xf>,
++ <64 0 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xd 0xd 0xd 0xd 0xc 0xc 0xc 0xc 0 0xf>,
++ <100 0 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xd 0xd 0xd 0xd 0xc 0xc 0xc 0xc 0 0xf>,
++ <104 0 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xd 0xd 0xd 0xd 0xc 0xc 0xc 0xc 0 0xf>,
++ <108 0 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xd 0xd 0xd 0xd 0xc 0xc 0xc 0xc 0 0xf>,
++ <112 0 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xd 0xd 0xd 0xd 0xc 0xc 0xc 0xc 0 0xf>,
++ <116 0 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xd 0xd 0xd 0xd 0xc 0xc 0xc 0xc 0 0xf>,
++ <120 0 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xd 0xd 0xd 0xd 0xc 0xc 0xc 0xc 0 0xf>,
++ <124 0 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xd 0xd 0xd 0xd 0xc 0xc 0xc 0xc 0 0xf>,
++ <128 0 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xd 0xd 0xd 0xd 0xc 0xc 0xc 0xc 0 0xf>,
++ <132 0 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xd 0xd 0xd 0xd 0xc 0xc 0xc 0xc 0 0xf>,
++ <136 0 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xd 0xd 0xd 0xd 0xc 0xc 0xc 0xc 0 0xf>,
++ <140 0 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xd 0xd 0xd 0xd 0xc 0xc 0xc 0xc 0 0xf>,
++ <149 0 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xd 0xd 0xd 0xd 0xc 0xc 0xc 0xc 0 0xf>;
++ };
++ };
+ };
+ };
+
diff --git a/target/linux/mvebu/patches-6.1/304-revert_i2c_delay.patch b/target/linux/mvebu/patches-6.1/304-revert_i2c_delay.patch
new file mode 100644
index 0000000000..930c0f9494
--- /dev/null
+++ b/target/linux/mvebu/patches-6.1/304-revert_i2c_delay.patch
@@ -0,0 +1,15 @@
+--- a/arch/arm/boot/dts/armada-xp.dtsi
++++ b/arch/arm/boot/dts/armada-xp.dtsi
+@@ -237,12 +237,10 @@
+ };
+
+ &i2c0 {
+- compatible = "marvell,mv78230-i2c", "marvell,mv64xxx-i2c";
+ reg = <0x11000 0x100>;
+ };
+
+ &i2c1 {
+- compatible = "marvell,mv78230-i2c", "marvell,mv64xxx-i2c";
+ reg = <0x11100 0x100>;
+ };
+
diff --git a/target/linux/mvebu/patches-6.1/305-armada-385-rd-mtd-partitions.patch b/target/linux/mvebu/patches-6.1/305-armada-385-rd-mtd-partitions.patch
new file mode 100644
index 0000000000..31bd53b1f3
--- /dev/null
+++ b/target/linux/mvebu/patches-6.1/305-armada-385-rd-mtd-partitions.patch
@@ -0,0 +1,19 @@
+--- a/arch/arm/boot/dts/armada-388-rd.dts
++++ b/arch/arm/boot/dts/armada-388-rd.dts
+@@ -103,6 +103,16 @@
+ compatible = "st,m25p128", "jedec,spi-nor";
+ reg = <0>; /* Chip select 0 */
+ spi-max-frequency = <108000000>;
++
++ partition at 0 {
++ label = "uboot";
++ reg = <0 0x400000>;
++ };
++
++ partition at 1 {
++ label = "firmware";
++ reg = <0x400000 0xc00000>;
++ };
+ };
+ };
+
diff --git a/target/linux/mvebu/patches-6.1/306-ARM-mvebu-385-ap-Add-partitions.patch b/target/linux/mvebu/patches-6.1/306-ARM-mvebu-385-ap-Add-partitions.patch
new file mode 100644
index 0000000000..2057e31c7e
--- /dev/null
+++ b/target/linux/mvebu/patches-6.1/306-ARM-mvebu-385-ap-Add-partitions.patch
@@ -0,0 +1,35 @@
+From 9861f93a59142a3131870df2521eb2deb73026d7 Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime.ripard at free-electrons.com>
+Date: Tue, 13 Jan 2015 11:14:09 +0100
+Subject: [PATCH 2/2] ARM: mvebu: 385-ap: Add partitions
+
+Signed-off-by: Maxime Ripard <maxime.ripard at free-electrons.com>
+---
+ arch/arm/boot/dts/armada-385-db-ap.dts | 15 +++++++++++++++
+ 1 file changed, 15 insertions(+)
+
+--- a/arch/arm/boot/dts/armada-385-db-ap.dts
++++ b/arch/arm/boot/dts/armada-385-db-ap.dts
+@@ -218,19 +218,19 @@
+ #size-cells = <1>;
+
+ partition at 0 {
+- label = "U-Boot";
++ label = "u-boot";
+ reg = <0x00000000 0x00800000>;
+ read-only;
+ };
+
+ partition at 800000 {
+- label = "uImage";
++ label = "kernel";
+ reg = <0x00800000 0x00400000>;
+ read-only;
+ };
+
+ partition at c00000 {
+- label = "Root";
++ label = "ubi";
+ reg = <0x00c00000 0x3f400000>;
+ };
+ };
diff --git a/target/linux/mvebu/patches-6.1/307-armada-xp-linksys-mamba-broken-idle.patch b/target/linux/mvebu/patches-6.1/307-armada-xp-linksys-mamba-broken-idle.patch
new file mode 100644
index 0000000000..16112d53fc
--- /dev/null
+++ b/target/linux/mvebu/patches-6.1/307-armada-xp-linksys-mamba-broken-idle.patch
@@ -0,0 +1,10 @@
+--- a/arch/arm/boot/dts/armada-xp-linksys-mamba.dts
++++ b/arch/arm/boot/dts/armada-xp-linksys-mamba.dts
+@@ -485,3 +485,7 @@
+ };
+ };
+ };
++
++&coherencyfab {
++ broken-idle;
++};
diff --git a/target/linux/mvebu/patches-6.1/308-armada-xp-linksys-mamba-wan.patch b/target/linux/mvebu/patches-6.1/308-armada-xp-linksys-mamba-wan.patch
new file mode 100644
index 0000000000..4315abc7d2
--- /dev/null
+++ b/target/linux/mvebu/patches-6.1/308-armada-xp-linksys-mamba-wan.patch
@@ -0,0 +1,11 @@
+--- a/arch/arm/boot/dts/armada-xp-linksys-mamba.dts
++++ b/arch/arm/boot/dts/armada-xp-linksys-mamba.dts
+@@ -387,7 +387,7 @@
+
+ port at 4 {
+ reg = <4>;
+- label = "internet";
++ label = "wan";
+ };
+
+ port at 5 {
diff --git a/target/linux/mvebu/patches-6.1/309-linksys-status-led.patch b/target/linux/mvebu/patches-6.1/309-linksys-status-led.patch
new file mode 100644
index 0000000000..e5e83572c9
--- /dev/null
+++ b/target/linux/mvebu/patches-6.1/309-linksys-status-led.patch
@@ -0,0 +1,50 @@
+--- a/arch/arm/boot/dts/armada-385-linksys.dtsi
++++ b/arch/arm/boot/dts/armada-385-linksys.dtsi
+@@ -14,6 +14,13 @@
+ compatible = "linksys,armada385", "marvell,armada385",
+ "marvell,armada380";
+
++ aliases {
++ led-boot = &led_power;
++ led-failsafe = &led_power;
++ led-running = &led_power;
++ led-upgrade = &led_power;
++ };
++
+ chosen {
+ stdout-path = "serial0:115200n8";
+ };
+@@ -71,7 +78,7 @@
+ pinctrl-0 = <&gpio_leds_pins>;
+ pinctrl-names = "default";
+
+- power {
++ led_power: power {
+ gpios = <&gpio1 23 GPIO_ACTIVE_HIGH>;
+ default-state = "on";
+ };
+--- a/arch/arm/boot/dts/armada-xp-linksys-mamba.dts
++++ b/arch/arm/boot/dts/armada-xp-linksys-mamba.dts
+@@ -26,6 +26,13 @@
+ compatible = "linksys,mamba", "marvell,armadaxp-mv78230",
+ "marvell,armadaxp", "marvell,armada-370-xp";
+
++ aliases {
++ led-boot = &led_power;
++ led-failsafe = &led_power;
++ led-running = &led_power;
++ led-upgrade = &led_power;
++ };
++
+ chosen {
+ bootargs = "console=ttyS0,115200";
+ stdout-path = &uart0;
+@@ -197,7 +204,7 @@
+ pinctrl-0 = <&power_led_pin>;
+ pinctrl-names = "default";
+
+- power {
++ led_power: power {
+ label = "mamba:white:power";
+ gpios = <&gpio1 8 GPIO_ACTIVE_HIGH>;
+ default-state = "on";
diff --git a/target/linux/mvebu/patches-6.1/310-linksys-use-eth0-as-cpu-port.patch b/target/linux/mvebu/patches-6.1/310-linksys-use-eth0-as-cpu-port.patch
new file mode 100644
index 0000000000..84d49a004b
--- /dev/null
+++ b/target/linux/mvebu/patches-6.1/310-linksys-use-eth0-as-cpu-port.patch
@@ -0,0 +1,25 @@
+--- a/arch/arm/boot/dts/armada-385-linksys.dtsi
++++ b/arch/arm/boot/dts/armada-385-linksys.dtsi
+@@ -116,7 +116,7 @@
+ };
+
+ ð2 {
+- status = "okay";
++ status = "disabled";
+ phy-mode = "sgmii";
+ buffer-manager = <&bm>;
+ bm,pool-long = <2>;
+@@ -200,10 +200,10 @@
+ label = "wan";
+ };
+
+- port at 5 {
+- reg = <5>;
++ port at 6 {
++ reg = <6>;
+ label = "cpu";
+- ethernet = <ð2>;
++ ethernet = <ð0>;
+
+ fixed-link {
+ speed = <1000>;
diff --git a/target/linux/mvebu/patches-6.1/311-adjust-compatible-for-linksys.patch b/target/linux/mvebu/patches-6.1/311-adjust-compatible-for-linksys.patch
new file mode 100644
index 0000000000..a5d3e63810
--- /dev/null
+++ b/target/linux/mvebu/patches-6.1/311-adjust-compatible-for-linksys.patch
@@ -0,0 +1,68 @@
+--- a/arch/arm/boot/dts/armada-385-linksys-rango.dts
++++ b/arch/arm/boot/dts/armada-385-linksys-rango.dts
+@@ -12,8 +12,8 @@
+
+ / {
+ model = "Linksys WRT3200ACM";
+- compatible = "linksys,rango", "linksys,armada385", "marvell,armada385",
+- "marvell,armada380";
++ compatible = "linksys,wrt3200acm", "linksys,rango", "linksys,armada385",
++ "marvell,armada385", "marvell,armada380";
+ };
+
+ &expander0 {
+--- a/arch/arm/boot/dts/armada-xp-linksys-mamba.dts
++++ b/arch/arm/boot/dts/armada-xp-linksys-mamba.dts
+@@ -22,9 +22,10 @@
+ #include "armada-xp-mv78230.dtsi"
+
+ / {
+- model = "Linksys WRT1900AC";
+- compatible = "linksys,mamba", "marvell,armadaxp-mv78230",
+- "marvell,armadaxp", "marvell,armada-370-xp";
++ model = "Linksys WRT1900AC v1";
++ compatible = "linksys,wrt1900ac-v1", "linksys,mamba",
++ "marvell,armadaxp-mv78230", "marvell,armadaxp",
++ "marvell,armada-370-xp";
+
+ aliases {
+ led-boot = &led_power;
+--- a/arch/arm/boot/dts/armada-385-linksys-cobra.dts
++++ b/arch/arm/boot/dts/armada-385-linksys-cobra.dts
+@@ -9,8 +9,9 @@
+ #include "armada-385-linksys.dtsi"
+
+ / {
+- model = "Linksys WRT1900ACv2";
+- compatible = "linksys,cobra", "linksys,armada385", "marvell,armada385",
++ model = "Linksys WRT1900AC v2";
++ compatible = "linksys,wrt1900ac-v2", "linksys,cobra",
++ "linksys,armada385", "marvell,armada385",
+ "marvell,armada380";
+ };
+
+--- a/arch/arm/boot/dts/armada-385-linksys-caiman.dts
++++ b/arch/arm/boot/dts/armada-385-linksys-caiman.dts
+@@ -10,8 +10,8 @@
+
+ / {
+ model = "Linksys WRT1200AC";
+- compatible = "linksys,caiman", "linksys,armada385", "marvell,armada385",
+- "marvell,armada380";
++ compatible = "linksys,wrt1200ac", "linksys,caiman", "linksys,armada385",
++ "marvell,armada385", "marvell,armada380";
+ };
+
+ &expander0 {
+--- a/arch/arm/boot/dts/armada-385-linksys-shelby.dts
++++ b/arch/arm/boot/dts/armada-385-linksys-shelby.dts
+@@ -10,7 +10,8 @@
+
+ / {
+ model = "Linksys WRT1900ACS";
+- compatible = "linksys,shelby", "linksys,armada385", "marvell,armada385",
++ compatible = "linksys,wrt1900acs", "linksys,shelby",
++ "linksys,armada385", "marvell,armada385",
+ "marvell,armada380";
+ };
+
diff --git a/target/linux/mvebu/patches-6.1/312-ARM-dts-armada388-clearfog-emmc-on-clearfog-base.patch b/target/linux/mvebu/patches-6.1/312-ARM-dts-armada388-clearfog-emmc-on-clearfog-base.patch
new file mode 100644
index 0000000000..dd2bef7f63
--- /dev/null
+++ b/target/linux/mvebu/patches-6.1/312-ARM-dts-armada388-clearfog-emmc-on-clearfog-base.patch
@@ -0,0 +1,87 @@
+From 8137da20701c776ad3481115305a5e8e410871ba Mon Sep 17 00:00:00 2001
+From: Russell King <rmk+kernel at armlinux.org.uk>
+Date: Tue, 29 Nov 2016 10:15:45 +0000
+Subject: ARM: dts: armada388-clearfog: emmc on clearfog base
+
+Signed-off-by: Russell King <rmk+kernel at armlinux.org.uk>
+---
+ arch/arm/boot/dts/armada-388-clearfog-base.dts | 1 +
+ .../dts/armada-38x-solidrun-microsom-emmc.dtsi | 62 ++++++++++++++++++++++
+ 2 files changed, 63 insertions(+)
+ create mode 100644 arch/arm/boot/dts/armada-38x-solidrun-microsom-emmc.dtsi
+
+--- a/arch/arm/boot/dts/armada-388-clearfog-base.dts
++++ b/arch/arm/boot/dts/armada-388-clearfog-base.dts
+@@ -7,6 +7,7 @@
+
+ /dts-v1/;
+ #include "armada-388-clearfog.dtsi"
++#include "armada-38x-solidrun-microsom-emmc.dtsi"
+
+ / {
+ model = "SolidRun Clearfog Base A1";
+--- /dev/null
++++ b/arch/arm/boot/dts/armada-38x-solidrun-microsom-emmc.dtsi
+@@ -0,0 +1,62 @@
++/*
++ * Device Tree file for SolidRun Armada 38x Microsom add-on for eMMC
++ *
++ * Copyright (C) 2015 Russell King
++ *
++ * This board is in development; the contents of this file work with
++ * the A1 rev 2.0 of the board, which does not represent final
++ * production board. Things will change, don't expect this file to
++ * remain compatible info the future.
++ *
++ * This file is dual-licensed: you can use it either under the terms
++ * of the GPL or the X11 license, at your option. Note that this dual
++ * licensing only applies to this file, and not this project as a
++ * whole.
++ *
++ * a) This file is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License
++ * version 2 as published by the Free Software Foundation.
++ *
++ * This file is distributed in the hope that it will be useful
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * Or, alternatively
++ *
++ * b) Permission is hereby granted, free of charge, to any person
++ * obtaining a copy of this software and associated documentation
++ * files (the "Software"), to deal in the Software without
++ * restriction, including without limitation the rights to use
++ * copy, modify, merge, publish, distribute, sublicense, and/or
++ * sell copies of the Software, and to permit persons to whom the
++ * Software is furnished to do so, subject to the following
++ * conditions:
++ *
++ * The above copyright notice and this permission notice shall be
++ * included in all copies or substantial portions of the Software.
++ *
++ * THE SOFTWARE IS PROVIDED , WITHOUT WARRANTY OF ANY KIND
++ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
++ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
++ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
++ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY
++ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
++ * OTHER DEALINGS IN THE SOFTWARE.
++ */
++/ {
++ soc {
++ internal-regs {
++ sdhci at d8000 {
++ bus-width = <4>;
++ no-1-8-v;
++ non-removable;
++ pinctrl-0 = <µsom_sdhci_pins>;
++ pinctrl-names = "default";
++ status = "okay";
++ wp-inverted;
++ };
++ };
++ };
++};
diff --git a/target/linux/mvebu/patches-6.1/313-helios4-dts-status-led-alias.patch b/target/linux/mvebu/patches-6.1/313-helios4-dts-status-led-alias.patch
new file mode 100644
index 0000000000..607f436297
--- /dev/null
+++ b/target/linux/mvebu/patches-6.1/313-helios4-dts-status-led-alias.patch
@@ -0,0 +1,28 @@
+--- a/arch/arm/boot/dts/armada-388-helios4.dts
++++ b/arch/arm/boot/dts/armada-388-helios4.dts
+@@ -15,6 +15,13 @@
+ model = "Helios4";
+ compatible = "kobol,helios4", "marvell,armada388",
+ "marvell,armada385", "marvell,armada380";
++
++ aliases {
++ led-boot = &led_status;
++ led-failsafe = &led_status;
++ led-running = &led_status;
++ led-upgrade = &led_status;
++ };
+
+ memory {
+ device_type = "memory";
+@@ -73,10 +80,9 @@
+ pinctrl-names = "default";
+ pinctrl-0 = <&helios_system_led_pins>;
+
+- status-led {
++ led_status: status-led {
+ label = "helios4:green:status";
+ gpios = <&gpio0 24 GPIO_ACTIVE_LOW>;
+- linux,default-trigger = "heartbeat";
+ default-state = "on";
+ };
+
diff --git a/target/linux/mvebu/patches-6.1/315-armada-xp-linksys-mamba-resize-kernel.patch b/target/linux/mvebu/patches-6.1/315-armada-xp-linksys-mamba-resize-kernel.patch
new file mode 100644
index 0000000000..f1fddceff4
--- /dev/null
+++ b/target/linux/mvebu/patches-6.1/315-armada-xp-linksys-mamba-resize-kernel.patch
@@ -0,0 +1,37 @@
+From 258233f00bcd013050efee00c5d9128ef8cd62dd Mon Sep 17 00:00:00 2001
+From: Tad <tad at spotco.us>
+Date: Fri, 5 Feb 2021 22:32:11 -0500
+Subject: [PATCH] ARM: dts: armada-xp-linksys-mamba: Increase kernel
+ partition to 4MB
+
+Signed-off-by: Tad Davanzo <tad at spotco.us>
+---
+ arch/arm/boot/dts/armada-xp-linksys-mamba.dts | 8 ++++----
+ 1 file changed, 4 insertions(+), 4 deletions(-)
+
+--- a/arch/arm/boot/dts/armada-xp-linksys-mamba.dts
++++ b/arch/arm/boot/dts/armada-xp-linksys-mamba.dts
+@@ -456,9 +456,9 @@
+ reg = <0xa00000 0x2800000>; /* 40MB */
+ };
+
+- partition at d00000 {
++ partition at e00000 {
+ label = "rootfs1";
+- reg = <0xd00000 0x2500000>; /* 37MB */
++ reg = <0xe00000 0x2400000>; /* 36MB */
+ };
+
+ /* kernel2 overlaps with rootfs2 by design */
+@@ -467,9 +467,9 @@
+ reg = <0x3200000 0x2800000>; /* 40MB */
+ };
+
+- partition at 3500000 {
++ partition at 3600000 {
+ label = "rootfs2";
+- reg = <0x3500000 0x2500000>; /* 37MB */
++ reg = <0x3600000 0x2400000>; /* 36MB */
+ };
+
+ /*
diff --git a/target/linux/mvebu/patches-6.1/316-armada-370-dts-fix-crypto-engine.patch b/target/linux/mvebu/patches-6.1/316-armada-370-dts-fix-crypto-engine.patch
new file mode 100644
index 0000000000..19378870ef
--- /dev/null
+++ b/target/linux/mvebu/patches-6.1/316-armada-370-dts-fix-crypto-engine.patch
@@ -0,0 +1,29 @@
+--- a/arch/arm/boot/dts/armada-370.dtsi
++++ b/arch/arm/boot/dts/armada-370.dtsi
+@@ -234,7 +234,7 @@
+ clocks = <&gateclk 23>;
+ clock-names = "cesa0";
+ marvell,crypto-srams = <&crypto_sram>;
+- marvell,crypto-sram-size = <0x7e0>;
++ marvell,crypto-sram-size = <0x800>;
+ };
+ };
+
+@@ -255,12 +255,17 @@
+ * cpuidle workaround.
+ */
+ idle-sram at 0 {
++ status = "disabled";
+ reg = <0x0 0x20>;
+ };
+ };
+ };
+ };
+
++&coherencyfab {
++ broken-idle;
++};
++
+ /*
+ * Default UART pinctrl setting without RTS/CTS, can be overwritten on
+ * board level if a different configuration is used.
diff --git a/target/linux/mvebu/patches-6.1/400-find_active_root.patch b/target/linux/mvebu/patches-6.1/400-find_active_root.patch
new file mode 100644
index 0000000000..5582d20c68
--- /dev/null
+++ b/target/linux/mvebu/patches-6.1/400-find_active_root.patch
@@ -0,0 +1,60 @@
+The WRT1900AC among other Linksys routers uses a dual-firmware layout.
+Dynamically rename the active partition to "ubi".
+
+Signed-off-by: Imre Kaloz <kaloz at openwrt.org>
+
+--- a/drivers/mtd/parsers/ofpart_core.c
++++ b/drivers/mtd/parsers/ofpart_core.c
+@@ -38,6 +38,8 @@ static bool node_has_compatible(struct d
+ return of_get_property(pp, "compatible", NULL);
+ }
+
++static int mangled_rootblock;
++
+ static int parse_fixed_partitions(struct mtd_info *master,
+ const struct mtd_partition **pparts,
+ struct mtd_part_parser_data *data)
+@@ -48,6 +50,7 @@ static int parse_fixed_partitions(struct
+ struct device_node *mtd_node;
+ struct device_node *ofpart_node;
+ const char *partname;
++ const char *owrtpart = "ubi";
+ struct device_node *pp;
+ int nr_parts, i, ret = 0;
+ bool dedicated = true;
+@@ -133,9 +136,13 @@ static int parse_fixed_partitions(struct
+ parts[i].size = of_read_number(reg + a_cells, s_cells);
+ parts[i].of_node = pp;
+
+- partname = of_get_property(pp, "label", &len);
+- if (!partname)
+- partname = of_get_property(pp, "name", &len);
++ if (mangled_rootblock && (i == mangled_rootblock)) {
++ partname = owrtpart;
++ } else {
++ partname = of_get_property(pp, "label", &len);
++ if (!partname)
++ partname = of_get_property(pp, "name", &len);
++ }
+ parts[i].name = partname;
+
+ if (of_get_property(pp, "read-only", &len))
+@@ -252,6 +259,18 @@ static int __init ofpart_parser_init(voi
+ return 0;
+ }
+
++static int __init active_root(char *str)
++{
++ get_option(&str, &mangled_rootblock);
++
++ if (!mangled_rootblock)
++ return 1;
++
++ return 1;
++}
++
++__setup("mangled_rootblock=", active_root);
++
+ static void __exit ofpart_parser_exit(void)
+ {
+ deregister_mtd_parser(&ofpart_parser);
diff --git a/target/linux/mvebu/patches-6.1/700-mvneta-tx-queue-workaround.patch b/target/linux/mvebu/patches-6.1/700-mvneta-tx-queue-workaround.patch
new file mode 100644
index 0000000000..32e8ef4b7d
--- /dev/null
+++ b/target/linux/mvebu/patches-6.1/700-mvneta-tx-queue-workaround.patch
@@ -0,0 +1,38 @@
+The hardware queue scheduling is apparently configured with fixed
+priorities, which creates a nasty fairness issue where traffic from one
+CPU can starve traffic from all other CPUs.
+
+Work around this issue by forcing all tx packets to go through one CPU,
+until this issue is fixed properly.
+
+Signed-off-by: Felix Fietkau <nbd at nbd.name>
+---
+--- a/drivers/net/ethernet/marvell/mvneta.c
++++ b/drivers/net/ethernet/marvell/mvneta.c
+@@ -5006,6 +5006,16 @@ static int mvneta_setup_tc(struct net_de
+ }
+ }
+
++#ifndef CONFIG_ARM64
++static u16 mvneta_select_queue(struct net_device *dev, struct sk_buff *skb,
++ struct net_device *sb_dev)
++{
++ /* XXX: hardware queue scheduling is broken,
++ * use only one queue until it is fixed */
++ return 0;
++}
++#endif
++
+ static const struct net_device_ops mvneta_netdev_ops = {
+ .ndo_open = mvneta_open,
+ .ndo_stop = mvneta_stop,
+@@ -5016,6 +5026,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,
++#ifndef CONFIG_ARM64
++ .ndo_select_queue = mvneta_select_queue,
++#endif
+ .ndo_bpf = mvneta_xdp,
+ .ndo_xdp_xmit = mvneta_xdp_xmit,
+ .ndo_setup_tc = mvneta_setup_tc,
diff --git a/target/linux/mvebu/patches-6.1/702-net-next-ethernet-marvell-mvnetaMQPrioOffload.patch b/target/linux/mvebu/patches-6.1/702-net-next-ethernet-marvell-mvnetaMQPrioOffload.patch
new file mode 100644
index 0000000000..019b9528c3
--- /dev/null
+++ b/target/linux/mvebu/patches-6.1/702-net-next-ethernet-marvell-mvnetaMQPrioOffload.patch
@@ -0,0 +1,66 @@
+From 75fa71e3acadbb4ab5eda18505277eb9a1f69b23 Mon Sep 17 00:00:00 2001
+From: Maxime Chevallier <maxime.chevallier at bootlin.com>
+Date: Fri, 26 Nov 2021 12:20:53 +0100
+Subject: net: mvneta: Use struct tc_mqprio_qopt_offload for MQPrio
+ configuration
+
+The struct tc_mqprio_qopt_offload is a container for struct tc_mqprio_qopt,
+that allows passing extra parameters, such as traffic shaping. This commit
+converts the current mqprio code to that new struct.
+
+Signed-off-by: Maxime Chevallier <maxime.chevallier at bootlin.com>
+Signed-off-by: David S. Miller <davem at davemloft.net>
+---
+ drivers/net/ethernet/marvell/mvneta.c | 17 ++++++++++-------
+ 1 file changed, 10 insertions(+), 7 deletions(-)
+
+(limited to 'drivers/net/ethernet/marvell/mvneta.c')
+
+--- a/drivers/net/ethernet/marvell/mvneta.c
++++ b/drivers/net/ethernet/marvell/mvneta.c
+@@ -38,6 +38,7 @@
+ #include <net/ipv6.h>
+ #include <net/tso.h>
+ #include <net/page_pool.h>
++#include <net/pkt_cls.h>
+ #include <linux/bpf_trace.h>
+
+ /* Registers */
+@@ -4966,14 +4967,14 @@ static void mvneta_setup_rx_prio_map(str
+ }
+
+ static int mvneta_setup_mqprio(struct net_device *dev,
+- struct tc_mqprio_qopt *qopt)
++ struct tc_mqprio_qopt_offload *mqprio)
+ {
+ struct mvneta_port *pp = netdev_priv(dev);
+ u8 num_tc;
+ int i;
+
+- qopt->hw = TC_MQPRIO_HW_OFFLOAD_TCS;
+- num_tc = qopt->num_tc;
++ mqprio->qopt.hw = TC_MQPRIO_HW_OFFLOAD_TCS;
++ num_tc = mqprio->qopt.num_tc;
+
+ if (num_tc > rxq_number)
+ return -EINVAL;
+@@ -4984,13 +4985,15 @@ static int mvneta_setup_mqprio(struct ne
+ return 0;
+ }
+
+- memcpy(pp->prio_tc_map, qopt->prio_tc_map, sizeof(pp->prio_tc_map));
++ memcpy(pp->prio_tc_map, mqprio->qopt.prio_tc_map,
++ sizeof(pp->prio_tc_map));
+
+ mvneta_setup_rx_prio_map(pp);
+
+- netdev_set_num_tc(dev, qopt->num_tc);
+- for (i = 0; i < qopt->num_tc; i++)
+- netdev_set_tc_queue(dev, i, qopt->count[i], qopt->offset[i]);
++ netdev_set_num_tc(dev, mqprio->qopt.num_tc);
++ for (i = 0; i < mqprio->qopt.num_tc; i++)
++ netdev_set_tc_queue(dev, i, mqprio->qopt.count[i],
++ mqprio->qopt.offset[i]);
+
+ return 0;
+ }
diff --git a/target/linux/mvebu/patches-6.1/703-net-next-ethernet-marvell-mvnetaMQPrioFlag.patch b/target/linux/mvebu/patches-6.1/703-net-next-ethernet-marvell-mvnetaMQPrioFlag.patch
new file mode 100644
index 0000000000..c878a28843
--- /dev/null
+++ b/target/linux/mvebu/patches-6.1/703-net-next-ethernet-marvell-mvnetaMQPrioFlag.patch
@@ -0,0 +1,30 @@
+From e7ca75fe6662f78bfeb0112671c812e4c7b8e214 Mon Sep 17 00:00:00 2001
+From: Maxime Chevallier <maxime.chevallier at bootlin.com>
+Date: Fri, 26 Nov 2021 12:20:54 +0100
+Subject: net: mvneta: Don't force-set the offloading flag
+
+The qopt->hw flag is set by the TC code according to the offloading mode
+asked by user. Don't force-set it in the driver, but instead read it to
+make sure we do what's asked.
+
+Signed-off-by: Maxime Chevallier <maxime.chevallier at bootlin.com>
+Signed-off-by: David S. Miller <davem at davemloft.net>
+---
+ drivers/net/ethernet/marvell/mvneta.c | 4 +++-
+ 1 file changed, 3 insertions(+), 1 deletion(-)
+
+(limited to 'drivers/net/ethernet/marvell/mvneta.c')
+
+--- a/drivers/net/ethernet/marvell/mvneta.c
++++ b/drivers/net/ethernet/marvell/mvneta.c
+@@ -4973,7 +4973,9 @@ static int mvneta_setup_mqprio(struct ne
+ u8 num_tc;
+ int i;
+
+- mqprio->qopt.hw = TC_MQPRIO_HW_OFFLOAD_TCS;
++ if (mqprio->qopt.hw != TC_MQPRIO_HW_OFFLOAD_TCS)
++ return 0;
++
+ num_tc = mqprio->qopt.num_tc;
+
+ if (num_tc > rxq_number)
diff --git a/target/linux/mvebu/patches-6.1/704-net-next-ethernet-marvell-mvnetaMQPrioQueue.patch b/target/linux/mvebu/patches-6.1/704-net-next-ethernet-marvell-mvnetaMQPrioQueue.patch
new file mode 100644
index 0000000000..546a8486ef
--- /dev/null
+++ b/target/linux/mvebu/patches-6.1/704-net-next-ethernet-marvell-mvnetaMQPrioQueue.patch
@@ -0,0 +1,97 @@
+From e9f7099d0730341b24c057acbf545dd019581db6 Mon Sep 17 00:00:00 2001
+From: Maxime Chevallier <maxime.chevallier at bootlin.com>
+Date: Fri, 26 Nov 2021 12:20:55 +0100
+Subject: net: mvneta: Allow having more than one queue per TC
+
+The current mqprio implementation assumed that we are only using one
+queue per TC. Use the offset and count parameters to allow using
+multiple queues per TC. In that case, the controller will use a standard
+round-robin algorithm to pick queues assigned to the same TC, with the
+same priority.
+
+This only applies to VLAN priorities in ingress traffic, each TC
+corresponding to a vlan priority.
+
+Signed-off-by: Maxime Chevallier <maxime.chevallier at bootlin.com>
+Signed-off-by: David S. Miller <davem at davemloft.net>
+---
+ drivers/net/ethernet/marvell/mvneta.c | 35 ++++++++++++++++++++---------------
+ 1 file changed, 20 insertions(+), 15 deletions(-)
+
+(limited to 'drivers/net/ethernet/marvell/mvneta.c')
+
+--- a/drivers/net/ethernet/marvell/mvneta.c
++++ b/drivers/net/ethernet/marvell/mvneta.c
+@@ -498,7 +498,6 @@ struct mvneta_port {
+ u8 mcast_count[256];
+ u16 tx_ring_size;
+ u16 rx_ring_size;
+- u8 prio_tc_map[8];
+
+ phy_interface_t phy_interface;
+ struct device_node *dn;
+@@ -4955,13 +4954,12 @@ static void mvneta_clear_rx_prio_map(str
+ mvreg_write(pp, MVNETA_VLAN_PRIO_TO_RXQ, 0);
+ }
+
+-static void mvneta_setup_rx_prio_map(struct mvneta_port *pp)
++static void mvneta_map_vlan_prio_to_rxq(struct mvneta_port *pp, u8 pri, u8 rxq)
+ {
+- u32 val = 0;
+- int i;
++ u32 val = mvreg_read(pp, MVNETA_VLAN_PRIO_TO_RXQ);
+
+- for (i = 0; i < rxq_number; i++)
+- val |= MVNETA_VLAN_PRIO_RXQ_MAP(i, pp->prio_tc_map[i]);
++ val &= ~MVNETA_VLAN_PRIO_RXQ_MAP(pri, 0x7);
++ val |= MVNETA_VLAN_PRIO_RXQ_MAP(pri, rxq);
+
+ mvreg_write(pp, MVNETA_VLAN_PRIO_TO_RXQ, val);
+ }
+@@ -4970,8 +4968,8 @@ static int mvneta_setup_mqprio(struct ne
+ struct tc_mqprio_qopt_offload *mqprio)
+ {
+ struct mvneta_port *pp = netdev_priv(dev);
++ int rxq, tc;
+ u8 num_tc;
+- int i;
+
+ if (mqprio->qopt.hw != TC_MQPRIO_HW_OFFLOAD_TCS)
+ return 0;
+@@ -4981,21 +4979,28 @@ static int mvneta_setup_mqprio(struct ne
+ if (num_tc > rxq_number)
+ return -EINVAL;
+
++ mvneta_clear_rx_prio_map(pp);
++
+ if (!num_tc) {
+- mvneta_clear_rx_prio_map(pp);
+ netdev_reset_tc(dev);
+ return 0;
+ }
+
+- memcpy(pp->prio_tc_map, mqprio->qopt.prio_tc_map,
+- sizeof(pp->prio_tc_map));
++ netdev_set_num_tc(dev, mqprio->qopt.num_tc);
+
+- mvneta_setup_rx_prio_map(pp);
++ for (tc = 0; tc < mqprio->qopt.num_tc; tc++) {
++ netdev_set_tc_queue(dev, tc, mqprio->qopt.count[tc],
++ mqprio->qopt.offset[tc]);
++
++ for (rxq = mqprio->qopt.offset[tc];
++ rxq < mqprio->qopt.count[tc] + mqprio->qopt.offset[tc];
++ rxq++) {
++ if (rxq >= rxq_number)
++ return -EINVAL;
+
+- netdev_set_num_tc(dev, mqprio->qopt.num_tc);
+- for (i = 0; i < mqprio->qopt.num_tc; i++)
+- netdev_set_tc_queue(dev, i, mqprio->qopt.count[i],
+- mqprio->qopt.offset[i]);
++ mvneta_map_vlan_prio_to_rxq(pp, tc, rxq);
++ }
++ }
+
+ return 0;
+ }
diff --git a/target/linux/mvebu/patches-6.1/705-net-next-ethernet-marvell-mvnetaMQPrioTCOffload.patch b/target/linux/mvebu/patches-6.1/705-net-next-ethernet-marvell-mvnetaMQPrioTCOffload.patch
new file mode 100644
index 0000000000..1d4a055a71
--- /dev/null
+++ b/target/linux/mvebu/patches-6.1/705-net-next-ethernet-marvell-mvnetaMQPrioTCOffload.patch
@@ -0,0 +1,182 @@
+From 2551dc9e398c37a15e52122d385c29a8b06be45f Mon Sep 17 00:00:00 2001
+From: Maxime Chevallier <maxime.chevallier at bootlin.com>
+Date: Fri, 26 Nov 2021 12:20:56 +0100
+Subject: net: mvneta: Add TC traffic shaping offload
+
+The mvneta controller is able to do some tocken-bucket per-queue traffic
+shaping. This commit adds support for setting these using the TC mqprio
+interface.
+
+The token-bucket parameters are customisable, but the current
+implementation configures them to have a 10kbps resolution for the
+rate limitation, since it allows to cover the whole range of max_rate
+values from 10kbps to 5Gbps with 10kbps increments.
+
+Signed-off-by: Maxime Chevallier <maxime.chevallier at bootlin.com>
+Signed-off-by: David S. Miller <davem at davemloft.net>
+---
+ drivers/net/ethernet/marvell/mvneta.c | 120 +++++++++++++++++++++++++++++++++-
+ 1 file changed, 119 insertions(+), 1 deletion(-)
+
+(limited to 'drivers/net/ethernet/marvell/mvneta.c')
+
+--- a/drivers/net/ethernet/marvell/mvneta.c
++++ b/drivers/net/ethernet/marvell/mvneta.c
+@@ -248,12 +248,39 @@
+ #define MVNETA_TXQ_SENT_DESC_MASK 0x3fff0000
+ #define MVNETA_PORT_TX_RESET 0x3cf0
+ #define MVNETA_PORT_TX_DMA_RESET BIT(0)
++#define MVNETA_TXQ_CMD1_REG 0x3e00
++#define MVNETA_TXQ_CMD1_BW_LIM_SEL_V1 BIT(3)
++#define MVNETA_TXQ_CMD1_BW_LIM_EN BIT(0)
++#define MVNETA_REFILL_NUM_CLK_REG 0x3e08
++#define MVNETA_REFILL_MAX_NUM_CLK 0x0000ffff
+ #define MVNETA_TX_MTU 0x3e0c
+ #define MVNETA_TX_TOKEN_SIZE 0x3e14
+ #define MVNETA_TX_TOKEN_SIZE_MAX 0xffffffff
++#define MVNETA_TXQ_BUCKET_REFILL_REG(q) (0x3e20 + ((q) << 2))
++#define MVNETA_TXQ_BUCKET_REFILL_PERIOD_MASK 0x3ff00000
++#define MVNETA_TXQ_BUCKET_REFILL_PERIOD_SHIFT 20
++#define MVNETA_TXQ_BUCKET_REFILL_VALUE_MAX 0x0007ffff
+ #define MVNETA_TXQ_TOKEN_SIZE_REG(q) (0x3e40 + ((q) << 2))
+ #define MVNETA_TXQ_TOKEN_SIZE_MAX 0x7fffffff
+
++/* The values of the bucket refill base period and refill period are taken from
++ * the reference manual, and adds up to a base resolution of 10Kbps. This allows
++ * to cover all rate-limit values from 10Kbps up to 5Gbps
++ */
++
++/* Base period for the rate limit algorithm */
++#define MVNETA_TXQ_BUCKET_REFILL_BASE_PERIOD_NS 100
++
++/* Number of Base Period to wait between each bucket refill */
++#define MVNETA_TXQ_BUCKET_REFILL_PERIOD 1000
++
++/* The base resolution for rate limiting, in bps. Any max_rate value should be
++ * a multiple of that value.
++ */
++#define MVNETA_TXQ_RATE_LIMIT_RESOLUTION (NSEC_PER_SEC / \
++ (MVNETA_TXQ_BUCKET_REFILL_BASE_PERIOD_NS * \
++ MVNETA_TXQ_BUCKET_REFILL_PERIOD))
++
+ #define MVNETA_LPI_CTRL_0 0x2cc0
+ #define MVNETA_LPI_CTRL_1 0x2cc4
+ #define MVNETA_LPI_REQUEST_ENABLE BIT(0)
+@@ -4964,11 +4991,74 @@ static void mvneta_map_vlan_prio_to_rxq(
+ mvreg_write(pp, MVNETA_VLAN_PRIO_TO_RXQ, val);
+ }
+
++static int mvneta_enable_per_queue_rate_limit(struct mvneta_port *pp)
++{
++ unsigned long core_clk_rate;
++ u32 refill_cycles;
++ u32 val;
++
++ core_clk_rate = clk_get_rate(pp->clk);
++ if (!core_clk_rate)
++ return -EINVAL;
++
++ refill_cycles = MVNETA_TXQ_BUCKET_REFILL_BASE_PERIOD_NS /
++ (NSEC_PER_SEC / core_clk_rate);
++
++ if (refill_cycles > MVNETA_REFILL_MAX_NUM_CLK)
++ return -EINVAL;
++
++ /* Enable bw limit algorithm version 3 */
++ val = mvreg_read(pp, MVNETA_TXQ_CMD1_REG);
++ val &= ~(MVNETA_TXQ_CMD1_BW_LIM_SEL_V1 | MVNETA_TXQ_CMD1_BW_LIM_EN);
++ mvreg_write(pp, MVNETA_TXQ_CMD1_REG, val);
++
++ /* Set the base refill rate */
++ mvreg_write(pp, MVNETA_REFILL_NUM_CLK_REG, refill_cycles);
++
++ return 0;
++}
++
++static void mvneta_disable_per_queue_rate_limit(struct mvneta_port *pp)
++{
++ u32 val = mvreg_read(pp, MVNETA_TXQ_CMD1_REG);
++
++ val |= (MVNETA_TXQ_CMD1_BW_LIM_SEL_V1 | MVNETA_TXQ_CMD1_BW_LIM_EN);
++ mvreg_write(pp, MVNETA_TXQ_CMD1_REG, val);
++}
++
++static int mvneta_setup_queue_rates(struct mvneta_port *pp, int queue,
++ u64 min_rate, u64 max_rate)
++{
++ u32 refill_val, rem;
++ u32 val = 0;
++
++ /* Convert to from Bps to bps */
++ max_rate *= 8;
++
++ if (min_rate)
++ return -EINVAL;
++
++ refill_val = div_u64_rem(max_rate, MVNETA_TXQ_RATE_LIMIT_RESOLUTION,
++ &rem);
++
++ if (rem || !refill_val ||
++ refill_val > MVNETA_TXQ_BUCKET_REFILL_VALUE_MAX)
++ return -EINVAL;
++
++ val = refill_val;
++ val |= (MVNETA_TXQ_BUCKET_REFILL_PERIOD <<
++ MVNETA_TXQ_BUCKET_REFILL_PERIOD_SHIFT);
++
++ mvreg_write(pp, MVNETA_TXQ_BUCKET_REFILL_REG(queue), val);
++
++ return 0;
++}
++
+ static int mvneta_setup_mqprio(struct net_device *dev,
+ struct tc_mqprio_qopt_offload *mqprio)
+ {
+ struct mvneta_port *pp = netdev_priv(dev);
+- int rxq, tc;
++ int rxq, txq, tc, ret;
+ u8 num_tc;
+
+ if (mqprio->qopt.hw != TC_MQPRIO_HW_OFFLOAD_TCS)
+@@ -4982,6 +5072,7 @@ static int mvneta_setup_mqprio(struct ne
+ mvneta_clear_rx_prio_map(pp);
+
+ if (!num_tc) {
++ mvneta_disable_per_queue_rate_limit(pp);
+ netdev_reset_tc(dev);
+ return 0;
+ }
+@@ -5002,6 +5093,33 @@ static int mvneta_setup_mqprio(struct ne
+ }
+ }
+
++ if (mqprio->shaper != TC_MQPRIO_SHAPER_BW_RATE) {
++ mvneta_disable_per_queue_rate_limit(pp);
++ return 0;
++ }
++
++ if (mqprio->qopt.num_tc > txq_number)
++ return -EINVAL;
++
++ ret = mvneta_enable_per_queue_rate_limit(pp);
++ if (ret)
++ return ret;
++
++ for (tc = 0; tc < mqprio->qopt.num_tc; tc++) {
++ for (txq = mqprio->qopt.offset[tc];
++ txq < mqprio->qopt.count[tc] + mqprio->qopt.offset[tc];
++ txq++) {
++ if (txq >= txq_number)
++ return -EINVAL;
++
++ ret = mvneta_setup_queue_rates(pp, txq,
++ mqprio->min_rate[tc],
++ mqprio->max_rate[tc]);
++ if (ret)
++ return ret;
++ }
++ }
++
+ return 0;
+ }
+
diff --git a/target/linux/mvebu/patches-6.1/710-v6.2-phy-marvell-phy-mvebu-a3700-comphy-Reset-COMPHY-regi.patch b/target/linux/mvebu/patches-6.1/710-v6.2-phy-marvell-phy-mvebu-a3700-comphy-Reset-COMPHY-regi.patch
new file mode 100644
index 0000000000..a852dc1fc1
--- /dev/null
+++ b/target/linux/mvebu/patches-6.1/710-v6.2-phy-marvell-phy-mvebu-a3700-comphy-Reset-COMPHY-regi.patch
@@ -0,0 +1,50 @@
+From b01d622d76134e9401970ffd3fbbb9a7051f976a Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali at kernel.org>
+Date: Tue, 20 Sep 2022 14:11:54 +0200
+Subject: [PATCH] phy: marvell: phy-mvebu-a3700-comphy: Reset COMPHY registers
+ before USB 3.0 power on
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Turris MOX board with older ARM Trusted Firmware version v1.5 is not able
+to detect any USB 3.0 device connected to USB-A port on Mox-A module after
+commit 0a6fc70d76bd ("phy: marvell: phy-mvebu-a3700-comphy: Remove broken
+reset support"). On the other hand USB 2.0 devices connected to the same
+USB-A port are working fine.
+
+It looks as if the older firmware configures COMPHY registers for USB 3.0
+somehow incompatibly for kernel driver. Experiments show that resetting
+COMPHY registers via setting SFT_RST auto-clearing bit in COMPHY_SFT_RESET
+register fixes this issue.
+
+Reset the COMPHY in mvebu_a3700_comphy_usb3_power_on() function as a first
+step after selecting COMPHY lane and USB 3.0 function. With this change
+Turris MOX board can successfully detect USB 3.0 devices again.
+
+Before the above mentioned commit this reset was implemented in PHY reset
+method, so this is the reason why there was no issue with older firmware
+version then.
+
+Fixes: 0a6fc70d76bd ("phy: marvell: phy-mvebu-a3700-comphy: Remove broken reset support")
+Reported-by: Marek Behún <kabel at kernel.org>
+Signed-off-by: Pali Rohár <pali at kernel.org>
+Tested-by: Shin'ichiro Kawasaki <shinichiro.kawasaki at wdc.com>
+Link: https://lore.kernel.org/r/20220920121154.30115-1-pali@kernel.org
+Signed-off-by: Vinod Koul <vkoul at kernel.org>
+---
+ drivers/phy/marvell/phy-mvebu-a3700-comphy.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+--- a/drivers/phy/marvell/phy-mvebu-a3700-comphy.c
++++ b/drivers/phy/marvell/phy-mvebu-a3700-comphy.c
+@@ -826,6 +826,9 @@ mvebu_a3700_comphy_usb3_power_on(struct
+ if (ret)
+ return ret;
+
++ /* COMPHY register reset (cleared automatically) */
++ comphy_lane_reg_set(lane, COMPHY_SFT_RESET, SFT_RST, SFT_RST);
++
+ /*
+ * 0. Set PHY OTG Control(0x5d034), bit 4, Power up OTG module The
+ * register belong to UTMI module, so it is set in UTMI phy driver.
diff --git a/target/linux/mvebu/patches-6.1/800-cpuidle-mvebu-indicate-failure-to-enter-deeper-sleep.patch b/target/linux/mvebu/patches-6.1/800-cpuidle-mvebu-indicate-failure-to-enter-deeper-sleep.patch
new file mode 100644
index 0000000000..29f36be460
--- /dev/null
+++ b/target/linux/mvebu/patches-6.1/800-cpuidle-mvebu-indicate-failure-to-enter-deeper-sleep.patch
@@ -0,0 +1,40 @@
+From c28b2d367da8a471482e6a4aa8337ab6369a80c2 Mon Sep 17 00:00:00 2001
+From: Russell King <rmk+kernel at arm.linux.org.uk>
+Date: Sat, 3 Oct 2015 09:13:05 +0100
+Subject: cpuidle: mvebu: indicate failure to enter deeper sleep states
+
+The cpuidle ->enter method expects the return value to be the sleep
+state we entered. Returning negative numbers or other codes is not
+permissible since coupled CPU idle was merged.
+
+At least some of the mvebu_v7_cpu_suspend() implementations return the
+value from cpu_suspend(), which returns zero if the CPU vectors back
+into the kernel via cpu_resume() (the success case), or the non-zero
+return value of the suspend actor, or one (failure cases).
+
+We do not want to be returning the failure case value back to CPU idle
+as that indicates that we successfully entered one of the deeper idle
+states. Always return zero instead, indicating that we slept for the
+shortest amount of time.
+
+Signed-off-by: Russell King <rmk+kernel at arm.linux.org.uk>
+---
+ drivers/cpuidle/cpuidle-mvebu-v7.c | 6 +++++-
+ 1 file changed, 5 insertions(+), 1 deletion(-)
+
+--- a/drivers/cpuidle/cpuidle-mvebu-v7.c
++++ b/drivers/cpuidle/cpuidle-mvebu-v7.c
+@@ -39,8 +39,12 @@ static int mvebu_v7_enter_idle(struct cp
+ ret = mvebu_v7_cpu_suspend(deepidle);
+ cpu_pm_exit();
+
++ /*
++ * If we failed to enter the desired state, indicate that we
++ * slept lightly.
++ */
+ if (ret)
+- return ret;
++ return 0;
+
+ return index;
+ }
diff --git a/target/linux/mvebu/patches-6.1/801-pci-mvebu-time-out-reset-on-link-up.patch b/target/linux/mvebu/patches-6.1/801-pci-mvebu-time-out-reset-on-link-up.patch
new file mode 100644
index 0000000000..dba1253699
--- /dev/null
+++ b/target/linux/mvebu/patches-6.1/801-pci-mvebu-time-out-reset-on-link-up.patch
@@ -0,0 +1,60 @@
+From 287b9df160b6159f8d385424904f8bac501280c1 Mon Sep 17 00:00:00 2001
+From: Russell King <rmk+kernel at armlinux.org.uk>
+Date: Sat, 9 Jul 2016 10:58:16 +0100
+Subject: pci: mvebu: time out reset on link up
+
+If the port reports that the link is up while we are resetting, there's
+little point in waiting for the full duration.
+
+Signed-off-by: Russell King <rmk+kernel at armlinux.org.uk>
+---
+ drivers/pci/controller/pci-mvebu.c | 20 ++++++++++++++------
+ 1 file changed, 14 insertions(+), 6 deletions(-)
+
+--- a/drivers/pci/controller/pci-mvebu.c
++++ b/drivers/pci/controller/pci-mvebu.c
+@@ -1023,6 +1023,7 @@ static int mvebu_pcie_powerup(struct mve
+
+ if (port->reset_gpio) {
+ u32 reset_udelay = PCI_PM_D3COLD_WAIT * 1000;
++ unsigned int i;
+
+ of_property_read_u32(port->dn, "reset-delay-us",
+ &reset_udelay);
+@@ -1030,7 +1031,13 @@ static int mvebu_pcie_powerup(struct mve
+ udelay(100);
+
+ gpiod_set_value_cansleep(port->reset_gpio, 0);
+- msleep(reset_udelay / 1000);
++ for (i = 0; i < reset_udelay; i += 1000) {
++ if (mvebu_pcie_link_up(port))
++ break;
++ msleep(1);
++ }
++
++ printk("%s: reset completed in %dus\n", port->name, i);
+ }
+
+ return 0;
+@@ -1190,15 +1197,16 @@ static int mvebu_pcie_probe(struct platf
+ if (!child)
+ continue;
+
+- ret = mvebu_pcie_powerup(port);
+- if (ret < 0)
+- continue;
+-
+ port->base = mvebu_pcie_map_registers(pdev, child, port);
+ if (IS_ERR(port->base)) {
+ dev_err(dev, "%s: cannot map registers\n", port->name);
+ port->base = NULL;
+- mvebu_pcie_powerdown(port);
++ continue;
++ }
++
++ ret = mvebu_pcie_powerup(port);
++ if (ret < 0) {
++ port->base = NULL;
+ continue;
+ }
+
diff --git a/target/linux/mvebu/patches-6.1/901-dt-bindings-Add-IEI-vendor-prefix-and-IEI-WT61P803-P.patch b/target/linux/mvebu/patches-6.1/901-dt-bindings-Add-IEI-vendor-prefix-and-IEI-WT61P803-P.patch
new file mode 100644
index 0000000000..3446086cad
--- /dev/null
+++ b/target/linux/mvebu/patches-6.1/901-dt-bindings-Add-IEI-vendor-prefix-and-IEI-WT61P803-P.patch
@@ -0,0 +1,218 @@
+From aa4a0ccc41997f2da172165c92803abace43bd1c Mon Sep 17 00:00:00 2001
+From: Luka Kovacic <luka.kovacic () sartura ! hr>
+Date: Tue, 24 Aug 2021 12:44:32 +0000
+Subject: [PATCH 1/7] dt-bindings: Add IEI vendor prefix and IEI WT61P803
+ PUZZLE driver bindings
+
+Add the IEI WT61P803 PUZZLE Device Tree bindings for MFD, HWMON and LED
+drivers. A new vendor prefix is also added accordingly for
+IEI Integration Corp.
+
+Signed-off-by: Luka Kovacic <luka.kovacic at sartura.hr>
+Signed-off-by: Pavo Banicevic <pavo.banicevic at sartura.hr>
+Cc: Luka Perkov <luka.perkov at sartura.hr>
+Cc: Robert Marko <robert.marko at sartura.hr>
+---
+ .../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(+)
+ 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
+
+--- /dev/null
++++ b/Documentation/devicetree/bindings/hwmon/iei,wt61p803-puzzle-hwmon.yaml
+@@ -0,0 +1,53 @@
++# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
++%YAML 1.2
++---
++$id: http://devicetree.org/schemas/hwmon/iei,wt61p803-puzzle-hwmon.yaml#
++$schema: http://devicetree.org/meta-schemas/core.yaml#
++
++title: IEI WT61P803 PUZZLE MCU HWMON module from IEI Integration Corp.
++
++maintainers:
++ - Luka Kovacic <luka.kovacic at sartura.hr>
++
++description: |
++ This module is a part of the IEI WT61P803 PUZZLE MFD device. For more details
++ see Documentation/devicetree/bindings/mfd/iei,wt61p803-puzzle.yaml.
++
++ The HWMON module is a sub-node of the MCU node in the Device Tree.
++
++properties:
++ compatible:
++ const: iei,wt61p803-puzzle-hwmon
++
++ "#address-cells":
++ const: 1
++
++ "#size-cells":
++ const: 0
++
++patternProperties:
++ "^fan-group@[0-1]$":
++ type: object
++ properties:
++ reg:
++ minimum: 0
++ maximum: 1
++ description:
++ Fan group ID
++
++ cooling-levels:
++ minItems: 1
++ maxItems: 255
++ description:
++ Cooling levels for the fans (PWM value mapping)
++ description: |
++ Properties for each fan group.
++ required:
++ - reg
++
++required:
++ - compatible
++ - "#address-cells"
++ - "#size-cells"
++
++additionalProperties: false
+--- /dev/null
++++ b/Documentation/devicetree/bindings/leds/iei,wt61p803-puzzle-leds.yaml
+@@ -0,0 +1,39 @@
++# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
++%YAML 1.2
++---
++$id: http://devicetree.org/schemas/leds/iei,wt61p803-puzzle-leds.yaml#
++$schema: http://devicetree.org/meta-schemas/core.yaml#
++
++title: IEI WT61P803 PUZZLE MCU LED module from IEI Integration Corp.
++
++maintainers:
++ - Luka Kovacic <luka.kovacic at sartura.hr>
++
++description: |
++ This module is a part of the IEI WT61P803 PUZZLE MFD device. For more details
++ see Documentation/devicetree/bindings/mfd/iei,wt61p803-puzzle.yaml.
++
++ The LED module is a sub-node of the MCU node in the Device Tree.
++
++properties:
++ compatible:
++ const: iei,wt61p803-puzzle-leds
++
++ "#address-cells":
++ const: 1
++
++ "#size-cells":
++ const: 0
++
++ led at 0:
++ type: object
++ $ref: common.yaml
++ description: |
++ Properties for a single LED.
++
++required:
++ - compatible
++ - "#address-cells"
++ - "#size-cells"
++
++additionalProperties: false
+--- /dev/null
++++ b/Documentation/devicetree/bindings/mfd/iei,wt61p803-puzzle.yaml
+@@ -0,0 +1,82 @@
++# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
++%YAML 1.2
++---
++$id: http://devicetree.org/schemas/mfd/iei,wt61p803-puzzle.yaml#
++$schema: http://devicetree.org/meta-schemas/core.yaml#
++
++title: IEI WT61P803 PUZZLE MCU from IEI Integration Corp.
++
++maintainers:
++ - Luka Kovacic <luka.kovacic at sartura.hr>
++
++description: |
++ IEI WT61P803 PUZZLE MCU is embedded in some IEI Puzzle series boards.
++ It's used for controlling system power states, fans, LEDs and temperature
++ sensors.
++
++ For Device Tree bindings of other sub-modules (HWMON, LEDs) refer to the
++ binding documents under the respective subsystem directories.
++
++properties:
++ compatible:
++ const: iei,wt61p803-puzzle
++
++ current-speed:
++ description:
++ Serial bus speed in bps
++ maxItems: 1
++
++ enable-beep: true
++
++ hwmon:
++ $ref: /schemas/hwmon/iei,wt61p803-puzzle-hwmon.yaml
++
++ leds:
++ $ref: /schemas/leds/iei,wt61p803-puzzle-leds.yaml
++
++required:
++ - compatible
++ - current-speed
++
++additionalProperties: false
++
++examples:
++ - |
++ #include <dt-bindings/leds/common.h>
++ serial {
++ mcu {
++ compatible = "iei,wt61p803-puzzle";
++ current-speed = <115200>;
++ enable-beep;
++
++ leds {
++ compatible = "iei,wt61p803-puzzle-leds";
++ #address-cells = <1>;
++ #size-cells = <0>;
++
++ led at 0 {
++ reg = <0>;
++ function = LED_FUNCTION_POWER;
++ color = <LED_COLOR_ID_BLUE>;
++ };
++ };
++
++ hwmon {
++ compatible = "iei,wt61p803-puzzle-hwmon";
++ #address-cells = <1>;
++ #size-cells = <0>;
++
++ fan-group at 0 {
++ #cooling-cells = <2>;
++ reg = <0x00>;
++ cooling-levels = <64 102 170 230 250>;
++ };
++
++ fan-group at 1 {
++ #cooling-cells = <2>;
++ reg = <0x01>;
++ cooling-levels = <64 102 170 230 250>;
++ };
++ };
++ };
++ };
+--- a/Documentation/devicetree/bindings/vendor-prefixes.yaml
++++ b/Documentation/devicetree/bindings/vendor-prefixes.yaml
+@@ -519,6 +519,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.1/902-drivers-mfd-Add-a-driver-for-IEI-WT61P803-PUZZLE-MCU.patch b/target/linux/mvebu/patches-6.1/902-drivers-mfd-Add-a-driver-for-IEI-WT61P803-PUZZLE-MCU.patch
new file mode 100644
index 0000000000..0e546b426e
--- /dev/null
+++ b/target/linux/mvebu/patches-6.1/902-drivers-mfd-Add-a-driver-for-IEI-WT61P803-PUZZLE-MCU.patch
@@ -0,0 +1,1034 @@
+From 692cfa85272dd12995b427c0a7a585ced5d54f32 Mon Sep 17 00:00:00 2001
+From: Luka Kovacic <luka.kovacic () sartura ! hr>
+Date: Tue, 24 Aug 2021 12:44:33 +0000
+Subject: [PATCH 2/7] drivers: mfd: Add a driver for IEI WT61P803 PUZZLE MCU
+
+Add a driver for the IEI WT61P803 PUZZLE microcontroller, used in some
+IEI Puzzle series devices. The microcontroller controls system power,
+temperature sensors, fans and LEDs.
+
+This driver implements the core functionality for device communication
+over the system serial (serdev bus). It handles MCU messages and the
+internal MCU properties. Some properties can be managed over sysfs.
+
+Signed-off-by: Luka Kovacic <luka.kovacic at sartura.hr>
+Signed-off-by: Pavo Banicevic <pavo.banicevic at sartura.hr>
+Cc: Luka Perkov <luka.perkov at sartura.hr>
+Cc: Robert Marko <robert.marko at sartura.hr>
+---
+ drivers/mfd/Kconfig | 8 +
+ drivers/mfd/Makefile | 1 +
+ drivers/mfd/iei-wt61p803-puzzle.c | 908 ++++++++++++++++++++++++
+ include/linux/mfd/iei-wt61p803-puzzle.h | 66 ++
+ 4 files changed, 983 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
+@@ -2189,6 +2189,15 @@ config SGI_MFD_IOC3
+ If you have an SGI Origin, Octane, or a PCI IOC3 card,
+ then say Y. Otherwise say N.
+
++config MFD_IEI_WT61P803_PUZZLE
++ tristate "IEI WT61P803 PUZZLE MCU driver"
++ depends on SERIAL_DEV_BUS
++ select MFD_CORE
++ help
++ IEI WT61P803 PUZZLE is a system power management microcontroller
++ used for fan control, temperature sensor reading, LED control
++ and system identification.
++
+ config MFD_INTEL_M10_BMC
+ tristate "Intel MAX 10 Board Management Controller"
+ depends on SPI_MASTER
+--- a/drivers/mfd/Makefile
++++ b/drivers/mfd/Makefile
+@@ -237,6 +237,7 @@ obj-$(CONFIG_MFD_DLN2) += dln2.o
+ obj-$(CONFIG_MFD_RT4831) += rt4831.o
+ obj-$(CONFIG_MFD_RT5033) += rt5033.o
+ obj-$(CONFIG_MFD_SKY81452) += sky81452.o
++obj-$(CONFIG_MFD_IEI_WT61P803_PUZZLE) += iei-wt61p803-puzzle.o
+
+ intel-soc-pmic-objs := intel_soc_pmic_core.o intel_soc_pmic_crc.o
+ obj-$(CONFIG_INTEL_SOC_PMIC) += intel-soc-pmic.o
+--- /dev/null
++++ b/drivers/mfd/iei-wt61p803-puzzle.c
+@@ -0,0 +1,908 @@
++// SPDX-License-Identifier: GPL-2.0-only
++/* IEI WT61P803 PUZZLE MCU Driver
++ * System management microcontroller for fan control, temperature sensor reading,
++ * LED control and system identification on IEI Puzzle series ARM-based appliances.
++ *
++ * Copyright (C) 2020 Sartura Ltd.
++ * Author: Luka Kovacic <luka.kovacic at sartura.hr>
++ */
++
++#include <linux/atomic.h>
++#include <linux/delay.h>
++#include <linux/export.h>
++#include <linux/init.h>
++#include <linux/kernel.h>
++#include <linux/mfd/core.h>
++#include <linux/mfd/iei-wt61p803-puzzle.h>
++#include <linux/mod_devicetable.h>
++#include <linux/module.h>
++#include <linux/of_platform.h>
++#include <linux/property.h>
++#include <linux/sched.h>
++#include <linux/serdev.h>
++#include <linux/slab.h>
++#include <linux/sysfs.h>
++#include <asm/unaligned.h>
++
++/* start, payload and XOR checksum at end */
++#define IEI_WT61P803_PUZZLE_MAX_COMMAND_LENGTH (1 + 20 + 1)
++#define IEI_WT61P803_PUZZLE_RESP_BUF_SIZE 512
++
++#define IEI_WT61P803_PUZZLE_MAC_LENGTH 17
++#define IEI_WT61P803_PUZZLE_SN_LENGTH 36
++#define IEI_WT61P803_PUZZLE_VERSION_LENGTH 6
++#define IEI_WT61P803_PUZZLE_BUILD_INFO_LENGTH 16
++#define IEI_WT61P803_PUZZLE_PROTOCOL_VERSION_LENGTH 8
++#define IEI_WT61P803_PUZZLE_NB_MAC 8
++
++/* Use HZ as a timeout value throughout the driver */
++#define IEI_WT61P803_PUZZLE_GENERAL_TIMEOUT HZ
++
++enum iei_wt61p803_puzzle_attribute_type {
++ IEI_WT61P803_PUZZLE_VERSION,
++ IEI_WT61P803_PUZZLE_BUILD_INFO,
++ IEI_WT61P803_PUZZLE_BOOTLOADER_MODE,
++ IEI_WT61P803_PUZZLE_PROTOCOL_VERSION,
++ IEI_WT61P803_PUZZLE_SERIAL_NUMBER,
++ IEI_WT61P803_PUZZLE_MAC_ADDRESS,
++ IEI_WT61P803_PUZZLE_AC_RECOVERY_STATUS,
++ IEI_WT61P803_PUZZLE_POWER_LOSS_RECOVERY,
++ IEI_WT61P803_PUZZLE_POWER_STATUS,
++};
++
++struct iei_wt61p803_puzzle_device_attribute {
++ struct device_attribute dev_attr;
++ enum iei_wt61p803_puzzle_attribute_type type;
++ u8 index;
++};
++
++/**
++ * struct iei_wt61p803_puzzle_mcu_status - MCU flags state
++ * @ac_recovery_status_flag: AC Recovery Status Flag
++ * @power_loss_recovery: System recovery after power loss
++ * @power_status: System Power-on Method
++ */
++struct iei_wt61p803_puzzle_mcu_status {
++ u8 ac_recovery_status_flag;
++ u8 power_loss_recovery;
++ u8 power_status;
++};
++
++/**
++ * struct iei_wt61p803_puzzle_reply - MCU reply
++ * @size: Size of the MCU reply
++ * @data: Full MCU reply buffer
++ * @state: Current state of the packet
++ * @received: Was the response fullfilled
++ */
++struct iei_wt61p803_puzzle_reply {
++ size_t size;
++ unsigned char data[IEI_WT61P803_PUZZLE_RESP_BUF_SIZE];
++ struct completion received;
++};
++
++/**
++ * struct iei_wt61p803_puzzle_mcu_version - MCU version status
++ * @version: Primary firmware version
++ * @build_info: Build date and time
++ * @bootloader_mode: Status of the MCU operation
++ * @protocol_version: MCU communication protocol version
++ * @serial_number: Device factory serial number
++ * @mac_address: Device factory MAC addresses
++ *
++ * Last element of arrays is reserved for '\0'.
++ */
++struct iei_wt61p803_puzzle_mcu_version {
++ char version[IEI_WT61P803_PUZZLE_VERSION_LENGTH + 1];
++ char build_info[IEI_WT61P803_PUZZLE_BUILD_INFO_LENGTH + 1];
++ bool bootloader_mode;
++ char protocol_version[IEI_WT61P803_PUZZLE_PROTOCOL_VERSION_LENGTH + 1];
++ char serial_number[IEI_WT61P803_PUZZLE_SN_LENGTH + 1];
++ char mac_address[IEI_WT61P803_PUZZLE_NB_MAC][IEI_WT61P803_PUZZLE_MAC_LENGTH + 1];
++};
++
++/**
++ * struct iei_wt61p803_puzzle - IEI WT61P803 PUZZLE MCU Driver
++ * @serdev: Pointer to underlying serdev device
++ * @dev: Pointer to underlying dev device
++ * @reply_lock: Reply mutex lock
++ * @reply: Pointer to the iei_wt61p803_puzzle_reply struct
++ * @version: MCU version related data
++ * @status: MCU status related data
++ * @response_buffer Command response buffer allocation
++ * @lock General member mutex lock
++ */
++struct iei_wt61p803_puzzle {
++ struct serdev_device *serdev;
++ struct device *dev;
++ struct mutex reply_lock; /* lock to prevent multiple firmware calls */
++ struct iei_wt61p803_puzzle_reply *reply;
++ struct iei_wt61p803_puzzle_mcu_version version;
++ struct iei_wt61p803_puzzle_mcu_status status;
++ unsigned char response_buffer[IEI_WT61P803_PUZZLE_BUF_SIZE];
++ struct mutex lock; /* lock to protect response buffer */
++};
++
++static unsigned char iei_wt61p803_puzzle_checksum(unsigned char *buf, size_t len)
++{
++ unsigned char checksum = 0;
++ size_t i;
++
++ for (i = 0; i < len; i++)
++ checksum ^= buf[i];
++ return checksum;
++}
++
++static int iei_wt61p803_puzzle_process_resp(struct iei_wt61p803_puzzle *mcu,
++ const unsigned char *raw_resp_data, size_t size)
++{
++ unsigned char checksum;
++
++ /* Check the incoming frame header */
++ if (!(raw_resp_data[0] == IEI_WT61P803_PUZZLE_CMD_HEADER_START ||
++ raw_resp_data[0] == IEI_WT61P803_PUZZLE_CMD_HEADER_START_OTHER ||
++ (raw_resp_data[0] == IEI_WT61P803_PUZZLE_CMD_HEADER_EEPROM &&
++ raw_resp_data[1] == IEI_WT61P803_PUZZLE_CMD_EEPROM_READ))) {
++ if (mcu->reply->size + size >= sizeof(mcu->reply->data))
++ return -EIO;
++
++ /* Append the frame to existing data */
++ memcpy(mcu->reply->data + mcu->reply->size, raw_resp_data, size);
++ mcu->reply->size += size;
++ } else {
++ if (size >= sizeof(mcu->reply->data))
++ return -EIO;
++
++ /* Start processing a new frame */
++ memcpy(mcu->reply->data, raw_resp_data, size);
++ mcu->reply->size = size;
++ }
++
++ checksum = iei_wt61p803_puzzle_checksum(mcu->reply->data, mcu->reply->size - 1);
++ if (checksum != mcu->reply->data[mcu->reply->size - 1]) {
++ /* The checksum isn't matched yet, wait for new frames */
++ return size;
++ }
++
++ /* Received all the data */
++ complete(&mcu->reply->received);
++
++ return size;
++}
++
++static int iei_wt61p803_puzzle_recv_buf(struct serdev_device *serdev,
++ const unsigned char *data, size_t size)
++{
++ struct iei_wt61p803_puzzle *mcu = serdev_device_get_drvdata(serdev);
++ int ret;
++
++ ret = iei_wt61p803_puzzle_process_resp(mcu, data, size);
++ /* Return the number of processed bytes if function returns error,
++ * discard the remaining incoming data, since the frame this data
++ * belongs to is broken anyway
++ */
++ if (ret < 0)
++ return size;
++
++ return ret;
++}
++
++static const struct serdev_device_ops iei_wt61p803_puzzle_serdev_device_ops = {
++ .receive_buf = iei_wt61p803_puzzle_recv_buf,
++ .write_wakeup = serdev_device_write_wakeup,
++};
++
++/**
++ * iei_wt61p803_puzzle_write_command_watchdog() - Watchdog of the normal cmd
++ * @mcu: Pointer to the iei_wt61p803_puzzle core MFD struct
++ * @cmd: Pointer to the char array to send (size should be content + 1 (xor))
++ * @size: Size of the cmd char array
++ * @reply_data: Pointer to the reply/response data array (should be allocated)
++ * @reply_size: Pointer to size_t (size of reply_data)
++ * @retry_count: Number of times to retry sending the command to the MCU
++ */
++int iei_wt61p803_puzzle_write_command_watchdog(struct iei_wt61p803_puzzle *mcu,
++ unsigned char *cmd, size_t size,
++ unsigned char *reply_data,
++ size_t *reply_size, int retry_count)
++{
++ struct device *dev = &mcu->serdev->dev;
++ int ret, i;
++
++ for (i = 0; i < retry_count; i++) {
++ ret = iei_wt61p803_puzzle_write_command(mcu, cmd, size,
++ reply_data, reply_size);
++ if (ret != -ETIMEDOUT)
++ return ret;
++ }
++
++ dev_err(dev, "Command response timed out. Retries: %d\n", retry_count);
++
++ return -ETIMEDOUT;
++}
++EXPORT_SYMBOL_GPL(iei_wt61p803_puzzle_write_command_watchdog);
++
++/**
++ * iei_wt61p803_puzzle_write_command() - Send a structured command to the MCU
++ * @mcu: Pointer to the iei_wt61p803_puzzle core MFD struct
++ * @cmd: Pointer to the char array to send (size should be content + 1 (xor))
++ * @size: Size of the cmd char array
++ * @reply_data: Pointer to the reply/response data array (should be allocated)
++ *
++ * Sends a structured command to the MCU.
++ */
++int iei_wt61p803_puzzle_write_command(struct iei_wt61p803_puzzle *mcu,
++ unsigned char *cmd, size_t size,
++ unsigned char *reply_data,
++ size_t *reply_size)
++{
++ struct device *dev = &mcu->serdev->dev;
++ int ret;
++
++ if (size <= 1 || size > IEI_WT61P803_PUZZLE_MAX_COMMAND_LENGTH)
++ return -EINVAL;
++
++ mutex_lock(&mcu->reply_lock);
++
++ cmd[size - 1] = iei_wt61p803_puzzle_checksum(cmd, size - 1);
++
++ /* Initialize reply struct */
++ reinit_completion(&mcu->reply->received);
++ mcu->reply->size = 0;
++ usleep_range(2000, 10000);
++ serdev_device_write_flush(mcu->serdev);
++ ret = serdev_device_write_buf(mcu->serdev, cmd, size);
++ if (ret < 0)
++ goto exit;
++
++ serdev_device_wait_until_sent(mcu->serdev, IEI_WT61P803_PUZZLE_GENERAL_TIMEOUT);
++ ret = wait_for_completion_timeout(&mcu->reply->received,
++ IEI_WT61P803_PUZZLE_GENERAL_TIMEOUT);
++ if (ret == 0) {
++ dev_err(dev, "Command reply receive timeout\n");
++ ret = -ETIMEDOUT;
++ goto exit;
++ }
++
++ *reply_size = mcu->reply->size;
++ /* Copy the received data, as it will not be available after a new frame is received */
++ memcpy(reply_data, mcu->reply->data, mcu->reply->size);
++ ret = 0;
++exit:
++ mutex_unlock(&mcu->reply_lock);
++ return ret;
++}
++EXPORT_SYMBOL_GPL(iei_wt61p803_puzzle_write_command);
++
++static int iei_wt61p803_puzzle_buzzer(struct iei_wt61p803_puzzle *mcu, bool long_beep)
++{
++ unsigned char *resp_buf = mcu->response_buffer;
++ unsigned char buzzer_cmd[4] = {};
++ size_t reply_size;
++ int ret;
++
++ buzzer_cmd[0] = IEI_WT61P803_PUZZLE_CMD_HEADER_START;
++ buzzer_cmd[1] = IEI_WT61P803_PUZZLE_CMD_FUNCTION_SINGLE;
++ buzzer_cmd[2] = long_beep ? '3' : '2'; /* Buzzer 1.5 / 0.5 second beep */
++
++ mutex_lock(&mcu->lock);
++ ret = iei_wt61p803_puzzle_write_command(mcu, buzzer_cmd, sizeof(buzzer_cmd),
++ resp_buf, &reply_size);
++ if (ret)
++ goto exit;
++
++ if (reply_size != 3) {
++ ret = -EIO;
++ goto exit;
++ }
++
++ if (!(resp_buf[0] == IEI_WT61P803_PUZZLE_CMD_HEADER_START &&
++ resp_buf[1] == IEI_WT61P803_PUZZLE_CMD_RESPONSE_OK &&
++ resp_buf[2] == IEI_WT61P803_PUZZLE_CHECKSUM_RESPONSE_OK)) {
++ ret = -EPROTO;
++ goto exit;
++ }
++exit:
++ mutex_unlock(&mcu->lock);
++ return ret;
++}
++
++static int iei_wt61p803_puzzle_get_version(struct iei_wt61p803_puzzle *mcu)
++{
++ unsigned char version_cmd[3] = {
++ IEI_WT61P803_PUZZLE_CMD_HEADER_START_OTHER,
++ IEI_WT61P803_PUZZLE_CMD_OTHER_VERSION,
++ };
++ unsigned char build_info_cmd[3] = {
++ IEI_WT61P803_PUZZLE_CMD_HEADER_START_OTHER,
++ IEI_WT61P803_PUZZLE_CMD_OTHER_BUILD,
++ };
++ unsigned char bootloader_mode_cmd[3] = {
++ IEI_WT61P803_PUZZLE_CMD_HEADER_START_OTHER,
++ IEI_WT61P803_PUZZLE_CMD_OTHER_BOOTLOADER_MODE,
++ };
++ unsigned char protocol_version_cmd[3] = {
++ IEI_WT61P803_PUZZLE_CMD_HEADER_START_OTHER,
++ IEI_WT61P803_PUZZLE_CMD_OTHER_PROTOCOL_VERSION,
++ };
++ unsigned char *rb = mcu->response_buffer;
++ size_t reply_size;
++ int ret;
++
++ mutex_lock(&mcu->lock);
++
++ ret = iei_wt61p803_puzzle_write_command(mcu, version_cmd, sizeof(version_cmd),
++ rb, &reply_size);
++ if (ret)
++ goto err;
++ if (reply_size < 7) {
++ ret = -EIO;
++ goto err;
++ }
++ sprintf(mcu->version.version, "v%c.%.3s", rb[2], &rb[3]);
++
++ ret = iei_wt61p803_puzzle_write_command(mcu, build_info_cmd,
++ sizeof(build_info_cmd), rb,
++ &reply_size);
++ if (ret)
++ goto err;
++ if (reply_size < 15) {
++ ret = -EIO;
++ goto err;
++ }
++ sprintf(mcu->version.build_info, "%c%c/%c%c/%.4s %c%c:%c%c",
++ rb[8], rb[9], rb[6], rb[7], &rb[2], rb[10], rb[11],
++ rb[12], rb[13]);
++
++ ret = iei_wt61p803_puzzle_write_command(mcu, bootloader_mode_cmd,
++ sizeof(bootloader_mode_cmd), rb,
++ &reply_size);
++ if (ret)
++ goto err;
++ if (reply_size < 4) {
++ ret = -EIO;
++ goto err;
++ }
++ if (rb[2] == IEI_WT61P803_PUZZLE_CMD_OTHER_MODE_APPS)
++ mcu->version.bootloader_mode = false;
++ else if (rb[2] == IEI_WT61P803_PUZZLE_CMD_OTHER_MODE_BOOTLOADER)
++ mcu->version.bootloader_mode = true;
++
++ ret = iei_wt61p803_puzzle_write_command(mcu, protocol_version_cmd,
++ sizeof(protocol_version_cmd), rb,
++ &reply_size);
++ if (ret)
++ goto err;
++ if (reply_size < 9) {
++ ret = -EIO;
++ goto err;
++ }
++ sprintf(mcu->version.protocol_version, "v%c.%c%c%c%c%c",
++ rb[7], rb[6], rb[5], rb[4], rb[3], rb[2]);
++err:
++ mutex_unlock(&mcu->lock);
++ return ret;
++}
++
++static int iei_wt61p803_puzzle_get_mcu_status(struct iei_wt61p803_puzzle *mcu)
++{
++ unsigned char mcu_status_cmd[5] = {
++ IEI_WT61P803_PUZZLE_CMD_HEADER_START,
++ IEI_WT61P803_PUZZLE_CMD_FUNCTION_OTHER,
++ IEI_WT61P803_PUZZLE_CMD_FUNCTION_OTHER_STATUS,
++ IEI_WT61P803_PUZZLE_CMD_FUNCTION_OTHER_STATUS,
++ };
++ unsigned char *resp_buf = mcu->response_buffer;
++ size_t reply_size;
++ int ret;
++
++ mutex_lock(&mcu->lock);
++ ret = iei_wt61p803_puzzle_write_command(mcu, mcu_status_cmd, sizeof(mcu_status_cmd),
++ resp_buf, &reply_size);
++ if (ret)
++ goto exit;
++ if (reply_size < 20) {
++ ret = -EIO;
++ goto exit;
++ }
++
++ /* Response format:
++ * (IDX RESPONSE)
++ * 0 @
++ * 1 O
++ * 2 S
++ * 3 S
++ * ...
++ * 5 AC Recovery Status Flag
++ * ...
++ * 10 Power Loss Recovery
++ * ...
++ * 19 Power Status (system power on method)
++ * 20 XOR checksum
++ */
++ if (resp_buf[0] == IEI_WT61P803_PUZZLE_CMD_HEADER_START &&
++ resp_buf[1] == IEI_WT61P803_PUZZLE_CMD_FUNCTION_OTHER &&
++ resp_buf[2] == IEI_WT61P803_PUZZLE_CMD_FUNCTION_OTHER_STATUS &&
++ resp_buf[3] == IEI_WT61P803_PUZZLE_CMD_FUNCTION_OTHER_STATUS) {
++ mcu->status.ac_recovery_status_flag = resp_buf[5];
++ mcu->status.power_loss_recovery = resp_buf[10];
++ mcu->status.power_status = resp_buf[19];
++ }
++exit:
++ mutex_unlock(&mcu->lock);
++ return ret;
++}
++
++static int iei_wt61p803_puzzle_get_serial_number(struct iei_wt61p803_puzzle *mcu)
++{
++ unsigned char *resp_buf = mcu->response_buffer;
++ unsigned char serial_number_cmd[5] = {
++ IEI_WT61P803_PUZZLE_CMD_HEADER_EEPROM,
++ IEI_WT61P803_PUZZLE_CMD_EEPROM_READ,
++ 0x00, /* EEPROM read address */
++ 0x24, /* Data length */
++ };
++ size_t reply_size;
++ int ret;
++
++ mutex_lock(&mcu->lock);
++ ret = iei_wt61p803_puzzle_write_command(mcu, serial_number_cmd,
++ sizeof(serial_number_cmd),
++ resp_buf, &reply_size);
++ if (ret)
++ goto err;
++
++ if (reply_size < IEI_WT61P803_PUZZLE_SN_LENGTH + 4) {
++ ret = -EIO;
++ goto err;
++ }
++
++ sprintf(mcu->version.serial_number, "%.*s",
++ IEI_WT61P803_PUZZLE_SN_LENGTH, resp_buf + 4);
++err:
++ mutex_unlock(&mcu->lock);
++ return ret;
++}
++
++static int iei_wt61p803_puzzle_write_serial_number(struct iei_wt61p803_puzzle *mcu,
++ unsigned char serial_number[36])
++{
++ unsigned char *resp_buf = mcu->response_buffer;
++ unsigned char serial_number_header[4] = {
++ IEI_WT61P803_PUZZLE_CMD_HEADER_EEPROM,
++ IEI_WT61P803_PUZZLE_CMD_EEPROM_WRITE,
++ 0x00, /* EEPROM write address */
++ 0xC, /* Data length */
++ };
++ unsigned char serial_number_cmd[4 + 12 + 1]; /* header, serial number, XOR checksum */
++ int ret, sn_counter;
++ size_t reply_size;
++
++ /* The MCU can only handle 22 byte messages, send the S/N in 12 byte chunks */
++ mutex_lock(&mcu->lock);
++ for (sn_counter = 0; sn_counter < 3; sn_counter++) {
++ serial_number_header[2] = 0x0 + 0xC * sn_counter;
++
++ memcpy(serial_number_cmd, serial_number_header, sizeof(serial_number_header));
++ memcpy(serial_number_cmd + sizeof(serial_number_header),
++ serial_number + 0xC * sn_counter, 0xC);
++
++ ret = iei_wt61p803_puzzle_write_command(mcu, serial_number_cmd,
++ sizeof(serial_number_cmd),
++ resp_buf, &reply_size);
++ if (ret)
++ goto err;
++ if (reply_size != 3) {
++ ret = -EIO;
++ goto err;
++ }
++ if (!(resp_buf[0] == IEI_WT61P803_PUZZLE_CMD_HEADER_START &&
++ resp_buf[1] == IEI_WT61P803_PUZZLE_CMD_RESPONSE_OK &&
++ resp_buf[2] == IEI_WT61P803_PUZZLE_CHECKSUM_RESPONSE_OK)) {
++ ret = -EPROTO;
++ goto err;
++ }
++ }
++
++ sprintf(mcu->version.serial_number, "%.*s",
++ IEI_WT61P803_PUZZLE_SN_LENGTH, serial_number);
++err:
++ mutex_unlock(&mcu->lock);
++ return ret;
++}
++
++static int iei_wt61p803_puzzle_get_mac_address(struct iei_wt61p803_puzzle *mcu, int index)
++{
++ unsigned char *resp_buf = mcu->response_buffer;
++ unsigned char mac_address_cmd[5] = {
++ IEI_WT61P803_PUZZLE_CMD_HEADER_EEPROM,
++ IEI_WT61P803_PUZZLE_CMD_EEPROM_READ,
++ 0x00, /* EEPROM read address */
++ 0x11, /* Data length */
++ };
++ size_t reply_size;
++ int ret;
++
++ mutex_lock(&mcu->lock);
++ mac_address_cmd[2] = 0x24 + 0x11 * index;
++
++ ret = iei_wt61p803_puzzle_write_command(mcu, mac_address_cmd,
++ sizeof(mac_address_cmd),
++ resp_buf, &reply_size);
++ if (ret)
++ goto err;
++
++ if (reply_size < 22) {
++ ret = -EIO;
++ goto err;
++ }
++
++ sprintf(mcu->version.mac_address[index], "%.*s",
++ IEI_WT61P803_PUZZLE_MAC_LENGTH, resp_buf + 4);
++err:
++ mutex_unlock(&mcu->lock);
++ return ret;
++}
++
++static int
++iei_wt61p803_puzzle_write_mac_address(struct iei_wt61p803_puzzle *mcu,
++ unsigned char mac_address[IEI_WT61P803_PUZZLE_MAC_LENGTH],
++ int mac_address_idx)
++{
++ unsigned char mac_address_cmd[4 + IEI_WT61P803_PUZZLE_MAC_LENGTH + 1];
++ unsigned char *resp_buf = mcu->response_buffer;
++ unsigned char mac_address_header[4] = {
++ IEI_WT61P803_PUZZLE_CMD_HEADER_EEPROM,
++ IEI_WT61P803_PUZZLE_CMD_EEPROM_WRITE,
++ 0x00, /* EEPROM write address */
++ 0x11, /* Data length */
++ };
++ size_t reply_size;
++ int ret;
++
++ if (mac_address_idx < 0 || mac_address_idx >= IEI_WT61P803_PUZZLE_NB_MAC)
++ return -EINVAL;
++
++ mac_address_header[2] = 0x24 + 0x11 * mac_address_idx;
++
++ /* Concat mac_address_header, mac_address to mac_address_cmd */
++ memcpy(mac_address_cmd, mac_address_header, sizeof(mac_address_header));
++ memcpy(mac_address_cmd + sizeof(mac_address_header), mac_address,
++ IEI_WT61P803_PUZZLE_MAC_LENGTH);
++
++ mutex_lock(&mcu->lock);
++ ret = iei_wt61p803_puzzle_write_command(mcu, mac_address_cmd,
++ sizeof(mac_address_cmd),
++ resp_buf, &reply_size);
++ if (ret)
++ goto err;
++ if (reply_size != 3) {
++ ret = -EIO;
++ goto err;
++ }
++ if (!(resp_buf[0] == IEI_WT61P803_PUZZLE_CMD_HEADER_START &&
++ resp_buf[1] == IEI_WT61P803_PUZZLE_CMD_RESPONSE_OK &&
++ resp_buf[2] == IEI_WT61P803_PUZZLE_CHECKSUM_RESPONSE_OK)) {
++ ret = -EPROTO;
++ goto err;
++ }
++
++ sprintf(mcu->version.mac_address[mac_address_idx], "%.*s",
++ IEI_WT61P803_PUZZLE_MAC_LENGTH, mac_address);
++err:
++ mutex_unlock(&mcu->lock);
++ return ret;
++}
++
++static int iei_wt61p803_puzzle_write_power_loss_recovery(struct iei_wt61p803_puzzle *mcu,
++ int power_loss_recovery_action)
++{
++ unsigned char *resp_buf = mcu->response_buffer;
++ unsigned char power_loss_recovery_cmd[5] = {};
++ size_t reply_size;
++ int ret;
++
++ if (power_loss_recovery_action < 0 || power_loss_recovery_action > 4)
++ return -EINVAL;
++
++ power_loss_recovery_cmd[0] = IEI_WT61P803_PUZZLE_CMD_HEADER_START;
++ power_loss_recovery_cmd[1] = IEI_WT61P803_PUZZLE_CMD_FUNCTION_OTHER;
++ power_loss_recovery_cmd[2] = IEI_WT61P803_PUZZLE_CMD_FUNCTION_OTHER_POWER_LOSS;
++ power_loss_recovery_cmd[3] = hex_asc[power_loss_recovery_action];
++
++ mutex_lock(&mcu->lock);
++ ret = iei_wt61p803_puzzle_write_command(mcu, power_loss_recovery_cmd,
++ sizeof(power_loss_recovery_cmd),
++ resp_buf, &reply_size);
++ if (ret)
++ goto exit;
++ mcu->status.power_loss_recovery = power_loss_recovery_action;
++exit:
++ mutex_unlock(&mcu->lock);
++ return ret;
++}
++
++#define to_puzzle_dev_attr(_attr) \
++ container_of(_attr, struct iei_wt61p803_puzzle_device_attribute, dev_attr)
++
++static ssize_t show_output(struct device *dev,
++ struct device_attribute *attr, char *buf)
++{
++ struct iei_wt61p803_puzzle *mcu = dev_get_drvdata(dev);
++ struct iei_wt61p803_puzzle_device_attribute *pattr = to_puzzle_dev_attr(attr);
++ int ret;
++
++ switch (pattr->type) {
++ case IEI_WT61P803_PUZZLE_VERSION:
++ return scnprintf(buf, PAGE_SIZE, "%s\n", mcu->version.version);
++ case IEI_WT61P803_PUZZLE_BUILD_INFO:
++ return scnprintf(buf, PAGE_SIZE, "%s\n", mcu->version.build_info);
++ case IEI_WT61P803_PUZZLE_BOOTLOADER_MODE:
++ return scnprintf(buf, PAGE_SIZE, "%d\n", mcu->version.bootloader_mode);
++ case IEI_WT61P803_PUZZLE_PROTOCOL_VERSION:
++ return scnprintf(buf, PAGE_SIZE, "%s\n", mcu->version.protocol_version);
++ case IEI_WT61P803_PUZZLE_SERIAL_NUMBER:
++ ret = iei_wt61p803_puzzle_get_serial_number(mcu);
++ if (!ret)
++ ret = scnprintf(buf, PAGE_SIZE, "%s\n", mcu->version.serial_number);
++ else
++ ret = 0;
++ return ret;
++ case IEI_WT61P803_PUZZLE_MAC_ADDRESS:
++ ret = iei_wt61p803_puzzle_get_mac_address(mcu, pattr->index);
++ if (!ret)
++ ret = scnprintf(buf, PAGE_SIZE, "%s\n",
++ mcu->version.mac_address[pattr->index]);
++ else
++ ret = 0;
++ return ret;
++ case IEI_WT61P803_PUZZLE_AC_RECOVERY_STATUS:
++ case IEI_WT61P803_PUZZLE_POWER_LOSS_RECOVERY:
++ case IEI_WT61P803_PUZZLE_POWER_STATUS:
++ ret = iei_wt61p803_puzzle_get_mcu_status(mcu);
++ if (ret)
++ return ret;
++
++ mutex_lock(&mcu->lock);
++ switch (pattr->type) {
++ case IEI_WT61P803_PUZZLE_AC_RECOVERY_STATUS:
++ ret = scnprintf(buf, PAGE_SIZE, "%x\n",
++ mcu->status.ac_recovery_status_flag);
++ break;
++ case IEI_WT61P803_PUZZLE_POWER_LOSS_RECOVERY:
++ ret = scnprintf(buf, PAGE_SIZE, "%x\n", mcu->status.power_loss_recovery);
++ break;
++ case IEI_WT61P803_PUZZLE_POWER_STATUS:
++ ret = scnprintf(buf, PAGE_SIZE, "%x\n", mcu->status.power_status);
++ break;
++ default:
++ ret = 0;
++ break;
++ }
++ mutex_unlock(&mcu->lock);
++ return ret;
++ default:
++ return 0;
++ }
++
++ return 0;
++}
++
++static ssize_t store_output(struct device *dev,
++ struct device_attribute *attr,
++ const char *buf, size_t len)
++{
++ unsigned char serial_number[IEI_WT61P803_PUZZLE_SN_LENGTH];
++ unsigned char mac_address[IEI_WT61P803_PUZZLE_MAC_LENGTH];
++ struct iei_wt61p803_puzzle *mcu = dev_get_drvdata(dev);
++ struct iei_wt61p803_puzzle_device_attribute *pattr = to_puzzle_dev_attr(attr);
++ int power_loss_recovery_action = 0;
++ int ret;
++
++ switch (pattr->type) {
++ case IEI_WT61P803_PUZZLE_SERIAL_NUMBER:
++ if (len != (size_t)(IEI_WT61P803_PUZZLE_SN_LENGTH + 1))
++ return -EINVAL;
++ memcpy(serial_number, buf, sizeof(serial_number));
++ ret = iei_wt61p803_puzzle_write_serial_number(mcu, serial_number);
++ if (ret)
++ return ret;
++ return len;
++ case IEI_WT61P803_PUZZLE_MAC_ADDRESS:
++ if (len != (size_t)(IEI_WT61P803_PUZZLE_MAC_LENGTH + 1))
++ return -EINVAL;
++
++ memcpy(mac_address, buf, sizeof(mac_address));
++
++ if (strlen(attr->attr.name) != 13)
++ return -EIO;
++
++ ret = iei_wt61p803_puzzle_write_mac_address(mcu, mac_address, pattr->index);
++ if (ret)
++ return ret;
++ return len;
++ case IEI_WT61P803_PUZZLE_POWER_LOSS_RECOVERY:
++ ret = kstrtoint(buf, 10, &power_loss_recovery_action);
++ if (ret)
++ return ret;
++ ret = iei_wt61p803_puzzle_write_power_loss_recovery(mcu,
++ power_loss_recovery_action);
++ if (ret)
++ return ret;
++ return len;
++ default:
++ return -EINVAL;
++ }
++
++ return 0;
++}
++
++#define IEI_WT61P803_PUZZLE_ATTR(_name, _mode, _show, _store, _type, _index) \
++ struct iei_wt61p803_puzzle_device_attribute dev_attr_##_name = \
++ { .dev_attr = __ATTR(_name, _mode, _show, _store), \
++ .type = _type, \
++ .index = _index }
++
++#define IEI_WT61P803_PUZZLE_ATTR_RO(_name, _type, _id) \
++ IEI_WT61P803_PUZZLE_ATTR(_name, 0444, show_output, NULL, _type, _id)
++
++#define IEI_WT61P803_PUZZLE_ATTR_RW(_name, _type, _id) \
++ IEI_WT61P803_PUZZLE_ATTR(_name, 0644, show_output, store_output, _type, _id)
++
++static IEI_WT61P803_PUZZLE_ATTR_RO(version, IEI_WT61P803_PUZZLE_VERSION, 0);
++static IEI_WT61P803_PUZZLE_ATTR_RO(build_info, IEI_WT61P803_PUZZLE_BUILD_INFO, 0);
++static IEI_WT61P803_PUZZLE_ATTR_RO(bootloader_mode, IEI_WT61P803_PUZZLE_BOOTLOADER_MODE, 0);
++static IEI_WT61P803_PUZZLE_ATTR_RO(protocol_version, IEI_WT61P803_PUZZLE_PROTOCOL_VERSION, 0);
++static IEI_WT61P803_PUZZLE_ATTR_RW(serial_number, IEI_WT61P803_PUZZLE_SERIAL_NUMBER, 0);
++static IEI_WT61P803_PUZZLE_ATTR_RW(mac_address_0, IEI_WT61P803_PUZZLE_MAC_ADDRESS, 0);
++static IEI_WT61P803_PUZZLE_ATTR_RW(mac_address_1, IEI_WT61P803_PUZZLE_MAC_ADDRESS, 1);
++static IEI_WT61P803_PUZZLE_ATTR_RW(mac_address_2, IEI_WT61P803_PUZZLE_MAC_ADDRESS, 2);
++static IEI_WT61P803_PUZZLE_ATTR_RW(mac_address_3, IEI_WT61P803_PUZZLE_MAC_ADDRESS, 3);
++static IEI_WT61P803_PUZZLE_ATTR_RW(mac_address_4, IEI_WT61P803_PUZZLE_MAC_ADDRESS, 4);
++static IEI_WT61P803_PUZZLE_ATTR_RW(mac_address_5, IEI_WT61P803_PUZZLE_MAC_ADDRESS, 5);
++static IEI_WT61P803_PUZZLE_ATTR_RW(mac_address_6, IEI_WT61P803_PUZZLE_MAC_ADDRESS, 6);
++static IEI_WT61P803_PUZZLE_ATTR_RW(mac_address_7, IEI_WT61P803_PUZZLE_MAC_ADDRESS, 7);
++static IEI_WT61P803_PUZZLE_ATTR_RO(ac_recovery_status, IEI_WT61P803_PUZZLE_AC_RECOVERY_STATUS, 0);
++static IEI_WT61P803_PUZZLE_ATTR_RW(power_loss_recovery, IEI_WT61P803_PUZZLE_POWER_LOSS_RECOVERY, 0);
++static IEI_WT61P803_PUZZLE_ATTR_RO(power_status, IEI_WT61P803_PUZZLE_POWER_STATUS, 0);
++
++static struct attribute *iei_wt61p803_puzzle_attrs[] = {
++ &dev_attr_version.dev_attr.attr,
++ &dev_attr_build_info.dev_attr.attr,
++ &dev_attr_bootloader_mode.dev_attr.attr,
++ &dev_attr_protocol_version.dev_attr.attr,
++ &dev_attr_serial_number.dev_attr.attr,
++ &dev_attr_mac_address_0.dev_attr.attr,
++ &dev_attr_mac_address_1.dev_attr.attr,
++ &dev_attr_mac_address_2.dev_attr.attr,
++ &dev_attr_mac_address_3.dev_attr.attr,
++ &dev_attr_mac_address_4.dev_attr.attr,
++ &dev_attr_mac_address_5.dev_attr.attr,
++ &dev_attr_mac_address_6.dev_attr.attr,
++ &dev_attr_mac_address_7.dev_attr.attr,
++ &dev_attr_ac_recovery_status.dev_attr.attr,
++ &dev_attr_power_loss_recovery.dev_attr.attr,
++ &dev_attr_power_status.dev_attr.attr,
++ NULL
++};
++ATTRIBUTE_GROUPS(iei_wt61p803_puzzle);
++
++static int iei_wt61p803_puzzle_sysfs_create(struct device *dev,
++ struct iei_wt61p803_puzzle *mcu)
++{
++ int ret;
++
++ ret = sysfs_create_groups(&mcu->dev->kobj, iei_wt61p803_puzzle_groups);
++ if (ret)
++ mfd_remove_devices(mcu->dev);
++
++ return ret;
++}
++
++static int iei_wt61p803_puzzle_sysfs_remove(struct device *dev,
++ struct iei_wt61p803_puzzle *mcu)
++{
++ /* Remove sysfs groups */
++ sysfs_remove_groups(&mcu->dev->kobj, iei_wt61p803_puzzle_groups);
++ mfd_remove_devices(mcu->dev);
++
++ return 0;
++}
++
++static int iei_wt61p803_puzzle_probe(struct serdev_device *serdev)
++{
++ struct device *dev = &serdev->dev;
++ struct iei_wt61p803_puzzle *mcu;
++ u32 baud;
++ int ret;
++
++ /* Read the baud rate from 'current-speed', because the MCU supports different rates */
++ if (device_property_read_u32(dev, "current-speed", &baud)) {
++ dev_err(dev,
++ "'current-speed' is not specified in device node\n");
++ return -EINVAL;
++ }
++ dev_dbg(dev, "Driver baud rate: %d\n", baud);
++
++ /* Allocate the memory */
++ mcu = devm_kzalloc(dev, sizeof(*mcu), GFP_KERNEL);
++ if (!mcu)
++ return -ENOMEM;
++
++ mcu->reply = devm_kzalloc(dev, sizeof(*mcu->reply), GFP_KERNEL);
++ if (!mcu->reply)
++ return -ENOMEM;
++
++ /* Initialize device struct data */
++ mcu->serdev = serdev;
++ mcu->dev = dev;
++ init_completion(&mcu->reply->received);
++ mutex_init(&mcu->reply_lock);
++ mutex_init(&mcu->lock);
++
++ /* Setup UART interface */
++ serdev_device_set_drvdata(serdev, mcu);
++ serdev_device_set_client_ops(serdev, &iei_wt61p803_puzzle_serdev_device_ops);
++ ret = devm_serdev_device_open(dev, serdev);
++ if (ret)
++ return ret;
++ serdev_device_set_baudrate(serdev, baud);
++ serdev_device_set_flow_control(serdev, false);
++ ret = serdev_device_set_parity(serdev, SERDEV_PARITY_NONE);
++ if (ret) {
++ dev_err(dev, "Failed to set parity\n");
++ return ret;
++ }
++
++ ret = iei_wt61p803_puzzle_get_version(mcu);
++ if (ret)
++ return ret;
++
++ dev_dbg(dev, "MCU version: %s\n", mcu->version.version);
++ dev_dbg(dev, "MCU firmware build info: %s\n", mcu->version.build_info);
++ dev_dbg(dev, "MCU in bootloader mode: %s\n",
++ mcu->version.bootloader_mode ? "true" : "false");
++ dev_dbg(dev, "MCU protocol version: %s\n", mcu->version.protocol_version);
++
++ if (device_property_read_bool(dev, "enable-beep")) {
++ ret = iei_wt61p803_puzzle_buzzer(mcu, false);
++ if (ret)
++ return ret;
++ }
++
++ ret = iei_wt61p803_puzzle_sysfs_create(dev, mcu);
++ if (ret)
++ return ret;
++
++ return devm_of_platform_populate(dev);
++}
++
++static void iei_wt61p803_puzzle_remove(struct serdev_device *serdev)
++{
++ struct device *dev = &serdev->dev;
++ struct iei_wt61p803_puzzle *mcu = dev_get_drvdata(dev);
++
++ iei_wt61p803_puzzle_sysfs_remove(dev, mcu);
++}
++
++static const struct of_device_id iei_wt61p803_puzzle_dt_ids[] = {
++ { .compatible = "iei,wt61p803-puzzle" },
++ { }
++};
++
++MODULE_DEVICE_TABLE(of, iei_wt61p803_puzzle_dt_ids);
++
++static struct serdev_device_driver iei_wt61p803_puzzle_drv = {
++ .probe = iei_wt61p803_puzzle_probe,
++ .remove = iei_wt61p803_puzzle_remove,
++ .driver = {
++ .name = "iei-wt61p803-puzzle",
++ .of_match_table = iei_wt61p803_puzzle_dt_ids,
++ },
++};
++
++module_serdev_device_driver(iei_wt61p803_puzzle_drv);
++
++MODULE_LICENSE("GPL v2");
++MODULE_AUTHOR("Luka Kovacic <luka.kovacic at sartura.hr>");
++MODULE_DESCRIPTION("IEI WT61P803 PUZZLE MCU Driver");
+--- /dev/null
++++ b/include/linux/mfd/iei-wt61p803-puzzle.h
+@@ -0,0 +1,66 @@
++/* SPDX-License-Identifier: GPL-2.0-only */
++/* IEI WT61P803 PUZZLE MCU Driver
++ * System management microcontroller for fan control, temperature sensor reading,
++ * LED control and system identification on IEI Puzzle series ARM-based appliances.
++ *
++ * Copyright (C) 2020 Sartura Ltd.
++ * Author: Luka Kovacic <luka.kovacic at sartura.hr>
++ */
++
++#ifndef _MFD_IEI_WT61P803_PUZZLE_H_
++#define _MFD_IEI_WT61P803_PUZZLE_H_
++
++#define IEI_WT61P803_PUZZLE_BUF_SIZE 512
++
++/* Command magic numbers */
++#define IEI_WT61P803_PUZZLE_CMD_HEADER_START 0x40 /* @ */
++#define IEI_WT61P803_PUZZLE_CMD_HEADER_START_OTHER 0x25 /* % */
++#define IEI_WT61P803_PUZZLE_CMD_HEADER_EEPROM 0xF7
++
++#define IEI_WT61P803_PUZZLE_CMD_RESPONSE_OK 0x30 /* 0 */
++#define IEI_WT61P803_PUZZLE_CHECKSUM_RESPONSE_OK 0x70
++
++#define IEI_WT61P803_PUZZLE_CMD_EEPROM_READ 0xA1
++#define IEI_WT61P803_PUZZLE_CMD_EEPROM_WRITE 0xA0
++
++#define IEI_WT61P803_PUZZLE_CMD_OTHER_VERSION 0x56 /* V */
++#define IEI_WT61P803_PUZZLE_CMD_OTHER_BUILD 0x42 /* B */
++#define IEI_WT61P803_PUZZLE_CMD_OTHER_BOOTLOADER_MODE 0x4D /* M */
++#define IEI_WT61P803_PUZZLE_CMD_OTHER_MODE_BOOTLOADER 0x30
++#define IEI_WT61P803_PUZZLE_CMD_OTHER_MODE_APPS 0x31
++#define IEI_WT61P803_PUZZLE_CMD_OTHER_PROTOCOL_VERSION 0x50 /* P */
++
++#define IEI_WT61P803_PUZZLE_CMD_FUNCTION_SINGLE 0x43 /* C */
++#define IEI_WT61P803_PUZZLE_CMD_FUNCTION_OTHER 0x4F /* O */
++#define IEI_WT61P803_PUZZLE_CMD_FUNCTION_OTHER_STATUS 0x53 /* S */
++#define IEI_WT61P803_PUZZLE_CMD_FUNCTION_OTHER_POWER_LOSS 0x41 /* A */
++
++#define IEI_WT61P803_PUZZLE_CMD_LED 0x52 /* R */
++#define IEI_WT61P803_PUZZLE_CMD_LED_POWER 0x31 /* 1 */
++
++#define IEI_WT61P803_PUZZLE_CMD_TEMP 0x54 /* T */
++#define IEI_WT61P803_PUZZLE_CMD_TEMP_ALL 0x41 /* A */
++
++#define IEI_WT61P803_PUZZLE_CMD_FAN 0x46 /* F */
++#define IEI_WT61P803_PUZZLE_CMD_FAN_PWM_READ 0x5A /* Z */
++#define IEI_WT61P803_PUZZLE_CMD_FAN_PWM_WRITE 0x57 /* W */
++#define IEI_WT61P803_PUZZLE_CMD_FAN_PWM_BASE 0x30
++#define IEI_WT61P803_PUZZLE_CMD_FAN_RPM_BASE 0x41 /* A */
++
++#define IEI_WT61P803_PUZZLE_CMD_FAN_PWM(x) (IEI_WT61P803_PUZZLE_CMD_FAN_PWM_BASE + (x)) /* 0 - 1 */
++#define IEI_WT61P803_PUZZLE_CMD_FAN_RPM(x) (IEI_WT61P803_PUZZLE_CMD_FAN_RPM_BASE + (x)) /* 0 - 5 */
++
++struct iei_wt61p803_puzzle_mcu_version;
++struct iei_wt61p803_puzzle_reply;
++struct iei_wt61p803_puzzle;
++
++int iei_wt61p803_puzzle_write_command_watchdog(struct iei_wt61p803_puzzle *mcu,
++ unsigned char *cmd, size_t size,
++ unsigned char *reply_data, size_t *reply_size,
++ int retry_count);
++
++int iei_wt61p803_puzzle_write_command(struct iei_wt61p803_puzzle *mcu,
++ unsigned char *cmd, size_t size,
++ unsigned char *reply_data, size_t *reply_size);
++
++#endif /* _MFD_IEI_WT61P803_PUZZLE_H_ */
diff --git a/target/linux/mvebu/patches-6.1/903-drivers-hwmon-Add-the-IEI-WT61P803-PUZZLE-HWMON-driv.patch b/target/linux/mvebu/patches-6.1/903-drivers-hwmon-Add-the-IEI-WT61P803-PUZZLE-HWMON-driv.patch
new file mode 100644
index 0000000000..023495373b
--- /dev/null
+++ b/target/linux/mvebu/patches-6.1/903-drivers-hwmon-Add-the-IEI-WT61P803-PUZZLE-HWMON-driv.patch
@@ -0,0 +1,501 @@
+From e3310a638cd310bfd93dbbc6d2732ab6aea18dd2 Mon Sep 17 00:00:00 2001
+From: Luka Kovacic <luka.kovacic () sartura ! hr>
+Date: Tue, 24 Aug 2021 12:44:34 +0000
+Subject: [PATCH 3/7] drivers: hwmon: Add the IEI WT61P803 PUZZLE HWMON driver
+
+Add the IEI WT61P803 PUZZLE HWMON driver, that handles the fan speed
+control via PWM, reading fan speed and reading on-board temperature
+sensors.
+
+The driver registers a HWMON device and a simple thermal cooling device to
+enable in-kernel fan management.
+
+This driver depends on the IEI WT61P803 PUZZLE MFD driver.
+
+Signed-off-by: Luka Kovacic <luka.kovacic at sartura.hr>
+Signed-off-by: Pavo Banicevic <pavo.banicevic at sartura.hr>
+Acked-by: Guenter Roeck <linux at roeck-us.net>
+Cc: Luka Perkov <luka.perkov at sartura.hr>
+Cc: Robert Marko <robert.marko at sartura.hr>
+---
+ drivers/hwmon/Kconfig | 8 +
+ drivers/hwmon/Makefile | 1 +
+ drivers/hwmon/iei-wt61p803-puzzle-hwmon.c | 413 ++++++++++++++++++++++
+ 3 files changed, 422 insertions(+)
+ create mode 100644 drivers/hwmon/iei-wt61p803-puzzle-hwmon.c
+
+--- a/drivers/hwmon/Kconfig
++++ b/drivers/hwmon/Kconfig
+@@ -732,6 +732,14 @@ config SENSORS_IBMPOWERNV
+ This driver can also be built as a module. If so, the module
+ will be called ibmpowernv.
+
++config SENSORS_IEI_WT61P803_PUZZLE_HWMON
++ tristate "IEI WT61P803 PUZZLE MFD HWMON Driver"
++ depends on MFD_IEI_WT61P803_PUZZLE
++ help
++ The IEI WT61P803 PUZZLE MFD HWMON Driver handles reading fan speed
++ and writing fan PWM values. It also supports reading on-board
++ temperature sensors.
++
+ config SENSORS_IIO_HWMON
+ tristate "Hwmon driver that uses channels specified via iio maps"
+ depends on IIO
+--- a/drivers/hwmon/Makefile
++++ b/drivers/hwmon/Makefile
+@@ -84,6 +84,7 @@ obj-$(CONFIG_SENSORS_HIH6130) += hih6130
+ obj-$(CONFIG_SENSORS_ULTRA45) += ultra45_env.o
+ obj-$(CONFIG_SENSORS_I5500) += i5500_temp.o
+ obj-$(CONFIG_SENSORS_I5K_AMB) += i5k_amb.o
++obj-$(CONFIG_SENSORS_IEI_WT61P803_PUZZLE_HWMON) += iei-wt61p803-puzzle-hwmon.o
+ obj-$(CONFIG_SENSORS_IBMAEM) += ibmaem.o
+ obj-$(CONFIG_SENSORS_IBMPEX) += ibmpex.o
+ obj-$(CONFIG_SENSORS_IBMPOWERNV)+= ibmpowernv.o
+--- /dev/null
++++ b/drivers/hwmon/iei-wt61p803-puzzle-hwmon.c
+@@ -0,0 +1,445 @@
++// SPDX-License-Identifier: GPL-2.0-only
++/* IEI WT61P803 PUZZLE MCU HWMON Driver
++ *
++ * Copyright (C) 2020 Sartura Ltd.
++ * Author: Luka Kovacic <luka.kovacic at sartura.hr>
++ */
++
++#include <linux/err.h>
++#include <linux/hwmon.h>
++#include <linux/interrupt.h>
++#include <linux/irq.h>
++#include <linux/math64.h>
++#include <linux/mfd/iei-wt61p803-puzzle.h>
++#include <linux/mod_devicetable.h>
++#include <linux/module.h>
++#include <linux/platform_device.h>
++#include <linux/property.h>
++#include <linux/slab.h>
++#include <linux/thermal.h>
++
++#define IEI_WT61P803_PUZZLE_HWMON_MAX_PWM 2
++#define IEI_WT61P803_PUZZLE_HWMON_MAX_PWM_VAL 255
++
++/**
++ * struct iei_wt61p803_puzzle_thermal_cooling_device - Thermal cooling device instance
++ * @mcu_hwmon: Parent driver struct pointer
++ * @tcdev: Thermal cooling device pointer
++ * @name: Thermal cooling device name
++ * @pwm_channel: Controlled PWM channel (0 or 1)
++ * @cooling_levels: Thermal cooling device cooling levels (DT)
++ * @cur_level: Current cooling level
++ * @num_levels: Number of cooling levels
++ */
++struct iei_wt61p803_puzzle_thermal_cooling_device {
++ struct iei_wt61p803_puzzle_hwmon *mcu_hwmon;
++ struct thermal_cooling_device *tcdev;
++ char name[THERMAL_NAME_LENGTH];
++ int pwm_channel;
++ u32 *cooling_levels;
++ int cur_level;
++ u8 num_levels;
++};
++
++/**
++ * struct iei_wt61p803_puzzle_hwmon - MCU HWMON Driver
++ * @mcu: MCU struct pointer
++ * @response_buffer Global MCU response buffer
++ * @thermal_cooling_dev_present: Per-channel thermal cooling device control indicator
++ * @cdev: Per-channel thermal cooling device private structure
++ */
++struct iei_wt61p803_puzzle_hwmon {
++ struct iei_wt61p803_puzzle *mcu;
++ unsigned char response_buffer[IEI_WT61P803_PUZZLE_BUF_SIZE];
++ bool thermal_cooling_dev_present[IEI_WT61P803_PUZZLE_HWMON_MAX_PWM];
++ struct iei_wt61p803_puzzle_thermal_cooling_device
++ *cdev[IEI_WT61P803_PUZZLE_HWMON_MAX_PWM];
++ struct mutex lock; /* mutex to protect response_buffer array */
++};
++
++#define raw_temp_to_milidegree_celsius(x) (((x) - 0x80) * 1000)
++static int iei_wt61p803_puzzle_read_temp_sensor(struct iei_wt61p803_puzzle_hwmon *mcu_hwmon,
++ int channel, long *value)
++{
++ unsigned char *resp_buf = mcu_hwmon->response_buffer;
++ unsigned char temp_sensor_ntc_cmd[4] = {
++ IEI_WT61P803_PUZZLE_CMD_HEADER_START,
++ IEI_WT61P803_PUZZLE_CMD_TEMP,
++ IEI_WT61P803_PUZZLE_CMD_TEMP_ALL,
++ };
++ size_t reply_size;
++ int ret;
++
++ mutex_lock(&mcu_hwmon->lock);
++ ret = iei_wt61p803_puzzle_write_command(mcu_hwmon->mcu, temp_sensor_ntc_cmd,
++ sizeof(temp_sensor_ntc_cmd), resp_buf,
++ &reply_size);
++ if (ret)
++ goto exit;
++
++ if (reply_size != 7) {
++ ret = -EIO;
++ goto exit;
++ }
++
++ /* Check the number of NTC values */
++ if (resp_buf[3] != '2') {
++ ret = -EIO;
++ goto exit;
++ }
++
++ *value = raw_temp_to_milidegree_celsius(resp_buf[4 + channel]);
++exit:
++ mutex_unlock(&mcu_hwmon->lock);
++ return ret;
++}
++
++#define raw_fan_val_to_rpm(x, y) ((((x) << 8 | (y)) / 2) * 60)
++static int iei_wt61p803_puzzle_read_fan_speed(struct iei_wt61p803_puzzle_hwmon *mcu_hwmon,
++ int channel, long *value)
++{
++ unsigned char *resp_buf = mcu_hwmon->response_buffer;
++ unsigned char fan_speed_cmd[4] = {};
++ size_t reply_size;
++ int ret;
++
++ fan_speed_cmd[0] = IEI_WT61P803_PUZZLE_CMD_HEADER_START;
++ fan_speed_cmd[1] = IEI_WT61P803_PUZZLE_CMD_FAN;
++ fan_speed_cmd[2] = IEI_WT61P803_PUZZLE_CMD_FAN_RPM(channel);
++
++ mutex_lock(&mcu_hwmon->lock);
++ ret = iei_wt61p803_puzzle_write_command(mcu_hwmon->mcu, fan_speed_cmd,
++ sizeof(fan_speed_cmd), resp_buf,
++ &reply_size);
++ if (ret)
++ goto exit;
++
++ if (reply_size != 7) {
++ ret = -EIO;
++ goto exit;
++ }
++
++ *value = raw_fan_val_to_rpm(resp_buf[3], resp_buf[4]);
++exit:
++ mutex_unlock(&mcu_hwmon->lock);
++ return ret;
++}
++
++static int iei_wt61p803_puzzle_write_pwm_channel(struct iei_wt61p803_puzzle_hwmon *mcu_hwmon,
++ int channel, long pwm_set_val)
++{
++ unsigned char *resp_buf = mcu_hwmon->response_buffer;
++ unsigned char pwm_set_cmd[6] = {};
++ size_t reply_size;
++ int ret;
++
++ pwm_set_cmd[0] = IEI_WT61P803_PUZZLE_CMD_HEADER_START;
++ pwm_set_cmd[1] = IEI_WT61P803_PUZZLE_CMD_FAN;
++ pwm_set_cmd[2] = IEI_WT61P803_PUZZLE_CMD_FAN_PWM_WRITE;
++ pwm_set_cmd[3] = IEI_WT61P803_PUZZLE_CMD_FAN_PWM(channel);
++ pwm_set_cmd[4] = pwm_set_val;
++
++ mutex_lock(&mcu_hwmon->lock);
++ ret = iei_wt61p803_puzzle_write_command(mcu_hwmon->mcu, pwm_set_cmd,
++ sizeof(pwm_set_cmd), resp_buf,
++ &reply_size);
++ if (ret)
++ goto exit;
++
++ if (reply_size != 3) {
++ ret = -EIO;
++ goto exit;
++ }
++
++ if (!(resp_buf[0] == IEI_WT61P803_PUZZLE_CMD_HEADER_START &&
++ resp_buf[1] == IEI_WT61P803_PUZZLE_CMD_RESPONSE_OK &&
++ resp_buf[2] == IEI_WT61P803_PUZZLE_CHECKSUM_RESPONSE_OK)) {
++ ret = -EIO;
++ goto exit;
++ }
++exit:
++ mutex_unlock(&mcu_hwmon->lock);
++ return ret;
++}
++
++static int iei_wt61p803_puzzle_read_pwm_channel(struct iei_wt61p803_puzzle_hwmon *mcu_hwmon,
++ int channel, long *value)
++{
++ unsigned char *resp_buf = mcu_hwmon->response_buffer;
++ unsigned char pwm_get_cmd[5] = {};
++ size_t reply_size;
++ int ret;
++
++ pwm_get_cmd[0] = IEI_WT61P803_PUZZLE_CMD_HEADER_START;
++ pwm_get_cmd[1] = IEI_WT61P803_PUZZLE_CMD_FAN;
++ pwm_get_cmd[2] = IEI_WT61P803_PUZZLE_CMD_FAN_PWM_READ;
++ pwm_get_cmd[3] = IEI_WT61P803_PUZZLE_CMD_FAN_PWM(channel);
++
++ ret = iei_wt61p803_puzzle_write_command(mcu_hwmon->mcu, pwm_get_cmd,
++ sizeof(pwm_get_cmd), resp_buf,
++ &reply_size);
++ if (ret)
++ return ret;
++
++ if (reply_size != 5)
++ return -EIO;
++
++ if (resp_buf[2] != IEI_WT61P803_PUZZLE_CMD_FAN_PWM_READ)
++ return -EIO;
++
++ *value = resp_buf[3];
++
++ return 0;
++}
++
++static int iei_wt61p803_puzzle_read(struct device *dev, enum hwmon_sensor_types type,
++ u32 attr, int channel, long *val)
++{
++ struct iei_wt61p803_puzzle_hwmon *mcu_hwmon = dev_get_drvdata(dev->parent);
++
++ switch (type) {
++ case hwmon_pwm:
++ return iei_wt61p803_puzzle_read_pwm_channel(mcu_hwmon, channel, val);
++ case hwmon_fan:
++ return iei_wt61p803_puzzle_read_fan_speed(mcu_hwmon, channel, val);
++ case hwmon_temp:
++ return iei_wt61p803_puzzle_read_temp_sensor(mcu_hwmon, channel, val);
++ default:
++ return -EINVAL;
++ }
++}
++
++static int iei_wt61p803_puzzle_write(struct device *dev, enum hwmon_sensor_types type,
++ u32 attr, int channel, long val)
++{
++ struct iei_wt61p803_puzzle_hwmon *mcu_hwmon = dev_get_drvdata(dev->parent);
++
++ return iei_wt61p803_puzzle_write_pwm_channel(mcu_hwmon, channel, val);
++}
++
++static umode_t iei_wt61p803_puzzle_is_visible(const void *data, enum hwmon_sensor_types type,
++ u32 attr, int channel)
++{
++ const struct iei_wt61p803_puzzle_hwmon *mcu_hwmon = data;
++
++ switch (type) {
++ case hwmon_pwm:
++ if (mcu_hwmon->thermal_cooling_dev_present[channel])
++ return 0444;
++ if (attr == hwmon_pwm_input)
++ return 0644;
++ break;
++ case hwmon_fan:
++ if (attr == hwmon_fan_input)
++ return 0444;
++ break;
++ case hwmon_temp:
++ if (attr == hwmon_temp_input)
++ return 0444;
++ break;
++ default:
++ return 0;
++ }
++
++ return 0;
++}
++
++static const struct hwmon_ops iei_wt61p803_puzzle_hwmon_ops = {
++ .is_visible = iei_wt61p803_puzzle_is_visible,
++ .read = iei_wt61p803_puzzle_read,
++ .write = iei_wt61p803_puzzle_write,
++};
++
++static const struct hwmon_channel_info *iei_wt61p803_puzzle_info[] = {
++ HWMON_CHANNEL_INFO(pwm,
++ HWMON_PWM_INPUT,
++ HWMON_PWM_INPUT),
++ HWMON_CHANNEL_INFO(fan,
++ HWMON_F_INPUT,
++ HWMON_F_INPUT,
++ HWMON_F_INPUT,
++ HWMON_F_INPUT,
++ HWMON_F_INPUT),
++ HWMON_CHANNEL_INFO(temp,
++ HWMON_T_INPUT,
++ HWMON_T_INPUT),
++ NULL
++};
++
++static const struct hwmon_chip_info iei_wt61p803_puzzle_chip_info = {
++ .ops = &iei_wt61p803_puzzle_hwmon_ops,
++ .info = iei_wt61p803_puzzle_info,
++};
++
++static int iei_wt61p803_puzzle_get_max_state(struct thermal_cooling_device *tcdev,
++ unsigned long *state)
++{
++ struct iei_wt61p803_puzzle_thermal_cooling_device *cdev = tcdev->devdata;
++
++ if (!cdev)
++ return -EINVAL;
++
++ *state = cdev->num_levels - 1;
++ return 0;
++}
++
++static int iei_wt61p803_puzzle_get_cur_state(struct thermal_cooling_device *tcdev,
++ unsigned long *state)
++{
++ struct iei_wt61p803_puzzle_thermal_cooling_device *cdev = tcdev->devdata;
++
++ if (!cdev)
++ return -EINVAL;
++
++ if (cdev->cur_level < 0)
++ return -EAGAIN;
++
++ *state = cdev->cur_level;
++ return 0;
++}
++
++static int iei_wt61p803_puzzle_set_cur_state(struct thermal_cooling_device *tcdev,
++ unsigned long state)
++{
++ struct iei_wt61p803_puzzle_thermal_cooling_device *cdev = tcdev->devdata;
++ u8 pwm_level;
++
++ if (!cdev)
++ return -EINVAL;
++
++ if (state >= cdev->num_levels)
++ return -EINVAL;
++
++ if (state == cdev->cur_level)
++ return 0;
++
++ cdev->cur_level = state;
++ pwm_level = cdev->cooling_levels[state];
++
++ return iei_wt61p803_puzzle_write_pwm_channel(cdev->mcu_hwmon, cdev->pwm_channel, pwm_level);
++}
++
++static const struct thermal_cooling_device_ops iei_wt61p803_puzzle_cooling_ops = {
++ .get_max_state = iei_wt61p803_puzzle_get_max_state,
++ .get_cur_state = iei_wt61p803_puzzle_get_cur_state,
++ .set_cur_state = iei_wt61p803_puzzle_set_cur_state,
++};
++
++static int
++iei_wt61p803_puzzle_enable_thermal_cooling_dev(struct device *dev,
++ struct fwnode_handle *child,
++ struct iei_wt61p803_puzzle_hwmon *mcu_hwmon)
++{
++ struct iei_wt61p803_puzzle_thermal_cooling_device *cdev;
++ u32 pwm_channel;
++ u8 num_levels;
++ int i, ret;
++
++ ret = fwnode_property_read_u32(child, "reg", &pwm_channel);
++ if (ret)
++ return ret;
++
++ mcu_hwmon->thermal_cooling_dev_present[pwm_channel] = true;
++
++ num_levels = fwnode_property_count_u32(child, "cooling-levels");
++ if (!num_levels)
++ return -EINVAL;
++
++ cdev = devm_kzalloc(dev, sizeof(*cdev), GFP_KERNEL);
++ if (!cdev)
++ return -ENOMEM;
++
++ cdev->cooling_levels = devm_kmalloc_array(dev, num_levels, sizeof(u32), GFP_KERNEL);
++ if (!cdev->cooling_levels)
++ return -ENOMEM;
++
++ ret = fwnode_property_read_u32_array(child, "cooling-levels",
++ cdev->cooling_levels,
++ num_levels);
++ if (ret) {
++ dev_err(dev, "Couldn't read property 'cooling-levels'\n");
++ return ret;
++ }
++
++ for (i = 0; i < num_levels; i++) {
++ if (cdev->cooling_levels[i] >
++ IEI_WT61P803_PUZZLE_HWMON_MAX_PWM_VAL) {
++ dev_err(dev, "iei_wt61p803_fan state[%d]:%d > %d\n", i,
++ cdev->cooling_levels[i],
++ IEI_WT61P803_PUZZLE_HWMON_MAX_PWM_VAL);
++ return -EINVAL;
++ }
++ }
++
++ cdev->mcu_hwmon = mcu_hwmon;
++ cdev->pwm_channel = pwm_channel;
++ cdev->num_levels = num_levels;
++ cdev->cur_level = -1;
++ mcu_hwmon->cdev[pwm_channel] = cdev;
++
++ snprintf(cdev->name, THERMAL_NAME_LENGTH, "wt61p803_puzzle_%d", pwm_channel);
++ cdev->tcdev = devm_thermal_of_cooling_device_register(dev, to_of_node(child), cdev->name,
++ cdev, &iei_wt61p803_puzzle_cooling_ops);
++ if (IS_ERR(cdev->tcdev))
++ return PTR_ERR(cdev->tcdev);
++
++ return 0;
++}
++
++static int iei_wt61p803_puzzle_hwmon_probe(struct platform_device *pdev)
++{
++ struct device *dev = &pdev->dev;
++ struct iei_wt61p803_puzzle *mcu = dev_get_drvdata(dev->parent);
++ struct iei_wt61p803_puzzle_hwmon *mcu_hwmon;
++ struct fwnode_handle *child;
++ struct device *hwmon_dev;
++ int ret;
++
++ mcu_hwmon = devm_kzalloc(dev, sizeof(*mcu_hwmon), GFP_KERNEL);
++ if (!mcu_hwmon)
++ return -ENOMEM;
++
++ mcu_hwmon->mcu = mcu;
++ platform_set_drvdata(pdev, mcu_hwmon);
++ mutex_init(&mcu_hwmon->lock);
++
++ hwmon_dev = devm_hwmon_device_register_with_info(dev, "iei_wt61p803_puzzle",
++ mcu_hwmon,
++ &iei_wt61p803_puzzle_chip_info,
++ NULL);
++ if (IS_ERR(hwmon_dev))
++ return PTR_ERR(hwmon_dev);
++
++ /* Control fans via PWM lines via Linux Kernel */
++ if (IS_ENABLED(CONFIG_THERMAL)) {
++ device_for_each_child_node(dev, child) {
++ ret = iei_wt61p803_puzzle_enable_thermal_cooling_dev(dev, child, mcu_hwmon);
++ if (ret) {
++ dev_err(dev, "Enabling the PWM fan failed\n");
++ fwnode_handle_put(child);
++ return ret;
++ }
++ }
++ }
++ return 0;
++}
++
++static const struct of_device_id iei_wt61p803_puzzle_hwmon_id_table[] = {
++ { .compatible = "iei,wt61p803-puzzle-hwmon" },
++ {}
++};
++MODULE_DEVICE_TABLE(of, iei_wt61p803_puzzle_hwmon_id_table);
++
++static struct platform_driver iei_wt61p803_puzzle_hwmon_driver = {
++ .driver = {
++ .name = "iei-wt61p803-puzzle-hwmon",
++ .of_match_table = iei_wt61p803_puzzle_hwmon_id_table,
++ },
++ .probe = iei_wt61p803_puzzle_hwmon_probe,
++};
++
++module_platform_driver(iei_wt61p803_puzzle_hwmon_driver);
++
++MODULE_DESCRIPTION("IEI WT61P803 PUZZLE MCU HWMON Driver");
++MODULE_AUTHOR("Luka Kovacic <luka.kovacic at sartura.hr>");
++MODULE_LICENSE("GPL v2");
diff --git a/target/linux/mvebu/patches-6.1/904-drivers-leds-Add-the-IEI-WT61P803-PUZZLE-LED-driver.patch b/target/linux/mvebu/patches-6.1/904-drivers-leds-Add-the-IEI-WT61P803-PUZZLE-LED-driver.patch
new file mode 100644
index 0000000000..a3fc731c47
--- /dev/null
+++ b/target/linux/mvebu/patches-6.1/904-drivers-leds-Add-the-IEI-WT61P803-PUZZLE-LED-driver.patch
@@ -0,0 +1,207 @@
+From f3b44eb69cc561cf05d00506dcec0dd9be003ed8 Mon Sep 17 00:00:00 2001
+From: Luka Kovacic <luka.kovacic () sartura ! hr>
+Date: Tue, 24 Aug 2021 12:44:35 +0000
+Subject: [PATCH 4/7] drivers: leds: Add the IEI WT61P803 PUZZLE LED driver
+
+Add support for the IEI WT61P803 PUZZLE LED driver.
+Currently only the front panel power LED is supported,
+since it is the only LED on this board wired through the
+MCU.
+
+The LED is wired directly to the on-board MCU controller
+and is toggled using an MCU command.
+
+Support for more LEDs is going to be added in case more
+boards implement this microcontroller, as LEDs use many
+different GPIOs.
+
+This driver depends on the IEI WT61P803 PUZZLE MFD driver.
+
+Signed-off-by: Luka Kovacic <luka.kovacic at sartura.hr>
+Signed-off-by: Pavo Banicevic <pavo.banicevic at sartura.hr>
+Cc: Luka Perkov <luka.perkov at sartura.hr>
+Cc: Robert Marko <robert.marko at sartura.hr>
+---
+ drivers/leds/Kconfig | 8 ++
+ drivers/leds/Makefile | 1 +
+ drivers/leds/leds-iei-wt61p803-puzzle.c | 147 ++++++++++++++++++++++++
+ 3 files changed, 156 insertions(+)
+ create mode 100644 drivers/leds/leds-iei-wt61p803-puzzle.c
+
+--- a/drivers/leds/Kconfig
++++ b/drivers/leds/Kconfig
+@@ -306,6 +306,14 @@ config LEDS_IPAQ_MICRO
+ Choose this option if you want to use the notification LED on
+ Compaq/HP iPAQ h3100 and h3600.
+
++config LEDS_IEI_WT61P803_PUZZLE
++ tristate "LED Support for the IEI WT61P803 PUZZLE MCU"
++ depends on LEDS_CLASS
++ depends on MFD_IEI_WT61P803_PUZZLE
++ help
++ This option enables support for LEDs controlled by the IEI WT61P803
++ M801 MCU.
++
+ config LEDS_HP6XX
+ tristate "LED Support for the HP Jornada 6xx"
+ depends on LEDS_CLASS
+--- a/drivers/leds/Makefile
++++ b/drivers/leds/Makefile
+@@ -33,6 +33,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
++obj-$(CONFIG_LEDS_IEI_WT61P803_PUZZLE) += leds-iei-wt61p803-puzzle.o
+ obj-$(CONFIG_LEDS_IS31FL319X) += leds-is31fl319x.o
+ obj-$(CONFIG_LEDS_IS31FL32XX) += leds-is31fl32xx.o
+ obj-$(CONFIG_LEDS_LM3530) += leds-lm3530.o
+--- /dev/null
++++ b/drivers/leds/leds-iei-wt61p803-puzzle.c
+@@ -0,0 +1,147 @@
++// SPDX-License-Identifier: GPL-2.0-only
++/* IEI WT61P803 PUZZLE MCU LED Driver
++ *
++ * Copyright (C) 2020 Sartura Ltd.
++ * Author: Luka Kovacic <luka.kovacic at sartura.hr>
++ */
++
++#include <linux/leds.h>
++#include <linux/mfd/iei-wt61p803-puzzle.h>
++#include <linux/mod_devicetable.h>
++#include <linux/module.h>
++#include <linux/platform_device.h>
++#include <linux/property.h>
++#include <linux/slab.h>
++
++enum iei_wt61p803_puzzle_led_state {
++ IEI_LED_OFF = 0x30,
++ IEI_LED_ON = 0x31,
++ IEI_LED_BLINK_5HZ = 0x32,
++ IEI_LED_BLINK_1HZ = 0x33,
++};
++
++/**
++ * struct iei_wt61p803_puzzle_led - MCU LED Driver
++ * @cdev: LED classdev
++ * @mcu: MCU struct pointer
++ * @response_buffer Global MCU response buffer
++ * @lock: General mutex lock to protect simultaneous R/W access to led_power_state
++ * @led_power_state: State of the front panel power LED
++ */
++struct iei_wt61p803_puzzle_led {
++ struct led_classdev cdev;
++ struct iei_wt61p803_puzzle *mcu;
++ unsigned char response_buffer[IEI_WT61P803_PUZZLE_BUF_SIZE];
++ struct mutex lock; /* mutex to protect led_power_state */
++ int led_power_state;
++};
++
++static inline struct iei_wt61p803_puzzle_led *cdev_to_iei_wt61p803_puzzle_led
++ (struct led_classdev *led_cdev)
++{
++ return container_of(led_cdev, struct iei_wt61p803_puzzle_led, cdev);
++}
++
++static int iei_wt61p803_puzzle_led_brightness_set_blocking(struct led_classdev *cdev,
++ enum led_brightness brightness)
++{
++ struct iei_wt61p803_puzzle_led *priv = cdev_to_iei_wt61p803_puzzle_led(cdev);
++ unsigned char *resp_buf = priv->response_buffer;
++ unsigned char led_power_cmd[5] = {};
++ size_t reply_size;
++ int ret;
++
++ led_power_cmd[0] = IEI_WT61P803_PUZZLE_CMD_HEADER_START;
++ led_power_cmd[1] = IEI_WT61P803_PUZZLE_CMD_LED;
++ led_power_cmd[2] = IEI_WT61P803_PUZZLE_CMD_LED_POWER;
++ led_power_cmd[3] = brightness == LED_OFF ? IEI_LED_OFF : IEI_LED_ON;
++
++ ret = iei_wt61p803_puzzle_write_command(priv->mcu, led_power_cmd,
++ sizeof(led_power_cmd),
++ resp_buf,
++ &reply_size);
++ if (ret)
++ return ret;
++
++ if (reply_size != 3)
++ return -EIO;
++
++ if (!(resp_buf[0] == IEI_WT61P803_PUZZLE_CMD_HEADER_START &&
++ resp_buf[1] == IEI_WT61P803_PUZZLE_CMD_RESPONSE_OK &&
++ resp_buf[2] == IEI_WT61P803_PUZZLE_CHECKSUM_RESPONSE_OK))
++ return -EIO;
++
++ mutex_lock(&priv->lock);
++ priv->led_power_state = brightness;
++ mutex_unlock(&priv->lock);
++
++ return 0;
++}
++
++static enum led_brightness iei_wt61p803_puzzle_led_brightness_get(struct led_classdev *cdev)
++{
++ struct iei_wt61p803_puzzle_led *priv = cdev_to_iei_wt61p803_puzzle_led(cdev);
++ int led_state;
++
++ mutex_lock(&priv->lock);
++ led_state = priv->led_power_state;
++ mutex_unlock(&priv->lock);
++
++ return led_state;
++}
++
++static int iei_wt61p803_puzzle_led_probe(struct platform_device *pdev)
++{
++ struct device *dev = &pdev->dev;
++ struct iei_wt61p803_puzzle *mcu = dev_get_drvdata(dev->parent);
++ struct iei_wt61p803_puzzle_led *priv;
++ struct led_init_data init_data = {};
++ struct fwnode_handle *child;
++ int ret;
++
++ if (device_get_child_node_count(dev) != 1)
++ return -EINVAL;
++
++ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
++ if (!priv)
++ return -ENOMEM;
++
++ priv->mcu = mcu;
++ priv->led_power_state = 1;
++ mutex_init(&priv->lock);
++ dev_set_drvdata(dev, priv);
++
++ child = device_get_next_child_node(dev, NULL);
++ init_data.fwnode = child;
++
++ priv->cdev.brightness_set_blocking = iei_wt61p803_puzzle_led_brightness_set_blocking;
++ priv->cdev.brightness_get = iei_wt61p803_puzzle_led_brightness_get;
++ priv->cdev.max_brightness = 1;
++
++ ret = devm_led_classdev_register_ext(dev, &priv->cdev, &init_data);
++ if (ret)
++ dev_err(dev, "Could not register LED\n");
++
++ fwnode_handle_put(child);
++ return ret;
++}
++
++static const struct of_device_id iei_wt61p803_puzzle_led_of_match[] = {
++ { .compatible = "iei,wt61p803-puzzle-leds" },
++ { }
++};
++MODULE_DEVICE_TABLE(of, iei_wt61p803_puzzle_led_of_match);
++
++static struct platform_driver iei_wt61p803_puzzle_led_driver = {
++ .driver = {
++ .name = "iei-wt61p803-puzzle-led",
++ .of_match_table = iei_wt61p803_puzzle_led_of_match,
++ },
++ .probe = iei_wt61p803_puzzle_led_probe,
++};
++module_platform_driver(iei_wt61p803_puzzle_led_driver);
++
++MODULE_DESCRIPTION("IEI WT61P803 PUZZLE front panel LED driver");
++MODULE_AUTHOR("Luka Kovacic <luka.kovacic at sartura.hr>");
++MODULE_LICENSE("GPL v2");
++MODULE_ALIAS("platform:leds-iei-wt61p803-puzzle");
diff --git a/target/linux/mvebu/patches-6.1/905-Documentation-ABI-Add-iei-wt61p803-puzzle-driver-sys.patch b/target/linux/mvebu/patches-6.1/905-Documentation-ABI-Add-iei-wt61p803-puzzle-driver-sys.patch
new file mode 100644
index 0000000000..b1d420ef0a
--- /dev/null
+++ b/target/linux/mvebu/patches-6.1/905-Documentation-ABI-Add-iei-wt61p803-puzzle-driver-sys.patch
@@ -0,0 +1,82 @@
+From 2fab3b4956c5b2f83c1e1abffc1df39de2933d83 Mon Sep 17 00:00:00 2001
+From: Luka Kovacic <luka.kovacic () sartura ! hr>
+Date: Tue, 24 Aug 2021 12:44:36 +0000
+Subject: [PATCH 5/7] Documentation/ABI: Add iei-wt61p803-puzzle driver sysfs
+ interface documentation
+
+Add the iei-wt61p803-puzzle driver sysfs interface documentation to allow
+monitoring and control of the microcontroller from user space.
+
+Signed-off-by: Luka Kovacic <luka.kovacic at sartura.hr>
+Signed-off-by: Pavo Banicevic <pavo.banicevic at sartura.hr>
+Cc: Luka Perkov <luka.perkov at sartura.hr>
+Cc: Robert Marko <robert.marko at sartura.hr>
+---
+ .../testing/sysfs-driver-iei-wt61p803-puzzle | 61 +++++++++++++++++++
+ 1 file changed, 61 insertions(+)
+ create mode 100644 Documentation/ABI/testing/sysfs-driver-iei-wt61p803-puzzle
+
+--- /dev/null
++++ b/Documentation/ABI/testing/sysfs-driver-iei-wt61p803-puzzle
+@@ -0,0 +1,61 @@
++What: /sys/bus/serial/devices/.../mac_address_*
++Date: September 2020
++Contact: Luka Kovacic <luka.kovacic at sartura.hr>
++Description: (RW) Internal factory assigned MAC address values
++
++What: /sys/bus/serial/devices/.../serial_number
++Date: September 2020
++Contact: Luka Kovacic <luka.kovacic at sartura.hr>
++Description: (RW) Internal factory assigned serial number
++
++What: /sys/bus/serial/devices/.../version
++Date: September 2020
++Contact: Luka Kovacic <luka.kovacic at sartura.hr>
++Description: (RO) Internal MCU firmware version
++
++What: /sys/bus/serial/devices/.../protocol_version
++Date: September 2020
++Contact: Luka Kovacic <luka.kovacic at sartura.hr>
++Description: (RO) Internal MCU communication protocol version
++
++What: /sys/bus/serial/devices/.../power_loss_recovery
++Date: September 2020
++Contact: Luka Kovacic <luka.kovacic at sartura.hr>
++Description: (RW) Host platform power loss recovery settings
++ Value mapping: 0 - Always-On, 1 - Always-Off, 2 - Always-AC, 3 - Always-WA
++
++What: /sys/bus/serial/devices/.../bootloader_mode
++Date: September 2020
++Contact: Luka Kovacic <luka.kovacic at sartura.hr>
++Description: (RO) Internal MCU bootloader mode status
++ Value mapping:
++ 0 - normal mode
++ 1 - bootloader mode
++
++What: /sys/bus/serial/devices/.../power_status
++Date: September 2020
++Contact: Luka Kovacic <luka.kovacic at sartura.hr>
++Description: (RO) Power status indicates the host platform power on method.
++ Value mapping (bitwise list):
++ 0x80 - Null
++ 0x40 - Firmware flag
++ 0x20 - Power loss detection flag (powered off)
++ 0x10 - Power loss detection flag (AC mode)
++ 0x08 - Button power on
++ 0x04 - Wake-on-LAN power on
++ 0x02 - RTC alarm power on
++ 0x01 - AC recover power on
++
++What: /sys/bus/serial/devices/.../build_info
++Date: September 2020
++Contact: Luka Kovacic <luka.kovacic at sartura.hr>
++Description: (RO) Internal MCU firmware build date
++ Format: yyyy/mm/dd hh:mm
++
++What: /sys/bus/serial/devices/.../ac_recovery_status
++Date: September 2020
++Contact: Luka Kovacic <luka.kovacic at sartura.hr>
++Description: (RO) Host platform AC recovery status value
++ Value mapping:
++ 0 - board has not been recovered from power down
++ 1 - board has been recovered from power down
diff --git a/target/linux/mvebu/patches-6.1/906-Documentation-hwmon-Add-iei-wt61p803-puzzle-hwmon-dr.patch b/target/linux/mvebu/patches-6.1/906-Documentation-hwmon-Add-iei-wt61p803-puzzle-hwmon-dr.patch
new file mode 100644
index 0000000000..9015436f6a
--- /dev/null
+++ b/target/linux/mvebu/patches-6.1/906-Documentation-hwmon-Add-iei-wt61p803-puzzle-hwmon-dr.patch
@@ -0,0 +1,74 @@
+From 0aff3e5923fecc6842473ad07a688d6e2f2c2d55 Mon Sep 17 00:00:00 2001
+From: Luka Kovacic <luka.kovacic () sartura ! hr>
+Date: Tue, 24 Aug 2021 12:44:37 +0000
+Subject: [PATCH 6/7] Documentation/hwmon: Add iei-wt61p803-puzzle hwmon driver
+ documentation
+
+Add the iei-wt61p803-puzzle driver hwmon driver interface documentation.
+
+Signed-off-by: Luka Kovacic <luka.kovacic at sartura.hr>
+Signed-off-by: Pavo Banicevic <pavo.banicevic at sartura.hr>
+Cc: Luka Perkov <luka.perkov at sartura.hr>
+Cc: Robert Marko <robert.marko at sartura.hr>
+---
+ .../hwmon/iei-wt61p803-puzzle-hwmon.rst | 43 +++++++++++++++++++
+ Documentation/hwmon/index.rst | 1 +
+ 2 files changed, 44 insertions(+)
+ create mode 100644 Documentation/hwmon/iei-wt61p803-puzzle-hwmon.rst
+
+--- /dev/null
++++ b/Documentation/hwmon/iei-wt61p803-puzzle-hwmon.rst
+@@ -0,0 +1,43 @@
++.. SPDX-License-Identifier: GPL-2.0-only
++
++Kernel driver iei-wt61p803-puzzle-hwmon
++=======================================
++
++Supported chips:
++ * IEI WT61P803 PUZZLE for IEI Puzzle M801
++
++ Prefix: 'iei-wt61p803-puzzle-hwmon'
++
++Author: Luka Kovacic <luka.kovacic at sartura.hr>
++
++
++Description
++-----------
++
++This driver adds fan and temperature sensor reading for some IEI Puzzle
++series boards.
++
++Sysfs attributes
++----------------
++
++The following attributes are supported:
++
++- IEI WT61P803 PUZZLE for IEI Puzzle M801
++
++/sys files in hwmon subsystem
++-----------------------------
++
++================= == =====================================================
++fan[1-5]_input RO files for fan speed (in RPM)
++pwm[1-2] RW files for fan[1-2] target duty cycle (0..255)
++temp[1-2]_input RO files for temperature sensors, in millidegree Celsius
++================= == =====================================================
++
++/sys files in thermal subsystem
++-------------------------------
++
++================= == =====================================================
++cur_state RW file for current cooling state of the cooling device
++ (0..max_state)
++max_state RO file for maximum cooling state of the cooling device
++================= == =====================================================
+--- a/Documentation/hwmon/index.rst
++++ b/Documentation/hwmon/index.rst
+@@ -74,6 +74,7 @@ Hardware Monitoring Kernel Drivers
+ ibmaem
+ ibm-cffps
+ ibmpowernv
++ iei-wt61p803-puzzle-hwmon
+ ina209
+ ina2xx
+ ina3221
diff --git a/target/linux/mvebu/patches-6.1/907-MAINTAINERS-Add-an-entry-for-the-IEI-WT61P803-PUZZLE.patch b/target/linux/mvebu/patches-6.1/907-MAINTAINERS-Add-an-entry-for-the-IEI-WT61P803-PUZZLE.patch
new file mode 100644
index 0000000000..b8accf180b
--- /dev/null
+++ b/target/linux/mvebu/patches-6.1/907-MAINTAINERS-Add-an-entry-for-the-IEI-WT61P803-PUZZLE.patch
@@ -0,0 +1,41 @@
+From 12479baad28d2a08c6cb9e83471057635fa1635c Mon Sep 17 00:00:00 2001
+From: Luka Kovacic <luka.kovacic () sartura ! hr>
+Date: Tue, 24 Aug 2021 12:44:38 +0000
+Subject: [PATCH 7/7] MAINTAINERS: Add an entry for the IEI WT61P803 PUZZLE
+ driver
+
+Add an entry for the IEI WT61P803 PUZZLE driver (MFD, HWMON, LED drivers).
+
+Signed-off-by: Luka Kovacic <luka.kovacic at sartura.hr>
+Signed-off-by: Pavo Banicevic <pavo.banicevic at sartura.hr>
+Cc: Luka Perkov <luka.perkov at sartura.hr>
+Cc: Robert Marko <robert.marko at sartura.hr>
+---
+ MAINTAINERS | 16 ++++++++++++++++
+ 1 file changed, 16 insertions(+)
+
+--- a/MAINTAINERS
++++ b/MAINTAINERS
+@@ -9063,6 +9063,22 @@ F: include/net/nl802154.h
+ F: net/ieee802154/
+ F: net/mac802154/
+
++IEI WT61P803 M801 MFD DRIVER
++M: Luka Kovacic <luka.kovacic at sartura.hr>
++M: Luka Perkov <luka.perkov at sartura.hr>
++M: Goran Medic <goran.medic at sartura.hr>
++L: linux-kernel at vger.kernel.org
++S: Maintained
++F: Documentation/ABI/stable/sysfs-driver-iei-wt61p803-puzzle
++F: Documentation/devicetree/bindings/hwmon/iei,wt61p803-puzzle-hwmon.yaml
++F: Documentation/devicetree/bindings/leds/iei,wt61p803-puzzle-leds.yaml
++F: Documentation/devicetree/bindings/mfd/iei,wt61p803-puzzle.yaml
++F: Documentation/hwmon/iei-wt61p803-puzzle-hwmon.rst
++F: drivers/hwmon/iei-wt61p803-puzzle-hwmon.c
++F: drivers/leds/leds-iei-wt61p803-puzzle.c
++F: drivers/mfd/iei-wt61p803-puzzle.c
++F: include/linux/mfd/iei-wt61p803-puzzle.h
++
+ IFE PROTOCOL
+ M: Yotam Gigi <yotam.gi at gmail.com>
+ M: Jamal Hadi Salim <jhs at mojatatu.com>
diff --git a/target/linux/mvebu/patches-6.1/910-drivers-leds-wt61p803-puzzle-improvements.patch b/target/linux/mvebu/patches-6.1/910-drivers-leds-wt61p803-puzzle-improvements.patch
new file mode 100644
index 0000000000..150a65498c
--- /dev/null
+++ b/target/linux/mvebu/patches-6.1/910-drivers-leds-wt61p803-puzzle-improvements.patch
@@ -0,0 +1,271 @@
+--- a/drivers/leds/leds-iei-wt61p803-puzzle.c
++++ b/drivers/leds/leds-iei-wt61p803-puzzle.c
+@@ -9,9 +9,13 @@
+ #include <linux/mfd/iei-wt61p803-puzzle.h>
+ #include <linux/mod_devicetable.h>
+ #include <linux/module.h>
++#include <linux/of.h>
+ #include <linux/platform_device.h>
+ #include <linux/property.h>
+ #include <linux/slab.h>
++#include <linux/workqueue.h>
++
++#define IEI_LEDS_MAX 4
+
+ enum iei_wt61p803_puzzle_led_state {
+ IEI_LED_OFF = 0x30,
+@@ -33,7 +37,11 @@ struct iei_wt61p803_puzzle_led {
+ struct iei_wt61p803_puzzle *mcu;
+ unsigned char response_buffer[IEI_WT61P803_PUZZLE_BUF_SIZE];
+ struct mutex lock; /* mutex to protect led_power_state */
++ struct work_struct work;
+ int led_power_state;
++ int id;
++ u8 blinking;
++ bool active_low;
+ };
+
+ static inline struct iei_wt61p803_puzzle_led *cdev_to_iei_wt61p803_puzzle_led
+@@ -51,10 +59,18 @@ static int iei_wt61p803_puzzle_led_brigh
+ size_t reply_size;
+ int ret;
+
++ if (priv->blinking) {
++ if (brightness == LED_OFF)
++ priv->blinking = 0;
++ else
++ return 0;
++ }
++
+ led_power_cmd[0] = IEI_WT61P803_PUZZLE_CMD_HEADER_START;
+ led_power_cmd[1] = IEI_WT61P803_PUZZLE_CMD_LED;
+- led_power_cmd[2] = IEI_WT61P803_PUZZLE_CMD_LED_POWER;
+- led_power_cmd[3] = brightness == LED_OFF ? IEI_LED_OFF : IEI_LED_ON;
++ led_power_cmd[2] = IEI_WT61P803_PUZZLE_CMD_LED_SET(priv->id);
++ led_power_cmd[3] = ((brightness == LED_OFF) ^ priv->active_low) ?
++ IEI_LED_OFF : priv->blinking?priv->blinking:IEI_LED_ON;
+
+ ret = iei_wt61p803_puzzle_write_command(priv->mcu, led_power_cmd,
+ sizeof(led_power_cmd),
+@@ -90,39 +106,166 @@ static enum led_brightness iei_wt61p803_
+ return led_state;
+ }
+
++static void iei_wt61p803_puzzle_led_apply_blink(struct work_struct *work)
++{
++ struct iei_wt61p803_puzzle_led *priv = container_of(work, struct iei_wt61p803_puzzle_led, work);
++ unsigned char led_blink_cmd[5] = {};
++ unsigned char resp_buf[IEI_WT61P803_PUZZLE_BUF_SIZE];
++ size_t reply_size;
++
++ led_blink_cmd[0] = IEI_WT61P803_PUZZLE_CMD_HEADER_START;
++ led_blink_cmd[1] = IEI_WT61P803_PUZZLE_CMD_LED;
++ led_blink_cmd[2] = IEI_WT61P803_PUZZLE_CMD_LED_SET(priv->id);
++ led_blink_cmd[3] = priv->blinking;
++
++ iei_wt61p803_puzzle_write_command(priv->mcu, led_blink_cmd,
++ sizeof(led_blink_cmd),
++ resp_buf,
++ &reply_size);
++
++ return;
++}
++
++static int iei_wt61p803_puzzle_led_set_blink(struct led_classdev *cdev,
++ unsigned long *delay_on,
++ unsigned long *delay_off)
++{
++ struct iei_wt61p803_puzzle_led *priv = cdev_to_iei_wt61p803_puzzle_led(cdev);
++ u8 blink_mode = 0;
++ int ret = 0;
++
++ /* set defaults */
++ if (!*delay_on && !*delay_off) {
++ *delay_on = 500;
++ *delay_off = 500;
++ }
++
++ /* minimum delay for soft-driven blinking is 100ms to keep load low */
++ if (*delay_on < 100)
++ *delay_on = 100;
++
++ if (*delay_off < 100)
++ *delay_off = 100;
++
++ /* offload blinking to hardware, if possible */
++ if (*delay_on != *delay_off) {
++ ret = -EINVAL;
++ } else if (*delay_on == 100) {
++ blink_mode = IEI_LED_BLINK_5HZ;
++ *delay_on = 100;
++ *delay_off = 100;
++ } else if (*delay_on <= 500) {
++ blink_mode = IEI_LED_BLINK_1HZ;
++ *delay_on = 500;
++ *delay_off = 500;
++ } else {
++ ret = -EINVAL;
++ }
++
++ mutex_lock(&priv->lock);
++ priv->blinking = blink_mode;
++ mutex_unlock(&priv->lock);
++
++ if (blink_mode)
++ schedule_work(&priv->work);
++
++ return ret;
++}
++
++
++static int iei_wt61p803_puzzle_led_set_dt_default(struct led_classdev *cdev,
++ struct device_node *np)
++{
++ const char *state;
++ int ret = 0;
++
++ state = of_get_property(np, "default-state", NULL);
++ if (state) {
++ if (!strcmp(state, "on")) {
++ ret =
++ iei_wt61p803_puzzle_led_brightness_set_blocking(
++ cdev, cdev->max_brightness);
++ } else {
++ ret = iei_wt61p803_puzzle_led_brightness_set_blocking(
++ cdev, LED_OFF);
++ }
++ }
++
++ return ret;
++}
++
+ static int iei_wt61p803_puzzle_led_probe(struct platform_device *pdev)
+ {
+ struct device *dev = &pdev->dev;
++ struct device_node *np = dev_of_node(dev);
++ struct device_node *child;
+ struct iei_wt61p803_puzzle *mcu = dev_get_drvdata(dev->parent);
+ struct iei_wt61p803_puzzle_led *priv;
+- struct led_init_data init_data = {};
+- struct fwnode_handle *child;
+ int ret;
++ u32 reg;
+
+- if (device_get_child_node_count(dev) != 1)
++ if (device_get_child_node_count(dev) > IEI_LEDS_MAX)
+ return -EINVAL;
+
+- priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+- if (!priv)
+- return -ENOMEM;
+-
+- priv->mcu = mcu;
+- priv->led_power_state = 1;
+- mutex_init(&priv->lock);
+- dev_set_drvdata(dev, priv);
+-
+- child = device_get_next_child_node(dev, NULL);
+- init_data.fwnode = child;
+-
+- priv->cdev.brightness_set_blocking = iei_wt61p803_puzzle_led_brightness_set_blocking;
+- priv->cdev.brightness_get = iei_wt61p803_puzzle_led_brightness_get;
+- priv->cdev.max_brightness = 1;
++ for_each_available_child_of_node(np, child) {
++ struct led_init_data init_data = {};
+
+- ret = devm_led_classdev_register_ext(dev, &priv->cdev, &init_data);
+- if (ret)
+- dev_err(dev, "Could not register LED\n");
++ ret = of_property_read_u32(child, "reg", ®);
++ if (ret) {
++ dev_err(dev, "Failed to read led 'reg' property\n");
++ goto put_child_node;
++ }
++
++ if (reg > IEI_LEDS_MAX) {
++ dev_err(dev, "Invalid led reg %u\n", reg);
++ ret = -EINVAL;
++ goto put_child_node;
++ }
++
++ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
++ if (!priv) {
++ ret = -ENOMEM;
++ goto put_child_node;
++ }
++
++ mutex_init(&priv->lock);
++
++ dev_set_drvdata(dev, priv);
++
++ if (of_property_read_bool(child, "active-low"))
++ priv->active_low = true;
++
++ priv->mcu = mcu;
++ priv->id = reg;
++ priv->led_power_state = 1;
++ priv->blinking = 0;
++ init_data.fwnode = of_fwnode_handle(child);
++
++ priv->cdev.brightness_set_blocking = iei_wt61p803_puzzle_led_brightness_set_blocking;
++ priv->cdev.brightness_get = iei_wt61p803_puzzle_led_brightness_get;
++ priv->cdev.blink_set = iei_wt61p803_puzzle_led_set_blink;
++
++ priv->cdev.max_brightness = 1;
++
++ INIT_WORK(&priv->work, iei_wt61p803_puzzle_led_apply_blink);
++
++ ret = iei_wt61p803_puzzle_led_set_dt_default(&priv->cdev, child);
++ if (ret) {
++ dev_err(dev, "Could apply default from DT\n");
++ goto put_child_node;
++ }
++
++ ret = devm_led_classdev_register_ext(dev, &priv->cdev, &init_data);
++ if (ret) {
++ dev_err(dev, "Could not register LED\n");
++ goto put_child_node;
++ }
++ }
++
++ return ret;
+
+- fwnode_handle_put(child);
++put_child_node:
++ of_node_put(child);
+ return ret;
+ }
+
+--- a/include/linux/mfd/iei-wt61p803-puzzle.h
++++ b/include/linux/mfd/iei-wt61p803-puzzle.h
+@@ -36,7 +36,7 @@
+ #define IEI_WT61P803_PUZZLE_CMD_FUNCTION_OTHER_POWER_LOSS 0x41 /* A */
+
+ #define IEI_WT61P803_PUZZLE_CMD_LED 0x52 /* R */
+-#define IEI_WT61P803_PUZZLE_CMD_LED_POWER 0x31 /* 1 */
++#define IEI_WT61P803_PUZZLE_CMD_LED_SET(n) (0x30 | (n))
+
+ #define IEI_WT61P803_PUZZLE_CMD_TEMP 0x54 /* T */
+ #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(
+ struct iei_wt61p803_puzzle *mcu = serdev_device_get_drvdata(serdev);
+ int ret;
+
++ print_hex_dump_debug("puzzle-mcu rx: ", DUMP_PREFIX_NONE,
++ 16, 1, data, size, false);
++
+ ret = iei_wt61p803_puzzle_process_resp(mcu, data, size);
+ /* Return the number of processed bytes if function returns error,
+ * discard the remaining incoming data, since the frame this data
+@@ -246,6 +249,9 @@ int iei_wt61p803_puzzle_write_command(st
+
+ cmd[size - 1] = iei_wt61p803_puzzle_checksum(cmd, size - 1);
+
++ print_hex_dump_debug("puzzle-mcu tx: ", DUMP_PREFIX_NONE,
++ 16, 1, cmd, size, false);
++
+ /* Initialize reply struct */
+ reinit_completion(&mcu->reply->received);
+ mcu->reply->size = 0;
diff --git a/target/linux/mvebu/patches-6.1/911-drivers-leds-wt61p803-puzzle-mcu-retry.patch b/target/linux/mvebu/patches-6.1/911-drivers-leds-wt61p803-puzzle-mcu-retry.patch
new file mode 100644
index 0000000000..2f0b1788ff
--- /dev/null
+++ b/target/linux/mvebu/patches-6.1/911-drivers-leds-wt61p803-puzzle-mcu-retry.patch
@@ -0,0 +1,63 @@
+--- a/drivers/mfd/iei-wt61p803-puzzle.c
++++ b/drivers/mfd/iei-wt61p803-puzzle.c
+@@ -241,6 +241,7 @@ int iei_wt61p803_puzzle_write_command(st
+ {
+ struct device *dev = &mcu->serdev->dev;
+ int ret;
++ int retries;
+
+ if (size <= 1 || size > IEI_WT61P803_PUZZLE_MAX_COMMAND_LENGTH)
+ return -EINVAL;
+@@ -252,24 +253,36 @@ int iei_wt61p803_puzzle_write_command(st
+ print_hex_dump_debug("puzzle-mcu tx: ", DUMP_PREFIX_NONE,
+ 16, 1, cmd, size, false);
+
++ retries = 3;
+ /* Initialize reply struct */
+- reinit_completion(&mcu->reply->received);
+- mcu->reply->size = 0;
+- usleep_range(2000, 10000);
+- serdev_device_write_flush(mcu->serdev);
+- ret = serdev_device_write_buf(mcu->serdev, cmd, size);
+- if (ret < 0)
+- goto exit;
+-
+- serdev_device_wait_until_sent(mcu->serdev, IEI_WT61P803_PUZZLE_GENERAL_TIMEOUT);
+- ret = wait_for_completion_timeout(&mcu->reply->received,
+- IEI_WT61P803_PUZZLE_GENERAL_TIMEOUT);
+- if (ret == 0) {
+- dev_err(dev, "Command reply receive timeout\n");
+- ret = -ETIMEDOUT;
+- goto exit;
++ while (retries) {
++ reinit_completion(&mcu->reply->received);
++ mcu->reply->size = 0;
++ usleep_range(2000, 10000);
++ serdev_device_write_flush(mcu->serdev);
++ ret = serdev_device_write_buf(mcu->serdev, cmd, size);
++ if (ret < 0)
++ goto exit;
++
++ serdev_device_wait_until_sent(mcu->serdev, IEI_WT61P803_PUZZLE_GENERAL_TIMEOUT);
++ ret = wait_for_completion_timeout(&mcu->reply->received,
++ IEI_WT61P803_PUZZLE_GENERAL_TIMEOUT);
++ retries--;
++ if (ret == 0) {
++ if (retries == 0) {
++ dev_err(dev, "Command reply receive timeout\n");
++ ret = -ETIMEDOUT;
++ goto exit;
++ }
++ }
++ else {
++ if (mcu->reply->data[0] == IEI_WT61P803_PUZZLE_CMD_HEADER_START &&
++ mcu->reply->data[1] == IEI_WT61P803_PUZZLE_CMD_RESPONSE_OK &&
++ mcu->reply->data[2] == IEI_WT61P803_PUZZLE_CHECKSUM_RESPONSE_OK) {
++ break;
++ }
++ }
+ }
+-
+ *reply_size = mcu->reply->size;
+ /* Copy the received data, as it will not be available after a new frame is received */
+ memcpy(reply_data, mcu->reply->data, mcu->reply->size);
More information about the lede-commits
mailing list