From 2432dcfb4162f9cbaa816e7de3086d5a22ffea88 Mon Sep 17 00:00:00 2001 From: Weiping Yang Date: Mon, 27 Feb 2023 01:50:38 -0500 Subject: [PATCH] 22.03.2:support mv1000 --- ...031-modify-image-rootfs-size-to-512M.patch | 25 + ...-add-mv1000-uboot-envtools-partition.patch | 26 + .../1002-adapt-to-mv1000-usb-devise.patch | 25 + .../3005-mvebu-adjust-01-leds.patch | 37 + .../3006-mvebu-adjust-02_network.patch | 72 + .../3007-mvebu-adjust-usb-devise.patch | 500 ++ .../3008-mvebu-adjust-platform-sh.patch | 40 + ...st-emmc-img-and-add-usb-devise-drive.patch | 4856 +++++++++++++++++ .../3010-update-mvebu-fwtools-script.patch | 82 + .../3011-mvebu-adjust-cortexa53.patch | 33 + .../4005-support-mv1000-target.patch | 112 + 11 files changed, 5808 insertions(+) create mode 100644 patches-22.03.2/0031-modify-image-rootfs-size-to-512M.patch create mode 100644 patches-22.03.2/1001-add-mv1000-uboot-envtools-partition.patch create mode 100644 patches-22.03.2/1002-adapt-to-mv1000-usb-devise.patch create mode 100644 patches-22.03.2/3005-mvebu-adjust-01-leds.patch create mode 100644 patches-22.03.2/3006-mvebu-adjust-02_network.patch create mode 100644 patches-22.03.2/3007-mvebu-adjust-usb-devise.patch create mode 100644 patches-22.03.2/3008-mvebu-adjust-platform-sh.patch create mode 100644 patches-22.03.2/3009-mvebu-adjust-emmc-img-and-add-usb-devise-drive.patch create mode 100644 patches-22.03.2/3010-update-mvebu-fwtools-script.patch create mode 100644 patches-22.03.2/3011-mvebu-adjust-cortexa53.patch create mode 100644 patches-22.03.2/4005-support-mv1000-target.patch diff --git a/patches-22.03.2/0031-modify-image-rootfs-size-to-512M.patch b/patches-22.03.2/0031-modify-image-rootfs-size-to-512M.patch new file mode 100644 index 0000000..4171390 --- /dev/null +++ b/patches-22.03.2/0031-modify-image-rootfs-size-to-512M.patch @@ -0,0 +1,25 @@ +From d9a02dfb7e3589f813ccb9f0508af2111db81f6d Mon Sep 17 00:00:00 2001 +From: Weiping Yang +Date: Sun, 26 Feb 2023 23:29:38 -0500 +Subject: [PATCH] modify image rootfs size to 512M + +--- + config/Config-images.in | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/config/Config-images.in b/config/Config-images.in +index dcd7575c3e..b676e0f018 100644 +--- a/config/Config-images.in ++++ b/config/Config-images.in +@@ -297,7 +297,7 @@ menu "Target Images" + config TARGET_ROOTFS_PARTSIZE + int "Root filesystem partition size (in MiB)" + depends on USES_ROOTFS_PART || TARGET_ROOTFS_EXT4FS +- default 104 ++ default 512 + help + Select the root filesystem partition size. + +-- +2.17.1 + diff --git a/patches-22.03.2/1001-add-mv1000-uboot-envtools-partition.patch b/patches-22.03.2/1001-add-mv1000-uboot-envtools-partition.patch new file mode 100644 index 0000000..471677f --- /dev/null +++ b/patches-22.03.2/1001-add-mv1000-uboot-envtools-partition.patch @@ -0,0 +1,26 @@ +From 25f925b53c5c772a125b89b373b04a3e982ec422 Mon Sep 17 00:00:00 2001 +From: Weiping Yang +Date: Sun, 26 Feb 2023 23:31:47 -0500 +Subject: [PATCH] add mv1000 uboot-envtools + +--- + package/boot/uboot-envtools/files/mvebu | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/package/boot/uboot-envtools/files/mvebu b/package/boot/uboot-envtools/files/mvebu +index cffd396ae1..757b69779b 100644 +--- a/package/boot/uboot-envtools/files/mvebu ++++ b/package/boot/uboot-envtools/files/mvebu +@@ -26,7 +26,8 @@ cznic,turris-omnia) + ubootenv_add_uci_config "/dev/mtd0" "0xf0000" "0x10000" "0x10000" + fi + ;; +-glinet,gl-mv1000) ++glinet,gl-mv1000|\ ++gl-mv1000) + ubootenv_add_uci_config "/dev/mtd1" "0x0" "0x8000" "0x8000" "1" + ;; + globalscale,espressobin|\ +-- +2.17.1 + diff --git a/patches-22.03.2/1002-adapt-to-mv1000-usb-devise.patch b/patches-22.03.2/1002-adapt-to-mv1000-usb-devise.patch new file mode 100644 index 0000000..7076336 --- /dev/null +++ b/patches-22.03.2/1002-adapt-to-mv1000-usb-devise.patch @@ -0,0 +1,25 @@ +From b8b39a3904c5b686b3ff3b5808459d608f46aec5 Mon Sep 17 00:00:00 2001 +From: Weiping Yang +Date: Mon, 27 Feb 2023 01:22:07 -0500 +Subject: [PATCH] adapt-to-mv1000-usb-devise + +--- + package/kernel/linux/modules/usb.mk | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/package/kernel/linux/modules/usb.mk b/package/kernel/linux/modules/usb.mk +index 1fc3541f10..763b6fced3 100644 +--- a/package/kernel/linux/modules/usb.mk ++++ b/package/kernel/linux/modules/usb.mk +@@ -169,7 +169,7 @@ define KernelPackage/usb-gadget-eth + $(LINUX_DIR)/drivers/usb/gadget/function/usb_f_ecm_subset.ko \ + $(LINUX_DIR)/drivers/usb/gadget/function/usb_f_rndis.ko \ + $(LINUX_DIR)/drivers/usb/gadget/legacy/g_ether.ko +- AUTOLOAD:=$(call AutoLoad,52,usb_f_ecm) ++ AUTOLOAD:=$(call AutoLoad,52,usb_f_ecm g_ether) + $(call AddDepends/usb) + endef + +-- +2.17.1 + diff --git a/patches-22.03.2/3005-mvebu-adjust-01-leds.patch b/patches-22.03.2/3005-mvebu-adjust-01-leds.patch new file mode 100644 index 0000000..8537d3f --- /dev/null +++ b/patches-22.03.2/3005-mvebu-adjust-01-leds.patch @@ -0,0 +1,37 @@ +From 1592185f17d4f13518d95a24b9289aab374540af Mon Sep 17 00:00:00 2001 +From: Weiping Yang +Date: Sat, 25 Feb 2023 04:16:01 -0500 +Subject: [PATCH] mvebu-adjust-01-leds + +--- + .../linux/mvebu/cortexa53/base-files/etc/board.d/01_leds | 7 +++++-- + 1 file changed, 5 insertions(+), 2 deletions(-) + mode change 100644 => 100755 target/linux/mvebu/cortexa53/base-files/etc/board.d/01_leds + +diff --git a/target/linux/mvebu/cortexa53/base-files/etc/board.d/01_leds b/target/linux/mvebu/cortexa53/base-files/etc/board.d/01_leds +old mode 100644 +new mode 100755 +index 4dce204f14..0f7b53a8dd +--- a/target/linux/mvebu/cortexa53/base-files/etc/board.d/01_leds ++++ b/target/linux/mvebu/cortexa53/base-files/etc/board.d/01_leds +@@ -1,3 +1,5 @@ ++#!/bin/sh ++# + # SPDX-License-Identifier: GPL-2.0-only + + . /lib/functions/uci-defaults.sh +@@ -7,8 +9,9 @@ board_config_update + board=$(board_name) + + case "$board" in +-glinet,gl-mv1000) +- ucidef_set_led_netdev "wan" "WAN" "green:wan" "wan" ++glinet,gl-mv1000|\ ++gl-mv1000) ++ ucidef_set_led_netdev "wan" "WAN" "gl-mv1000:white:wan" "wan" + ;; + esac + +-- +2.17.1 + diff --git a/patches-22.03.2/3006-mvebu-adjust-02_network.patch b/patches-22.03.2/3006-mvebu-adjust-02_network.patch new file mode 100644 index 0000000..347e064 --- /dev/null +++ b/patches-22.03.2/3006-mvebu-adjust-02_network.patch @@ -0,0 +1,72 @@ +From a70578eadae572187daab68d6e7318f10daeb140 Mon Sep 17 00:00:00 2001 +From: Weiping Yang +Date: Sun, 26 Feb 2023 22:44:41 -0500 +Subject: [PATCH] mvebu-adjust-02_network + +--- + .../base-files/etc/board.d/02_network | 25 ++++++++++++++++++- + 1 file changed, 24 insertions(+), 1 deletion(-) + mode change 100644 => 100755 target/linux/mvebu/cortexa53/base-files/etc/board.d/02_network + +diff --git a/target/linux/mvebu/cortexa53/base-files/etc/board.d/02_network b/target/linux/mvebu/cortexa53/base-files/etc/board.d/02_network +old mode 100644 +new mode 100755 +index 7da9de58c7..441fc286e8 +--- a/target/linux/mvebu/cortexa53/base-files/etc/board.d/02_network ++++ b/target/linux/mvebu/cortexa53/base-files/etc/board.d/02_network +@@ -1,9 +1,12 @@ ++#!/bin/sh + # + # Copyright (C) 2014-2016 OpenWrt.org + # Copyright (C) 2016 LEDE-Project.org + # + ++. /lib/functions.sh + . /lib/functions/uci-defaults.sh ++. /lib/functions/system.sh + + board_config_update + +@@ -11,11 +14,12 @@ board=$(board_name) + + case "$board" in + glinet,gl-mv1000|\ ++gl-mv1000|\ + globalscale,espressobin|\ + globalscale,espressobin-emmc|\ + globalscale,espressobin-v7|\ + globalscale,espressobin-v7-emmc) +- ucidef_set_interfaces_lan_wan "lan0 lan1" "wan" ++ ucidef_set_interfaces_lan_wan "lan0 lan1 usb0" "wan" + ;; + globalscale,espressobin-ultra) + ucidef_set_interfaces_lan_wan "lan0 lan1 lan2 lan3" "wan" +@@ -29,6 +33,25 @@ methode,udpu) + ;; + esac + ++mvebu_setup_macs() ++{ ++ local board="$1" ++ local lan_mac="" ++ local wan_mac="" ++ ++ case "$board" in ++ glinet,gl-mv1000|\ ++ gl-mv1000) ++ wan_mac=$(mtd_get_mac_binary "factory" 0) ++ lan_mac=$(macaddr_add "$wan_mac" 1) ++ ;; ++ esac ++ ++ [ -n "$lan_mac" ] && ucidef_set_interface_macaddr "lan" $lan_mac ++ [ -n "$wan_mac" ] && ucidef_set_interface_macaddr "wan" $wan_mac ++} ++ ++mvebu_setup_macs $board + board_config_flush + + exit 0 +-- +2.17.1 + diff --git a/patches-22.03.2/3007-mvebu-adjust-usb-devise.patch b/patches-22.03.2/3007-mvebu-adjust-usb-devise.patch new file mode 100644 index 0000000..a4bad65 --- /dev/null +++ b/patches-22.03.2/3007-mvebu-adjust-usb-devise.patch @@ -0,0 +1,500 @@ +From 1aa3f596fc06cfcf302e3c4aa718447abc958196 Mon Sep 17 00:00:00 2001 +From: Weiping Yang +Date: Mon, 27 Feb 2023 01:28:15 -0500 +Subject: [PATCH] mvebu-adjust-usb-devise + +--- + target/linux/mvebu/Makefile | 2 +- + target/linux/mvebu/config-5.10 | 223 +++++++++++++-------------------- + 2 files changed, 86 insertions(+), 139 deletions(-) + +diff --git a/target/linux/mvebu/Makefile b/target/linux/mvebu/Makefile +index ce43c8652d..4b17f74e5d 100644 +--- a/target/linux/mvebu/Makefile ++++ b/target/linux/mvebu/Makefile +@@ -6,7 +6,7 @@ include $(TOPDIR)/rules.mk + + BOARD:=mvebu + BOARDNAME:=Marvell EBU Armada +-FEATURES:=fpu usb pci pcie gpio nand squashfs ramdisk boot-part rootfs-part legacy-sdcard targz ++FEATURES:=fpu usb pci pcie gpio nand squashfs ramdisk boot-part rootfs-part legacy-sdcard targz usbgadget + SUBTARGETS:=cortexa9 cortexa53 cortexa72 + + KERNEL_PATCHVER:=5.10 +diff --git a/target/linux/mvebu/config-5.10 b/target/linux/mvebu/config-5.10 +index 0bce22daee..46ec8b1d26 100644 +--- a/target/linux/mvebu/config-5.10 ++++ b/target/linux/mvebu/config-5.10 +@@ -1,81 +1,63 @@ ++CONFIG_64BIT=y + CONFIG_AHCI_MVEBU=y +-CONFIG_ALIGNMENT_TRAP=y +-CONFIG_ARCH_32BIT_OFF_T=y ++CONFIG_ARCH_DMA_ADDR_T_64BIT=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_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_MVEBU=y +-CONFIG_ARCH_NR_GPIO=0 +-CONFIG_ARCH_OPTIONAL_KERNEL_RWX=y +-CONFIG_ARCH_OPTIONAL_KERNEL_RWX_DEFAULT=y ++CONFIG_ARCH_PROC_KCORE_TEXT=y + CONFIG_ARCH_SELECT_MEMORY_MODEL=y ++CONFIG_ARCH_SPARSEMEM_DEFAULT=y + CONFIG_ARCH_SPARSEMEM_ENABLE=y ++CONFIG_ARCH_STACKWALK=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_ARM64=y ++CONFIG_ARM64_4K_PAGES=y ++CONFIG_ARM64_PAGE_SHIFT=12 ++CONFIG_ARM64_PA_BITS=48 ++CONFIG_ARM64_PA_BITS_48=y ++CONFIG_ARM64_PTR_AUTH=y ++CONFIG_ARM64_SVE=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_WATCHDOG is not set +-CONFIG_ARMADA_38X_CLK=y ++CONFIG_ARMADA_AP806_SYSCON=y ++CONFIG_ARMADA_AP_CP_HELPER=y ++CONFIG_ARMADA_CP110_SYSCON=y + CONFIG_ARMADA_THERMAL=y +-CONFIG_ARMADA_XP_CLK=y +-CONFIG_ARM_APPENDED_DTB=y ++CONFIG_ARM_AMBA=y + CONFIG_ARM_ARCH_TIMER=y + CONFIG_ARM_ARCH_TIMER_EVTSTREAM=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_CRYPTO=y +-CONFIG_ARM_ERRATA_720789=y +-CONFIG_ARM_ERRATA_764369=y + CONFIG_ARM_GIC=y +-CONFIG_ARM_GLOBAL_TIMER=y +-CONFIG_ARM_HAS_SG_CHAIN=y +-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_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_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_AUDIT_ARCH_COMPAT_GENERIC=y + CONFIG_BLK_DEV_LOOP=y + CONFIG_BLK_DEV_NVME=y + CONFIG_BLK_DEV_SD=y + CONFIG_BLK_MQ_PCI=y + CONFIG_BLK_SCSI_REQUEST=y + CONFIG_BOUNCE=y +-# CONFIG_CACHE_FEROCEON_L2 is not set +-CONFIG_CACHE_L2X0=y ++CONFIG_CC_HAVE_STACKPROTECTOR_SYSREG=y + CONFIG_CLKDEV_LOOKUP=y +-CONFIG_CLKSRC_ARM_GLOBAL_TIMER_SCHED_CLOCK=y +-CONFIG_CLKSRC_MMIO=y + CONFIG_CLONE_BACKWARDS=y + CONFIG_COMMON_CLK=y + CONFIG_COMPAT_32BIT_TIME=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 +@@ -87,22 +69,12 @@ 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_ACOMP2=y +-CONFIG_CRYPTO_AES_ARM=y +-CONFIG_CRYPTO_AES_ARM_BS=y + CONFIG_CRYPTO_AUTHENC=y + CONFIG_CRYPTO_CRC32=y + CONFIG_CRYPTO_CRC32C=y +@@ -114,54 +86,40 @@ 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_LZO=y +-CONFIG_CRYPTO_NULL=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_UNCOMPRESS=y +-CONFIG_DEBUG_USER=y + CONFIG_DMADEVICES=y ++CONFIG_DMA_DIRECT_REMAP=y + CONFIG_DMA_ENGINE=y + CONFIG_DMA_ENGINE_RAID=y + CONFIG_DMA_OF=y +-CONFIG_DMA_OPS=y + CONFIG_DMA_REMAP=y + CONFIG_DTC=y +-CONFIG_EARLY_PRINTK=y +-CONFIG_EDAC_ATOMIC_SCRUB=y + CONFIG_EDAC_SUPPORT=y + CONFIG_EXT4_FS=y + CONFIG_EXTCON=y + CONFIG_F2FS_FS=y + CONFIG_FIXED_PHY=y + CONFIG_FIX_EARLYCON_MEM=y ++CONFIG_FRAME_POINTER=y + CONFIG_FS_IOMAP=y + CONFIG_FS_MBCACHE=y + CONFIG_FW_LOADER_PAGED_BUF=y + CONFIG_GENERIC_ALLOCATOR=y + CONFIG_GENERIC_ARCH_TOPOLOGY=y + CONFIG_GENERIC_BUG=y ++CONFIG_GENERIC_BUG_RELATIVE_POINTERS=y + CONFIG_GENERIC_CLOCKEVENTS=y + CONFIG_GENERIC_CLOCKEVENTS_BROADCAST=y + CONFIG_GENERIC_CPU_AUTOPROBE=y ++CONFIG_GENERIC_CPU_VULNERABILITIES=y ++CONFIG_GENERIC_CSUM=y + CONFIG_GENERIC_EARLY_IOREMAP=y + CONFIG_GENERIC_GETTIMEOFDAY=y + CONFIG_GENERIC_IDLE_POLL_SETUP=y +@@ -175,12 +133,12 @@ CONFIG_GENERIC_MSI_IRQ=y + CONFIG_GENERIC_MSI_IRQ_DOMAIN=y + CONFIG_GENERIC_PCI_IOMAP=y + CONFIG_GENERIC_PHY=y ++CONFIG_GENERIC_PINCONF=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=y + CONFIG_GPIOLIB_IRQCHIP=y +@@ -191,32 +149,30 @@ CONFIG_GPIO_PCA953X=y + CONFIG_GPIO_PCA953X_IRQ=y + CONFIG_GRO_CELLS=y + CONFIG_HANDLE_DOMAIN_IRQ=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_HOLES_IN_ZONE=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_ILLEGAL_POINTER_VALUE=0xdead000000000000 + CONFIG_INITRAMFS_SOURCE="" ++CONFIG_INPUT=y ++CONFIG_INPUT_KEYBOARD=y ++CONFIG_INPUT_POLLDEV=y + 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_LEDS_GPIO=y + CONFIG_LEDS_PCA963X=y +@@ -227,32 +183,24 @@ 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_MFD_SYSCON=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_MODULES_USE_ELF_RELA=y + CONFIG_MTD_CFI_STAA=y + CONFIG_MTD_NAND_CORE=y + CONFIG_MTD_NAND_ECC=y +@@ -261,24 +209,24 @@ CONFIG_MTD_NAND_MARVELL=y + CONFIG_MTD_RAW_NAND=y + CONFIG_MTD_SPI_NOR=y + CONFIG_MTD_SPLIT_FIRMWARE=y ++CONFIG_MTD_SPLIT_LZMA_FW=y ++CONFIG_MTD_SPLIT_UIMAGE_FW=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_MVEBU_GICP=y ++CONFIG_MVEBU_ICU=y ++CONFIG_MVEBU_ODMI=y ++CONFIG_MVEBU_PIC=y ++CONFIG_MVEBU_SEI=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_NEED_SG_DMA_LENGTH=y + CONFIG_NET_DEVLINK=y + CONFIG_NET_DSA=y + CONFIG_NET_DSA_MV88E6XXX=y +@@ -294,7 +242,6 @@ CONFIG_NVMEM=y + CONFIG_NVME_CORE=y + # CONFIG_NVME_HWMON is not set + # CONFIG_NVME_MULTIPATH is not set +-# CONFIG_NVME_TCP is not set + CONFIG_OF=y + CONFIG_OF_ADDRESS=y + CONFIG_OF_EARLY_FLATTREE=y +@@ -304,44 +251,38 @@ CONFIG_OF_IRQ=y + CONFIG_OF_KOBJ=y + CONFIG_OF_MDIO=y + CONFIG_OF_NET=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_PARTITION_PERCPU=y + CONFIG_PCI=y +-CONFIG_PCI_BRIDGE_EMUL=y ++# CONFIG_PCI_AARDVARK is not set + 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_PGTABLE_LEVELS=3 + CONFIG_PHYLIB=y + CONFIG_PHYLINK=y ++CONFIG_PHYS_ADDR_T_64BIT=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_ARMADA_37XX=y ++CONFIG_PINCTRL_ARMADA_AP806=y ++CONFIG_PINCTRL_ARMADA_CP110=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_POWER_SUPPLY=y + CONFIG_PRINTK_SAFE_LOG_BUF_SHIFT=11 + CONFIG_PWM=y + CONFIG_PWM_SYSFS=y ++CONFIG_QUEUED_RWLOCKS=y ++CONFIG_QUEUED_SPINLOCKS=y + CONFIG_RATIONAL=y + CONFIG_REGMAP=y + CONFIG_REGMAP_I2C=y +@@ -349,12 +290,12 @@ CONFIG_REGMAP_MMIO=y + CONFIG_REGULATOR=y + CONFIG_REGULATOR_FIXED_VOLTAGE=y + CONFIG_RFS_ACCEL=y ++CONFIG_RODATA_FULL_DEFAULT_ENABLED=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 +@@ -373,8 +314,7 @@ CONFIG_SFP=y + CONFIG_SGL_ALLOC=y + CONFIG_SG_POOL=y + CONFIG_SMP=y +-CONFIG_SMP_ON_UP=y +-CONFIG_SOC_BUS=y ++CONFIG_SPARSEMEM_VMEMMAP_ENABLE=y + CONFIG_SPARSE_IRQ=y + CONFIG_SPI=y + # CONFIG_SPI_ARMADA_3700 is not set +@@ -382,45 +322,52 @@ CONFIG_SPI_MASTER=y + CONFIG_SPI_MEM=y + CONFIG_SPI_ORION=y + CONFIG_SRAM=y +-CONFIG_SRAM_EXEC=y + CONFIG_SRCU=y ++CONFIG_SWIOTLB=y + CONFIG_SWPHY=y +-CONFIG_SWP_EMULATE=y +-CONFIG_SYS_SUPPORTS_APM_EMULATION=y ++CONFIG_SYSCTL_EXCEPTION_TRACE=y ++CONFIG_SYS_SUPPORTS_HUGETLBFS=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_THREAD_INFO_IN_TASK=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_UNMAP_KERNEL_AT_EL0=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_GADGET=y + CONFIG_USB_LEDS_TRIGGER_USBPORT=y ++CONFIG_USB_MVEBU_U3D=y ++CONFIG_USB_MV_UDC=y ++CONFIG_USB_NET_DRIVERS=y ++CONFIG_USB_NET_QMI_WWAN=y + CONFIG_USB_PHY=y + CONFIG_USB_STORAGE=y + CONFIG_USB_SUPPORT=y ++CONFIG_USB_USBNET=y ++CONFIG_USB_WDM=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_VMAP_STACK=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_ZONE_DMA32=y ++CONFIG_ZSTD_COMPRESS=y ++CONFIG_ZSTD_DECOMPRESS=y +-- +2.17.1 + diff --git a/patches-22.03.2/3008-mvebu-adjust-platform-sh.patch b/patches-22.03.2/3008-mvebu-adjust-platform-sh.patch new file mode 100644 index 0000000..7ac9cd2 --- /dev/null +++ b/patches-22.03.2/3008-mvebu-adjust-platform-sh.patch @@ -0,0 +1,40 @@ +From 5cd470a43678423e906879340e9d782830491a6a Mon Sep 17 00:00:00 2001 +From: Weiping Yang +Date: Mon, 27 Feb 2023 01:32:53 -0500 +Subject: [PATCH] mvebu-adjust-platform-sh + +--- + .../linux/mvebu/cortexa53/base-files/lib/upgrade/platform.sh | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/target/linux/mvebu/cortexa53/base-files/lib/upgrade/platform.sh b/target/linux/mvebu/cortexa53/base-files/lib/upgrade/platform.sh +index 0ee1d61506..d52baf1b02 100755 +--- a/target/linux/mvebu/cortexa53/base-files/lib/upgrade/platform.sh ++++ b/target/linux/mvebu/cortexa53/base-files/lib/upgrade/platform.sh +@@ -10,6 +10,7 @@ REQUIRE_IMAGE_METADATA=1 + platform_check_image() { + case "$(board_name)" in + glinet,gl-mv1000|\ ++ gl-mv1000|\ + globalscale,espressobin|\ + globalscale,espressobin-emmc|\ + globalscale,espressobin-ultra|\ +@@ -26,6 +27,7 @@ platform_check_image() { + platform_do_upgrade() { + case "$(board_name)" in + glinet,gl-mv1000|\ ++ gl-mv1000|\ + globalscale,espressobin|\ + globalscale,espressobin-emmc|\ + globalscale,espressobin-ultra|\ +@@ -44,6 +46,7 @@ platform_do_upgrade() { + platform_copy_config() { + case "$(board_name)" in + glinet,gl-mv1000|\ ++ gl-mv1000|\ + globalscale,espressobin|\ + globalscale,espressobin-emmc|\ + globalscale,espressobin-ultra|\ +-- +2.17.1 + diff --git a/patches-22.03.2/3009-mvebu-adjust-emmc-img-and-add-usb-devise-drive.patch b/patches-22.03.2/3009-mvebu-adjust-emmc-img-and-add-usb-devise-drive.patch new file mode 100644 index 0000000..39f6e11 --- /dev/null +++ b/patches-22.03.2/3009-mvebu-adjust-emmc-img-and-add-usb-devise-drive.patch @@ -0,0 +1,4856 @@ +From 3cfbd19eaefddbd5a3cc90813518e68266c1d14f Mon Sep 17 00:00:00 2001 +From: Weiping Yang +Date: Mon, 27 Feb 2023 01:39:18 -0500 +Subject: [PATCH] mvebu-adjust-emmc-img-and-add-usb-devise-drive + +--- + target/linux/mvebu/image/Makefile | 9 + + .../linux/mvebu/image/gen_mvebu_sdcard_img.sh | 10 +- + .../mvebu/image/generic-arm64-emmc.bootscript | 20 + + .../941-fit-crash-log-for-mvebu.patch | 11 + + ...-mvebu-fix-wireguard-support-hotplug.patch | 222 + + ...modify-sdhci-initialization-sequence.patch | 38 + + .../944-arm64-usb-device-dts.patch | 25 + + .../945-arm64-usb-gadget-ether-fix-mac.patch | 50 + + .../946-arm64-add-udc-driver.patch | 4370 +++++++++++++++++ + 9 files changed, 4753 insertions(+), 2 deletions(-) + create mode 100644 target/linux/mvebu/image/generic-arm64-emmc.bootscript + create mode 100644 target/linux/mvebu/patches-5.10/941-fit-crash-log-for-mvebu.patch + create mode 100644 target/linux/mvebu/patches-5.10/942-mvebu-fix-wireguard-support-hotplug.patch + create mode 100644 target/linux/mvebu/patches-5.10/943-mvebu-modify-sdhci-initialization-sequence.patch + create mode 100644 target/linux/mvebu/patches-5.10/944-arm64-usb-device-dts.patch + create mode 100644 target/linux/mvebu/patches-5.10/945-arm64-usb-gadget-ether-fix-mac.patch + create mode 100644 target/linux/mvebu/patches-5.10/946-arm64-add-udc-driver.patch + +diff --git a/target/linux/mvebu/image/Makefile b/target/linux/mvebu/image/Makefile +index 44e66a5f87..81d2832423 100644 +--- a/target/linux/mvebu/image/Makefile ++++ b/target/linux/mvebu/image/Makefile +@@ -173,6 +173,15 @@ define Device/Default-arm64 + KERNEL := kernel-bin + endef + ++define Device/Default-arm64-emmc ++ BOOT_SCRIPT := generic-arm64-emmc ++ DEVICE_DTS_DIR := $(DTS_DIR)/marvell ++ IMAGES := emmc.img ++ IMAGE/emmc.img := boot-scr | boot-img-ext4 | sdcard-img-ext4 | append-gl-metadata ++ KERNEL_NAME := Image ++ KERNEL := kernel-bin ++endef ++ + define Device/NAND-128K + BLOCKSIZE := 128k + PAGESIZE := 2048 +diff --git a/target/linux/mvebu/image/gen_mvebu_sdcard_img.sh b/target/linux/mvebu/image/gen_mvebu_sdcard_img.sh +index d6f895d09d..702a6d3597 100755 +--- a/target/linux/mvebu/image/gen_mvebu_sdcard_img.sh ++++ b/target/linux/mvebu/image/gen_mvebu_sdcard_img.sh +@@ -1,4 +1,4 @@ +-#!/bin/sh ++#!/usr/bin/env bash + # SPDX-License-Identifier: GPL-2.0-or-later + # + # Copyright (C) 2016 Josua Mayer +@@ -37,6 +37,12 @@ while [ "$#" -ge 3 ]; do + shift; shift; shift + done + ++model='' ++model=$(echo $OUTFILE | grep "gl-mv1000-emmc") ++[ "$model" != "" ] && { ++ ptgen_args="$ptgen_args -t 83 -p 7093504" ++} ++ + head=16 + sect=63 + +@@ -65,6 +71,6 @@ while [ "$#" -ge 2 ]; do + ) | dd of="$OUTFILE" bs=512 seek=$(($1 / 512)) conv=notrunc 2>/dev/null + printf "Done\n" + +- i=$((i+1)) ++ let i=i+1 + shift; shift + done +diff --git a/target/linux/mvebu/image/generic-arm64-emmc.bootscript b/target/linux/mvebu/image/generic-arm64-emmc.bootscript +new file mode 100644 +index 0000000000..b519e56d0f +--- /dev/null ++++ b/target/linux/mvebu/image/generic-arm64-emmc.bootscript +@@ -0,0 +1,20 @@ ++setenv bootargs "root=/dev/mmcblk0p2 rw rootwait" ++ ++if test -n "${console}"; then ++ setenv bootargs "${bootargs} ${console}" ++fi ++ ++setenv mmcdev 0 ++ ++if test -n "${fdt_addr_r}"; then ++ setenv fdt_addr ${fdt_addr_r} ++fi ++ ++if test -n "${kernel_addr_r}"; then ++ setenv kernel_addr ${kernel_addr_r} ++fi ++ ++load mmc ${mmcdev}:1 ${fdt_addr} @DTB@.dtb ++load mmc ${mmcdev}:1 ${kernel_addr} Image ++ ++booti ${kernel_addr} - ${fdt_addr} +diff --git a/target/linux/mvebu/patches-5.10/941-fit-crash-log-for-mvebu.patch b/target/linux/mvebu/patches-5.10/941-fit-crash-log-for-mvebu.patch +new file mode 100644 +index 0000000000..9da5579eaa +--- /dev/null ++++ b/target/linux/mvebu/patches-5.10/941-fit-crash-log-for-mvebu.patch +@@ -0,0 +1,11 @@ ++--- a/drivers/mtd/spi-nor/core.c +++++ b/drivers/mtd/spi-nor/core.c ++@@ -3207,6 +3207,8 @@ int spi_nor_scan(struct spi_nor *nor, co ++ ++ mtd->_write = spi_nor_write; ++ +++ mtd->_panic_write = spi_nor_write; +++ ++ /* Init flash parameters based on flash_info struct and SFDP */ ++ ret = spi_nor_init_params(nor); ++ if (ret) +diff --git a/target/linux/mvebu/patches-5.10/942-mvebu-fix-wireguard-support-hotplug.patch b/target/linux/mvebu/patches-5.10/942-mvebu-fix-wireguard-support-hotplug.patch +new file mode 100644 +index 0000000000..140263eba0 +--- /dev/null ++++ b/target/linux/mvebu/patches-5.10/942-mvebu-fix-wireguard-support-hotplug.patch +@@ -0,0 +1,222 @@ ++--- a/drivers/net/wireguard/Makefile +++++ b/drivers/net/wireguard/Makefile ++@@ -14,4 +14,5 @@ wireguard-y += allowedips.o ++ wireguard-y += ratelimiter.o ++ wireguard-y += cookie.o ++ wireguard-y += netlink.o +++wireguard-y += hotplug.o ++ obj-$(CONFIG_WIREGUARD) := wireguard.o ++--- /dev/null +++++ b/drivers/net/wireguard/hotplug.c ++@@ -0,0 +1,124 @@ +++/* +++ * oui-tertf Hotplug driver +++ * +++ */ +++ +++#include +++#include +++#include +++#include +++ +++#include +++#include +++#include +++#include +++ +++#include +++ +++#define SUBSYSTEM_NAME "wireguard" +++ +++ +++#define BH_SKB_SIZE 2048 +++#define IFNAMSIZ 16 +++ +++struct work_struct wg_event_work; +++struct wg_event { +++ struct work_struct work; +++ char ifname[IFNAMSIZ]; +++ const char *action; +++}; +++struct wg_event *gl_wg_event; +++ +++static DEFINE_SPINLOCK(event_lock); +++ +++ +++/* -------------------------------------------------------------------------*/ +++static int bh_event_add_var(struct sk_buff *skb, int argv, +++ const char *format, ...) +++{ +++ static char buf[128]; +++ char *s; +++ va_list args; +++ int len; +++ +++ if (argv) +++ return 0; +++ +++ va_start(args, format); +++ len = vsnprintf(buf, sizeof(buf), format, args); +++ va_end(args); +++ +++ if (len >= sizeof(buf)) { +++ //WARN(1, "buffer size too small\n"); +++ return -ENOMEM; +++ } +++ +++ s = skb_put(skb, len + 1); +++ strcpy(s, buf); +++ +++ return 0; +++} +++ +++ +++static void wireguard_send_hotplug(struct work_struct *mywork) +++{ +++ struct wg_event * event; +++ struct sk_buff *skb; +++ char ifname[IFNAMSIZ]; +++ char *action; +++ +++ event = container_of(mywork, struct wg_event, work); +++ if (!event) +++ return; +++ skb = alloc_skb(BH_SKB_SIZE, GFP_KERNEL); +++ if (!skb) +++ return; +++ strcpy(ifname, event->ifname); +++ action = event->action; +++ +++ pr_info("wireguard-hotplug IFNAME=%s ACTION=%s\n", ifname, action); +++ +++ bh_event_add_var(skb, 0, "SUBSYSTEM=%s", SUBSYSTEM_NAME); +++ +++ spin_lock(&event_lock); +++ bh_event_add_var(skb, 0, "ACTION=%s", action); +++ bh_event_add_var(skb, 0, "ifname=%s", ifname); +++ spin_unlock(&event_lock); +++ +++ NETLINK_CB(skb).dst_group = 1; +++ broadcast_uevent(skb, 0, 1, GFP_KERNEL); +++} +++ +++void wireguard_hotplug(const char *ifname, const char *action) +++{ +++ if(gl_wg_event == NULL){ +++ return; +++ } +++ if (0 == strcmp(ifname,"wgserver")) +++ return; +++ +++ spin_lock(&event_lock); +++ memcpy(gl_wg_event->ifname, ifname, IFNAMSIZ); +++ gl_wg_event->action = action; +++ spin_unlock(&event_lock); +++ +++ schedule_work(&gl_wg_event->work); +++} +++ +++void wg_hotplug_init(void) +++{ +++ gl_wg_event = (struct wg_event *)kzalloc(sizeof(struct wg_event),GFP_KERNEL); +++ if(gl_wg_event == NULL){ +++ return; +++ } +++ gl_wg_event->work = wg_event_work; +++ INIT_WORK(&gl_wg_event->work, wireguard_send_hotplug); +++} +++ +++void wg_hotplug_free(void) +++{ +++ if(gl_wg_event){ +++ kfree(gl_wg_event); +++ } +++ return; +++} ++--- /dev/null +++++ b/drivers/net/wireguard/hotplug.h ++@@ -0,0 +1,13 @@ +++#ifndef __HOTPLUG_H__ +++#define __HOTPLUG_H__ +++ +++#define REKEY_GIVEUP_EVENT "REKEY-GIVEUP" +++#define REKEY_TIMEOUT_EVENT "REKEY-TIMEOUT" +++#define KEYPAIR_CREATED_EVENT "KEYPAIR-CREATED" +++ +++extern void wireguard_hotplug(const char *ifname, const char *action); +++extern void wg_hotplug_init(void); +++extern void wg_hotplug_free(void); +++ +++#endif +++ ++--- a/drivers/net/wireguard/main.c +++++ b/drivers/net/wireguard/main.c ++@@ -17,6 +17,8 @@ ++ #include ++ #include ++ +++#include "hotplug.h" +++ ++ static int __init mod_init(void) ++ { ++ int ret; ++@@ -44,6 +46,7 @@ static int __init mod_init(void) ++ ret = wg_genetlink_init(); ++ if (ret < 0) ++ goto err_netlink; +++ wg_hotplug_init(); ++ ++ pr_info("WireGuard " WIREGUARD_VERSION " loaded. See www.wireguard.com for information.\n"); ++ pr_info("Copyright (C) 2015-2019 Jason A. Donenfeld . All Rights Reserved.\n"); ++@@ -62,6 +65,7 @@ err_allowedips: ++ ++ static void __exit mod_exit(void) ++ { +++ wg_hotplug_free(); ++ wg_genetlink_uninit(); ++ wg_device_uninit(); ++ wg_peer_uninit(); ++--- a/drivers/net/wireguard/noise.c +++++ b/drivers/net/wireguard/noise.c ++@@ -9,6 +9,7 @@ ++ #include "messages.h" ++ #include "queueing.h" ++ #include "peerlookup.h" +++#include "hotplug.h" ++ ++ #include ++ #include ++@@ -850,6 +851,7 @@ bool wg_noise_handshake_begin_session(st ++ ret = wg_index_hashtable_replace( ++ handshake->entry.peer->device->index_hashtable, ++ &handshake->entry, &new_keypair->entry); +++ wireguard_hotplug(handshake->entry.peer->device->dev->name, KEYPAIR_CREATED_EVENT); ++ } else { ++ kfree_sensitive(new_keypair); ++ } ++--- a/drivers/net/wireguard/timers.c +++++ b/drivers/net/wireguard/timers.c ++@@ -8,6 +8,7 @@ ++ #include "peer.h" ++ #include "queueing.h" ++ #include "socket.h" +++#include "hotplug.h" ++ ++ /* ++ * - Timer for retransmitting the handshake if we don't hear back after ++@@ -60,6 +61,7 @@ static void wg_expired_retransmit_handsh ++ if (!timer_pending(&peer->timer_zero_key_material)) ++ mod_peer_timer(peer, &peer->timer_zero_key_material, ++ jiffies + REJECT_AFTER_TIME * 3 * HZ); +++ wireguard_hotplug(peer->device->dev->name, REKEY_GIVEUP_EVENT); ++ } else { ++ ++peer->timer_handshake_attempts; ++ pr_debug("%s: Handshake for peer %llu (%pISpfsc) did not complete after %d seconds, retrying (try %d)\n", ++@@ -73,6 +75,7 @@ static void wg_expired_retransmit_handsh ++ wg_socket_clear_peer_endpoint_src(peer); ++ ++ wg_packet_send_queued_handshake_initiation(peer, true); +++ wireguard_hotplug(peer->device->dev->name, REKEY_GIVEUP_EVENT); ++ } ++ } ++ +diff --git a/target/linux/mvebu/patches-5.10/943-mvebu-modify-sdhci-initialization-sequence.patch b/target/linux/mvebu/patches-5.10/943-mvebu-modify-sdhci-initialization-sequence.patch +new file mode 100644 +index 0000000000..ee9c2c51e5 +--- /dev/null ++++ b/target/linux/mvebu/patches-5.10/943-mvebu-modify-sdhci-initialization-sequence.patch +@@ -0,0 +1,38 @@ ++--- a/arch/arm64/boot/dts/marvell/armada-37xx.dtsi +++++ b/arch/arm64/boot/dts/marvell/armada-37xx.dtsi ++@@ -434,17 +434,6 @@ ++ #mbox-cells = <1>; ++ }; ++ ++- sdhci1: sdhci@d0000 { ++- compatible = "marvell,armada-3700-sdhci", ++- "marvell,sdhci-xenon"; ++- reg = <0xd0000 0x300>, ++- <0x1e808 0x4>; ++- interrupts = ; ++- clocks = <&nb_periph_clk 0>; ++- clock-names = "core"; ++- status = "disabled"; ++- }; ++- ++ sdhci0: sdhci@d8000 { ++ compatible = "marvell,armada-3700-sdhci", ++ "marvell,sdhci-xenon"; ++@@ -456,6 +445,17 @@ ++ status = "disabled"; ++ }; ++ +++ sdhci1: sdhci@d0000 { +++ compatible = "marvell,armada-3700-sdhci", +++ "marvell,sdhci-xenon"; +++ reg = <0xd0000 0x300>, +++ <0x1e808 0x4>; +++ interrupts = ; +++ clocks = <&nb_periph_clk 0>; +++ clock-names = "core"; +++ status = "disabled"; +++ }; +++ ++ sata: sata@e0000 { ++ compatible = "marvell,armada-3700-ahci"; ++ reg = <0xe0000 0x178>; +diff --git a/target/linux/mvebu/patches-5.10/944-arm64-usb-device-dts.patch b/target/linux/mvebu/patches-5.10/944-arm64-usb-device-dts.patch +new file mode 100644 +index 0000000000..efe99c2c74 +--- /dev/null ++++ b/target/linux/mvebu/patches-5.10/944-arm64-usb-device-dts.patch +@@ -0,0 +1,25 @@ ++--- a/arch/arm64/boot/dts/marvell/armada-37xx.dtsi +++++ b/arch/arm64/boot/dts/marvell/armada-37xx.dtsi ++@@ -400,6 +400,22 @@ ++ reg = <0x5f800 0x800>; ++ }; ++ +++ u3d@50000 { +++ compatible = "marvell,armada3700-u3d"; +++ /* 0: 0x50000: USB 3.0 Device port 0: DEV_INFO_REG(0:15 - version_id) */ +++ reg = <0x50000 0x2000>; +++ interrupts = ; +++ clocks = <&sb_periph_clk 12>; +++ status = "okay"; +++ }; +++ udc@54100 { +++ compatible = "marvell,mv-udc"; +++ reg = <0x54100 0x2000>; +++ interrupts = ; +++ clocks = <&sb_periph_clk 12>; +++ status = "okay"; +++ }; +++ ++ xor@60900 { ++ compatible = "marvell,armada-3700-xor"; ++ reg = <0x60900 0x100>, +diff --git a/target/linux/mvebu/patches-5.10/945-arm64-usb-gadget-ether-fix-mac.patch b/target/linux/mvebu/patches-5.10/945-arm64-usb-gadget-ether-fix-mac.patch +new file mode 100644 +index 0000000000..b1e1180126 +--- /dev/null ++++ b/target/linux/mvebu/patches-5.10/945-arm64-usb-gadget-ether-fix-mac.patch +@@ -0,0 +1,50 @@ ++--- a/drivers/usb/gadget/legacy/ether.c +++++ b/drivers/usb/gadget/legacy/ether.c ++@@ -20,6 +20,8 @@ ++ #endif ++ ++ #include "u_ether.h" +++#include +++#include ++ ++ ++ /* ++@@ -309,6 +311,11 @@ static int eth_bind(struct usb_composite ++ struct f_gether_opts *geth_opts = NULL; ++ struct net_device *net; ++ int status; +++ struct mtd_info *mtd; +++ int mtd_mac_ok = 1; +++ size_t retlen; +++ u8 mac[ETH_ALEN]; +++ char mac_addr[ETH_ALEN]; ++ ++ /* set up main config label and device descriptor */ ++ if (use_eem) { ++@@ -357,6 +364,26 @@ static int eth_bind(struct usb_composite ++ } ++ ++ gether_set_qmult(net, qmult); +++ +++ mtd = get_mtd_device_nm("art"); +++ if (IS_ERR(mtd)){ +++ mtd_mac_ok = 0; +++ } else { +++ mtd_read(mtd, 0, 6, &retlen, mac); +++ if (!is_valid_ether_addr(mac)) +++ mtd_mac_ok = 0; +++ } +++ +++ if(mtd_mac_ok){ +++ mac[0] -= 2; +++ sprintf(mac_addr, "%x:%x:%x:%x:%x:%x", mac[0], mac[1],mac[2], mac[3], mac[4], mac[5]); +++ if (!gether_set_host_addr(net, mac_addr)) +++ pr_info("using host ethernet address from mtd: %s",mac_addr); +++ mac[0] -= 4; +++ sprintf(mac_addr, "%x:%x:%x:%x:%x:%x", mac[0], mac[1],mac[2], mac[3], mac[4], mac[5]); +++ if (!gether_set_dev_addr(net, mac_addr)) +++ pr_info("using self ethernet address from mtd: %s",mac_addr); +++ } ++ if (!gether_set_host_addr(net, host_addr)) ++ pr_info("using host ethernet address: %s", host_addr); ++ if (!gether_set_dev_addr(net, dev_addr)) +diff --git a/target/linux/mvebu/patches-5.10/946-arm64-add-udc-driver.patch b/target/linux/mvebu/patches-5.10/946-arm64-add-udc-driver.patch +new file mode 100644 +index 0000000000..346f6d1c45 +--- /dev/null ++++ b/target/linux/mvebu/patches-5.10/946-arm64-add-udc-driver.patch +@@ -0,0 +1,4370 @@ ++--- a/drivers/usb/gadget/udc/Kconfig +++++ b/drivers/usb/gadget/udc/Kconfig ++@@ -258,6 +258,12 @@ config USB_MV_U3D ++ MARVELL PXA2128 Processor series include a super speed USB3.0 device ++ controller, which support super speed USB peripheral. ++ +++config USB_MVEBU_U3D +++ tristate "Marvell Armada 38X/3700/8K USB 3.0 controller" +++ help +++ MARVELL Armada 38X/3700/8K Processors series include a super speed +++ USB3.0 device controller, which support super speed USB peripheral. +++ ++ config USB_SNP_CORE ++ depends on (USB_AMD5536UDC || USB_SNP_UDC_PLAT) ++ depends on HAS_DMA ++--- a/drivers/usb/gadget/udc/Makefile +++++ b/drivers/usb/gadget/udc/Makefile ++@@ -37,6 +37,7 @@ mv_udc-y := mv_udc_core.o ++ obj-$(CONFIG_USB_FUSB300) += fusb300_udc.o ++ obj-$(CONFIG_USB_FOTG210_UDC) += fotg210-udc.o ++ obj-$(CONFIG_USB_MV_U3D) += mv_u3d_core.o +++obj-$(CONFIG_USB_MVEBU_U3D) += mvebu_glue.o mvebu_u3d.o ++ obj-$(CONFIG_USB_GR_UDC) += gr_udc.o ++ obj-$(CONFIG_USB_GADGET_XILINX) += udc-xilinx.o ++ obj-$(CONFIG_USB_SNP_UDC_PLAT) += snps_udc_plat.o ++--- a/drivers/usb/gadget/udc/core.c +++++ b/drivers/usb/gadget/udc/core.c ++@@ -21,26 +21,6 @@ ++ ++ #include "trace.h" ++ ++-/** ++- * struct usb_udc - describes one usb device controller ++- * @driver: the gadget driver pointer. For use by the class code ++- * @dev: the child device to the actual controller ++- * @gadget: the gadget. For use by the class code ++- * @list: for use by the udc class driver ++- * @vbus: for udcs who care about vbus status, this value is real vbus status; ++- * for udcs who do not care about vbus status, this value is always true ++- * ++- * This represents the internal data structure which is used by the UDC-class ++- * to hold information about udc driver and gadget together. ++- */ ++-struct usb_udc { ++- struct usb_gadget_driver *driver; ++- struct usb_gadget *gadget; ++- struct device dev; ++- struct list_head list; ++- bool vbus; ++-}; ++- ++ static struct class *udc_class; ++ static LIST_HEAD(udc_list); ++ static LIST_HEAD(gadget_driver_pending_list); ++@@ -1438,37 +1418,13 @@ int usb_gadget_probe_driver(struct usb_g ++ return -EINVAL; ++ ++ mutex_lock(&udc_lock); ++- if (driver->udc_name) { ++- list_for_each_entry(udc, &udc_list, list) { ++- ret = strcmp(driver->udc_name, dev_name(&udc->dev)); ++- if (!ret) ++- break; ++- } ++- if (ret) ++- ret = -ENODEV; ++- else if (udc->driver) ++- ret = -EBUSY; ++- else ++- goto found; ++- } else { ++- list_for_each_entry(udc, &udc_list, list) { ++- /* For now we take the first one */ ++- if (!udc->driver) ++- goto found; ++- } ++- } ++- ++- if (!driver->match_existing_only) { ++- list_add_tail(&driver->pending, &gadget_driver_pending_list); ++- pr_info("udc-core: couldn't find an available UDC - added [%s] to list of pending drivers\n", ++- driver->function); ++- ret = 0; ++- } +++ udc = udc_detect(&udc_list, driver); +++ if (udc) +++ goto found; ++ +++ pr_debug("couldn't find an available UDC\n"); ++ mutex_unlock(&udc_lock); ++- if (ret) ++- pr_warn("udc-core: couldn't find an available UDC or it's busy\n"); ++- return ret; +++ return -ENODEV; ++ found: ++ ret = udc_bind_to_driver(udc, driver); ++ mutex_unlock(&udc_lock); ++--- a/drivers/usb/gadget/udc/mv_udc.h +++++ b/drivers/usb/gadget/udc/mv_udc.h ++@@ -192,6 +192,8 @@ struct mv_udc { ++ struct mv_req *status_req; ++ struct usb_ctrlrequest local_setup_buff; ++ +++ struct phy *utmi_phy; +++ ++ unsigned int resume_state; /* USB state to resume */ ++ unsigned int usb_state; /* USB current state */ ++ unsigned int ep0_state; /* Endpoint zero state */ ++@@ -210,6 +212,7 @@ struct mv_udc { ++ active:1, ++ stopped:1; /* stop bit is setted */ ++ +++ int vbus_pin; ++ struct work_struct vbus_work; ++ struct workqueue_struct *qwork; ++ ++--- a/drivers/usb/gadget/udc/mv_udc_core.c +++++ b/drivers/usb/gadget/udc/mv_udc_core.c ++@@ -29,12 +29,16 @@ ++ #include ++ #include ++ #include +++#include +++#include ++ #include ++ #include +++#include ++ ++ #include "mv_udc.h" ++ ++ #define DRIVER_DESC "Marvell PXA USB Device Controller driver" +++#define DRIVER_VERSION "8 Nov 2010" ++ ++ #define ep_dir(ep) (((ep)->ep_num == 0) ? \ ++ ((ep)->udc->ep0_dir) : ((ep)->direction)) ++@@ -63,7 +67,7 @@ static const struct usb_endpoint_descrip ++ .bDescriptorType = USB_DT_ENDPOINT, ++ .bEndpointAddress = 0, ++ .bmAttributes = USB_ENDPOINT_XFER_CONTROL, ++- .wMaxPacketSize = EP0_MAX_PKT_SIZE, +++ .wMaxPacketSize = cpu_to_le16(EP0_MAX_PKT_SIZE), ++ }; ++ ++ static void ep0_reset(struct mv_udc *udc) ++@@ -81,11 +85,11 @@ static void ep0_reset(struct mv_udc *udc ++ ep->dqh = &udc->ep_dqh[i]; ++ ++ /* configure ep0 endpoint capabilities in dQH */ ++- ep->dqh->max_packet_length = +++ ep->dqh->max_packet_length = cpu_to_le32( ++ (EP0_MAX_PKT_SIZE << EP_QUEUE_HEAD_MAX_PKT_LEN_POS) ++- | EP_QUEUE_HEAD_IOS; +++ | EP_QUEUE_HEAD_IOS); ++ ++- ep->dqh->next_dtd_ptr = EP_QUEUE_HEAD_NEXT_TERMINATE; +++ ep->dqh->next_dtd_ptr = cpu_to_le32(EP_QUEUE_HEAD_NEXT_TERMINATE); ++ ++ epctrlx = readl(&udc->op_regs->epctrlx[0]); ++ if (i) { /* TX */ ++@@ -123,7 +127,7 @@ static int process_ep_req(struct mv_udc ++ { ++ struct mv_dtd *curr_dtd; ++ struct mv_dqh *curr_dqh; ++- int actual, remaining_length; +++ int td_complete, actual, remaining_length; ++ int i, direction; ++ int retval = 0; ++ u32 errors; ++@@ -133,19 +137,20 @@ static int process_ep_req(struct mv_udc ++ direction = index % 2; ++ ++ curr_dtd = curr_req->head; +++ td_complete = 0; ++ actual = curr_req->req.length; ++ ++ for (i = 0; i < curr_req->dtd_count; i++) { ++- if (curr_dtd->size_ioc_sts & DTD_STATUS_ACTIVE) { +++ if (le32_to_cpu(curr_dtd->size_ioc_sts) & DTD_STATUS_ACTIVE) { ++ dev_dbg(&udc->dev->dev, "%s, dTD not completed\n", ++ udc->eps[index].name); ++ return 1; ++ } ++ ++- errors = curr_dtd->size_ioc_sts & DTD_ERROR_MASK; +++ errors = le32_to_cpu(curr_dtd->size_ioc_sts) & DTD_ERROR_MASK; ++ if (!errors) { ++ remaining_length = ++- (curr_dtd->size_ioc_sts & DTD_PACKET_SIZE) +++ (le32_to_cpu(curr_dtd->size_ioc_sts) & DTD_PACKET_SIZE) ++ >> DTD_LENGTH_BIT_POS; ++ actual -= remaining_length; ++ ++@@ -165,7 +170,7 @@ static int process_ep_req(struct mv_udc ++ errors); ++ if (errors & DTD_STATUS_HALTED) { ++ /* Clear the errors and Halt condition */ ++- curr_dqh->size_ioc_int_sts &= ~errors; +++ curr_dqh->size_ioc_int_sts = cpu_to_le32(le32_to_cpu(curr_dqh->size_ioc_int_sts) & (~errors)); ++ retval = -EPIPE; ++ } else if (errors & DTD_STATUS_DATA_BUFF_ERR) { ++ retval = -EPROTO; ++@@ -184,8 +189,8 @@ static int process_ep_req(struct mv_udc ++ else ++ bit_pos = 1 << (16 + curr_req->ep->ep_num); ++ ++- while (curr_dqh->curr_dtd_ptr == curr_dtd->td_dma) { ++- if (curr_dtd->dtd_next == EP_QUEUE_HEAD_NEXT_TERMINATE) { +++ while ((curr_dqh->curr_dtd_ptr == cpu_to_le32(curr_dtd->td_dma))) { +++ if (curr_dtd->dtd_next == cpu_to_le32(EP_QUEUE_HEAD_NEXT_TERMINATE)) { ++ while (readl(&udc->op_regs->epstatus) & bit_pos) ++ udelay(1); ++ break; ++@@ -242,7 +247,8 @@ static void done(struct mv_ep *ep, struc ++ ++ spin_unlock(&ep->udc->lock); ++ ++- usb_gadget_giveback_request(&ep->ep, &req->req); +++ if (req->req.complete) +++ usb_gadget_giveback_request(&ep->ep, &req->req); ++ ++ spin_lock(&ep->udc->lock); ++ ep->stopped = stopped; ++@@ -267,7 +273,7 @@ static int queue_dtd(struct mv_ep *ep, s ++ struct mv_req *lastreq; ++ lastreq = list_entry(ep->queue.prev, struct mv_req, queue); ++ lastreq->tail->dtd_next = ++- req->head->td_dma & EP_QUEUE_HEAD_NEXT_POINTER_MASK; +++ cpu_to_le32(req->head->td_dma & EP_QUEUE_HEAD_NEXT_POINTER_MASK); ++ ++ wmb(); ++ ++@@ -315,11 +321,12 @@ static int queue_dtd(struct mv_ep *ep, s ++ } ++ ++ /* Write dQH next pointer and terminate bit to 0 */ ++- dqh->next_dtd_ptr = req->head->td_dma ++- & EP_QUEUE_HEAD_NEXT_POINTER_MASK; +++ dqh->next_dtd_ptr = cpu_to_le32(req->head->td_dma +++ & EP_QUEUE_HEAD_NEXT_POINTER_MASK); ++ ++ /* clear active and halt bit, in case set from a previous error */ ++- dqh->size_ioc_int_sts &= ~(DTD_STATUS_ACTIVE | DTD_STATUS_HALTED); +++ dqh->size_ioc_int_sts = +++ cpu_to_le32(le32_to_cpu(dqh->size_ioc_int_sts) & (~(DTD_STATUS_ACTIVE | DTD_STATUS_HALTED))); ++ ++ /* Ensure that updates to the QH will occur before priming. */ ++ wmb(); ++@@ -342,7 +349,7 @@ static struct mv_dtd *build_dtd(struct m ++ /* how big will this transfer be? */ ++ if (usb_endpoint_xfer_isoc(req->ep->ep.desc)) { ++ dqh = req->ep->dqh; ++- mult = (dqh->max_packet_length >> EP_QUEUE_HEAD_MULT_POS) +++ mult = (le32_to_cpu(dqh->max_packet_length) >> EP_QUEUE_HEAD_MULT_POS) ++ & 0x3; ++ *length = min(req->req.length - req->req.actual, ++ (unsigned)(mult * req->ep->ep.maxpacket)); ++@@ -392,7 +399,7 @@ static struct mv_dtd *build_dtd(struct m ++ ++ temp |= mult << 10; ++ ++- dtd->size_ioc_sts = temp; +++ dtd->size_ioc_sts = cpu_to_le32(temp); ++ ++ mb(); ++ ++@@ -405,8 +412,11 @@ static int req_to_dtd(struct mv_req *req ++ unsigned count; ++ int is_last, is_first = 1; ++ struct mv_dtd *dtd, *last_dtd = NULL; +++ struct mv_udc *udc; ++ dma_addr_t dma; ++ +++ udc = req->ep->udc; +++ ++ do { ++ dtd = build_dtd(req, &count, &dma, &is_last); ++ if (dtd == NULL) ++@@ -416,7 +426,7 @@ static int req_to_dtd(struct mv_req *req ++ is_first = 0; ++ req->head = dtd; ++ } else { ++- last_dtd->dtd_next = dma; +++ last_dtd->dtd_next = cpu_to_le32(dma); ++ last_dtd->next_dtd_virt = dtd; ++ } ++ last_dtd = dtd; ++@@ -424,7 +434,7 @@ static int req_to_dtd(struct mv_req *req ++ } while (!is_last); ++ ++ /* set terminate bit to 1 for the last dTD */ ++- dtd->dtd_next = DTD_NEXT_TERMINATE; +++ dtd->dtd_next = cpu_to_le32(DTD_NEXT_TERMINATE); ++ ++ req->tail = dtd; ++ ++@@ -439,8 +449,7 @@ static int mv_ep_enable(struct usb_ep *_ ++ struct mv_dqh *dqh; ++ u16 max = 0; ++ u32 bit_pos, epctrlx, direction; ++- const unsigned char zlt = 1; ++- unsigned char ios, mult; +++ unsigned char zlt = 0, ios = 0, mult = 0; ++ unsigned long flags; ++ ++ ep = container_of(_ep, struct mv_ep, ep); ++@@ -460,6 +469,8 @@ static int mv_ep_enable(struct usb_ep *_ ++ * disable HW zero length termination select ++ * driver handles zero length packet through req->req.zero ++ */ +++ zlt = 1; +++ ++ bit_pos = 1 << ((direction == EP_DIR_OUT ? 0 : 16) + ep->ep_num); ++ ++ /* Check if the Endpoint is Primed */ ++@@ -476,18 +487,20 @@ static int mv_ep_enable(struct usb_ep *_ ++ } ++ ++ /* Set the max packet length, interrupt on Setup and Mult fields */ ++- ios = 0; ++- mult = 0; ++ switch (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) { ++ case USB_ENDPOINT_XFER_BULK: ++- case USB_ENDPOINT_XFER_INT: +++ zlt = 1; +++ mult = 0; ++ break; ++ case USB_ENDPOINT_XFER_CONTROL: ++ ios = 1; +++ case USB_ENDPOINT_XFER_INT: +++ mult = 0; ++ break; ++ case USB_ENDPOINT_XFER_ISOC: ++ /* Calculate transactions needed for high bandwidth iso */ ++- mult = usb_endpoint_maxp_mult(desc); +++ mult = (unsigned char)(1 + ((max >> 11) & 0x03)); +++ max = max & 0x7ff; /* bit 0~10 */ ++ /* 3 transactions at most */ ++ if (mult > 3) ++ goto en_done; ++@@ -499,13 +512,12 @@ static int mv_ep_enable(struct usb_ep *_ ++ spin_lock_irqsave(&udc->lock, flags); ++ /* Get the endpoint queue head address */ ++ dqh = ep->dqh; ++- dqh->max_packet_length = (max << EP_QUEUE_HEAD_MAX_PKT_LEN_POS) +++ dqh->max_packet_length = cpu_to_le32((max << EP_QUEUE_HEAD_MAX_PKT_LEN_POS) ++ | (mult << EP_QUEUE_HEAD_MULT_POS) ++ | (zlt ? EP_QUEUE_HEAD_ZLT_SEL : 0) ++- | (ios ? EP_QUEUE_HEAD_IOS : 0); ++- dqh->next_dtd_ptr = 1; ++- dqh->size_ioc_int_sts = 0; ++- +++ | (ios ? EP_QUEUE_HEAD_IOS : 0)); +++ dqh->next_dtd_ptr = cpu_to_le32(1); +++ dqh->size_ioc_int_sts = cpu_to_le32(0); ++ ep->ep.maxpacket = max; ++ ep->ep.desc = desc; ++ ep->stopped = 0; ++@@ -555,7 +567,7 @@ static int mv_ep_disable(struct usb_ep ++ struct mv_udc *udc; ++ struct mv_ep *ep; ++ struct mv_dqh *dqh; ++- u32 epctrlx, direction; +++ u32 bit_pos, epctrlx, direction; ++ unsigned long flags; ++ ++ ep = container_of(_ep, struct mv_ep, ep); ++@@ -570,9 +582,10 @@ static int mv_ep_disable(struct usb_ep ++ spin_lock_irqsave(&udc->lock, flags); ++ ++ direction = ep_dir(ep); +++ bit_pos = 1 << ((direction == EP_DIR_OUT ? 0 : 16) + ep->ep_num); ++ ++ /* Reset the max packet length and the interrupt on Setup */ ++- dqh->max_packet_length = 0; +++ dqh->max_packet_length = cpu_to_le32(0); ++ ++ /* Disable the endpoint for Rx or Tx and reset the endpoint type */ ++ epctrlx = readl(&udc->op_regs->epctrlx[ep->ep_num]); ++@@ -752,11 +765,12 @@ static void mv_prime_ep(struct mv_ep *ep ++ u32 bit_pos; ++ ++ /* Write dQH next pointer and terminate bit to 0 */ ++- dqh->next_dtd_ptr = req->head->td_dma ++- & EP_QUEUE_HEAD_NEXT_POINTER_MASK; +++ dqh->next_dtd_ptr = cpu_to_le32(req->head->td_dma +++ & EP_QUEUE_HEAD_NEXT_POINTER_MASK); ++ ++ /* clear active and halt bit, in case set from a previous error */ ++- dqh->size_ioc_int_sts &= ~(DTD_STATUS_ACTIVE | DTD_STATUS_HALTED); +++ dqh->size_ioc_int_sts = +++ cpu_to_le32(le32_to_cpu(dqh->size_ioc_int_sts) & (~(DTD_STATUS_ACTIVE | DTD_STATUS_HALTED))); ++ ++ /* Ensure that updates to the QH will occure before priming. */ ++ wmb(); ++@@ -820,8 +834,8 @@ static int mv_ep_dequeue(struct usb_ep * ++ struct mv_dqh *qh; ++ ++ qh = ep->dqh; ++- qh->next_dtd_ptr = 1; ++- qh->size_ioc_int_sts = 0; +++ qh->next_dtd_ptr = cpu_to_le32(1); +++ qh->size_ioc_int_sts = cpu_to_le32(0); ++ } ++ ++ /* The request hasn't been processed, patch up the TD chain */ ++@@ -831,7 +845,6 @@ static int mv_ep_dequeue(struct usb_ep * ++ prev_req = list_entry(req->queue.prev, struct mv_req, queue); ++ writel(readl(&req->tail->dtd_next), ++ &prev_req->tail->dtd_next); ++- ++ } ++ ++ done(ep, req, -ECONNRESET); ++@@ -939,7 +952,7 @@ static int mv_ep_set_wedge(struct usb_ep ++ return mv_ep_set_halt_wedge(_ep, 1, 1); ++ } ++ ++-static const struct usb_ep_ops mv_ep_ops = { +++static struct usb_ep_ops mv_ep_ops = { ++ .enable = mv_ep_enable, ++ .disable = mv_ep_disable, ++ ++@@ -954,9 +967,9 @@ static const struct usb_ep_ops mv_ep_ops ++ .fifo_flush = mv_ep_fifo_flush, /* flush fifo */ ++ }; ++ ++-static int udc_clock_enable(struct mv_udc *udc) +++static void udc_clock_enable(struct mv_udc *udc) ++ { ++- return clk_prepare_enable(udc->clk); +++ clk_prepare_enable(udc->clk); ++ } ++ ++ static void udc_clock_disable(struct mv_udc *udc) ++@@ -1064,11 +1077,8 @@ static int mv_udc_enable_internal(struct ++ return 0; ++ ++ dev_dbg(&udc->dev->dev, "enable udc\n"); ++- retval = udc_clock_enable(udc); ++- if (retval) ++- return retval; ++- ++- if (udc->pdata->phy_init) { +++ udc_clock_enable(udc); +++ if (udc->pdata && udc->pdata->phy_init) { ++ retval = udc->pdata->phy_init(udc->phy_regs); ++ if (retval) { ++ dev_err(&udc->dev->dev, ++@@ -1094,7 +1104,7 @@ static void mv_udc_disable_internal(stru ++ { ++ if (udc->active) { ++ dev_dbg(&udc->dev->dev, "disable udc\n"); ++- if (udc->pdata->phy_deinit) +++ if (udc->pdata && udc->pdata->phy_deinit) ++ udc->pdata->phy_deinit(udc->phy_regs); ++ udc_clock_disable(udc); ++ udc->active = 0; ++@@ -1364,6 +1374,9 @@ static int mv_udc_start(struct usb_gadge ++ udc->ep0_state = WAIT_FOR_SETUP; ++ udc->ep0_dir = EP_DIR_OUT; ++ +++ if (gpio_is_valid(udc->vbus_pin)) +++ enable_irq(gpio_to_irq(udc->vbus_pin)); +++ ++ spin_unlock_irqrestore(&udc->lock, flags); ++ ++ if (udc->transceiver) { ++@@ -1391,6 +1404,9 @@ static int mv_udc_stop(struct usb_gadget ++ ++ udc = container_of(gadget, struct mv_udc, gadget); ++ +++ if (gpio_is_valid(udc->vbus_pin)) +++ disable_irq(gpio_to_irq(udc->vbus_pin)); +++ ++ spin_lock_irqsave(&udc->lock, flags); ++ ++ mv_udc_enable(udc); ++@@ -1512,7 +1528,7 @@ static void mv_udc_testmode(struct mv_ud ++ ++ static void ch9setaddress(struct mv_udc *udc, struct usb_ctrlrequest *setup) ++ { ++- udc->dev_addr = (u8)setup->wValue; +++ udc->dev_addr = le16_to_cpu(setup->wValue); ++ ++ /* update usb state */ ++ udc->usb_state = USB_STATE_ADDRESS; ++@@ -1542,8 +1558,8 @@ static void ch9getstatus(struct mv_udc * ++ == USB_RECIP_ENDPOINT) { ++ u8 ep_num, direction; ++ ++- ep_num = setup->wIndex & USB_ENDPOINT_NUMBER_MASK; ++- direction = (setup->wIndex & USB_ENDPOINT_DIR_MASK) +++ ep_num = le16_to_cpu(setup->wIndex) & USB_ENDPOINT_NUMBER_MASK; +++ direction = (le16_to_cpu(setup->wIndex) & USB_ENDPOINT_DIR_MASK) ++ ? EP_DIR_IN : EP_DIR_OUT; ++ status = ep_is_stall(udc, ep_num, direction) ++ << USB_ENDPOINT_HALT; ++@@ -1564,7 +1580,7 @@ static void ch9clearfeature(struct mv_ud ++ ++ if ((setup->bRequestType & (USB_TYPE_MASK | USB_RECIP_MASK)) ++ == ((USB_TYPE_STANDARD | USB_RECIP_DEVICE))) { ++- switch (setup->wValue) { +++ switch (le16_to_cpu(setup->wValue)) { ++ case USB_DEVICE_REMOTE_WAKEUP: ++ udc->remote_wakeup = 0; ++ break; ++@@ -1573,12 +1589,12 @@ static void ch9clearfeature(struct mv_ud ++ } ++ } else if ((setup->bRequestType & (USB_TYPE_MASK | USB_RECIP_MASK)) ++ == ((USB_TYPE_STANDARD | USB_RECIP_ENDPOINT))) { ++- switch (setup->wValue) { +++ switch (le16_to_cpu(setup->wValue)) { ++ case USB_ENDPOINT_HALT: ++- ep_num = setup->wIndex & USB_ENDPOINT_NUMBER_MASK; ++- direction = (setup->wIndex & USB_ENDPOINT_DIR_MASK) +++ ep_num = le16_to_cpu(setup->wIndex) & USB_ENDPOINT_NUMBER_MASK; +++ direction = (le16_to_cpu(setup->wIndex) & USB_ENDPOINT_DIR_MASK) ++ ? EP_DIR_IN : EP_DIR_OUT; ++- if (setup->wValue != 0 || setup->wLength != 0 +++ if (le16_to_cpu(setup->wValue) != 0 || le16_to_cpu(setup->wLength) != 0 ++ || ep_num > udc->max_eps) ++ goto out; ++ ep = &udc->eps[ep_num * 2 + direction]; ++@@ -1607,12 +1623,12 @@ static void ch9setfeature(struct mv_udc ++ ++ if ((setup->bRequestType & (USB_TYPE_MASK | USB_RECIP_MASK)) ++ == ((USB_TYPE_STANDARD | USB_RECIP_DEVICE))) { ++- switch (setup->wValue) { +++ switch (le16_to_cpu(setup->wValue)) { ++ case USB_DEVICE_REMOTE_WAKEUP: ++ udc->remote_wakeup = 1; ++ break; ++ case USB_DEVICE_TEST_MODE: ++- if (setup->wIndex & 0xFF +++ if (le16_to_cpu(setup->wIndex) & 0xFF ++ || udc->gadget.speed != USB_SPEED_HIGH) ++ ep0_stall(udc); ++ ++@@ -1621,19 +1637,19 @@ static void ch9setfeature(struct mv_udc ++ && udc->usb_state != USB_STATE_DEFAULT) ++ ep0_stall(udc); ++ ++- mv_udc_testmode(udc, (setup->wIndex >> 8)); +++ mv_udc_testmode(udc, (le16_to_cpu(setup->wIndex) >> 8)); ++ goto out; ++ default: ++ goto out; ++ } ++ } else if ((setup->bRequestType & (USB_TYPE_MASK | USB_RECIP_MASK)) ++ == ((USB_TYPE_STANDARD | USB_RECIP_ENDPOINT))) { ++- switch (setup->wValue) { +++ switch (le16_to_cpu(setup->wValue)) { ++ case USB_ENDPOINT_HALT: ++- ep_num = setup->wIndex & USB_ENDPOINT_NUMBER_MASK; ++- direction = (setup->wIndex & USB_ENDPOINT_DIR_MASK) +++ ep_num = le16_to_cpu(setup->wIndex) & USB_ENDPOINT_NUMBER_MASK; +++ direction = (le16_to_cpu(setup->wIndex) & USB_ENDPOINT_DIR_MASK) ++ ? EP_DIR_IN : EP_DIR_OUT; ++- if (setup->wValue != 0 || setup->wLength != 0 +++ if (le16_to_cpu(setup->wValue) != 0 || le16_to_cpu(setup->wLength) != 0 ++ || ep_num > udc->max_eps) ++ goto out; ++ spin_unlock(&udc->lock); ++@@ -1692,7 +1708,7 @@ static void handle_setup_packet(struct m ++ /* delegate USB standard requests to the gadget driver */ ++ if (delegate == true) { ++ /* USB requests handled by gadget */ ++- if (setup->wLength) { +++ if (le16_to_cpu(setup->wLength)) { ++ /* DATA phase from gadget, STATUS phase from udc */ ++ udc->ep0_dir = (setup->bRequestType & USB_DIR_IN) ++ ? EP_DIR_IN : EP_DIR_OUT; ++@@ -1994,6 +2010,32 @@ static void irq_process_error(struct mv_ ++ udc->errors++; ++ } ++ +++static ATOMIC_NOTIFIER_HEAD(mv_udc_status_list); +++ +++int mv_udc_register_status_notify(struct notifier_block *nb) +++{ +++ int ret = 0; +++ +++ ret = atomic_notifier_chain_register(&mv_udc_status_list, nb); +++ if (ret) +++ return ret; +++ +++ return 0; +++ +++} +++EXPORT_SYMBOL(mv_udc_register_status_notify); +++ +++int mv_udc_unregister_status_notify(struct notifier_block *nb) +++{ +++ return atomic_notifier_chain_unregister(&mv_udc_status_list, nb); +++} +++EXPORT_SYMBOL(mv_udc_unregister_status_notify); +++ +++static void status_change(struct mv_udc *udc, int event) +++{ +++ atomic_notifier_call_chain(&mv_udc_status_list, event, NULL); +++} +++ ++ static irqreturn_t mv_udc_irq(int irq, void *dev) ++ { ++ struct mv_udc *udc = (struct mv_udc *)dev; ++@@ -2007,6 +2049,7 @@ static irqreturn_t mv_udc_irq(int irq, v ++ ++ status = readl(&udc->op_regs->usbsts); ++ intr = readl(&udc->op_regs->usbintr); +++ ++ status &= intr; ++ ++ if (status == 0) { ++@@ -2020,8 +2063,10 @@ static irqreturn_t mv_udc_irq(int irq, v ++ if (status & USBSTS_ERR) ++ irq_process_error(udc); ++ ++- if (status & USBSTS_RESET) +++ if (status & USBSTS_RESET){ ++ irq_process_reset(udc); +++ status_change(udc, 1); +++ } ++ ++ if (status & USBSTS_PORT_CHANGE) ++ irq_process_port_change(udc); ++@@ -2029,8 +2074,10 @@ static irqreturn_t mv_udc_irq(int irq, v ++ if (status & USBSTS_INT) ++ irq_process_tr_complete(udc); ++ ++- if (status & USBSTS_SUSPEND) +++ if (status & USBSTS_SUSPEND){ ++ irq_process_suspend(udc); +++ status_change(udc, 1); +++ } ++ ++ spin_unlock(&udc->lock); ++ ++@@ -2054,10 +2101,13 @@ static void mv_udc_vbus_work(struct work ++ unsigned int vbus; ++ ++ udc = container_of(work, struct mv_udc, vbus_work); ++- if (!udc->pdata->vbus) +++ if (udc->pdata && udc->pdata->vbus) +++ vbus = udc->pdata->vbus->poll(); +++ else if (gpio_is_valid(udc->vbus_pin)) +++ vbus = gpio_get_value(udc->vbus_pin); +++ else ++ return; ++ ++- vbus = udc->pdata->vbus->poll(); ++ dev_info(&udc->dev->dev, "vbus is %d\n", vbus); ++ ++ if (vbus == VBUS_HIGH) ++@@ -2101,6 +2151,12 @@ static int mv_udc_remove(struct platform ++ /* free dev, wait for the release() finished */ ++ wait_for_completion(udc->done); ++ +++ /* Power off PHY and exit */ +++ if (udc->utmi_phy) { +++ phy_power_off(udc->utmi_phy); +++ phy_exit(udc->utmi_phy); +++ } +++ ++ return 0; ++ } ++ ++@@ -2109,69 +2165,103 @@ static int mv_udc_probe(struct platform_ ++ struct mv_usb_platform_data *pdata = dev_get_platdata(&pdev->dev); ++ struct mv_udc *udc; ++ int retval = 0; ++- struct resource *r; +++ struct resource *capregs, *phyregs, *irq; ++ size_t size; ++ ++- if (pdata == NULL) { ++- dev_err(&pdev->dev, "missing platform_data\n"); ++- return -ENODEV; ++- } +++ struct clk *clk; +++ int err; ++ ++ udc = devm_kzalloc(&pdev->dev, sizeof(*udc), GFP_KERNEL); ++- if (udc == NULL) +++ if (!udc) ++ return -ENOMEM; ++ ++- udc->done = &release_done; ++- udc->pdata = dev_get_platdata(&pdev->dev); ++- spin_lock_init(&udc->lock); ++- ++- udc->dev = pdev; +++ /* udc only have one sysclk. */ +++ clk = devm_clk_get(&pdev->dev, NULL); +++ if (IS_ERR(clk)) +++ return PTR_ERR(clk); +++ +++ if (pdev->dev.of_node) { +++ udc->pdata = NULL; +++ +++ capregs = platform_get_resource(pdev, IORESOURCE_MEM, 0); +++ /* no phyregs for mvebu platform */ +++ phyregs = NULL; +++ +++ /* VBUS pin via GPIO */ +++ udc->vbus_pin = of_get_named_gpio(pdev->dev.of_node, "vbus-gpio", 0); +++ if (udc->vbus_pin < 0) +++ udc->vbus_pin = -ENODEV; +++ +++ /* Get comphy and init if there is */ +++ udc->utmi_phy = devm_of_phy_get(&pdev->dev, pdev->dev.of_node, "usb"); +++ if (!IS_ERR(udc->utmi_phy)) { +++ err = phy_init(udc->utmi_phy); +++ if (err) +++ goto disable_phys; +++ +++ err = phy_power_on(udc->utmi_phy); +++ if (err) { +++ phy_exit(udc->utmi_phy); +++ goto disable_phys; +++ } +++ } ++ ++- if (pdata->mode == MV_USB_MODE_OTG) { ++- udc->transceiver = devm_usb_get_phy(&pdev->dev, ++- USB_PHY_TYPE_USB2); ++- if (IS_ERR(udc->transceiver)) { ++- retval = PTR_ERR(udc->transceiver); +++ } else if (pdata) { +++ udc->pdata = pdev->dev.platform_data; +++ if (pdata->mode == MV_USB_MODE_OTG) { +++ udc->transceiver = devm_usb_get_phy(&pdev->dev, USB_PHY_TYPE_USB2); +++ if (IS_ERR(udc->transceiver)) { +++ retval = PTR_ERR(udc->transceiver); +++ if (retval == -ENXIO) +++ return retval; ++ ++- if (retval == -ENXIO) ++- return retval; +++ udc->transceiver = NULL; +++ return -EPROBE_DEFER; +++ } +++ } ++ ++- udc->transceiver = NULL; ++- return -EPROBE_DEFER; +++ capregs = platform_get_resource_byname(pdev, IORESOURCE_MEM, "capregs"); +++ phyregs = platform_get_resource_byname(pdev, IORESOURCE_MEM, "phyregs"); +++ if (!phyregs) { +++ dev_err(&pdev->dev, "no phy I/O memory resource defined\n"); +++ return -ENODEV; ++ } ++- } ++ ++- /* udc only have one sysclk. */ ++- udc->clk = devm_clk_get(&pdev->dev, NULL); ++- if (IS_ERR(udc->clk)) ++- return PTR_ERR(udc->clk); ++- ++- r = platform_get_resource_byname(udc->dev, IORESOURCE_MEM, "capregs"); ++- if (r == NULL) { ++- dev_err(&pdev->dev, "no I/O memory resource defined\n"); +++ /* platform data registration doesn't use the VBUS GPIO subsystem */ +++ udc->vbus_pin = -ENODEV; +++ +++ } else { +++ dev_err(&pdev->dev, "missing platform_data or of_node\n"); ++ return -ENODEV; ++ } ++ +++ /* set udc struct*/ +++ udc->done = &release_done; +++ udc->clk = clk; +++ udc->dev = pdev; +++ spin_lock_init(&udc->lock); +++ +++ if (!capregs) { +++ dev_err(&pdev->dev, "no capregs I/O memory resource defined\n"); +++ return -ENXIO; +++ } +++ ++ udc->cap_regs = (struct mv_cap_regs __iomem *) ++- devm_ioremap(&pdev->dev, r->start, resource_size(r)); ++- if (udc->cap_regs == NULL) { +++ devm_ioremap(&pdev->dev, capregs->start, resource_size(capregs)); +++ if (!udc->cap_regs) { ++ dev_err(&pdev->dev, "failed to map I/O memory\n"); ++ return -EBUSY; ++ } ++ ++- r = platform_get_resource_byname(udc->dev, IORESOURCE_MEM, "phyregs"); ++- if (r == NULL) { ++- dev_err(&pdev->dev, "no phy I/O memory resource defined\n"); ++- return -ENODEV; ++- } ++- ++- udc->phy_regs = devm_ioremap(&pdev->dev, r->start, resource_size(r)); ++- if (udc->phy_regs == NULL) { ++- dev_err(&pdev->dev, "failed to map phy I/O memory\n"); ++- return -EBUSY; +++ if (phyregs) { +++ udc->phy_regs = ioremap(phyregs->start, resource_size(capregs)); +++ if (!udc->phy_regs) { +++ dev_err(&pdev->dev, "failed to map phy I/O memory\n"); +++ return -EBUSY; +++ } ++ } ++ ++- /* we will acces controller register, so enable the clk */ +++ /* we will access controller register, so enable the clk */ ++ retval = mv_udc_enable_internal(udc); ++ if (retval) ++ return retval; ++@@ -2238,13 +2328,14 @@ static int mv_udc_probe(struct platform_ ++ udc->ep0_dir = EP_DIR_OUT; ++ udc->remote_wakeup = 0; ++ ++- r = platform_get_resource(udc->dev, IORESOURCE_IRQ, 0); ++- if (r == NULL) { +++ /* request irq */ +++ irq = platform_get_resource(udc->dev, IORESOURCE_IRQ, 0); +++ if (irq == NULL) { ++ dev_err(&pdev->dev, "no IRQ resource defined\n"); ++ retval = -ENODEV; ++ goto err_destroy_dma; ++ } ++- udc->irq = r->start; +++ udc->irq = irq->start; ++ if (devm_request_irq(&pdev->dev, udc->irq, mv_udc_irq, ++ IRQF_SHARED, driver_name, udc)) { ++ dev_err(&pdev->dev, "Request irq %d for UDC failed\n", ++@@ -2268,7 +2359,7 @@ static int mv_udc_probe(struct platform_ ++ /* VBUS detect: we can disable/enable clock on demand.*/ ++ if (udc->transceiver) ++ udc->clock_gating = 1; ++- else if (pdata->vbus) { +++ else if (pdata && pdata->vbus) { ++ udc->clock_gating = 1; ++ retval = devm_request_threaded_irq(&pdev->dev, ++ pdata->vbus->irq, NULL, ++@@ -2280,6 +2371,26 @@ static int mv_udc_probe(struct platform_ ++ udc->clock_gating = 0; ++ } ++ +++ } else if (gpio_is_valid(udc->vbus_pin)) { +++ udc->clock_gating = 1; +++ if (!devm_gpio_request(&pdev->dev, udc->vbus_pin, "mv-udc")) { +++ retval = devm_request_irq(&pdev->dev, gpio_to_irq(udc->vbus_pin), +++ mv_udc_vbus_irq, IRQ_TYPE_EDGE_BOTH, "mv-udc", udc); +++ if (retval) { +++ udc->vbus_pin = -ENODEV; +++ dev_warn(&pdev->dev, +++ "failed to request vbus irq; " +++ "assuming always on\n"); +++ } else +++ disable_irq(gpio_to_irq(udc->vbus_pin)); +++ } else { +++ /* gpio_request fail so use -EINVAL for gpio_is_valid */ +++ udc->vbus_pin = -EINVAL; +++ } +++ } +++ +++ /* if using VBUS interrupt, initialize work queue */ +++ if ((pdata && pdata->vbus) || gpio_is_valid(udc->vbus_pin)) { ++ udc->qwork = create_singlethread_workqueue("mv_udc_queue"); ++ if (!udc->qwork) { ++ dev_err(&pdev->dev, "cannot create workqueue\n"); ++@@ -2321,6 +2432,11 @@ err_free_dma: ++ udc->ep_dqh, udc->ep_dqh_dma); ++ err_disable_clock: ++ mv_udc_disable_internal(udc); +++disable_phys: +++ if (udc->utmi_phy) { +++ phy_power_off(udc->utmi_phy); +++ phy_exit(udc->utmi_phy); +++ } ++ ++ return retval; ++ } ++@@ -2336,7 +2452,7 @@ static int mv_udc_suspend(struct device ++ if (udc->transceiver) ++ return 0; ++ ++- if (udc->pdata->vbus && udc->pdata->vbus->poll) +++ if (udc->pdata && udc->pdata->vbus && udc->pdata->vbus->poll) ++ if (udc->pdata->vbus->poll() == VBUS_HIGH) { ++ dev_info(&udc->dev->dev, "USB cable is connected!\n"); ++ return -EAGAIN; ++@@ -2357,6 +2473,12 @@ static int mv_udc_suspend(struct device ++ mv_udc_disable_internal(udc); ++ } ++ +++ /* PHY exit if there is */ +++ if (udc->utmi_phy) { +++ phy_power_off(udc->utmi_phy); +++ phy_exit(udc->utmi_phy); +++ } +++ ++ return 0; ++ } ++ ++@@ -2371,6 +2493,20 @@ static int mv_udc_resume(struct device * ++ if (udc->transceiver) ++ return 0; ++ +++ /* PHY init if there is */ +++ if (udc->utmi_phy) { +++ retval = phy_init(udc->utmi_phy); +++ if (retval) +++ return retval; +++ +++ retval = phy_power_on(udc->utmi_phy); +++ if (retval) { +++ phy_power_off(udc->utmi_phy); +++ phy_exit(udc->utmi_phy); +++ return retval; +++ } +++ } +++ ++ if (!udc->clock_gating) { ++ retval = mv_udc_enable_internal(udc); ++ if (retval) ++@@ -2406,12 +2542,19 @@ static void mv_udc_shutdown(struct platf ++ mv_udc_disable(udc); ++ } ++ +++static const struct of_device_id mv_udc_dt_match[] = { +++ { .compatible = "marvell,mv-udc" }, +++ {}, +++}; +++MODULE_DEVICE_TABLE(of, mv_udc_dt_match); +++ ++ static struct platform_driver udc_driver = { ++ .probe = mv_udc_probe, ++ .remove = mv_udc_remove, ++ .shutdown = mv_udc_shutdown, ++ .driver = { ++ .name = "mv-udc", +++ .of_match_table = of_match_ptr(mv_udc_dt_match), ++ #ifdef CONFIG_PM ++ .pm = &mv_udc_pm_ops, ++ #endif ++@@ -2422,4 +2565,5 @@ module_platform_driver(udc_driver); ++ MODULE_ALIAS("platform:mv-udc"); ++ MODULE_DESCRIPTION(DRIVER_DESC); ++ MODULE_AUTHOR("Chao Xie "); +++MODULE_VERSION(DRIVER_VERSION); ++ MODULE_LICENSE("GPL"); ++--- /dev/null +++++ b/drivers/usb/gadget/udc/mvebu_glue.c ++@@ -0,0 +1,189 @@ +++/* +++ * Copyright (C) 2013 Marvell International Ltd. All rights reserved. +++ * +++ * This program is free software; you can redistribute it and/or modify it +++ * under the terms and conditions of the GNU General Public License, +++ * version 2, as published by the Free Software Foundation. +++ */ +++ +++#include +++#include +++#include +++#include +++#include +++#include +++#include +++#include +++#include +++#include +++#include +++#include +++ +++#include "mvebu_u3d.h" +++ +++#define CONNECTION_MAX_NUM 3 +++ +++struct mvc2_glue glue; +++static struct work_struct glue_work; +++static DEFINE_MUTEX(work_lock); +++struct usb_udc *udc_detect(struct list_head *udc_list, +++ struct usb_gadget_driver *driver) +++{ +++ struct usb_udc *udc20, *udc30, *udc; +++ struct mvc2 *cp; +++ +++ udc20 = udc30 = NULL; +++ list_for_each_entry(udc, udc_list, list) { +++ if (strncmp(udc->gadget->name, "mv_udc", 6) == 0) +++ udc20 = udc; +++ +++ if (strncmp(udc->gadget->name, "mvebu-u3d", 9) == 0) +++ udc30 = udc; +++ } +++ +++ /* We need at least 3.0 controller driver being installed! */ +++ if (!udc30) { +++ pr_err("Failed to detect usb3 device!\n"); +++ return NULL; +++ } +++ +++ cp = container_of(udc30->gadget, struct mvc2, gadget); +++ cp->work = &glue_work; +++ glue.u20 = udc20; +++ glue.u30 = udc30; +++ +++ if (glue.usb2_connect) +++ return udc20; +++ else +++ return udc30; +++} +++ +++void mvc2_usb2_connect(void) +++{ +++ struct mvc2 *cp; +++ struct usb_udc *u30 = glue.u30; +++ struct usb_gadget_driver *driver = u30->driver; +++ struct usb_gadget *u3d = u30->gadget; +++ +++ cp = container_of(u3d, struct mvc2, gadget); +++ pr_info("USB device: USB2.0 connected\n"); +++ /* +++ * add de-bounce for usb cable plug +++ */ +++ msleep(200); +++ if (mvc2_checkvbus(cp) == 0) { +++ pr_info("USB device: power off\n"); +++ return; +++ } +++ +++ /* +++ * The de-bounce time added before just can filter +++ * most cases but not all. +++ * The power off interrupt still has chance to break +++ * this workqueue. +++ * So we disable the USB3 irq here to guarantee this +++ * workqueue will not be interrupted by USB3 interrupt anymore, +++ * such as the power off interrupt, until all of the works have +++ * been done. +++ * The power off interrupt may happen when +++ * the USB3 irq was disabled. +++ * We hope this interrupt still there once +++ * we enabled USB3 irq again. +++ * To achieve this, need to keep the corresponding +++ * interrupt status(refer to mvc2_pullup, +++ * don't clear the ref int status register). +++ * Note, during the USB3 irq disabled, there may be +++ * may times plug/unplug, thus, the power on/off interrupt +++ * may co-exisit once enable the irq again. +++ * To avoid this, we need to check the VBUS of the final state, +++ * please refer to mvc2_irq. +++ */ +++ +++ disable_irq(cp->irq); +++ +++ glue.usb2_connect = 1; +++ usb_gadget_unregister_driver(driver); +++ usb_gadget_probe_driver(driver); +++ +++ enable_irq(cp->irq); +++ +++} +++ +++void mvc2_usb2_disconnect(void) +++{ +++ struct mvc2 *cp; +++ struct usb_udc *u30 = glue.u30; +++ struct usb_gadget *u3d = u30->gadget; +++ struct usb_udc *u20 = glue.u20; +++ struct usb_gadget_driver *driver = u20->driver; +++ int has_setup = 0; +++ +++ cp = container_of(u3d, struct mvc2, gadget); +++ +++ if (u20->driver) +++ driver = u20->driver; +++ else if (u30->driver) { +++ driver = u30->driver; +++ return; +++ } +++ +++ pr_info("USB device: USB2.0 disconnected\n"); +++ glue.usb2_connect = 0; +++ usb_gadget_unregister_driver(driver); +++ disable_irq(cp->irq); +++ usb3_disconnect = false; +++ +++ if (ioread32(cp->base + MVCP_SS_CORE_INT) & MVCP_SS_CORE_INT_SETUP) +++ has_setup = 1; +++ usb_gadget_probe_driver(driver); +++ usb3_disconnect = true; +++ enable_irq(cp->irq); +++ if (has_setup) +++ mvc2_handle_setup(cp); +++ +++} +++ +++static int +++u20_status_change(struct notifier_block *this, unsigned long event, void *ptr) +++{ +++ struct mvc2 *cp; +++ struct usb_gadget *u30 = glue.u30->gadget; +++ +++ cp = container_of(u30, struct mvc2, gadget); +++ +++ mvc2_usb2_operation(cp, event); +++ +++ return NOTIFY_DONE; +++} +++ +++static struct notifier_block u20_status = { +++ .notifier_call = u20_status_change, +++}; +++ +++void mv_connect_work(struct work_struct *work) +++{ +++ struct mvc2 *cp; +++ struct usb_gadget *u30 = glue.u30->gadget; +++ +++ cp = container_of(u30, struct mvc2, gadget); +++ +++ mutex_lock(&work_lock); +++ +++ if (glue.status & MVCP_STATUS_USB2) +++ mvc2_usb2_connect(); +++ else +++ mvc2_usb2_disconnect(); +++ +++ mutex_unlock(&work_lock); +++} +++ +++static int __init mvc2_glue_init(void) +++{ +++ glue.u20 = glue.u30 = NULL; +++ glue.usb2_connect = 0; +++ mv_udc_register_status_notify(&u20_status); +++ INIT_WORK(&glue_work, mv_connect_work); +++ return 0; +++} +++ +++device_initcall(mvc2_glue_init); ++--- /dev/null +++++ b/drivers/usb/gadget/udc/mvebu_u3d.c ++@@ -0,0 +1,2626 @@ +++/* +++ * Copyright (C) 2013 Marvell International Ltd. All rights reserved. +++ * +++ * This program is free software; you can redistribute it and/or modify it +++ * under the terms and conditions of the GNU General Public License, +++ * version 2, as published by the Free Software Foundation. +++ */ +++ +++#include +++#include +++#include +++#include +++#include +++#include +++#include +++#include +++#include +++#include +++#include +++#include +++#include +++#include +++#include +++#include +++#include +++#include +++#include +++#include +++#include +++#include +++#include +++#include +++#include +++#include +++#include +++#include +++#include +++#include +++#include +++#include +++#include +++#include +++#include +++#include +++#include +++#include +++ +++#include "mvebu_u3d.h" +++ +++#define DRIVER_DESC "Marvell Central IP USB3.0 Device Controller driver" +++ +++static unsigned int u1u2; +++module_param(u1u2, uint, S_IRUGO | S_IWUSR); +++MODULE_PARM_DESC(u1u2, "u1u2 enable"); +++ +++static const char driver_desc[] = DRIVER_DESC; +++ +++unsigned int u1u2_enabled(void) +++{ +++ return u1u2; +++} +++ +++#define EP0_MAX_PKT_SIZE 512 +++ +++/* for endpoint 0 operations */ +++static const struct usb_endpoint_descriptor mvc2_ep0_out_desc = { +++ .bLength = USB_DT_ENDPOINT_SIZE, +++ .bDescriptorType = USB_DT_ENDPOINT, +++ .bEndpointAddress = USB_DIR_OUT, +++ .bmAttributes = USB_ENDPOINT_XFER_CONTROL, +++ .wMaxPacketSize = EP0_MAX_PKT_SIZE, +++}; +++ +++static const struct usb_endpoint_descriptor mvc2_ep0_in_desc = { +++ .bLength = USB_DT_ENDPOINT_SIZE, +++ .bDescriptorType = USB_DT_ENDPOINT, +++ .bEndpointAddress = USB_DIR_IN, +++ .bmAttributes = USB_ENDPOINT_XFER_CONTROL, +++ .wMaxPacketSize = EP0_MAX_PKT_SIZE, +++}; +++ +++static struct usb_ss_ep_comp_descriptor ep0_comp = { +++ .bLength = sizeof(ep0_comp), +++ .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, +++}; +++ +++static int mvc2_ep0_handle_status(struct mvc2 *cp, struct usb_ctrlrequest *ctrl) +++{ +++ unsigned int recip; +++ u16 usb_status = 0, lowpower; +++ __le16 *response_pkt; +++ int num, dir; +++ struct mvc2_ep *ep; +++ +++ recip = ctrl->bRequestType & USB_RECIP_MASK; +++ switch (recip) { +++ case USB_RECIP_DEVICE: +++ /* +++ * LTM will be set once we know how to set this in HW. +++ */ +++ if (cp->status & MVCP_STATUS_SELF_POWERED) +++ usb_status |= USB_DEVICE_SELF_POWERED; +++ +++ lowpower = MV_CP_READ(MVCP_LOWPOWER); +++ if (lowpower & MVCP_LOWPOWER_U1_EN) +++ usb_status |= 1 << USB_DEV_STAT_U1_ENABLED; +++ +++ if (lowpower & MVCP_LOWPOWER_U2_EN) +++ usb_status |= 1 << USB_DEV_STAT_U2_ENABLED; +++ +++ break; +++ +++ case USB_RECIP_INTERFACE: +++ /* +++ * Function Remote Wake Capable D0 +++ * Function Remote Wakeup D1 +++ */ +++ break; +++ +++ case USB_RECIP_ENDPOINT: +++ +++ num = ctrl->wIndex & USB_ENDPOINT_NUMBER_MASK; +++ dir = ctrl->wIndex & USB_DIR_IN; +++ ep = &cp->eps[2 * num + !!dir]; +++ +++ if (ep->state & MV_CP_EP_STALL) +++ usb_status = 1 << USB_ENDPOINT_HALT; +++ break; +++ default: +++ return -EINVAL; +++ } +++ +++ response_pkt = (__le16 *) cp->setup_buf; +++ *response_pkt = cpu_to_le16(usb_status); +++ +++ return sizeof(*response_pkt); +++} +++ +++static void enable_lowpower(struct mvc2 *cp, unsigned int lowpower, int on) +++{ +++ unsigned int val, state; +++ +++ val = MV_CP_READ(MVCP_LOWPOWER); +++ if (lowpower == USB_DEVICE_U1_ENABLE) +++ state = MVCP_LOWPOWER_U1_EN; +++ else +++ state = MVCP_LOWPOWER_U2_EN; +++ +++ if (on) +++ val |= state; +++ else +++ val &= ~state; +++ +++ if (u1u2_enabled()) +++ MV_CP_WRITE(val, MVCP_LOWPOWER); +++} +++ +++static int mvc2_ep0_handle_feature(struct mvc2 *cp, +++ struct usb_ctrlrequest *ctrl, int set) +++{ +++ u32 wValue, wIndex, recip; +++ int ret = -EINVAL; +++ int num, dir; +++ struct mvc2_ep *ep; +++ unsigned long flags; +++ +++ wValue = le16_to_cpu(ctrl->wValue); +++ wIndex = le16_to_cpu(ctrl->wIndex); +++ recip = ctrl->bRequestType & USB_RECIP_MASK; +++ +++ switch (recip) { +++ case USB_RECIP_DEVICE: +++ switch (wValue) { +++ case USB_DEVICE_REMOTE_WAKEUP: +++ ret = 0; +++ break; +++ +++ case USB_DEVICE_U1_ENABLE: +++ case USB_DEVICE_U2_ENABLE: +++ if (cp->dev_state != MVCP_CONFIGURED_STATE) { +++ ret = -EINVAL; +++ break; +++ } +++ +++ ret = 0; +++ +++ enable_lowpower(cp, wValue, set); +++ break; +++ case USB_DEVICE_TEST_MODE: +++ if (set && (wIndex & 0xff)) +++ cp->status |= MVCP_STATUS_TEST(wIndex >> 8); +++ break; +++ } +++ break; +++ case USB_RECIP_INTERFACE: +++ switch (wValue) { +++ case USB_INTRF_FUNC_SUSPEND: +++ ret = 0; +++ } +++ break; +++ case USB_RECIP_ENDPOINT: +++ switch (wValue) { +++ case USB_ENDPOINT_HALT: +++ num = wIndex & USB_ENDPOINT_NUMBER_MASK; +++ dir = wIndex & USB_DIR_IN; +++ ep = &cp->eps[2 * num + !!dir]; +++ if (!set) { +++ spin_lock_irqsave(&ep->lock, flags); +++ reset_seqencenum(ep, num, dir); +++ spin_unlock_irqrestore(&ep->lock, flags); +++ if (!(ep->state & MV_CP_EP_WEDGE)) +++ usb_ep_clear_halt(&ep->ep); +++ } else +++ usb_ep_set_halt(&ep->ep); +++ ret = 0; +++ } +++ } +++ +++ return ret; +++} +++ +++static void mvcp_ep0_set_sel_cmpl(struct usb_ep *ep, struct usb_request *req) +++{ +++ struct mvc2_ep *_ep = container_of(ep, struct mvc2_ep, ep); +++ struct mvc2 *cp = _ep->cp; +++ struct timing { +++ u8 u1sel; +++ u8 u1pel; +++ u16 u2sel; +++ u16 u2pel; +++ } __packed timing; +++ +++ memcpy(&timing, req->buf, sizeof(timing)); +++ cp->u1sel = timing.u1sel; +++ cp->u1pel = timing.u1pel; +++ cp->u2sel = le16_to_cpu(timing.u2sel); +++ cp->u2pel = le16_to_cpu(timing.u2pel); +++} +++ +++int mvc2_std_request(struct mvc2 *cp, struct usb_ctrlrequest *r, +++ bool *delegate) +++{ +++ int ret = 0; +++ struct usb_request *req; +++ u16 wLength = le16_to_cpu(r->wLength); +++ u16 wValue = le16_to_cpu(r->wValue); +++ u16 wIndex = le16_to_cpu(r->wIndex); +++ +++ *delegate = true; +++ req = &cp->ep0_req.req; +++ switch (r->bRequest) { +++ case USB_REQ_SET_ADDRESS: +++ if (r->bRequestType != (USB_DIR_OUT | USB_RECIP_DEVICE)) +++ break; +++ +++ *delegate = false; +++ if (wValue > 127) { +++ dev_dbg(cp->dev, "invalid device address %d\n", wValue); +++ break; +++ } +++ +++ if (cp->dev_state == MVCP_CONFIGURED_STATE) { +++ dev_dbg(cp->dev, +++ "trying to set address when configured\n"); +++ break; +++ } +++ +++ if (wValue) +++ cp->dev_state = MVCP_ADDRESS_STATE; +++ else +++ cp->dev_state = MVCP_DEFAULT_STATE; +++ break; +++ case USB_REQ_GET_STATUS: +++ if (r->bRequestType != (USB_DIR_IN | USB_RECIP_DEVICE) && +++ r->bRequestType != (USB_DIR_IN | USB_RECIP_ENDPOINT) && +++ r->bRequestType != (USB_DIR_IN | USB_RECIP_INTERFACE)) +++ break; +++ +++ ret = mvc2_ep0_handle_status(cp, r); +++ *delegate = false; +++ +++ break; +++ case USB_REQ_CLEAR_FEATURE: +++ case USB_REQ_SET_FEATURE: +++ if (r->bRequestType != (USB_DIR_OUT | USB_RECIP_DEVICE) && +++ r->bRequestType != (USB_DIR_OUT | USB_RECIP_ENDPOINT) && +++ r->bRequestType != (USB_DIR_OUT | USB_RECIP_INTERFACE)) +++ break; +++ +++ ret = mvc2_ep0_handle_feature(cp, r, +++ r->bRequest == +++ USB_REQ_SET_FEATURE); +++ *delegate = false; +++ break; +++ case USB_REQ_SET_CONFIGURATION: +++ switch (cp->dev_state) { +++ case MVCP_DEFAULT_STATE: +++ break; +++ case MVCP_ADDRESS_STATE: +++ if (wValue) { +++ enable_lowpower(cp, USB_DEVICE_U1_ENABLE, 0); +++ enable_lowpower(cp, USB_DEVICE_U2_ENABLE, 0); +++ cp->dev_state = MVCP_CONFIGURED_STATE; +++ } +++ break; +++ case MVCP_CONFIGURED_STATE: +++ if (!wValue) +++ cp->dev_state = MVCP_ADDRESS_STATE; +++ break; +++ } +++ break; +++ case USB_REQ_SET_SEL: +++ *delegate = false; +++ if (cp->dev_state == MVCP_DEFAULT_STATE) +++ break; +++ +++ if (wLength == 6) { +++ ret = wLength; +++ req->complete = mvcp_ep0_set_sel_cmpl; +++ } +++ break; +++ case USB_REQ_SET_ISOCH_DELAY: +++ *delegate = false; +++ if (!wIndex && !wLength) { +++ ret = 0; +++ cp->isoch_delay = wValue; +++ } +++ break; +++ } +++ +++ if (ret > 0) { +++ req->length = ret; +++ req->zero = ret < wLength; +++ req->buf = cp->setup_buf; +++ ret = usb_ep_queue(cp->gadget.ep0, req, GFP_ATOMIC); +++ } +++ +++ if (ret < 0) +++ *delegate = false; +++ +++ return ret; +++} +++ +++int eps_init(struct mvc2 *cp) +++{ +++ struct mvc2_ep *ep; +++ int i, j, ret; +++ struct bd *bd; +++ unsigned int phys, bd_interval; +++ +++ bd_interval = sizeof(struct bd); +++ +++ /* initialize endpoints */ +++ for (i = 0; i < cp->epnum * 2; i++) { +++ ep = &cp->eps[i]; +++ ep->ep.name = ep->name; +++ ep->cp = cp; +++ INIT_LIST_HEAD(&ep->queue); +++ INIT_LIST_HEAD(&ep->wait); +++ INIT_LIST_HEAD(&ep->tmp); +++ spin_lock_init(&ep->lock); +++ +++ if (i < 2) { +++ +++ strncpy(ep->name, "ep0", MAXNAME); +++ usb_ep_set_maxpacket_limit(&ep->ep, EP0_MAX_PKT_SIZE); +++ ep->ep.desc = (i) ? &mvc2_ep0_in_desc : +++ &mvc2_ep0_out_desc; +++ ep->ep.comp_desc = &ep0_comp; +++ ep->bd_sz = MAX_QUEUE_SLOT; +++ ep->left_bds = MAX_QUEUE_SLOT; +++ ep->dir = i ? 1 : 0; +++ if (ep->dir == 1) +++ ep->ep.caps.dir_in = true; +++ else +++ ep->ep.caps.dir_out = true; +++ ep->ep.caps.type_control = true; +++ +++ } else { +++ if (i & 0x1) { +++ ep->dir = 1; +++ snprintf(ep->name, MAXNAME, "ep%din", i >> 1); +++ ep->ep.caps.dir_in = true; +++ } else { +++ ep->dir = 0; +++ snprintf(ep->name, MAXNAME, "ep%dout", i >> 1); +++ ep->ep.caps.dir_out = true; +++ } +++ usb_ep_set_maxpacket_limit(&ep->ep, (unsigned short) ~0); +++ ep->bd_sz = MAX_QUEUE_SLOT; +++ ep->left_bds = MAX_QUEUE_SLOT; +++ ep->ep.caps.type_iso = true; +++ ep->ep.caps.type_bulk = true; +++ ep->ep.caps.type_int = true; +++ } +++ +++ ep->ep_num = i / 2; +++ +++ ep->doneq_start = dma_alloc_coherent(cp->dev, +++ sizeof(struct doneq) * +++ ep->bd_sz, +++ &ep->doneq_start_phys, +++ GFP_KERNEL); +++ if (ep->doneq_start == NULL) { +++ dev_err(cp->dev, "failed to allocate doneq buffer!\n"); +++ return -ENOMEM; +++ } +++ +++ ep->bd_ring = dma_alloc_coherent(cp->dev, +++ sizeof(struct bd) * ep->bd_sz, +++ &ep->bd_ring_phys, GFP_KERNEL); +++ if (ep->bd_ring == NULL) { +++ dev_err(cp->dev, "failed to allocate bd buffer!\n"); +++ return -ENOMEM; +++ } +++ bd = (struct bd *)ep->bd_ring; +++ phys = ep->bd_ring_phys; +++ /* Generate the TransferQ ring */ +++ for (j = 0; j < ep->bd_sz - 1; j++) { +++ phys += bd_interval; +++ bd->phys_next = phys; +++ bd->cmd = 0; +++ if (ip_ver(cp) < USB3_IP_VER_A0) +++ bd->cmd = BD_NXT_PTR_JUMP; +++ bd++; +++ } +++ bd->cmd = 0; +++ if (ip_ver(cp) < USB3_IP_VER_A0) +++ bd->cmd = BD_NXT_PTR_JUMP; +++ bd->phys_next = ep->bd_ring_phys; +++ } +++ +++ cp->setup_buf = kzalloc(EP0_MAX_PKT_SIZE, GFP_KERNEL); +++ if (!cp->setup_buf) +++ ret = -ENOMEM; +++ +++ return 0; +++} +++ +++#define CREATE_TRACE_POINTS +++/* #define ASSEMBLE_REQ */ +++ +++static const char driver_name[] = "mvebu-u3d"; +++ +++#define ep_dir(ep) (((ep)->dir)) +++ +++static bool irq_enabled; +++bool usb3_disconnect = true; +++ +++/* return the actual ep number */ +++static int ip_ep_num(struct mvc2 *cp) +++{ +++ return MVCP_EP_COUNT; +++} +++ +++static void done(struct mvc2_ep *ep, struct mvc2_req *req, int status); +++static void nuke(struct mvc2_ep *ep, int status); +++static void stop_activity(struct mvc2 *udc, struct usb_gadget_driver *driver); +++ +++static void set_top_int(struct mvc2 *cp, unsigned int val) +++{ +++ if (ip_ver(cp) >= USB3_IP_VER_Z2) +++ MV_CP_WRITE(val, MVCP_TOP_INT_EN); +++} +++ +++static void ep_dma_enable(struct mvc2 *cp, int num, int dir, int enable) +++{ +++ unsigned int tmp, val, reg; +++ +++ if (ip_ver(cp) <= USB3_IP_VER_Z2) { +++ tmp = (dir) ? 0x10000 : 0x1; +++ tmp = tmp << num; +++ reg = MVCP_DMA_ENABLE; +++ } else { +++ tmp = DONEQ_CONFIG; +++ if (dir) +++ reg = SS_IN_DMA_CONTROL_REG(num); +++ else +++ reg = SS_OUT_DMA_CONTROL_REG(num); +++ } +++ +++ val = MV_CP_READ(reg); +++ if (enable) +++ MV_CP_WRITE(val | tmp, reg); +++ else +++ MV_CP_WRITE(val & ~tmp, reg); +++} +++ +++static void ep_dma_struct_init(struct mvc2 *cp, +++ struct mvc2_ep *ep, int num, int dir) +++{ +++ dma_addr_t addr; +++ +++ addr = ep->doneq_start_phys + sizeof(struct doneq) * (ep->bd_sz - 1); +++ MV_CP_WRITE(ep->bd_ring_phys, ep_dma_addr(num, dir)); +++ +++ ep_dma_enable(cp, num, dir, 0); +++ MV_CP_WRITE(ep->doneq_start_phys, ep_doneq_start(num, dir)); +++ MV_CP_WRITE(ep->doneq_start_phys, ep_doneq_read(num, dir)); +++ MV_CP_WRITE(addr, ep_doneq_end(num, dir)); +++ ep_dma_enable(cp, num, dir, 1); +++} +++ +++/* Need to be included in ep lock protection */ +++static void mvc2_dma_reset(struct mvc2 *cp, +++ struct mvc2_ep *ep, int num, int dir) +++{ +++ unsigned int epbit, val, creg, sreg; +++ int timeout = 10000; +++ struct mvc2_req *req, *tmp; +++ unsigned long flags; +++ +++ spin_lock_irqsave(&ep->lock, flags); +++ if (ip_ver(cp) <= USB3_IP_VER_Z2) { +++ epbit = EPBIT(num, dir); +++ MV_CP_WRITE(epbit, MVCP_DMA_HALT); +++ while ((!(MV_CP_READ(MVCP_DMA_HALT_DONE) & +++ epbit)) && timeout-- > 0) +++ cpu_relax(); +++ MV_CP_WRITE(epbit, MVCP_DMA_HALT_DONE); +++ } else { +++ if (dir) { +++ creg = SS_IN_DMA_CONTROL_REG(num); +++ sreg = SS_IN_EP_INT_STATUS_REG(num); +++ } else { +++ creg = SS_OUT_DMA_CONTROL_REG(num); +++ sreg = SS_OUT_EP_INT_STATUS_REG(num); +++ } +++ val = MV_CP_READ(creg); +++ val |= DMA_HALT; +++ MV_CP_WRITE(val, creg); +++ while ((!(MV_CP_READ(sreg) & DMA_HALT_DONE)) && timeout-- > 0) +++ cpu_relax(); +++ MV_CP_WRITE(DMA_HALT_DONE, sreg); +++ } +++ +++ if (timeout <= 0) { +++ pr_info("### dma reset timeout, num = %d, dir = %d\n", num, +++ dir); +++ WARN_ON(1); +++ } +++ +++ list_for_each_entry_safe(req, tmp, &ep->queue, queue) +++ done(ep, req, -ESHUTDOWN); +++ +++ ep->bd_cur = ep->doneq_cur = 0; +++ ep_dma_struct_init(cp, &cp->eps[2 * num + !!dir], num, dir); +++ spin_unlock_irqrestore(&ep->lock, flags); +++} +++ +++static struct usb_request *mvc2_alloc_request(struct usb_ep *_ep, +++ gfp_t gfp_flags) +++{ +++ struct mvc2_req *req = NULL; +++ +++ req = kzalloc(sizeof(*req), gfp_flags); +++ if (!req) +++ return NULL; +++ +++ memset(req, 0, sizeof(*req)); +++ INIT_LIST_HEAD(&req->queue); +++ return &req->req; +++} +++ +++static void mvc2_free_request(struct usb_ep *_ep, struct usb_request *_req) +++{ +++ struct mvc2_ep *ep = container_of(_ep, struct mvc2_ep, ep); +++ struct mvc2_req *req = container_of(_req, struct mvc2_req, req); +++ unsigned long flags; +++ +++ spin_lock_irqsave(&ep->lock, flags); +++ list_del_init(&req->queue); +++ spin_unlock_irqrestore(&ep->lock, flags); +++ kfree(req); +++} +++ +++static int +++alloc_one_bd_chain(struct mvc2 *cp, struct mvc2_ep *ep, struct mvc2_req *req, +++ int num, int dir, dma_addr_t dma, unsigned length, +++ unsigned offset, unsigned *last) +++{ +++ unsigned int bd_num, remain, bd_cur, len, buf; +++ struct bd *bd; +++ int left_bds, cur_bd; +++ +++ remain = length - offset; +++ +++ /* In the zero length packet case, we still need one BD to make it happen */ +++ if (remain) +++ bd_num = (remain + BD_MAX_SIZE - 1) >> BD_SEGMENT_SHIFT; +++ else +++ bd_num = 1; +++ +++ bd_cur = ep->bd_cur; +++ left_bds = ep->left_bds; +++ if (left_bds == 0) +++ goto no_bds; +++ if (bd_num > left_bds) +++ goto no_bds; +++ ep->left_bds -= bd_num; +++ WARN_ON(ep->left_bds > ep->bd_sz); +++ +++ ep->bd_cur += bd_num; +++ if (ep->bd_cur >= ep->bd_sz) +++ ep->bd_cur -= ep->bd_sz; +++ +++ ep->state |= MV_CP_EP_TRANSERING; +++ req->bd_total += bd_num; +++ buf = (unsigned int)dma; +++ /* +++ * format BD chains: +++ * BD_NXT_RDY make a BD chain segment, and +++ * one BD chain segment is natually one usb_request. +++ * But with exception that if current number of BD +++ * cannot fulfill usb_request, so that we may divide +++ * one request into several segments, so that it could +++ * complete gradually. +++ * DMA engine would never cache across two segments +++ * without MVCP_EPDMA_START being set, which indicate +++ * new BD segment is coming. +++ */ +++ cur_bd = bd_num; +++ do { +++ if (remain > BD_MAX_SIZE) +++ len = BD_MAX_SIZE; +++ else { +++ /* +++ * HW require out ep's BD length is 1024 aligned, +++ * or there is problem in receiving the compelte interrupt +++ */ +++ len = remain; +++ if (!dir && (len & 0x3ff)) +++ len = ((len + 0x3ff) >> 10) << 10; +++ } +++ remain -= len; +++ +++ bd = ep->bd_ring + bd_cur; +++ +++ bd_cur++; +++ if (bd_cur == ep->bd_sz) +++ bd_cur = 0; +++ +++ if (!offset) +++ req->bd = bd; +++ +++ /* +++ * There are three method to indicate one bd is finished +++ * 1. Receive the short packet which is less than 1024 +++ * 2. Receive the zero length packet +++ * 3. Receive the data length equal to size set by BD +++ */ +++ bd->cmd = BD_NXT_RDY | BD_BUF_RDY | BD_BUF_SZ(len); +++ if (ip_ver(cp) < USB3_IP_VER_A0) +++ bd->cmd |= BD_NXT_PTR_JUMP; +++ bd->buf = (unsigned int)dma + offset; +++ +++ offset += len; +++ } while (--cur_bd > 0); +++ +++ if (*last) { +++ /* Only raise the interrupt at the last bd */ +++#ifndef ASSEMBLE_REQ +++#if 0 +++ /* due to usb2 rx interrupt optimization, no_interrupt is +++ * is always 1. Due to HW bug, this is currently irrelevant for +++ * our case since an interrupt will be returned regardless of +++ * BD_INT_EN. +++ */ +++ if (!req->req.no_interrupt) +++#endif +++#endif +++ bd->cmd |= BD_INT_EN; +++ /* At the end of one segment, clear the BD_NXT_RDY */ +++ bd->cmd &= ~BD_NXT_RDY; +++ } +++ *last = left_bds; +++ +++ return bd_num; +++no_bds: +++ WARN_ON(ep->ep_num == 0); +++ return 0; +++} +++ +++static int alloc_bds(struct mvc2 *cp, struct mvc2_ep *ep, struct mvc2_req *req) +++{ +++ dma_addr_t dma; +++ unsigned length, bd_num, actual; +++ struct usb_request *request = &req->req; +++ int num, dir, last; +++ +++ bd_num = 0; +++ actual = req->req.actual; +++ num = ep->ep.desc->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK; +++ dir = ep->ep.desc->bEndpointAddress & USB_DIR_IN; +++ +++ req->bd_total = 0; +++ last = 1; +++ if (req->req.num_mapped_sgs > 0) { +++ struct scatterlist *sg = request->sg; +++ struct scatterlist *s; +++ int i; +++ +++ last = 0; +++ for_each_sg(sg, s, request->num_mapped_sgs, i) { +++ length = sg_dma_len(s); +++ if (actual >= length) { +++ actual -= length; +++ continue; +++ } +++ +++ actual += sg->offset; +++ dma = sg_dma_address(s); +++ if (sg_is_last(s)) +++ last = 1; +++ +++ bd_num = alloc_one_bd_chain(cp, ep, req, num, dir, +++ dma, length, actual, &last); +++ if (last > 1) +++ last = 0; +++ if (!bd_num) +++ break; +++ } +++ } else { +++ dma = req->req.dma; +++ length = req->req.length; +++ +++ bd_num = alloc_one_bd_chain(cp, ep, req, num, dir, +++ dma, length, actual, &last); +++ } +++ +++ if (bd_num) +++ list_add_tail(&req->queue, &ep->queue); +++ +++ return bd_num; +++} +++ +++#ifdef ASSEMBLE_REQ +++static int +++alloc_in_bds(struct mvc2 *cp, struct mvc2_ep *ep, struct mvc2_req *req, +++ int *last) +++{ +++ dma_addr_t dma; +++ unsigned length, bd_num, actual; +++ struct usb_request *request = &req->req; +++ int num, dir; +++ +++ bd_num = 0; +++ actual = req->req.actual; +++ num = ep->ep.desc->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK; +++ dir = ep->ep.desc->bEndpointAddress & USB_DIR_IN; +++ +++ req->bd_total = 0; +++ +++ dma = req->req.dma; +++ length = req->req.length; +++ +++ bd_num = alloc_one_bd_chain(cp, ep, req, num, dir, +++ dma, length, actual, last); +++ +++ list_add_tail(&req->queue, &ep->queue); +++ +++ return bd_num; +++} +++#endif +++ +++static inline void +++mvc2_ring_incoming(struct mvc2 *cp, unsigned int num, unsigned dir) +++{ +++ unsigned int reg; +++ +++ /* Ensure that updates to the EP Context will occur before Ring Bell */ +++ wmb(); +++ +++ /* Ring the data incoming bell to ask hw to reload the bd chain */ +++ if (ip_ver(cp) <= USB3_IP_VER_Z2) { +++ MV_CP_WRITE(MVCP_EPDMA_START, ep_dma_config(num, dir)); +++ } else { +++ if (dir) +++ reg = SS_IN_DMA_CONTROL_REG(num); +++ else +++ reg = SS_OUT_DMA_CONTROL_REG(num); +++ MV_CP_WRITE(MV_CP_READ(reg) | DMA_START, reg); +++ } +++} +++ +++static void ep_enable(struct mvc2_ep *ep, int num, int in, int type) +++{ +++ struct mvc2 *cp = ep->cp; +++ struct usb_ep *_ep = &ep->ep; +++ unsigned int config, val, config_base; +++ struct mvc2_req *req, *tmp; +++ unsigned long flags, ring, reg; +++ +++ /* We suppose there is no item in run queue here */ +++ WARN_ON(!list_empty(&ep->queue)); +++ +++ ring = ep->state = 0; +++ config_base = epcon(num, in); +++ config = MVCP_EP_MAX_PKT(_ep->desc->wMaxPacketSize); +++ if (_ep->comp_desc) +++ config |= MVCP_EP_BURST(_ep->comp_desc->bMaxBurst); +++ +++ if (num) { +++ config |= MVCP_EP_ENABLE | MVCP_EP_NUM(num); +++ switch (type) { +++ case USB_ENDPOINT_XFER_BULK: +++ if (_ep->comp_desc && +++ _ep->comp_desc->bmAttributes & 0x1f) +++ ep->state |= MV_CP_EP_BULK_STREAM; +++ else +++ ep->state &= ~MV_CP_EP_BULK_STREAM; +++ +++ config |= MVCP_EP_TYPE_BLK; +++ +++ /* Enable bulk stream if need */ +++ spin_lock_irqsave(&cp->lock, flags); +++ if (ip_ver(cp) <= USB3_IP_VER_Z2) { +++ val = MV_CP_READ(MVCP_BULK_STREAMING_ENABLE); +++ if (ep->state & MV_CP_EP_BULK_STREAM) +++ val |= EPBIT(num, in); +++ else +++ val &= ~EPBIT(num, in); +++ MV_CP_WRITE(val, MVCP_BULK_STREAMING_ENABLE); +++ } else { +++ if (ep->state & MV_CP_EP_BULK_STREAM) +++ config |= MVCP_EP_BULK_STREAM_EN; +++ else +++ config &= ~MVCP_EP_BULK_STREAM_EN; +++ } +++ spin_unlock_irqrestore(&cp->lock, flags); +++ break; +++ case USB_ENDPOINT_XFER_ISOC: +++ config |= MVCP_EP_TYPE_ISO; +++ if (ip_ver(cp) <= USB3_IP_VER_Z2) { +++ if (in) +++ reg = EP_IN_BINTERVAL_REG_1_2_3 + +++ 4 * (num / 4); +++ else +++ reg = EP_OUT_BINTERVAL_REG_1_2_3 + +++ 4 * (num / 4); +++ val = MV_CP_READ(reg); +++ val |= (_ep->desc->bInterval) << (num % 4) * 8; +++ MV_CP_WRITE(val, reg); +++ } else { +++ if (in) +++ reg = EP_IN_BINTERVAL_REG(num); +++ else +++ reg = EP_OUT_BINTERVAL_REG(num); +++ MV_CP_WRITE(_ep->desc->bInterval, reg); +++ } +++ break; +++ case USB_ENDPOINT_XFER_INT: +++ config |= MVCP_EP_TYPE_INT; +++ break; +++ } +++ } +++ +++ MV_CP_WRITE(config, config_base); +++ spin_unlock(&ep->lock); +++ mvc2_dma_reset(cp, ep, num, in); +++ spin_lock(&ep->lock); +++ /* Reset sequence number */ +++ if (num != 0) +++ reset_seqencenum(ep, num, in); +++ +++ /* Requeue the bd */ +++ list_for_each_entry_safe(req, tmp, &ep->wait, queue) { +++ list_del_init(&req->queue); +++ val = alloc_bds(cp, ep, req); +++ /* Current all bds have been allocated, just wait for previous complete */ +++ if (val) +++ ring = 1; +++ else { +++ dev_dbg(cp->dev, "%s %d\n", __func__, __LINE__); +++ list_add(&req->queue, &ep->wait); +++ break; +++ } +++ } +++ +++ if (ring) +++ mvc2_ring_incoming(cp, num, in); +++} +++ +++static int mvc2_ep_enable(struct usb_ep *_ep, +++ const struct usb_endpoint_descriptor *desc) +++{ +++ struct mvc2_ep *ep = container_of(_ep, struct mvc2_ep, ep); +++ int n = desc->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK; +++ int in = (desc->bEndpointAddress & USB_DIR_IN) != 0; +++ unsigned int state; +++ unsigned long flags; +++ +++ _ep->maxpacket = le16_to_cpu(desc->wMaxPacketSize); +++ _ep->desc = desc; +++ +++ spin_lock_irqsave(&ep->lock, flags); +++ ep_enable(ep, n, in, desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK); +++ +++ state = (ep->state & MV_CP_EP_WEDGE) | MV_CP_EP_NUM(n); +++ state |= in ? MV_CP_EP_DIRIN : 0; +++ ep->state = state; +++ spin_unlock_irqrestore(&ep->lock, flags); +++ +++ return 0; +++} +++ +++static void ep_disable(struct mvc2 *cp, int num, int dir) +++{ +++ unsigned int config; +++ struct mvc2_ep *ep = &cp->eps[2 * num + !!dir]; +++ +++ config = MV_CP_READ(epcon(num, dir)); +++ config &= ~MVCP_EP_ENABLE; +++ MV_CP_WRITE(config, epcon(num, dir)); +++ +++ spin_unlock(&ep->lock); +++ /* nuke all pending requests (does flush) */ +++ nuke(ep, -ESHUTDOWN); +++ spin_lock(&ep->lock); +++} +++ +++static int mvc2_ep_disable(struct usb_ep *_ep) +++{ +++ struct mvc2_ep *ep = container_of(_ep, struct mvc2_ep, ep); +++ struct mvc2 *cp = ep->cp; +++ unsigned long flags; +++ +++ if (!(ep->state & MV_CP_EP_NUM_MASK)) +++ return 0; +++ +++ spin_lock_irqsave(&ep->lock, flags); +++ ep_disable(cp, ep->state & MV_CP_EP_NUM_MASK, +++ ep->state & MV_CP_EP_DIRIN); +++ spin_unlock_irqrestore(&ep->lock, flags); +++ +++ return 0; +++} +++ +++static inline void mvc2_send_erdy(struct mvc2 *cp) +++{ +++ /* ep0 erdy should be smp safe, and no lock is needed */ +++ MV_CP_WRITE(MV_CP_READ(MVCP_ENDPOINT_0_CONFIG) | +++ MVCP_ENDPOINT_0_CONFIG_CHG_STATE, MVCP_ENDPOINT_0_CONFIG); +++} +++ +++#ifndef ASSEMBLE_REQ +++/* queues (submits) an I/O request to an endpoint */ +++static int +++mvc2_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags) +++{ +++ struct mvc2_ep *ep = container_of(_ep, struct mvc2_ep, ep); +++ struct mvc2_req *req = container_of(_req, struct mvc2_req, req); +++ struct mvc2 *cp = ep->cp; +++ unsigned int dir, num; +++ unsigned long flags; +++ int ret; +++ +++ if (_ep == NULL || _req == NULL) +++ return -EINVAL; +++ +++ num = _ep->desc->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK; +++ /* Reset the endpoint 0 to prevent previous left data */ +++ if (num == 0) { +++ /* +++ * After USB_GADGET_DELAYED_STATUS is set and the USB upper layer in USB function thread +++ * finishes the handling, the USB compsite layer will send request to continue with the +++ * control transfer, within this request, the request length is set 0. +++ * Since the request length will not be 0 for normal transfer, once it is 0, it means +++ * that to continue the transfer after USB_GADGET_DELAYED_STATUS. Thus the erdy is set +++ * here to notify the host that device is ready for latter transfer. +++ */ +++ if (!req->req.length) { +++ mvc2_send_erdy(cp); +++ return 0; +++ } +++ +++ if (cp->ep0_dir == USB_DIR_IN) +++ ep = &cp->eps[1]; +++ else +++ ep = &cp->eps[0]; +++ +++ dir = cp->ep0_dir; +++ +++ spin_lock_irqsave(&ep->lock, flags); +++ +++ MV_CP_WRITE(ep->doneq_cur * 8 + ep->doneq_start_phys, +++ ep_doneq_read(num, dir)); +++ ep->doneq_cur++; +++ if (ep->doneq_cur == ep->bd_sz) +++ ep->doneq_cur = 0; +++ +++ spin_unlock_irqrestore(&ep->lock, flags); +++ } else +++ dir = _ep->desc->bEndpointAddress & USB_DIR_IN; +++ +++ ret = usb_gadget_map_request(&cp->gadget, &req->req, dir); +++ if (ret) +++ return ret; +++ +++ _req->actual = 0; +++ _req->status = -EINPROGRESS; +++ spin_lock_irqsave(&ep->lock, flags); +++ +++ ret = alloc_bds(cp, ep, req); +++ /* Current all bds have been allocated, just wait for previous complete */ +++ if (!ret) { +++ dev_dbg(cp->dev, "%s %d\n", __func__, __LINE__); +++ list_add_tail(&req->queue, &ep->wait); +++ } else +++ mvc2_ring_incoming(cp, num, dir); +++ +++ spin_unlock_irqrestore(&ep->lock, flags); +++ +++ return 0; +++} +++#else +++static int +++mvc2_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags) +++{ +++ struct mvc2_ep *ep = container_of(_ep, struct mvc2_ep, ep); +++ struct mvc2_req *req = container_of(_req, struct mvc2_req, req), *tmp; +++ struct mvc2 *cp = ep->cp; +++ unsigned int dir, num; +++ unsigned long flags; +++ int ret, last, reqcnt; +++ static int cnt; +++#define CNT 10 +++ if (_ep == NULL || _req == NULL) +++ return -EINVAL; +++ +++ num = _ep->desc->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK; +++ /* Reset the endpoint 0 to prevent previous left data */ +++ if (num == 0) { +++ /* +++ * After USB_GADGET_DELAYED_STATUS is set and the USB upper layer in USB function thread +++ * finishes the handling, the USB compsite layer will send request to continue with the +++ * control transfer. Within this request, the request length is set 0. +++ * Since the request length will not be 0 for normal transfer, once it is 0, it means +++ * that to continue the transfer after USB_GADGET_DELAYED_STATUS. Thus the erdy is set +++ * here to notify the host that device is ready for latter transfer. +++ */ +++ if (!req->req.length) { +++ mvc2_send_erdy(cp); +++ return 0; +++ } +++ +++ if (cp->ep0_dir == USB_DIR_IN) +++ ep = &cp->eps[1]; +++ else +++ ep = &cp->eps[0]; +++ +++ dir = cp->ep0_dir; +++ +++ spin_lock_irqsave(&ep->lock, flags); +++ mvc2_dma_reset(cp, ep, num, dir); +++ MV_CP_WRITE(ep->doneq_cur + ep->doneq_start_phys, +++ ep_doneq_read(num, dir)); +++ spin_unlock_irqrestore(&ep->lock, flags); +++ } else +++ dir = _ep->desc->bEndpointAddress & USB_DIR_IN; +++ +++ ret = usb_gadget_map_request(&cp->gadget, &req->req, dir); +++ if (ret) +++ return ret; +++ +++ _req->actual = 0; +++ _req->status = -EINPROGRESS; +++ spin_lock_irqsave(&ep->lock, flags); +++ +++ if (dir == USB_DIR_OUT) { +++ ret = alloc_bds(cp, ep, req); +++ /* Current all bds have been allocated, just wait for previous complete */ +++ if (!ret) +++ list_add_tail(&req->queue, &ep->wait); +++ else +++ mvc2_ring_incoming(cp, num, dir); +++ } else { +++ list_add_tail(&req->queue, &ep->tmp); +++ cnt++; +++ +++ if (req->req.length > 1000 && cnt < CNT) +++ goto out; +++ if (cnt == CNT || req->req.length < 1000) { +++ list_for_each_entry_safe(req, tmp, &ep->tmp, queue) { +++ list_del_init(&req->queue); +++ cnt--; +++ if (cnt) +++ last = 0; +++ else +++ last = 1; +++#if 1 +++ ret = alloc_in_bds(cp, ep, req, &last); +++#else +++ ret = alloc_bds(cp, ep, req); +++ /* Current all bds have been allocated, just wait for previous complete */ +++ if (!ret) +++ list_add_tail(&req->queue, &ep->wait); +++#endif +++ } +++ mvc2_ring_incoming(cp, num, dir); +++ } +++ } +++out: +++ spin_unlock_irqrestore(&ep->lock, flags); +++ +++ return 0; +++} +++#endif +++ +++/* +++ * done() - retire a request; caller blocked irqs +++ * @status : request status to be set, only works when +++ * request is still in progress. +++ */ +++static void done(struct mvc2_ep *ep, struct mvc2_req *req, int status) +++{ +++ struct mvc2 *cp = NULL; +++ +++ cp = (struct mvc2 *)ep->cp; +++ /* Removed the req from fsl_ep->queue */ +++ list_del_init(&req->queue); +++ +++ ep->left_bds += req->bd_total; +++ WARN_ON(ep->left_bds > ep->bd_sz); +++ +++ /* req.status should be set as -EINPROGRESS in ep_queue() */ +++ if (req->req.status == -EINPROGRESS) +++ req->req.status = status; +++ else +++ status = req->req.status; +++ +++ usb_gadget_unmap_request(&cp->gadget, &req->req, ep_dir(ep)); +++ +++ if (status && (status != -ESHUTDOWN)) +++ dev_info(cp->dev, "complete %s req %p stat %d len %u/%u", +++ ep->ep.name, &req->req, status, +++ req->req.actual, req->req.length); +++ +++ spin_unlock(&ep->lock); +++ /* +++ * complete() is from gadget layer, +++ * eg fsg->bulk_in_complete() +++ */ +++ if (req->req.complete) +++ req->req.complete(&ep->ep, &req->req); +++ +++ spin_lock(&ep->lock); +++} +++ +++static void ep_fifo_flush(struct mvc2 *cp, int num, int dir, int all) +++{ +++ struct mvc2_ep *ep; +++ +++ ep = &cp->eps[2 * num + !!dir]; +++ /* +++ * Only current transferring bd would be transferred out, +++ * for those bd still chained after would be left untouched +++ */ +++ mvc2_dma_reset(cp, ep, num, dir); +++} +++ +++/* dequeues (cancels, unlinks) an I/O request from an endpoint */ +++static int mvc2_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req) +++{ +++ struct mvc2_ep *ep = container_of(_ep, struct mvc2_ep, ep); +++ struct mvc2 *cp = ep->cp; +++ struct mvc2_req *req = container_of(_req, struct mvc2_req, req), *tmp; +++ int num = _ep->desc->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK; +++ int dir = _ep->desc->bEndpointAddress & USB_DIR_IN; +++ int type = _ep->desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK; +++ unsigned long flags; +++ int ret, ring; +++ +++ ring = ret = 0; +++ if (_ep == NULL || _req == NULL || list_empty(&req->queue)) +++ return -EINVAL; +++ +++ spin_lock_irqsave(&ep->lock, flags); +++ ep_disable(cp, num, dir); +++ +++ list_for_each_entry(tmp, &ep->wait, queue) +++ if (tmp == req) +++ break; +++ +++ /* If don't find the request in both run/wait queue, quit */ +++ if (tmp != req) { +++ ret = -EINVAL; +++ goto out; +++ } +++ +++ list_del_init(&req->queue); +++ if (req->req.length) +++ usb_gadget_unmap_request(&ep->cp->gadget, _req, dir); +++ spin_unlock_irqrestore(&ep->lock, flags); +++ if (req->req.complete) { +++ req->req.status = -ECONNRESET; +++ req->req.complete(&ep->ep, &req->req); +++ } +++ +++ spin_lock_irqsave(&ep->lock, flags); +++out: +++ ep_enable(ep, num, dir, type); +++ spin_unlock_irqrestore(&ep->lock, flags); +++ +++ return ret; +++} +++ +++static int ep_set_halt(struct mvc2 *cp, int n, int in, int halt) +++{ +++ unsigned int config, config_base; +++ struct mvc2_ep *ep = &cp->eps[2 * n + !!in]; +++ unsigned long flags; +++ int bulk; +++ +++ config_base = epcon(n, in); +++ bulk = ep->ep.desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK; +++ +++ spin_lock_irqsave(&ep->lock, flags); +++ +++ if (halt && (bulk == USB_ENDPOINT_XFER_BULK) && in +++ && !list_empty(&ep->queue)) { +++ spin_unlock_irqrestore(&ep->lock, flags); +++ return -EAGAIN; +++ } +++ +++ config = MV_CP_READ(config_base); +++ if (halt) { +++ config |= MVCP_EP_STALL; +++ if (n) +++ ep->state |= MV_CP_EP_STALL; +++ } else { +++ config &= ~MVCP_EP_STALL; +++ ep->state &= ~MV_CP_EP_STALL; +++ } +++ MV_CP_WRITE(config, config_base); +++ spin_unlock_irqrestore(&ep->lock, flags); +++ +++ return 0; +++} +++ +++static int mvc2_ep_set_halt(struct usb_ep *_ep, int halt) +++{ +++ struct mvc2_ep *ep = container_of(_ep, struct mvc2_ep, ep); +++ struct mvc2 *cp = ep->cp; +++ unsigned int n, in; +++ +++ if (_ep == NULL || _ep->desc == NULL) +++ return -EINVAL; +++ +++ if (usb_endpoint_xfer_isoc(_ep->desc)) +++ return -EOPNOTSUPP; +++ +++ if (!halt) +++ ep->state &= ~MV_CP_EP_WEDGE; +++ +++ n = _ep->desc->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK; +++ in = _ep->desc->bEndpointAddress & USB_DIR_IN; +++ +++ return ep_set_halt(cp, n, in, halt); +++} +++ +++static int mvc2_ep_set_wedge(struct usb_ep *_ep) +++{ +++ struct mvc2_ep *ep = container_of(_ep, struct mvc2_ep, ep); +++ +++ ep->state |= MV_CP_EP_WEDGE; +++ return mvc2_ep_set_halt(_ep, 1); +++} +++ +++static void mvc2_ep_fifo_flush(struct usb_ep *_ep) +++{ +++ struct mvc2_ep *ep = container_of(_ep, struct mvc2_ep, ep); +++ struct mvc2 *cp = ep->cp; +++ +++ ep_fifo_flush(cp, ep->ep_num, ep_dir(ep), 1); +++} +++ +++static struct usb_ep_ops mvc2_ep_ops = { +++ .enable = mvc2_ep_enable, +++ .disable = mvc2_ep_disable, +++ +++ .alloc_request = mvc2_alloc_request, +++ .free_request = mvc2_free_request, +++ +++ .queue = mvc2_ep_queue, +++ .dequeue = mvc2_ep_dequeue, +++ +++ .set_wedge = mvc2_ep_set_wedge, +++ .set_halt = mvc2_ep_set_halt, +++ .fifo_flush = mvc2_ep_fifo_flush, +++}; +++ +++/* delete all endpoint requests, called with spinlock held */ +++static void nuke(struct mvc2_ep *ep, int status) +++{ +++ struct mvc2_req *req, *tmp; +++ unsigned long flags; +++ /* called with spinlock held */ +++ ep->stopped = 1; +++ +++ /* endpoint fifo flush */ +++ mvc2_ep_fifo_flush(&ep->ep); +++ spin_lock_irqsave(&ep->lock, flags); +++ list_for_each_entry_safe(req, tmp, &ep->queue, queue) +++ done(ep, req, status); +++ list_for_each_entry_safe(req, tmp, &ep->wait, queue) +++ done(ep, req, status); +++ spin_unlock_irqrestore(&ep->lock, flags); +++} +++ +++/* stop all USB activities */ +++static void stop_activity(struct mvc2 *udc, struct usb_gadget_driver *driver) +++{ +++ struct mvc2_ep *ep; +++ +++ nuke(&udc->eps[0], -ESHUTDOWN); +++ nuke(&udc->eps[1], -ESHUTDOWN); +++ +++ list_for_each_entry(ep, &udc->gadget.ep_list, ep.ep_list) { +++ if (ep->ep_num <= ip_ep_num(udc)) +++ nuke(ep, -ESHUTDOWN); +++ } +++ +++ /* report disconnect; the driver is already quiesced */ +++ if (driver) +++ driver->disconnect(&udc->gadget); +++} +++ +++static void mvc2_init_interrupt(struct mvc2 *cp) +++{ +++ int i; +++ +++ if (ip_ver(cp) <= USB3_IP_VER_Z2) { +++ MV_CP_WRITE(~0, MVCP_DMA_COMPLETE_SUCCESS); +++ MV_CP_WRITE(~0, MVCP_DMA_COMPLETE_ERROR); +++ MV_CP_WRITE(~0, MVCP_SS_CORE_INT); +++ MV_CP_WRITE(~0, MVCP_SS_SYS_INT); +++ /* +++ * Don't clear the ref int status. +++ * Refer to the comments in mvc2_usb2_connect for details. +++ * val = MV_CP_READ(cp->reg->ref_int); +++ * MV_CP_WRITE(val, cp->reg->ref_int); +++ */ +++ MV_CP_WRITE(MVCP_SS_CORE_INTEN_SETUP +++ | MVCP_SS_CORE_INTEN_HOT_RESET +++ | MVCP_SS_CORE_INTEN_LTSSM_CHG, MVCP_SS_CORE_INTEN); +++ MV_CP_WRITE(MVCP_SS_SYS_INTEN_DMA, MVCP_SS_SYS_INTEN); +++ MV_CP_WRITE(MVCP_REF_INTEN_USB2_CNT +++ | MVCP_REF_INTEN_USB2_DISCNT +++ | MVCP_REF_INTEN_RESET +++ | MVCP_REF_INTEN_POWERON +++ | MVCP_REF_INTEN_POWEROFF +++ | MVCP_REF_INTEN_SUSPEND +++ | MVCP_REF_INTEN_RESUME, cp->reg->ref_inten); +++ +++ set_top_int(cp, 0xf); +++ } else { +++ MV_CP_WRITE(~0, MVCP_SS_CORE_INT); +++ MV_CP_WRITE(~0, MVCP_TOP_INT_STATUS); +++ MV_CP_WRITE(~0, SS_EP_TOP_INT_STATUS_REG); +++ MV_CP_WRITE(~0, SS_EP_TOP_INT_ENABLE_REG); +++ MV_CP_WRITE(~0, SS_AXI_INT_STATUS_REG); +++ MV_CP_WRITE(~0, SS_AXI_INT_ENABLE_REG); +++ +++ /* enbale all interrupts of endpoints +++ * except doneq_full interrupt +++ */ +++ for (i = 0; i < cp->epnum; i++) { +++ MV_CP_WRITE(~0, SS_IN_EP_INT_STATUS_REG(i)); +++ MV_CP_WRITE(~DONEQ_FULL, SS_IN_EP_INT_ENABLE_REG(i)); +++ MV_CP_WRITE(~0, SS_OUT_EP_INT_STATUS_REG(i)); +++ MV_CP_WRITE(~DONEQ_FULL, SS_OUT_EP_INT_ENABLE_REG(i)); +++ } +++ /* +++ * Don't clear the ref int status. +++ * Refer to the comments in mvc2_usb2_connect for details. +++ * val = MV_CP_READ(cp->reg->ref_int); +++ * MV_CP_WRITE(val, cp->reg->ref_int); +++ */ +++ /* Since decode_err_8_10b & disparity_err will +++ * generate very frequenlty when enable U1/U2, +++ * we disable these two error interrupt +++ */ +++ MV_CP_WRITE(MVCP_SS_CORE_INTEN_SETUP +++ | MVCP_SS_CORE_INTEN_HOT_RESET +++ | MVCP_SS_CORE_INTEN_LTSSM_CHG +++ /*| 0x3F */, MVCP_SS_CORE_INTEN); +++ MV_CP_WRITE(MVCP_REF_INTEN_USB2_CNT +++ | MVCP_REF_INTEN_USB2_DISCNT +++ | MVCP_REF_INTEN_RESET +++ | MVCP_REF_INTEN_POWERON +++ | MVCP_REF_INTEN_POWEROFF +++ | MVCP_REF_INTEN_SUSPEND +++ | MVCP_REF_INTEN_RESUME, cp->reg->ref_inten); +++ +++ set_top_int(cp, 0x4f); +++ } +++} +++ +++static int mvc2_pullup(struct usb_gadget *gadget, int is_on) +++{ +++ struct mvc2 *cp = container_of(gadget, struct mvc2, gadget); +++ unsigned int val; +++ +++ /* +++ * For every switch from 2.0 to 3.0, this dma global config +++ * and interrupt enable register would get reset +++ */ +++ if (is_on && !irq_enabled) { +++ irq_enabled = true; +++ enable_irq(cp->irq); +++ } +++ +++ if (!usb3_disconnect) +++ is_on = 1; +++ +++ mvc2_connect(cp, is_on); +++ mvc2_config_mac(cp); +++ val = MV_CP_READ(MVCP_DMA_GLOBAL_CONFIG); +++ val |= MVCP_DMA_GLOBAL_CONFIG_RUN | MVCP_DMA_GLOBAL_CONFIG_INTCLR; +++ MV_CP_WRITE(val, MVCP_DMA_GLOBAL_CONFIG); +++ +++ mvc2_init_interrupt(cp); +++ +++ if (is_on == 0) +++ stop_activity(cp, cp->driver); +++ return 0; +++} +++ +++static int mvc2_start(struct usb_gadget *gadget, +++ struct usb_gadget_driver *driver) +++{ +++ struct mvc2 *cp = container_of(gadget, struct mvc2, gadget); +++ unsigned long flags; +++ struct mvc2_ep *ep; +++ +++ cp->driver = driver; +++ +++ /* enable ep0, dma int */ +++ ep = &cp->eps[0]; +++ spin_lock_irqsave(&ep->lock, flags); +++ ep_enable(ep, 0, 0, 0); +++ spin_unlock_irqrestore(&ep->lock, flags); +++ +++ ep = &cp->eps[1]; +++ spin_lock_irqsave(&ep->lock, flags); +++ ep_enable(ep, 0, 1, 0); +++ +++ spin_unlock_irqrestore(&ep->lock, flags); +++ +++ /* pullup is always on */ +++ mvc2_pullup(gadget, 1); +++ +++ return 0; +++} +++ +++static int mvc2_first_start(struct usb_gadget *gadget, +++ struct usb_gadget_driver *driver) +++{ +++ struct mvc2 *cp = container_of(gadget, struct mvc2, gadget); +++ +++ mvc2_start(gadget, driver); +++ +++ /* When boot with cable attached, there will be no vbus irq occurred */ +++ if (cp->qwork) +++ queue_work(cp->qwork, &cp->vbus_work); +++ +++ return 0; +++} +++ +++static int mvc2_stop(struct usb_gadget *gadget) +++{ +++ struct mvc2 *cp = container_of(gadget, struct mvc2, gadget); +++ +++ cp->driver = NULL; +++ return 0; +++} +++ +++int mvc2_checkvbus(struct mvc2 *cp) +++{ +++ int tmp; +++ +++ tmp = MV_CP_READ(cp->reg->global_control); +++ return tmp & MVCP_GLOBAL_CONTROL_POWERPRESENT; +++} +++ +++static int mvc2_vbus_session(struct usb_gadget *gadget, int is_active) +++{ +++ struct mvc2 *cp = container_of(gadget, struct mvc2, gadget); +++ unsigned int val; +++ +++ /* We only do real work when gadget driver is ready */ +++ if (!cp->driver) +++ return -ENODEV; +++ +++ val = MV_CP_READ(MVCP_DMA_GLOBAL_CONFIG); +++ if (is_active) { +++ /* For Armada 3700, need to skip PHY HW reset */ +++ if (cp->phy_hw_reset) +++ mvc2_hw_reset(cp); +++ pm_stay_awake(cp->dev); +++ /* turn on dma int */ +++ val |= MVCP_DMA_GLOBAL_CONFIG_RUN +++ | MVCP_DMA_GLOBAL_CONFIG_INTCLR; +++ MV_CP_WRITE(val, MVCP_DMA_GLOBAL_CONFIG); +++ usb_gadget_connect(&cp->gadget); +++ mvc2_start(gadget, cp->driver); +++ +++ } else { +++ /* need to stop activity before disable dma engine. +++ * stop_activity will call mvc2_dma_reset, +++ * if disable dma before mvc2_dma_reset, then dma reset +++ * timeout issue will happen. +++ */ +++ stop_activity(cp, cp->driver); +++ +++ /* disable dma engine */ +++ val &= ~MVCP_DMA_GLOBAL_CONFIG_RUN; +++ MV_CP_WRITE(val, MVCP_DMA_GLOBAL_CONFIG); +++ } +++ +++ return 0; +++} +++ +++static irqreturn_t mvc2_vbus_irq(int irq, void *dev) +++{ +++ struct mvc2 *cp = (struct mvc2 *)dev; +++ +++ /* polling VBUS and init phy may cause too much time */ +++ if (cp->qwork) +++ queue_work(cp->qwork, &cp->vbus_work); +++ +++ return IRQ_HANDLED; +++} +++ +++static void mvc2_vbus_work(struct work_struct *work) +++{ +++ struct mvc2 *cp; +++ unsigned int vbus; +++ unsigned int reg; +++ +++ cp = container_of(work, struct mvc2, vbus_work); +++ +++ if (gpio_is_valid(cp->vbus_pin)) +++ vbus = gpio_get_value_cansleep(cp->vbus_pin); +++ else { +++ dev_err(cp->dev, "VBUS interrupt status is missing\n"); +++ return; +++ } +++ +++ if (cp->prev_vbus != vbus) +++ cp->prev_vbus = vbus; +++ else +++ return; +++ +++ if (!cp->phy_base) { +++ dev_err(cp->dev, "PHY register is missing\n"); +++ return; +++ } +++ +++ if (vbus == VBUS_HIGH) { +++ reg = readl(cp->phy_base); +++ reg |= 0x8000; +++ writel(reg, cp->phy_base); +++ } else if (vbus == VBUS_LOW) { +++ reg = readl(cp->phy_base); +++ reg &= ~0x8000; +++ writel(reg, cp->phy_base); +++ } +++} +++ +++static int mvc2_vbus_draw(struct usb_gadget *gadget, unsigned mA) +++{ +++ return -ENOTSUPP; +++} +++ +++static int mvc2_set_selfpowered(struct usb_gadget *gadget, int is_selfpowered) +++{ +++ struct mvc2 *cp = container_of(gadget, struct mvc2, gadget); +++ +++ if (is_selfpowered) +++ cp->status |= MVCP_STATUS_SELF_POWERED; +++ else +++ cp->status &= ~MVCP_STATUS_SELF_POWERED; +++ +++ return 0; +++} +++ +++#ifdef CONFIG_USB_REMOTE_WAKEUP +++ +++#define MVCP_GLOBAL_CONTROL_STATUS 0x2c +++#define MVCP_GLOBAL_CONTROL_STATUS_LPFS_EXIT (1<<7) +++static int mvc2_wakeup(struct usb_gadget *gadget) +++{ +++ struct mvc2 *cp = container_of(gadget, struct mvc2, gadget); +++ unsigned int phy, val; +++ +++ phy = MV_CP_READ(MVCP_PHY); +++ if ((phy & MVCP_PHY_LTSSM_MASK) == LTSSM_U3) { +++ dev_info(cp->dev, "usb3 is enter u3 , can be wakeup now\n"); +++ val = MV_CP_READ(MVCP_GLOBAL_CONTROL_STATUS); +++ val |= MVCP_GLOBAL_CONTROL_STATUS_LPFS_EXIT; +++ MV_CP_WRITE(val, MVCP_GLOBAL_CONTROL_STATUS); +++ } +++ +++ return 0; +++} +++#endif +++ +++/* device controller usb_gadget_ops structure */ +++static const struct usb_gadget_ops mvc2_ops = { +++ /* notify controller that VBUS is powered or not */ +++ .vbus_session = mvc2_vbus_session, +++ +++ /* constrain controller's VBUS power usage */ +++ .vbus_draw = mvc2_vbus_draw, +++ .set_selfpowered = mvc2_set_selfpowered, +++ +++ .pullup = mvc2_pullup, +++ .udc_start = mvc2_first_start, +++ .udc_stop = mvc2_stop, +++#ifdef CONFIG_USB_REMOTE_WAKEUP +++ .wakeup = mvc2_wakeup, +++#endif +++}; +++ +++void mvc2_handle_setup(struct mvc2 *cp) +++{ +++ struct usb_ctrlrequest *r; +++ unsigned int tmp[2]; +++ int ret = -EINVAL; +++ bool delegate; +++ +++ tmp[0] = MV_CP_READ(MVCP_SETUP_DP_LOW); +++ tmp[1] = MV_CP_READ(MVCP_SETUP_DP_HIGH); +++ MV_CP_WRITE(MVCP_SETUP_CONTROL_FETCHED, MVCP_SETUP_CONTROL); +++ +++ r = (struct usb_ctrlrequest *)tmp; +++ +++ if (r->wLength) { +++ if (r->bRequestType & USB_DIR_IN) +++ cp->ep0_dir = USB_DIR_IN; +++ else +++ cp->ep0_dir = USB_DIR_OUT; +++ } else +++ cp->ep0_dir = USB_DIR_IN; +++ +++ ret = mvc2_std_request(cp, r, &delegate); +++ if (delegate) +++ ret = cp->driver->setup(&cp->gadget, r); +++ /* indicate setup pharse already complete */ +++ mvc2_send_erdy(cp); +++ +++ /* Stall the endpoint if protocol not support */ +++ if (ret < 0) +++ ep_set_halt(cp, 0, 0, 1); +++ /* +++ * If current setup has no data pharse or failed, we would directly +++ * jump to status pharse. +++ * If the USB_GADGET_DELAYED_STATUS is set, the USB interface requests +++ * delay for it to handle the setup, thus here should not send erdy to +++ * continue the transfer. Instead, the erdy will be sent from mvc2_ep_queue, +++ * once a request with length 0 is issued. +++ */ +++ if ((ret < 0) || (r->wLength == 0 && ret != USB_GADGET_DELAYED_STATUS)) +++ mvc2_send_erdy(cp); +++} +++ +++static void mvc2_dma_complete(struct mvc2 *cp) +++{ +++ unsigned int val, i, n, in, short_packet, finish, ret; +++ struct doneq *done; +++ struct mvc2_ep *ep; +++ struct mvc2_req *req, *tmp; +++ struct bd *bd; +++ unsigned int writeq, len, doneq, ring; +++ unsigned int sreg, ep_status; +++ +++ if (ip_ver(cp) <= USB3_IP_VER_Z2) +++ sreg = MVCP_DMA_COMPLETE_SUCCESS; +++ else +++ sreg = SS_EP_TOP_INT_STATUS_REG; +++ val = MV_CP_READ(sreg); +++ if (!val) +++ return; +++ MV_CP_WRITE(val, sreg); +++ +++ for (i = 0; i < (cp->epnum << 1); i++) { +++ if (!(val & (1 << i))) +++ continue; +++ +++ if (i < cp->epnum) { +++ n = i; +++ in = 0; +++ } else { +++ n = i - cp->epnum; +++ in = 1; +++ } +++ +++ if (ip_ver(cp) >= USB3_IP_VER_Z3) { +++ in = in ? 0 : 1; +++ if (in) +++ sreg = SS_IN_EP_INT_STATUS_REG(n); +++ else +++ sreg = SS_OUT_EP_INT_STATUS_REG(n); +++ ep_status = MV_CP_READ(sreg); +++ /* clear interrupt status */ +++ MV_CP_WRITE(ep_status, sreg); +++ +++ if (ep_status & COMPLETION_SUCCESS) +++ goto success; +++ +++ /* some error may happen */ +++ pr_warn("### %s %d: num %d, dir %d, status 0x%x\n", +++ __func__, __LINE__, n, in, ep_status); +++ continue; +++ } +++ +++success: +++ ep = &cp->eps[(n << 1) + in]; +++ +++ /* +++ * info hw that sw has prepared data +++ * hw would auto send erdy after data stage complete +++ */ +++ if (n == 0) { +++ mvc2_send_erdy(cp); +++ ep->state &= ~MV_CP_EP_TRANSERING; +++ if (!list_empty(&ep->queue)) { +++ req = list_first_entry(&ep->queue, +++ struct mvc2_req, queue); +++ +++ if (req->req.complete) { +++ req->req.status = 0; +++ req->req.complete(&ep->ep, &req->req); +++ } +++ ep->left_bds++; +++ WARN_ON(req->bd_total > 1); +++ WARN_ON(ep->left_bds > ep->bd_sz); +++ spin_lock(&ep->lock); +++ INIT_LIST_HEAD(&ep->queue); +++ INIT_LIST_HEAD(&ep->wait); +++ spin_unlock(&ep->lock); +++ } +++ +++ continue; +++ } +++ +++ writeq = MV_CP_READ(ep_doneq_write(n, in)); +++ if (!writeq) +++ continue; +++ +++ /* Get the DoneQ write pointer relative position */ +++ writeq -= ep->doneq_start_phys; +++ writeq /= sizeof(struct doneq); +++ if (writeq == ep->bd_sz) +++ writeq = 0; +++ +++ doneq = ep->doneq_cur; +++ short_packet = 0; +++ ring = 0; +++ spin_lock(&ep->lock); +++ while (doneq != writeq) { +++ len = 0; +++ req = list_first_entry_or_null(&ep->queue, +++ struct mvc2_req, queue); +++ if (!req) { +++ pr_info("req null, doneq = %d,writeq = %d\n", +++ doneq, writeq); +++ break; +++ } +++ bd = req->bd; +++ finish = 1; +++ do { +++ done = (struct doneq *)(ep->doneq_start +++ + doneq); +++ +++ if (done->status & DONE_AXI_ERROR) { +++ req->req.status = -EPROTO; +++ break; +++ } +++ +++ /* +++ * Note: for the short packet, if originally +++ * there are several BDs chained, but host +++ * only send short packet for the first BD, +++ * then doneq would be updated accordingly. +++ * And later BDs in the chain would be used +++ * for storing data that host send in another +++ * transfer. +++ * +++ * But if the first BD is not set as INT_EN, +++ * there would be no interrupt be generated. +++ */ +++ if (done->status & DONE_SHORT_PKT) +++ short_packet = 1; +++ +++ len += DONE_LEN(done->status); +++ doneq++; +++ if (doneq == ep->bd_sz) +++ doneq = 0; +++ +++ WARN_ON(doneq == (writeq + 1)); +++ bd->cmd = 0; +++ bd++; +++ ep->left_bds++; +++ WARN_ON(ep->left_bds > ep->bd_sz); +++ +++ } while (--req->bd_total > 0); +++ +++ /* +++ * Ring the finish data handle bell +++ * to kick hardware to continue +++ */ +++ MV_CP_WRITE(doneq * 8 + ep->doneq_start_phys, +++ ep_doneq_read(n, in)); +++ ep->doneq_cur = doneq; +++ +++ req->req.actual += len; +++ list_del_init(&req->queue); +++ ep->state &= ~MV_CP_EP_TRANSERING; +++ +++ ret = UINT_MAX; +++ /* There still something left not being transferred */ +++ if ((req->req.actual < req->req.length) +++ && !short_packet) { +++ dev_dbg(cp->dev, "%s %d\n", __func__, __LINE__); +++ ret = alloc_bds(cp, ep, req); +++ finish = 0; +++ } +++ +++ /* +++ * Refill BD if there is any request +++ * following in the chain +++ */ +++ while (!list_empty(&ep->wait) && ret) { +++ tmp = list_first_entry(&ep->wait, +++ struct mvc2_req, queue); +++ list_del_init(&tmp->queue); +++ ret = alloc_bds(cp, ep, tmp); +++ if (!ret) +++ list_add(&tmp->queue, &ep->wait); +++ } +++ +++ if (finish) { +++ spin_unlock(&ep->lock); +++ if (req->req.length) +++ usb_gadget_unmap_request(&cp->gadget, +++ &req->req, in); +++ +++ if (req->req.complete) { +++ req->req.status = 0; +++ req->req.complete(&ep->ep, &req->req); +++ } +++ spin_lock(&ep->lock); +++ } +++ +++ if (ret != UINT_MAX) +++ ring = 1; +++ } +++ +++ if (ring) +++ mvc2_ring_incoming(cp, n, in); +++ spin_unlock(&ep->lock); +++ } +++} +++ +++static void mvc2_process_link_change(struct mvc2 *cp) +++{ +++ unsigned int val; +++ +++ cp->status &= ~MVCP_STATUS_POWER_MASK; +++ val = MV_CP_READ(MVCP_PHY); +++ switch (val & MVCP_PHY_LTSSM_MASK) { +++ case LTSSM_U0: +++ cp->gadget.speed = USB_SPEED_SUPER; +++ cp->status |= MVCP_STATUS_U0; +++ cp->status |= MVCP_STATUS_CONNECTED; +++ break; +++ case LTSSM_U1: +++ cp->status |= MVCP_STATUS_U1; +++ break; +++ case LTSSM_U2: +++ cp->status |= MVCP_STATUS_U2; +++ break; +++ case LTSSM_U3: +++ cp->status |= MVCP_STATUS_U3; +++ break; +++ } +++} +++ +++static irqreturn_t mvc2_irq(int irq, void *devid) +++{ +++ struct mvc2 *cp = devid; +++ unsigned int topint, coreint, sysint, refint, val; +++ +++ topint = MV_CP_READ(MVCP_TOP_INT_STATUS); +++ +++ if (topint == 0) +++ return IRQ_HANDLED; +++ +++ MV_CP_WRITE(topint, MVCP_TOP_INT_STATUS); +++ +++ if (ip_ver(cp) <= USB3_IP_VER_Z2) { +++ if (topint & MVCP_TOP_INT_SS_SYS) { +++ sysint = MV_CP_READ(MVCP_SS_SYS_INT); +++ MV_CP_WRITE(sysint, MVCP_SS_SYS_INT); +++ +++ if (sysint & MVCP_SS_SYS_INT_DMA) +++ mvc2_dma_complete(cp); +++ } +++ } else { +++ if (topint & MVCP_TOP_INT_SS_EP) +++ mvc2_dma_complete(cp); +++ +++ if (topint & MVCP_TOP_INT_SS_AXI) { +++ val = MV_CP_READ(SS_AXI_INT_STATUS_REG); +++ MV_CP_WRITE(val, SS_AXI_INT_STATUS_REG); +++ pr_warn("### %s %d: SS_AXI_INT_STATUS_REG = 0x%x\r\n", +++ __func__, __LINE__, val); +++ } +++ } +++ +++ if (topint & MVCP_TOP_INT_SS_CORE) { +++ coreint = MV_CP_READ(MVCP_SS_CORE_INT); +++ MV_CP_WRITE(coreint, MVCP_SS_CORE_INT); +++ +++ if (coreint & MVCP_SS_CORE_INT_HOT_RESET) { +++ pr_info("USB device: hot reset\n"); +++ stop_activity(cp, cp->driver); +++ } +++ +++ if (coreint & MVCP_SS_CORE_INT_SETUP) +++ mvc2_handle_setup(cp); +++ +++ if (coreint & MVCP_SS_CORE_INT_LTSSM_CHG) +++ mvc2_process_link_change(cp); +++ +++ /* We enabled error interrupt from Z3, +++ * need to check the error here. +++ */ +++#if 0 +++ if (ip_ver(cp) >= USB3_IP_VER_Z3) { +++ if (coreint & 0x3F) +++ pr_warn("### coreint = 0x%x\n", coreint); +++ } +++#endif +++ } +++ +++ if (topint & MVCP_TOP_INT_REF) { +++ refint = MV_CP_READ(cp->reg->ref_int); +++ MV_CP_WRITE(refint, cp->reg->ref_int); +++ +++ if (refint & MVCP_REF_INTEN_POWERON) { +++ /* +++ * Note, during the USB3 irq disabled, there may be +++ * may times plug/unplug, +++ * thus, the power on/off interrupt +++ * may co-exisit once enable the irq again. +++ * To avoid this, we need to check +++ * the VBUS of the final state. +++ * Refer to mvc2_usb2_connect. +++ */ +++ if (mvc2_checkvbus(cp)) { +++ pr_info("USB device: connected\n"); +++ usb_gadget_vbus_connect(&cp->gadget); +++ cp->status |= MVCP_STATUS_CONNECTED; +++ } +++ } +++ +++ if (refint & MVCP_REF_INTEN_POWEROFF) { +++ /* +++ * Note, during the USB3 irq disabled, there may be +++ * may times plug/unplug, +++ * thus, the power on/off interrupt +++ * may co-exisit once enable the irq again. +++ * To avoid this, we need to check +++ * the VBUS of the final state. +++ * Refer to mvc2_usb2_connect. +++ */ +++ if (!mvc2_checkvbus(cp)) { +++ pr_info("USB device: disconnected\n"); +++ usb3_disconnect = true; +++ usb_gadget_vbus_disconnect(&cp->gadget); +++ cp->status &= ~MVCP_STATUS_CONNECTED; +++ +++ cp->gadget.speed = USB_SPEED_UNKNOWN; +++ +++ cp->status &= ~MVCP_STATUS_USB2; +++ glue.status = cp->status; +++ if (cp->work) +++ schedule_work(cp->work); +++ } +++ } +++ +++ if (refint & MVCP_REF_INTEN_RESET) { +++ pr_info("USB device: warm reset\n"); +++ /* +++ * The doneq write point will be set to 0 when warm/hot reset occurred. +++ * This will cause device abnormal, one example is CV test can't pass +++ * at this situation. +++ * Add dma reset here will set doneq write point to doneq start point. +++ */ +++ stop_activity(cp, cp->driver); +++ } +++ +++ if ((refint & MVCP_REF_INTEN_USB2_CNT) && +++ (MV_CP_READ(cp->reg->ref_inten) & +++ MVCP_REF_INTEN_USB2_CNT)) { +++ usb3_disconnect = false; +++ stop_activity(cp, cp->driver); +++ +++ cp->status |= MVCP_STATUS_USB2; +++ glue.status = cp->status; +++ if (cp->work) +++ schedule_work(cp->work); +++ } +++ +++ if ((refint & MVCP_REF_INTEN_USB2_DISCNT) && +++ (MV_CP_READ(cp->reg->ref_inten) & +++ MVCP_REF_INTEN_USB2_DISCNT)) { +++ usb3_disconnect = true; +++ if (mvc2_checkvbus(cp)) { +++ cp->status &= ~MVCP_STATUS_USB2; +++ glue.status = cp->status; +++ if (cp->work) +++ schedule_work(cp->work); +++ } +++ } +++ +++ if (refint & MVCP_REF_INTEN_RESUME) +++ pr_info("USB device: resume\n"); +++ +++ if (refint & MVCP_REF_INTEN_SUSPEND) +++ pr_info("USB device: suspend\n"); +++ } +++ +++ if (topint & MVCP_TOP_INT_USB2) +++ return IRQ_NONE; +++ +++ return IRQ_HANDLED; +++} +++ +++int mvc2_gadget_init(struct mvc2 *cp) +++{ +++ int ret, i, irq; +++ struct mvc2_ep *ep; +++ +++ irq = platform_get_irq(to_platform_device(cp->dev), 0); +++ ret = request_irq(irq, mvc2_irq, IRQF_SHARED, "mvcp_usb3", cp); +++ if (ret) { +++ dev_err(cp->dev, "can't request irq %i, err: %d\n", irq, ret); +++ return -EINVAL; +++ } +++ +++ /* initialize gadget structure */ +++ cp->gadget.ops = &mvc2_ops; +++ cp->gadget.ep0 = &cp->eps[0].ep; +++ INIT_LIST_HEAD(&cp->gadget.ep_list); +++ cp->gadget.speed = USB_SPEED_UNKNOWN; +++ cp->gadget.max_speed = USB_SPEED_SUPER; +++ cp->gadget.is_otg = 0; +++ cp->gadget.name = driver_name; +++ cp->gadget.dev.parent = cp->dev; +++ cp->gadget.dev.dma_mask = cp->dev->dma_mask; +++ cp->irq = irq; +++ disable_irq(cp->irq); +++ +++ for (i = 0; i < cp->epnum * 2; i++) { +++ ep = &cp->eps[i]; +++ ep->ep.ops = &mvc2_ep_ops; +++ if (i > 1) { +++ INIT_LIST_HEAD(&ep->ep.ep_list); +++ list_add_tail(&ep->ep.ep_list, &cp->gadget.ep_list); +++ } +++ } +++ +++ ret = usb_add_gadget_udc(cp->dev, &cp->gadget); +++ if (ret) +++ return ret; +++ +++ return 0; +++} +++ +++void mvc2_config_mac(struct mvc2 *cp) +++{ +++ unsigned int val; +++ +++ /* NOTE: this setting is related to reference clock, +++ * it indicates number of ref clock pulses for 100 ns, +++ * refer to Q&A adjust 100ns timer +++ */ +++ val = MV_CP_READ(cp->reg->counter_pulse); +++ val &= ~(0xff << 24); +++ /* The formula is 2*100ns/(ref clcok speed) */ +++ val |= (5 << 24); +++ MV_CP_WRITE(val, cp->reg->counter_pulse); +++ +++ /* set min value for transceiver side U1 tx_t12_t10 to 600ns +++ * set max value for transceiver side U1 tx_t12_t11 to 900ns +++ * set LFPS Receive side t13 - t11 duration for U2 to 600us +++ * Receive side t13 - t11 duration for u3, set to 200us +++ */ +++ val = MV_CP_READ(lfps_signal(cp, 1)); +++ val &= ~(0x0f00000); +++ val |= (0x0900000); +++ MV_CP_WRITE(val, lfps_signal(cp, 1)); +++#if 0 +++ val = MV_CP_READ(lfps_signal(cp, 1)); +++ val &= ~(0xf8); +++ val |= (0x30); +++ MV_CP_WRITE(val, lfps_signal(cp, 1)); +++#endif +++ val = MV_CP_READ(lfps_signal(cp, 1)); +++ val &= ~(0xff << 16); +++ val |= (0x2 << 16); +++ MV_CP_WRITE(val, lfps_signal(cp, 1)); +++ +++ if (ip_ver(cp) < USB3_IP_VER_Z2) { +++ /* IP version 2.04, 2.05 */ +++ val = MV_CP_READ(lfps_signal(cp, 1)); +++ val &= ~(0x7); +++ val |= (0x3); +++ MV_CP_WRITE(val, lfps_signal(cp, 1)); +++ +++ val = MV_CP_READ(lfps_signal(cp, 2)); +++ val &= ~(0x7fff); +++ val |= (0x4e20); +++ MV_CP_WRITE(val, lfps_signal(cp, 2)); +++ +++ val = MV_CP_READ(lfps_signal(cp, 4)); +++ val &= ~(0x7fff); +++ val |= (0x7d0); +++ MV_CP_WRITE(val, lfps_signal(cp, 4)); +++ +++ MV_CP_WRITE(0x1388000d, lfps_signal(cp, 5)); +++ } else { +++ /* IP version 2.06 above */ +++ /* Transmit side t11 - t10 duration for u2, max value set to 2ms */ +++ val = MV_CP_READ(lfps_signal(cp, 2)); +++ val &= ~0xf; +++ val |= 0x3; +++ MV_CP_WRITE(val, lfps_signal(cp, 2)); +++ +++ val = MV_CP_READ(lfps_signal(cp, 2)); +++ val &= ~(0xff << 16); +++ val |= (0x6 << 16); +++ MV_CP_WRITE(val, lfps_signal(cp, 2)); +++ +++ /*Transmit side min value of t12 - t11 duration for u2, set to 100us */ +++ val = MV_CP_READ(lfps_signal(cp, 3)); +++ val &= ~0x7fff; +++ val |= 0x4e20; +++ MV_CP_WRITE(val, lfps_signal(cp, 3)); +++ +++ val = MV_CP_READ(lfps_signal(cp, 3)); +++ val &= ~0x7fff; +++ val |= 0x7d0; +++ MV_CP_WRITE(val, lfps_signal(cp, 3)); +++ +++ /* +++ * if U2 is disabled set U1 rx t13 - t11 to 900ns, if U2 is enabled, +++ * set U1 rx t13-t11 to 500us +++ */ +++ MV_CP_WRITE(0x13880009, lfps_signal(cp, 6)); +++ } +++ +++ /*reconfig LFPS length for PING to 70ns */ +++ val = MV_CP_READ(MVCP_TIMER_TIMEOUT(2)); +++ val &= ~0xff; +++ val |= 0x50; +++ MV_CP_WRITE(val, MVCP_TIMER_TIMEOUT(2)); +++ +++ val = MV_CP_READ(MVCP_LFPS_TX_CONFIG); +++ val &= ~0xf; +++ val |= 0x3; +++ MV_CP_WRITE(val, MVCP_LFPS_TX_CONFIG); +++ +++#ifdef ELECTRICAL_TEST +++ /*set min_num_tx_ts1 to 131us, set min_num_tx_ts2 to 2us */ +++ val = MV_CP_READ(MVCP_TX_TSI_NUM); +++ val |= 0x1000 << 16; +++ MV_CP_WRITE(val, MVCP_TX_TSI_NUM); +++#else +++ /* for normal usage, 1us ts1 would be enough */ +++ val = MV_CP_READ(MVCP_TX_TSI_NUM); +++ val |= 0x8 << 16; +++ MV_CP_WRITE(val, MVCP_TX_TSI_NUM); +++#endif +++ val = MV_CP_READ(MVCP_START_STATE_DELAY); +++ val |= 0x3e << 16; +++ MV_CP_WRITE(val, MVCP_START_STATE_DELAY); +++ +++ val = MV_CP_READ(MVCP_TX_TSI_NUM); +++ val |= 0xfff0; +++ MV_CP_WRITE(val, MVCP_TX_TSI_NUM); +++ +++ if (u1u2_enabled()) { +++ val = MV_CP_READ(MVCP_LOWPOWER); +++ val &= ~0x3; +++ MV_CP_WRITE(val, MVCP_LOWPOWER); +++ } +++ +++ val = MV_CP_READ(MVCP_COUNTER_DELAY_TX); +++ val |= 4 << 16; +++ MV_CP_WRITE(val, MVCP_COUNTER_DELAY_TX); +++ +++ val = MV_CP_READ(MVCP_COUNTER_DELAY_TX); +++ if (ip_ver(cp) <= USB3_IP_VER_Z3) { +++ /* +++ * Jira NEZHA3-152/153 +++ * Set the U2 Timeout Value bigger than the Max value(65024). +++ * This will make device never send LFPS.Exit, thus can +++ * avoid NEZHA3-152/153. +++ */ +++ val |= (65024 + 100); +++ } else +++ val |= 5; +++ MV_CP_WRITE(val, MVCP_COUNTER_DELAY_TX); +++} +++ +++/* Need to be included in ep lock protection */ +++void reset_seqencenum(struct mvc2_ep *ep, int num, int in) +++{ +++ struct mvc2 *cp = ep->cp; +++ unsigned int config; +++ +++ config = MV_CP_READ(epcon(num, in)); +++ config |= MVCP_EP_RESETSEQ; +++ MV_CP_WRITE(config, epcon(num, in)); +++} +++ +++void mvc2_hw_reset(struct mvc2 *cp) +++{ +++ unsigned int val, timeout = 5000; +++ +++ if (ip_ver(cp) < USB3_IP_VER_Z2) { +++ val = MV_CP_READ(cp->reg->global_control); +++ val |= MVCP_GLOBAL_CONTROL_SOFT_RESET; +++ MV_CP_WRITE(val, cp->reg->global_control); +++ /* wait controller reset complete */ +++ while (timeout-- > 0) { +++ val = MV_CP_READ(cp->reg->global_control); +++ if (!(val & MVCP_GLOBAL_CONTROL_SOFT_RESET)) +++ break; +++ cpu_relax(); +++ } +++ } else { +++ val = MV_CP_READ(cp->reg->global_control); +++ val |= MVCP_GLOBAL_CONTROL_PHYRESET; +++ MV_CP_WRITE(val, cp->reg->global_control); +++ +++ val = MV_CP_READ(MVCP_SOFTWARE_RESET); +++ val |= 1; +++ MV_CP_WRITE(val, MVCP_SOFTWARE_RESET); +++ while (timeout-- > 0) { +++ val = MV_CP_READ(MVCP_SOFTWARE_RESET); +++ if (!(val & 1)) +++ break; +++ cpu_relax(); +++ } +++ } +++ +++ /* delay before mac config */ +++ mdelay(100); +++ mvc2_config_mac(cp); +++} +++ +++void mvc2_usb2_operation(struct mvc2 *cp, int op) +++{ +++ unsigned int val; +++ +++ if (op) { +++ val = MV_CP_READ(cp->reg->global_control); +++ val |= MVCP_GLOBAL_CONTROL_USB2_BUS_RESET; +++ MV_CP_WRITE(val, cp->reg->global_control); +++ udelay(10); +++ val &= ~MVCP_GLOBAL_CONTROL_USB2_BUS_RESET; +++ MV_CP_WRITE(val, cp->reg->global_control); +++ } +++} +++ +++void mvc2_connect(struct mvc2 *cp, int is_on) +++{ +++ unsigned int val; +++ +++ if (is_on) { +++ val = MV_CP_READ(cp->reg->global_control); +++ /* bypass lowpower mode */ +++ val |= MVCP_GLOBAL_CONTROL_SAFE | +++ MVCP_GLOBAL_CONTROL_SOFT_CONNECT; +++ MV_CP_WRITE(val, cp->reg->global_control); +++ } else { +++ val = MV_CP_READ(cp->reg->ref_inten); +++ val &= ~MVCP_REF_INTEN_USB2_CNT; +++ MV_CP_WRITE(val, cp->reg->ref_inten); +++ +++ val = MV_CP_READ(cp->reg->global_control); +++ val &= ~MVCP_GLOBAL_CONTROL_SOFT_CONNECT; +++ MV_CP_WRITE(val, cp->reg->global_control); +++ } +++} +++ +++static int mvc2_probe(struct platform_device *pdev) +++{ +++ struct mvc2 *cp = NULL; +++ struct resource *res; +++ unsigned int ver; +++ int ret = 0; +++ void __iomem *base; +++ void __iomem *phy_base = NULL; +++ struct clk *clk; +++ +++ /* disable U1/U2 mode, as part of the detection WA */ +++ u1u2 = 0; +++ +++ /* private struct */ +++ cp = devm_kzalloc(&pdev->dev, sizeof(*cp), GFP_KERNEL); +++ if (!cp) +++ return -ENOMEM; +++ +++ /* a38x specific initializations */ +++ /* ungate unit clocks */ +++ clk = devm_clk_get(&pdev->dev, NULL); +++ if (IS_ERR(clk)) { +++ ret = PTR_ERR(clk); +++ goto err_mem; +++ } +++ +++ ret = clk_prepare_enable(clk); +++ if (ret < 0) +++ goto err_mem; +++ +++ /* phy address for VBUS toggling */ +++ res = platform_get_resource(pdev, IORESOURCE_MEM, 1); +++ if (res) { +++ phy_base = devm_ioremap(&pdev->dev, res->start, resource_size(res)); +++ if (!phy_base) { +++ dev_err(&pdev->dev, "%s: register mapping failed\n", __func__); +++ ret = -ENXIO; +++ goto err_clk; +++ } +++ } +++ +++ /* general USB3 device initializations */ +++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); +++ if (!res) { +++ dev_err(&pdev->dev, "missing mem resource\n"); +++ ret = -ENODEV; +++ goto err_clk; +++ } +++ +++ base = devm_ioremap_resource(&pdev->dev, res); +++ if (!base) { +++ dev_err(&pdev->dev, "%s: register mapping failed\n", __func__); +++ ret = -ENXIO; +++ goto err_clk; +++ } +++ +++ ver = ioread32(base); +++ if (ver == 0) { +++ dev_err(&pdev->dev, "IP version error!\n"); +++ ret = -ENXIO; +++ goto err_clk; +++ } +++ +++ cp->mvc2_version = ver & 0xFFFF; +++ +++ /* setup vbus gpio */ +++ cp->vbus_pin = of_get_named_gpio(pdev->dev.of_node, "vbus-gpio", 0); +++ if ((cp->vbus_pin == -ENODEV) || (cp->vbus_pin == -EPROBE_DEFER)) { +++ ret = -EPROBE_DEFER; +++ goto err_clk; +++ } +++ +++ if (cp->vbus_pin < 0) +++ cp->vbus_pin = -ENODEV; +++ +++ if (gpio_is_valid(cp->vbus_pin)) { +++ cp->prev_vbus = 0; +++ if (!devm_gpio_request(&pdev->dev, cp->vbus_pin, "mvebu-u3d")) { +++ /* Use the 'any_context' version of function to allow +++ * requesting both direct GPIO interrupt (hardirq) and +++ * IO-expander's GPIO (nested interrupt) +++ */ +++ ret = devm_request_any_context_irq(&pdev->dev, +++ gpio_to_irq(cp->vbus_pin), +++ mvc2_vbus_irq, +++ IRQ_TYPE_EDGE_BOTH | IRQF_ONESHOT, +++ "mvebu-u3d", cp); +++ if (ret < 0) { +++ cp->vbus_pin = -ENODEV; +++ dev_warn(&pdev->dev, +++ "failed to request vbus irq; " +++ "assuming always on\n"); +++ } +++ } +++ +++ /* setup work queue */ +++ cp->qwork = create_singlethread_workqueue("mvc2_queue"); +++ if (!cp->qwork) { +++ dev_err(&pdev->dev, "cannot create workqueue\n"); +++ ret = -ENOMEM; +++ goto err_clk; +++ } +++ +++ INIT_WORK(&cp->vbus_work, mvc2_vbus_work); +++ } +++ +++ cp->reg = devm_kzalloc(&pdev->dev, sizeof(struct mvc2_register), +++ GFP_KERNEL); +++ if (!cp->reg) { +++ ret = -ENOMEM; +++ goto err_qwork; +++ } +++ +++ cp->dev = &pdev->dev; +++ cp->base = base; +++ cp->epnum = 16; +++ if (phy_base) +++ cp->phy_base = phy_base; +++ +++ if (cp->mvc2_version >= USB3_IP_VER_Z2) { +++ cp->reg->lfps_signal = 0x8; +++ cp->reg->counter_pulse = 0x20; +++ cp->reg->ref_int = 0x24; +++ cp->reg->ref_inten = 0x28; +++ cp->reg->global_control = 0x2c; +++ } else { +++ cp->reg->lfps_signal = 0x4; +++ cp->reg->counter_pulse = 0x18; +++ cp->reg->ref_int = 0x1c; +++ cp->reg->ref_inten = 0x20; +++ cp->reg->global_control = 0x24; +++ } +++ +++ /* For Armada 3700, need to skip PHY HW reset */ +++ if (of_device_is_compatible(pdev->dev.of_node, +++ "marvell,armada3700-u3d")) +++ cp->phy_hw_reset = false; +++ else +++ cp->phy_hw_reset = true; +++ +++ /* Get comphy and init if there is */ +++ cp->comphy = devm_of_phy_get(&pdev->dev, pdev->dev.of_node, "usb"); +++ if (!IS_ERR(cp->comphy)) { +++ ret = phy_init(cp->comphy); +++ if (ret) +++ goto disable_phy; +++ +++ ret = phy_power_on(cp->comphy); +++ if (ret) { +++ phy_exit(cp->comphy); +++ goto disable_phy; +++ } +++ } +++ +++ spin_lock_init(&cp->lock); +++ +++ /* init irq status */ +++ irq_enabled = false; +++ +++ cp->eps = kzalloc(cp->epnum * sizeof(struct mvc2_ep) * 2, GFP_KERNEL); +++ if (!cp->eps) { +++ ret = -ENOMEM; +++ goto err_qwork; +++ } +++ +++ ret = mvc2_gadget_init(cp); +++ if (ret < 0) +++ goto err_alloc_eps; +++ +++ eps_init(cp); +++ +++ if (cp->phy_hw_reset) +++ mvc2_hw_reset(cp); +++ +++ dev_set_drvdata(cp->dev, cp); +++ dev_info(cp->dev, "Detected ver %x from Marvell Central IP.\n", ver); +++ +++ return 0; +++ +++err_alloc_eps: +++ kfree(cp->eps); +++err_qwork: +++ if (cp->qwork) +++ destroy_workqueue(cp->qwork); +++disable_phy: +++ if (cp->comphy) { +++ phy_power_off(cp->comphy); +++ phy_exit(cp->comphy); +++ } +++err_clk: +++ clk_disable_unprepare(cp->clk); +++err_mem: +++ devm_kfree(&pdev->dev, cp); +++ return ret; +++} +++ +++#ifdef CONFIG_PM +++static int mvc2_suspend(struct device *dev) +++{ +++ struct mvc2 *cp = (struct mvc2 *)dev_get_drvdata(dev); +++ +++ /* Stop the current activities */ +++ if (cp->driver) +++ stop_activity(cp, cp->driver); +++ +++ /* PHY exit if there is */ +++ if (cp->comphy) { +++ phy_power_off(cp->comphy); +++ phy_exit(cp->comphy); +++ } +++ +++ return 0; +++} +++ +++static int mvc2_resume(struct device *dev) +++{ +++ struct mvc2 *cp = (struct mvc2 *)dev_get_drvdata(dev); +++ int ret; +++ +++ /* PHY init if there is */ +++ if (cp->comphy) { +++ ret = phy_init(cp->comphy); +++ if (ret) +++ return ret; +++ +++ ret = phy_power_on(cp->comphy); +++ if (ret) { +++ phy_power_off(cp->comphy); +++ phy_exit(cp->comphy); +++ return ret; +++ } +++ } +++ +++ /* +++ * USB device will be started only in mvc2_complete, once all other +++ * required device drivers have been resumed. +++ * This is done to avoid a state which U3D driver is resumed too early +++ * before mass storage thread has been resumed, which will lead to USB +++ * transfer time out. +++ */ +++ +++ return 0; +++} +++ +++/* +++ * The PM core executes complete() callbacks after it has executed +++ * the appropriate resume callbacks for all device drivers. +++ * This routine enables USB3 irq in device mode, later on the USB device will be started +++ * once it receives VBUS on interrupt, which starts USB device by enabling EP, and starts +++ * the USB transfer between host and device. +++ * Later on the USB mass storage function thread will be resumed, which will finish the +++ * USB transfer to let the USB device continue to work after resume. +++ * If start the USB device in "resume" operation, some device resuming after USB device +++ * resuming might take long time, which leads to USB transfer time out. +++ */ +++static void mvc2_complete(struct device *dev) +++{ +++ struct mvc2 *cp = (struct mvc2 *)dev_get_drvdata(dev); +++ +++ /* Re-enable USB3 device irq */ +++ mvc2_init_interrupt(cp); +++} +++ +++static const struct dev_pm_ops mvc2_pm_ops = { +++ .suspend = mvc2_suspend, +++ .resume = mvc2_resume, +++ .complete = mvc2_complete +++}; +++#endif +++ +++static int mvc2_remove(struct platform_device *dev) +++{ +++ struct mvc2 *cp; +++ +++ cp = (struct mvc2 *)platform_get_drvdata(dev); +++ mvc2_connect(cp, 0); +++ +++ if (cp->qwork) { +++ flush_workqueue(cp->qwork); +++ destroy_workqueue(cp->qwork); +++ } +++ +++ /* PHY exit if there is */ +++ if (cp->comphy) { +++ phy_power_off(cp->comphy); +++ phy_exit(cp->comphy); +++ } +++ +++ return 0; +++} +++ +++static void mvc2_shutdown(struct platform_device *dev) +++{ +++} +++ +++static const struct of_device_id mv_usb3_dt_match[] = { +++ {.compatible = "marvell,mvebu-u3d"}, +++ {.compatible = "marvell,armada3700-u3d"}, +++ {}, +++}; +++ +++MODULE_DEVICE_TABLE(of, mv_usb3_dt_match); +++ +++static struct platform_driver mvc2_driver = { +++ .probe = mvc2_probe, +++ .remove = mvc2_remove, +++ .shutdown = mvc2_shutdown, +++ .driver = { +++ .name = "mvebu-u3d", +++#ifdef CONFIG_OF +++ .of_match_table = of_match_ptr(mv_usb3_dt_match), +++#endif +++#ifdef CONFIG_PM +++ .pm = &mvc2_pm_ops, +++#endif +++ }, +++}; +++ +++module_platform_driver(mvc2_driver); +++MODULE_ALIAS("platform:mvc2"); +++MODULE_DESCRIPTION(DRIVER_DESC); +++MODULE_AUTHOR("Lei Wen "); +++MODULE_LICENSE("GPL"); ++--- /dev/null +++++ b/drivers/usb/gadget/udc/mvebu_u3d.h ++@@ -0,0 +1,572 @@ +++/** +++ * core.h - Marvell Central IP usb3 core header +++ * +++ * Copyright (C) 2013 Marvell Inc. +++ * +++ * Authors: Lei Wen +++ * +++ * Redistribution and use in source and binary forms, with or without +++ * modification, are permitted provided that the following conditions +++ * are met: +++ * 1. Redistributions of source code must retain the above copyright +++ * notice, this list of conditions, and the following disclaimer, +++ * without modification. +++ * 2. Redistributions in binary form must reproduce the above copyright +++ * notice, this list of conditions and the following disclaimer in the +++ * documentation and/or other materials provided with the distribution. +++ * 3. The names of the above-listed copyright holders may not be used +++ * to endorse or promote products derived from this software without +++ * specific prior written permission. +++ * +++ * ALTERNATIVELY, this software may be distributed under the terms of the +++ * GNU General Public License ("GPL") version 2, as published by the Free +++ * Software Foundation. +++ * +++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +++ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +++ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +++ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +++ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +++ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +++ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +++ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +++ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +++ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +++ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +++ */ +++#ifndef __DRIVERS_USB_MVC2_H +++#define __DRIVERS_USB_MVC2_H +++ +++#define USB3_IP_VER_Z2 0x215 +++#define USB3_IP_VER_Z3 0x220 +++#define USB3_IP_VER_A0 0x221 +++ +++#define MVCP_DEV_INFO 0x0 +++ +++#define MVCP_EP_COUNT 2 +++ +++#define MVCP_LFPS_SIGNAL(n) (0x8 + ((n-1) << 2)) +++#define MVCP_COUNTER_PULSE 0x20 +++#define MVCP_REF_INT 0x24 +++#define MVCP_REF_INTEN 0x28 +++ #define MVCP_REF_INTEN_USB2_CNT (1 << 31) +++ #define MVCP_REF_INTEN_USB2_DISCNT (1 << 30) +++ #define MVCP_REF_INTEN_RESUME (1 << 29) +++ #define MVCP_REF_INTEN_SUSPEND (1 << 28) +++ #define MVCP_REF_INTEN_RESET (1 << 27) +++ #define MVCP_REF_INTEN_POWERON (1 << 26) +++ #define MVCP_REF_INTEN_POWEROFF (1 << 25) +++ +++#define MVCP_GLOBAL_CONTROL 0x2C +++ #define MVCP_GLOBAL_CONTROL_SOFT_CONNECT (1 << 31) +++ #define MVCP_GLOBAL_CONTROL_SOFT_RESET (1 << 30) +++ #define MVCP_GLOBAL_CONTROL_SAFE (1 << 29) +++ #define MVCP_GLOBAL_CONTROL_PHYRESET (1 << 28) +++ #define MVCP_GLOBAL_CONTROL_SOCACCESS (1 << 27) +++ #define MVCP_GLOBAL_CONTROL_SS_VBUS (1 << 3) +++ #define MVCP_GLOBAL_CONTROL_POWERPRESENT (1 << 2) +++ #define MVCP_GLOBAL_CONTROL_USB2_BUS_RESET (1 << 0) +++ +++#define MVCP_SYSTEM_DEBUG 0x30 +++ +++#define MVCP_POWER_MANAGEMENT_DEVICE 0xC8 +++#define MVCP_POWER_MANAGEMENT_SOC 0xCC +++#define MVCP_LOW_POWER_STATUS 0xD0 +++#define MVCP_SOFTWARE_RESET 0xD4 +++ +++#define MVCP_TOP_INT_STATUS 0xD8 +++ #define MVCP_TOP_INT_SS_EP (0x1<<6) +++ #define MVCP_TOP_INT_VBUS (0x1<<5) +++ #define MVCP_TOP_INT_PME (0x1<<4) +++ #define MVCP_TOP_INT_REF (0x1<<3) +++ #define MVCP_TOP_INT_SS_CORE (0x1<<2) +++ #define MVCP_TOP_INT_SS_SYS (0x1<<1) +++ #define MVCP_TOP_INT_SS_AXI (0x1<<1) +++ #define MVCP_TOP_INT_USB2 (0x1<<0) +++ +++#define MVCP_TOP_INT_EN 0xDC +++ +++#define MVCP_TIMER_TIMEOUT(n) (0x194 + ((n-1) << 2)) +++#define MVCP_LFPS_TX_CONFIG 0x19C +++#define MVCP_LFPS_RX_CONFIG 0x1A0 +++#define MVCP_LFPS_WR_TRESET 0x1A4 +++#define MVCP_COUNTER_DELAY_TX 0x1A8 +++#define MVCP_DEV_USB_ADDRESS 0x320 +++#define MVCP_FUNCTION_WAKEUP 0x324 +++ +++#define MVCP_ENDPOINT_0_CONFIG 0x328 +++ #define MVCP_ENDPOINT_0_CONFIG_CHG_STATE (1 << 7) +++ +++#define MVCP_OUT_ENDPOINT_CONFIG_BASE 0x32C +++ +++#define MVCP_IN_ENDPOINT_CONFIG_BASE 0x368 +++ #define MVCP_EP_BURST(x) ((x + 1) << 24) +++ #define MVCP_EP_MAX_PKT(x) ((((x >> 8) & 0x7) \ +++ << 8) | ((x & 0xff) \ +++ << 16)) +++ #define MVCP_EP_RESETSEQ (1 << 14) +++ #define MVCP_EP_STALL (1 << 11) +++ #define MVCP_EP_BULK_STREAM_EN (1 << 7) +++ #define MVCP_EP_ENABLE (1 << 6) +++ #define MVCP_EP_TYPE_INT (0x3 << 4) +++ #define MVCP_EP_TYPE_BLK (0x2 << 4) +++ #define MVCP_EP_TYPE_ISO (0x1 << 4) +++ #define MVCP_EP_TYPE_CTL (0x0 << 4) +++ #define MVCP_EP_TYPE(x) ((x & 0x3) << 4) +++ #define MVCP_EP_NUM(x) (x & 0xf) +++ +++static inline unsigned int epcon(int n, int in) +++{ +++ if (n == 0) +++ return MVCP_ENDPOINT_0_CONFIG; +++ +++ if (in) +++ return MVCP_IN_ENDPOINT_CONFIG_BASE + ((n - 1) << 2); +++ else +++ return MVCP_OUT_ENDPOINT_CONFIG_BASE + ((n - 1) << 2); +++} +++ +++#define MVCP_PHY 0x3A4 +++ #define MVCP_PHY_LTSSM_MASK 0x1f +++ #define LTSSM_DISABLED 0x1 +++ #define LTSSM_U0 0xc +++ #define LTSSM_U1 0xd +++ #define LTSSM_U2 0xe +++ #define LTSSM_U3 0xf +++ +++#define MVCP_SS_CORE_INT 0x3B8 +++ #define MVCP_SS_CORE_INT_SETUP (1 << 14) +++ #define MVCP_SS_CORE_INT_HOT_RESET (1 << 11) +++ #define MVCP_SS_CORE_INT_LTSSM_CHG (1 << 8) +++ +++#define MVCP_SS_CORE_INTEN 0x3BC +++ #define MVCP_SS_CORE_INTEN_SETUP (1 << 14) +++ #define MVCP_SS_CORE_INTEN_HOT_RESET (1 << 11) +++ #define MVCP_SS_CORE_INTEN_LTSSM_CHG (1 << 8) +++ +++/* IP_VERSION <= USB3_IP_VER_Z2 */ +++#define EP_IN_BINTERVAL_REG_1_2_3 0x03C0 +++#define EP_IN_BINTERVAL_REG_4_5_6_7 0x03C4 +++#define EP_IN_BINTERVAL_REG_8_9_10_11 0x03C8 +++#define EP_IN_BINTERVAL_REG_12_13_14_15 0x03CC +++#define EP_OUT_BINTERVAL_REG_1_2_3 0x03D0 +++#define EP_OUT_BINTERVAL_REG_4_5_6_7 0x03D4 +++#define EP_OUT_BINTERVAL_REG_8_9_10_11 0x03D8 +++#define EP_OUT_BINTERVAL_REG_12_13_14_15 0x03DC +++ +++#define MVCP_TX_TSI_NUM 0x3EC +++#define MVCP_START_STATE_DELAY 0x3F0 +++ +++#define MVCP_LOWPOWER 0x3F4 +++ #define MVCP_LOWPOWER_U2_EN (1 << 3) +++ #define MVCP_LOWPOWER_U1_EN (1 << 2) +++ #define MVCP_LOWPOWER_U2_REJ (1 << 1) +++ #define MVCP_LOWPOWER_U1_REJ (1 << 0) +++ +++#define MVCP_SETUP_DP_LOW 0x3F8 +++#define MVCP_SETUP_DP_HIGH 0x3FC +++ +++#define MVCP_SETUP_CONTROL 0x400 +++ #define MVCP_SETUP_CONTROL_FETCHED (1 << 0) +++ +++#define MVCP_DMA_GLOBAL_CONFIG 0x7D0 +++ #define MVCP_DMA_GLOBAL_CONFIG_INTCLR (1 << 3) +++ #define MVCP_DMA_GLOBAL_CONFIG_RESETDONE (1 << 2) +++ #define MVCP_DMA_GLOBAL_CONFIG_RUN (1 << 1) +++ #define MVCP_DMA_GLOBAL_CONFIG_RESET (1 << 0) +++ +++#define MVCP_BULK_STREAMING_ENABLE 0x7D4 +++#define MVCP_EP_OUT_REC_STREAM_ID_BASE 0x7D8 +++#define MVCP_EP_IN_REC_STREAM_ID_BASE 0x814 +++static inline int streamid(int n, int i) +++{ +++ if (n) +++ return MVCP_EP_IN_REC_STREAM_ID_BASE + ((n - 1) << 2); +++ else +++ return MVCP_EP_OUT_REC_STREAM_ID_BASE + ((n - 1) << 2); +++} +++ +++#define MVCP_DMA_COMPLETE_SUCCESS 0x850 +++#define MVCP_DMA_COMPLETE_ERROR 0x854 +++#define MVCP_DMA_BD_FETCH_ERROR 0x858 +++#define MVCP_DMA_BD_FETCH_ERROR_EN 0x85C +++#define MVCP_DMA_DATA_ERROR 0x860 +++#define MVCP_DMA_DATA_ERROR_EN 0x864 +++#define MVCP_DMA_ERROR_HANDLING 0x868 +++#define MVCP_EP_OUT_RX_DMA_CONFIG_BASE 0x86C +++ +++#define MVCP_EP_IN_TX_DMA_CONFIG_BASE 0x8AC +++ #define MVCP_EPDMA_START (1 << 6) +++ +++static inline int ep_dma_config(int num, int dir) +++{ +++ if (dir) +++ return MVCP_EP_IN_TX_DMA_CONFIG_BASE + (num << 2); +++ else +++ return MVCP_EP_OUT_RX_DMA_CONFIG_BASE + (num << 2); +++} +++ +++#define MVCP_EP_OUT_RX_DMA_START_BASE 0x8EC +++#define MVCP_EP_IN_TX_DMA_START_BASE 0x92C +++ +++static inline int ep_dma_addr(int num, int dir) +++{ +++ if (dir) +++ return MVCP_EP_IN_TX_DMA_START_BASE + (num << 2); +++ else +++ return MVCP_EP_OUT_RX_DMA_START_BASE + (num << 2); +++} +++ +++#define MVCP_DMA_SUSPEND 0x9EC +++#define MVCP_DMA_SUSPEND_DONE 0x9F0 +++#define MVCP_DMA_HALT 0x9F4 +++#define MVCP_DMA_HALT_DONE 0x9F8 +++ +++#define MVCP_SS_SYS_INT 0xA0C +++ #define MVCP_SS_AXI_DATA_ERR (1 << 29) +++ #define MVCP_SS_AXI_BDF_ERR (1 << 28) +++ #define MVCP_SS_DONEQ_FULL_ERR (1 << 27) +++ #define MVCP_SS_SYS_INT_DMA (1 << 25) +++ +++#define MVCP_SS_SYS_INTEN 0xA10 +++ #define MVCP_SS_SYS_INTEN_DMA (1 << 25) +++ +++#define MVCP_DMA_STATE(n) (0xA24 + ((n-1) << 2)) +++ #define MVCP_DMA_STATE_DBG_CACHE(x) ((x & 0x3) << 16) +++ +++#define MVCP_SEGMENT_COUNTER(n) (0xA38 + ((n-1) << 2)) +++ +++#define MVCP_EP_IN_DONEQ_START_BASE 0xA78 +++#define MVCP_EP_IN_DONEQ_END_BASE 0xAB8 +++#define MVCP_EP_OUT_DONEQ_START_BASE 0xAF8 +++#define MVCP_EP_OUT_DONEQ_END_BASE 0xB38 +++#define MVCP_EP_IN_DONEQ_WRITE_BASE 0xB78 +++#define MVCP_EP_IN_DONEQ_READ_BASE 0xBB8 +++#define MVCP_EP_OUT_DONEQ_WRITE_BASE 0xBF8 +++#define MVCP_EP_OUT_DONEQ_READ_BASE 0xC38 +++#define MVCP_DONEQ_FULL_STATUS 0xC78 +++ +++static inline int ep_doneq_start(int num, int dir) +++{ +++ if (dir) +++ return MVCP_EP_IN_DONEQ_START_BASE + (num << 2); +++ else +++ return MVCP_EP_OUT_DONEQ_START_BASE + (num << 2); +++} +++ +++static inline int ep_doneq_end(int num, int dir) +++{ +++ if (dir) +++ return MVCP_EP_IN_DONEQ_END_BASE + (num << 2); +++ else +++ return MVCP_EP_OUT_DONEQ_END_BASE + (num << 2); +++} +++ +++static inline int ep_doneq_write(int num, int dir) +++{ +++ if (dir) +++ return MVCP_EP_IN_DONEQ_WRITE_BASE + (num << 2); +++ else +++ return MVCP_EP_OUT_DONEQ_WRITE_BASE + (num << 2); +++} +++ +++static inline int ep_doneq_read(int num, int dir) +++{ +++ if (dir) +++ return MVCP_EP_IN_DONEQ_READ_BASE + (num << 2); +++ else +++ return MVCP_EP_OUT_DONEQ_READ_BASE + (num << 2); +++} +++ +++#define MVCP_BD_MEM_IN 0x123C +++#define MVCP_PL_DEBUG(n) (0x1264 + ((n-1) << 2)) +++#define MVCP_DMA_DBG_IN(n) (0x1274 + ((n-1) << 2)) +++#define MVCP_DMA_DBG_OUT(n) (0x127C + ((n-1) << 2)) +++ +++#define MVCP_DMA_ENABLE 0x1284 +++ +++/* IP_VERSION >= 0X218 */ +++#define SS_IN_DMA_CONTROL_REG(x) (0x1300 + 4 * (x)) +++#define SS_OUT_DMA_CONTROL_REG(x) (0x1340 + 4 * (x)) +++#define DMA_START (1 << 0) +++#define DMA_HALT (1 << 1) +++#define DMA_SUSPEND (1 << 2) +++#define DONEQ_CONFIG (1 << 3) +++#define ABORT_REQ (1 << 4) +++#define ABORT_DONE (1 << 5) +++ +++#define SS_IN_EP_INT_STATUS_REG(x) (0x1380 + 4 * (x)) +++#define SS_OUT_EP_INT_STATUS_REG(x) (0x13C0 + 4 * (x)) +++#define SS_IN_EP_INT_ENABLE_REG(x) (0x1400 + 4 * (x)) +++#define SS_OUT_EP_INT_ENABLE_REG(x) (0x1440 + 4 * (x)) +++ +++#define COMPLETION_SUCCESS (1 << 0) +++#define COMPLETION_WITH_ERR (1 << 1) +++#define BD_FETCH_ERROR (1 << 2) +++#define DMA_DATA_ERROR (1 << 3) +++#define DONEQ_FULL (1 << 4) +++#define DMA_SUSPEND_DONE (1 << 5) +++#define DMA_HALT_DONE (1 << 6) +++#define PRIME_REC (1 << 16) +++#define HIMD_REC (1 << 17) +++#define STREAM_REJ (1 << 18) +++#define HOST_FLOW_CTRL (1 << 19) +++ +++#define SS_EP_TOP_INT_STATUS_REG 0x1480 +++#define SS_EP_TOP_INT_ENABLE_REG 0x1484 +++#define SS_AXI_INT_STATUS_REG 0x1488 +++#define SS_AXI_INT_ENABLE_REG 0x148C +++ +++#define EP_IN_BINTERVAL_REG(x) (0x1490 + 4 * ((x)-1)) +++#define EP_OUT_BINTERVAL_REG(x) (0x14CC + 4 * ((x)-1)) +++/* END IP_VERSION >= 0X218 */ +++ +++struct mvc2_ep; +++ +++struct mvc2_req { +++ struct usb_request req; +++ int bd_total; +++ struct bd *bd; +++ struct list_head queue; +++}; +++ +++enum mvc2_dev_state { +++ MVCP_DEFAULT_STATE, +++ MVCP_ADDRESS_STATE, +++ MVCP_CONFIGURED_STATE, +++}; +++ +++struct mvc2_register { +++ unsigned int lfps_signal; +++ unsigned int counter_pulse; +++ unsigned int ref_int; +++ unsigned int ref_inten; +++ unsigned int global_control; +++}; +++ +++struct mvc2 { +++ struct usb_gadget gadget; +++ struct usb_gadget_driver *driver; +++ struct device *dev; +++ struct clk *clk; +++ struct usb_phy *phy; +++ struct phy *comphy; +++ int irq; +++ void __iomem *base; +++ void __iomem *win_base; +++ void __iomem *phy_base; +++ #define MVCP_STATUS_USB2 (1 << 10) +++ #define MVCP_STATUS_CONNECTED (1 << 9) +++ #define MVCP_STATUS_TEST_MASK (0x7 << 5) +++ #define MVCP_STATUS_TEST(x) (((x) & 0x7) << 5) +++ #define MVCP_STATUS_U3 (1 << 4) +++ #define MVCP_STATUS_U2 (1 << 3) +++ #define MVCP_STATUS_U1 (1 << 2) +++ #define MVCP_STATUS_U0 (1 << 1) +++ #define MVCP_STATUS_POWER_MASK (0xf << 1) +++ #define MVCP_STATUS_SELF_POWERED (1 << 0) +++ unsigned int status; +++ enum mvc2_dev_state dev_state; +++ spinlock_t lock; +++ +++ struct mvc2_req ep0_req; +++ int ep0_dir; +++ void *setup_buf; +++ +++ struct dma_pool *bd_pool; +++ struct mvc2_ep *eps; +++ +++ unsigned int epnum; +++ unsigned int dma_status; +++ +++ struct work_struct *work; +++ struct pm_qos_request qos_idle; +++ s32 lpm_qos; +++ +++ unsigned int isoch_delay; +++ unsigned int u1sel; +++ unsigned int u1pel; +++ unsigned int u2sel; +++ unsigned int u2pel; +++ struct mvc2_register *reg; +++ unsigned int mvc2_version; +++ int vbus_pin; +++ int prev_vbus; +++ struct work_struct vbus_work; +++ struct workqueue_struct *qwork; +++ /* Flags for HW reset. false: no need reset; true: need reset */ +++ bool phy_hw_reset; +++}; +++ +++extern void mvc2_usb2_connect(void); +++extern void mvc2_usb2_disconnect(void); +++extern int eps_init(struct mvc2 *cp); +++extern void reset_seqencenum(struct mvc2_ep *ep, int num, int in); +++extern void mvc2_config_mac(struct mvc2 *cp); +++extern void mvc2_hw_reset(struct mvc2 *cp); +++extern int mvc2_std_request(struct mvc2 *cp, struct usb_ctrlrequest *r, +++ bool *delegate); +++extern int mvc2_gadget_init(struct mvc2 *cp); +++extern void mvc2_usb2_operation(struct mvc2 *cp, int op); +++extern void mvc2_connect(struct mvc2 *cp, int is_on); +++extern unsigned int u1u2_enabled(void); +++ +++#define BD_DMA_BOUNDARY 4096 +++#define BD_ADDR_ALIGN 4 +++/* +++ * Although one BD could transfer 64k-1 bytes data, +++ * for calculation efficiency, we short it for 32k +++ */ +++#define BD_SEGMENT_SHIFT (15) +++#define BD_MAX_SIZE (1 << BD_SEGMENT_SHIFT) +++struct bd { +++ #define BD_BUF_RDY (1 << 31) +++ #define BD_INT_EN (1 << 30) +++ #define BD_NXT_RDY (1 << 29) +++ #define BD_NXT_PTR_JUMP (1 << 28) +++ #define BD_FLUSH_BIT (1 << 27) +++ #define BD_ABORT_BIT (1 << 26) +++ #define BD_ZLP (1 << 25) +++ #define BD_CHAIN_BIT (1 << 24) +++ #define BD_ENCODED_STREAM_ID(x) ((x & 0xff) << 16) +++ #define BD_BUF_SZ(x) (x & 0xffff) +++ unsigned int cmd; +++ unsigned int buf; +++ +++ /* This field should be next bd's physical addr */ +++ unsigned int phys_next; +++#define BD_STREAM_ID(x) ((x & 0xffff) << 16) +++#define BD_STREAM_LEN(x) (x & 0xffff) +++ unsigned int stream; +++}; +++ +++/* +++ * Since each BD would transfer BD_MAX_SIZE, so for each endpoint, it allow to +++ * hold (MAX_QUEUE_SLOT-1)*BD_MAX_SIZE in pending status to be transferred +++ */ +++#define MAX_QUEUE_SLOT 256 +++struct doneq { +++ unsigned int addr; +++ #define DONE_LEN(x) ((x >> 16) & 0xffff) +++ #define DONE_AXI_ERROR (1 << 4) +++ #define DONE_SHORT_PKT (1 << 3) +++ #define DONE_FLUSH (1 << 2) +++ #define DONE_ABORT (1 << 1) +++ #define DONE_CYCLE (1 << 0) +++ unsigned int status; +++}; +++ +++#define MAXNAME 14 +++/* +++ * For one ep, it would pending for several req, +++ * while hardware would handle only one req for one time. +++ * Other req is pending over queue list. +++ * +++ * For the transferring req, we would separate it into several bd to info +++ * hw to transfer. And dma engine would auto feature those chained bd, and +++ * send them out. When each bd finish, its BD_BUF_RDY flag would be cleared. +++ * If BD_INT_EN flag is set, interrupt would be raised correspondingly. +++ * ep -- +++ * \--req->req-> +++ * \ \--bd->bd->null +++ * \--bd->bd->null +++ */ +++struct mvc2_ep { +++ struct usb_ep ep; +++ struct mvc2 *cp; +++ +++ #define MV_CP_EP_TRANSERING (1 << 8) +++ #define MV_CP_EP_STALL (1 << 7) +++ #define MV_CP_EP_BULK_STREAM (1 << 6) +++ #define MV_CP_EP_WEDGE (1 << 5) +++ #define MV_CP_EP_DIRIN (1 << 4) +++ #define MV_CP_EP_NUM_MASK (0xf) +++ #define MV_CP_EP_NUM(x) (x & MV_CP_EP_NUM_MASK) +++ unsigned int state; +++ +++ /* +++ * Actually in current hw solution, +++ * TransferQ and DoneQ size should be equal +++ */ +++ /* +++ * TransferQ: +++ * doneq_cur bd_cur +++ * | | +++ * v v +++ * |-------0============------| +++ * ^ +++ * | +++ * not ready bd +++ * +++ * In above diagram, "-" shows current available bd could be allocated, +++ * while "=" shows bd cannot be touched by the sw. +++ * When we need to do enqueue operation, we need to allocate bd +++ * from "-" pool. +++ * +++ * Note: we need ensure at least one bd in the ring as not ready +++ */ +++ struct bd *bd_ring; +++ dma_addr_t bd_ring_phys; +++ unsigned int bd_cur; +++ unsigned int bd_sz; +++ +++ /* DoneQ */ +++ struct doneq *doneq_start; +++ dma_addr_t doneq_start_phys; +++ unsigned int doneq_cur; +++ +++ char name[MAXNAME]; +++ struct list_head queue; +++ struct list_head wait, tmp; +++ unsigned int dir; +++ unsigned stopped:1, +++ wedge:1, +++ ep_type:2, +++ ep_num:8; +++ unsigned int left_bds; +++ /* Lock to keep queue is safe operated */ +++ spinlock_t lock; +++}; +++ +++#define EPBIT(epnum, dir) ({unsigned int tmp; \ +++ tmp = (dir) ? (0x10000 << epnum) : (1 << epnum); tmp; }) +++ +++#define MV_CP_READ(reg) ({unsigned int val; \ +++ val = ioread32(cp->base + reg); val; }) +++#define MV_CP_WRITE(val, reg) ({iowrite32(val, cp->base + reg); }) +++ +++struct mvc2; +++ +++static inline unsigned int ip_ver(struct mvc2 *cp) +++{ +++ return cp->mvc2_version; +++} +++ +++static inline unsigned int lfps_signal(struct mvc2 *cp, unsigned int n) +++{ +++ return cp->reg->lfps_signal + ((n - 1) << 2); +++} +++ +++/* +++ * struct mvc2_glue - glue structure to combine 2.0/3.0 udc together +++ * @u20: 2.0 driver udc +++ * @u30: 3.0 driver udc +++ * @usb2_connect: whether usb2.0 is in connection +++ * @connect_num: how many usb3 has been tried +++ */ +++struct mvc2_glue { +++ struct usb_udc *u20; +++ struct usb_udc *u30; +++ +++ int usb2_connect; +++ unsigned int status; +++}; +++ +++extern struct mvc2_glue glue; +++extern bool usb3_disconnect; +++ +++int mvc2_checkvbus(struct mvc2 *cp); +++void mvc2_handle_setup(struct mvc2 *cp); +++int mv_udc_register_status_notify(struct notifier_block *nb); +++ +++#endif ++--- a/include/linux/usb/gadget.h +++++ b/include/linux/usb/gadget.h ++@@ -916,4 +916,24 @@ extern void usb_ep_autoconfig_release(st ++ ++ extern void usb_ep_autoconfig_reset(struct usb_gadget *); ++ +++/** +++* struct usb_udc - describes one usb device controller +++* @driver - the gadget driver pointer. For use by the class code +++* @dev - the child device to the actual controller +++* @gadget - the gadget. For use by the class code +++* @list - for use by the udc class driver +++* +++* This represents the internal data structure which is used by the UDC-class +++* to hold information about udc driver and gadget together. +++*/ +++struct usb_udc { +++ struct usb_gadget_driver *driver; +++ struct usb_gadget *gadget; +++ struct device dev; +++ struct list_head list; +++ bool vbus; +++}; +++ +++extern struct usb_udc *udc_detect(struct list_head *udc_list, struct usb_gadget_driver *driver); +++ ++ #endif /* __LINUX_USB_GADGET_H */ +-- +2.17.1 + diff --git a/patches-22.03.2/3010-update-mvebu-fwtools-script.patch b/patches-22.03.2/3010-update-mvebu-fwtools-script.patch new file mode 100644 index 0000000..99d4a0e --- /dev/null +++ b/patches-22.03.2/3010-update-mvebu-fwtools-script.patch @@ -0,0 +1,82 @@ +From d5d5a2cd3dd981a0fc68a528151a5ba772e7ab0a Mon Sep 17 00:00:00 2001 +From: Weiping Yang +Date: Mon, 27 Feb 2023 01:41:14 -0500 +Subject: [PATCH] update-mvebu-fwtools-script + +--- + .../base-files/lib/upgrade/fwtool.sh | 63 +++++++++++++++++++ + 1 file changed, 63 insertions(+) + create mode 100644 target/linux/mvebu/cortexa53/base-files/lib/upgrade/fwtool.sh + +diff --git a/target/linux/mvebu/cortexa53/base-files/lib/upgrade/fwtool.sh b/target/linux/mvebu/cortexa53/base-files/lib/upgrade/fwtool.sh +new file mode 100644 +index 0000000000..7c82bc5289 +--- /dev/null ++++ b/target/linux/mvebu/cortexa53/base-files/lib/upgrade/fwtool.sh +@@ -0,0 +1,63 @@ ++fwtool_check_signature() { ++ [ $# -gt 1 ] && return 1 ++ ++ [ ! -x /usr/bin/ucert ] && { ++ if [ "$REQUIRE_IMAGE_SIGNATURE" = 1 ]; then ++ return 1 ++ else ++ return 0 ++ fi ++ } ++ ++ if ! fwtool -q -s /tmp/sysupgrade.ucert "$1"; then ++ v "Image signature not present" ++ [ "$REQUIRE_IMAGE_SIGNATURE" = 1 -a "$FORCE" != 1 ] && { ++ v "Use sysupgrade -F to override this check when downgrading or flashing to vendor firmware" ++ } ++ [ "$REQUIRE_IMAGE_SIGNATURE" = 1 ] && return 1 ++ return 0 ++ fi ++ ++ fwtool -q -T -s /dev/null "$1" | \ ++ ucert -V -m - -c "/tmp/sysupgrade.ucert" -P /etc/opkg/keys ++ ++ return $? ++} ++ ++fwtool_check_image() { ++ [ $# -gt 1 ] && return 1 ++ ++ . /usr/share/libubox/jshn.sh ++ ++ if ! fwtool -q -i /tmp/sysupgrade.meta "$1"; then ++ v "Image metadata not present" ++ [ "$REQUIRE_IMAGE_METADATA" = 1 -a "$FORCE" != 1 ] && { ++ v "Use sysupgrade -F to override this check when downgrading or flashing to vendor firmware" ++ } ++ [ "$REQUIRE_IMAGE_METADATA" = 1 ] && return 1 ++ return 0 ++ fi ++ ++ json_load "$(cat /tmp/sysupgrade.meta)" || { ++ v "Invalid image metadata" ++ return 1 ++ } ++ ++ device="$(cat /tmp/sysinfo/board_name)" ++ ++ json_select supported_devices || return 1 ++ ++ json_get_keys dev_keys ++ for k in $dev_keys; do ++ json_get_var dev "$k" ++ dev=$(echo $dev | awk -F "-emmc" '{print $1}') ++ [ "$dev" = "$device" ] && return 0 ++ done ++ ++ v "Device $device not supported by this image" ++ for k in $dev_keys; do ++ json_get_var dev "$k" ++ v "$dev" ++ done ++ return 1 ++} +-- +2.17.1 + diff --git a/patches-22.03.2/3011-mvebu-adjust-cortexa53.patch b/patches-22.03.2/3011-mvebu-adjust-cortexa53.patch new file mode 100644 index 0000000..10ba6df --- /dev/null +++ b/patches-22.03.2/3011-mvebu-adjust-cortexa53.patch @@ -0,0 +1,33 @@ +From 61725b10deb08c3e39dd239164a436c999675543 Mon Sep 17 00:00:00 2001 +From: Weiping Yang +Date: Mon, 27 Feb 2023 01:42:57 -0500 +Subject: [PATCH] mvebu-adjust-cortexa53 + +--- + target/linux/mvebu/image/cortexa53.mk | 11 ++++++----- + 1 file changed, 6 insertions(+), 5 deletions(-) + +diff --git a/target/linux/mvebu/image/cortexa53.mk b/target/linux/mvebu/image/cortexa53.mk +index bfaa597d9a..6f560f728e 100644 +--- a/target/linux/mvebu/image/cortexa53.mk ++++ b/target/linux/mvebu/image/cortexa53.mk +@@ -1,10 +1,11 @@ +-define Device/glinet_gl-mv1000 +- $(call Device/Default-arm64) +- DEVICE_VENDOR := GL.iNet +- DEVICE_MODEL := GL-MV1000 ++define Device/gl-mv1000-emmc ++ $(call Device/Default-arm64-emmc) ++ DEVICE_TITLE := GL.iNet GL-MV1000 EMMC + SOC := armada-3720 ++ DEVICE_DTS := armada-gl-mv1000-emmc ++ SUPPORTED_DEVICES := gl-mv1000-emmc + endef +-TARGET_DEVICES += glinet_gl-mv1000 ++TARGET_DEVICES += gl-mv1000-emmc + + define Device/globalscale_espressobin + $(call Device/Default-arm64) +-- +2.17.1 + diff --git a/patches-22.03.2/4005-support-mv1000-target.patch b/patches-22.03.2/4005-support-mv1000-target.patch new file mode 100644 index 0000000..3646352 --- /dev/null +++ b/patches-22.03.2/4005-support-mv1000-target.patch @@ -0,0 +1,112 @@ +From 384c05eb480ae34d5fa59435330682470747e68c Mon Sep 17 00:00:00 2001 +From: Weiping Yang +Date: Mon, 27 Feb 2023 01:44:01 -0500 +Subject: [PATCH] support-mv1000-target + +--- + ...l-mv1000.dts => armada-gl-mv1000-emmc.dts} | 48 ++++++++++++++----- + 1 file changed, 37 insertions(+), 11 deletions(-) + rename target/linux/mvebu/files/arch/arm64/boot/dts/marvell/{armada-3720-gl-mv1000.dts => armada-gl-mv1000-emmc.dts} (78%) + +diff --git a/target/linux/mvebu/files/arch/arm64/boot/dts/marvell/armada-3720-gl-mv1000.dts b/target/linux/mvebu/files/arch/arm64/boot/dts/marvell/armada-gl-mv1000-emmc.dts +similarity index 78% +rename from target/linux/mvebu/files/arch/arm64/boot/dts/marvell/armada-3720-gl-mv1000.dts +rename to target/linux/mvebu/files/arch/arm64/boot/dts/marvell/armada-gl-mv1000-emmc.dts +index acf15e8ca9..3701b238f4 100644 +--- a/target/linux/mvebu/files/arch/arm64/boot/dts/marvell/armada-3720-gl-mv1000.dts ++++ b/target/linux/mvebu/files/arch/arm64/boot/dts/marvell/armada-gl-mv1000-emmc.dts +@@ -7,13 +7,34 @@ + + / { + model = "GL.iNet GL-MV1000"; +- compatible = "glinet,gl-mv1000", "marvell,armada3720"; ++ compatible = "gl-mv1000", "marvell,armada3720"; + + aliases { +- led-boot = &led_power; +- led-failsafe = &led_power; +- led-running = &led_power; +- led-upgrade = &led_power; ++ //led-boot = &led_power; ++ //led-failsafe = &led_power; ++ //led-running = &led_power; ++ //led-upgrade = &led_power; ++ }; ++ ++ gl_hw { ++ compatible = "gl-hw-info"; ++ ++ #address-cells = <1>; ++ #size-cells = <1>; ++ model = "mv1000"; ++ wan = "wan"; ++ lan = "lan0 lan1 usb0"; ++ flash_size = <7456>; ++ switch-button = "gpio-22"; ++ reset-button = "gpio-14"; ++ usb-port = "1-1"; ++ factory_data { ++ device_mac = "factory"; ++ device_ddns = "factory", "0x10"; ++ device_sn_bak = "factory", "0x20"; ++ device_sn = "factory", "0x30"; ++ country_code = "factory", "0x88"; ++ }; + }; + + chosen { +@@ -49,7 +70,7 @@ + + switch { + label = "switch"; +- linux,code = ; ++ linux,code = ; + gpios = <&gpiosb 22 GPIO_ACTIVE_LOW>; + }; + }; +@@ -58,17 +79,17 @@ + compatible = "gpio-leds"; + + vpn { +- label = "green:vpn"; ++ label = "gl-mv1000:white:vpn"; + gpios = <&gpionb 11 GPIO_ACTIVE_LOW>; + }; + + wan { +- label = "green:wan"; ++ label = "gl-mv1000:white:wan"; + gpios = <&gpionb 12 GPIO_ACTIVE_LOW>; + }; + + led_power: power { +- label = "green:power"; ++ label = "gl-mv1000:white:power"; + gpios = <&gpionb 13 GPIO_ACTIVE_LOW>; + default-state = "on"; + }; +@@ -97,14 +118,19 @@ + partition@f0000 { + label = "u-boot-env"; + reg = <0Xf0000 0x8000>; +- read-only; ++ //read-only; + }; + + factory: partition@f8000 { + label = "factory"; + reg = <0xf8000 0x8000>; +- read-only; ++ //read-only; + }; ++ ++ partition@100000 { ++ label = "log"; ++ reg = <0x100000 0x20000>; ++ }; + }; + }; + }; +-- +2.17.1 +