FEATURES:=fpu usb pci pcie gpio nand squashfs ramdisk boot-part rootfs-part legacy-sdcard targz
SUBTARGETS:=cortexa9 cortexa53 cortexa72
-KERNEL_PATCHVER:=6.6
-KERNEL_TESTING_PATCHVER:=6.12
+KERNEL_PATCHVER:=6.12
include $(INCLUDE_DIR)/target.mk
+++ /dev/null
-CONFIG_AHCI_MVEBU=y
-CONFIG_ALIGNMENT_TRAP=y
-CONFIG_ARCH_32BIT_OFF_T=y
-CONFIG_ARCH_HIBERNATION_POSSIBLE=y
-CONFIG_ARCH_KEEP_MEMBLOCK=y
-CONFIG_ARCH_MIGHT_HAVE_PC_PARPORT=y
-CONFIG_ARCH_MULTIPLATFORM=y
-CONFIG_ARCH_MULTI_V6_V7=y
-CONFIG_ARCH_MULTI_V7=y
-CONFIG_ARCH_MVEBU=y
-CONFIG_ARCH_NR_GPIO=0
-CONFIG_ARCH_OPTIONAL_KERNEL_RWX=y
-CONFIG_ARCH_OPTIONAL_KERNEL_RWX_DEFAULT=y
-CONFIG_ARCH_SELECT_MEMORY_MODEL=y
-CONFIG_ARCH_SPARSEMEM_ENABLE=y
-CONFIG_ARCH_SUSPEND_POSSIBLE=y
-CONFIG_ARM=y
-CONFIG_ARMADA_370_CLK=y
-CONFIG_ARMADA_370_XP_IRQ=y
-CONFIG_ARMADA_370_XP_TIMER=y
-# CONFIG_ARMADA_37XX_WATCHDOG is not set
-CONFIG_ARMADA_38X_CLK=y
-CONFIG_ARMADA_THERMAL=y
-CONFIG_ARMADA_XP_CLK=y
-CONFIG_ARM_APPENDED_DTB=y
-# CONFIG_ARM_ARMADA_37XX_CPUFREQ is not set
-# CONFIG_ARM_ARMADA_8K_CPUFREQ is not set
-CONFIG_ARM_ATAG_DTB_COMPAT=y
-# CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_FROM_BOOTLOADER is not set
-CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_MANGLE=y
-CONFIG_ARM_CPU_SUSPEND=y
-CONFIG_ARM_ERRATA_720789=y
-CONFIG_ARM_ERRATA_764369=y
-CONFIG_ARM_GIC=y
-CONFIG_ARM_GLOBAL_TIMER=y
-CONFIG_ARM_GT_INITIAL_PRESCALER_VAL=1
-CONFIG_ARM_HEAVY_MB=y
-CONFIG_ARM_L1_CACHE_SHIFT=6
-CONFIG_ARM_L1_CACHE_SHIFT_6=y
-CONFIG_ARM_MVEBU_V7_CPUIDLE=y
-CONFIG_ARM_PATCH_IDIV=y
-CONFIG_ARM_PATCH_PHYS_VIRT=y
-CONFIG_ARM_THUMB=y
-CONFIG_ARM_UNWIND=y
-CONFIG_ARM_VIRT_EXT=y
-CONFIG_ASYNC_TX_ENABLE_CHANNEL_SWITCH=y
-CONFIG_ATA=y
-CONFIG_ATAGS=y
-CONFIG_ATA_LEDS=y
-CONFIG_AUTO_ZRELADDR=y
-CONFIG_BINFMT_FLAT_ARGVP_ENVP_ON_STACK=y
-CONFIG_BLK_DEV_LOOP=y
-CONFIG_BLK_DEV_NVME=y
-CONFIG_BLK_DEV_SD=y
-CONFIG_BLK_MQ_PCI=y
-CONFIG_BOUNCE=y
-# CONFIG_CACHE_FEROCEON_L2 is not set
-CONFIG_CACHE_L2X0=y
-CONFIG_CC_HAVE_STACKPROTECTOR_TLS=y
-CONFIG_CLKSRC_ARM_GLOBAL_TIMER_SCHED_CLOCK=y
-CONFIG_CLKSRC_MMIO=y
-CONFIG_CLONE_BACKWARDS=y
-CONFIG_COMMON_CLK=y
-CONFIG_COMPACT_UNEVICTABLE_DEFAULT=1
-CONFIG_COMPAT_32BIT_TIME=y
-CONFIG_CONTEXT_TRACKING=y
-CONFIG_CONTEXT_TRACKING_IDLE=y
-CONFIG_CPUFREQ_DT=y
-CONFIG_CPUFREQ_DT_PLATDEV=y
-CONFIG_CPU_32v6K=y
-CONFIG_CPU_32v7=y
-CONFIG_CPU_ABRT_EV7=y
-CONFIG_CPU_CACHE_V7=y
-CONFIG_CPU_CACHE_VIPT=y
-CONFIG_CPU_COPY_V6=y
-CONFIG_CPU_CP15=y
-CONFIG_CPU_CP15_MMU=y
-CONFIG_CPU_FREQ=y
-CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND=y
-# CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE is not set
-CONFIG_CPU_FREQ_GOV_ATTR_SET=y
-CONFIG_CPU_FREQ_GOV_COMMON=y
-# CONFIG_CPU_FREQ_GOV_CONSERVATIVE is not set
-CONFIG_CPU_FREQ_GOV_ONDEMAND=y
-CONFIG_CPU_FREQ_GOV_PERFORMANCE=y
-# CONFIG_CPU_FREQ_GOV_POWERSAVE is not set
-# CONFIG_CPU_FREQ_GOV_USERSPACE is not set
-CONFIG_CPU_FREQ_STAT=y
-CONFIG_CPU_HAS_ASID=y
-CONFIG_CPU_IDLE=y
-CONFIG_CPU_IDLE_GOV_LADDER=y
-CONFIG_CPU_PABRT_V7=y
-CONFIG_CPU_PJ4B=y
-CONFIG_CPU_PM=y
-CONFIG_CPU_RMAP=y
-CONFIG_CPU_SPECTRE=y
-CONFIG_CPU_THERMAL=y
-CONFIG_CPU_THUMB_CAPABLE=y
-CONFIG_CPU_TLB_V7=y
-CONFIG_CPU_V7=y
-CONFIG_CRC16=y
-CONFIG_CRYPTO_AES_ARM=y
-CONFIG_CRYPTO_AES_ARM_BS=y
-CONFIG_CRYPTO_AUTHENC=y
-CONFIG_CRYPTO_CBC=y
-CONFIG_CRYPTO_CRC32=y
-CONFIG_CRYPTO_CRC32C=y
-CONFIG_CRYPTO_CRYPTD=y
-CONFIG_CRYPTO_DEFLATE=y
-CONFIG_CRYPTO_DES=y
-CONFIG_CRYPTO_DEV_MARVELL=y
-CONFIG_CRYPTO_DEV_MARVELL_CESA=y
-CONFIG_CRYPTO_ESSIV=y
-CONFIG_CRYPTO_HASH_INFO=y
-CONFIG_CRYPTO_HW=y
-CONFIG_CRYPTO_LIB_BLAKE2S_GENERIC=y
-CONFIG_CRYPTO_LIB_DES=y
-CONFIG_CRYPTO_LIB_SHA1=y
-CONFIG_CRYPTO_LIB_UTILS=y
-CONFIG_CRYPTO_LZO=y
-CONFIG_CRYPTO_RNG2=y
-CONFIG_CRYPTO_SHA1=y
-CONFIG_CRYPTO_SHA1_ARM=y
-CONFIG_CRYPTO_SHA1_ARM_NEON=y
-CONFIG_CRYPTO_SHA256_ARM=y
-CONFIG_CRYPTO_SHA512_ARM=y
-CONFIG_CRYPTO_SIMD=y
-CONFIG_CRYPTO_ZSTD=y
-CONFIG_DCACHE_WORD_ACCESS=y
-CONFIG_DEBUG_ALIGN_RODATA=y
-CONFIG_DEBUG_INFO=y
-CONFIG_DEBUG_LL=y
-CONFIG_DEBUG_LL_INCLUDE="debug/8250.S"
-CONFIG_DEBUG_MVEBU_UART0=y
-# CONFIG_DEBUG_MVEBU_UART0_ALTERNATE is not set
-# CONFIG_DEBUG_MVEBU_UART1_ALTERNATE is not set
-CONFIG_DEBUG_UART_8250=y
-CONFIG_DEBUG_UART_8250_SHIFT=2
-CONFIG_DEBUG_UART_PHYS=0xd0012000
-CONFIG_DEBUG_UART_VIRT=0xfec12000
-CONFIG_DEBUG_USER=y
-CONFIG_DMADEVICES=y
-CONFIG_DMA_ENGINE=y
-CONFIG_DMA_ENGINE_RAID=y
-CONFIG_DMA_OF=y
-CONFIG_DMA_OPS=y
-CONFIG_DTC=y
-CONFIG_EARLY_PRINTK=y
-CONFIG_EDAC_ATOMIC_SCRUB=y
-CONFIG_EDAC_SUPPORT=y
-CONFIG_EXCLUSIVE_SYSTEM_RAM=y
-CONFIG_EXT4_FS=y
-CONFIG_EXTCON=y
-CONFIG_F2FS_FS=y
-CONFIG_FIXED_PHY=y
-CONFIG_FIX_EARLYCON_MEM=y
-CONFIG_FS_IOMAP=y
-CONFIG_FS_MBCACHE=y
-CONFIG_FWNODE_MDIO=y
-CONFIG_FW_LOADER_PAGED_BUF=y
-CONFIG_FW_LOADER_SYSFS=y
-CONFIG_GENERIC_ALLOCATOR=y
-CONFIG_GENERIC_ARCH_TOPOLOGY=y
-CONFIG_GENERIC_BUG=y
-CONFIG_GENERIC_CLOCKEVENTS=y
-CONFIG_GENERIC_CLOCKEVENTS_BROADCAST=y
-CONFIG_GENERIC_CPU_AUTOPROBE=y
-CONFIG_GENERIC_CPU_VULNERABILITIES=y
-CONFIG_GENERIC_EARLY_IOREMAP=y
-CONFIG_GENERIC_GETTIMEOFDAY=y
-CONFIG_GENERIC_IDLE_POLL_SETUP=y
-CONFIG_GENERIC_IRQ_CHIP=y
-CONFIG_GENERIC_IRQ_EFFECTIVE_AFF_MASK=y
-CONFIG_GENERIC_IRQ_MIGRATION=y
-CONFIG_GENERIC_IRQ_MULTI_HANDLER=y
-CONFIG_GENERIC_IRQ_SHOW=y
-CONFIG_GENERIC_IRQ_SHOW_LEVEL=y
-CONFIG_GENERIC_LIB_DEVMEM_IS_ALLOWED=y
-CONFIG_GENERIC_MSI_IRQ=y
-CONFIG_GENERIC_MSI_IRQ_DOMAIN=y
-CONFIG_GENERIC_PCI_IOMAP=y
-CONFIG_GENERIC_PHY=y
-CONFIG_GENERIC_SCHED_CLOCK=y
-CONFIG_GENERIC_SMP_IDLE_THREAD=y
-CONFIG_GENERIC_STRNCPY_FROM_USER=y
-CONFIG_GENERIC_STRNLEN_USER=y
-CONFIG_GENERIC_TIME_VSYSCALL=y
-CONFIG_GENERIC_VDSO_32=y
-CONFIG_GLOB=y
-CONFIG_GPIOLIB_IRQCHIP=y
-CONFIG_GPIO_CDEV=y
-CONFIG_GPIO_GENERIC=y
-CONFIG_GPIO_GENERIC_PLATFORM=y
-CONFIG_GPIO_MVEBU=y
-CONFIG_GPIO_PCA953X=y
-CONFIG_GPIO_PCA953X_IRQ=y
-CONFIG_GRO_CELLS=y
-CONFIG_HARDEN_BRANCH_PREDICTOR=y
-CONFIG_HARDIRQS_SW_RESEND=y
-CONFIG_HAS_DMA=y
-CONFIG_HAS_IOMEM=y
-CONFIG_HAS_IOPORT_MAP=y
-CONFIG_HAVE_SMP=y
-CONFIG_HIGHMEM=y
-CONFIG_HIGHPTE=y
-CONFIG_HOTPLUG_CPU=y
-CONFIG_HWBM=y
-CONFIG_HWMON=y
-CONFIG_HW_RANDOM=y
-CONFIG_HZ_FIXED=0
-CONFIG_HZ_PERIODIC=y
-CONFIG_I2C=y
-CONFIG_I2C_BOARDINFO=y
-CONFIG_I2C_CHARDEV=y
-CONFIG_I2C_MV64XXX=y
-# CONFIG_I2C_PXA is not set
-CONFIG_INITRAMFS_SOURCE=""
-CONFIG_IRQCHIP=y
-CONFIG_IRQ_DOMAIN=y
-CONFIG_IRQ_DOMAIN_HIERARCHY=y
-CONFIG_IRQ_FORCED_THREADING=y
-CONFIG_IRQ_WORK=y
-# CONFIG_IWMMXT is not set
-CONFIG_JBD2=y
-CONFIG_KMAP_LOCAL=y
-CONFIG_KMAP_LOCAL_NON_LINEAR_PTE_ARRAY=y
-CONFIG_LEDS_GPIO=y
-CONFIG_LEDS_PCA963X=y
-CONFIG_LEDS_TLC591XX=y
-CONFIG_LEDS_TRIGGER_DISK=y
-CONFIG_LIBFDT=y
-CONFIG_LOCK_DEBUGGING_SUPPORT=y
-CONFIG_LOCK_SPIN_ON_OWNER=y
-CONFIG_LZO_COMPRESS=y
-CONFIG_LZO_DECOMPRESS=y
-CONFIG_MACH_ARMADA_370=y
-# CONFIG_MACH_ARMADA_375 is not set
-CONFIG_MACH_ARMADA_38X=y
-# CONFIG_MACH_ARMADA_39X is not set
-CONFIG_MACH_ARMADA_XP=y
-# CONFIG_MACH_DOVE is not set
-CONFIG_MACH_MVEBU_ANY=y
-CONFIG_MACH_MVEBU_V7=y
-CONFIG_MAGIC_SYSRQ=y
-CONFIG_MANGLE_BOOTARGS=y
-CONFIG_MARVELL_PHY=y
-CONFIG_MDIO_BUS=y
-CONFIG_MDIO_DEVICE=y
-CONFIG_MDIO_DEVRES=y
-CONFIG_MDIO_I2C=y
-CONFIG_MEMFD_CREATE=y
-CONFIG_MEMORY=y
-CONFIG_MIGHT_HAVE_CACHE_L2X0=y
-CONFIG_MIGRATION=y
-CONFIG_MMC=y
-CONFIG_MMC_BLOCK=y
-CONFIG_MMC_MVSDIO=y
-CONFIG_MMC_SDHCI=y
-# CONFIG_MMC_SDHCI_PCI is not set
-CONFIG_MMC_SDHCI_PLTFM=y
-CONFIG_MMC_SDHCI_PXAV3=y
-CONFIG_MODULES_USE_ELF_REL=y
-CONFIG_MTD_CFI_STAA=y
-CONFIG_MTD_NAND_CORE=y
-CONFIG_MTD_NAND_ECC=y
-CONFIG_MTD_NAND_ECC_SW_HAMMING=y
-CONFIG_MTD_NAND_MARVELL=y
-CONFIG_MTD_RAW_NAND=y
-CONFIG_MTD_SPI_NOR=y
-CONFIG_MTD_SPI_NOR_USE_VARIABLE_ERASE=y
-CONFIG_MTD_SPLIT_FIRMWARE=y
-CONFIG_MTD_UBI=y
-CONFIG_MTD_UBI_BEB_LIMIT=20
-CONFIG_MTD_UBI_BLOCK=y
-CONFIG_MTD_UBI_WL_THRESHOLD=4096
-CONFIG_MUTEX_SPIN_ON_OWNER=y
-CONFIG_MVEBU_CLK_COMMON=y
-CONFIG_MVEBU_CLK_COREDIV=y
-CONFIG_MVEBU_CLK_CPU=y
-CONFIG_MVEBU_DEVBUS=y
-CONFIG_MVEBU_MBUS=y
-CONFIG_MVMDIO=y
-CONFIG_MVNETA=y
-CONFIG_MVNETA_BM=y
-CONFIG_MVNETA_BM_ENABLE=y
-# CONFIG_MVPP2 is not set
-CONFIG_MV_XOR=y
-CONFIG_NEED_DMA_MAP_STATE=y
-CONFIG_NEON=y
-CONFIG_NET_DEVLINK=y
-CONFIG_NET_FLOW_LIMIT=y
-CONFIG_NET_SELFTESTS=y
-CONFIG_NLS=y
-CONFIG_NOP_USB_XCEIV=y
-CONFIG_NR_CPUS=4
-CONFIG_NVMEM=y
-CONFIG_NVME_CORE=y
-# CONFIG_NVME_HWMON is not set
-# CONFIG_NVME_MULTIPATH is not set
-CONFIG_OF=y
-CONFIG_OF_ADDRESS=y
-CONFIG_OF_EARLY_FLATTREE=y
-CONFIG_OF_FLATTREE=y
-CONFIG_OF_GPIO=y
-CONFIG_OF_IRQ=y
-CONFIG_OF_KOBJ=y
-CONFIG_OF_MDIO=y
-CONFIG_OLD_SIGACTION=y
-CONFIG_OLD_SIGSUSPEND3=y
-CONFIG_ORION_WATCHDOG=y
-CONFIG_OUTER_CACHE=y
-CONFIG_OUTER_CACHE_SYNC=y
-CONFIG_PADATA=y
-CONFIG_PAGE_OFFSET=0xC0000000
-CONFIG_PAGE_POOL=y
-CONFIG_PAGE_POOL_STATS=y
-CONFIG_PAGE_SIZE_LESS_THAN_256KB=y
-CONFIG_PAGE_SIZE_LESS_THAN_64KB=y
-CONFIG_PCI=y
-CONFIG_PCI_BRIDGE_EMUL=y
-CONFIG_PCI_DOMAINS=y
-CONFIG_PCI_DOMAINS_GENERIC=y
-CONFIG_PCI_MSI=y
-CONFIG_PCI_MSI_IRQ_DOMAIN=y
-CONFIG_PCI_MVEBU=y
-CONFIG_PERF_USE_VMALLOC=y
-CONFIG_PGTABLE_LEVELS=2
-CONFIG_PHYLIB=y
-CONFIG_PHYLINK=y
-# CONFIG_PHY_MVEBU_A3700_COMPHY is not set
-# CONFIG_PHY_MVEBU_A3700_UTMI is not set
-# CONFIG_PHY_MVEBU_A38X_COMPHY is not set
-# CONFIG_PHY_MVEBU_CP110_COMPHY is not set
-CONFIG_PINCTRL=y
-CONFIG_PINCTRL_ARMADA_370=y
-CONFIG_PINCTRL_ARMADA_38X=y
-CONFIG_PINCTRL_ARMADA_XP=y
-CONFIG_PINCTRL_MVEBU=y
-# CONFIG_PINCTRL_SINGLE is not set
-CONFIG_PJ4B_ERRATA_4742=y
-CONFIG_PL310_ERRATA_753970=y
-CONFIG_PLAT_ORION=y
-CONFIG_PM_OPP=y
-CONFIG_POWER_RESET=y
-CONFIG_POWER_RESET_GPIO=y
-CONFIG_PREEMPT_NONE_BUILD=y
-CONFIG_PTP_1588_CLOCK_OPTIONAL=y
-CONFIG_PWM=y
-CONFIG_PWM_SYSFS=y
-CONFIG_RANDSTRUCT_NONE=y
-CONFIG_RATIONAL=y
-CONFIG_REGMAP=y
-CONFIG_REGMAP_I2C=y
-CONFIG_REGMAP_MMIO=y
-CONFIG_REGULATOR=y
-CONFIG_REGULATOR_FIXED_VOLTAGE=y
-CONFIG_RFS_ACCEL=y
-CONFIG_RPS=y
-CONFIG_RTC_CLASS=y
-CONFIG_RTC_DRV_ARMADA38X=y
-# CONFIG_RTC_DRV_MV is not set
-CONFIG_RTC_I2C_AND_SPI=y
-CONFIG_RTC_MC146818_LIB=y
-CONFIG_RWSEM_SPIN_ON_OWNER=y
-CONFIG_SATA_AHCI_PLATFORM=y
-CONFIG_SATA_HOST=y
-CONFIG_SATA_MV=y
-CONFIG_SATA_PMP=y
-CONFIG_SCSI=y
-CONFIG_SCSI_COMMON=y
-CONFIG_SENSORS_PWM_FAN=y
-CONFIG_SENSORS_TMP421=y
-CONFIG_SERIAL_8250_DW=y
-CONFIG_SERIAL_8250_DWLIB=y
-CONFIG_SERIAL_8250_FSL=y
-CONFIG_SERIAL_MCTRL_GPIO=y
-CONFIG_SERIAL_MVEBU_CONSOLE=y
-CONFIG_SERIAL_MVEBU_UART=y
-CONFIG_SFP=y
-CONFIG_SGL_ALLOC=y
-CONFIG_SG_POOL=y
-CONFIG_SMP=y
-CONFIG_SMP_ON_UP=y
-CONFIG_SOCK_RX_QUEUE_MAPPING=y
-CONFIG_SOC_BUS=y
-CONFIG_SOFTIRQ_ON_OWN_STACK=y
-CONFIG_SPARSE_IRQ=y
-CONFIG_SPI=y
-# CONFIG_SPI_ARMADA_3700 is not set
-CONFIG_SPI_MASTER=y
-CONFIG_SPI_MEM=y
-CONFIG_SPI_ORION=y
-CONFIG_SRAM=y
-CONFIG_SRAM_EXEC=y
-CONFIG_SRCU=y
-CONFIG_SWPHY=y
-CONFIG_SWP_EMULATE=y
-CONFIG_SYS_SUPPORTS_APM_EMULATION=y
-CONFIG_THERMAL=y
-CONFIG_THERMAL_DEFAULT_GOV_STEP_WISE=y
-CONFIG_THERMAL_EMERGENCY_POWEROFF_DELAY_MS=0
-CONFIG_THERMAL_GOV_STEP_WISE=y
-CONFIG_THERMAL_HWMON=y
-CONFIG_THERMAL_OF=y
-CONFIG_TICK_CPU_ACCOUNTING=y
-CONFIG_TIMER_OF=y
-CONFIG_TIMER_PROBE=y
-CONFIG_TREE_RCU=y
-CONFIG_TREE_SRCU=y
-CONFIG_UBIFS_FS=y
-CONFIG_UNCOMPRESS_INCLUDE="debug/uncompress.h"
-CONFIG_UNWINDER_ARM=y
-CONFIG_USB=y
-CONFIG_USB_COMMON=y
-CONFIG_USB_EHCI_HCD=y
-CONFIG_USB_EHCI_HCD_ORION=y
-CONFIG_USB_EHCI_HCD_PLATFORM=y
-CONFIG_USB_LEDS_TRIGGER_USBPORT=y
-CONFIG_USB_PHY=y
-CONFIG_USB_STORAGE=y
-CONFIG_USB_SUPPORT=y
-CONFIG_USB_XHCI_HCD=y
-CONFIG_USB_XHCI_MVEBU=y
-CONFIG_USB_XHCI_PLATFORM=y
-CONFIG_USE_OF=y
-CONFIG_VFP=y
-CONFIG_VFPv3=y
-CONFIG_WATCHDOG_CORE=y
-CONFIG_XPS=y
-CONFIG_XXHASH=y
-CONFIG_XZ_DEC_ARM=y
-CONFIG_XZ_DEC_BCJ=y
-CONFIG_ZBOOT_ROM_BSS=0x0
-CONFIG_ZBOOT_ROM_TEXT=0x0
-CONFIG_ZLIB_DEFLATE=y
-CONFIG_ZLIB_INFLATE=y
-CONFIG_ZSTD_COMMON=y
-CONFIG_ZSTD_COMPRESS=y
-CONFIG_ZSTD_DECOMPRESS=y
+++ /dev/null
-CONFIG_64BIT=y
-CONFIG_ARCH_BINFMT_ELF_EXTRA_PHDRS=y
-CONFIG_ARCH_CORRECT_STACKTRACE_ON_KRETPROBE=y
-CONFIG_ARCH_DMA_ADDR_T_64BIT=y
-CONFIG_ARCH_MHP_MEMMAP_ON_MEMORY_ENABLE=y
-CONFIG_ARCH_MMAP_RND_BITS=18
-CONFIG_ARCH_MMAP_RND_BITS_MAX=24
-CONFIG_ARCH_MMAP_RND_BITS_MIN=18
-CONFIG_ARCH_MMAP_RND_COMPAT_BITS_MIN=11
-CONFIG_ARCH_PROC_KCORE_TEXT=y
-CONFIG_ARCH_STACKWALK=y
-CONFIG_ARCH_WANTS_NO_INSTR=y
-CONFIG_ARCH_WANTS_THP_SWAP=y
-CONFIG_ARM64=y
-CONFIG_ARM64_4K_PAGES=y
-CONFIG_ARM64_LD_HAS_FIX_ERRATUM_843419=y
-CONFIG_ARM64_PAGE_SHIFT=12
-CONFIG_ARM64_PA_BITS=48
-CONFIG_ARM64_PA_BITS_48=y
-CONFIG_ARM64_TAGGED_ADDR_ABI=y
-CONFIG_ARM64_VA_BITS=39
-CONFIG_ARM64_VA_BITS_39=y
-CONFIG_ARMADA_37XX_CLK=y
-CONFIG_ARMADA_37XX_RWTM_MBOX=y
-CONFIG_ARMADA_37XX_WATCHDOG=y
-CONFIG_ARMADA_AP806_SYSCON=y
-CONFIG_ARMADA_AP_CP_HELPER=y
-CONFIG_ARMADA_CP110_SYSCON=y
-CONFIG_ARM_AMBA=y
-CONFIG_ARM_ARCH_TIMER=y
-# CONFIG_ARM_ARCH_TIMER_EVTSTREAM is not set
-CONFIG_ARM_ARMADA_37XX_CPUFREQ=y
-CONFIG_ARM_GIC_V2M=y
-CONFIG_ARM_GIC_V3=y
-CONFIG_ARM_GIC_V3_ITS=y
-CONFIG_ARM_GIC_V3_ITS_PCI=y
-# CONFIG_ARM_MHU_V2 is not set
-# CONFIG_ARM_PL172_MPMC is not set
-CONFIG_ARM_PSCI_FW=y
-CONFIG_AUDIT_ARCH_COMPAT_GENERIC=y
-CONFIG_CC_HAVE_SHADOW_CALL_STACK=y
-CONFIG_CC_HAVE_STACKPROTECTOR_SYSREG=y
-CONFIG_CPU_LITTLE_ENDIAN=y
-CONFIG_DMA_DIRECT_REMAP=y
-CONFIG_F2FS_FS_COMPRESSION=y
-# CONFIG_F2FS_FS_LZ4 is not set
-# CONFIG_F2FS_FS_LZO is not set
-CONFIG_F2FS_FS_ZSTD=y
-CONFIG_FRAME_POINTER=y
-CONFIG_GCC_SUPPORTS_DYNAMIC_FTRACE_WITH_REGS=y
-CONFIG_GENERIC_BUG_RELATIVE_POINTERS=y
-CONFIG_GENERIC_CSUM=y
-CONFIG_GENERIC_IOREMAP=y
-CONFIG_GENERIC_PINCONF=y
-CONFIG_ILLEGAL_POINTER_VALUE=0xdead000000000000
-CONFIG_MAILBOX=y
-# CONFIG_MAILBOX_TEST is not set
-CONFIG_MFD_SYSCON=y
-CONFIG_MMC_SDHCI_XENON=y
-CONFIG_MODULES_USE_ELF_RELA=y
-CONFIG_MVEBU_GICP=y
-CONFIG_MVEBU_ICU=y
-CONFIG_MVEBU_ODMI=y
-CONFIG_MVEBU_PIC=y
-CONFIG_MVEBU_SEI=y
-CONFIG_NEED_SG_DMA_LENGTH=y
-CONFIG_PARTITION_PERCPU=y
-CONFIG_PCI_AARDVARK=y
-CONFIG_PGTABLE_LEVELS=3
-CONFIG_PHYS_ADDR_T_64BIT=y
-CONFIG_PHY_MVEBU_A3700_COMPHY=y
-CONFIG_PHY_MVEBU_A3700_UTMI=y
-CONFIG_PINCTRL_AC5=y
-CONFIG_PINCTRL_ARMADA_37XX=y
-CONFIG_PINCTRL_ARMADA_AP806=y
-CONFIG_PINCTRL_ARMADA_CP110=y
-CONFIG_POSIX_CPU_TIMERS_TASK_WORK=y
-CONFIG_POWER_SUPPLY=y
-CONFIG_QUEUED_RWLOCKS=y
-CONFIG_QUEUED_SPINLOCKS=y
-CONFIG_REGULATOR_GPIO=y
-CONFIG_RODATA_FULL_DEFAULT_ENABLED=y
-CONFIG_SPARSEMEM=y
-CONFIG_SPARSEMEM_EXTREME=y
-CONFIG_SPARSEMEM_VMEMMAP=y
-CONFIG_SPARSEMEM_VMEMMAP_ENABLE=y
-CONFIG_SPI_ARMADA_3700=y
-CONFIG_SWIOTLB=y
-CONFIG_SYSCTL_EXCEPTION_TRACE=y
-CONFIG_THREAD_INFO_IN_TASK=y
-CONFIG_TRACE_IRQFLAGS_NMI_SUPPORT=y
-CONFIG_TURRIS_MOX_RWTM=y
-CONFIG_UNMAP_KERNEL_AT_EL0=y
-CONFIG_VMAP_STACK=y
-CONFIG_ZONE_DMA32=y
+++ /dev/null
-CONFIG_64BIT=y
-CONFIG_AQUANTIA_PHY=y
-CONFIG_ARCH_BINFMT_ELF_EXTRA_PHDRS=y
-CONFIG_ARCH_CORRECT_STACKTRACE_ON_KRETPROBE=y
-CONFIG_ARCH_DMA_ADDR_T_64BIT=y
-CONFIG_ARCH_MHP_MEMMAP_ON_MEMORY_ENABLE=y
-CONFIG_ARCH_MMAP_RND_BITS=18
-CONFIG_ARCH_MMAP_RND_BITS_MAX=24
-CONFIG_ARCH_MMAP_RND_BITS_MIN=18
-CONFIG_ARCH_MMAP_RND_COMPAT_BITS_MIN=11
-CONFIG_ARCH_PROC_KCORE_TEXT=y
-CONFIG_ARCH_STACKWALK=y
-CONFIG_ARCH_WANTS_NO_INSTR=y
-CONFIG_ARCH_WANTS_THP_SWAP=y
-CONFIG_ARM64=y
-CONFIG_ARM64_4K_PAGES=y
-CONFIG_ARM64_LD_HAS_FIX_ERRATUM_843419=y
-CONFIG_ARM64_PAGE_SHIFT=12
-CONFIG_ARM64_PA_BITS=48
-CONFIG_ARM64_PA_BITS_48=y
-CONFIG_ARM64_SVE=y
-# CONFIG_ARM64_TAGGED_ADDR_ABI is not set
-CONFIG_ARM64_VA_BITS=39
-CONFIG_ARM64_VA_BITS_39=y
-CONFIG_ARMADA_37XX_CLK=y
-CONFIG_ARMADA_AP806_SYSCON=y
-CONFIG_ARMADA_AP_CPU_CLK=y
-CONFIG_ARMADA_AP_CP_HELPER=y
-CONFIG_ARMADA_CP110_SYSCON=y
-CONFIG_ARM_AMBA=y
-CONFIG_ARM_ARCH_TIMER=y
-# CONFIG_ARM_ARCH_TIMER_EVTSTREAM is not set
-CONFIG_ARM_ARMADA_8K_CPUFREQ=y
-CONFIG_ARM_GIC_V2M=y
-CONFIG_ARM_GIC_V3=y
-CONFIG_ARM_GIC_V3_ITS=y
-CONFIG_ARM_GIC_V3_ITS_PCI=y
-# CONFIG_ARM_PL172_MPMC is not set
-CONFIG_ARM_PSCI_FW=y
-CONFIG_ARM_SBSA_WATCHDOG=y
-CONFIG_AUDIT_ARCH_COMPAT_GENERIC=y
-CONFIG_CC_HAVE_SHADOW_CALL_STACK=y
-CONFIG_CC_HAVE_STACKPROTECTOR_SYSREG=y
-CONFIG_CMDLINE_PARTITION=y
-CONFIG_CPU_LITTLE_ENDIAN=y
-CONFIG_CRC_CCITT=y
-CONFIG_DMA_DIRECT_REMAP=y
-CONFIG_EEPROM_AT24=y
-CONFIG_FRAME_POINTER=y
-CONFIG_GCC_SUPPORTS_DYNAMIC_FTRACE_WITH_REGS=y
-CONFIG_GENERIC_BUG_RELATIVE_POINTERS=y
-CONFIG_GENERIC_CSUM=y
-CONFIG_GENERIC_IOREMAP=y
-CONFIG_GENERIC_PINCONF=y
-CONFIG_HW_RANDOM_OMAP=y
-CONFIG_ILLEGAL_POINTER_VALUE=0xdead000000000000
-CONFIG_LEDS_IEI_WT61P803_PUZZLE=y
-CONFIG_LEDS_IS31FL319X=y
-CONFIG_MARVELL_10G_PHY=y
-CONFIG_MFD_CORE=y
-CONFIG_MFD_IEI_WT61P803_PUZZLE=y
-CONFIG_MFD_SYSCON=y
-CONFIG_MIKROTIK=y
-CONFIG_MIKROTIK_RB_SYSFS=y
-# CONFIG_MIKROTIK_WLAN_DECOMPRESS_LZ77 is not set
-CONFIG_MMC_SDHCI_XENON=y
-CONFIG_MODULES_USE_ELF_RELA=y
-CONFIG_MTD_ROUTERBOOT_PARTS=y
-CONFIG_MTD_SPI_NOR_USE_VARIABLE_ERASE=y
-CONFIG_MVEBU_GICP=y
-CONFIG_MVEBU_ICU=y
-CONFIG_MVEBU_ODMI=y
-CONFIG_MVEBU_PIC=y
-CONFIG_MVEBU_SEI=y
-CONFIG_MVPP2=y
-CONFIG_MV_XOR_V2=y
-CONFIG_NEED_SG_DMA_LENGTH=y
-CONFIG_NVMEM_LAYOUTS=y
-CONFIG_NVMEM_LAYOUT_MIKROTIK=y
-CONFIG_NVMEM_LAYOUT_ONIE_TLV=y
-CONFIG_NVMEM_SYSFS=y
-CONFIG_NVMEM_U_BOOT_ENV=y
-CONFIG_PARTITION_PERCPU=y
-CONFIG_PCIEAER=y
-CONFIG_PCIEPORTBUS=y
-CONFIG_PCIE_ARMADA_8K=y
-CONFIG_PCIE_DW=y
-CONFIG_PCIE_DW_HOST=y
-# CONFIG_PCI_AARDVARK is not set
-CONFIG_PGTABLE_LEVELS=3
-CONFIG_PHYLIB_LEDS=y
-CONFIG_PHYS_ADDR_T_64BIT=y
-CONFIG_PHY_MVEBU_CP110_COMPHY=y
-CONFIG_PHY_MVEBU_CP110_UTMI=y
-CONFIG_PINCTRL_AC5=y
-CONFIG_PINCTRL_ARMADA_37XX=y
-CONFIG_PINCTRL_ARMADA_AP806=y
-CONFIG_PINCTRL_ARMADA_CP110=y
-CONFIG_POSIX_CPU_TIMERS_TASK_WORK=y
-CONFIG_POWER_SUPPLY=y
-CONFIG_QCA808X_PHY=y
-CONFIG_QUEUED_RWLOCKS=y
-CONFIG_QUEUED_SPINLOCKS=y
-CONFIG_RAS=y
-# CONFIG_RAVE_SP_CORE is not set
-CONFIG_REGULATOR_GPIO=y
-CONFIG_REGULATOR_USERSPACE_CONSUMER=y
-# CONFIG_RODATA_FULL_DEFAULT_ENABLED is not set
-CONFIG_SENSORS_IEI_WT61P803_PUZZLE_HWMON=y
-CONFIG_SERIAL_DEV_BUS=y
-CONFIG_SERIAL_DEV_CTRL_TTYPORT=y
-CONFIG_SPARSEMEM=y
-CONFIG_SPARSEMEM_EXTREME=y
-CONFIG_SPARSEMEM_VMEMMAP=y
-CONFIG_SPARSEMEM_VMEMMAP_ENABLE=y
-CONFIG_SWIOTLB=y
-CONFIG_SYSCTL_EXCEPTION_TRACE=y
-CONFIG_THREAD_INFO_IN_TASK=y
-CONFIG_TRACE_IRQFLAGS_NMI_SUPPORT=y
-CONFIG_UNMAP_KERNEL_AT_EL0=y
-CONFIG_VMAP_STACK=y
-CONFIG_ZONE_DMA32=y
+++ /dev/null
-CONFIG_ARM_HAS_GROUP_RELOCS=y
-CONFIG_CPU_LITTLE_ENDIAN=y
-CONFIG_CURRENT_POINTER_IN_TPIDRURO=y
-CONFIG_IRQSTACKS=y
-CONFIG_LED_TRIGGER_PHY=y
-CONFIG_MTD_SPLIT_SEIL_FW=y
-CONFIG_MTD_SPLIT_UIMAGE_FW=y
-CONFIG_MTD_VIRT_CONCAT=y
-CONFIG_PHY_MVEBU_A38X_COMPHY=y
-CONFIG_POWER_RESET_QNAP=y
-CONFIG_RTC_DRV_MV=y
-CONFIG_THREAD_INFO_IN_TASK=y
+++ /dev/null
-From 0396a8731efd17aec4719ad9981fefeaa13ffa56 Mon Sep 17 00:00:00 2001
-From: Gabor Juhos <j4g8y7@gmail.com>
-Date: Wed, 14 May 2025 21:18:34 +0200
-Subject: [PATCH 3/7] pinctrl: armada-37xx: propagate error from
- armada_37xx_gpio_direction_output()
-
-The regmap_update_bits() function can fail, so propagate its error
-up to the stack instead of silently ignoring that.
-
-Signed-off-by: Imre Kaloz <kaloz@openwrt.org>
-Reviewed-by: Andrew Lunn <andrew@lunn.ch>
-Signed-off-by: Gabor Juhos <j4g8y7@gmail.com>
-Link: https://lore.kernel.org/20250514-pinctrl-a37xx-fixes-v2-3-07e9ac1ab737@gmail.com
-Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
----
- drivers/pinctrl/mvebu/pinctrl-armada-37xx.c | 4 +---
- 1 file changed, 1 insertion(+), 3 deletions(-)
-
---- a/drivers/pinctrl/mvebu/pinctrl-armada-37xx.c
-+++ b/drivers/pinctrl/mvebu/pinctrl-armada-37xx.c
-@@ -433,9 +433,7 @@ static int armada_37xx_gpio_direction_ou
- reg = OUTPUT_EN;
- armada_37xx_update_reg(®, &en_offset);
-
-- regmap_update_bits(info->regmap, reg, mask, mask);
--
-- return 0;
-+ return regmap_update_bits(info->regmap, reg, mask, mask);
- }
-
- static int armada_37xx_gpio_get(struct gpio_chip *chip, unsigned int offset)
+++ /dev/null
-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@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@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@bootlin.com>
-Signed-off-by: Pali Rohár <pali@kernel.org>
-Fixes: 8c39d710363c ("PCI: aardvark: Add Aardvark PCI host controller driver")
-Cc: stable@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
-@@ -212,6 +212,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
-@@ -560,6 +562,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;
-@@ -1661,6 +1668,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;
+++ /dev/null
---- 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,
- },
+++ /dev/null
-From 71270226b14733a4b1f2cde58ea9265caa50b38d Mon Sep 17 00:00:00 2001
-From: Adrian Panella <ianchi74@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@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@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
-@@ -1566,6 +1566,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
-@@ -6,6 +6,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
-@@ -21,6 +23,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)
- {
-@@ -29,6 +32,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)
-@@ -39,6 +43,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)
- {
-@@ -47,6 +52,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)
-@@ -59,6 +65,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;
-@@ -70,6 +77,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];
-@@ -89,18 +164,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;
-@@ -118,6 +203,7 @@ static void hex_str(char *out, uint32_t
- }
- *out = '\0';
- }
-+#endif
-
- /*
- * Convert and fold provided ATAGs into the provided FDT.
-@@ -132,9 +218,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)
-@@ -169,7 +257,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)
-@@ -213,6 +303,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
-@@ -112,6 +112,10 @@
-
- #include <kunit/test.h>
-
-+#if defined(CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_MANGLE)
-+#include <linux/of.h>
-+#endif
-+
- static int kernel_init(void *);
-
- /*
-@@ -929,6 +933,18 @@ void start_kernel(void)
- boot_cpu_hotplug_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();
+++ /dev/null
---- a/arch/arm/mach-mvebu/Kconfig
-+++ b/arch/arm/mach-mvebu/Kconfig
-@@ -66,6 +66,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.
+++ /dev/null
---- a/arch/arm/boot/dts/marvell/armada-385-linksys.dtsi
-+++ b/arch/arm/boot/dts/marvell/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/marvell/armada-385-linksys-caiman.dts
-+++ b/arch/arm/boot/dts/marvell/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/marvell/armada-385-linksys-cobra.dts
-+++ b/arch/arm/boot/dts/marvell/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/marvell/armada-385-linksys-shelby.dts
-+++ b/arch/arm/boot/dts/marvell/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/marvell/armada-385-linksys-rango.dts
-+++ b/arch/arm/boot/dts/marvell/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/marvell/armada-xp-linksys-mamba.dts
-+++ b/arch/arm/boot/dts/marvell/armada-xp-linksys-mamba.dts
-@@ -223,12 +223,100 @@
- pcie@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@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>;
-+ };
-+ };
- };
- };
-
+++ /dev/null
---- a/arch/arm/boot/dts/marvell/armada-xp.dtsi
-+++ b/arch/arm/boot/dts/marvell/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>;
- };
-
+++ /dev/null
---- a/arch/arm/boot/dts/marvell/armada-388-rd.dts
-+++ b/arch/arm/boot/dts/marvell/armada-388-rd.dts
-@@ -103,6 +103,16 @@
- compatible = "st,m25p128", "jedec,spi-nor";
- reg = <0>; /* Chip select 0 */
- spi-max-frequency = <108000000>;
-+
-+ partition@0 {
-+ label = "uboot";
-+ reg = <0 0x400000>;
-+ };
-+
-+ partition@1 {
-+ label = "firmware";
-+ reg = <0x400000 0xc00000>;
-+ };
- };
- };
-
+++ /dev/null
-From 9861f93a59142a3131870df2521eb2deb73026d7 Mon Sep 17 00:00:00 2001
-From: Maxime Ripard <maxime.ripard@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@free-electrons.com>
----
- arch/arm/boot/dts/marvell/armada-385-db-ap.dts | 6 +++---
- 1 file changed, 3 insertions(+), 3 deletions(-)
-
---- a/arch/arm/boot/dts/marvell/armada-385-db-ap.dts
-+++ b/arch/arm/boot/dts/marvell/armada-385-db-ap.dts
-@@ -218,19 +218,19 @@
- #size-cells = <1>;
-
- partition@0 {
-- label = "U-Boot";
-+ label = "u-boot";
- reg = <0x00000000 0x00800000>;
- read-only;
- };
-
- partition@800000 {
-- label = "uImage";
-+ label = "kernel";
- reg = <0x00800000 0x00400000>;
- read-only;
- };
-
- partition@c00000 {
-- label = "Root";
-+ label = "ubi";
- reg = <0x00c00000 0x3f400000>;
- };
- };
+++ /dev/null
---- a/arch/arm/boot/dts/marvell/armada-xp-linksys-mamba.dts
-+++ b/arch/arm/boot/dts/marvell/armada-xp-linksys-mamba.dts
-@@ -483,3 +483,7 @@
- };
- };
- };
-+
-+&coherencyfab {
-+ broken-idle;
-+};
+++ /dev/null
---- a/arch/arm/boot/dts/marvell/armada-xp-linksys-mamba.dts
-+++ b/arch/arm/boot/dts/marvell/armada-xp-linksys-mamba.dts
-@@ -385,7 +385,7 @@
-
- port@4 {
- reg = <4>;
-- label = "internet";
-+ label = "wan";
- };
-
- port@5 {
+++ /dev/null
---- a/arch/arm/boot/dts/marvell/armada-385-linksys.dtsi
-+++ b/arch/arm/boot/dts/marvell/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";
-
-- led-power {
-+ led_power: led-power {
- gpios = <&gpio1 23 GPIO_ACTIVE_HIGH>;
- default-state = "on";
- };
---- a/arch/arm/boot/dts/marvell/armada-xp-linksys-mamba.dts
-+++ b/arch/arm/boot/dts/marvell/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;
-@@ -195,7 +202,7 @@
- pinctrl-0 = <&power_led_pin>;
- pinctrl-names = "default";
-
-- led-power {
-+ led_power: led-power {
- label = "mamba:white:power";
- gpios = <&gpio1 8 GPIO_ACTIVE_HIGH>;
- default-state = "on";
+++ /dev/null
---- a/arch/arm/boot/dts/marvell/armada-385-linksys.dtsi
-+++ b/arch/arm/boot/dts/marvell/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@5 {
-- reg = <5>;
-+ port@6 {
-+ reg = <6>;
- phy-mode = "sgmii";
-- ethernet = <ð2>;
-+ ethernet = <ð0>;
-
- fixed-link {
- speed = <1000>;
+++ /dev/null
---- a/arch/arm/boot/dts/marvell/armada-385-linksys-rango.dts
-+++ b/arch/arm/boot/dts/marvell/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/marvell/armada-xp-linksys-mamba.dts
-+++ b/arch/arm/boot/dts/marvell/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/marvell/armada-385-linksys-cobra.dts
-+++ b/arch/arm/boot/dts/marvell/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/marvell/armada-385-linksys-caiman.dts
-+++ b/arch/arm/boot/dts/marvell/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/marvell/armada-385-linksys-shelby.dts
-+++ b/arch/arm/boot/dts/marvell/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";
- };
-
+++ /dev/null
-From 8137da20701c776ad3481115305a5e8e410871ba Mon Sep 17 00:00:00 2001
-From: Russell King <rmk+kernel@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@armlinux.org.uk>
----
- .../arm/boot/dts/marvell/armada-388-clearfog-base.dts | 1 +
- .../armada-38x-solidrun-microsom-emmc.dtsi | 62 +++++++++++++++++++
- 2 files changed, 63 insertions(+)
- create mode 100644 arch/arm/boot/dts/marvell/armada-38x-solidrun-microsom-emmc.dtsi
-
---- a/arch/arm/boot/dts/marvell/armada-388-clearfog-base.dts
-+++ b/arch/arm/boot/dts/marvell/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/marvell/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@d8000 {
-+ bus-width = <4>;
-+ no-1-8-v;
-+ non-removable;
-+ pinctrl-0 = <µsom_sdhci_pins>;
-+ pinctrl-names = "default";
-+ status = "okay";
-+ wp-inverted;
-+ };
-+ };
-+ };
-+};
+++ /dev/null
---- a/arch/arm/boot/dts/marvell/armada-388-helios4.dts
-+++ b/arch/arm/boot/dts/marvell/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";
- };
-
+++ /dev/null
-From: Tomasz Maciej Nowak <tmn505@gmail.com>
-Date: Fri, 7 Jul 2023 19:06:05 +0200
-Subject: [PATCH] arm64: dts: marvell: enable heartbeat LED by default
-
-Some boards could be placed in an enclosure, so enable LED18 by default,
-since that'll be the only visible indicator that the board is operating.
-
-Signed-off-by: Tomasz Maciej Nowak <tmn505@gmail.com>
----
- arch/arm64/boot/dts/marvell/armada-8040-mcbin-singleshot.dts | 1 +
- 1 file changed, 1 insertion(+)
-
---- a/arch/arm64/boot/dts/marvell/armada-8040-mcbin-singleshot.dts
-+++ b/arch/arm64/boot/dts/marvell/armada-8040-mcbin-singleshot.dts
-@@ -25,6 +25,7 @@
- function = LED_FUNCTION_HEARTBEAT;
- color = <LED_COLOR_ID_GREEN>;
- linux,default-trigger = "heartbeat";
-+ default-state = "on";
- };
- };
- };
+++ /dev/null
-From 258233f00bcd013050efee00c5d9128ef8cd62dd Mon Sep 17 00:00:00 2001
-From: Tad <tad@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@spotco.us>
----
- arch/arm/boot/dts/marvell/armada-xp-linksys-mamba.dts | 8 ++++----
- 1 file changed, 4 insertions(+), 4 deletions(-)
-
---- a/arch/arm/boot/dts/marvell/armada-xp-linksys-mamba.dts
-+++ b/arch/arm/boot/dts/marvell/armada-xp-linksys-mamba.dts
-@@ -454,9 +454,9 @@
- reg = <0xa00000 0x2800000>; /* 40MB */
- };
-
-- partition@d00000 {
-+ partition@e00000 {
- label = "rootfs1";
-- reg = <0xd00000 0x2500000>; /* 37MB */
-+ reg = <0xe00000 0x2400000>; /* 36MB */
- };
-
- /* kernel2 overlaps with rootfs2 by design */
-@@ -465,9 +465,9 @@
- reg = <0x3200000 0x2800000>; /* 40MB */
- };
-
-- partition@3500000 {
-+ partition@3600000 {
- label = "rootfs2";
-- reg = <0x3500000 0x2500000>; /* 37MB */
-+ reg = <0x3600000 0x2400000>; /* 36MB */
- };
-
- /*
+++ /dev/null
---- a/arch/arm/boot/dts/marvell/armada-370.dtsi
-+++ b/arch/arm/boot/dts/marvell/armada-370.dtsi
-@@ -254,7 +254,7 @@
- clocks = <&gateclk 23>;
- clock-names = "cesa0";
- marvell,crypto-srams = <&crypto_sram>;
-- marvell,crypto-sram-size = <0x7e0>;
-+ marvell,crypto-sram-size = <0x800>;
- };
- };
-
-@@ -275,12 +275,17 @@
- * cpuidle workaround.
- */
- idle-sram@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.
+++ /dev/null
---- a/arch/arm/boot/dts/marvell/armada-370-synology-ds213j.dts
-+++ b/arch/arm/boot/dts/marvell/armada-370-synology-ds213j.dts
-@@ -31,6 +31,7 @@
-
- chosen {
- stdout-path = "serial0:115200n8";
-+ append-rootblock = "nullparameter="; /* override the bootloader args */
- };
-
- memory@0 {
-@@ -94,6 +95,8 @@
- status = "okay";
- phy = <&phy1>;
- phy-mode = "sgmii";
-+ nvmem-cells = <&macaddr_vendor_0>;
-+ nvmem-cell-names = "mac-address";
- };
-
- sata@a0000 {
-@@ -175,6 +178,24 @@
- phy1: ethernet-phy@1 { /* Marvell 88E1512 */
- reg = <1>;
- };
-+
-+ virtual_flash {
-+ compatible = "mtd-concat";
-+
-+ devices = <&mtd_kernel &mtd_gap &mtd_gap2>;
-+
-+ partitions {
-+ compatible = "fixed-partitions";
-+ #address-cells = <1>;
-+ #size-cells = <1>;
-+
-+ partition@0 {
-+ compatible = "openwrt,uimage", "denx,uimage";
-+ label = "firmware";
-+ reg = <0x0 0x0>;
-+ };
-+ };
-+ };
- };
-
- &pinctrl {
-@@ -259,48 +280,50 @@
- reg = <0>; /* Chip select 0 */
- spi-max-frequency = <20000000>;
-
-- /*
-- * Warning!
-- *
-- * Synology u-boot uses its compiled-in environment
-- * and it seems Synology did not care to change u-boot
-- * default configuration in order to allow saving a
-- * modified environment at a sensible location. So,
-- * if you do a 'saveenv' under u-boot, your modified
-- * environment will be saved at 1MB after the start
-- * of the flash, i.e. in the middle of the uImage.
-- * For that reason, it is strongly advised not to
-- * change the default environment, unless you know
-- * what you are doing.
-- */
-- partition@0 { /* u-boot */
-- label = "RedBoot";
-- reg = <0x00000000 0x000c0000>; /* 768KB */
-- };
-+ partitions {
-+ compatible = "fixed-partitions";
-
-- partition@c0000 { /* uImage */
-- label = "zImage";
-- reg = <0x000c0000 0x002d0000>; /* 2880KB */
-- };
-+ partition@0 { /* u-boot */
-+ label = "u-boot";
-+ reg = <0x00000000 0x000c0000>; /* 768KB */
-+ read-only;
-+ };
-
-- partition@390000 { /* uInitramfs */
-- label = "rd.gz";
-- reg = <0x00390000 0x00440000>; /* 4250KB */
-- };
-+ mtd_gap: partition@c0000 { /* gap */
-+ label = "gap";
-+ reg = <0x000c0000 0x00040000>; /* 256KB */
-+ };
-
-- partition@7d0000 { /* MAC address and serial number */
-- label = "vendor";
-- reg = <0x007d0000 0x00010000>; /* 64KB */
-- };
-+ partition@100000 { /* u-boot-env */
-+ label = "u-boot-env";
-+ reg = <0x00100000 0x00010000>; /* 64KB */
-+ };
-
-- partition@7e0000 {
-- label = "RedBoot config";
-- reg = <0x007e0000 0x00010000>; /* 64KB */
-- };
-+ mtd_kernel: partition@110000 {
-+ label = "kernel";
-+ reg = <0x00110000 0x006c0000>; /* 6912KB */
-+ };
-
-- partition@7f0000 {
-- label = "FIS directory";
-- reg = <0x007f0000 0x00010000>; /* 64KB */
-+ partition@7d0000 { /* MAC address and serial number */
-+ reg = <0x007d0000 0x00010000>; /* 64KB */
-+ label = "vendor";
-+ read-only;
-+
-+ nvmem-layout {
-+ compatible = "fixed-layout";
-+ #address-cells = <1>;
-+ #size-cells = <1>;
-+
-+ macaddr_vendor_0: macaddr@0 {
-+ reg = <0x0 0x6>;
-+ };
-+ };
-+ };
-+
-+ mtd_gap2: partition@7e0000 {
-+ label = "gap2";
-+ reg = <0x007e0000 0x00020000>; /* 128KB */
-+ };
- };
- };
- };
+++ /dev/null
-From 9685ce100f0d302501117113ef0a526ad1acca1d Mon Sep 17 00:00:00 2001
-From: Ram Chandrasekar <rkumbako@codeaurora.org>
-Date: Mon, 7 May 2018 11:54:08 -0600
-Subject: [PATCH] drivers: thermal: step_wise: add support for hysteresis
-
-Step wise governor increases the mitigation level when the temperature
-goes above a threshold and will decrease the mitigation when the
-temperature falls below the threshold. If it were a case, where the
-temperature hovers around a threshold, the mitigation will be applied
-and removed at every iteration. This reaction to the temperature is
-inefficient for performance.
-
-The use of hysteresis temperature could avoid this ping-pong of
-mitigation by relaxing the mitigation to happen only when the
-temperature goes below this lower hysteresis value.
-
-Signed-off-by: Ram Chandrasekar <rkumbako@codeaurora.org>
-Signed-off-by: Lina Iyer <ilina@codeaurora.org>
-[forward-ported for Linux 6.6, as stop-gap downstream solution]
-Signed-off-by: Daniel Golle <daniel@makrotopia.org>
----
- drivers/thermal/gov_step_wise.c | 23 ++++++++++++++++-------
- 1 file changed, 16 insertions(+), 7 deletions(-)
-
---- a/drivers/thermal/gov_step_wise.c
-+++ b/drivers/thermal/gov_step_wise.c
-@@ -86,22 +86,31 @@ static void thermal_zone_trip_update(str
- struct thermal_instance *instance;
- bool throttle = false;
- int old_target;
-+ int hyst_temp;
-
- trend = get_tz_trend(tz, trip_id);
-
-- if (tz->temperature >= trip->temperature) {
-- throttle = true;
-- trace_thermal_zone_trip(tz, trip_id, trip->type);
-- }
--
-- dev_dbg(&tz->device, "Trip%d[type=%d,temp=%d]:trend=%d,throttle=%d\n",
-- trip_id, trip->type, trip->temperature, trend, throttle);
-+ hyst_temp = trip->temperature - trip->hysteresis;
-+ dev_dbg(&tz->device, "Trip%d[type=%d,temp=%d,hyst=%d]:trend=%d,throttle=%d\n",
-+ trip_id, trip->type, trip->temperature, hyst_temp, trend, throttle);
-
- list_for_each_entry(instance, &tz->thermal_instances, tz_node) {
- if (instance->trip != trip)
- continue;
-
- old_target = instance->target;
-+ throttle = false;
-+ /*
-+ * Lower the mitigation only if the temperature
-+ * goes below the hysteresis temperature.
-+ */
-+ if (tz->temperature >= trip->temperature ||
-+ (tz->temperature >= hyst_temp &&
-+ old_target != THERMAL_NO_TARGET)) {
-+ throttle = true;
-+ trace_thermal_zone_trip(tz, trip_id, trip->type);
-+ }
-+
- instance->target = get_target_state(instance, trend, throttle);
- dev_dbg(&instance->cdev->device, "old_target=%d, target=%d\n",
- old_target, (int)instance->target);
+++ /dev/null
-The WRT1900AC among other Linksys routers uses a dual-firmware layout.
-Dynamically rename the active partition to "ubi".
-
-Signed-off-by: Imre Kaloz <kaloz@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;
-@@ -152,9 +155,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))
-@@ -271,6 +278,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);
+++ /dev/null
-From: Felix Fietkau <nbd@nbd.name>
-Subject: mvneta: tx queue workaround
-
-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.
-
-Ref: https://github.com/openwrt/openwrt/issues/5411
-
-Signed-off-by: Felix Fietkau <nbd@nbd.name>
----
---- a/drivers/net/ethernet/marvell/mvneta.c
-+++ b/drivers/net/ethernet/marvell/mvneta.c
-@@ -5294,6 +5294,16 @@ static int mvneta_setup_tc(struct net_de
- }
- }
-
-+#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,
-@@ -5304,6 +5314,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,
+++ /dev/null
-From: Tobias Schramm <tobias@t-sys.eu>
-Subject: mvpp2: support fetching mac address from nvmem
-
-The mvpp2 driver did not query nvmem for hardware mac addresses. This
-patch adds querying of mac addresses stored in nvmem cells as a further
-fallback option before assigning a random address.
-Purposely added separately to fwnode_get_mac_address() above to maintain
-existing behaviour with builtin adapter mac address still taking
-precedence.
-
-Signed-off-by: Tobias Schramm <tobias@t-sys.eu>
----
---- a/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c
-+++ b/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c
-@@ -6156,6 +6156,12 @@ static int mvpp2_port_copy_mac_addr(stru
- return 0;
- }
-
-+ if (!of_get_mac_address(to_of_node(fwnode), hw_mac_addr)) {
-+ *mac_from = "nvmem cell";
-+ eth_hw_addr_set(dev, hw_mac_addr);
-+ return 0;
-+ }
-+
- *mac_from = "random";
- eth_hw_addr_random(dev);
-
+++ /dev/null
-From c28b2d367da8a471482e6a4aa8337ab6369a80c2 Mon Sep 17 00:00:00 2001
-From: Russell King <rmk+kernel@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@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
-@@ -42,8 +42,12 @@ static __cpuidle int mvebu_v7_enter_idle
-
- cpu_pm_exit();
-
-+ /*
-+ * If we failed to enter the desired state, indicate that we
-+ * slept lightly.
-+ */
- if (ret)
-- return ret;
-+ return 0;
-
- return index;
- }
+++ /dev/null
-From 287b9df160b6159f8d385424904f8bac501280c1 Mon Sep 17 00:00:00 2001
-From: Russell King <rmk+kernel@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@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
-@@ -1397,6 +1397,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);
-@@ -1404,7 +1405,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;
-@@ -1521,15 +1528,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;
- }
-
+++ /dev/null
-From cdfed4d05780450817ef96567e2cd8d355ca9e70 Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Marek=20Beh=C3=BAn?= <kabel@kernel.org>
-Date: Mon, 1 Jul 2024 13:30:03 +0200
-Subject: [PATCH 01/11] dt-bindings: firmware: add cznic,turris-omnia-mcu
- binding
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Add binding for cznic,turris-omnia-mcu, the device-tree node
-representing the system-controller features provided by the MCU on the
-Turris Omnia router.
-
-Signed-off-by: Marek Behún <kabel@kernel.org>
-Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
-Reviewed-by: Conor Dooley <conor.dooley@microchip.com>
-Reviewed-by: Andy Shevchenko <andy@kernel.org>
-Acked-by: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
-Link: https://lore.kernel.org/r/20240701113010.16447-2-kabel@kernel.org
-Signed-off-by: Arnd Bergmann <arnd@arndb.de>
----
- .../firmware/cznic,turris-omnia-mcu.yaml | 86 +++++++++++++++++++
- MAINTAINERS | 1 +
- 2 files changed, 87 insertions(+)
- create mode 100644 Documentation/devicetree/bindings/firmware/cznic,turris-omnia-mcu.yaml
-
---- /dev/null
-+++ b/Documentation/devicetree/bindings/firmware/cznic,turris-omnia-mcu.yaml
-@@ -0,0 +1,86 @@
-+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
-+%YAML 1.2
-+---
-+$id: http://devicetree.org/schemas/firmware/cznic,turris-omnia-mcu.yaml#
-+$schema: http://devicetree.org/meta-schemas/core.yaml#
-+
-+title: CZ.NIC's Turris Omnia MCU
-+
-+maintainers:
-+ - Marek Behún <kabel@kernel.org>
-+
-+description:
-+ The MCU on Turris Omnia acts as a system controller providing additional
-+ GPIOs, interrupts, watchdog, system power off and wakeup configuration.
-+
-+properties:
-+ compatible:
-+ const: cznic,turris-omnia-mcu
-+
-+ reg:
-+ description: MCU I2C slave address
-+ maxItems: 1
-+
-+ interrupts:
-+ maxItems: 1
-+
-+ interrupt-controller: true
-+
-+ '#interrupt-cells':
-+ const: 2
-+ description: |
-+ The first cell specifies the interrupt number (0 to 63), the second cell
-+ specifies interrupt type (which can be one of IRQ_TYPE_EDGE_RISING,
-+ IRQ_TYPE_EDGE_FALLING or IRQ_TYPE_EDGE_BOTH).
-+ The interrupt numbers correspond sequentially to GPIO numbers, taking the
-+ GPIO banks into account:
-+ IRQ number GPIO bank GPIO pin within bank
-+ 0 - 15 0 0 - 15
-+ 16 - 47 1 0 - 31
-+ 48 - 63 2 0 - 15
-+ There are several exceptions:
-+ IRQ number meaning
-+ 11 LED panel brightness changed by button press
-+ 13 TRNG entropy ready
-+ 14 ECDSA message signature computation done
-+
-+ gpio-controller: true
-+
-+ '#gpio-cells':
-+ const: 3
-+ description:
-+ The first cell is bank number (0, 1 or 2), the second cell is pin number
-+ within the bank (0 to 15 for banks 0 and 2, 0 to 31 for bank 1), and the
-+ third cell specifies consumer flags.
-+
-+required:
-+ - compatible
-+ - reg
-+ - interrupts
-+ - interrupt-controller
-+ - gpio-controller
-+
-+additionalProperties: false
-+
-+examples:
-+ - |
-+ #include <dt-bindings/interrupt-controller/irq.h>
-+
-+ i2c {
-+ #address-cells = <1>;
-+ #size-cells = <0>;
-+
-+ system-controller@2a {
-+ compatible = "cznic,turris-omnia-mcu";
-+ reg = <0x2a>;
-+
-+ interrupt-parent = <&gpio1>;
-+ interrupts = <11 IRQ_TYPE_NONE>;
-+
-+ gpio-controller;
-+ #gpio-cells = <3>;
-+
-+ interrupt-controller;
-+ #interrupt-cells = <2>;
-+ };
-+ };
---- a/MAINTAINERS
-+++ b/MAINTAINERS
-@@ -2104,6 +2104,7 @@ F: Documentation/ABI/testing/sysfs-bus-m
- F: Documentation/ABI/testing/sysfs-firmware-turris-mox-rwtm
- F: Documentation/devicetree/bindings/bus/moxtet.txt
- F: Documentation/devicetree/bindings/firmware/cznic,turris-mox-rwtm.txt
-+F: Documentation/devicetree/bindings/firmware/cznic,turris-omnia-mcu.yaml
- F: Documentation/devicetree/bindings/gpio/gpio-moxtet.txt
- F: Documentation/devicetree/bindings/leds/cznic,turris-omnia-leds.yaml
- F: Documentation/devicetree/bindings/watchdog/armada-37xx-wdt.txt
+++ /dev/null
-From 4a63f684c8badfc43f384df2291ed2566909a3bc Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Marek=20Beh=C3=BAn?= <kabel@kernel.org>
-Date: Mon, 1 Jul 2024 13:30:04 +0200
-Subject: [PATCH 02/11] platform: cznic: Add preliminary support for Turris
- Omnia MCU
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Add the basic skeleton for a new platform driver for the microcontroller
-found on the Turris Omnia board.
-
-Signed-off-by: Marek Behún <kabel@kernel.org>
-Reviewed-by: Andy Shevchenko <andy@kernel.org>
-Acked-by: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
-Link: https://lore.kernel.org/r/20240701113010.16447-3-kabel@kernel.org
-Signed-off-by: Arnd Bergmann <arnd@arndb.de>
----
- .../sysfs-bus-i2c-devices-turris-omnia-mcu | 81 ++++
- MAINTAINERS | 3 +
- drivers/platform/Kconfig | 2 +
- drivers/platform/Makefile | 1 +
- drivers/platform/cznic/Kconfig | 25 ++
- drivers/platform/cznic/Makefile | 4 +
- .../platform/cznic/turris-omnia-mcu-base.c | 393 ++++++++++++++++++
- drivers/platform/cznic/turris-omnia-mcu.h | 74 ++++
- include/linux/turris-omnia-mcu-interface.h | 249 +++++++++++
- 9 files changed, 832 insertions(+)
- create mode 100644 Documentation/ABI/testing/sysfs-bus-i2c-devices-turris-omnia-mcu
- create mode 100644 drivers/platform/cznic/Kconfig
- create mode 100644 drivers/platform/cznic/Makefile
- create mode 100644 drivers/platform/cznic/turris-omnia-mcu-base.c
- create mode 100644 drivers/platform/cznic/turris-omnia-mcu.h
- create mode 100644 include/linux/turris-omnia-mcu-interface.h
-
---- /dev/null
-+++ b/Documentation/ABI/testing/sysfs-bus-i2c-devices-turris-omnia-mcu
-@@ -0,0 +1,81 @@
-+What: /sys/bus/i2c/devices/<mcu_device>/board_revision
-+Date: September 2024
-+KernelVersion: 6.11
-+Contact: Marek Behún <kabel@kernel.org>
-+Description: (RO) Contains board revision number.
-+
-+ Only available if board information is burned in the MCU (older
-+ revisions have board information burned in the ATSHA204-A chip).
-+
-+ Format: %u.
-+
-+What: /sys/bus/i2c/devices/<mcu_device>/first_mac_address
-+Date: September 2024
-+KernelVersion: 6.11
-+Contact: Marek Behún <kabel@kernel.org>
-+Description: (RO) Contains device first MAC address. Each Turris Omnia is
-+ allocated 3 MAC addresses. The two additional addresses are
-+ computed from the first one by incrementing it.
-+
-+ Only available if board information is burned in the MCU (older
-+ revisions have board information burned in the ATSHA204-A chip).
-+
-+ Format: %pM.
-+
-+What: /sys/bus/i2c/devices/<mcu_device>/fw_features
-+Date: September 2024
-+KernelVersion: 6.11
-+Contact: Marek Behún <kabel@kernel.org>
-+Description: (RO) Newer versions of the microcontroller firmware report the
-+ features they support. These can be read from this file. If the
-+ MCU firmware is too old, this file reads 0x0.
-+
-+ Format: 0x%x.
-+
-+What: /sys/bus/i2c/devices/<mcu_device>/fw_version_hash_application
-+Date: September 2024
-+KernelVersion: 6.11
-+Contact: Marek Behún <kabel@kernel.org>
-+Description: (RO) Contains the version hash (commit hash) of the application
-+ part of the microcontroller firmware.
-+
-+ Format: %s.
-+
-+What: /sys/bus/i2c/devices/<mcu_device>/fw_version_hash_bootloader
-+Date: September 2024
-+KernelVersion: 6.11
-+Contact: Marek Behún <kabel@kernel.org>
-+Description: (RO) Contains the version hash (commit hash) of the bootloader
-+ part of the microcontroller firmware.
-+
-+ Format: %s.
-+
-+What: /sys/bus/i2c/devices/<mcu_device>/mcu_type
-+Date: September 2024
-+KernelVersion: 6.11
-+Contact: Marek Behún <kabel@kernel.org>
-+Description: (RO) Contains the microcontroller type (STM32, GD32, MKL).
-+
-+ Format: %s.
-+
-+What: /sys/bus/i2c/devices/<mcu_device>/reset_selector
-+Date: September 2024
-+KernelVersion: 6.11
-+Contact: Marek Behún <kabel@kernel.org>
-+Description: (RO) Contains the selected factory reset level, determined by
-+ how long the rear reset button was held by the user during board
-+ reset.
-+
-+ Format: %i.
-+
-+What: /sys/bus/i2c/devices/<mcu_device>/serial_number
-+Date: September 2024
-+KernelVersion: 6.11
-+Contact: Marek Behún <kabel@kernel.org>
-+Description: (RO) Contains the 64-bit board serial number in hexadecimal
-+ format.
-+
-+ Only available if board information is burned in the MCU (older
-+ revisions have board information burned in the ATSHA204-A chip).
-+
-+ Format: %016X.
---- a/MAINTAINERS
-+++ b/MAINTAINERS
-@@ -2100,6 +2100,7 @@ M: Marek Behún <kabel@kernel.org>
- S: Maintained
- W: https://www.turris.cz/
- F: Documentation/ABI/testing/debugfs-moxtet
-+F: Documentation/ABI/testing/sysfs-bus-i2c-devices-turris-omnia-mcu
- F: Documentation/ABI/testing/sysfs-bus-moxtet-devices
- F: Documentation/ABI/testing/sysfs-firmware-turris-mox-rwtm
- F: Documentation/devicetree/bindings/bus/moxtet.txt
-@@ -2113,10 +2114,12 @@ F: drivers/firmware/turris-mox-rwtm.c
- F: drivers/gpio/gpio-moxtet.c
- F: drivers/leds/leds-turris-omnia.c
- F: drivers/mailbox/armada-37xx-rwtm-mailbox.c
-+F: drivers/platform/cznic/
- F: drivers/watchdog/armada_37xx_wdt.c
- F: include/dt-bindings/bus/moxtet.h
- F: include/linux/armada-37xx-rwtm-mailbox.h
- F: include/linux/moxtet.h
-+F: include/linux/turris-omnia-mcu-interface.h
-
- ARM/FARADAY FA526 PORT
- M: Hans Ulli Kroll <ulli.kroll@googlemail.com>
---- a/drivers/platform/Kconfig
-+++ b/drivers/platform/Kconfig
-@@ -7,6 +7,8 @@ source "drivers/platform/goldfish/Kconfi
-
- source "drivers/platform/chrome/Kconfig"
-
-+source "drivers/platform/cznic/Kconfig"
-+
- source "drivers/platform/mellanox/Kconfig"
-
- source "drivers/platform/olpc/Kconfig"
---- a/drivers/platform/Makefile
-+++ b/drivers/platform/Makefile
-@@ -10,5 +10,6 @@ obj-$(CONFIG_MIPS) += mips/
- obj-$(CONFIG_OLPC_EC) += olpc/
- obj-$(CONFIG_GOLDFISH) += goldfish/
- obj-$(CONFIG_CHROME_PLATFORMS) += chrome/
-+obj-$(CONFIG_CZNIC_PLATFORMS) += cznic/
- obj-$(CONFIG_SURFACE_PLATFORMS) += surface/
- obj-$(CONFIG_MIKROTIK) += mikrotik/
---- /dev/null
-+++ b/drivers/platform/cznic/Kconfig
-@@ -0,0 +1,25 @@
-+# SPDX-License-Identifier: GPL-2.0-only
-+#
-+# For a description of the syntax of this configuration file,
-+# see Documentation/kbuild/kconfig-language.rst.
-+#
-+
-+menuconfig CZNIC_PLATFORMS
-+ bool "Platform support for CZ.NIC's Turris hardware"
-+ help
-+ Say Y here to be able to choose driver support for CZ.NIC's Turris
-+ devices. This option alone does not add any kernel code.
-+
-+if CZNIC_PLATFORMS
-+
-+config TURRIS_OMNIA_MCU
-+ tristate "Turris Omnia MCU driver"
-+ depends on MACH_ARMADA_38X || COMPILE_TEST
-+ depends on I2C
-+ help
-+ Say Y here to add support for the features implemented by the
-+ microcontroller on the CZ.NIC's Turris Omnia SOHO router.
-+ To compile this driver as a module, choose M here; the module will be
-+ called turris-omnia-mcu.
-+
-+endif # CZNIC_PLATFORMS
---- /dev/null
-+++ b/drivers/platform/cznic/Makefile
-@@ -0,0 +1,4 @@
-+# SPDX-License-Identifier: GPL-2.0-only
-+
-+obj-$(CONFIG_TURRIS_OMNIA_MCU) += turris-omnia-mcu.o
-+turris-omnia-mcu-y := turris-omnia-mcu-base.o
---- /dev/null
-+++ b/drivers/platform/cznic/turris-omnia-mcu-base.c
-@@ -0,0 +1,393 @@
-+// SPDX-License-Identifier: GPL-2.0
-+/*
-+ * CZ.NIC's Turris Omnia MCU driver
-+ *
-+ * 2024 by Marek Behún <kabel@kernel.org>
-+ */
-+
-+#include <linux/bits.h>
-+#include <linux/device.h>
-+#include <linux/errno.h>
-+#include <linux/hex.h>
-+#include <linux/i2c.h>
-+#include <linux/module.h>
-+#include <linux/string.h>
-+#include <linux/sysfs.h>
-+#include <linux/types.h>
-+
-+#include <linux/turris-omnia-mcu-interface.h>
-+#include "turris-omnia-mcu.h"
-+
-+#define OMNIA_FW_VERSION_LEN 20
-+#define OMNIA_FW_VERSION_HEX_LEN (2 * OMNIA_FW_VERSION_LEN + 1)
-+#define OMNIA_BOARD_INFO_LEN 16
-+
-+int omnia_cmd_write_read(const struct i2c_client *client,
-+ void *cmd, unsigned int cmd_len,
-+ void *reply, unsigned int reply_len)
-+{
-+ struct i2c_msg msgs[2];
-+ int ret, num;
-+
-+ msgs[0].addr = client->addr;
-+ msgs[0].flags = 0;
-+ msgs[0].len = cmd_len;
-+ msgs[0].buf = cmd;
-+ num = 1;
-+
-+ if (reply_len) {
-+ msgs[1].addr = client->addr;
-+ msgs[1].flags = I2C_M_RD;
-+ msgs[1].len = reply_len;
-+ msgs[1].buf = reply;
-+ num++;
-+ }
-+
-+ ret = i2c_transfer(client->adapter, msgs, num);
-+ if (ret < 0)
-+ return ret;
-+ if (ret != num)
-+ return -EIO;
-+
-+ return 0;
-+}
-+
-+static int omnia_get_version_hash(struct omnia_mcu *mcu, bool bootloader,
-+ char version[static OMNIA_FW_VERSION_HEX_LEN])
-+{
-+ u8 reply[OMNIA_FW_VERSION_LEN];
-+ char *p;
-+ int err;
-+
-+ err = omnia_cmd_read(mcu->client,
-+ bootloader ? OMNIA_CMD_GET_FW_VERSION_BOOT
-+ : OMNIA_CMD_GET_FW_VERSION_APP,
-+ reply, sizeof(reply));
-+ if (err)
-+ return err;
-+
-+ p = bin2hex(version, reply, OMNIA_FW_VERSION_LEN);
-+ *p = '\0';
-+
-+ return 0;
-+}
-+
-+static ssize_t fw_version_hash_show(struct device *dev, char *buf,
-+ bool bootloader)
-+{
-+ struct omnia_mcu *mcu = dev_get_drvdata(dev);
-+ char version[OMNIA_FW_VERSION_HEX_LEN];
-+ int err;
-+
-+ err = omnia_get_version_hash(mcu, bootloader, version);
-+ if (err)
-+ return err;
-+
-+ return sysfs_emit(buf, "%s\n", version);
-+}
-+
-+static ssize_t fw_version_hash_application_show(struct device *dev,
-+ struct device_attribute *a,
-+ char *buf)
-+{
-+ return fw_version_hash_show(dev, buf, false);
-+}
-+static DEVICE_ATTR_RO(fw_version_hash_application);
-+
-+static ssize_t fw_version_hash_bootloader_show(struct device *dev,
-+ struct device_attribute *a,
-+ char *buf)
-+{
-+ return fw_version_hash_show(dev, buf, true);
-+}
-+static DEVICE_ATTR_RO(fw_version_hash_bootloader);
-+
-+static ssize_t fw_features_show(struct device *dev, struct device_attribute *a,
-+ char *buf)
-+{
-+ struct omnia_mcu *mcu = dev_get_drvdata(dev);
-+
-+ return sysfs_emit(buf, "0x%x\n", mcu->features);
-+}
-+static DEVICE_ATTR_RO(fw_features);
-+
-+static ssize_t mcu_type_show(struct device *dev, struct device_attribute *a,
-+ char *buf)
-+{
-+ struct omnia_mcu *mcu = dev_get_drvdata(dev);
-+
-+ return sysfs_emit(buf, "%s\n", mcu->type);
-+}
-+static DEVICE_ATTR_RO(mcu_type);
-+
-+static ssize_t reset_selector_show(struct device *dev,
-+ struct device_attribute *a, char *buf)
-+{
-+ u8 reply;
-+ int err;
-+
-+ err = omnia_cmd_read_u8(to_i2c_client(dev), OMNIA_CMD_GET_RESET,
-+ &reply);
-+ if (err)
-+ return err;
-+
-+ return sysfs_emit(buf, "%d\n", reply);
-+}
-+static DEVICE_ATTR_RO(reset_selector);
-+
-+static ssize_t serial_number_show(struct device *dev,
-+ struct device_attribute *a, char *buf)
-+{
-+ struct omnia_mcu *mcu = dev_get_drvdata(dev);
-+
-+ return sysfs_emit(buf, "%016llX\n", mcu->board_serial_number);
-+}
-+static DEVICE_ATTR_RO(serial_number);
-+
-+static ssize_t first_mac_address_show(struct device *dev,
-+ struct device_attribute *a, char *buf)
-+{
-+ struct omnia_mcu *mcu = dev_get_drvdata(dev);
-+
-+ return sysfs_emit(buf, "%pM\n", mcu->board_first_mac);
-+}
-+static DEVICE_ATTR_RO(first_mac_address);
-+
-+static ssize_t board_revision_show(struct device *dev,
-+ struct device_attribute *a, char *buf)
-+{
-+ struct omnia_mcu *mcu = dev_get_drvdata(dev);
-+
-+ return sysfs_emit(buf, "%u\n", mcu->board_revision);
-+}
-+static DEVICE_ATTR_RO(board_revision);
-+
-+static struct attribute *omnia_mcu_base_attrs[] = {
-+ &dev_attr_fw_version_hash_application.attr,
-+ &dev_attr_fw_version_hash_bootloader.attr,
-+ &dev_attr_fw_features.attr,
-+ &dev_attr_mcu_type.attr,
-+ &dev_attr_reset_selector.attr,
-+ &dev_attr_serial_number.attr,
-+ &dev_attr_first_mac_address.attr,
-+ &dev_attr_board_revision.attr,
-+ NULL
-+};
-+
-+static umode_t omnia_mcu_base_attrs_visible(struct kobject *kobj,
-+ struct attribute *a, int n)
-+{
-+ struct device *dev = kobj_to_dev(kobj);
-+ struct omnia_mcu *mcu = dev_get_drvdata(dev);
-+
-+ if ((a == &dev_attr_serial_number.attr ||
-+ a == &dev_attr_first_mac_address.attr ||
-+ a == &dev_attr_board_revision.attr) &&
-+ !(mcu->features & OMNIA_FEAT_BOARD_INFO))
-+ return 0;
-+
-+ return a->mode;
-+}
-+
-+static const struct attribute_group omnia_mcu_base_group = {
-+ .attrs = omnia_mcu_base_attrs,
-+ .is_visible = omnia_mcu_base_attrs_visible,
-+};
-+
-+static const struct attribute_group *omnia_mcu_groups[] = {
-+ &omnia_mcu_base_group,
-+ NULL
-+};
-+
-+static void omnia_mcu_print_version_hash(struct omnia_mcu *mcu, bool bootloader)
-+{
-+ const char *type = bootloader ? "bootloader" : "application";
-+ struct device *dev = &mcu->client->dev;
-+ char version[OMNIA_FW_VERSION_HEX_LEN];
-+ int err;
-+
-+ err = omnia_get_version_hash(mcu, bootloader, version);
-+ if (err) {
-+ dev_err(dev, "Cannot read MCU %s firmware version: %d\n",
-+ type, err);
-+ return;
-+ }
-+
-+ dev_info(dev, "MCU %s firmware version hash: %s\n", type, version);
-+}
-+
-+static const char *omnia_status_to_mcu_type(u16 status)
-+{
-+ switch (status & OMNIA_STS_MCU_TYPE_MASK) {
-+ case OMNIA_STS_MCU_TYPE_STM32:
-+ return "STM32";
-+ case OMNIA_STS_MCU_TYPE_GD32:
-+ return "GD32";
-+ case OMNIA_STS_MCU_TYPE_MKL:
-+ return "MKL";
-+ default:
-+ return "unknown";
-+ }
-+}
-+
-+static void omnia_info_missing_feature(struct device *dev, const char *feature)
-+{
-+ dev_info(dev,
-+ "Your board's MCU firmware does not support the %s feature.\n",
-+ feature);
-+}
-+
-+static int omnia_mcu_read_features(struct omnia_mcu *mcu)
-+{
-+ static const struct {
-+ u16 mask;
-+ const char *name;
-+ } features[] = {
-+#define _DEF_FEAT(_n, _m) { OMNIA_FEAT_ ## _n, _m }
-+ _DEF_FEAT(EXT_CMDS, "extended control and status"),
-+ _DEF_FEAT(WDT_PING, "watchdog pinging"),
-+ _DEF_FEAT(LED_STATE_EXT_MASK, "peripheral LED pins reading"),
-+ _DEF_FEAT(NEW_INT_API, "new interrupt API"),
-+ _DEF_FEAT(POWEROFF_WAKEUP, "poweroff and wakeup"),
-+ _DEF_FEAT(TRNG, "true random number generator"),
-+#undef _DEF_FEAT
-+ };
-+ struct i2c_client *client = mcu->client;
-+ struct device *dev = &client->dev;
-+ bool suggest_fw_upgrade = false;
-+ u16 status;
-+ int err;
-+
-+ /* status word holds MCU type, which we need below */
-+ err = omnia_cmd_read_u16(client, OMNIA_CMD_GET_STATUS_WORD, &status);
-+ if (err)
-+ return err;
-+
-+ /*
-+ * Check whether MCU firmware supports the OMNIA_CMD_GET_FEATURES
-+ * command.
-+ */
-+ if (status & OMNIA_STS_FEATURES_SUPPORTED) {
-+ /* try read 32-bit features */
-+ err = omnia_cmd_read_u32(client, OMNIA_CMD_GET_FEATURES,
-+ &mcu->features);
-+ if (err) {
-+ /* try read 16-bit features */
-+ u16 features16;
-+
-+ err = omnia_cmd_read_u16(client, OMNIA_CMD_GET_FEATURES,
-+ &features16);
-+ if (err)
-+ return err;
-+
-+ mcu->features = features16;
-+ } else {
-+ if (mcu->features & OMNIA_FEAT_FROM_BIT_16_INVALID)
-+ mcu->features &= GENMASK(15, 0);
-+ }
-+ } else {
-+ dev_info(dev,
-+ "Your board's MCU firmware does not support feature reading.\n");
-+ suggest_fw_upgrade = true;
-+ }
-+
-+ mcu->type = omnia_status_to_mcu_type(status);
-+ dev_info(dev, "MCU type %s%s\n", mcu->type,
-+ (mcu->features & OMNIA_FEAT_PERIPH_MCU) ?
-+ ", with peripheral resets wired" : "");
-+
-+ omnia_mcu_print_version_hash(mcu, true);
-+
-+ if (mcu->features & OMNIA_FEAT_BOOTLOADER)
-+ dev_warn(dev,
-+ "MCU is running bootloader firmware. Was firmware upgrade interrupted?\n");
-+ else
-+ omnia_mcu_print_version_hash(mcu, false);
-+
-+ for (unsigned int i = 0; i < ARRAY_SIZE(features); i++) {
-+ if (mcu->features & features[i].mask)
-+ continue;
-+
-+ omnia_info_missing_feature(dev, features[i].name);
-+ suggest_fw_upgrade = true;
-+ }
-+
-+ if (suggest_fw_upgrade)
-+ dev_info(dev,
-+ "Consider upgrading MCU firmware with the omnia-mcutool utility.\n");
-+
-+ return 0;
-+}
-+
-+static int omnia_mcu_read_board_info(struct omnia_mcu *mcu)
-+{
-+ u8 reply[1 + OMNIA_BOARD_INFO_LEN];
-+ int err;
-+
-+ err = omnia_cmd_read(mcu->client, OMNIA_CMD_BOARD_INFO_GET, reply,
-+ sizeof(reply));
-+ if (err)
-+ return err;
-+
-+ if (reply[0] != OMNIA_BOARD_INFO_LEN)
-+ return -EIO;
-+
-+ mcu->board_serial_number = get_unaligned_le64(&reply[1]);
-+
-+ /* we can't use ether_addr_copy() because reply is not u16-aligned */
-+ memcpy(mcu->board_first_mac, &reply[9], sizeof(mcu->board_first_mac));
-+
-+ mcu->board_revision = reply[15];
-+
-+ return 0;
-+}
-+
-+static int omnia_mcu_probe(struct i2c_client *client)
-+{
-+ struct device *dev = &client->dev;
-+ struct omnia_mcu *mcu;
-+ int err;
-+
-+ if (!client->irq)
-+ return dev_err_probe(dev, -EINVAL, "IRQ resource not found\n");
-+
-+ mcu = devm_kzalloc(dev, sizeof(*mcu), GFP_KERNEL);
-+ if (!mcu)
-+ return -ENOMEM;
-+
-+ mcu->client = client;
-+ i2c_set_clientdata(client, mcu);
-+
-+ err = omnia_mcu_read_features(mcu);
-+ if (err)
-+ return dev_err_probe(dev, err,
-+ "Cannot determine MCU supported features\n");
-+
-+ if (mcu->features & OMNIA_FEAT_BOARD_INFO) {
-+ err = omnia_mcu_read_board_info(mcu);
-+ if (err)
-+ return dev_err_probe(dev, err,
-+ "Cannot read board info\n");
-+ }
-+
-+ return 0;
-+}
-+
-+static const struct of_device_id of_omnia_mcu_match[] = {
-+ { .compatible = "cznic,turris-omnia-mcu" },
-+ {}
-+};
-+
-+static struct i2c_driver omnia_mcu_driver = {
-+ .probe = omnia_mcu_probe,
-+ .driver = {
-+ .name = "turris-omnia-mcu",
-+ .of_match_table = of_omnia_mcu_match,
-+ .dev_groups = omnia_mcu_groups,
-+ },
-+};
-+module_i2c_driver(omnia_mcu_driver);
-+
-+MODULE_AUTHOR("Marek Behun <kabel@kernel.org>");
-+MODULE_DESCRIPTION("CZ.NIC's Turris Omnia MCU");
-+MODULE_LICENSE("GPL");
---- /dev/null
-+++ b/drivers/platform/cznic/turris-omnia-mcu.h
-@@ -0,0 +1,74 @@
-+/* SPDX-License-Identifier: GPL-2.0 */
-+/*
-+ * CZ.NIC's Turris Omnia MCU driver
-+ *
-+ * 2024 by Marek Behún <kabel@kernel.org>
-+ */
-+
-+#ifndef __TURRIS_OMNIA_MCU_H
-+#define __TURRIS_OMNIA_MCU_H
-+
-+#include <linux/if_ether.h>
-+#include <linux/types.h>
-+#include <asm/byteorder.h>
-+
-+struct i2c_client;
-+
-+struct omnia_mcu {
-+ struct i2c_client *client;
-+ const char *type;
-+ u32 features;
-+
-+ /* board information */
-+ u64 board_serial_number;
-+ u8 board_first_mac[ETH_ALEN];
-+ u8 board_revision;
-+};
-+
-+int omnia_cmd_write_read(const struct i2c_client *client,
-+ void *cmd, unsigned int cmd_len,
-+ void *reply, unsigned int reply_len);
-+
-+static inline int omnia_cmd_read(const struct i2c_client *client, u8 cmd,
-+ void *reply, unsigned int len)
-+{
-+ return omnia_cmd_write_read(client, &cmd, 1, reply, len);
-+}
-+
-+static inline int omnia_cmd_read_u32(const struct i2c_client *client, u8 cmd,
-+ u32 *dst)
-+{
-+ __le32 reply;
-+ int err;
-+
-+ err = omnia_cmd_read(client, cmd, &reply, sizeof(reply));
-+ if (err)
-+ return err;
-+
-+ *dst = le32_to_cpu(reply);
-+
-+ return 0;
-+}
-+
-+static inline int omnia_cmd_read_u16(const struct i2c_client *client, u8 cmd,
-+ u16 *dst)
-+{
-+ __le16 reply;
-+ int err;
-+
-+ err = omnia_cmd_read(client, cmd, &reply, sizeof(reply));
-+ if (err)
-+ return err;
-+
-+ *dst = le16_to_cpu(reply);
-+
-+ return 0;
-+}
-+
-+static inline int omnia_cmd_read_u8(const struct i2c_client *client, u8 cmd,
-+ u8 *reply)
-+{
-+ return omnia_cmd_read(client, cmd, reply, sizeof(*reply));
-+}
-+
-+#endif /* __TURRIS_OMNIA_MCU_H */
---- /dev/null
-+++ b/include/linux/turris-omnia-mcu-interface.h
-@@ -0,0 +1,249 @@
-+/* SPDX-License-Identifier: GPL-2.0 */
-+/*
-+ * CZ.NIC's Turris Omnia MCU I2C interface commands definitions
-+ *
-+ * 2024 by Marek Behún <kabel@kernel.org>
-+ */
-+
-+#ifndef __TURRIS_OMNIA_MCU_INTERFACE_H
-+#define __TURRIS_OMNIA_MCU_INTERFACE_H
-+
-+#include <linux/bitfield.h>
-+#include <linux/bits.h>
-+
-+enum omnia_commands_e {
-+ OMNIA_CMD_GET_STATUS_WORD = 0x01, /* slave sends status word back */
-+ OMNIA_CMD_GENERAL_CONTROL = 0x02,
-+ OMNIA_CMD_LED_MODE = 0x03, /* default/user */
-+ OMNIA_CMD_LED_STATE = 0x04, /* LED on/off */
-+ OMNIA_CMD_LED_COLOR = 0x05, /* LED number + RED + GREEN + BLUE */
-+ OMNIA_CMD_USER_VOLTAGE = 0x06,
-+ OMNIA_CMD_SET_BRIGHTNESS = 0x07,
-+ OMNIA_CMD_GET_BRIGHTNESS = 0x08,
-+ OMNIA_CMD_GET_RESET = 0x09,
-+ OMNIA_CMD_GET_FW_VERSION_APP = 0x0A, /* 20B git hash number */
-+ OMNIA_CMD_SET_WATCHDOG_STATE = 0x0B, /* 0 - disable
-+ * 1 - enable / ping
-+ * after boot watchdog is started
-+ * with 2 minutes timeout
-+ */
-+
-+ /* OMNIA_CMD_WATCHDOG_STATUS = 0x0C, not implemented anymore */
-+
-+ OMNIA_CMD_GET_WATCHDOG_STATE = 0x0D,
-+ OMNIA_CMD_GET_FW_VERSION_BOOT = 0x0E, /* 20B Git hash number */
-+ OMNIA_CMD_GET_FW_CHECKSUM = 0x0F, /* 4B length, 4B checksum */
-+
-+ /* available if FEATURES_SUPPORTED bit set in status word */
-+ OMNIA_CMD_GET_FEATURES = 0x10,
-+
-+ /* available if EXT_CMD bit set in features */
-+ OMNIA_CMD_GET_EXT_STATUS_DWORD = 0x11,
-+ OMNIA_CMD_EXT_CONTROL = 0x12,
-+ OMNIA_CMD_GET_EXT_CONTROL_STATUS = 0x13,
-+
-+ /* available if NEW_INT_API bit set in features */
-+ OMNIA_CMD_GET_INT_AND_CLEAR = 0x14,
-+ OMNIA_CMD_GET_INT_MASK = 0x15,
-+ OMNIA_CMD_SET_INT_MASK = 0x16,
-+
-+ /* available if FLASHING bit set in features */
-+ OMNIA_CMD_FLASH = 0x19,
-+
-+ /* available if WDT_PING bit set in features */
-+ OMNIA_CMD_SET_WDT_TIMEOUT = 0x20,
-+ OMNIA_CMD_GET_WDT_TIMELEFT = 0x21,
-+
-+ /* available if POWEROFF_WAKEUP bit set in features */
-+ OMNIA_CMD_SET_WAKEUP = 0x22,
-+ OMNIA_CMD_GET_UPTIME_AND_WAKEUP = 0x23,
-+ OMNIA_CMD_POWER_OFF = 0x24,
-+
-+ /* available if USB_OVC_PROT_SETTING bit set in features */
-+ OMNIA_CMD_SET_USB_OVC_PROT = 0x25,
-+ OMNIA_CMD_GET_USB_OVC_PROT = 0x26,
-+
-+ /* available if TRNG bit set in features */
-+ OMNIA_CMD_TRNG_COLLECT_ENTROPY = 0x28,
-+
-+ /* available if CRYPTO bit set in features */
-+ OMNIA_CMD_CRYPTO_GET_PUBLIC_KEY = 0x29,
-+ OMNIA_CMD_CRYPTO_SIGN_MESSAGE = 0x2A,
-+ OMNIA_CMD_CRYPTO_COLLECT_SIGNATURE = 0x2B,
-+
-+ /* available if BOARD_INFO it set in features */
-+ OMNIA_CMD_BOARD_INFO_GET = 0x2C,
-+ OMNIA_CMD_BOARD_INFO_BURN = 0x2D,
-+
-+ /* available only at address 0x2b (LED-controller) */
-+ /* available only if LED_GAMMA_CORRECTION bit set in features */
-+ OMNIA_CMD_SET_GAMMA_CORRECTION = 0x30,
-+ OMNIA_CMD_GET_GAMMA_CORRECTION = 0x31,
-+
-+ /* available only at address 0x2b (LED-controller) */
-+ /* available only if PER_LED_CORRECTION bit set in features */
-+ /* available only if FROM_BIT_16_INVALID bit NOT set in features */
-+ OMNIA_CMD_SET_LED_CORRECTIONS = 0x32,
-+ OMNIA_CMD_GET_LED_CORRECTIONS = 0x33,
-+};
-+
-+enum omnia_flashing_commands_e {
-+ OMNIA_FLASH_CMD_UNLOCK = 0x01,
-+ OMNIA_FLASH_CMD_SIZE_AND_CSUM = 0x02,
-+ OMNIA_FLASH_CMD_PROGRAM = 0x03,
-+ OMNIA_FLASH_CMD_RESET = 0x04,
-+};
-+
-+enum omnia_sts_word_e {
-+ OMNIA_STS_MCU_TYPE_MASK = GENMASK(1, 0),
-+ OMNIA_STS_MCU_TYPE_STM32 = FIELD_PREP_CONST(OMNIA_STS_MCU_TYPE_MASK, 0),
-+ OMNIA_STS_MCU_TYPE_GD32 = FIELD_PREP_CONST(OMNIA_STS_MCU_TYPE_MASK, 1),
-+ OMNIA_STS_MCU_TYPE_MKL = FIELD_PREP_CONST(OMNIA_STS_MCU_TYPE_MASK, 2),
-+ OMNIA_STS_FEATURES_SUPPORTED = BIT(2),
-+ OMNIA_STS_USER_REGULATOR_NOT_SUPPORTED = BIT(3),
-+ OMNIA_STS_CARD_DET = BIT(4),
-+ OMNIA_STS_MSATA_IND = BIT(5),
-+ OMNIA_STS_USB30_OVC = BIT(6),
-+ OMNIA_STS_USB31_OVC = BIT(7),
-+ OMNIA_STS_USB30_PWRON = BIT(8),
-+ OMNIA_STS_USB31_PWRON = BIT(9),
-+ OMNIA_STS_ENABLE_4V5 = BIT(10),
-+ OMNIA_STS_BUTTON_MODE = BIT(11),
-+ OMNIA_STS_BUTTON_PRESSED = BIT(12),
-+ OMNIA_STS_BUTTON_COUNTER_MASK = GENMASK(15, 13),
-+};
-+
-+enum omnia_ctl_byte_e {
-+ OMNIA_CTL_LIGHT_RST = BIT(0),
-+ OMNIA_CTL_HARD_RST = BIT(1),
-+ /* BIT(2) is currently reserved */
-+ OMNIA_CTL_USB30_PWRON = BIT(3),
-+ OMNIA_CTL_USB31_PWRON = BIT(4),
-+ OMNIA_CTL_ENABLE_4V5 = BIT(5),
-+ OMNIA_CTL_BUTTON_MODE = BIT(6),
-+ OMNIA_CTL_BOOTLOADER = BIT(7),
-+};
-+
-+enum omnia_features_e {
-+ OMNIA_FEAT_PERIPH_MCU = BIT(0),
-+ OMNIA_FEAT_EXT_CMDS = BIT(1),
-+ OMNIA_FEAT_WDT_PING = BIT(2),
-+ OMNIA_FEAT_LED_STATE_EXT_MASK = GENMASK(4, 3),
-+ OMNIA_FEAT_LED_STATE_EXT = FIELD_PREP_CONST(OMNIA_FEAT_LED_STATE_EXT_MASK, 1),
-+ OMNIA_FEAT_LED_STATE_EXT_V32 = FIELD_PREP_CONST(OMNIA_FEAT_LED_STATE_EXT_MASK, 2),
-+ OMNIA_FEAT_LED_GAMMA_CORRECTION = BIT(5),
-+ OMNIA_FEAT_NEW_INT_API = BIT(6),
-+ OMNIA_FEAT_BOOTLOADER = BIT(7),
-+ OMNIA_FEAT_FLASHING = BIT(8),
-+ OMNIA_FEAT_NEW_MESSAGE_API = BIT(9),
-+ OMNIA_FEAT_BRIGHTNESS_INT = BIT(10),
-+ OMNIA_FEAT_POWEROFF_WAKEUP = BIT(11),
-+ OMNIA_FEAT_CAN_OLD_MESSAGE_API = BIT(12),
-+ OMNIA_FEAT_TRNG = BIT(13),
-+ OMNIA_FEAT_CRYPTO = BIT(14),
-+ OMNIA_FEAT_BOARD_INFO = BIT(15),
-+
-+ /*
-+ * Orginally the features command replied only 16 bits. If more were
-+ * read, either the I2C transaction failed or 0xff bytes were sent.
-+ * Therefore to consider bits 16 - 31 valid, one bit (20) was reserved
-+ * to be zero.
-+ */
-+
-+ /* Bits 16 - 19 correspond to bits 0 - 3 of status word */
-+ OMNIA_FEAT_MCU_TYPE_MASK = GENMASK(17, 16),
-+ OMNIA_FEAT_MCU_TYPE_STM32 = FIELD_PREP_CONST(OMNIA_FEAT_MCU_TYPE_MASK, 0),
-+ OMNIA_FEAT_MCU_TYPE_GD32 = FIELD_PREP_CONST(OMNIA_FEAT_MCU_TYPE_MASK, 1),
-+ OMNIA_FEAT_MCU_TYPE_MKL = FIELD_PREP_CONST(OMNIA_FEAT_MCU_TYPE_MASK, 2),
-+ OMNIA_FEAT_FEATURES_SUPPORTED = BIT(18),
-+ OMNIA_FEAT_USER_REGULATOR_NOT_SUPPORTED = BIT(19),
-+
-+ /* must not be set */
-+ OMNIA_FEAT_FROM_BIT_16_INVALID = BIT(20),
-+
-+ OMNIA_FEAT_PER_LED_CORRECTION = BIT(21),
-+ OMNIA_FEAT_USB_OVC_PROT_SETTING = BIT(22),
-+};
-+
-+enum omnia_ext_sts_dword_e {
-+ OMNIA_EXT_STS_SFP_nDET = BIT(0),
-+ OMNIA_EXT_STS_LED_STATES_MASK = GENMASK(31, 12),
-+ OMNIA_EXT_STS_WLAN0_MSATA_LED = BIT(12),
-+ OMNIA_EXT_STS_WLAN1_LED = BIT(13),
-+ OMNIA_EXT_STS_WLAN2_LED = BIT(14),
-+ OMNIA_EXT_STS_WPAN0_LED = BIT(15),
-+ OMNIA_EXT_STS_WPAN1_LED = BIT(16),
-+ OMNIA_EXT_STS_WPAN2_LED = BIT(17),
-+ OMNIA_EXT_STS_WAN_LED0 = BIT(18),
-+ OMNIA_EXT_STS_WAN_LED1 = BIT(19),
-+ OMNIA_EXT_STS_LAN0_LED0 = BIT(20),
-+ OMNIA_EXT_STS_LAN0_LED1 = BIT(21),
-+ OMNIA_EXT_STS_LAN1_LED0 = BIT(22),
-+ OMNIA_EXT_STS_LAN1_LED1 = BIT(23),
-+ OMNIA_EXT_STS_LAN2_LED0 = BIT(24),
-+ OMNIA_EXT_STS_LAN2_LED1 = BIT(25),
-+ OMNIA_EXT_STS_LAN3_LED0 = BIT(26),
-+ OMNIA_EXT_STS_LAN3_LED1 = BIT(27),
-+ OMNIA_EXT_STS_LAN4_LED0 = BIT(28),
-+ OMNIA_EXT_STS_LAN4_LED1 = BIT(29),
-+ OMNIA_EXT_STS_LAN5_LED0 = BIT(30),
-+ OMNIA_EXT_STS_LAN5_LED1 = BIT(31),
-+};
-+
-+enum omnia_ext_ctl_e {
-+ OMNIA_EXT_CTL_nRES_MMC = BIT(0),
-+ OMNIA_EXT_CTL_nRES_LAN = BIT(1),
-+ OMNIA_EXT_CTL_nRES_PHY = BIT(2),
-+ OMNIA_EXT_CTL_nPERST0 = BIT(3),
-+ OMNIA_EXT_CTL_nPERST1 = BIT(4),
-+ OMNIA_EXT_CTL_nPERST2 = BIT(5),
-+ OMNIA_EXT_CTL_PHY_SFP = BIT(6),
-+ OMNIA_EXT_CTL_PHY_SFP_AUTO = BIT(7),
-+ OMNIA_EXT_CTL_nVHV_CTRL = BIT(8),
-+};
-+
-+enum omnia_int_e {
-+ OMNIA_INT_CARD_DET = BIT(0),
-+ OMNIA_INT_MSATA_IND = BIT(1),
-+ OMNIA_INT_USB30_OVC = BIT(2),
-+ OMNIA_INT_USB31_OVC = BIT(3),
-+ OMNIA_INT_BUTTON_PRESSED = BIT(4),
-+ OMNIA_INT_SFP_nDET = BIT(5),
-+ OMNIA_INT_BRIGHTNESS_CHANGED = BIT(6),
-+ OMNIA_INT_TRNG = BIT(7),
-+ OMNIA_INT_MESSAGE_SIGNED = BIT(8),
-+
-+ OMNIA_INT_LED_STATES_MASK = GENMASK(31, 12),
-+ OMNIA_INT_WLAN0_MSATA_LED = BIT(12),
-+ OMNIA_INT_WLAN1_LED = BIT(13),
-+ OMNIA_INT_WLAN2_LED = BIT(14),
-+ OMNIA_INT_WPAN0_LED = BIT(15),
-+ OMNIA_INT_WPAN1_LED = BIT(16),
-+ OMNIA_INT_WPAN2_LED = BIT(17),
-+ OMNIA_INT_WAN_LED0 = BIT(18),
-+ OMNIA_INT_WAN_LED1 = BIT(19),
-+ OMNIA_INT_LAN0_LED0 = BIT(20),
-+ OMNIA_INT_LAN0_LED1 = BIT(21),
-+ OMNIA_INT_LAN1_LED0 = BIT(22),
-+ OMNIA_INT_LAN1_LED1 = BIT(23),
-+ OMNIA_INT_LAN2_LED0 = BIT(24),
-+ OMNIA_INT_LAN2_LED1 = BIT(25),
-+ OMNIA_INT_LAN3_LED0 = BIT(26),
-+ OMNIA_INT_LAN3_LED1 = BIT(27),
-+ OMNIA_INT_LAN4_LED0 = BIT(28),
-+ OMNIA_INT_LAN4_LED1 = BIT(29),
-+ OMNIA_INT_LAN5_LED0 = BIT(30),
-+ OMNIA_INT_LAN5_LED1 = BIT(31),
-+};
-+
-+enum omnia_cmd_poweroff_e {
-+ OMNIA_CMD_POWER_OFF_POWERON_BUTTON = BIT(0),
-+ OMNIA_CMD_POWER_OFF_MAGIC = 0xdead,
-+};
-+
-+enum omnia_cmd_usb_ovc_prot_e {
-+ OMNIA_CMD_xET_USB_OVC_PROT_PORT_MASK = GENMASK(3, 0),
-+ OMNIA_CMD_xET_USB_OVC_PROT_ENABLE = BIT(4),
-+};
-+
-+#endif /* __TURRIS_OMNIA_MCU_INTERFACE_H */
+++ /dev/null
-From 7f4f2744f9788312e12940b516b51a0a466b137e Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Marek=20Beh=C3=BAn?= <kabel@kernel.org>
-Date: Mon, 1 Jul 2024 13:30:05 +0200
-Subject: [PATCH 03/11] platform: cznic: turris-omnia-mcu: Add support for MCU
- connected GPIOs
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Add support for GPIOs connected to the MCU on the Turris Omnia board.
-
-This includes:
-- front button pin
-- enable pins for USB regulators
-- MiniPCIe / mSATA card presence pins in MiniPCIe port 0
-- LED output pins from WAN ethernet PHY, LAN switch and MiniPCIe ports
-- on board revisions 32+ also various peripheral resets and another
- voltage regulator enable pin
-
-Signed-off-by: Marek Behún <kabel@kernel.org>
-Acked-by: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
-Link: https://lore.kernel.org/r/20240701113010.16447-4-kabel@kernel.org
-Signed-off-by: Arnd Bergmann <arnd@arndb.de>
----
- .../sysfs-bus-i2c-devices-turris-omnia-mcu | 16 +
- drivers/platform/cznic/Kconfig | 15 +
- drivers/platform/cznic/Makefile | 1 +
- .../platform/cznic/turris-omnia-mcu-base.c | 3 +-
- .../platform/cznic/turris-omnia-mcu-gpio.c | 1094 +++++++++++++++++
- drivers/platform/cznic/turris-omnia-mcu.h | 68 +
- 6 files changed, 1196 insertions(+), 1 deletion(-)
- create mode 100644 drivers/platform/cznic/turris-omnia-mcu-gpio.c
-
---- a/Documentation/ABI/testing/sysfs-bus-i2c-devices-turris-omnia-mcu
-+++ b/Documentation/ABI/testing/sysfs-bus-i2c-devices-turris-omnia-mcu
-@@ -22,6 +22,22 @@ Description: (RO) Contains device first
-
- Format: %pM.
-
-+What: /sys/bus/i2c/devices/<mcu_device>/front_button_mode
-+Date: September 2024
-+KernelVersion: 6.11
-+Contact: Marek Behún <kabel@kernel.org>
-+Description: (RW) The front button on the Turris Omnia router can be
-+ configured either to change the intensity of all the LEDs on the
-+ front panel, or to send the press event to the CPU as an
-+ interrupt.
-+
-+ This file switches between these two modes:
-+ - "mcu" makes the button press event be handled by the MCU to
-+ change the LEDs panel intensity.
-+ - "cpu" makes the button press event be handled by the CPU.
-+
-+ Format: %s.
-+
- What: /sys/bus/i2c/devices/<mcu_device>/fw_features
- Date: September 2024
- KernelVersion: 6.11
---- a/drivers/platform/cznic/Kconfig
-+++ b/drivers/platform/cznic/Kconfig
-@@ -16,9 +16,24 @@ config TURRIS_OMNIA_MCU
- tristate "Turris Omnia MCU driver"
- depends on MACH_ARMADA_38X || COMPILE_TEST
- depends on I2C
-+ select GPIOLIB
-+ select GPIOLIB_IRQCHIP
- help
- Say Y here to add support for the features implemented by the
- microcontroller on the CZ.NIC's Turris Omnia SOHO router.
-+ The features include:
-+ - GPIO pins
-+ - to get front button press events (the front button can be
-+ configured either to generate press events to the CPU or to change
-+ front LEDs panel brightness)
-+ - to enable / disable USB port voltage regulators and to detect
-+ USB overcurrent
-+ - to detect MiniPCIe / mSATA card presence in MiniPCIe port 0
-+ - to configure resets of various peripherals on board revisions 32+
-+ - to enable / disable the VHV voltage regulator to the SOC in order
-+ to be able to program SOC's OTP on board revisions 32+
-+ - to get input from the LED output pins of the WAN ethernet PHY, LAN
-+ switch and MiniPCIe ports
- To compile this driver as a module, choose M here; the module will be
- called turris-omnia-mcu.
-
---- a/drivers/platform/cznic/Makefile
-+++ b/drivers/platform/cznic/Makefile
-@@ -2,3 +2,4 @@
-
- obj-$(CONFIG_TURRIS_OMNIA_MCU) += turris-omnia-mcu.o
- turris-omnia-mcu-y := turris-omnia-mcu-base.o
-+turris-omnia-mcu-y += turris-omnia-mcu-gpio.o
---- a/drivers/platform/cznic/turris-omnia-mcu-base.c
-+++ b/drivers/platform/cznic/turris-omnia-mcu-base.c
-@@ -196,6 +196,7 @@ static const struct attribute_group omni
-
- static const struct attribute_group *omnia_mcu_groups[] = {
- &omnia_mcu_base_group,
-+ &omnia_mcu_gpio_group,
- NULL
- };
-
-@@ -370,7 +371,7 @@ static int omnia_mcu_probe(struct i2c_cl
- "Cannot read board info\n");
- }
-
-- return 0;
-+ return omnia_mcu_register_gpiochip(mcu);
- }
-
- static const struct of_device_id of_omnia_mcu_match[] = {
---- /dev/null
-+++ b/drivers/platform/cznic/turris-omnia-mcu-gpio.c
-@@ -0,0 +1,1094 @@
-+// SPDX-License-Identifier: GPL-2.0
-+/*
-+ * CZ.NIC's Turris Omnia MCU GPIO and IRQ driver
-+ *
-+ * 2024 by Marek Behún <kabel@kernel.org>
-+ */
-+
-+#include <linux/bitfield.h>
-+#include <linux/bitops.h>
-+#include <linux/bug.h>
-+#include <linux/cleanup.h>
-+#include <linux/device.h>
-+#include <linux/devm-helpers.h>
-+#include <linux/errno.h>
-+#include <linux/gpio/driver.h>
-+#include <linux/i2c.h>
-+#include <linux/interrupt.h>
-+#include <linux/mutex.h>
-+#include <linux/sysfs.h>
-+#include <linux/types.h>
-+#include <linux/workqueue.h>
-+#include <asm/unaligned.h>
-+
-+#include <linux/turris-omnia-mcu-interface.h>
-+#include "turris-omnia-mcu.h"
-+
-+#define OMNIA_CMD_INT_ARG_LEN 8
-+#define FRONT_BUTTON_RELEASE_DELAY_MS 50
-+
-+static const char * const omnia_mcu_gpio_templates[64] = {
-+ /* GPIOs with value read from the 16-bit wide status */
-+ [4] = "MiniPCIe0 Card Detect",
-+ [5] = "MiniPCIe0 mSATA Indicator",
-+ [6] = "Front USB3 port over-current",
-+ [7] = "Rear USB3 port over-current",
-+ [8] = "Front USB3 port power",
-+ [9] = "Rear USB3 port power",
-+ [12] = "Front Button",
-+
-+ /* GPIOs with value read from the 32-bit wide extended status */
-+ [16] = "SFP nDET",
-+ [28] = "MiniPCIe0 LED",
-+ [29] = "MiniPCIe1 LED",
-+ [30] = "MiniPCIe2 LED",
-+ [31] = "MiniPCIe0 PAN LED",
-+ [32] = "MiniPCIe1 PAN LED",
-+ [33] = "MiniPCIe2 PAN LED",
-+ [34] = "WAN PHY LED0",
-+ [35] = "WAN PHY LED1",
-+ [36] = "LAN switch p0 LED0",
-+ [37] = "LAN switch p0 LED1",
-+ [38] = "LAN switch p1 LED0",
-+ [39] = "LAN switch p1 LED1",
-+ [40] = "LAN switch p2 LED0",
-+ [41] = "LAN switch p2 LED1",
-+ [42] = "LAN switch p3 LED0",
-+ [43] = "LAN switch p3 LED1",
-+ [44] = "LAN switch p4 LED0",
-+ [45] = "LAN switch p4 LED1",
-+ [46] = "LAN switch p5 LED0",
-+ [47] = "LAN switch p5 LED1",
-+
-+ /* GPIOs with value read from the 16-bit wide extended control status */
-+ [48] = "eMMC nRESET",
-+ [49] = "LAN switch nRESET",
-+ [50] = "WAN PHY nRESET",
-+ [51] = "MiniPCIe0 nPERST",
-+ [52] = "MiniPCIe1 nPERST",
-+ [53] = "MiniPCIe2 nPERST",
-+ [54] = "WAN PHY SFP mux",
-+ [56] = "VHV power disable",
-+};
-+
-+struct omnia_gpio {
-+ u8 cmd;
-+ u8 ctl_cmd;
-+ u8 bit;
-+ u8 ctl_bit;
-+ u8 int_bit;
-+ u16 feat;
-+ u16 feat_mask;
-+};
-+
-+#define OMNIA_GPIO_INVALID_INT_BIT 0xff
-+
-+#define _DEF_GPIO(_cmd, _ctl_cmd, _bit, _ctl_bit, _int_bit, _feat, _feat_mask) \
-+ { \
-+ .cmd = _cmd, \
-+ .ctl_cmd = _ctl_cmd, \
-+ .bit = _bit, \
-+ .ctl_bit = _ctl_bit, \
-+ .int_bit = (_int_bit) < 0 ? OMNIA_GPIO_INVALID_INT_BIT \
-+ : (_int_bit), \
-+ .feat = _feat, \
-+ .feat_mask = _feat_mask, \
-+ }
-+
-+#define _DEF_GPIO_STS(_name) \
-+ _DEF_GPIO(OMNIA_CMD_GET_STATUS_WORD, 0, __bf_shf(OMNIA_STS_ ## _name), \
-+ 0, __bf_shf(OMNIA_INT_ ## _name), 0, 0)
-+
-+#define _DEF_GPIO_CTL(_name) \
-+ _DEF_GPIO(OMNIA_CMD_GET_STATUS_WORD, OMNIA_CMD_GENERAL_CONTROL, \
-+ __bf_shf(OMNIA_STS_ ## _name), __bf_shf(OMNIA_CTL_ ## _name), \
-+ -1, 0, 0)
-+
-+#define _DEF_GPIO_EXT_STS(_name, _feat) \
-+ _DEF_GPIO(OMNIA_CMD_GET_EXT_STATUS_DWORD, 0, \
-+ __bf_shf(OMNIA_EXT_STS_ ## _name), 0, \
-+ __bf_shf(OMNIA_INT_ ## _name), \
-+ OMNIA_FEAT_ ## _feat | OMNIA_FEAT_EXT_CMDS, \
-+ OMNIA_FEAT_ ## _feat | OMNIA_FEAT_EXT_CMDS)
-+
-+#define _DEF_GPIO_EXT_STS_LED(_name, _ledext) \
-+ _DEF_GPIO(OMNIA_CMD_GET_EXT_STATUS_DWORD, 0, \
-+ __bf_shf(OMNIA_EXT_STS_ ## _name), 0, \
-+ __bf_shf(OMNIA_INT_ ## _name), \
-+ OMNIA_FEAT_LED_STATE_ ## _ledext, \
-+ OMNIA_FEAT_LED_STATE_EXT_MASK)
-+
-+#define _DEF_GPIO_EXT_STS_LEDALL(_name) \
-+ _DEF_GPIO(OMNIA_CMD_GET_EXT_STATUS_DWORD, 0, \
-+ __bf_shf(OMNIA_EXT_STS_ ## _name), 0, \
-+ __bf_shf(OMNIA_INT_ ## _name), \
-+ OMNIA_FEAT_LED_STATE_EXT_MASK, 0)
-+
-+#define _DEF_GPIO_EXT_CTL(_name, _feat) \
-+ _DEF_GPIO(OMNIA_CMD_GET_EXT_CONTROL_STATUS, OMNIA_CMD_EXT_CONTROL, \
-+ __bf_shf(OMNIA_EXT_CTL_ ## _name), \
-+ __bf_shf(OMNIA_EXT_CTL_ ## _name), -1, \
-+ OMNIA_FEAT_ ## _feat | OMNIA_FEAT_EXT_CMDS, \
-+ OMNIA_FEAT_ ## _feat | OMNIA_FEAT_EXT_CMDS)
-+
-+#define _DEF_INT(_name) \
-+ _DEF_GPIO(0, 0, 0, 0, __bf_shf(OMNIA_INT_ ## _name), 0, 0)
-+
-+static inline bool is_int_bit_valid(const struct omnia_gpio *gpio)
-+{
-+ return gpio->int_bit != OMNIA_GPIO_INVALID_INT_BIT;
-+}
-+
-+static const struct omnia_gpio omnia_gpios[64] = {
-+ /* GPIOs with value read from the 16-bit wide status */
-+ [4] = _DEF_GPIO_STS(CARD_DET),
-+ [5] = _DEF_GPIO_STS(MSATA_IND),
-+ [6] = _DEF_GPIO_STS(USB30_OVC),
-+ [7] = _DEF_GPIO_STS(USB31_OVC),
-+ [8] = _DEF_GPIO_CTL(USB30_PWRON),
-+ [9] = _DEF_GPIO_CTL(USB31_PWRON),
-+
-+ /* brightness changed interrupt, no GPIO */
-+ [11] = _DEF_INT(BRIGHTNESS_CHANGED),
-+
-+ [12] = _DEF_GPIO_STS(BUTTON_PRESSED),
-+
-+ /* TRNG interrupt, no GPIO */
-+ [13] = _DEF_INT(TRNG),
-+
-+ /* MESSAGE_SIGNED interrupt, no GPIO */
-+ [14] = _DEF_INT(MESSAGE_SIGNED),
-+
-+ /* GPIOs with value read from the 32-bit wide extended status */
-+ [16] = _DEF_GPIO_EXT_STS(SFP_nDET, PERIPH_MCU),
-+ [28] = _DEF_GPIO_EXT_STS_LEDALL(WLAN0_MSATA_LED),
-+ [29] = _DEF_GPIO_EXT_STS_LEDALL(WLAN1_LED),
-+ [30] = _DEF_GPIO_EXT_STS_LEDALL(WLAN2_LED),
-+ [31] = _DEF_GPIO_EXT_STS_LED(WPAN0_LED, EXT),
-+ [32] = _DEF_GPIO_EXT_STS_LED(WPAN1_LED, EXT),
-+ [33] = _DEF_GPIO_EXT_STS_LED(WPAN2_LED, EXT),
-+ [34] = _DEF_GPIO_EXT_STS_LEDALL(WAN_LED0),
-+ [35] = _DEF_GPIO_EXT_STS_LED(WAN_LED1, EXT_V32),
-+ [36] = _DEF_GPIO_EXT_STS_LEDALL(LAN0_LED0),
-+ [37] = _DEF_GPIO_EXT_STS_LEDALL(LAN0_LED1),
-+ [38] = _DEF_GPIO_EXT_STS_LEDALL(LAN1_LED0),
-+ [39] = _DEF_GPIO_EXT_STS_LEDALL(LAN1_LED1),
-+ [40] = _DEF_GPIO_EXT_STS_LEDALL(LAN2_LED0),
-+ [41] = _DEF_GPIO_EXT_STS_LEDALL(LAN2_LED1),
-+ [42] = _DEF_GPIO_EXT_STS_LEDALL(LAN3_LED0),
-+ [43] = _DEF_GPIO_EXT_STS_LEDALL(LAN3_LED1),
-+ [44] = _DEF_GPIO_EXT_STS_LEDALL(LAN4_LED0),
-+ [45] = _DEF_GPIO_EXT_STS_LEDALL(LAN4_LED1),
-+ [46] = _DEF_GPIO_EXT_STS_LEDALL(LAN5_LED0),
-+ [47] = _DEF_GPIO_EXT_STS_LEDALL(LAN5_LED1),
-+
-+ /* GPIOs with value read from the 16-bit wide extended control status */
-+ [48] = _DEF_GPIO_EXT_CTL(nRES_MMC, PERIPH_MCU),
-+ [49] = _DEF_GPIO_EXT_CTL(nRES_LAN, PERIPH_MCU),
-+ [50] = _DEF_GPIO_EXT_CTL(nRES_PHY, PERIPH_MCU),
-+ [51] = _DEF_GPIO_EXT_CTL(nPERST0, PERIPH_MCU),
-+ [52] = _DEF_GPIO_EXT_CTL(nPERST1, PERIPH_MCU),
-+ [53] = _DEF_GPIO_EXT_CTL(nPERST2, PERIPH_MCU),
-+ [54] = _DEF_GPIO_EXT_CTL(PHY_SFP, PERIPH_MCU),
-+ [56] = _DEF_GPIO_EXT_CTL(nVHV_CTRL, PERIPH_MCU),
-+};
-+
-+/* mapping from interrupts to indexes of GPIOs in the omnia_gpios array */
-+static const u8 omnia_int_to_gpio_idx[32] = {
-+ [__bf_shf(OMNIA_INT_CARD_DET)] = 4,
-+ [__bf_shf(OMNIA_INT_MSATA_IND)] = 5,
-+ [__bf_shf(OMNIA_INT_USB30_OVC)] = 6,
-+ [__bf_shf(OMNIA_INT_USB31_OVC)] = 7,
-+ [__bf_shf(OMNIA_INT_BUTTON_PRESSED)] = 12,
-+ [__bf_shf(OMNIA_INT_TRNG)] = 13,
-+ [__bf_shf(OMNIA_INT_MESSAGE_SIGNED)] = 14,
-+ [__bf_shf(OMNIA_INT_SFP_nDET)] = 16,
-+ [__bf_shf(OMNIA_INT_BRIGHTNESS_CHANGED)] = 11,
-+ [__bf_shf(OMNIA_INT_WLAN0_MSATA_LED)] = 28,
-+ [__bf_shf(OMNIA_INT_WLAN1_LED)] = 29,
-+ [__bf_shf(OMNIA_INT_WLAN2_LED)] = 30,
-+ [__bf_shf(OMNIA_INT_WPAN0_LED)] = 31,
-+ [__bf_shf(OMNIA_INT_WPAN1_LED)] = 32,
-+ [__bf_shf(OMNIA_INT_WPAN2_LED)] = 33,
-+ [__bf_shf(OMNIA_INT_WAN_LED0)] = 34,
-+ [__bf_shf(OMNIA_INT_WAN_LED1)] = 35,
-+ [__bf_shf(OMNIA_INT_LAN0_LED0)] = 36,
-+ [__bf_shf(OMNIA_INT_LAN0_LED1)] = 37,
-+ [__bf_shf(OMNIA_INT_LAN1_LED0)] = 38,
-+ [__bf_shf(OMNIA_INT_LAN1_LED1)] = 39,
-+ [__bf_shf(OMNIA_INT_LAN2_LED0)] = 40,
-+ [__bf_shf(OMNIA_INT_LAN2_LED1)] = 41,
-+ [__bf_shf(OMNIA_INT_LAN3_LED0)] = 42,
-+ [__bf_shf(OMNIA_INT_LAN3_LED1)] = 43,
-+ [__bf_shf(OMNIA_INT_LAN4_LED0)] = 44,
-+ [__bf_shf(OMNIA_INT_LAN4_LED1)] = 45,
-+ [__bf_shf(OMNIA_INT_LAN5_LED0)] = 46,
-+ [__bf_shf(OMNIA_INT_LAN5_LED1)] = 47,
-+};
-+
-+/* index of PHY_SFP GPIO in the omnia_gpios array */
-+#define OMNIA_GPIO_PHY_SFP_OFFSET 54
-+
-+static int omnia_ctl_cmd_locked(struct omnia_mcu *mcu, u8 cmd, u16 val, u16 mask)
-+{
-+ unsigned int len;
-+ u8 buf[5];
-+
-+ buf[0] = cmd;
-+
-+ switch (cmd) {
-+ case OMNIA_CMD_GENERAL_CONTROL:
-+ buf[1] = val;
-+ buf[2] = mask;
-+ len = 3;
-+ break;
-+
-+ case OMNIA_CMD_EXT_CONTROL:
-+ put_unaligned_le16(val, &buf[1]);
-+ put_unaligned_le16(mask, &buf[3]);
-+ len = 5;
-+ break;
-+
-+ default:
-+ BUG();
-+ }
-+
-+ return omnia_cmd_write(mcu->client, buf, len);
-+}
-+
-+static int omnia_ctl_cmd(struct omnia_mcu *mcu, u8 cmd, u16 val, u16 mask)
-+{
-+ guard(mutex)(&mcu->lock);
-+
-+ return omnia_ctl_cmd_locked(mcu, cmd, val, mask);
-+}
-+
-+static int omnia_gpio_request(struct gpio_chip *gc, unsigned int offset)
-+{
-+ if (!omnia_gpios[offset].cmd)
-+ return -EINVAL;
-+
-+ return 0;
-+}
-+
-+static int omnia_gpio_get_direction(struct gpio_chip *gc, unsigned int offset)
-+{
-+ struct omnia_mcu *mcu = gpiochip_get_data(gc);
-+
-+ if (offset == OMNIA_GPIO_PHY_SFP_OFFSET) {
-+ int val;
-+
-+ scoped_guard(mutex, &mcu->lock) {
-+ val = omnia_cmd_read_bit(mcu->client,
-+ OMNIA_CMD_GET_EXT_CONTROL_STATUS,
-+ OMNIA_EXT_CTL_PHY_SFP_AUTO);
-+ if (val < 0)
-+ return val;
-+ }
-+
-+ if (val)
-+ return GPIO_LINE_DIRECTION_IN;
-+
-+ return GPIO_LINE_DIRECTION_OUT;
-+ }
-+
-+ if (omnia_gpios[offset].ctl_cmd)
-+ return GPIO_LINE_DIRECTION_OUT;
-+
-+ return GPIO_LINE_DIRECTION_IN;
-+}
-+
-+static int omnia_gpio_direction_input(struct gpio_chip *gc, unsigned int offset)
-+{
-+ const struct omnia_gpio *gpio = &omnia_gpios[offset];
-+ struct omnia_mcu *mcu = gpiochip_get_data(gc);
-+
-+ if (offset == OMNIA_GPIO_PHY_SFP_OFFSET)
-+ return omnia_ctl_cmd(mcu, OMNIA_CMD_EXT_CONTROL,
-+ OMNIA_EXT_CTL_PHY_SFP_AUTO,
-+ OMNIA_EXT_CTL_PHY_SFP_AUTO);
-+
-+ if (gpio->ctl_cmd)
-+ return -ENOTSUPP;
-+
-+ return 0;
-+}
-+
-+static int omnia_gpio_direction_output(struct gpio_chip *gc,
-+ unsigned int offset, int value)
-+{
-+ const struct omnia_gpio *gpio = &omnia_gpios[offset];
-+ struct omnia_mcu *mcu = gpiochip_get_data(gc);
-+ u16 val, mask;
-+
-+ if (!gpio->ctl_cmd)
-+ return -ENOTSUPP;
-+
-+ mask = BIT(gpio->ctl_bit);
-+ val = value ? mask : 0;
-+
-+ if (offset == OMNIA_GPIO_PHY_SFP_OFFSET)
-+ mask |= OMNIA_EXT_CTL_PHY_SFP_AUTO;
-+
-+ return omnia_ctl_cmd(mcu, gpio->ctl_cmd, val, mask);
-+}
-+
-+static int omnia_gpio_get(struct gpio_chip *gc, unsigned int offset)
-+{
-+ const struct omnia_gpio *gpio = &omnia_gpios[offset];
-+ struct omnia_mcu *mcu = gpiochip_get_data(gc);
-+
-+ /*
-+ * If firmware does not support the new interrupt API, we are informed
-+ * of every change of the status word by an interrupt from MCU and save
-+ * its value in the interrupt service routine. Simply return the saved
-+ * value.
-+ */
-+ if (gpio->cmd == OMNIA_CMD_GET_STATUS_WORD &&
-+ !(mcu->features & OMNIA_FEAT_NEW_INT_API))
-+ return test_bit(gpio->bit, &mcu->last_status);
-+
-+ guard(mutex)(&mcu->lock);
-+
-+ /*
-+ * If firmware does support the new interrupt API, we may have cached
-+ * the value of a GPIO in the interrupt service routine. If not, read
-+ * the relevant bit now.
-+ */
-+ if (is_int_bit_valid(gpio) && test_bit(gpio->int_bit, &mcu->is_cached))
-+ return test_bit(gpio->int_bit, &mcu->cached);
-+
-+ return omnia_cmd_read_bit(mcu->client, gpio->cmd, BIT(gpio->bit));
-+}
-+
-+static unsigned long *
-+_relevant_field_for_sts_cmd(u8 cmd, unsigned long *sts, unsigned long *ext_sts,
-+ unsigned long *ext_ctl)
-+{
-+ switch (cmd) {
-+ case OMNIA_CMD_GET_STATUS_WORD:
-+ return sts;
-+ case OMNIA_CMD_GET_EXT_STATUS_DWORD:
-+ return ext_sts;
-+ case OMNIA_CMD_GET_EXT_CONTROL_STATUS:
-+ return ext_ctl;
-+ default:
-+ return NULL;
-+ }
-+}
-+
-+static int omnia_gpio_get_multiple(struct gpio_chip *gc, unsigned long *mask,
-+ unsigned long *bits)
-+{
-+ unsigned long sts = 0, ext_sts = 0, ext_ctl = 0, *field;
-+ struct omnia_mcu *mcu = gpiochip_get_data(gc);
-+ struct i2c_client *client = mcu->client;
-+ unsigned int i;
-+ int err;
-+
-+ /* determine which bits to read from the 3 possible commands */
-+ for_each_set_bit(i, mask, ARRAY_SIZE(omnia_gpios)) {
-+ field = _relevant_field_for_sts_cmd(omnia_gpios[i].cmd,
-+ &sts, &ext_sts, &ext_ctl);
-+ if (!field)
-+ continue;
-+
-+ __set_bit(omnia_gpios[i].bit, field);
-+ }
-+
-+ guard(mutex)(&mcu->lock);
-+
-+ if (mcu->features & OMNIA_FEAT_NEW_INT_API) {
-+ /* read relevant bits from status */
-+ err = omnia_cmd_read_bits(client, OMNIA_CMD_GET_STATUS_WORD,
-+ sts, &sts);
-+ if (err)
-+ return err;
-+ } else {
-+ /*
-+ * Use status word value cached in the interrupt service routine
-+ * if firmware does not support the new interrupt API.
-+ */
-+ sts = mcu->last_status;
-+ }
-+
-+ /* read relevant bits from extended status */
-+ err = omnia_cmd_read_bits(client, OMNIA_CMD_GET_EXT_STATUS_DWORD,
-+ ext_sts, &ext_sts);
-+ if (err)
-+ return err;
-+
-+ /* read relevant bits from extended control */
-+ err = omnia_cmd_read_bits(client, OMNIA_CMD_GET_EXT_CONTROL_STATUS,
-+ ext_ctl, &ext_ctl);
-+ if (err)
-+ return err;
-+
-+ /* assign relevant bits in result */
-+ for_each_set_bit(i, mask, ARRAY_SIZE(omnia_gpios)) {
-+ field = _relevant_field_for_sts_cmd(omnia_gpios[i].cmd,
-+ &sts, &ext_sts, &ext_ctl);
-+ if (!field)
-+ continue;
-+
-+ __assign_bit(i, bits, test_bit(omnia_gpios[i].bit, field));
-+ }
-+
-+ return 0;
-+}
-+
-+static void omnia_gpio_set(struct gpio_chip *gc, unsigned int offset, int value)
-+{
-+ const struct omnia_gpio *gpio = &omnia_gpios[offset];
-+ struct omnia_mcu *mcu = gpiochip_get_data(gc);
-+ u16 val, mask;
-+
-+ if (!gpio->ctl_cmd)
-+ return;
-+
-+ mask = BIT(gpio->ctl_bit);
-+ val = value ? mask : 0;
-+
-+ omnia_ctl_cmd(mcu, gpio->ctl_cmd, val, mask);
-+}
-+
-+static void omnia_gpio_set_multiple(struct gpio_chip *gc, unsigned long *mask,
-+ unsigned long *bits)
-+{
-+ unsigned long ctl = 0, ctl_mask = 0, ext_ctl = 0, ext_ctl_mask = 0;
-+ struct omnia_mcu *mcu = gpiochip_get_data(gc);
-+ unsigned int i;
-+
-+ for_each_set_bit(i, mask, ARRAY_SIZE(omnia_gpios)) {
-+ unsigned long *field, *field_mask;
-+ u8 bit = omnia_gpios[i].ctl_bit;
-+
-+ switch (omnia_gpios[i].ctl_cmd) {
-+ case OMNIA_CMD_GENERAL_CONTROL:
-+ field = &ctl;
-+ field_mask = &ctl_mask;
-+ break;
-+ case OMNIA_CMD_EXT_CONTROL:
-+ field = &ext_ctl;
-+ field_mask = &ext_ctl_mask;
-+ break;
-+ default:
-+ field = field_mask = NULL;
-+ break;
-+ }
-+
-+ if (!field)
-+ continue;
-+
-+ __set_bit(bit, field_mask);
-+ __assign_bit(bit, field, test_bit(i, bits));
-+ }
-+
-+ guard(mutex)(&mcu->lock);
-+
-+ if (ctl_mask)
-+ omnia_ctl_cmd_locked(mcu, OMNIA_CMD_GENERAL_CONTROL,
-+ ctl, ctl_mask);
-+
-+ if (ext_ctl_mask)
-+ omnia_ctl_cmd_locked(mcu, OMNIA_CMD_EXT_CONTROL,
-+ ext_ctl, ext_ctl_mask);
-+}
-+
-+static bool omnia_gpio_available(struct omnia_mcu *mcu,
-+ const struct omnia_gpio *gpio)
-+{
-+ if (gpio->feat_mask)
-+ return (mcu->features & gpio->feat_mask) == gpio->feat;
-+
-+ if (gpio->feat)
-+ return mcu->features & gpio->feat;
-+
-+ return true;
-+}
-+
-+static int omnia_gpio_init_valid_mask(struct gpio_chip *gc,
-+ unsigned long *valid_mask,
-+ unsigned int ngpios)
-+{
-+ struct omnia_mcu *mcu = gpiochip_get_data(gc);
-+
-+ for (unsigned int i = 0; i < ngpios; i++) {
-+ const struct omnia_gpio *gpio = &omnia_gpios[i];
-+
-+ if (gpio->cmd || is_int_bit_valid(gpio))
-+ __assign_bit(i, valid_mask,
-+ omnia_gpio_available(mcu, gpio));
-+ else
-+ __clear_bit(i, valid_mask);
-+ }
-+
-+ return 0;
-+}
-+
-+static int omnia_gpio_of_xlate(struct gpio_chip *gc,
-+ const struct of_phandle_args *gpiospec,
-+ u32 *flags)
-+{
-+ u32 bank, gpio;
-+
-+ if (WARN_ON(gpiospec->args_count != 3))
-+ return -EINVAL;
-+
-+ if (flags)
-+ *flags = gpiospec->args[2];
-+
-+ bank = gpiospec->args[0];
-+ gpio = gpiospec->args[1];
-+
-+ switch (bank) {
-+ case 0:
-+ return gpio < 16 ? gpio : -EINVAL;
-+ case 1:
-+ return gpio < 32 ? 16 + gpio : -EINVAL;
-+ case 2:
-+ return gpio < 16 ? 48 + gpio : -EINVAL;
-+ default:
-+ return -EINVAL;
-+ }
-+}
-+
-+static void omnia_irq_shutdown(struct irq_data *d)
-+{
-+ struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
-+ struct omnia_mcu *mcu = gpiochip_get_data(gc);
-+ irq_hw_number_t hwirq = irqd_to_hwirq(d);
-+ u8 bit = omnia_gpios[hwirq].int_bit;
-+
-+ __clear_bit(bit, &mcu->rising);
-+ __clear_bit(bit, &mcu->falling);
-+}
-+
-+static void omnia_irq_mask(struct irq_data *d)
-+{
-+ struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
-+ struct omnia_mcu *mcu = gpiochip_get_data(gc);
-+ irq_hw_number_t hwirq = irqd_to_hwirq(d);
-+ u8 bit = omnia_gpios[hwirq].int_bit;
-+
-+ if (!omnia_gpios[hwirq].cmd)
-+ __clear_bit(bit, &mcu->rising);
-+ __clear_bit(bit, &mcu->mask);
-+ gpiochip_disable_irq(gc, hwirq);
-+}
-+
-+static void omnia_irq_unmask(struct irq_data *d)
-+{
-+ struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
-+ struct omnia_mcu *mcu = gpiochip_get_data(gc);
-+ irq_hw_number_t hwirq = irqd_to_hwirq(d);
-+ u8 bit = omnia_gpios[hwirq].int_bit;
-+
-+ gpiochip_enable_irq(gc, hwirq);
-+ __set_bit(bit, &mcu->mask);
-+ if (!omnia_gpios[hwirq].cmd)
-+ __set_bit(bit, &mcu->rising);
-+}
-+
-+static int omnia_irq_set_type(struct irq_data *d, unsigned int type)
-+{
-+ struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
-+ struct omnia_mcu *mcu = gpiochip_get_data(gc);
-+ irq_hw_number_t hwirq = irqd_to_hwirq(d);
-+ struct device *dev = &mcu->client->dev;
-+ u8 bit = omnia_gpios[hwirq].int_bit;
-+
-+ if (!(type & IRQ_TYPE_EDGE_BOTH)) {
-+ dev_err(dev, "irq %u: unsupported type %u\n", d->irq, type);
-+ return -EINVAL;
-+ }
-+
-+ __assign_bit(bit, &mcu->rising, type & IRQ_TYPE_EDGE_RISING);
-+ __assign_bit(bit, &mcu->falling, type & IRQ_TYPE_EDGE_FALLING);
-+
-+ return 0;
-+}
-+
-+static void omnia_irq_bus_lock(struct irq_data *d)
-+{
-+ struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
-+ struct omnia_mcu *mcu = gpiochip_get_data(gc);
-+
-+ /* nothing to do if MCU firmware does not support new interrupt API */
-+ if (!(mcu->features & OMNIA_FEAT_NEW_INT_API))
-+ return;
-+
-+ mutex_lock(&mcu->lock);
-+}
-+
-+/**
-+ * omnia_mask_interleave - Interleaves the bytes from @rising and @falling
-+ * @dst: the destination u8 array of interleaved bytes
-+ * @rising: rising mask
-+ * @falling: falling mask
-+ *
-+ * Interleaves the little-endian bytes from @rising and @falling words.
-+ *
-+ * If @rising = (r0, r1, r2, r3) and @falling = (f0, f1, f2, f3), the result is
-+ * @dst = (r0, f0, r1, f1, r2, f2, r3, f3).
-+ *
-+ * The MCU receives an interrupt mask and reports a pending interrupt bitmap in
-+ * this interleaved format. The rationale behind this is that the low-indexed
-+ * bits are more important - in many cases, the user will be interested only in
-+ * interrupts with indexes 0 to 7, and so the system can stop reading after
-+ * first 2 bytes (r0, f0), to save time on the slow I2C bus.
-+ *
-+ * Feel free to remove this function and its inverse, omnia_mask_deinterleave,
-+ * and use an appropriate bitmap_*() function once such a function exists.
-+ */
-+static void
-+omnia_mask_interleave(u8 *dst, unsigned long rising, unsigned long falling)
-+{
-+ for (unsigned int i = 0; i < sizeof(u32); i++) {
-+ dst[2 * i] = rising >> (8 * i);
-+ dst[2 * i + 1] = falling >> (8 * i);
-+ }
-+}
-+
-+/**
-+ * omnia_mask_deinterleave - Deinterleaves the bytes into @rising and @falling
-+ * @src: the source u8 array containing the interleaved bytes
-+ * @rising: pointer where to store the rising mask gathered from @src
-+ * @falling: pointer where to store the falling mask gathered from @src
-+ *
-+ * This is the inverse function to omnia_mask_interleave.
-+ */
-+static void omnia_mask_deinterleave(const u8 *src, unsigned long *rising,
-+ unsigned long *falling)
-+{
-+ *rising = *falling = 0;
-+
-+ for (unsigned int i = 0; i < sizeof(u32); i++) {
-+ *rising |= src[2 * i] << (8 * i);
-+ *falling |= src[2 * i + 1] << (8 * i);
-+ }
-+}
-+
-+static void omnia_irq_bus_sync_unlock(struct irq_data *d)
-+{
-+ struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
-+ struct omnia_mcu *mcu = gpiochip_get_data(gc);
-+ struct device *dev = &mcu->client->dev;
-+ u8 cmd[1 + OMNIA_CMD_INT_ARG_LEN];
-+ unsigned long rising, falling;
-+ int err;
-+
-+ /* nothing to do if MCU firmware does not support new interrupt API */
-+ if (!(mcu->features & OMNIA_FEAT_NEW_INT_API))
-+ return;
-+
-+ cmd[0] = OMNIA_CMD_SET_INT_MASK;
-+
-+ rising = mcu->rising & mcu->mask;
-+ falling = mcu->falling & mcu->mask;
-+
-+ /* interleave the rising and falling bytes into the command arguments */
-+ omnia_mask_interleave(&cmd[1], rising, falling);
-+
-+ dev_dbg(dev, "set int mask %8ph\n", &cmd[1]);
-+
-+ err = omnia_cmd_write(mcu->client, cmd, sizeof(cmd));
-+ if (err) {
-+ dev_err(dev, "Cannot set mask: %d\n", err);
-+ goto unlock;
-+ }
-+
-+ /*
-+ * Remember which GPIOs have both rising and falling interrupts enabled.
-+ * For those we will cache their value so that .get() method is faster.
-+ * We also need to forget cached values of GPIOs that aren't cached
-+ * anymore.
-+ */
-+ mcu->both = rising & falling;
-+ mcu->is_cached &= mcu->both;
-+
-+unlock:
-+ mutex_unlock(&mcu->lock);
-+}
-+
-+static const struct irq_chip omnia_mcu_irq_chip = {
-+ .name = "Turris Omnia MCU interrupts",
-+ .irq_shutdown = omnia_irq_shutdown,
-+ .irq_mask = omnia_irq_mask,
-+ .irq_unmask = omnia_irq_unmask,
-+ .irq_set_type = omnia_irq_set_type,
-+ .irq_bus_lock = omnia_irq_bus_lock,
-+ .irq_bus_sync_unlock = omnia_irq_bus_sync_unlock,
-+ .flags = IRQCHIP_IMMUTABLE,
-+ GPIOCHIP_IRQ_RESOURCE_HELPERS,
-+};
-+
-+static void omnia_irq_init_valid_mask(struct gpio_chip *gc,
-+ unsigned long *valid_mask,
-+ unsigned int ngpios)
-+{
-+ struct omnia_mcu *mcu = gpiochip_get_data(gc);
-+
-+ for (unsigned int i = 0; i < ngpios; i++) {
-+ const struct omnia_gpio *gpio = &omnia_gpios[i];
-+
-+ if (is_int_bit_valid(gpio))
-+ __assign_bit(i, valid_mask,
-+ omnia_gpio_available(mcu, gpio));
-+ else
-+ __clear_bit(i, valid_mask);
-+ }
-+}
-+
-+static int omnia_irq_init_hw(struct gpio_chip *gc)
-+{
-+ struct omnia_mcu *mcu = gpiochip_get_data(gc);
-+ u8 cmd[1 + OMNIA_CMD_INT_ARG_LEN] = {};
-+
-+ cmd[0] = OMNIA_CMD_SET_INT_MASK;
-+
-+ return omnia_cmd_write(mcu->client, cmd, sizeof(cmd));
-+}
-+
-+/*
-+ * Determine how many bytes we need to read from the reply to the
-+ * OMNIA_CMD_GET_INT_AND_CLEAR command in order to retrieve all unmasked
-+ * interrupts.
-+ */
-+static unsigned int
-+omnia_irq_compute_pending_length(unsigned long rising, unsigned long falling)
-+{
-+ return max(omnia_compute_reply_length(rising, true, 0),
-+ omnia_compute_reply_length(falling, true, 1));
-+}
-+
-+static bool omnia_irq_read_pending_new(struct omnia_mcu *mcu,
-+ unsigned long *pending)
-+{
-+ struct device *dev = &mcu->client->dev;
-+ u8 reply[OMNIA_CMD_INT_ARG_LEN] = {};
-+ unsigned long rising, falling;
-+ unsigned int len;
-+ int err;
-+
-+ len = omnia_irq_compute_pending_length(mcu->rising & mcu->mask,
-+ mcu->falling & mcu->mask);
-+ if (!len)
-+ return false;
-+
-+ guard(mutex)(&mcu->lock);
-+
-+ err = omnia_cmd_read(mcu->client, OMNIA_CMD_GET_INT_AND_CLEAR, reply,
-+ len);
-+ if (err) {
-+ dev_err(dev, "Cannot read pending IRQs: %d\n", err);
-+ return false;
-+ }
-+
-+ /* deinterleave the reply bytes into rising and falling */
-+ omnia_mask_deinterleave(reply, &rising, &falling);
-+
-+ rising &= mcu->mask;
-+ falling &= mcu->mask;
-+ *pending = rising | falling;
-+
-+ /* cache values for GPIOs that have both edges enabled */
-+ mcu->is_cached &= ~(rising & falling);
-+ mcu->is_cached |= mcu->both & (rising ^ falling);
-+ mcu->cached = (mcu->cached | rising) & ~falling;
-+
-+ return true;
-+}
-+
-+static int omnia_read_status_word_old_fw(struct omnia_mcu *mcu,
-+ unsigned long *status)
-+{
-+ u16 raw_status;
-+ int err;
-+
-+ err = omnia_cmd_read_u16(mcu->client, OMNIA_CMD_GET_STATUS_WORD,
-+ &raw_status);
-+ if (err)
-+ return err;
-+
-+ /*
-+ * Old firmware has a bug wherein it never resets the USB port
-+ * overcurrent bits back to zero. Ignore them.
-+ */
-+ *status = raw_status & ~(OMNIA_STS_USB30_OVC | OMNIA_STS_USB31_OVC);
-+
-+ return 0;
-+}
-+
-+static void button_release_emul_fn(struct work_struct *work)
-+{
-+ struct omnia_mcu *mcu = container_of(to_delayed_work(work),
-+ struct omnia_mcu,
-+ button_release_emul_work);
-+
-+ mcu->button_pressed_emul = false;
-+ generic_handle_irq_safe(mcu->client->irq);
-+}
-+
-+static void
-+fill_int_from_sts(unsigned long *rising, unsigned long *falling,
-+ unsigned long rising_sts, unsigned long falling_sts,
-+ unsigned long sts_bit, unsigned long int_bit)
-+{
-+ if (rising_sts & sts_bit)
-+ *rising |= int_bit;
-+ if (falling_sts & sts_bit)
-+ *falling |= int_bit;
-+}
-+
-+static bool omnia_irq_read_pending_old(struct omnia_mcu *mcu,
-+ unsigned long *pending)
-+{
-+ unsigned long status, rising_sts, falling_sts, rising, falling;
-+ struct device *dev = &mcu->client->dev;
-+ int err;
-+
-+ guard(mutex)(&mcu->lock);
-+
-+ err = omnia_read_status_word_old_fw(mcu, &status);
-+ if (err) {
-+ dev_err(dev, "Cannot read pending IRQs: %d\n", err);
-+ return false;
-+ }
-+
-+ /*
-+ * The old firmware triggers an interrupt whenever status word changes,
-+ * but does not inform about which bits rose or fell. We need to compute
-+ * this here by comparing with the last status word value.
-+ *
-+ * The OMNIA_STS_BUTTON_PRESSED bit needs special handling, because the
-+ * old firmware clears the OMNIA_STS_BUTTON_PRESSED bit on successful
-+ * completion of the OMNIA_CMD_GET_STATUS_WORD command, resulting in
-+ * another interrupt:
-+ * - first we get an interrupt, we read the status word where
-+ * OMNIA_STS_BUTTON_PRESSED is present,
-+ * - MCU clears the OMNIA_STS_BUTTON_PRESSED bit because we read the
-+ * status word,
-+ * - we get another interrupt because the status word changed again
-+ * (the OMNIA_STS_BUTTON_PRESSED bit was cleared).
-+ *
-+ * The gpiolib-cdev, gpiolib-sysfs and gpio-keys input driver all call
-+ * the gpiochip's .get() method after an edge event on a requested GPIO
-+ * occurs.
-+ *
-+ * We ensure that the .get() method reads 1 for the button GPIO for some
-+ * time.
-+ */
-+
-+ if (status & OMNIA_STS_BUTTON_PRESSED) {
-+ mcu->button_pressed_emul = true;
-+ mod_delayed_work(system_wq, &mcu->button_release_emul_work,
-+ msecs_to_jiffies(FRONT_BUTTON_RELEASE_DELAY_MS));
-+ } else if (mcu->button_pressed_emul) {
-+ status |= OMNIA_STS_BUTTON_PRESSED;
-+ }
-+
-+ rising_sts = ~mcu->last_status & status;
-+ falling_sts = mcu->last_status & ~status;
-+
-+ mcu->last_status = status;
-+
-+ /*
-+ * Fill in the relevant interrupt bits from status bits for CARD_DET,
-+ * MSATA_IND and BUTTON_PRESSED.
-+ */
-+ rising = 0;
-+ falling = 0;
-+ fill_int_from_sts(&rising, &falling, rising_sts, falling_sts,
-+ OMNIA_STS_CARD_DET, OMNIA_INT_CARD_DET);
-+ fill_int_from_sts(&rising, &falling, rising_sts, falling_sts,
-+ OMNIA_STS_MSATA_IND, OMNIA_INT_MSATA_IND);
-+ fill_int_from_sts(&rising, &falling, rising_sts, falling_sts,
-+ OMNIA_STS_BUTTON_PRESSED, OMNIA_INT_BUTTON_PRESSED);
-+
-+ /* Use only bits that are enabled */
-+ rising &= mcu->rising & mcu->mask;
-+ falling &= mcu->falling & mcu->mask;
-+ *pending = rising | falling;
-+
-+ return true;
-+}
-+
-+static bool omnia_irq_read_pending(struct omnia_mcu *mcu,
-+ unsigned long *pending)
-+{
-+ if (mcu->features & OMNIA_FEAT_NEW_INT_API)
-+ return omnia_irq_read_pending_new(mcu, pending);
-+ else
-+ return omnia_irq_read_pending_old(mcu, pending);
-+}
-+
-+static irqreturn_t omnia_irq_thread_handler(int irq, void *dev_id)
-+{
-+ struct omnia_mcu *mcu = dev_id;
-+ struct irq_domain *domain;
-+ unsigned long pending;
-+ unsigned int i;
-+
-+ if (!omnia_irq_read_pending(mcu, &pending))
-+ return IRQ_NONE;
-+
-+ domain = mcu->gc.irq.domain;
-+
-+ for_each_set_bit(i, &pending, 32) {
-+ unsigned int nested_irq;
-+
-+ nested_irq = irq_find_mapping(domain, omnia_int_to_gpio_idx[i]);
-+
-+ handle_nested_irq(nested_irq);
-+ }
-+
-+ return IRQ_RETVAL(pending);
-+}
-+
-+static const char * const front_button_modes[] = { "mcu", "cpu" };
-+
-+static ssize_t front_button_mode_show(struct device *dev,
-+ struct device_attribute *a, char *buf)
-+{
-+ struct omnia_mcu *mcu = dev_get_drvdata(dev);
-+ int val;
-+
-+ if (mcu->features & OMNIA_FEAT_NEW_INT_API) {
-+ val = omnia_cmd_read_bit(mcu->client, OMNIA_CMD_GET_STATUS_WORD,
-+ OMNIA_STS_BUTTON_MODE);
-+ if (val < 0)
-+ return val;
-+ } else {
-+ val = !!(mcu->last_status & OMNIA_STS_BUTTON_MODE);
-+ }
-+
-+ return sysfs_emit(buf, "%s\n", front_button_modes[val]);
-+}
-+
-+static ssize_t front_button_mode_store(struct device *dev,
-+ struct device_attribute *a,
-+ const char *buf, size_t count)
-+{
-+ struct omnia_mcu *mcu = dev_get_drvdata(dev);
-+ int err, i;
-+
-+ i = sysfs_match_string(front_button_modes, buf);
-+ if (i < 0)
-+ return i;
-+
-+ err = omnia_ctl_cmd_locked(mcu, OMNIA_CMD_GENERAL_CONTROL,
-+ i ? OMNIA_CTL_BUTTON_MODE : 0,
-+ OMNIA_CTL_BUTTON_MODE);
-+ if (err)
-+ return err;
-+
-+ return count;
-+}
-+static DEVICE_ATTR_RW(front_button_mode);
-+
-+static struct attribute *omnia_mcu_gpio_attrs[] = {
-+ &dev_attr_front_button_mode.attr,
-+ NULL
-+};
-+
-+const struct attribute_group omnia_mcu_gpio_group = {
-+ .attrs = omnia_mcu_gpio_attrs,
-+};
-+
-+int omnia_mcu_register_gpiochip(struct omnia_mcu *mcu)
-+{
-+ bool new_api = mcu->features & OMNIA_FEAT_NEW_INT_API;
-+ struct device *dev = &mcu->client->dev;
-+ unsigned long irqflags;
-+ int err;
-+
-+ err = devm_mutex_init(dev, &mcu->lock);
-+ if (err)
-+ return err;
-+
-+ mcu->gc.request = omnia_gpio_request;
-+ mcu->gc.get_direction = omnia_gpio_get_direction;
-+ mcu->gc.direction_input = omnia_gpio_direction_input;
-+ mcu->gc.direction_output = omnia_gpio_direction_output;
-+ mcu->gc.get = omnia_gpio_get;
-+ mcu->gc.get_multiple = omnia_gpio_get_multiple;
-+ mcu->gc.set = omnia_gpio_set;
-+ mcu->gc.set_multiple = omnia_gpio_set_multiple;
-+ mcu->gc.init_valid_mask = omnia_gpio_init_valid_mask;
-+ mcu->gc.can_sleep = true;
-+ mcu->gc.names = omnia_mcu_gpio_templates;
-+ mcu->gc.base = -1;
-+ mcu->gc.ngpio = ARRAY_SIZE(omnia_gpios);
-+ mcu->gc.label = "Turris Omnia MCU GPIOs";
-+ mcu->gc.parent = dev;
-+ mcu->gc.owner = THIS_MODULE;
-+ mcu->gc.of_gpio_n_cells = 3;
-+ mcu->gc.of_xlate = omnia_gpio_of_xlate;
-+
-+ gpio_irq_chip_set_chip(&mcu->gc.irq, &omnia_mcu_irq_chip);
-+ /* This will let us handle the parent IRQ in the driver */
-+ mcu->gc.irq.parent_handler = NULL;
-+ mcu->gc.irq.num_parents = 0;
-+ mcu->gc.irq.parents = NULL;
-+ mcu->gc.irq.default_type = IRQ_TYPE_NONE;
-+ mcu->gc.irq.handler = handle_bad_irq;
-+ mcu->gc.irq.threaded = true;
-+ if (new_api)
-+ mcu->gc.irq.init_hw = omnia_irq_init_hw;
-+ mcu->gc.irq.init_valid_mask = omnia_irq_init_valid_mask;
-+
-+ err = devm_gpiochip_add_data(dev, &mcu->gc, mcu);
-+ if (err)
-+ return dev_err_probe(dev, err, "Cannot add GPIO chip\n");
-+
-+ /*
-+ * Before requesting the interrupt, if firmware does not support the new
-+ * interrupt API, we need to cache the value of the status word, so that
-+ * when it changes, we may compare the new value with the cached one in
-+ * the interrupt handler.
-+ */
-+ if (!new_api) {
-+ err = omnia_read_status_word_old_fw(mcu, &mcu->last_status);
-+ if (err)
-+ return dev_err_probe(dev, err,
-+ "Cannot read status word\n");
-+
-+ INIT_DELAYED_WORK(&mcu->button_release_emul_work,
-+ button_release_emul_fn);
-+ }
-+
-+ irqflags = IRQF_ONESHOT;
-+ if (new_api)
-+ irqflags |= IRQF_TRIGGER_LOW;
-+ else
-+ irqflags |= IRQF_TRIGGER_FALLING;
-+
-+ err = devm_request_threaded_irq(dev, mcu->client->irq, NULL,
-+ omnia_irq_thread_handler, irqflags,
-+ "turris-omnia-mcu", mcu);
-+ if (err)
-+ return dev_err_probe(dev, err, "Cannot request IRQ\n");
-+
-+ if (!new_api) {
-+ /*
-+ * The button_release_emul_work has to be initialized before the
-+ * thread is requested, and on driver remove it needs to be
-+ * canceled before the thread is freed. Therefore we can't use
-+ * devm_delayed_work_autocancel() directly, because the order
-+ * devm_delayed_work_autocancel();
-+ * devm_request_threaded_irq();
-+ * would cause improper release order:
-+ * free_irq();
-+ * cancel_delayed_work_sync();
-+ * Instead we first initialize the work above, and only now
-+ * after IRQ is requested we add the work devm action.
-+ */
-+ err = devm_add_action(dev, devm_delayed_work_drop,
-+ &mcu->button_release_emul_work);
-+ if (err)
-+ return err;
-+ }
-+
-+ return 0;
-+}
---- a/drivers/platform/cznic/turris-omnia-mcu.h
-+++ b/drivers/platform/cznic/turris-omnia-mcu.h
-@@ -8,8 +8,12 @@
- #ifndef __TURRIS_OMNIA_MCU_H
- #define __TURRIS_OMNIA_MCU_H
-
-+#include <linux/bitops.h>
-+#include <linux/gpio/driver.h>
- #include <linux/if_ether.h>
-+#include <linux/mutex.h>
- #include <linux/types.h>
-+#include <linux/workqueue.h>
- #include <asm/byteorder.h>
-
- struct i2c_client;
-@@ -23,18 +27,78 @@ struct omnia_mcu {
- u64 board_serial_number;
- u8 board_first_mac[ETH_ALEN];
- u8 board_revision;
-+
-+ /* GPIO chip */
-+ struct gpio_chip gc;
-+ struct mutex lock;
-+ unsigned long mask, rising, falling, both, cached, is_cached;
-+ /* Old MCU firmware handling needs the following */
-+ struct delayed_work button_release_emul_work;
-+ unsigned long last_status;
-+ bool button_pressed_emul;
- };
-
- int omnia_cmd_write_read(const struct i2c_client *client,
- void *cmd, unsigned int cmd_len,
- void *reply, unsigned int reply_len);
-
-+static inline int omnia_cmd_write(const struct i2c_client *client, void *cmd,
-+ unsigned int len)
-+{
-+ return omnia_cmd_write_read(client, cmd, len, NULL, 0);
-+}
-+
- static inline int omnia_cmd_read(const struct i2c_client *client, u8 cmd,
- void *reply, unsigned int len)
- {
- return omnia_cmd_write_read(client, &cmd, 1, reply, len);
- }
-
-+static inline unsigned int
-+omnia_compute_reply_length(unsigned long mask, bool interleaved,
-+ unsigned int offset)
-+{
-+ if (!mask)
-+ return 0;
-+
-+ return ((__fls(mask) >> 3) << interleaved) + 1 + offset;
-+}
-+
-+/* Returns 0 on success */
-+static inline int omnia_cmd_read_bits(const struct i2c_client *client, u8 cmd,
-+ unsigned long bits, unsigned long *dst)
-+{
-+ __le32 reply;
-+ int err;
-+
-+ if (!bits) {
-+ *dst = 0;
-+ return 0;
-+ }
-+
-+ err = omnia_cmd_read(client, cmd, &reply,
-+ omnia_compute_reply_length(bits, false, 0));
-+ if (err)
-+ return err;
-+
-+ *dst = le32_to_cpu(reply) & bits;
-+
-+ return 0;
-+}
-+
-+static inline int omnia_cmd_read_bit(const struct i2c_client *client, u8 cmd,
-+ unsigned long bit)
-+{
-+ unsigned long reply;
-+ int err;
-+
-+ err = omnia_cmd_read_bits(client, cmd, bit, &reply);
-+ if (err)
-+ return err;
-+
-+ return !!reply;
-+}
-+
- static inline int omnia_cmd_read_u32(const struct i2c_client *client, u8 cmd,
- u32 *dst)
- {
-@@ -71,4 +135,8 @@ static inline int omnia_cmd_read_u8(cons
- return omnia_cmd_read(client, cmd, reply, sizeof(*reply));
- }
-
-+extern const struct attribute_group omnia_mcu_gpio_group;
-+
-+int omnia_mcu_register_gpiochip(struct omnia_mcu *mcu);
-+
- #endif /* __TURRIS_OMNIA_MCU_H */
+++ /dev/null
-From f69e0a731ab471f3a57c48258ad2d9990820c173 Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Marek=20Beh=C3=BAn?= <kabel@kernel.org>
-Date: Mon, 1 Jul 2024 13:30:06 +0200
-Subject: [PATCH 04/11] platform: cznic: turris-omnia-mcu: Add support for
- poweroff and wakeup
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Add support for true board poweroff (MCU can disable all unnecessary
-voltage regulators) and wakeup at a specified time, implemented via a
-RTC driver so that the rtcwake utility can be used to configure it.
-
-Signed-off-by: Marek Behún <kabel@kernel.org>
-Reviewed-by: Andy Shevchenko <andy@kernel.org>
-Acked-by: Alexandre Belloni <alexandre.belloni@bootlin.com>
-Acked-by: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
-Link: https://lore.kernel.org/r/20240701113010.16447-5-kabel@kernel.org
-Signed-off-by: Arnd Bergmann <arnd@arndb.de>
----
- .../sysfs-bus-i2c-devices-turris-omnia-mcu | 16 ++
- drivers/platform/cznic/Kconfig | 4 +
- drivers/platform/cznic/Makefile | 1 +
- .../platform/cznic/turris-omnia-mcu-base.c | 5 +
- .../cznic/turris-omnia-mcu-sys-off-wakeup.c | 260 ++++++++++++++++++
- drivers/platform/cznic/turris-omnia-mcu.h | 20 ++
- 6 files changed, 306 insertions(+)
- create mode 100644 drivers/platform/cznic/turris-omnia-mcu-sys-off-wakeup.c
-
---- a/Documentation/ABI/testing/sysfs-bus-i2c-devices-turris-omnia-mcu
-+++ b/Documentation/ABI/testing/sysfs-bus-i2c-devices-turris-omnia-mcu
-@@ -38,6 +38,22 @@ Description: (RW) The front button on th
-
- Format: %s.
-
-+What: /sys/bus/i2c/devices/<mcu_device>/front_button_poweron
-+Date: September 2024
-+KernelVersion: 6.11
-+Contact: Marek Behún <kabel@kernel.org>
-+Description: (RW) Newer versions of the microcontroller firmware of the
-+ Turris Omnia router support powering off the router into true
-+ low power mode. The router can be powered on by pressing the
-+ front button.
-+
-+ This file configures whether front button power on is enabled.
-+
-+ This file is present only if the power off feature is supported
-+ by the firmware.
-+
-+ Format: %i.
-+
- What: /sys/bus/i2c/devices/<mcu_device>/fw_features
- Date: September 2024
- KernelVersion: 6.11
---- a/drivers/platform/cznic/Kconfig
-+++ b/drivers/platform/cznic/Kconfig
-@@ -18,10 +18,14 @@ config TURRIS_OMNIA_MCU
- depends on I2C
- select GPIOLIB
- select GPIOLIB_IRQCHIP
-+ select RTC_CLASS
- help
- Say Y here to add support for the features implemented by the
- microcontroller on the CZ.NIC's Turris Omnia SOHO router.
- The features include:
-+ - board poweroff into true low power mode (with voltage regulators
-+ disabled) and the ability to configure wake up from this mode (via
-+ rtcwake)
- - GPIO pins
- - to get front button press events (the front button can be
- configured either to generate press events to the CPU or to change
---- a/drivers/platform/cznic/Makefile
-+++ b/drivers/platform/cznic/Makefile
-@@ -3,3 +3,4 @@
- obj-$(CONFIG_TURRIS_OMNIA_MCU) += turris-omnia-mcu.o
- turris-omnia-mcu-y := turris-omnia-mcu-base.o
- turris-omnia-mcu-y += turris-omnia-mcu-gpio.o
-+turris-omnia-mcu-y += turris-omnia-mcu-sys-off-wakeup.o
---- a/drivers/platform/cznic/turris-omnia-mcu-base.c
-+++ b/drivers/platform/cznic/turris-omnia-mcu-base.c
-@@ -197,6 +197,7 @@ static const struct attribute_group omni
- static const struct attribute_group *omnia_mcu_groups[] = {
- &omnia_mcu_base_group,
- &omnia_mcu_gpio_group,
-+ &omnia_mcu_poweroff_group,
- NULL
- };
-
-@@ -371,6 +372,10 @@ static int omnia_mcu_probe(struct i2c_cl
- "Cannot read board info\n");
- }
-
-+ err = omnia_mcu_register_sys_off_and_wakeup(mcu);
-+ if (err)
-+ return err;
-+
- return omnia_mcu_register_gpiochip(mcu);
- }
-
---- /dev/null
-+++ b/drivers/platform/cznic/turris-omnia-mcu-sys-off-wakeup.c
-@@ -0,0 +1,260 @@
-+// SPDX-License-Identifier: GPL-2.0
-+/*
-+ * CZ.NIC's Turris Omnia MCU system off and RTC wakeup driver
-+ *
-+ * This is not a true RTC driver (in the sense that it does not provide a
-+ * real-time clock), rather the MCU implements a wakeup from powered off state
-+ * at a specified time relative to MCU boot, and we expose this feature via RTC
-+ * alarm, so that it can be used via the rtcwake command, which is the standard
-+ * Linux command for this.
-+ *
-+ * 2024 by Marek Behún <kabel@kernel.org>
-+ */
-+
-+#include <linux/crc32.h>
-+#include <linux/delay.h>
-+#include <linux/device.h>
-+#include <linux/err.h>
-+#include <linux/i2c.h>
-+#include <linux/kstrtox.h>
-+#include <linux/reboot.h>
-+#include <linux/rtc.h>
-+#include <linux/sysfs.h>
-+#include <linux/types.h>
-+
-+#include <linux/turris-omnia-mcu-interface.h>
-+#include "turris-omnia-mcu.h"
-+
-+static int omnia_get_uptime_wakeup(const struct i2c_client *client, u32 *uptime,
-+ u32 *wakeup)
-+{
-+ __le32 reply[2];
-+ int err;
-+
-+ err = omnia_cmd_read(client, OMNIA_CMD_GET_UPTIME_AND_WAKEUP, reply,
-+ sizeof(reply));
-+ if (err)
-+ return err;
-+
-+ if (uptime)
-+ *uptime = le32_to_cpu(reply[0]);
-+
-+ if (wakeup)
-+ *wakeup = le32_to_cpu(reply[1]);
-+
-+ return 0;
-+}
-+
-+static int omnia_read_time(struct device *dev, struct rtc_time *tm)
-+{
-+ u32 uptime;
-+ int err;
-+
-+ err = omnia_get_uptime_wakeup(to_i2c_client(dev), &uptime, NULL);
-+ if (err)
-+ return err;
-+
-+ rtc_time64_to_tm(uptime, tm);
-+
-+ return 0;
-+}
-+
-+static int omnia_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
-+{
-+ struct i2c_client *client = to_i2c_client(dev);
-+ struct omnia_mcu *mcu = i2c_get_clientdata(client);
-+ u32 wakeup;
-+ int err;
-+
-+ err = omnia_get_uptime_wakeup(client, NULL, &wakeup);
-+ if (err)
-+ return err;
-+
-+ alrm->enabled = !!wakeup;
-+ rtc_time64_to_tm(wakeup ?: mcu->rtc_alarm, &alrm->time);
-+
-+ return 0;
-+}
-+
-+static int omnia_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
-+{
-+ struct i2c_client *client = to_i2c_client(dev);
-+ struct omnia_mcu *mcu = i2c_get_clientdata(client);
-+
-+ mcu->rtc_alarm = rtc_tm_to_time64(&alrm->time);
-+
-+ if (alrm->enabled)
-+ return omnia_cmd_write_u32(client, OMNIA_CMD_SET_WAKEUP,
-+ mcu->rtc_alarm);
-+
-+ return 0;
-+}
-+
-+static int omnia_alarm_irq_enable(struct device *dev, unsigned int enabled)
-+{
-+ struct i2c_client *client = to_i2c_client(dev);
-+ struct omnia_mcu *mcu = i2c_get_clientdata(client);
-+
-+ return omnia_cmd_write_u32(client, OMNIA_CMD_SET_WAKEUP,
-+ enabled ? mcu->rtc_alarm : 0);
-+}
-+
-+static const struct rtc_class_ops omnia_rtc_ops = {
-+ .read_time = omnia_read_time,
-+ .read_alarm = omnia_read_alarm,
-+ .set_alarm = omnia_set_alarm,
-+ .alarm_irq_enable = omnia_alarm_irq_enable,
-+};
-+
-+static int omnia_power_off(struct sys_off_data *data)
-+{
-+ struct omnia_mcu *mcu = data->cb_data;
-+ __be32 tmp;
-+ u8 cmd[9];
-+ u16 arg;
-+ int err;
-+
-+ if (mcu->front_button_poweron)
-+ arg = OMNIA_CMD_POWER_OFF_POWERON_BUTTON;
-+ else
-+ arg = 0;
-+
-+ cmd[0] = OMNIA_CMD_POWER_OFF;
-+ put_unaligned_le16(OMNIA_CMD_POWER_OFF_MAGIC, &cmd[1]);
-+ put_unaligned_le16(arg, &cmd[3]);
-+
-+ /*
-+ * Although all values from and to MCU are passed in little-endian, the
-+ * MCU's CRC unit uses big-endian CRC32 polynomial (0x04c11db7), so we
-+ * need to use crc32_be() here.
-+ */
-+ tmp = cpu_to_be32(get_unaligned_le32(&cmd[1]));
-+ put_unaligned_le32(crc32_be(~0, (void *)&tmp, sizeof(tmp)), &cmd[5]);
-+
-+ err = omnia_cmd_write(mcu->client, cmd, sizeof(cmd));
-+ if (err)
-+ dev_err(&mcu->client->dev,
-+ "Unable to send the poweroff command: %d\n", err);
-+
-+ return NOTIFY_DONE;
-+}
-+
-+static int omnia_restart(struct sys_off_data *data)
-+{
-+ struct omnia_mcu *mcu = data->cb_data;
-+ u8 cmd[3];
-+ int err;
-+
-+ cmd[0] = OMNIA_CMD_GENERAL_CONTROL;
-+
-+ if (reboot_mode == REBOOT_HARD)
-+ cmd[1] = cmd[2] = OMNIA_CTL_HARD_RST;
-+ else
-+ cmd[1] = cmd[2] = OMNIA_CTL_LIGHT_RST;
-+
-+ err = omnia_cmd_write(mcu->client, cmd, sizeof(cmd));
-+ if (err)
-+ dev_err(&mcu->client->dev,
-+ "Unable to send the restart command: %d\n", err);
-+
-+ /*
-+ * MCU needs a little bit to process the I2C command, otherwise it will
-+ * do a light reset based on SOC SYSRES_OUT pin.
-+ */
-+ mdelay(1);
-+
-+ return NOTIFY_DONE;
-+}
-+
-+static ssize_t front_button_poweron_show(struct device *dev,
-+ struct device_attribute *a, char *buf)
-+{
-+ struct omnia_mcu *mcu = dev_get_drvdata(dev);
-+
-+ return sysfs_emit(buf, "%d\n", mcu->front_button_poweron);
-+}
-+
-+static ssize_t front_button_poweron_store(struct device *dev,
-+ struct device_attribute *a,
-+ const char *buf, size_t count)
-+{
-+ struct omnia_mcu *mcu = dev_get_drvdata(dev);
-+ bool val;
-+ int err;
-+
-+ err = kstrtobool(buf, &val);
-+ if (err)
-+ return err;
-+
-+ mcu->front_button_poweron = val;
-+
-+ return count;
-+}
-+static DEVICE_ATTR_RW(front_button_poweron);
-+
-+static struct attribute *omnia_mcu_poweroff_attrs[] = {
-+ &dev_attr_front_button_poweron.attr,
-+ NULL
-+};
-+
-+static umode_t poweroff_attrs_visible(struct kobject *kobj, struct attribute *a,
-+ int n)
-+{
-+ struct device *dev = kobj_to_dev(kobj);
-+ struct omnia_mcu *mcu = dev_get_drvdata(dev);
-+
-+ if (mcu->features & OMNIA_FEAT_POWEROFF_WAKEUP)
-+ return a->mode;
-+
-+ return 0;
-+}
-+
-+const struct attribute_group omnia_mcu_poweroff_group = {
-+ .attrs = omnia_mcu_poweroff_attrs,
-+ .is_visible = poweroff_attrs_visible,
-+};
-+
-+int omnia_mcu_register_sys_off_and_wakeup(struct omnia_mcu *mcu)
-+{
-+ struct device *dev = &mcu->client->dev;
-+ int err;
-+
-+ /* MCU restart is always available */
-+ err = devm_register_sys_off_handler(dev, SYS_OFF_MODE_RESTART,
-+ SYS_OFF_PRIO_FIRMWARE,
-+ omnia_restart, mcu);
-+ if (err)
-+ return dev_err_probe(dev, err,
-+ "Cannot register system restart handler\n");
-+
-+ /*
-+ * Poweroff and wakeup are available only if POWEROFF_WAKEUP feature is
-+ * present.
-+ */
-+ if (!(mcu->features & OMNIA_FEAT_POWEROFF_WAKEUP))
-+ return 0;
-+
-+ err = devm_register_sys_off_handler(dev, SYS_OFF_MODE_POWER_OFF,
-+ SYS_OFF_PRIO_FIRMWARE,
-+ omnia_power_off, mcu);
-+ if (err)
-+ return dev_err_probe(dev, err,
-+ "Cannot register system power off handler\n");
-+
-+ mcu->rtcdev = devm_rtc_allocate_device(dev);
-+ if (IS_ERR(mcu->rtcdev))
-+ return dev_err_probe(dev, PTR_ERR(mcu->rtcdev),
-+ "Cannot allocate RTC device\n");
-+
-+ mcu->rtcdev->ops = &omnia_rtc_ops;
-+ mcu->rtcdev->range_max = U32_MAX;
-+ set_bit(RTC_FEATURE_ALARM_WAKEUP_ONLY, mcu->rtcdev->features);
-+
-+ err = devm_rtc_register_device(mcu->rtcdev);
-+ if (err)
-+ return dev_err_probe(dev, err, "Cannot register RTC device\n");
-+
-+ mcu->front_button_poweron = true;
-+
-+ return 0;
-+}
---- a/drivers/platform/cznic/turris-omnia-mcu.h
-+++ b/drivers/platform/cznic/turris-omnia-mcu.h
-@@ -15,8 +15,10 @@
- #include <linux/types.h>
- #include <linux/workqueue.h>
- #include <asm/byteorder.h>
-+#include <asm/unaligned.h>
-
- struct i2c_client;
-+struct rtc_device;
-
- struct omnia_mcu {
- struct i2c_client *client;
-@@ -36,6 +38,11 @@ struct omnia_mcu {
- struct delayed_work button_release_emul_work;
- unsigned long last_status;
- bool button_pressed_emul;
-+
-+ /* RTC device for configuring wake-up */
-+ struct rtc_device *rtcdev;
-+ u32 rtc_alarm;
-+ bool front_button_poweron;
- };
-
- int omnia_cmd_write_read(const struct i2c_client *client,
-@@ -48,6 +55,17 @@ static inline int omnia_cmd_write(const
- return omnia_cmd_write_read(client, cmd, len, NULL, 0);
- }
-
-+static inline int omnia_cmd_write_u32(const struct i2c_client *client, u8 cmd,
-+ u32 val)
-+{
-+ u8 buf[5];
-+
-+ buf[0] = cmd;
-+ put_unaligned_le32(val, &buf[1]);
-+
-+ return omnia_cmd_write(client, buf, sizeof(buf));
-+}
-+
- static inline int omnia_cmd_read(const struct i2c_client *client, u8 cmd,
- void *reply, unsigned int len)
- {
-@@ -136,7 +154,9 @@ static inline int omnia_cmd_read_u8(cons
- }
-
- extern const struct attribute_group omnia_mcu_gpio_group;
-+extern const struct attribute_group omnia_mcu_poweroff_group;
-
- int omnia_mcu_register_gpiochip(struct omnia_mcu *mcu);
-+int omnia_mcu_register_sys_off_and_wakeup(struct omnia_mcu *mcu);
-
- #endif /* __TURRIS_OMNIA_MCU_H */
+++ /dev/null
-From 33ae4e4c86bc6ff298489fb8b743e2743dd0af6d Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Marek=20Beh=C3=BAn?= <kabel@kernel.org>
-Date: Mon, 1 Jul 2024 13:30:07 +0200
-Subject: [PATCH 05/11] platform: cznic: turris-omnia-mcu: Add support for MCU
- watchdog
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Add support for the watchdog mechanism provided by the MCU.
-
-Signed-off-by: Marek Behún <kabel@kernel.org>
-Reviewed-by: Andy Shevchenko <andy@kernel.org>
-Reviewed-by: Guenter Roeck <linux@roeck-us.net>
-Acked-by: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
-Link: https://lore.kernel.org/r/20240701113010.16447-6-kabel@kernel.org
-Signed-off-by: Arnd Bergmann <arnd@arndb.de>
----
- drivers/platform/cznic/Kconfig | 2 +
- drivers/platform/cznic/Makefile | 1 +
- .../platform/cznic/turris-omnia-mcu-base.c | 4 +
- .../cznic/turris-omnia-mcu-watchdog.c | 130 ++++++++++++++++++
- drivers/platform/cznic/turris-omnia-mcu.h | 24 ++++
- 5 files changed, 161 insertions(+)
- create mode 100644 drivers/platform/cznic/turris-omnia-mcu-watchdog.c
-
---- a/drivers/platform/cznic/Kconfig
-+++ b/drivers/platform/cznic/Kconfig
-@@ -19,6 +19,7 @@ config TURRIS_OMNIA_MCU
- select GPIOLIB
- select GPIOLIB_IRQCHIP
- select RTC_CLASS
-+ select WATCHDOG_CORE
- help
- Say Y here to add support for the features implemented by the
- microcontroller on the CZ.NIC's Turris Omnia SOHO router.
-@@ -26,6 +27,7 @@ config TURRIS_OMNIA_MCU
- - board poweroff into true low power mode (with voltage regulators
- disabled) and the ability to configure wake up from this mode (via
- rtcwake)
-+ - MCU watchdog
- - GPIO pins
- - to get front button press events (the front button can be
- configured either to generate press events to the CPU or to change
---- a/drivers/platform/cznic/Makefile
-+++ b/drivers/platform/cznic/Makefile
-@@ -4,3 +4,4 @@ obj-$(CONFIG_TURRIS_OMNIA_MCU) += turris
- turris-omnia-mcu-y := turris-omnia-mcu-base.o
- turris-omnia-mcu-y += turris-omnia-mcu-gpio.o
- turris-omnia-mcu-y += turris-omnia-mcu-sys-off-wakeup.o
-+turris-omnia-mcu-y += turris-omnia-mcu-watchdog.o
---- a/drivers/platform/cznic/turris-omnia-mcu-base.c
-+++ b/drivers/platform/cznic/turris-omnia-mcu-base.c
-@@ -376,6 +376,10 @@ static int omnia_mcu_probe(struct i2c_cl
- if (err)
- return err;
-
-+ err = omnia_mcu_register_watchdog(mcu);
-+ if (err)
-+ return err;
-+
- return omnia_mcu_register_gpiochip(mcu);
- }
-
---- /dev/null
-+++ b/drivers/platform/cznic/turris-omnia-mcu-watchdog.c
-@@ -0,0 +1,130 @@
-+// SPDX-License-Identifier: GPL-2.0
-+/*
-+ * CZ.NIC's Turris Omnia MCU watchdog driver
-+ *
-+ * 2024 by Marek Behún <kabel@kernel.org>
-+ */
-+
-+#include <linux/bitops.h>
-+#include <linux/device.h>
-+#include <linux/i2c.h>
-+#include <linux/moduleparam.h>
-+#include <linux/types.h>
-+#include <linux/units.h>
-+#include <linux/watchdog.h>
-+
-+#include <linux/turris-omnia-mcu-interface.h>
-+#include "turris-omnia-mcu.h"
-+
-+#define WATCHDOG_TIMEOUT 120
-+
-+static unsigned int timeout;
-+module_param(timeout, int, 0);
-+MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds");
-+
-+static bool nowayout = WATCHDOG_NOWAYOUT;
-+module_param(nowayout, bool, 0);
-+MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
-+ __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
-+
-+static int omnia_wdt_start(struct watchdog_device *wdt)
-+{
-+ struct omnia_mcu *mcu = watchdog_get_drvdata(wdt);
-+
-+ return omnia_cmd_write_u8(mcu->client, OMNIA_CMD_SET_WATCHDOG_STATE, 1);
-+}
-+
-+static int omnia_wdt_stop(struct watchdog_device *wdt)
-+{
-+ struct omnia_mcu *mcu = watchdog_get_drvdata(wdt);
-+
-+ return omnia_cmd_write_u8(mcu->client, OMNIA_CMD_SET_WATCHDOG_STATE, 0);
-+}
-+
-+static int omnia_wdt_ping(struct watchdog_device *wdt)
-+{
-+ struct omnia_mcu *mcu = watchdog_get_drvdata(wdt);
-+
-+ return omnia_cmd_write_u8(mcu->client, OMNIA_CMD_SET_WATCHDOG_STATE, 1);
-+}
-+
-+static int omnia_wdt_set_timeout(struct watchdog_device *wdt,
-+ unsigned int timeout)
-+{
-+ struct omnia_mcu *mcu = watchdog_get_drvdata(wdt);
-+
-+ return omnia_cmd_write_u16(mcu->client, OMNIA_CMD_SET_WDT_TIMEOUT,
-+ timeout * DECI);
-+}
-+
-+static unsigned int omnia_wdt_get_timeleft(struct watchdog_device *wdt)
-+{
-+ struct omnia_mcu *mcu = watchdog_get_drvdata(wdt);
-+ u16 timeleft;
-+ int err;
-+
-+ err = omnia_cmd_read_u16(mcu->client, OMNIA_CMD_GET_WDT_TIMELEFT,
-+ &timeleft);
-+ if (err) {
-+ dev_err(&mcu->client->dev, "Cannot get watchdog timeleft: %d\n",
-+ err);
-+ return 0;
-+ }
-+
-+ return timeleft / DECI;
-+}
-+
-+static const struct watchdog_info omnia_wdt_info = {
-+ .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
-+ .identity = "Turris Omnia MCU Watchdog",
-+};
-+
-+static const struct watchdog_ops omnia_wdt_ops = {
-+ .owner = THIS_MODULE,
-+ .start = omnia_wdt_start,
-+ .stop = omnia_wdt_stop,
-+ .ping = omnia_wdt_ping,
-+ .set_timeout = omnia_wdt_set_timeout,
-+ .get_timeleft = omnia_wdt_get_timeleft,
-+};
-+
-+int omnia_mcu_register_watchdog(struct omnia_mcu *mcu)
-+{
-+ struct device *dev = &mcu->client->dev;
-+ u8 state;
-+ int err;
-+
-+ if (!(mcu->features & OMNIA_FEAT_WDT_PING))
-+ return 0;
-+
-+ mcu->wdt.info = &omnia_wdt_info;
-+ mcu->wdt.ops = &omnia_wdt_ops;
-+ mcu->wdt.parent = dev;
-+ mcu->wdt.min_timeout = 1;
-+ mcu->wdt.max_timeout = 65535 / DECI;
-+
-+ mcu->wdt.timeout = WATCHDOG_TIMEOUT;
-+ watchdog_init_timeout(&mcu->wdt, timeout, dev);
-+
-+ watchdog_set_drvdata(&mcu->wdt, mcu);
-+
-+ omnia_wdt_set_timeout(&mcu->wdt, mcu->wdt.timeout);
-+
-+ err = omnia_cmd_read_u8(mcu->client, OMNIA_CMD_GET_WATCHDOG_STATE,
-+ &state);
-+ if (err)
-+ return dev_err_probe(dev, err,
-+ "Cannot get MCU watchdog state\n");
-+
-+ if (state)
-+ set_bit(WDOG_HW_RUNNING, &mcu->wdt.status);
-+
-+ watchdog_set_nowayout(&mcu->wdt, nowayout);
-+ watchdog_stop_on_reboot(&mcu->wdt);
-+ err = devm_watchdog_register_device(dev, &mcu->wdt);
-+ if (err)
-+ return dev_err_probe(dev, err,
-+ "Cannot register MCU watchdog\n");
-+
-+ return 0;
-+}
---- a/drivers/platform/cznic/turris-omnia-mcu.h
-+++ b/drivers/platform/cznic/turris-omnia-mcu.h
-@@ -13,6 +13,7 @@
- #include <linux/if_ether.h>
- #include <linux/mutex.h>
- #include <linux/types.h>
-+#include <linux/watchdog.h>
- #include <linux/workqueue.h>
- #include <asm/byteorder.h>
- #include <asm/unaligned.h>
-@@ -43,6 +44,9 @@ struct omnia_mcu {
- struct rtc_device *rtcdev;
- u32 rtc_alarm;
- bool front_button_poweron;
-+
-+ /* MCU watchdog */
-+ struct watchdog_device wdt;
- };
-
- int omnia_cmd_write_read(const struct i2c_client *client,
-@@ -55,6 +59,25 @@ static inline int omnia_cmd_write(const
- return omnia_cmd_write_read(client, cmd, len, NULL, 0);
- }
-
-+static inline int omnia_cmd_write_u8(const struct i2c_client *client, u8 cmd,
-+ u8 val)
-+{
-+ u8 buf[2] = { cmd, val };
-+
-+ return omnia_cmd_write(client, buf, sizeof(buf));
-+}
-+
-+static inline int omnia_cmd_write_u16(const struct i2c_client *client, u8 cmd,
-+ u16 val)
-+{
-+ u8 buf[3];
-+
-+ buf[0] = cmd;
-+ put_unaligned_le16(val, &buf[1]);
-+
-+ return omnia_cmd_write(client, buf, sizeof(buf));
-+}
-+
- static inline int omnia_cmd_write_u32(const struct i2c_client *client, u8 cmd,
- u32 val)
- {
-@@ -158,5 +181,6 @@ extern const struct attribute_group omni
-
- int omnia_mcu_register_gpiochip(struct omnia_mcu *mcu);
- int omnia_mcu_register_sys_off_and_wakeup(struct omnia_mcu *mcu);
-+int omnia_mcu_register_watchdog(struct omnia_mcu *mcu);
-
- #endif /* __TURRIS_OMNIA_MCU_H */
+++ /dev/null
-From b3ed8645c45567b598bef0868dca166f8ed166a0 Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Marek=20Beh=C3=BAn?= <kabel@kernel.org>
-Date: Mon, 1 Jul 2024 13:30:08 +0200
-Subject: [PATCH 06/11] platform: cznic: turris-omnia-mcu: Add support for MCU
- provided TRNG
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Add support for true random number generator provided by the MCU.
-New Omnia boards come without the Atmel SHA204-A chip. Instead the
-crypto functionality is provided by new microcontroller, which has
-a TRNG peripheral.
-
-Signed-off-by: Marek Behún <kabel@kernel.org>
-Acked-by: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
-Link: https://lore.kernel.org/r/20240701113010.16447-7-kabel@kernel.org
-Signed-off-by: Arnd Bergmann <arnd@arndb.de>
----
- drivers/platform/cznic/Kconfig | 2 +
- drivers/platform/cznic/Makefile | 1 +
- .../platform/cznic/turris-omnia-mcu-base.c | 6 +-
- .../platform/cznic/turris-omnia-mcu-gpio.c | 2 +-
- .../platform/cznic/turris-omnia-mcu-trng.c | 105 ++++++++++++++++++
- drivers/platform/cznic/turris-omnia-mcu.h | 8 ++
- 6 files changed, 122 insertions(+), 2 deletions(-)
- create mode 100644 drivers/platform/cznic/turris-omnia-mcu-trng.c
-
---- a/drivers/platform/cznic/Kconfig
-+++ b/drivers/platform/cznic/Kconfig
-@@ -18,6 +18,7 @@ config TURRIS_OMNIA_MCU
- depends on I2C
- select GPIOLIB
- select GPIOLIB_IRQCHIP
-+ select HW_RANDOM
- select RTC_CLASS
- select WATCHDOG_CORE
- help
-@@ -27,6 +28,7 @@ config TURRIS_OMNIA_MCU
- - board poweroff into true low power mode (with voltage regulators
- disabled) and the ability to configure wake up from this mode (via
- rtcwake)
-+ - true random number generator (if available on the MCU)
- - MCU watchdog
- - GPIO pins
- - to get front button press events (the front button can be
---- a/drivers/platform/cznic/Makefile
-+++ b/drivers/platform/cznic/Makefile
-@@ -4,4 +4,5 @@ obj-$(CONFIG_TURRIS_OMNIA_MCU) += turris
- turris-omnia-mcu-y := turris-omnia-mcu-base.o
- turris-omnia-mcu-y += turris-omnia-mcu-gpio.o
- turris-omnia-mcu-y += turris-omnia-mcu-sys-off-wakeup.o
-+turris-omnia-mcu-y += turris-omnia-mcu-trng.o
- turris-omnia-mcu-y += turris-omnia-mcu-watchdog.o
---- a/drivers/platform/cznic/turris-omnia-mcu-base.c
-+++ b/drivers/platform/cznic/turris-omnia-mcu-base.c
-@@ -380,7 +380,11 @@ static int omnia_mcu_probe(struct i2c_cl
- if (err)
- return err;
-
-- return omnia_mcu_register_gpiochip(mcu);
-+ err = omnia_mcu_register_gpiochip(mcu);
-+ if (err)
-+ return err;
-+
-+ return omnia_mcu_register_trng(mcu);
- }
-
- static const struct of_device_id of_omnia_mcu_match[] = {
---- a/drivers/platform/cznic/turris-omnia-mcu-gpio.c
-+++ b/drivers/platform/cznic/turris-omnia-mcu-gpio.c
-@@ -194,7 +194,7 @@ static const struct omnia_gpio omnia_gpi
- };
-
- /* mapping from interrupts to indexes of GPIOs in the omnia_gpios array */
--static const u8 omnia_int_to_gpio_idx[32] = {
-+const u8 omnia_int_to_gpio_idx[32] = {
- [__bf_shf(OMNIA_INT_CARD_DET)] = 4,
- [__bf_shf(OMNIA_INT_MSATA_IND)] = 5,
- [__bf_shf(OMNIA_INT_USB30_OVC)] = 6,
---- /dev/null
-+++ b/drivers/platform/cznic/turris-omnia-mcu-trng.c
-@@ -0,0 +1,105 @@
-+// SPDX-License-Identifier: GPL-2.0
-+/*
-+ * CZ.NIC's Turris Omnia MCU TRNG driver
-+ *
-+ * 2024 by Marek Behún <kabel@kernel.org>
-+ */
-+
-+#include <linux/bitfield.h>
-+#include <linux/completion.h>
-+#include <linux/container_of.h>
-+#include <linux/errno.h>
-+#include <linux/gpio/consumer.h>
-+#include <linux/gpio/driver.h>
-+#include <linux/hw_random.h>
-+#include <linux/i2c.h>
-+#include <linux/interrupt.h>
-+#include <linux/minmax.h>
-+#include <linux/string.h>
-+#include <linux/types.h>
-+
-+#include "../../gpio/gpiolib.h"
-+
-+#include <linux/turris-omnia-mcu-interface.h>
-+#include "turris-omnia-mcu.h"
-+
-+#define OMNIA_CMD_TRNG_MAX_ENTROPY_LEN 64
-+
-+static irqreturn_t omnia_trng_irq_handler(int irq, void *dev_id)
-+{
-+ struct omnia_mcu *mcu = dev_id;
-+
-+ complete(&mcu->trng_entropy_ready);
-+
-+ return IRQ_HANDLED;
-+}
-+
-+static int omnia_trng_read(struct hwrng *rng, void *data, size_t max, bool wait)
-+{
-+ struct omnia_mcu *mcu = container_of(rng, struct omnia_mcu, trng);
-+ u8 reply[1 + OMNIA_CMD_TRNG_MAX_ENTROPY_LEN];
-+ int err, bytes;
-+
-+ if (!wait && !completion_done(&mcu->trng_entropy_ready))
-+ return 0;
-+
-+ do {
-+ if (wait_for_completion_interruptible(&mcu->trng_entropy_ready))
-+ return -ERESTARTSYS;
-+
-+ err = omnia_cmd_read(mcu->client,
-+ OMNIA_CMD_TRNG_COLLECT_ENTROPY,
-+ reply, sizeof(reply));
-+ if (err)
-+ return err;
-+
-+ bytes = min3(reply[0], max, OMNIA_CMD_TRNG_MAX_ENTROPY_LEN);
-+ } while (wait && !bytes);
-+
-+ memcpy(data, &reply[1], bytes);
-+
-+ return bytes;
-+}
-+
-+int omnia_mcu_register_trng(struct omnia_mcu *mcu)
-+{
-+ struct device *dev = &mcu->client->dev;
-+ u8 irq_idx, dummy;
-+ int irq, err;
-+
-+ if (!(mcu->features & OMNIA_FEAT_TRNG))
-+ return 0;
-+
-+ irq_idx = omnia_int_to_gpio_idx[__bf_shf(OMNIA_INT_TRNG)];
-+ irq = gpiod_to_irq(gpiochip_get_desc(&mcu->gc, irq_idx));
-+ if (!irq)
-+ return dev_err_probe(dev, -ENXIO, "Cannot get TRNG IRQ\n");
-+
-+ /*
-+ * If someone else cleared the TRNG interrupt but did not read the
-+ * entropy, a new interrupt won't be generated, and entropy collection
-+ * will be stuck. Ensure an interrupt will be generated by executing
-+ * the collect entropy command (and discarding the result).
-+ */
-+ err = omnia_cmd_read(mcu->client, OMNIA_CMD_TRNG_COLLECT_ENTROPY,
-+ &dummy, 1);
-+ if (err)
-+ return err;
-+
-+ init_completion(&mcu->trng_entropy_ready);
-+
-+ err = devm_request_threaded_irq(dev, irq, NULL, omnia_trng_irq_handler,
-+ IRQF_ONESHOT, "turris-omnia-mcu-trng",
-+ mcu);
-+ if (err)
-+ return dev_err_probe(dev, err, "Cannot request TRNG IRQ\n");
-+
-+ mcu->trng.name = "turris-omnia-mcu-trng";
-+ mcu->trng.read = omnia_trng_read;
-+
-+ err = devm_hwrng_register(dev, &mcu->trng);
-+ if (err)
-+ return dev_err_probe(dev, err, "Cannot register TRNG\n");
-+
-+ return 0;
-+}
---- a/drivers/platform/cznic/turris-omnia-mcu.h
-+++ b/drivers/platform/cznic/turris-omnia-mcu.h
-@@ -9,7 +9,9 @@
- #define __TURRIS_OMNIA_MCU_H
-
- #include <linux/bitops.h>
-+#include <linux/completion.h>
- #include <linux/gpio/driver.h>
-+#include <linux/hw_random.h>
- #include <linux/if_ether.h>
- #include <linux/mutex.h>
- #include <linux/types.h>
-@@ -47,6 +49,10 @@ struct omnia_mcu {
-
- /* MCU watchdog */
- struct watchdog_device wdt;
-+
-+ /* true random number generator */
-+ struct hwrng trng;
-+ struct completion trng_entropy_ready;
- };
-
- int omnia_cmd_write_read(const struct i2c_client *client,
-@@ -176,11 +182,13 @@ static inline int omnia_cmd_read_u8(cons
- return omnia_cmd_read(client, cmd, reply, sizeof(*reply));
- }
-
-+extern const u8 omnia_int_to_gpio_idx[32];
- extern const struct attribute_group omnia_mcu_gpio_group;
- extern const struct attribute_group omnia_mcu_poweroff_group;
-
- int omnia_mcu_register_gpiochip(struct omnia_mcu *mcu);
- int omnia_mcu_register_sys_off_and_wakeup(struct omnia_mcu *mcu);
-+int omnia_mcu_register_trng(struct omnia_mcu *mcu);
- int omnia_mcu_register_watchdog(struct omnia_mcu *mcu);
-
- #endif /* __TURRIS_OMNIA_MCU_H */
+++ /dev/null
-From 4f11095a4ae00b2fe4cebb21e36ee37cc62f5e1a Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Marek=20Beh=C3=BAn?= <kabel@kernel.org>
-Date: Mon, 1 Jul 2024 13:30:09 +0200
-Subject: [PATCH 07/11] ARM: dts: turris-omnia: Add MCU system-controller node
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Turris Omnia's MCU provides various features that can be configured over
-I2C at address 0x2a. Add device-tree node.
-
-This does not carry a Fixes tag - we do not want this to get backported
-to stable kernels for the following reason: U-Boot since v2022.10
-inserts a phy-reset-gpio property into the WAN ethernet node pointing to
-the MCU node if it finds the MCU node with a cznic,turris-omnia-mcu
-compatible. Thus if this change got backported to a stable kernel, the
-WAN interface driver would defer probe indefinitely (since it would wait
-for the turris-omnia-mcu driver which would not be present).
-
-Signed-off-by: Marek Behún <kabel@kernel.org>
-Reviewed-by: Andrew Lunn <andrew@lunn.ch>
-Reviewed-by: Andy Shevchenko <andy@kernel.org>
-Reviewed-by: Conor Dooley <conor.dooley@microchip.com>
-Acked-by: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
-Acked-by: Alexandre Belloni <alexandre.belloni@bootlin.com>
-Link: https://lore.kernel.org/r/20240701113010.16447-8-kabel@kernel.org
-Signed-off-by: Arnd Bergmann <arnd@arndb.de>
----
- .../dts/marvell/armada-385-turris-omnia.dts | 22 ++++++++++++++++++-
- 1 file changed, 21 insertions(+), 1 deletion(-)
-
---- a/arch/arm/boot/dts/marvell/armada-385-turris-omnia.dts
-+++ b/arch/arm/boot/dts/marvell/armada-385-turris-omnia.dts
-@@ -218,7 +218,22 @@
- #size-cells = <0>;
- reg = <0>;
-
-- /* STM32F0 command interface at address 0x2a */
-+ mcu: system-controller@2a {
-+ compatible = "cznic,turris-omnia-mcu";
-+ reg = <0x2a>;
-+
-+ pinctrl-names = "default";
-+ pinctrl-0 = <&mcu_pins>;
-+
-+ interrupt-parent = <&gpio1>;
-+ interrupts = <11 IRQ_TYPE_NONE>;
-+
-+ gpio-controller;
-+ #gpio-cells = <3>;
-+
-+ interrupt-controller;
-+ #interrupt-cells = <2>;
-+ };
-
- led-controller@2b {
- compatible = "cznic,turris-omnia-leds";
-@@ -503,6 +518,11 @@
- };
-
- &pinctrl {
-+ mcu_pins: mcu-pins {
-+ marvell,pins = "mpp43";
-+ marvell,function = "gpio";
-+ };
-+
- pcawan_pins: pcawan-pins {
- marvell,pins = "mpp46";
- marvell,function = "gpio";
+++ /dev/null
-From c3eeabe0b8d22d7c869278cc0cb35b83512fbed5 Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Marek=20Beh=C3=BAn?= <kabel@kernel.org>
-Date: Mon, 1 Jul 2024 13:30:10 +0200
-Subject: [PATCH 08/11] ARM: dts: turris-omnia: Add GPIO key node for front
- button
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Now that we have the MCU device-tree node, which acts as a GPIO
-controller, add GPIO key node for the front button.
-
-Signed-off-by: Marek Behún <kabel@kernel.org>
-Reviewed-by: Andrew Lunn <andrew@lunn.ch>
-Reviewed-by: Andy Shevchenko <andy@kernel.org>
-Reviewed-by: Conor Dooley <conor.dooley@microchip.com>
-Acked-by: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
-Acked-by: Alexandre Belloni <alexandre.belloni@bootlin.com>
-Link: https://lore.kernel.org/r/20240701113010.16447-9-kabel@kernel.org
-Signed-off-by: Arnd Bergmann <arnd@arndb.de>
----
- .../boot/dts/marvell/armada-385-turris-omnia.dts | 13 +++++++++++++
- 1 file changed, 13 insertions(+)
-
---- a/arch/arm/boot/dts/marvell/armada-385-turris-omnia.dts
-+++ b/arch/arm/boot/dts/marvell/armada-385-turris-omnia.dts
-@@ -112,6 +112,19 @@
- status = "disabled";
- };
-
-+ gpio-keys {
-+ compatible = "gpio-keys";
-+
-+ front-button {
-+ label = "Front Button";
-+ linux,code = <KEY_VENDOR>;
-+ linux,can-disable;
-+ gpios = <&mcu 0 12 GPIO_ACTIVE_HIGH>;
-+ /* debouncing is done by the microcontroller */
-+ debounce-interval = <0>;
-+ };
-+ };
-+
- sound {
- compatible = "simple-audio-card";
- simple-audio-card,name = "SPDIF";
+++ /dev/null
-From 08838657bbc35494276c7ba4ef53f30a9816f8c9 Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Marek=20Beh=C3=BAn?= <kabel@kernel.org>
-Date: Mon, 8 Jul 2024 13:40:01 +0200
-Subject: [PATCH 09/11] platform: cznic: turris-omnia-mcu: Depend on OF
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Add depend on OF, otherwise the compilation fails with
- error: no member named 'of_gpio_n_cells' in 'struct gpio_chip'
- error: no member named 'of_xlate' in 'struct gpio_chip'
-
-Fixes: dfa556e45ae9 ("platform: cznic: turris-omnia-mcu: Add support for MCU connected GPIOs")
-Reported-by: kernel test robot <lkp@intel.com>
-Closes: https://lore.kernel.org/oe-kbuild-all/202407031646.trNSwajF-lkp@intel.com/
-Signed-off-by: Marek Behún <kabel@kernel.org>
-Link: https://lore.kernel.org/r/20240708114002.4285-2-kabel@kernel.org
-Signed-off-by: Arnd Bergmann <arnd@arndb.de>
----
- drivers/platform/cznic/Kconfig | 1 +
- 1 file changed, 1 insertion(+)
-
---- a/drivers/platform/cznic/Kconfig
-+++ b/drivers/platform/cznic/Kconfig
-@@ -16,6 +16,7 @@ config TURRIS_OMNIA_MCU
- tristate "Turris Omnia MCU driver"
- depends on MACH_ARMADA_38X || COMPILE_TEST
- depends on I2C
-+ depends on OF
- select GPIOLIB
- select GPIOLIB_IRQCHIP
- select HW_RANDOM
+++ /dev/null
-From 5e425e6eca155c162da58d4e58e896ed4109c7fd Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Marek=20Beh=C3=BAn?= <kabel@kernel.org>
-Date: Mon, 8 Jul 2024 13:40:02 +0200
-Subject: [PATCH 10/11] platform: cznic: turris-omnia-mcu: Depend on WATCHDOG
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Add depend on WATCHDOG, otherwise modpost fails with
- ERROR: modpost: "watchdog_init_timeout" [drivers/platform/cznic/turris-omnia-mcu.ko] undefined!
- ERROR: modpost: "devm_watchdog_register_device" [drivers/platform/cznic/turris-omnia-mcu.ko] undefined!
-
-Fixes: ab89fb5fb92c ("platform: cznic: turris-omnia-mcu: Add support for MCU watchdog")
-Reported-by: kernel test robot <lkp@intel.com>
-Closes: https://lore.kernel.org/oe-kbuild-all/202407040711.g19y3cWq-lkp@intel.com/
-Signed-off-by: Marek Behún <kabel@kernel.org>
-Link: https://lore.kernel.org/r/20240708114002.4285-3-kabel@kernel.org
-Signed-off-by: Arnd Bergmann <arnd@arndb.de>
----
- drivers/platform/cznic/Kconfig | 1 +
- 1 file changed, 1 insertion(+)
-
---- a/drivers/platform/cznic/Kconfig
-+++ b/drivers/platform/cznic/Kconfig
-@@ -17,6 +17,7 @@ config TURRIS_OMNIA_MCU
- depends on MACH_ARMADA_38X || COMPILE_TEST
- depends on I2C
- depends on OF
-+ depends on WATCHDOG
- select GPIOLIB
- select GPIOLIB_IRQCHIP
- select HW_RANDOM
+++ /dev/null
-From 24c68c2525de5fcd0f3b16b2ad1028fb13b53393 Mon Sep 17 00:00:00 2001
-From: Arnd Bergmann <arnd@arndb.de>
-Date: Mon, 15 Jul 2024 08:02:30 +0200
-Subject: [PATCH 11/11] platform: cznic: turris-omnia-mcu: fix Kconfig
- dependencies
-
-The newly added driver causes a Kconfig warning:
-
-WARNING: unmet direct dependencies detected for RTC_CLASS
- Depends on [n]: !S390 [=y]
- Selected by [m]:
- - TURRIS_OMNIA_MCU [=m] && CZNIC_PLATFORMS [=y] && (MACH_ARMADA_38X || COMPILE_TEST [=y]) && I2C [=m] && OF [=y] && WATCHDOG [=y]
-
-The problem here is that it selects entire subsystems, which normal
-device drivers should not do. Changes all of these to 'depends on'
-instead.
-
-Fixes: dfa556e45ae9e ("platform: cznic: turris-omnia-mcu: Add support for MCU connected GPIOs")
-Fixes: 90e700fd12b61 ("platform: cznic: turris-omnia-mcu: Add support for poweroff and wakeup")
-Fixes: ab89fb5fb92c7 ("platform: cznic: turris-omnia-mcu: Add support for MCU watchdog")
-Fixes: 41bb142a40289 ("platform: cznic: turris-omnia-mcu: Add support for MCU provided TRNG")
-Reported-by: Nathan Chancellor <nathan@kernel.org>
-Signed-off-by: Arnd Bergmann <arnd@arndb.de>
----
- drivers/platform/cznic/Kconfig | 8 ++++----
- 1 file changed, 4 insertions(+), 4 deletions(-)
-
---- a/drivers/platform/cznic/Kconfig
-+++ b/drivers/platform/cznic/Kconfig
-@@ -18,11 +18,11 @@ config TURRIS_OMNIA_MCU
- depends on I2C
- depends on OF
- depends on WATCHDOG
-- select GPIOLIB
-+ depends on GPIOLIB
-+ depends on HW_RANDOM
-+ depends on RTC_CLASS
-+ depends on WATCHDOG_CORE
- select GPIOLIB_IRQCHIP
-- select HW_RANDOM
-- select RTC_CLASS
-- select WATCHDOG_CORE
- help
- Say Y here to add support for the features implemented by the
- microcontroller on the CZ.NIC's Turris Omnia SOHO router.
+++ /dev/null
-From patchwork Sun May 11 13:31:05 2025
-Content-Type: text/plain; charset="utf-8"
-MIME-Version: 1.0
-Content-Transfer-Encoding: 7bit
-X-Patchwork-Submitter: Gabor Juhos <j4g8y7@gmail.com>
-X-Patchwork-Id: 14084055
-Return-Path:
- <linux-arm-kernel-bounces+linux-arm-kernel=archiver.kernel.org@lists.infradead.org>
-X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on
- aws-us-west-2-korg-lkml-1.web.codeaurora.org
-Received: from bombadil.infradead.org (bombadil.infradead.org
- [198.137.202.133])
- (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits))
- (No client certificate requested)
- by smtp.lore.kernel.org (Postfix) with ESMTPS id 0802BC3ABC3
- for <linux-arm-kernel@archiver.kernel.org>;
- Sun, 11 May 2025 13:35:46 +0000 (UTC)
-DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed;
- d=lists.infradead.org; s=bombadil.20210309; h=Sender:List-Subscribe:List-Help
- :List-Post:List-Archive:List-Unsubscribe:List-Id:Cc:To:In-Reply-To:References
- :Message-Id:Content-Transfer-Encoding:Content-Type:MIME-Version:Subject:Date:
- From:Reply-To:Content-ID:Content-Description:Resent-Date:Resent-From:
- Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID:List-Owner;
- bh=n9RYlne5nhTN7kCu5Yc7zW4CF0YUJUWg2clQo/viA0M=; b=LBsqIvaHPjMCwZZfbGyry56BIg
- sEpAZY6rWjqf0DWQjODfNe8G8EZOzfHbMRA8HElCRdUR54+uQkesdQ9CuQip0FUIEkRlfRHM8HYTB
- iWLgZE6pnElndi/4uhAmfvgQFvMbDhxrsoAqAjLY4K+sPr53QSEGO7ygY+TPHmcpyVfD+kRqjMAv3
- wbypdxFC5lHAOvIiAdaKreajEf7HBqztprh/D1aE29TQdWILixJTAl5isgLzJyZUOEnqrefGvBQ9g
- +zg0jQwLLnjnsCMzkjho6mjwi1Q7I3ERQVRnebKf9eslmNstr9apYy5yfz6BKzHGxBk3yC+Boyvde
- FP62KNGQ==;
-Received: from localhost ([::1] helo=bombadil.infradead.org)
- by bombadil.infradead.org with esmtp (Exim 4.98.2 #2 (Red Hat Linux))
- id 1uE6qC-00000007Glw-1vse;
- Sun, 11 May 2025 13:35:40 +0000
-Received: from mail-ej1-x630.google.com ([2a00:1450:4864:20::630])
- by bombadil.infradead.org with esmtps (Exim 4.98.2 #2 (Red Hat Linux))
- id 1uE6mD-00000007GP8-397T
- for linux-arm-kernel@lists.infradead.org;
- Sun, 11 May 2025 13:31:34 +0000
-Received: by mail-ej1-x630.google.com with SMTP id
- a640c23a62f3a-ad1f6aa2f84so764633466b.0
- for <linux-arm-kernel@lists.infradead.org>;
- Sun, 11 May 2025 06:31:33 -0700 (PDT)
-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;
- d=gmail.com; s=20230601; t=1746970292; x=1747575092;
- darn=lists.infradead.org;
- h=cc:to:in-reply-to:references:message-id:content-transfer-encoding
- :mime-version:subject:date:from:from:to:cc:subject:date:message-id
- :reply-to;
- bh=n9RYlne5nhTN7kCu5Yc7zW4CF0YUJUWg2clQo/viA0M=;
- b=mK6aKMspWjttCQGv7A/DF5F1n7JFLuDEaCeCKlEp4FrClZQD6JhAlON6yEPPTc86q2
- XnRAAh8tIk3Gu/QyNYXIsrlBeqtv5b+sxUxV+ebyFg0AaMi+tbhf8koryKoVPDMQljmm
- i/Rv5GrycEwvnGMNsTHc+xLCwkjAy15KoLGdsfc+Uw3HrPmX+/8UFaVCmxltLgEprwVA
- drnzRzYh3IIFjsPYJIrp75cIMMXIHwm2IqKiGuzMaDeCsB3iA/En8PHWr2trxMV7K98g
- 442ZOjY0CqX1mAIKQLXmN7X3fCbOHinxxy7HefUe2YNclBf6UHr3pT2i9UBsalEpUWUl
- OQ7A==
-X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;
- d=1e100.net; s=20230601; t=1746970292; x=1747575092;
- h=cc:to:in-reply-to:references:message-id:content-transfer-encoding
- :mime-version:subject:date:from:x-gm-message-state:from:to:cc
- :subject:date:message-id:reply-to;
- bh=n9RYlne5nhTN7kCu5Yc7zW4CF0YUJUWg2clQo/viA0M=;
- b=HSWiBJsVViBeM5B5hMn4oUOZ7JTd6/O5/rjNuDl1Q//i4aFGGSy0L11zacjYVSLegk
- 2j9UiS7yLaVSlnNJle+YrtIy/kbCayoNUrYXtU5v9MvYAHjS0WMYijekCDQ9+lDLpXwg
- kpERQ/Tu1Fmqb+8Yv/XycWLCyTPR0XGXxVvGiAPs1KnXsuj71MK7/HXof0p3IDqS444Q
- dBuRDx1xby4/806P/EPvdr4Xj0lVF3tkimKtaPpBdXp+kkJ8xyYlMhLHAUX16F8Xktqs
- yuzG1OASvIocfiEiluOxqrM6727wJhtt8qiBxCiI/UBaEr5bUkIBvmvktYSVy+/N4Itf
- +qAw==
-X-Forwarded-Encrypted: i=1;
- AJvYcCWF5SWWguUIUPp10Yp8M+dIc/7999B/vb7xwMluPb/Vj9FZyJ4hnhDgLC60CD9icfYXYWwLlHN06vKw5onkKXF5@lists.infradead.org
-X-Gm-Message-State: AOJu0Yx7y5pv/+Mt1FmIK+MTvi1kVFAvXtKteYPDneNKNfNZuwqrLoNt
- F6DpKLyTyp8r/NQFiROomu/JLI5qMM4JRr5VwmbDeYC0dWKD4xH1
-X-Gm-Gg: ASbGncttO+Rf69HYuAf22AOp+cMso2pjn2/z/ZJhaxvqOUUqzypHmtdf5RV1S1/NJt+
- 2U8XzEy/JHeASbx9scc+3q0mJPtPNp8qCQpF+0zPaU8cbrfcsiggtSVfMhZP6sxqoI2iJtnMdLF
- JVZhFcynNHOqZYLBh8pWdnToMBYILJM7VhgFcJZIeDPaGbC5JDSKus+OUR31/TKl/K/+83r309I
- dYCCeBcJgLPA+Z5pDDHhFh/tjag+5ID+tTI3hH7IrEjz0LCb3DFL0QEsrwm9yQn4xUtQ9ms+KST
- 4DF3TYm4r0/Uqf8zWSPTbC4CL69YtYYBDCt22gKAW+PPDTQ4UBcDfIwI7MxiOOT2rnjfzWNtD+1
- 6uO29
-X-Google-Smtp-Source:
- AGHT+IFAmDDIIuhS51ef1EGvEQpU//VoeH0fQBhRPIfrKUiYydYis8s+qNymb53gRE7q67J/kVk0HA==
-X-Received: by 2002:a17:907:94d5:b0:acb:3acd:2845 with SMTP id
- a640c23a62f3a-ad1fccfced9mr1181053766b.25.1746970291841;
- Sun, 11 May 2025 06:31:31 -0700 (PDT)
-Received: from [192.168.0.253] (5D59A51C.catv.pool.telekom.hu. [93.89.165.28])
- by smtp.googlemail.com with ESMTPSA id
- a640c23a62f3a-ad2197bd398sm466765366b.152.2025.05.11.06.31.30
- (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256);
- Sun, 11 May 2025 06:31:31 -0700 (PDT)
-From: Gabor Juhos <j4g8y7@gmail.com>
-Date: Sun, 11 May 2025 15:31:05 +0200
-Subject: [PATCH 1/3] i2c: add init_recovery() callback
-MIME-Version: 1.0
-Message-Id:
- <20250511-i2c-pxa-fix-i2c-communication-v1-1-e9097d09a015@gmail.com>
-References:
- <20250511-i2c-pxa-fix-i2c-communication-v1-0-e9097d09a015@gmail.com>
-In-Reply-To:
- <20250511-i2c-pxa-fix-i2c-communication-v1-0-e9097d09a015@gmail.com>
-To: Wolfram Sang <wsa@kernel.org>, Andi Shyti <andi.shyti@kernel.org>,
- Russell King <rmk+kernel@armlinux.org.uk>, Andrew Lunn <andrew@lunn.ch>
-Cc: Robert Marko <robert.marko@sartura.hr>,
- Linus Walleij <linus.walleij@linaro.org>,
- Russell King <rmk+kernel@armlinux.org.uk>, linux-i2c@vger.kernel.org,
- linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org,
- Gabor Juhos <j4g8y7@gmail.com>, Imre Kaloz <kaloz@openwrt.org>,
- stable@vger.kernel.org
-X-Mailer: b4 0.14.2
-X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3
-X-CRM114-CacheID: sfid-20250511_063133_790603_F3EAC078
-X-CRM114-Status: GOOD ( 20.27 )
-X-BeenThere: linux-arm-kernel@lists.infradead.org
-X-Mailman-Version: 2.1.34
-Precedence: list
-List-Id: <linux-arm-kernel.lists.infradead.org>
-List-Unsubscribe:
- <http://lists.infradead.org/mailman/options/linux-arm-kernel>,
- <mailto:linux-arm-kernel-request@lists.infradead.org?subject=unsubscribe>
-List-Archive: <http://lists.infradead.org/pipermail/linux-arm-kernel/>
-List-Post: <mailto:linux-arm-kernel@lists.infradead.org>
-List-Help: <mailto:linux-arm-kernel-request@lists.infradead.org?subject=help>
-List-Subscribe:
- <http://lists.infradead.org/mailman/listinfo/linux-arm-kernel>,
- <mailto:linux-arm-kernel-request@lists.infradead.org?subject=subscribe>
-Sender: "linux-arm-kernel" <linux-arm-kernel-bounces@lists.infradead.org>
-Errors-To:
- linux-arm-kernel-bounces+linux-arm-kernel=archiver.kernel.org@lists.infradead.org
-
-Add a new init_recovery() callback to struct 'i2c_bus_recovery_info'
-and modify the i2c_init_recovery() function to call that if specified
-instead of the generic i2c_gpio_init_recovery() function.
-
-This allows controller drivers to skip calling the generic code by
-implementing a dummy callback function, or alternatively to run a
-fine tuned custom implementation.
-
-This is needed for the 'i2c-pxa' driver in order to be able to fix
-a long standing bug for which the fix will be implemented in a
-followup patch.
-
-Cc: stable@vger.kernel.org # 6.3+
-Signed-off-by: Gabor Juhos <j4g8y7@gmail.com>
-Signed-off-by: Imre Kaloz <kaloz@openwrt.org>
-Reviewed-by: Linus Walleij <linus.walleij@linaro.org>
----
- drivers/i2c/i2c-core-base.c | 8 +++++++-
- include/linux/i2c.h | 4 ++++
- 2 files changed, 11 insertions(+), 1 deletion(-)
-
---- a/drivers/i2c/i2c-core-base.c
-+++ b/drivers/i2c/i2c-core-base.c
-@@ -427,12 +427,18 @@ static int i2c_init_recovery(struct i2c_
- struct i2c_bus_recovery_info *bri = adap->bus_recovery_info;
- bool is_error_level = true;
- char *err_str;
-+ int ret;
-
- if (!bri)
- return 0;
-
-- if (i2c_gpio_init_recovery(adap) == -EPROBE_DEFER)
-+ if (bri->init_recovery) {
-+ ret = bri->init_recovery(adap);
-+ if (ret)
-+ return ret;
-+ } else if (i2c_gpio_init_recovery(adap) == -EPROBE_DEFER) {
- return -EPROBE_DEFER;
-+ }
-
- if (!bri->recover_bus) {
- err_str = "no suitable method provided";
---- a/include/linux/i2c.h
-+++ b/include/linux/i2c.h
-@@ -622,6 +622,9 @@ struct i2c_timings {
- * for generic GPIO recovery.
- * @get_bus_free: Returns the bus free state as seen from the IP core in case it
- * has a more complex internal logic than just reading SDA. Optional.
-+ * @init_recovery: If specified, it will be called instead of the generic GPIO
-+ * recovery initialization code. Platform may use a dummy callback to skip
-+ * calling the generic code, or it may use a custom implementation.
- * @prepare_recovery: This will be called before starting recovery. Platform may
- * configure padmux here for SDA/SCL line or something else they want.
- * @unprepare_recovery: This will be called after completing recovery. Platform
-@@ -646,6 +649,7 @@ struct i2c_bus_recovery_info {
- void (*set_sda)(struct i2c_adapter *adap, int val);
- int (*get_bus_free)(struct i2c_adapter *adap);
-
-+ int (*init_recovery)(struct i2c_adapter *adap);
- void (*prepare_recovery)(struct i2c_adapter *adap);
- void (*unprepare_recovery)(struct i2c_adapter *adap);
-
+++ /dev/null
-From patchwork Sun May 11 13:31:06 2025
-Content-Type: text/plain; charset="utf-8"
-MIME-Version: 1.0
-Content-Transfer-Encoding: 7bit
-X-Patchwork-Submitter: Gabor Juhos <j4g8y7@gmail.com>
-X-Patchwork-Id: 14084056
-Return-Path:
- <linux-arm-kernel-bounces+linux-arm-kernel=archiver.kernel.org@lists.infradead.org>
-X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on
- aws-us-west-2-korg-lkml-1.web.codeaurora.org
-Received: from bombadil.infradead.org (bombadil.infradead.org
- [198.137.202.133])
- (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits))
- (No client certificate requested)
- by smtp.lore.kernel.org (Postfix) with ESMTPS id 72E7DC3ABC3
- for <linux-arm-kernel@archiver.kernel.org>;
- Sun, 11 May 2025 13:37:50 +0000 (UTC)
-DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed;
- d=lists.infradead.org; s=bombadil.20210309; h=Sender:List-Subscribe:List-Help
- :List-Post:List-Archive:List-Unsubscribe:List-Id:Cc:To:In-Reply-To:References
- :Message-Id:Content-Transfer-Encoding:Content-Type:MIME-Version:Subject:Date:
- From:Reply-To:Content-ID:Content-Description:Resent-Date:Resent-From:
- Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID:List-Owner;
- bh=BySnXAIGhp18eRP0sg1mjpdXdHwBsus0rtjMi26U9uM=; b=ba0yGs/0cSs2cep9T3wjo3iDxy
- VSaQu2j7YGuiAMJt8ERY5OVL7YiPC/6+MqGcLqkrGunZq/TEjXRGX9ztKqyko6KY9fLbiH1wXYBjK
- /9yxbEyt9bPrIw/r64uaUOdRzZMF0i2oBn53RgZJJFaD4ou1E28BMc22AuVGshj99Dlz63ncr/Lz7
- M3/ptMOWGRp0SrImYRUdvnWj+LKyb4zqRFEYIQuq9WzyhhNuDZDaHFpdJn4go/eoS4kUvTFfixpwV
- FevQupqdOn62R3ull5YYdI6BFQVL6dISjD34mjoNXJABSjuXmq8FRWuX/V3rPrvnVIflxOmzSrKrF
- 8rdYY/fg==;
-Received: from localhost ([::1] helo=bombadil.infradead.org)
- by bombadil.infradead.org with esmtp (Exim 4.98.2 #2 (Red Hat Linux))
- id 1uE6sA-00000007GuC-0j29;
- Sun, 11 May 2025 13:37:42 +0000
-Received: from mail-ej1-x636.google.com ([2a00:1450:4864:20::636])
- by bombadil.infradead.org with esmtps (Exim 4.98.2 #2 (Red Hat Linux))
- id 1uE6mF-00000007GPb-1uOE
- for linux-arm-kernel@lists.infradead.org;
- Sun, 11 May 2025 13:31:36 +0000
-Received: by mail-ej1-x636.google.com with SMTP id
- a640c23a62f3a-acae7e7587dso539429266b.2
- for <linux-arm-kernel@lists.infradead.org>;
- Sun, 11 May 2025 06:31:35 -0700 (PDT)
-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;
- d=gmail.com; s=20230601; t=1746970294; x=1747575094;
- darn=lists.infradead.org;
- h=cc:to:in-reply-to:references:message-id:content-transfer-encoding
- :mime-version:subject:date:from:from:to:cc:subject:date:message-id
- :reply-to;
- bh=BySnXAIGhp18eRP0sg1mjpdXdHwBsus0rtjMi26U9uM=;
- b=QtIYufKg61b066knR9OpyawErNmkYyoP/2lIkHhyGY7qtGXkc1jJmC0TpPyhp0WGlm
- xWP3K/PG23VcjEuGj/880thPietuKQuTF029WPfm+yrQ3uHLMzkHqyOiMYyiBR9N0Zjw
- NICgywX4iqVHVBnXZTiBA7sRSjQqteatDPTGbKgZxpIHpg4ZJtfXZcCE8RiKVVgZCWsq
- JgTJuTO/4J0cq5TWVOIGLrt0XOslaMmuISPbUDdArr5yKfGZqu51i747A0o0RBdEuH9Y
- UPI8/GExcWWa+62KCVEwB9OmCZR81ieb8To/XdZO8hIQAlExOzKa3OOPCkS0PF0M9BBa
- d++Q==
-X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;
- d=1e100.net; s=20230601; t=1746970294; x=1747575094;
- h=cc:to:in-reply-to:references:message-id:content-transfer-encoding
- :mime-version:subject:date:from:x-gm-message-state:from:to:cc
- :subject:date:message-id:reply-to;
- bh=BySnXAIGhp18eRP0sg1mjpdXdHwBsus0rtjMi26U9uM=;
- b=A+LfIVmyzogyr2akFoGR3PIbjd8E/TVXGlur91KBF7h2W8msMTeMyv0PDVjqEBxWFI
- cqNLira3TWf9soXrRBGaJVB111/r5woARoN4aO5otCDrh3o5U2gJ5/eZLGbcX49TqJG5
- rbI1Eq0GGXFdidQ4T5CMLtooa6B5pg7j+o2gcyG/vQlHr6MbG4/2sIDPX0JbL08xibWC
- 5LqP933W+yopGIbnp3YZ9pybbvOUA9x4cYkUm1Of3idmkTN0xUh8kmUWz7FJCH4R/PAN
- 5CA/vn8q1myUvXisU0oAbC9y/WrlExPfvguXjru1LTx9AdPaC9+D3Qxmh2Raz9b4NrKl
- ZlVw==
-X-Forwarded-Encrypted: i=1;
- AJvYcCWMm/Er5t6Xp05DMDNak7mbsqx8YZmOrqz9Y39eovLKPkbqcMhQwOzxqxCY8ZfQtETNpZ8X9NptEow+bBHo+z3t@lists.infradead.org
-X-Gm-Message-State: AOJu0YwBTrjBcryZjqJBq66bcSYfV2AWsDAqAfMinxG8gRUJjpKq8RXn
- kKSKTp2XvMObVTWx7cEMM5BBPWl7LfLwBkjVIusbgScQ8qW8YA9H
-X-Gm-Gg: ASbGnctKtEUhBLJdOfPb7cKwM8a1ZTe5+8zPX9TaLdomZybHHeteF9o/ZyUNbunUsSK
- fuMy3pPFmYcC4TZtPqfZPdpagbDpOb2R/9xbSTMd1TKDDj4vFnkTIzQHA9E1ZxX44Nw6JkxA0up
- S94PBow9mEb3tkHLN9hbNe7W8ksyf7RZ25IGC4FYj/wEGQsMy2VidJsfG4bIeImZ8zj8ufUCNby
- 7jIjmP1taNt5U8omPEWhFuRdJfiPqIXx85tIsGVFwG7VSr/JW8LwxBCJLGhWLUZhxYqV4cEfSRC
- zYKj2qkuRfx5AxzN+JGtfcxYPijsy/rI51OnjOv2CQRPuE6rESamNJiR7pmnDgvHSRm5nps5chp
- /HVXU
-X-Google-Smtp-Source:
- AGHT+IG8Y/7VNg8lhY275mIfLbq8EaG9jCRvvK1wX7rlbuoMvEUYYC3k7sZOxh3RPmKhAB94Kfvlwg==
-X-Received: by 2002:a17:907:7b04:b0:ad1:fab8:88ab with SMTP id
- a640c23a62f3a-ad219085c07mr897192766b.29.1746970293535;
- Sun, 11 May 2025 06:31:33 -0700 (PDT)
-Received: from [192.168.0.253] (5D59A51C.catv.pool.telekom.hu. [93.89.165.28])
- by smtp.googlemail.com with ESMTPSA id
- a640c23a62f3a-ad2197bd398sm466765366b.152.2025.05.11.06.31.32
- (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256);
- Sun, 11 May 2025 06:31:33 -0700 (PDT)
-From: Gabor Juhos <j4g8y7@gmail.com>
-Date: Sun, 11 May 2025 15:31:06 +0200
-Subject: [PATCH 2/3] i2c: pxa: prevent calling of the generic recovery init
- code
-MIME-Version: 1.0
-Message-Id:
- <20250511-i2c-pxa-fix-i2c-communication-v1-2-e9097d09a015@gmail.com>
-References:
- <20250511-i2c-pxa-fix-i2c-communication-v1-0-e9097d09a015@gmail.com>
-In-Reply-To:
- <20250511-i2c-pxa-fix-i2c-communication-v1-0-e9097d09a015@gmail.com>
-To: Wolfram Sang <wsa@kernel.org>, Andi Shyti <andi.shyti@kernel.org>,
- Russell King <rmk+kernel@armlinux.org.uk>, Andrew Lunn <andrew@lunn.ch>
-Cc: Robert Marko <robert.marko@sartura.hr>,
- Linus Walleij <linus.walleij@linaro.org>,
- Russell King <rmk+kernel@armlinux.org.uk>, linux-i2c@vger.kernel.org,
- linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org,
- Gabor Juhos <j4g8y7@gmail.com>, Imre Kaloz <kaloz@openwrt.org>,
- stable@vger.kernel.org
-X-Mailer: b4 0.14.2
-X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3
-X-CRM114-CacheID: sfid-20250511_063135_499888_294068DC
-X-CRM114-Status: GOOD ( 21.30 )
-X-BeenThere: linux-arm-kernel@lists.infradead.org
-X-Mailman-Version: 2.1.34
-Precedence: list
-List-Id: <linux-arm-kernel.lists.infradead.org>
-List-Unsubscribe:
- <http://lists.infradead.org/mailman/options/linux-arm-kernel>,
- <mailto:linux-arm-kernel-request@lists.infradead.org?subject=unsubscribe>
-List-Archive: <http://lists.infradead.org/pipermail/linux-arm-kernel/>
-List-Post: <mailto:linux-arm-kernel@lists.infradead.org>
-List-Help: <mailto:linux-arm-kernel-request@lists.infradead.org?subject=help>
-List-Subscribe:
- <http://lists.infradead.org/mailman/listinfo/linux-arm-kernel>,
- <mailto:linux-arm-kernel-request@lists.infradead.org?subject=subscribe>
-Sender: "linux-arm-kernel" <linux-arm-kernel-bounces@lists.infradead.org>
-Errors-To:
- linux-arm-kernel-bounces+linux-arm-kernel=archiver.kernel.org@lists.infradead.org
-
-The I2C communication is completely broken on the Armada 3700 platform
-since commit 0b01392c18b9 ("i2c: pxa: move to generic GPIO recovery").
-
-For example, on the Methode uDPU board, probing of the two onboard
-temperature sensors fails ...
-
- [ 7.271713] i2c i2c-0: using pinctrl states for GPIO recovery
- [ 7.277503] i2c i2c-0: PXA I2C adapter
- [ 7.282199] i2c i2c-1: using pinctrl states for GPIO recovery
- [ 7.288241] i2c i2c-1: PXA I2C adapter
- [ 7.292947] sfp sfp-eth1: Host maximum power 3.0W
- [ 7.299614] sfp sfp-eth0: Host maximum power 3.0W
- [ 7.308178] lm75 1-0048: supply vs not found, using dummy regulator
- [ 32.489631] lm75 1-0048: probe with driver lm75 failed with error -121
- [ 32.496833] lm75 1-0049: supply vs not found, using dummy regulator
- [ 82.890614] lm75 1-0049: probe with driver lm75 failed with error -121
-
-... and accessing the plugged-in SFP modules also does not work:
-
- [ 511.298537] sfp sfp-eth1: please wait, module slow to respond
- [ 536.488530] sfp sfp-eth0: please wait, module slow to respond
- ...
- [ 1065.688536] sfp sfp-eth1: failed to read EEPROM: -EREMOTEIO
- [ 1090.888532] sfp sfp-eth0: failed to read EEPROM: -EREMOTEIO
-
-After a discussion [1], there was an attempt to fix the problem by
-reverting the offending change by commit 7b211c767121 ("Revert "i2c:
-pxa: move to generic GPIO recovery""), but that only helped to fix
-the issue in the 6.1.y stable tree. The reason behind the partial succes
-is that there was another change in commit 20cb3fce4d60 ("i2c: Set i2c
-pinctrl recovery info from it's device pinctrl") in the 6.3-rc1 cycle
-which broke things further.
-
-The cause of the problem is the same in case of both offending commits
-mentioned above. Namely, the I2C core code changes the pinctrl state to
-GPIO while running the recovery initialization code. Although the PXA
-specific initialization also does this, but the key difference is that
-it happens before the conrtoller is getting enabled in i2c_pxa_reset(),
-whereas in the case of the generic initialization it happens after that.
-
-To resolve the problem, provide an empty init_recovery() callback
-function thus preventing the I2C core to call the generic recovery
-initialization code.
-
-As the result this change restores the original behaviour, which in
-turn makes the I2C communication to work again as it can be seen from
-the following log:
-
- [ 7.305277] i2c i2c-0: PXA I2C adapter
- [ 7.310198] i2c i2c-1: PXA I2C adapter
- [ 7.315012] sfp sfp-eth1: Host maximum power 3.0W
- [ 7.324061] lm75 1-0048: supply vs not found, using dummy regulator
- [ 7.331738] sfp sfp-eth0: Host maximum power 3.0W
- [ 7.337000] hwmon hwmon0: temp1_input not attached to any thermal zone
- [ 7.343593] lm75 1-0048: hwmon0: sensor 'tmp75c'
- [ 7.348526] lm75 1-0049: supply vs not found, using dummy regulator
- [ 7.356858] hwmon hwmon1: temp1_input not attached to any thermal zone
- [ 7.363463] lm75 1-0049: hwmon1: sensor 'tmp75c'
- ...
- [ 7.730315] sfp sfp-eth1: module Mikrotik S-RJ01 rev 1.0 sn 61B103C55C58 dc 201022
- [ 7.840318] sfp sfp-eth0: module MENTECHOPTO POS22-LDCC-KR rev 1.0 sn MNC208U90009 dc 200828
- [ 7.850083] mvneta d0030000.ethernet eth0: unsupported SFP module: no common interface modes
- [ 7.990335] hwmon hwmon2: temp1_input not attached to any thermal zone
-
-[1] https://lore.kernel.org/r/20230926160255.330417-1-robert.marko@sartura.hr
-
-Cc: stable@vger.kernel.org # 6.3+
-Fixes: 20cb3fce4d60 ("i2c: Set i2c pinctrl recovery info from it's device pinctrl")
-Signed-off-by: Gabor Juhos <j4g8y7@gmail.com>
-Signed-off-by: Imre Kaloz <kaloz@openwrt.org>
----
- drivers/i2c/busses/i2c-pxa.c | 7 +++++++
- 1 file changed, 7 insertions(+)
-
---- a/drivers/i2c/busses/i2c-pxa.c
-+++ b/drivers/i2c/busses/i2c-pxa.c
-@@ -1331,6 +1331,12 @@ static void i2c_pxa_unprepare_recovery(s
- i2c_pxa_enable(i2c);
- }
-
-+static int i2c_pxa_init_recovery_cb(struct i2c_adapter *adap)
-+{
-+ /* We have initialized everything already, so nothing to do here. */
-+ return 0;
-+}
-+
- static int i2c_pxa_init_recovery(struct pxa_i2c *i2c)
- {
- struct i2c_bus_recovery_info *bri = &i2c->recovery;
-@@ -1399,6 +1405,7 @@ static int i2c_pxa_init_recovery(struct
- return 0;
- }
-
-+ bri->init_recovery = i2c_pxa_init_recovery_cb;
- bri->prepare_recovery = i2c_pxa_prepare_recovery;
- bri->unprepare_recovery = i2c_pxa_unprepare_recovery;
- bri->recover_bus = i2c_generic_scl_recovery;
+++ /dev/null
-From patchwork Sun May 11 13:31:07 2025
-Content-Type: text/plain; charset="utf-8"
-MIME-Version: 1.0
-Content-Transfer-Encoding: 7bit
-X-Patchwork-Submitter: Gabor Juhos <j4g8y7@gmail.com>
-X-Patchwork-Id: 14084057
-Return-Path:
- <linux-arm-kernel-bounces+linux-arm-kernel=archiver.kernel.org@lists.infradead.org>
-X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on
- aws-us-west-2-korg-lkml-1.web.codeaurora.org
-Received: from bombadil.infradead.org (bombadil.infradead.org
- [198.137.202.133])
- (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits))
- (No client certificate requested)
- by smtp.lore.kernel.org (Postfix) with ESMTPS id E1323C3ABC3
- for <linux-arm-kernel@archiver.kernel.org>;
- Sun, 11 May 2025 13:39:50 +0000 (UTC)
-DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed;
- d=lists.infradead.org; s=bombadil.20210309; h=Sender:List-Subscribe:List-Help
- :List-Post:List-Archive:List-Unsubscribe:List-Id:Cc:To:In-Reply-To:References
- :Message-Id:Content-Transfer-Encoding:Content-Type:MIME-Version:Subject:Date:
- From:Reply-To:Content-ID:Content-Description:Resent-Date:Resent-From:
- Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID:List-Owner;
- bh=jCnitIQrLGML0v/ZGaz7m1EqKZaS5okX2gqREoZgqFA=; b=gjUPiYgZ9tzalLC8hdQSBahsEr
- yHwsfVsfhm/onN8EySOnZ9iefbvCDb/y94s1ll7+f7UeDZ3epWr0Kl1WPgPVKwCa7AIYumKWi/l4S
- rNqCfOmyVGO0CspTKlxvV/ZHk+jAJqKqHmd/QtVxJkMi4a9d1J8BMHWdltfYPvpkJkaKxx9WINZ5v
- BftwJv2+35b3ZWGRWYXmCBFUXaV/w6I0+dE51I25k4NZcTMRbR1sGYEXTm5Eu2KBtJ/UbhiKKCk0b
- +BTg/BJ/O7lSKl/W3DHnImYv6s65FMGxgHCaKZlN/IWCWLanqi2hg3hmGlDqyZUvezUrcuYEed2et
- w6FrGCGg==;
-Received: from localhost ([::1] helo=bombadil.infradead.org)
- by bombadil.infradead.org with esmtp (Exim 4.98.2 #2 (Red Hat Linux))
- id 1uE6u7-00000007H4F-41sI;
- Sun, 11 May 2025 13:39:43 +0000
-Received: from mail-ej1-x62e.google.com ([2a00:1450:4864:20::62e])
- by bombadil.infradead.org with esmtps (Exim 4.98.2 #2 (Red Hat Linux))
- id 1uE6mG-00000007GQ2-3M7w
- for linux-arm-kernel@lists.infradead.org;
- Sun, 11 May 2025 13:31:37 +0000
-Received: by mail-ej1-x62e.google.com with SMTP id
- a640c23a62f3a-ad1a87d93f7so576360566b.0
- for <linux-arm-kernel@lists.infradead.org>;
- Sun, 11 May 2025 06:31:36 -0700 (PDT)
-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;
- d=gmail.com; s=20230601; t=1746970295; x=1747575095;
- darn=lists.infradead.org;
- h=cc:to:in-reply-to:references:message-id:content-transfer-encoding
- :mime-version:subject:date:from:from:to:cc:subject:date:message-id
- :reply-to;
- bh=jCnitIQrLGML0v/ZGaz7m1EqKZaS5okX2gqREoZgqFA=;
- b=dFmhAe55mI0ZYgTbA9z06jAcGpibU1yIvZWl8vsEWd0Cow+yYkQlcsnlJcoq22vUt+
- An87/vI28iqdwgYZpu+vabyC6ytUUqI5IuqTOlv/QtLak3Oi6cbHjoebb9cVfGhTpO16
- gN8BLLH5Lf9shCAy3lLQahAf3jiE41hB3YKxIusuBnm+DIMYVGMJID5Pt72YgU322uXW
- hnMLbX75rnBu5DTGM12UT9+nOkBYjITOdnfEA+o3IVlzUEQRr/G7s9RNKTKnsjJDwaZb
- 91UdhJBE/yz2fiFrtN6PGr1B90kJd+GAxRjrbQ8UunMu6C4ggE6aRazKcsQEZIWLa6tR
- n/Vg==
-X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;
- d=1e100.net; s=20230601; t=1746970295; x=1747575095;
- h=cc:to:in-reply-to:references:message-id:content-transfer-encoding
- :mime-version:subject:date:from:x-gm-message-state:from:to:cc
- :subject:date:message-id:reply-to;
- bh=jCnitIQrLGML0v/ZGaz7m1EqKZaS5okX2gqREoZgqFA=;
- b=tlFtjTi8qCbMBXjF1lESzpnVH51JUuhJV5lirRh7dyHdWcVtBgKdnrbGpR+ZDHFvvV
- 1FJUR377ZyrZPwleAZ8bfUcjMx1NSDhxGsHI+05Mpt7LzRZCCUE/nuTHROytYhalDR35
- yh8YprIJwMhZ9zFNNXrZ/GQCPbRMA0fkOJYWCfMRPsf+VGHsDniGzF9Dbsy3aq0hG+mw
- 5r85WCALVE55cSW22kla9QPeOxpX6nheglHKz2svFKgo+nI9xXgcR2F9vsSah5DBTxkQ
- WK1fEkdtF+6If4W/u3cZ1kQTbkrifLbpV8i+z6VqFq+iRZuf9eZjyyuMXP53A7PQGpW3
- BKKA==
-X-Forwarded-Encrypted: i=1;
- AJvYcCWtVH3IlKfQOiuwxLM94ic5Bi0XTfGdTLWK5BWhh9b026VHto4M4zjf8wBmIUmekjUFrgWvBcW3eJkv9VuD0NNu@lists.infradead.org
-X-Gm-Message-State: AOJu0YxUrhwcNLQkyGCiTZ3Ci5GaLENJhrPiMNwOQ2HqRWQ5fFYTksDJ
- 6eb+myA0GGrJOmcEortiBND+ipH4vXcxwtKOVyNjgBBJMq27NJBTQ16Asw==
-X-Gm-Gg: ASbGncu1guGFlZm8KpI1qJ3LeFM8WO7ZvfanWSzK7lzUZES0sLxvvjBNy8SgeycdGUF
- QifE6Z+xBPK9i0m0CRfzWuNm9a/dr/W14EAzBiSWq5pOW0fbH1rGo/FFbaUZ8T7OroQ0wipdZ9J
- tIZJz+dbSRk4kRsYhhm4cJZh4GfKL8me6+8NM/db/a0oKAE0VMhHLmqDw0BFjgpwdWO4YnaUBLt
- MU5Pu+S3TKwVV7SHRma2c7662QZ2pxXIngJdGW2qnrr1TejUMaK39IRBPm7sgzBCntS8fd9uVdb
- 35NbstsZrboDcHBnDhFroXwe5QWwncAo1aJm/DVM39fE5OCOqNDp7iz6XZbPE/fzVdEDg00L9fR
- x8yAy
-X-Google-Smtp-Source:
- AGHT+IFtWSfALiUVVnpFFz7ImelE+nmATZJYkY40gYG/szbhM9XPyGAl4heVD8RQkSCE7sXqGFzn6Q==
-X-Received: by 2002:a17:907:d30e:b0:ad2:4cad:9824 with SMTP id
- a640c23a62f3a-ad24cad9bd2mr198358366b.28.1746970294838;
- Sun, 11 May 2025 06:31:34 -0700 (PDT)
-Received: from [192.168.0.253] (5D59A51C.catv.pool.telekom.hu. [93.89.165.28])
- by smtp.googlemail.com with ESMTPSA id
- a640c23a62f3a-ad2197bd398sm466765366b.152.2025.05.11.06.31.33
- (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256);
- Sun, 11 May 2025 06:31:34 -0700 (PDT)
-From: Gabor Juhos <j4g8y7@gmail.com>
-Date: Sun, 11 May 2025 15:31:07 +0200
-Subject: [PATCH 3/3] i2c: pxa: handle 'Early Bus Busy' condition on Armada
- 3700
-MIME-Version: 1.0
-Message-Id:
- <20250511-i2c-pxa-fix-i2c-communication-v1-3-e9097d09a015@gmail.com>
-References:
- <20250511-i2c-pxa-fix-i2c-communication-v1-0-e9097d09a015@gmail.com>
-In-Reply-To:
- <20250511-i2c-pxa-fix-i2c-communication-v1-0-e9097d09a015@gmail.com>
-To: Wolfram Sang <wsa@kernel.org>, Andi Shyti <andi.shyti@kernel.org>,
- Russell King <rmk+kernel@armlinux.org.uk>, Andrew Lunn <andrew@lunn.ch>
-Cc: Robert Marko <robert.marko@sartura.hr>,
- Linus Walleij <linus.walleij@linaro.org>,
- Russell King <rmk+kernel@armlinux.org.uk>, linux-i2c@vger.kernel.org,
- linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org,
- Gabor Juhos <j4g8y7@gmail.com>, Imre Kaloz <kaloz@openwrt.org>,
- stable@vger.kernel.org
-X-Mailer: b4 0.14.2
-X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3
-X-CRM114-CacheID: sfid-20250511_063136_845554_2BF89522
-X-CRM114-Status: GOOD ( 32.59 )
-X-BeenThere: linux-arm-kernel@lists.infradead.org
-X-Mailman-Version: 2.1.34
-Precedence: list
-List-Id: <linux-arm-kernel.lists.infradead.org>
-List-Unsubscribe:
- <http://lists.infradead.org/mailman/options/linux-arm-kernel>,
- <mailto:linux-arm-kernel-request@lists.infradead.org?subject=unsubscribe>
-List-Archive: <http://lists.infradead.org/pipermail/linux-arm-kernel/>
-List-Post: <mailto:linux-arm-kernel@lists.infradead.org>
-List-Help: <mailto:linux-arm-kernel-request@lists.infradead.org?subject=help>
-List-Subscribe:
- <http://lists.infradead.org/mailman/listinfo/linux-arm-kernel>,
- <mailto:linux-arm-kernel-request@lists.infradead.org?subject=subscribe>
-Sender: "linux-arm-kernel" <linux-arm-kernel-bounces@lists.infradead.org>
-Errors-To:
- linux-arm-kernel-bounces+linux-arm-kernel=archiver.kernel.org@lists.infradead.org
-
-Under some circumstances I2C recovery fails on Armada 3700. At least
-on the Methode uDPU board, removing and replugging an SFP module fails
-often, like this:
-
- [ 36.953127] sfp sfp-eth1: module removed
- [ 38.468549] i2c i2c-1: i2c_pxa: timeout waiting for bus free
- [ 38.486960] sfp sfp-eth1: module MENTECHOPTO POS22-LDCC-KR rev 1.0 sn MNC208U90009 dc 200828
- [ 38.496867] mvneta d0040000.ethernet eth1: unsupported SFP module: no common interface modes
- [ 38.521448] hwmon hwmon2: temp1_input not attached to any thermal zone
- [ 39.249196] sfp sfp-eth1: module removed
- ...
- [ 292.568799] sfp sfp-eth1: please wait, module slow to respond
- ...
- [ 625.208814] sfp sfp-eth1: failed to read EEPROM: -EREMOTEIO
-
-Note that the 'unsupported SFP module' messages are not relevant. The
-module is used only for testing the I2C recovery funcionality, because
-the error can be triggered easily with this specific one.
-
-Enabling debug in the i2c-pxa driver reveals the following:
-
- [ 82.034678] sfp sfp-eth1: module removed
- [ 90.008654] i2c i2c-1: slave_0x50 error: timeout with active message
- [ 90.015112] i2c i2c-1: msg_num: 2 msg_idx: 0 msg_ptr: 0
- [ 90.020464] i2c i2c-1: IBMR: 00000003 IDBR: 000000a0 ICR: 000007e0 ISR: 00000802
- [ 90.027906] i2c i2c-1: log:
- [ 90.030787]
-
-This continues until the retries are exhausted ...
-
- [ 110.192489] i2c i2c-1: slave_0x50 error: exhausted retries
- [ 110.198012] i2c i2c-1: msg_num: 2 msg_idx: 0 msg_ptr: 0
- [ 110.203323] i2c i2c-1: IBMR: 00000003 IDBR: 000000a0 ICR: 000007e0 ISR: 00000802
- [ 110.210810] i2c i2c-1: log:
- [ 110.213633]
-
-... then the whole sequence starts again ...
-
- [ 115.368641] i2c i2c-1: slave_0x50 error: timeout with active message
-
-... while finally the SFP core gives up:
-
- [ 671.975258] sfp sfp-eth1: failed to read EEPROM: -EREMOTEIO
-
-When we analyze the log, it can be seen that bit 1 and 11 is set in the
-ISR (Interface Status Register). Bit 1 indicates the ACK/NACK status, but
-the purpose of bit 11 is not documented in the driver code unfortunately.
-
-The 'Functional Specification' document of the Armada 3700 SoCs family
-however says that this bit indicates an 'Early Bus Busy' condition. The
-document also notes that whenever this bit is set, it is not possible to
-initiate a transaction on the I2C bus. The observed behaviour corresponds
-to this statement.
-
-Unfortunately, I2C recovery does not help as it never runs in this
-special case. Although the driver checks the busyness of the bus at
-several places, but since it does not consider the A3700 specific bit
-in these checks it can't determine the actual status of the bus correctly
-which results in the errors above.
-
-In order to fix the problem, add a new member to struct 'i2c_pxa' to
-store a controller specific bitmask containing the bits indicating the
-busy status, and use that in the code while checking the actual status
-of the bus. This ensures that the correct status can be determined on
-the Armada 3700 based devices without causing functional changes on
-devices based on other SoCs.
-
-With the change applied, the driver detects the busy condition, and runs
-the recovery process:
-
- [ 742.617312] i2c i2c-1: state:i2c_pxa_wait_bus_not_busy:449: ISR=00000802, ICR=000007e0, IBMR=03
- [ 742.626099] i2c i2c-1: i2c_pxa: timeout waiting for bus free
- [ 742.631933] i2c i2c-1: recovery: resetting controller, ISR=0x00000802
- [ 742.638421] i2c i2c-1: recovery: IBMR 0x00000003 ISR 0x00000000
-
-This clears the EBB bit in the ISR register, so it makes it possible to
-initiate transactions on the I2C bus again.
-
-After this patch, the SFP module used for testing can be removed and
-replugged numerous times without causing the error described at the
-beginning. Previously, the error happened after a few such attempts.
-
-The patch has been tested also with the following kernel versions:
-5.10.237, 5.15.182, 6.1.138, 6.6.90, 6.12.28, 6.14.6. It improves
-recoverabilty on all of them.
-
-Cc: stable@vger.kernel.org # 5.8+
-Fixes: 7c9ec2c52518 ("i2c: pxa: implement generic i2c bus recovery")
-Signed-off-by: Gabor Juhos <j4g8y7@gmail.com>
-Signed-off-by: Imre Kaloz <kaloz@openwrt.org>
----
-Note: the patch is included in this series for completeness however
-it can be applied independently from the preceding patches. On kernels
-6.3+, it restores I2C functionality even in itself because it recovers
-the controller from the bad state described in the previous patch.
----
- drivers/i2c/busses/i2c-pxa.c | 18 ++++++++++++------
- 1 file changed, 12 insertions(+), 6 deletions(-)
-
---- a/drivers/i2c/busses/i2c-pxa.c
-+++ b/drivers/i2c/busses/i2c-pxa.c
-@@ -70,6 +70,7 @@
- #define ISR_GCAD (1 << 8) /* general call address detected */
- #define ISR_SAD (1 << 9) /* slave address detected */
- #define ISR_BED (1 << 10) /* bus error no ACK/NAK */
-+#define ISR_A3700_EBB (1 << 11) /* early bus busy for armada 3700 */
-
- #define ILCR_SLV_SHIFT 0
- #define ILCR_SLV_MASK (0x1FF << ILCR_SLV_SHIFT)
-@@ -262,6 +263,7 @@ struct pxa_i2c {
- bool highmode_enter;
- u32 fm_mask;
- u32 hs_mask;
-+ u32 busy_mask;
-
- struct i2c_bus_recovery_info recovery;
- struct pinctrl *pinctrl;
-@@ -428,7 +430,7 @@ static int i2c_pxa_wait_bus_not_busy(str
-
- while (1) {
- isr = readl(_ISR(i2c));
-- if (!(isr & (ISR_IBB | ISR_UB)))
-+ if (!(isr & i2c->busy_mask))
- return 0;
-
- if (isr & ISR_SAD)
-@@ -465,7 +467,7 @@ static int i2c_pxa_wait_master(struct px
- * quick check of the i2c lines themselves to ensure they've
- * gone high...
- */
-- if ((readl(_ISR(i2c)) & (ISR_UB | ISR_IBB)) == 0 &&
-+ if ((readl(_ISR(i2c)) & i2c->busy_mask) == 0 &&
- readl(_IBMR(i2c)) == (IBMR_SCLS | IBMR_SDAS)) {
- if (i2c_debug > 0)
- dev_dbg(&i2c->adap.dev, "%s: done\n", __func__);
-@@ -486,7 +488,7 @@ static int i2c_pxa_set_master(struct pxa
- if (i2c_debug)
- dev_dbg(&i2c->adap.dev, "setting to bus master\n");
-
-- if ((readl(_ISR(i2c)) & (ISR_UB | ISR_IBB)) != 0) {
-+ if ((readl(_ISR(i2c)) & i2c->busy_mask) != 0) {
- dev_dbg(&i2c->adap.dev, "%s: unit is busy\n", __func__);
- if (!i2c_pxa_wait_master(i2c)) {
- dev_dbg(&i2c->adap.dev, "%s: error: unit busy\n", __func__);
-@@ -512,7 +514,7 @@ static int i2c_pxa_wait_slave(struct pxa
- dev_dbg(&i2c->adap.dev, "%s: %ld: ISR=%08x, ICR=%08x, IBMR=%02x\n",
- __func__, (long)jiffies, readl(_ISR(i2c)), readl(_ICR(i2c)), readl(_IBMR(i2c)));
-
-- if ((readl(_ISR(i2c)) & (ISR_UB|ISR_IBB)) == 0 ||
-+ if ((readl(_ISR(i2c)) & i2c->busy_mask) == 0 ||
- (readl(_ISR(i2c)) & ISR_SAD) != 0 ||
- (readl(_ICR(i2c)) & ICR_SCLE) == 0) {
- if (i2c_debug > 1)
-@@ -1170,7 +1172,7 @@ static int i2c_pxa_pio_set_master(struct
- /*
- * Wait for the bus to become free.
- */
-- while (timeout-- && readl(_ISR(i2c)) & (ISR_IBB | ISR_UB))
-+ while (timeout-- && readl(_ISR(i2c)) & i2c->busy_mask)
- udelay(1000);
-
- if (timeout < 0) {
-@@ -1317,7 +1319,7 @@ static void i2c_pxa_unprepare_recovery(s
- * handing control of the bus back to avoid the bus changing state.
- */
- isr = readl(_ISR(i2c));
-- if (isr & (ISR_UB | ISR_IBB)) {
-+ if (isr & i2c->busy_mask) {
- dev_dbg(&i2c->adap.dev,
- "recovery: resetting controller, ISR=0x%08x\n", isr);
- i2c_pxa_do_reset(i2c);
-@@ -1481,6 +1483,10 @@ static int i2c_pxa_probe(struct platform
- i2c->fm_mask = pxa_reg_layout[i2c_type].fm;
- i2c->hs_mask = pxa_reg_layout[i2c_type].hs;
-
-+ i2c->busy_mask = ISR_UB | ISR_IBB;
-+ if (i2c_type == REGS_A3700)
-+ i2c->busy_mask |= ISR_A3700_EBB;
-+
- if (i2c_type != REGS_CE4100)
- i2c->reg_isar = i2c->reg_base + pxa_reg_layout[i2c_type].isar;
-
+++ /dev/null
-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@sartura.hr>
-Signed-off-by: Pavo Banicevic <pavo.banicevic@sartura.hr>
-Cc: Luka Perkov <luka.perkov@sartura.hr>
-Cc: Robert Marko <robert.marko@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@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@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@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@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@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@0 {
-+ #cooling-cells = <2>;
-+ reg = <0x00>;
-+ cooling-levels = <64 102 170 230 250>;
-+ };
-+
-+ fan-group@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
-@@ -611,6 +611,8 @@ patternProperties:
- description: IC Plus Corp.
- "^idt,.*":
- description: Integrated Device Technologies, Inc.
-+ "^iei,.*":
-+ description: IEI Integration Corp.
- "^ifi,.*":
- description: Ingenieurburo Fur Ic-Technologie (I/F/I)
- "^ilitek,.*":
+++ /dev/null
-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@sartura.hr>
-Signed-off-by: Pavo Banicevic <pavo.banicevic@sartura.hr>
-Cc: Luka Perkov <luka.perkov@sartura.hr>
-Cc: Robert Marko <robert.marko@sartura.hr>
----
- drivers/mfd/Kconfig | 9 +
- drivers/mfd/Makefile | 1 +
- drivers/mfd/iei-wt61p803-puzzle.c | 908 ++++++++++++++++++++++++
- include/linux/mfd/iei-wt61p803-puzzle.h | 66 ++
- 4 files changed, 984 insertions(+)
- create mode 100644 drivers/mfd/iei-wt61p803-puzzle.c
- create mode 100644 include/linux/mfd/iei-wt61p803-puzzle.h
-
---- a/drivers/mfd/Kconfig
-+++ b/drivers/mfd/Kconfig
-@@ -2274,6 +2274,15 @@ config SGI_MFD_IOC3
- 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_CORE
- tristate
- select MFD_CORE
---- a/drivers/mfd/Makefile
-+++ b/drivers/mfd/Makefile
-@@ -244,6 +244,7 @@ obj-$(CONFIG_MFD_RT4831) += rt4831.o
- obj-$(CONFIG_MFD_RT5033) += rt5033.o
- obj-$(CONFIG_MFD_RT5120) += rt5120.o
- obj-$(CONFIG_MFD_SKY81452) += sky81452.o
-+obj-$(CONFIG_MFD_IEI_WT61P803_PUZZLE) += iei-wt61p803-puzzle.o
-
- obj-$(CONFIG_INTEL_SOC_PMIC) += intel_soc_pmic_crc.o
- obj-$(CONFIG_INTEL_SOC_PMIC_BXTWC) += intel_soc_pmic_bxtwc.o
---- /dev/null
-+++ b/drivers/mfd/iei-wt61p803-puzzle.c
-@@ -0,0 +1,912 @@
-+// 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@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);
-+ ret = devm_mutex_init(dev, &mcu->reply_lock);
-+ if (ret)
-+ return ret;
-+ ret = devm_mutex_init(dev, &mcu->lock);
-+ if (ret)
-+ return ret;
-+
-+ /* 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@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@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_ */
+++ /dev/null
-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@sartura.hr>
-Signed-off-by: Pavo Banicevic <pavo.banicevic@sartura.hr>
-Acked-by: Guenter Roeck <linux@roeck-us.net>
-Cc: Luka Perkov <luka.perkov@sartura.hr>
-Cc: Robert Marko <robert.marko@sartura.hr>
----
- drivers/hwmon/Kconfig | 8 +
- drivers/hwmon/Makefile | 1 +
- drivers/hwmon/iei-wt61p803-puzzle-hwmon.c | 445 ++++++++++++++++++++++
- 3 files changed, 454 insertions(+)
- create mode 100644 drivers/hwmon/iei-wt61p803-puzzle-hwmon.c
-
---- a/drivers/hwmon/Kconfig
-+++ b/drivers/hwmon/Kconfig
-@@ -785,6 +785,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
-@@ -90,6 +90,7 @@ obj-$(CONFIG_SENSORS_HS3001) += hs3001.o
- obj-$(CONFIG_SENSORS_ULTRA45) += ultra45_env.o
- obj-$(CONFIG_SENSORS_I5500) += i5500_temp.o
- obj-$(CONFIG_SENSORS_I5K_AMB) += i5k_amb.o
-+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,447 @@
-+// SPDX-License-Identifier: GPL-2.0-only
-+/* IEI WT61P803 PUZZLE MCU HWMON Driver
-+ *
-+ * Copyright (C) 2020 Sartura Ltd.
-+ * Author: Luka Kovacic <luka.kovacic@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);
-+ ret = devm_mutex_init(dev, &mcu_hwmon->lock);
-+ if (ret)
-+ return ret;
-+
-+ 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@sartura.hr>");
-+MODULE_LICENSE("GPL v2");
+++ /dev/null
-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@sartura.hr>
-Signed-off-by: Pavo Banicevic <pavo.banicevic@sartura.hr>
-Cc: Luka Perkov <luka.perkov@sartura.hr>
-Cc: Robert Marko <robert.marko@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
-@@ -316,6 +316,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
-@@ -34,6 +34,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@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@sartura.hr>");
-+MODULE_LICENSE("GPL v2");
-+MODULE_ALIAS("platform:leds-iei-wt61p803-puzzle");
+++ /dev/null
-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@sartura.hr>
-Signed-off-by: Pavo Banicevic <pavo.banicevic@sartura.hr>
-Cc: Luka Perkov <luka.perkov@sartura.hr>
-Cc: Robert Marko <robert.marko@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@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@sartura.hr>
-+Description: (RW) Internal factory assigned serial number
-+
-+What: /sys/bus/serial/devices/.../version
-+Date: September 2020
-+Contact: Luka Kovacic <luka.kovacic@sartura.hr>
-+Description: (RO) Internal MCU firmware version
-+
-+What: /sys/bus/serial/devices/.../protocol_version
-+Date: September 2020
-+Contact: Luka Kovacic <luka.kovacic@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@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@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@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@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@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
+++ /dev/null
-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@sartura.hr>
-Signed-off-by: Pavo Banicevic <pavo.banicevic@sartura.hr>
-Cc: Luka Perkov <luka.perkov@sartura.hr>
-Cc: Robert Marko <robert.marko@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@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
-@@ -82,6 +82,7 @@ Hardware Monitoring Kernel Drivers
- ibmaem
- ibm-cffps
- ibmpowernv
-+ iei-wt61p803-puzzle-hwmon
- ina209
- ina2xx
- ina238
+++ /dev/null
-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@sartura.hr>
-Signed-off-by: Pavo Banicevic <pavo.banicevic@sartura.hr>
-Cc: Luka Perkov <luka.perkov@sartura.hr>
-Cc: Robert Marko <robert.marko@sartura.hr>
----
- MAINTAINERS | 16 ++++++++++++++++
- 1 file changed, 16 insertions(+)
-
---- a/MAINTAINERS
-+++ b/MAINTAINERS
-@@ -10143,6 +10143,22 @@ IFCVF VIRTIO DATA PATH ACCELERATOR
- R: Zhu Lingshan <lingshan.zhu@intel.com>
- F: drivers/vdpa/ifcvf/
-
-+IEI WT61P803 M801 MFD DRIVER
-+M: Luka Kovacic <luka.kovacic@sartura.hr>
-+M: Luka Perkov <luka.perkov@sartura.hr>
-+M: Goran Medic <goran.medic@sartura.hr>
-+L: linux-kernel@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@gmail.com>
- M: Jamal Hadi Salim <jhs@mojatatu.com>
+++ /dev/null
---- 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,168 @@ 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;
-+ }
-+
-+ ret = devm_mutex_init(dev, &priv->lock);
-+ if (ret)
-+ goto put_child_node;
-+
-+ 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;
+++ /dev/null
---- 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);
+++ /dev/null
---- a/drivers/hwmon/iei-wt61p803-puzzle-hwmon.c
-+++ b/drivers/hwmon/iei-wt61p803-puzzle-hwmon.c
-@@ -251,6 +251,7 @@ static const struct hwmon_ops iei_wt61p8
- };
-
- static const struct hwmon_channel_info *iei_wt61p803_puzzle_info[] = {
-+ HWMON_CHANNEL_INFO(chip, HWMON_C_REGISTER_TZ),
- HWMON_CHANNEL_INFO(pwm,
- HWMON_PWM_INPUT,
- HWMON_PWM_INPUT),