diff --git a/include/image.mk b/include/image.mk index b6b796dd97..284298e5ed 100644 --- a/include/image.mk +++ b/include/image.mk @@ -279,12 +279,11 @@ endef define Image/Manifest $(call opkg,$(TARGET_DIR_ORIG)) list-installed > \ $(BIN_DIR)/$(IMG_PREFIX)$(if $(PROFILE_SANITIZED),-$(PROFILE_SANITIZED)).manifest -ifndef IB - $(if $(CONFIG_JSON_CYCLONEDX_SBOM), \ - $(SCRIPT_DIR)/package-metadata.pl imgcyclonedxsbom \ - $(TMP_DIR)/.packageinfo \ +ifneq ($(CONFIG_JSON_CYCLONEDX_SBOM),) + $(SCRIPT_DIR)/package-metadata.pl imgcyclonedxsbom \ + $(if $(IB),$(TOPDIR)/.packageinfo, $(TMP_DIR)/.packageinfo) \ $(BIN_DIR)/$(IMG_PREFIX)$(if $(PROFILE_SANITIZED),-$(PROFILE_SANITIZED)).manifest > \ - $(BIN_DIR)/$(IMG_PREFIX)$(if $(PROFILE_SANITIZED),-$(PROFILE_SANITIZED)).bom.cdx.json) + $(BIN_DIR)/$(IMG_PREFIX)$(if $(PROFILE_SANITIZED),-$(PROFILE_SANITIZED)).bom.cdx.json endif endef diff --git a/include/package-dumpinfo.mk b/include/package-dumpinfo.mk index fc25099ad3..e180c770e3 100644 --- a/include/package-dumpinfo.mk +++ b/include/package-dumpinfo.mk @@ -44,7 +44,6 @@ $(if $(KCONFIG),Kernel-Config: $(KCONFIG) )$(if $(BUILDONLY),Build-Only: $(BUILDONLY) )$(if $(HIDDEN),Hidden: $(HIDDEN) )Description: $(if $(Package/$(1)/description),$(Package/$(1)/description),$(TITLE)) -$(MAINTAINER) @@ $(if $(Package/$(1)/config),Config: $(Package/$(1)/config) diff --git a/include/trusted-firmware-a.mk b/include/trusted-firmware-a.mk index e292b393ca..9fef2bf51e 100644 --- a/include/trusted-firmware-a.mk +++ b/include/trusted-firmware-a.mk @@ -63,9 +63,11 @@ define Build/Trusted-Firmware-A/Target URL:=https://www.trustedfirmware.org/projects/tf-a/ endef - define Package/trusted-firmware-a-$(1)/install + ifndef Package/trusted-firmware-a-$(1)/install + define Package/trusted-firmware-a-$(1)/install $$(Package/trusted-firmware-a/install) - endef + endef + endif endef define Build/Configure/Trusted-Firmware-A diff --git a/package/boot/arm-trusted-firmware-mediatek/Makefile b/package/boot/arm-trusted-firmware-mediatek/Makefile index c065c7e67d..37d71e183c 100644 --- a/package/boot/arm-trusted-firmware-mediatek/Makefile +++ b/package/boot/arm-trusted-firmware-mediatek/Makefile @@ -33,6 +33,7 @@ define Trusted-Firmware-A/Default NAND_TYPE:= BOARD_QFN:= DRAM_USE_COMB:= + RAM_BOOT_UART_DL:= USE_UBI:= endef @@ -113,6 +114,17 @@ define Trusted-Firmware-A/mt7622-sdmmc-2ddr DDR3_FLYBY:=1 endef +define Trusted-Firmware-A/mt7981-ram-ddr4 + NAME:=MediaTek MT7981 (RAM, DDR4) + BOOT_DEVICE:=ram + BUILD_SUBTARGET:=filogic + PLAT:=mt7981 + DDR_TYPE:=ddr4 + RAM_BOOT_UART_DL:=1 + HIDDEN:= + DEFAULT:=TARGET_mediatek_filogic +endef + define Trusted-Firmware-A/mt7981-emmc-ddr4 NAME:=MediaTek MT7981 (eMMC, DDR4) BOOT_DEVICE:=emmc @@ -137,6 +149,17 @@ define Trusted-Firmware-A/mt7981-nor-ddr3 DDR_TYPE:=ddr3 endef +define Trusted-Firmware-A/mt7981-ram-ddr3 + NAME:=MediaTek MT7981 (RAM, DDR3) + BOOT_DEVICE:=ram + BUILD_SUBTARGET:=filogic + PLAT:=mt7981 + DDR_TYPE:=ddr3 + RAM_BOOT_UART_DL:=1 + HIDDEN:= + DEFAULT:=TARGET_mediatek_filogic +endef + define Trusted-Firmware-A/mt7981-emmc-ddr3 NAME:=MediaTek MT7981 (eMMC, DDR3) BOOT_DEVICE:=emmc @@ -169,6 +192,17 @@ define Trusted-Firmware-A/mt7981-spim-nand-ddr3 DDR_TYPE:=ddr3 endef +define Trusted-Firmware-A/mt7986-ram-ddr4 + NAME:=MediaTek MT7986 (RAM, DDR4) + BOOT_DEVICE:=ram + BUILD_SUBTARGET:=filogic + PLAT:=mt7986 + DDR_TYPE:=ddr4 + RAM_BOOT_UART_DL:=1 + HIDDEN:= + DEFAULT:=TARGET_mediatek_filogic +endef + define Trusted-Firmware-A/mt7986-nor-ddr4 NAME:=MediaTek MT7986 (SPI-NOR, DDR4) BOOT_DEVICE:=nor @@ -229,6 +263,17 @@ define Trusted-Firmware-A/mt7986-spim-nand-4k-ddr4 NAND_TYPE:=spim:4k+256 endef +define Trusted-Firmware-A/mt7986-ram-ddr3 + NAME:=MediaTek MT7986 (RAM, DDR3) + BOOT_DEVICE:=ram + BUILD_SUBTARGET:=filogic + PLAT:=mt7986 + DDR_TYPE:=ddr3 + RAM_BOOT_UART_DL:=1 + HIDDEN:= + DEFAULT:=TARGET_mediatek_filogic +endef + define Trusted-Firmware-A/mt7986-nor-ddr3 NAME:=MediaTek MT7986 (SPI-NOR, DDR3) BOOT_DEVICE:=nor @@ -349,6 +394,17 @@ define Trusted-Firmware-A/mt7988-spim-nand-ddr4 DDR_TYPE:=ddr4 endef +define Trusted-Firmware-A/mt7988-ram-comb + NAME:=MediaTek MT7988 (RAM) + BOOT_DEVICE:=ram + BUILD_SUBTARGET:=filogic + PLAT:=mt7988 + DRAM_USE_COMB:=1 + RAM_BOOT_UART_DL:=1 + HIDDEN:= + DEFAULT:=TARGET_mediatek_filogic +endef + define Trusted-Firmware-A/mt7988-nor-comb NAME:=MediaTek MT7988 (SPI-NOR) BOOT_DEVICE:=nor @@ -418,18 +474,22 @@ TFA_TARGETS:= \ mt7622-emmc-2ddr \ mt7622-sdmmc-1ddr \ mt7622-sdmmc-2ddr \ + mt7981-ram-ddr3 \ mt7981-emmc-ddr3 \ mt7981-nor-ddr3 \ mt7981-sdmmc-ddr3 \ mt7981-snand-ddr3 \ mt7981-spim-nand-ddr3 \ + mt7981-ram-ddr4 \ mt7981-emmc-ddr4 \ mt7981-spim-nand-ddr4 \ + mt7986-ram-ddr3 \ mt7986-emmc-ddr3 \ mt7986-nor-ddr3 \ mt7986-sdmmc-ddr3 \ mt7986-snand-ddr3 \ mt7986-spim-nand-ddr3 \ + mt7986-ram-ddr4 \ mt7986-emmc-ddr4 \ mt7986-nor-ddr4 \ mt7986-sdmmc-ddr4 \ @@ -447,6 +507,7 @@ TFA_TARGETS:= \ mt7988-sdmmc-ddr4 \ mt7988-snand-ddr4 \ mt7988-spim-nand-ddr4 \ + mt7988-ram-comb \ mt7988-emmc-comb \ mt7988-nor-comb \ mt7988-sdmmc-comb \ @@ -464,9 +525,20 @@ TFA_MAKE_FLAGS += \ HAVE_DRAM_OBJ_FILE=yes \ $(if $(DDR3_FLYBY),DDR3_FLYBY=1) \ $(if $(DRAM_USE_COMB),DRAM_USE_COMB=1) \ + $(if $(RAM_BOOT_UART_DL),RAM_BOOT_UART_DL=1) \ $(if $(USE_UBI),UBI=1 $(if $(findstring mt7622,$(PLAT)),OVERRIDE_UBI_START_ADDR=0x80000)) \ all +define Package/trusted-firmware-a-ram/install + $(INSTALL_DIR) $(STAGING_DIR_IMAGE) + $(INSTALL_DATA) $(PKG_BUILD_DIR)/build/$(PLAT)/release/bl2.bin $(BIN_DIR)/$(BUILD_VARIANT)-bl2.bin +endef +Package/trusted-firmware-a-mt7981-ram-ddr3/install = $(Package/trusted-firmware-a-ram/install) +Package/trusted-firmware-a-mt7981-ram-ddr4/install = $(Package/trusted-firmware-a-ram/install) +Package/trusted-firmware-a-mt7986-ram-ddr3/install = $(Package/trusted-firmware-a-ram/install) +Package/trusted-firmware-a-mt7986-ram-ddr4/install = $(Package/trusted-firmware-a-ram/install) +Package/trusted-firmware-a-mt7988-ram-comb/install = $(Package/trusted-firmware-a-ram/install) + define Package/trusted-firmware-a/install $(INSTALL_DIR) $(STAGING_DIR_IMAGE) $(INSTALL_DATA) $(PKG_BUILD_DIR)/build/$(PLAT)/release/bl2.img $(STAGING_DIR_IMAGE)/$(BUILD_VARIANT)-bl2.img diff --git a/package/boot/uboot-envtools/files/mediatek_mt7622 b/package/boot/uboot-envtools/files/mediatek_mt7622 index c8d3857484..6698e06ee3 100644 --- a/package/boot/uboot-envtools/files/mediatek_mt7622 +++ b/package/boot/uboot-envtools/files/mediatek_mt7622 @@ -57,6 +57,9 @@ ubnt,unifi-6-lr-v2-ubootmod|\ ubnt,unifi-6-lr-v3-ubootmod) ubootenv_add_uci_config "/dev/mtd$(find_mtd_index "u-boot-env")" "0x0" "0x4000" "0x1000" ;; +ubnt,unifi-6-lr-v2) + ubootenv_add_uci_config "/dev/mtd3" "0x0" "0x1000" "0x1000" "1" + ;; xiaomi,redmi-router-ax6s) ubootenv_add_uci_config "/dev/mtd3" "0x0" "0x10000" "0x40000" ;; diff --git a/package/boot/uboot-mediatek/patches/401-update-u7623-defconfig.patch b/package/boot/uboot-mediatek/patches/401-update-u7623-defconfig.patch index 5b1010c005..f2a79b19d5 100644 --- a/package/boot/uboot-mediatek/patches/401-update-u7623-defconfig.patch +++ b/package/boot/uboot-mediatek/patches/401-update-u7623-defconfig.patch @@ -186,7 +186,7 @@ +reset_factory=eraseenv && reset +_init_env=setenv _init_env ; saveenv ; saveenv +_firstboot=setenv _firstboot ; run _switch_to_menu _update_bootdev _init_env boot_first -+_update_bootdev=setenv _update_bootdev ; setenv bootargs "$console root=/dev/mmcblk0p65" ++_update_bootdev=setenv _update_bootdev ; setenv bootargs "$console root=/dev/fit0 rootwait" +_switch_to_menu=setenv _switch_to_menu ; setenv bootdelay 3 ; setenv bootmenu_delay 3 ; setenv bootmenu_0 $bootmenu_0d ; setenv bootmenu_0d ; run _bootmenu_update_title +_bootmenu_update_title=setenv _bootmenu_update_title ; setenv bootmenu_title "$bootmenu_title $ver" --- a/arch/arm/dts/mt7623a-unielec-u7623-02-emmc.dts diff --git a/package/devel/kselftests-bpf/Makefile b/package/devel/kselftests-bpf/Makefile index 0a5b874e04..d69e1e6dc3 100644 --- a/package/devel/kselftests-bpf/Makefile +++ b/package/devel/kselftests-bpf/Makefile @@ -13,7 +13,7 @@ PKG_VERSION:=$(LINUX_VERSION) PKG_RELEASE:=1 PKG_MAINTAINER:=Tony Ambardar -PKG_BUILD_FLAGS:=gc-sections lto +PKG_BUILD_FLAGS:=no-lto PKG_BUILD_PARALLEL:=1 PKG_FLAGS:=nonshared @@ -23,8 +23,9 @@ include $(INCLUDE_DIR)/nls.mk define Package/kselftests-bpf SECTION:=devel CATEGORY:=Development - DEPENDS:= +libelf +zlib +libpthread +librt @!IN_SDK \ - @KERNEL_DEBUG_FS @KERNEL_DEBUG_INFO_BTF @KERNEL_BPF_EVENTS + DEPENDS:= \ + +libelf +zlib +libpthread +librt @!IN_SDK \ + @KERNEL_DEBUG_FS @KERNEL_DEBUG_INFO_BTF @KERNEL_BPF_EVENTS TITLE:=Linux Kernel Selftests (BPF) URL:=http://www.kernel.org endef @@ -33,31 +34,40 @@ define Package/kselftests-bpf/description kselftests-bpf is the Linux kernel BPF test suite endef -TEST_TARGET = test_verifier +EXE_TARGETS = test_verifier + +MOD_TARGETS = $(if $(call kernel_patchver_ge,6.4),bpf_testmod.ko) MAKE_PATH:=tools/testing/selftests/bpf MAKE_VARS = \ ARCH="$(LINUX_KARCH)" \ CROSS_COMPILE="$(TARGET_CROSS)" \ - SAN_CFLAGS="$(TARGET_CFLAGS) $(TARGET_CPPFLAGS)" \ + EXTRA_CFLAGS="$(TARGET_CFLAGS) $(TARGET_CPPFLAGS)" \ LDLIBS="$(TARGET_LDFLAGS)" \ TOOLCHAIN_INCLUDE="$(TOOLCHAIN_INC_DIRS)" \ - VMLINUX_BTF="$(LINUX_DIR)/vmlinux" + KBUILD_OUTPUT="$(LINUX_DIR)" MAKE_FLAGS = \ $(if $(findstring c,$(OPENWRT_VERBOSE)),V=1,V='') \ - O=$(PKG_BUILD_DIR) + OUTPUT=$(PKG_BUILD_DIR) define Build/Compile +$(MAKE_VARS) \ $(MAKE) $(PKG_JOBS) -C $(LINUX_DIR)/$(MAKE_PATH) \ - $(MAKE_FLAGS) $(TEST_TARGET) ; + $(MAKE_FLAGS) $(EXE_TARGETS) $(MOD_TARGETS) ; endef define Package/kselftests-bpf/install - $(INSTALL_DIR) $(1)/usr/bin - $(INSTALL_BIN) $(PKG_BUILD_DIR)/$(TEST_TARGET) $(1)/usr/bin/ + $(INSTALL_DIR) $(1)/usr/libexec/$(PKG_NAME) + $(foreach tgt,$(MOD_TARGETS), \ + $(INSTALL_DATA) \ + $(PKG_BUILD_DIR)/$(tgt) $(1)/usr/libexec/$(PKG_NAME); \ + ) + $(foreach tgt,$(EXE_TARGETS), \ + $(INSTALL_BIN) \ + $(PKG_BUILD_DIR)/$(tgt) $(1)/usr/libexec/$(PKG_NAME); \ +) endef $(eval $(call BuildPackage,kselftests-bpf)) diff --git a/package/kernel/qca-nss-dp/Makefile b/package/kernel/qca-nss-dp/Makefile index e6c380aead..a917f0518c 100644 --- a/package/kernel/qca-nss-dp/Makefile +++ b/package/kernel/qca-nss-dp/Makefile @@ -1,13 +1,13 @@ include $(TOPDIR)/rules.mk PKG_NAME:=qca-nss-dp -PKG_RELEASE:=3 +PKG_RELEASE:=1 PKG_SOURCE_URL:=https://git.codelinaro.org/clo/qsdk/oss/lklm/nss-dp.git PKG_SOURCE_PROTO:=git -PKG_SOURCE_DATE:=2023-06-06 -PKG_SOURCE_VERSION:=fa67464466f69f00967cc373d1bdd6025f57eb89 -PKG_MIRROR_HASH:=39329770042c85b32780cd12eef2aad2c5df79f34d1b7081e5ba1e1cc0b1b161 +PKG_SOURCE_DATE:=2024-04-16 +PKG_SOURCE_VERSION:=5bf8b91e9fc209f175f9a58723b03055ace3d581 +PKG_MIRROR_HASH:=e86b04ea674c18fb69cd09a45ccab50317b85117e40d76c8457052c2e55d7c18 PKG_BUILD_PARALLEL:=1 PKG_FLAGS:=nonshared diff --git a/package/kernel/qca-nss-dp/patches/0006-nss_dp_main-Use-a-phy-handle-property-to-connect-to-.patch b/package/kernel/qca-nss-dp/patches/0006-nss_dp_main-Use-a-phy-handle-property-to-connect-to-.patch index 0432b82dda..20a7e6b350 100644 --- a/package/kernel/qca-nss-dp/patches/0006-nss_dp_main-Use-a-phy-handle-property-to-connect-to-.patch +++ b/package/kernel/qca-nss-dp/patches/0006-nss_dp_main-Use-a-phy-handle-property-to-connect-to-.patch @@ -26,7 +26,7 @@ Signed-off-by: Robert Marko --- a/include/nss_dp_dev.h +++ b/include/nss_dp_dev.h -@@ -202,13 +202,10 @@ struct nss_dp_dev { +@@ -225,13 +225,10 @@ struct nss_dp_dev { unsigned long drv_flags; /* Driver specific feature flags */ /* Phy related stuff */ @@ -43,7 +43,7 @@ Signed-off-by: Robert Marko --- a/nss_dp_main.c +++ b/nss_dp_main.c -@@ -418,7 +418,7 @@ static int nss_dp_open(struct net_device +@@ -436,7 +436,7 @@ static int nss_dp_open(struct net_device netif_start_queue(netdev); @@ -52,7 +52,7 @@ Signed-off-by: Robert Marko /* Notify data plane link is up */ if (dp_priv->data_plane_ops->link_state(dp_priv->dpc, 1)) { netdev_dbg(netdev, "Data plane set link failed\n"); -@@ -615,6 +615,12 @@ static int32_t nss_dp_of_get_pdata(struc +@@ -633,6 +633,12 @@ static int32_t nss_dp_of_get_pdata(struc return -EFAULT; } @@ -65,7 +65,7 @@ Signed-off-by: Robert Marko if (of_property_read_u32(np, "qcom,mactype", &hal_pdata->mactype)) { pr_err("%s: error reading mactype\n", np->name); return -EFAULT; -@@ -635,18 +641,6 @@ static int32_t nss_dp_of_get_pdata(struc +@@ -653,18 +659,6 @@ static int32_t nss_dp_of_get_pdata(struc return -EFAULT; #endif @@ -84,7 +84,7 @@ Signed-off-by: Robert Marko #if (LINUX_VERSION_CODE < KERNEL_VERSION(6, 1, 0)) maddr = (uint8_t *)of_get_mac_address(np); #if (LINUX_VERSION_CODE > KERNEL_VERSION(5, 4, 0)) -@@ -695,56 +689,6 @@ static int32_t nss_dp_of_get_pdata(struc +@@ -753,56 +747,6 @@ static int32_t nss_dp_of_get_pdata(struc return 0; } @@ -141,7 +141,7 @@ Signed-off-by: Robert Marko #ifdef CONFIG_NET_SWITCHDEV /* * nss_dp_is_phy_dev() -@@ -803,7 +747,6 @@ static int32_t nss_dp_probe(struct platf +@@ -861,7 +805,6 @@ static int32_t nss_dp_probe(struct platf struct device_node *np = pdev->dev.of_node; struct nss_gmac_hal_platform_data gmac_hal_pdata; int32_t ret = 0; @@ -149,7 +149,7 @@ Signed-off-by: Robert Marko #if defined(NSS_DP_PPE_SUPPORT) uint32_t vsi_id; fal_port_t port_id; -@@ -880,22 +823,14 @@ static int32_t nss_dp_probe(struct platf +@@ -940,22 +883,15 @@ static int32_t nss_dp_probe(struct platf dp_priv->drv_flags |= NSS_DP_PRIV_FLAG(INIT_DONE); @@ -161,20 +161,22 @@ Signed-off-by: Robert Marko - } - snprintf(phy_id, MII_BUS_ID_SIZE + 3, PHY_ID_FMT, - dp_priv->miibus->id, dp_priv->phy_mdio_addr); -- + if (dp_priv->phy_node) { - SET_NETDEV_DEV(netdev, &pdev->dev); - dp_priv->phydev = phy_connect(netdev, phy_id, - &nss_dp_adjust_link, - dp_priv->phy_mii_type); - if (IS_ERR(dp_priv->phydev)) { - netdev_dbg(netdev, "failed to connect to phy device\n"); +- goto phy_setup_fail; +- } + dp_priv->phydev = of_phy_connect(netdev, dp_priv->phy_node, -+ &nss_dp_adjust_link, 0, -+ dp_priv->phy_mii_type); ++ &nss_dp_adjust_link, 0, ++ dp_priv->phy_mii_type); + if (!(dp_priv->phydev)) { + netdev_err(netdev, "failed to connect to phy device\n"); - goto phy_setup_fail; - } ++ goto phy_setup_fail; ++ } } + + #if defined(NSS_DP_PPE_SUPPORT) diff --git a/package/kernel/qca-nss-dp/patches/0008-nss-dp-allow-setting-netdev-name-from-DTS.patch b/package/kernel/qca-nss-dp/patches/0008-nss-dp-allow-setting-netdev-name-from-DTS.patch index e90bf32ced..d70284dfb5 100644 --- a/package/kernel/qca-nss-dp/patches/0008-nss-dp-allow-setting-netdev-name-from-DTS.patch +++ b/package/kernel/qca-nss-dp/patches/0008-nss-dp-allow-setting-netdev-name-from-DTS.patch @@ -15,7 +15,7 @@ Signed-off-by: Robert Marko --- a/nss_dp_main.c +++ b/nss_dp_main.c -@@ -746,18 +746,29 @@ static int32_t nss_dp_probe(struct platf +@@ -804,18 +804,29 @@ static int32_t nss_dp_probe(struct platf struct nss_dp_dev *dp_priv; struct device_node *np = pdev->dev.of_node; struct nss_gmac_hal_platform_data gmac_hal_pdata; diff --git a/package/kernel/qca-nss-dp/patches/0009-nss-dp-switchdev-fix-FDB-roaming.patch b/package/kernel/qca-nss-dp/patches/0009-nss-dp-switchdev-fix-FDB-roaming.patch index ec10bdc2d9..7fccfac76d 100644 --- a/package/kernel/qca-nss-dp/patches/0009-nss-dp-switchdev-fix-FDB-roaming.patch +++ b/package/kernel/qca-nss-dp/patches/0009-nss-dp-switchdev-fix-FDB-roaming.patch @@ -31,7 +31,7 @@ Signed-off-by: Robert Marko #define NSS_DP_SWITCH_ID 0 #define NSS_DP_SW_ETHTYPE_PID 0 /* PPE ethtype profile ID for slow protocols */ -@@ -521,7 +523,76 @@ static struct notifier_block *nss_dp_sw_ +@@ -534,7 +536,76 @@ static struct notifier_block *nss_dp_sw_ #else diff --git a/package/kernel/qca-nss-dp/patches/0010-nss-dp-include-net-netdev_rx_queue.h.patch b/package/kernel/qca-nss-dp/patches/0010-nss-dp-include-net-netdev_rx_queue.h.patch deleted file mode 100644 index ddbf342868..0000000000 --- a/package/kernel/qca-nss-dp/patches/0010-nss-dp-include-net-netdev_rx_queue.h.patch +++ /dev/null @@ -1,25 +0,0 @@ -From 01ec275bd0942ddc6b80e1d3671cdc66be670f57 Mon Sep 17 00:00:00 2001 -From: Robert Marko -Date: Fri, 1 Sep 2023 12:23:58 +0200 -Subject: [PATCH] nss-dp: include - -Since 6.5 netdev_rx_queue was moved out of netdevice.h so include the new -header since that is where it lives now. - -Signed-off-by: Robert Marko ---- - nss_dp_main.c | 3 +++ - 1 file changed, 3 insertions(+) - ---- a/nss_dp_main.c -+++ b/nss_dp_main.c -@@ -34,6 +34,9 @@ - #if defined(NSS_DP_MAC_POLL_SUPPORT) - #include - #endif -+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(6, 5, 0)) -+#include -+#endif - - #include "nss_dp_hal.h" - diff --git a/package/kernel/qca-nss-dp/patches/0011-02-nss_dp_switchdev-correctly-unregister-notifier-on-dp.patch b/package/kernel/qca-nss-dp/patches/0011-02-nss_dp_switchdev-correctly-unregister-notifier-on-dp.patch index e0a47cfca0..8379fcf20c 100644 --- a/package/kernel/qca-nss-dp/patches/0011-02-nss_dp_switchdev-correctly-unregister-notifier-on-dp.patch +++ b/package/kernel/qca-nss-dp/patches/0011-02-nss_dp_switchdev-correctly-unregister-notifier-on-dp.patch @@ -16,7 +16,7 @@ Signed-off-by: Christian Marangi --- a/include/nss_dp_dev.h +++ b/include/nss_dp_dev.h -@@ -312,6 +312,7 @@ void nss_dp_set_ethtool_ops(struct net_d +@@ -349,6 +349,7 @@ void nss_dp_set_ethtool_ops(struct net_d */ #ifdef CONFIG_NET_SWITCHDEV void nss_dp_switchdev_setup(struct net_device *dev); @@ -26,7 +26,7 @@ Signed-off-by: Christian Marangi --- a/nss_dp_main.c +++ b/nss_dp_main.c -@@ -913,6 +913,10 @@ static int nss_dp_remove(struct platform +@@ -972,6 +972,10 @@ static int nss_dp_remove(struct platform if (!dp_priv) continue; @@ -39,7 +39,7 @@ Signed-off-by: Christian Marangi --- a/nss_dp_switchdev.c +++ b/nss_dp_switchdev.c -@@ -635,4 +635,17 @@ void nss_dp_switchdev_setup(struct net_d +@@ -648,4 +648,17 @@ void nss_dp_switchdev_setup(struct net_d switch_init_done = true; } diff --git a/package/kernel/qca-nss-dp/patches/0011-03-nss_dp_main-swap-dp_exit-function-call.patch b/package/kernel/qca-nss-dp/patches/0011-03-nss_dp_main-swap-dp_exit-function-call.patch index 7ffde3d286..68a9821ceb 100644 --- a/package/kernel/qca-nss-dp/patches/0011-03-nss_dp_main-swap-dp_exit-function-call.patch +++ b/package/kernel/qca-nss-dp/patches/0011-03-nss_dp_main-swap-dp_exit-function-call.patch @@ -15,7 +15,7 @@ Signed-off-by: Christian Marangi --- a/nss_dp_main.c +++ b/nss_dp_main.c -@@ -1082,6 +1082,8 @@ int __init nss_dp_init(void) +@@ -1163,6 +1163,8 @@ int __init nss_dp_init(void) */ void __exit nss_dp_exit(void) { @@ -24,7 +24,7 @@ Signed-off-by: Christian Marangi /* * TODO Move this to soc_ops */ -@@ -1089,8 +1091,6 @@ void __exit nss_dp_exit(void) +@@ -1170,8 +1172,6 @@ void __exit nss_dp_exit(void) nss_dp_hal_cleanup(); dp_global_ctx.common_init_done = false; } diff --git a/package/kernel/qca-nss-dp/patches/0011-04-nss_dp_main-call-unregister_netdev-first-in-dp_remov.patch b/package/kernel/qca-nss-dp/patches/0011-04-nss_dp_main-call-unregister_netdev-first-in-dp_remov.patch index 20e87459f8..2721d2cfba 100644 --- a/package/kernel/qca-nss-dp/patches/0011-04-nss_dp_main-call-unregister_netdev-first-in-dp_remov.patch +++ b/package/kernel/qca-nss-dp/patches/0011-04-nss_dp_main-call-unregister_netdev-first-in-dp_remov.patch @@ -15,7 +15,7 @@ Signed-off-by: Christian Marangi --- a/nss_dp_main.c +++ b/nss_dp_main.c -@@ -920,6 +920,9 @@ static int nss_dp_remove(struct platform +@@ -979,6 +979,9 @@ static int nss_dp_remove(struct platform dp_ops = dp_priv->data_plane_ops; hal_ops = dp_priv->gmac_hal_ops; @@ -25,7 +25,7 @@ Signed-off-by: Christian Marangi if (dp_priv->phydev) phy_disconnect(dp_priv->phydev); -@@ -931,7 +934,6 @@ static int nss_dp_remove(struct platform +@@ -990,7 +993,6 @@ static int nss_dp_remove(struct platform #endif hal_ops->exit(dp_priv->gmac_hal_ctx); dp_ops->deinit(dp_priv->dpc); diff --git a/package/kernel/qca-nss-dp/patches/0011-05-nss_dp_main-use-phy_detach-instead-of-disconnect-in-.patch b/package/kernel/qca-nss-dp/patches/0011-05-nss_dp_main-use-phy_detach-instead-of-disconnect-in-.patch index 6e87e4e8c4..3c99cae2d5 100644 --- a/package/kernel/qca-nss-dp/patches/0011-05-nss_dp_main-use-phy_detach-instead-of-disconnect-in-.patch +++ b/package/kernel/qca-nss-dp/patches/0011-05-nss_dp_main-use-phy_detach-instead-of-disconnect-in-.patch @@ -15,7 +15,7 @@ Signed-off-by: Christian Marangi --- a/nss_dp_main.c +++ b/nss_dp_main.c -@@ -924,7 +924,7 @@ static int nss_dp_remove(struct platform +@@ -983,7 +983,7 @@ static int nss_dp_remove(struct platform unregister_netdev(dp_priv->netdev); if (dp_priv->phydev) diff --git a/package/kernel/qca-ssdk/Makefile b/package/kernel/qca-ssdk/Makefile index bbe9f12051..ed18f17504 100644 --- a/package/kernel/qca-ssdk/Makefile +++ b/package/kernel/qca-ssdk/Makefile @@ -1,13 +1,13 @@ include $(TOPDIR)/rules.mk PKG_NAME:=qca-ssdk -PKG_RELEASE:=6 +PKG_RELEASE:=1 PKG_SOURCE_URL:=https://git.codelinaro.org/clo/qsdk/oss/lklm/qca-ssdk.git PKG_SOURCE_PROTO:=git -PKG_SOURCE_DATE:=2023-10-04 -PKG_SOURCE_VERSION:=23a5aa4a4d5834da7a07efb58baebfbee91786b0 -PKG_MIRROR_HASH:=53fb201053b3aca004c4da07b06a0608b0b3322a2062b1f7ab3b3a7871ddabcb +PKG_SOURCE_DATE:=2024-04-17 +PKG_SOURCE_VERSION:=3d060f7ad70d087f6b0452abe79ab6d042e8cd53 +PKG_MIRROR_HASH:=6f5e390b294e699491584094f5d7eb941de6237ad8c5320191e9e306fbcd8eb5 PKG_FLAGS:=nonshared PKG_BUILD_PARALLEL:=1 diff --git a/package/kernel/qca-ssdk/patches/0001-config-identify-kernel-6.6.patch b/package/kernel/qca-ssdk/patches/0001-config-identify-kernel-6.6.patch deleted file mode 100644 index 2dc0923263..0000000000 --- a/package/kernel/qca-ssdk/patches/0001-config-identify-kernel-6.6.patch +++ /dev/null @@ -1,47 +0,0 @@ -From f6c0115daaac586740e873a3b8145c5370a73dce Mon Sep 17 00:00:00 2001 -From: Robert Marko -Date: Sat, 17 Feb 2024 13:02:31 +0100 -Subject: [PATCH] config: identify kernel 6.6 - -Identify kernel 6.6 so it can be compiled against. - -Signed-off-by: Robert Marko ---- - config | 5 +++++ - make/linux_opt.mk | 4 ++-- - 2 files changed, 7 insertions(+), 2 deletions(-) - ---- a/config -+++ b/config -@@ -27,6 +27,11 @@ endif - ifeq ($(KVER),$(filter 6.1%,$(KVER))) - OS_VER=6_1 - endif -+ -+ifeq ($(KVER),$(filter 6.6%,$(KVER))) -+ OS_VER=6_6 -+endif -+ - ifeq ($(KVER), 3.4.0) - OS_VER=3_4 - endif ---- a/make/linux_opt.mk -+++ b/make/linux_opt.mk -@@ -450,7 +450,7 @@ ifeq (KSLIB, $(MODULE_TYPE)) - KASAN_SHADOW_SCALE_SHIFT := 3 - endif - -- ifeq ($(OS_VER),$(filter 5_4 6_1, $(OS_VER))) -+ ifeq ($(OS_VER),$(filter 5_4 6_1 6_6, $(OS_VER))) - ifeq ($(ARCH), arm64) - KASAN_OPTION += -DKASAN_SHADOW_SCALE_SHIFT=$(KASAN_SHADOW_SCALE_SHIFT) - endif -@@ -481,7 +481,7 @@ ifeq (KSLIB, $(MODULE_TYPE)) - - endif - -- ifeq ($(OS_VER),$(filter 4_4 5_4 6_1, $(OS_VER))) -+ ifeq ($(OS_VER),$(filter 4_4 5_4 6_1 6_6, $(OS_VER))) - MODULE_CFLAG += -DKVER34 - MODULE_CFLAG += -DKVER32 - MODULE_CFLAG += -DLNX26_22 diff --git a/package/kernel/qca-ssdk/patches/101-hsl_phy-add-support-for-detection-PSGMII-PHY-mode.patch b/package/kernel/qca-ssdk/patches/101-hsl_phy-add-support-for-detection-PSGMII-PHY-mode.patch index c27902c4ce..9d028992a7 100644 --- a/package/kernel/qca-ssdk/patches/101-hsl_phy-add-support-for-detection-PSGMII-PHY-mode.patch +++ b/package/kernel/qca-ssdk/patches/101-hsl_phy-add-support-for-detection-PSGMII-PHY-mode.patch @@ -13,7 +13,7 @@ Signed-off-by: Christian Marangi --- a/src/hsl/phy/hsl_phy.c +++ b/src/hsl/phy/hsl_phy.c -@@ -1335,6 +1335,9 @@ hsl_port_phydev_interface_mode_status_ge +@@ -1322,6 +1322,9 @@ hsl_port_phydev_interface_mode_status_ge case PHY_INTERFACE_MODE_10GKR: *interface_mode_status = PORT_10GBASE_R; break; diff --git a/package/kernel/qca-ssdk/patches/102-qca-ssdk-support-selecting-PCS-channel-for-PORT3-on-.patch b/package/kernel/qca-ssdk/patches/102-qca-ssdk-support-selecting-PCS-channel-for-PORT3-on-.patch index 5e390d8ee3..db84ea1422 100644 --- a/package/kernel/qca-ssdk/patches/102-qca-ssdk-support-selecting-PCS-channel-for-PORT3-on-.patch +++ b/package/kernel/qca-ssdk/patches/102-qca-ssdk-support-selecting-PCS-channel-for-PORT3-on-.patch @@ -24,15 +24,15 @@ Signed-off-by: Mantas Pucka --- a/include/init/ssdk_dts.h +++ b/include/init/ssdk_dts.h -@@ -101,6 +101,7 @@ typedef struct +@@ -99,6 +99,7 @@ typedef struct a_uint32_t emu_chip_ver; /*only valid when is_emulation is true*/ a_uint32_t clk_mode; a_uint32_t pcie_hw_base; + a_uint32_t port3_pcs_channel; + led_ctrl_pattern_t source_pattern[SSDK_MAX_PORT_NUM][PORT_LED_SOURCE_MAX]; } ssdk_dt_cfg; - #define SSDK_MAX_NR_ETH 6 -@@ -162,6 +163,7 @@ a_uint32_t ssdk_device_id_get(a_uint32_t +@@ -161,6 +162,7 @@ a_uint32_t ssdk_device_id_get(a_uint32_t struct device_node *ssdk_dts_node_get(a_uint32_t dev_id); struct clk *ssdk_dts_essclk_get(a_uint32_t dev_id); struct clk *ssdk_dts_cmnclk_get(a_uint32_t dev_id); @@ -62,7 +62,7 @@ Signed-off-by: Mantas Pucka cppe_port_mux_ctrl.bf.port4_pcs_sel = --- a/src/adpt/hppe/adpt_hppe_uniphy.c +++ b/src/adpt/hppe/adpt_hppe_uniphy.c -@@ -1122,9 +1122,6 @@ __adpt_hppe_uniphy_psgmii_mode_set(a_uin +@@ -1160,9 +1160,6 @@ __adpt_hppe_uniphy_psgmii_mode_set(a_uin { a_uint32_t i; sw_error_t rv = SW_OK; @@ -72,7 +72,7 @@ Signed-off-by: Mantas Pucka union uniphy_mode_ctrl_u uniphy_mode_ctrl; -@@ -1134,9 +1131,7 @@ __adpt_hppe_uniphy_psgmii_mode_set(a_uin +@@ -1172,9 +1169,7 @@ __adpt_hppe_uniphy_psgmii_mode_set(a_uin SSDK_DEBUG("uniphy %d is psgmii mode\n", uniphy_index); #if defined(CPPE) if (adpt_ppe_type_get(dev_id) == CPPE_TYPE) { @@ -92,14 +92,14 @@ Signed-off-by: Mantas Pucka +a_uint32_t ssdk_dts_port3_pcs_channel_get(a_uint32_t dev_id) +{ + ssdk_dt_cfg* cfg = ssdk_dt_global.ssdk_dt_switch_nodes[dev_id]; -+ ++ + return cfg->port3_pcs_channel; +} + - #ifndef BOARD_AR71XX #if defined(CONFIG_OF) && (LINUX_VERSION_CODE >= KERNEL_VERSION(3,14,0)) static void ssdk_dt_parse_mac_mode(a_uint32_t dev_id, -@@ -306,6 +313,25 @@ static void ssdk_dt_parse_mac_mode(a_uin + struct device_node *switch_node, ssdk_init_cfg *cfg) +@@ -305,6 +312,25 @@ static void ssdk_dt_parse_mac_mode(a_uin return; } @@ -109,7 +109,7 @@ Signed-off-by: Mantas Pucka +{ + const __be32 *port3_pcs_channel; + a_uint32_t len = 0; -+ ++ + port3_pcs_channel = of_get_property(switch_node, "port3_pcs_channel", &len); + if (!port3_pcs_channel) { + ssdk_dt_global.ssdk_dt_switch_nodes[dev_id]->port3_pcs_channel = 2; @@ -125,7 +125,7 @@ Signed-off-by: Mantas Pucka #ifdef IN_UNIPHY static void ssdk_dt_parse_uniphy(a_uint32_t dev_id) { -@@ -1292,6 +1318,7 @@ sw_error_t ssdk_dt_parse(ssdk_init_cfg * +@@ -1347,6 +1373,7 @@ sw_error_t ssdk_dt_parse(ssdk_init_cfg * rv = ssdk_dt_parse_access_mode(switch_node, ssdk_dt_priv); SW_RTN_ON_ERROR(rv); ssdk_dt_parse_mac_mode(*dev_id, switch_node, cfg); diff --git a/package/kernel/qca-ssdk/patches/103-mdio-adapt-to-C22-and-C45-read-write-split.patch b/package/kernel/qca-ssdk/patches/103-mdio-adapt-to-C22-and-C45-read-write-split.patch deleted file mode 100644 index 7ddca554ec..0000000000 --- a/package/kernel/qca-ssdk/patches/103-mdio-adapt-to-C22-and-C45-read-write-split.patch +++ /dev/null @@ -1,98 +0,0 @@ -From bdae481e89cbe551068a99028bb57119b59f5ff4 Mon Sep 17 00:00:00 2001 -From: Robert Marko -Date: Tue, 26 Mar 2024 12:19:49 +0100 -Subject: [PATCH] mdio: adapt to C22 and C45 read/write split - -Kernel 6.3 has introduced separate C45 read/write operations, and thus -split them out of the C22 operations completely so the old way of marking -C45 reads and writes via the register value does not work anymore. - -This is causing SSDK to fail and find C45 only PHY-s such as Aquantia ones: -[ 22.187877] ssdk_phy_driver_init[371]:INFO:dev_id = 0, phy_adress = 8, phy_id = 0x0 phytype doesn't match -[ 22.209924] ssdk_phy_driver_init[371]:INFO:dev_id = 0, phy_adress = 0, phy_id = 0x0 phytype doesn't match - -This in turn causes USXGMII MAC autoneg bit to not get set and then UNIPHY -autoneg will time out, causing the 10G ports not to work: -[ 37.292784] uniphy autoneg time out! - -So, lets detect C45 reads and writes by the magic BIT(30) in the register -argument and if so call separate C45 mdiobus read/write functions. - -Signed-off-by: Robert Marko ---- - include/init/ssdk_plat.h | 7 +++++++ - src/init/ssdk_plat.c | 30 ++++++++++++++++++++++++++++++ - 2 files changed, 37 insertions(+) - ---- a/include/init/ssdk_plat.h -+++ b/include/init/ssdk_plat.h -@@ -505,3 +505,10 @@ void ssdk_plat_exit(a_uint32_t dev_id); - - #endif - /*qca808x_end*/ -+ -+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(6,3,0)) -+#define MII_ADDR_C45 (1<<30) -+#define MII_DEVADDR_C45_SHIFT 16 -+#define MII_DEVADDR_C45_MASK GENMASK(20, 16) -+#define MII_REGADDR_C45_MASK GENMASK(15, 0) -+#endif ---- a/src/init/ssdk_plat.c -+++ b/src/init/ssdk_plat.c -@@ -356,6 +356,18 @@ phy_addr_validation_check(a_uint32_t phy - return A_TRUE; - } - -+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(6,3,0)) -+static inline u16 mdiobus_c45_regad(u32 regnum) -+{ -+ return FIELD_GET(MII_REGADDR_C45_MASK, regnum); -+} -+ -+static inline u16 mdiobus_c45_devad(u32 regnum) -+{ -+ return FIELD_GET(MII_DEVADDR_C45_MASK, regnum); -+} -+#endif -+ - sw_error_t - qca_ar8327_phy_read(a_uint32_t dev_id, a_uint32_t phy_addr, - a_uint32_t reg, a_uint16_t* data) -@@ -371,9 +383,18 @@ qca_ar8327_phy_read(a_uint32_t dev_id, a - if (!bus) - return SW_NOT_SUPPORTED; - phy_addr = TO_PHY_ADDR(phy_addr); -+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(6,3,0)) -+ mutex_lock(&bus->mdio_lock); -+ if (reg & MII_ADDR_C45) -+ *data = __mdiobus_c45_read(bus, phy_addr, mdiobus_c45_devad(reg), mdiobus_c45_regad(reg)); -+ else -+ *data = __mdiobus_read(bus, phy_addr, reg); -+ mutex_unlock(&bus->mdio_lock); -+#else - mutex_lock(&bus->mdio_lock); - *data = __mdiobus_read(bus, phy_addr, reg); - mutex_unlock(&bus->mdio_lock); -+#endif - - return 0; - } -@@ -393,9 +414,18 @@ qca_ar8327_phy_write(a_uint32_t dev_id, - if (!bus) - return SW_NOT_SUPPORTED; - phy_addr = TO_PHY_ADDR(phy_addr); -+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(6,3,0)) -+ mutex_lock(&bus->mdio_lock); -+ if (reg & MII_ADDR_C45) -+ __mdiobus_c45_write(bus, phy_addr, mdiobus_c45_devad(reg), mdiobus_c45_regad(reg), data); -+ else -+ __mdiobus_write(bus, phy_addr, reg, data); -+ mutex_unlock(&bus->mdio_lock); -+#else - mutex_lock(&bus->mdio_lock); - __mdiobus_write(bus, phy_addr, reg, data); - mutex_unlock(&bus->mdio_lock); -+#endif - - return 0; - } diff --git a/package/kernel/qca-ssdk/patches/200-allow-parallel-build.patch b/package/kernel/qca-ssdk/patches/200-allow-parallel-build.patch index 5635c2fdcf..6c28e0ff2e 100644 --- a/package/kernel/qca-ssdk/patches/200-allow-parallel-build.patch +++ b/package/kernel/qca-ssdk/patches/200-allow-parallel-build.patch @@ -40,7 +40,7 @@ kslib_c: --- a/make/linux_opt.mk +++ b/make/linux_opt.mk -@@ -777,6 +777,6 @@ LOCAL_CFLAGS += $(CPU_CFLAG) -D"KBUILD_M +@@ -778,6 +778,6 @@ LOCAL_CFLAGS += $(CPU_CFLAG) -D"KBUILD_M #################################################################### # cflags for LNX Modules-Style Makefile #################################################################### diff --git a/package/kernel/qca-ssdk/patches/201-fix-compile-warnings.patch b/package/kernel/qca-ssdk/patches/201-fix-compile-warnings.patch new file mode 100644 index 0000000000..5b57f41975 --- /dev/null +++ b/package/kernel/qca-ssdk/patches/201-fix-compile-warnings.patch @@ -0,0 +1,31 @@ +--- a/src/fal/fal_port_ctrl.c ++++ b/src/fal/fal_port_ctrl.c +@@ -2089,7 +2089,7 @@ fal_port_hibernate_get (a_uint32_t dev_i + */ + sw_error_t + fal_port_cdt (a_uint32_t dev_id, fal_port_t port_id, a_uint32_t mdi_pair, +- a_uint32_t * cable_status, a_uint32_t * cable_len) ++ fal_cable_status_t * cable_status, a_uint32_t * cable_len) + { + sw_error_t rv; + +--- a/src/fal/fal_portvlan.c ++++ b/src/fal/fal_portvlan.c +@@ -2173,7 +2173,7 @@ fal_netisolate_get(a_uint32_t dev_id, a_ + * @return SW_OK or error code + */ + sw_error_t +-fal_eg_trans_filter_bypass_en_set(a_uint32_t dev_id, a_bool_t enable) ++fal_eg_trans_filter_bypass_en_set(a_uint32_t dev_id, a_uint32_t enable) + { + sw_error_t rv; + +@@ -2190,7 +2190,7 @@ fal_eg_trans_filter_bypass_en_set(a_uint + * @return SW_OK or error code + */ + sw_error_t +-fal_eg_trans_filter_bypass_en_get(a_uint32_t dev_id, a_bool_t* enable) ++fal_eg_trans_filter_bypass_en_get(a_uint32_t dev_id, a_uint32_t* enable) + { + sw_error_t rv; + diff --git a/package/libs/libbpf/Makefile b/package/libs/libbpf/Makefile index 666786e9f6..6b894972a7 100644 --- a/package/libs/libbpf/Makefile +++ b/package/libs/libbpf/Makefile @@ -8,13 +8,13 @@ include $(TOPDIR)/rules.mk PKG_NAME:=libbpf -PKG_VERSION:=1.4.0 +PKG_VERSION:=1.4.1 PKG_RELEASE:=1 PKG_SOURCE_URL:=https://github.com/libbpf/libbpf -PKG_MIRROR_HASH:=4c37636699c604de345937bdbdf8f2e6ce69cbf768a4aa669c32b542e5302de6 +PKG_MIRROR_HASH:=46469f720ed246529e46d84a6444ae1c1a1eaf2a717a5a055c9973bb52159ec3 PKG_SOURCE_PROTO:=git -PKG_SOURCE_VERSION:=v1.4.0 +PKG_SOURCE_VERSION:=v1.4.1 PKG_ABI_VERSION:=$(firstword $(subst .,$(space),$(PKG_VERSION))) PKG_MAINTAINER:=Tony Ambardar diff --git a/package/libs/mbedtls/Makefile b/package/libs/mbedtls/Makefile index 3d8c7158fa..c76594c58f 100644 --- a/package/libs/mbedtls/Makefile +++ b/package/libs/mbedtls/Makefile @@ -161,6 +161,7 @@ define Build/InstallDev $(INSTALL_DIR) $(1)/usr/lib $(CP) $(PKG_INSTALL_DIR)/usr/lib/lib*.so* $(1)/usr/lib/ $(CP) $(PKG_INSTALL_DIR)/usr/lib/lib*.a $(1)/usr/lib/ + $(CP) $(PKG_INSTALL_DIR)/usr/lib/cmake $(1)/usr/lib/ $(INSTALL_DIR) $(1)/usr/lib/pkgconfig $(CP) \ $(PKG_INSTALL_DIR)/usr/lib/pkgconfig/mbedcrypto.pc \ diff --git a/package/network/services/dropbear/Makefile b/package/network/services/dropbear/Makefile index b0f486f990..abb46157ea 100644 --- a/package/network/services/dropbear/Makefile +++ b/package/network/services/dropbear/Makefile @@ -9,7 +9,7 @@ include $(TOPDIR)/rules.mk PKG_NAME:=dropbear PKG_VERSION:=2022.83 -PKG_RELEASE:=1 +PKG_RELEASE:=2 PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.bz2 PKG_SOURCE_URL:= \ @@ -57,7 +57,7 @@ define Package/dropbear CATEGORY:=Base system TITLE:=Small SSH2 client/server DEPENDS:= +DROPBEAR_ZLIB:zlib - ALTERNATIVES:= + ALTERNATIVES:=100:/usr/bin/ssh-keygen:/usr/sbin/dropbear $(if $(CONFIG_DROPBEAR_SCP),ALTERNATIVES+= \ 100:/usr/bin/scp:/usr/sbin/dropbear,) $(if $(CONFIG_DROPBEAR_DBCLIENT),ALTERNATIVES+= \ diff --git a/package/network/services/odhcpd/Makefile b/package/network/services/odhcpd/Makefile index 55a24a90e5..4092588353 100644 --- a/package/network/services/odhcpd/Makefile +++ b/package/network/services/odhcpd/Makefile @@ -12,9 +12,9 @@ PKG_RELEASE:=1 PKG_SOURCE_PROTO:=git PKG_SOURCE_URL=$(PROJECT_GIT)/project/odhcpd.git -PKG_MIRROR_HASH:=08fddf4294929d1713e0c3f7b258f8c7bf4abe731d5f34fceb797faa411f7a58 -PKG_SOURCE_DATE:=2023-10-24 -PKG_SOURCE_VERSION:=d8118f6e76e5519881f9a37137c3a06b3cb60fd2 +PKG_MIRROR_HASH:=f6e1c18551a00e01229fa12caa7b3fe33ad82785150fedcbe615fcc651ba2876 +PKG_SOURCE_DATE:=2024-05-08 +PKG_SOURCE_VERSION:=a29882318a4ccb3ae26f7cc0145e06ad4ead224b PKG_MAINTAINER:=Hans Dedecker PKG_LICENSE:=GPL-2.0 diff --git a/package/network/services/odhcpd/patches/001-odhcpd-RFC-9096-compliance.patch b/package/network/services/odhcpd/patches/001-odhcpd-RFC-9096-compliance.patch index e3df56b577..d6281669fa 100644 --- a/package/network/services/odhcpd/patches/001-odhcpd-RFC-9096-compliance.patch +++ b/package/network/services/odhcpd/patches/001-odhcpd-RFC-9096-compliance.patch @@ -20,7 +20,7 @@ Signed-off-by: Aviana Cruz leasetime string 12h DHCPv4 address leasetime start integer 100 DHCPv4 pool start limit integer 150 DHCPv4 pool size --preferred_lifetime string 12h Value for the preferred lifetime +-preferred_lifetime string 7d Value for the preferred lifetime +max_preferred_lifetime string 45m Upper limit for the preferred lifetime + for a prefix +max_valid_lifetime string 90m Upper limit for the valid lifetime @@ -84,7 +84,7 @@ Signed-off-by: Aviana Cruz iface->ndp = MODE_DISABLED; iface->learn_routes = 1; iface->dhcp_leasetime = 43200; -- iface->preferred_lifetime = 43200; +- iface->preferred_lifetime = 604800; /* rfc4861#section-6.2.1: AdvPreferredLifetime 7 days */ + iface->max_preferred_lifetime = ND_PREFERRED_LIMIT; + iface->max_valid_lifetime = ND_VALID_LIMIT; iface->dhcpv4_start.s_addr = htonl(START_DEFAULT); @@ -105,14 +105,14 @@ Signed-off-by: Aviana Cruz + iface->max_preferred_lifetime = time; + } else { syslog(LOG_ERR, "Invalid %s value configured for interface '%s'", -- iface_attrs[IFACE_ATTR_PREFERRED_LIFETIME].name, iface->name); +- iface_attrs[IFACE_ATTR_PREFERRED_LIFETIME].name, iface->name); + iface_attrs[IFACE_ATTR_MAX_PREFERRED_LIFETIME].name, iface->name); + } + } - ++ + if ((c = tb[IFACE_ATTR_MAX_VALID_LIFETIME])) { + double time = parse_leasetime(c); -+ + + if (time >= 0) { + iface->max_valid_lifetime = time; + } else { @@ -122,7 +122,7 @@ Signed-off-by: Aviana Cruz } if ((c = tb[IFACE_ATTR_START])) { -@@ -978,9 +990,6 @@ int config_parse_interface(void *data, s +@@ -979,9 +991,6 @@ int config_parse_interface(void *data, s if ((c = tb[IFACE_ATTR_RA_LIFETIME])) iface->ra_lifetime = blobmsg_get_u32(c); @@ -138,8 +138,8 @@ Signed-off-by: Aviana Cruz static inline bool valid_addr(const struct odhcpd_ipaddr *addr, time_t now) { -- return (addr->prefix <= 96 && addr->preferred > (uint32_t)now); -+ return (addr->prefix <= 96 && addr->valid > (uint32_t)now && addr->preferred > (uint32_t)now); +- return (addr->prefix <= 96 && addr->preferred_lt > (uint32_t)now); ++ return (addr->prefix <= 96 && addr->valid_lt > (uint32_t)now && addr->preferred_lt > (uint32_t)now); } static size_t get_preferred_addr(const struct odhcpd_ipaddr *addrs, const size_t addrlen) @@ -147,18 +147,18 @@ Signed-off-by: Aviana Cruz } if (a) { -- uint32_t leasetime, pref; +- uint32_t leasetime, preferred_lt; + uint32_t leasetime; if (a->leasetime) { leasetime = a->leasetime; -- pref = a->leasetime; +- preferred_lt = a->leasetime; } else { leasetime = iface->dhcp_leasetime; -- pref = iface->preferred_lifetime; +- preferred_lt = iface->preferred_lifetime; } -- uint32_t valid = leasetime; +- uint32_t valid_lt = leasetime; + uint32_t floor_preferred_lifetime, floor_valid_lifetime; /* For calculating T1 / T2 */ + + if (iface->max_preferred_lifetime && iface->max_preferred_lifetime < leasetime) { @@ -175,60 +175,59 @@ Signed-off-by: Aviana Cruz struct odhcpd_ipaddr *addrs = (a->managed) ? a->managed : iface->addr6; size_t addrlen = (a->managed) ? (size_t)a->managed_size : iface->addr6_len; -@@ -1071,17 +1081,19 @@ static size_t build_ia(uint8_t *buf, siz - prefix_pref = addrs[i].preferred; - prefix_valid = addrs[i].valid; +@@ -1071,15 +1081,20 @@ static size_t build_ia(uint8_t *buf, siz + prefix_preferred_lt = addrs[i].preferred_lt; + prefix_valid_lt = addrs[i].valid_lt; -- if (prefix_pref != UINT32_MAX) -+ if (prefix_pref != UINT32_MAX) { - prefix_pref -= now; +- if (prefix_preferred_lt != UINT32_MAX) ++ if (prefix_preferred_lt != UINT32_MAX) { + prefix_preferred_lt -= now; -- if (prefix_pref > pref) -- prefix_pref = pref; -+ if (iface->max_preferred_lifetime && prefix_pref > iface->max_preferred_lifetime) -+ prefix_pref = iface->max_preferred_lifetime; +- if (prefix_preferred_lt > preferred_lt) +- prefix_preferred_lt = preferred_lt; ++ if (iface->max_preferred_lifetime && prefix_preferred_lt > iface->max_preferred_lifetime) ++ prefix_preferred_lt = iface->max_preferred_lifetime; + } -- if (prefix_valid != UINT32_MAX) -+ if (prefix_valid != UINT32_MAX) { - prefix_valid -= now; +- if (prefix_valid_lt != UINT32_MAX) ++ if (prefix_valid_lt != UINT32_MAX) { + prefix_valid_lt -= now; -- if (prefix_valid > leasetime) -- prefix_valid = leasetime; -+ if (iface->max_valid_lifetime && prefix_valid > iface->max_valid_lifetime) -+ prefix_valid = iface->max_valid_lifetime; ++ if (iface->max_valid_lifetime && prefix_valid_lt > iface->max_valid_lifetime) ++ prefix_valid_lt = iface->max_valid_lifetime; + } ++ + if (prefix_valid_lt > leasetime) + prefix_valid_lt = leasetime; - if (prefix_pref > prefix_valid) - prefix_pref = prefix_valid; -@@ -1133,24 +1145,24 @@ static size_t build_ia(uint8_t *buf, siz +@@ -1133,24 +1148,24 @@ static size_t build_ia(uint8_t *buf, siz /* Calculate T1 / T2 based on non-deprecated addresses */ - if (prefix_pref > 0) { -- if (prefix_pref < pref) -- pref = prefix_pref; -+ if (floor_preferred_lifetime > prefix_pref) -+ floor_preferred_lifetime = prefix_pref; + if (prefix_preferred_lt > 0) { +- if (prefix_preferred_lt < preferred_lt) +- preferred_lt = prefix_preferred_lt; ++ if (floor_preferred_lifetime > prefix_preferred_lt) ++ floor_preferred_lifetime = prefix_preferred_lt; -- if (prefix_valid < valid) -- valid = prefix_valid; -+ if (floor_valid_lifetime > prefix_valid) -+ floor_valid_lifetime = prefix_valid; +- if (prefix_valid_lt < valid_lt) +- valid_lt = prefix_valid_lt; ++ if (floor_valid_lifetime > prefix_valid_lt) ++ floor_valid_lifetime = prefix_valid_lt; } } if (!INFINITE_VALID(a->valid_until)) - /* UINT32_MAX is considered as infinite leasetime */ -- a->valid_until = (valid == UINT32_MAX) ? 0 : valid + now; + /* UINT32_MAX is RFC defined as infinite lease-time */ +- a->valid_until = (valid_lt == UINT32_MAX) ? 0 : valid_lt + now; + a->valid_until = (floor_valid_lifetime == UINT32_MAX) ? 0 : floor_valid_lifetime + now; if (!INFINITE_VALID(a->preferred_until)) - /* UINT32_MAX is considered as infinite leasetime */ -- a->preferred_until = (pref == UINT32_MAX) ? 0 : pref + now; + /* UINT32_MAX is RFC defined as infinite lease-time */ +- a->preferred_until = (preferred_lt == UINT32_MAX) ? 0 : preferred_lt + now; + a->preferred_until = (floor_preferred_lifetime == UINT32_MAX) ? 0 : floor_preferred_lifetime + now; -- o_ia.t1 = htonl((pref == UINT32_MAX) ? pref : pref * 5 / 10); -- o_ia.t2 = htonl((pref == UINT32_MAX) ? pref : pref * 8 / 10); +- o_ia.t1 = htonl((preferred_lt == UINT32_MAX) ? preferred_lt : preferred_lt * 5 / 10); +- o_ia.t2 = htonl((preferred_lt == UINT32_MAX) ? preferred_lt : preferred_lt * 8 / 10); + o_ia.t1 = htonl((floor_preferred_lifetime == UINT32_MAX) ? floor_preferred_lifetime : floor_preferred_lifetime * 5 / 10); + o_ia.t2 = htonl((floor_preferred_lifetime == UINT32_MAX) ? floor_preferred_lifetime : floor_preferred_lifetime * 8 / 10); @@ -267,7 +266,7 @@ Signed-off-by: Aviana Cruz uint32_t dhcp_leasetime; --- a/src/router.c +++ b/src/router.c -@@ -371,7 +371,7 @@ static int calc_adv_interval(struct inte +@@ -376,7 +376,7 @@ static int calc_adv_interval(struct inte static uint32_t calc_ra_lifetime(struct interface *iface, uint32_t maxival) { @@ -276,43 +275,44 @@ Signed-off-by: Aviana Cruz if (iface->ra_lifetime >= 0) { lifetime = iface->ra_lifetime; -@@ -590,16 +590,15 @@ static int send_router_advert(struct int - if (addr->preferred > (uint32_t)now) { - preferred = TIME_LEFT(addr->preferred, now); +@@ -600,17 +600,16 @@ static int send_router_advert(struct int + if (addr->preferred_lt > (uint32_t)now) { + preferred_lt = TIME_LEFT(addr->preferred_lt, now); -- if (iface->ra_useleasetime && -- preferred > iface->preferred_lifetime) -- preferred = iface->preferred_lifetime; -+ if (iface->max_preferred_lifetime && preferred > iface->max_preferred_lifetime) -+ preferred = iface->max_preferred_lifetime; +- if (preferred_lt > iface->preferred_lifetime) { +- /* set to possibly user mandated preferred_lt */ +- preferred_lt = iface->preferred_lifetime; ++ if (iface->max_preferred_lifetime && preferred_lt > iface->max_preferred_lifetime) { ++ preferred_lt = iface->max_preferred_lifetime; + } } - if (addr->valid > (uint32_t)now) { - valid = TIME_LEFT(addr->valid, now); + if (addr->valid_lt > (uint32_t)now) { + valid_lt = TIME_LEFT(addr->valid_lt, now); -- if (iface->ra_useleasetime && valid > iface->dhcp_leasetime) -- valid = iface->dhcp_leasetime; -+ if (iface->max_valid_lifetime && valid > iface->max_valid_lifetime) -+ valid = iface->max_valid_lifetime; +- if (iface->ra_useleasetime && valid_lt > iface->dhcp_leasetime) +- valid_lt = iface->dhcp_leasetime; ++ if (iface->max_valid_lifetime && valid_lt > iface->max_valid_lifetime) ++ valid_lt = iface->max_valid_lifetime; } - if (minvalid > valid) -@@ -643,9 +642,9 @@ static int send_router_advert(struct int + if (preferred_lt > valid_lt) { +@@ -663,9 +662,9 @@ static int send_router_advert(struct int if (default_route) { syslog(LOG_WARNING, "A default route is present but there is no public prefix " -- "on %s thus we don't announce a default route by overriding ra_lifetime!", iface->name); -+ "on %s thus we don't announce a default route by setting ra_lifetime to zero!", iface->name); +- "on %s thus we announce no default route by overriding ra_lifetime to 0!", iface->name); ++ "on %s thus we announce no default route by setting ra_lifetime to 0!", iface->name); } else { -- syslog(LOG_WARNING, "No default route present, overriding ra_lifetime!"); -+ syslog(LOG_WARNING, "No default route present, setting ra_lifetime to zero!"); +- syslog(LOG_WARNING, "No default route present, overriding ra_lifetime to 0!"); ++ syslog(LOG_WARNING, "No default route present, setting ra_lifetime to 0!"); } } -@@ -710,7 +709,7 @@ static int send_router_advert(struct int +@@ -730,7 +729,7 @@ static int send_router_advert(struct int if (iface->pref64_length) { - /* RFC 8781 § 4.1 rounding up lifetime to multiply of 8 */ + /* RFC 8781 § 4.1 rounding up lifetime to multiple of 8 */ - uint16_t pref64_lifetime = lifetime < (UINT16_MAX - 7) ? lifetime + 7 : UINT16_MAX; + uint16_t pref64_lifetime = lifetime < (UINT16_MAX - 7) ? lifetime + 7 : (UINT16_MAX - 7); uint8_t prefix_length_code; diff --git a/package/utils/mtd-utils/Makefile b/package/utils/mtd-utils/Makefile index fd1cb75e51..c0ea0abbcb 100644 --- a/package/utils/mtd-utils/Makefile +++ b/package/utils/mtd-utils/Makefile @@ -9,7 +9,7 @@ include $(TOPDIR)/rules.mk PKG_NAME:=mtd-utils PKG_VERSION:=2.1.6 -PKG_RELEASE:=1 +PKG_RELEASE:=2 PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.bz2 PKG_SOURCE_URL:=https://infraroot.at/pub/mtd/ @@ -59,7 +59,7 @@ endef MAKE_FLAGS += LDLIBS+="$(LIBGCC_S)" CONFIGURE_ARGS += \ - --disable-tests \ + --enable-tests \ --without-crypto \ --without-xattr \ --without-zstd \ @@ -76,7 +76,8 @@ endef define Package/nand-utils/install $(INSTALL_DIR) $(1)/usr/sbin $(INSTALL_BIN) \ - $(PKG_INSTALL_DIR)/usr/sbin/{flash_erase,nanddump,nandwrite,nandtest,mtdinfo} $(1)/usr/sbin/ + $(PKG_INSTALL_DIR)/usr/sbin/{flash_erase,nanddump,nandwrite,nandtest,mtdinfo} \ + $(PKG_INSTALL_DIR)/usr/lib/mtd-utils/nandbiterrs $(1)/usr/sbin/ endef $(eval $(call BuildPackage,ubi-utils)) diff --git a/package/utils/ucode/Makefile b/package/utils/ucode/Makefile index f9db6b060e..ac23161c5e 100644 --- a/package/utils/ucode/Makefile +++ b/package/utils/ucode/Makefile @@ -12,9 +12,9 @@ PKG_RELEASE:=1 PKG_SOURCE_PROTO:=git PKG_SOURCE_URL=https://github.com/jow-/ucode.git -PKG_SOURCE_DATE:=2024-04-07 -PKG_SOURCE_VERSION:=5507654a498a339c44b642f62e203e1d5fb1f725 -PKG_MIRROR_HASH:=40d3df5308faaf3cddfca4ebbcd9ee7fff98cf7e7d406dc177972a7abf0ca16b +PKG_SOURCE_DATE:=2024-05-09 +PKG_SOURCE_VERSION:=0d823e702bfe5f2bb5be694030a98afedf34aa6b +PKG_MIRROR_HASH:=c52d499d2490e958e36ed80c32e8fd6d94cacf3b43b9d14c45c68a25bc44d536 PKG_MAINTAINER:=Jo-Philipp Wich PKG_LICENSE:=ISC @@ -155,6 +155,10 @@ $(eval $(call UcodeModule, \ rtnl, RTNL_SUPPORT, +libnl-tiny +libubox, \ The rtnl plugin provides access to the Linux routing netlink API.)) +$(eval $(call UcodeModule, \ + socket, SOCKET_SUPPORT, , \ + The socket plugin provides access to IPv4, IPv6, Unix and packet socket APIs.)) + $(eval $(call UcodeModule, \ struct, STRUCT_SUPPORT, , \ The struct plugin implements Python 3 compatible struct.pack/unpack functionality.)) diff --git a/package/utils/ucode/patches/100-ubus-fix-uc_ubus_have_uloop-for-eloop-uloop-combinat.patch b/package/utils/ucode/patches/100-ubus-fix-uc_ubus_have_uloop-for-eloop-uloop-combinat.patch deleted file mode 100644 index a1659be3c8..0000000000 --- a/package/utils/ucode/patches/100-ubus-fix-uc_ubus_have_uloop-for-eloop-uloop-combinat.patch +++ /dev/null @@ -1,26 +0,0 @@ -From: Felix Fietkau -Date: Wed, 1 May 2024 18:40:19 +0200 -Subject: [PATCH] ubus: fix uc_ubus_have_uloop for eloop+uloop combination - -When uloop has been integrated with eloop (in hostapd/wpa_supplicant), -uloop_cancelling will return false, since uloop_run is not being called. -This leads to ubus.defer() calling uloop_run in a context where it can -prevent the other event loop from running. - -Fix this by detecting event loop integration via uloop_fd_set_cb being set - -Signed-off-by: Felix Fietkau ---- - ---- a/lib/ubus.c -+++ b/lib/ubus.c -@@ -665,6 +665,9 @@ uc_ubus_have_uloop(void) - bool prev = uloop_cancelled; - bool active; - -+ if (uloop_fd_set_cb) -+ return true; -+ - uloop_cancelled = true; - active = uloop_cancelling(); - uloop_cancelled = prev; diff --git a/target/linux/armsr/Makefile b/target/linux/armsr/Makefile index 7de77decb5..c2e57e52da 100644 --- a/target/linux/armsr/Makefile +++ b/target/linux/armsr/Makefile @@ -10,6 +10,7 @@ FEATURES:=fpu pci pcie rtc usb boot-part rootfs-part FEATURES+=cpiogz ext4 ramdisk squashfs targz vmdk KERNEL_PATCHVER:=6.1 +KERNEL_TESTING_PATCHVER:=6.6 include $(INCLUDE_DIR)/target.mk diff --git a/target/linux/armsr/armv7/config-6.6 b/target/linux/armsr/armv7/config-6.6 new file mode 100644 index 0000000000..18f5cd7c79 --- /dev/null +++ b/target/linux/armsr/armv7/config-6.6 @@ -0,0 +1,83 @@ +CONFIG_ALIGNMENT_TRAP=y +CONFIG_ARCH_32BIT_OFF_T=y +CONFIG_ARCH_MIGHT_HAVE_PC_PARPORT=y +CONFIG_ARCH_MMAP_RND_BITS=8 +CONFIG_ARCH_MULTIPLATFORM=y +# CONFIG_ARCH_MULTI_V4 is not set +# CONFIG_ARCH_MULTI_V4T is not set +CONFIG_ARCH_MULTI_V6_V7=y +CONFIG_ARCH_MULTI_V7=y +CONFIG_ARCH_NR_GPIO=0 +CONFIG_ARCH_OPTIONAL_KERNEL_RWX=y +CONFIG_ARCH_OPTIONAL_KERNEL_RWX_DEFAULT=y +CONFIG_ARCH_SELECT_MEMORY_MODEL=y +CONFIG_ARCH_VIRT=y +CONFIG_ARM=y +CONFIG_ARM_CPU_SUSPEND=y +CONFIG_ARM_HAS_SG_CHAIN=y +CONFIG_ARM_HEAVY_MB=y +# CONFIG_ARM_HIGHBANK_CPUIDLE is not set +CONFIG_ARM_L1_CACHE_SHIFT=6 +CONFIG_ARM_L1_CACHE_SHIFT_6=y +CONFIG_ARM_LPAE=y +CONFIG_ARM_PATCH_IDIV=y +CONFIG_ARM_PATCH_PHYS_VIRT=y +CONFIG_ARM_PSCI=y +CONFIG_ARM_THUMB=y +CONFIG_ARM_UNWIND=y +CONFIG_ARM_VIRT_EXT=y +CONFIG_AUTO_ZRELADDR=y +CONFIG_BINFMT_FLAT_ARGVP_ENVP_ON_STACK=y +CONFIG_CACHE_L2X0=y +CONFIG_COMPAT_32BIT_TIME=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_DCACHE_WRITETHROUGH is not set +CONFIG_CPU_HAS_ASID=y +CONFIG_CPU_PABRT_V7=y +CONFIG_CPU_SPECTRE=y +CONFIG_CPU_THUMB_CAPABLE=y +CONFIG_CPU_TLB_V7=y +CONFIG_CPU_V7=y +CONFIG_CRYPTO_LIB_BLAKE2S_GENERIC=y +CONFIG_DEBUG_LL_INCLUDE="mach/debug-macro.S" +CONFIG_DMA_OPS=y +CONFIG_EDAC_ATOMIC_SCRUB=y +CONFIG_GENERIC_IRQ_MULTI_HANDLER=y +CONFIG_GENERIC_VDSO_32=y +CONFIG_HARDEN_BRANCH_PREDICTOR=y +CONFIG_HAVE_SMP=y +CONFIG_HZ_FIXED=0 +CONFIG_HZ_PERIODIC=y +CONFIG_MIGHT_HAVE_CACHE_L2X0=y +CONFIG_MODULES_USE_ELF_REL=y +CONFIG_NEON=y +CONFIG_NR_CPUS=4 +CONFIG_OLD_SIGACTION=y +CONFIG_OLD_SIGSUSPEND3=y +CONFIG_OUTER_CACHE=y +CONFIG_OUTER_CACHE_SYNC=y +CONFIG_PAGE_OFFSET=0xC0000000 +CONFIG_PERF_USE_VMALLOC=y +CONFIG_PHYS_OFFSET=0 +CONFIG_RTC_MC146818_LIB=y +CONFIG_SERIAL_OF_PLATFORM=y +CONFIG_SMP_ON_UP=y +CONFIG_SWP_EMULATE=y +CONFIG_SYS_SUPPORTS_APM_EMULATION=y +CONFIG_UNCOMPRESS_INCLUDE="debug/uncompress.h" +CONFIG_UNWINDER_ARM=y +# CONFIG_UNWINDER_FRAME_POINTER is not set +CONFIG_USE_OF=y +CONFIG_VFP=y +CONFIG_VFPv3=y +CONFIG_XZ_DEC_ARM=y +CONFIG_XZ_DEC_BCJ=y +CONFIG_ZBOOT_ROM_BSS=0x0 +CONFIG_ZBOOT_ROM_TEXT=0x0 diff --git a/target/linux/armsr/armv8/config-6.6 b/target/linux/armsr/armv8/config-6.6 new file mode 100644 index 0000000000..aa5774a7b6 --- /dev/null +++ b/target/linux/armsr/armv8/config-6.6 @@ -0,0 +1,852 @@ +CONFIG_64BIT=y +CONFIG_ACPI_APEI=y +# CONFIG_ACPI_FFH is not set +# CONFIG_ACPI_FPDT is not set +CONFIG_ACPI_HMAT=y +CONFIG_ACPI_PCC=y +CONFIG_AHCI_IMX=y +CONFIG_AHCI_MVEBU=y +CONFIG_AHCI_QORIQ=y +CONFIG_AMPERE_ERRATUM_AC03_CPU_38=y +CONFIG_ARCH_BCM=y +CONFIG_ARCH_BCM2835=y +# CONFIG_ARCH_BCMBCA is not set +CONFIG_ARCH_BCM_IPROC=y +CONFIG_ARCH_BRCMSTB=y +CONFIG_ARCH_HISI=y +CONFIG_ARCH_INTEL_SOCFPGA=y +CONFIG_ARCH_LAYERSCAPE=y +CONFIG_ARCH_MHP_MEMMAP_ON_MEMORY_ENABLE=y +CONFIG_ARCH_MMAP_RND_BITS=18 +CONFIG_ARCH_MMAP_RND_BITS_MAX=24 +CONFIG_ARCH_MMAP_RND_BITS_MIN=18 +CONFIG_ARCH_MMAP_RND_COMPAT_BITS_MIN=11 +CONFIG_ARCH_MVEBU=y +CONFIG_ARCH_MXC=y +CONFIG_ARCH_NXP=y +CONFIG_ARCH_PROC_KCORE_TEXT=y +CONFIG_ARCH_R8A774A1=y +CONFIG_ARCH_R8A774B1=y +CONFIG_ARCH_R8A774C0=y +CONFIG_ARCH_R8A774E1=y +# CONFIG_ARCH_R8A77950 is not set +# CONFIG_ARCH_R8A77951 is not set +# CONFIG_ARCH_R8A77960 is not set +# CONFIG_ARCH_R8A77961 is not set +# CONFIG_ARCH_R8A77965 is not set +# CONFIG_ARCH_R8A77970 is not set +# CONFIG_ARCH_R8A77980 is not set +# CONFIG_ARCH_R8A77990 is not set +# CONFIG_ARCH_R8A77995 is not set +# CONFIG_ARCH_R8A779A0 is not set +# CONFIG_ARCH_R8A779F0 is not set +# CONFIG_ARCH_R8A779G0 is not set +CONFIG_ARCH_R9A07G043=y +CONFIG_ARCH_R9A07G044=y +CONFIG_ARCH_R9A07G054=y +CONFIG_ARCH_R9A09G011=y +CONFIG_ARCH_RENESAS=y +CONFIG_ARCH_ROCKCHIP=y +CONFIG_ARCH_STACKWALK=y +CONFIG_ARCH_SUNXI=y +CONFIG_ARCH_SYNQUACER=y +CONFIG_ARCH_THUNDER=y +CONFIG_ARCH_THUNDER2=y +CONFIG_ARCH_VEXPRESS=y +CONFIG_ARCH_WANTS_NO_INSTR=y +CONFIG_ARCH_ZYNQMP=y +CONFIG_ARM64=y +CONFIG_ARM64_4K_PAGES=y +CONFIG_ARM64_AMU_EXTN=y +CONFIG_ARM64_BTI=y +CONFIG_ARM64_CNP=y +CONFIG_ARM64_CRYPTO=y +CONFIG_ARM64_E0PD=y +CONFIG_ARM64_EPAN=y +CONFIG_ARM64_ERRATUM_1024718=y +CONFIG_ARM64_ERRATUM_1165522=y +CONFIG_ARM64_ERRATUM_1286807=y +CONFIG_ARM64_ERRATUM_1319367=y +CONFIG_ARM64_ERRATUM_1418040=y +CONFIG_ARM64_ERRATUM_1463225=y +CONFIG_ARM64_ERRATUM_1508412=y +CONFIG_ARM64_ERRATUM_1530923=y +CONFIG_ARM64_ERRATUM_1542419=y +CONFIG_ARM64_ERRATUM_1742098=y +CONFIG_ARM64_ERRATUM_2051678=y +CONFIG_ARM64_ERRATUM_2054223=y +CONFIG_ARM64_ERRATUM_2067961=y +CONFIG_ARM64_ERRATUM_2077057=y +CONFIG_ARM64_ERRATUM_2441007=y +CONFIG_ARM64_ERRATUM_2441009=y +CONFIG_ARM64_ERRATUM_2457168=y +CONFIG_ARM64_ERRATUM_2658417=y +CONFIG_ARM64_ERRATUM_819472=y +CONFIG_ARM64_ERRATUM_824069=y +CONFIG_ARM64_ERRATUM_826319=y +CONFIG_ARM64_ERRATUM_827319=y +CONFIG_ARM64_ERRATUM_832075=y +CONFIG_ARM64_ERRATUM_834220=y +CONFIG_ARM64_ERRATUM_843419=y +CONFIG_ARM64_ERRATUM_845719=y +CONFIG_ARM64_HW_AFDBM=y +CONFIG_ARM64_LD_HAS_FIX_ERRATUM_843419=y +CONFIG_ARM64_MTE=y +CONFIG_ARM64_PAGE_SHIFT=12 +CONFIG_ARM64_PAN=y +CONFIG_ARM64_PA_BITS=48 +CONFIG_ARM64_PA_BITS_48=y +CONFIG_ARM64_PTR_AUTH=y +CONFIG_ARM64_PTR_AUTH_KERNEL=y +CONFIG_ARM64_RAS_EXTN=y +CONFIG_ARM64_SME=y +CONFIG_ARM64_SVE=y +CONFIG_ARM64_TAGGED_ADDR_ABI=y +CONFIG_ARM64_TLB_RANGE=y +CONFIG_ARM64_VA_BITS=48 +CONFIG_ARM64_VA_BITS_48=y +CONFIG_ARM64_WORKAROUND_CLEAN_CACHE=y +CONFIG_ARM64_WORKAROUND_REPEAT_TLBI=y +CONFIG_ARM64_WORKAROUND_SPECULATIVE_AT=y +CONFIG_ARM64_WORKAROUND_TSB_FLUSH_FAILURE=y +# CONFIG_ARMADA_37XX_RWTM_MBOX is not set +CONFIG_ARMADA_37XX_WATCHDOG=y +CONFIG_ARMADA_THERMAL=y +CONFIG_ARM_ARCH_TIMER_OOL_WORKAROUND=y +# CONFIG_ARM_DMC620_PMU is not set +# CONFIG_ARM_MHU_V2 is not set +CONFIG_ARM_PSCI_CPUIDLE=y +CONFIG_ARM_PSCI_CPUIDLE_DOMAIN=y +CONFIG_ARM_SBSA_WATCHDOG=y +CONFIG_ARM_SCPI_POWER_DOMAIN=y +CONFIG_ARM_SCPI_PROTOCOL=y +CONFIG_ARM_SMCCC_SOC_ID=y +CONFIG_ARM_SMC_WATCHDOG=y +CONFIG_ARM_SMMU=y +# CONFIG_ARM_SMMU_DISABLE_BYPASS_BY_DEFAULT is not set +# CONFIG_ARM_SMMU_LEGACY_DT_BINDINGS is not set +CONFIG_ARM_SMMU_V3=y +# CONFIG_ARM_SMMU_V3_PMU is not set +# CONFIG_ARM_SMMU_V3_SVA is not set +CONFIG_ATOMIC64_SELFTEST=y +CONFIG_AUDIT_ARCH_COMPAT_GENERIC=y +# CONFIG_AXI_DMAC is not set +CONFIG_BACKLIGHT_CLASS_DEVICE=y +# CONFIG_BCM2711_THERMAL is not set +CONFIG_BCM2835_MBOX=y +CONFIG_BCM2835_POWER=y +# CONFIG_BCM2835_THERMAL is not set +# CONFIG_BCM2835_VCHIQ is not set +CONFIG_BCM2835_WDT=y +# CONFIG_BCMASP is not set +# CONFIG_BCMGENET is not set +# CONFIG_BCM_CYGNUS_PHY is not set +# CONFIG_BCM_FLEXRM_MBOX is not set +# CONFIG_BCM_NS_THERMAL is not set +# CONFIG_BCM_PDC_MBOX is not set +# CONFIG_BCM_SR_THERMAL is not set +CONFIG_BCM_VIDEOCORE=y +# CONFIG_BGMAC_PLATFORM is not set +CONFIG_BLK_PM=y +# CONFIG_BRCMSTB_PM is not set +# CONFIG_BRCMSTB_THERMAL is not set +CONFIG_BRCM_USB_PINMAP=y +CONFIG_CAVIUM_ERRATUM_22375=y +CONFIG_CAVIUM_ERRATUM_23144=y +CONFIG_CAVIUM_ERRATUM_23154=y +CONFIG_CAVIUM_ERRATUM_27456=y +CONFIG_CAVIUM_ERRATUM_30115=y +CONFIG_CAVIUM_TX2_ERRATUM_219=y +CONFIG_CC_HAVE_STACKPROTECTOR_SYSREG=y +CONFIG_CLK_BCM2711_DVP=y +CONFIG_CLK_BCM2835=y +CONFIG_CLK_BCM_NS2=y +CONFIG_CLK_BCM_SR=y +CONFIG_CLK_IMX8MM=y +CONFIG_CLK_IMX8MN=y +CONFIG_CLK_IMX8MP=y +CONFIG_CLK_IMX8MQ=y +CONFIG_CLK_IMX8QXP=y +CONFIG_CLK_IMX8ULP=y +CONFIG_CLK_IMX93=y +CONFIG_CLK_INTEL_SOCFPGA=y +CONFIG_CLK_INTEL_SOCFPGA64=y +CONFIG_CLK_LS1028A_PLLDIG=y +CONFIG_CLK_PX30=y +CONFIG_CLK_QORIQ=y +CONFIG_CLK_RASPBERRYPI=y +CONFIG_CLK_RCAR_USB2_CLOCK_SEL=y +CONFIG_CLK_RENESAS=y +CONFIG_CLK_RK3308=y +CONFIG_CLK_RK3328=y +CONFIG_CLK_RK3368=y +CONFIG_CLK_RK3399=y +CONFIG_CLK_RK3568=y +CONFIG_CLK_RK3588=y +CONFIG_CLK_SP810=y +CONFIG_CLK_SUNXI=y +CONFIG_CLK_SUNXI_CLOCKS=y +# CONFIG_CLK_SUNXI_PRCM_SUN6I is not set +# CONFIG_CLK_SUNXI_PRCM_SUN8I is not set +# CONFIG_CLK_SUNXI_PRCM_SUN9I is not set +CONFIG_CLK_VEXPRESS_OSC=y +CONFIG_CMA=y +CONFIG_CMA_ALIGNMENT=8 +CONFIG_CMA_AREAS=19 +# CONFIG_CMA_DEBUG is not set +# CONFIG_CMA_DEBUGFS is not set +CONFIG_CMA_SIZE_MBYTES=32 +# CONFIG_CMA_SIZE_SEL_MAX is not set +CONFIG_CMA_SIZE_SEL_MBYTES=y +# CONFIG_CMA_SIZE_SEL_MIN is not set +# CONFIG_CMA_SIZE_SEL_PERCENTAGE is not set +# CONFIG_CMA_SYSFS is not set +# CONFIG_COMMON_CLK_FSL_FLEXSPI is not set +# CONFIG_COMMON_CLK_FSL_SAI is not set +CONFIG_COMMON_CLK_HI3516CV300=y +CONFIG_COMMON_CLK_HI3519=y +CONFIG_COMMON_CLK_HI3559A=y +CONFIG_COMMON_CLK_HI3660=y +CONFIG_COMMON_CLK_HI3670=y +CONFIG_COMMON_CLK_HI3798CV200=y +CONFIG_COMMON_CLK_HI6220=y +CONFIG_COMMON_CLK_HI655X=y +CONFIG_COMMON_CLK_ROCKCHIP=y +CONFIG_COMMON_CLK_SCPI=y +CONFIG_COMMON_CLK_ZYNQMP=y +CONFIG_COMMON_RESET_HI3660=y +CONFIG_COMMON_RESET_HI6220=y +# CONFIG_COMPAT_32BIT_TIME is not set +CONFIG_CPU_IDLE=y +CONFIG_CPU_IDLE_GOV_MENU=y +CONFIG_CPU_LITTLE_ENDIAN=y +CONFIG_CPU_PM=y +CONFIG_CRYPTO_AES_ARM64=y +CONFIG_CRYPTO_AES_ARM64_BS=y +CONFIG_CRYPTO_AES_ARM64_CE=y +CONFIG_CRYPTO_AES_ARM64_CE_BLK=y +CONFIG_CRYPTO_AES_ARM64_CE_CCM=y +CONFIG_CRYPTO_AES_ARM64_NEON_BLK=y +CONFIG_CRYPTO_ARCH_HAVE_LIB_CHACHA=y +CONFIG_CRYPTO_CHACHA20=y +CONFIG_CRYPTO_CHACHA20_NEON=y +CONFIG_CRYPTO_CRYPTD=y +# CONFIG_CRYPTO_DEV_ALLWINNER is not set +# CONFIG_CRYPTO_DEV_BCM_SPU is not set +# CONFIG_CRYPTO_DEV_FSL_DPAA2_CAAM is not set +# CONFIG_CRYPTO_DEV_HISI_HPRE is not set +# CONFIG_CRYPTO_DEV_HISI_SEC2 is not set +# CONFIG_CRYPTO_DEV_HISI_TRNG is not set +# CONFIG_CRYPTO_DEV_OCTEONTX2_CPT is not set +# CONFIG_CRYPTO_DEV_ROCKCHIP is not set +# CONFIG_CRYPTO_DEV_ZYNQMP_AES is not set +# CONFIG_CRYPTO_DEV_ZYNQMP_SHA3 is not set +CONFIG_CRYPTO_GHASH_ARM64_CE=y +CONFIG_CRYPTO_LIB_BLAKE2S_GENERIC=y +CONFIG_CRYPTO_LIB_CHACHA_GENERIC=y +CONFIG_CRYPTO_POLYVAL_ARM64_CE=y +CONFIG_CRYPTO_SHA1=y +CONFIG_CRYPTO_SHA1_ARM64_CE=y +CONFIG_CRYPTO_SHA256_ARM64=y +CONFIG_CRYPTO_SHA2_ARM64_CE=y +CONFIG_CRYPTO_SHA512_ARM64=y +CONFIG_CRYPTO_SIMD=y +# CONFIG_CRYPTO_SM4_ARM64_CE_BLK is not set +# CONFIG_CRYPTO_SM4_ARM64_CE_CCM is not set +# CONFIG_CRYPTO_SM4_ARM64_CE_GCM is not set +# CONFIG_CRYPTO_SM4_ARM64_NEON_BLK is not set +# CONFIG_DEV_DAX_HMEM is not set +CONFIG_DMA_BCM2835=y +CONFIG_DMA_CMA=y +CONFIG_DMA_DIRECT_REMAP=y +# CONFIG_DMA_NUMA_CMA is not set +CONFIG_DMA_SHARED_BUFFER=y +CONFIG_DMA_SUN6I=y +CONFIG_DRM=y +CONFIG_DRM_BOCHS=y +CONFIG_DRM_BRIDGE=y +# CONFIG_DRM_FSL_LDB is not set +CONFIG_DRM_GEM_SHMEM_HELPER=y +# CONFIG_DRM_IMX8QM_LDB is not set +# CONFIG_DRM_IMX8QXP_LDB is not set +# CONFIG_DRM_IMX8QXP_PIXEL_COMBINER is not set +# CONFIG_DRM_IMX8QXP_PIXEL_LINK is not set +# CONFIG_DRM_IMX8QXP_PIXEL_LINK_TO_DPI is not set +# CONFIG_DRM_IMX_DCSS is not set +# CONFIG_DRM_IMX_LCDC is not set +CONFIG_DRM_KMS_HELPER=y +CONFIG_DRM_PANEL=y +CONFIG_DRM_PANEL_BRIDGE=y +# CONFIG_DRM_PANEL_HIMAX_HX8394 is not set +# CONFIG_DRM_PANEL_JADARD_JD9365DA_H3 is not set +# CONFIG_DRM_PANEL_NEWVISION_NV3051D is not set +# CONFIG_DRM_PANEL_NOVATEK_NT36523 is not set +CONFIG_DRM_PANEL_ORIENTATION_QUIRKS=y +# CONFIG_DRM_PANEL_SONY_TD4353_JDI is not set +# CONFIG_DRM_PANEL_STARTEK_KD070FHFID015 is not set +# CONFIG_DRM_PANEL_VISIONOX_R66451 is not set +# CONFIG_DRM_PANEL_VISIONOX_VTDR6130 is not set +CONFIG_DRM_QXL=y +# CONFIG_DRM_RCAR_DU is not set +# CONFIG_DRM_ROCKCHIP is not set +# CONFIG_DRM_RZG2L_MIPI_DSI is not set +# CONFIG_DRM_SHMOBILE is not set +CONFIG_DRM_TTM=y +CONFIG_DRM_TTM_HELPER=y +# CONFIG_DRM_V3D is not set +CONFIG_DRM_VIRTIO_GPU=y +CONFIG_DRM_VIRTIO_GPU_KMS=y +CONFIG_DRM_VRAM_HELPER=y +# CONFIG_DWMAC_SUN8I is not set +# CONFIG_DWMAC_SUNXI is not set +CONFIG_DW_WATCHDOG=y +CONFIG_EFI_CAPSULE_LOADER=y +CONFIG_EFI_CUSTOM_SSDT_OVERLAYS=y +CONFIG_EFI_SOFT_RESERVE=y +CONFIG_EFI_VARS_PSTORE=y +# CONFIG_EFI_VARS_PSTORE_DEFAULT_DISABLE is not set +CONFIG_FB=y +CONFIG_FB_ARMCLCD=y +CONFIG_FB_CFB_COPYAREA=y +CONFIG_FB_CFB_FILLRECT=y +CONFIG_FB_CFB_IMAGEBLIT=y +CONFIG_FB_CMDLINE=y +CONFIG_FB_MODE_HELPERS=y +CONFIG_FB_MX3=y +# CONFIG_FB_SH_MOBILE_LCDC is not set +# CONFIG_FB_XILINX is not set +CONFIG_FRAME_POINTER=y +# CONFIG_FSL_DPAA is not set +# CONFIG_FSL_DPAA2_QDMA is not set +CONFIG_FSL_ERRATUM_A008585=y +# CONFIG_FSL_IMX8_DDR_PMU is not set +# CONFIG_FSL_PQ_MDIO is not set +CONFIG_FUJITSU_ERRATUM_010001=y +CONFIG_GENERIC_BUG_RELATIVE_POINTERS=y +CONFIG_GENERIC_CSUM=y +CONFIG_GENERIC_FIND_FIRST_BIT=y +# CONFIG_GIANFAR is not set +CONFIG_GPIO_BCM_XGS_IPROC=y +CONFIG_GPIO_BRCMSTB=y +CONFIG_GPIO_GENERIC=y +CONFIG_GPIO_GENERIC_PLATFORM=y +CONFIG_GPIO_MPC8XXX=y +CONFIG_GPIO_MXC=y +CONFIG_GPIO_RASPBERRYPI_EXP=y +CONFIG_GPIO_ROCKCHIP=y +CONFIG_GPIO_THUNDERX=y +CONFIG_GPIO_XLP=y +CONFIG_GPIO_ZYNQ=y +CONFIG_GPIO_ZYNQMP_MODEPIN=y +CONFIG_HDMI=y +CONFIG_HI3660_MBOX=y +CONFIG_HI6220_MBOX=y +CONFIG_HISILICON_ERRATUM_161600802=y +CONFIG_HISILICON_LPC=y +CONFIG_HISI_PMU=y +CONFIG_HISI_THERMAL=y +CONFIG_HOTPLUG_PCI=y +CONFIG_HOTPLUG_PCI_ACPI=y +# CONFIG_HOTPLUG_PCI_ACPI_IBM is not set +# CONFIG_HOTPLUG_PCI_CPCI is not set +# CONFIG_HOTPLUG_PCI_PCIE is not set +# CONFIG_HOTPLUG_PCI_SHPC is not set +CONFIG_HW_RANDOM=y +CONFIG_HW_RANDOM_ARM_SMCCC_TRNG=y +# CONFIG_HW_RANDOM_HISI is not set +# CONFIG_HW_RANDOM_HISTB is not set +CONFIG_HW_RANDOM_VIRTIO=y +CONFIG_I2C=y +CONFIG_I2C_ALGOBIT=y +CONFIG_I2C_ALTERA=y +# CONFIG_I2C_BCM2835 is not set +CONFIG_I2C_BOARDINFO=y +# CONFIG_I2C_HIX5HD2 is not set +CONFIG_I2C_IMX=y +CONFIG_I2C_IMX_LPI2C=y +CONFIG_I2C_RIIC=y +# CONFIG_I2C_RZV2M is not set +# CONFIG_I2C_SLAVE_TESTUNIT is not set +CONFIG_I2C_SYNQUACER=y +CONFIG_I2C_THUNDERX=y +# CONFIG_I2C_XLP9XX is not set +CONFIG_ILLEGAL_POINTER_VALUE=0xdead000000000000 +# CONFIG_IMX2_WDT is not set +# CONFIG_IMX8MM_THERMAL is not set +# CONFIG_IMX8QXP_ADC is not set +# CONFIG_IMX93_ADC is not set +# CONFIG_IMX_DMA is not set +# CONFIG_IMX_DSP is not set +CONFIG_IMX_INTMUX=y +CONFIG_IMX_IRQSTEER=y +CONFIG_IMX_MBOX=y +# CONFIG_IMX_MU_MSI is not set +CONFIG_IMX_SCU=y +CONFIG_IMX_SCU_PD=y +# CONFIG_IMX_SC_THERMAL is not set +# CONFIG_IMX_SC_WDT is not set +# CONFIG_IMX_SDMA is not set +# CONFIG_IMX_WEIM is not set +# CONFIG_INPUT_BBNSM_PWRKEY is not set +# CONFIG_INPUT_HISI_POWERKEY is not set +# CONFIG_INPUT_IBM_PANEL is not set +# CONFIG_INTEL_STRATIX10_RSU is not set +# CONFIG_INTEL_STRATIX10_SERVICE is not set +CONFIG_INTERCONNECT=y +CONFIG_INTERCONNECT_IMX=y +CONFIG_INTERCONNECT_IMX8MM=y +CONFIG_INTERCONNECT_IMX8MN=y +CONFIG_INTERCONNECT_IMX8MP=y +CONFIG_INTERCONNECT_IMX8MQ=y +# CONFIG_IOMMUFD is not set +# CONFIG_IOMMU_DEBUGFS is not set +# CONFIG_IOMMU_DEFAULT_DMA_LAZY is not set +CONFIG_IOMMU_DEFAULT_DMA_STRICT=y +CONFIG_IOMMU_DEFAULT_PASSTHROUGH=y +# CONFIG_IOMMU_IO_PGTABLE_ARMV7S is not set +# CONFIG_IOMMU_IO_PGTABLE_DART is not set +# CONFIG_IOMMU_IO_PGTABLE_LPAE_SELFTEST is not set +CONFIG_IOMMU_SUPPORT=y +# CONFIG_IPMMU_VMSA is not set +# CONFIG_K3_DMA is not set +CONFIG_KCMP=y +# CONFIG_KEYBOARD_IMX_SC_KEY is not set +# CONFIG_KEYBOARD_SUN4I_LRADC is not set +CONFIG_KSM=y +# CONFIG_KUNPENG_HCCS is not set +CONFIG_KVM=y +CONFIG_LCD_CLASS_DEVICE=m +# CONFIG_LCD_PLATFORM is not set +# CONFIG_MAILBOX_TEST is not set +CONFIG_MARVELL_10G_PHY=y +# CONFIG_MARVELL_CN10K_DDR_PMU is not set +# CONFIG_MARVELL_CN10K_TAD_PMU is not set +# CONFIG_MARVELL_GTI_WDT is not set +CONFIG_MDIO_BCM_IPROC=y +CONFIG_MDIO_BUS_MUX_BCM_IPROC=y +CONFIG_MDIO_SUN4I=y +# CONFIG_MFD_ALTERA_A10SR is not set +CONFIG_MFD_ALTERA_SYSMGR=y +# CONFIG_MFD_AXP20X_RSB is not set +CONFIG_MFD_CORE=y +CONFIG_MFD_HI655X_PMIC=y +# CONFIG_MFD_KHADAS_MCU is not set +CONFIG_MFD_SUN4I_GPADC=y +# CONFIG_MFD_SUN6I_PRCM is not set +CONFIG_MFD_SYSCON=y +CONFIG_MFD_VEXPRESS_SYSREG=y +CONFIG_MMC=y +CONFIG_MMC_ARMMMCI=y +CONFIG_MMC_BCM2835=y +CONFIG_MMC_BLOCK=y +CONFIG_MMC_CAVIUM_THUNDERX=y +CONFIG_MMC_DW=y +# CONFIG_MMC_DW_BLUEFIELD is not set +# CONFIG_MMC_DW_EXYNOS is not set +# CONFIG_MMC_DW_HI3798CV200 is not set +# CONFIG_MMC_DW_K3 is not set +# CONFIG_MMC_DW_PCI is not set +CONFIG_MMC_DW_PLTFM=y +CONFIG_MMC_DW_ROCKCHIP=y +# CONFIG_MMC_MXC is not set +CONFIG_MMC_RICOH_MMC=y +CONFIG_MMC_SDHCI=y +CONFIG_MMC_SDHCI_ACPI=y +CONFIG_MMC_SDHCI_CADENCE=y +CONFIG_MMC_SDHCI_ESDHC_IMX=y +CONFIG_MMC_SDHCI_IPROC=y +CONFIG_MMC_SDHCI_OF_ESDHC=y +CONFIG_MMC_SDHCI_PCI=y +CONFIG_MMC_SDHCI_PLTFM=y +CONFIG_MMC_SDHI=y +CONFIG_MMC_SDHI_INTERNAL_DMAC=y +# CONFIG_MMC_SDHI_SYS_DMAC is not set +# CONFIG_MMC_SH_MMCIF is not set +CONFIG_MMC_SUNXI=y +CONFIG_MODULES_USE_ELF_RELA=y +# CONFIG_MVNETA is not set +# CONFIG_MVPP2 is not set +# CONFIG_MV_XOR is not set +# CONFIG_MX3_IPU is not set +CONFIG_MXC_CLK=y +CONFIG_MXC_CLK_SCU=y +# CONFIG_MXS_DMA is not set +CONFIG_NEED_SG_DMA_LENGTH=y +# CONFIG_NET_VENDOR_ALLWINNER is not set +CONFIG_NODES_SHIFT=4 +CONFIG_NOP_USB_XCEIV=y +CONFIG_NO_HZ=y +CONFIG_NO_HZ_COMMON=y +CONFIG_NO_HZ_IDLE=y +CONFIG_NR_CPUS=256 +CONFIG_NUMA=y +CONFIG_NUMA_BALANCING=y +CONFIG_NUMA_BALANCING_DEFAULT_ENABLED=y +# CONFIG_NVHE_EL2_DEBUG is not set +CONFIG_NVIDIA_CARMEL_CNP_ERRATUM=y +# CONFIG_NVMEM_IMX_IIM is not set +# CONFIG_NVMEM_IMX_OCOTP_ELE is not set +CONFIG_NVMEM_IMX_OCOTP_SCU=y +# CONFIG_NVMEM_LAYERSCAPE_SFP is not set +CONFIG_NVMEM_ROCKCHIP_EFUSE=y +# CONFIG_NVMEM_ROCKCHIP_OTP is not set +# CONFIG_NVMEM_SNVS_LPGPR is not set +# CONFIG_NVMEM_SUNXI_SID is not set +# CONFIG_NVMEM_ZYNQMP is not set +CONFIG_PCC=y +CONFIG_PCIEAER=y +CONFIG_PCIEASPM=y +CONFIG_PCIEASPM_DEFAULT=y +# CONFIG_PCIEASPM_PERFORMANCE is not set +# CONFIG_PCIEASPM_POWERSAVE is not set +# CONFIG_PCIEASPM_POWER_SUPERSAVE is not set +CONFIG_PCIEPORTBUS=y +CONFIG_PCIE_ARMADA_8K=y +CONFIG_PCIE_BRCMSTB=y +CONFIG_PCIE_HISI_STB=y +CONFIG_PCIE_IPROC_MSI=y +CONFIG_PCIE_IPROC_PLATFORM=y +CONFIG_PCIE_LAYERSCAPE=y +CONFIG_PCIE_MOBIVEIL_PLAT=y +# CONFIG_PCIE_RCAR_EP is not set +CONFIG_PCIE_RCAR_HOST=y +CONFIG_PCIE_ROCKCHIP=y +# CONFIG_PCIE_ROCKCHIP_DW_HOST is not set +CONFIG_PCIE_ROCKCHIP_HOST=y +CONFIG_PCIE_XILINX_CPM=y +CONFIG_PCIE_XILINX_NWL=y +CONFIG_PCI_AARDVARK=y +CONFIG_PCI_HISI=y +CONFIG_PCI_HOST_THUNDER_ECAM=y +CONFIG_PCI_HOST_THUNDER_PEM=y +CONFIG_PCI_IMX6=y +CONFIG_PCI_IMX6_HOST=y +CONFIG_PCI_IOV=y +CONFIG_PCI_LAYERSCAPE=y +CONFIG_PCI_PASID=y +# CONFIG_PCI_RCAR_GEN2 is not set +CONFIG_PHY_BCM_SR_PCIE=y +CONFIG_PHY_BCM_SR_USB=y +CONFIG_PHY_BRCM_SATA=y +CONFIG_PHY_BRCM_USB=y +CONFIG_PHY_FSL_IMX8M_PCIE=y +# CONFIG_PHY_FSL_LYNX_28G is not set +CONFIG_PHY_HI3660_USB=y +CONFIG_PHY_HI3670_PCIE=y +CONFIG_PHY_HI3670_USB=y +CONFIG_PHY_HI6220_USB=y +CONFIG_PHY_HISI_INNO_USB2=y +# CONFIG_PHY_HISTB_COMBPHY is not set +# CONFIG_PHY_MIXEL_LVDS_PHY is not set +CONFIG_PHY_MVEBU_A3700_COMPHY=y +CONFIG_PHY_MVEBU_A3700_UTMI=y +CONFIG_PHY_MVEBU_A38X_COMPHY=y +CONFIG_PHY_MVEBU_CP110_COMPHY=y +CONFIG_PHY_NS2_PCIE=y +CONFIG_PHY_NS2_USB_DRD=y +# CONFIG_PHY_R8A779F0_ETHERNET_SERDES is not set +# CONFIG_PHY_RCAR_GEN2 is not set +CONFIG_PHY_RCAR_GEN3_PCIE=y +CONFIG_PHY_RCAR_GEN3_USB2=y +CONFIG_PHY_RCAR_GEN3_USB3=y +# CONFIG_PHY_ROCKCHIP_DP is not set +# CONFIG_PHY_ROCKCHIP_DPHY_RX0 is not set +CONFIG_PHY_ROCKCHIP_EMMC=y +# CONFIG_PHY_ROCKCHIP_INNO_CSIDPHY is not set +# CONFIG_PHY_ROCKCHIP_INNO_DSIDPHY is not set +# CONFIG_PHY_ROCKCHIP_INNO_HDMI is not set +CONFIG_PHY_ROCKCHIP_INNO_USB2=y +# CONFIG_PHY_ROCKCHIP_NANENG_COMBO_PHY is not set +CONFIG_PHY_ROCKCHIP_PCIE=y +CONFIG_PHY_ROCKCHIP_SNPS_PCIE3=y +CONFIG_PHY_ROCKCHIP_TYPEC=y +# CONFIG_PHY_ROCKCHIP_USB is not set +CONFIG_PHY_SUN4I_USB=y +CONFIG_PHY_SUN50I_USB3=y +# CONFIG_PHY_SUN6I_MIPI_DPHY is not set +CONFIG_PHY_SUN9I_USB=y +# CONFIG_PHY_XILINX_ZYNQMP is not set +CONFIG_PINCTRL_IMX=y +CONFIG_PINCTRL_IMX8DXL=y +CONFIG_PINCTRL_IMX8MM=y +CONFIG_PINCTRL_IMX8MN=y +CONFIG_PINCTRL_IMX8MP=y +CONFIG_PINCTRL_IMX8MQ=y +CONFIG_PINCTRL_IMX8QM=y +CONFIG_PINCTRL_IMX8QXP=y +CONFIG_PINCTRL_IMX8ULP=y +CONFIG_PINCTRL_IMX93=y +# CONFIG_PINCTRL_IMXRT1050 is not set +# CONFIG_PINCTRL_IMXRT1170 is not set +CONFIG_PINCTRL_IMX_SCU=y +CONFIG_PINCTRL_IPROC_GPIO=y +CONFIG_PINCTRL_NS2_MUX=y +CONFIG_PINCTRL_ROCKCHIP=y +# CONFIG_PINCTRL_SUN20I_D1 is not set +CONFIG_PINCTRL_SUN4I_A10=y +CONFIG_PINCTRL_SUN50I_A100=y +CONFIG_PINCTRL_SUN50I_A100_R=y +CONFIG_PINCTRL_SUN50I_A64=y +CONFIG_PINCTRL_SUN50I_A64_R=y +CONFIG_PINCTRL_SUN50I_H5=y +CONFIG_PINCTRL_SUN50I_H6=y +CONFIG_PINCTRL_SUN50I_H616=y +CONFIG_PINCTRL_SUN50I_H616_R=y +CONFIG_PINCTRL_SUN50I_H6_R=y +CONFIG_PINCTRL_SUN5I=y +# CONFIG_PINCTRL_SUN6I_A31 is not set +# CONFIG_PINCTRL_SUN6I_A31_R is not set +# CONFIG_PINCTRL_SUN8I_A23 is not set +# CONFIG_PINCTRL_SUN8I_A23_R is not set +# CONFIG_PINCTRL_SUN8I_A33 is not set +# CONFIG_PINCTRL_SUN8I_A83T is not set +# CONFIG_PINCTRL_SUN8I_A83T_R is not set +# CONFIG_PINCTRL_SUN8I_H3 is not set +# CONFIG_PINCTRL_SUN8I_H3_R is not set +# CONFIG_PINCTRL_SUN8I_V3S is not set +# CONFIG_PINCTRL_SUN9I_A80 is not set +# CONFIG_PINCTRL_SUN9I_A80_R is not set +CONFIG_PINCTRL_ZYNQMP=y +CONFIG_PM=y +CONFIG_PM_CLK=y +CONFIG_PM_GENERIC_DOMAINS=y +CONFIG_PM_GENERIC_DOMAINS_OF=y +CONFIG_POWER_RESET=y +CONFIG_POWER_RESET_HISI=y +CONFIG_POWER_RESET_VEXPRESS=y +CONFIG_POWER_SUPPLY=y +# CONFIG_PTP_1588_CLOCK_DTE is not set +# CONFIG_PWM_BCM2835 is not set +CONFIG_QCOM_FALKOR_ERRATUM_1003=y +CONFIG_QCOM_FALKOR_ERRATUM_1009=y +CONFIG_QCOM_FALKOR_ERRATUM_E1041=y +CONFIG_QCOM_QDF2400_ERRATUM_0065=y +CONFIG_QORIQ_THERMAL=y +CONFIG_QUEUED_RWLOCKS=y +CONFIG_QUEUED_SPINLOCKS=y +CONFIG_RANDOMIZE_BASE=y +CONFIG_RANDOMIZE_MODULE_REGION_FULL=y +CONFIG_RANDSTRUCT_NONE=y +CONFIG_RASPBERRYPI_FIRMWARE=y +CONFIG_RASPBERRYPI_POWER=y +# CONFIG_RAVB is not set +CONFIG_RCAR_DMAC=y +# CONFIG_RCAR_GEN3_THERMAL is not set +# CONFIG_RCAR_THERMAL is not set +CONFIG_REGMAP=y +CONFIG_REGMAP_MMIO=y +CONFIG_REGULATOR=y +CONFIG_REGULATOR_ANATOP=y +CONFIG_REGULATOR_AXP20X=y +CONFIG_REGULATOR_FIXED_VOLTAGE=y +CONFIG_REGULATOR_HI655X=y +CONFIG_REGULATOR_PFUZE100=y +# CONFIG_REGULATOR_VEXPRESS is not set +CONFIG_RELOCATABLE=y +# CONFIG_RENESAS_ETHER_SWITCH is not set +CONFIG_RENESAS_OSTM=y +# CONFIG_RENESAS_RZAWDT is not set +# CONFIG_RENESAS_RZG2LWDT is not set +# CONFIG_RENESAS_RZN1WDT is not set +CONFIG_RENESAS_USB_DMAC=y +# CONFIG_RENESAS_WDT is not set +# CONFIG_RESET_BRCMSTB is not set +CONFIG_RESET_IMX7=y +# CONFIG_RESET_RASPBERRYPI is not set +CONFIG_RESET_RZG2L_USBPHY_CTRL=y +CONFIG_ROCKCHIP_IODOMAIN=y +CONFIG_ROCKCHIP_IOMMU=y +# CONFIG_ROCKCHIP_MBOX is not set +CONFIG_ROCKCHIP_PM_DOMAINS=y +# CONFIG_ROCKCHIP_SARADC is not set +# CONFIG_ROCKCHIP_THERMAL is not set +CONFIG_RODATA_FULL_DEFAULT_ENABLED=y +# CONFIG_RTC_DRV_BBNSM is not set +# CONFIG_RTC_DRV_BRCMSTB is not set +# CONFIG_RTC_DRV_FSL_FTM_ALARM is not set +# CONFIG_RTC_DRV_IMXDI is not set +# CONFIG_RTC_DRV_IMX_SC is not set +CONFIG_RTC_DRV_MV=y +# CONFIG_RTC_DRV_MXC is not set +# CONFIG_RTC_DRV_MXC_V2 is not set +# CONFIG_RTC_DRV_SH is not set +CONFIG_RTC_I2C_AND_SPI=y +# CONFIG_RZG2L_ADC is not set +# CONFIG_RZG2L_THERMAL is not set +CONFIG_RZ_DMAC=y +CONFIG_RZ_MTU3=y +CONFIG_SATA_SIL24=y +# CONFIG_SCHED_CORE is not set +CONFIG_SCHED_MC=y +CONFIG_SCHED_SMT=y +# CONFIG_SENSORS_ARM_SCPI is not set +CONFIG_SERIAL_8250_BCM2835AUX=y +CONFIG_SERIAL_8250_BCM7271=y +# CONFIG_SERIAL_8250_EXAR is not set +CONFIG_SERIAL_8250_FSL=y +CONFIG_SERIAL_8250_PCI=y +CONFIG_SERIAL_FSL_LINFLEXUART=y +CONFIG_SERIAL_FSL_LINFLEXUART_CONSOLE=y +CONFIG_SERIAL_FSL_LPUART=y +CONFIG_SERIAL_FSL_LPUART_CONSOLE=y +CONFIG_SERIAL_IMX=y +CONFIG_SERIAL_IMX_CONSOLE=y +CONFIG_SERIAL_IMX_EARLYCON=y +CONFIG_SERIAL_MVEBU_CONSOLE=y +CONFIG_SERIAL_MVEBU_UART=y +CONFIG_SERIAL_SAMSUNG=y +CONFIG_SERIAL_SAMSUNG_CONSOLE=y +CONFIG_SERIAL_SH_SCI=y +CONFIG_SERIAL_SH_SCI_CONSOLE=y +CONFIG_SERIAL_SH_SCI_DMA=y +CONFIG_SERIAL_SH_SCI_EARLYCON=y +CONFIG_SERIAL_SH_SCI_NR_UARTS=18 +# CONFIG_SMC91X is not set +# CONFIG_SND_SOC_RCAR is not set +# CONFIG_SND_SOC_RZ is not set +# CONFIG_SND_SOC_SH4_FSI is not set +# CONFIG_SND_SUN4I_I2S is not set +# CONFIG_SND_SUN50I_CODEC_ANALOG is not set +# CONFIG_SND_SUN50I_DMIC is not set +# CONFIG_SND_SUN8I_CODEC is not set +# CONFIG_SND_SUN8I_CODEC_ANALOG is not set +# CONFIG_SNI_NETSEC is not set +CONFIG_SOCIONEXT_SYNQUACER_PREITS=y +CONFIG_SOC_IMX8M=y +CONFIG_SOC_IMX9=y +CONFIG_SPARSEMEM=y +CONFIG_SPARSEMEM_EXTREME=y +CONFIG_SPARSEMEM_VMEMMAP=y +CONFIG_SPARSEMEM_VMEMMAP_ENABLE=y +CONFIG_SPI_ARMADA_3700=y +# CONFIG_SPI_BCM2835 is not set +CONFIG_SPI_FSL_LPSPI=y +# CONFIG_SPI_FSL_QUADSPI is not set +# CONFIG_SPI_HISI_KUNPENG is not set +# CONFIG_SPI_HISI_SFC is not set +# CONFIG_SPI_HISI_SFC_V3XX is not set +CONFIG_SPI_IMX=y +# CONFIG_SPI_ROCKCHIP_SFC is not set +# CONFIG_SPI_RSPI is not set +# CONFIG_SPI_RZV2M_CSI is not set +# CONFIG_SPI_SH_HSPI is not set +# CONFIG_SPI_SH_MSIOF is not set +# CONFIG_SPI_SUN4I is not set +# CONFIG_SPI_SUN6I is not set +# CONFIG_SPI_SYNQUACER is not set +CONFIG_SPI_THUNDERX=y +# CONFIG_SPI_XLP is not set +# CONFIG_SSIF_IPMI_BMC is not set +CONFIG_STUB_CLK_HI3660=y +CONFIG_STUB_CLK_HI6220=y +# CONFIG_SUN20I_GPADC is not set +# CONFIG_SUN20I_PPU is not set +CONFIG_SUN50I_A100_CCU=y +CONFIG_SUN50I_A100_R_CCU=y +CONFIG_SUN50I_A64_CCU=y +CONFIG_SUN50I_H616_CCU=y +CONFIG_SUN50I_H6_CCU=y +CONFIG_SUN50I_H6_R_CCU=y +CONFIG_SUN50I_IOMMU=y +CONFIG_SUN6I_MSGBOX=y +CONFIG_SUN6I_RTC_CCU=y +# CONFIG_SUN8I_A83T_CCU is not set +CONFIG_SUN8I_DE2_CCU=y +# CONFIG_SUN8I_H3_CCU is not set +CONFIG_SUN8I_R_CCU=y +CONFIG_SUN8I_THERMAL=y +CONFIG_SUNXI_CCU=y +CONFIG_SUNXI_RSB=y +CONFIG_SUNXI_WATCHDOG=y +CONFIG_SYNC_FILE=y +CONFIG_SYSCTL_EXCEPTION_TRACE=y +# CONFIG_TCG_TIS_SYNQUACER is not set +CONFIG_THREAD_INFO_IN_TASK=y +# CONFIG_THUNDERX2_PMU is not set +CONFIG_TRANSPARENT_HUGEPAGE=y +CONFIG_TRANSPARENT_HUGEPAGE_ALWAYS=y +# CONFIG_TRANSPARENT_HUGEPAGE_MADVISE is not set +# CONFIG_TURRIS_MOX_RWTM is not set +CONFIG_TYPEC=y +# CONFIG_TYPEC_ANX7411 is not set +# CONFIG_TYPEC_DP_ALTMODE is not set +# CONFIG_TYPEC_FUSB302 is not set +# CONFIG_TYPEC_HD3SS3220 is not set +# CONFIG_TYPEC_MUX_FSA4480 is not set +# CONFIG_TYPEC_MUX_GPIO_SBU is not set +# CONFIG_TYPEC_MUX_NB7VPQ904M is not set +# CONFIG_TYPEC_MUX_PI3USB30532 is not set +# CONFIG_TYPEC_RT1711H is not set +# CONFIG_TYPEC_RT1719 is not set +# CONFIG_TYPEC_STUSB160X is not set +CONFIG_TYPEC_TCPCI=y +# CONFIG_TYPEC_TCPCI_MAXIM is not set +CONFIG_TYPEC_TCPM=y +# CONFIG_TYPEC_TPS6598X is not set +# CONFIG_TYPEC_WUSB3801 is not set +# CONFIG_UACCE is not set +CONFIG_UNMAP_KERNEL_AT_EL0=y +# CONFIG_USB_BRCMSTB is not set +# CONFIG_USB_CDNS2_UDC is not set +CONFIG_USB_CHIPIDEA=y +CONFIG_USB_CHIPIDEA_GENERIC=y +CONFIG_USB_CHIPIDEA_HOST=y +CONFIG_USB_CHIPIDEA_IMX=y +CONFIG_USB_CHIPIDEA_PCI=y +CONFIG_USB_CHIPIDEA_UDC=y +CONFIG_USB_DWC3=y +CONFIG_USB_DWC3_DUAL_ROLE=y +# CONFIG_USB_DWC3_GADGET is not set +CONFIG_USB_DWC3_HAPS=y +# CONFIG_USB_DWC3_HOST is not set +CONFIG_USB_DWC3_IMX8MP=y +# CONFIG_USB_DWC3_OF_SIMPLE is not set +CONFIG_USB_DWC3_PCI=y +# CONFIG_USB_DWC3_ULPI is not set +CONFIG_USB_DWC3_XILINX=y +CONFIG_USB_EHCI_FSL=y +CONFIG_USB_EHCI_HCD=y +CONFIG_USB_EHCI_HCD_ORION=y +CONFIG_USB_EHCI_HCD_PLATFORM=y +# CONFIG_USB_EMXX is not set +CONFIG_USB_GADGET=y +CONFIG_USB_MXS_PHY=y +CONFIG_USB_OHCI_EXYNOS=y +CONFIG_USB_OHCI_HCD=y +CONFIG_USB_OHCI_HCD_PCI=y +CONFIG_USB_OHCI_HCD_PLATFORM=y +CONFIG_USB_OTG=y +CONFIG_USB_OTG_FSM=y +CONFIG_USB_RENESAS_USB3=y +CONFIG_USB_RENESAS_USBF=y +CONFIG_USB_RENESAS_USBHS=y +CONFIG_USB_RENESAS_USBHS_HCD=y +CONFIG_USB_RENESAS_USBHS_UDC=y +CONFIG_USB_RZV2M_USB3DRD=y +CONFIG_USB_XHCI_HCD=y +CONFIG_USB_XHCI_HISTB=y +CONFIG_USB_XHCI_MVEBU=y +CONFIG_USB_XHCI_PLATFORM=y +# CONFIG_USB_XHCI_RCAR is not set +CONFIG_USB_XHCI_RZV2M=y +CONFIG_VEXPRESS_CONFIG=y +# CONFIG_VFIO_AMBA is not set +CONFIG_VIDEOMODE_HELPERS=y +# CONFIG_VIDEO_IMX7_CSI is not set +# CONFIG_VIDEO_IMX8MQ_MIPI_CSI2 is not set +# CONFIG_VIDEO_IMX8_ISI is not set +# CONFIG_VIDEO_RZG2L_CRU is not set +# CONFIG_VIDEO_RZG2L_CSI2 is not set +CONFIG_VIRTIO_DMA_SHARED_BUFFER=y +# CONFIG_VIRTIO_IOMMU is not set +CONFIG_VIRTUALIZATION=y +CONFIG_VMAP_STACK=y +CONFIG_WDAT_WDT=y +# CONFIG_XILINX_AMS is not set +# CONFIG_XILINX_INTC is not set +CONFIG_XLNX_EVENT_MANAGER=y +CONFIG_ZONE_DMA32=y +CONFIG_ZYNQMP_FIRMWARE=y +# CONFIG_ZYNQMP_FIRMWARE_DEBUG is not set +CONFIG_ZYNQMP_PM_DOMAINS=y +CONFIG_ZYNQMP_POWER=y diff --git a/target/linux/armsr/base-files/etc/board.d/03_gpio_switches b/target/linux/armsr/base-files/etc/board.d/03_gpio_switches index cf07bc0f54..72f310277a 100644 --- a/target/linux/armsr/base-files/etc/board.d/03_gpio_switches +++ b/target/linux/armsr/base-files/etc/board.d/03_gpio_switches @@ -3,18 +3,26 @@ . /lib/functions/uci-defaults.sh +KERNEL_MAJOR=$(uname -r | awk -F '.' '{print $1}') +KERNEL_MINOR=$(uname -r | awk -F '.' '{print $2}') + board_config_update board=$(board_name) case "$board" in traverse,ten64) - ucidef_add_gpio_switch "lte_reset" "Cell Modem Reset" "376" - ucidef_add_gpio_switch "lte_power" "Cell Modem Power" "377" - ucidef_add_gpio_switch "lte_disable" "Cell Modem Airplane mode" "378" - ucidef_add_gpio_switch "gnss_disable" "Cell Modem Disable GNSS receiver" "379" - ucidef_add_gpio_switch "lower_sfp_txidsable" "Lower SFP+ TX Disable" "369" - ucidef_add_gpio_switch "upper_sfp_txdisable" "Upper SFP+ TX Disable" "373" + if [ "${KERNEL_MAJOR}" -ge "6" ] && [ "${KERNEL_MINOR}" -ge "6" ]; then + I2C_GPIO_BASE=640 + else + I2C_GPIO_BASE=368 + fi + ucidef_add_gpio_switch "lte_reset" "Cell Modem Reset" "$(($I2C_GPIO_BASE + 8))" + ucidef_add_gpio_switch "lte_power" "Cell Modem Power" "$(($I2C_GPIO_BASE + 9))" + ucidef_add_gpio_switch "lte_disable" "Cell Modem Airplane mode" "$((I2C_GPIO_BASE + 10))" + ucidef_add_gpio_switch "gnss_disable" "Cell Modem Disable GNSS receiver" "$(($I2C_GPIO_BASE + 11))" + ucidef_add_gpio_switch "lower_sfp_txidsable" "Lower SFP+ TX Disable" "$(($I2C_GPIO_BASE + 1))" + ucidef_add_gpio_switch "upper_sfp_txdisable" "Upper SFP+ TX Disable" "$(($I2C_GPIO_BASE + 5))" ;; esac diff --git a/target/linux/armsr/base-files/etc/uci-defaults/05-migrate-ten64-gpio b/target/linux/armsr/base-files/etc/uci-defaults/05-migrate-ten64-gpio new file mode 100644 index 0000000000..dc8648e570 --- /dev/null +++ b/target/linux/armsr/base-files/etc/uci-defaults/05-migrate-ten64-gpio @@ -0,0 +1,37 @@ +#!/bin/sh +# SPDX-License-Identifier: GPL-2.0-or-later + +# This script migrates GPIO switch pin numbers +# from kernel versions prior to 6.6 +# See https://lists.openwrt.org/pipermail/openwrt-devel/2024-March/042448.html + +. /lib/functions.sh + +ten64_update_gpioswitch_num() { + local section="$1" + config_get gpio_pin "${section}" gpio_pin + config_get gpio_name "${section}" name + if [ -z "${gpio_pin}" ]; then + return + fi + local this_pin_name=$(uci get "system.${section}.name") + if [ "${gpio_pin}" -lt 640 ]; then + new_pin_value=$(( $gpio_pin + 272 )) + uci set "system.${section}.gpio_pin=${new_pin_value}" + fi +} + +board=$(board_name) +if [ "${board}" != "traverse,ten64" ]; then + exit 0 +fi + +KERNEL_MINOR=$(uname -r | awk -F '.' '{print $2}') +if [ "${KERNEL_MINOR}" -lt "6" ]; then + exit 0 +fi + +config_load system +config_foreach ten64_update_gpioswitch_num gpio_switch + +exit 0 \ No newline at end of file diff --git a/target/linux/armsr/config-6.6 b/target/linux/armsr/config-6.6 new file mode 100644 index 0000000000..8b4f291c9f --- /dev/null +++ b/target/linux/armsr/config-6.6 @@ -0,0 +1,338 @@ +CONFIG_64BIT=y +CONFIG_9P_FS=y +# CONFIG_9P_FS_POSIX_ACL is not set +# CONFIG_9P_FS_SECURITY is not set +# CONFIG_A64FX_DIAG is not set +CONFIG_ACPI=y +CONFIG_ACPI_AC=y +CONFIG_ACPI_APEI=y +CONFIG_ACPI_APEI_EINJ=y +# CONFIG_ACPI_APEI_ERST_DEBUG is not set +CONFIG_ACPI_APEI_GHES=y +CONFIG_ACPI_APEI_MEMORY_FAILURE=y +CONFIG_ACPI_APEI_PCIEAER=y +CONFIG_ACPI_BATTERY=y +# CONFIG_ACPI_BGRT is not set +CONFIG_ACPI_BUTTON=y +CONFIG_ACPI_CCA_REQUIRED=y +CONFIG_ACPI_CONTAINER=y +CONFIG_ACPI_CPPC_CPUFREQ=y +# CONFIG_ACPI_DEBUG is not set +# CONFIG_ACPI_DEBUGGER is not set +# CONFIG_ACPI_DOCK is not set +# CONFIG_ACPI_EC_DEBUGFS is not set +CONFIG_ACPI_FAN=y +CONFIG_ACPI_GENERIC_GSI=y +CONFIG_ACPI_GTDT=y +CONFIG_ACPI_HOTPLUG_CPU=y +CONFIG_ACPI_I2C_OPREGION=y +CONFIG_ACPI_IORT=y +CONFIG_ACPI_MCFG=y +# CONFIG_ACPI_PCI_SLOT is not set +# CONFIG_ACPI_PFRUT is not set +CONFIG_ACPI_PPTT=y +CONFIG_ACPI_PRMT=y +CONFIG_ACPI_PROCESSOR=y +CONFIG_ACPI_PROCESSOR_IDLE=y +CONFIG_ACPI_REDUCED_HARDWARE_ONLY=y +CONFIG_ACPI_SPCR_TABLE=y +CONFIG_ACPI_THERMAL=y +# CONFIG_ACPI_TINY_POWER_BUTTON is not set +# CONFIG_ALIBABA_UNCORE_DRW_PMU is not set +CONFIG_ARCH_DMA_ADDR_T_64BIT=y +CONFIG_ARCH_HIBERNATION_POSSIBLE=y +CONFIG_ARCH_KEEP_MEMBLOCK=y +CONFIG_ARCH_MHP_MEMMAP_ON_MEMORY_ENABLE=y +CONFIG_ARCH_MMAP_RND_BITS=18 +CONFIG_ARCH_MMAP_RND_BITS_MAX=24 +CONFIG_ARCH_MMAP_RND_BITS_MIN=18 +CONFIG_ARCH_MMAP_RND_COMPAT_BITS_MIN=11 +CONFIG_ARCH_PROC_KCORE_TEXT=y +CONFIG_ARCH_SPARSEMEM_ENABLE=y +CONFIG_ARCH_STACKWALK=y +CONFIG_ARCH_SUSPEND_POSSIBLE=y +CONFIG_ARCH_WANTS_NO_INSTR=y +CONFIG_ARM64=y +CONFIG_ARM64_4K_PAGES=y +# CONFIG_ARM64_ACPI_PARKING_PROTOCOL is not set +CONFIG_ARM64_LD_HAS_FIX_ERRATUM_843419=y +CONFIG_ARM64_PAGE_SHIFT=12 +CONFIG_ARM64_PA_BITS=48 +CONFIG_ARM64_PA_BITS_48=y +CONFIG_ARM64_TAGGED_ADDR_ABI=y +CONFIG_ARM64_VA_BITS=39 +CONFIG_ARM64_VA_BITS_39=y +CONFIG_ARM_AMBA=y +CONFIG_ARM_ARCH_TIMER=y +CONFIG_ARM_ARCH_TIMER_EVTSTREAM=y +CONFIG_ARM_GIC=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_PSCI_FW=y +# CONFIG_ARM_SMMU_V3_PMU is not set +CONFIG_ATA=y +CONFIG_AUDIT_ARCH_COMPAT_GENERIC=y +CONFIG_BALLOON_COMPACTION=y +CONFIG_BLK_DEV_LOOP=y +CONFIG_BLK_DEV_NVME=y +CONFIG_BLK_DEV_SD=y +CONFIG_BLK_MQ_PCI=y +CONFIG_BLK_MQ_VIRTIO=y +CONFIG_CC_HAVE_STACKPROTECTOR_SYSREG=y +CONFIG_CLONE_BACKWARDS=y +CONFIG_COMMON_CLK=y +# CONFIG_COMPAT_32BIT_TIME is not set +CONFIG_CONSOLE_TRANSLATIONS=y +CONFIG_CPU_IDLE=y +CONFIG_CPU_IDLE_GOV_LADDER=y +CONFIG_CPU_PM=y +CONFIG_CPU_RMAP=y +CONFIG_CRC16=y +CONFIG_CRYPTO_CRC32=y +CONFIG_CRYPTO_CRC32C=y +CONFIG_CRYPTO_LIB_BLAKE2S_GENERIC=y +CONFIG_CRYPTO_RNG2=y +CONFIG_DCACHE_WORD_ACCESS=y +CONFIG_DEBUG_BUGVERBOSE=y +CONFIG_DMADEVICES=y +CONFIG_DMA_ACPI=y +CONFIG_DMA_DIRECT_REMAP=y +CONFIG_DMA_ENGINE=y +CONFIG_DMA_OF=y +CONFIG_DMA_REMAP=y +CONFIG_DMI=y +CONFIG_DMIID=y +CONFIG_DMI_SYSFS=y +CONFIG_DTC=y +CONFIG_EDAC_SUPPORT=y +CONFIG_EFI=y +CONFIG_EFIVAR_FS=y +CONFIG_EFI_ARMSTUB_DTB_LOADER=y +# CONFIG_EFI_BOOTLOADER_CONTROL is not set +# CONFIG_EFI_CAPSULE_LOADER is not set +# CONFIG_EFI_COCO_SECRET is not set +# CONFIG_EFI_CUSTOM_SSDT_OVERLAYS is not set +# CONFIG_EFI_DISABLE_PCI_DMA is not set +# CONFIG_EFI_DISABLE_RUNTIME is not set +CONFIG_EFI_EARLYCON=y +CONFIG_EFI_ESRT=y +CONFIG_EFI_GENERIC_STUB=y +# CONFIG_EFI_GENERIC_STUB_INITRD_CMDLINE_LOADER is not set +CONFIG_EFI_PARAMS_FROM_FDT=y +CONFIG_EFI_RUNTIME_WRAPPERS=y +CONFIG_EFI_STUB=y +# CONFIG_EFI_TEST is not set +# CONFIG_EFI_ZBOOT is not set +CONFIG_EXT4_FS=y +CONFIG_F2FS_FS=y +CONFIG_FAILOVER=y +CONFIG_FB_EFI=y +CONFIG_FIX_EARLYCON_MEM=y +CONFIG_FONT_8x16=y +CONFIG_FONT_AUTOSELECT=y +CONFIG_FONT_SUPPORT=y +CONFIG_FRAMEBUFFER_CONSOLE=y +CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY=y +# CONFIG_FRAMEBUFFER_CONSOLE_ROTATION is not set +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_FIND_FIRST_BIT=y +CONFIG_GENERIC_GETTIMEOFDAY=y +CONFIG_GENERIC_IDLE_POLL_SETUP=y +CONFIG_GENERIC_IRQ_EFFECTIVE_AFF_MASK=y +CONFIG_GENERIC_IRQ_MIGRATION=y +CONFIG_GENERIC_IRQ_SHOW=y +CONFIG_GENERIC_IRQ_SHOW_LEVEL=y +CONFIG_GENERIC_LIB_DEVMEM_IS_ALLOWED=y +CONFIG_GENERIC_MSI_IRQ=y +CONFIG_GENERIC_MSI_IRQ_DOMAIN=y +CONFIG_GENERIC_PCI_IOMAP=y +CONFIG_GENERIC_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_GPIOLIB_IRQCHIP=y +CONFIG_GPIO_ACPI=y +CONFIG_GPIO_CDEV=y +# CONFIG_GPIO_HISI is not set +CONFIG_GPIO_PL061=y +# CONFIG_GPIO_VF610 is not set +CONFIG_HANDLE_DOMAIN_IRQ=y +CONFIG_HARDIRQS_SW_RESEND=y +CONFIG_HAS_DMA=y +CONFIG_HAS_IOMEM=y +CONFIG_HAS_IOPORT_MAP=y +CONFIG_HID=y +CONFIG_HID_GENERIC=y +CONFIG_HID_SUPPORT=y +CONFIG_HOTPLUG_CPU=y +CONFIG_HOTPLUG_PCI_ACPI=y +CONFIG_HVC_DRIVER=y +CONFIG_HZ_PERIODIC=y +# CONFIG_I2C_AMD_MP2 is not set +CONFIG_I2C_HID_ACPI=y +# CONFIG_I2C_HISI is not set +# CONFIG_I2C_SLAVE_TESTUNIT is not set +CONFIG_ILLEGAL_POINTER_VALUE=0xdead000000000000 +# CONFIG_IMA_SECURE_AND_OR_TRUSTED_BOOT is not set +CONFIG_INITRAMFS_SOURCE="" +CONFIG_INPUT_KEYBOARD=y +CONFIG_IRQCHIP=y +CONFIG_IRQ_DOMAIN=y +CONFIG_IRQ_DOMAIN_HIERARCHY=y +CONFIG_IRQ_FORCED_THREADING=y +CONFIG_IRQ_WORK=y +# CONFIG_ISCSI_IBFT is not set +CONFIG_JBD2=y +CONFIG_LIBFDT=y +CONFIG_LOCK_DEBUGGING_SUPPORT=y +CONFIG_LOCK_SPIN_ON_OWNER=y +CONFIG_MEMFD_CREATE=y +CONFIG_MEMORY_BALLOON=y +CONFIG_MIGRATION=y +# CONFIG_MLXBF_GIGE is not set +CONFIG_MMC_SDHCI_ACPI=y +CONFIG_MODULES_USE_ELF_RELA=y +CONFIG_MUTEX_SPIN_ON_OWNER=y +CONFIG_MVMDIO=y +CONFIG_NEED_DMA_MAP_STATE=y +CONFIG_NEED_SG_DMA_LENGTH=y +CONFIG_NET_9P=y +# CONFIG_NET_9P_DEBUG is not set +# CONFIG_NET_9P_FD is not set +CONFIG_NET_9P_VIRTIO=y +CONFIG_NET_FAILOVER=y +CONFIG_NET_FLOW_LIMIT=y +CONFIG_NLS=y +CONFIG_NR_CPUS=256 +CONFIG_NVMEM=y +CONFIG_NVME_CORE=y +# CONFIG_NVME_MULTIPATH is not set +CONFIG_OF=y +CONFIG_OF_ADDRESS=y +CONFIG_OF_EARLY_FLATTREE=y +CONFIG_OF_FLATTREE=y +CONFIG_OF_GPIO=y +CONFIG_OF_IRQ=y +CONFIG_OF_KOBJ=y +CONFIG_PADATA=y +CONFIG_PAGE_REPORTING=y +CONFIG_PARTITION_PERCPU=y +CONFIG_PCI=y +# CONFIG_PCIE_HISI_ERR is not set +CONFIG_PCI_DOMAINS=y +CONFIG_PCI_DOMAINS_GENERIC=y +CONFIG_PCI_ECAM=y +CONFIG_PCI_HOST_COMMON=y +CONFIG_PCI_HOST_GENERIC=y +CONFIG_PCI_LABEL=y +CONFIG_PCI_MSI=y +CONFIG_PCI_MSI_IRQ_DOMAIN=y +CONFIG_PGTABLE_LEVELS=3 +CONFIG_PHYS_ADDR_T_64BIT=y +# CONFIG_PMIC_OPREGION is not set +CONFIG_PNP=y +CONFIG_PNPACPI=y +CONFIG_PNP_DEBUG_MESSAGES=y +CONFIG_POWER_RESET=y +CONFIG_POWER_SUPPLY=y +CONFIG_PTP_1588_CLOCK_OPTIONAL=y +CONFIG_QUEUED_RWLOCKS=y +CONFIG_QUEUED_SPINLOCKS=y +CONFIG_RATIONAL=y +# CONFIG_RESET_ATTACK_MITIGATION is not set +CONFIG_RFS_ACCEL=y +CONFIG_RODATA_FULL_DEFAULT_ENABLED=y +CONFIG_RPS=y +CONFIG_RTC_CLASS=y +CONFIG_RTC_DRV_EFI=y +CONFIG_RTC_DRV_PL031=y +CONFIG_RWSEM_SPIN_ON_OWNER=y +CONFIG_SATA_AHCI=y +CONFIG_SATA_AHCI_PLATFORM=y +CONFIG_SATA_HOST=y +CONFIG_SCSI=y +CONFIG_SCSI_COMMON=y +CONFIG_SCSI_VIRTIO=y +CONFIG_SERIAL_8250=y +CONFIG_SERIAL_8250_DEPRECATED_OPTIONS=y +CONFIG_SERIAL_8250_EXTENDED=y +CONFIG_SERIAL_8250_FSL=y +CONFIG_SERIAL_8250_NR_UARTS=4 +CONFIG_SERIAL_8250_PNP=y +CONFIG_SERIAL_8250_RUNTIME_UARTS=4 +CONFIG_SERIAL_8250_SHARE_IRQ=y +CONFIG_SERIAL_AMBA_PL011=y +CONFIG_SERIAL_AMBA_PL011_CONSOLE=y +CONFIG_SERIAL_EARLYCON=y +CONFIG_SERIAL_MCTRL_GPIO=y +CONFIG_SERIAL_OF_PLATFORM=y +CONFIG_SG_POOL=y +CONFIG_SMP=y +CONFIG_SOCK_RX_QUEUE_MAPPING=y +CONFIG_SPARSEMEM=y +CONFIG_SPARSEMEM_EXTREME=y +CONFIG_SPARSEMEM_VMEMMAP=y +CONFIG_SPARSEMEM_VMEMMAP_ENABLE=y +CONFIG_SPARSE_IRQ=y +CONFIG_SRCU=y +# CONFIG_SURFACE_PLATFORMS is not set +CONFIG_SWIOTLB=y +CONFIG_SYSCTL_EXCEPTION_TRACE=y +CONFIG_SYSFB=y +# CONFIG_SYSFB_SIMPLEFB is not set +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_OF=y +CONFIG_THREAD_INFO_IN_TASK=y +CONFIG_TICK_CPU_ACCOUNTING=y +CONFIG_TIMER_ACPI=y +CONFIG_TIMER_OF=y +CONFIG_TIMER_PROBE=y +CONFIG_TREE_RCU=y +CONFIG_TREE_SRCU=y +# CONFIG_UACCE is not set +CONFIG_UCS2_STRING=y +CONFIG_UNMAP_KERNEL_AT_EL0=y +CONFIG_USB=y +CONFIG_USB_HID=y +CONFIG_USB_HIDDEV=y +CONFIG_USB_PCI=y +CONFIG_USB_STORAGE=y +CONFIG_USB_SUPPORT=y +CONFIG_USB_XHCI_HCD=y +CONFIG_USB_XHCI_PCI=y +CONFIG_VIRTIO=y +CONFIG_VIRTIO_BALLOON=y +CONFIG_VIRTIO_BLK=y +CONFIG_VIRTIO_CONSOLE=y +CONFIG_VIRTIO_MMIO=y +CONFIG_VIRTIO_MMIO_CMDLINE_DEVICES=y +CONFIG_VIRTIO_NET=y +CONFIG_VIRTIO_PCI=y +CONFIG_VIRTIO_PCI_LEGACY=y +CONFIG_VIRTIO_PCI_LIB=y +CONFIG_VMAP_STACK=y +CONFIG_VT=y +CONFIG_VT_CONSOLE=y +# CONFIG_VT_HW_CONSOLE_BINDING is not set +CONFIG_WATCHDOG_CORE=y +CONFIG_XPS=y +CONFIG_ZONE_DMA32=y diff --git a/target/linux/armsr/modules.mk b/target/linux/armsr/modules.mk index 7dd3739ffa..d5a5d5c407 100644 --- a/target/linux/armsr/modules.mk +++ b/target/linux/armsr/modules.mk @@ -92,6 +92,7 @@ define KernelPackage/fsl-enetc-net CONFIG_FSL_ENETC_QOS=y FILES:= \ $(LINUX_DIR)/drivers/net/ethernet/freescale/enetc/fsl-enetc.ko \ + $(LINUX_DIR)/drivers/net/ethernet/freescale/enetc/fsl-enetc-core.ko@ge6.3 \ $(LINUX_DIR)/drivers/net/ethernet/freescale/enetc/fsl-enetc-vf.ko \ $(LINUX_DIR)/drivers/net/ethernet/freescale/enetc/fsl-enetc-mdio.ko \ $(LINUX_DIR)/drivers/net/ethernet/freescale/enetc/fsl-enetc-ierb.ko diff --git a/target/linux/armsr/patches-6.6/221-armsr-disable_gc_sections_armv7.patch b/target/linux/armsr/patches-6.6/221-armsr-disable_gc_sections_armv7.patch new file mode 100644 index 0000000000..c9dbdf2521 --- /dev/null +++ b/target/linux/armsr/patches-6.6/221-armsr-disable_gc_sections_armv7.patch @@ -0,0 +1,23 @@ +From b77c0ecdc7915e5c5c515da1aa6cfaf6f4eb8351 Mon Sep 17 00:00:00 2001 +From: Mathew McBride +Date: Wed, 28 Sep 2022 16:39:31 +1000 +Subject: [PATCH] arm: disable code size reduction measures + (gc-sections,-f*-sections) + +This interferes with the EFI boot stub on armv7l. + +Signed-off-by: Mathew McBride +--- + arch/arm/Kconfig | 1 - + 1 file changed, 1 deletion(-) + +--- a/arch/arm/Kconfig ++++ b/arch/arm/Kconfig +@@ -128,7 +128,6 @@ config ARM + select HOTPLUG_CORE_SYNC_DEAD if HOTPLUG_CPU + select IRQ_FORCED_THREADING + select LOCK_MM_AND_FIND_VMA +- select HAVE_LD_DEAD_CODE_DATA_ELIMINATION + select MODULES_USE_ELF_REL + select NEED_DMA_MAP_STATE + select OF_EARLY_FLATTREE if OF diff --git a/target/linux/gemini/Makefile b/target/linux/gemini/Makefile index b7f1962c9a..b2869ff72e 100644 --- a/target/linux/gemini/Makefile +++ b/target/linux/gemini/Makefile @@ -11,7 +11,7 @@ FEATURES:=squashfs pci rtc usb dt gpio display ext4 rootfs-part boot-part CPU_TYPE:=fa526 SUBTARGETS:=generic -KERNEL_PATCHVER:=6.1 +KERNEL_PATCHVER:=6.6 define Target/Description Build firmware images for the StorLink/Cortina Gemini CS351x ARM FA526 CPU diff --git a/target/linux/gemini/config-6.1 b/target/linux/gemini/config-6.6 similarity index 92% rename from target/linux/gemini/config-6.1 rename to target/linux/gemini/config-6.6 index ae0922f5dc..d670279135 100644 --- a/target/linux/gemini/config-6.1 +++ b/target/linux/gemini/config-6.6 @@ -10,10 +10,10 @@ CONFIG_ARCH_MULTI_V4=y # CONFIG_ARCH_MULTI_V4T is not set CONFIG_ARCH_MULTI_V4_V5=y # CONFIG_ARCH_MULTI_V5 is not set -CONFIG_ARCH_NR_GPIO=0 CONFIG_ARCH_OPTIONAL_KERNEL_RWX=y CONFIG_ARCH_SELECT_MEMORY_MODEL=y CONFIG_ARCH_SPARSEMEM_ENABLE=y +CONFIG_ARCH_STACKWALK=y CONFIG_ARM=y CONFIG_ARM_AMBA=y CONFIG_ARM_APPENDED_DTB=y @@ -21,7 +21,6 @@ CONFIG_ARM_APPENDED_DTB=y CONFIG_ARM_HAS_GROUP_RELOCS=y CONFIG_ARM_L1_CACHE_SHIFT=5 CONFIG_ARM_PATCH_PHYS_VIRT=y -# CONFIG_ARM_SMMU is not set CONFIG_ARM_UNWIND=y CONFIG_ATA=y CONFIG_ATAGS=y @@ -33,6 +32,8 @@ CONFIG_BLK_DEV_SD=y CONFIG_BLK_MQ_PCI=y CONFIG_BLK_PM=y CONFIG_BOUNCE=y +CONFIG_BUFFER_HEAD=y +CONFIG_CACHESTAT_SYSCALL=y CONFIG_CC_HAVE_STACKPROTECTOR_TLS=y CONFIG_CC_IMPLICIT_FALLTHROUGH="-Wimplicit-fallthrough=5" CONFIG_CC_NO_ARRAY_BOUNDS=y @@ -84,6 +85,7 @@ CONFIG_CROSS_MEMORY_ATTACH=y CONFIG_CRYPTO_CMAC=y CONFIG_CRYPTO_CRC32C=y CONFIG_CRYPTO_DES=y +# CONFIG_CRYPTO_DEV_JH7110 is not set CONFIG_CRYPTO_DEV_SL3516=y # CONFIG_CRYPTO_DEV_SL3516_DEBUG is not set CONFIG_CRYPTO_DRBG=y @@ -92,14 +94,18 @@ CONFIG_CRYPTO_DRBG_MENU=y CONFIG_CRYPTO_ECB=y CONFIG_CRYPTO_ECHAINIV=y CONFIG_CRYPTO_ENGINE=y +CONFIG_CRYPTO_GENIV=y CONFIG_CRYPTO_HMAC=y CONFIG_CRYPTO_HW=y CONFIG_CRYPTO_JITTERENTROPY=y CONFIG_CRYPTO_LIB_BLAKE2S_GENERIC=y CONFIG_CRYPTO_LIB_DES=y +CONFIG_CRYPTO_LIB_GF128MUL=y CONFIG_CRYPTO_LIB_SHA1=y CONFIG_CRYPTO_LIB_SHA256=y CONFIG_CRYPTO_LIB_UTILS=y +# CONFIG_CRYPTO_MANAGER_DISABLE_TESTS is not set +# CONFIG_CRYPTO_MANAGER_EXTRA_TESTS is not set CONFIG_CRYPTO_MD4=y CONFIG_CRYPTO_MD5=y CONFIG_CRYPTO_RNG=y @@ -107,7 +113,10 @@ CONFIG_CRYPTO_RNG2=y CONFIG_CRYPTO_RNG_DEFAULT=y CONFIG_CRYPTO_SEQIV=y CONFIG_CRYPTO_SHA256=y +CONFIG_CRYPTO_SHA3=y CONFIG_CRYPTO_SHA512=y +CONFIG_CRYPTO_SIG2=y +CONFIG_CRYPTO_USER=y CONFIG_DEBUG_BUGVERBOSE=y CONFIG_DEBUG_INFO=y CONFIG_DEBUG_LL_INCLUDE="mach/debug-macro.S" @@ -133,7 +142,6 @@ CONFIG_DRM_FBDEV_EMULATION=y CONFIG_DRM_FBDEV_OVERALLOC=100 CONFIG_DRM_GEM_DMA_HELPER=y CONFIG_DRM_KMS_HELPER=y -CONFIG_DRM_NOMODESET=y CONFIG_DRM_PANEL=y CONFIG_DRM_PANEL_BRIDGE=y CONFIG_DRM_PANEL_ILITEK_IL9322=y @@ -145,18 +153,17 @@ CONFIG_EDAC_ATOMIC_SCRUB=y CONFIG_EDAC_SUPPORT=y CONFIG_EEPROM_93CX6=y CONFIG_ELF_CORE=y -# CONFIG_EMBEDDED is not set CONFIG_EXCLUSIVE_SYSTEM_RAM=y # CONFIG_EXPERT is not set CONFIG_EXT4_FS=y CONFIG_EXTCON=y CONFIG_FARADAY_FTINTC010=y CONFIG_FB=y -CONFIG_FB_CFB_COPYAREA=y -CONFIG_FB_CFB_FILLRECT=y -CONFIG_FB_CFB_IMAGEBLIT=y -CONFIG_FB_CMDLINE=y +CONFIG_FB_CORE=y CONFIG_FB_DEFERRED_IO=y +CONFIG_FB_DMAMEM_HELPERS=y +CONFIG_FB_SYSMEM_HELPERS=y +CONFIG_FB_SYSMEM_HELPERS_DEFERRED=y CONFIG_FB_SYS_COPYAREA=y CONFIG_FB_SYS_FILLRECT=y CONFIG_FB_SYS_FOPS=y @@ -175,11 +182,13 @@ CONFIG_FS_MBCACHE=y CONFIG_FS_POSIX_ACL=y CONFIG_FTTMR010_TIMER=y CONFIG_FTWDT010_WATCHDOG=y +CONFIG_FUNCTION_ALIGNMENT=0 CONFIG_FWNODE_MDIO=y CONFIG_FW_LOADER_PAGED_BUF=y CONFIG_FW_LOADER_SYSFS=y # CONFIG_FW_LOADER_USER_HELPER_FALLBACK is not set -CONFIG_GCC11_NO_ARRAY_BOUNDS=y +CONFIG_GCC10_NO_ARRAY_BOUNDS=y +CONFIG_GCC_ASM_GOTO_OUTPUT_WORKAROUND=y CONFIG_GEMINI_ETHERNET=y CONFIG_GENERIC_ALLOCATOR=y CONFIG_GENERIC_ATOMIC64=y @@ -207,6 +216,7 @@ CONFIG_GRO_CELLS=y CONFIG_HARDIRQS_SW_RESEND=y CONFIG_HAS_DMA=y CONFIG_HAS_IOMEM=y +CONFIG_HAS_IOPORT=y CONFIG_HAS_IOPORT_MAP=y CONFIG_HDMI=y CONFIG_HIGHMEM=y @@ -224,10 +234,6 @@ CONFIG_I2C_HELPER_AUTO=y CONFIG_INITRAMFS_SOURCE="" CONFIG_INPUT=y CONFIG_INPUT_KEYBOARD=y -# CONFIG_IOMMU_DEBUGFS is not set -# CONFIG_IOMMU_IO_PGTABLE_ARMV7S is not set -# CONFIG_IOMMU_IO_PGTABLE_LPAE is not set -CONFIG_IOMMU_SUPPORT=y CONFIG_IO_URING=y CONFIG_IPC_NS=y CONFIG_IRQCHIP=y @@ -263,10 +269,10 @@ CONFIG_MDIO_BUS=y CONFIG_MDIO_DEVICE=y CONFIG_MDIO_DEVRES=y CONFIG_MDIO_GPIO=y -CONFIG_MEMFD_CREATE=y CONFIG_MEMORY_ISOLATION=y CONFIG_MFD_SYSCON=y CONFIG_MIGRATION=y +CONFIG_MMU_LAZY_TLB_REFCOUNT=y CONFIG_MODULES_USE_ELF_REL=y # CONFIG_MODULE_UNLOAD is not set CONFIG_MQ_IOSCHED_DEADLINE=y @@ -282,6 +288,7 @@ CONFIG_NAMESPACES=y CONFIG_NEED_DMA_MAP_STATE=y CONFIG_NEED_KUSER_HELPERS=y CONFIG_NEED_PER_CPU_KM=y +CONFIG_NEED_SRCU_NMI_SAFE=y CONFIG_NET_DEVLINK=y CONFIG_NET_DSA=y CONFIG_NET_DSA_REALTEK=y @@ -290,13 +297,17 @@ CONFIG_NET_DSA_REALTEK=y CONFIG_NET_DSA_REALTEK_RTL8366RB=y CONFIG_NET_DSA_REALTEK_SMI=y CONFIG_NET_DSA_TAG_RTL4_A=y +CONFIG_NET_EGRESS=y +CONFIG_NET_INGRESS=y CONFIG_NET_NS=y CONFIG_NET_SELFTESTS=y CONFIG_NET_SWITCHDEV=y +CONFIG_NET_XGRESS=y CONFIG_NLS=y CONFIG_NO_HZ_COMMON=y CONFIG_NO_HZ_IDLE=y CONFIG_NVMEM=y +CONFIG_NVMEM_LAYOUTS=y CONFIG_OF=y CONFIG_OF_ADDRESS=y CONFIG_OF_EARLY_FLATTREE=y @@ -311,6 +322,7 @@ CONFIG_PAGE_OFFSET=0xC0000000 CONFIG_PAGE_POOL=y CONFIG_PAGE_SIZE_LESS_THAN_256KB=y CONFIG_PAGE_SIZE_LESS_THAN_64KB=y +CONFIG_PAHOLE_HAS_LANG_EXCLUDE=y # CONFIG_PANIC_ON_OOPS is not set CONFIG_PANIC_ON_OOPS_VALUE=0 CONFIG_PANIC_TIMEOUT=0 @@ -327,6 +339,7 @@ CONFIG_PCI_FTPCI100=y CONFIG_PERF_USE_VMALLOC=y CONFIG_PGTABLE_LEVELS=2 CONFIG_PHYLIB=y +CONFIG_PHYLIB_LEDS=y CONFIG_PHYLINK=y CONFIG_PID_NS=y CONFIG_PINCTRL=y @@ -382,12 +395,14 @@ CONFIG_SERIAL_8250_EXAR=y CONFIG_SERIAL_8250_FSL=y CONFIG_SERIAL_8250_NR_UARTS=1 CONFIG_SERIAL_8250_PCI=y +CONFIG_SERIAL_8250_PCILIB=y CONFIG_SERIAL_8250_RUNTIME_UARTS=1 CONFIG_SERIAL_MCTRL_GPIO=y CONFIG_SERIAL_OF_PLATFORM=y CONFIG_SERIO=y CONFIG_SERIO_LIBPS2=y CONFIG_SERIO_SERPORT=y +CONFIG_SGL_ALLOC=y CONFIG_SG_POOL=y CONFIG_SLUB_DEBUG=y CONFIG_SOFTIRQ_ON_OWN_STACK=y @@ -397,7 +412,7 @@ CONFIG_SPI_BITBANG=y CONFIG_SPI_GPIO=y CONFIG_SPI_MASTER=y CONFIG_SPLIT_PTLOCK_CPUS=999999 -CONFIG_SRCU=y +CONFIG_SQUASHFS_DECOMP_MULTI_PERCPU=y CONFIG_STACKDEPOT=y CONFIG_STACKTRACE=y # CONFIG_STRIP_ASM_SYMS is not set @@ -432,6 +447,8 @@ CONFIG_USE_OF=y CONFIG_UTS_NS=y CONFIG_VGA_ARB=y CONFIG_VGA_ARB_MAX_GPUS=16 +CONFIG_VIDEO_CMDLINE=y +CONFIG_VIDEO_NOMODESET=y CONFIG_VITESSE_PHY=y CONFIG_VM_EVENT_COUNTERS=y CONFIG_VT=y diff --git a/target/linux/gemini/image/Makefile b/target/linux/gemini/image/Makefile index 3fce3172ed..3ddb6e5554 100644 --- a/target/linux/gemini/image/Makefile +++ b/target/linux/gemini/image/Makefile @@ -124,6 +124,7 @@ endef # All DTB files are prefixed with "gemini-" define Device/Default PROFILES := Default + DEVICE_DTS_DIR = $$(DTS_DIR)/gemini KERNEL_DEPENDS = $$(wildcard $(DTS_DIR)/$$(DEVICE_DTS).dts) KERNEL_NAME := zImage KERNEL := kernel-bin | append-dtb diff --git a/target/linux/gemini/patches-6.1/0001-usb-phy-phy-gpio-vbus-usb-Add-device-tree-probing.patch b/target/linux/gemini/patches-6.1/0001-usb-phy-phy-gpio-vbus-usb-Add-device-tree-probing.patch deleted file mode 100644 index 943b166d7e..0000000000 --- a/target/linux/gemini/patches-6.1/0001-usb-phy-phy-gpio-vbus-usb-Add-device-tree-probing.patch +++ /dev/null @@ -1,67 +0,0 @@ -From d5a026cc8306ccd3e99e1455c87e38f8e6fa18df Mon Sep 17 00:00:00 2001 -From: Linus Walleij -Date: Mon, 7 Nov 2022 00:05:06 +0100 -Subject: [PATCH 01/29] usb: phy: phy-gpio-vbus-usb: Add device tree probing - -Make it possible to probe the GPIO VBUS detection driver -from the device tree compatible for GPIO USB B connectors. - -Since this driver is using the "gpio-usb-b-connector" -compatible, it is important to discern it from the role -switch connector driver (which does not provide a phy), -so we add some Kconfig text and depend on !USB_CONN_GPIO. - -Cc: Rob Herring -Cc: Prashant Malani -Cc: Felipe Balbi -Signed-off-by: Linus Walleij -Link: https://lore.kernel.org/r/20221106230506.1646101-1-linus.walleij@linaro.org -Signed-off-by: Greg Kroah-Hartman ---- ---- a/drivers/usb/phy/Kconfig -+++ b/drivers/usb/phy/Kconfig -@@ -93,12 +93,16 @@ config USB_GPIO_VBUS - tristate "GPIO based peripheral-only VBUS sensing 'transceiver'" - depends on GPIOLIB || COMPILE_TEST - depends on USB_GADGET || !USB_GADGET # if USB_GADGET=m, this can't be 'y' -+ depends on !USB_CONN_GPIO - select USB_PHY - help - Provides simple GPIO VBUS sensing for controllers with an - internal transceiver via the usb_phy interface, and - optionally control of a D+ pullup GPIO as well as a VBUS -- current limit regulator. -+ current limit regulator. This driver is for devices that do -+ NOT support role switch. OTG devices that can do role switch -+ (master/peripheral) shall use the USB based connection -+ detection driver USB_CONN_GPIO. - - config OMAP_OTG - tristate "OMAP USB OTG controller driver" ---- a/drivers/usb/phy/phy-gpio-vbus-usb.c -+++ b/drivers/usb/phy/phy-gpio-vbus-usb.c -@@ -366,12 +366,24 @@ static const struct dev_pm_ops gpio_vbus - - MODULE_ALIAS("platform:gpio-vbus"); - -+/* -+ * NOTE: this driver matches against "gpio-usb-b-connector" for -+ * devices that do NOT support role switch. -+ */ -+static const struct of_device_id gpio_vbus_of_match[] = { -+ { -+ .compatible = "gpio-usb-b-connector", -+ }, -+ {}, -+}; -+ - static struct platform_driver gpio_vbus_driver = { - .driver = { - .name = "gpio-vbus", - #ifdef CONFIG_PM - .pm = &gpio_vbus_dev_pm_ops, - #endif -+ .of_match_table = gpio_vbus_of_match, - }, - .probe = gpio_vbus_probe, - .remove = gpio_vbus_remove, diff --git a/target/linux/gemini/patches-6.1/0002-usb-fotg210-Collect-pieces-of-dual-mode-controller.patch b/target/linux/gemini/patches-6.1/0002-usb-fotg210-Collect-pieces-of-dual-mode-controller.patch deleted file mode 100644 index 1ee4f27c46..0000000000 --- a/target/linux/gemini/patches-6.1/0002-usb-fotg210-Collect-pieces-of-dual-mode-controller.patch +++ /dev/null @@ -1,15990 +0,0 @@ -From 30367636930864f71b2bd462adedcf8484313864 Mon Sep 17 00:00:00 2001 -From: Linus Walleij -Date: Sun, 23 Oct 2022 16:47:06 +0200 -Subject: [PATCH 02/29] usb: fotg210: Collect pieces of dual mode controller - -The Faraday FOTG210 is a dual-mode OTG USB controller that can -act as host, peripheral or both. To be able to probe from one -hardware description and to follow the pattern of other dual- -mode controllers such as MUSB or MTU3 we need to collect the -two, currently completely separate drivers in the same -directory. - -After this, users need to select the main symbol USB_FOTG210 -and then each respective subdriver. We pave the road to -compile both drivers into the same kernel and select the -one we want to use at probe() time, and possibly add OTG -support in the end. - -This patch doesn't do much more than create the new symbol -and collect the drivers in one place. We also add a comment -for the section of dual-mode controllers in the Kconfig -file so people can see what these selections are about. - -Also add myself as maintainer as there has been little -response on my patches to these drivers. - -Cc: Fabian Vogt -Cc: Yuan-Hsin Chen -Cc: Felipe Balbi -Signed-off-by: Linus Walleij -Link: https://lore.kernel.org/r/20221023144708.3596563-1-linus.walleij@linaro.org -Signed-off-by: Greg Kroah-Hartman ---- ---- a/drivers/usb/Kconfig -+++ b/drivers/usb/Kconfig -@@ -111,8 +111,12 @@ source "drivers/usb/usbip/Kconfig" - - endif - -+comment "USB dual-mode controller drivers" -+ - source "drivers/usb/cdns3/Kconfig" - -+source "drivers/usb/fotg210/Kconfig" -+ - source "drivers/usb/mtu3/Kconfig" - - source "drivers/usb/musb/Kconfig" ---- a/drivers/usb/Makefile -+++ b/drivers/usb/Makefile -@@ -17,6 +17,8 @@ obj-$(CONFIG_USB_CDNS_SUPPORT) += cdns3/ - obj-$(CONFIG_USB_CDNS3) += cdns3/ - obj-$(CONFIG_USB_CDNSP_PCI) += cdns3/ - -+obj-$(CONFIG_USB_FOTG210) += fotg210/ -+ - obj-$(CONFIG_USB_MON) += mon/ - obj-$(CONFIG_USB_MTU3) += mtu3/ - ---- /dev/null -+++ b/drivers/usb/fotg210/Kconfig -@@ -0,0 +1,36 @@ -+# SPDX-License-Identifier: GPL-2.0 -+ -+config USB_FOTG210 -+ tristate "Faraday FOTG210 USB2 Dual Role controller" -+ depends on USB || USB_GADGET -+ depends on HAS_DMA && HAS_IOMEM -+ default ARCH_GEMINI -+ help -+ Faraday FOTG210 is a dual-mode USB controller that can act -+ in both host controller and peripheral controller mode. -+ -+if USB_FOTG210 -+ -+config USB_FOTG210_HCD -+ tristate "Faraday FOTG210 USB Host Controller support" -+ depends on USB -+ help -+ Faraday FOTG210 is an OTG controller which can be configured as -+ an USB2.0 host. It is designed to meet USB2.0 EHCI specification -+ with minor modification. -+ -+ To compile this driver as a module, choose M here: the -+ module will be called fotg210-hcd. -+ -+config USB_FOTG210_UDC -+ depends on USB_GADGET -+ tristate "Faraday FOTG210 USB Peripheral Controller support" -+ help -+ Faraday USB2.0 OTG controller which can be configured as -+ high speed or full speed USB device. This driver suppports -+ Bulk Transfer so far. -+ -+ Say "y" to link the driver statically, or "m" to build a -+ dynamically linked module called "fotg210-udc". -+ -+endif ---- /dev/null -+++ b/drivers/usb/fotg210/Makefile -@@ -0,0 +1,3 @@ -+# SPDX-License-Identifier: GPL-2.0 -+obj-$(CONFIG_USB_FOTG210_HCD) += fotg210-hcd.o -+obj-$(CONFIG_USB_FOTG210_UDC) += fotg210-udc.o ---- a/drivers/usb/host/fotg210-hcd.c -+++ /dev/null -@@ -1,5724 +0,0 @@ --// SPDX-License-Identifier: GPL-2.0+ --/* Faraday FOTG210 EHCI-like driver -- * -- * Copyright (c) 2013 Faraday Technology Corporation -- * -- * Author: Yuan-Hsin Chen -- * Feng-Hsin Chiang -- * Po-Yu Chuang -- * -- * Most of code borrowed from the Linux-3.7 EHCI driver -- */ --#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 -- --#define DRIVER_AUTHOR "Yuan-Hsin Chen" --#define DRIVER_DESC "FOTG210 Host Controller (EHCI) Driver" --static const char hcd_name[] = "fotg210_hcd"; -- --#undef FOTG210_URB_TRACE --#define FOTG210_STATS -- --/* magic numbers that can affect system performance */ --#define FOTG210_TUNE_CERR 3 /* 0-3 qtd retries; 0 == don't stop */ --#define FOTG210_TUNE_RL_HS 4 /* nak throttle; see 4.9 */ --#define FOTG210_TUNE_RL_TT 0 --#define FOTG210_TUNE_MULT_HS 1 /* 1-3 transactions/uframe; 4.10.3 */ --#define FOTG210_TUNE_MULT_TT 1 -- --/* Some drivers think it's safe to schedule isochronous transfers more than 256 -- * ms into the future (partly as a result of an old bug in the scheduling -- * code). In an attempt to avoid trouble, we will use a minimum scheduling -- * length of 512 frames instead of 256. -- */ --#define FOTG210_TUNE_FLS 1 /* (medium) 512-frame schedule */ -- --/* Initial IRQ latency: faster than hw default */ --static int log2_irq_thresh; /* 0 to 6 */ --module_param(log2_irq_thresh, int, S_IRUGO); --MODULE_PARM_DESC(log2_irq_thresh, "log2 IRQ latency, 1-64 microframes"); -- --/* initial park setting: slower than hw default */ --static unsigned park; --module_param(park, uint, S_IRUGO); --MODULE_PARM_DESC(park, "park setting; 1-3 back-to-back async packets"); -- --/* for link power management(LPM) feature */ --static unsigned int hird; --module_param(hird, int, S_IRUGO); --MODULE_PARM_DESC(hird, "host initiated resume duration, +1 for each 75us"); -- --#define INTR_MASK (STS_IAA | STS_FATAL | STS_PCD | STS_ERR | STS_INT) -- --#include "fotg210.h" -- --#define fotg210_dbg(fotg210, fmt, args...) \ -- dev_dbg(fotg210_to_hcd(fotg210)->self.controller, fmt, ## args) --#define fotg210_err(fotg210, fmt, args...) \ -- dev_err(fotg210_to_hcd(fotg210)->self.controller, fmt, ## args) --#define fotg210_info(fotg210, fmt, args...) \ -- dev_info(fotg210_to_hcd(fotg210)->self.controller, fmt, ## args) --#define fotg210_warn(fotg210, fmt, args...) \ -- dev_warn(fotg210_to_hcd(fotg210)->self.controller, fmt, ## args) -- --/* check the values in the HCSPARAMS register (host controller _Structural_ -- * parameters) see EHCI spec, Table 2-4 for each value -- */ --static void dbg_hcs_params(struct fotg210_hcd *fotg210, char *label) --{ -- u32 params = fotg210_readl(fotg210, &fotg210->caps->hcs_params); -- -- fotg210_dbg(fotg210, "%s hcs_params 0x%x ports=%d\n", label, params, -- HCS_N_PORTS(params)); --} -- --/* check the values in the HCCPARAMS register (host controller _Capability_ -- * parameters) see EHCI Spec, Table 2-5 for each value -- */ --static void dbg_hcc_params(struct fotg210_hcd *fotg210, char *label) --{ -- u32 params = fotg210_readl(fotg210, &fotg210->caps->hcc_params); -- -- fotg210_dbg(fotg210, "%s hcc_params %04x uframes %s%s\n", label, -- params, -- HCC_PGM_FRAMELISTLEN(params) ? "256/512/1024" : "1024", -- HCC_CANPARK(params) ? " park" : ""); --} -- --static void __maybe_unused --dbg_qtd(const char *label, struct fotg210_hcd *fotg210, struct fotg210_qtd *qtd) --{ -- fotg210_dbg(fotg210, "%s td %p n%08x %08x t%08x p0=%08x\n", label, qtd, -- hc32_to_cpup(fotg210, &qtd->hw_next), -- hc32_to_cpup(fotg210, &qtd->hw_alt_next), -- hc32_to_cpup(fotg210, &qtd->hw_token), -- hc32_to_cpup(fotg210, &qtd->hw_buf[0])); -- if (qtd->hw_buf[1]) -- fotg210_dbg(fotg210, " p1=%08x p2=%08x p3=%08x p4=%08x\n", -- hc32_to_cpup(fotg210, &qtd->hw_buf[1]), -- hc32_to_cpup(fotg210, &qtd->hw_buf[2]), -- hc32_to_cpup(fotg210, &qtd->hw_buf[3]), -- hc32_to_cpup(fotg210, &qtd->hw_buf[4])); --} -- --static void __maybe_unused --dbg_qh(const char *label, struct fotg210_hcd *fotg210, struct fotg210_qh *qh) --{ -- struct fotg210_qh_hw *hw = qh->hw; -- -- fotg210_dbg(fotg210, "%s qh %p n%08x info %x %x qtd %x\n", label, qh, -- hw->hw_next, hw->hw_info1, hw->hw_info2, -- hw->hw_current); -- -- dbg_qtd("overlay", fotg210, (struct fotg210_qtd *) &hw->hw_qtd_next); --} -- --static void __maybe_unused --dbg_itd(const char *label, struct fotg210_hcd *fotg210, struct fotg210_itd *itd) --{ -- fotg210_dbg(fotg210, "%s[%d] itd %p, next %08x, urb %p\n", label, -- itd->frame, itd, hc32_to_cpu(fotg210, itd->hw_next), -- itd->urb); -- -- fotg210_dbg(fotg210, -- " trans: %08x %08x %08x %08x %08x %08x %08x %08x\n", -- hc32_to_cpu(fotg210, itd->hw_transaction[0]), -- hc32_to_cpu(fotg210, itd->hw_transaction[1]), -- hc32_to_cpu(fotg210, itd->hw_transaction[2]), -- hc32_to_cpu(fotg210, itd->hw_transaction[3]), -- hc32_to_cpu(fotg210, itd->hw_transaction[4]), -- hc32_to_cpu(fotg210, itd->hw_transaction[5]), -- hc32_to_cpu(fotg210, itd->hw_transaction[6]), -- hc32_to_cpu(fotg210, itd->hw_transaction[7])); -- -- fotg210_dbg(fotg210, -- " buf: %08x %08x %08x %08x %08x %08x %08x\n", -- hc32_to_cpu(fotg210, itd->hw_bufp[0]), -- hc32_to_cpu(fotg210, itd->hw_bufp[1]), -- hc32_to_cpu(fotg210, itd->hw_bufp[2]), -- hc32_to_cpu(fotg210, itd->hw_bufp[3]), -- hc32_to_cpu(fotg210, itd->hw_bufp[4]), -- hc32_to_cpu(fotg210, itd->hw_bufp[5]), -- hc32_to_cpu(fotg210, itd->hw_bufp[6])); -- -- fotg210_dbg(fotg210, " index: %d %d %d %d %d %d %d %d\n", -- itd->index[0], itd->index[1], itd->index[2], -- itd->index[3], itd->index[4], itd->index[5], -- itd->index[6], itd->index[7]); --} -- --static int __maybe_unused --dbg_status_buf(char *buf, unsigned len, const char *label, u32 status) --{ -- return scnprintf(buf, len, "%s%sstatus %04x%s%s%s%s%s%s%s%s%s%s", -- label, label[0] ? " " : "", status, -- (status & STS_ASS) ? " Async" : "", -- (status & STS_PSS) ? " Periodic" : "", -- (status & STS_RECL) ? " Recl" : "", -- (status & STS_HALT) ? " Halt" : "", -- (status & STS_IAA) ? " IAA" : "", -- (status & STS_FATAL) ? " FATAL" : "", -- (status & STS_FLR) ? " FLR" : "", -- (status & STS_PCD) ? " PCD" : "", -- (status & STS_ERR) ? " ERR" : "", -- (status & STS_INT) ? " INT" : ""); --} -- --static int __maybe_unused --dbg_intr_buf(char *buf, unsigned len, const char *label, u32 enable) --{ -- return scnprintf(buf, len, "%s%sintrenable %02x%s%s%s%s%s%s", -- label, label[0] ? " " : "", enable, -- (enable & STS_IAA) ? " IAA" : "", -- (enable & STS_FATAL) ? " FATAL" : "", -- (enable & STS_FLR) ? " FLR" : "", -- (enable & STS_PCD) ? " PCD" : "", -- (enable & STS_ERR) ? " ERR" : "", -- (enable & STS_INT) ? " INT" : ""); --} -- --static const char *const fls_strings[] = { "1024", "512", "256", "??" }; -- --static int dbg_command_buf(char *buf, unsigned len, const char *label, -- u32 command) --{ -- return scnprintf(buf, len, -- "%s%scommand %07x %s=%d ithresh=%d%s%s%s period=%s%s %s", -- label, label[0] ? " " : "", command, -- (command & CMD_PARK) ? " park" : "(park)", -- CMD_PARK_CNT(command), -- (command >> 16) & 0x3f, -- (command & CMD_IAAD) ? " IAAD" : "", -- (command & CMD_ASE) ? " Async" : "", -- (command & CMD_PSE) ? " Periodic" : "", -- fls_strings[(command >> 2) & 0x3], -- (command & CMD_RESET) ? " Reset" : "", -- (command & CMD_RUN) ? "RUN" : "HALT"); --} -- --static char *dbg_port_buf(char *buf, unsigned len, const char *label, int port, -- u32 status) --{ -- char *sig; -- -- /* signaling state */ -- switch (status & (3 << 10)) { -- case 0 << 10: -- sig = "se0"; -- break; -- case 1 << 10: -- sig = "k"; -- break; /* low speed */ -- case 2 << 10: -- sig = "j"; -- break; -- default: -- sig = "?"; -- break; -- } -- -- scnprintf(buf, len, "%s%sport:%d status %06x %d sig=%s%s%s%s%s%s%s%s", -- label, label[0] ? " " : "", port, status, -- status >> 25, /*device address */ -- sig, -- (status & PORT_RESET) ? " RESET" : "", -- (status & PORT_SUSPEND) ? " SUSPEND" : "", -- (status & PORT_RESUME) ? " RESUME" : "", -- (status & PORT_PEC) ? " PEC" : "", -- (status & PORT_PE) ? " PE" : "", -- (status & PORT_CSC) ? " CSC" : "", -- (status & PORT_CONNECT) ? " CONNECT" : ""); -- -- return buf; --} -- --/* functions have the "wrong" filename when they're output... */ --#define dbg_status(fotg210, label, status) { \ -- char _buf[80]; \ -- dbg_status_buf(_buf, sizeof(_buf), label, status); \ -- fotg210_dbg(fotg210, "%s\n", _buf); \ --} -- --#define dbg_cmd(fotg210, label, command) { \ -- char _buf[80]; \ -- dbg_command_buf(_buf, sizeof(_buf), label, command); \ -- fotg210_dbg(fotg210, "%s\n", _buf); \ --} -- --#define dbg_port(fotg210, label, port, status) { \ -- char _buf[80]; \ -- fotg210_dbg(fotg210, "%s\n", \ -- dbg_port_buf(_buf, sizeof(_buf), label, port, status));\ --} -- --/* troubleshooting help: expose state in debugfs */ --static int debug_async_open(struct inode *, struct file *); --static int debug_periodic_open(struct inode *, struct file *); --static int debug_registers_open(struct inode *, struct file *); --static int debug_async_open(struct inode *, struct file *); -- --static ssize_t debug_output(struct file*, char __user*, size_t, loff_t*); --static int debug_close(struct inode *, struct file *); -- --static const struct file_operations debug_async_fops = { -- .owner = THIS_MODULE, -- .open = debug_async_open, -- .read = debug_output, -- .release = debug_close, -- .llseek = default_llseek, --}; --static const struct file_operations debug_periodic_fops = { -- .owner = THIS_MODULE, -- .open = debug_periodic_open, -- .read = debug_output, -- .release = debug_close, -- .llseek = default_llseek, --}; --static const struct file_operations debug_registers_fops = { -- .owner = THIS_MODULE, -- .open = debug_registers_open, -- .read = debug_output, -- .release = debug_close, -- .llseek = default_llseek, --}; -- --static struct dentry *fotg210_debug_root; -- --struct debug_buffer { -- ssize_t (*fill_func)(struct debug_buffer *); /* fill method */ -- struct usb_bus *bus; -- struct mutex mutex; /* protect filling of buffer */ -- size_t count; /* number of characters filled into buffer */ -- char *output_buf; -- size_t alloc_size; --}; -- --static inline char speed_char(u32 scratch) --{ -- switch (scratch & (3 << 12)) { -- case QH_FULL_SPEED: -- return 'f'; -- -- case QH_LOW_SPEED: -- return 'l'; -- -- case QH_HIGH_SPEED: -- return 'h'; -- -- default: -- return '?'; -- } --} -- --static inline char token_mark(struct fotg210_hcd *fotg210, __hc32 token) --{ -- __u32 v = hc32_to_cpu(fotg210, token); -- -- if (v & QTD_STS_ACTIVE) -- return '*'; -- if (v & QTD_STS_HALT) -- return '-'; -- if (!IS_SHORT_READ(v)) -- return ' '; -- /* tries to advance through hw_alt_next */ -- return '/'; --} -- --static void qh_lines(struct fotg210_hcd *fotg210, struct fotg210_qh *qh, -- char **nextp, unsigned *sizep) --{ -- u32 scratch; -- u32 hw_curr; -- struct fotg210_qtd *td; -- unsigned temp; -- unsigned size = *sizep; -- char *next = *nextp; -- char mark; -- __le32 list_end = FOTG210_LIST_END(fotg210); -- struct fotg210_qh_hw *hw = qh->hw; -- -- if (hw->hw_qtd_next == list_end) /* NEC does this */ -- mark = '@'; -- else -- mark = token_mark(fotg210, hw->hw_token); -- if (mark == '/') { /* qh_alt_next controls qh advance? */ -- if ((hw->hw_alt_next & QTD_MASK(fotg210)) == -- fotg210->async->hw->hw_alt_next) -- mark = '#'; /* blocked */ -- else if (hw->hw_alt_next == list_end) -- mark = '.'; /* use hw_qtd_next */ -- /* else alt_next points to some other qtd */ -- } -- scratch = hc32_to_cpup(fotg210, &hw->hw_info1); -- hw_curr = (mark == '*') ? hc32_to_cpup(fotg210, &hw->hw_current) : 0; -- temp = scnprintf(next, size, -- "qh/%p dev%d %cs ep%d %08x %08x(%08x%c %s nak%d)", -- qh, scratch & 0x007f, -- speed_char(scratch), -- (scratch >> 8) & 0x000f, -- scratch, hc32_to_cpup(fotg210, &hw->hw_info2), -- hc32_to_cpup(fotg210, &hw->hw_token), mark, -- (cpu_to_hc32(fotg210, QTD_TOGGLE) & hw->hw_token) -- ? "data1" : "data0", -- (hc32_to_cpup(fotg210, &hw->hw_alt_next) >> 1) & 0x0f); -- size -= temp; -- next += temp; -- -- /* hc may be modifying the list as we read it ... */ -- list_for_each_entry(td, &qh->qtd_list, qtd_list) { -- scratch = hc32_to_cpup(fotg210, &td->hw_token); -- mark = ' '; -- if (hw_curr == td->qtd_dma) -- mark = '*'; -- else if (hw->hw_qtd_next == cpu_to_hc32(fotg210, td->qtd_dma)) -- mark = '+'; -- else if (QTD_LENGTH(scratch)) { -- if (td->hw_alt_next == fotg210->async->hw->hw_alt_next) -- mark = '#'; -- else if (td->hw_alt_next != list_end) -- mark = '/'; -- } -- temp = snprintf(next, size, -- "\n\t%p%c%s len=%d %08x urb %p", -- td, mark, ({ char *tmp; -- switch ((scratch>>8)&0x03) { -- case 0: -- tmp = "out"; -- break; -- case 1: -- tmp = "in"; -- break; -- case 2: -- tmp = "setup"; -- break; -- default: -- tmp = "?"; -- break; -- } tmp; }), -- (scratch >> 16) & 0x7fff, -- scratch, -- td->urb); -- if (size < temp) -- temp = size; -- size -= temp; -- next += temp; -- } -- -- temp = snprintf(next, size, "\n"); -- if (size < temp) -- temp = size; -- -- size -= temp; -- next += temp; -- -- *sizep = size; -- *nextp = next; --} -- --static ssize_t fill_async_buffer(struct debug_buffer *buf) --{ -- struct usb_hcd *hcd; -- struct fotg210_hcd *fotg210; -- unsigned long flags; -- unsigned temp, size; -- char *next; -- struct fotg210_qh *qh; -- -- hcd = bus_to_hcd(buf->bus); -- fotg210 = hcd_to_fotg210(hcd); -- next = buf->output_buf; -- size = buf->alloc_size; -- -- *next = 0; -- -- /* dumps a snapshot of the async schedule. -- * usually empty except for long-term bulk reads, or head. -- * one QH per line, and TDs we know about -- */ -- spin_lock_irqsave(&fotg210->lock, flags); -- for (qh = fotg210->async->qh_next.qh; size > 0 && qh; -- qh = qh->qh_next.qh) -- qh_lines(fotg210, qh, &next, &size); -- if (fotg210->async_unlink && size > 0) { -- temp = scnprintf(next, size, "\nunlink =\n"); -- size -= temp; -- next += temp; -- -- for (qh = fotg210->async_unlink; size > 0 && qh; -- qh = qh->unlink_next) -- qh_lines(fotg210, qh, &next, &size); -- } -- spin_unlock_irqrestore(&fotg210->lock, flags); -- -- return strlen(buf->output_buf); --} -- --/* count tds, get ep direction */ --static unsigned output_buf_tds_dir(char *buf, struct fotg210_hcd *fotg210, -- struct fotg210_qh_hw *hw, struct fotg210_qh *qh, unsigned size) --{ -- u32 scratch = hc32_to_cpup(fotg210, &hw->hw_info1); -- struct fotg210_qtd *qtd; -- char *type = ""; -- unsigned temp = 0; -- -- /* count tds, get ep direction */ -- list_for_each_entry(qtd, &qh->qtd_list, qtd_list) { -- temp++; -- switch ((hc32_to_cpu(fotg210, qtd->hw_token) >> 8) & 0x03) { -- case 0: -- type = "out"; -- continue; -- case 1: -- type = "in"; -- continue; -- } -- } -- -- return scnprintf(buf, size, "(%c%d ep%d%s [%d/%d] q%d p%d)", -- speed_char(scratch), scratch & 0x007f, -- (scratch >> 8) & 0x000f, type, qh->usecs, -- qh->c_usecs, temp, (scratch >> 16) & 0x7ff); --} -- --#define DBG_SCHED_LIMIT 64 --static ssize_t fill_periodic_buffer(struct debug_buffer *buf) --{ -- struct usb_hcd *hcd; -- struct fotg210_hcd *fotg210; -- unsigned long flags; -- union fotg210_shadow p, *seen; -- unsigned temp, size, seen_count; -- char *next; -- unsigned i; -- __hc32 tag; -- -- seen = kmalloc_array(DBG_SCHED_LIMIT, sizeof(*seen), GFP_ATOMIC); -- if (!seen) -- return 0; -- -- seen_count = 0; -- -- hcd = bus_to_hcd(buf->bus); -- fotg210 = hcd_to_fotg210(hcd); -- next = buf->output_buf; -- size = buf->alloc_size; -- -- temp = scnprintf(next, size, "size = %d\n", fotg210->periodic_size); -- size -= temp; -- next += temp; -- -- /* dump a snapshot of the periodic schedule. -- * iso changes, interrupt usually doesn't. -- */ -- spin_lock_irqsave(&fotg210->lock, flags); -- for (i = 0; i < fotg210->periodic_size; i++) { -- p = fotg210->pshadow[i]; -- if (likely(!p.ptr)) -- continue; -- -- tag = Q_NEXT_TYPE(fotg210, fotg210->periodic[i]); -- -- temp = scnprintf(next, size, "%4d: ", i); -- size -= temp; -- next += temp; -- -- do { -- struct fotg210_qh_hw *hw; -- -- switch (hc32_to_cpu(fotg210, tag)) { -- case Q_TYPE_QH: -- hw = p.qh->hw; -- temp = scnprintf(next, size, " qh%d-%04x/%p", -- p.qh->period, -- hc32_to_cpup(fotg210, -- &hw->hw_info2) -- /* uframe masks */ -- & (QH_CMASK | QH_SMASK), -- p.qh); -- size -= temp; -- next += temp; -- /* don't repeat what follows this qh */ -- for (temp = 0; temp < seen_count; temp++) { -- if (seen[temp].ptr != p.ptr) -- continue; -- if (p.qh->qh_next.ptr) { -- temp = scnprintf(next, size, -- " ..."); -- size -= temp; -- next += temp; -- } -- break; -- } -- /* show more info the first time around */ -- if (temp == seen_count) { -- temp = output_buf_tds_dir(next, -- fotg210, hw, -- p.qh, size); -- -- if (seen_count < DBG_SCHED_LIMIT) -- seen[seen_count++].qh = p.qh; -- } else -- temp = 0; -- tag = Q_NEXT_TYPE(fotg210, hw->hw_next); -- p = p.qh->qh_next; -- break; -- case Q_TYPE_FSTN: -- temp = scnprintf(next, size, -- " fstn-%8x/%p", -- p.fstn->hw_prev, p.fstn); -- tag = Q_NEXT_TYPE(fotg210, p.fstn->hw_next); -- p = p.fstn->fstn_next; -- break; -- case Q_TYPE_ITD: -- temp = scnprintf(next, size, -- " itd/%p", p.itd); -- tag = Q_NEXT_TYPE(fotg210, p.itd->hw_next); -- p = p.itd->itd_next; -- break; -- } -- size -= temp; -- next += temp; -- } while (p.ptr); -- -- temp = scnprintf(next, size, "\n"); -- size -= temp; -- next += temp; -- } -- spin_unlock_irqrestore(&fotg210->lock, flags); -- kfree(seen); -- -- return buf->alloc_size - size; --} --#undef DBG_SCHED_LIMIT -- --static const char *rh_state_string(struct fotg210_hcd *fotg210) --{ -- switch (fotg210->rh_state) { -- case FOTG210_RH_HALTED: -- return "halted"; -- case FOTG210_RH_SUSPENDED: -- return "suspended"; -- case FOTG210_RH_RUNNING: -- return "running"; -- case FOTG210_RH_STOPPING: -- return "stopping"; -- } -- return "?"; --} -- --static ssize_t fill_registers_buffer(struct debug_buffer *buf) --{ -- struct usb_hcd *hcd; -- struct fotg210_hcd *fotg210; -- unsigned long flags; -- unsigned temp, size, i; -- char *next, scratch[80]; -- static const char fmt[] = "%*s\n"; -- static const char label[] = ""; -- -- hcd = bus_to_hcd(buf->bus); -- fotg210 = hcd_to_fotg210(hcd); -- next = buf->output_buf; -- size = buf->alloc_size; -- -- spin_lock_irqsave(&fotg210->lock, flags); -- -- if (!HCD_HW_ACCESSIBLE(hcd)) { -- size = scnprintf(next, size, -- "bus %s, device %s\n" -- "%s\n" -- "SUSPENDED(no register access)\n", -- hcd->self.controller->bus->name, -- dev_name(hcd->self.controller), -- hcd->product_desc); -- goto done; -- } -- -- /* Capability Registers */ -- i = HC_VERSION(fotg210, fotg210_readl(fotg210, -- &fotg210->caps->hc_capbase)); -- temp = scnprintf(next, size, -- "bus %s, device %s\n" -- "%s\n" -- "EHCI %x.%02x, rh state %s\n", -- hcd->self.controller->bus->name, -- dev_name(hcd->self.controller), -- hcd->product_desc, -- i >> 8, i & 0x0ff, rh_state_string(fotg210)); -- size -= temp; -- next += temp; -- -- /* FIXME interpret both types of params */ -- i = fotg210_readl(fotg210, &fotg210->caps->hcs_params); -- temp = scnprintf(next, size, "structural params 0x%08x\n", i); -- size -= temp; -- next += temp; -- -- i = fotg210_readl(fotg210, &fotg210->caps->hcc_params); -- temp = scnprintf(next, size, "capability params 0x%08x\n", i); -- size -= temp; -- next += temp; -- -- /* Operational Registers */ -- temp = dbg_status_buf(scratch, sizeof(scratch), label, -- fotg210_readl(fotg210, &fotg210->regs->status)); -- temp = scnprintf(next, size, fmt, temp, scratch); -- size -= temp; -- next += temp; -- -- temp = dbg_command_buf(scratch, sizeof(scratch), label, -- fotg210_readl(fotg210, &fotg210->regs->command)); -- temp = scnprintf(next, size, fmt, temp, scratch); -- size -= temp; -- next += temp; -- -- temp = dbg_intr_buf(scratch, sizeof(scratch), label, -- fotg210_readl(fotg210, &fotg210->regs->intr_enable)); -- temp = scnprintf(next, size, fmt, temp, scratch); -- size -= temp; -- next += temp; -- -- temp = scnprintf(next, size, "uframe %04x\n", -- fotg210_read_frame_index(fotg210)); -- size -= temp; -- next += temp; -- -- if (fotg210->async_unlink) { -- temp = scnprintf(next, size, "async unlink qh %p\n", -- fotg210->async_unlink); -- size -= temp; -- next += temp; -- } -- --#ifdef FOTG210_STATS -- temp = scnprintf(next, size, -- "irq normal %ld err %ld iaa %ld(lost %ld)\n", -- fotg210->stats.normal, fotg210->stats.error, -- fotg210->stats.iaa, fotg210->stats.lost_iaa); -- size -= temp; -- next += temp; -- -- temp = scnprintf(next, size, "complete %ld unlink %ld\n", -- fotg210->stats.complete, fotg210->stats.unlink); -- size -= temp; -- next += temp; --#endif -- --done: -- spin_unlock_irqrestore(&fotg210->lock, flags); -- -- return buf->alloc_size - size; --} -- --static struct debug_buffer --*alloc_buffer(struct usb_bus *bus, ssize_t (*fill_func)(struct debug_buffer *)) --{ -- struct debug_buffer *buf; -- -- buf = kzalloc(sizeof(struct debug_buffer), GFP_KERNEL); -- -- if (buf) { -- buf->bus = bus; -- buf->fill_func = fill_func; -- mutex_init(&buf->mutex); -- buf->alloc_size = PAGE_SIZE; -- } -- -- return buf; --} -- --static int fill_buffer(struct debug_buffer *buf) --{ -- int ret = 0; -- -- if (!buf->output_buf) -- buf->output_buf = vmalloc(buf->alloc_size); -- -- if (!buf->output_buf) { -- ret = -ENOMEM; -- goto out; -- } -- -- ret = buf->fill_func(buf); -- -- if (ret >= 0) { -- buf->count = ret; -- ret = 0; -- } -- --out: -- return ret; --} -- --static ssize_t debug_output(struct file *file, char __user *user_buf, -- size_t len, loff_t *offset) --{ -- struct debug_buffer *buf = file->private_data; -- int ret = 0; -- -- mutex_lock(&buf->mutex); -- if (buf->count == 0) { -- ret = fill_buffer(buf); -- if (ret != 0) { -- mutex_unlock(&buf->mutex); -- goto out; -- } -- } -- mutex_unlock(&buf->mutex); -- -- ret = simple_read_from_buffer(user_buf, len, offset, -- buf->output_buf, buf->count); -- --out: -- return ret; -- --} -- --static int debug_close(struct inode *inode, struct file *file) --{ -- struct debug_buffer *buf = file->private_data; -- -- if (buf) { -- vfree(buf->output_buf); -- kfree(buf); -- } -- -- return 0; --} --static int debug_async_open(struct inode *inode, struct file *file) --{ -- file->private_data = alloc_buffer(inode->i_private, fill_async_buffer); -- -- return file->private_data ? 0 : -ENOMEM; --} -- --static int debug_periodic_open(struct inode *inode, struct file *file) --{ -- struct debug_buffer *buf; -- -- buf = alloc_buffer(inode->i_private, fill_periodic_buffer); -- if (!buf) -- return -ENOMEM; -- -- buf->alloc_size = (sizeof(void *) == 4 ? 6 : 8)*PAGE_SIZE; -- file->private_data = buf; -- return 0; --} -- --static int debug_registers_open(struct inode *inode, struct file *file) --{ -- file->private_data = alloc_buffer(inode->i_private, -- fill_registers_buffer); -- -- return file->private_data ? 0 : -ENOMEM; --} -- --static inline void create_debug_files(struct fotg210_hcd *fotg210) --{ -- struct usb_bus *bus = &fotg210_to_hcd(fotg210)->self; -- struct dentry *root; -- -- root = debugfs_create_dir(bus->bus_name, fotg210_debug_root); -- -- debugfs_create_file("async", S_IRUGO, root, bus, &debug_async_fops); -- debugfs_create_file("periodic", S_IRUGO, root, bus, -- &debug_periodic_fops); -- debugfs_create_file("registers", S_IRUGO, root, bus, -- &debug_registers_fops); --} -- --static inline void remove_debug_files(struct fotg210_hcd *fotg210) --{ -- struct usb_bus *bus = &fotg210_to_hcd(fotg210)->self; -- -- debugfs_lookup_and_remove(bus->bus_name, fotg210_debug_root); --} -- --/* handshake - spin reading hc until handshake completes or fails -- * @ptr: address of hc register to be read -- * @mask: bits to look at in result of read -- * @done: value of those bits when handshake succeeds -- * @usec: timeout in microseconds -- * -- * Returns negative errno, or zero on success -- * -- * Success happens when the "mask" bits have the specified value (hardware -- * handshake done). There are two failure modes: "usec" have passed (major -- * hardware flakeout), or the register reads as all-ones (hardware removed). -- * -- * That last failure should_only happen in cases like physical cardbus eject -- * before driver shutdown. But it also seems to be caused by bugs in cardbus -- * bridge shutdown: shutting down the bridge before the devices using it. -- */ --static int handshake(struct fotg210_hcd *fotg210, void __iomem *ptr, -- u32 mask, u32 done, int usec) --{ -- u32 result; -- int ret; -- -- ret = readl_poll_timeout_atomic(ptr, result, -- ((result & mask) == done || -- result == U32_MAX), 1, usec); -- if (result == U32_MAX) /* card removed */ -- return -ENODEV; -- -- return ret; --} -- --/* Force HC to halt state from unknown (EHCI spec section 2.3). -- * Must be called with interrupts enabled and the lock not held. -- */ --static int fotg210_halt(struct fotg210_hcd *fotg210) --{ -- u32 temp; -- -- spin_lock_irq(&fotg210->lock); -- -- /* disable any irqs left enabled by previous code */ -- fotg210_writel(fotg210, 0, &fotg210->regs->intr_enable); -- -- /* -- * This routine gets called during probe before fotg210->command -- * has been initialized, so we can't rely on its value. -- */ -- fotg210->command &= ~CMD_RUN; -- temp = fotg210_readl(fotg210, &fotg210->regs->command); -- temp &= ~(CMD_RUN | CMD_IAAD); -- fotg210_writel(fotg210, temp, &fotg210->regs->command); -- -- spin_unlock_irq(&fotg210->lock); -- synchronize_irq(fotg210_to_hcd(fotg210)->irq); -- -- return handshake(fotg210, &fotg210->regs->status, -- STS_HALT, STS_HALT, 16 * 125); --} -- --/* Reset a non-running (STS_HALT == 1) controller. -- * Must be called with interrupts enabled and the lock not held. -- */ --static int fotg210_reset(struct fotg210_hcd *fotg210) --{ -- int retval; -- u32 command = fotg210_readl(fotg210, &fotg210->regs->command); -- -- /* If the EHCI debug controller is active, special care must be -- * taken before and after a host controller reset -- */ -- if (fotg210->debug && !dbgp_reset_prep(fotg210_to_hcd(fotg210))) -- fotg210->debug = NULL; -- -- command |= CMD_RESET; -- dbg_cmd(fotg210, "reset", command); -- fotg210_writel(fotg210, command, &fotg210->regs->command); -- fotg210->rh_state = FOTG210_RH_HALTED; -- fotg210->next_statechange = jiffies; -- retval = handshake(fotg210, &fotg210->regs->command, -- CMD_RESET, 0, 250 * 1000); -- -- if (retval) -- return retval; -- -- if (fotg210->debug) -- dbgp_external_startup(fotg210_to_hcd(fotg210)); -- -- fotg210->port_c_suspend = fotg210->suspended_ports = -- fotg210->resuming_ports = 0; -- return retval; --} -- --/* Idle the controller (turn off the schedules). -- * Must be called with interrupts enabled and the lock not held. -- */ --static void fotg210_quiesce(struct fotg210_hcd *fotg210) --{ -- u32 temp; -- -- if (fotg210->rh_state != FOTG210_RH_RUNNING) -- return; -- -- /* wait for any schedule enables/disables to take effect */ -- temp = (fotg210->command << 10) & (STS_ASS | STS_PSS); -- handshake(fotg210, &fotg210->regs->status, STS_ASS | STS_PSS, temp, -- 16 * 125); -- -- /* then disable anything that's still active */ -- spin_lock_irq(&fotg210->lock); -- fotg210->command &= ~(CMD_ASE | CMD_PSE); -- fotg210_writel(fotg210, fotg210->command, &fotg210->regs->command); -- spin_unlock_irq(&fotg210->lock); -- -- /* hardware can take 16 microframes to turn off ... */ -- handshake(fotg210, &fotg210->regs->status, STS_ASS | STS_PSS, 0, -- 16 * 125); --} -- --static void end_unlink_async(struct fotg210_hcd *fotg210); --static void unlink_empty_async(struct fotg210_hcd *fotg210); --static void fotg210_work(struct fotg210_hcd *fotg210); --static void start_unlink_intr(struct fotg210_hcd *fotg210, -- struct fotg210_qh *qh); --static void end_unlink_intr(struct fotg210_hcd *fotg210, struct fotg210_qh *qh); -- --/* Set a bit in the USBCMD register */ --static void fotg210_set_command_bit(struct fotg210_hcd *fotg210, u32 bit) --{ -- fotg210->command |= bit; -- fotg210_writel(fotg210, fotg210->command, &fotg210->regs->command); -- -- /* unblock posted write */ -- fotg210_readl(fotg210, &fotg210->regs->command); --} -- --/* Clear a bit in the USBCMD register */ --static void fotg210_clear_command_bit(struct fotg210_hcd *fotg210, u32 bit) --{ -- fotg210->command &= ~bit; -- fotg210_writel(fotg210, fotg210->command, &fotg210->regs->command); -- -- /* unblock posted write */ -- fotg210_readl(fotg210, &fotg210->regs->command); --} -- --/* EHCI timer support... Now using hrtimers. -- * -- * Lots of different events are triggered from fotg210->hrtimer. Whenever -- * the timer routine runs, it checks each possible event; events that are -- * currently enabled and whose expiration time has passed get handled. -- * The set of enabled events is stored as a collection of bitflags in -- * fotg210->enabled_hrtimer_events, and they are numbered in order of -- * increasing delay values (ranging between 1 ms and 100 ms). -- * -- * Rather than implementing a sorted list or tree of all pending events, -- * we keep track only of the lowest-numbered pending event, in -- * fotg210->next_hrtimer_event. Whenever fotg210->hrtimer gets restarted, its -- * expiration time is set to the timeout value for this event. -- * -- * As a result, events might not get handled right away; the actual delay -- * could be anywhere up to twice the requested delay. This doesn't -- * matter, because none of the events are especially time-critical. The -- * ones that matter most all have a delay of 1 ms, so they will be -- * handled after 2 ms at most, which is okay. In addition to this, we -- * allow for an expiration range of 1 ms. -- */ -- --/* Delay lengths for the hrtimer event types. -- * Keep this list sorted by delay length, in the same order as -- * the event types indexed by enum fotg210_hrtimer_event in fotg210.h. -- */ --static unsigned event_delays_ns[] = { -- 1 * NSEC_PER_MSEC, /* FOTG210_HRTIMER_POLL_ASS */ -- 1 * NSEC_PER_MSEC, /* FOTG210_HRTIMER_POLL_PSS */ -- 1 * NSEC_PER_MSEC, /* FOTG210_HRTIMER_POLL_DEAD */ -- 1125 * NSEC_PER_USEC, /* FOTG210_HRTIMER_UNLINK_INTR */ -- 2 * NSEC_PER_MSEC, /* FOTG210_HRTIMER_FREE_ITDS */ -- 6 * NSEC_PER_MSEC, /* FOTG210_HRTIMER_ASYNC_UNLINKS */ -- 10 * NSEC_PER_MSEC, /* FOTG210_HRTIMER_IAA_WATCHDOG */ -- 10 * NSEC_PER_MSEC, /* FOTG210_HRTIMER_DISABLE_PERIODIC */ -- 15 * NSEC_PER_MSEC, /* FOTG210_HRTIMER_DISABLE_ASYNC */ -- 100 * NSEC_PER_MSEC, /* FOTG210_HRTIMER_IO_WATCHDOG */ --}; -- --/* Enable a pending hrtimer event */ --static void fotg210_enable_event(struct fotg210_hcd *fotg210, unsigned event, -- bool resched) --{ -- ktime_t *timeout = &fotg210->hr_timeouts[event]; -- -- if (resched) -- *timeout = ktime_add(ktime_get(), event_delays_ns[event]); -- fotg210->enabled_hrtimer_events |= (1 << event); -- -- /* Track only the lowest-numbered pending event */ -- if (event < fotg210->next_hrtimer_event) { -- fotg210->next_hrtimer_event = event; -- hrtimer_start_range_ns(&fotg210->hrtimer, *timeout, -- NSEC_PER_MSEC, HRTIMER_MODE_ABS); -- } --} -- -- --/* Poll the STS_ASS status bit; see when it agrees with CMD_ASE */ --static void fotg210_poll_ASS(struct fotg210_hcd *fotg210) --{ -- unsigned actual, want; -- -- /* Don't enable anything if the controller isn't running (e.g., died) */ -- if (fotg210->rh_state != FOTG210_RH_RUNNING) -- return; -- -- want = (fotg210->command & CMD_ASE) ? STS_ASS : 0; -- actual = fotg210_readl(fotg210, &fotg210->regs->status) & STS_ASS; -- -- if (want != actual) { -- -- /* Poll again later, but give up after about 20 ms */ -- if (fotg210->ASS_poll_count++ < 20) { -- fotg210_enable_event(fotg210, FOTG210_HRTIMER_POLL_ASS, -- true); -- return; -- } -- fotg210_dbg(fotg210, "Waited too long for the async schedule status (%x/%x), giving up\n", -- want, actual); -- } -- fotg210->ASS_poll_count = 0; -- -- /* The status is up-to-date; restart or stop the schedule as needed */ -- if (want == 0) { /* Stopped */ -- if (fotg210->async_count > 0) -- fotg210_set_command_bit(fotg210, CMD_ASE); -- -- } else { /* Running */ -- if (fotg210->async_count == 0) { -- -- /* Turn off the schedule after a while */ -- fotg210_enable_event(fotg210, -- FOTG210_HRTIMER_DISABLE_ASYNC, -- true); -- } -- } --} -- --/* Turn off the async schedule after a brief delay */ --static void fotg210_disable_ASE(struct fotg210_hcd *fotg210) --{ -- fotg210_clear_command_bit(fotg210, CMD_ASE); --} -- -- --/* Poll the STS_PSS status bit; see when it agrees with CMD_PSE */ --static void fotg210_poll_PSS(struct fotg210_hcd *fotg210) --{ -- unsigned actual, want; -- -- /* Don't do anything if the controller isn't running (e.g., died) */ -- if (fotg210->rh_state != FOTG210_RH_RUNNING) -- return; -- -- want = (fotg210->command & CMD_PSE) ? STS_PSS : 0; -- actual = fotg210_readl(fotg210, &fotg210->regs->status) & STS_PSS; -- -- if (want != actual) { -- -- /* Poll again later, but give up after about 20 ms */ -- if (fotg210->PSS_poll_count++ < 20) { -- fotg210_enable_event(fotg210, FOTG210_HRTIMER_POLL_PSS, -- true); -- return; -- } -- fotg210_dbg(fotg210, "Waited too long for the periodic schedule status (%x/%x), giving up\n", -- want, actual); -- } -- fotg210->PSS_poll_count = 0; -- -- /* The status is up-to-date; restart or stop the schedule as needed */ -- if (want == 0) { /* Stopped */ -- if (fotg210->periodic_count > 0) -- fotg210_set_command_bit(fotg210, CMD_PSE); -- -- } else { /* Running */ -- if (fotg210->periodic_count == 0) { -- -- /* Turn off the schedule after a while */ -- fotg210_enable_event(fotg210, -- FOTG210_HRTIMER_DISABLE_PERIODIC, -- true); -- } -- } --} -- --/* Turn off the periodic schedule after a brief delay */ --static void fotg210_disable_PSE(struct fotg210_hcd *fotg210) --{ -- fotg210_clear_command_bit(fotg210, CMD_PSE); --} -- -- --/* Poll the STS_HALT status bit; see when a dead controller stops */ --static void fotg210_handle_controller_death(struct fotg210_hcd *fotg210) --{ -- if (!(fotg210_readl(fotg210, &fotg210->regs->status) & STS_HALT)) { -- -- /* Give up after a few milliseconds */ -- if (fotg210->died_poll_count++ < 5) { -- /* Try again later */ -- fotg210_enable_event(fotg210, -- FOTG210_HRTIMER_POLL_DEAD, true); -- return; -- } -- fotg210_warn(fotg210, "Waited too long for the controller to stop, giving up\n"); -- } -- -- /* Clean up the mess */ -- fotg210->rh_state = FOTG210_RH_HALTED; -- fotg210_writel(fotg210, 0, &fotg210->regs->intr_enable); -- fotg210_work(fotg210); -- end_unlink_async(fotg210); -- -- /* Not in process context, so don't try to reset the controller */ --} -- -- --/* Handle unlinked interrupt QHs once they are gone from the hardware */ --static void fotg210_handle_intr_unlinks(struct fotg210_hcd *fotg210) --{ -- bool stopped = (fotg210->rh_state < FOTG210_RH_RUNNING); -- -- /* -- * Process all the QHs on the intr_unlink list that were added -- * before the current unlink cycle began. The list is in -- * temporal order, so stop when we reach the first entry in the -- * current cycle. But if the root hub isn't running then -- * process all the QHs on the list. -- */ -- fotg210->intr_unlinking = true; -- while (fotg210->intr_unlink) { -- struct fotg210_qh *qh = fotg210->intr_unlink; -- -- if (!stopped && qh->unlink_cycle == fotg210->intr_unlink_cycle) -- break; -- fotg210->intr_unlink = qh->unlink_next; -- qh->unlink_next = NULL; -- end_unlink_intr(fotg210, qh); -- } -- -- /* Handle remaining entries later */ -- if (fotg210->intr_unlink) { -- fotg210_enable_event(fotg210, FOTG210_HRTIMER_UNLINK_INTR, -- true); -- ++fotg210->intr_unlink_cycle; -- } -- fotg210->intr_unlinking = false; --} -- -- --/* Start another free-iTDs/siTDs cycle */ --static void start_free_itds(struct fotg210_hcd *fotg210) --{ -- if (!(fotg210->enabled_hrtimer_events & -- BIT(FOTG210_HRTIMER_FREE_ITDS))) { -- fotg210->last_itd_to_free = list_entry( -- fotg210->cached_itd_list.prev, -- struct fotg210_itd, itd_list); -- fotg210_enable_event(fotg210, FOTG210_HRTIMER_FREE_ITDS, true); -- } --} -- --/* Wait for controller to stop using old iTDs and siTDs */ --static void end_free_itds(struct fotg210_hcd *fotg210) --{ -- struct fotg210_itd *itd, *n; -- -- if (fotg210->rh_state < FOTG210_RH_RUNNING) -- fotg210->last_itd_to_free = NULL; -- -- list_for_each_entry_safe(itd, n, &fotg210->cached_itd_list, itd_list) { -- list_del(&itd->itd_list); -- dma_pool_free(fotg210->itd_pool, itd, itd->itd_dma); -- if (itd == fotg210->last_itd_to_free) -- break; -- } -- -- if (!list_empty(&fotg210->cached_itd_list)) -- start_free_itds(fotg210); --} -- -- --/* Handle lost (or very late) IAA interrupts */ --static void fotg210_iaa_watchdog(struct fotg210_hcd *fotg210) --{ -- if (fotg210->rh_state != FOTG210_RH_RUNNING) -- return; -- -- /* -- * Lost IAA irqs wedge things badly; seen first with a vt8235. -- * So we need this watchdog, but must protect it against both -- * (a) SMP races against real IAA firing and retriggering, and -- * (b) clean HC shutdown, when IAA watchdog was pending. -- */ -- if (fotg210->async_iaa) { -- u32 cmd, status; -- -- /* If we get here, IAA is *REALLY* late. It's barely -- * conceivable that the system is so busy that CMD_IAAD -- * is still legitimately set, so let's be sure it's -- * clear before we read STS_IAA. (The HC should clear -- * CMD_IAAD when it sets STS_IAA.) -- */ -- cmd = fotg210_readl(fotg210, &fotg210->regs->command); -- -- /* -- * If IAA is set here it either legitimately triggered -- * after the watchdog timer expired (_way_ late, so we'll -- * still count it as lost) ... or a silicon erratum: -- * - VIA seems to set IAA without triggering the IRQ; -- * - IAAD potentially cleared without setting IAA. -- */ -- status = fotg210_readl(fotg210, &fotg210->regs->status); -- if ((status & STS_IAA) || !(cmd & CMD_IAAD)) { -- INCR(fotg210->stats.lost_iaa); -- fotg210_writel(fotg210, STS_IAA, -- &fotg210->regs->status); -- } -- -- fotg210_dbg(fotg210, "IAA watchdog: status %x cmd %x\n", -- status, cmd); -- end_unlink_async(fotg210); -- } --} -- -- --/* Enable the I/O watchdog, if appropriate */ --static void turn_on_io_watchdog(struct fotg210_hcd *fotg210) --{ -- /* Not needed if the controller isn't running or it's already enabled */ -- if (fotg210->rh_state != FOTG210_RH_RUNNING || -- (fotg210->enabled_hrtimer_events & -- BIT(FOTG210_HRTIMER_IO_WATCHDOG))) -- return; -- -- /* -- * Isochronous transfers always need the watchdog. -- * For other sorts we use it only if the flag is set. -- */ -- if (fotg210->isoc_count > 0 || (fotg210->need_io_watchdog && -- fotg210->async_count + fotg210->intr_count > 0)) -- fotg210_enable_event(fotg210, FOTG210_HRTIMER_IO_WATCHDOG, -- true); --} -- -- --/* Handler functions for the hrtimer event types. -- * Keep this array in the same order as the event types indexed by -- * enum fotg210_hrtimer_event in fotg210.h. -- */ --static void (*event_handlers[])(struct fotg210_hcd *) = { -- fotg210_poll_ASS, /* FOTG210_HRTIMER_POLL_ASS */ -- fotg210_poll_PSS, /* FOTG210_HRTIMER_POLL_PSS */ -- fotg210_handle_controller_death, /* FOTG210_HRTIMER_POLL_DEAD */ -- fotg210_handle_intr_unlinks, /* FOTG210_HRTIMER_UNLINK_INTR */ -- end_free_itds, /* FOTG210_HRTIMER_FREE_ITDS */ -- unlink_empty_async, /* FOTG210_HRTIMER_ASYNC_UNLINKS */ -- fotg210_iaa_watchdog, /* FOTG210_HRTIMER_IAA_WATCHDOG */ -- fotg210_disable_PSE, /* FOTG210_HRTIMER_DISABLE_PERIODIC */ -- fotg210_disable_ASE, /* FOTG210_HRTIMER_DISABLE_ASYNC */ -- fotg210_work, /* FOTG210_HRTIMER_IO_WATCHDOG */ --}; -- --static enum hrtimer_restart fotg210_hrtimer_func(struct hrtimer *t) --{ -- struct fotg210_hcd *fotg210 = -- container_of(t, struct fotg210_hcd, hrtimer); -- ktime_t now; -- unsigned long events; -- unsigned long flags; -- unsigned e; -- -- spin_lock_irqsave(&fotg210->lock, flags); -- -- events = fotg210->enabled_hrtimer_events; -- fotg210->enabled_hrtimer_events = 0; -- fotg210->next_hrtimer_event = FOTG210_HRTIMER_NO_EVENT; -- -- /* -- * Check each pending event. If its time has expired, handle -- * the event; otherwise re-enable it. -- */ -- now = ktime_get(); -- for_each_set_bit(e, &events, FOTG210_HRTIMER_NUM_EVENTS) { -- if (ktime_compare(now, fotg210->hr_timeouts[e]) >= 0) -- event_handlers[e](fotg210); -- else -- fotg210_enable_event(fotg210, e, false); -- } -- -- spin_unlock_irqrestore(&fotg210->lock, flags); -- return HRTIMER_NORESTART; --} -- --#define fotg210_bus_suspend NULL --#define fotg210_bus_resume NULL -- --static int check_reset_complete(struct fotg210_hcd *fotg210, int index, -- u32 __iomem *status_reg, int port_status) --{ -- if (!(port_status & PORT_CONNECT)) -- return port_status; -- -- /* if reset finished and it's still not enabled -- handoff */ -- if (!(port_status & PORT_PE)) -- /* with integrated TT, there's nobody to hand it to! */ -- fotg210_dbg(fotg210, "Failed to enable port %d on root hub TT\n", -- index + 1); -- else -- fotg210_dbg(fotg210, "port %d reset complete, port enabled\n", -- index + 1); -- -- return port_status; --} -- -- --/* build "status change" packet (one or two bytes) from HC registers */ -- --static int fotg210_hub_status_data(struct usb_hcd *hcd, char *buf) --{ -- struct fotg210_hcd *fotg210 = hcd_to_fotg210(hcd); -- u32 temp, status; -- u32 mask; -- int retval = 1; -- unsigned long flags; -- -- /* init status to no-changes */ -- buf[0] = 0; -- -- /* Inform the core about resumes-in-progress by returning -- * a non-zero value even if there are no status changes. -- */ -- status = fotg210->resuming_ports; -- -- mask = PORT_CSC | PORT_PEC; -- /* PORT_RESUME from hardware ~= PORT_STAT_C_SUSPEND */ -- -- /* no hub change reports (bit 0) for now (power, ...) */ -- -- /* port N changes (bit N)? */ -- spin_lock_irqsave(&fotg210->lock, flags); -- -- temp = fotg210_readl(fotg210, &fotg210->regs->port_status); -- -- /* -- * Return status information even for ports with OWNER set. -- * Otherwise hub_wq wouldn't see the disconnect event when a -- * high-speed device is switched over to the companion -- * controller by the user. -- */ -- -- if ((temp & mask) != 0 || test_bit(0, &fotg210->port_c_suspend) || -- (fotg210->reset_done[0] && -- time_after_eq(jiffies, fotg210->reset_done[0]))) { -- buf[0] |= 1 << 1; -- status = STS_PCD; -- } -- /* FIXME autosuspend idle root hubs */ -- spin_unlock_irqrestore(&fotg210->lock, flags); -- return status ? retval : 0; --} -- --static void fotg210_hub_descriptor(struct fotg210_hcd *fotg210, -- struct usb_hub_descriptor *desc) --{ -- int ports = HCS_N_PORTS(fotg210->hcs_params); -- u16 temp; -- -- desc->bDescriptorType = USB_DT_HUB; -- desc->bPwrOn2PwrGood = 10; /* fotg210 1.0, 2.3.9 says 20ms max */ -- desc->bHubContrCurrent = 0; -- -- desc->bNbrPorts = ports; -- temp = 1 + (ports / 8); -- desc->bDescLength = 7 + 2 * temp; -- -- /* two bitmaps: ports removable, and usb 1.0 legacy PortPwrCtrlMask */ -- memset(&desc->u.hs.DeviceRemovable[0], 0, temp); -- memset(&desc->u.hs.DeviceRemovable[temp], 0xff, temp); -- -- temp = HUB_CHAR_INDV_PORT_OCPM; /* per-port overcurrent reporting */ -- temp |= HUB_CHAR_NO_LPSM; /* no power switching */ -- desc->wHubCharacteristics = cpu_to_le16(temp); --} -- --static int fotg210_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, -- u16 wIndex, char *buf, u16 wLength) --{ -- struct fotg210_hcd *fotg210 = hcd_to_fotg210(hcd); -- int ports = HCS_N_PORTS(fotg210->hcs_params); -- u32 __iomem *status_reg = &fotg210->regs->port_status; -- u32 temp, temp1, status; -- unsigned long flags; -- int retval = 0; -- unsigned selector; -- -- /* -- * FIXME: support SetPortFeatures USB_PORT_FEAT_INDICATOR. -- * HCS_INDICATOR may say we can change LEDs to off/amber/green. -- * (track current state ourselves) ... blink for diagnostics, -- * power, "this is the one", etc. EHCI spec supports this. -- */ -- -- spin_lock_irqsave(&fotg210->lock, flags); -- switch (typeReq) { -- case ClearHubFeature: -- switch (wValue) { -- case C_HUB_LOCAL_POWER: -- case C_HUB_OVER_CURRENT: -- /* no hub-wide feature/status flags */ -- break; -- default: -- goto error; -- } -- break; -- case ClearPortFeature: -- if (!wIndex || wIndex > ports) -- goto error; -- wIndex--; -- temp = fotg210_readl(fotg210, status_reg); -- temp &= ~PORT_RWC_BITS; -- -- /* -- * Even if OWNER is set, so the port is owned by the -- * companion controller, hub_wq needs to be able to clear -- * the port-change status bits (especially -- * USB_PORT_STAT_C_CONNECTION). -- */ -- -- switch (wValue) { -- case USB_PORT_FEAT_ENABLE: -- fotg210_writel(fotg210, temp & ~PORT_PE, status_reg); -- break; -- case USB_PORT_FEAT_C_ENABLE: -- fotg210_writel(fotg210, temp | PORT_PEC, status_reg); -- break; -- case USB_PORT_FEAT_SUSPEND: -- if (temp & PORT_RESET) -- goto error; -- if (!(temp & PORT_SUSPEND)) -- break; -- if ((temp & PORT_PE) == 0) -- goto error; -- -- /* resume signaling for 20 msec */ -- fotg210_writel(fotg210, temp | PORT_RESUME, status_reg); -- fotg210->reset_done[wIndex] = jiffies -- + msecs_to_jiffies(USB_RESUME_TIMEOUT); -- break; -- case USB_PORT_FEAT_C_SUSPEND: -- clear_bit(wIndex, &fotg210->port_c_suspend); -- break; -- case USB_PORT_FEAT_C_CONNECTION: -- fotg210_writel(fotg210, temp | PORT_CSC, status_reg); -- break; -- case USB_PORT_FEAT_C_OVER_CURRENT: -- fotg210_writel(fotg210, temp | OTGISR_OVC, -- &fotg210->regs->otgisr); -- break; -- case USB_PORT_FEAT_C_RESET: -- /* GetPortStatus clears reset */ -- break; -- default: -- goto error; -- } -- fotg210_readl(fotg210, &fotg210->regs->command); -- break; -- case GetHubDescriptor: -- fotg210_hub_descriptor(fotg210, (struct usb_hub_descriptor *) -- buf); -- break; -- case GetHubStatus: -- /* no hub-wide feature/status flags */ -- memset(buf, 0, 4); -- /*cpu_to_le32s ((u32 *) buf); */ -- break; -- case GetPortStatus: -- if (!wIndex || wIndex > ports) -- goto error; -- wIndex--; -- status = 0; -- temp = fotg210_readl(fotg210, status_reg); -- -- /* wPortChange bits */ -- if (temp & PORT_CSC) -- status |= USB_PORT_STAT_C_CONNECTION << 16; -- if (temp & PORT_PEC) -- status |= USB_PORT_STAT_C_ENABLE << 16; -- -- temp1 = fotg210_readl(fotg210, &fotg210->regs->otgisr); -- if (temp1 & OTGISR_OVC) -- status |= USB_PORT_STAT_C_OVERCURRENT << 16; -- -- /* whoever resumes must GetPortStatus to complete it!! */ -- if (temp & PORT_RESUME) { -- -- /* Remote Wakeup received? */ -- if (!fotg210->reset_done[wIndex]) { -- /* resume signaling for 20 msec */ -- fotg210->reset_done[wIndex] = jiffies -- + msecs_to_jiffies(20); -- /* check the port again */ -- mod_timer(&fotg210_to_hcd(fotg210)->rh_timer, -- fotg210->reset_done[wIndex]); -- } -- -- /* resume completed? */ -- else if (time_after_eq(jiffies, -- fotg210->reset_done[wIndex])) { -- clear_bit(wIndex, &fotg210->suspended_ports); -- set_bit(wIndex, &fotg210->port_c_suspend); -- fotg210->reset_done[wIndex] = 0; -- -- /* stop resume signaling */ -- temp = fotg210_readl(fotg210, status_reg); -- fotg210_writel(fotg210, temp & -- ~(PORT_RWC_BITS | PORT_RESUME), -- status_reg); -- clear_bit(wIndex, &fotg210->resuming_ports); -- retval = handshake(fotg210, status_reg, -- PORT_RESUME, 0, 2000);/* 2ms */ -- if (retval != 0) { -- fotg210_err(fotg210, -- "port %d resume error %d\n", -- wIndex + 1, retval); -- goto error; -- } -- temp &= ~(PORT_SUSPEND|PORT_RESUME|(3<<10)); -- } -- } -- -- /* whoever resets must GetPortStatus to complete it!! */ -- if ((temp & PORT_RESET) && time_after_eq(jiffies, -- fotg210->reset_done[wIndex])) { -- status |= USB_PORT_STAT_C_RESET << 16; -- fotg210->reset_done[wIndex] = 0; -- clear_bit(wIndex, &fotg210->resuming_ports); -- -- /* force reset to complete */ -- fotg210_writel(fotg210, -- temp & ~(PORT_RWC_BITS | PORT_RESET), -- status_reg); -- /* REVISIT: some hardware needs 550+ usec to clear -- * this bit; seems too long to spin routinely... -- */ -- retval = handshake(fotg210, status_reg, -- PORT_RESET, 0, 1000); -- if (retval != 0) { -- fotg210_err(fotg210, "port %d reset error %d\n", -- wIndex + 1, retval); -- goto error; -- } -- -- /* see what we found out */ -- temp = check_reset_complete(fotg210, wIndex, status_reg, -- fotg210_readl(fotg210, status_reg)); -- -- /* restart schedule */ -- fotg210->command |= CMD_RUN; -- fotg210_writel(fotg210, fotg210->command, &fotg210->regs->command); -- } -- -- if (!(temp & (PORT_RESUME|PORT_RESET))) { -- fotg210->reset_done[wIndex] = 0; -- clear_bit(wIndex, &fotg210->resuming_ports); -- } -- -- /* transfer dedicated ports to the companion hc */ -- if ((temp & PORT_CONNECT) && -- test_bit(wIndex, &fotg210->companion_ports)) { -- temp &= ~PORT_RWC_BITS; -- fotg210_writel(fotg210, temp, status_reg); -- fotg210_dbg(fotg210, "port %d --> companion\n", -- wIndex + 1); -- temp = fotg210_readl(fotg210, status_reg); -- } -- -- /* -- * Even if OWNER is set, there's no harm letting hub_wq -- * see the wPortStatus values (they should all be 0 except -- * for PORT_POWER anyway). -- */ -- -- if (temp & PORT_CONNECT) { -- status |= USB_PORT_STAT_CONNECTION; -- status |= fotg210_port_speed(fotg210, temp); -- } -- if (temp & PORT_PE) -- status |= USB_PORT_STAT_ENABLE; -- -- /* maybe the port was unsuspended without our knowledge */ -- if (temp & (PORT_SUSPEND|PORT_RESUME)) { -- status |= USB_PORT_STAT_SUSPEND; -- } else if (test_bit(wIndex, &fotg210->suspended_ports)) { -- clear_bit(wIndex, &fotg210->suspended_ports); -- clear_bit(wIndex, &fotg210->resuming_ports); -- fotg210->reset_done[wIndex] = 0; -- if (temp & PORT_PE) -- set_bit(wIndex, &fotg210->port_c_suspend); -- } -- -- temp1 = fotg210_readl(fotg210, &fotg210->regs->otgisr); -- if (temp1 & OTGISR_OVC) -- status |= USB_PORT_STAT_OVERCURRENT; -- if (temp & PORT_RESET) -- status |= USB_PORT_STAT_RESET; -- if (test_bit(wIndex, &fotg210->port_c_suspend)) -- status |= USB_PORT_STAT_C_SUSPEND << 16; -- -- if (status & ~0xffff) /* only if wPortChange is interesting */ -- dbg_port(fotg210, "GetStatus", wIndex + 1, temp); -- put_unaligned_le32(status, buf); -- break; -- case SetHubFeature: -- switch (wValue) { -- case C_HUB_LOCAL_POWER: -- case C_HUB_OVER_CURRENT: -- /* no hub-wide feature/status flags */ -- break; -- default: -- goto error; -- } -- break; -- case SetPortFeature: -- selector = wIndex >> 8; -- wIndex &= 0xff; -- -- if (!wIndex || wIndex > ports) -- goto error; -- wIndex--; -- temp = fotg210_readl(fotg210, status_reg); -- temp &= ~PORT_RWC_BITS; -- switch (wValue) { -- case USB_PORT_FEAT_SUSPEND: -- if ((temp & PORT_PE) == 0 -- || (temp & PORT_RESET) != 0) -- goto error; -- -- /* After above check the port must be connected. -- * Set appropriate bit thus could put phy into low power -- * mode if we have hostpc feature -- */ -- fotg210_writel(fotg210, temp | PORT_SUSPEND, -- status_reg); -- set_bit(wIndex, &fotg210->suspended_ports); -- break; -- case USB_PORT_FEAT_RESET: -- if (temp & PORT_RESUME) -- goto error; -- /* line status bits may report this as low speed, -- * which can be fine if this root hub has a -- * transaction translator built in. -- */ -- fotg210_dbg(fotg210, "port %d reset\n", wIndex + 1); -- temp |= PORT_RESET; -- temp &= ~PORT_PE; -- -- /* -- * caller must wait, then call GetPortStatus -- * usb 2.0 spec says 50 ms resets on root -- */ -- fotg210->reset_done[wIndex] = jiffies -- + msecs_to_jiffies(50); -- fotg210_writel(fotg210, temp, status_reg); -- break; -- -- /* For downstream facing ports (these): one hub port is put -- * into test mode according to USB2 11.24.2.13, then the hub -- * must be reset (which for root hub now means rmmod+modprobe, -- * or else system reboot). See EHCI 2.3.9 and 4.14 for info -- * about the EHCI-specific stuff. -- */ -- case USB_PORT_FEAT_TEST: -- if (!selector || selector > 5) -- goto error; -- spin_unlock_irqrestore(&fotg210->lock, flags); -- fotg210_quiesce(fotg210); -- spin_lock_irqsave(&fotg210->lock, flags); -- -- /* Put all enabled ports into suspend */ -- temp = fotg210_readl(fotg210, status_reg) & -- ~PORT_RWC_BITS; -- if (temp & PORT_PE) -- fotg210_writel(fotg210, temp | PORT_SUSPEND, -- status_reg); -- -- spin_unlock_irqrestore(&fotg210->lock, flags); -- fotg210_halt(fotg210); -- spin_lock_irqsave(&fotg210->lock, flags); -- -- temp = fotg210_readl(fotg210, status_reg); -- temp |= selector << 16; -- fotg210_writel(fotg210, temp, status_reg); -- break; -- -- default: -- goto error; -- } -- fotg210_readl(fotg210, &fotg210->regs->command); -- break; -- -- default: --error: -- /* "stall" on error */ -- retval = -EPIPE; -- } -- spin_unlock_irqrestore(&fotg210->lock, flags); -- return retval; --} -- --static void __maybe_unused fotg210_relinquish_port(struct usb_hcd *hcd, -- int portnum) --{ -- return; --} -- --static int __maybe_unused fotg210_port_handed_over(struct usb_hcd *hcd, -- int portnum) --{ -- return 0; --} -- --/* There's basically three types of memory: -- * - data used only by the HCD ... kmalloc is fine -- * - async and periodic schedules, shared by HC and HCD ... these -- * need to use dma_pool or dma_alloc_coherent -- * - driver buffers, read/written by HC ... single shot DMA mapped -- * -- * There's also "register" data (e.g. PCI or SOC), which is memory mapped. -- * No memory seen by this driver is pageable. -- */ -- --/* Allocate the key transfer structures from the previously allocated pool */ --static inline void fotg210_qtd_init(struct fotg210_hcd *fotg210, -- struct fotg210_qtd *qtd, dma_addr_t dma) --{ -- memset(qtd, 0, sizeof(*qtd)); -- qtd->qtd_dma = dma; -- qtd->hw_token = cpu_to_hc32(fotg210, QTD_STS_HALT); -- qtd->hw_next = FOTG210_LIST_END(fotg210); -- qtd->hw_alt_next = FOTG210_LIST_END(fotg210); -- INIT_LIST_HEAD(&qtd->qtd_list); --} -- --static struct fotg210_qtd *fotg210_qtd_alloc(struct fotg210_hcd *fotg210, -- gfp_t flags) --{ -- struct fotg210_qtd *qtd; -- dma_addr_t dma; -- -- qtd = dma_pool_alloc(fotg210->qtd_pool, flags, &dma); -- if (qtd != NULL) -- fotg210_qtd_init(fotg210, qtd, dma); -- -- return qtd; --} -- --static inline void fotg210_qtd_free(struct fotg210_hcd *fotg210, -- struct fotg210_qtd *qtd) --{ -- dma_pool_free(fotg210->qtd_pool, qtd, qtd->qtd_dma); --} -- -- --static void qh_destroy(struct fotg210_hcd *fotg210, struct fotg210_qh *qh) --{ -- /* clean qtds first, and know this is not linked */ -- if (!list_empty(&qh->qtd_list) || qh->qh_next.ptr) { -- fotg210_dbg(fotg210, "unused qh not empty!\n"); -- BUG(); -- } -- if (qh->dummy) -- fotg210_qtd_free(fotg210, qh->dummy); -- dma_pool_free(fotg210->qh_pool, qh->hw, qh->qh_dma); -- kfree(qh); --} -- --static struct fotg210_qh *fotg210_qh_alloc(struct fotg210_hcd *fotg210, -- gfp_t flags) --{ -- struct fotg210_qh *qh; -- dma_addr_t dma; -- -- qh = kzalloc(sizeof(*qh), GFP_ATOMIC); -- if (!qh) -- goto done; -- qh->hw = (struct fotg210_qh_hw *) -- dma_pool_zalloc(fotg210->qh_pool, flags, &dma); -- if (!qh->hw) -- goto fail; -- qh->qh_dma = dma; -- INIT_LIST_HEAD(&qh->qtd_list); -- -- /* dummy td enables safe urb queuing */ -- qh->dummy = fotg210_qtd_alloc(fotg210, flags); -- if (qh->dummy == NULL) { -- fotg210_dbg(fotg210, "no dummy td\n"); -- goto fail1; -- } --done: -- return qh; --fail1: -- dma_pool_free(fotg210->qh_pool, qh->hw, qh->qh_dma); --fail: -- kfree(qh); -- return NULL; --} -- --/* The queue heads and transfer descriptors are managed from pools tied -- * to each of the "per device" structures. -- * This is the initialisation and cleanup code. -- */ -- --static void fotg210_mem_cleanup(struct fotg210_hcd *fotg210) --{ -- if (fotg210->async) -- qh_destroy(fotg210, fotg210->async); -- fotg210->async = NULL; -- -- if (fotg210->dummy) -- qh_destroy(fotg210, fotg210->dummy); -- fotg210->dummy = NULL; -- -- /* DMA consistent memory and pools */ -- dma_pool_destroy(fotg210->qtd_pool); -- fotg210->qtd_pool = NULL; -- -- dma_pool_destroy(fotg210->qh_pool); -- fotg210->qh_pool = NULL; -- -- dma_pool_destroy(fotg210->itd_pool); -- fotg210->itd_pool = NULL; -- -- if (fotg210->periodic) -- dma_free_coherent(fotg210_to_hcd(fotg210)->self.controller, -- fotg210->periodic_size * sizeof(u32), -- fotg210->periodic, fotg210->periodic_dma); -- fotg210->periodic = NULL; -- -- /* shadow periodic table */ -- kfree(fotg210->pshadow); -- fotg210->pshadow = NULL; --} -- --/* remember to add cleanup code (above) if you add anything here */ --static int fotg210_mem_init(struct fotg210_hcd *fotg210, gfp_t flags) --{ -- int i; -- -- /* QTDs for control/bulk/intr transfers */ -- fotg210->qtd_pool = dma_pool_create("fotg210_qtd", -- fotg210_to_hcd(fotg210)->self.controller, -- sizeof(struct fotg210_qtd), -- 32 /* byte alignment (for hw parts) */, -- 4096 /* can't cross 4K */); -- if (!fotg210->qtd_pool) -- goto fail; -- -- /* QHs for control/bulk/intr transfers */ -- fotg210->qh_pool = dma_pool_create("fotg210_qh", -- fotg210_to_hcd(fotg210)->self.controller, -- sizeof(struct fotg210_qh_hw), -- 32 /* byte alignment (for hw parts) */, -- 4096 /* can't cross 4K */); -- if (!fotg210->qh_pool) -- goto fail; -- -- fotg210->async = fotg210_qh_alloc(fotg210, flags); -- if (!fotg210->async) -- goto fail; -- -- /* ITD for high speed ISO transfers */ -- fotg210->itd_pool = dma_pool_create("fotg210_itd", -- fotg210_to_hcd(fotg210)->self.controller, -- sizeof(struct fotg210_itd), -- 64 /* byte alignment (for hw parts) */, -- 4096 /* can't cross 4K */); -- if (!fotg210->itd_pool) -- goto fail; -- -- /* Hardware periodic table */ -- fotg210->periodic = -- dma_alloc_coherent(fotg210_to_hcd(fotg210)->self.controller, -- fotg210->periodic_size * sizeof(__le32), -- &fotg210->periodic_dma, 0); -- if (fotg210->periodic == NULL) -- goto fail; -- -- for (i = 0; i < fotg210->periodic_size; i++) -- fotg210->periodic[i] = FOTG210_LIST_END(fotg210); -- -- /* software shadow of hardware table */ -- fotg210->pshadow = kcalloc(fotg210->periodic_size, sizeof(void *), -- flags); -- if (fotg210->pshadow != NULL) -- return 0; -- --fail: -- fotg210_dbg(fotg210, "couldn't init memory\n"); -- fotg210_mem_cleanup(fotg210); -- return -ENOMEM; --} --/* EHCI hardware queue manipulation ... the core. QH/QTD manipulation. -- * -- * Control, bulk, and interrupt traffic all use "qh" lists. They list "qtd" -- * entries describing USB transactions, max 16-20kB/entry (with 4kB-aligned -- * buffers needed for the larger number). We use one QH per endpoint, queue -- * multiple urbs (all three types) per endpoint. URBs may need several qtds. -- * -- * ISO traffic uses "ISO TD" (itd) records, and (along with -- * interrupts) needs careful scheduling. Performance improvements can be -- * an ongoing challenge. That's in "ehci-sched.c". -- * -- * USB 1.1 devices are handled (a) by "companion" OHCI or UHCI root hubs, -- * or otherwise through transaction translators (TTs) in USB 2.0 hubs using -- * (b) special fields in qh entries or (c) split iso entries. TTs will -- * buffer low/full speed data so the host collects it at high speed. -- */ -- --/* fill a qtd, returning how much of the buffer we were able to queue up */ --static int qtd_fill(struct fotg210_hcd *fotg210, struct fotg210_qtd *qtd, -- dma_addr_t buf, size_t len, int token, int maxpacket) --{ -- int i, count; -- u64 addr = buf; -- -- /* one buffer entry per 4K ... first might be short or unaligned */ -- qtd->hw_buf[0] = cpu_to_hc32(fotg210, (u32)addr); -- qtd->hw_buf_hi[0] = cpu_to_hc32(fotg210, (u32)(addr >> 32)); -- count = 0x1000 - (buf & 0x0fff); /* rest of that page */ -- if (likely(len < count)) /* ... iff needed */ -- count = len; -- else { -- buf += 0x1000; -- buf &= ~0x0fff; -- -- /* per-qtd limit: from 16K to 20K (best alignment) */ -- for (i = 1; count < len && i < 5; i++) { -- addr = buf; -- qtd->hw_buf[i] = cpu_to_hc32(fotg210, (u32)addr); -- qtd->hw_buf_hi[i] = cpu_to_hc32(fotg210, -- (u32)(addr >> 32)); -- buf += 0x1000; -- if ((count + 0x1000) < len) -- count += 0x1000; -- else -- count = len; -- } -- -- /* short packets may only terminate transfers */ -- if (count != len) -- count -= (count % maxpacket); -- } -- qtd->hw_token = cpu_to_hc32(fotg210, (count << 16) | token); -- qtd->length = count; -- -- return count; --} -- --static inline void qh_update(struct fotg210_hcd *fotg210, -- struct fotg210_qh *qh, struct fotg210_qtd *qtd) --{ -- struct fotg210_qh_hw *hw = qh->hw; -- -- /* writes to an active overlay are unsafe */ -- BUG_ON(qh->qh_state != QH_STATE_IDLE); -- -- hw->hw_qtd_next = QTD_NEXT(fotg210, qtd->qtd_dma); -- hw->hw_alt_next = FOTG210_LIST_END(fotg210); -- -- /* Except for control endpoints, we make hardware maintain data -- * toggle (like OHCI) ... here (re)initialize the toggle in the QH, -- * and set the pseudo-toggle in udev. Only usb_clear_halt() will -- * ever clear it. -- */ -- if (!(hw->hw_info1 & cpu_to_hc32(fotg210, QH_TOGGLE_CTL))) { -- unsigned is_out, epnum; -- -- is_out = qh->is_out; -- epnum = (hc32_to_cpup(fotg210, &hw->hw_info1) >> 8) & 0x0f; -- if (unlikely(!usb_gettoggle(qh->dev, epnum, is_out))) { -- hw->hw_token &= ~cpu_to_hc32(fotg210, QTD_TOGGLE); -- usb_settoggle(qh->dev, epnum, is_out, 1); -- } -- } -- -- hw->hw_token &= cpu_to_hc32(fotg210, QTD_TOGGLE | QTD_STS_PING); --} -- --/* if it weren't for a common silicon quirk (writing the dummy into the qh -- * overlay, so qh->hw_token wrongly becomes inactive/halted), only fault -- * recovery (including urb dequeue) would need software changes to a QH... -- */ --static void qh_refresh(struct fotg210_hcd *fotg210, struct fotg210_qh *qh) --{ -- struct fotg210_qtd *qtd; -- -- if (list_empty(&qh->qtd_list)) -- qtd = qh->dummy; -- else { -- qtd = list_entry(qh->qtd_list.next, -- struct fotg210_qtd, qtd_list); -- /* -- * first qtd may already be partially processed. -- * If we come here during unlink, the QH overlay region -- * might have reference to the just unlinked qtd. The -- * qtd is updated in qh_completions(). Update the QH -- * overlay here. -- */ -- if (cpu_to_hc32(fotg210, qtd->qtd_dma) == qh->hw->hw_current) { -- qh->hw->hw_qtd_next = qtd->hw_next; -- qtd = NULL; -- } -- } -- -- if (qtd) -- qh_update(fotg210, qh, qtd); --} -- --static void qh_link_async(struct fotg210_hcd *fotg210, struct fotg210_qh *qh); -- --static void fotg210_clear_tt_buffer_complete(struct usb_hcd *hcd, -- struct usb_host_endpoint *ep) --{ -- struct fotg210_hcd *fotg210 = hcd_to_fotg210(hcd); -- struct fotg210_qh *qh = ep->hcpriv; -- unsigned long flags; -- -- spin_lock_irqsave(&fotg210->lock, flags); -- qh->clearing_tt = 0; -- if (qh->qh_state == QH_STATE_IDLE && !list_empty(&qh->qtd_list) -- && fotg210->rh_state == FOTG210_RH_RUNNING) -- qh_link_async(fotg210, qh); -- spin_unlock_irqrestore(&fotg210->lock, flags); --} -- --static void fotg210_clear_tt_buffer(struct fotg210_hcd *fotg210, -- struct fotg210_qh *qh, struct urb *urb, u32 token) --{ -- -- /* If an async split transaction gets an error or is unlinked, -- * the TT buffer may be left in an indeterminate state. We -- * have to clear the TT buffer. -- * -- * Note: this routine is never called for Isochronous transfers. -- */ -- if (urb->dev->tt && !usb_pipeint(urb->pipe) && !qh->clearing_tt) { -- struct usb_device *tt = urb->dev->tt->hub; -- -- dev_dbg(&tt->dev, -- "clear tt buffer port %d, a%d ep%d t%08x\n", -- urb->dev->ttport, urb->dev->devnum, -- usb_pipeendpoint(urb->pipe), token); -- -- if (urb->dev->tt->hub != -- fotg210_to_hcd(fotg210)->self.root_hub) { -- if (usb_hub_clear_tt_buffer(urb) == 0) -- qh->clearing_tt = 1; -- } -- } --} -- --static int qtd_copy_status(struct fotg210_hcd *fotg210, struct urb *urb, -- size_t length, u32 token) --{ -- int status = -EINPROGRESS; -- -- /* count IN/OUT bytes, not SETUP (even short packets) */ -- if (likely(QTD_PID(token) != 2)) -- urb->actual_length += length - QTD_LENGTH(token); -- -- /* don't modify error codes */ -- if (unlikely(urb->unlinked)) -- return status; -- -- /* force cleanup after short read; not always an error */ -- if (unlikely(IS_SHORT_READ(token))) -- status = -EREMOTEIO; -- -- /* serious "can't proceed" faults reported by the hardware */ -- if (token & QTD_STS_HALT) { -- if (token & QTD_STS_BABBLE) { -- /* FIXME "must" disable babbling device's port too */ -- status = -EOVERFLOW; -- /* CERR nonzero + halt --> stall */ -- } else if (QTD_CERR(token)) { -- status = -EPIPE; -- -- /* In theory, more than one of the following bits can be set -- * since they are sticky and the transaction is retried. -- * Which to test first is rather arbitrary. -- */ -- } else if (token & QTD_STS_MMF) { -- /* fs/ls interrupt xfer missed the complete-split */ -- status = -EPROTO; -- } else if (token & QTD_STS_DBE) { -- status = (QTD_PID(token) == 1) /* IN ? */ -- ? -ENOSR /* hc couldn't read data */ -- : -ECOMM; /* hc couldn't write data */ -- } else if (token & QTD_STS_XACT) { -- /* timeout, bad CRC, wrong PID, etc */ -- fotg210_dbg(fotg210, "devpath %s ep%d%s 3strikes\n", -- urb->dev->devpath, -- usb_pipeendpoint(urb->pipe), -- usb_pipein(urb->pipe) ? "in" : "out"); -- status = -EPROTO; -- } else { /* unknown */ -- status = -EPROTO; -- } -- -- fotg210_dbg(fotg210, -- "dev%d ep%d%s qtd token %08x --> status %d\n", -- usb_pipedevice(urb->pipe), -- usb_pipeendpoint(urb->pipe), -- usb_pipein(urb->pipe) ? "in" : "out", -- token, status); -- } -- -- return status; --} -- --static void fotg210_urb_done(struct fotg210_hcd *fotg210, struct urb *urb, -- int status) --__releases(fotg210->lock) --__acquires(fotg210->lock) --{ -- if (likely(urb->hcpriv != NULL)) { -- struct fotg210_qh *qh = (struct fotg210_qh *) urb->hcpriv; -- -- /* S-mask in a QH means it's an interrupt urb */ -- if ((qh->hw->hw_info2 & cpu_to_hc32(fotg210, QH_SMASK)) != 0) { -- -- /* ... update hc-wide periodic stats (for usbfs) */ -- fotg210_to_hcd(fotg210)->self.bandwidth_int_reqs--; -- } -- } -- -- if (unlikely(urb->unlinked)) { -- INCR(fotg210->stats.unlink); -- } else { -- /* report non-error and short read status as zero */ -- if (status == -EINPROGRESS || status == -EREMOTEIO) -- status = 0; -- INCR(fotg210->stats.complete); -- } -- --#ifdef FOTG210_URB_TRACE -- fotg210_dbg(fotg210, -- "%s %s urb %p ep%d%s status %d len %d/%d\n", -- __func__, urb->dev->devpath, urb, -- usb_pipeendpoint(urb->pipe), -- usb_pipein(urb->pipe) ? "in" : "out", -- status, -- urb->actual_length, urb->transfer_buffer_length); --#endif -- -- /* complete() can reenter this HCD */ -- usb_hcd_unlink_urb_from_ep(fotg210_to_hcd(fotg210), urb); -- spin_unlock(&fotg210->lock); -- usb_hcd_giveback_urb(fotg210_to_hcd(fotg210), urb, status); -- spin_lock(&fotg210->lock); --} -- --static int qh_schedule(struct fotg210_hcd *fotg210, struct fotg210_qh *qh); -- --/* Process and free completed qtds for a qh, returning URBs to drivers. -- * Chases up to qh->hw_current. Returns number of completions called, -- * indicating how much "real" work we did. -- */ --static unsigned qh_completions(struct fotg210_hcd *fotg210, -- struct fotg210_qh *qh) --{ -- struct fotg210_qtd *last, *end = qh->dummy; -- struct fotg210_qtd *qtd, *tmp; -- int last_status; -- int stopped; -- unsigned count = 0; -- u8 state; -- struct fotg210_qh_hw *hw = qh->hw; -- -- if (unlikely(list_empty(&qh->qtd_list))) -- return count; -- -- /* completions (or tasks on other cpus) must never clobber HALT -- * till we've gone through and cleaned everything up, even when -- * they add urbs to this qh's queue or mark them for unlinking. -- * -- * NOTE: unlinking expects to be done in queue order. -- * -- * It's a bug for qh->qh_state to be anything other than -- * QH_STATE_IDLE, unless our caller is scan_async() or -- * scan_intr(). -- */ -- state = qh->qh_state; -- qh->qh_state = QH_STATE_COMPLETING; -- stopped = (state == QH_STATE_IDLE); -- --rescan: -- last = NULL; -- last_status = -EINPROGRESS; -- qh->needs_rescan = 0; -- -- /* remove de-activated QTDs from front of queue. -- * after faults (including short reads), cleanup this urb -- * then let the queue advance. -- * if queue is stopped, handles unlinks. -- */ -- list_for_each_entry_safe(qtd, tmp, &qh->qtd_list, qtd_list) { -- struct urb *urb; -- u32 token = 0; -- -- urb = qtd->urb; -- -- /* clean up any state from previous QTD ...*/ -- if (last) { -- if (likely(last->urb != urb)) { -- fotg210_urb_done(fotg210, last->urb, -- last_status); -- count++; -- last_status = -EINPROGRESS; -- } -- fotg210_qtd_free(fotg210, last); -- last = NULL; -- } -- -- /* ignore urbs submitted during completions we reported */ -- if (qtd == end) -- break; -- -- /* hardware copies qtd out of qh overlay */ -- rmb(); -- token = hc32_to_cpu(fotg210, qtd->hw_token); -- -- /* always clean up qtds the hc de-activated */ --retry_xacterr: -- if ((token & QTD_STS_ACTIVE) == 0) { -- -- /* Report Data Buffer Error: non-fatal but useful */ -- if (token & QTD_STS_DBE) -- fotg210_dbg(fotg210, -- "detected DataBufferErr for urb %p ep%d%s len %d, qtd %p [qh %p]\n", -- urb, usb_endpoint_num(&urb->ep->desc), -- usb_endpoint_dir_in(&urb->ep->desc) -- ? "in" : "out", -- urb->transfer_buffer_length, qtd, qh); -- -- /* on STALL, error, and short reads this urb must -- * complete and all its qtds must be recycled. -- */ -- if ((token & QTD_STS_HALT) != 0) { -- -- /* retry transaction errors until we -- * reach the software xacterr limit -- */ -- if ((token & QTD_STS_XACT) && -- QTD_CERR(token) == 0 && -- ++qh->xacterrs < QH_XACTERR_MAX && -- !urb->unlinked) { -- fotg210_dbg(fotg210, -- "detected XactErr len %zu/%zu retry %d\n", -- qtd->length - QTD_LENGTH(token), -- qtd->length, -- qh->xacterrs); -- -- /* reset the token in the qtd and the -- * qh overlay (which still contains -- * the qtd) so that we pick up from -- * where we left off -- */ -- token &= ~QTD_STS_HALT; -- token |= QTD_STS_ACTIVE | -- (FOTG210_TUNE_CERR << 10); -- qtd->hw_token = cpu_to_hc32(fotg210, -- token); -- wmb(); -- hw->hw_token = cpu_to_hc32(fotg210, -- token); -- goto retry_xacterr; -- } -- stopped = 1; -- -- /* magic dummy for some short reads; qh won't advance. -- * that silicon quirk can kick in with this dummy too. -- * -- * other short reads won't stop the queue, including -- * control transfers (status stage handles that) or -- * most other single-qtd reads ... the queue stops if -- * URB_SHORT_NOT_OK was set so the driver submitting -- * the urbs could clean it up. -- */ -- } else if (IS_SHORT_READ(token) && -- !(qtd->hw_alt_next & -- FOTG210_LIST_END(fotg210))) { -- stopped = 1; -- } -- -- /* stop scanning when we reach qtds the hc is using */ -- } else if (likely(!stopped -- && fotg210->rh_state >= FOTG210_RH_RUNNING)) { -- break; -- -- /* scan the whole queue for unlinks whenever it stops */ -- } else { -- stopped = 1; -- -- /* cancel everything if we halt, suspend, etc */ -- if (fotg210->rh_state < FOTG210_RH_RUNNING) -- last_status = -ESHUTDOWN; -- -- /* this qtd is active; skip it unless a previous qtd -- * for its urb faulted, or its urb was canceled. -- */ -- else if (last_status == -EINPROGRESS && !urb->unlinked) -- continue; -- -- /* qh unlinked; token in overlay may be most current */ -- if (state == QH_STATE_IDLE && -- cpu_to_hc32(fotg210, qtd->qtd_dma) -- == hw->hw_current) { -- token = hc32_to_cpu(fotg210, hw->hw_token); -- -- /* An unlink may leave an incomplete -- * async transaction in the TT buffer. -- * We have to clear it. -- */ -- fotg210_clear_tt_buffer(fotg210, qh, urb, -- token); -- } -- } -- -- /* unless we already know the urb's status, collect qtd status -- * and update count of bytes transferred. in common short read -- * cases with only one data qtd (including control transfers), -- * queue processing won't halt. but with two or more qtds (for -- * example, with a 32 KB transfer), when the first qtd gets a -- * short read the second must be removed by hand. -- */ -- if (last_status == -EINPROGRESS) { -- last_status = qtd_copy_status(fotg210, urb, -- qtd->length, token); -- if (last_status == -EREMOTEIO && -- (qtd->hw_alt_next & -- FOTG210_LIST_END(fotg210))) -- last_status = -EINPROGRESS; -- -- /* As part of low/full-speed endpoint-halt processing -- * we must clear the TT buffer (11.17.5). -- */ -- if (unlikely(last_status != -EINPROGRESS && -- last_status != -EREMOTEIO)) { -- /* The TT's in some hubs malfunction when they -- * receive this request following a STALL (they -- * stop sending isochronous packets). Since a -- * STALL can't leave the TT buffer in a busy -- * state (if you believe Figures 11-48 - 11-51 -- * in the USB 2.0 spec), we won't clear the TT -- * buffer in this case. Strictly speaking this -- * is a violation of the spec. -- */ -- if (last_status != -EPIPE) -- fotg210_clear_tt_buffer(fotg210, qh, -- urb, token); -- } -- } -- -- /* if we're removing something not at the queue head, -- * patch the hardware queue pointer. -- */ -- if (stopped && qtd->qtd_list.prev != &qh->qtd_list) { -- last = list_entry(qtd->qtd_list.prev, -- struct fotg210_qtd, qtd_list); -- last->hw_next = qtd->hw_next; -- } -- -- /* remove qtd; it's recycled after possible urb completion */ -- list_del(&qtd->qtd_list); -- last = qtd; -- -- /* reinit the xacterr counter for the next qtd */ -- qh->xacterrs = 0; -- } -- -- /* last urb's completion might still need calling */ -- if (likely(last != NULL)) { -- fotg210_urb_done(fotg210, last->urb, last_status); -- count++; -- fotg210_qtd_free(fotg210, last); -- } -- -- /* Do we need to rescan for URBs dequeued during a giveback? */ -- if (unlikely(qh->needs_rescan)) { -- /* If the QH is already unlinked, do the rescan now. */ -- if (state == QH_STATE_IDLE) -- goto rescan; -- -- /* Otherwise we have to wait until the QH is fully unlinked. -- * Our caller will start an unlink if qh->needs_rescan is -- * set. But if an unlink has already started, nothing needs -- * to be done. -- */ -- if (state != QH_STATE_LINKED) -- qh->needs_rescan = 0; -- } -- -- /* restore original state; caller must unlink or relink */ -- qh->qh_state = state; -- -- /* be sure the hardware's done with the qh before refreshing -- * it after fault cleanup, or recovering from silicon wrongly -- * overlaying the dummy qtd (which reduces DMA chatter). -- */ -- if (stopped != 0 || hw->hw_qtd_next == FOTG210_LIST_END(fotg210)) { -- switch (state) { -- case QH_STATE_IDLE: -- qh_refresh(fotg210, qh); -- break; -- case QH_STATE_LINKED: -- /* We won't refresh a QH that's linked (after the HC -- * stopped the queue). That avoids a race: -- * - HC reads first part of QH; -- * - CPU updates that first part and the token; -- * - HC reads rest of that QH, including token -- * Result: HC gets an inconsistent image, and then -- * DMAs to/from the wrong memory (corrupting it). -- * -- * That should be rare for interrupt transfers, -- * except maybe high bandwidth ... -- */ -- -- /* Tell the caller to start an unlink */ -- qh->needs_rescan = 1; -- break; -- /* otherwise, unlink already started */ -- } -- } -- -- return count; --} -- --/* reverse of qh_urb_transaction: free a list of TDs. -- * used for cleanup after errors, before HC sees an URB's TDs. -- */ --static void qtd_list_free(struct fotg210_hcd *fotg210, struct urb *urb, -- struct list_head *head) --{ -- struct fotg210_qtd *qtd, *temp; -- -- list_for_each_entry_safe(qtd, temp, head, qtd_list) { -- list_del(&qtd->qtd_list); -- fotg210_qtd_free(fotg210, qtd); -- } --} -- --/* create a list of filled qtds for this URB; won't link into qh. -- */ --static struct list_head *qh_urb_transaction(struct fotg210_hcd *fotg210, -- struct urb *urb, struct list_head *head, gfp_t flags) --{ -- struct fotg210_qtd *qtd, *qtd_prev; -- dma_addr_t buf; -- int len, this_sg_len, maxpacket; -- int is_input; -- u32 token; -- int i; -- struct scatterlist *sg; -- -- /* -- * URBs map to sequences of QTDs: one logical transaction -- */ -- qtd = fotg210_qtd_alloc(fotg210, flags); -- if (unlikely(!qtd)) -- return NULL; -- list_add_tail(&qtd->qtd_list, head); -- qtd->urb = urb; -- -- token = QTD_STS_ACTIVE; -- token |= (FOTG210_TUNE_CERR << 10); -- /* for split transactions, SplitXState initialized to zero */ -- -- len = urb->transfer_buffer_length; -- is_input = usb_pipein(urb->pipe); -- if (usb_pipecontrol(urb->pipe)) { -- /* SETUP pid */ -- qtd_fill(fotg210, qtd, urb->setup_dma, -- sizeof(struct usb_ctrlrequest), -- token | (2 /* "setup" */ << 8), 8); -- -- /* ... and always at least one more pid */ -- token ^= QTD_TOGGLE; -- qtd_prev = qtd; -- qtd = fotg210_qtd_alloc(fotg210, flags); -- if (unlikely(!qtd)) -- goto cleanup; -- qtd->urb = urb; -- qtd_prev->hw_next = QTD_NEXT(fotg210, qtd->qtd_dma); -- list_add_tail(&qtd->qtd_list, head); -- -- /* for zero length DATA stages, STATUS is always IN */ -- if (len == 0) -- token |= (1 /* "in" */ << 8); -- } -- -- /* -- * data transfer stage: buffer setup -- */ -- i = urb->num_mapped_sgs; -- if (len > 0 && i > 0) { -- sg = urb->sg; -- buf = sg_dma_address(sg); -- -- /* urb->transfer_buffer_length may be smaller than the -- * size of the scatterlist (or vice versa) -- */ -- this_sg_len = min_t(int, sg_dma_len(sg), len); -- } else { -- sg = NULL; -- buf = urb->transfer_dma; -- this_sg_len = len; -- } -- -- if (is_input) -- token |= (1 /* "in" */ << 8); -- /* else it's already initted to "out" pid (0 << 8) */ -- -- maxpacket = usb_maxpacket(urb->dev, urb->pipe); -- -- /* -- * buffer gets wrapped in one or more qtds; -- * last one may be "short" (including zero len) -- * and may serve as a control status ack -- */ -- for (;;) { -- int this_qtd_len; -- -- this_qtd_len = qtd_fill(fotg210, qtd, buf, this_sg_len, token, -- maxpacket); -- this_sg_len -= this_qtd_len; -- len -= this_qtd_len; -- buf += this_qtd_len; -- -- /* -- * short reads advance to a "magic" dummy instead of the next -- * qtd ... that forces the queue to stop, for manual cleanup. -- * (this will usually be overridden later.) -- */ -- if (is_input) -- qtd->hw_alt_next = fotg210->async->hw->hw_alt_next; -- -- /* qh makes control packets use qtd toggle; maybe switch it */ -- if ((maxpacket & (this_qtd_len + (maxpacket - 1))) == 0) -- token ^= QTD_TOGGLE; -- -- if (likely(this_sg_len <= 0)) { -- if (--i <= 0 || len <= 0) -- break; -- sg = sg_next(sg); -- buf = sg_dma_address(sg); -- this_sg_len = min_t(int, sg_dma_len(sg), len); -- } -- -- qtd_prev = qtd; -- qtd = fotg210_qtd_alloc(fotg210, flags); -- if (unlikely(!qtd)) -- goto cleanup; -- qtd->urb = urb; -- qtd_prev->hw_next = QTD_NEXT(fotg210, qtd->qtd_dma); -- list_add_tail(&qtd->qtd_list, head); -- } -- -- /* -- * unless the caller requires manual cleanup after short reads, -- * have the alt_next mechanism keep the queue running after the -- * last data qtd (the only one, for control and most other cases). -- */ -- if (likely((urb->transfer_flags & URB_SHORT_NOT_OK) == 0 || -- usb_pipecontrol(urb->pipe))) -- qtd->hw_alt_next = FOTG210_LIST_END(fotg210); -- -- /* -- * control requests may need a terminating data "status" ack; -- * other OUT ones may need a terminating short packet -- * (zero length). -- */ -- if (likely(urb->transfer_buffer_length != 0)) { -- int one_more = 0; -- -- if (usb_pipecontrol(urb->pipe)) { -- one_more = 1; -- token ^= 0x0100; /* "in" <--> "out" */ -- token |= QTD_TOGGLE; /* force DATA1 */ -- } else if (usb_pipeout(urb->pipe) -- && (urb->transfer_flags & URB_ZERO_PACKET) -- && !(urb->transfer_buffer_length % maxpacket)) { -- one_more = 1; -- } -- if (one_more) { -- qtd_prev = qtd; -- qtd = fotg210_qtd_alloc(fotg210, flags); -- if (unlikely(!qtd)) -- goto cleanup; -- qtd->urb = urb; -- qtd_prev->hw_next = QTD_NEXT(fotg210, qtd->qtd_dma); -- list_add_tail(&qtd->qtd_list, head); -- -- /* never any data in such packets */ -- qtd_fill(fotg210, qtd, 0, 0, token, 0); -- } -- } -- -- /* by default, enable interrupt on urb completion */ -- if (likely(!(urb->transfer_flags & URB_NO_INTERRUPT))) -- qtd->hw_token |= cpu_to_hc32(fotg210, QTD_IOC); -- return head; -- --cleanup: -- qtd_list_free(fotg210, urb, head); -- return NULL; --} -- --/* Would be best to create all qh's from config descriptors, -- * when each interface/altsetting is established. Unlink -- * any previous qh and cancel its urbs first; endpoints are -- * implicitly reset then (data toggle too). -- * That'd mean updating how usbcore talks to HCDs. (2.7?) -- */ -- -- --/* Each QH holds a qtd list; a QH is used for everything except iso. -- * -- * For interrupt urbs, the scheduler must set the microframe scheduling -- * mask(s) each time the QH gets scheduled. For highspeed, that's -- * just one microframe in the s-mask. For split interrupt transactions -- * there are additional complications: c-mask, maybe FSTNs. -- */ --static struct fotg210_qh *qh_make(struct fotg210_hcd *fotg210, struct urb *urb, -- gfp_t flags) --{ -- struct fotg210_qh *qh = fotg210_qh_alloc(fotg210, flags); -- struct usb_host_endpoint *ep; -- u32 info1 = 0, info2 = 0; -- int is_input, type; -- int maxp = 0; -- int mult; -- struct usb_tt *tt = urb->dev->tt; -- struct fotg210_qh_hw *hw; -- -- if (!qh) -- return qh; -- -- /* -- * init endpoint/device data for this QH -- */ -- info1 |= usb_pipeendpoint(urb->pipe) << 8; -- info1 |= usb_pipedevice(urb->pipe) << 0; -- -- is_input = usb_pipein(urb->pipe); -- type = usb_pipetype(urb->pipe); -- ep = usb_pipe_endpoint(urb->dev, urb->pipe); -- maxp = usb_endpoint_maxp(&ep->desc); -- mult = usb_endpoint_maxp_mult(&ep->desc); -- -- /* 1024 byte maxpacket is a hardware ceiling. High bandwidth -- * acts like up to 3KB, but is built from smaller packets. -- */ -- if (maxp > 1024) { -- fotg210_dbg(fotg210, "bogus qh maxpacket %d\n", maxp); -- goto done; -- } -- -- /* Compute interrupt scheduling parameters just once, and save. -- * - allowing for high bandwidth, how many nsec/uframe are used? -- * - split transactions need a second CSPLIT uframe; same question -- * - splits also need a schedule gap (for full/low speed I/O) -- * - qh has a polling interval -- * -- * For control/bulk requests, the HC or TT handles these. -- */ -- if (type == PIPE_INTERRUPT) { -- qh->usecs = NS_TO_US(usb_calc_bus_time(USB_SPEED_HIGH, -- is_input, 0, mult * maxp)); -- qh->start = NO_FRAME; -- -- if (urb->dev->speed == USB_SPEED_HIGH) { -- qh->c_usecs = 0; -- qh->gap_uf = 0; -- -- qh->period = urb->interval >> 3; -- if (qh->period == 0 && urb->interval != 1) { -- /* NOTE interval 2 or 4 uframes could work. -- * But interval 1 scheduling is simpler, and -- * includes high bandwidth. -- */ -- urb->interval = 1; -- } else if (qh->period > fotg210->periodic_size) { -- qh->period = fotg210->periodic_size; -- urb->interval = qh->period << 3; -- } -- } else { -- int think_time; -- -- /* gap is f(FS/LS transfer times) */ -- qh->gap_uf = 1 + usb_calc_bus_time(urb->dev->speed, -- is_input, 0, maxp) / (125 * 1000); -- -- /* FIXME this just approximates SPLIT/CSPLIT times */ -- if (is_input) { /* SPLIT, gap, CSPLIT+DATA */ -- qh->c_usecs = qh->usecs + HS_USECS(0); -- qh->usecs = HS_USECS(1); -- } else { /* SPLIT+DATA, gap, CSPLIT */ -- qh->usecs += HS_USECS(1); -- qh->c_usecs = HS_USECS(0); -- } -- -- think_time = tt ? tt->think_time : 0; -- qh->tt_usecs = NS_TO_US(think_time + -- usb_calc_bus_time(urb->dev->speed, -- is_input, 0, maxp)); -- qh->period = urb->interval; -- if (qh->period > fotg210->periodic_size) { -- qh->period = fotg210->periodic_size; -- urb->interval = qh->period; -- } -- } -- } -- -- /* support for tt scheduling, and access to toggles */ -- qh->dev = urb->dev; -- -- /* using TT? */ -- switch (urb->dev->speed) { -- case USB_SPEED_LOW: -- info1 |= QH_LOW_SPEED; -- fallthrough; -- -- case USB_SPEED_FULL: -- /* EPS 0 means "full" */ -- if (type != PIPE_INTERRUPT) -- info1 |= (FOTG210_TUNE_RL_TT << 28); -- if (type == PIPE_CONTROL) { -- info1 |= QH_CONTROL_EP; /* for TT */ -- info1 |= QH_TOGGLE_CTL; /* toggle from qtd */ -- } -- info1 |= maxp << 16; -- -- info2 |= (FOTG210_TUNE_MULT_TT << 30); -- -- /* Some Freescale processors have an erratum in which the -- * port number in the queue head was 0..N-1 instead of 1..N. -- */ -- if (fotg210_has_fsl_portno_bug(fotg210)) -- info2 |= (urb->dev->ttport-1) << 23; -- else -- info2 |= urb->dev->ttport << 23; -- -- /* set the address of the TT; for TDI's integrated -- * root hub tt, leave it zeroed. -- */ -- if (tt && tt->hub != fotg210_to_hcd(fotg210)->self.root_hub) -- info2 |= tt->hub->devnum << 16; -- -- /* NOTE: if (PIPE_INTERRUPT) { scheduler sets c-mask } */ -- -- break; -- -- case USB_SPEED_HIGH: /* no TT involved */ -- info1 |= QH_HIGH_SPEED; -- if (type == PIPE_CONTROL) { -- info1 |= (FOTG210_TUNE_RL_HS << 28); -- info1 |= 64 << 16; /* usb2 fixed maxpacket */ -- info1 |= QH_TOGGLE_CTL; /* toggle from qtd */ -- info2 |= (FOTG210_TUNE_MULT_HS << 30); -- } else if (type == PIPE_BULK) { -- info1 |= (FOTG210_TUNE_RL_HS << 28); -- /* The USB spec says that high speed bulk endpoints -- * always use 512 byte maxpacket. But some device -- * vendors decided to ignore that, and MSFT is happy -- * to help them do so. So now people expect to use -- * such nonconformant devices with Linux too; sigh. -- */ -- info1 |= maxp << 16; -- info2 |= (FOTG210_TUNE_MULT_HS << 30); -- } else { /* PIPE_INTERRUPT */ -- info1 |= maxp << 16; -- info2 |= mult << 30; -- } -- break; -- default: -- fotg210_dbg(fotg210, "bogus dev %p speed %d\n", urb->dev, -- urb->dev->speed); --done: -- qh_destroy(fotg210, qh); -- return NULL; -- } -- -- /* NOTE: if (PIPE_INTERRUPT) { scheduler sets s-mask } */ -- -- /* init as live, toggle clear, advance to dummy */ -- qh->qh_state = QH_STATE_IDLE; -- hw = qh->hw; -- hw->hw_info1 = cpu_to_hc32(fotg210, info1); -- hw->hw_info2 = cpu_to_hc32(fotg210, info2); -- qh->is_out = !is_input; -- usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe), !is_input, 1); -- qh_refresh(fotg210, qh); -- return qh; --} -- --static void enable_async(struct fotg210_hcd *fotg210) --{ -- if (fotg210->async_count++) -- return; -- -- /* Stop waiting to turn off the async schedule */ -- fotg210->enabled_hrtimer_events &= ~BIT(FOTG210_HRTIMER_DISABLE_ASYNC); -- -- /* Don't start the schedule until ASS is 0 */ -- fotg210_poll_ASS(fotg210); -- turn_on_io_watchdog(fotg210); --} -- --static void disable_async(struct fotg210_hcd *fotg210) --{ -- if (--fotg210->async_count) -- return; -- -- /* The async schedule and async_unlink list are supposed to be empty */ -- WARN_ON(fotg210->async->qh_next.qh || fotg210->async_unlink); -- -- /* Don't turn off the schedule until ASS is 1 */ -- fotg210_poll_ASS(fotg210); --} -- --/* move qh (and its qtds) onto async queue; maybe enable queue. */ -- --static void qh_link_async(struct fotg210_hcd *fotg210, struct fotg210_qh *qh) --{ -- __hc32 dma = QH_NEXT(fotg210, qh->qh_dma); -- struct fotg210_qh *head; -- -- /* Don't link a QH if there's a Clear-TT-Buffer pending */ -- if (unlikely(qh->clearing_tt)) -- return; -- -- WARN_ON(qh->qh_state != QH_STATE_IDLE); -- -- /* clear halt and/or toggle; and maybe recover from silicon quirk */ -- qh_refresh(fotg210, qh); -- -- /* splice right after start */ -- head = fotg210->async; -- qh->qh_next = head->qh_next; -- qh->hw->hw_next = head->hw->hw_next; -- wmb(); -- -- head->qh_next.qh = qh; -- head->hw->hw_next = dma; -- -- qh->xacterrs = 0; -- qh->qh_state = QH_STATE_LINKED; -- /* qtd completions reported later by interrupt */ -- -- enable_async(fotg210); --} -- --/* For control/bulk/interrupt, return QH with these TDs appended. -- * Allocates and initializes the QH if necessary. -- * Returns null if it can't allocate a QH it needs to. -- * If the QH has TDs (urbs) already, that's great. -- */ --static struct fotg210_qh *qh_append_tds(struct fotg210_hcd *fotg210, -- struct urb *urb, struct list_head *qtd_list, -- int epnum, void **ptr) --{ -- struct fotg210_qh *qh = NULL; -- __hc32 qh_addr_mask = cpu_to_hc32(fotg210, 0x7f); -- -- qh = (struct fotg210_qh *) *ptr; -- if (unlikely(qh == NULL)) { -- /* can't sleep here, we have fotg210->lock... */ -- qh = qh_make(fotg210, urb, GFP_ATOMIC); -- *ptr = qh; -- } -- if (likely(qh != NULL)) { -- struct fotg210_qtd *qtd; -- -- if (unlikely(list_empty(qtd_list))) -- qtd = NULL; -- else -- qtd = list_entry(qtd_list->next, struct fotg210_qtd, -- qtd_list); -- -- /* control qh may need patching ... */ -- if (unlikely(epnum == 0)) { -- /* usb_reset_device() briefly reverts to address 0 */ -- if (usb_pipedevice(urb->pipe) == 0) -- qh->hw->hw_info1 &= ~qh_addr_mask; -- } -- -- /* just one way to queue requests: swap with the dummy qtd. -- * only hc or qh_refresh() ever modify the overlay. -- */ -- if (likely(qtd != NULL)) { -- struct fotg210_qtd *dummy; -- dma_addr_t dma; -- __hc32 token; -- -- /* to avoid racing the HC, use the dummy td instead of -- * the first td of our list (becomes new dummy). both -- * tds stay deactivated until we're done, when the -- * HC is allowed to fetch the old dummy (4.10.2). -- */ -- token = qtd->hw_token; -- qtd->hw_token = HALT_BIT(fotg210); -- -- dummy = qh->dummy; -- -- dma = dummy->qtd_dma; -- *dummy = *qtd; -- dummy->qtd_dma = dma; -- -- list_del(&qtd->qtd_list); -- list_add(&dummy->qtd_list, qtd_list); -- list_splice_tail(qtd_list, &qh->qtd_list); -- -- fotg210_qtd_init(fotg210, qtd, qtd->qtd_dma); -- qh->dummy = qtd; -- -- /* hc must see the new dummy at list end */ -- dma = qtd->qtd_dma; -- qtd = list_entry(qh->qtd_list.prev, -- struct fotg210_qtd, qtd_list); -- qtd->hw_next = QTD_NEXT(fotg210, dma); -- -- /* let the hc process these next qtds */ -- wmb(); -- dummy->hw_token = token; -- -- urb->hcpriv = qh; -- } -- } -- return qh; --} -- --static int submit_async(struct fotg210_hcd *fotg210, struct urb *urb, -- struct list_head *qtd_list, gfp_t mem_flags) --{ -- int epnum; -- unsigned long flags; -- struct fotg210_qh *qh = NULL; -- int rc; -- -- epnum = urb->ep->desc.bEndpointAddress; -- --#ifdef FOTG210_URB_TRACE -- { -- struct fotg210_qtd *qtd; -- -- qtd = list_entry(qtd_list->next, struct fotg210_qtd, qtd_list); -- fotg210_dbg(fotg210, -- "%s %s urb %p ep%d%s len %d, qtd %p [qh %p]\n", -- __func__, urb->dev->devpath, urb, -- epnum & 0x0f, (epnum & USB_DIR_IN) -- ? "in" : "out", -- urb->transfer_buffer_length, -- qtd, urb->ep->hcpriv); -- } --#endif -- -- spin_lock_irqsave(&fotg210->lock, flags); -- if (unlikely(!HCD_HW_ACCESSIBLE(fotg210_to_hcd(fotg210)))) { -- rc = -ESHUTDOWN; -- goto done; -- } -- rc = usb_hcd_link_urb_to_ep(fotg210_to_hcd(fotg210), urb); -- if (unlikely(rc)) -- goto done; -- -- qh = qh_append_tds(fotg210, urb, qtd_list, epnum, &urb->ep->hcpriv); -- if (unlikely(qh == NULL)) { -- usb_hcd_unlink_urb_from_ep(fotg210_to_hcd(fotg210), urb); -- rc = -ENOMEM; -- goto done; -- } -- -- /* Control/bulk operations through TTs don't need scheduling, -- * the HC and TT handle it when the TT has a buffer ready. -- */ -- if (likely(qh->qh_state == QH_STATE_IDLE)) -- qh_link_async(fotg210, qh); --done: -- spin_unlock_irqrestore(&fotg210->lock, flags); -- if (unlikely(qh == NULL)) -- qtd_list_free(fotg210, urb, qtd_list); -- return rc; --} -- --static void single_unlink_async(struct fotg210_hcd *fotg210, -- struct fotg210_qh *qh) --{ -- struct fotg210_qh *prev; -- -- /* Add to the end of the list of QHs waiting for the next IAAD */ -- qh->qh_state = QH_STATE_UNLINK; -- if (fotg210->async_unlink) -- fotg210->async_unlink_last->unlink_next = qh; -- else -- fotg210->async_unlink = qh; -- fotg210->async_unlink_last = qh; -- -- /* Unlink it from the schedule */ -- prev = fotg210->async; -- while (prev->qh_next.qh != qh) -- prev = prev->qh_next.qh; -- -- prev->hw->hw_next = qh->hw->hw_next; -- prev->qh_next = qh->qh_next; -- if (fotg210->qh_scan_next == qh) -- fotg210->qh_scan_next = qh->qh_next.qh; --} -- --static void start_iaa_cycle(struct fotg210_hcd *fotg210, bool nested) --{ -- /* -- * Do nothing if an IAA cycle is already running or -- * if one will be started shortly. -- */ -- if (fotg210->async_iaa || fotg210->async_unlinking) -- return; -- -- /* Do all the waiting QHs at once */ -- fotg210->async_iaa = fotg210->async_unlink; -- fotg210->async_unlink = NULL; -- -- /* If the controller isn't running, we don't have to wait for it */ -- if (unlikely(fotg210->rh_state < FOTG210_RH_RUNNING)) { -- if (!nested) /* Avoid recursion */ -- end_unlink_async(fotg210); -- -- /* Otherwise start a new IAA cycle */ -- } else if (likely(fotg210->rh_state == FOTG210_RH_RUNNING)) { -- /* Make sure the unlinks are all visible to the hardware */ -- wmb(); -- -- fotg210_writel(fotg210, fotg210->command | CMD_IAAD, -- &fotg210->regs->command); -- fotg210_readl(fotg210, &fotg210->regs->command); -- fotg210_enable_event(fotg210, FOTG210_HRTIMER_IAA_WATCHDOG, -- true); -- } --} -- --/* the async qh for the qtds being unlinked are now gone from the HC */ -- --static void end_unlink_async(struct fotg210_hcd *fotg210) --{ -- struct fotg210_qh *qh; -- -- /* Process the idle QHs */ --restart: -- fotg210->async_unlinking = true; -- while (fotg210->async_iaa) { -- qh = fotg210->async_iaa; -- fotg210->async_iaa = qh->unlink_next; -- qh->unlink_next = NULL; -- -- qh->qh_state = QH_STATE_IDLE; -- qh->qh_next.qh = NULL; -- -- qh_completions(fotg210, qh); -- if (!list_empty(&qh->qtd_list) && -- fotg210->rh_state == FOTG210_RH_RUNNING) -- qh_link_async(fotg210, qh); -- disable_async(fotg210); -- } -- fotg210->async_unlinking = false; -- -- /* Start a new IAA cycle if any QHs are waiting for it */ -- if (fotg210->async_unlink) { -- start_iaa_cycle(fotg210, true); -- if (unlikely(fotg210->rh_state < FOTG210_RH_RUNNING)) -- goto restart; -- } --} -- --static void unlink_empty_async(struct fotg210_hcd *fotg210) --{ -- struct fotg210_qh *qh, *next; -- bool stopped = (fotg210->rh_state < FOTG210_RH_RUNNING); -- bool check_unlinks_later = false; -- -- /* Unlink all the async QHs that have been empty for a timer cycle */ -- next = fotg210->async->qh_next.qh; -- while (next) { -- qh = next; -- next = qh->qh_next.qh; -- -- if (list_empty(&qh->qtd_list) && -- qh->qh_state == QH_STATE_LINKED) { -- if (!stopped && qh->unlink_cycle == -- fotg210->async_unlink_cycle) -- check_unlinks_later = true; -- else -- single_unlink_async(fotg210, qh); -- } -- } -- -- /* Start a new IAA cycle if any QHs are waiting for it */ -- if (fotg210->async_unlink) -- start_iaa_cycle(fotg210, false); -- -- /* QHs that haven't been empty for long enough will be handled later */ -- if (check_unlinks_later) { -- fotg210_enable_event(fotg210, FOTG210_HRTIMER_ASYNC_UNLINKS, -- true); -- ++fotg210->async_unlink_cycle; -- } --} -- --/* makes sure the async qh will become idle */ --/* caller must own fotg210->lock */ -- --static void start_unlink_async(struct fotg210_hcd *fotg210, -- struct fotg210_qh *qh) --{ -- /* -- * If the QH isn't linked then there's nothing we can do -- * unless we were called during a giveback, in which case -- * qh_completions() has to deal with it. -- */ -- if (qh->qh_state != QH_STATE_LINKED) { -- if (qh->qh_state == QH_STATE_COMPLETING) -- qh->needs_rescan = 1; -- return; -- } -- -- single_unlink_async(fotg210, qh); -- start_iaa_cycle(fotg210, false); --} -- --static void scan_async(struct fotg210_hcd *fotg210) --{ -- struct fotg210_qh *qh; -- bool check_unlinks_later = false; -- -- fotg210->qh_scan_next = fotg210->async->qh_next.qh; -- while (fotg210->qh_scan_next) { -- qh = fotg210->qh_scan_next; -- fotg210->qh_scan_next = qh->qh_next.qh; --rescan: -- /* clean any finished work for this qh */ -- if (!list_empty(&qh->qtd_list)) { -- int temp; -- -- /* -- * Unlinks could happen here; completion reporting -- * drops the lock. That's why fotg210->qh_scan_next -- * always holds the next qh to scan; if the next qh -- * gets unlinked then fotg210->qh_scan_next is adjusted -- * in single_unlink_async(). -- */ -- temp = qh_completions(fotg210, qh); -- if (qh->needs_rescan) { -- start_unlink_async(fotg210, qh); -- } else if (list_empty(&qh->qtd_list) -- && qh->qh_state == QH_STATE_LINKED) { -- qh->unlink_cycle = fotg210->async_unlink_cycle; -- check_unlinks_later = true; -- } else if (temp != 0) -- goto rescan; -- } -- } -- -- /* -- * Unlink empty entries, reducing DMA usage as well -- * as HCD schedule-scanning costs. Delay for any qh -- * we just scanned, there's a not-unusual case that it -- * doesn't stay idle for long. -- */ -- if (check_unlinks_later && fotg210->rh_state == FOTG210_RH_RUNNING && -- !(fotg210->enabled_hrtimer_events & -- BIT(FOTG210_HRTIMER_ASYNC_UNLINKS))) { -- fotg210_enable_event(fotg210, -- FOTG210_HRTIMER_ASYNC_UNLINKS, true); -- ++fotg210->async_unlink_cycle; -- } --} --/* EHCI scheduled transaction support: interrupt, iso, split iso -- * These are called "periodic" transactions in the EHCI spec. -- * -- * Note that for interrupt transfers, the QH/QTD manipulation is shared -- * with the "asynchronous" transaction support (control/bulk transfers). -- * The only real difference is in how interrupt transfers are scheduled. -- * -- * For ISO, we make an "iso_stream" head to serve the same role as a QH. -- * It keeps track of every ITD (or SITD) that's linked, and holds enough -- * pre-calculated schedule data to make appending to the queue be quick. -- */ --static int fotg210_get_frame(struct usb_hcd *hcd); -- --/* periodic_next_shadow - return "next" pointer on shadow list -- * @periodic: host pointer to qh/itd -- * @tag: hardware tag for type of this record -- */ --static union fotg210_shadow *periodic_next_shadow(struct fotg210_hcd *fotg210, -- union fotg210_shadow *periodic, __hc32 tag) --{ -- switch (hc32_to_cpu(fotg210, tag)) { -- case Q_TYPE_QH: -- return &periodic->qh->qh_next; -- case Q_TYPE_FSTN: -- return &periodic->fstn->fstn_next; -- default: -- return &periodic->itd->itd_next; -- } --} -- --static __hc32 *shadow_next_periodic(struct fotg210_hcd *fotg210, -- union fotg210_shadow *periodic, __hc32 tag) --{ -- switch (hc32_to_cpu(fotg210, tag)) { -- /* our fotg210_shadow.qh is actually software part */ -- case Q_TYPE_QH: -- return &periodic->qh->hw->hw_next; -- /* others are hw parts */ -- default: -- return periodic->hw_next; -- } --} -- --/* caller must hold fotg210->lock */ --static void periodic_unlink(struct fotg210_hcd *fotg210, unsigned frame, -- void *ptr) --{ -- union fotg210_shadow *prev_p = &fotg210->pshadow[frame]; -- __hc32 *hw_p = &fotg210->periodic[frame]; -- union fotg210_shadow here = *prev_p; -- -- /* find predecessor of "ptr"; hw and shadow lists are in sync */ -- while (here.ptr && here.ptr != ptr) { -- prev_p = periodic_next_shadow(fotg210, prev_p, -- Q_NEXT_TYPE(fotg210, *hw_p)); -- hw_p = shadow_next_periodic(fotg210, &here, -- Q_NEXT_TYPE(fotg210, *hw_p)); -- here = *prev_p; -- } -- /* an interrupt entry (at list end) could have been shared */ -- if (!here.ptr) -- return; -- -- /* update shadow and hardware lists ... the old "next" pointers -- * from ptr may still be in use, the caller updates them. -- */ -- *prev_p = *periodic_next_shadow(fotg210, &here, -- Q_NEXT_TYPE(fotg210, *hw_p)); -- -- *hw_p = *shadow_next_periodic(fotg210, &here, -- Q_NEXT_TYPE(fotg210, *hw_p)); --} -- --/* how many of the uframe's 125 usecs are allocated? */ --static unsigned short periodic_usecs(struct fotg210_hcd *fotg210, -- unsigned frame, unsigned uframe) --{ -- __hc32 *hw_p = &fotg210->periodic[frame]; -- union fotg210_shadow *q = &fotg210->pshadow[frame]; -- unsigned usecs = 0; -- struct fotg210_qh_hw *hw; -- -- while (q->ptr) { -- switch (hc32_to_cpu(fotg210, Q_NEXT_TYPE(fotg210, *hw_p))) { -- case Q_TYPE_QH: -- hw = q->qh->hw; -- /* is it in the S-mask? */ -- if (hw->hw_info2 & cpu_to_hc32(fotg210, 1 << uframe)) -- usecs += q->qh->usecs; -- /* ... or C-mask? */ -- if (hw->hw_info2 & cpu_to_hc32(fotg210, -- 1 << (8 + uframe))) -- usecs += q->qh->c_usecs; -- hw_p = &hw->hw_next; -- q = &q->qh->qh_next; -- break; -- /* case Q_TYPE_FSTN: */ -- default: -- /* for "save place" FSTNs, count the relevant INTR -- * bandwidth from the previous frame -- */ -- if (q->fstn->hw_prev != FOTG210_LIST_END(fotg210)) -- fotg210_dbg(fotg210, "ignoring FSTN cost ...\n"); -- -- hw_p = &q->fstn->hw_next; -- q = &q->fstn->fstn_next; -- break; -- case Q_TYPE_ITD: -- if (q->itd->hw_transaction[uframe]) -- usecs += q->itd->stream->usecs; -- hw_p = &q->itd->hw_next; -- q = &q->itd->itd_next; -- break; -- } -- } -- if (usecs > fotg210->uframe_periodic_max) -- fotg210_err(fotg210, "uframe %d sched overrun: %d usecs\n", -- frame * 8 + uframe, usecs); -- return usecs; --} -- --static int same_tt(struct usb_device *dev1, struct usb_device *dev2) --{ -- if (!dev1->tt || !dev2->tt) -- return 0; -- if (dev1->tt != dev2->tt) -- return 0; -- if (dev1->tt->multi) -- return dev1->ttport == dev2->ttport; -- else -- return 1; --} -- --/* return true iff the device's transaction translator is available -- * for a periodic transfer starting at the specified frame, using -- * all the uframes in the mask. -- */ --static int tt_no_collision(struct fotg210_hcd *fotg210, unsigned period, -- struct usb_device *dev, unsigned frame, u32 uf_mask) --{ -- if (period == 0) /* error */ -- return 0; -- -- /* note bandwidth wastage: split never follows csplit -- * (different dev or endpoint) until the next uframe. -- * calling convention doesn't make that distinction. -- */ -- for (; frame < fotg210->periodic_size; frame += period) { -- union fotg210_shadow here; -- __hc32 type; -- struct fotg210_qh_hw *hw; -- -- here = fotg210->pshadow[frame]; -- type = Q_NEXT_TYPE(fotg210, fotg210->periodic[frame]); -- while (here.ptr) { -- switch (hc32_to_cpu(fotg210, type)) { -- case Q_TYPE_ITD: -- type = Q_NEXT_TYPE(fotg210, here.itd->hw_next); -- here = here.itd->itd_next; -- continue; -- case Q_TYPE_QH: -- hw = here.qh->hw; -- if (same_tt(dev, here.qh->dev)) { -- u32 mask; -- -- mask = hc32_to_cpu(fotg210, -- hw->hw_info2); -- /* "knows" no gap is needed */ -- mask |= mask >> 8; -- if (mask & uf_mask) -- break; -- } -- type = Q_NEXT_TYPE(fotg210, hw->hw_next); -- here = here.qh->qh_next; -- continue; -- /* case Q_TYPE_FSTN: */ -- default: -- fotg210_dbg(fotg210, -- "periodic frame %d bogus type %d\n", -- frame, type); -- } -- -- /* collision or error */ -- return 0; -- } -- } -- -- /* no collision */ -- return 1; --} -- --static void enable_periodic(struct fotg210_hcd *fotg210) --{ -- if (fotg210->periodic_count++) -- return; -- -- /* Stop waiting to turn off the periodic schedule */ -- fotg210->enabled_hrtimer_events &= -- ~BIT(FOTG210_HRTIMER_DISABLE_PERIODIC); -- -- /* Don't start the schedule until PSS is 0 */ -- fotg210_poll_PSS(fotg210); -- turn_on_io_watchdog(fotg210); --} -- --static void disable_periodic(struct fotg210_hcd *fotg210) --{ -- if (--fotg210->periodic_count) -- return; -- -- /* Don't turn off the schedule until PSS is 1 */ -- fotg210_poll_PSS(fotg210); --} -- --/* periodic schedule slots have iso tds (normal or split) first, then a -- * sparse tree for active interrupt transfers. -- * -- * this just links in a qh; caller guarantees uframe masks are set right. -- * no FSTN support (yet; fotg210 0.96+) -- */ --static void qh_link_periodic(struct fotg210_hcd *fotg210, struct fotg210_qh *qh) --{ -- unsigned i; -- unsigned period = qh->period; -- -- dev_dbg(&qh->dev->dev, -- "link qh%d-%04x/%p start %d [%d/%d us]\n", period, -- hc32_to_cpup(fotg210, &qh->hw->hw_info2) & -- (QH_CMASK | QH_SMASK), qh, qh->start, qh->usecs, -- qh->c_usecs); -- -- /* high bandwidth, or otherwise every microframe */ -- if (period == 0) -- period = 1; -- -- for (i = qh->start; i < fotg210->periodic_size; i += period) { -- union fotg210_shadow *prev = &fotg210->pshadow[i]; -- __hc32 *hw_p = &fotg210->periodic[i]; -- union fotg210_shadow here = *prev; -- __hc32 type = 0; -- -- /* skip the iso nodes at list head */ -- while (here.ptr) { -- type = Q_NEXT_TYPE(fotg210, *hw_p); -- if (type == cpu_to_hc32(fotg210, Q_TYPE_QH)) -- break; -- prev = periodic_next_shadow(fotg210, prev, type); -- hw_p = shadow_next_periodic(fotg210, &here, type); -- here = *prev; -- } -- -- /* sorting each branch by period (slow-->fast) -- * enables sharing interior tree nodes -- */ -- while (here.ptr && qh != here.qh) { -- if (qh->period > here.qh->period) -- break; -- prev = &here.qh->qh_next; -- hw_p = &here.qh->hw->hw_next; -- here = *prev; -- } -- /* link in this qh, unless some earlier pass did that */ -- if (qh != here.qh) { -- qh->qh_next = here; -- if (here.qh) -- qh->hw->hw_next = *hw_p; -- wmb(); -- prev->qh = qh; -- *hw_p = QH_NEXT(fotg210, qh->qh_dma); -- } -- } -- qh->qh_state = QH_STATE_LINKED; -- qh->xacterrs = 0; -- -- /* update per-qh bandwidth for usbfs */ -- fotg210_to_hcd(fotg210)->self.bandwidth_allocated += qh->period -- ? ((qh->usecs + qh->c_usecs) / qh->period) -- : (qh->usecs * 8); -- -- list_add(&qh->intr_node, &fotg210->intr_qh_list); -- -- /* maybe enable periodic schedule processing */ -- ++fotg210->intr_count; -- enable_periodic(fotg210); --} -- --static void qh_unlink_periodic(struct fotg210_hcd *fotg210, -- struct fotg210_qh *qh) --{ -- unsigned i; -- unsigned period; -- -- /* -- * If qh is for a low/full-speed device, simply unlinking it -- * could interfere with an ongoing split transaction. To unlink -- * it safely would require setting the QH_INACTIVATE bit and -- * waiting at least one frame, as described in EHCI 4.12.2.5. -- * -- * We won't bother with any of this. Instead, we assume that the -- * only reason for unlinking an interrupt QH while the current URB -- * is still active is to dequeue all the URBs (flush the whole -- * endpoint queue). -- * -- * If rebalancing the periodic schedule is ever implemented, this -- * approach will no longer be valid. -- */ -- -- /* high bandwidth, or otherwise part of every microframe */ -- period = qh->period; -- if (!period) -- period = 1; -- -- for (i = qh->start; i < fotg210->periodic_size; i += period) -- periodic_unlink(fotg210, i, qh); -- -- /* update per-qh bandwidth for usbfs */ -- fotg210_to_hcd(fotg210)->self.bandwidth_allocated -= qh->period -- ? ((qh->usecs + qh->c_usecs) / qh->period) -- : (qh->usecs * 8); -- -- dev_dbg(&qh->dev->dev, -- "unlink qh%d-%04x/%p start %d [%d/%d us]\n", -- qh->period, hc32_to_cpup(fotg210, &qh->hw->hw_info2) & -- (QH_CMASK | QH_SMASK), qh, qh->start, qh->usecs, -- qh->c_usecs); -- -- /* qh->qh_next still "live" to HC */ -- qh->qh_state = QH_STATE_UNLINK; -- qh->qh_next.ptr = NULL; -- -- if (fotg210->qh_scan_next == qh) -- fotg210->qh_scan_next = list_entry(qh->intr_node.next, -- struct fotg210_qh, intr_node); -- list_del(&qh->intr_node); --} -- --static void start_unlink_intr(struct fotg210_hcd *fotg210, -- struct fotg210_qh *qh) --{ -- /* If the QH isn't linked then there's nothing we can do -- * unless we were called during a giveback, in which case -- * qh_completions() has to deal with it. -- */ -- if (qh->qh_state != QH_STATE_LINKED) { -- if (qh->qh_state == QH_STATE_COMPLETING) -- qh->needs_rescan = 1; -- return; -- } -- -- qh_unlink_periodic(fotg210, qh); -- -- /* Make sure the unlinks are visible before starting the timer */ -- wmb(); -- -- /* -- * The EHCI spec doesn't say how long it takes the controller to -- * stop accessing an unlinked interrupt QH. The timer delay is -- * 9 uframes; presumably that will be long enough. -- */ -- qh->unlink_cycle = fotg210->intr_unlink_cycle; -- -- /* New entries go at the end of the intr_unlink list */ -- if (fotg210->intr_unlink) -- fotg210->intr_unlink_last->unlink_next = qh; -- else -- fotg210->intr_unlink = qh; -- fotg210->intr_unlink_last = qh; -- -- if (fotg210->intr_unlinking) -- ; /* Avoid recursive calls */ -- else if (fotg210->rh_state < FOTG210_RH_RUNNING) -- fotg210_handle_intr_unlinks(fotg210); -- else if (fotg210->intr_unlink == qh) { -- fotg210_enable_event(fotg210, FOTG210_HRTIMER_UNLINK_INTR, -- true); -- ++fotg210->intr_unlink_cycle; -- } --} -- --static void end_unlink_intr(struct fotg210_hcd *fotg210, struct fotg210_qh *qh) --{ -- struct fotg210_qh_hw *hw = qh->hw; -- int rc; -- -- qh->qh_state = QH_STATE_IDLE; -- hw->hw_next = FOTG210_LIST_END(fotg210); -- -- qh_completions(fotg210, qh); -- -- /* reschedule QH iff another request is queued */ -- if (!list_empty(&qh->qtd_list) && -- fotg210->rh_state == FOTG210_RH_RUNNING) { -- rc = qh_schedule(fotg210, qh); -- -- /* An error here likely indicates handshake failure -- * or no space left in the schedule. Neither fault -- * should happen often ... -- * -- * FIXME kill the now-dysfunctional queued urbs -- */ -- if (rc != 0) -- fotg210_err(fotg210, "can't reschedule qh %p, err %d\n", -- qh, rc); -- } -- -- /* maybe turn off periodic schedule */ -- --fotg210->intr_count; -- disable_periodic(fotg210); --} -- --static int check_period(struct fotg210_hcd *fotg210, unsigned frame, -- unsigned uframe, unsigned period, unsigned usecs) --{ -- int claimed; -- -- /* complete split running into next frame? -- * given FSTN support, we could sometimes check... -- */ -- if (uframe >= 8) -- return 0; -- -- /* convert "usecs we need" to "max already claimed" */ -- usecs = fotg210->uframe_periodic_max - usecs; -- -- /* we "know" 2 and 4 uframe intervals were rejected; so -- * for period 0, check _every_ microframe in the schedule. -- */ -- if (unlikely(period == 0)) { -- do { -- for (uframe = 0; uframe < 7; uframe++) { -- claimed = periodic_usecs(fotg210, frame, -- uframe); -- if (claimed > usecs) -- return 0; -- } -- } while ((frame += 1) < fotg210->periodic_size); -- -- /* just check the specified uframe, at that period */ -- } else { -- do { -- claimed = periodic_usecs(fotg210, frame, uframe); -- if (claimed > usecs) -- return 0; -- } while ((frame += period) < fotg210->periodic_size); -- } -- -- /* success! */ -- return 1; --} -- --static int check_intr_schedule(struct fotg210_hcd *fotg210, unsigned frame, -- unsigned uframe, const struct fotg210_qh *qh, __hc32 *c_maskp) --{ -- int retval = -ENOSPC; -- u8 mask = 0; -- -- if (qh->c_usecs && uframe >= 6) /* FSTN territory? */ -- goto done; -- -- if (!check_period(fotg210, frame, uframe, qh->period, qh->usecs)) -- goto done; -- if (!qh->c_usecs) { -- retval = 0; -- *c_maskp = 0; -- goto done; -- } -- -- /* Make sure this tt's buffer is also available for CSPLITs. -- * We pessimize a bit; probably the typical full speed case -- * doesn't need the second CSPLIT. -- * -- * NOTE: both SPLIT and CSPLIT could be checked in just -- * one smart pass... -- */ -- mask = 0x03 << (uframe + qh->gap_uf); -- *c_maskp = cpu_to_hc32(fotg210, mask << 8); -- -- mask |= 1 << uframe; -- if (tt_no_collision(fotg210, qh->period, qh->dev, frame, mask)) { -- if (!check_period(fotg210, frame, uframe + qh->gap_uf + 1, -- qh->period, qh->c_usecs)) -- goto done; -- if (!check_period(fotg210, frame, uframe + qh->gap_uf, -- qh->period, qh->c_usecs)) -- goto done; -- retval = 0; -- } --done: -- return retval; --} -- --/* "first fit" scheduling policy used the first time through, -- * or when the previous schedule slot can't be re-used. -- */ --static int qh_schedule(struct fotg210_hcd *fotg210, struct fotg210_qh *qh) --{ -- int status; -- unsigned uframe; -- __hc32 c_mask; -- unsigned frame; /* 0..(qh->period - 1), or NO_FRAME */ -- struct fotg210_qh_hw *hw = qh->hw; -- -- qh_refresh(fotg210, qh); -- hw->hw_next = FOTG210_LIST_END(fotg210); -- frame = qh->start; -- -- /* reuse the previous schedule slots, if we can */ -- if (frame < qh->period) { -- uframe = ffs(hc32_to_cpup(fotg210, &hw->hw_info2) & QH_SMASK); -- status = check_intr_schedule(fotg210, frame, --uframe, -- qh, &c_mask); -- } else { -- uframe = 0; -- c_mask = 0; -- status = -ENOSPC; -- } -- -- /* else scan the schedule to find a group of slots such that all -- * uframes have enough periodic bandwidth available. -- */ -- if (status) { -- /* "normal" case, uframing flexible except with splits */ -- if (qh->period) { -- int i; -- -- for (i = qh->period; status && i > 0; --i) { -- frame = ++fotg210->random_frame % qh->period; -- for (uframe = 0; uframe < 8; uframe++) { -- status = check_intr_schedule(fotg210, -- frame, uframe, qh, -- &c_mask); -- if (status == 0) -- break; -- } -- } -- -- /* qh->period == 0 means every uframe */ -- } else { -- frame = 0; -- status = check_intr_schedule(fotg210, 0, 0, qh, -- &c_mask); -- } -- if (status) -- goto done; -- qh->start = frame; -- -- /* reset S-frame and (maybe) C-frame masks */ -- hw->hw_info2 &= cpu_to_hc32(fotg210, ~(QH_CMASK | QH_SMASK)); -- hw->hw_info2 |= qh->period -- ? cpu_to_hc32(fotg210, 1 << uframe) -- : cpu_to_hc32(fotg210, QH_SMASK); -- hw->hw_info2 |= c_mask; -- } else -- fotg210_dbg(fotg210, "reused qh %p schedule\n", qh); -- -- /* stuff into the periodic schedule */ -- qh_link_periodic(fotg210, qh); --done: -- return status; --} -- --static int intr_submit(struct fotg210_hcd *fotg210, struct urb *urb, -- struct list_head *qtd_list, gfp_t mem_flags) --{ -- unsigned epnum; -- unsigned long flags; -- struct fotg210_qh *qh; -- int status; -- struct list_head empty; -- -- /* get endpoint and transfer/schedule data */ -- epnum = urb->ep->desc.bEndpointAddress; -- -- spin_lock_irqsave(&fotg210->lock, flags); -- -- if (unlikely(!HCD_HW_ACCESSIBLE(fotg210_to_hcd(fotg210)))) { -- status = -ESHUTDOWN; -- goto done_not_linked; -- } -- status = usb_hcd_link_urb_to_ep(fotg210_to_hcd(fotg210), urb); -- if (unlikely(status)) -- goto done_not_linked; -- -- /* get qh and force any scheduling errors */ -- INIT_LIST_HEAD(&empty); -- qh = qh_append_tds(fotg210, urb, &empty, epnum, &urb->ep->hcpriv); -- if (qh == NULL) { -- status = -ENOMEM; -- goto done; -- } -- if (qh->qh_state == QH_STATE_IDLE) { -- status = qh_schedule(fotg210, qh); -- if (status) -- goto done; -- } -- -- /* then queue the urb's tds to the qh */ -- qh = qh_append_tds(fotg210, urb, qtd_list, epnum, &urb->ep->hcpriv); -- BUG_ON(qh == NULL); -- -- /* ... update usbfs periodic stats */ -- fotg210_to_hcd(fotg210)->self.bandwidth_int_reqs++; -- --done: -- if (unlikely(status)) -- usb_hcd_unlink_urb_from_ep(fotg210_to_hcd(fotg210), urb); --done_not_linked: -- spin_unlock_irqrestore(&fotg210->lock, flags); -- if (status) -- qtd_list_free(fotg210, urb, qtd_list); -- -- return status; --} -- --static void scan_intr(struct fotg210_hcd *fotg210) --{ -- struct fotg210_qh *qh; -- -- list_for_each_entry_safe(qh, fotg210->qh_scan_next, -- &fotg210->intr_qh_list, intr_node) { --rescan: -- /* clean any finished work for this qh */ -- if (!list_empty(&qh->qtd_list)) { -- int temp; -- -- /* -- * Unlinks could happen here; completion reporting -- * drops the lock. That's why fotg210->qh_scan_next -- * always holds the next qh to scan; if the next qh -- * gets unlinked then fotg210->qh_scan_next is adjusted -- * in qh_unlink_periodic(). -- */ -- temp = qh_completions(fotg210, qh); -- if (unlikely(qh->needs_rescan || -- (list_empty(&qh->qtd_list) && -- qh->qh_state == QH_STATE_LINKED))) -- start_unlink_intr(fotg210, qh); -- else if (temp != 0) -- goto rescan; -- } -- } --} -- --/* fotg210_iso_stream ops work with both ITD and SITD */ -- --static struct fotg210_iso_stream *iso_stream_alloc(gfp_t mem_flags) --{ -- struct fotg210_iso_stream *stream; -- -- stream = kzalloc(sizeof(*stream), mem_flags); -- if (likely(stream != NULL)) { -- INIT_LIST_HEAD(&stream->td_list); -- INIT_LIST_HEAD(&stream->free_list); -- stream->next_uframe = -1; -- } -- return stream; --} -- --static void iso_stream_init(struct fotg210_hcd *fotg210, -- struct fotg210_iso_stream *stream, struct usb_device *dev, -- int pipe, unsigned interval) --{ -- u32 buf1; -- unsigned epnum, maxp; -- int is_input; -- long bandwidth; -- unsigned multi; -- struct usb_host_endpoint *ep; -- -- /* -- * this might be a "high bandwidth" highspeed endpoint, -- * as encoded in the ep descriptor's wMaxPacket field -- */ -- epnum = usb_pipeendpoint(pipe); -- is_input = usb_pipein(pipe) ? USB_DIR_IN : 0; -- ep = usb_pipe_endpoint(dev, pipe); -- maxp = usb_endpoint_maxp(&ep->desc); -- if (is_input) -- buf1 = (1 << 11); -- else -- buf1 = 0; -- -- multi = usb_endpoint_maxp_mult(&ep->desc); -- buf1 |= maxp; -- maxp *= multi; -- -- stream->buf0 = cpu_to_hc32(fotg210, (epnum << 8) | dev->devnum); -- stream->buf1 = cpu_to_hc32(fotg210, buf1); -- stream->buf2 = cpu_to_hc32(fotg210, multi); -- -- /* usbfs wants to report the average usecs per frame tied up -- * when transfers on this endpoint are scheduled ... -- */ -- if (dev->speed == USB_SPEED_FULL) { -- interval <<= 3; -- stream->usecs = NS_TO_US(usb_calc_bus_time(dev->speed, -- is_input, 1, maxp)); -- stream->usecs /= 8; -- } else { -- stream->highspeed = 1; -- stream->usecs = HS_USECS_ISO(maxp); -- } -- bandwidth = stream->usecs * 8; -- bandwidth /= interval; -- -- stream->bandwidth = bandwidth; -- stream->udev = dev; -- stream->bEndpointAddress = is_input | epnum; -- stream->interval = interval; -- stream->maxp = maxp; --} -- --static struct fotg210_iso_stream *iso_stream_find(struct fotg210_hcd *fotg210, -- struct urb *urb) --{ -- unsigned epnum; -- struct fotg210_iso_stream *stream; -- struct usb_host_endpoint *ep; -- unsigned long flags; -- -- epnum = usb_pipeendpoint(urb->pipe); -- if (usb_pipein(urb->pipe)) -- ep = urb->dev->ep_in[epnum]; -- else -- ep = urb->dev->ep_out[epnum]; -- -- spin_lock_irqsave(&fotg210->lock, flags); -- stream = ep->hcpriv; -- -- if (unlikely(stream == NULL)) { -- stream = iso_stream_alloc(GFP_ATOMIC); -- if (likely(stream != NULL)) { -- ep->hcpriv = stream; -- stream->ep = ep; -- iso_stream_init(fotg210, stream, urb->dev, urb->pipe, -- urb->interval); -- } -- -- /* if dev->ep[epnum] is a QH, hw is set */ -- } else if (unlikely(stream->hw != NULL)) { -- fotg210_dbg(fotg210, "dev %s ep%d%s, not iso??\n", -- urb->dev->devpath, epnum, -- usb_pipein(urb->pipe) ? "in" : "out"); -- stream = NULL; -- } -- -- spin_unlock_irqrestore(&fotg210->lock, flags); -- return stream; --} -- --/* fotg210_iso_sched ops can be ITD-only or SITD-only */ -- --static struct fotg210_iso_sched *iso_sched_alloc(unsigned packets, -- gfp_t mem_flags) --{ -- struct fotg210_iso_sched *iso_sched; -- -- iso_sched = kzalloc(struct_size(iso_sched, packet, packets), mem_flags); -- if (likely(iso_sched != NULL)) -- INIT_LIST_HEAD(&iso_sched->td_list); -- -- return iso_sched; --} -- --static inline void itd_sched_init(struct fotg210_hcd *fotg210, -- struct fotg210_iso_sched *iso_sched, -- struct fotg210_iso_stream *stream, struct urb *urb) --{ -- unsigned i; -- dma_addr_t dma = urb->transfer_dma; -- -- /* how many uframes are needed for these transfers */ -- iso_sched->span = urb->number_of_packets * stream->interval; -- -- /* figure out per-uframe itd fields that we'll need later -- * when we fit new itds into the schedule. -- */ -- for (i = 0; i < urb->number_of_packets; i++) { -- struct fotg210_iso_packet *uframe = &iso_sched->packet[i]; -- unsigned length; -- dma_addr_t buf; -- u32 trans; -- -- length = urb->iso_frame_desc[i].length; -- buf = dma + urb->iso_frame_desc[i].offset; -- -- trans = FOTG210_ISOC_ACTIVE; -- trans |= buf & 0x0fff; -- if (unlikely(((i + 1) == urb->number_of_packets)) -- && !(urb->transfer_flags & URB_NO_INTERRUPT)) -- trans |= FOTG210_ITD_IOC; -- trans |= length << 16; -- uframe->transaction = cpu_to_hc32(fotg210, trans); -- -- /* might need to cross a buffer page within a uframe */ -- uframe->bufp = (buf & ~(u64)0x0fff); -- buf += length; -- if (unlikely((uframe->bufp != (buf & ~(u64)0x0fff)))) -- uframe->cross = 1; -- } --} -- --static void iso_sched_free(struct fotg210_iso_stream *stream, -- struct fotg210_iso_sched *iso_sched) --{ -- if (!iso_sched) -- return; -- /* caller must hold fotg210->lock!*/ -- list_splice(&iso_sched->td_list, &stream->free_list); -- kfree(iso_sched); --} -- --static int itd_urb_transaction(struct fotg210_iso_stream *stream, -- struct fotg210_hcd *fotg210, struct urb *urb, gfp_t mem_flags) --{ -- struct fotg210_itd *itd; -- dma_addr_t itd_dma; -- int i; -- unsigned num_itds; -- struct fotg210_iso_sched *sched; -- unsigned long flags; -- -- sched = iso_sched_alloc(urb->number_of_packets, mem_flags); -- if (unlikely(sched == NULL)) -- return -ENOMEM; -- -- itd_sched_init(fotg210, sched, stream, urb); -- -- if (urb->interval < 8) -- num_itds = 1 + (sched->span + 7) / 8; -- else -- num_itds = urb->number_of_packets; -- -- /* allocate/init ITDs */ -- spin_lock_irqsave(&fotg210->lock, flags); -- for (i = 0; i < num_itds; i++) { -- -- /* -- * Use iTDs from the free list, but not iTDs that may -- * still be in use by the hardware. -- */ -- if (likely(!list_empty(&stream->free_list))) { -- itd = list_first_entry(&stream->free_list, -- struct fotg210_itd, itd_list); -- if (itd->frame == fotg210->now_frame) -- goto alloc_itd; -- list_del(&itd->itd_list); -- itd_dma = itd->itd_dma; -- } else { --alloc_itd: -- spin_unlock_irqrestore(&fotg210->lock, flags); -- itd = dma_pool_alloc(fotg210->itd_pool, mem_flags, -- &itd_dma); -- spin_lock_irqsave(&fotg210->lock, flags); -- if (!itd) { -- iso_sched_free(stream, sched); -- spin_unlock_irqrestore(&fotg210->lock, flags); -- return -ENOMEM; -- } -- } -- -- memset(itd, 0, sizeof(*itd)); -- itd->itd_dma = itd_dma; -- list_add(&itd->itd_list, &sched->td_list); -- } -- spin_unlock_irqrestore(&fotg210->lock, flags); -- -- /* temporarily store schedule info in hcpriv */ -- urb->hcpriv = sched; -- urb->error_count = 0; -- return 0; --} -- --static inline int itd_slot_ok(struct fotg210_hcd *fotg210, u32 mod, u32 uframe, -- u8 usecs, u32 period) --{ -- uframe %= period; -- do { -- /* can't commit more than uframe_periodic_max usec */ -- if (periodic_usecs(fotg210, uframe >> 3, uframe & 0x7) -- > (fotg210->uframe_periodic_max - usecs)) -- return 0; -- -- /* we know urb->interval is 2^N uframes */ -- uframe += period; -- } while (uframe < mod); -- return 1; --} -- --/* This scheduler plans almost as far into the future as it has actual -- * periodic schedule slots. (Affected by TUNE_FLS, which defaults to -- * "as small as possible" to be cache-friendlier.) That limits the size -- * transfers you can stream reliably; avoid more than 64 msec per urb. -- * Also avoid queue depths of less than fotg210's worst irq latency (affected -- * by the per-urb URB_NO_INTERRUPT hint, the log2_irq_thresh module parameter, -- * and other factors); or more than about 230 msec total (for portability, -- * given FOTG210_TUNE_FLS and the slop). Or, write a smarter scheduler! -- */ -- --#define SCHEDULE_SLOP 80 /* microframes */ -- --static int iso_stream_schedule(struct fotg210_hcd *fotg210, struct urb *urb, -- struct fotg210_iso_stream *stream) --{ -- u32 now, next, start, period, span; -- int status; -- unsigned mod = fotg210->periodic_size << 3; -- struct fotg210_iso_sched *sched = urb->hcpriv; -- -- period = urb->interval; -- span = sched->span; -- -- if (span > mod - SCHEDULE_SLOP) { -- fotg210_dbg(fotg210, "iso request %p too long\n", urb); -- status = -EFBIG; -- goto fail; -- } -- -- now = fotg210_read_frame_index(fotg210) & (mod - 1); -- -- /* Typical case: reuse current schedule, stream is still active. -- * Hopefully there are no gaps from the host falling behind -- * (irq delays etc), but if there are we'll take the next -- * slot in the schedule, implicitly assuming URB_ISO_ASAP. -- */ -- if (likely(!list_empty(&stream->td_list))) { -- u32 excess; -- -- /* For high speed devices, allow scheduling within the -- * isochronous scheduling threshold. For full speed devices -- * and Intel PCI-based controllers, don't (work around for -- * Intel ICH9 bug). -- */ -- if (!stream->highspeed && fotg210->fs_i_thresh) -- next = now + fotg210->i_thresh; -- else -- next = now; -- -- /* Fell behind (by up to twice the slop amount)? -- * We decide based on the time of the last currently-scheduled -- * slot, not the time of the next available slot. -- */ -- excess = (stream->next_uframe - period - next) & (mod - 1); -- if (excess >= mod - 2 * SCHEDULE_SLOP) -- start = next + excess - mod + period * -- DIV_ROUND_UP(mod - excess, period); -- else -- start = next + excess + period; -- if (start - now >= mod) { -- fotg210_dbg(fotg210, "request %p would overflow (%d+%d >= %d)\n", -- urb, start - now - period, period, -- mod); -- status = -EFBIG; -- goto fail; -- } -- } -- -- /* need to schedule; when's the next (u)frame we could start? -- * this is bigger than fotg210->i_thresh allows; scheduling itself -- * isn't free, the slop should handle reasonably slow cpus. it -- * can also help high bandwidth if the dma and irq loads don't -- * jump until after the queue is primed. -- */ -- else { -- int done = 0; -- -- start = SCHEDULE_SLOP + (now & ~0x07); -- -- /* NOTE: assumes URB_ISO_ASAP, to limit complexity/bugs */ -- -- /* find a uframe slot with enough bandwidth. -- * Early uframes are more precious because full-speed -- * iso IN transfers can't use late uframes, -- * and therefore they should be allocated last. -- */ -- next = start; -- start += period; -- do { -- start--; -- /* check schedule: enough space? */ -- if (itd_slot_ok(fotg210, mod, start, -- stream->usecs, period)) -- done = 1; -- } while (start > next && !done); -- -- /* no room in the schedule */ -- if (!done) { -- fotg210_dbg(fotg210, "iso resched full %p (now %d max %d)\n", -- urb, now, now + mod); -- status = -ENOSPC; -- goto fail; -- } -- } -- -- /* Tried to schedule too far into the future? */ -- if (unlikely(start - now + span - period >= -- mod - 2 * SCHEDULE_SLOP)) { -- fotg210_dbg(fotg210, "request %p would overflow (%d+%d >= %d)\n", -- urb, start - now, span - period, -- mod - 2 * SCHEDULE_SLOP); -- status = -EFBIG; -- goto fail; -- } -- -- stream->next_uframe = start & (mod - 1); -- -- /* report high speed start in uframes; full speed, in frames */ -- urb->start_frame = stream->next_uframe; -- if (!stream->highspeed) -- urb->start_frame >>= 3; -- -- /* Make sure scan_isoc() sees these */ -- if (fotg210->isoc_count == 0) -- fotg210->next_frame = now >> 3; -- return 0; -- --fail: -- iso_sched_free(stream, sched); -- urb->hcpriv = NULL; -- return status; --} -- --static inline void itd_init(struct fotg210_hcd *fotg210, -- struct fotg210_iso_stream *stream, struct fotg210_itd *itd) --{ -- int i; -- -- /* it's been recently zeroed */ -- itd->hw_next = FOTG210_LIST_END(fotg210); -- itd->hw_bufp[0] = stream->buf0; -- itd->hw_bufp[1] = stream->buf1; -- itd->hw_bufp[2] = stream->buf2; -- -- for (i = 0; i < 8; i++) -- itd->index[i] = -1; -- -- /* All other fields are filled when scheduling */ --} -- --static inline void itd_patch(struct fotg210_hcd *fotg210, -- struct fotg210_itd *itd, struct fotg210_iso_sched *iso_sched, -- unsigned index, u16 uframe) --{ -- struct fotg210_iso_packet *uf = &iso_sched->packet[index]; -- unsigned pg = itd->pg; -- -- uframe &= 0x07; -- itd->index[uframe] = index; -- -- itd->hw_transaction[uframe] = uf->transaction; -- itd->hw_transaction[uframe] |= cpu_to_hc32(fotg210, pg << 12); -- itd->hw_bufp[pg] |= cpu_to_hc32(fotg210, uf->bufp & ~(u32)0); -- itd->hw_bufp_hi[pg] |= cpu_to_hc32(fotg210, (u32)(uf->bufp >> 32)); -- -- /* iso_frame_desc[].offset must be strictly increasing */ -- if (unlikely(uf->cross)) { -- u64 bufp = uf->bufp + 4096; -- -- itd->pg = ++pg; -- itd->hw_bufp[pg] |= cpu_to_hc32(fotg210, bufp & ~(u32)0); -- itd->hw_bufp_hi[pg] |= cpu_to_hc32(fotg210, (u32)(bufp >> 32)); -- } --} -- --static inline void itd_link(struct fotg210_hcd *fotg210, unsigned frame, -- struct fotg210_itd *itd) --{ -- union fotg210_shadow *prev = &fotg210->pshadow[frame]; -- __hc32 *hw_p = &fotg210->periodic[frame]; -- union fotg210_shadow here = *prev; -- __hc32 type = 0; -- -- /* skip any iso nodes which might belong to previous microframes */ -- while (here.ptr) { -- type = Q_NEXT_TYPE(fotg210, *hw_p); -- if (type == cpu_to_hc32(fotg210, Q_TYPE_QH)) -- break; -- prev = periodic_next_shadow(fotg210, prev, type); -- hw_p = shadow_next_periodic(fotg210, &here, type); -- here = *prev; -- } -- -- itd->itd_next = here; -- itd->hw_next = *hw_p; -- prev->itd = itd; -- itd->frame = frame; -- wmb(); -- *hw_p = cpu_to_hc32(fotg210, itd->itd_dma | Q_TYPE_ITD); --} -- --/* fit urb's itds into the selected schedule slot; activate as needed */ --static void itd_link_urb(struct fotg210_hcd *fotg210, struct urb *urb, -- unsigned mod, struct fotg210_iso_stream *stream) --{ -- int packet; -- unsigned next_uframe, uframe, frame; -- struct fotg210_iso_sched *iso_sched = urb->hcpriv; -- struct fotg210_itd *itd; -- -- next_uframe = stream->next_uframe & (mod - 1); -- -- if (unlikely(list_empty(&stream->td_list))) { -- fotg210_to_hcd(fotg210)->self.bandwidth_allocated -- += stream->bandwidth; -- fotg210_dbg(fotg210, -- "schedule devp %s ep%d%s-iso period %d start %d.%d\n", -- urb->dev->devpath, stream->bEndpointAddress & 0x0f, -- (stream->bEndpointAddress & USB_DIR_IN) ? "in" : "out", -- urb->interval, -- next_uframe >> 3, next_uframe & 0x7); -- } -- -- /* fill iTDs uframe by uframe */ -- for (packet = 0, itd = NULL; packet < urb->number_of_packets;) { -- if (itd == NULL) { -- /* ASSERT: we have all necessary itds */ -- -- /* ASSERT: no itds for this endpoint in this uframe */ -- -- itd = list_entry(iso_sched->td_list.next, -- struct fotg210_itd, itd_list); -- list_move_tail(&itd->itd_list, &stream->td_list); -- itd->stream = stream; -- itd->urb = urb; -- itd_init(fotg210, stream, itd); -- } -- -- uframe = next_uframe & 0x07; -- frame = next_uframe >> 3; -- -- itd_patch(fotg210, itd, iso_sched, packet, uframe); -- -- next_uframe += stream->interval; -- next_uframe &= mod - 1; -- packet++; -- -- /* link completed itds into the schedule */ -- if (((next_uframe >> 3) != frame) -- || packet == urb->number_of_packets) { -- itd_link(fotg210, frame & (fotg210->periodic_size - 1), -- itd); -- itd = NULL; -- } -- } -- stream->next_uframe = next_uframe; -- -- /* don't need that schedule data any more */ -- iso_sched_free(stream, iso_sched); -- urb->hcpriv = NULL; -- -- ++fotg210->isoc_count; -- enable_periodic(fotg210); --} -- --#define ISO_ERRS (FOTG210_ISOC_BUF_ERR | FOTG210_ISOC_BABBLE |\ -- FOTG210_ISOC_XACTERR) -- --/* Process and recycle a completed ITD. Return true iff its urb completed, -- * and hence its completion callback probably added things to the hardware -- * schedule. -- * -- * Note that we carefully avoid recycling this descriptor until after any -- * completion callback runs, so that it won't be reused quickly. That is, -- * assuming (a) no more than two urbs per frame on this endpoint, and also -- * (b) only this endpoint's completions submit URBs. It seems some silicon -- * corrupts things if you reuse completed descriptors very quickly... -- */ --static bool itd_complete(struct fotg210_hcd *fotg210, struct fotg210_itd *itd) --{ -- struct urb *urb = itd->urb; -- struct usb_iso_packet_descriptor *desc; -- u32 t; -- unsigned uframe; -- int urb_index = -1; -- struct fotg210_iso_stream *stream = itd->stream; -- struct usb_device *dev; -- bool retval = false; -- -- /* for each uframe with a packet */ -- for (uframe = 0; uframe < 8; uframe++) { -- if (likely(itd->index[uframe] == -1)) -- continue; -- urb_index = itd->index[uframe]; -- desc = &urb->iso_frame_desc[urb_index]; -- -- t = hc32_to_cpup(fotg210, &itd->hw_transaction[uframe]); -- itd->hw_transaction[uframe] = 0; -- -- /* report transfer status */ -- if (unlikely(t & ISO_ERRS)) { -- urb->error_count++; -- if (t & FOTG210_ISOC_BUF_ERR) -- desc->status = usb_pipein(urb->pipe) -- ? -ENOSR /* hc couldn't read */ -- : -ECOMM; /* hc couldn't write */ -- else if (t & FOTG210_ISOC_BABBLE) -- desc->status = -EOVERFLOW; -- else /* (t & FOTG210_ISOC_XACTERR) */ -- desc->status = -EPROTO; -- -- /* HC need not update length with this error */ -- if (!(t & FOTG210_ISOC_BABBLE)) { -- desc->actual_length = FOTG210_ITD_LENGTH(t); -- urb->actual_length += desc->actual_length; -- } -- } else if (likely((t & FOTG210_ISOC_ACTIVE) == 0)) { -- desc->status = 0; -- desc->actual_length = FOTG210_ITD_LENGTH(t); -- urb->actual_length += desc->actual_length; -- } else { -- /* URB was too late */ -- desc->status = -EXDEV; -- } -- } -- -- /* handle completion now? */ -- if (likely((urb_index + 1) != urb->number_of_packets)) -- goto done; -- -- /* ASSERT: it's really the last itd for this urb -- * list_for_each_entry (itd, &stream->td_list, itd_list) -- * BUG_ON (itd->urb == urb); -- */ -- -- /* give urb back to the driver; completion often (re)submits */ -- dev = urb->dev; -- fotg210_urb_done(fotg210, urb, 0); -- retval = true; -- urb = NULL; -- -- --fotg210->isoc_count; -- disable_periodic(fotg210); -- -- if (unlikely(list_is_singular(&stream->td_list))) { -- fotg210_to_hcd(fotg210)->self.bandwidth_allocated -- -= stream->bandwidth; -- fotg210_dbg(fotg210, -- "deschedule devp %s ep%d%s-iso\n", -- dev->devpath, stream->bEndpointAddress & 0x0f, -- (stream->bEndpointAddress & USB_DIR_IN) ? "in" : "out"); -- } -- --done: -- itd->urb = NULL; -- -- /* Add to the end of the free list for later reuse */ -- list_move_tail(&itd->itd_list, &stream->free_list); -- -- /* Recycle the iTDs when the pipeline is empty (ep no longer in use) */ -- if (list_empty(&stream->td_list)) { -- list_splice_tail_init(&stream->free_list, -- &fotg210->cached_itd_list); -- start_free_itds(fotg210); -- } -- -- return retval; --} -- --static int itd_submit(struct fotg210_hcd *fotg210, struct urb *urb, -- gfp_t mem_flags) --{ -- int status = -EINVAL; -- unsigned long flags; -- struct fotg210_iso_stream *stream; -- -- /* Get iso_stream head */ -- stream = iso_stream_find(fotg210, urb); -- if (unlikely(stream == NULL)) { -- fotg210_dbg(fotg210, "can't get iso stream\n"); -- return -ENOMEM; -- } -- if (unlikely(urb->interval != stream->interval && -- fotg210_port_speed(fotg210, 0) == -- USB_PORT_STAT_HIGH_SPEED)) { -- fotg210_dbg(fotg210, "can't change iso interval %d --> %d\n", -- stream->interval, urb->interval); -- goto done; -- } -- --#ifdef FOTG210_URB_TRACE -- fotg210_dbg(fotg210, -- "%s %s urb %p ep%d%s len %d, %d pkts %d uframes[%p]\n", -- __func__, urb->dev->devpath, urb, -- usb_pipeendpoint(urb->pipe), -- usb_pipein(urb->pipe) ? "in" : "out", -- urb->transfer_buffer_length, -- urb->number_of_packets, urb->interval, -- stream); --#endif -- -- /* allocate ITDs w/o locking anything */ -- status = itd_urb_transaction(stream, fotg210, urb, mem_flags); -- if (unlikely(status < 0)) { -- fotg210_dbg(fotg210, "can't init itds\n"); -- goto done; -- } -- -- /* schedule ... need to lock */ -- spin_lock_irqsave(&fotg210->lock, flags); -- if (unlikely(!HCD_HW_ACCESSIBLE(fotg210_to_hcd(fotg210)))) { -- status = -ESHUTDOWN; -- goto done_not_linked; -- } -- status = usb_hcd_link_urb_to_ep(fotg210_to_hcd(fotg210), urb); -- if (unlikely(status)) -- goto done_not_linked; -- status = iso_stream_schedule(fotg210, urb, stream); -- if (likely(status == 0)) -- itd_link_urb(fotg210, urb, fotg210->periodic_size << 3, stream); -- else -- usb_hcd_unlink_urb_from_ep(fotg210_to_hcd(fotg210), urb); --done_not_linked: -- spin_unlock_irqrestore(&fotg210->lock, flags); --done: -- return status; --} -- --static inline int scan_frame_queue(struct fotg210_hcd *fotg210, unsigned frame, -- unsigned now_frame, bool live) --{ -- unsigned uf; -- bool modified; -- union fotg210_shadow q, *q_p; -- __hc32 type, *hw_p; -- -- /* scan each element in frame's queue for completions */ -- q_p = &fotg210->pshadow[frame]; -- hw_p = &fotg210->periodic[frame]; -- q.ptr = q_p->ptr; -- type = Q_NEXT_TYPE(fotg210, *hw_p); -- modified = false; -- -- while (q.ptr) { -- switch (hc32_to_cpu(fotg210, type)) { -- case Q_TYPE_ITD: -- /* If this ITD is still active, leave it for -- * later processing ... check the next entry. -- * No need to check for activity unless the -- * frame is current. -- */ -- if (frame == now_frame && live) { -- rmb(); -- for (uf = 0; uf < 8; uf++) { -- if (q.itd->hw_transaction[uf] & -- ITD_ACTIVE(fotg210)) -- break; -- } -- if (uf < 8) { -- q_p = &q.itd->itd_next; -- hw_p = &q.itd->hw_next; -- type = Q_NEXT_TYPE(fotg210, -- q.itd->hw_next); -- q = *q_p; -- break; -- } -- } -- -- /* Take finished ITDs out of the schedule -- * and process them: recycle, maybe report -- * URB completion. HC won't cache the -- * pointer for much longer, if at all. -- */ -- *q_p = q.itd->itd_next; -- *hw_p = q.itd->hw_next; -- type = Q_NEXT_TYPE(fotg210, q.itd->hw_next); -- wmb(); -- modified = itd_complete(fotg210, q.itd); -- q = *q_p; -- break; -- default: -- fotg210_dbg(fotg210, "corrupt type %d frame %d shadow %p\n", -- type, frame, q.ptr); -- fallthrough; -- case Q_TYPE_QH: -- case Q_TYPE_FSTN: -- /* End of the iTDs and siTDs */ -- q.ptr = NULL; -- break; -- } -- -- /* assume completion callbacks modify the queue */ -- if (unlikely(modified && fotg210->isoc_count > 0)) -- return -EINVAL; -- } -- return 0; --} -- --static void scan_isoc(struct fotg210_hcd *fotg210) --{ -- unsigned uf, now_frame, frame, ret; -- unsigned fmask = fotg210->periodic_size - 1; -- bool live; -- -- /* -- * When running, scan from last scan point up to "now" -- * else clean up by scanning everything that's left. -- * Touches as few pages as possible: cache-friendly. -- */ -- if (fotg210->rh_state >= FOTG210_RH_RUNNING) { -- uf = fotg210_read_frame_index(fotg210); -- now_frame = (uf >> 3) & fmask; -- live = true; -- } else { -- now_frame = (fotg210->next_frame - 1) & fmask; -- live = false; -- } -- fotg210->now_frame = now_frame; -- -- frame = fotg210->next_frame; -- for (;;) { -- ret = 1; -- while (ret != 0) -- ret = scan_frame_queue(fotg210, frame, -- now_frame, live); -- -- /* Stop when we have reached the current frame */ -- if (frame == now_frame) -- break; -- frame = (frame + 1) & fmask; -- } -- fotg210->next_frame = now_frame; --} -- --/* Display / Set uframe_periodic_max -- */ --static ssize_t uframe_periodic_max_show(struct device *dev, -- struct device_attribute *attr, char *buf) --{ -- struct fotg210_hcd *fotg210; -- int n; -- -- fotg210 = hcd_to_fotg210(bus_to_hcd(dev_get_drvdata(dev))); -- n = scnprintf(buf, PAGE_SIZE, "%d\n", fotg210->uframe_periodic_max); -- return n; --} -- -- --static ssize_t uframe_periodic_max_store(struct device *dev, -- struct device_attribute *attr, const char *buf, size_t count) --{ -- struct fotg210_hcd *fotg210; -- unsigned uframe_periodic_max; -- unsigned frame, uframe; -- unsigned short allocated_max; -- unsigned long flags; -- ssize_t ret; -- -- fotg210 = hcd_to_fotg210(bus_to_hcd(dev_get_drvdata(dev))); -- if (kstrtouint(buf, 0, &uframe_periodic_max) < 0) -- return -EINVAL; -- -- if (uframe_periodic_max < 100 || uframe_periodic_max >= 125) { -- fotg210_info(fotg210, "rejecting invalid request for uframe_periodic_max=%u\n", -- uframe_periodic_max); -- return -EINVAL; -- } -- -- ret = -EINVAL; -- -- /* -- * lock, so that our checking does not race with possible periodic -- * bandwidth allocation through submitting new urbs. -- */ -- spin_lock_irqsave(&fotg210->lock, flags); -- -- /* -- * for request to decrease max periodic bandwidth, we have to check -- * every microframe in the schedule to see whether the decrease is -- * possible. -- */ -- if (uframe_periodic_max < fotg210->uframe_periodic_max) { -- allocated_max = 0; -- -- for (frame = 0; frame < fotg210->periodic_size; ++frame) -- for (uframe = 0; uframe < 7; ++uframe) -- allocated_max = max(allocated_max, -- periodic_usecs(fotg210, frame, -- uframe)); -- -- if (allocated_max > uframe_periodic_max) { -- fotg210_info(fotg210, -- "cannot decrease uframe_periodic_max because periodic bandwidth is already allocated (%u > %u)\n", -- allocated_max, uframe_periodic_max); -- goto out_unlock; -- } -- } -- -- /* increasing is always ok */ -- -- fotg210_info(fotg210, -- "setting max periodic bandwidth to %u%% (== %u usec/uframe)\n", -- 100 * uframe_periodic_max/125, uframe_periodic_max); -- -- if (uframe_periodic_max != 100) -- fotg210_warn(fotg210, "max periodic bandwidth set is non-standard\n"); -- -- fotg210->uframe_periodic_max = uframe_periodic_max; -- ret = count; -- --out_unlock: -- spin_unlock_irqrestore(&fotg210->lock, flags); -- return ret; --} -- --static DEVICE_ATTR_RW(uframe_periodic_max); -- --static inline int create_sysfs_files(struct fotg210_hcd *fotg210) --{ -- struct device *controller = fotg210_to_hcd(fotg210)->self.controller; -- -- return device_create_file(controller, &dev_attr_uframe_periodic_max); --} -- --static inline void remove_sysfs_files(struct fotg210_hcd *fotg210) --{ -- struct device *controller = fotg210_to_hcd(fotg210)->self.controller; -- -- device_remove_file(controller, &dev_attr_uframe_periodic_max); --} --/* On some systems, leaving remote wakeup enabled prevents system shutdown. -- * The firmware seems to think that powering off is a wakeup event! -- * This routine turns off remote wakeup and everything else, on all ports. -- */ --static void fotg210_turn_off_all_ports(struct fotg210_hcd *fotg210) --{ -- u32 __iomem *status_reg = &fotg210->regs->port_status; -- -- fotg210_writel(fotg210, PORT_RWC_BITS, status_reg); --} -- --/* Halt HC, turn off all ports, and let the BIOS use the companion controllers. -- * Must be called with interrupts enabled and the lock not held. -- */ --static void fotg210_silence_controller(struct fotg210_hcd *fotg210) --{ -- fotg210_halt(fotg210); -- -- spin_lock_irq(&fotg210->lock); -- fotg210->rh_state = FOTG210_RH_HALTED; -- fotg210_turn_off_all_ports(fotg210); -- spin_unlock_irq(&fotg210->lock); --} -- --/* fotg210_shutdown kick in for silicon on any bus (not just pci, etc). -- * This forcibly disables dma and IRQs, helping kexec and other cases -- * where the next system software may expect clean state. -- */ --static void fotg210_shutdown(struct usb_hcd *hcd) --{ -- struct fotg210_hcd *fotg210 = hcd_to_fotg210(hcd); -- -- spin_lock_irq(&fotg210->lock); -- fotg210->shutdown = true; -- fotg210->rh_state = FOTG210_RH_STOPPING; -- fotg210->enabled_hrtimer_events = 0; -- spin_unlock_irq(&fotg210->lock); -- -- fotg210_silence_controller(fotg210); -- -- hrtimer_cancel(&fotg210->hrtimer); --} -- --/* fotg210_work is called from some interrupts, timers, and so on. -- * it calls driver completion functions, after dropping fotg210->lock. -- */ --static void fotg210_work(struct fotg210_hcd *fotg210) --{ -- /* another CPU may drop fotg210->lock during a schedule scan while -- * it reports urb completions. this flag guards against bogus -- * attempts at re-entrant schedule scanning. -- */ -- if (fotg210->scanning) { -- fotg210->need_rescan = true; -- return; -- } -- fotg210->scanning = true; -- --rescan: -- fotg210->need_rescan = false; -- if (fotg210->async_count) -- scan_async(fotg210); -- if (fotg210->intr_count > 0) -- scan_intr(fotg210); -- if (fotg210->isoc_count > 0) -- scan_isoc(fotg210); -- if (fotg210->need_rescan) -- goto rescan; -- fotg210->scanning = false; -- -- /* the IO watchdog guards against hardware or driver bugs that -- * misplace IRQs, and should let us run completely without IRQs. -- * such lossage has been observed on both VT6202 and VT8235. -- */ -- turn_on_io_watchdog(fotg210); --} -- --/* Called when the fotg210_hcd module is removed. -- */ --static void fotg210_stop(struct usb_hcd *hcd) --{ -- struct fotg210_hcd *fotg210 = hcd_to_fotg210(hcd); -- -- fotg210_dbg(fotg210, "stop\n"); -- -- /* no more interrupts ... */ -- -- spin_lock_irq(&fotg210->lock); -- fotg210->enabled_hrtimer_events = 0; -- spin_unlock_irq(&fotg210->lock); -- -- fotg210_quiesce(fotg210); -- fotg210_silence_controller(fotg210); -- fotg210_reset(fotg210); -- -- hrtimer_cancel(&fotg210->hrtimer); -- remove_sysfs_files(fotg210); -- remove_debug_files(fotg210); -- -- /* root hub is shut down separately (first, when possible) */ -- spin_lock_irq(&fotg210->lock); -- end_free_itds(fotg210); -- spin_unlock_irq(&fotg210->lock); -- fotg210_mem_cleanup(fotg210); -- --#ifdef FOTG210_STATS -- fotg210_dbg(fotg210, "irq normal %ld err %ld iaa %ld (lost %ld)\n", -- fotg210->stats.normal, fotg210->stats.error, -- fotg210->stats.iaa, fotg210->stats.lost_iaa); -- fotg210_dbg(fotg210, "complete %ld unlink %ld\n", -- fotg210->stats.complete, fotg210->stats.unlink); --#endif -- -- dbg_status(fotg210, "fotg210_stop completed", -- fotg210_readl(fotg210, &fotg210->regs->status)); --} -- --/* one-time init, only for memory state */ --static int hcd_fotg210_init(struct usb_hcd *hcd) --{ -- struct fotg210_hcd *fotg210 = hcd_to_fotg210(hcd); -- u32 temp; -- int retval; -- u32 hcc_params; -- struct fotg210_qh_hw *hw; -- -- spin_lock_init(&fotg210->lock); -- -- /* -- * keep io watchdog by default, those good HCDs could turn off it later -- */ -- fotg210->need_io_watchdog = 1; -- -- hrtimer_init(&fotg210->hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS); -- fotg210->hrtimer.function = fotg210_hrtimer_func; -- fotg210->next_hrtimer_event = FOTG210_HRTIMER_NO_EVENT; -- -- hcc_params = fotg210_readl(fotg210, &fotg210->caps->hcc_params); -- -- /* -- * by default set standard 80% (== 100 usec/uframe) max periodic -- * bandwidth as required by USB 2.0 -- */ -- fotg210->uframe_periodic_max = 100; -- -- /* -- * hw default: 1K periodic list heads, one per frame. -- * periodic_size can shrink by USBCMD update if hcc_params allows. -- */ -- fotg210->periodic_size = DEFAULT_I_TDPS; -- INIT_LIST_HEAD(&fotg210->intr_qh_list); -- INIT_LIST_HEAD(&fotg210->cached_itd_list); -- -- if (HCC_PGM_FRAMELISTLEN(hcc_params)) { -- /* periodic schedule size can be smaller than default */ -- switch (FOTG210_TUNE_FLS) { -- case 0: -- fotg210->periodic_size = 1024; -- break; -- case 1: -- fotg210->periodic_size = 512; -- break; -- case 2: -- fotg210->periodic_size = 256; -- break; -- default: -- BUG(); -- } -- } -- retval = fotg210_mem_init(fotg210, GFP_KERNEL); -- if (retval < 0) -- return retval; -- -- /* controllers may cache some of the periodic schedule ... */ -- fotg210->i_thresh = 2; -- -- /* -- * dedicate a qh for the async ring head, since we couldn't unlink -- * a 'real' qh without stopping the async schedule [4.8]. use it -- * as the 'reclamation list head' too. -- * its dummy is used in hw_alt_next of many tds, to prevent the qh -- * from automatically advancing to the next td after short reads. -- */ -- fotg210->async->qh_next.qh = NULL; -- hw = fotg210->async->hw; -- hw->hw_next = QH_NEXT(fotg210, fotg210->async->qh_dma); -- hw->hw_info1 = cpu_to_hc32(fotg210, QH_HEAD); -- hw->hw_token = cpu_to_hc32(fotg210, QTD_STS_HALT); -- hw->hw_qtd_next = FOTG210_LIST_END(fotg210); -- fotg210->async->qh_state = QH_STATE_LINKED; -- hw->hw_alt_next = QTD_NEXT(fotg210, fotg210->async->dummy->qtd_dma); -- -- /* clear interrupt enables, set irq latency */ -- if (log2_irq_thresh < 0 || log2_irq_thresh > 6) -- log2_irq_thresh = 0; -- temp = 1 << (16 + log2_irq_thresh); -- if (HCC_CANPARK(hcc_params)) { -- /* HW default park == 3, on hardware that supports it (like -- * NVidia and ALI silicon), maximizes throughput on the async -- * schedule by avoiding QH fetches between transfers. -- * -- * With fast usb storage devices and NForce2, "park" seems to -- * make problems: throughput reduction (!), data errors... -- */ -- if (park) { -- park = min_t(unsigned, park, 3); -- temp |= CMD_PARK; -- temp |= park << 8; -- } -- fotg210_dbg(fotg210, "park %d\n", park); -- } -- if (HCC_PGM_FRAMELISTLEN(hcc_params)) { -- /* periodic schedule size can be smaller than default */ -- temp &= ~(3 << 2); -- temp |= (FOTG210_TUNE_FLS << 2); -- } -- fotg210->command = temp; -- -- /* Accept arbitrarily long scatter-gather lists */ -- if (!hcd->localmem_pool) -- hcd->self.sg_tablesize = ~0; -- return 0; --} -- --/* start HC running; it's halted, hcd_fotg210_init() has been run (once) */ --static int fotg210_run(struct usb_hcd *hcd) --{ -- struct fotg210_hcd *fotg210 = hcd_to_fotg210(hcd); -- u32 temp; -- -- hcd->uses_new_polling = 1; -- -- /* EHCI spec section 4.1 */ -- -- fotg210_writel(fotg210, fotg210->periodic_dma, -- &fotg210->regs->frame_list); -- fotg210_writel(fotg210, (u32)fotg210->async->qh_dma, -- &fotg210->regs->async_next); -- -- /* -- * hcc_params controls whether fotg210->regs->segment must (!!!) -- * be used; it constrains QH/ITD/SITD and QTD locations. -- * dma_pool consistent memory always uses segment zero. -- * streaming mappings for I/O buffers, like dma_map_single(), -- * can return segments above 4GB, if the device allows. -- * -- * NOTE: the dma mask is visible through dev->dma_mask, so -- * drivers can pass this info along ... like NETIF_F_HIGHDMA, -- * Scsi_Host.highmem_io, and so forth. It's readonly to all -- * host side drivers though. -- */ -- fotg210_readl(fotg210, &fotg210->caps->hcc_params); -- -- /* -- * Philips, Intel, and maybe others need CMD_RUN before the -- * root hub will detect new devices (why?); NEC doesn't -- */ -- fotg210->command &= ~(CMD_IAAD|CMD_PSE|CMD_ASE|CMD_RESET); -- fotg210->command |= CMD_RUN; -- fotg210_writel(fotg210, fotg210->command, &fotg210->regs->command); -- dbg_cmd(fotg210, "init", fotg210->command); -- -- /* -- * Start, enabling full USB 2.0 functionality ... usb 1.1 devices -- * are explicitly handed to companion controller(s), so no TT is -- * involved with the root hub. (Except where one is integrated, -- * and there's no companion controller unless maybe for USB OTG.) -- * -- * Turning on the CF flag will transfer ownership of all ports -- * from the companions to the EHCI controller. If any of the -- * companions are in the middle of a port reset at the time, it -- * could cause trouble. Write-locking ehci_cf_port_reset_rwsem -- * guarantees that no resets are in progress. After we set CF, -- * a short delay lets the hardware catch up; new resets shouldn't -- * be started before the port switching actions could complete. -- */ -- down_write(&ehci_cf_port_reset_rwsem); -- fotg210->rh_state = FOTG210_RH_RUNNING; -- /* unblock posted writes */ -- fotg210_readl(fotg210, &fotg210->regs->command); -- usleep_range(5000, 10000); -- up_write(&ehci_cf_port_reset_rwsem); -- fotg210->last_periodic_enable = ktime_get_real(); -- -- temp = HC_VERSION(fotg210, -- fotg210_readl(fotg210, &fotg210->caps->hc_capbase)); -- fotg210_info(fotg210, -- "USB %x.%x started, EHCI %x.%02x\n", -- ((fotg210->sbrn & 0xf0) >> 4), (fotg210->sbrn & 0x0f), -- temp >> 8, temp & 0xff); -- -- fotg210_writel(fotg210, INTR_MASK, -- &fotg210->regs->intr_enable); /* Turn On Interrupts */ -- -- /* GRR this is run-once init(), being done every time the HC starts. -- * So long as they're part of class devices, we can't do it init() -- * since the class device isn't created that early. -- */ -- create_debug_files(fotg210); -- create_sysfs_files(fotg210); -- -- return 0; --} -- --static int fotg210_setup(struct usb_hcd *hcd) --{ -- struct fotg210_hcd *fotg210 = hcd_to_fotg210(hcd); -- int retval; -- -- fotg210->regs = (void __iomem *)fotg210->caps + -- HC_LENGTH(fotg210, -- fotg210_readl(fotg210, &fotg210->caps->hc_capbase)); -- dbg_hcs_params(fotg210, "reset"); -- dbg_hcc_params(fotg210, "reset"); -- -- /* cache this readonly data; minimize chip reads */ -- fotg210->hcs_params = fotg210_readl(fotg210, -- &fotg210->caps->hcs_params); -- -- fotg210->sbrn = HCD_USB2; -- -- /* data structure init */ -- retval = hcd_fotg210_init(hcd); -- if (retval) -- return retval; -- -- retval = fotg210_halt(fotg210); -- if (retval) -- return retval; -- -- fotg210_reset(fotg210); -- -- return 0; --} -- --static irqreturn_t fotg210_irq(struct usb_hcd *hcd) --{ -- struct fotg210_hcd *fotg210 = hcd_to_fotg210(hcd); -- u32 status, masked_status, pcd_status = 0, cmd; -- int bh; -- -- spin_lock(&fotg210->lock); -- -- status = fotg210_readl(fotg210, &fotg210->regs->status); -- -- /* e.g. cardbus physical eject */ -- if (status == ~(u32) 0) { -- fotg210_dbg(fotg210, "device removed\n"); -- goto dead; -- } -- -- /* -- * We don't use STS_FLR, but some controllers don't like it to -- * remain on, so mask it out along with the other status bits. -- */ -- masked_status = status & (INTR_MASK | STS_FLR); -- -- /* Shared IRQ? */ -- if (!masked_status || -- unlikely(fotg210->rh_state == FOTG210_RH_HALTED)) { -- spin_unlock(&fotg210->lock); -- return IRQ_NONE; -- } -- -- /* clear (just) interrupts */ -- fotg210_writel(fotg210, masked_status, &fotg210->regs->status); -- cmd = fotg210_readl(fotg210, &fotg210->regs->command); -- bh = 0; -- -- /* unrequested/ignored: Frame List Rollover */ -- dbg_status(fotg210, "irq", status); -- -- /* INT, ERR, and IAA interrupt rates can be throttled */ -- -- /* normal [4.15.1.2] or error [4.15.1.1] completion */ -- if (likely((status & (STS_INT|STS_ERR)) != 0)) { -- if (likely((status & STS_ERR) == 0)) -- INCR(fotg210->stats.normal); -- else -- INCR(fotg210->stats.error); -- bh = 1; -- } -- -- /* complete the unlinking of some qh [4.15.2.3] */ -- if (status & STS_IAA) { -- -- /* Turn off the IAA watchdog */ -- fotg210->enabled_hrtimer_events &= -- ~BIT(FOTG210_HRTIMER_IAA_WATCHDOG); -- -- /* -- * Mild optimization: Allow another IAAD to reset the -- * hrtimer, if one occurs before the next expiration. -- * In theory we could always cancel the hrtimer, but -- * tests show that about half the time it will be reset -- * for some other event anyway. -- */ -- if (fotg210->next_hrtimer_event == FOTG210_HRTIMER_IAA_WATCHDOG) -- ++fotg210->next_hrtimer_event; -- -- /* guard against (alleged) silicon errata */ -- if (cmd & CMD_IAAD) -- fotg210_dbg(fotg210, "IAA with IAAD still set?\n"); -- if (fotg210->async_iaa) { -- INCR(fotg210->stats.iaa); -- end_unlink_async(fotg210); -- } else -- fotg210_dbg(fotg210, "IAA with nothing unlinked?\n"); -- } -- -- /* remote wakeup [4.3.1] */ -- if (status & STS_PCD) { -- int pstatus; -- u32 __iomem *status_reg = &fotg210->regs->port_status; -- -- /* kick root hub later */ -- pcd_status = status; -- -- /* resume root hub? */ -- if (fotg210->rh_state == FOTG210_RH_SUSPENDED) -- usb_hcd_resume_root_hub(hcd); -- -- pstatus = fotg210_readl(fotg210, status_reg); -- -- if (test_bit(0, &fotg210->suspended_ports) && -- ((pstatus & PORT_RESUME) || -- !(pstatus & PORT_SUSPEND)) && -- (pstatus & PORT_PE) && -- fotg210->reset_done[0] == 0) { -- -- /* start 20 msec resume signaling from this port, -- * and make hub_wq collect PORT_STAT_C_SUSPEND to -- * stop that signaling. Use 5 ms extra for safety, -- * like usb_port_resume() does. -- */ -- fotg210->reset_done[0] = jiffies + msecs_to_jiffies(25); -- set_bit(0, &fotg210->resuming_ports); -- fotg210_dbg(fotg210, "port 1 remote wakeup\n"); -- mod_timer(&hcd->rh_timer, fotg210->reset_done[0]); -- } -- } -- -- /* PCI errors [4.15.2.4] */ -- if (unlikely((status & STS_FATAL) != 0)) { -- fotg210_err(fotg210, "fatal error\n"); -- dbg_cmd(fotg210, "fatal", cmd); -- dbg_status(fotg210, "fatal", status); --dead: -- usb_hc_died(hcd); -- -- /* Don't let the controller do anything more */ -- fotg210->shutdown = true; -- fotg210->rh_state = FOTG210_RH_STOPPING; -- fotg210->command &= ~(CMD_RUN | CMD_ASE | CMD_PSE); -- fotg210_writel(fotg210, fotg210->command, -- &fotg210->regs->command); -- fotg210_writel(fotg210, 0, &fotg210->regs->intr_enable); -- fotg210_handle_controller_death(fotg210); -- -- /* Handle completions when the controller stops */ -- bh = 0; -- } -- -- if (bh) -- fotg210_work(fotg210); -- spin_unlock(&fotg210->lock); -- if (pcd_status) -- usb_hcd_poll_rh_status(hcd); -- return IRQ_HANDLED; --} -- --/* non-error returns are a promise to giveback() the urb later -- * we drop ownership so next owner (or urb unlink) can get it -- * -- * urb + dev is in hcd.self.controller.urb_list -- * we're queueing TDs onto software and hardware lists -- * -- * hcd-specific init for hcpriv hasn't been done yet -- * -- * NOTE: control, bulk, and interrupt share the same code to append TDs -- * to a (possibly active) QH, and the same QH scanning code. -- */ --static int fotg210_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, -- gfp_t mem_flags) --{ -- struct fotg210_hcd *fotg210 = hcd_to_fotg210(hcd); -- struct list_head qtd_list; -- -- INIT_LIST_HEAD(&qtd_list); -- -- switch (usb_pipetype(urb->pipe)) { -- case PIPE_CONTROL: -- /* qh_completions() code doesn't handle all the fault cases -- * in multi-TD control transfers. Even 1KB is rare anyway. -- */ -- if (urb->transfer_buffer_length > (16 * 1024)) -- return -EMSGSIZE; -- fallthrough; -- /* case PIPE_BULK: */ -- default: -- if (!qh_urb_transaction(fotg210, urb, &qtd_list, mem_flags)) -- return -ENOMEM; -- return submit_async(fotg210, urb, &qtd_list, mem_flags); -- -- case PIPE_INTERRUPT: -- if (!qh_urb_transaction(fotg210, urb, &qtd_list, mem_flags)) -- return -ENOMEM; -- return intr_submit(fotg210, urb, &qtd_list, mem_flags); -- -- case PIPE_ISOCHRONOUS: -- return itd_submit(fotg210, urb, mem_flags); -- } --} -- --/* remove from hardware lists -- * completions normally happen asynchronously -- */ -- --static int fotg210_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) --{ -- struct fotg210_hcd *fotg210 = hcd_to_fotg210(hcd); -- struct fotg210_qh *qh; -- unsigned long flags; -- int rc; -- -- spin_lock_irqsave(&fotg210->lock, flags); -- rc = usb_hcd_check_unlink_urb(hcd, urb, status); -- if (rc) -- goto done; -- -- switch (usb_pipetype(urb->pipe)) { -- /* case PIPE_CONTROL: */ -- /* case PIPE_BULK:*/ -- default: -- qh = (struct fotg210_qh *) urb->hcpriv; -- if (!qh) -- break; -- switch (qh->qh_state) { -- case QH_STATE_LINKED: -- case QH_STATE_COMPLETING: -- start_unlink_async(fotg210, qh); -- break; -- case QH_STATE_UNLINK: -- case QH_STATE_UNLINK_WAIT: -- /* already started */ -- break; -- case QH_STATE_IDLE: -- /* QH might be waiting for a Clear-TT-Buffer */ -- qh_completions(fotg210, qh); -- break; -- } -- break; -- -- case PIPE_INTERRUPT: -- qh = (struct fotg210_qh *) urb->hcpriv; -- if (!qh) -- break; -- switch (qh->qh_state) { -- case QH_STATE_LINKED: -- case QH_STATE_COMPLETING: -- start_unlink_intr(fotg210, qh); -- break; -- case QH_STATE_IDLE: -- qh_completions(fotg210, qh); -- break; -- default: -- fotg210_dbg(fotg210, "bogus qh %p state %d\n", -- qh, qh->qh_state); -- goto done; -- } -- break; -- -- case PIPE_ISOCHRONOUS: -- /* itd... */ -- -- /* wait till next completion, do it then. */ -- /* completion irqs can wait up to 1024 msec, */ -- break; -- } --done: -- spin_unlock_irqrestore(&fotg210->lock, flags); -- return rc; --} -- --/* bulk qh holds the data toggle */ -- --static void fotg210_endpoint_disable(struct usb_hcd *hcd, -- struct usb_host_endpoint *ep) --{ -- struct fotg210_hcd *fotg210 = hcd_to_fotg210(hcd); -- unsigned long flags; -- struct fotg210_qh *qh, *tmp; -- -- /* ASSERT: any requests/urbs are being unlinked */ -- /* ASSERT: nobody can be submitting urbs for this any more */ -- --rescan: -- spin_lock_irqsave(&fotg210->lock, flags); -- qh = ep->hcpriv; -- if (!qh) -- goto done; -- -- /* endpoints can be iso streams. for now, we don't -- * accelerate iso completions ... so spin a while. -- */ -- if (qh->hw == NULL) { -- struct fotg210_iso_stream *stream = ep->hcpriv; -- -- if (!list_empty(&stream->td_list)) -- goto idle_timeout; -- -- /* BUG_ON(!list_empty(&stream->free_list)); */ -- kfree(stream); -- goto done; -- } -- -- if (fotg210->rh_state < FOTG210_RH_RUNNING) -- qh->qh_state = QH_STATE_IDLE; -- switch (qh->qh_state) { -- case QH_STATE_LINKED: -- case QH_STATE_COMPLETING: -- for (tmp = fotg210->async->qh_next.qh; -- tmp && tmp != qh; -- tmp = tmp->qh_next.qh) -- continue; -- /* periodic qh self-unlinks on empty, and a COMPLETING qh -- * may already be unlinked. -- */ -- if (tmp) -- start_unlink_async(fotg210, qh); -- fallthrough; -- case QH_STATE_UNLINK: /* wait for hw to finish? */ -- case QH_STATE_UNLINK_WAIT: --idle_timeout: -- spin_unlock_irqrestore(&fotg210->lock, flags); -- schedule_timeout_uninterruptible(1); -- goto rescan; -- case QH_STATE_IDLE: /* fully unlinked */ -- if (qh->clearing_tt) -- goto idle_timeout; -- if (list_empty(&qh->qtd_list)) { -- qh_destroy(fotg210, qh); -- break; -- } -- fallthrough; -- default: -- /* caller was supposed to have unlinked any requests; -- * that's not our job. just leak this memory. -- */ -- fotg210_err(fotg210, "qh %p (#%02x) state %d%s\n", -- qh, ep->desc.bEndpointAddress, qh->qh_state, -- list_empty(&qh->qtd_list) ? "" : "(has tds)"); -- break; -- } --done: -- ep->hcpriv = NULL; -- spin_unlock_irqrestore(&fotg210->lock, flags); --} -- --static void fotg210_endpoint_reset(struct usb_hcd *hcd, -- struct usb_host_endpoint *ep) --{ -- struct fotg210_hcd *fotg210 = hcd_to_fotg210(hcd); -- struct fotg210_qh *qh; -- int eptype = usb_endpoint_type(&ep->desc); -- int epnum = usb_endpoint_num(&ep->desc); -- int is_out = usb_endpoint_dir_out(&ep->desc); -- unsigned long flags; -- -- if (eptype != USB_ENDPOINT_XFER_BULK && eptype != USB_ENDPOINT_XFER_INT) -- return; -- -- spin_lock_irqsave(&fotg210->lock, flags); -- qh = ep->hcpriv; -- -- /* For Bulk and Interrupt endpoints we maintain the toggle state -- * in the hardware; the toggle bits in udev aren't used at all. -- * When an endpoint is reset by usb_clear_halt() we must reset -- * the toggle bit in the QH. -- */ -- if (qh) { -- usb_settoggle(qh->dev, epnum, is_out, 0); -- if (!list_empty(&qh->qtd_list)) { -- WARN_ONCE(1, "clear_halt for a busy endpoint\n"); -- } else if (qh->qh_state == QH_STATE_LINKED || -- qh->qh_state == QH_STATE_COMPLETING) { -- -- /* The toggle value in the QH can't be updated -- * while the QH is active. Unlink it now; -- * re-linking will call qh_refresh(). -- */ -- if (eptype == USB_ENDPOINT_XFER_BULK) -- start_unlink_async(fotg210, qh); -- else -- start_unlink_intr(fotg210, qh); -- } -- } -- spin_unlock_irqrestore(&fotg210->lock, flags); --} -- --static int fotg210_get_frame(struct usb_hcd *hcd) --{ -- struct fotg210_hcd *fotg210 = hcd_to_fotg210(hcd); -- -- return (fotg210_read_frame_index(fotg210) >> 3) % -- fotg210->periodic_size; --} -- --/* The EHCI in ChipIdea HDRC cannot be a separate module or device, -- * because its registers (and irq) are shared between host/gadget/otg -- * functions and in order to facilitate role switching we cannot -- * give the fotg210 driver exclusive access to those. -- */ --MODULE_DESCRIPTION(DRIVER_DESC); --MODULE_AUTHOR(DRIVER_AUTHOR); --MODULE_LICENSE("GPL"); -- --static const struct hc_driver fotg210_fotg210_hc_driver = { -- .description = hcd_name, -- .product_desc = "Faraday USB2.0 Host Controller", -- .hcd_priv_size = sizeof(struct fotg210_hcd), -- -- /* -- * generic hardware linkage -- */ -- .irq = fotg210_irq, -- .flags = HCD_MEMORY | HCD_DMA | HCD_USB2, -- -- /* -- * basic lifecycle operations -- */ -- .reset = hcd_fotg210_init, -- .start = fotg210_run, -- .stop = fotg210_stop, -- .shutdown = fotg210_shutdown, -- -- /* -- * managing i/o requests and associated device resources -- */ -- .urb_enqueue = fotg210_urb_enqueue, -- .urb_dequeue = fotg210_urb_dequeue, -- .endpoint_disable = fotg210_endpoint_disable, -- .endpoint_reset = fotg210_endpoint_reset, -- -- /* -- * scheduling support -- */ -- .get_frame_number = fotg210_get_frame, -- -- /* -- * root hub support -- */ -- .hub_status_data = fotg210_hub_status_data, -- .hub_control = fotg210_hub_control, -- .bus_suspend = fotg210_bus_suspend, -- .bus_resume = fotg210_bus_resume, -- -- .relinquish_port = fotg210_relinquish_port, -- .port_handed_over = fotg210_port_handed_over, -- -- .clear_tt_buffer_complete = fotg210_clear_tt_buffer_complete, --}; -- --static void fotg210_init(struct fotg210_hcd *fotg210) --{ -- u32 value; -- -- iowrite32(GMIR_MDEV_INT | GMIR_MOTG_INT | GMIR_INT_POLARITY, -- &fotg210->regs->gmir); -- -- value = ioread32(&fotg210->regs->otgcsr); -- value &= ~OTGCSR_A_BUS_DROP; -- value |= OTGCSR_A_BUS_REQ; -- iowrite32(value, &fotg210->regs->otgcsr); --} -- --/* -- * fotg210_hcd_probe - initialize faraday FOTG210 HCDs -- * -- * Allocates basic resources for this USB host controller, and -- * then invokes the start() method for the HCD associated with it -- * through the hotplug entry's driver_data. -- */ --static int fotg210_hcd_probe(struct platform_device *pdev) --{ -- struct device *dev = &pdev->dev; -- struct usb_hcd *hcd; -- struct resource *res; -- int irq; -- int retval; -- struct fotg210_hcd *fotg210; -- -- if (usb_disabled()) -- return -ENODEV; -- -- pdev->dev.power.power_state = PMSG_ON; -- -- irq = platform_get_irq(pdev, 0); -- if (irq < 0) -- return irq; -- -- hcd = usb_create_hcd(&fotg210_fotg210_hc_driver, dev, -- dev_name(dev)); -- if (!hcd) { -- dev_err(dev, "failed to create hcd\n"); -- retval = -ENOMEM; -- goto fail_create_hcd; -- } -- -- hcd->has_tt = 1; -- -- res = platform_get_resource(pdev, IORESOURCE_MEM, 0); -- hcd->regs = devm_ioremap_resource(&pdev->dev, res); -- if (IS_ERR(hcd->regs)) { -- retval = PTR_ERR(hcd->regs); -- goto failed_put_hcd; -- } -- -- hcd->rsrc_start = res->start; -- hcd->rsrc_len = resource_size(res); -- -- fotg210 = hcd_to_fotg210(hcd); -- -- fotg210->caps = hcd->regs; -- -- /* It's OK not to supply this clock */ -- fotg210->pclk = clk_get(dev, "PCLK"); -- if (!IS_ERR(fotg210->pclk)) { -- retval = clk_prepare_enable(fotg210->pclk); -- if (retval) { -- dev_err(dev, "failed to enable PCLK\n"); -- goto failed_put_hcd; -- } -- } else if (PTR_ERR(fotg210->pclk) == -EPROBE_DEFER) { -- /* -- * Percolate deferrals, for anything else, -- * just live without the clocking. -- */ -- retval = PTR_ERR(fotg210->pclk); -- goto failed_dis_clk; -- } -- -- retval = fotg210_setup(hcd); -- if (retval) -- goto failed_dis_clk; -- -- fotg210_init(fotg210); -- -- retval = usb_add_hcd(hcd, irq, IRQF_SHARED); -- if (retval) { -- dev_err(dev, "failed to add hcd with err %d\n", retval); -- goto failed_dis_clk; -- } -- device_wakeup_enable(hcd->self.controller); -- platform_set_drvdata(pdev, hcd); -- -- return retval; -- --failed_dis_clk: -- if (!IS_ERR(fotg210->pclk)) { -- clk_disable_unprepare(fotg210->pclk); -- clk_put(fotg210->pclk); -- } --failed_put_hcd: -- usb_put_hcd(hcd); --fail_create_hcd: -- dev_err(dev, "init %s fail, %d\n", dev_name(dev), retval); -- return retval; --} -- --/* -- * fotg210_hcd_remove - shutdown processing for EHCI HCDs -- * @dev: USB Host Controller being removed -- * -- */ --static int fotg210_hcd_remove(struct platform_device *pdev) --{ -- struct usb_hcd *hcd = platform_get_drvdata(pdev); -- struct fotg210_hcd *fotg210 = hcd_to_fotg210(hcd); -- -- if (!IS_ERR(fotg210->pclk)) { -- clk_disable_unprepare(fotg210->pclk); -- clk_put(fotg210->pclk); -- } -- -- usb_remove_hcd(hcd); -- usb_put_hcd(hcd); -- -- return 0; --} -- --#ifdef CONFIG_OF --static const struct of_device_id fotg210_of_match[] = { -- { .compatible = "faraday,fotg210" }, -- {}, --}; --MODULE_DEVICE_TABLE(of, fotg210_of_match); --#endif -- --static struct platform_driver fotg210_hcd_driver = { -- .driver = { -- .name = "fotg210-hcd", -- .of_match_table = of_match_ptr(fotg210_of_match), -- }, -- .probe = fotg210_hcd_probe, -- .remove = fotg210_hcd_remove, --}; -- --static int __init fotg210_hcd_init(void) --{ -- int retval = 0; -- -- if (usb_disabled()) -- return -ENODEV; -- -- set_bit(USB_EHCI_LOADED, &usb_hcds_loaded); -- if (test_bit(USB_UHCI_LOADED, &usb_hcds_loaded) || -- test_bit(USB_OHCI_LOADED, &usb_hcds_loaded)) -- pr_warn("Warning! fotg210_hcd should always be loaded before uhci_hcd and ohci_hcd, not after\n"); -- -- pr_debug("%s: block sizes: qh %zd qtd %zd itd %zd\n", -- hcd_name, sizeof(struct fotg210_qh), -- sizeof(struct fotg210_qtd), -- sizeof(struct fotg210_itd)); -- -- fotg210_debug_root = debugfs_create_dir("fotg210", usb_debug_root); -- -- retval = platform_driver_register(&fotg210_hcd_driver); -- if (retval < 0) -- goto clean; -- return retval; -- --clean: -- debugfs_remove(fotg210_debug_root); -- fotg210_debug_root = NULL; -- -- clear_bit(USB_EHCI_LOADED, &usb_hcds_loaded); -- return retval; --} --module_init(fotg210_hcd_init); -- --static void __exit fotg210_hcd_cleanup(void) --{ -- platform_driver_unregister(&fotg210_hcd_driver); -- debugfs_remove(fotg210_debug_root); -- clear_bit(USB_EHCI_LOADED, &usb_hcds_loaded); --} --module_exit(fotg210_hcd_cleanup); ---- /dev/null -+++ b/drivers/usb/fotg210/fotg210-hcd.c -@@ -0,0 +1,5727 @@ -+// SPDX-License-Identifier: GPL-2.0+ -+/* Faraday FOTG210 EHCI-like driver -+ * -+ * Copyright (c) 2013 Faraday Technology Corporation -+ * -+ * Author: Yuan-Hsin Chen -+ * Feng-Hsin Chiang -+ * Po-Yu Chuang -+ * -+ * Most of code borrowed from the Linux-3.7 EHCI driver -+ */ -+#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 -+ -+#define DRIVER_AUTHOR "Yuan-Hsin Chen" -+#define DRIVER_DESC "FOTG210 Host Controller (EHCI) Driver" -+static const char hcd_name[] = "fotg210_hcd"; -+ -+#undef FOTG210_URB_TRACE -+#define FOTG210_STATS -+ -+/* magic numbers that can affect system performance */ -+#define FOTG210_TUNE_CERR 3 /* 0-3 qtd retries; 0 == don't stop */ -+#define FOTG210_TUNE_RL_HS 4 /* nak throttle; see 4.9 */ -+#define FOTG210_TUNE_RL_TT 0 -+#define FOTG210_TUNE_MULT_HS 1 /* 1-3 transactions/uframe; 4.10.3 */ -+#define FOTG210_TUNE_MULT_TT 1 -+ -+/* Some drivers think it's safe to schedule isochronous transfers more than 256 -+ * ms into the future (partly as a result of an old bug in the scheduling -+ * code). In an attempt to avoid trouble, we will use a minimum scheduling -+ * length of 512 frames instead of 256. -+ */ -+#define FOTG210_TUNE_FLS 1 /* (medium) 512-frame schedule */ -+ -+/* Initial IRQ latency: faster than hw default */ -+static int log2_irq_thresh; /* 0 to 6 */ -+module_param(log2_irq_thresh, int, S_IRUGO); -+MODULE_PARM_DESC(log2_irq_thresh, "log2 IRQ latency, 1-64 microframes"); -+ -+/* initial park setting: slower than hw default */ -+static unsigned park; -+module_param(park, uint, S_IRUGO); -+MODULE_PARM_DESC(park, "park setting; 1-3 back-to-back async packets"); -+ -+/* for link power management(LPM) feature */ -+static unsigned int hird; -+module_param(hird, int, S_IRUGO); -+MODULE_PARM_DESC(hird, "host initiated resume duration, +1 for each 75us"); -+ -+#define INTR_MASK (STS_IAA | STS_FATAL | STS_PCD | STS_ERR | STS_INT) -+ -+#include "fotg210-hcd.h" -+ -+#define fotg210_dbg(fotg210, fmt, args...) \ -+ dev_dbg(fotg210_to_hcd(fotg210)->self.controller, fmt, ## args) -+#define fotg210_err(fotg210, fmt, args...) \ -+ dev_err(fotg210_to_hcd(fotg210)->self.controller, fmt, ## args) -+#define fotg210_info(fotg210, fmt, args...) \ -+ dev_info(fotg210_to_hcd(fotg210)->self.controller, fmt, ## args) -+#define fotg210_warn(fotg210, fmt, args...) \ -+ dev_warn(fotg210_to_hcd(fotg210)->self.controller, fmt, ## args) -+ -+/* check the values in the HCSPARAMS register (host controller _Structural_ -+ * parameters) see EHCI spec, Table 2-4 for each value -+ */ -+static void dbg_hcs_params(struct fotg210_hcd *fotg210, char *label) -+{ -+ u32 params = fotg210_readl(fotg210, &fotg210->caps->hcs_params); -+ -+ fotg210_dbg(fotg210, "%s hcs_params 0x%x ports=%d\n", label, params, -+ HCS_N_PORTS(params)); -+} -+ -+/* check the values in the HCCPARAMS register (host controller _Capability_ -+ * parameters) see EHCI Spec, Table 2-5 for each value -+ */ -+static void dbg_hcc_params(struct fotg210_hcd *fotg210, char *label) -+{ -+ u32 params = fotg210_readl(fotg210, &fotg210->caps->hcc_params); -+ -+ fotg210_dbg(fotg210, "%s hcc_params %04x uframes %s%s\n", label, -+ params, -+ HCC_PGM_FRAMELISTLEN(params) ? "256/512/1024" : "1024", -+ HCC_CANPARK(params) ? " park" : ""); -+} -+ -+static void __maybe_unused -+dbg_qtd(const char *label, struct fotg210_hcd *fotg210, struct fotg210_qtd *qtd) -+{ -+ fotg210_dbg(fotg210, "%s td %p n%08x %08x t%08x p0=%08x\n", label, qtd, -+ hc32_to_cpup(fotg210, &qtd->hw_next), -+ hc32_to_cpup(fotg210, &qtd->hw_alt_next), -+ hc32_to_cpup(fotg210, &qtd->hw_token), -+ hc32_to_cpup(fotg210, &qtd->hw_buf[0])); -+ if (qtd->hw_buf[1]) -+ fotg210_dbg(fotg210, " p1=%08x p2=%08x p3=%08x p4=%08x\n", -+ hc32_to_cpup(fotg210, &qtd->hw_buf[1]), -+ hc32_to_cpup(fotg210, &qtd->hw_buf[2]), -+ hc32_to_cpup(fotg210, &qtd->hw_buf[3]), -+ hc32_to_cpup(fotg210, &qtd->hw_buf[4])); -+} -+ -+static void __maybe_unused -+dbg_qh(const char *label, struct fotg210_hcd *fotg210, struct fotg210_qh *qh) -+{ -+ struct fotg210_qh_hw *hw = qh->hw; -+ -+ fotg210_dbg(fotg210, "%s qh %p n%08x info %x %x qtd %x\n", label, qh, -+ hw->hw_next, hw->hw_info1, hw->hw_info2, -+ hw->hw_current); -+ -+ dbg_qtd("overlay", fotg210, (struct fotg210_qtd *) &hw->hw_qtd_next); -+} -+ -+static void __maybe_unused -+dbg_itd(const char *label, struct fotg210_hcd *fotg210, struct fotg210_itd *itd) -+{ -+ fotg210_dbg(fotg210, "%s[%d] itd %p, next %08x, urb %p\n", label, -+ itd->frame, itd, hc32_to_cpu(fotg210, itd->hw_next), -+ itd->urb); -+ -+ fotg210_dbg(fotg210, -+ " trans: %08x %08x %08x %08x %08x %08x %08x %08x\n", -+ hc32_to_cpu(fotg210, itd->hw_transaction[0]), -+ hc32_to_cpu(fotg210, itd->hw_transaction[1]), -+ hc32_to_cpu(fotg210, itd->hw_transaction[2]), -+ hc32_to_cpu(fotg210, itd->hw_transaction[3]), -+ hc32_to_cpu(fotg210, itd->hw_transaction[4]), -+ hc32_to_cpu(fotg210, itd->hw_transaction[5]), -+ hc32_to_cpu(fotg210, itd->hw_transaction[6]), -+ hc32_to_cpu(fotg210, itd->hw_transaction[7])); -+ -+ fotg210_dbg(fotg210, -+ " buf: %08x %08x %08x %08x %08x %08x %08x\n", -+ hc32_to_cpu(fotg210, itd->hw_bufp[0]), -+ hc32_to_cpu(fotg210, itd->hw_bufp[1]), -+ hc32_to_cpu(fotg210, itd->hw_bufp[2]), -+ hc32_to_cpu(fotg210, itd->hw_bufp[3]), -+ hc32_to_cpu(fotg210, itd->hw_bufp[4]), -+ hc32_to_cpu(fotg210, itd->hw_bufp[5]), -+ hc32_to_cpu(fotg210, itd->hw_bufp[6])); -+ -+ fotg210_dbg(fotg210, " index: %d %d %d %d %d %d %d %d\n", -+ itd->index[0], itd->index[1], itd->index[2], -+ itd->index[3], itd->index[4], itd->index[5], -+ itd->index[6], itd->index[7]); -+} -+ -+static int __maybe_unused -+dbg_status_buf(char *buf, unsigned len, const char *label, u32 status) -+{ -+ return scnprintf(buf, len, "%s%sstatus %04x%s%s%s%s%s%s%s%s%s%s", -+ label, label[0] ? " " : "", status, -+ (status & STS_ASS) ? " Async" : "", -+ (status & STS_PSS) ? " Periodic" : "", -+ (status & STS_RECL) ? " Recl" : "", -+ (status & STS_HALT) ? " Halt" : "", -+ (status & STS_IAA) ? " IAA" : "", -+ (status & STS_FATAL) ? " FATAL" : "", -+ (status & STS_FLR) ? " FLR" : "", -+ (status & STS_PCD) ? " PCD" : "", -+ (status & STS_ERR) ? " ERR" : "", -+ (status & STS_INT) ? " INT" : ""); -+} -+ -+static int __maybe_unused -+dbg_intr_buf(char *buf, unsigned len, const char *label, u32 enable) -+{ -+ return scnprintf(buf, len, "%s%sintrenable %02x%s%s%s%s%s%s", -+ label, label[0] ? " " : "", enable, -+ (enable & STS_IAA) ? " IAA" : "", -+ (enable & STS_FATAL) ? " FATAL" : "", -+ (enable & STS_FLR) ? " FLR" : "", -+ (enable & STS_PCD) ? " PCD" : "", -+ (enable & STS_ERR) ? " ERR" : "", -+ (enable & STS_INT) ? " INT" : ""); -+} -+ -+static const char *const fls_strings[] = { "1024", "512", "256", "??" }; -+ -+static int dbg_command_buf(char *buf, unsigned len, const char *label, -+ u32 command) -+{ -+ return scnprintf(buf, len, -+ "%s%scommand %07x %s=%d ithresh=%d%s%s%s period=%s%s %s", -+ label, label[0] ? " " : "", command, -+ (command & CMD_PARK) ? " park" : "(park)", -+ CMD_PARK_CNT(command), -+ (command >> 16) & 0x3f, -+ (command & CMD_IAAD) ? " IAAD" : "", -+ (command & CMD_ASE) ? " Async" : "", -+ (command & CMD_PSE) ? " Periodic" : "", -+ fls_strings[(command >> 2) & 0x3], -+ (command & CMD_RESET) ? " Reset" : "", -+ (command & CMD_RUN) ? "RUN" : "HALT"); -+} -+ -+static char *dbg_port_buf(char *buf, unsigned len, const char *label, int port, -+ u32 status) -+{ -+ char *sig; -+ -+ /* signaling state */ -+ switch (status & (3 << 10)) { -+ case 0 << 10: -+ sig = "se0"; -+ break; -+ case 1 << 10: -+ sig = "k"; -+ break; /* low speed */ -+ case 2 << 10: -+ sig = "j"; -+ break; -+ default: -+ sig = "?"; -+ break; -+ } -+ -+ scnprintf(buf, len, "%s%sport:%d status %06x %d sig=%s%s%s%s%s%s%s%s", -+ label, label[0] ? " " : "", port, status, -+ status >> 25, /*device address */ -+ sig, -+ (status & PORT_RESET) ? " RESET" : "", -+ (status & PORT_SUSPEND) ? " SUSPEND" : "", -+ (status & PORT_RESUME) ? " RESUME" : "", -+ (status & PORT_PEC) ? " PEC" : "", -+ (status & PORT_PE) ? " PE" : "", -+ (status & PORT_CSC) ? " CSC" : "", -+ (status & PORT_CONNECT) ? " CONNECT" : ""); -+ -+ return buf; -+} -+ -+/* functions have the "wrong" filename when they're output... */ -+#define dbg_status(fotg210, label, status) { \ -+ char _buf[80]; \ -+ dbg_status_buf(_buf, sizeof(_buf), label, status); \ -+ fotg210_dbg(fotg210, "%s\n", _buf); \ -+} -+ -+#define dbg_cmd(fotg210, label, command) { \ -+ char _buf[80]; \ -+ dbg_command_buf(_buf, sizeof(_buf), label, command); \ -+ fotg210_dbg(fotg210, "%s\n", _buf); \ -+} -+ -+#define dbg_port(fotg210, label, port, status) { \ -+ char _buf[80]; \ -+ fotg210_dbg(fotg210, "%s\n", \ -+ dbg_port_buf(_buf, sizeof(_buf), label, port, status));\ -+} -+ -+/* troubleshooting help: expose state in debugfs */ -+static int debug_async_open(struct inode *, struct file *); -+static int debug_periodic_open(struct inode *, struct file *); -+static int debug_registers_open(struct inode *, struct file *); -+static int debug_async_open(struct inode *, struct file *); -+ -+static ssize_t debug_output(struct file*, char __user*, size_t, loff_t*); -+static int debug_close(struct inode *, struct file *); -+ -+static const struct file_operations debug_async_fops = { -+ .owner = THIS_MODULE, -+ .open = debug_async_open, -+ .read = debug_output, -+ .release = debug_close, -+ .llseek = default_llseek, -+}; -+static const struct file_operations debug_periodic_fops = { -+ .owner = THIS_MODULE, -+ .open = debug_periodic_open, -+ .read = debug_output, -+ .release = debug_close, -+ .llseek = default_llseek, -+}; -+static const struct file_operations debug_registers_fops = { -+ .owner = THIS_MODULE, -+ .open = debug_registers_open, -+ .read = debug_output, -+ .release = debug_close, -+ .llseek = default_llseek, -+}; -+ -+static struct dentry *fotg210_debug_root; -+ -+struct debug_buffer { -+ ssize_t (*fill_func)(struct debug_buffer *); /* fill method */ -+ struct usb_bus *bus; -+ struct mutex mutex; /* protect filling of buffer */ -+ size_t count; /* number of characters filled into buffer */ -+ char *output_buf; -+ size_t alloc_size; -+}; -+ -+static inline char speed_char(u32 scratch) -+{ -+ switch (scratch & (3 << 12)) { -+ case QH_FULL_SPEED: -+ return 'f'; -+ -+ case QH_LOW_SPEED: -+ return 'l'; -+ -+ case QH_HIGH_SPEED: -+ return 'h'; -+ -+ default: -+ return '?'; -+ } -+} -+ -+static inline char token_mark(struct fotg210_hcd *fotg210, __hc32 token) -+{ -+ __u32 v = hc32_to_cpu(fotg210, token); -+ -+ if (v & QTD_STS_ACTIVE) -+ return '*'; -+ if (v & QTD_STS_HALT) -+ return '-'; -+ if (!IS_SHORT_READ(v)) -+ return ' '; -+ /* tries to advance through hw_alt_next */ -+ return '/'; -+} -+ -+static void qh_lines(struct fotg210_hcd *fotg210, struct fotg210_qh *qh, -+ char **nextp, unsigned *sizep) -+{ -+ u32 scratch; -+ u32 hw_curr; -+ struct fotg210_qtd *td; -+ unsigned temp; -+ unsigned size = *sizep; -+ char *next = *nextp; -+ char mark; -+ __le32 list_end = FOTG210_LIST_END(fotg210); -+ struct fotg210_qh_hw *hw = qh->hw; -+ -+ if (hw->hw_qtd_next == list_end) /* NEC does this */ -+ mark = '@'; -+ else -+ mark = token_mark(fotg210, hw->hw_token); -+ if (mark == '/') { /* qh_alt_next controls qh advance? */ -+ if ((hw->hw_alt_next & QTD_MASK(fotg210)) == -+ fotg210->async->hw->hw_alt_next) -+ mark = '#'; /* blocked */ -+ else if (hw->hw_alt_next == list_end) -+ mark = '.'; /* use hw_qtd_next */ -+ /* else alt_next points to some other qtd */ -+ } -+ scratch = hc32_to_cpup(fotg210, &hw->hw_info1); -+ hw_curr = (mark == '*') ? hc32_to_cpup(fotg210, &hw->hw_current) : 0; -+ temp = scnprintf(next, size, -+ "qh/%p dev%d %cs ep%d %08x %08x(%08x%c %s nak%d)", -+ qh, scratch & 0x007f, -+ speed_char(scratch), -+ (scratch >> 8) & 0x000f, -+ scratch, hc32_to_cpup(fotg210, &hw->hw_info2), -+ hc32_to_cpup(fotg210, &hw->hw_token), mark, -+ (cpu_to_hc32(fotg210, QTD_TOGGLE) & hw->hw_token) -+ ? "data1" : "data0", -+ (hc32_to_cpup(fotg210, &hw->hw_alt_next) >> 1) & 0x0f); -+ size -= temp; -+ next += temp; -+ -+ /* hc may be modifying the list as we read it ... */ -+ list_for_each_entry(td, &qh->qtd_list, qtd_list) { -+ scratch = hc32_to_cpup(fotg210, &td->hw_token); -+ mark = ' '; -+ if (hw_curr == td->qtd_dma) -+ mark = '*'; -+ else if (hw->hw_qtd_next == cpu_to_hc32(fotg210, td->qtd_dma)) -+ mark = '+'; -+ else if (QTD_LENGTH(scratch)) { -+ if (td->hw_alt_next == fotg210->async->hw->hw_alt_next) -+ mark = '#'; -+ else if (td->hw_alt_next != list_end) -+ mark = '/'; -+ } -+ temp = snprintf(next, size, -+ "\n\t%p%c%s len=%d %08x urb %p", -+ td, mark, ({ char *tmp; -+ switch ((scratch>>8)&0x03) { -+ case 0: -+ tmp = "out"; -+ break; -+ case 1: -+ tmp = "in"; -+ break; -+ case 2: -+ tmp = "setup"; -+ break; -+ default: -+ tmp = "?"; -+ break; -+ } tmp; }), -+ (scratch >> 16) & 0x7fff, -+ scratch, -+ td->urb); -+ if (size < temp) -+ temp = size; -+ size -= temp; -+ next += temp; -+ if (temp == size) -+ goto done; -+ } -+ -+ temp = snprintf(next, size, "\n"); -+ if (size < temp) -+ temp = size; -+ -+ size -= temp; -+ next += temp; -+ -+done: -+ *sizep = size; -+ *nextp = next; -+} -+ -+static ssize_t fill_async_buffer(struct debug_buffer *buf) -+{ -+ struct usb_hcd *hcd; -+ struct fotg210_hcd *fotg210; -+ unsigned long flags; -+ unsigned temp, size; -+ char *next; -+ struct fotg210_qh *qh; -+ -+ hcd = bus_to_hcd(buf->bus); -+ fotg210 = hcd_to_fotg210(hcd); -+ next = buf->output_buf; -+ size = buf->alloc_size; -+ -+ *next = 0; -+ -+ /* dumps a snapshot of the async schedule. -+ * usually empty except for long-term bulk reads, or head. -+ * one QH per line, and TDs we know about -+ */ -+ spin_lock_irqsave(&fotg210->lock, flags); -+ for (qh = fotg210->async->qh_next.qh; size > 0 && qh; -+ qh = qh->qh_next.qh) -+ qh_lines(fotg210, qh, &next, &size); -+ if (fotg210->async_unlink && size > 0) { -+ temp = scnprintf(next, size, "\nunlink =\n"); -+ size -= temp; -+ next += temp; -+ -+ for (qh = fotg210->async_unlink; size > 0 && qh; -+ qh = qh->unlink_next) -+ qh_lines(fotg210, qh, &next, &size); -+ } -+ spin_unlock_irqrestore(&fotg210->lock, flags); -+ -+ return strlen(buf->output_buf); -+} -+ -+/* count tds, get ep direction */ -+static unsigned output_buf_tds_dir(char *buf, struct fotg210_hcd *fotg210, -+ struct fotg210_qh_hw *hw, struct fotg210_qh *qh, unsigned size) -+{ -+ u32 scratch = hc32_to_cpup(fotg210, &hw->hw_info1); -+ struct fotg210_qtd *qtd; -+ char *type = ""; -+ unsigned temp = 0; -+ -+ /* count tds, get ep direction */ -+ list_for_each_entry(qtd, &qh->qtd_list, qtd_list) { -+ temp++; -+ switch ((hc32_to_cpu(fotg210, qtd->hw_token) >> 8) & 0x03) { -+ case 0: -+ type = "out"; -+ continue; -+ case 1: -+ type = "in"; -+ continue; -+ } -+ } -+ -+ return scnprintf(buf, size, "(%c%d ep%d%s [%d/%d] q%d p%d)", -+ speed_char(scratch), scratch & 0x007f, -+ (scratch >> 8) & 0x000f, type, qh->usecs, -+ qh->c_usecs, temp, (scratch >> 16) & 0x7ff); -+} -+ -+#define DBG_SCHED_LIMIT 64 -+static ssize_t fill_periodic_buffer(struct debug_buffer *buf) -+{ -+ struct usb_hcd *hcd; -+ struct fotg210_hcd *fotg210; -+ unsigned long flags; -+ union fotg210_shadow p, *seen; -+ unsigned temp, size, seen_count; -+ char *next; -+ unsigned i; -+ __hc32 tag; -+ -+ seen = kmalloc_array(DBG_SCHED_LIMIT, sizeof(*seen), GFP_ATOMIC); -+ if (!seen) -+ return 0; -+ -+ seen_count = 0; -+ -+ hcd = bus_to_hcd(buf->bus); -+ fotg210 = hcd_to_fotg210(hcd); -+ next = buf->output_buf; -+ size = buf->alloc_size; -+ -+ temp = scnprintf(next, size, "size = %d\n", fotg210->periodic_size); -+ size -= temp; -+ next += temp; -+ -+ /* dump a snapshot of the periodic schedule. -+ * iso changes, interrupt usually doesn't. -+ */ -+ spin_lock_irqsave(&fotg210->lock, flags); -+ for (i = 0; i < fotg210->periodic_size; i++) { -+ p = fotg210->pshadow[i]; -+ if (likely(!p.ptr)) -+ continue; -+ -+ tag = Q_NEXT_TYPE(fotg210, fotg210->periodic[i]); -+ -+ temp = scnprintf(next, size, "%4d: ", i); -+ size -= temp; -+ next += temp; -+ -+ do { -+ struct fotg210_qh_hw *hw; -+ -+ switch (hc32_to_cpu(fotg210, tag)) { -+ case Q_TYPE_QH: -+ hw = p.qh->hw; -+ temp = scnprintf(next, size, " qh%d-%04x/%p", -+ p.qh->period, -+ hc32_to_cpup(fotg210, -+ &hw->hw_info2) -+ /* uframe masks */ -+ & (QH_CMASK | QH_SMASK), -+ p.qh); -+ size -= temp; -+ next += temp; -+ /* don't repeat what follows this qh */ -+ for (temp = 0; temp < seen_count; temp++) { -+ if (seen[temp].ptr != p.ptr) -+ continue; -+ if (p.qh->qh_next.ptr) { -+ temp = scnprintf(next, size, -+ " ..."); -+ size -= temp; -+ next += temp; -+ } -+ break; -+ } -+ /* show more info the first time around */ -+ if (temp == seen_count) { -+ temp = output_buf_tds_dir(next, -+ fotg210, hw, -+ p.qh, size); -+ -+ if (seen_count < DBG_SCHED_LIMIT) -+ seen[seen_count++].qh = p.qh; -+ } else -+ temp = 0; -+ tag = Q_NEXT_TYPE(fotg210, hw->hw_next); -+ p = p.qh->qh_next; -+ break; -+ case Q_TYPE_FSTN: -+ temp = scnprintf(next, size, -+ " fstn-%8x/%p", -+ p.fstn->hw_prev, p.fstn); -+ tag = Q_NEXT_TYPE(fotg210, p.fstn->hw_next); -+ p = p.fstn->fstn_next; -+ break; -+ case Q_TYPE_ITD: -+ temp = scnprintf(next, size, -+ " itd/%p", p.itd); -+ tag = Q_NEXT_TYPE(fotg210, p.itd->hw_next); -+ p = p.itd->itd_next; -+ break; -+ } -+ size -= temp; -+ next += temp; -+ } while (p.ptr); -+ -+ temp = scnprintf(next, size, "\n"); -+ size -= temp; -+ next += temp; -+ } -+ spin_unlock_irqrestore(&fotg210->lock, flags); -+ kfree(seen); -+ -+ return buf->alloc_size - size; -+} -+#undef DBG_SCHED_LIMIT -+ -+static const char *rh_state_string(struct fotg210_hcd *fotg210) -+{ -+ switch (fotg210->rh_state) { -+ case FOTG210_RH_HALTED: -+ return "halted"; -+ case FOTG210_RH_SUSPENDED: -+ return "suspended"; -+ case FOTG210_RH_RUNNING: -+ return "running"; -+ case FOTG210_RH_STOPPING: -+ return "stopping"; -+ } -+ return "?"; -+} -+ -+static ssize_t fill_registers_buffer(struct debug_buffer *buf) -+{ -+ struct usb_hcd *hcd; -+ struct fotg210_hcd *fotg210; -+ unsigned long flags; -+ unsigned temp, size, i; -+ char *next, scratch[80]; -+ static const char fmt[] = "%*s\n"; -+ static const char label[] = ""; -+ -+ hcd = bus_to_hcd(buf->bus); -+ fotg210 = hcd_to_fotg210(hcd); -+ next = buf->output_buf; -+ size = buf->alloc_size; -+ -+ spin_lock_irqsave(&fotg210->lock, flags); -+ -+ if (!HCD_HW_ACCESSIBLE(hcd)) { -+ size = scnprintf(next, size, -+ "bus %s, device %s\n" -+ "%s\n" -+ "SUSPENDED(no register access)\n", -+ hcd->self.controller->bus->name, -+ dev_name(hcd->self.controller), -+ hcd->product_desc); -+ goto done; -+ } -+ -+ /* Capability Registers */ -+ i = HC_VERSION(fotg210, fotg210_readl(fotg210, -+ &fotg210->caps->hc_capbase)); -+ temp = scnprintf(next, size, -+ "bus %s, device %s\n" -+ "%s\n" -+ "EHCI %x.%02x, rh state %s\n", -+ hcd->self.controller->bus->name, -+ dev_name(hcd->self.controller), -+ hcd->product_desc, -+ i >> 8, i & 0x0ff, rh_state_string(fotg210)); -+ size -= temp; -+ next += temp; -+ -+ /* FIXME interpret both types of params */ -+ i = fotg210_readl(fotg210, &fotg210->caps->hcs_params); -+ temp = scnprintf(next, size, "structural params 0x%08x\n", i); -+ size -= temp; -+ next += temp; -+ -+ i = fotg210_readl(fotg210, &fotg210->caps->hcc_params); -+ temp = scnprintf(next, size, "capability params 0x%08x\n", i); -+ size -= temp; -+ next += temp; -+ -+ /* Operational Registers */ -+ temp = dbg_status_buf(scratch, sizeof(scratch), label, -+ fotg210_readl(fotg210, &fotg210->regs->status)); -+ temp = scnprintf(next, size, fmt, temp, scratch); -+ size -= temp; -+ next += temp; -+ -+ temp = dbg_command_buf(scratch, sizeof(scratch), label, -+ fotg210_readl(fotg210, &fotg210->regs->command)); -+ temp = scnprintf(next, size, fmt, temp, scratch); -+ size -= temp; -+ next += temp; -+ -+ temp = dbg_intr_buf(scratch, sizeof(scratch), label, -+ fotg210_readl(fotg210, &fotg210->regs->intr_enable)); -+ temp = scnprintf(next, size, fmt, temp, scratch); -+ size -= temp; -+ next += temp; -+ -+ temp = scnprintf(next, size, "uframe %04x\n", -+ fotg210_read_frame_index(fotg210)); -+ size -= temp; -+ next += temp; -+ -+ if (fotg210->async_unlink) { -+ temp = scnprintf(next, size, "async unlink qh %p\n", -+ fotg210->async_unlink); -+ size -= temp; -+ next += temp; -+ } -+ -+#ifdef FOTG210_STATS -+ temp = scnprintf(next, size, -+ "irq normal %ld err %ld iaa %ld(lost %ld)\n", -+ fotg210->stats.normal, fotg210->stats.error, -+ fotg210->stats.iaa, fotg210->stats.lost_iaa); -+ size -= temp; -+ next += temp; -+ -+ temp = scnprintf(next, size, "complete %ld unlink %ld\n", -+ fotg210->stats.complete, fotg210->stats.unlink); -+ size -= temp; -+ next += temp; -+#endif -+ -+done: -+ spin_unlock_irqrestore(&fotg210->lock, flags); -+ -+ return buf->alloc_size - size; -+} -+ -+static struct debug_buffer -+*alloc_buffer(struct usb_bus *bus, ssize_t (*fill_func)(struct debug_buffer *)) -+{ -+ struct debug_buffer *buf; -+ -+ buf = kzalloc(sizeof(struct debug_buffer), GFP_KERNEL); -+ -+ if (buf) { -+ buf->bus = bus; -+ buf->fill_func = fill_func; -+ mutex_init(&buf->mutex); -+ buf->alloc_size = PAGE_SIZE; -+ } -+ -+ return buf; -+} -+ -+static int fill_buffer(struct debug_buffer *buf) -+{ -+ int ret = 0; -+ -+ if (!buf->output_buf) -+ buf->output_buf = vmalloc(buf->alloc_size); -+ -+ if (!buf->output_buf) { -+ ret = -ENOMEM; -+ goto out; -+ } -+ -+ ret = buf->fill_func(buf); -+ -+ if (ret >= 0) { -+ buf->count = ret; -+ ret = 0; -+ } -+ -+out: -+ return ret; -+} -+ -+static ssize_t debug_output(struct file *file, char __user *user_buf, -+ size_t len, loff_t *offset) -+{ -+ struct debug_buffer *buf = file->private_data; -+ int ret = 0; -+ -+ mutex_lock(&buf->mutex); -+ if (buf->count == 0) { -+ ret = fill_buffer(buf); -+ if (ret != 0) { -+ mutex_unlock(&buf->mutex); -+ goto out; -+ } -+ } -+ mutex_unlock(&buf->mutex); -+ -+ ret = simple_read_from_buffer(user_buf, len, offset, -+ buf->output_buf, buf->count); -+ -+out: -+ return ret; -+ -+} -+ -+static int debug_close(struct inode *inode, struct file *file) -+{ -+ struct debug_buffer *buf = file->private_data; -+ -+ if (buf) { -+ vfree(buf->output_buf); -+ kfree(buf); -+ } -+ -+ return 0; -+} -+static int debug_async_open(struct inode *inode, struct file *file) -+{ -+ file->private_data = alloc_buffer(inode->i_private, fill_async_buffer); -+ -+ return file->private_data ? 0 : -ENOMEM; -+} -+ -+static int debug_periodic_open(struct inode *inode, struct file *file) -+{ -+ struct debug_buffer *buf; -+ -+ buf = alloc_buffer(inode->i_private, fill_periodic_buffer); -+ if (!buf) -+ return -ENOMEM; -+ -+ buf->alloc_size = (sizeof(void *) == 4 ? 6 : 8)*PAGE_SIZE; -+ file->private_data = buf; -+ return 0; -+} -+ -+static int debug_registers_open(struct inode *inode, struct file *file) -+{ -+ file->private_data = alloc_buffer(inode->i_private, -+ fill_registers_buffer); -+ -+ return file->private_data ? 0 : -ENOMEM; -+} -+ -+static inline void create_debug_files(struct fotg210_hcd *fotg210) -+{ -+ struct usb_bus *bus = &fotg210_to_hcd(fotg210)->self; -+ struct dentry *root; -+ -+ root = debugfs_create_dir(bus->bus_name, fotg210_debug_root); -+ -+ debugfs_create_file("async", S_IRUGO, root, bus, &debug_async_fops); -+ debugfs_create_file("periodic", S_IRUGO, root, bus, -+ &debug_periodic_fops); -+ debugfs_create_file("registers", S_IRUGO, root, bus, -+ &debug_registers_fops); -+} -+ -+static inline void remove_debug_files(struct fotg210_hcd *fotg210) -+{ -+ struct usb_bus *bus = &fotg210_to_hcd(fotg210)->self; -+ -+ debugfs_lookup_and_remove(bus->bus_name, fotg210_debug_root); -+} -+ -+/* handshake - spin reading hc until handshake completes or fails -+ * @ptr: address of hc register to be read -+ * @mask: bits to look at in result of read -+ * @done: value of those bits when handshake succeeds -+ * @usec: timeout in microseconds -+ * -+ * Returns negative errno, or zero on success -+ * -+ * Success happens when the "mask" bits have the specified value (hardware -+ * handshake done). There are two failure modes: "usec" have passed (major -+ * hardware flakeout), or the register reads as all-ones (hardware removed). -+ * -+ * That last failure should_only happen in cases like physical cardbus eject -+ * before driver shutdown. But it also seems to be caused by bugs in cardbus -+ * bridge shutdown: shutting down the bridge before the devices using it. -+ */ -+static int handshake(struct fotg210_hcd *fotg210, void __iomem *ptr, -+ u32 mask, u32 done, int usec) -+{ -+ u32 result; -+ int ret; -+ -+ ret = readl_poll_timeout_atomic(ptr, result, -+ ((result & mask) == done || -+ result == U32_MAX), 1, usec); -+ if (result == U32_MAX) /* card removed */ -+ return -ENODEV; -+ -+ return ret; -+} -+ -+/* Force HC to halt state from unknown (EHCI spec section 2.3). -+ * Must be called with interrupts enabled and the lock not held. -+ */ -+static int fotg210_halt(struct fotg210_hcd *fotg210) -+{ -+ u32 temp; -+ -+ spin_lock_irq(&fotg210->lock); -+ -+ /* disable any irqs left enabled by previous code */ -+ fotg210_writel(fotg210, 0, &fotg210->regs->intr_enable); -+ -+ /* -+ * This routine gets called during probe before fotg210->command -+ * has been initialized, so we can't rely on its value. -+ */ -+ fotg210->command &= ~CMD_RUN; -+ temp = fotg210_readl(fotg210, &fotg210->regs->command); -+ temp &= ~(CMD_RUN | CMD_IAAD); -+ fotg210_writel(fotg210, temp, &fotg210->regs->command); -+ -+ spin_unlock_irq(&fotg210->lock); -+ synchronize_irq(fotg210_to_hcd(fotg210)->irq); -+ -+ return handshake(fotg210, &fotg210->regs->status, -+ STS_HALT, STS_HALT, 16 * 125); -+} -+ -+/* Reset a non-running (STS_HALT == 1) controller. -+ * Must be called with interrupts enabled and the lock not held. -+ */ -+static int fotg210_reset(struct fotg210_hcd *fotg210) -+{ -+ int retval; -+ u32 command = fotg210_readl(fotg210, &fotg210->regs->command); -+ -+ /* If the EHCI debug controller is active, special care must be -+ * taken before and after a host controller reset -+ */ -+ if (fotg210->debug && !dbgp_reset_prep(fotg210_to_hcd(fotg210))) -+ fotg210->debug = NULL; -+ -+ command |= CMD_RESET; -+ dbg_cmd(fotg210, "reset", command); -+ fotg210_writel(fotg210, command, &fotg210->regs->command); -+ fotg210->rh_state = FOTG210_RH_HALTED; -+ fotg210->next_statechange = jiffies; -+ retval = handshake(fotg210, &fotg210->regs->command, -+ CMD_RESET, 0, 250 * 1000); -+ -+ if (retval) -+ return retval; -+ -+ if (fotg210->debug) -+ dbgp_external_startup(fotg210_to_hcd(fotg210)); -+ -+ fotg210->port_c_suspend = fotg210->suspended_ports = -+ fotg210->resuming_ports = 0; -+ return retval; -+} -+ -+/* Idle the controller (turn off the schedules). -+ * Must be called with interrupts enabled and the lock not held. -+ */ -+static void fotg210_quiesce(struct fotg210_hcd *fotg210) -+{ -+ u32 temp; -+ -+ if (fotg210->rh_state != FOTG210_RH_RUNNING) -+ return; -+ -+ /* wait for any schedule enables/disables to take effect */ -+ temp = (fotg210->command << 10) & (STS_ASS | STS_PSS); -+ handshake(fotg210, &fotg210->regs->status, STS_ASS | STS_PSS, temp, -+ 16 * 125); -+ -+ /* then disable anything that's still active */ -+ spin_lock_irq(&fotg210->lock); -+ fotg210->command &= ~(CMD_ASE | CMD_PSE); -+ fotg210_writel(fotg210, fotg210->command, &fotg210->regs->command); -+ spin_unlock_irq(&fotg210->lock); -+ -+ /* hardware can take 16 microframes to turn off ... */ -+ handshake(fotg210, &fotg210->regs->status, STS_ASS | STS_PSS, 0, -+ 16 * 125); -+} -+ -+static void end_unlink_async(struct fotg210_hcd *fotg210); -+static void unlink_empty_async(struct fotg210_hcd *fotg210); -+static void fotg210_work(struct fotg210_hcd *fotg210); -+static void start_unlink_intr(struct fotg210_hcd *fotg210, -+ struct fotg210_qh *qh); -+static void end_unlink_intr(struct fotg210_hcd *fotg210, struct fotg210_qh *qh); -+ -+/* Set a bit in the USBCMD register */ -+static void fotg210_set_command_bit(struct fotg210_hcd *fotg210, u32 bit) -+{ -+ fotg210->command |= bit; -+ fotg210_writel(fotg210, fotg210->command, &fotg210->regs->command); -+ -+ /* unblock posted write */ -+ fotg210_readl(fotg210, &fotg210->regs->command); -+} -+ -+/* Clear a bit in the USBCMD register */ -+static void fotg210_clear_command_bit(struct fotg210_hcd *fotg210, u32 bit) -+{ -+ fotg210->command &= ~bit; -+ fotg210_writel(fotg210, fotg210->command, &fotg210->regs->command); -+ -+ /* unblock posted write */ -+ fotg210_readl(fotg210, &fotg210->regs->command); -+} -+ -+/* EHCI timer support... Now using hrtimers. -+ * -+ * Lots of different events are triggered from fotg210->hrtimer. Whenever -+ * the timer routine runs, it checks each possible event; events that are -+ * currently enabled and whose expiration time has passed get handled. -+ * The set of enabled events is stored as a collection of bitflags in -+ * fotg210->enabled_hrtimer_events, and they are numbered in order of -+ * increasing delay values (ranging between 1 ms and 100 ms). -+ * -+ * Rather than implementing a sorted list or tree of all pending events, -+ * we keep track only of the lowest-numbered pending event, in -+ * fotg210->next_hrtimer_event. Whenever fotg210->hrtimer gets restarted, its -+ * expiration time is set to the timeout value for this event. -+ * -+ * As a result, events might not get handled right away; the actual delay -+ * could be anywhere up to twice the requested delay. This doesn't -+ * matter, because none of the events are especially time-critical. The -+ * ones that matter most all have a delay of 1 ms, so they will be -+ * handled after 2 ms at most, which is okay. In addition to this, we -+ * allow for an expiration range of 1 ms. -+ */ -+ -+/* Delay lengths for the hrtimer event types. -+ * Keep this list sorted by delay length, in the same order as -+ * the event types indexed by enum fotg210_hrtimer_event in fotg210.h. -+ */ -+static unsigned event_delays_ns[] = { -+ 1 * NSEC_PER_MSEC, /* FOTG210_HRTIMER_POLL_ASS */ -+ 1 * NSEC_PER_MSEC, /* FOTG210_HRTIMER_POLL_PSS */ -+ 1 * NSEC_PER_MSEC, /* FOTG210_HRTIMER_POLL_DEAD */ -+ 1125 * NSEC_PER_USEC, /* FOTG210_HRTIMER_UNLINK_INTR */ -+ 2 * NSEC_PER_MSEC, /* FOTG210_HRTIMER_FREE_ITDS */ -+ 6 * NSEC_PER_MSEC, /* FOTG210_HRTIMER_ASYNC_UNLINKS */ -+ 10 * NSEC_PER_MSEC, /* FOTG210_HRTIMER_IAA_WATCHDOG */ -+ 10 * NSEC_PER_MSEC, /* FOTG210_HRTIMER_DISABLE_PERIODIC */ -+ 15 * NSEC_PER_MSEC, /* FOTG210_HRTIMER_DISABLE_ASYNC */ -+ 100 * NSEC_PER_MSEC, /* FOTG210_HRTIMER_IO_WATCHDOG */ -+}; -+ -+/* Enable a pending hrtimer event */ -+static void fotg210_enable_event(struct fotg210_hcd *fotg210, unsigned event, -+ bool resched) -+{ -+ ktime_t *timeout = &fotg210->hr_timeouts[event]; -+ -+ if (resched) -+ *timeout = ktime_add(ktime_get(), event_delays_ns[event]); -+ fotg210->enabled_hrtimer_events |= (1 << event); -+ -+ /* Track only the lowest-numbered pending event */ -+ if (event < fotg210->next_hrtimer_event) { -+ fotg210->next_hrtimer_event = event; -+ hrtimer_start_range_ns(&fotg210->hrtimer, *timeout, -+ NSEC_PER_MSEC, HRTIMER_MODE_ABS); -+ } -+} -+ -+ -+/* Poll the STS_ASS status bit; see when it agrees with CMD_ASE */ -+static void fotg210_poll_ASS(struct fotg210_hcd *fotg210) -+{ -+ unsigned actual, want; -+ -+ /* Don't enable anything if the controller isn't running (e.g., died) */ -+ if (fotg210->rh_state != FOTG210_RH_RUNNING) -+ return; -+ -+ want = (fotg210->command & CMD_ASE) ? STS_ASS : 0; -+ actual = fotg210_readl(fotg210, &fotg210->regs->status) & STS_ASS; -+ -+ if (want != actual) { -+ -+ /* Poll again later, but give up after about 20 ms */ -+ if (fotg210->ASS_poll_count++ < 20) { -+ fotg210_enable_event(fotg210, FOTG210_HRTIMER_POLL_ASS, -+ true); -+ return; -+ } -+ fotg210_dbg(fotg210, "Waited too long for the async schedule status (%x/%x), giving up\n", -+ want, actual); -+ } -+ fotg210->ASS_poll_count = 0; -+ -+ /* The status is up-to-date; restart or stop the schedule as needed */ -+ if (want == 0) { /* Stopped */ -+ if (fotg210->async_count > 0) -+ fotg210_set_command_bit(fotg210, CMD_ASE); -+ -+ } else { /* Running */ -+ if (fotg210->async_count == 0) { -+ -+ /* Turn off the schedule after a while */ -+ fotg210_enable_event(fotg210, -+ FOTG210_HRTIMER_DISABLE_ASYNC, -+ true); -+ } -+ } -+} -+ -+/* Turn off the async schedule after a brief delay */ -+static void fotg210_disable_ASE(struct fotg210_hcd *fotg210) -+{ -+ fotg210_clear_command_bit(fotg210, CMD_ASE); -+} -+ -+ -+/* Poll the STS_PSS status bit; see when it agrees with CMD_PSE */ -+static void fotg210_poll_PSS(struct fotg210_hcd *fotg210) -+{ -+ unsigned actual, want; -+ -+ /* Don't do anything if the controller isn't running (e.g., died) */ -+ if (fotg210->rh_state != FOTG210_RH_RUNNING) -+ return; -+ -+ want = (fotg210->command & CMD_PSE) ? STS_PSS : 0; -+ actual = fotg210_readl(fotg210, &fotg210->regs->status) & STS_PSS; -+ -+ if (want != actual) { -+ -+ /* Poll again later, but give up after about 20 ms */ -+ if (fotg210->PSS_poll_count++ < 20) { -+ fotg210_enable_event(fotg210, FOTG210_HRTIMER_POLL_PSS, -+ true); -+ return; -+ } -+ fotg210_dbg(fotg210, "Waited too long for the periodic schedule status (%x/%x), giving up\n", -+ want, actual); -+ } -+ fotg210->PSS_poll_count = 0; -+ -+ /* The status is up-to-date; restart or stop the schedule as needed */ -+ if (want == 0) { /* Stopped */ -+ if (fotg210->periodic_count > 0) -+ fotg210_set_command_bit(fotg210, CMD_PSE); -+ -+ } else { /* Running */ -+ if (fotg210->periodic_count == 0) { -+ -+ /* Turn off the schedule after a while */ -+ fotg210_enable_event(fotg210, -+ FOTG210_HRTIMER_DISABLE_PERIODIC, -+ true); -+ } -+ } -+} -+ -+/* Turn off the periodic schedule after a brief delay */ -+static void fotg210_disable_PSE(struct fotg210_hcd *fotg210) -+{ -+ fotg210_clear_command_bit(fotg210, CMD_PSE); -+} -+ -+ -+/* Poll the STS_HALT status bit; see when a dead controller stops */ -+static void fotg210_handle_controller_death(struct fotg210_hcd *fotg210) -+{ -+ if (!(fotg210_readl(fotg210, &fotg210->regs->status) & STS_HALT)) { -+ -+ /* Give up after a few milliseconds */ -+ if (fotg210->died_poll_count++ < 5) { -+ /* Try again later */ -+ fotg210_enable_event(fotg210, -+ FOTG210_HRTIMER_POLL_DEAD, true); -+ return; -+ } -+ fotg210_warn(fotg210, "Waited too long for the controller to stop, giving up\n"); -+ } -+ -+ /* Clean up the mess */ -+ fotg210->rh_state = FOTG210_RH_HALTED; -+ fotg210_writel(fotg210, 0, &fotg210->regs->intr_enable); -+ fotg210_work(fotg210); -+ end_unlink_async(fotg210); -+ -+ /* Not in process context, so don't try to reset the controller */ -+} -+ -+ -+/* Handle unlinked interrupt QHs once they are gone from the hardware */ -+static void fotg210_handle_intr_unlinks(struct fotg210_hcd *fotg210) -+{ -+ bool stopped = (fotg210->rh_state < FOTG210_RH_RUNNING); -+ -+ /* -+ * Process all the QHs on the intr_unlink list that were added -+ * before the current unlink cycle began. The list is in -+ * temporal order, so stop when we reach the first entry in the -+ * current cycle. But if the root hub isn't running then -+ * process all the QHs on the list. -+ */ -+ fotg210->intr_unlinking = true; -+ while (fotg210->intr_unlink) { -+ struct fotg210_qh *qh = fotg210->intr_unlink; -+ -+ if (!stopped && qh->unlink_cycle == fotg210->intr_unlink_cycle) -+ break; -+ fotg210->intr_unlink = qh->unlink_next; -+ qh->unlink_next = NULL; -+ end_unlink_intr(fotg210, qh); -+ } -+ -+ /* Handle remaining entries later */ -+ if (fotg210->intr_unlink) { -+ fotg210_enable_event(fotg210, FOTG210_HRTIMER_UNLINK_INTR, -+ true); -+ ++fotg210->intr_unlink_cycle; -+ } -+ fotg210->intr_unlinking = false; -+} -+ -+ -+/* Start another free-iTDs/siTDs cycle */ -+static void start_free_itds(struct fotg210_hcd *fotg210) -+{ -+ if (!(fotg210->enabled_hrtimer_events & -+ BIT(FOTG210_HRTIMER_FREE_ITDS))) { -+ fotg210->last_itd_to_free = list_entry( -+ fotg210->cached_itd_list.prev, -+ struct fotg210_itd, itd_list); -+ fotg210_enable_event(fotg210, FOTG210_HRTIMER_FREE_ITDS, true); -+ } -+} -+ -+/* Wait for controller to stop using old iTDs and siTDs */ -+static void end_free_itds(struct fotg210_hcd *fotg210) -+{ -+ struct fotg210_itd *itd, *n; -+ -+ if (fotg210->rh_state < FOTG210_RH_RUNNING) -+ fotg210->last_itd_to_free = NULL; -+ -+ list_for_each_entry_safe(itd, n, &fotg210->cached_itd_list, itd_list) { -+ list_del(&itd->itd_list); -+ dma_pool_free(fotg210->itd_pool, itd, itd->itd_dma); -+ if (itd == fotg210->last_itd_to_free) -+ break; -+ } -+ -+ if (!list_empty(&fotg210->cached_itd_list)) -+ start_free_itds(fotg210); -+} -+ -+ -+/* Handle lost (or very late) IAA interrupts */ -+static void fotg210_iaa_watchdog(struct fotg210_hcd *fotg210) -+{ -+ if (fotg210->rh_state != FOTG210_RH_RUNNING) -+ return; -+ -+ /* -+ * Lost IAA irqs wedge things badly; seen first with a vt8235. -+ * So we need this watchdog, but must protect it against both -+ * (a) SMP races against real IAA firing and retriggering, and -+ * (b) clean HC shutdown, when IAA watchdog was pending. -+ */ -+ if (fotg210->async_iaa) { -+ u32 cmd, status; -+ -+ /* If we get here, IAA is *REALLY* late. It's barely -+ * conceivable that the system is so busy that CMD_IAAD -+ * is still legitimately set, so let's be sure it's -+ * clear before we read STS_IAA. (The HC should clear -+ * CMD_IAAD when it sets STS_IAA.) -+ */ -+ cmd = fotg210_readl(fotg210, &fotg210->regs->command); -+ -+ /* -+ * If IAA is set here it either legitimately triggered -+ * after the watchdog timer expired (_way_ late, so we'll -+ * still count it as lost) ... or a silicon erratum: -+ * - VIA seems to set IAA without triggering the IRQ; -+ * - IAAD potentially cleared without setting IAA. -+ */ -+ status = fotg210_readl(fotg210, &fotg210->regs->status); -+ if ((status & STS_IAA) || !(cmd & CMD_IAAD)) { -+ INCR(fotg210->stats.lost_iaa); -+ fotg210_writel(fotg210, STS_IAA, -+ &fotg210->regs->status); -+ } -+ -+ fotg210_dbg(fotg210, "IAA watchdog: status %x cmd %x\n", -+ status, cmd); -+ end_unlink_async(fotg210); -+ } -+} -+ -+ -+/* Enable the I/O watchdog, if appropriate */ -+static void turn_on_io_watchdog(struct fotg210_hcd *fotg210) -+{ -+ /* Not needed if the controller isn't running or it's already enabled */ -+ if (fotg210->rh_state != FOTG210_RH_RUNNING || -+ (fotg210->enabled_hrtimer_events & -+ BIT(FOTG210_HRTIMER_IO_WATCHDOG))) -+ return; -+ -+ /* -+ * Isochronous transfers always need the watchdog. -+ * For other sorts we use it only if the flag is set. -+ */ -+ if (fotg210->isoc_count > 0 || (fotg210->need_io_watchdog && -+ fotg210->async_count + fotg210->intr_count > 0)) -+ fotg210_enable_event(fotg210, FOTG210_HRTIMER_IO_WATCHDOG, -+ true); -+} -+ -+ -+/* Handler functions for the hrtimer event types. -+ * Keep this array in the same order as the event types indexed by -+ * enum fotg210_hrtimer_event in fotg210.h. -+ */ -+static void (*event_handlers[])(struct fotg210_hcd *) = { -+ fotg210_poll_ASS, /* FOTG210_HRTIMER_POLL_ASS */ -+ fotg210_poll_PSS, /* FOTG210_HRTIMER_POLL_PSS */ -+ fotg210_handle_controller_death, /* FOTG210_HRTIMER_POLL_DEAD */ -+ fotg210_handle_intr_unlinks, /* FOTG210_HRTIMER_UNLINK_INTR */ -+ end_free_itds, /* FOTG210_HRTIMER_FREE_ITDS */ -+ unlink_empty_async, /* FOTG210_HRTIMER_ASYNC_UNLINKS */ -+ fotg210_iaa_watchdog, /* FOTG210_HRTIMER_IAA_WATCHDOG */ -+ fotg210_disable_PSE, /* FOTG210_HRTIMER_DISABLE_PERIODIC */ -+ fotg210_disable_ASE, /* FOTG210_HRTIMER_DISABLE_ASYNC */ -+ fotg210_work, /* FOTG210_HRTIMER_IO_WATCHDOG */ -+}; -+ -+static enum hrtimer_restart fotg210_hrtimer_func(struct hrtimer *t) -+{ -+ struct fotg210_hcd *fotg210 = -+ container_of(t, struct fotg210_hcd, hrtimer); -+ ktime_t now; -+ unsigned long events; -+ unsigned long flags; -+ unsigned e; -+ -+ spin_lock_irqsave(&fotg210->lock, flags); -+ -+ events = fotg210->enabled_hrtimer_events; -+ fotg210->enabled_hrtimer_events = 0; -+ fotg210->next_hrtimer_event = FOTG210_HRTIMER_NO_EVENT; -+ -+ /* -+ * Check each pending event. If its time has expired, handle -+ * the event; otherwise re-enable it. -+ */ -+ now = ktime_get(); -+ for_each_set_bit(e, &events, FOTG210_HRTIMER_NUM_EVENTS) { -+ if (ktime_compare(now, fotg210->hr_timeouts[e]) >= 0) -+ event_handlers[e](fotg210); -+ else -+ fotg210_enable_event(fotg210, e, false); -+ } -+ -+ spin_unlock_irqrestore(&fotg210->lock, flags); -+ return HRTIMER_NORESTART; -+} -+ -+#define fotg210_bus_suspend NULL -+#define fotg210_bus_resume NULL -+ -+static int check_reset_complete(struct fotg210_hcd *fotg210, int index, -+ u32 __iomem *status_reg, int port_status) -+{ -+ if (!(port_status & PORT_CONNECT)) -+ return port_status; -+ -+ /* if reset finished and it's still not enabled -- handoff */ -+ if (!(port_status & PORT_PE)) -+ /* with integrated TT, there's nobody to hand it to! */ -+ fotg210_dbg(fotg210, "Failed to enable port %d on root hub TT\n", -+ index + 1); -+ else -+ fotg210_dbg(fotg210, "port %d reset complete, port enabled\n", -+ index + 1); -+ -+ return port_status; -+} -+ -+ -+/* build "status change" packet (one or two bytes) from HC registers */ -+ -+static int fotg210_hub_status_data(struct usb_hcd *hcd, char *buf) -+{ -+ struct fotg210_hcd *fotg210 = hcd_to_fotg210(hcd); -+ u32 temp, status; -+ u32 mask; -+ int retval = 1; -+ unsigned long flags; -+ -+ /* init status to no-changes */ -+ buf[0] = 0; -+ -+ /* Inform the core about resumes-in-progress by returning -+ * a non-zero value even if there are no status changes. -+ */ -+ status = fotg210->resuming_ports; -+ -+ mask = PORT_CSC | PORT_PEC; -+ /* PORT_RESUME from hardware ~= PORT_STAT_C_SUSPEND */ -+ -+ /* no hub change reports (bit 0) for now (power, ...) */ -+ -+ /* port N changes (bit N)? */ -+ spin_lock_irqsave(&fotg210->lock, flags); -+ -+ temp = fotg210_readl(fotg210, &fotg210->regs->port_status); -+ -+ /* -+ * Return status information even for ports with OWNER set. -+ * Otherwise hub_wq wouldn't see the disconnect event when a -+ * high-speed device is switched over to the companion -+ * controller by the user. -+ */ -+ -+ if ((temp & mask) != 0 || test_bit(0, &fotg210->port_c_suspend) || -+ (fotg210->reset_done[0] && -+ time_after_eq(jiffies, fotg210->reset_done[0]))) { -+ buf[0] |= 1 << 1; -+ status = STS_PCD; -+ } -+ /* FIXME autosuspend idle root hubs */ -+ spin_unlock_irqrestore(&fotg210->lock, flags); -+ return status ? retval : 0; -+} -+ -+static void fotg210_hub_descriptor(struct fotg210_hcd *fotg210, -+ struct usb_hub_descriptor *desc) -+{ -+ int ports = HCS_N_PORTS(fotg210->hcs_params); -+ u16 temp; -+ -+ desc->bDescriptorType = USB_DT_HUB; -+ desc->bPwrOn2PwrGood = 10; /* fotg210 1.0, 2.3.9 says 20ms max */ -+ desc->bHubContrCurrent = 0; -+ -+ desc->bNbrPorts = ports; -+ temp = 1 + (ports / 8); -+ desc->bDescLength = 7 + 2 * temp; -+ -+ /* two bitmaps: ports removable, and usb 1.0 legacy PortPwrCtrlMask */ -+ memset(&desc->u.hs.DeviceRemovable[0], 0, temp); -+ memset(&desc->u.hs.DeviceRemovable[temp], 0xff, temp); -+ -+ temp = HUB_CHAR_INDV_PORT_OCPM; /* per-port overcurrent reporting */ -+ temp |= HUB_CHAR_NO_LPSM; /* no power switching */ -+ desc->wHubCharacteristics = cpu_to_le16(temp); -+} -+ -+static int fotg210_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, -+ u16 wIndex, char *buf, u16 wLength) -+{ -+ struct fotg210_hcd *fotg210 = hcd_to_fotg210(hcd); -+ int ports = HCS_N_PORTS(fotg210->hcs_params); -+ u32 __iomem *status_reg = &fotg210->regs->port_status; -+ u32 temp, temp1, status; -+ unsigned long flags; -+ int retval = 0; -+ unsigned selector; -+ -+ /* -+ * FIXME: support SetPortFeatures USB_PORT_FEAT_INDICATOR. -+ * HCS_INDICATOR may say we can change LEDs to off/amber/green. -+ * (track current state ourselves) ... blink for diagnostics, -+ * power, "this is the one", etc. EHCI spec supports this. -+ */ -+ -+ spin_lock_irqsave(&fotg210->lock, flags); -+ switch (typeReq) { -+ case ClearHubFeature: -+ switch (wValue) { -+ case C_HUB_LOCAL_POWER: -+ case C_HUB_OVER_CURRENT: -+ /* no hub-wide feature/status flags */ -+ break; -+ default: -+ goto error; -+ } -+ break; -+ case ClearPortFeature: -+ if (!wIndex || wIndex > ports) -+ goto error; -+ wIndex--; -+ temp = fotg210_readl(fotg210, status_reg); -+ temp &= ~PORT_RWC_BITS; -+ -+ /* -+ * Even if OWNER is set, so the port is owned by the -+ * companion controller, hub_wq needs to be able to clear -+ * the port-change status bits (especially -+ * USB_PORT_STAT_C_CONNECTION). -+ */ -+ -+ switch (wValue) { -+ case USB_PORT_FEAT_ENABLE: -+ fotg210_writel(fotg210, temp & ~PORT_PE, status_reg); -+ break; -+ case USB_PORT_FEAT_C_ENABLE: -+ fotg210_writel(fotg210, temp | PORT_PEC, status_reg); -+ break; -+ case USB_PORT_FEAT_SUSPEND: -+ if (temp & PORT_RESET) -+ goto error; -+ if (!(temp & PORT_SUSPEND)) -+ break; -+ if ((temp & PORT_PE) == 0) -+ goto error; -+ -+ /* resume signaling for 20 msec */ -+ fotg210_writel(fotg210, temp | PORT_RESUME, status_reg); -+ fotg210->reset_done[wIndex] = jiffies -+ + msecs_to_jiffies(USB_RESUME_TIMEOUT); -+ break; -+ case USB_PORT_FEAT_C_SUSPEND: -+ clear_bit(wIndex, &fotg210->port_c_suspend); -+ break; -+ case USB_PORT_FEAT_C_CONNECTION: -+ fotg210_writel(fotg210, temp | PORT_CSC, status_reg); -+ break; -+ case USB_PORT_FEAT_C_OVER_CURRENT: -+ fotg210_writel(fotg210, temp | OTGISR_OVC, -+ &fotg210->regs->otgisr); -+ break; -+ case USB_PORT_FEAT_C_RESET: -+ /* GetPortStatus clears reset */ -+ break; -+ default: -+ goto error; -+ } -+ fotg210_readl(fotg210, &fotg210->regs->command); -+ break; -+ case GetHubDescriptor: -+ fotg210_hub_descriptor(fotg210, (struct usb_hub_descriptor *) -+ buf); -+ break; -+ case GetHubStatus: -+ /* no hub-wide feature/status flags */ -+ memset(buf, 0, 4); -+ /*cpu_to_le32s ((u32 *) buf); */ -+ break; -+ case GetPortStatus: -+ if (!wIndex || wIndex > ports) -+ goto error; -+ wIndex--; -+ status = 0; -+ temp = fotg210_readl(fotg210, status_reg); -+ -+ /* wPortChange bits */ -+ if (temp & PORT_CSC) -+ status |= USB_PORT_STAT_C_CONNECTION << 16; -+ if (temp & PORT_PEC) -+ status |= USB_PORT_STAT_C_ENABLE << 16; -+ -+ temp1 = fotg210_readl(fotg210, &fotg210->regs->otgisr); -+ if (temp1 & OTGISR_OVC) -+ status |= USB_PORT_STAT_C_OVERCURRENT << 16; -+ -+ /* whoever resumes must GetPortStatus to complete it!! */ -+ if (temp & PORT_RESUME) { -+ -+ /* Remote Wakeup received? */ -+ if (!fotg210->reset_done[wIndex]) { -+ /* resume signaling for 20 msec */ -+ fotg210->reset_done[wIndex] = jiffies -+ + msecs_to_jiffies(20); -+ /* check the port again */ -+ mod_timer(&fotg210_to_hcd(fotg210)->rh_timer, -+ fotg210->reset_done[wIndex]); -+ } -+ -+ /* resume completed? */ -+ else if (time_after_eq(jiffies, -+ fotg210->reset_done[wIndex])) { -+ clear_bit(wIndex, &fotg210->suspended_ports); -+ set_bit(wIndex, &fotg210->port_c_suspend); -+ fotg210->reset_done[wIndex] = 0; -+ -+ /* stop resume signaling */ -+ temp = fotg210_readl(fotg210, status_reg); -+ fotg210_writel(fotg210, temp & -+ ~(PORT_RWC_BITS | PORT_RESUME), -+ status_reg); -+ clear_bit(wIndex, &fotg210->resuming_ports); -+ retval = handshake(fotg210, status_reg, -+ PORT_RESUME, 0, 2000);/* 2ms */ -+ if (retval != 0) { -+ fotg210_err(fotg210, -+ "port %d resume error %d\n", -+ wIndex + 1, retval); -+ goto error; -+ } -+ temp &= ~(PORT_SUSPEND|PORT_RESUME|(3<<10)); -+ } -+ } -+ -+ /* whoever resets must GetPortStatus to complete it!! */ -+ if ((temp & PORT_RESET) && time_after_eq(jiffies, -+ fotg210->reset_done[wIndex])) { -+ status |= USB_PORT_STAT_C_RESET << 16; -+ fotg210->reset_done[wIndex] = 0; -+ clear_bit(wIndex, &fotg210->resuming_ports); -+ -+ /* force reset to complete */ -+ fotg210_writel(fotg210, -+ temp & ~(PORT_RWC_BITS | PORT_RESET), -+ status_reg); -+ /* REVISIT: some hardware needs 550+ usec to clear -+ * this bit; seems too long to spin routinely... -+ */ -+ retval = handshake(fotg210, status_reg, -+ PORT_RESET, 0, 1000); -+ if (retval != 0) { -+ fotg210_err(fotg210, "port %d reset error %d\n", -+ wIndex + 1, retval); -+ goto error; -+ } -+ -+ /* see what we found out */ -+ temp = check_reset_complete(fotg210, wIndex, status_reg, -+ fotg210_readl(fotg210, status_reg)); -+ -+ /* restart schedule */ -+ fotg210->command |= CMD_RUN; -+ fotg210_writel(fotg210, fotg210->command, &fotg210->regs->command); -+ } -+ -+ if (!(temp & (PORT_RESUME|PORT_RESET))) { -+ fotg210->reset_done[wIndex] = 0; -+ clear_bit(wIndex, &fotg210->resuming_ports); -+ } -+ -+ /* transfer dedicated ports to the companion hc */ -+ if ((temp & PORT_CONNECT) && -+ test_bit(wIndex, &fotg210->companion_ports)) { -+ temp &= ~PORT_RWC_BITS; -+ fotg210_writel(fotg210, temp, status_reg); -+ fotg210_dbg(fotg210, "port %d --> companion\n", -+ wIndex + 1); -+ temp = fotg210_readl(fotg210, status_reg); -+ } -+ -+ /* -+ * Even if OWNER is set, there's no harm letting hub_wq -+ * see the wPortStatus values (they should all be 0 except -+ * for PORT_POWER anyway). -+ */ -+ -+ if (temp & PORT_CONNECT) { -+ status |= USB_PORT_STAT_CONNECTION; -+ status |= fotg210_port_speed(fotg210, temp); -+ } -+ if (temp & PORT_PE) -+ status |= USB_PORT_STAT_ENABLE; -+ -+ /* maybe the port was unsuspended without our knowledge */ -+ if (temp & (PORT_SUSPEND|PORT_RESUME)) { -+ status |= USB_PORT_STAT_SUSPEND; -+ } else if (test_bit(wIndex, &fotg210->suspended_ports)) { -+ clear_bit(wIndex, &fotg210->suspended_ports); -+ clear_bit(wIndex, &fotg210->resuming_ports); -+ fotg210->reset_done[wIndex] = 0; -+ if (temp & PORT_PE) -+ set_bit(wIndex, &fotg210->port_c_suspend); -+ } -+ -+ temp1 = fotg210_readl(fotg210, &fotg210->regs->otgisr); -+ if (temp1 & OTGISR_OVC) -+ status |= USB_PORT_STAT_OVERCURRENT; -+ if (temp & PORT_RESET) -+ status |= USB_PORT_STAT_RESET; -+ if (test_bit(wIndex, &fotg210->port_c_suspend)) -+ status |= USB_PORT_STAT_C_SUSPEND << 16; -+ -+ if (status & ~0xffff) /* only if wPortChange is interesting */ -+ dbg_port(fotg210, "GetStatus", wIndex + 1, temp); -+ put_unaligned_le32(status, buf); -+ break; -+ case SetHubFeature: -+ switch (wValue) { -+ case C_HUB_LOCAL_POWER: -+ case C_HUB_OVER_CURRENT: -+ /* no hub-wide feature/status flags */ -+ break; -+ default: -+ goto error; -+ } -+ break; -+ case SetPortFeature: -+ selector = wIndex >> 8; -+ wIndex &= 0xff; -+ -+ if (!wIndex || wIndex > ports) -+ goto error; -+ wIndex--; -+ temp = fotg210_readl(fotg210, status_reg); -+ temp &= ~PORT_RWC_BITS; -+ switch (wValue) { -+ case USB_PORT_FEAT_SUSPEND: -+ if ((temp & PORT_PE) == 0 -+ || (temp & PORT_RESET) != 0) -+ goto error; -+ -+ /* After above check the port must be connected. -+ * Set appropriate bit thus could put phy into low power -+ * mode if we have hostpc feature -+ */ -+ fotg210_writel(fotg210, temp | PORT_SUSPEND, -+ status_reg); -+ set_bit(wIndex, &fotg210->suspended_ports); -+ break; -+ case USB_PORT_FEAT_RESET: -+ if (temp & PORT_RESUME) -+ goto error; -+ /* line status bits may report this as low speed, -+ * which can be fine if this root hub has a -+ * transaction translator built in. -+ */ -+ fotg210_dbg(fotg210, "port %d reset\n", wIndex + 1); -+ temp |= PORT_RESET; -+ temp &= ~PORT_PE; -+ -+ /* -+ * caller must wait, then call GetPortStatus -+ * usb 2.0 spec says 50 ms resets on root -+ */ -+ fotg210->reset_done[wIndex] = jiffies -+ + msecs_to_jiffies(50); -+ fotg210_writel(fotg210, temp, status_reg); -+ break; -+ -+ /* For downstream facing ports (these): one hub port is put -+ * into test mode according to USB2 11.24.2.13, then the hub -+ * must be reset (which for root hub now means rmmod+modprobe, -+ * or else system reboot). See EHCI 2.3.9 and 4.14 for info -+ * about the EHCI-specific stuff. -+ */ -+ case USB_PORT_FEAT_TEST: -+ if (!selector || selector > 5) -+ goto error; -+ spin_unlock_irqrestore(&fotg210->lock, flags); -+ fotg210_quiesce(fotg210); -+ spin_lock_irqsave(&fotg210->lock, flags); -+ -+ /* Put all enabled ports into suspend */ -+ temp = fotg210_readl(fotg210, status_reg) & -+ ~PORT_RWC_BITS; -+ if (temp & PORT_PE) -+ fotg210_writel(fotg210, temp | PORT_SUSPEND, -+ status_reg); -+ -+ spin_unlock_irqrestore(&fotg210->lock, flags); -+ fotg210_halt(fotg210); -+ spin_lock_irqsave(&fotg210->lock, flags); -+ -+ temp = fotg210_readl(fotg210, status_reg); -+ temp |= selector << 16; -+ fotg210_writel(fotg210, temp, status_reg); -+ break; -+ -+ default: -+ goto error; -+ } -+ fotg210_readl(fotg210, &fotg210->regs->command); -+ break; -+ -+ default: -+error: -+ /* "stall" on error */ -+ retval = -EPIPE; -+ } -+ spin_unlock_irqrestore(&fotg210->lock, flags); -+ return retval; -+} -+ -+static void __maybe_unused fotg210_relinquish_port(struct usb_hcd *hcd, -+ int portnum) -+{ -+ return; -+} -+ -+static int __maybe_unused fotg210_port_handed_over(struct usb_hcd *hcd, -+ int portnum) -+{ -+ return 0; -+} -+ -+/* There's basically three types of memory: -+ * - data used only by the HCD ... kmalloc is fine -+ * - async and periodic schedules, shared by HC and HCD ... these -+ * need to use dma_pool or dma_alloc_coherent -+ * - driver buffers, read/written by HC ... single shot DMA mapped -+ * -+ * There's also "register" data (e.g. PCI or SOC), which is memory mapped. -+ * No memory seen by this driver is pageable. -+ */ -+ -+/* Allocate the key transfer structures from the previously allocated pool */ -+static inline void fotg210_qtd_init(struct fotg210_hcd *fotg210, -+ struct fotg210_qtd *qtd, dma_addr_t dma) -+{ -+ memset(qtd, 0, sizeof(*qtd)); -+ qtd->qtd_dma = dma; -+ qtd->hw_token = cpu_to_hc32(fotg210, QTD_STS_HALT); -+ qtd->hw_next = FOTG210_LIST_END(fotg210); -+ qtd->hw_alt_next = FOTG210_LIST_END(fotg210); -+ INIT_LIST_HEAD(&qtd->qtd_list); -+} -+ -+static struct fotg210_qtd *fotg210_qtd_alloc(struct fotg210_hcd *fotg210, -+ gfp_t flags) -+{ -+ struct fotg210_qtd *qtd; -+ dma_addr_t dma; -+ -+ qtd = dma_pool_alloc(fotg210->qtd_pool, flags, &dma); -+ if (qtd != NULL) -+ fotg210_qtd_init(fotg210, qtd, dma); -+ -+ return qtd; -+} -+ -+static inline void fotg210_qtd_free(struct fotg210_hcd *fotg210, -+ struct fotg210_qtd *qtd) -+{ -+ dma_pool_free(fotg210->qtd_pool, qtd, qtd->qtd_dma); -+} -+ -+ -+static void qh_destroy(struct fotg210_hcd *fotg210, struct fotg210_qh *qh) -+{ -+ /* clean qtds first, and know this is not linked */ -+ if (!list_empty(&qh->qtd_list) || qh->qh_next.ptr) { -+ fotg210_dbg(fotg210, "unused qh not empty!\n"); -+ BUG(); -+ } -+ if (qh->dummy) -+ fotg210_qtd_free(fotg210, qh->dummy); -+ dma_pool_free(fotg210->qh_pool, qh->hw, qh->qh_dma); -+ kfree(qh); -+} -+ -+static struct fotg210_qh *fotg210_qh_alloc(struct fotg210_hcd *fotg210, -+ gfp_t flags) -+{ -+ struct fotg210_qh *qh; -+ dma_addr_t dma; -+ -+ qh = kzalloc(sizeof(*qh), GFP_ATOMIC); -+ if (!qh) -+ goto done; -+ qh->hw = (struct fotg210_qh_hw *) -+ dma_pool_zalloc(fotg210->qh_pool, flags, &dma); -+ if (!qh->hw) -+ goto fail; -+ qh->qh_dma = dma; -+ INIT_LIST_HEAD(&qh->qtd_list); -+ -+ /* dummy td enables safe urb queuing */ -+ qh->dummy = fotg210_qtd_alloc(fotg210, flags); -+ if (qh->dummy == NULL) { -+ fotg210_dbg(fotg210, "no dummy td\n"); -+ goto fail1; -+ } -+done: -+ return qh; -+fail1: -+ dma_pool_free(fotg210->qh_pool, qh->hw, qh->qh_dma); -+fail: -+ kfree(qh); -+ return NULL; -+} -+ -+/* The queue heads and transfer descriptors are managed from pools tied -+ * to each of the "per device" structures. -+ * This is the initialisation and cleanup code. -+ */ -+ -+static void fotg210_mem_cleanup(struct fotg210_hcd *fotg210) -+{ -+ if (fotg210->async) -+ qh_destroy(fotg210, fotg210->async); -+ fotg210->async = NULL; -+ -+ if (fotg210->dummy) -+ qh_destroy(fotg210, fotg210->dummy); -+ fotg210->dummy = NULL; -+ -+ /* DMA consistent memory and pools */ -+ dma_pool_destroy(fotg210->qtd_pool); -+ fotg210->qtd_pool = NULL; -+ -+ dma_pool_destroy(fotg210->qh_pool); -+ fotg210->qh_pool = NULL; -+ -+ dma_pool_destroy(fotg210->itd_pool); -+ fotg210->itd_pool = NULL; -+ -+ if (fotg210->periodic) -+ dma_free_coherent(fotg210_to_hcd(fotg210)->self.controller, -+ fotg210->periodic_size * sizeof(u32), -+ fotg210->periodic, fotg210->periodic_dma); -+ fotg210->periodic = NULL; -+ -+ /* shadow periodic table */ -+ kfree(fotg210->pshadow); -+ fotg210->pshadow = NULL; -+} -+ -+/* remember to add cleanup code (above) if you add anything here */ -+static int fotg210_mem_init(struct fotg210_hcd *fotg210, gfp_t flags) -+{ -+ int i; -+ -+ /* QTDs for control/bulk/intr transfers */ -+ fotg210->qtd_pool = dma_pool_create("fotg210_qtd", -+ fotg210_to_hcd(fotg210)->self.controller, -+ sizeof(struct fotg210_qtd), -+ 32 /* byte alignment (for hw parts) */, -+ 4096 /* can't cross 4K */); -+ if (!fotg210->qtd_pool) -+ goto fail; -+ -+ /* QHs for control/bulk/intr transfers */ -+ fotg210->qh_pool = dma_pool_create("fotg210_qh", -+ fotg210_to_hcd(fotg210)->self.controller, -+ sizeof(struct fotg210_qh_hw), -+ 32 /* byte alignment (for hw parts) */, -+ 4096 /* can't cross 4K */); -+ if (!fotg210->qh_pool) -+ goto fail; -+ -+ fotg210->async = fotg210_qh_alloc(fotg210, flags); -+ if (!fotg210->async) -+ goto fail; -+ -+ /* ITD for high speed ISO transfers */ -+ fotg210->itd_pool = dma_pool_create("fotg210_itd", -+ fotg210_to_hcd(fotg210)->self.controller, -+ sizeof(struct fotg210_itd), -+ 64 /* byte alignment (for hw parts) */, -+ 4096 /* can't cross 4K */); -+ if (!fotg210->itd_pool) -+ goto fail; -+ -+ /* Hardware periodic table */ -+ fotg210->periodic = -+ dma_alloc_coherent(fotg210_to_hcd(fotg210)->self.controller, -+ fotg210->periodic_size * sizeof(__le32), -+ &fotg210->periodic_dma, 0); -+ if (fotg210->periodic == NULL) -+ goto fail; -+ -+ for (i = 0; i < fotg210->periodic_size; i++) -+ fotg210->periodic[i] = FOTG210_LIST_END(fotg210); -+ -+ /* software shadow of hardware table */ -+ fotg210->pshadow = kcalloc(fotg210->periodic_size, sizeof(void *), -+ flags); -+ if (fotg210->pshadow != NULL) -+ return 0; -+ -+fail: -+ fotg210_dbg(fotg210, "couldn't init memory\n"); -+ fotg210_mem_cleanup(fotg210); -+ return -ENOMEM; -+} -+/* EHCI hardware queue manipulation ... the core. QH/QTD manipulation. -+ * -+ * Control, bulk, and interrupt traffic all use "qh" lists. They list "qtd" -+ * entries describing USB transactions, max 16-20kB/entry (with 4kB-aligned -+ * buffers needed for the larger number). We use one QH per endpoint, queue -+ * multiple urbs (all three types) per endpoint. URBs may need several qtds. -+ * -+ * ISO traffic uses "ISO TD" (itd) records, and (along with -+ * interrupts) needs careful scheduling. Performance improvements can be -+ * an ongoing challenge. That's in "ehci-sched.c". -+ * -+ * USB 1.1 devices are handled (a) by "companion" OHCI or UHCI root hubs, -+ * or otherwise through transaction translators (TTs) in USB 2.0 hubs using -+ * (b) special fields in qh entries or (c) split iso entries. TTs will -+ * buffer low/full speed data so the host collects it at high speed. -+ */ -+ -+/* fill a qtd, returning how much of the buffer we were able to queue up */ -+static int qtd_fill(struct fotg210_hcd *fotg210, struct fotg210_qtd *qtd, -+ dma_addr_t buf, size_t len, int token, int maxpacket) -+{ -+ int i, count; -+ u64 addr = buf; -+ -+ /* one buffer entry per 4K ... first might be short or unaligned */ -+ qtd->hw_buf[0] = cpu_to_hc32(fotg210, (u32)addr); -+ qtd->hw_buf_hi[0] = cpu_to_hc32(fotg210, (u32)(addr >> 32)); -+ count = 0x1000 - (buf & 0x0fff); /* rest of that page */ -+ if (likely(len < count)) /* ... iff needed */ -+ count = len; -+ else { -+ buf += 0x1000; -+ buf &= ~0x0fff; -+ -+ /* per-qtd limit: from 16K to 20K (best alignment) */ -+ for (i = 1; count < len && i < 5; i++) { -+ addr = buf; -+ qtd->hw_buf[i] = cpu_to_hc32(fotg210, (u32)addr); -+ qtd->hw_buf_hi[i] = cpu_to_hc32(fotg210, -+ (u32)(addr >> 32)); -+ buf += 0x1000; -+ if ((count + 0x1000) < len) -+ count += 0x1000; -+ else -+ count = len; -+ } -+ -+ /* short packets may only terminate transfers */ -+ if (count != len) -+ count -= (count % maxpacket); -+ } -+ qtd->hw_token = cpu_to_hc32(fotg210, (count << 16) | token); -+ qtd->length = count; -+ -+ return count; -+} -+ -+static inline void qh_update(struct fotg210_hcd *fotg210, -+ struct fotg210_qh *qh, struct fotg210_qtd *qtd) -+{ -+ struct fotg210_qh_hw *hw = qh->hw; -+ -+ /* writes to an active overlay are unsafe */ -+ BUG_ON(qh->qh_state != QH_STATE_IDLE); -+ -+ hw->hw_qtd_next = QTD_NEXT(fotg210, qtd->qtd_dma); -+ hw->hw_alt_next = FOTG210_LIST_END(fotg210); -+ -+ /* Except for control endpoints, we make hardware maintain data -+ * toggle (like OHCI) ... here (re)initialize the toggle in the QH, -+ * and set the pseudo-toggle in udev. Only usb_clear_halt() will -+ * ever clear it. -+ */ -+ if (!(hw->hw_info1 & cpu_to_hc32(fotg210, QH_TOGGLE_CTL))) { -+ unsigned is_out, epnum; -+ -+ is_out = qh->is_out; -+ epnum = (hc32_to_cpup(fotg210, &hw->hw_info1) >> 8) & 0x0f; -+ if (unlikely(!usb_gettoggle(qh->dev, epnum, is_out))) { -+ hw->hw_token &= ~cpu_to_hc32(fotg210, QTD_TOGGLE); -+ usb_settoggle(qh->dev, epnum, is_out, 1); -+ } -+ } -+ -+ hw->hw_token &= cpu_to_hc32(fotg210, QTD_TOGGLE | QTD_STS_PING); -+} -+ -+/* if it weren't for a common silicon quirk (writing the dummy into the qh -+ * overlay, so qh->hw_token wrongly becomes inactive/halted), only fault -+ * recovery (including urb dequeue) would need software changes to a QH... -+ */ -+static void qh_refresh(struct fotg210_hcd *fotg210, struct fotg210_qh *qh) -+{ -+ struct fotg210_qtd *qtd; -+ -+ if (list_empty(&qh->qtd_list)) -+ qtd = qh->dummy; -+ else { -+ qtd = list_entry(qh->qtd_list.next, -+ struct fotg210_qtd, qtd_list); -+ /* -+ * first qtd may already be partially processed. -+ * If we come here during unlink, the QH overlay region -+ * might have reference to the just unlinked qtd. The -+ * qtd is updated in qh_completions(). Update the QH -+ * overlay here. -+ */ -+ if (cpu_to_hc32(fotg210, qtd->qtd_dma) == qh->hw->hw_current) { -+ qh->hw->hw_qtd_next = qtd->hw_next; -+ qtd = NULL; -+ } -+ } -+ -+ if (qtd) -+ qh_update(fotg210, qh, qtd); -+} -+ -+static void qh_link_async(struct fotg210_hcd *fotg210, struct fotg210_qh *qh); -+ -+static void fotg210_clear_tt_buffer_complete(struct usb_hcd *hcd, -+ struct usb_host_endpoint *ep) -+{ -+ struct fotg210_hcd *fotg210 = hcd_to_fotg210(hcd); -+ struct fotg210_qh *qh = ep->hcpriv; -+ unsigned long flags; -+ -+ spin_lock_irqsave(&fotg210->lock, flags); -+ qh->clearing_tt = 0; -+ if (qh->qh_state == QH_STATE_IDLE && !list_empty(&qh->qtd_list) -+ && fotg210->rh_state == FOTG210_RH_RUNNING) -+ qh_link_async(fotg210, qh); -+ spin_unlock_irqrestore(&fotg210->lock, flags); -+} -+ -+static void fotg210_clear_tt_buffer(struct fotg210_hcd *fotg210, -+ struct fotg210_qh *qh, struct urb *urb, u32 token) -+{ -+ -+ /* If an async split transaction gets an error or is unlinked, -+ * the TT buffer may be left in an indeterminate state. We -+ * have to clear the TT buffer. -+ * -+ * Note: this routine is never called for Isochronous transfers. -+ */ -+ if (urb->dev->tt && !usb_pipeint(urb->pipe) && !qh->clearing_tt) { -+ struct usb_device *tt = urb->dev->tt->hub; -+ -+ dev_dbg(&tt->dev, -+ "clear tt buffer port %d, a%d ep%d t%08x\n", -+ urb->dev->ttport, urb->dev->devnum, -+ usb_pipeendpoint(urb->pipe), token); -+ -+ if (urb->dev->tt->hub != -+ fotg210_to_hcd(fotg210)->self.root_hub) { -+ if (usb_hub_clear_tt_buffer(urb) == 0) -+ qh->clearing_tt = 1; -+ } -+ } -+} -+ -+static int qtd_copy_status(struct fotg210_hcd *fotg210, struct urb *urb, -+ size_t length, u32 token) -+{ -+ int status = -EINPROGRESS; -+ -+ /* count IN/OUT bytes, not SETUP (even short packets) */ -+ if (likely(QTD_PID(token) != 2)) -+ urb->actual_length += length - QTD_LENGTH(token); -+ -+ /* don't modify error codes */ -+ if (unlikely(urb->unlinked)) -+ return status; -+ -+ /* force cleanup after short read; not always an error */ -+ if (unlikely(IS_SHORT_READ(token))) -+ status = -EREMOTEIO; -+ -+ /* serious "can't proceed" faults reported by the hardware */ -+ if (token & QTD_STS_HALT) { -+ if (token & QTD_STS_BABBLE) { -+ /* FIXME "must" disable babbling device's port too */ -+ status = -EOVERFLOW; -+ /* CERR nonzero + halt --> stall */ -+ } else if (QTD_CERR(token)) { -+ status = -EPIPE; -+ -+ /* In theory, more than one of the following bits can be set -+ * since they are sticky and the transaction is retried. -+ * Which to test first is rather arbitrary. -+ */ -+ } else if (token & QTD_STS_MMF) { -+ /* fs/ls interrupt xfer missed the complete-split */ -+ status = -EPROTO; -+ } else if (token & QTD_STS_DBE) { -+ status = (QTD_PID(token) == 1) /* IN ? */ -+ ? -ENOSR /* hc couldn't read data */ -+ : -ECOMM; /* hc couldn't write data */ -+ } else if (token & QTD_STS_XACT) { -+ /* timeout, bad CRC, wrong PID, etc */ -+ fotg210_dbg(fotg210, "devpath %s ep%d%s 3strikes\n", -+ urb->dev->devpath, -+ usb_pipeendpoint(urb->pipe), -+ usb_pipein(urb->pipe) ? "in" : "out"); -+ status = -EPROTO; -+ } else { /* unknown */ -+ status = -EPROTO; -+ } -+ -+ fotg210_dbg(fotg210, -+ "dev%d ep%d%s qtd token %08x --> status %d\n", -+ usb_pipedevice(urb->pipe), -+ usb_pipeendpoint(urb->pipe), -+ usb_pipein(urb->pipe) ? "in" : "out", -+ token, status); -+ } -+ -+ return status; -+} -+ -+static void fotg210_urb_done(struct fotg210_hcd *fotg210, struct urb *urb, -+ int status) -+__releases(fotg210->lock) -+__acquires(fotg210->lock) -+{ -+ if (likely(urb->hcpriv != NULL)) { -+ struct fotg210_qh *qh = (struct fotg210_qh *) urb->hcpriv; -+ -+ /* S-mask in a QH means it's an interrupt urb */ -+ if ((qh->hw->hw_info2 & cpu_to_hc32(fotg210, QH_SMASK)) != 0) { -+ -+ /* ... update hc-wide periodic stats (for usbfs) */ -+ fotg210_to_hcd(fotg210)->self.bandwidth_int_reqs--; -+ } -+ } -+ -+ if (unlikely(urb->unlinked)) { -+ INCR(fotg210->stats.unlink); -+ } else { -+ /* report non-error and short read status as zero */ -+ if (status == -EINPROGRESS || status == -EREMOTEIO) -+ status = 0; -+ INCR(fotg210->stats.complete); -+ } -+ -+#ifdef FOTG210_URB_TRACE -+ fotg210_dbg(fotg210, -+ "%s %s urb %p ep%d%s status %d len %d/%d\n", -+ __func__, urb->dev->devpath, urb, -+ usb_pipeendpoint(urb->pipe), -+ usb_pipein(urb->pipe) ? "in" : "out", -+ status, -+ urb->actual_length, urb->transfer_buffer_length); -+#endif -+ -+ /* complete() can reenter this HCD */ -+ usb_hcd_unlink_urb_from_ep(fotg210_to_hcd(fotg210), urb); -+ spin_unlock(&fotg210->lock); -+ usb_hcd_giveback_urb(fotg210_to_hcd(fotg210), urb, status); -+ spin_lock(&fotg210->lock); -+} -+ -+static int qh_schedule(struct fotg210_hcd *fotg210, struct fotg210_qh *qh); -+ -+/* Process and free completed qtds for a qh, returning URBs to drivers. -+ * Chases up to qh->hw_current. Returns number of completions called, -+ * indicating how much "real" work we did. -+ */ -+static unsigned qh_completions(struct fotg210_hcd *fotg210, -+ struct fotg210_qh *qh) -+{ -+ struct fotg210_qtd *last, *end = qh->dummy; -+ struct fotg210_qtd *qtd, *tmp; -+ int last_status; -+ int stopped; -+ unsigned count = 0; -+ u8 state; -+ struct fotg210_qh_hw *hw = qh->hw; -+ -+ if (unlikely(list_empty(&qh->qtd_list))) -+ return count; -+ -+ /* completions (or tasks on other cpus) must never clobber HALT -+ * till we've gone through and cleaned everything up, even when -+ * they add urbs to this qh's queue or mark them for unlinking. -+ * -+ * NOTE: unlinking expects to be done in queue order. -+ * -+ * It's a bug for qh->qh_state to be anything other than -+ * QH_STATE_IDLE, unless our caller is scan_async() or -+ * scan_intr(). -+ */ -+ state = qh->qh_state; -+ qh->qh_state = QH_STATE_COMPLETING; -+ stopped = (state == QH_STATE_IDLE); -+ -+rescan: -+ last = NULL; -+ last_status = -EINPROGRESS; -+ qh->needs_rescan = 0; -+ -+ /* remove de-activated QTDs from front of queue. -+ * after faults (including short reads), cleanup this urb -+ * then let the queue advance. -+ * if queue is stopped, handles unlinks. -+ */ -+ list_for_each_entry_safe(qtd, tmp, &qh->qtd_list, qtd_list) { -+ struct urb *urb; -+ u32 token = 0; -+ -+ urb = qtd->urb; -+ -+ /* clean up any state from previous QTD ...*/ -+ if (last) { -+ if (likely(last->urb != urb)) { -+ fotg210_urb_done(fotg210, last->urb, -+ last_status); -+ count++; -+ last_status = -EINPROGRESS; -+ } -+ fotg210_qtd_free(fotg210, last); -+ last = NULL; -+ } -+ -+ /* ignore urbs submitted during completions we reported */ -+ if (qtd == end) -+ break; -+ -+ /* hardware copies qtd out of qh overlay */ -+ rmb(); -+ token = hc32_to_cpu(fotg210, qtd->hw_token); -+ -+ /* always clean up qtds the hc de-activated */ -+retry_xacterr: -+ if ((token & QTD_STS_ACTIVE) == 0) { -+ -+ /* Report Data Buffer Error: non-fatal but useful */ -+ if (token & QTD_STS_DBE) -+ fotg210_dbg(fotg210, -+ "detected DataBufferErr for urb %p ep%d%s len %d, qtd %p [qh %p]\n", -+ urb, usb_endpoint_num(&urb->ep->desc), -+ usb_endpoint_dir_in(&urb->ep->desc) -+ ? "in" : "out", -+ urb->transfer_buffer_length, qtd, qh); -+ -+ /* on STALL, error, and short reads this urb must -+ * complete and all its qtds must be recycled. -+ */ -+ if ((token & QTD_STS_HALT) != 0) { -+ -+ /* retry transaction errors until we -+ * reach the software xacterr limit -+ */ -+ if ((token & QTD_STS_XACT) && -+ QTD_CERR(token) == 0 && -+ ++qh->xacterrs < QH_XACTERR_MAX && -+ !urb->unlinked) { -+ fotg210_dbg(fotg210, -+ "detected XactErr len %zu/%zu retry %d\n", -+ qtd->length - QTD_LENGTH(token), -+ qtd->length, -+ qh->xacterrs); -+ -+ /* reset the token in the qtd and the -+ * qh overlay (which still contains -+ * the qtd) so that we pick up from -+ * where we left off -+ */ -+ token &= ~QTD_STS_HALT; -+ token |= QTD_STS_ACTIVE | -+ (FOTG210_TUNE_CERR << 10); -+ qtd->hw_token = cpu_to_hc32(fotg210, -+ token); -+ wmb(); -+ hw->hw_token = cpu_to_hc32(fotg210, -+ token); -+ goto retry_xacterr; -+ } -+ stopped = 1; -+ -+ /* magic dummy for some short reads; qh won't advance. -+ * that silicon quirk can kick in with this dummy too. -+ * -+ * other short reads won't stop the queue, including -+ * control transfers (status stage handles that) or -+ * most other single-qtd reads ... the queue stops if -+ * URB_SHORT_NOT_OK was set so the driver submitting -+ * the urbs could clean it up. -+ */ -+ } else if (IS_SHORT_READ(token) && -+ !(qtd->hw_alt_next & -+ FOTG210_LIST_END(fotg210))) { -+ stopped = 1; -+ } -+ -+ /* stop scanning when we reach qtds the hc is using */ -+ } else if (likely(!stopped -+ && fotg210->rh_state >= FOTG210_RH_RUNNING)) { -+ break; -+ -+ /* scan the whole queue for unlinks whenever it stops */ -+ } else { -+ stopped = 1; -+ -+ /* cancel everything if we halt, suspend, etc */ -+ if (fotg210->rh_state < FOTG210_RH_RUNNING) -+ last_status = -ESHUTDOWN; -+ -+ /* this qtd is active; skip it unless a previous qtd -+ * for its urb faulted, or its urb was canceled. -+ */ -+ else if (last_status == -EINPROGRESS && !urb->unlinked) -+ continue; -+ -+ /* qh unlinked; token in overlay may be most current */ -+ if (state == QH_STATE_IDLE && -+ cpu_to_hc32(fotg210, qtd->qtd_dma) -+ == hw->hw_current) { -+ token = hc32_to_cpu(fotg210, hw->hw_token); -+ -+ /* An unlink may leave an incomplete -+ * async transaction in the TT buffer. -+ * We have to clear it. -+ */ -+ fotg210_clear_tt_buffer(fotg210, qh, urb, -+ token); -+ } -+ } -+ -+ /* unless we already know the urb's status, collect qtd status -+ * and update count of bytes transferred. in common short read -+ * cases with only one data qtd (including control transfers), -+ * queue processing won't halt. but with two or more qtds (for -+ * example, with a 32 KB transfer), when the first qtd gets a -+ * short read the second must be removed by hand. -+ */ -+ if (last_status == -EINPROGRESS) { -+ last_status = qtd_copy_status(fotg210, urb, -+ qtd->length, token); -+ if (last_status == -EREMOTEIO && -+ (qtd->hw_alt_next & -+ FOTG210_LIST_END(fotg210))) -+ last_status = -EINPROGRESS; -+ -+ /* As part of low/full-speed endpoint-halt processing -+ * we must clear the TT buffer (11.17.5). -+ */ -+ if (unlikely(last_status != -EINPROGRESS && -+ last_status != -EREMOTEIO)) { -+ /* The TT's in some hubs malfunction when they -+ * receive this request following a STALL (they -+ * stop sending isochronous packets). Since a -+ * STALL can't leave the TT buffer in a busy -+ * state (if you believe Figures 11-48 - 11-51 -+ * in the USB 2.0 spec), we won't clear the TT -+ * buffer in this case. Strictly speaking this -+ * is a violation of the spec. -+ */ -+ if (last_status != -EPIPE) -+ fotg210_clear_tt_buffer(fotg210, qh, -+ urb, token); -+ } -+ } -+ -+ /* if we're removing something not at the queue head, -+ * patch the hardware queue pointer. -+ */ -+ if (stopped && qtd->qtd_list.prev != &qh->qtd_list) { -+ last = list_entry(qtd->qtd_list.prev, -+ struct fotg210_qtd, qtd_list); -+ last->hw_next = qtd->hw_next; -+ } -+ -+ /* remove qtd; it's recycled after possible urb completion */ -+ list_del(&qtd->qtd_list); -+ last = qtd; -+ -+ /* reinit the xacterr counter for the next qtd */ -+ qh->xacterrs = 0; -+ } -+ -+ /* last urb's completion might still need calling */ -+ if (likely(last != NULL)) { -+ fotg210_urb_done(fotg210, last->urb, last_status); -+ count++; -+ fotg210_qtd_free(fotg210, last); -+ } -+ -+ /* Do we need to rescan for URBs dequeued during a giveback? */ -+ if (unlikely(qh->needs_rescan)) { -+ /* If the QH is already unlinked, do the rescan now. */ -+ if (state == QH_STATE_IDLE) -+ goto rescan; -+ -+ /* Otherwise we have to wait until the QH is fully unlinked. -+ * Our caller will start an unlink if qh->needs_rescan is -+ * set. But if an unlink has already started, nothing needs -+ * to be done. -+ */ -+ if (state != QH_STATE_LINKED) -+ qh->needs_rescan = 0; -+ } -+ -+ /* restore original state; caller must unlink or relink */ -+ qh->qh_state = state; -+ -+ /* be sure the hardware's done with the qh before refreshing -+ * it after fault cleanup, or recovering from silicon wrongly -+ * overlaying the dummy qtd (which reduces DMA chatter). -+ */ -+ if (stopped != 0 || hw->hw_qtd_next == FOTG210_LIST_END(fotg210)) { -+ switch (state) { -+ case QH_STATE_IDLE: -+ qh_refresh(fotg210, qh); -+ break; -+ case QH_STATE_LINKED: -+ /* We won't refresh a QH that's linked (after the HC -+ * stopped the queue). That avoids a race: -+ * - HC reads first part of QH; -+ * - CPU updates that first part and the token; -+ * - HC reads rest of that QH, including token -+ * Result: HC gets an inconsistent image, and then -+ * DMAs to/from the wrong memory (corrupting it). -+ * -+ * That should be rare for interrupt transfers, -+ * except maybe high bandwidth ... -+ */ -+ -+ /* Tell the caller to start an unlink */ -+ qh->needs_rescan = 1; -+ break; -+ /* otherwise, unlink already started */ -+ } -+ } -+ -+ return count; -+} -+ -+/* reverse of qh_urb_transaction: free a list of TDs. -+ * used for cleanup after errors, before HC sees an URB's TDs. -+ */ -+static void qtd_list_free(struct fotg210_hcd *fotg210, struct urb *urb, -+ struct list_head *head) -+{ -+ struct fotg210_qtd *qtd, *temp; -+ -+ list_for_each_entry_safe(qtd, temp, head, qtd_list) { -+ list_del(&qtd->qtd_list); -+ fotg210_qtd_free(fotg210, qtd); -+ } -+} -+ -+/* create a list of filled qtds for this URB; won't link into qh. -+ */ -+static struct list_head *qh_urb_transaction(struct fotg210_hcd *fotg210, -+ struct urb *urb, struct list_head *head, gfp_t flags) -+{ -+ struct fotg210_qtd *qtd, *qtd_prev; -+ dma_addr_t buf; -+ int len, this_sg_len, maxpacket; -+ int is_input; -+ u32 token; -+ int i; -+ struct scatterlist *sg; -+ -+ /* -+ * URBs map to sequences of QTDs: one logical transaction -+ */ -+ qtd = fotg210_qtd_alloc(fotg210, flags); -+ if (unlikely(!qtd)) -+ return NULL; -+ list_add_tail(&qtd->qtd_list, head); -+ qtd->urb = urb; -+ -+ token = QTD_STS_ACTIVE; -+ token |= (FOTG210_TUNE_CERR << 10); -+ /* for split transactions, SplitXState initialized to zero */ -+ -+ len = urb->transfer_buffer_length; -+ is_input = usb_pipein(urb->pipe); -+ if (usb_pipecontrol(urb->pipe)) { -+ /* SETUP pid */ -+ qtd_fill(fotg210, qtd, urb->setup_dma, -+ sizeof(struct usb_ctrlrequest), -+ token | (2 /* "setup" */ << 8), 8); -+ -+ /* ... and always at least one more pid */ -+ token ^= QTD_TOGGLE; -+ qtd_prev = qtd; -+ qtd = fotg210_qtd_alloc(fotg210, flags); -+ if (unlikely(!qtd)) -+ goto cleanup; -+ qtd->urb = urb; -+ qtd_prev->hw_next = QTD_NEXT(fotg210, qtd->qtd_dma); -+ list_add_tail(&qtd->qtd_list, head); -+ -+ /* for zero length DATA stages, STATUS is always IN */ -+ if (len == 0) -+ token |= (1 /* "in" */ << 8); -+ } -+ -+ /* -+ * data transfer stage: buffer setup -+ */ -+ i = urb->num_mapped_sgs; -+ if (len > 0 && i > 0) { -+ sg = urb->sg; -+ buf = sg_dma_address(sg); -+ -+ /* urb->transfer_buffer_length may be smaller than the -+ * size of the scatterlist (or vice versa) -+ */ -+ this_sg_len = min_t(int, sg_dma_len(sg), len); -+ } else { -+ sg = NULL; -+ buf = urb->transfer_dma; -+ this_sg_len = len; -+ } -+ -+ if (is_input) -+ token |= (1 /* "in" */ << 8); -+ /* else it's already initted to "out" pid (0 << 8) */ -+ -+ maxpacket = usb_maxpacket(urb->dev, urb->pipe); -+ -+ /* -+ * buffer gets wrapped in one or more qtds; -+ * last one may be "short" (including zero len) -+ * and may serve as a control status ack -+ */ -+ for (;;) { -+ int this_qtd_len; -+ -+ this_qtd_len = qtd_fill(fotg210, qtd, buf, this_sg_len, token, -+ maxpacket); -+ this_sg_len -= this_qtd_len; -+ len -= this_qtd_len; -+ buf += this_qtd_len; -+ -+ /* -+ * short reads advance to a "magic" dummy instead of the next -+ * qtd ... that forces the queue to stop, for manual cleanup. -+ * (this will usually be overridden later.) -+ */ -+ if (is_input) -+ qtd->hw_alt_next = fotg210->async->hw->hw_alt_next; -+ -+ /* qh makes control packets use qtd toggle; maybe switch it */ -+ if ((maxpacket & (this_qtd_len + (maxpacket - 1))) == 0) -+ token ^= QTD_TOGGLE; -+ -+ if (likely(this_sg_len <= 0)) { -+ if (--i <= 0 || len <= 0) -+ break; -+ sg = sg_next(sg); -+ buf = sg_dma_address(sg); -+ this_sg_len = min_t(int, sg_dma_len(sg), len); -+ } -+ -+ qtd_prev = qtd; -+ qtd = fotg210_qtd_alloc(fotg210, flags); -+ if (unlikely(!qtd)) -+ goto cleanup; -+ qtd->urb = urb; -+ qtd_prev->hw_next = QTD_NEXT(fotg210, qtd->qtd_dma); -+ list_add_tail(&qtd->qtd_list, head); -+ } -+ -+ /* -+ * unless the caller requires manual cleanup after short reads, -+ * have the alt_next mechanism keep the queue running after the -+ * last data qtd (the only one, for control and most other cases). -+ */ -+ if (likely((urb->transfer_flags & URB_SHORT_NOT_OK) == 0 || -+ usb_pipecontrol(urb->pipe))) -+ qtd->hw_alt_next = FOTG210_LIST_END(fotg210); -+ -+ /* -+ * control requests may need a terminating data "status" ack; -+ * other OUT ones may need a terminating short packet -+ * (zero length). -+ */ -+ if (likely(urb->transfer_buffer_length != 0)) { -+ int one_more = 0; -+ -+ if (usb_pipecontrol(urb->pipe)) { -+ one_more = 1; -+ token ^= 0x0100; /* "in" <--> "out" */ -+ token |= QTD_TOGGLE; /* force DATA1 */ -+ } else if (usb_pipeout(urb->pipe) -+ && (urb->transfer_flags & URB_ZERO_PACKET) -+ && !(urb->transfer_buffer_length % maxpacket)) { -+ one_more = 1; -+ } -+ if (one_more) { -+ qtd_prev = qtd; -+ qtd = fotg210_qtd_alloc(fotg210, flags); -+ if (unlikely(!qtd)) -+ goto cleanup; -+ qtd->urb = urb; -+ qtd_prev->hw_next = QTD_NEXT(fotg210, qtd->qtd_dma); -+ list_add_tail(&qtd->qtd_list, head); -+ -+ /* never any data in such packets */ -+ qtd_fill(fotg210, qtd, 0, 0, token, 0); -+ } -+ } -+ -+ /* by default, enable interrupt on urb completion */ -+ if (likely(!(urb->transfer_flags & URB_NO_INTERRUPT))) -+ qtd->hw_token |= cpu_to_hc32(fotg210, QTD_IOC); -+ return head; -+ -+cleanup: -+ qtd_list_free(fotg210, urb, head); -+ return NULL; -+} -+ -+/* Would be best to create all qh's from config descriptors, -+ * when each interface/altsetting is established. Unlink -+ * any previous qh and cancel its urbs first; endpoints are -+ * implicitly reset then (data toggle too). -+ * That'd mean updating how usbcore talks to HCDs. (2.7?) -+ */ -+ -+ -+/* Each QH holds a qtd list; a QH is used for everything except iso. -+ * -+ * For interrupt urbs, the scheduler must set the microframe scheduling -+ * mask(s) each time the QH gets scheduled. For highspeed, that's -+ * just one microframe in the s-mask. For split interrupt transactions -+ * there are additional complications: c-mask, maybe FSTNs. -+ */ -+static struct fotg210_qh *qh_make(struct fotg210_hcd *fotg210, struct urb *urb, -+ gfp_t flags) -+{ -+ struct fotg210_qh *qh = fotg210_qh_alloc(fotg210, flags); -+ struct usb_host_endpoint *ep; -+ u32 info1 = 0, info2 = 0; -+ int is_input, type; -+ int maxp = 0; -+ int mult; -+ struct usb_tt *tt = urb->dev->tt; -+ struct fotg210_qh_hw *hw; -+ -+ if (!qh) -+ return qh; -+ -+ /* -+ * init endpoint/device data for this QH -+ */ -+ info1 |= usb_pipeendpoint(urb->pipe) << 8; -+ info1 |= usb_pipedevice(urb->pipe) << 0; -+ -+ is_input = usb_pipein(urb->pipe); -+ type = usb_pipetype(urb->pipe); -+ ep = usb_pipe_endpoint(urb->dev, urb->pipe); -+ maxp = usb_endpoint_maxp(&ep->desc); -+ mult = usb_endpoint_maxp_mult(&ep->desc); -+ -+ /* 1024 byte maxpacket is a hardware ceiling. High bandwidth -+ * acts like up to 3KB, but is built from smaller packets. -+ */ -+ if (maxp > 1024) { -+ fotg210_dbg(fotg210, "bogus qh maxpacket %d\n", maxp); -+ goto done; -+ } -+ -+ /* Compute interrupt scheduling parameters just once, and save. -+ * - allowing for high bandwidth, how many nsec/uframe are used? -+ * - split transactions need a second CSPLIT uframe; same question -+ * - splits also need a schedule gap (for full/low speed I/O) -+ * - qh has a polling interval -+ * -+ * For control/bulk requests, the HC or TT handles these. -+ */ -+ if (type == PIPE_INTERRUPT) { -+ qh->usecs = NS_TO_US(usb_calc_bus_time(USB_SPEED_HIGH, -+ is_input, 0, mult * maxp)); -+ qh->start = NO_FRAME; -+ -+ if (urb->dev->speed == USB_SPEED_HIGH) { -+ qh->c_usecs = 0; -+ qh->gap_uf = 0; -+ -+ qh->period = urb->interval >> 3; -+ if (qh->period == 0 && urb->interval != 1) { -+ /* NOTE interval 2 or 4 uframes could work. -+ * But interval 1 scheduling is simpler, and -+ * includes high bandwidth. -+ */ -+ urb->interval = 1; -+ } else if (qh->period > fotg210->periodic_size) { -+ qh->period = fotg210->periodic_size; -+ urb->interval = qh->period << 3; -+ } -+ } else { -+ int think_time; -+ -+ /* gap is f(FS/LS transfer times) */ -+ qh->gap_uf = 1 + usb_calc_bus_time(urb->dev->speed, -+ is_input, 0, maxp) / (125 * 1000); -+ -+ /* FIXME this just approximates SPLIT/CSPLIT times */ -+ if (is_input) { /* SPLIT, gap, CSPLIT+DATA */ -+ qh->c_usecs = qh->usecs + HS_USECS(0); -+ qh->usecs = HS_USECS(1); -+ } else { /* SPLIT+DATA, gap, CSPLIT */ -+ qh->usecs += HS_USECS(1); -+ qh->c_usecs = HS_USECS(0); -+ } -+ -+ think_time = tt ? tt->think_time : 0; -+ qh->tt_usecs = NS_TO_US(think_time + -+ usb_calc_bus_time(urb->dev->speed, -+ is_input, 0, maxp)); -+ qh->period = urb->interval; -+ if (qh->period > fotg210->periodic_size) { -+ qh->period = fotg210->periodic_size; -+ urb->interval = qh->period; -+ } -+ } -+ } -+ -+ /* support for tt scheduling, and access to toggles */ -+ qh->dev = urb->dev; -+ -+ /* using TT? */ -+ switch (urb->dev->speed) { -+ case USB_SPEED_LOW: -+ info1 |= QH_LOW_SPEED; -+ fallthrough; -+ -+ case USB_SPEED_FULL: -+ /* EPS 0 means "full" */ -+ if (type != PIPE_INTERRUPT) -+ info1 |= (FOTG210_TUNE_RL_TT << 28); -+ if (type == PIPE_CONTROL) { -+ info1 |= QH_CONTROL_EP; /* for TT */ -+ info1 |= QH_TOGGLE_CTL; /* toggle from qtd */ -+ } -+ info1 |= maxp << 16; -+ -+ info2 |= (FOTG210_TUNE_MULT_TT << 30); -+ -+ /* Some Freescale processors have an erratum in which the -+ * port number in the queue head was 0..N-1 instead of 1..N. -+ */ -+ if (fotg210_has_fsl_portno_bug(fotg210)) -+ info2 |= (urb->dev->ttport-1) << 23; -+ else -+ info2 |= urb->dev->ttport << 23; -+ -+ /* set the address of the TT; for TDI's integrated -+ * root hub tt, leave it zeroed. -+ */ -+ if (tt && tt->hub != fotg210_to_hcd(fotg210)->self.root_hub) -+ info2 |= tt->hub->devnum << 16; -+ -+ /* NOTE: if (PIPE_INTERRUPT) { scheduler sets c-mask } */ -+ -+ break; -+ -+ case USB_SPEED_HIGH: /* no TT involved */ -+ info1 |= QH_HIGH_SPEED; -+ if (type == PIPE_CONTROL) { -+ info1 |= (FOTG210_TUNE_RL_HS << 28); -+ info1 |= 64 << 16; /* usb2 fixed maxpacket */ -+ info1 |= QH_TOGGLE_CTL; /* toggle from qtd */ -+ info2 |= (FOTG210_TUNE_MULT_HS << 30); -+ } else if (type == PIPE_BULK) { -+ info1 |= (FOTG210_TUNE_RL_HS << 28); -+ /* The USB spec says that high speed bulk endpoints -+ * always use 512 byte maxpacket. But some device -+ * vendors decided to ignore that, and MSFT is happy -+ * to help them do so. So now people expect to use -+ * such nonconformant devices with Linux too; sigh. -+ */ -+ info1 |= maxp << 16; -+ info2 |= (FOTG210_TUNE_MULT_HS << 30); -+ } else { /* PIPE_INTERRUPT */ -+ info1 |= maxp << 16; -+ info2 |= mult << 30; -+ } -+ break; -+ default: -+ fotg210_dbg(fotg210, "bogus dev %p speed %d\n", urb->dev, -+ urb->dev->speed); -+done: -+ qh_destroy(fotg210, qh); -+ return NULL; -+ } -+ -+ /* NOTE: if (PIPE_INTERRUPT) { scheduler sets s-mask } */ -+ -+ /* init as live, toggle clear, advance to dummy */ -+ qh->qh_state = QH_STATE_IDLE; -+ hw = qh->hw; -+ hw->hw_info1 = cpu_to_hc32(fotg210, info1); -+ hw->hw_info2 = cpu_to_hc32(fotg210, info2); -+ qh->is_out = !is_input; -+ usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe), !is_input, 1); -+ qh_refresh(fotg210, qh); -+ return qh; -+} -+ -+static void enable_async(struct fotg210_hcd *fotg210) -+{ -+ if (fotg210->async_count++) -+ return; -+ -+ /* Stop waiting to turn off the async schedule */ -+ fotg210->enabled_hrtimer_events &= ~BIT(FOTG210_HRTIMER_DISABLE_ASYNC); -+ -+ /* Don't start the schedule until ASS is 0 */ -+ fotg210_poll_ASS(fotg210); -+ turn_on_io_watchdog(fotg210); -+} -+ -+static void disable_async(struct fotg210_hcd *fotg210) -+{ -+ if (--fotg210->async_count) -+ return; -+ -+ /* The async schedule and async_unlink list are supposed to be empty */ -+ WARN_ON(fotg210->async->qh_next.qh || fotg210->async_unlink); -+ -+ /* Don't turn off the schedule until ASS is 1 */ -+ fotg210_poll_ASS(fotg210); -+} -+ -+/* move qh (and its qtds) onto async queue; maybe enable queue. */ -+ -+static void qh_link_async(struct fotg210_hcd *fotg210, struct fotg210_qh *qh) -+{ -+ __hc32 dma = QH_NEXT(fotg210, qh->qh_dma); -+ struct fotg210_qh *head; -+ -+ /* Don't link a QH if there's a Clear-TT-Buffer pending */ -+ if (unlikely(qh->clearing_tt)) -+ return; -+ -+ WARN_ON(qh->qh_state != QH_STATE_IDLE); -+ -+ /* clear halt and/or toggle; and maybe recover from silicon quirk */ -+ qh_refresh(fotg210, qh); -+ -+ /* splice right after start */ -+ head = fotg210->async; -+ qh->qh_next = head->qh_next; -+ qh->hw->hw_next = head->hw->hw_next; -+ wmb(); -+ -+ head->qh_next.qh = qh; -+ head->hw->hw_next = dma; -+ -+ qh->xacterrs = 0; -+ qh->qh_state = QH_STATE_LINKED; -+ /* qtd completions reported later by interrupt */ -+ -+ enable_async(fotg210); -+} -+ -+/* For control/bulk/interrupt, return QH with these TDs appended. -+ * Allocates and initializes the QH if necessary. -+ * Returns null if it can't allocate a QH it needs to. -+ * If the QH has TDs (urbs) already, that's great. -+ */ -+static struct fotg210_qh *qh_append_tds(struct fotg210_hcd *fotg210, -+ struct urb *urb, struct list_head *qtd_list, -+ int epnum, void **ptr) -+{ -+ struct fotg210_qh *qh = NULL; -+ __hc32 qh_addr_mask = cpu_to_hc32(fotg210, 0x7f); -+ -+ qh = (struct fotg210_qh *) *ptr; -+ if (unlikely(qh == NULL)) { -+ /* can't sleep here, we have fotg210->lock... */ -+ qh = qh_make(fotg210, urb, GFP_ATOMIC); -+ *ptr = qh; -+ } -+ if (likely(qh != NULL)) { -+ struct fotg210_qtd *qtd; -+ -+ if (unlikely(list_empty(qtd_list))) -+ qtd = NULL; -+ else -+ qtd = list_entry(qtd_list->next, struct fotg210_qtd, -+ qtd_list); -+ -+ /* control qh may need patching ... */ -+ if (unlikely(epnum == 0)) { -+ /* usb_reset_device() briefly reverts to address 0 */ -+ if (usb_pipedevice(urb->pipe) == 0) -+ qh->hw->hw_info1 &= ~qh_addr_mask; -+ } -+ -+ /* just one way to queue requests: swap with the dummy qtd. -+ * only hc or qh_refresh() ever modify the overlay. -+ */ -+ if (likely(qtd != NULL)) { -+ struct fotg210_qtd *dummy; -+ dma_addr_t dma; -+ __hc32 token; -+ -+ /* to avoid racing the HC, use the dummy td instead of -+ * the first td of our list (becomes new dummy). both -+ * tds stay deactivated until we're done, when the -+ * HC is allowed to fetch the old dummy (4.10.2). -+ */ -+ token = qtd->hw_token; -+ qtd->hw_token = HALT_BIT(fotg210); -+ -+ dummy = qh->dummy; -+ -+ dma = dummy->qtd_dma; -+ *dummy = *qtd; -+ dummy->qtd_dma = dma; -+ -+ list_del(&qtd->qtd_list); -+ list_add(&dummy->qtd_list, qtd_list); -+ list_splice_tail(qtd_list, &qh->qtd_list); -+ -+ fotg210_qtd_init(fotg210, qtd, qtd->qtd_dma); -+ qh->dummy = qtd; -+ -+ /* hc must see the new dummy at list end */ -+ dma = qtd->qtd_dma; -+ qtd = list_entry(qh->qtd_list.prev, -+ struct fotg210_qtd, qtd_list); -+ qtd->hw_next = QTD_NEXT(fotg210, dma); -+ -+ /* let the hc process these next qtds */ -+ wmb(); -+ dummy->hw_token = token; -+ -+ urb->hcpriv = qh; -+ } -+ } -+ return qh; -+} -+ -+static int submit_async(struct fotg210_hcd *fotg210, struct urb *urb, -+ struct list_head *qtd_list, gfp_t mem_flags) -+{ -+ int epnum; -+ unsigned long flags; -+ struct fotg210_qh *qh = NULL; -+ int rc; -+ -+ epnum = urb->ep->desc.bEndpointAddress; -+ -+#ifdef FOTG210_URB_TRACE -+ { -+ struct fotg210_qtd *qtd; -+ -+ qtd = list_entry(qtd_list->next, struct fotg210_qtd, qtd_list); -+ fotg210_dbg(fotg210, -+ "%s %s urb %p ep%d%s len %d, qtd %p [qh %p]\n", -+ __func__, urb->dev->devpath, urb, -+ epnum & 0x0f, (epnum & USB_DIR_IN) -+ ? "in" : "out", -+ urb->transfer_buffer_length, -+ qtd, urb->ep->hcpriv); -+ } -+#endif -+ -+ spin_lock_irqsave(&fotg210->lock, flags); -+ if (unlikely(!HCD_HW_ACCESSIBLE(fotg210_to_hcd(fotg210)))) { -+ rc = -ESHUTDOWN; -+ goto done; -+ } -+ rc = usb_hcd_link_urb_to_ep(fotg210_to_hcd(fotg210), urb); -+ if (unlikely(rc)) -+ goto done; -+ -+ qh = qh_append_tds(fotg210, urb, qtd_list, epnum, &urb->ep->hcpriv); -+ if (unlikely(qh == NULL)) { -+ usb_hcd_unlink_urb_from_ep(fotg210_to_hcd(fotg210), urb); -+ rc = -ENOMEM; -+ goto done; -+ } -+ -+ /* Control/bulk operations through TTs don't need scheduling, -+ * the HC and TT handle it when the TT has a buffer ready. -+ */ -+ if (likely(qh->qh_state == QH_STATE_IDLE)) -+ qh_link_async(fotg210, qh); -+done: -+ spin_unlock_irqrestore(&fotg210->lock, flags); -+ if (unlikely(qh == NULL)) -+ qtd_list_free(fotg210, urb, qtd_list); -+ return rc; -+} -+ -+static void single_unlink_async(struct fotg210_hcd *fotg210, -+ struct fotg210_qh *qh) -+{ -+ struct fotg210_qh *prev; -+ -+ /* Add to the end of the list of QHs waiting for the next IAAD */ -+ qh->qh_state = QH_STATE_UNLINK; -+ if (fotg210->async_unlink) -+ fotg210->async_unlink_last->unlink_next = qh; -+ else -+ fotg210->async_unlink = qh; -+ fotg210->async_unlink_last = qh; -+ -+ /* Unlink it from the schedule */ -+ prev = fotg210->async; -+ while (prev->qh_next.qh != qh) -+ prev = prev->qh_next.qh; -+ -+ prev->hw->hw_next = qh->hw->hw_next; -+ prev->qh_next = qh->qh_next; -+ if (fotg210->qh_scan_next == qh) -+ fotg210->qh_scan_next = qh->qh_next.qh; -+} -+ -+static void start_iaa_cycle(struct fotg210_hcd *fotg210, bool nested) -+{ -+ /* -+ * Do nothing if an IAA cycle is already running or -+ * if one will be started shortly. -+ */ -+ if (fotg210->async_iaa || fotg210->async_unlinking) -+ return; -+ -+ /* Do all the waiting QHs at once */ -+ fotg210->async_iaa = fotg210->async_unlink; -+ fotg210->async_unlink = NULL; -+ -+ /* If the controller isn't running, we don't have to wait for it */ -+ if (unlikely(fotg210->rh_state < FOTG210_RH_RUNNING)) { -+ if (!nested) /* Avoid recursion */ -+ end_unlink_async(fotg210); -+ -+ /* Otherwise start a new IAA cycle */ -+ } else if (likely(fotg210->rh_state == FOTG210_RH_RUNNING)) { -+ /* Make sure the unlinks are all visible to the hardware */ -+ wmb(); -+ -+ fotg210_writel(fotg210, fotg210->command | CMD_IAAD, -+ &fotg210->regs->command); -+ fotg210_readl(fotg210, &fotg210->regs->command); -+ fotg210_enable_event(fotg210, FOTG210_HRTIMER_IAA_WATCHDOG, -+ true); -+ } -+} -+ -+/* the async qh for the qtds being unlinked are now gone from the HC */ -+ -+static void end_unlink_async(struct fotg210_hcd *fotg210) -+{ -+ struct fotg210_qh *qh; -+ -+ /* Process the idle QHs */ -+restart: -+ fotg210->async_unlinking = true; -+ while (fotg210->async_iaa) { -+ qh = fotg210->async_iaa; -+ fotg210->async_iaa = qh->unlink_next; -+ qh->unlink_next = NULL; -+ -+ qh->qh_state = QH_STATE_IDLE; -+ qh->qh_next.qh = NULL; -+ -+ qh_completions(fotg210, qh); -+ if (!list_empty(&qh->qtd_list) && -+ fotg210->rh_state == FOTG210_RH_RUNNING) -+ qh_link_async(fotg210, qh); -+ disable_async(fotg210); -+ } -+ fotg210->async_unlinking = false; -+ -+ /* Start a new IAA cycle if any QHs are waiting for it */ -+ if (fotg210->async_unlink) { -+ start_iaa_cycle(fotg210, true); -+ if (unlikely(fotg210->rh_state < FOTG210_RH_RUNNING)) -+ goto restart; -+ } -+} -+ -+static void unlink_empty_async(struct fotg210_hcd *fotg210) -+{ -+ struct fotg210_qh *qh, *next; -+ bool stopped = (fotg210->rh_state < FOTG210_RH_RUNNING); -+ bool check_unlinks_later = false; -+ -+ /* Unlink all the async QHs that have been empty for a timer cycle */ -+ next = fotg210->async->qh_next.qh; -+ while (next) { -+ qh = next; -+ next = qh->qh_next.qh; -+ -+ if (list_empty(&qh->qtd_list) && -+ qh->qh_state == QH_STATE_LINKED) { -+ if (!stopped && qh->unlink_cycle == -+ fotg210->async_unlink_cycle) -+ check_unlinks_later = true; -+ else -+ single_unlink_async(fotg210, qh); -+ } -+ } -+ -+ /* Start a new IAA cycle if any QHs are waiting for it */ -+ if (fotg210->async_unlink) -+ start_iaa_cycle(fotg210, false); -+ -+ /* QHs that haven't been empty for long enough will be handled later */ -+ if (check_unlinks_later) { -+ fotg210_enable_event(fotg210, FOTG210_HRTIMER_ASYNC_UNLINKS, -+ true); -+ ++fotg210->async_unlink_cycle; -+ } -+} -+ -+/* makes sure the async qh will become idle */ -+/* caller must own fotg210->lock */ -+ -+static void start_unlink_async(struct fotg210_hcd *fotg210, -+ struct fotg210_qh *qh) -+{ -+ /* -+ * If the QH isn't linked then there's nothing we can do -+ * unless we were called during a giveback, in which case -+ * qh_completions() has to deal with it. -+ */ -+ if (qh->qh_state != QH_STATE_LINKED) { -+ if (qh->qh_state == QH_STATE_COMPLETING) -+ qh->needs_rescan = 1; -+ return; -+ } -+ -+ single_unlink_async(fotg210, qh); -+ start_iaa_cycle(fotg210, false); -+} -+ -+static void scan_async(struct fotg210_hcd *fotg210) -+{ -+ struct fotg210_qh *qh; -+ bool check_unlinks_later = false; -+ -+ fotg210->qh_scan_next = fotg210->async->qh_next.qh; -+ while (fotg210->qh_scan_next) { -+ qh = fotg210->qh_scan_next; -+ fotg210->qh_scan_next = qh->qh_next.qh; -+rescan: -+ /* clean any finished work for this qh */ -+ if (!list_empty(&qh->qtd_list)) { -+ int temp; -+ -+ /* -+ * Unlinks could happen here; completion reporting -+ * drops the lock. That's why fotg210->qh_scan_next -+ * always holds the next qh to scan; if the next qh -+ * gets unlinked then fotg210->qh_scan_next is adjusted -+ * in single_unlink_async(). -+ */ -+ temp = qh_completions(fotg210, qh); -+ if (qh->needs_rescan) { -+ start_unlink_async(fotg210, qh); -+ } else if (list_empty(&qh->qtd_list) -+ && qh->qh_state == QH_STATE_LINKED) { -+ qh->unlink_cycle = fotg210->async_unlink_cycle; -+ check_unlinks_later = true; -+ } else if (temp != 0) -+ goto rescan; -+ } -+ } -+ -+ /* -+ * Unlink empty entries, reducing DMA usage as well -+ * as HCD schedule-scanning costs. Delay for any qh -+ * we just scanned, there's a not-unusual case that it -+ * doesn't stay idle for long. -+ */ -+ if (check_unlinks_later && fotg210->rh_state == FOTG210_RH_RUNNING && -+ !(fotg210->enabled_hrtimer_events & -+ BIT(FOTG210_HRTIMER_ASYNC_UNLINKS))) { -+ fotg210_enable_event(fotg210, -+ FOTG210_HRTIMER_ASYNC_UNLINKS, true); -+ ++fotg210->async_unlink_cycle; -+ } -+} -+/* EHCI scheduled transaction support: interrupt, iso, split iso -+ * These are called "periodic" transactions in the EHCI spec. -+ * -+ * Note that for interrupt transfers, the QH/QTD manipulation is shared -+ * with the "asynchronous" transaction support (control/bulk transfers). -+ * The only real difference is in how interrupt transfers are scheduled. -+ * -+ * For ISO, we make an "iso_stream" head to serve the same role as a QH. -+ * It keeps track of every ITD (or SITD) that's linked, and holds enough -+ * pre-calculated schedule data to make appending to the queue be quick. -+ */ -+static int fotg210_get_frame(struct usb_hcd *hcd); -+ -+/* periodic_next_shadow - return "next" pointer on shadow list -+ * @periodic: host pointer to qh/itd -+ * @tag: hardware tag for type of this record -+ */ -+static union fotg210_shadow *periodic_next_shadow(struct fotg210_hcd *fotg210, -+ union fotg210_shadow *periodic, __hc32 tag) -+{ -+ switch (hc32_to_cpu(fotg210, tag)) { -+ case Q_TYPE_QH: -+ return &periodic->qh->qh_next; -+ case Q_TYPE_FSTN: -+ return &periodic->fstn->fstn_next; -+ default: -+ return &periodic->itd->itd_next; -+ } -+} -+ -+static __hc32 *shadow_next_periodic(struct fotg210_hcd *fotg210, -+ union fotg210_shadow *periodic, __hc32 tag) -+{ -+ switch (hc32_to_cpu(fotg210, tag)) { -+ /* our fotg210_shadow.qh is actually software part */ -+ case Q_TYPE_QH: -+ return &periodic->qh->hw->hw_next; -+ /* others are hw parts */ -+ default: -+ return periodic->hw_next; -+ } -+} -+ -+/* caller must hold fotg210->lock */ -+static void periodic_unlink(struct fotg210_hcd *fotg210, unsigned frame, -+ void *ptr) -+{ -+ union fotg210_shadow *prev_p = &fotg210->pshadow[frame]; -+ __hc32 *hw_p = &fotg210->periodic[frame]; -+ union fotg210_shadow here = *prev_p; -+ -+ /* find predecessor of "ptr"; hw and shadow lists are in sync */ -+ while (here.ptr && here.ptr != ptr) { -+ prev_p = periodic_next_shadow(fotg210, prev_p, -+ Q_NEXT_TYPE(fotg210, *hw_p)); -+ hw_p = shadow_next_periodic(fotg210, &here, -+ Q_NEXT_TYPE(fotg210, *hw_p)); -+ here = *prev_p; -+ } -+ /* an interrupt entry (at list end) could have been shared */ -+ if (!here.ptr) -+ return; -+ -+ /* update shadow and hardware lists ... the old "next" pointers -+ * from ptr may still be in use, the caller updates them. -+ */ -+ *prev_p = *periodic_next_shadow(fotg210, &here, -+ Q_NEXT_TYPE(fotg210, *hw_p)); -+ -+ *hw_p = *shadow_next_periodic(fotg210, &here, -+ Q_NEXT_TYPE(fotg210, *hw_p)); -+} -+ -+/* how many of the uframe's 125 usecs are allocated? */ -+static unsigned short periodic_usecs(struct fotg210_hcd *fotg210, -+ unsigned frame, unsigned uframe) -+{ -+ __hc32 *hw_p = &fotg210->periodic[frame]; -+ union fotg210_shadow *q = &fotg210->pshadow[frame]; -+ unsigned usecs = 0; -+ struct fotg210_qh_hw *hw; -+ -+ while (q->ptr) { -+ switch (hc32_to_cpu(fotg210, Q_NEXT_TYPE(fotg210, *hw_p))) { -+ case Q_TYPE_QH: -+ hw = q->qh->hw; -+ /* is it in the S-mask? */ -+ if (hw->hw_info2 & cpu_to_hc32(fotg210, 1 << uframe)) -+ usecs += q->qh->usecs; -+ /* ... or C-mask? */ -+ if (hw->hw_info2 & cpu_to_hc32(fotg210, -+ 1 << (8 + uframe))) -+ usecs += q->qh->c_usecs; -+ hw_p = &hw->hw_next; -+ q = &q->qh->qh_next; -+ break; -+ /* case Q_TYPE_FSTN: */ -+ default: -+ /* for "save place" FSTNs, count the relevant INTR -+ * bandwidth from the previous frame -+ */ -+ if (q->fstn->hw_prev != FOTG210_LIST_END(fotg210)) -+ fotg210_dbg(fotg210, "ignoring FSTN cost ...\n"); -+ -+ hw_p = &q->fstn->hw_next; -+ q = &q->fstn->fstn_next; -+ break; -+ case Q_TYPE_ITD: -+ if (q->itd->hw_transaction[uframe]) -+ usecs += q->itd->stream->usecs; -+ hw_p = &q->itd->hw_next; -+ q = &q->itd->itd_next; -+ break; -+ } -+ } -+ if (usecs > fotg210->uframe_periodic_max) -+ fotg210_err(fotg210, "uframe %d sched overrun: %d usecs\n", -+ frame * 8 + uframe, usecs); -+ return usecs; -+} -+ -+static int same_tt(struct usb_device *dev1, struct usb_device *dev2) -+{ -+ if (!dev1->tt || !dev2->tt) -+ return 0; -+ if (dev1->tt != dev2->tt) -+ return 0; -+ if (dev1->tt->multi) -+ return dev1->ttport == dev2->ttport; -+ else -+ return 1; -+} -+ -+/* return true iff the device's transaction translator is available -+ * for a periodic transfer starting at the specified frame, using -+ * all the uframes in the mask. -+ */ -+static int tt_no_collision(struct fotg210_hcd *fotg210, unsigned period, -+ struct usb_device *dev, unsigned frame, u32 uf_mask) -+{ -+ if (period == 0) /* error */ -+ return 0; -+ -+ /* note bandwidth wastage: split never follows csplit -+ * (different dev or endpoint) until the next uframe. -+ * calling convention doesn't make that distinction. -+ */ -+ for (; frame < fotg210->periodic_size; frame += period) { -+ union fotg210_shadow here; -+ __hc32 type; -+ struct fotg210_qh_hw *hw; -+ -+ here = fotg210->pshadow[frame]; -+ type = Q_NEXT_TYPE(fotg210, fotg210->periodic[frame]); -+ while (here.ptr) { -+ switch (hc32_to_cpu(fotg210, type)) { -+ case Q_TYPE_ITD: -+ type = Q_NEXT_TYPE(fotg210, here.itd->hw_next); -+ here = here.itd->itd_next; -+ continue; -+ case Q_TYPE_QH: -+ hw = here.qh->hw; -+ if (same_tt(dev, here.qh->dev)) { -+ u32 mask; -+ -+ mask = hc32_to_cpu(fotg210, -+ hw->hw_info2); -+ /* "knows" no gap is needed */ -+ mask |= mask >> 8; -+ if (mask & uf_mask) -+ break; -+ } -+ type = Q_NEXT_TYPE(fotg210, hw->hw_next); -+ here = here.qh->qh_next; -+ continue; -+ /* case Q_TYPE_FSTN: */ -+ default: -+ fotg210_dbg(fotg210, -+ "periodic frame %d bogus type %d\n", -+ frame, type); -+ } -+ -+ /* collision or error */ -+ return 0; -+ } -+ } -+ -+ /* no collision */ -+ return 1; -+} -+ -+static void enable_periodic(struct fotg210_hcd *fotg210) -+{ -+ if (fotg210->periodic_count++) -+ return; -+ -+ /* Stop waiting to turn off the periodic schedule */ -+ fotg210->enabled_hrtimer_events &= -+ ~BIT(FOTG210_HRTIMER_DISABLE_PERIODIC); -+ -+ /* Don't start the schedule until PSS is 0 */ -+ fotg210_poll_PSS(fotg210); -+ turn_on_io_watchdog(fotg210); -+} -+ -+static void disable_periodic(struct fotg210_hcd *fotg210) -+{ -+ if (--fotg210->periodic_count) -+ return; -+ -+ /* Don't turn off the schedule until PSS is 1 */ -+ fotg210_poll_PSS(fotg210); -+} -+ -+/* periodic schedule slots have iso tds (normal or split) first, then a -+ * sparse tree for active interrupt transfers. -+ * -+ * this just links in a qh; caller guarantees uframe masks are set right. -+ * no FSTN support (yet; fotg210 0.96+) -+ */ -+static void qh_link_periodic(struct fotg210_hcd *fotg210, struct fotg210_qh *qh) -+{ -+ unsigned i; -+ unsigned period = qh->period; -+ -+ dev_dbg(&qh->dev->dev, -+ "link qh%d-%04x/%p start %d [%d/%d us]\n", period, -+ hc32_to_cpup(fotg210, &qh->hw->hw_info2) & -+ (QH_CMASK | QH_SMASK), qh, qh->start, qh->usecs, -+ qh->c_usecs); -+ -+ /* high bandwidth, or otherwise every microframe */ -+ if (period == 0) -+ period = 1; -+ -+ for (i = qh->start; i < fotg210->periodic_size; i += period) { -+ union fotg210_shadow *prev = &fotg210->pshadow[i]; -+ __hc32 *hw_p = &fotg210->periodic[i]; -+ union fotg210_shadow here = *prev; -+ __hc32 type = 0; -+ -+ /* skip the iso nodes at list head */ -+ while (here.ptr) { -+ type = Q_NEXT_TYPE(fotg210, *hw_p); -+ if (type == cpu_to_hc32(fotg210, Q_TYPE_QH)) -+ break; -+ prev = periodic_next_shadow(fotg210, prev, type); -+ hw_p = shadow_next_periodic(fotg210, &here, type); -+ here = *prev; -+ } -+ -+ /* sorting each branch by period (slow-->fast) -+ * enables sharing interior tree nodes -+ */ -+ while (here.ptr && qh != here.qh) { -+ if (qh->period > here.qh->period) -+ break; -+ prev = &here.qh->qh_next; -+ hw_p = &here.qh->hw->hw_next; -+ here = *prev; -+ } -+ /* link in this qh, unless some earlier pass did that */ -+ if (qh != here.qh) { -+ qh->qh_next = here; -+ if (here.qh) -+ qh->hw->hw_next = *hw_p; -+ wmb(); -+ prev->qh = qh; -+ *hw_p = QH_NEXT(fotg210, qh->qh_dma); -+ } -+ } -+ qh->qh_state = QH_STATE_LINKED; -+ qh->xacterrs = 0; -+ -+ /* update per-qh bandwidth for usbfs */ -+ fotg210_to_hcd(fotg210)->self.bandwidth_allocated += qh->period -+ ? ((qh->usecs + qh->c_usecs) / qh->period) -+ : (qh->usecs * 8); -+ -+ list_add(&qh->intr_node, &fotg210->intr_qh_list); -+ -+ /* maybe enable periodic schedule processing */ -+ ++fotg210->intr_count; -+ enable_periodic(fotg210); -+} -+ -+static void qh_unlink_periodic(struct fotg210_hcd *fotg210, -+ struct fotg210_qh *qh) -+{ -+ unsigned i; -+ unsigned period; -+ -+ /* -+ * If qh is for a low/full-speed device, simply unlinking it -+ * could interfere with an ongoing split transaction. To unlink -+ * it safely would require setting the QH_INACTIVATE bit and -+ * waiting at least one frame, as described in EHCI 4.12.2.5. -+ * -+ * We won't bother with any of this. Instead, we assume that the -+ * only reason for unlinking an interrupt QH while the current URB -+ * is still active is to dequeue all the URBs (flush the whole -+ * endpoint queue). -+ * -+ * If rebalancing the periodic schedule is ever implemented, this -+ * approach will no longer be valid. -+ */ -+ -+ /* high bandwidth, or otherwise part of every microframe */ -+ period = qh->period; -+ if (!period) -+ period = 1; -+ -+ for (i = qh->start; i < fotg210->periodic_size; i += period) -+ periodic_unlink(fotg210, i, qh); -+ -+ /* update per-qh bandwidth for usbfs */ -+ fotg210_to_hcd(fotg210)->self.bandwidth_allocated -= qh->period -+ ? ((qh->usecs + qh->c_usecs) / qh->period) -+ : (qh->usecs * 8); -+ -+ dev_dbg(&qh->dev->dev, -+ "unlink qh%d-%04x/%p start %d [%d/%d us]\n", -+ qh->period, hc32_to_cpup(fotg210, &qh->hw->hw_info2) & -+ (QH_CMASK | QH_SMASK), qh, qh->start, qh->usecs, -+ qh->c_usecs); -+ -+ /* qh->qh_next still "live" to HC */ -+ qh->qh_state = QH_STATE_UNLINK; -+ qh->qh_next.ptr = NULL; -+ -+ if (fotg210->qh_scan_next == qh) -+ fotg210->qh_scan_next = list_entry(qh->intr_node.next, -+ struct fotg210_qh, intr_node); -+ list_del(&qh->intr_node); -+} -+ -+static void start_unlink_intr(struct fotg210_hcd *fotg210, -+ struct fotg210_qh *qh) -+{ -+ /* If the QH isn't linked then there's nothing we can do -+ * unless we were called during a giveback, in which case -+ * qh_completions() has to deal with it. -+ */ -+ if (qh->qh_state != QH_STATE_LINKED) { -+ if (qh->qh_state == QH_STATE_COMPLETING) -+ qh->needs_rescan = 1; -+ return; -+ } -+ -+ qh_unlink_periodic(fotg210, qh); -+ -+ /* Make sure the unlinks are visible before starting the timer */ -+ wmb(); -+ -+ /* -+ * The EHCI spec doesn't say how long it takes the controller to -+ * stop accessing an unlinked interrupt QH. The timer delay is -+ * 9 uframes; presumably that will be long enough. -+ */ -+ qh->unlink_cycle = fotg210->intr_unlink_cycle; -+ -+ /* New entries go at the end of the intr_unlink list */ -+ if (fotg210->intr_unlink) -+ fotg210->intr_unlink_last->unlink_next = qh; -+ else -+ fotg210->intr_unlink = qh; -+ fotg210->intr_unlink_last = qh; -+ -+ if (fotg210->intr_unlinking) -+ ; /* Avoid recursive calls */ -+ else if (fotg210->rh_state < FOTG210_RH_RUNNING) -+ fotg210_handle_intr_unlinks(fotg210); -+ else if (fotg210->intr_unlink == qh) { -+ fotg210_enable_event(fotg210, FOTG210_HRTIMER_UNLINK_INTR, -+ true); -+ ++fotg210->intr_unlink_cycle; -+ } -+} -+ -+static void end_unlink_intr(struct fotg210_hcd *fotg210, struct fotg210_qh *qh) -+{ -+ struct fotg210_qh_hw *hw = qh->hw; -+ int rc; -+ -+ qh->qh_state = QH_STATE_IDLE; -+ hw->hw_next = FOTG210_LIST_END(fotg210); -+ -+ qh_completions(fotg210, qh); -+ -+ /* reschedule QH iff another request is queued */ -+ if (!list_empty(&qh->qtd_list) && -+ fotg210->rh_state == FOTG210_RH_RUNNING) { -+ rc = qh_schedule(fotg210, qh); -+ -+ /* An error here likely indicates handshake failure -+ * or no space left in the schedule. Neither fault -+ * should happen often ... -+ * -+ * FIXME kill the now-dysfunctional queued urbs -+ */ -+ if (rc != 0) -+ fotg210_err(fotg210, "can't reschedule qh %p, err %d\n", -+ qh, rc); -+ } -+ -+ /* maybe turn off periodic schedule */ -+ --fotg210->intr_count; -+ disable_periodic(fotg210); -+} -+ -+static int check_period(struct fotg210_hcd *fotg210, unsigned frame, -+ unsigned uframe, unsigned period, unsigned usecs) -+{ -+ int claimed; -+ -+ /* complete split running into next frame? -+ * given FSTN support, we could sometimes check... -+ */ -+ if (uframe >= 8) -+ return 0; -+ -+ /* convert "usecs we need" to "max already claimed" */ -+ usecs = fotg210->uframe_periodic_max - usecs; -+ -+ /* we "know" 2 and 4 uframe intervals were rejected; so -+ * for period 0, check _every_ microframe in the schedule. -+ */ -+ if (unlikely(period == 0)) { -+ do { -+ for (uframe = 0; uframe < 7; uframe++) { -+ claimed = periodic_usecs(fotg210, frame, -+ uframe); -+ if (claimed > usecs) -+ return 0; -+ } -+ } while ((frame += 1) < fotg210->periodic_size); -+ -+ /* just check the specified uframe, at that period */ -+ } else { -+ do { -+ claimed = periodic_usecs(fotg210, frame, uframe); -+ if (claimed > usecs) -+ return 0; -+ } while ((frame += period) < fotg210->periodic_size); -+ } -+ -+ /* success! */ -+ return 1; -+} -+ -+static int check_intr_schedule(struct fotg210_hcd *fotg210, unsigned frame, -+ unsigned uframe, const struct fotg210_qh *qh, __hc32 *c_maskp) -+{ -+ int retval = -ENOSPC; -+ u8 mask = 0; -+ -+ if (qh->c_usecs && uframe >= 6) /* FSTN territory? */ -+ goto done; -+ -+ if (!check_period(fotg210, frame, uframe, qh->period, qh->usecs)) -+ goto done; -+ if (!qh->c_usecs) { -+ retval = 0; -+ *c_maskp = 0; -+ goto done; -+ } -+ -+ /* Make sure this tt's buffer is also available for CSPLITs. -+ * We pessimize a bit; probably the typical full speed case -+ * doesn't need the second CSPLIT. -+ * -+ * NOTE: both SPLIT and CSPLIT could be checked in just -+ * one smart pass... -+ */ -+ mask = 0x03 << (uframe + qh->gap_uf); -+ *c_maskp = cpu_to_hc32(fotg210, mask << 8); -+ -+ mask |= 1 << uframe; -+ if (tt_no_collision(fotg210, qh->period, qh->dev, frame, mask)) { -+ if (!check_period(fotg210, frame, uframe + qh->gap_uf + 1, -+ qh->period, qh->c_usecs)) -+ goto done; -+ if (!check_period(fotg210, frame, uframe + qh->gap_uf, -+ qh->period, qh->c_usecs)) -+ goto done; -+ retval = 0; -+ } -+done: -+ return retval; -+} -+ -+/* "first fit" scheduling policy used the first time through, -+ * or when the previous schedule slot can't be re-used. -+ */ -+static int qh_schedule(struct fotg210_hcd *fotg210, struct fotg210_qh *qh) -+{ -+ int status; -+ unsigned uframe; -+ __hc32 c_mask; -+ unsigned frame; /* 0..(qh->period - 1), or NO_FRAME */ -+ struct fotg210_qh_hw *hw = qh->hw; -+ -+ qh_refresh(fotg210, qh); -+ hw->hw_next = FOTG210_LIST_END(fotg210); -+ frame = qh->start; -+ -+ /* reuse the previous schedule slots, if we can */ -+ if (frame < qh->period) { -+ uframe = ffs(hc32_to_cpup(fotg210, &hw->hw_info2) & QH_SMASK); -+ status = check_intr_schedule(fotg210, frame, --uframe, -+ qh, &c_mask); -+ } else { -+ uframe = 0; -+ c_mask = 0; -+ status = -ENOSPC; -+ } -+ -+ /* else scan the schedule to find a group of slots such that all -+ * uframes have enough periodic bandwidth available. -+ */ -+ if (status) { -+ /* "normal" case, uframing flexible except with splits */ -+ if (qh->period) { -+ int i; -+ -+ for (i = qh->period; status && i > 0; --i) { -+ frame = ++fotg210->random_frame % qh->period; -+ for (uframe = 0; uframe < 8; uframe++) { -+ status = check_intr_schedule(fotg210, -+ frame, uframe, qh, -+ &c_mask); -+ if (status == 0) -+ break; -+ } -+ } -+ -+ /* qh->period == 0 means every uframe */ -+ } else { -+ frame = 0; -+ status = check_intr_schedule(fotg210, 0, 0, qh, -+ &c_mask); -+ } -+ if (status) -+ goto done; -+ qh->start = frame; -+ -+ /* reset S-frame and (maybe) C-frame masks */ -+ hw->hw_info2 &= cpu_to_hc32(fotg210, ~(QH_CMASK | QH_SMASK)); -+ hw->hw_info2 |= qh->period -+ ? cpu_to_hc32(fotg210, 1 << uframe) -+ : cpu_to_hc32(fotg210, QH_SMASK); -+ hw->hw_info2 |= c_mask; -+ } else -+ fotg210_dbg(fotg210, "reused qh %p schedule\n", qh); -+ -+ /* stuff into the periodic schedule */ -+ qh_link_periodic(fotg210, qh); -+done: -+ return status; -+} -+ -+static int intr_submit(struct fotg210_hcd *fotg210, struct urb *urb, -+ struct list_head *qtd_list, gfp_t mem_flags) -+{ -+ unsigned epnum; -+ unsigned long flags; -+ struct fotg210_qh *qh; -+ int status; -+ struct list_head empty; -+ -+ /* get endpoint and transfer/schedule data */ -+ epnum = urb->ep->desc.bEndpointAddress; -+ -+ spin_lock_irqsave(&fotg210->lock, flags); -+ -+ if (unlikely(!HCD_HW_ACCESSIBLE(fotg210_to_hcd(fotg210)))) { -+ status = -ESHUTDOWN; -+ goto done_not_linked; -+ } -+ status = usb_hcd_link_urb_to_ep(fotg210_to_hcd(fotg210), urb); -+ if (unlikely(status)) -+ goto done_not_linked; -+ -+ /* get qh and force any scheduling errors */ -+ INIT_LIST_HEAD(&empty); -+ qh = qh_append_tds(fotg210, urb, &empty, epnum, &urb->ep->hcpriv); -+ if (qh == NULL) { -+ status = -ENOMEM; -+ goto done; -+ } -+ if (qh->qh_state == QH_STATE_IDLE) { -+ status = qh_schedule(fotg210, qh); -+ if (status) -+ goto done; -+ } -+ -+ /* then queue the urb's tds to the qh */ -+ qh = qh_append_tds(fotg210, urb, qtd_list, epnum, &urb->ep->hcpriv); -+ BUG_ON(qh == NULL); -+ -+ /* ... update usbfs periodic stats */ -+ fotg210_to_hcd(fotg210)->self.bandwidth_int_reqs++; -+ -+done: -+ if (unlikely(status)) -+ usb_hcd_unlink_urb_from_ep(fotg210_to_hcd(fotg210), urb); -+done_not_linked: -+ spin_unlock_irqrestore(&fotg210->lock, flags); -+ if (status) -+ qtd_list_free(fotg210, urb, qtd_list); -+ -+ return status; -+} -+ -+static void scan_intr(struct fotg210_hcd *fotg210) -+{ -+ struct fotg210_qh *qh; -+ -+ list_for_each_entry_safe(qh, fotg210->qh_scan_next, -+ &fotg210->intr_qh_list, intr_node) { -+rescan: -+ /* clean any finished work for this qh */ -+ if (!list_empty(&qh->qtd_list)) { -+ int temp; -+ -+ /* -+ * Unlinks could happen here; completion reporting -+ * drops the lock. That's why fotg210->qh_scan_next -+ * always holds the next qh to scan; if the next qh -+ * gets unlinked then fotg210->qh_scan_next is adjusted -+ * in qh_unlink_periodic(). -+ */ -+ temp = qh_completions(fotg210, qh); -+ if (unlikely(qh->needs_rescan || -+ (list_empty(&qh->qtd_list) && -+ qh->qh_state == QH_STATE_LINKED))) -+ start_unlink_intr(fotg210, qh); -+ else if (temp != 0) -+ goto rescan; -+ } -+ } -+} -+ -+/* fotg210_iso_stream ops work with both ITD and SITD */ -+ -+static struct fotg210_iso_stream *iso_stream_alloc(gfp_t mem_flags) -+{ -+ struct fotg210_iso_stream *stream; -+ -+ stream = kzalloc(sizeof(*stream), mem_flags); -+ if (likely(stream != NULL)) { -+ INIT_LIST_HEAD(&stream->td_list); -+ INIT_LIST_HEAD(&stream->free_list); -+ stream->next_uframe = -1; -+ } -+ return stream; -+} -+ -+static void iso_stream_init(struct fotg210_hcd *fotg210, -+ struct fotg210_iso_stream *stream, struct usb_device *dev, -+ int pipe, unsigned interval) -+{ -+ u32 buf1; -+ unsigned epnum, maxp; -+ int is_input; -+ long bandwidth; -+ unsigned multi; -+ struct usb_host_endpoint *ep; -+ -+ /* -+ * this might be a "high bandwidth" highspeed endpoint, -+ * as encoded in the ep descriptor's wMaxPacket field -+ */ -+ epnum = usb_pipeendpoint(pipe); -+ is_input = usb_pipein(pipe) ? USB_DIR_IN : 0; -+ ep = usb_pipe_endpoint(dev, pipe); -+ maxp = usb_endpoint_maxp(&ep->desc); -+ if (is_input) -+ buf1 = (1 << 11); -+ else -+ buf1 = 0; -+ -+ multi = usb_endpoint_maxp_mult(&ep->desc); -+ buf1 |= maxp; -+ maxp *= multi; -+ -+ stream->buf0 = cpu_to_hc32(fotg210, (epnum << 8) | dev->devnum); -+ stream->buf1 = cpu_to_hc32(fotg210, buf1); -+ stream->buf2 = cpu_to_hc32(fotg210, multi); -+ -+ /* usbfs wants to report the average usecs per frame tied up -+ * when transfers on this endpoint are scheduled ... -+ */ -+ if (dev->speed == USB_SPEED_FULL) { -+ interval <<= 3; -+ stream->usecs = NS_TO_US(usb_calc_bus_time(dev->speed, -+ is_input, 1, maxp)); -+ stream->usecs /= 8; -+ } else { -+ stream->highspeed = 1; -+ stream->usecs = HS_USECS_ISO(maxp); -+ } -+ bandwidth = stream->usecs * 8; -+ bandwidth /= interval; -+ -+ stream->bandwidth = bandwidth; -+ stream->udev = dev; -+ stream->bEndpointAddress = is_input | epnum; -+ stream->interval = interval; -+ stream->maxp = maxp; -+} -+ -+static struct fotg210_iso_stream *iso_stream_find(struct fotg210_hcd *fotg210, -+ struct urb *urb) -+{ -+ unsigned epnum; -+ struct fotg210_iso_stream *stream; -+ struct usb_host_endpoint *ep; -+ unsigned long flags; -+ -+ epnum = usb_pipeendpoint(urb->pipe); -+ if (usb_pipein(urb->pipe)) -+ ep = urb->dev->ep_in[epnum]; -+ else -+ ep = urb->dev->ep_out[epnum]; -+ -+ spin_lock_irqsave(&fotg210->lock, flags); -+ stream = ep->hcpriv; -+ -+ if (unlikely(stream == NULL)) { -+ stream = iso_stream_alloc(GFP_ATOMIC); -+ if (likely(stream != NULL)) { -+ ep->hcpriv = stream; -+ stream->ep = ep; -+ iso_stream_init(fotg210, stream, urb->dev, urb->pipe, -+ urb->interval); -+ } -+ -+ /* if dev->ep[epnum] is a QH, hw is set */ -+ } else if (unlikely(stream->hw != NULL)) { -+ fotg210_dbg(fotg210, "dev %s ep%d%s, not iso??\n", -+ urb->dev->devpath, epnum, -+ usb_pipein(urb->pipe) ? "in" : "out"); -+ stream = NULL; -+ } -+ -+ spin_unlock_irqrestore(&fotg210->lock, flags); -+ return stream; -+} -+ -+/* fotg210_iso_sched ops can be ITD-only or SITD-only */ -+ -+static struct fotg210_iso_sched *iso_sched_alloc(unsigned packets, -+ gfp_t mem_flags) -+{ -+ struct fotg210_iso_sched *iso_sched; -+ -+ iso_sched = kzalloc(struct_size(iso_sched, packet, packets), mem_flags); -+ if (likely(iso_sched != NULL)) -+ INIT_LIST_HEAD(&iso_sched->td_list); -+ -+ return iso_sched; -+} -+ -+static inline void itd_sched_init(struct fotg210_hcd *fotg210, -+ struct fotg210_iso_sched *iso_sched, -+ struct fotg210_iso_stream *stream, struct urb *urb) -+{ -+ unsigned i; -+ dma_addr_t dma = urb->transfer_dma; -+ -+ /* how many uframes are needed for these transfers */ -+ iso_sched->span = urb->number_of_packets * stream->interval; -+ -+ /* figure out per-uframe itd fields that we'll need later -+ * when we fit new itds into the schedule. -+ */ -+ for (i = 0; i < urb->number_of_packets; i++) { -+ struct fotg210_iso_packet *uframe = &iso_sched->packet[i]; -+ unsigned length; -+ dma_addr_t buf; -+ u32 trans; -+ -+ length = urb->iso_frame_desc[i].length; -+ buf = dma + urb->iso_frame_desc[i].offset; -+ -+ trans = FOTG210_ISOC_ACTIVE; -+ trans |= buf & 0x0fff; -+ if (unlikely(((i + 1) == urb->number_of_packets)) -+ && !(urb->transfer_flags & URB_NO_INTERRUPT)) -+ trans |= FOTG210_ITD_IOC; -+ trans |= length << 16; -+ uframe->transaction = cpu_to_hc32(fotg210, trans); -+ -+ /* might need to cross a buffer page within a uframe */ -+ uframe->bufp = (buf & ~(u64)0x0fff); -+ buf += length; -+ if (unlikely((uframe->bufp != (buf & ~(u64)0x0fff)))) -+ uframe->cross = 1; -+ } -+} -+ -+static void iso_sched_free(struct fotg210_iso_stream *stream, -+ struct fotg210_iso_sched *iso_sched) -+{ -+ if (!iso_sched) -+ return; -+ /* caller must hold fotg210->lock!*/ -+ list_splice(&iso_sched->td_list, &stream->free_list); -+ kfree(iso_sched); -+} -+ -+static int itd_urb_transaction(struct fotg210_iso_stream *stream, -+ struct fotg210_hcd *fotg210, struct urb *urb, gfp_t mem_flags) -+{ -+ struct fotg210_itd *itd; -+ dma_addr_t itd_dma; -+ int i; -+ unsigned num_itds; -+ struct fotg210_iso_sched *sched; -+ unsigned long flags; -+ -+ sched = iso_sched_alloc(urb->number_of_packets, mem_flags); -+ if (unlikely(sched == NULL)) -+ return -ENOMEM; -+ -+ itd_sched_init(fotg210, sched, stream, urb); -+ -+ if (urb->interval < 8) -+ num_itds = 1 + (sched->span + 7) / 8; -+ else -+ num_itds = urb->number_of_packets; -+ -+ /* allocate/init ITDs */ -+ spin_lock_irqsave(&fotg210->lock, flags); -+ for (i = 0; i < num_itds; i++) { -+ -+ /* -+ * Use iTDs from the free list, but not iTDs that may -+ * still be in use by the hardware. -+ */ -+ if (likely(!list_empty(&stream->free_list))) { -+ itd = list_first_entry(&stream->free_list, -+ struct fotg210_itd, itd_list); -+ if (itd->frame == fotg210->now_frame) -+ goto alloc_itd; -+ list_del(&itd->itd_list); -+ itd_dma = itd->itd_dma; -+ } else { -+alloc_itd: -+ spin_unlock_irqrestore(&fotg210->lock, flags); -+ itd = dma_pool_alloc(fotg210->itd_pool, mem_flags, -+ &itd_dma); -+ spin_lock_irqsave(&fotg210->lock, flags); -+ if (!itd) { -+ iso_sched_free(stream, sched); -+ spin_unlock_irqrestore(&fotg210->lock, flags); -+ return -ENOMEM; -+ } -+ } -+ -+ memset(itd, 0, sizeof(*itd)); -+ itd->itd_dma = itd_dma; -+ list_add(&itd->itd_list, &sched->td_list); -+ } -+ spin_unlock_irqrestore(&fotg210->lock, flags); -+ -+ /* temporarily store schedule info in hcpriv */ -+ urb->hcpriv = sched; -+ urb->error_count = 0; -+ return 0; -+} -+ -+static inline int itd_slot_ok(struct fotg210_hcd *fotg210, u32 mod, u32 uframe, -+ u8 usecs, u32 period) -+{ -+ uframe %= period; -+ do { -+ /* can't commit more than uframe_periodic_max usec */ -+ if (periodic_usecs(fotg210, uframe >> 3, uframe & 0x7) -+ > (fotg210->uframe_periodic_max - usecs)) -+ return 0; -+ -+ /* we know urb->interval is 2^N uframes */ -+ uframe += period; -+ } while (uframe < mod); -+ return 1; -+} -+ -+/* This scheduler plans almost as far into the future as it has actual -+ * periodic schedule slots. (Affected by TUNE_FLS, which defaults to -+ * "as small as possible" to be cache-friendlier.) That limits the size -+ * transfers you can stream reliably; avoid more than 64 msec per urb. -+ * Also avoid queue depths of less than fotg210's worst irq latency (affected -+ * by the per-urb URB_NO_INTERRUPT hint, the log2_irq_thresh module parameter, -+ * and other factors); or more than about 230 msec total (for portability, -+ * given FOTG210_TUNE_FLS and the slop). Or, write a smarter scheduler! -+ */ -+ -+#define SCHEDULE_SLOP 80 /* microframes */ -+ -+static int iso_stream_schedule(struct fotg210_hcd *fotg210, struct urb *urb, -+ struct fotg210_iso_stream *stream) -+{ -+ u32 now, next, start, period, span; -+ int status; -+ unsigned mod = fotg210->periodic_size << 3; -+ struct fotg210_iso_sched *sched = urb->hcpriv; -+ -+ period = urb->interval; -+ span = sched->span; -+ -+ if (span > mod - SCHEDULE_SLOP) { -+ fotg210_dbg(fotg210, "iso request %p too long\n", urb); -+ status = -EFBIG; -+ goto fail; -+ } -+ -+ now = fotg210_read_frame_index(fotg210) & (mod - 1); -+ -+ /* Typical case: reuse current schedule, stream is still active. -+ * Hopefully there are no gaps from the host falling behind -+ * (irq delays etc), but if there are we'll take the next -+ * slot in the schedule, implicitly assuming URB_ISO_ASAP. -+ */ -+ if (likely(!list_empty(&stream->td_list))) { -+ u32 excess; -+ -+ /* For high speed devices, allow scheduling within the -+ * isochronous scheduling threshold. For full speed devices -+ * and Intel PCI-based controllers, don't (work around for -+ * Intel ICH9 bug). -+ */ -+ if (!stream->highspeed && fotg210->fs_i_thresh) -+ next = now + fotg210->i_thresh; -+ else -+ next = now; -+ -+ /* Fell behind (by up to twice the slop amount)? -+ * We decide based on the time of the last currently-scheduled -+ * slot, not the time of the next available slot. -+ */ -+ excess = (stream->next_uframe - period - next) & (mod - 1); -+ if (excess >= mod - 2 * SCHEDULE_SLOP) -+ start = next + excess - mod + period * -+ DIV_ROUND_UP(mod - excess, period); -+ else -+ start = next + excess + period; -+ if (start - now >= mod) { -+ fotg210_dbg(fotg210, "request %p would overflow (%d+%d >= %d)\n", -+ urb, start - now - period, period, -+ mod); -+ status = -EFBIG; -+ goto fail; -+ } -+ } -+ -+ /* need to schedule; when's the next (u)frame we could start? -+ * this is bigger than fotg210->i_thresh allows; scheduling itself -+ * isn't free, the slop should handle reasonably slow cpus. it -+ * can also help high bandwidth if the dma and irq loads don't -+ * jump until after the queue is primed. -+ */ -+ else { -+ int done = 0; -+ -+ start = SCHEDULE_SLOP + (now & ~0x07); -+ -+ /* NOTE: assumes URB_ISO_ASAP, to limit complexity/bugs */ -+ -+ /* find a uframe slot with enough bandwidth. -+ * Early uframes are more precious because full-speed -+ * iso IN transfers can't use late uframes, -+ * and therefore they should be allocated last. -+ */ -+ next = start; -+ start += period; -+ do { -+ start--; -+ /* check schedule: enough space? */ -+ if (itd_slot_ok(fotg210, mod, start, -+ stream->usecs, period)) -+ done = 1; -+ } while (start > next && !done); -+ -+ /* no room in the schedule */ -+ if (!done) { -+ fotg210_dbg(fotg210, "iso resched full %p (now %d max %d)\n", -+ urb, now, now + mod); -+ status = -ENOSPC; -+ goto fail; -+ } -+ } -+ -+ /* Tried to schedule too far into the future? */ -+ if (unlikely(start - now + span - period >= -+ mod - 2 * SCHEDULE_SLOP)) { -+ fotg210_dbg(fotg210, "request %p would overflow (%d+%d >= %d)\n", -+ urb, start - now, span - period, -+ mod - 2 * SCHEDULE_SLOP); -+ status = -EFBIG; -+ goto fail; -+ } -+ -+ stream->next_uframe = start & (mod - 1); -+ -+ /* report high speed start in uframes; full speed, in frames */ -+ urb->start_frame = stream->next_uframe; -+ if (!stream->highspeed) -+ urb->start_frame >>= 3; -+ -+ /* Make sure scan_isoc() sees these */ -+ if (fotg210->isoc_count == 0) -+ fotg210->next_frame = now >> 3; -+ return 0; -+ -+fail: -+ iso_sched_free(stream, sched); -+ urb->hcpriv = NULL; -+ return status; -+} -+ -+static inline void itd_init(struct fotg210_hcd *fotg210, -+ struct fotg210_iso_stream *stream, struct fotg210_itd *itd) -+{ -+ int i; -+ -+ /* it's been recently zeroed */ -+ itd->hw_next = FOTG210_LIST_END(fotg210); -+ itd->hw_bufp[0] = stream->buf0; -+ itd->hw_bufp[1] = stream->buf1; -+ itd->hw_bufp[2] = stream->buf2; -+ -+ for (i = 0; i < 8; i++) -+ itd->index[i] = -1; -+ -+ /* All other fields are filled when scheduling */ -+} -+ -+static inline void itd_patch(struct fotg210_hcd *fotg210, -+ struct fotg210_itd *itd, struct fotg210_iso_sched *iso_sched, -+ unsigned index, u16 uframe) -+{ -+ struct fotg210_iso_packet *uf = &iso_sched->packet[index]; -+ unsigned pg = itd->pg; -+ -+ uframe &= 0x07; -+ itd->index[uframe] = index; -+ -+ itd->hw_transaction[uframe] = uf->transaction; -+ itd->hw_transaction[uframe] |= cpu_to_hc32(fotg210, pg << 12); -+ itd->hw_bufp[pg] |= cpu_to_hc32(fotg210, uf->bufp & ~(u32)0); -+ itd->hw_bufp_hi[pg] |= cpu_to_hc32(fotg210, (u32)(uf->bufp >> 32)); -+ -+ /* iso_frame_desc[].offset must be strictly increasing */ -+ if (unlikely(uf->cross)) { -+ u64 bufp = uf->bufp + 4096; -+ -+ itd->pg = ++pg; -+ itd->hw_bufp[pg] |= cpu_to_hc32(fotg210, bufp & ~(u32)0); -+ itd->hw_bufp_hi[pg] |= cpu_to_hc32(fotg210, (u32)(bufp >> 32)); -+ } -+} -+ -+static inline void itd_link(struct fotg210_hcd *fotg210, unsigned frame, -+ struct fotg210_itd *itd) -+{ -+ union fotg210_shadow *prev = &fotg210->pshadow[frame]; -+ __hc32 *hw_p = &fotg210->periodic[frame]; -+ union fotg210_shadow here = *prev; -+ __hc32 type = 0; -+ -+ /* skip any iso nodes which might belong to previous microframes */ -+ while (here.ptr) { -+ type = Q_NEXT_TYPE(fotg210, *hw_p); -+ if (type == cpu_to_hc32(fotg210, Q_TYPE_QH)) -+ break; -+ prev = periodic_next_shadow(fotg210, prev, type); -+ hw_p = shadow_next_periodic(fotg210, &here, type); -+ here = *prev; -+ } -+ -+ itd->itd_next = here; -+ itd->hw_next = *hw_p; -+ prev->itd = itd; -+ itd->frame = frame; -+ wmb(); -+ *hw_p = cpu_to_hc32(fotg210, itd->itd_dma | Q_TYPE_ITD); -+} -+ -+/* fit urb's itds into the selected schedule slot; activate as needed */ -+static void itd_link_urb(struct fotg210_hcd *fotg210, struct urb *urb, -+ unsigned mod, struct fotg210_iso_stream *stream) -+{ -+ int packet; -+ unsigned next_uframe, uframe, frame; -+ struct fotg210_iso_sched *iso_sched = urb->hcpriv; -+ struct fotg210_itd *itd; -+ -+ next_uframe = stream->next_uframe & (mod - 1); -+ -+ if (unlikely(list_empty(&stream->td_list))) { -+ fotg210_to_hcd(fotg210)->self.bandwidth_allocated -+ += stream->bandwidth; -+ fotg210_dbg(fotg210, -+ "schedule devp %s ep%d%s-iso period %d start %d.%d\n", -+ urb->dev->devpath, stream->bEndpointAddress & 0x0f, -+ (stream->bEndpointAddress & USB_DIR_IN) ? "in" : "out", -+ urb->interval, -+ next_uframe >> 3, next_uframe & 0x7); -+ } -+ -+ /* fill iTDs uframe by uframe */ -+ for (packet = 0, itd = NULL; packet < urb->number_of_packets;) { -+ if (itd == NULL) { -+ /* ASSERT: we have all necessary itds */ -+ -+ /* ASSERT: no itds for this endpoint in this uframe */ -+ -+ itd = list_entry(iso_sched->td_list.next, -+ struct fotg210_itd, itd_list); -+ list_move_tail(&itd->itd_list, &stream->td_list); -+ itd->stream = stream; -+ itd->urb = urb; -+ itd_init(fotg210, stream, itd); -+ } -+ -+ uframe = next_uframe & 0x07; -+ frame = next_uframe >> 3; -+ -+ itd_patch(fotg210, itd, iso_sched, packet, uframe); -+ -+ next_uframe += stream->interval; -+ next_uframe &= mod - 1; -+ packet++; -+ -+ /* link completed itds into the schedule */ -+ if (((next_uframe >> 3) != frame) -+ || packet == urb->number_of_packets) { -+ itd_link(fotg210, frame & (fotg210->periodic_size - 1), -+ itd); -+ itd = NULL; -+ } -+ } -+ stream->next_uframe = next_uframe; -+ -+ /* don't need that schedule data any more */ -+ iso_sched_free(stream, iso_sched); -+ urb->hcpriv = NULL; -+ -+ ++fotg210->isoc_count; -+ enable_periodic(fotg210); -+} -+ -+#define ISO_ERRS (FOTG210_ISOC_BUF_ERR | FOTG210_ISOC_BABBLE |\ -+ FOTG210_ISOC_XACTERR) -+ -+/* Process and recycle a completed ITD. Return true iff its urb completed, -+ * and hence its completion callback probably added things to the hardware -+ * schedule. -+ * -+ * Note that we carefully avoid recycling this descriptor until after any -+ * completion callback runs, so that it won't be reused quickly. That is, -+ * assuming (a) no more than two urbs per frame on this endpoint, and also -+ * (b) only this endpoint's completions submit URBs. It seems some silicon -+ * corrupts things if you reuse completed descriptors very quickly... -+ */ -+static bool itd_complete(struct fotg210_hcd *fotg210, struct fotg210_itd *itd) -+{ -+ struct urb *urb = itd->urb; -+ struct usb_iso_packet_descriptor *desc; -+ u32 t; -+ unsigned uframe; -+ int urb_index = -1; -+ struct fotg210_iso_stream *stream = itd->stream; -+ struct usb_device *dev; -+ bool retval = false; -+ -+ /* for each uframe with a packet */ -+ for (uframe = 0; uframe < 8; uframe++) { -+ if (likely(itd->index[uframe] == -1)) -+ continue; -+ urb_index = itd->index[uframe]; -+ desc = &urb->iso_frame_desc[urb_index]; -+ -+ t = hc32_to_cpup(fotg210, &itd->hw_transaction[uframe]); -+ itd->hw_transaction[uframe] = 0; -+ -+ /* report transfer status */ -+ if (unlikely(t & ISO_ERRS)) { -+ urb->error_count++; -+ if (t & FOTG210_ISOC_BUF_ERR) -+ desc->status = usb_pipein(urb->pipe) -+ ? -ENOSR /* hc couldn't read */ -+ : -ECOMM; /* hc couldn't write */ -+ else if (t & FOTG210_ISOC_BABBLE) -+ desc->status = -EOVERFLOW; -+ else /* (t & FOTG210_ISOC_XACTERR) */ -+ desc->status = -EPROTO; -+ -+ /* HC need not update length with this error */ -+ if (!(t & FOTG210_ISOC_BABBLE)) { -+ desc->actual_length = FOTG210_ITD_LENGTH(t); -+ urb->actual_length += desc->actual_length; -+ } -+ } else if (likely((t & FOTG210_ISOC_ACTIVE) == 0)) { -+ desc->status = 0; -+ desc->actual_length = FOTG210_ITD_LENGTH(t); -+ urb->actual_length += desc->actual_length; -+ } else { -+ /* URB was too late */ -+ desc->status = -EXDEV; -+ } -+ } -+ -+ /* handle completion now? */ -+ if (likely((urb_index + 1) != urb->number_of_packets)) -+ goto done; -+ -+ /* ASSERT: it's really the last itd for this urb -+ * list_for_each_entry (itd, &stream->td_list, itd_list) -+ * BUG_ON (itd->urb == urb); -+ */ -+ -+ /* give urb back to the driver; completion often (re)submits */ -+ dev = urb->dev; -+ fotg210_urb_done(fotg210, urb, 0); -+ retval = true; -+ urb = NULL; -+ -+ --fotg210->isoc_count; -+ disable_periodic(fotg210); -+ -+ if (unlikely(list_is_singular(&stream->td_list))) { -+ fotg210_to_hcd(fotg210)->self.bandwidth_allocated -+ -= stream->bandwidth; -+ fotg210_dbg(fotg210, -+ "deschedule devp %s ep%d%s-iso\n", -+ dev->devpath, stream->bEndpointAddress & 0x0f, -+ (stream->bEndpointAddress & USB_DIR_IN) ? "in" : "out"); -+ } -+ -+done: -+ itd->urb = NULL; -+ -+ /* Add to the end of the free list for later reuse */ -+ list_move_tail(&itd->itd_list, &stream->free_list); -+ -+ /* Recycle the iTDs when the pipeline is empty (ep no longer in use) */ -+ if (list_empty(&stream->td_list)) { -+ list_splice_tail_init(&stream->free_list, -+ &fotg210->cached_itd_list); -+ start_free_itds(fotg210); -+ } -+ -+ return retval; -+} -+ -+static int itd_submit(struct fotg210_hcd *fotg210, struct urb *urb, -+ gfp_t mem_flags) -+{ -+ int status = -EINVAL; -+ unsigned long flags; -+ struct fotg210_iso_stream *stream; -+ -+ /* Get iso_stream head */ -+ stream = iso_stream_find(fotg210, urb); -+ if (unlikely(stream == NULL)) { -+ fotg210_dbg(fotg210, "can't get iso stream\n"); -+ return -ENOMEM; -+ } -+ if (unlikely(urb->interval != stream->interval && -+ fotg210_port_speed(fotg210, 0) == -+ USB_PORT_STAT_HIGH_SPEED)) { -+ fotg210_dbg(fotg210, "can't change iso interval %d --> %d\n", -+ stream->interval, urb->interval); -+ goto done; -+ } -+ -+#ifdef FOTG210_URB_TRACE -+ fotg210_dbg(fotg210, -+ "%s %s urb %p ep%d%s len %d, %d pkts %d uframes[%p]\n", -+ __func__, urb->dev->devpath, urb, -+ usb_pipeendpoint(urb->pipe), -+ usb_pipein(urb->pipe) ? "in" : "out", -+ urb->transfer_buffer_length, -+ urb->number_of_packets, urb->interval, -+ stream); -+#endif -+ -+ /* allocate ITDs w/o locking anything */ -+ status = itd_urb_transaction(stream, fotg210, urb, mem_flags); -+ if (unlikely(status < 0)) { -+ fotg210_dbg(fotg210, "can't init itds\n"); -+ goto done; -+ } -+ -+ /* schedule ... need to lock */ -+ spin_lock_irqsave(&fotg210->lock, flags); -+ if (unlikely(!HCD_HW_ACCESSIBLE(fotg210_to_hcd(fotg210)))) { -+ status = -ESHUTDOWN; -+ goto done_not_linked; -+ } -+ status = usb_hcd_link_urb_to_ep(fotg210_to_hcd(fotg210), urb); -+ if (unlikely(status)) -+ goto done_not_linked; -+ status = iso_stream_schedule(fotg210, urb, stream); -+ if (likely(status == 0)) -+ itd_link_urb(fotg210, urb, fotg210->periodic_size << 3, stream); -+ else -+ usb_hcd_unlink_urb_from_ep(fotg210_to_hcd(fotg210), urb); -+done_not_linked: -+ spin_unlock_irqrestore(&fotg210->lock, flags); -+done: -+ return status; -+} -+ -+static inline int scan_frame_queue(struct fotg210_hcd *fotg210, unsigned frame, -+ unsigned now_frame, bool live) -+{ -+ unsigned uf; -+ bool modified; -+ union fotg210_shadow q, *q_p; -+ __hc32 type, *hw_p; -+ -+ /* scan each element in frame's queue for completions */ -+ q_p = &fotg210->pshadow[frame]; -+ hw_p = &fotg210->periodic[frame]; -+ q.ptr = q_p->ptr; -+ type = Q_NEXT_TYPE(fotg210, *hw_p); -+ modified = false; -+ -+ while (q.ptr) { -+ switch (hc32_to_cpu(fotg210, type)) { -+ case Q_TYPE_ITD: -+ /* If this ITD is still active, leave it for -+ * later processing ... check the next entry. -+ * No need to check for activity unless the -+ * frame is current. -+ */ -+ if (frame == now_frame && live) { -+ rmb(); -+ for (uf = 0; uf < 8; uf++) { -+ if (q.itd->hw_transaction[uf] & -+ ITD_ACTIVE(fotg210)) -+ break; -+ } -+ if (uf < 8) { -+ q_p = &q.itd->itd_next; -+ hw_p = &q.itd->hw_next; -+ type = Q_NEXT_TYPE(fotg210, -+ q.itd->hw_next); -+ q = *q_p; -+ break; -+ } -+ } -+ -+ /* Take finished ITDs out of the schedule -+ * and process them: recycle, maybe report -+ * URB completion. HC won't cache the -+ * pointer for much longer, if at all. -+ */ -+ *q_p = q.itd->itd_next; -+ *hw_p = q.itd->hw_next; -+ type = Q_NEXT_TYPE(fotg210, q.itd->hw_next); -+ wmb(); -+ modified = itd_complete(fotg210, q.itd); -+ q = *q_p; -+ break; -+ default: -+ fotg210_dbg(fotg210, "corrupt type %d frame %d shadow %p\n", -+ type, frame, q.ptr); -+ fallthrough; -+ case Q_TYPE_QH: -+ case Q_TYPE_FSTN: -+ /* End of the iTDs and siTDs */ -+ q.ptr = NULL; -+ break; -+ } -+ -+ /* assume completion callbacks modify the queue */ -+ if (unlikely(modified && fotg210->isoc_count > 0)) -+ return -EINVAL; -+ } -+ return 0; -+} -+ -+static void scan_isoc(struct fotg210_hcd *fotg210) -+{ -+ unsigned uf, now_frame, frame, ret; -+ unsigned fmask = fotg210->periodic_size - 1; -+ bool live; -+ -+ /* -+ * When running, scan from last scan point up to "now" -+ * else clean up by scanning everything that's left. -+ * Touches as few pages as possible: cache-friendly. -+ */ -+ if (fotg210->rh_state >= FOTG210_RH_RUNNING) { -+ uf = fotg210_read_frame_index(fotg210); -+ now_frame = (uf >> 3) & fmask; -+ live = true; -+ } else { -+ now_frame = (fotg210->next_frame - 1) & fmask; -+ live = false; -+ } -+ fotg210->now_frame = now_frame; -+ -+ frame = fotg210->next_frame; -+ for (;;) { -+ ret = 1; -+ while (ret != 0) -+ ret = scan_frame_queue(fotg210, frame, -+ now_frame, live); -+ -+ /* Stop when we have reached the current frame */ -+ if (frame == now_frame) -+ break; -+ frame = (frame + 1) & fmask; -+ } -+ fotg210->next_frame = now_frame; -+} -+ -+/* Display / Set uframe_periodic_max -+ */ -+static ssize_t uframe_periodic_max_show(struct device *dev, -+ struct device_attribute *attr, char *buf) -+{ -+ struct fotg210_hcd *fotg210; -+ int n; -+ -+ fotg210 = hcd_to_fotg210(bus_to_hcd(dev_get_drvdata(dev))); -+ n = scnprintf(buf, PAGE_SIZE, "%d\n", fotg210->uframe_periodic_max); -+ return n; -+} -+ -+ -+static ssize_t uframe_periodic_max_store(struct device *dev, -+ struct device_attribute *attr, const char *buf, size_t count) -+{ -+ struct fotg210_hcd *fotg210; -+ unsigned uframe_periodic_max; -+ unsigned frame, uframe; -+ unsigned short allocated_max; -+ unsigned long flags; -+ ssize_t ret; -+ -+ fotg210 = hcd_to_fotg210(bus_to_hcd(dev_get_drvdata(dev))); -+ if (kstrtouint(buf, 0, &uframe_periodic_max) < 0) -+ return -EINVAL; -+ -+ if (uframe_periodic_max < 100 || uframe_periodic_max >= 125) { -+ fotg210_info(fotg210, "rejecting invalid request for uframe_periodic_max=%u\n", -+ uframe_periodic_max); -+ return -EINVAL; -+ } -+ -+ ret = -EINVAL; -+ -+ /* -+ * lock, so that our checking does not race with possible periodic -+ * bandwidth allocation through submitting new urbs. -+ */ -+ spin_lock_irqsave(&fotg210->lock, flags); -+ -+ /* -+ * for request to decrease max periodic bandwidth, we have to check -+ * every microframe in the schedule to see whether the decrease is -+ * possible. -+ */ -+ if (uframe_periodic_max < fotg210->uframe_periodic_max) { -+ allocated_max = 0; -+ -+ for (frame = 0; frame < fotg210->periodic_size; ++frame) -+ for (uframe = 0; uframe < 7; ++uframe) -+ allocated_max = max(allocated_max, -+ periodic_usecs(fotg210, frame, -+ uframe)); -+ -+ if (allocated_max > uframe_periodic_max) { -+ fotg210_info(fotg210, -+ "cannot decrease uframe_periodic_max because periodic bandwidth is already allocated (%u > %u)\n", -+ allocated_max, uframe_periodic_max); -+ goto out_unlock; -+ } -+ } -+ -+ /* increasing is always ok */ -+ -+ fotg210_info(fotg210, -+ "setting max periodic bandwidth to %u%% (== %u usec/uframe)\n", -+ 100 * uframe_periodic_max/125, uframe_periodic_max); -+ -+ if (uframe_periodic_max != 100) -+ fotg210_warn(fotg210, "max periodic bandwidth set is non-standard\n"); -+ -+ fotg210->uframe_periodic_max = uframe_periodic_max; -+ ret = count; -+ -+out_unlock: -+ spin_unlock_irqrestore(&fotg210->lock, flags); -+ return ret; -+} -+ -+static DEVICE_ATTR_RW(uframe_periodic_max); -+ -+static inline int create_sysfs_files(struct fotg210_hcd *fotg210) -+{ -+ struct device *controller = fotg210_to_hcd(fotg210)->self.controller; -+ -+ return device_create_file(controller, &dev_attr_uframe_periodic_max); -+} -+ -+static inline void remove_sysfs_files(struct fotg210_hcd *fotg210) -+{ -+ struct device *controller = fotg210_to_hcd(fotg210)->self.controller; -+ -+ device_remove_file(controller, &dev_attr_uframe_periodic_max); -+} -+/* On some systems, leaving remote wakeup enabled prevents system shutdown. -+ * The firmware seems to think that powering off is a wakeup event! -+ * This routine turns off remote wakeup and everything else, on all ports. -+ */ -+static void fotg210_turn_off_all_ports(struct fotg210_hcd *fotg210) -+{ -+ u32 __iomem *status_reg = &fotg210->regs->port_status; -+ -+ fotg210_writel(fotg210, PORT_RWC_BITS, status_reg); -+} -+ -+/* Halt HC, turn off all ports, and let the BIOS use the companion controllers. -+ * Must be called with interrupts enabled and the lock not held. -+ */ -+static void fotg210_silence_controller(struct fotg210_hcd *fotg210) -+{ -+ fotg210_halt(fotg210); -+ -+ spin_lock_irq(&fotg210->lock); -+ fotg210->rh_state = FOTG210_RH_HALTED; -+ fotg210_turn_off_all_ports(fotg210); -+ spin_unlock_irq(&fotg210->lock); -+} -+ -+/* fotg210_shutdown kick in for silicon on any bus (not just pci, etc). -+ * This forcibly disables dma and IRQs, helping kexec and other cases -+ * where the next system software may expect clean state. -+ */ -+static void fotg210_shutdown(struct usb_hcd *hcd) -+{ -+ struct fotg210_hcd *fotg210 = hcd_to_fotg210(hcd); -+ -+ spin_lock_irq(&fotg210->lock); -+ fotg210->shutdown = true; -+ fotg210->rh_state = FOTG210_RH_STOPPING; -+ fotg210->enabled_hrtimer_events = 0; -+ spin_unlock_irq(&fotg210->lock); -+ -+ fotg210_silence_controller(fotg210); -+ -+ hrtimer_cancel(&fotg210->hrtimer); -+} -+ -+/* fotg210_work is called from some interrupts, timers, and so on. -+ * it calls driver completion functions, after dropping fotg210->lock. -+ */ -+static void fotg210_work(struct fotg210_hcd *fotg210) -+{ -+ /* another CPU may drop fotg210->lock during a schedule scan while -+ * it reports urb completions. this flag guards against bogus -+ * attempts at re-entrant schedule scanning. -+ */ -+ if (fotg210->scanning) { -+ fotg210->need_rescan = true; -+ return; -+ } -+ fotg210->scanning = true; -+ -+rescan: -+ fotg210->need_rescan = false; -+ if (fotg210->async_count) -+ scan_async(fotg210); -+ if (fotg210->intr_count > 0) -+ scan_intr(fotg210); -+ if (fotg210->isoc_count > 0) -+ scan_isoc(fotg210); -+ if (fotg210->need_rescan) -+ goto rescan; -+ fotg210->scanning = false; -+ -+ /* the IO watchdog guards against hardware or driver bugs that -+ * misplace IRQs, and should let us run completely without IRQs. -+ * such lossage has been observed on both VT6202 and VT8235. -+ */ -+ turn_on_io_watchdog(fotg210); -+} -+ -+/* Called when the fotg210_hcd module is removed. -+ */ -+static void fotg210_stop(struct usb_hcd *hcd) -+{ -+ struct fotg210_hcd *fotg210 = hcd_to_fotg210(hcd); -+ -+ fotg210_dbg(fotg210, "stop\n"); -+ -+ /* no more interrupts ... */ -+ -+ spin_lock_irq(&fotg210->lock); -+ fotg210->enabled_hrtimer_events = 0; -+ spin_unlock_irq(&fotg210->lock); -+ -+ fotg210_quiesce(fotg210); -+ fotg210_silence_controller(fotg210); -+ fotg210_reset(fotg210); -+ -+ hrtimer_cancel(&fotg210->hrtimer); -+ remove_sysfs_files(fotg210); -+ remove_debug_files(fotg210); -+ -+ /* root hub is shut down separately (first, when possible) */ -+ spin_lock_irq(&fotg210->lock); -+ end_free_itds(fotg210); -+ spin_unlock_irq(&fotg210->lock); -+ fotg210_mem_cleanup(fotg210); -+ -+#ifdef FOTG210_STATS -+ fotg210_dbg(fotg210, "irq normal %ld err %ld iaa %ld (lost %ld)\n", -+ fotg210->stats.normal, fotg210->stats.error, -+ fotg210->stats.iaa, fotg210->stats.lost_iaa); -+ fotg210_dbg(fotg210, "complete %ld unlink %ld\n", -+ fotg210->stats.complete, fotg210->stats.unlink); -+#endif -+ -+ dbg_status(fotg210, "fotg210_stop completed", -+ fotg210_readl(fotg210, &fotg210->regs->status)); -+} -+ -+/* one-time init, only for memory state */ -+static int hcd_fotg210_init(struct usb_hcd *hcd) -+{ -+ struct fotg210_hcd *fotg210 = hcd_to_fotg210(hcd); -+ u32 temp; -+ int retval; -+ u32 hcc_params; -+ struct fotg210_qh_hw *hw; -+ -+ spin_lock_init(&fotg210->lock); -+ -+ /* -+ * keep io watchdog by default, those good HCDs could turn off it later -+ */ -+ fotg210->need_io_watchdog = 1; -+ -+ hrtimer_init(&fotg210->hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS); -+ fotg210->hrtimer.function = fotg210_hrtimer_func; -+ fotg210->next_hrtimer_event = FOTG210_HRTIMER_NO_EVENT; -+ -+ hcc_params = fotg210_readl(fotg210, &fotg210->caps->hcc_params); -+ -+ /* -+ * by default set standard 80% (== 100 usec/uframe) max periodic -+ * bandwidth as required by USB 2.0 -+ */ -+ fotg210->uframe_periodic_max = 100; -+ -+ /* -+ * hw default: 1K periodic list heads, one per frame. -+ * periodic_size can shrink by USBCMD update if hcc_params allows. -+ */ -+ fotg210->periodic_size = DEFAULT_I_TDPS; -+ INIT_LIST_HEAD(&fotg210->intr_qh_list); -+ INIT_LIST_HEAD(&fotg210->cached_itd_list); -+ -+ if (HCC_PGM_FRAMELISTLEN(hcc_params)) { -+ /* periodic schedule size can be smaller than default */ -+ switch (FOTG210_TUNE_FLS) { -+ case 0: -+ fotg210->periodic_size = 1024; -+ break; -+ case 1: -+ fotg210->periodic_size = 512; -+ break; -+ case 2: -+ fotg210->periodic_size = 256; -+ break; -+ default: -+ BUG(); -+ } -+ } -+ retval = fotg210_mem_init(fotg210, GFP_KERNEL); -+ if (retval < 0) -+ return retval; -+ -+ /* controllers may cache some of the periodic schedule ... */ -+ fotg210->i_thresh = 2; -+ -+ /* -+ * dedicate a qh for the async ring head, since we couldn't unlink -+ * a 'real' qh without stopping the async schedule [4.8]. use it -+ * as the 'reclamation list head' too. -+ * its dummy is used in hw_alt_next of many tds, to prevent the qh -+ * from automatically advancing to the next td after short reads. -+ */ -+ fotg210->async->qh_next.qh = NULL; -+ hw = fotg210->async->hw; -+ hw->hw_next = QH_NEXT(fotg210, fotg210->async->qh_dma); -+ hw->hw_info1 = cpu_to_hc32(fotg210, QH_HEAD); -+ hw->hw_token = cpu_to_hc32(fotg210, QTD_STS_HALT); -+ hw->hw_qtd_next = FOTG210_LIST_END(fotg210); -+ fotg210->async->qh_state = QH_STATE_LINKED; -+ hw->hw_alt_next = QTD_NEXT(fotg210, fotg210->async->dummy->qtd_dma); -+ -+ /* clear interrupt enables, set irq latency */ -+ if (log2_irq_thresh < 0 || log2_irq_thresh > 6) -+ log2_irq_thresh = 0; -+ temp = 1 << (16 + log2_irq_thresh); -+ if (HCC_CANPARK(hcc_params)) { -+ /* HW default park == 3, on hardware that supports it (like -+ * NVidia and ALI silicon), maximizes throughput on the async -+ * schedule by avoiding QH fetches between transfers. -+ * -+ * With fast usb storage devices and NForce2, "park" seems to -+ * make problems: throughput reduction (!), data errors... -+ */ -+ if (park) { -+ park = min_t(unsigned, park, 3); -+ temp |= CMD_PARK; -+ temp |= park << 8; -+ } -+ fotg210_dbg(fotg210, "park %d\n", park); -+ } -+ if (HCC_PGM_FRAMELISTLEN(hcc_params)) { -+ /* periodic schedule size can be smaller than default */ -+ temp &= ~(3 << 2); -+ temp |= (FOTG210_TUNE_FLS << 2); -+ } -+ fotg210->command = temp; -+ -+ /* Accept arbitrarily long scatter-gather lists */ -+ if (!hcd->localmem_pool) -+ hcd->self.sg_tablesize = ~0; -+ return 0; -+} -+ -+/* start HC running; it's halted, hcd_fotg210_init() has been run (once) */ -+static int fotg210_run(struct usb_hcd *hcd) -+{ -+ struct fotg210_hcd *fotg210 = hcd_to_fotg210(hcd); -+ u32 temp; -+ -+ hcd->uses_new_polling = 1; -+ -+ /* EHCI spec section 4.1 */ -+ -+ fotg210_writel(fotg210, fotg210->periodic_dma, -+ &fotg210->regs->frame_list); -+ fotg210_writel(fotg210, (u32)fotg210->async->qh_dma, -+ &fotg210->regs->async_next); -+ -+ /* -+ * hcc_params controls whether fotg210->regs->segment must (!!!) -+ * be used; it constrains QH/ITD/SITD and QTD locations. -+ * dma_pool consistent memory always uses segment zero. -+ * streaming mappings for I/O buffers, like dma_map_single(), -+ * can return segments above 4GB, if the device allows. -+ * -+ * NOTE: the dma mask is visible through dev->dma_mask, so -+ * drivers can pass this info along ... like NETIF_F_HIGHDMA, -+ * Scsi_Host.highmem_io, and so forth. It's readonly to all -+ * host side drivers though. -+ */ -+ fotg210_readl(fotg210, &fotg210->caps->hcc_params); -+ -+ /* -+ * Philips, Intel, and maybe others need CMD_RUN before the -+ * root hub will detect new devices (why?); NEC doesn't -+ */ -+ fotg210->command &= ~(CMD_IAAD|CMD_PSE|CMD_ASE|CMD_RESET); -+ fotg210->command |= CMD_RUN; -+ fotg210_writel(fotg210, fotg210->command, &fotg210->regs->command); -+ dbg_cmd(fotg210, "init", fotg210->command); -+ -+ /* -+ * Start, enabling full USB 2.0 functionality ... usb 1.1 devices -+ * are explicitly handed to companion controller(s), so no TT is -+ * involved with the root hub. (Except where one is integrated, -+ * and there's no companion controller unless maybe for USB OTG.) -+ * -+ * Turning on the CF flag will transfer ownership of all ports -+ * from the companions to the EHCI controller. If any of the -+ * companions are in the middle of a port reset at the time, it -+ * could cause trouble. Write-locking ehci_cf_port_reset_rwsem -+ * guarantees that no resets are in progress. After we set CF, -+ * a short delay lets the hardware catch up; new resets shouldn't -+ * be started before the port switching actions could complete. -+ */ -+ down_write(&ehci_cf_port_reset_rwsem); -+ fotg210->rh_state = FOTG210_RH_RUNNING; -+ /* unblock posted writes */ -+ fotg210_readl(fotg210, &fotg210->regs->command); -+ usleep_range(5000, 10000); -+ up_write(&ehci_cf_port_reset_rwsem); -+ fotg210->last_periodic_enable = ktime_get_real(); -+ -+ temp = HC_VERSION(fotg210, -+ fotg210_readl(fotg210, &fotg210->caps->hc_capbase)); -+ fotg210_info(fotg210, -+ "USB %x.%x started, EHCI %x.%02x\n", -+ ((fotg210->sbrn & 0xf0) >> 4), (fotg210->sbrn & 0x0f), -+ temp >> 8, temp & 0xff); -+ -+ fotg210_writel(fotg210, INTR_MASK, -+ &fotg210->regs->intr_enable); /* Turn On Interrupts */ -+ -+ /* GRR this is run-once init(), being done every time the HC starts. -+ * So long as they're part of class devices, we can't do it init() -+ * since the class device isn't created that early. -+ */ -+ create_debug_files(fotg210); -+ create_sysfs_files(fotg210); -+ -+ return 0; -+} -+ -+static int fotg210_setup(struct usb_hcd *hcd) -+{ -+ struct fotg210_hcd *fotg210 = hcd_to_fotg210(hcd); -+ int retval; -+ -+ fotg210->regs = (void __iomem *)fotg210->caps + -+ HC_LENGTH(fotg210, -+ fotg210_readl(fotg210, &fotg210->caps->hc_capbase)); -+ dbg_hcs_params(fotg210, "reset"); -+ dbg_hcc_params(fotg210, "reset"); -+ -+ /* cache this readonly data; minimize chip reads */ -+ fotg210->hcs_params = fotg210_readl(fotg210, -+ &fotg210->caps->hcs_params); -+ -+ fotg210->sbrn = HCD_USB2; -+ -+ /* data structure init */ -+ retval = hcd_fotg210_init(hcd); -+ if (retval) -+ return retval; -+ -+ retval = fotg210_halt(fotg210); -+ if (retval) -+ return retval; -+ -+ fotg210_reset(fotg210); -+ -+ return 0; -+} -+ -+static irqreturn_t fotg210_irq(struct usb_hcd *hcd) -+{ -+ struct fotg210_hcd *fotg210 = hcd_to_fotg210(hcd); -+ u32 status, masked_status, pcd_status = 0, cmd; -+ int bh; -+ -+ spin_lock(&fotg210->lock); -+ -+ status = fotg210_readl(fotg210, &fotg210->regs->status); -+ -+ /* e.g. cardbus physical eject */ -+ if (status == ~(u32) 0) { -+ fotg210_dbg(fotg210, "device removed\n"); -+ goto dead; -+ } -+ -+ /* -+ * We don't use STS_FLR, but some controllers don't like it to -+ * remain on, so mask it out along with the other status bits. -+ */ -+ masked_status = status & (INTR_MASK | STS_FLR); -+ -+ /* Shared IRQ? */ -+ if (!masked_status || -+ unlikely(fotg210->rh_state == FOTG210_RH_HALTED)) { -+ spin_unlock(&fotg210->lock); -+ return IRQ_NONE; -+ } -+ -+ /* clear (just) interrupts */ -+ fotg210_writel(fotg210, masked_status, &fotg210->regs->status); -+ cmd = fotg210_readl(fotg210, &fotg210->regs->command); -+ bh = 0; -+ -+ /* unrequested/ignored: Frame List Rollover */ -+ dbg_status(fotg210, "irq", status); -+ -+ /* INT, ERR, and IAA interrupt rates can be throttled */ -+ -+ /* normal [4.15.1.2] or error [4.15.1.1] completion */ -+ if (likely((status & (STS_INT|STS_ERR)) != 0)) { -+ if (likely((status & STS_ERR) == 0)) -+ INCR(fotg210->stats.normal); -+ else -+ INCR(fotg210->stats.error); -+ bh = 1; -+ } -+ -+ /* complete the unlinking of some qh [4.15.2.3] */ -+ if (status & STS_IAA) { -+ -+ /* Turn off the IAA watchdog */ -+ fotg210->enabled_hrtimer_events &= -+ ~BIT(FOTG210_HRTIMER_IAA_WATCHDOG); -+ -+ /* -+ * Mild optimization: Allow another IAAD to reset the -+ * hrtimer, if one occurs before the next expiration. -+ * In theory we could always cancel the hrtimer, but -+ * tests show that about half the time it will be reset -+ * for some other event anyway. -+ */ -+ if (fotg210->next_hrtimer_event == FOTG210_HRTIMER_IAA_WATCHDOG) -+ ++fotg210->next_hrtimer_event; -+ -+ /* guard against (alleged) silicon errata */ -+ if (cmd & CMD_IAAD) -+ fotg210_dbg(fotg210, "IAA with IAAD still set?\n"); -+ if (fotg210->async_iaa) { -+ INCR(fotg210->stats.iaa); -+ end_unlink_async(fotg210); -+ } else -+ fotg210_dbg(fotg210, "IAA with nothing unlinked?\n"); -+ } -+ -+ /* remote wakeup [4.3.1] */ -+ if (status & STS_PCD) { -+ int pstatus; -+ u32 __iomem *status_reg = &fotg210->regs->port_status; -+ -+ /* kick root hub later */ -+ pcd_status = status; -+ -+ /* resume root hub? */ -+ if (fotg210->rh_state == FOTG210_RH_SUSPENDED) -+ usb_hcd_resume_root_hub(hcd); -+ -+ pstatus = fotg210_readl(fotg210, status_reg); -+ -+ if (test_bit(0, &fotg210->suspended_ports) && -+ ((pstatus & PORT_RESUME) || -+ !(pstatus & PORT_SUSPEND)) && -+ (pstatus & PORT_PE) && -+ fotg210->reset_done[0] == 0) { -+ -+ /* start 20 msec resume signaling from this port, -+ * and make hub_wq collect PORT_STAT_C_SUSPEND to -+ * stop that signaling. Use 5 ms extra for safety, -+ * like usb_port_resume() does. -+ */ -+ fotg210->reset_done[0] = jiffies + msecs_to_jiffies(25); -+ set_bit(0, &fotg210->resuming_ports); -+ fotg210_dbg(fotg210, "port 1 remote wakeup\n"); -+ mod_timer(&hcd->rh_timer, fotg210->reset_done[0]); -+ } -+ } -+ -+ /* PCI errors [4.15.2.4] */ -+ if (unlikely((status & STS_FATAL) != 0)) { -+ fotg210_err(fotg210, "fatal error\n"); -+ dbg_cmd(fotg210, "fatal", cmd); -+ dbg_status(fotg210, "fatal", status); -+dead: -+ usb_hc_died(hcd); -+ -+ /* Don't let the controller do anything more */ -+ fotg210->shutdown = true; -+ fotg210->rh_state = FOTG210_RH_STOPPING; -+ fotg210->command &= ~(CMD_RUN | CMD_ASE | CMD_PSE); -+ fotg210_writel(fotg210, fotg210->command, -+ &fotg210->regs->command); -+ fotg210_writel(fotg210, 0, &fotg210->regs->intr_enable); -+ fotg210_handle_controller_death(fotg210); -+ -+ /* Handle completions when the controller stops */ -+ bh = 0; -+ } -+ -+ if (bh) -+ fotg210_work(fotg210); -+ spin_unlock(&fotg210->lock); -+ if (pcd_status) -+ usb_hcd_poll_rh_status(hcd); -+ return IRQ_HANDLED; -+} -+ -+/* non-error returns are a promise to giveback() the urb later -+ * we drop ownership so next owner (or urb unlink) can get it -+ * -+ * urb + dev is in hcd.self.controller.urb_list -+ * we're queueing TDs onto software and hardware lists -+ * -+ * hcd-specific init for hcpriv hasn't been done yet -+ * -+ * NOTE: control, bulk, and interrupt share the same code to append TDs -+ * to a (possibly active) QH, and the same QH scanning code. -+ */ -+static int fotg210_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, -+ gfp_t mem_flags) -+{ -+ struct fotg210_hcd *fotg210 = hcd_to_fotg210(hcd); -+ struct list_head qtd_list; -+ -+ INIT_LIST_HEAD(&qtd_list); -+ -+ switch (usb_pipetype(urb->pipe)) { -+ case PIPE_CONTROL: -+ /* qh_completions() code doesn't handle all the fault cases -+ * in multi-TD control transfers. Even 1KB is rare anyway. -+ */ -+ if (urb->transfer_buffer_length > (16 * 1024)) -+ return -EMSGSIZE; -+ fallthrough; -+ /* case PIPE_BULK: */ -+ default: -+ if (!qh_urb_transaction(fotg210, urb, &qtd_list, mem_flags)) -+ return -ENOMEM; -+ return submit_async(fotg210, urb, &qtd_list, mem_flags); -+ -+ case PIPE_INTERRUPT: -+ if (!qh_urb_transaction(fotg210, urb, &qtd_list, mem_flags)) -+ return -ENOMEM; -+ return intr_submit(fotg210, urb, &qtd_list, mem_flags); -+ -+ case PIPE_ISOCHRONOUS: -+ return itd_submit(fotg210, urb, mem_flags); -+ } -+} -+ -+/* remove from hardware lists -+ * completions normally happen asynchronously -+ */ -+ -+static int fotg210_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) -+{ -+ struct fotg210_hcd *fotg210 = hcd_to_fotg210(hcd); -+ struct fotg210_qh *qh; -+ unsigned long flags; -+ int rc; -+ -+ spin_lock_irqsave(&fotg210->lock, flags); -+ rc = usb_hcd_check_unlink_urb(hcd, urb, status); -+ if (rc) -+ goto done; -+ -+ switch (usb_pipetype(urb->pipe)) { -+ /* case PIPE_CONTROL: */ -+ /* case PIPE_BULK:*/ -+ default: -+ qh = (struct fotg210_qh *) urb->hcpriv; -+ if (!qh) -+ break; -+ switch (qh->qh_state) { -+ case QH_STATE_LINKED: -+ case QH_STATE_COMPLETING: -+ start_unlink_async(fotg210, qh); -+ break; -+ case QH_STATE_UNLINK: -+ case QH_STATE_UNLINK_WAIT: -+ /* already started */ -+ break; -+ case QH_STATE_IDLE: -+ /* QH might be waiting for a Clear-TT-Buffer */ -+ qh_completions(fotg210, qh); -+ break; -+ } -+ break; -+ -+ case PIPE_INTERRUPT: -+ qh = (struct fotg210_qh *) urb->hcpriv; -+ if (!qh) -+ break; -+ switch (qh->qh_state) { -+ case QH_STATE_LINKED: -+ case QH_STATE_COMPLETING: -+ start_unlink_intr(fotg210, qh); -+ break; -+ case QH_STATE_IDLE: -+ qh_completions(fotg210, qh); -+ break; -+ default: -+ fotg210_dbg(fotg210, "bogus qh %p state %d\n", -+ qh, qh->qh_state); -+ goto done; -+ } -+ break; -+ -+ case PIPE_ISOCHRONOUS: -+ /* itd... */ -+ -+ /* wait till next completion, do it then. */ -+ /* completion irqs can wait up to 1024 msec, */ -+ break; -+ } -+done: -+ spin_unlock_irqrestore(&fotg210->lock, flags); -+ return rc; -+} -+ -+/* bulk qh holds the data toggle */ -+ -+static void fotg210_endpoint_disable(struct usb_hcd *hcd, -+ struct usb_host_endpoint *ep) -+{ -+ struct fotg210_hcd *fotg210 = hcd_to_fotg210(hcd); -+ unsigned long flags; -+ struct fotg210_qh *qh, *tmp; -+ -+ /* ASSERT: any requests/urbs are being unlinked */ -+ /* ASSERT: nobody can be submitting urbs for this any more */ -+ -+rescan: -+ spin_lock_irqsave(&fotg210->lock, flags); -+ qh = ep->hcpriv; -+ if (!qh) -+ goto done; -+ -+ /* endpoints can be iso streams. for now, we don't -+ * accelerate iso completions ... so spin a while. -+ */ -+ if (qh->hw == NULL) { -+ struct fotg210_iso_stream *stream = ep->hcpriv; -+ -+ if (!list_empty(&stream->td_list)) -+ goto idle_timeout; -+ -+ /* BUG_ON(!list_empty(&stream->free_list)); */ -+ kfree(stream); -+ goto done; -+ } -+ -+ if (fotg210->rh_state < FOTG210_RH_RUNNING) -+ qh->qh_state = QH_STATE_IDLE; -+ switch (qh->qh_state) { -+ case QH_STATE_LINKED: -+ case QH_STATE_COMPLETING: -+ for (tmp = fotg210->async->qh_next.qh; -+ tmp && tmp != qh; -+ tmp = tmp->qh_next.qh) -+ continue; -+ /* periodic qh self-unlinks on empty, and a COMPLETING qh -+ * may already be unlinked. -+ */ -+ if (tmp) -+ start_unlink_async(fotg210, qh); -+ fallthrough; -+ case QH_STATE_UNLINK: /* wait for hw to finish? */ -+ case QH_STATE_UNLINK_WAIT: -+idle_timeout: -+ spin_unlock_irqrestore(&fotg210->lock, flags); -+ schedule_timeout_uninterruptible(1); -+ goto rescan; -+ case QH_STATE_IDLE: /* fully unlinked */ -+ if (qh->clearing_tt) -+ goto idle_timeout; -+ if (list_empty(&qh->qtd_list)) { -+ qh_destroy(fotg210, qh); -+ break; -+ } -+ fallthrough; -+ default: -+ /* caller was supposed to have unlinked any requests; -+ * that's not our job. just leak this memory. -+ */ -+ fotg210_err(fotg210, "qh %p (#%02x) state %d%s\n", -+ qh, ep->desc.bEndpointAddress, qh->qh_state, -+ list_empty(&qh->qtd_list) ? "" : "(has tds)"); -+ break; -+ } -+done: -+ ep->hcpriv = NULL; -+ spin_unlock_irqrestore(&fotg210->lock, flags); -+} -+ -+static void fotg210_endpoint_reset(struct usb_hcd *hcd, -+ struct usb_host_endpoint *ep) -+{ -+ struct fotg210_hcd *fotg210 = hcd_to_fotg210(hcd); -+ struct fotg210_qh *qh; -+ int eptype = usb_endpoint_type(&ep->desc); -+ int epnum = usb_endpoint_num(&ep->desc); -+ int is_out = usb_endpoint_dir_out(&ep->desc); -+ unsigned long flags; -+ -+ if (eptype != USB_ENDPOINT_XFER_BULK && eptype != USB_ENDPOINT_XFER_INT) -+ return; -+ -+ spin_lock_irqsave(&fotg210->lock, flags); -+ qh = ep->hcpriv; -+ -+ /* For Bulk and Interrupt endpoints we maintain the toggle state -+ * in the hardware; the toggle bits in udev aren't used at all. -+ * When an endpoint is reset by usb_clear_halt() we must reset -+ * the toggle bit in the QH. -+ */ -+ if (qh) { -+ usb_settoggle(qh->dev, epnum, is_out, 0); -+ if (!list_empty(&qh->qtd_list)) { -+ WARN_ONCE(1, "clear_halt for a busy endpoint\n"); -+ } else if (qh->qh_state == QH_STATE_LINKED || -+ qh->qh_state == QH_STATE_COMPLETING) { -+ -+ /* The toggle value in the QH can't be updated -+ * while the QH is active. Unlink it now; -+ * re-linking will call qh_refresh(). -+ */ -+ if (eptype == USB_ENDPOINT_XFER_BULK) -+ start_unlink_async(fotg210, qh); -+ else -+ start_unlink_intr(fotg210, qh); -+ } -+ } -+ spin_unlock_irqrestore(&fotg210->lock, flags); -+} -+ -+static int fotg210_get_frame(struct usb_hcd *hcd) -+{ -+ struct fotg210_hcd *fotg210 = hcd_to_fotg210(hcd); -+ -+ return (fotg210_read_frame_index(fotg210) >> 3) % -+ fotg210->periodic_size; -+} -+ -+/* The EHCI in ChipIdea HDRC cannot be a separate module or device, -+ * because its registers (and irq) are shared between host/gadget/otg -+ * functions and in order to facilitate role switching we cannot -+ * give the fotg210 driver exclusive access to those. -+ */ -+MODULE_DESCRIPTION(DRIVER_DESC); -+MODULE_AUTHOR(DRIVER_AUTHOR); -+MODULE_LICENSE("GPL"); -+ -+static const struct hc_driver fotg210_fotg210_hc_driver = { -+ .description = hcd_name, -+ .product_desc = "Faraday USB2.0 Host Controller", -+ .hcd_priv_size = sizeof(struct fotg210_hcd), -+ -+ /* -+ * generic hardware linkage -+ */ -+ .irq = fotg210_irq, -+ .flags = HCD_MEMORY | HCD_DMA | HCD_USB2, -+ -+ /* -+ * basic lifecycle operations -+ */ -+ .reset = hcd_fotg210_init, -+ .start = fotg210_run, -+ .stop = fotg210_stop, -+ .shutdown = fotg210_shutdown, -+ -+ /* -+ * managing i/o requests and associated device resources -+ */ -+ .urb_enqueue = fotg210_urb_enqueue, -+ .urb_dequeue = fotg210_urb_dequeue, -+ .endpoint_disable = fotg210_endpoint_disable, -+ .endpoint_reset = fotg210_endpoint_reset, -+ -+ /* -+ * scheduling support -+ */ -+ .get_frame_number = fotg210_get_frame, -+ -+ /* -+ * root hub support -+ */ -+ .hub_status_data = fotg210_hub_status_data, -+ .hub_control = fotg210_hub_control, -+ .bus_suspend = fotg210_bus_suspend, -+ .bus_resume = fotg210_bus_resume, -+ -+ .relinquish_port = fotg210_relinquish_port, -+ .port_handed_over = fotg210_port_handed_over, -+ -+ .clear_tt_buffer_complete = fotg210_clear_tt_buffer_complete, -+}; -+ -+static void fotg210_init(struct fotg210_hcd *fotg210) -+{ -+ u32 value; -+ -+ iowrite32(GMIR_MDEV_INT | GMIR_MOTG_INT | GMIR_INT_POLARITY, -+ &fotg210->regs->gmir); -+ -+ value = ioread32(&fotg210->regs->otgcsr); -+ value &= ~OTGCSR_A_BUS_DROP; -+ value |= OTGCSR_A_BUS_REQ; -+ iowrite32(value, &fotg210->regs->otgcsr); -+} -+ -+/* -+ * fotg210_hcd_probe - initialize faraday FOTG210 HCDs -+ * -+ * Allocates basic resources for this USB host controller, and -+ * then invokes the start() method for the HCD associated with it -+ * through the hotplug entry's driver_data. -+ */ -+static int fotg210_hcd_probe(struct platform_device *pdev) -+{ -+ struct device *dev = &pdev->dev; -+ struct usb_hcd *hcd; -+ struct resource *res; -+ int irq; -+ int retval; -+ struct fotg210_hcd *fotg210; -+ -+ if (usb_disabled()) -+ return -ENODEV; -+ -+ pdev->dev.power.power_state = PMSG_ON; -+ -+ irq = platform_get_irq(pdev, 0); -+ if (irq < 0) -+ return irq; -+ -+ hcd = usb_create_hcd(&fotg210_fotg210_hc_driver, dev, -+ dev_name(dev)); -+ if (!hcd) { -+ dev_err(dev, "failed to create hcd\n"); -+ retval = -ENOMEM; -+ goto fail_create_hcd; -+ } -+ -+ hcd->has_tt = 1; -+ -+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); -+ hcd->regs = devm_ioremap_resource(&pdev->dev, res); -+ if (IS_ERR(hcd->regs)) { -+ retval = PTR_ERR(hcd->regs); -+ goto failed_put_hcd; -+ } -+ -+ hcd->rsrc_start = res->start; -+ hcd->rsrc_len = resource_size(res); -+ -+ fotg210 = hcd_to_fotg210(hcd); -+ -+ fotg210->caps = hcd->regs; -+ -+ /* It's OK not to supply this clock */ -+ fotg210->pclk = clk_get(dev, "PCLK"); -+ if (!IS_ERR(fotg210->pclk)) { -+ retval = clk_prepare_enable(fotg210->pclk); -+ if (retval) { -+ dev_err(dev, "failed to enable PCLK\n"); -+ goto failed_put_hcd; -+ } -+ } else if (PTR_ERR(fotg210->pclk) == -EPROBE_DEFER) { -+ /* -+ * Percolate deferrals, for anything else, -+ * just live without the clocking. -+ */ -+ retval = PTR_ERR(fotg210->pclk); -+ goto failed_dis_clk; -+ } -+ -+ retval = fotg210_setup(hcd); -+ if (retval) -+ goto failed_dis_clk; -+ -+ fotg210_init(fotg210); -+ -+ retval = usb_add_hcd(hcd, irq, IRQF_SHARED); -+ if (retval) { -+ dev_err(dev, "failed to add hcd with err %d\n", retval); -+ goto failed_dis_clk; -+ } -+ device_wakeup_enable(hcd->self.controller); -+ platform_set_drvdata(pdev, hcd); -+ -+ return retval; -+ -+failed_dis_clk: -+ if (!IS_ERR(fotg210->pclk)) { -+ clk_disable_unprepare(fotg210->pclk); -+ clk_put(fotg210->pclk); -+ } -+failed_put_hcd: -+ usb_put_hcd(hcd); -+fail_create_hcd: -+ dev_err(dev, "init %s fail, %d\n", dev_name(dev), retval); -+ return retval; -+} -+ -+/* -+ * fotg210_hcd_remove - shutdown processing for EHCI HCDs -+ * @dev: USB Host Controller being removed -+ * -+ */ -+static int fotg210_hcd_remove(struct platform_device *pdev) -+{ -+ struct usb_hcd *hcd = platform_get_drvdata(pdev); -+ struct fotg210_hcd *fotg210 = hcd_to_fotg210(hcd); -+ -+ if (!IS_ERR(fotg210->pclk)) { -+ clk_disable_unprepare(fotg210->pclk); -+ clk_put(fotg210->pclk); -+ } -+ -+ usb_remove_hcd(hcd); -+ usb_put_hcd(hcd); -+ -+ return 0; -+} -+ -+#ifdef CONFIG_OF -+static const struct of_device_id fotg210_of_match[] = { -+ { .compatible = "faraday,fotg210" }, -+ {}, -+}; -+MODULE_DEVICE_TABLE(of, fotg210_of_match); -+#endif -+ -+static struct platform_driver fotg210_hcd_driver = { -+ .driver = { -+ .name = "fotg210-hcd", -+ .of_match_table = of_match_ptr(fotg210_of_match), -+ }, -+ .probe = fotg210_hcd_probe, -+ .remove = fotg210_hcd_remove, -+}; -+ -+static int __init fotg210_hcd_init(void) -+{ -+ int retval = 0; -+ -+ if (usb_disabled()) -+ return -ENODEV; -+ -+ set_bit(USB_EHCI_LOADED, &usb_hcds_loaded); -+ if (test_bit(USB_UHCI_LOADED, &usb_hcds_loaded) || -+ test_bit(USB_OHCI_LOADED, &usb_hcds_loaded)) -+ pr_warn("Warning! fotg210_hcd should always be loaded before uhci_hcd and ohci_hcd, not after\n"); -+ -+ pr_debug("%s: block sizes: qh %zd qtd %zd itd %zd\n", -+ hcd_name, sizeof(struct fotg210_qh), -+ sizeof(struct fotg210_qtd), -+ sizeof(struct fotg210_itd)); -+ -+ fotg210_debug_root = debugfs_create_dir("fotg210", usb_debug_root); -+ -+ retval = platform_driver_register(&fotg210_hcd_driver); -+ if (retval < 0) -+ goto clean; -+ return retval; -+ -+clean: -+ debugfs_remove(fotg210_debug_root); -+ fotg210_debug_root = NULL; -+ -+ clear_bit(USB_EHCI_LOADED, &usb_hcds_loaded); -+ return retval; -+} -+module_init(fotg210_hcd_init); -+ -+static void __exit fotg210_hcd_cleanup(void) -+{ -+ platform_driver_unregister(&fotg210_hcd_driver); -+ debugfs_remove(fotg210_debug_root); -+ clear_bit(USB_EHCI_LOADED, &usb_hcds_loaded); -+} -+module_exit(fotg210_hcd_cleanup); ---- a/drivers/usb/gadget/udc/fotg210-udc.c -+++ /dev/null -@@ -1,1239 +0,0 @@ --// SPDX-License-Identifier: GPL-2.0 --/* -- * FOTG210 UDC Driver supports Bulk transfer so far -- * -- * Copyright (C) 2013 Faraday Technology Corporation -- * -- * Author : Yuan-Hsin Chen -- */ -- --#include --#include --#include --#include --#include --#include --#include --#include -- --#include "fotg210.h" -- --#define DRIVER_DESC "FOTG210 USB Device Controller Driver" --#define DRIVER_VERSION "30-April-2013" -- --static const char udc_name[] = "fotg210_udc"; --static const char * const fotg210_ep_name[] = { -- "ep0", "ep1", "ep2", "ep3", "ep4"}; -- --static void fotg210_disable_fifo_int(struct fotg210_ep *ep) --{ -- u32 value = ioread32(ep->fotg210->reg + FOTG210_DMISGR1); -- -- if (ep->dir_in) -- value |= DMISGR1_MF_IN_INT(ep->epnum - 1); -- else -- value |= DMISGR1_MF_OUTSPK_INT(ep->epnum - 1); -- iowrite32(value, ep->fotg210->reg + FOTG210_DMISGR1); --} -- --static void fotg210_enable_fifo_int(struct fotg210_ep *ep) --{ -- u32 value = ioread32(ep->fotg210->reg + FOTG210_DMISGR1); -- -- if (ep->dir_in) -- value &= ~DMISGR1_MF_IN_INT(ep->epnum - 1); -- else -- value &= ~DMISGR1_MF_OUTSPK_INT(ep->epnum - 1); -- iowrite32(value, ep->fotg210->reg + FOTG210_DMISGR1); --} -- --static void fotg210_set_cxdone(struct fotg210_udc *fotg210) --{ -- u32 value = ioread32(fotg210->reg + FOTG210_DCFESR); -- -- value |= DCFESR_CX_DONE; -- iowrite32(value, fotg210->reg + FOTG210_DCFESR); --} -- --static void fotg210_done(struct fotg210_ep *ep, struct fotg210_request *req, -- int status) --{ -- list_del_init(&req->queue); -- -- /* don't modify queue heads during completion callback */ -- if (ep->fotg210->gadget.speed == USB_SPEED_UNKNOWN) -- req->req.status = -ESHUTDOWN; -- else -- req->req.status = status; -- -- spin_unlock(&ep->fotg210->lock); -- usb_gadget_giveback_request(&ep->ep, &req->req); -- spin_lock(&ep->fotg210->lock); -- -- if (ep->epnum) { -- if (list_empty(&ep->queue)) -- fotg210_disable_fifo_int(ep); -- } else { -- fotg210_set_cxdone(ep->fotg210); -- } --} -- --static void fotg210_fifo_ep_mapping(struct fotg210_ep *ep, u32 epnum, -- u32 dir_in) --{ -- struct fotg210_udc *fotg210 = ep->fotg210; -- u32 val; -- -- /* Driver should map an ep to a fifo and then map the fifo -- * to the ep. What a brain-damaged design! -- */ -- -- /* map a fifo to an ep */ -- val = ioread32(fotg210->reg + FOTG210_EPMAP); -- val &= ~EPMAP_FIFONOMSK(epnum, dir_in); -- val |= EPMAP_FIFONO(epnum, dir_in); -- iowrite32(val, fotg210->reg + FOTG210_EPMAP); -- -- /* map the ep to the fifo */ -- val = ioread32(fotg210->reg + FOTG210_FIFOMAP); -- val &= ~FIFOMAP_EPNOMSK(epnum); -- val |= FIFOMAP_EPNO(epnum); -- iowrite32(val, fotg210->reg + FOTG210_FIFOMAP); -- -- /* enable fifo */ -- val = ioread32(fotg210->reg + FOTG210_FIFOCF); -- val |= FIFOCF_FIFO_EN(epnum - 1); -- iowrite32(val, fotg210->reg + FOTG210_FIFOCF); --} -- --static void fotg210_set_fifo_dir(struct fotg210_ep *ep, u32 epnum, u32 dir_in) --{ -- struct fotg210_udc *fotg210 = ep->fotg210; -- u32 val; -- -- val = ioread32(fotg210->reg + FOTG210_FIFOMAP); -- val |= (dir_in ? FIFOMAP_DIRIN(epnum - 1) : FIFOMAP_DIROUT(epnum - 1)); -- iowrite32(val, fotg210->reg + FOTG210_FIFOMAP); --} -- --static void fotg210_set_tfrtype(struct fotg210_ep *ep, u32 epnum, u32 type) --{ -- struct fotg210_udc *fotg210 = ep->fotg210; -- u32 val; -- -- val = ioread32(fotg210->reg + FOTG210_FIFOCF); -- val |= FIFOCF_TYPE(type, epnum - 1); -- iowrite32(val, fotg210->reg + FOTG210_FIFOCF); --} -- --static void fotg210_set_mps(struct fotg210_ep *ep, u32 epnum, u32 mps, -- u32 dir_in) --{ -- struct fotg210_udc *fotg210 = ep->fotg210; -- u32 val; -- u32 offset = dir_in ? FOTG210_INEPMPSR(epnum) : -- FOTG210_OUTEPMPSR(epnum); -- -- val = ioread32(fotg210->reg + offset); -- val |= INOUTEPMPSR_MPS(mps); -- iowrite32(val, fotg210->reg + offset); --} -- --static int fotg210_config_ep(struct fotg210_ep *ep, -- const struct usb_endpoint_descriptor *desc) --{ -- struct fotg210_udc *fotg210 = ep->fotg210; -- -- fotg210_set_fifo_dir(ep, ep->epnum, ep->dir_in); -- fotg210_set_tfrtype(ep, ep->epnum, ep->type); -- fotg210_set_mps(ep, ep->epnum, ep->ep.maxpacket, ep->dir_in); -- fotg210_fifo_ep_mapping(ep, ep->epnum, ep->dir_in); -- -- fotg210->ep[ep->epnum] = ep; -- -- return 0; --} -- --static int fotg210_ep_enable(struct usb_ep *_ep, -- const struct usb_endpoint_descriptor *desc) --{ -- struct fotg210_ep *ep; -- -- ep = container_of(_ep, struct fotg210_ep, ep); -- -- ep->desc = desc; -- ep->epnum = usb_endpoint_num(desc); -- ep->type = usb_endpoint_type(desc); -- ep->dir_in = usb_endpoint_dir_in(desc); -- ep->ep.maxpacket = usb_endpoint_maxp(desc); -- -- return fotg210_config_ep(ep, desc); --} -- --static void fotg210_reset_tseq(struct fotg210_udc *fotg210, u8 epnum) --{ -- struct fotg210_ep *ep = fotg210->ep[epnum]; -- u32 value; -- void __iomem *reg; -- -- reg = (ep->dir_in) ? -- fotg210->reg + FOTG210_INEPMPSR(epnum) : -- fotg210->reg + FOTG210_OUTEPMPSR(epnum); -- -- /* Note: Driver needs to set and clear INOUTEPMPSR_RESET_TSEQ -- * bit. Controller wouldn't clear this bit. WTF!!! -- */ -- -- value = ioread32(reg); -- value |= INOUTEPMPSR_RESET_TSEQ; -- iowrite32(value, reg); -- -- value = ioread32(reg); -- value &= ~INOUTEPMPSR_RESET_TSEQ; -- iowrite32(value, reg); --} -- --static int fotg210_ep_release(struct fotg210_ep *ep) --{ -- if (!ep->epnum) -- return 0; -- ep->epnum = 0; -- ep->stall = 0; -- ep->wedged = 0; -- -- fotg210_reset_tseq(ep->fotg210, ep->epnum); -- -- return 0; --} -- --static int fotg210_ep_disable(struct usb_ep *_ep) --{ -- struct fotg210_ep *ep; -- struct fotg210_request *req; -- unsigned long flags; -- -- BUG_ON(!_ep); -- -- ep = container_of(_ep, struct fotg210_ep, ep); -- -- while (!list_empty(&ep->queue)) { -- req = list_entry(ep->queue.next, -- struct fotg210_request, queue); -- spin_lock_irqsave(&ep->fotg210->lock, flags); -- fotg210_done(ep, req, -ECONNRESET); -- spin_unlock_irqrestore(&ep->fotg210->lock, flags); -- } -- -- return fotg210_ep_release(ep); --} -- --static struct usb_request *fotg210_ep_alloc_request(struct usb_ep *_ep, -- gfp_t gfp_flags) --{ -- struct fotg210_request *req; -- -- req = kzalloc(sizeof(struct fotg210_request), gfp_flags); -- if (!req) -- return NULL; -- -- INIT_LIST_HEAD(&req->queue); -- -- return &req->req; --} -- --static void fotg210_ep_free_request(struct usb_ep *_ep, -- struct usb_request *_req) --{ -- struct fotg210_request *req; -- -- req = container_of(_req, struct fotg210_request, req); -- kfree(req); --} -- --static void fotg210_enable_dma(struct fotg210_ep *ep, -- dma_addr_t d, u32 len) --{ -- u32 value; -- struct fotg210_udc *fotg210 = ep->fotg210; -- -- /* set transfer length and direction */ -- value = ioread32(fotg210->reg + FOTG210_DMACPSR1); -- value &= ~(DMACPSR1_DMA_LEN(0xFFFF) | DMACPSR1_DMA_TYPE(1)); -- value |= DMACPSR1_DMA_LEN(len) | DMACPSR1_DMA_TYPE(ep->dir_in); -- iowrite32(value, fotg210->reg + FOTG210_DMACPSR1); -- -- /* set device DMA target FIFO number */ -- value = ioread32(fotg210->reg + FOTG210_DMATFNR); -- if (ep->epnum) -- value |= DMATFNR_ACC_FN(ep->epnum - 1); -- else -- value |= DMATFNR_ACC_CXF; -- iowrite32(value, fotg210->reg + FOTG210_DMATFNR); -- -- /* set DMA memory address */ -- iowrite32(d, fotg210->reg + FOTG210_DMACPSR2); -- -- /* enable MDMA_EROR and MDMA_CMPLT interrupt */ -- value = ioread32(fotg210->reg + FOTG210_DMISGR2); -- value &= ~(DMISGR2_MDMA_CMPLT | DMISGR2_MDMA_ERROR); -- iowrite32(value, fotg210->reg + FOTG210_DMISGR2); -- -- /* start DMA */ -- value = ioread32(fotg210->reg + FOTG210_DMACPSR1); -- value |= DMACPSR1_DMA_START; -- iowrite32(value, fotg210->reg + FOTG210_DMACPSR1); --} -- --static void fotg210_disable_dma(struct fotg210_ep *ep) --{ -- iowrite32(DMATFNR_DISDMA, ep->fotg210->reg + FOTG210_DMATFNR); --} -- --static void fotg210_wait_dma_done(struct fotg210_ep *ep) --{ -- u32 value; -- -- do { -- value = ioread32(ep->fotg210->reg + FOTG210_DISGR2); -- if ((value & DISGR2_USBRST_INT) || -- (value & DISGR2_DMA_ERROR)) -- goto dma_reset; -- } while (!(value & DISGR2_DMA_CMPLT)); -- -- value &= ~DISGR2_DMA_CMPLT; -- iowrite32(value, ep->fotg210->reg + FOTG210_DISGR2); -- return; -- --dma_reset: -- value = ioread32(ep->fotg210->reg + FOTG210_DMACPSR1); -- value |= DMACPSR1_DMA_ABORT; -- iowrite32(value, ep->fotg210->reg + FOTG210_DMACPSR1); -- -- /* reset fifo */ -- if (ep->epnum) { -- value = ioread32(ep->fotg210->reg + -- FOTG210_FIBCR(ep->epnum - 1)); -- value |= FIBCR_FFRST; -- iowrite32(value, ep->fotg210->reg + -- FOTG210_FIBCR(ep->epnum - 1)); -- } else { -- value = ioread32(ep->fotg210->reg + FOTG210_DCFESR); -- value |= DCFESR_CX_CLR; -- iowrite32(value, ep->fotg210->reg + FOTG210_DCFESR); -- } --} -- --static void fotg210_start_dma(struct fotg210_ep *ep, -- struct fotg210_request *req) --{ -- struct device *dev = &ep->fotg210->gadget.dev; -- dma_addr_t d; -- u8 *buffer; -- u32 length; -- -- if (ep->epnum) { -- if (ep->dir_in) { -- buffer = req->req.buf; -- length = req->req.length; -- } else { -- buffer = req->req.buf + req->req.actual; -- length = ioread32(ep->fotg210->reg + -- FOTG210_FIBCR(ep->epnum - 1)) & FIBCR_BCFX; -- if (length > req->req.length - req->req.actual) -- length = req->req.length - req->req.actual; -- } -- } else { -- buffer = req->req.buf + req->req.actual; -- if (req->req.length - req->req.actual > ep->ep.maxpacket) -- length = ep->ep.maxpacket; -- else -- length = req->req.length - req->req.actual; -- } -- -- d = dma_map_single(dev, buffer, length, -- ep->dir_in ? DMA_TO_DEVICE : DMA_FROM_DEVICE); -- -- if (dma_mapping_error(dev, d)) { -- pr_err("dma_mapping_error\n"); -- return; -- } -- -- fotg210_enable_dma(ep, d, length); -- -- /* check if dma is done */ -- fotg210_wait_dma_done(ep); -- -- fotg210_disable_dma(ep); -- -- /* update actual transfer length */ -- req->req.actual += length; -- -- dma_unmap_single(dev, d, length, DMA_TO_DEVICE); --} -- --static void fotg210_ep0_queue(struct fotg210_ep *ep, -- struct fotg210_request *req) --{ -- if (!req->req.length) { -- fotg210_done(ep, req, 0); -- return; -- } -- if (ep->dir_in) { /* if IN */ -- fotg210_start_dma(ep, req); -- if (req->req.length == req->req.actual) -- fotg210_done(ep, req, 0); -- } else { /* OUT */ -- u32 value = ioread32(ep->fotg210->reg + FOTG210_DMISGR0); -- -- value &= ~DMISGR0_MCX_OUT_INT; -- iowrite32(value, ep->fotg210->reg + FOTG210_DMISGR0); -- } --} -- --static int fotg210_ep_queue(struct usb_ep *_ep, struct usb_request *_req, -- gfp_t gfp_flags) --{ -- struct fotg210_ep *ep; -- struct fotg210_request *req; -- unsigned long flags; -- int request = 0; -- -- ep = container_of(_ep, struct fotg210_ep, ep); -- req = container_of(_req, struct fotg210_request, req); -- -- if (ep->fotg210->gadget.speed == USB_SPEED_UNKNOWN) -- return -ESHUTDOWN; -- -- spin_lock_irqsave(&ep->fotg210->lock, flags); -- -- if (list_empty(&ep->queue)) -- request = 1; -- -- list_add_tail(&req->queue, &ep->queue); -- -- req->req.actual = 0; -- req->req.status = -EINPROGRESS; -- -- if (!ep->epnum) /* ep0 */ -- fotg210_ep0_queue(ep, req); -- else if (request && !ep->stall) -- fotg210_enable_fifo_int(ep); -- -- spin_unlock_irqrestore(&ep->fotg210->lock, flags); -- -- return 0; --} -- --static int fotg210_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req) --{ -- struct fotg210_ep *ep; -- struct fotg210_request *req; -- unsigned long flags; -- -- ep = container_of(_ep, struct fotg210_ep, ep); -- req = container_of(_req, struct fotg210_request, req); -- -- spin_lock_irqsave(&ep->fotg210->lock, flags); -- if (!list_empty(&ep->queue)) -- fotg210_done(ep, req, -ECONNRESET); -- spin_unlock_irqrestore(&ep->fotg210->lock, flags); -- -- return 0; --} -- --static void fotg210_set_epnstall(struct fotg210_ep *ep) --{ -- struct fotg210_udc *fotg210 = ep->fotg210; -- u32 value; -- void __iomem *reg; -- -- /* check if IN FIFO is empty before stall */ -- if (ep->dir_in) { -- do { -- value = ioread32(fotg210->reg + FOTG210_DCFESR); -- } while (!(value & DCFESR_FIFO_EMPTY(ep->epnum - 1))); -- } -- -- reg = (ep->dir_in) ? -- fotg210->reg + FOTG210_INEPMPSR(ep->epnum) : -- fotg210->reg + FOTG210_OUTEPMPSR(ep->epnum); -- value = ioread32(reg); -- value |= INOUTEPMPSR_STL_EP; -- iowrite32(value, reg); --} -- --static void fotg210_clear_epnstall(struct fotg210_ep *ep) --{ -- struct fotg210_udc *fotg210 = ep->fotg210; -- u32 value; -- void __iomem *reg; -- -- reg = (ep->dir_in) ? -- fotg210->reg + FOTG210_INEPMPSR(ep->epnum) : -- fotg210->reg + FOTG210_OUTEPMPSR(ep->epnum); -- value = ioread32(reg); -- value &= ~INOUTEPMPSR_STL_EP; -- iowrite32(value, reg); --} -- --static int fotg210_set_halt_and_wedge(struct usb_ep *_ep, int value, int wedge) --{ -- struct fotg210_ep *ep; -- struct fotg210_udc *fotg210; -- unsigned long flags; -- -- ep = container_of(_ep, struct fotg210_ep, ep); -- -- fotg210 = ep->fotg210; -- -- spin_lock_irqsave(&ep->fotg210->lock, flags); -- -- if (value) { -- fotg210_set_epnstall(ep); -- ep->stall = 1; -- if (wedge) -- ep->wedged = 1; -- } else { -- fotg210_reset_tseq(fotg210, ep->epnum); -- fotg210_clear_epnstall(ep); -- ep->stall = 0; -- ep->wedged = 0; -- if (!list_empty(&ep->queue)) -- fotg210_enable_fifo_int(ep); -- } -- -- spin_unlock_irqrestore(&ep->fotg210->lock, flags); -- return 0; --} -- --static int fotg210_ep_set_halt(struct usb_ep *_ep, int value) --{ -- return fotg210_set_halt_and_wedge(_ep, value, 0); --} -- --static int fotg210_ep_set_wedge(struct usb_ep *_ep) --{ -- return fotg210_set_halt_and_wedge(_ep, 1, 1); --} -- --static void fotg210_ep_fifo_flush(struct usb_ep *_ep) --{ --} -- --static const struct usb_ep_ops fotg210_ep_ops = { -- .enable = fotg210_ep_enable, -- .disable = fotg210_ep_disable, -- -- .alloc_request = fotg210_ep_alloc_request, -- .free_request = fotg210_ep_free_request, -- -- .queue = fotg210_ep_queue, -- .dequeue = fotg210_ep_dequeue, -- -- .set_halt = fotg210_ep_set_halt, -- .fifo_flush = fotg210_ep_fifo_flush, -- .set_wedge = fotg210_ep_set_wedge, --}; -- --static void fotg210_clear_tx0byte(struct fotg210_udc *fotg210) --{ -- u32 value = ioread32(fotg210->reg + FOTG210_TX0BYTE); -- -- value &= ~(TX0BYTE_EP1 | TX0BYTE_EP2 | TX0BYTE_EP3 -- | TX0BYTE_EP4); -- iowrite32(value, fotg210->reg + FOTG210_TX0BYTE); --} -- --static void fotg210_clear_rx0byte(struct fotg210_udc *fotg210) --{ -- u32 value = ioread32(fotg210->reg + FOTG210_RX0BYTE); -- -- value &= ~(RX0BYTE_EP1 | RX0BYTE_EP2 | RX0BYTE_EP3 -- | RX0BYTE_EP4); -- iowrite32(value, fotg210->reg + FOTG210_RX0BYTE); --} -- --/* read 8-byte setup packet only */ --static void fotg210_rdsetupp(struct fotg210_udc *fotg210, -- u8 *buffer) --{ -- int i = 0; -- u8 *tmp = buffer; -- u32 data; -- u32 length = 8; -- -- iowrite32(DMATFNR_ACC_CXF, fotg210->reg + FOTG210_DMATFNR); -- -- for (i = (length >> 2); i > 0; i--) { -- data = ioread32(fotg210->reg + FOTG210_CXPORT); -- *tmp = data & 0xFF; -- *(tmp + 1) = (data >> 8) & 0xFF; -- *(tmp + 2) = (data >> 16) & 0xFF; -- *(tmp + 3) = (data >> 24) & 0xFF; -- tmp = tmp + 4; -- } -- -- switch (length % 4) { -- case 1: -- data = ioread32(fotg210->reg + FOTG210_CXPORT); -- *tmp = data & 0xFF; -- break; -- case 2: -- data = ioread32(fotg210->reg + FOTG210_CXPORT); -- *tmp = data & 0xFF; -- *(tmp + 1) = (data >> 8) & 0xFF; -- break; -- case 3: -- data = ioread32(fotg210->reg + FOTG210_CXPORT); -- *tmp = data & 0xFF; -- *(tmp + 1) = (data >> 8) & 0xFF; -- *(tmp + 2) = (data >> 16) & 0xFF; -- break; -- default: -- break; -- } -- -- iowrite32(DMATFNR_DISDMA, fotg210->reg + FOTG210_DMATFNR); --} -- --static void fotg210_set_configuration(struct fotg210_udc *fotg210) --{ -- u32 value = ioread32(fotg210->reg + FOTG210_DAR); -- -- value |= DAR_AFT_CONF; -- iowrite32(value, fotg210->reg + FOTG210_DAR); --} -- --static void fotg210_set_dev_addr(struct fotg210_udc *fotg210, u32 addr) --{ -- u32 value = ioread32(fotg210->reg + FOTG210_DAR); -- -- value |= (addr & 0x7F); -- iowrite32(value, fotg210->reg + FOTG210_DAR); --} -- --static void fotg210_set_cxstall(struct fotg210_udc *fotg210) --{ -- u32 value = ioread32(fotg210->reg + FOTG210_DCFESR); -- -- value |= DCFESR_CX_STL; -- iowrite32(value, fotg210->reg + FOTG210_DCFESR); --} -- --static void fotg210_request_error(struct fotg210_udc *fotg210) --{ -- fotg210_set_cxstall(fotg210); -- pr_err("request error!!\n"); --} -- --static void fotg210_set_address(struct fotg210_udc *fotg210, -- struct usb_ctrlrequest *ctrl) --{ -- if (le16_to_cpu(ctrl->wValue) >= 0x0100) { -- fotg210_request_error(fotg210); -- } else { -- fotg210_set_dev_addr(fotg210, le16_to_cpu(ctrl->wValue)); -- fotg210_set_cxdone(fotg210); -- } --} -- --static void fotg210_set_feature(struct fotg210_udc *fotg210, -- struct usb_ctrlrequest *ctrl) --{ -- switch (ctrl->bRequestType & USB_RECIP_MASK) { -- case USB_RECIP_DEVICE: -- fotg210_set_cxdone(fotg210); -- break; -- case USB_RECIP_INTERFACE: -- fotg210_set_cxdone(fotg210); -- break; -- case USB_RECIP_ENDPOINT: { -- u8 epnum; -- epnum = le16_to_cpu(ctrl->wIndex) & USB_ENDPOINT_NUMBER_MASK; -- if (epnum) -- fotg210_set_epnstall(fotg210->ep[epnum]); -- else -- fotg210_set_cxstall(fotg210); -- fotg210_set_cxdone(fotg210); -- } -- break; -- default: -- fotg210_request_error(fotg210); -- break; -- } --} -- --static void fotg210_clear_feature(struct fotg210_udc *fotg210, -- struct usb_ctrlrequest *ctrl) --{ -- struct fotg210_ep *ep = -- fotg210->ep[ctrl->wIndex & USB_ENDPOINT_NUMBER_MASK]; -- -- switch (ctrl->bRequestType & USB_RECIP_MASK) { -- case USB_RECIP_DEVICE: -- fotg210_set_cxdone(fotg210); -- break; -- case USB_RECIP_INTERFACE: -- fotg210_set_cxdone(fotg210); -- break; -- case USB_RECIP_ENDPOINT: -- if (ctrl->wIndex & USB_ENDPOINT_NUMBER_MASK) { -- if (ep->wedged) { -- fotg210_set_cxdone(fotg210); -- break; -- } -- if (ep->stall) -- fotg210_set_halt_and_wedge(&ep->ep, 0, 0); -- } -- fotg210_set_cxdone(fotg210); -- break; -- default: -- fotg210_request_error(fotg210); -- break; -- } --} -- --static int fotg210_is_epnstall(struct fotg210_ep *ep) --{ -- struct fotg210_udc *fotg210 = ep->fotg210; -- u32 value; -- void __iomem *reg; -- -- reg = (ep->dir_in) ? -- fotg210->reg + FOTG210_INEPMPSR(ep->epnum) : -- fotg210->reg + FOTG210_OUTEPMPSR(ep->epnum); -- value = ioread32(reg); -- return value & INOUTEPMPSR_STL_EP ? 1 : 0; --} -- --/* For EP0 requests triggered by this driver (currently GET_STATUS response) */ --static void fotg210_ep0_complete(struct usb_ep *_ep, struct usb_request *req) --{ -- struct fotg210_ep *ep; -- struct fotg210_udc *fotg210; -- -- ep = container_of(_ep, struct fotg210_ep, ep); -- fotg210 = ep->fotg210; -- -- if (req->status || req->actual != req->length) { -- dev_warn(&fotg210->gadget.dev, "EP0 request failed: %d\n", req->status); -- } --} -- --static void fotg210_get_status(struct fotg210_udc *fotg210, -- struct usb_ctrlrequest *ctrl) --{ -- u8 epnum; -- -- switch (ctrl->bRequestType & USB_RECIP_MASK) { -- case USB_RECIP_DEVICE: -- fotg210->ep0_data = cpu_to_le16(1 << USB_DEVICE_SELF_POWERED); -- break; -- case USB_RECIP_INTERFACE: -- fotg210->ep0_data = cpu_to_le16(0); -- break; -- case USB_RECIP_ENDPOINT: -- epnum = ctrl->wIndex & USB_ENDPOINT_NUMBER_MASK; -- if (epnum) -- fotg210->ep0_data = -- cpu_to_le16(fotg210_is_epnstall(fotg210->ep[epnum]) -- << USB_ENDPOINT_HALT); -- else -- fotg210_request_error(fotg210); -- break; -- -- default: -- fotg210_request_error(fotg210); -- return; /* exit */ -- } -- -- fotg210->ep0_req->buf = &fotg210->ep0_data; -- fotg210->ep0_req->length = 2; -- -- spin_unlock(&fotg210->lock); -- fotg210_ep_queue(fotg210->gadget.ep0, fotg210->ep0_req, GFP_ATOMIC); -- spin_lock(&fotg210->lock); --} -- --static int fotg210_setup_packet(struct fotg210_udc *fotg210, -- struct usb_ctrlrequest *ctrl) --{ -- u8 *p = (u8 *)ctrl; -- u8 ret = 0; -- -- fotg210_rdsetupp(fotg210, p); -- -- fotg210->ep[0]->dir_in = ctrl->bRequestType & USB_DIR_IN; -- -- if (fotg210->gadget.speed == USB_SPEED_UNKNOWN) { -- u32 value = ioread32(fotg210->reg + FOTG210_DMCR); -- fotg210->gadget.speed = value & DMCR_HS_EN ? -- USB_SPEED_HIGH : USB_SPEED_FULL; -- } -- -- /* check request */ -- if ((ctrl->bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD) { -- switch (ctrl->bRequest) { -- case USB_REQ_GET_STATUS: -- fotg210_get_status(fotg210, ctrl); -- break; -- case USB_REQ_CLEAR_FEATURE: -- fotg210_clear_feature(fotg210, ctrl); -- break; -- case USB_REQ_SET_FEATURE: -- fotg210_set_feature(fotg210, ctrl); -- break; -- case USB_REQ_SET_ADDRESS: -- fotg210_set_address(fotg210, ctrl); -- break; -- case USB_REQ_SET_CONFIGURATION: -- fotg210_set_configuration(fotg210); -- ret = 1; -- break; -- default: -- ret = 1; -- break; -- } -- } else { -- ret = 1; -- } -- -- return ret; --} -- --static void fotg210_ep0out(struct fotg210_udc *fotg210) --{ -- struct fotg210_ep *ep = fotg210->ep[0]; -- -- if (!list_empty(&ep->queue) && !ep->dir_in) { -- struct fotg210_request *req; -- -- req = list_first_entry(&ep->queue, -- struct fotg210_request, queue); -- -- if (req->req.length) -- fotg210_start_dma(ep, req); -- -- if ((req->req.length - req->req.actual) < ep->ep.maxpacket) -- fotg210_done(ep, req, 0); -- } else { -- pr_err("%s : empty queue\n", __func__); -- } --} -- --static void fotg210_ep0in(struct fotg210_udc *fotg210) --{ -- struct fotg210_ep *ep = fotg210->ep[0]; -- -- if ((!list_empty(&ep->queue)) && (ep->dir_in)) { -- struct fotg210_request *req; -- -- req = list_entry(ep->queue.next, -- struct fotg210_request, queue); -- -- if (req->req.length) -- fotg210_start_dma(ep, req); -- -- if (req->req.actual == req->req.length) -- fotg210_done(ep, req, 0); -- } else { -- fotg210_set_cxdone(fotg210); -- } --} -- --static void fotg210_clear_comabt_int(struct fotg210_udc *fotg210) --{ -- u32 value = ioread32(fotg210->reg + FOTG210_DISGR0); -- -- value &= ~DISGR0_CX_COMABT_INT; -- iowrite32(value, fotg210->reg + FOTG210_DISGR0); --} -- --static void fotg210_in_fifo_handler(struct fotg210_ep *ep) --{ -- struct fotg210_request *req = list_entry(ep->queue.next, -- struct fotg210_request, queue); -- -- if (req->req.length) -- fotg210_start_dma(ep, req); -- fotg210_done(ep, req, 0); --} -- --static void fotg210_out_fifo_handler(struct fotg210_ep *ep) --{ -- struct fotg210_request *req = list_entry(ep->queue.next, -- struct fotg210_request, queue); -- int disgr1 = ioread32(ep->fotg210->reg + FOTG210_DISGR1); -- -- fotg210_start_dma(ep, req); -- -- /* Complete the request when it's full or a short packet arrived. -- * Like other drivers, short_not_ok isn't handled. -- */ -- -- if (req->req.length == req->req.actual || -- (disgr1 & DISGR1_SPK_INT(ep->epnum - 1))) -- fotg210_done(ep, req, 0); --} -- --static irqreturn_t fotg210_irq(int irq, void *_fotg210) --{ -- struct fotg210_udc *fotg210 = _fotg210; -- u32 int_grp = ioread32(fotg210->reg + FOTG210_DIGR); -- u32 int_msk = ioread32(fotg210->reg + FOTG210_DMIGR); -- -- int_grp &= ~int_msk; -- -- spin_lock(&fotg210->lock); -- -- if (int_grp & DIGR_INT_G2) { -- void __iomem *reg = fotg210->reg + FOTG210_DISGR2; -- u32 int_grp2 = ioread32(reg); -- u32 int_msk2 = ioread32(fotg210->reg + FOTG210_DMISGR2); -- u32 value; -- -- int_grp2 &= ~int_msk2; -- -- if (int_grp2 & DISGR2_USBRST_INT) { -- usb_gadget_udc_reset(&fotg210->gadget, -- fotg210->driver); -- value = ioread32(reg); -- value &= ~DISGR2_USBRST_INT; -- iowrite32(value, reg); -- pr_info("fotg210 udc reset\n"); -- } -- if (int_grp2 & DISGR2_SUSP_INT) { -- value = ioread32(reg); -- value &= ~DISGR2_SUSP_INT; -- iowrite32(value, reg); -- pr_info("fotg210 udc suspend\n"); -- } -- if (int_grp2 & DISGR2_RESM_INT) { -- value = ioread32(reg); -- value &= ~DISGR2_RESM_INT; -- iowrite32(value, reg); -- pr_info("fotg210 udc resume\n"); -- } -- if (int_grp2 & DISGR2_ISO_SEQ_ERR_INT) { -- value = ioread32(reg); -- value &= ~DISGR2_ISO_SEQ_ERR_INT; -- iowrite32(value, reg); -- pr_info("fotg210 iso sequence error\n"); -- } -- if (int_grp2 & DISGR2_ISO_SEQ_ABORT_INT) { -- value = ioread32(reg); -- value &= ~DISGR2_ISO_SEQ_ABORT_INT; -- iowrite32(value, reg); -- pr_info("fotg210 iso sequence abort\n"); -- } -- if (int_grp2 & DISGR2_TX0BYTE_INT) { -- fotg210_clear_tx0byte(fotg210); -- value = ioread32(reg); -- value &= ~DISGR2_TX0BYTE_INT; -- iowrite32(value, reg); -- pr_info("fotg210 transferred 0 byte\n"); -- } -- if (int_grp2 & DISGR2_RX0BYTE_INT) { -- fotg210_clear_rx0byte(fotg210); -- value = ioread32(reg); -- value &= ~DISGR2_RX0BYTE_INT; -- iowrite32(value, reg); -- pr_info("fotg210 received 0 byte\n"); -- } -- if (int_grp2 & DISGR2_DMA_ERROR) { -- value = ioread32(reg); -- value &= ~DISGR2_DMA_ERROR; -- iowrite32(value, reg); -- } -- } -- -- if (int_grp & DIGR_INT_G0) { -- void __iomem *reg = fotg210->reg + FOTG210_DISGR0; -- u32 int_grp0 = ioread32(reg); -- u32 int_msk0 = ioread32(fotg210->reg + FOTG210_DMISGR0); -- struct usb_ctrlrequest ctrl; -- -- int_grp0 &= ~int_msk0; -- -- /* the highest priority in this source register */ -- if (int_grp0 & DISGR0_CX_COMABT_INT) { -- fotg210_clear_comabt_int(fotg210); -- pr_info("fotg210 CX command abort\n"); -- } -- -- if (int_grp0 & DISGR0_CX_SETUP_INT) { -- if (fotg210_setup_packet(fotg210, &ctrl)) { -- spin_unlock(&fotg210->lock); -- if (fotg210->driver->setup(&fotg210->gadget, -- &ctrl) < 0) -- fotg210_set_cxstall(fotg210); -- spin_lock(&fotg210->lock); -- } -- } -- if (int_grp0 & DISGR0_CX_COMEND_INT) -- pr_info("fotg210 cmd end\n"); -- -- if (int_grp0 & DISGR0_CX_IN_INT) -- fotg210_ep0in(fotg210); -- -- if (int_grp0 & DISGR0_CX_OUT_INT) -- fotg210_ep0out(fotg210); -- -- if (int_grp0 & DISGR0_CX_COMFAIL_INT) { -- fotg210_set_cxstall(fotg210); -- pr_info("fotg210 ep0 fail\n"); -- } -- } -- -- if (int_grp & DIGR_INT_G1) { -- void __iomem *reg = fotg210->reg + FOTG210_DISGR1; -- u32 int_grp1 = ioread32(reg); -- u32 int_msk1 = ioread32(fotg210->reg + FOTG210_DMISGR1); -- int fifo; -- -- int_grp1 &= ~int_msk1; -- -- for (fifo = 0; fifo < FOTG210_MAX_FIFO_NUM; fifo++) { -- if (int_grp1 & DISGR1_IN_INT(fifo)) -- fotg210_in_fifo_handler(fotg210->ep[fifo + 1]); -- -- if ((int_grp1 & DISGR1_OUT_INT(fifo)) || -- (int_grp1 & DISGR1_SPK_INT(fifo))) -- fotg210_out_fifo_handler(fotg210->ep[fifo + 1]); -- } -- } -- -- spin_unlock(&fotg210->lock); -- -- return IRQ_HANDLED; --} -- --static void fotg210_disable_unplug(struct fotg210_udc *fotg210) --{ -- u32 reg = ioread32(fotg210->reg + FOTG210_PHYTMSR); -- -- reg &= ~PHYTMSR_UNPLUG; -- iowrite32(reg, fotg210->reg + FOTG210_PHYTMSR); --} -- --static int fotg210_udc_start(struct usb_gadget *g, -- struct usb_gadget_driver *driver) --{ -- struct fotg210_udc *fotg210 = gadget_to_fotg210(g); -- u32 value; -- -- /* hook up the driver */ -- fotg210->driver = driver; -- -- /* enable device global interrupt */ -- value = ioread32(fotg210->reg + FOTG210_DMCR); -- value |= DMCR_GLINT_EN; -- iowrite32(value, fotg210->reg + FOTG210_DMCR); -- -- return 0; --} -- --static void fotg210_init(struct fotg210_udc *fotg210) --{ -- u32 value; -- -- /* disable global interrupt and set int polarity to active high */ -- iowrite32(GMIR_MHC_INT | GMIR_MOTG_INT | GMIR_INT_POLARITY, -- fotg210->reg + FOTG210_GMIR); -- -- /* disable device global interrupt */ -- value = ioread32(fotg210->reg + FOTG210_DMCR); -- value &= ~DMCR_GLINT_EN; -- iowrite32(value, fotg210->reg + FOTG210_DMCR); -- -- /* enable only grp2 irqs we handle */ -- iowrite32(~(DISGR2_DMA_ERROR | DISGR2_RX0BYTE_INT | DISGR2_TX0BYTE_INT -- | DISGR2_ISO_SEQ_ABORT_INT | DISGR2_ISO_SEQ_ERR_INT -- | DISGR2_RESM_INT | DISGR2_SUSP_INT | DISGR2_USBRST_INT), -- fotg210->reg + FOTG210_DMISGR2); -- -- /* disable all fifo interrupt */ -- iowrite32(~(u32)0, fotg210->reg + FOTG210_DMISGR1); -- -- /* disable cmd end */ -- value = ioread32(fotg210->reg + FOTG210_DMISGR0); -- value |= DMISGR0_MCX_COMEND; -- iowrite32(value, fotg210->reg + FOTG210_DMISGR0); --} -- --static int fotg210_udc_stop(struct usb_gadget *g) --{ -- struct fotg210_udc *fotg210 = gadget_to_fotg210(g); -- unsigned long flags; -- -- spin_lock_irqsave(&fotg210->lock, flags); -- -- fotg210_init(fotg210); -- fotg210->driver = NULL; -- -- spin_unlock_irqrestore(&fotg210->lock, flags); -- -- return 0; --} -- --static const struct usb_gadget_ops fotg210_gadget_ops = { -- .udc_start = fotg210_udc_start, -- .udc_stop = fotg210_udc_stop, --}; -- --static int fotg210_udc_remove(struct platform_device *pdev) --{ -- struct fotg210_udc *fotg210 = platform_get_drvdata(pdev); -- int i; -- -- usb_del_gadget_udc(&fotg210->gadget); -- iounmap(fotg210->reg); -- free_irq(platform_get_irq(pdev, 0), fotg210); -- -- fotg210_ep_free_request(&fotg210->ep[0]->ep, fotg210->ep0_req); -- for (i = 0; i < FOTG210_MAX_NUM_EP; i++) -- kfree(fotg210->ep[i]); -- kfree(fotg210); -- -- return 0; --} -- --static int fotg210_udc_probe(struct platform_device *pdev) --{ -- struct resource *res, *ires; -- struct fotg210_udc *fotg210 = NULL; -- struct fotg210_ep *_ep[FOTG210_MAX_NUM_EP]; -- int ret = 0; -- int i; -- -- res = platform_get_resource(pdev, IORESOURCE_MEM, 0); -- if (!res) { -- pr_err("platform_get_resource error.\n"); -- return -ENODEV; -- } -- -- ires = platform_get_resource(pdev, IORESOURCE_IRQ, 0); -- if (!ires) { -- pr_err("platform_get_resource IORESOURCE_IRQ error.\n"); -- return -ENODEV; -- } -- -- ret = -ENOMEM; -- -- /* initialize udc */ -- fotg210 = kzalloc(sizeof(struct fotg210_udc), GFP_KERNEL); -- if (fotg210 == NULL) -- goto err; -- -- for (i = 0; i < FOTG210_MAX_NUM_EP; i++) { -- _ep[i] = kzalloc(sizeof(struct fotg210_ep), GFP_KERNEL); -- if (_ep[i] == NULL) -- goto err_alloc; -- fotg210->ep[i] = _ep[i]; -- } -- -- fotg210->reg = ioremap(res->start, resource_size(res)); -- if (fotg210->reg == NULL) { -- pr_err("ioremap error.\n"); -- goto err_alloc; -- } -- -- spin_lock_init(&fotg210->lock); -- -- platform_set_drvdata(pdev, fotg210); -- -- fotg210->gadget.ops = &fotg210_gadget_ops; -- -- fotg210->gadget.max_speed = USB_SPEED_HIGH; -- fotg210->gadget.dev.parent = &pdev->dev; -- fotg210->gadget.dev.dma_mask = pdev->dev.dma_mask; -- fotg210->gadget.name = udc_name; -- -- INIT_LIST_HEAD(&fotg210->gadget.ep_list); -- -- for (i = 0; i < FOTG210_MAX_NUM_EP; i++) { -- struct fotg210_ep *ep = fotg210->ep[i]; -- -- if (i) { -- INIT_LIST_HEAD(&fotg210->ep[i]->ep.ep_list); -- list_add_tail(&fotg210->ep[i]->ep.ep_list, -- &fotg210->gadget.ep_list); -- } -- ep->fotg210 = fotg210; -- INIT_LIST_HEAD(&ep->queue); -- ep->ep.name = fotg210_ep_name[i]; -- ep->ep.ops = &fotg210_ep_ops; -- usb_ep_set_maxpacket_limit(&ep->ep, (unsigned short) ~0); -- -- if (i == 0) { -- ep->ep.caps.type_control = true; -- } else { -- ep->ep.caps.type_iso = true; -- ep->ep.caps.type_bulk = true; -- ep->ep.caps.type_int = true; -- } -- -- ep->ep.caps.dir_in = true; -- ep->ep.caps.dir_out = true; -- } -- usb_ep_set_maxpacket_limit(&fotg210->ep[0]->ep, 0x40); -- fotg210->gadget.ep0 = &fotg210->ep[0]->ep; -- INIT_LIST_HEAD(&fotg210->gadget.ep0->ep_list); -- -- fotg210->ep0_req = fotg210_ep_alloc_request(&fotg210->ep[0]->ep, -- GFP_KERNEL); -- if (fotg210->ep0_req == NULL) -- goto err_map; -- -- fotg210->ep0_req->complete = fotg210_ep0_complete; -- -- fotg210_init(fotg210); -- -- fotg210_disable_unplug(fotg210); -- -- ret = request_irq(ires->start, fotg210_irq, IRQF_SHARED, -- udc_name, fotg210); -- if (ret < 0) { -- pr_err("request_irq error (%d)\n", ret); -- goto err_req; -- } -- -- ret = usb_add_gadget_udc(&pdev->dev, &fotg210->gadget); -- if (ret) -- goto err_add_udc; -- -- dev_info(&pdev->dev, "version %s\n", DRIVER_VERSION); -- -- return 0; -- --err_add_udc: -- free_irq(ires->start, fotg210); -- --err_req: -- fotg210_ep_free_request(&fotg210->ep[0]->ep, fotg210->ep0_req); -- --err_map: -- iounmap(fotg210->reg); -- --err_alloc: -- for (i = 0; i < FOTG210_MAX_NUM_EP; i++) -- kfree(fotg210->ep[i]); -- kfree(fotg210); -- --err: -- return ret; --} -- --static struct platform_driver fotg210_driver = { -- .driver = { -- .name = udc_name, -- }, -- .probe = fotg210_udc_probe, -- .remove = fotg210_udc_remove, --}; -- --module_platform_driver(fotg210_driver); -- --MODULE_AUTHOR("Yuan-Hsin Chen, Feng-Hsin Chiang "); --MODULE_LICENSE("GPL"); --MODULE_DESCRIPTION(DRIVER_DESC); ---- /dev/null -+++ b/drivers/usb/fotg210/fotg210-udc.c -@@ -0,0 +1,1239 @@ -+// SPDX-License-Identifier: GPL-2.0 -+/* -+ * FOTG210 UDC Driver supports Bulk transfer so far -+ * -+ * Copyright (C) 2013 Faraday Technology Corporation -+ * -+ * Author : Yuan-Hsin Chen -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "fotg210-udc.h" -+ -+#define DRIVER_DESC "FOTG210 USB Device Controller Driver" -+#define DRIVER_VERSION "30-April-2013" -+ -+static const char udc_name[] = "fotg210_udc"; -+static const char * const fotg210_ep_name[] = { -+ "ep0", "ep1", "ep2", "ep3", "ep4"}; -+ -+static void fotg210_disable_fifo_int(struct fotg210_ep *ep) -+{ -+ u32 value = ioread32(ep->fotg210->reg + FOTG210_DMISGR1); -+ -+ if (ep->dir_in) -+ value |= DMISGR1_MF_IN_INT(ep->epnum - 1); -+ else -+ value |= DMISGR1_MF_OUTSPK_INT(ep->epnum - 1); -+ iowrite32(value, ep->fotg210->reg + FOTG210_DMISGR1); -+} -+ -+static void fotg210_enable_fifo_int(struct fotg210_ep *ep) -+{ -+ u32 value = ioread32(ep->fotg210->reg + FOTG210_DMISGR1); -+ -+ if (ep->dir_in) -+ value &= ~DMISGR1_MF_IN_INT(ep->epnum - 1); -+ else -+ value &= ~DMISGR1_MF_OUTSPK_INT(ep->epnum - 1); -+ iowrite32(value, ep->fotg210->reg + FOTG210_DMISGR1); -+} -+ -+static void fotg210_set_cxdone(struct fotg210_udc *fotg210) -+{ -+ u32 value = ioread32(fotg210->reg + FOTG210_DCFESR); -+ -+ value |= DCFESR_CX_DONE; -+ iowrite32(value, fotg210->reg + FOTG210_DCFESR); -+} -+ -+static void fotg210_done(struct fotg210_ep *ep, struct fotg210_request *req, -+ int status) -+{ -+ list_del_init(&req->queue); -+ -+ /* don't modify queue heads during completion callback */ -+ if (ep->fotg210->gadget.speed == USB_SPEED_UNKNOWN) -+ req->req.status = -ESHUTDOWN; -+ else -+ req->req.status = status; -+ -+ spin_unlock(&ep->fotg210->lock); -+ usb_gadget_giveback_request(&ep->ep, &req->req); -+ spin_lock(&ep->fotg210->lock); -+ -+ if (ep->epnum) { -+ if (list_empty(&ep->queue)) -+ fotg210_disable_fifo_int(ep); -+ } else { -+ fotg210_set_cxdone(ep->fotg210); -+ } -+} -+ -+static void fotg210_fifo_ep_mapping(struct fotg210_ep *ep, u32 epnum, -+ u32 dir_in) -+{ -+ struct fotg210_udc *fotg210 = ep->fotg210; -+ u32 val; -+ -+ /* Driver should map an ep to a fifo and then map the fifo -+ * to the ep. What a brain-damaged design! -+ */ -+ -+ /* map a fifo to an ep */ -+ val = ioread32(fotg210->reg + FOTG210_EPMAP); -+ val &= ~EPMAP_FIFONOMSK(epnum, dir_in); -+ val |= EPMAP_FIFONO(epnum, dir_in); -+ iowrite32(val, fotg210->reg + FOTG210_EPMAP); -+ -+ /* map the ep to the fifo */ -+ val = ioread32(fotg210->reg + FOTG210_FIFOMAP); -+ val &= ~FIFOMAP_EPNOMSK(epnum); -+ val |= FIFOMAP_EPNO(epnum); -+ iowrite32(val, fotg210->reg + FOTG210_FIFOMAP); -+ -+ /* enable fifo */ -+ val = ioread32(fotg210->reg + FOTG210_FIFOCF); -+ val |= FIFOCF_FIFO_EN(epnum - 1); -+ iowrite32(val, fotg210->reg + FOTG210_FIFOCF); -+} -+ -+static void fotg210_set_fifo_dir(struct fotg210_ep *ep, u32 epnum, u32 dir_in) -+{ -+ struct fotg210_udc *fotg210 = ep->fotg210; -+ u32 val; -+ -+ val = ioread32(fotg210->reg + FOTG210_FIFOMAP); -+ val |= (dir_in ? FIFOMAP_DIRIN(epnum - 1) : FIFOMAP_DIROUT(epnum - 1)); -+ iowrite32(val, fotg210->reg + FOTG210_FIFOMAP); -+} -+ -+static void fotg210_set_tfrtype(struct fotg210_ep *ep, u32 epnum, u32 type) -+{ -+ struct fotg210_udc *fotg210 = ep->fotg210; -+ u32 val; -+ -+ val = ioread32(fotg210->reg + FOTG210_FIFOCF); -+ val |= FIFOCF_TYPE(type, epnum - 1); -+ iowrite32(val, fotg210->reg + FOTG210_FIFOCF); -+} -+ -+static void fotg210_set_mps(struct fotg210_ep *ep, u32 epnum, u32 mps, -+ u32 dir_in) -+{ -+ struct fotg210_udc *fotg210 = ep->fotg210; -+ u32 val; -+ u32 offset = dir_in ? FOTG210_INEPMPSR(epnum) : -+ FOTG210_OUTEPMPSR(epnum); -+ -+ val = ioread32(fotg210->reg + offset); -+ val |= INOUTEPMPSR_MPS(mps); -+ iowrite32(val, fotg210->reg + offset); -+} -+ -+static int fotg210_config_ep(struct fotg210_ep *ep, -+ const struct usb_endpoint_descriptor *desc) -+{ -+ struct fotg210_udc *fotg210 = ep->fotg210; -+ -+ fotg210_set_fifo_dir(ep, ep->epnum, ep->dir_in); -+ fotg210_set_tfrtype(ep, ep->epnum, ep->type); -+ fotg210_set_mps(ep, ep->epnum, ep->ep.maxpacket, ep->dir_in); -+ fotg210_fifo_ep_mapping(ep, ep->epnum, ep->dir_in); -+ -+ fotg210->ep[ep->epnum] = ep; -+ -+ return 0; -+} -+ -+static int fotg210_ep_enable(struct usb_ep *_ep, -+ const struct usb_endpoint_descriptor *desc) -+{ -+ struct fotg210_ep *ep; -+ -+ ep = container_of(_ep, struct fotg210_ep, ep); -+ -+ ep->desc = desc; -+ ep->epnum = usb_endpoint_num(desc); -+ ep->type = usb_endpoint_type(desc); -+ ep->dir_in = usb_endpoint_dir_in(desc); -+ ep->ep.maxpacket = usb_endpoint_maxp(desc); -+ -+ return fotg210_config_ep(ep, desc); -+} -+ -+static void fotg210_reset_tseq(struct fotg210_udc *fotg210, u8 epnum) -+{ -+ struct fotg210_ep *ep = fotg210->ep[epnum]; -+ u32 value; -+ void __iomem *reg; -+ -+ reg = (ep->dir_in) ? -+ fotg210->reg + FOTG210_INEPMPSR(epnum) : -+ fotg210->reg + FOTG210_OUTEPMPSR(epnum); -+ -+ /* Note: Driver needs to set and clear INOUTEPMPSR_RESET_TSEQ -+ * bit. Controller wouldn't clear this bit. WTF!!! -+ */ -+ -+ value = ioread32(reg); -+ value |= INOUTEPMPSR_RESET_TSEQ; -+ iowrite32(value, reg); -+ -+ value = ioread32(reg); -+ value &= ~INOUTEPMPSR_RESET_TSEQ; -+ iowrite32(value, reg); -+} -+ -+static int fotg210_ep_release(struct fotg210_ep *ep) -+{ -+ if (!ep->epnum) -+ return 0; -+ ep->epnum = 0; -+ ep->stall = 0; -+ ep->wedged = 0; -+ -+ fotg210_reset_tseq(ep->fotg210, ep->epnum); -+ -+ return 0; -+} -+ -+static int fotg210_ep_disable(struct usb_ep *_ep) -+{ -+ struct fotg210_ep *ep; -+ struct fotg210_request *req; -+ unsigned long flags; -+ -+ BUG_ON(!_ep); -+ -+ ep = container_of(_ep, struct fotg210_ep, ep); -+ -+ while (!list_empty(&ep->queue)) { -+ req = list_entry(ep->queue.next, -+ struct fotg210_request, queue); -+ spin_lock_irqsave(&ep->fotg210->lock, flags); -+ fotg210_done(ep, req, -ECONNRESET); -+ spin_unlock_irqrestore(&ep->fotg210->lock, flags); -+ } -+ -+ return fotg210_ep_release(ep); -+} -+ -+static struct usb_request *fotg210_ep_alloc_request(struct usb_ep *_ep, -+ gfp_t gfp_flags) -+{ -+ struct fotg210_request *req; -+ -+ req = kzalloc(sizeof(struct fotg210_request), gfp_flags); -+ if (!req) -+ return NULL; -+ -+ INIT_LIST_HEAD(&req->queue); -+ -+ return &req->req; -+} -+ -+static void fotg210_ep_free_request(struct usb_ep *_ep, -+ struct usb_request *_req) -+{ -+ struct fotg210_request *req; -+ -+ req = container_of(_req, struct fotg210_request, req); -+ kfree(req); -+} -+ -+static void fotg210_enable_dma(struct fotg210_ep *ep, -+ dma_addr_t d, u32 len) -+{ -+ u32 value; -+ struct fotg210_udc *fotg210 = ep->fotg210; -+ -+ /* set transfer length and direction */ -+ value = ioread32(fotg210->reg + FOTG210_DMACPSR1); -+ value &= ~(DMACPSR1_DMA_LEN(0xFFFF) | DMACPSR1_DMA_TYPE(1)); -+ value |= DMACPSR1_DMA_LEN(len) | DMACPSR1_DMA_TYPE(ep->dir_in); -+ iowrite32(value, fotg210->reg + FOTG210_DMACPSR1); -+ -+ /* set device DMA target FIFO number */ -+ value = ioread32(fotg210->reg + FOTG210_DMATFNR); -+ if (ep->epnum) -+ value |= DMATFNR_ACC_FN(ep->epnum - 1); -+ else -+ value |= DMATFNR_ACC_CXF; -+ iowrite32(value, fotg210->reg + FOTG210_DMATFNR); -+ -+ /* set DMA memory address */ -+ iowrite32(d, fotg210->reg + FOTG210_DMACPSR2); -+ -+ /* enable MDMA_EROR and MDMA_CMPLT interrupt */ -+ value = ioread32(fotg210->reg + FOTG210_DMISGR2); -+ value &= ~(DMISGR2_MDMA_CMPLT | DMISGR2_MDMA_ERROR); -+ iowrite32(value, fotg210->reg + FOTG210_DMISGR2); -+ -+ /* start DMA */ -+ value = ioread32(fotg210->reg + FOTG210_DMACPSR1); -+ value |= DMACPSR1_DMA_START; -+ iowrite32(value, fotg210->reg + FOTG210_DMACPSR1); -+} -+ -+static void fotg210_disable_dma(struct fotg210_ep *ep) -+{ -+ iowrite32(DMATFNR_DISDMA, ep->fotg210->reg + FOTG210_DMATFNR); -+} -+ -+static void fotg210_wait_dma_done(struct fotg210_ep *ep) -+{ -+ u32 value; -+ -+ do { -+ value = ioread32(ep->fotg210->reg + FOTG210_DISGR2); -+ if ((value & DISGR2_USBRST_INT) || -+ (value & DISGR2_DMA_ERROR)) -+ goto dma_reset; -+ } while (!(value & DISGR2_DMA_CMPLT)); -+ -+ value &= ~DISGR2_DMA_CMPLT; -+ iowrite32(value, ep->fotg210->reg + FOTG210_DISGR2); -+ return; -+ -+dma_reset: -+ value = ioread32(ep->fotg210->reg + FOTG210_DMACPSR1); -+ value |= DMACPSR1_DMA_ABORT; -+ iowrite32(value, ep->fotg210->reg + FOTG210_DMACPSR1); -+ -+ /* reset fifo */ -+ if (ep->epnum) { -+ value = ioread32(ep->fotg210->reg + -+ FOTG210_FIBCR(ep->epnum - 1)); -+ value |= FIBCR_FFRST; -+ iowrite32(value, ep->fotg210->reg + -+ FOTG210_FIBCR(ep->epnum - 1)); -+ } else { -+ value = ioread32(ep->fotg210->reg + FOTG210_DCFESR); -+ value |= DCFESR_CX_CLR; -+ iowrite32(value, ep->fotg210->reg + FOTG210_DCFESR); -+ } -+} -+ -+static void fotg210_start_dma(struct fotg210_ep *ep, -+ struct fotg210_request *req) -+{ -+ struct device *dev = &ep->fotg210->gadget.dev; -+ dma_addr_t d; -+ u8 *buffer; -+ u32 length; -+ -+ if (ep->epnum) { -+ if (ep->dir_in) { -+ buffer = req->req.buf; -+ length = req->req.length; -+ } else { -+ buffer = req->req.buf + req->req.actual; -+ length = ioread32(ep->fotg210->reg + -+ FOTG210_FIBCR(ep->epnum - 1)) & FIBCR_BCFX; -+ if (length > req->req.length - req->req.actual) -+ length = req->req.length - req->req.actual; -+ } -+ } else { -+ buffer = req->req.buf + req->req.actual; -+ if (req->req.length - req->req.actual > ep->ep.maxpacket) -+ length = ep->ep.maxpacket; -+ else -+ length = req->req.length - req->req.actual; -+ } -+ -+ d = dma_map_single(dev, buffer, length, -+ ep->dir_in ? DMA_TO_DEVICE : DMA_FROM_DEVICE); -+ -+ if (dma_mapping_error(dev, d)) { -+ pr_err("dma_mapping_error\n"); -+ return; -+ } -+ -+ fotg210_enable_dma(ep, d, length); -+ -+ /* check if dma is done */ -+ fotg210_wait_dma_done(ep); -+ -+ fotg210_disable_dma(ep); -+ -+ /* update actual transfer length */ -+ req->req.actual += length; -+ -+ dma_unmap_single(dev, d, length, DMA_TO_DEVICE); -+} -+ -+static void fotg210_ep0_queue(struct fotg210_ep *ep, -+ struct fotg210_request *req) -+{ -+ if (!req->req.length) { -+ fotg210_done(ep, req, 0); -+ return; -+ } -+ if (ep->dir_in) { /* if IN */ -+ fotg210_start_dma(ep, req); -+ if (req->req.length == req->req.actual) -+ fotg210_done(ep, req, 0); -+ } else { /* OUT */ -+ u32 value = ioread32(ep->fotg210->reg + FOTG210_DMISGR0); -+ -+ value &= ~DMISGR0_MCX_OUT_INT; -+ iowrite32(value, ep->fotg210->reg + FOTG210_DMISGR0); -+ } -+} -+ -+static int fotg210_ep_queue(struct usb_ep *_ep, struct usb_request *_req, -+ gfp_t gfp_flags) -+{ -+ struct fotg210_ep *ep; -+ struct fotg210_request *req; -+ unsigned long flags; -+ int request = 0; -+ -+ ep = container_of(_ep, struct fotg210_ep, ep); -+ req = container_of(_req, struct fotg210_request, req); -+ -+ if (ep->fotg210->gadget.speed == USB_SPEED_UNKNOWN) -+ return -ESHUTDOWN; -+ -+ spin_lock_irqsave(&ep->fotg210->lock, flags); -+ -+ if (list_empty(&ep->queue)) -+ request = 1; -+ -+ list_add_tail(&req->queue, &ep->queue); -+ -+ req->req.actual = 0; -+ req->req.status = -EINPROGRESS; -+ -+ if (!ep->epnum) /* ep0 */ -+ fotg210_ep0_queue(ep, req); -+ else if (request && !ep->stall) -+ fotg210_enable_fifo_int(ep); -+ -+ spin_unlock_irqrestore(&ep->fotg210->lock, flags); -+ -+ return 0; -+} -+ -+static int fotg210_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req) -+{ -+ struct fotg210_ep *ep; -+ struct fotg210_request *req; -+ unsigned long flags; -+ -+ ep = container_of(_ep, struct fotg210_ep, ep); -+ req = container_of(_req, struct fotg210_request, req); -+ -+ spin_lock_irqsave(&ep->fotg210->lock, flags); -+ if (!list_empty(&ep->queue)) -+ fotg210_done(ep, req, -ECONNRESET); -+ spin_unlock_irqrestore(&ep->fotg210->lock, flags); -+ -+ return 0; -+} -+ -+static void fotg210_set_epnstall(struct fotg210_ep *ep) -+{ -+ struct fotg210_udc *fotg210 = ep->fotg210; -+ u32 value; -+ void __iomem *reg; -+ -+ /* check if IN FIFO is empty before stall */ -+ if (ep->dir_in) { -+ do { -+ value = ioread32(fotg210->reg + FOTG210_DCFESR); -+ } while (!(value & DCFESR_FIFO_EMPTY(ep->epnum - 1))); -+ } -+ -+ reg = (ep->dir_in) ? -+ fotg210->reg + FOTG210_INEPMPSR(ep->epnum) : -+ fotg210->reg + FOTG210_OUTEPMPSR(ep->epnum); -+ value = ioread32(reg); -+ value |= INOUTEPMPSR_STL_EP; -+ iowrite32(value, reg); -+} -+ -+static void fotg210_clear_epnstall(struct fotg210_ep *ep) -+{ -+ struct fotg210_udc *fotg210 = ep->fotg210; -+ u32 value; -+ void __iomem *reg; -+ -+ reg = (ep->dir_in) ? -+ fotg210->reg + FOTG210_INEPMPSR(ep->epnum) : -+ fotg210->reg + FOTG210_OUTEPMPSR(ep->epnum); -+ value = ioread32(reg); -+ value &= ~INOUTEPMPSR_STL_EP; -+ iowrite32(value, reg); -+} -+ -+static int fotg210_set_halt_and_wedge(struct usb_ep *_ep, int value, int wedge) -+{ -+ struct fotg210_ep *ep; -+ struct fotg210_udc *fotg210; -+ unsigned long flags; -+ -+ ep = container_of(_ep, struct fotg210_ep, ep); -+ -+ fotg210 = ep->fotg210; -+ -+ spin_lock_irqsave(&ep->fotg210->lock, flags); -+ -+ if (value) { -+ fotg210_set_epnstall(ep); -+ ep->stall = 1; -+ if (wedge) -+ ep->wedged = 1; -+ } else { -+ fotg210_reset_tseq(fotg210, ep->epnum); -+ fotg210_clear_epnstall(ep); -+ ep->stall = 0; -+ ep->wedged = 0; -+ if (!list_empty(&ep->queue)) -+ fotg210_enable_fifo_int(ep); -+ } -+ -+ spin_unlock_irqrestore(&ep->fotg210->lock, flags); -+ return 0; -+} -+ -+static int fotg210_ep_set_halt(struct usb_ep *_ep, int value) -+{ -+ return fotg210_set_halt_and_wedge(_ep, value, 0); -+} -+ -+static int fotg210_ep_set_wedge(struct usb_ep *_ep) -+{ -+ return fotg210_set_halt_and_wedge(_ep, 1, 1); -+} -+ -+static void fotg210_ep_fifo_flush(struct usb_ep *_ep) -+{ -+} -+ -+static const struct usb_ep_ops fotg210_ep_ops = { -+ .enable = fotg210_ep_enable, -+ .disable = fotg210_ep_disable, -+ -+ .alloc_request = fotg210_ep_alloc_request, -+ .free_request = fotg210_ep_free_request, -+ -+ .queue = fotg210_ep_queue, -+ .dequeue = fotg210_ep_dequeue, -+ -+ .set_halt = fotg210_ep_set_halt, -+ .fifo_flush = fotg210_ep_fifo_flush, -+ .set_wedge = fotg210_ep_set_wedge, -+}; -+ -+static void fotg210_clear_tx0byte(struct fotg210_udc *fotg210) -+{ -+ u32 value = ioread32(fotg210->reg + FOTG210_TX0BYTE); -+ -+ value &= ~(TX0BYTE_EP1 | TX0BYTE_EP2 | TX0BYTE_EP3 -+ | TX0BYTE_EP4); -+ iowrite32(value, fotg210->reg + FOTG210_TX0BYTE); -+} -+ -+static void fotg210_clear_rx0byte(struct fotg210_udc *fotg210) -+{ -+ u32 value = ioread32(fotg210->reg + FOTG210_RX0BYTE); -+ -+ value &= ~(RX0BYTE_EP1 | RX0BYTE_EP2 | RX0BYTE_EP3 -+ | RX0BYTE_EP4); -+ iowrite32(value, fotg210->reg + FOTG210_RX0BYTE); -+} -+ -+/* read 8-byte setup packet only */ -+static void fotg210_rdsetupp(struct fotg210_udc *fotg210, -+ u8 *buffer) -+{ -+ int i = 0; -+ u8 *tmp = buffer; -+ u32 data; -+ u32 length = 8; -+ -+ iowrite32(DMATFNR_ACC_CXF, fotg210->reg + FOTG210_DMATFNR); -+ -+ for (i = (length >> 2); i > 0; i--) { -+ data = ioread32(fotg210->reg + FOTG210_CXPORT); -+ *tmp = data & 0xFF; -+ *(tmp + 1) = (data >> 8) & 0xFF; -+ *(tmp + 2) = (data >> 16) & 0xFF; -+ *(tmp + 3) = (data >> 24) & 0xFF; -+ tmp = tmp + 4; -+ } -+ -+ switch (length % 4) { -+ case 1: -+ data = ioread32(fotg210->reg + FOTG210_CXPORT); -+ *tmp = data & 0xFF; -+ break; -+ case 2: -+ data = ioread32(fotg210->reg + FOTG210_CXPORT); -+ *tmp = data & 0xFF; -+ *(tmp + 1) = (data >> 8) & 0xFF; -+ break; -+ case 3: -+ data = ioread32(fotg210->reg + FOTG210_CXPORT); -+ *tmp = data & 0xFF; -+ *(tmp + 1) = (data >> 8) & 0xFF; -+ *(tmp + 2) = (data >> 16) & 0xFF; -+ break; -+ default: -+ break; -+ } -+ -+ iowrite32(DMATFNR_DISDMA, fotg210->reg + FOTG210_DMATFNR); -+} -+ -+static void fotg210_set_configuration(struct fotg210_udc *fotg210) -+{ -+ u32 value = ioread32(fotg210->reg + FOTG210_DAR); -+ -+ value |= DAR_AFT_CONF; -+ iowrite32(value, fotg210->reg + FOTG210_DAR); -+} -+ -+static void fotg210_set_dev_addr(struct fotg210_udc *fotg210, u32 addr) -+{ -+ u32 value = ioread32(fotg210->reg + FOTG210_DAR); -+ -+ value |= (addr & 0x7F); -+ iowrite32(value, fotg210->reg + FOTG210_DAR); -+} -+ -+static void fotg210_set_cxstall(struct fotg210_udc *fotg210) -+{ -+ u32 value = ioread32(fotg210->reg + FOTG210_DCFESR); -+ -+ value |= DCFESR_CX_STL; -+ iowrite32(value, fotg210->reg + FOTG210_DCFESR); -+} -+ -+static void fotg210_request_error(struct fotg210_udc *fotg210) -+{ -+ fotg210_set_cxstall(fotg210); -+ pr_err("request error!!\n"); -+} -+ -+static void fotg210_set_address(struct fotg210_udc *fotg210, -+ struct usb_ctrlrequest *ctrl) -+{ -+ if (le16_to_cpu(ctrl->wValue) >= 0x0100) { -+ fotg210_request_error(fotg210); -+ } else { -+ fotg210_set_dev_addr(fotg210, le16_to_cpu(ctrl->wValue)); -+ fotg210_set_cxdone(fotg210); -+ } -+} -+ -+static void fotg210_set_feature(struct fotg210_udc *fotg210, -+ struct usb_ctrlrequest *ctrl) -+{ -+ switch (ctrl->bRequestType & USB_RECIP_MASK) { -+ case USB_RECIP_DEVICE: -+ fotg210_set_cxdone(fotg210); -+ break; -+ case USB_RECIP_INTERFACE: -+ fotg210_set_cxdone(fotg210); -+ break; -+ case USB_RECIP_ENDPOINT: { -+ u8 epnum; -+ epnum = le16_to_cpu(ctrl->wIndex) & USB_ENDPOINT_NUMBER_MASK; -+ if (epnum) -+ fotg210_set_epnstall(fotg210->ep[epnum]); -+ else -+ fotg210_set_cxstall(fotg210); -+ fotg210_set_cxdone(fotg210); -+ } -+ break; -+ default: -+ fotg210_request_error(fotg210); -+ break; -+ } -+} -+ -+static void fotg210_clear_feature(struct fotg210_udc *fotg210, -+ struct usb_ctrlrequest *ctrl) -+{ -+ struct fotg210_ep *ep = -+ fotg210->ep[ctrl->wIndex & USB_ENDPOINT_NUMBER_MASK]; -+ -+ switch (ctrl->bRequestType & USB_RECIP_MASK) { -+ case USB_RECIP_DEVICE: -+ fotg210_set_cxdone(fotg210); -+ break; -+ case USB_RECIP_INTERFACE: -+ fotg210_set_cxdone(fotg210); -+ break; -+ case USB_RECIP_ENDPOINT: -+ if (ctrl->wIndex & USB_ENDPOINT_NUMBER_MASK) { -+ if (ep->wedged) { -+ fotg210_set_cxdone(fotg210); -+ break; -+ } -+ if (ep->stall) -+ fotg210_set_halt_and_wedge(&ep->ep, 0, 0); -+ } -+ fotg210_set_cxdone(fotg210); -+ break; -+ default: -+ fotg210_request_error(fotg210); -+ break; -+ } -+} -+ -+static int fotg210_is_epnstall(struct fotg210_ep *ep) -+{ -+ struct fotg210_udc *fotg210 = ep->fotg210; -+ u32 value; -+ void __iomem *reg; -+ -+ reg = (ep->dir_in) ? -+ fotg210->reg + FOTG210_INEPMPSR(ep->epnum) : -+ fotg210->reg + FOTG210_OUTEPMPSR(ep->epnum); -+ value = ioread32(reg); -+ return value & INOUTEPMPSR_STL_EP ? 1 : 0; -+} -+ -+/* For EP0 requests triggered by this driver (currently GET_STATUS response) */ -+static void fotg210_ep0_complete(struct usb_ep *_ep, struct usb_request *req) -+{ -+ struct fotg210_ep *ep; -+ struct fotg210_udc *fotg210; -+ -+ ep = container_of(_ep, struct fotg210_ep, ep); -+ fotg210 = ep->fotg210; -+ -+ if (req->status || req->actual != req->length) { -+ dev_warn(&fotg210->gadget.dev, "EP0 request failed: %d\n", req->status); -+ } -+} -+ -+static void fotg210_get_status(struct fotg210_udc *fotg210, -+ struct usb_ctrlrequest *ctrl) -+{ -+ u8 epnum; -+ -+ switch (ctrl->bRequestType & USB_RECIP_MASK) { -+ case USB_RECIP_DEVICE: -+ fotg210->ep0_data = cpu_to_le16(1 << USB_DEVICE_SELF_POWERED); -+ break; -+ case USB_RECIP_INTERFACE: -+ fotg210->ep0_data = cpu_to_le16(0); -+ break; -+ case USB_RECIP_ENDPOINT: -+ epnum = ctrl->wIndex & USB_ENDPOINT_NUMBER_MASK; -+ if (epnum) -+ fotg210->ep0_data = -+ cpu_to_le16(fotg210_is_epnstall(fotg210->ep[epnum]) -+ << USB_ENDPOINT_HALT); -+ else -+ fotg210_request_error(fotg210); -+ break; -+ -+ default: -+ fotg210_request_error(fotg210); -+ return; /* exit */ -+ } -+ -+ fotg210->ep0_req->buf = &fotg210->ep0_data; -+ fotg210->ep0_req->length = 2; -+ -+ spin_unlock(&fotg210->lock); -+ fotg210_ep_queue(fotg210->gadget.ep0, fotg210->ep0_req, GFP_ATOMIC); -+ spin_lock(&fotg210->lock); -+} -+ -+static int fotg210_setup_packet(struct fotg210_udc *fotg210, -+ struct usb_ctrlrequest *ctrl) -+{ -+ u8 *p = (u8 *)ctrl; -+ u8 ret = 0; -+ -+ fotg210_rdsetupp(fotg210, p); -+ -+ fotg210->ep[0]->dir_in = ctrl->bRequestType & USB_DIR_IN; -+ -+ if (fotg210->gadget.speed == USB_SPEED_UNKNOWN) { -+ u32 value = ioread32(fotg210->reg + FOTG210_DMCR); -+ fotg210->gadget.speed = value & DMCR_HS_EN ? -+ USB_SPEED_HIGH : USB_SPEED_FULL; -+ } -+ -+ /* check request */ -+ if ((ctrl->bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD) { -+ switch (ctrl->bRequest) { -+ case USB_REQ_GET_STATUS: -+ fotg210_get_status(fotg210, ctrl); -+ break; -+ case USB_REQ_CLEAR_FEATURE: -+ fotg210_clear_feature(fotg210, ctrl); -+ break; -+ case USB_REQ_SET_FEATURE: -+ fotg210_set_feature(fotg210, ctrl); -+ break; -+ case USB_REQ_SET_ADDRESS: -+ fotg210_set_address(fotg210, ctrl); -+ break; -+ case USB_REQ_SET_CONFIGURATION: -+ fotg210_set_configuration(fotg210); -+ ret = 1; -+ break; -+ default: -+ ret = 1; -+ break; -+ } -+ } else { -+ ret = 1; -+ } -+ -+ return ret; -+} -+ -+static void fotg210_ep0out(struct fotg210_udc *fotg210) -+{ -+ struct fotg210_ep *ep = fotg210->ep[0]; -+ -+ if (!list_empty(&ep->queue) && !ep->dir_in) { -+ struct fotg210_request *req; -+ -+ req = list_first_entry(&ep->queue, -+ struct fotg210_request, queue); -+ -+ if (req->req.length) -+ fotg210_start_dma(ep, req); -+ -+ if ((req->req.length - req->req.actual) < ep->ep.maxpacket) -+ fotg210_done(ep, req, 0); -+ } else { -+ pr_err("%s : empty queue\n", __func__); -+ } -+} -+ -+static void fotg210_ep0in(struct fotg210_udc *fotg210) -+{ -+ struct fotg210_ep *ep = fotg210->ep[0]; -+ -+ if ((!list_empty(&ep->queue)) && (ep->dir_in)) { -+ struct fotg210_request *req; -+ -+ req = list_entry(ep->queue.next, -+ struct fotg210_request, queue); -+ -+ if (req->req.length) -+ fotg210_start_dma(ep, req); -+ -+ if (req->req.actual == req->req.length) -+ fotg210_done(ep, req, 0); -+ } else { -+ fotg210_set_cxdone(fotg210); -+ } -+} -+ -+static void fotg210_clear_comabt_int(struct fotg210_udc *fotg210) -+{ -+ u32 value = ioread32(fotg210->reg + FOTG210_DISGR0); -+ -+ value &= ~DISGR0_CX_COMABT_INT; -+ iowrite32(value, fotg210->reg + FOTG210_DISGR0); -+} -+ -+static void fotg210_in_fifo_handler(struct fotg210_ep *ep) -+{ -+ struct fotg210_request *req = list_entry(ep->queue.next, -+ struct fotg210_request, queue); -+ -+ if (req->req.length) -+ fotg210_start_dma(ep, req); -+ fotg210_done(ep, req, 0); -+} -+ -+static void fotg210_out_fifo_handler(struct fotg210_ep *ep) -+{ -+ struct fotg210_request *req = list_entry(ep->queue.next, -+ struct fotg210_request, queue); -+ int disgr1 = ioread32(ep->fotg210->reg + FOTG210_DISGR1); -+ -+ fotg210_start_dma(ep, req); -+ -+ /* Complete the request when it's full or a short packet arrived. -+ * Like other drivers, short_not_ok isn't handled. -+ */ -+ -+ if (req->req.length == req->req.actual || -+ (disgr1 & DISGR1_SPK_INT(ep->epnum - 1))) -+ fotg210_done(ep, req, 0); -+} -+ -+static irqreturn_t fotg210_irq(int irq, void *_fotg210) -+{ -+ struct fotg210_udc *fotg210 = _fotg210; -+ u32 int_grp = ioread32(fotg210->reg + FOTG210_DIGR); -+ u32 int_msk = ioread32(fotg210->reg + FOTG210_DMIGR); -+ -+ int_grp &= ~int_msk; -+ -+ spin_lock(&fotg210->lock); -+ -+ if (int_grp & DIGR_INT_G2) { -+ void __iomem *reg = fotg210->reg + FOTG210_DISGR2; -+ u32 int_grp2 = ioread32(reg); -+ u32 int_msk2 = ioread32(fotg210->reg + FOTG210_DMISGR2); -+ u32 value; -+ -+ int_grp2 &= ~int_msk2; -+ -+ if (int_grp2 & DISGR2_USBRST_INT) { -+ usb_gadget_udc_reset(&fotg210->gadget, -+ fotg210->driver); -+ value = ioread32(reg); -+ value &= ~DISGR2_USBRST_INT; -+ iowrite32(value, reg); -+ pr_info("fotg210 udc reset\n"); -+ } -+ if (int_grp2 & DISGR2_SUSP_INT) { -+ value = ioread32(reg); -+ value &= ~DISGR2_SUSP_INT; -+ iowrite32(value, reg); -+ pr_info("fotg210 udc suspend\n"); -+ } -+ if (int_grp2 & DISGR2_RESM_INT) { -+ value = ioread32(reg); -+ value &= ~DISGR2_RESM_INT; -+ iowrite32(value, reg); -+ pr_info("fotg210 udc resume\n"); -+ } -+ if (int_grp2 & DISGR2_ISO_SEQ_ERR_INT) { -+ value = ioread32(reg); -+ value &= ~DISGR2_ISO_SEQ_ERR_INT; -+ iowrite32(value, reg); -+ pr_info("fotg210 iso sequence error\n"); -+ } -+ if (int_grp2 & DISGR2_ISO_SEQ_ABORT_INT) { -+ value = ioread32(reg); -+ value &= ~DISGR2_ISO_SEQ_ABORT_INT; -+ iowrite32(value, reg); -+ pr_info("fotg210 iso sequence abort\n"); -+ } -+ if (int_grp2 & DISGR2_TX0BYTE_INT) { -+ fotg210_clear_tx0byte(fotg210); -+ value = ioread32(reg); -+ value &= ~DISGR2_TX0BYTE_INT; -+ iowrite32(value, reg); -+ pr_info("fotg210 transferred 0 byte\n"); -+ } -+ if (int_grp2 & DISGR2_RX0BYTE_INT) { -+ fotg210_clear_rx0byte(fotg210); -+ value = ioread32(reg); -+ value &= ~DISGR2_RX0BYTE_INT; -+ iowrite32(value, reg); -+ pr_info("fotg210 received 0 byte\n"); -+ } -+ if (int_grp2 & DISGR2_DMA_ERROR) { -+ value = ioread32(reg); -+ value &= ~DISGR2_DMA_ERROR; -+ iowrite32(value, reg); -+ } -+ } -+ -+ if (int_grp & DIGR_INT_G0) { -+ void __iomem *reg = fotg210->reg + FOTG210_DISGR0; -+ u32 int_grp0 = ioread32(reg); -+ u32 int_msk0 = ioread32(fotg210->reg + FOTG210_DMISGR0); -+ struct usb_ctrlrequest ctrl; -+ -+ int_grp0 &= ~int_msk0; -+ -+ /* the highest priority in this source register */ -+ if (int_grp0 & DISGR0_CX_COMABT_INT) { -+ fotg210_clear_comabt_int(fotg210); -+ pr_info("fotg210 CX command abort\n"); -+ } -+ -+ if (int_grp0 & DISGR0_CX_SETUP_INT) { -+ if (fotg210_setup_packet(fotg210, &ctrl)) { -+ spin_unlock(&fotg210->lock); -+ if (fotg210->driver->setup(&fotg210->gadget, -+ &ctrl) < 0) -+ fotg210_set_cxstall(fotg210); -+ spin_lock(&fotg210->lock); -+ } -+ } -+ if (int_grp0 & DISGR0_CX_COMEND_INT) -+ pr_info("fotg210 cmd end\n"); -+ -+ if (int_grp0 & DISGR0_CX_IN_INT) -+ fotg210_ep0in(fotg210); -+ -+ if (int_grp0 & DISGR0_CX_OUT_INT) -+ fotg210_ep0out(fotg210); -+ -+ if (int_grp0 & DISGR0_CX_COMFAIL_INT) { -+ fotg210_set_cxstall(fotg210); -+ pr_info("fotg210 ep0 fail\n"); -+ } -+ } -+ -+ if (int_grp & DIGR_INT_G1) { -+ void __iomem *reg = fotg210->reg + FOTG210_DISGR1; -+ u32 int_grp1 = ioread32(reg); -+ u32 int_msk1 = ioread32(fotg210->reg + FOTG210_DMISGR1); -+ int fifo; -+ -+ int_grp1 &= ~int_msk1; -+ -+ for (fifo = 0; fifo < FOTG210_MAX_FIFO_NUM; fifo++) { -+ if (int_grp1 & DISGR1_IN_INT(fifo)) -+ fotg210_in_fifo_handler(fotg210->ep[fifo + 1]); -+ -+ if ((int_grp1 & DISGR1_OUT_INT(fifo)) || -+ (int_grp1 & DISGR1_SPK_INT(fifo))) -+ fotg210_out_fifo_handler(fotg210->ep[fifo + 1]); -+ } -+ } -+ -+ spin_unlock(&fotg210->lock); -+ -+ return IRQ_HANDLED; -+} -+ -+static void fotg210_disable_unplug(struct fotg210_udc *fotg210) -+{ -+ u32 reg = ioread32(fotg210->reg + FOTG210_PHYTMSR); -+ -+ reg &= ~PHYTMSR_UNPLUG; -+ iowrite32(reg, fotg210->reg + FOTG210_PHYTMSR); -+} -+ -+static int fotg210_udc_start(struct usb_gadget *g, -+ struct usb_gadget_driver *driver) -+{ -+ struct fotg210_udc *fotg210 = gadget_to_fotg210(g); -+ u32 value; -+ -+ /* hook up the driver */ -+ fotg210->driver = driver; -+ -+ /* enable device global interrupt */ -+ value = ioread32(fotg210->reg + FOTG210_DMCR); -+ value |= DMCR_GLINT_EN; -+ iowrite32(value, fotg210->reg + FOTG210_DMCR); -+ -+ return 0; -+} -+ -+static void fotg210_init(struct fotg210_udc *fotg210) -+{ -+ u32 value; -+ -+ /* disable global interrupt and set int polarity to active high */ -+ iowrite32(GMIR_MHC_INT | GMIR_MOTG_INT | GMIR_INT_POLARITY, -+ fotg210->reg + FOTG210_GMIR); -+ -+ /* disable device global interrupt */ -+ value = ioread32(fotg210->reg + FOTG210_DMCR); -+ value &= ~DMCR_GLINT_EN; -+ iowrite32(value, fotg210->reg + FOTG210_DMCR); -+ -+ /* enable only grp2 irqs we handle */ -+ iowrite32(~(DISGR2_DMA_ERROR | DISGR2_RX0BYTE_INT | DISGR2_TX0BYTE_INT -+ | DISGR2_ISO_SEQ_ABORT_INT | DISGR2_ISO_SEQ_ERR_INT -+ | DISGR2_RESM_INT | DISGR2_SUSP_INT | DISGR2_USBRST_INT), -+ fotg210->reg + FOTG210_DMISGR2); -+ -+ /* disable all fifo interrupt */ -+ iowrite32(~(u32)0, fotg210->reg + FOTG210_DMISGR1); -+ -+ /* disable cmd end */ -+ value = ioread32(fotg210->reg + FOTG210_DMISGR0); -+ value |= DMISGR0_MCX_COMEND; -+ iowrite32(value, fotg210->reg + FOTG210_DMISGR0); -+} -+ -+static int fotg210_udc_stop(struct usb_gadget *g) -+{ -+ struct fotg210_udc *fotg210 = gadget_to_fotg210(g); -+ unsigned long flags; -+ -+ spin_lock_irqsave(&fotg210->lock, flags); -+ -+ fotg210_init(fotg210); -+ fotg210->driver = NULL; -+ -+ spin_unlock_irqrestore(&fotg210->lock, flags); -+ -+ return 0; -+} -+ -+static const struct usb_gadget_ops fotg210_gadget_ops = { -+ .udc_start = fotg210_udc_start, -+ .udc_stop = fotg210_udc_stop, -+}; -+ -+static int fotg210_udc_remove(struct platform_device *pdev) -+{ -+ struct fotg210_udc *fotg210 = platform_get_drvdata(pdev); -+ int i; -+ -+ usb_del_gadget_udc(&fotg210->gadget); -+ iounmap(fotg210->reg); -+ free_irq(platform_get_irq(pdev, 0), fotg210); -+ -+ fotg210_ep_free_request(&fotg210->ep[0]->ep, fotg210->ep0_req); -+ for (i = 0; i < FOTG210_MAX_NUM_EP; i++) -+ kfree(fotg210->ep[i]); -+ kfree(fotg210); -+ -+ return 0; -+} -+ -+static int fotg210_udc_probe(struct platform_device *pdev) -+{ -+ struct resource *res, *ires; -+ struct fotg210_udc *fotg210 = NULL; -+ struct fotg210_ep *_ep[FOTG210_MAX_NUM_EP]; -+ int ret = 0; -+ int i; -+ -+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); -+ if (!res) { -+ pr_err("platform_get_resource error.\n"); -+ return -ENODEV; -+ } -+ -+ ires = platform_get_resource(pdev, IORESOURCE_IRQ, 0); -+ if (!ires) { -+ pr_err("platform_get_resource IORESOURCE_IRQ error.\n"); -+ return -ENODEV; -+ } -+ -+ ret = -ENOMEM; -+ -+ /* initialize udc */ -+ fotg210 = kzalloc(sizeof(struct fotg210_udc), GFP_KERNEL); -+ if (fotg210 == NULL) -+ goto err; -+ -+ for (i = 0; i < FOTG210_MAX_NUM_EP; i++) { -+ _ep[i] = kzalloc(sizeof(struct fotg210_ep), GFP_KERNEL); -+ if (_ep[i] == NULL) -+ goto err_alloc; -+ fotg210->ep[i] = _ep[i]; -+ } -+ -+ fotg210->reg = ioremap(res->start, resource_size(res)); -+ if (fotg210->reg == NULL) { -+ pr_err("ioremap error.\n"); -+ goto err_alloc; -+ } -+ -+ spin_lock_init(&fotg210->lock); -+ -+ platform_set_drvdata(pdev, fotg210); -+ -+ fotg210->gadget.ops = &fotg210_gadget_ops; -+ -+ fotg210->gadget.max_speed = USB_SPEED_HIGH; -+ fotg210->gadget.dev.parent = &pdev->dev; -+ fotg210->gadget.dev.dma_mask = pdev->dev.dma_mask; -+ fotg210->gadget.name = udc_name; -+ -+ INIT_LIST_HEAD(&fotg210->gadget.ep_list); -+ -+ for (i = 0; i < FOTG210_MAX_NUM_EP; i++) { -+ struct fotg210_ep *ep = fotg210->ep[i]; -+ -+ if (i) { -+ INIT_LIST_HEAD(&fotg210->ep[i]->ep.ep_list); -+ list_add_tail(&fotg210->ep[i]->ep.ep_list, -+ &fotg210->gadget.ep_list); -+ } -+ ep->fotg210 = fotg210; -+ INIT_LIST_HEAD(&ep->queue); -+ ep->ep.name = fotg210_ep_name[i]; -+ ep->ep.ops = &fotg210_ep_ops; -+ usb_ep_set_maxpacket_limit(&ep->ep, (unsigned short) ~0); -+ -+ if (i == 0) { -+ ep->ep.caps.type_control = true; -+ } else { -+ ep->ep.caps.type_iso = true; -+ ep->ep.caps.type_bulk = true; -+ ep->ep.caps.type_int = true; -+ } -+ -+ ep->ep.caps.dir_in = true; -+ ep->ep.caps.dir_out = true; -+ } -+ usb_ep_set_maxpacket_limit(&fotg210->ep[0]->ep, 0x40); -+ fotg210->gadget.ep0 = &fotg210->ep[0]->ep; -+ INIT_LIST_HEAD(&fotg210->gadget.ep0->ep_list); -+ -+ fotg210->ep0_req = fotg210_ep_alloc_request(&fotg210->ep[0]->ep, -+ GFP_KERNEL); -+ if (fotg210->ep0_req == NULL) -+ goto err_map; -+ -+ fotg210->ep0_req->complete = fotg210_ep0_complete; -+ -+ fotg210_init(fotg210); -+ -+ fotg210_disable_unplug(fotg210); -+ -+ ret = request_irq(ires->start, fotg210_irq, IRQF_SHARED, -+ udc_name, fotg210); -+ if (ret < 0) { -+ pr_err("request_irq error (%d)\n", ret); -+ goto err_req; -+ } -+ -+ ret = usb_add_gadget_udc(&pdev->dev, &fotg210->gadget); -+ if (ret) -+ goto err_add_udc; -+ -+ dev_info(&pdev->dev, "version %s\n", DRIVER_VERSION); -+ -+ return 0; -+ -+err_add_udc: -+ free_irq(ires->start, fotg210); -+ -+err_req: -+ fotg210_ep_free_request(&fotg210->ep[0]->ep, fotg210->ep0_req); -+ -+err_map: -+ iounmap(fotg210->reg); -+ -+err_alloc: -+ for (i = 0; i < FOTG210_MAX_NUM_EP; i++) -+ kfree(fotg210->ep[i]); -+ kfree(fotg210); -+ -+err: -+ return ret; -+} -+ -+static struct platform_driver fotg210_driver = { -+ .driver = { -+ .name = udc_name, -+ }, -+ .probe = fotg210_udc_probe, -+ .remove = fotg210_udc_remove, -+}; -+ -+module_platform_driver(fotg210_driver); -+ -+MODULE_AUTHOR("Yuan-Hsin Chen, Feng-Hsin Chiang "); -+MODULE_LICENSE("GPL"); -+MODULE_DESCRIPTION(DRIVER_DESC); ---- a/drivers/usb/gadget/udc/Kconfig -+++ b/drivers/usb/gadget/udc/Kconfig -@@ -108,17 +108,6 @@ config USB_FUSB300 - help - Faraday usb device controller FUSB300 driver - --config USB_FOTG210_UDC -- depends on HAS_DMA -- tristate "Faraday FOTG210 USB Peripheral Controller" -- help -- Faraday USB2.0 OTG controller which can be configured as -- high speed or full speed USB device. This driver supppors -- Bulk Transfer so far. -- -- Say "y" to link the driver statically, or "m" to build a -- dynamically linked module called "fotg210_udc". -- - config USB_GR_UDC - tristate "Aeroflex Gaisler GRUSBDC USB Peripheral Controller Driver" - depends on HAS_DMA ---- a/drivers/usb/gadget/udc/Makefile -+++ b/drivers/usb/gadget/udc/Makefile -@@ -34,7 +34,6 @@ obj-$(CONFIG_USB_EG20T) += pch_udc.o - obj-$(CONFIG_USB_MV_UDC) += mv_udc.o - 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_GR_UDC) += gr_udc.o - obj-$(CONFIG_USB_GADGET_XILINX) += udc-xilinx.o ---- a/drivers/usb/host/Kconfig -+++ b/drivers/usb/host/Kconfig -@@ -389,17 +389,6 @@ config USB_ISP1362_HCD - To compile this driver as a module, choose M here: the - module will be called isp1362-hcd. - --config USB_FOTG210_HCD -- tristate "FOTG210 HCD support" -- depends on USB && HAS_DMA && HAS_IOMEM -- help -- Faraday FOTG210 is an OTG controller which can be configured as -- an USB2.0 host. It is designed to meet USB2.0 EHCI specification -- with minor modification. -- -- To compile this driver as a module, choose M here: the -- module will be called fotg210-hcd. -- - config USB_MAX3421_HCD - tristate "MAX3421 HCD (USB-over-SPI) support" - depends on USB && SPI ---- a/drivers/usb/host/Makefile -+++ b/drivers/usb/host/Makefile -@@ -84,6 +84,5 @@ obj-$(CONFIG_USB_EHCI_FSL) += ehci-fsl.o - obj-$(CONFIG_USB_EHCI_MV) += ehci-mv.o - obj-$(CONFIG_USB_HCD_BCMA) += bcma-hcd.o - obj-$(CONFIG_USB_HCD_SSB) += ssb-hcd.o --obj-$(CONFIG_USB_FOTG210_HCD) += fotg210-hcd.o - obj-$(CONFIG_USB_MAX3421_HCD) += max3421-hcd.o - obj-$(CONFIG_USB_XEN_HCD) += xen-hcd.o ---- /dev/null -+++ b/drivers/usb/fotg210/fotg210-hcd.h -@@ -0,0 +1,688 @@ -+/* SPDX-License-Identifier: GPL-2.0 */ -+#ifndef __LINUX_FOTG210_H -+#define __LINUX_FOTG210_H -+ -+#include -+ -+/* definitions used for the EHCI driver */ -+ -+/* -+ * __hc32 and __hc16 are "Host Controller" types, they may be equivalent to -+ * __leXX (normally) or __beXX (given FOTG210_BIG_ENDIAN_DESC), depending on -+ * the host controller implementation. -+ * -+ * To facilitate the strongest possible byte-order checking from "sparse" -+ * and so on, we use __leXX unless that's not practical. -+ */ -+#define __hc32 __le32 -+#define __hc16 __le16 -+ -+/* statistics can be kept for tuning/monitoring */ -+struct fotg210_stats { -+ /* irq usage */ -+ unsigned long normal; -+ unsigned long error; -+ unsigned long iaa; -+ unsigned long lost_iaa; -+ -+ /* termination of urbs from core */ -+ unsigned long complete; -+ unsigned long unlink; -+}; -+ -+/* fotg210_hcd->lock guards shared data against other CPUs: -+ * fotg210_hcd: async, unlink, periodic (and shadow), ... -+ * usb_host_endpoint: hcpriv -+ * fotg210_qh: qh_next, qtd_list -+ * fotg210_qtd: qtd_list -+ * -+ * Also, hold this lock when talking to HC registers or -+ * when updating hw_* fields in shared qh/qtd/... structures. -+ */ -+ -+#define FOTG210_MAX_ROOT_PORTS 1 /* see HCS_N_PORTS */ -+ -+/* -+ * fotg210_rh_state values of FOTG210_RH_RUNNING or above mean that the -+ * controller may be doing DMA. Lower values mean there's no DMA. -+ */ -+enum fotg210_rh_state { -+ FOTG210_RH_HALTED, -+ FOTG210_RH_SUSPENDED, -+ FOTG210_RH_RUNNING, -+ FOTG210_RH_STOPPING -+}; -+ -+/* -+ * Timer events, ordered by increasing delay length. -+ * Always update event_delays_ns[] and event_handlers[] (defined in -+ * ehci-timer.c) in parallel with this list. -+ */ -+enum fotg210_hrtimer_event { -+ FOTG210_HRTIMER_POLL_ASS, /* Poll for async schedule off */ -+ FOTG210_HRTIMER_POLL_PSS, /* Poll for periodic schedule off */ -+ FOTG210_HRTIMER_POLL_DEAD, /* Wait for dead controller to stop */ -+ FOTG210_HRTIMER_UNLINK_INTR, /* Wait for interrupt QH unlink */ -+ FOTG210_HRTIMER_FREE_ITDS, /* Wait for unused iTDs and siTDs */ -+ FOTG210_HRTIMER_ASYNC_UNLINKS, /* Unlink empty async QHs */ -+ FOTG210_HRTIMER_IAA_WATCHDOG, /* Handle lost IAA interrupts */ -+ FOTG210_HRTIMER_DISABLE_PERIODIC, /* Wait to disable periodic sched */ -+ FOTG210_HRTIMER_DISABLE_ASYNC, /* Wait to disable async sched */ -+ FOTG210_HRTIMER_IO_WATCHDOG, /* Check for missing IRQs */ -+ FOTG210_HRTIMER_NUM_EVENTS /* Must come last */ -+}; -+#define FOTG210_HRTIMER_NO_EVENT 99 -+ -+struct fotg210_hcd { /* one per controller */ -+ /* timing support */ -+ enum fotg210_hrtimer_event next_hrtimer_event; -+ unsigned enabled_hrtimer_events; -+ ktime_t hr_timeouts[FOTG210_HRTIMER_NUM_EVENTS]; -+ struct hrtimer hrtimer; -+ -+ int PSS_poll_count; -+ int ASS_poll_count; -+ int died_poll_count; -+ -+ /* glue to PCI and HCD framework */ -+ struct fotg210_caps __iomem *caps; -+ struct fotg210_regs __iomem *regs; -+ struct ehci_dbg_port __iomem *debug; -+ -+ __u32 hcs_params; /* cached register copy */ -+ spinlock_t lock; -+ enum fotg210_rh_state rh_state; -+ -+ /* general schedule support */ -+ bool scanning:1; -+ bool need_rescan:1; -+ bool intr_unlinking:1; -+ bool async_unlinking:1; -+ bool shutdown:1; -+ struct fotg210_qh *qh_scan_next; -+ -+ /* async schedule support */ -+ struct fotg210_qh *async; -+ struct fotg210_qh *dummy; /* For AMD quirk use */ -+ struct fotg210_qh *async_unlink; -+ struct fotg210_qh *async_unlink_last; -+ struct fotg210_qh *async_iaa; -+ unsigned async_unlink_cycle; -+ unsigned async_count; /* async activity count */ -+ -+ /* periodic schedule support */ -+#define DEFAULT_I_TDPS 1024 /* some HCs can do less */ -+ unsigned periodic_size; -+ __hc32 *periodic; /* hw periodic table */ -+ dma_addr_t periodic_dma; -+ struct list_head intr_qh_list; -+ unsigned i_thresh; /* uframes HC might cache */ -+ -+ union fotg210_shadow *pshadow; /* mirror hw periodic table */ -+ struct fotg210_qh *intr_unlink; -+ struct fotg210_qh *intr_unlink_last; -+ unsigned intr_unlink_cycle; -+ unsigned now_frame; /* frame from HC hardware */ -+ unsigned next_frame; /* scan periodic, start here */ -+ unsigned intr_count; /* intr activity count */ -+ unsigned isoc_count; /* isoc activity count */ -+ unsigned periodic_count; /* periodic activity count */ -+ /* max periodic time per uframe */ -+ unsigned uframe_periodic_max; -+ -+ -+ /* list of itds completed while now_frame was still active */ -+ struct list_head cached_itd_list; -+ struct fotg210_itd *last_itd_to_free; -+ -+ /* per root hub port */ -+ unsigned long reset_done[FOTG210_MAX_ROOT_PORTS]; -+ -+ /* bit vectors (one bit per port) -+ * which ports were already suspended at the start of a bus suspend -+ */ -+ unsigned long bus_suspended; -+ -+ /* which ports are edicated to the companion controller */ -+ unsigned long companion_ports; -+ -+ /* which ports are owned by the companion during a bus suspend */ -+ unsigned long owned_ports; -+ -+ /* which ports have the change-suspend feature turned on */ -+ unsigned long port_c_suspend; -+ -+ /* which ports are suspended */ -+ unsigned long suspended_ports; -+ -+ /* which ports have started to resume */ -+ unsigned long resuming_ports; -+ -+ /* per-HC memory pools (could be per-bus, but ...) */ -+ struct dma_pool *qh_pool; /* qh per active urb */ -+ struct dma_pool *qtd_pool; /* one or more per qh */ -+ struct dma_pool *itd_pool; /* itd per iso urb */ -+ -+ unsigned random_frame; -+ unsigned long next_statechange; -+ ktime_t last_periodic_enable; -+ u32 command; -+ -+ /* SILICON QUIRKS */ -+ unsigned need_io_watchdog:1; -+ unsigned fs_i_thresh:1; /* Intel iso scheduling */ -+ -+ u8 sbrn; /* packed release number */ -+ -+ /* irq statistics */ -+#ifdef FOTG210_STATS -+ struct fotg210_stats stats; -+# define INCR(x) ((x)++) -+#else -+# define INCR(x) do {} while (0) -+#endif -+ -+ /* silicon clock */ -+ struct clk *pclk; -+}; -+ -+/* convert between an HCD pointer and the corresponding FOTG210_HCD */ -+static inline struct fotg210_hcd *hcd_to_fotg210(struct usb_hcd *hcd) -+{ -+ return (struct fotg210_hcd *)(hcd->hcd_priv); -+} -+static inline struct usb_hcd *fotg210_to_hcd(struct fotg210_hcd *fotg210) -+{ -+ return container_of((void *) fotg210, struct usb_hcd, hcd_priv); -+} -+ -+/*-------------------------------------------------------------------------*/ -+ -+/* EHCI register interface, corresponds to EHCI Revision 0.95 specification */ -+ -+/* Section 2.2 Host Controller Capability Registers */ -+struct fotg210_caps { -+ /* these fields are specified as 8 and 16 bit registers, -+ * but some hosts can't perform 8 or 16 bit PCI accesses. -+ * some hosts treat caplength and hciversion as parts of a 32-bit -+ * register, others treat them as two separate registers, this -+ * affects the memory map for big endian controllers. -+ */ -+ u32 hc_capbase; -+#define HC_LENGTH(fotg210, p) (0x00ff&((p) >> /* bits 7:0 / offset 00h */ \ -+ (fotg210_big_endian_capbase(fotg210) ? 24 : 0))) -+#define HC_VERSION(fotg210, p) (0xffff&((p) >> /* bits 31:16 / offset 02h */ \ -+ (fotg210_big_endian_capbase(fotg210) ? 0 : 16))) -+ u32 hcs_params; /* HCSPARAMS - offset 0x4 */ -+#define HCS_N_PORTS(p) (((p)>>0)&0xf) /* bits 3:0, ports on HC */ -+ -+ u32 hcc_params; /* HCCPARAMS - offset 0x8 */ -+#define HCC_CANPARK(p) ((p)&(1 << 2)) /* true: can park on async qh */ -+#define HCC_PGM_FRAMELISTLEN(p) ((p)&(1 << 1)) /* true: periodic_size changes*/ -+ u8 portroute[8]; /* nibbles for routing - offset 0xC */ -+}; -+ -+ -+/* Section 2.3 Host Controller Operational Registers */ -+struct fotg210_regs { -+ -+ /* USBCMD: offset 0x00 */ -+ u32 command; -+ -+/* EHCI 1.1 addendum */ -+/* 23:16 is r/w intr rate, in microframes; default "8" == 1/msec */ -+#define CMD_PARK (1<<11) /* enable "park" on async qh */ -+#define CMD_PARK_CNT(c) (((c)>>8)&3) /* how many transfers to park for */ -+#define CMD_IAAD (1<<6) /* "doorbell" interrupt async advance */ -+#define CMD_ASE (1<<5) /* async schedule enable */ -+#define CMD_PSE (1<<4) /* periodic schedule enable */ -+/* 3:2 is periodic frame list size */ -+#define CMD_RESET (1<<1) /* reset HC not bus */ -+#define CMD_RUN (1<<0) /* start/stop HC */ -+ -+ /* USBSTS: offset 0x04 */ -+ u32 status; -+#define STS_ASS (1<<15) /* Async Schedule Status */ -+#define STS_PSS (1<<14) /* Periodic Schedule Status */ -+#define STS_RECL (1<<13) /* Reclamation */ -+#define STS_HALT (1<<12) /* Not running (any reason) */ -+/* some bits reserved */ -+ /* these STS_* flags are also intr_enable bits (USBINTR) */ -+#define STS_IAA (1<<5) /* Interrupted on async advance */ -+#define STS_FATAL (1<<4) /* such as some PCI access errors */ -+#define STS_FLR (1<<3) /* frame list rolled over */ -+#define STS_PCD (1<<2) /* port change detect */ -+#define STS_ERR (1<<1) /* "error" completion (overflow, ...) */ -+#define STS_INT (1<<0) /* "normal" completion (short, ...) */ -+ -+ /* USBINTR: offset 0x08 */ -+ u32 intr_enable; -+ -+ /* FRINDEX: offset 0x0C */ -+ u32 frame_index; /* current microframe number */ -+ /* CTRLDSSEGMENT: offset 0x10 */ -+ u32 segment; /* address bits 63:32 if needed */ -+ /* PERIODICLISTBASE: offset 0x14 */ -+ u32 frame_list; /* points to periodic list */ -+ /* ASYNCLISTADDR: offset 0x18 */ -+ u32 async_next; /* address of next async queue head */ -+ -+ u32 reserved1; -+ /* PORTSC: offset 0x20 */ -+ u32 port_status; -+/* 31:23 reserved */ -+#define PORT_USB11(x) (((x)&(3<<10)) == (1<<10)) /* USB 1.1 device */ -+#define PORT_RESET (1<<8) /* reset port */ -+#define PORT_SUSPEND (1<<7) /* suspend port */ -+#define PORT_RESUME (1<<6) /* resume it */ -+#define PORT_PEC (1<<3) /* port enable change */ -+#define PORT_PE (1<<2) /* port enable */ -+#define PORT_CSC (1<<1) /* connect status change */ -+#define PORT_CONNECT (1<<0) /* device connected */ -+#define PORT_RWC_BITS (PORT_CSC | PORT_PEC) -+ u32 reserved2[19]; -+ -+ /* OTGCSR: offet 0x70 */ -+ u32 otgcsr; -+#define OTGCSR_HOST_SPD_TYP (3 << 22) -+#define OTGCSR_A_BUS_DROP (1 << 5) -+#define OTGCSR_A_BUS_REQ (1 << 4) -+ -+ /* OTGISR: offset 0x74 */ -+ u32 otgisr; -+#define OTGISR_OVC (1 << 10) -+ -+ u32 reserved3[15]; -+ -+ /* GMIR: offset 0xB4 */ -+ u32 gmir; -+#define GMIR_INT_POLARITY (1 << 3) /*Active High*/ -+#define GMIR_MHC_INT (1 << 2) -+#define GMIR_MOTG_INT (1 << 1) -+#define GMIR_MDEV_INT (1 << 0) -+}; -+ -+/*-------------------------------------------------------------------------*/ -+ -+#define QTD_NEXT(fotg210, dma) cpu_to_hc32(fotg210, (u32)dma) -+ -+/* -+ * EHCI Specification 0.95 Section 3.5 -+ * QTD: describe data transfer components (buffer, direction, ...) -+ * See Fig 3-6 "Queue Element Transfer Descriptor Block Diagram". -+ * -+ * These are associated only with "QH" (Queue Head) structures, -+ * used with control, bulk, and interrupt transfers. -+ */ -+struct fotg210_qtd { -+ /* first part defined by EHCI spec */ -+ __hc32 hw_next; /* see EHCI 3.5.1 */ -+ __hc32 hw_alt_next; /* see EHCI 3.5.2 */ -+ __hc32 hw_token; /* see EHCI 3.5.3 */ -+#define QTD_TOGGLE (1 << 31) /* data toggle */ -+#define QTD_LENGTH(tok) (((tok)>>16) & 0x7fff) -+#define QTD_IOC (1 << 15) /* interrupt on complete */ -+#define QTD_CERR(tok) (((tok)>>10) & 0x3) -+#define QTD_PID(tok) (((tok)>>8) & 0x3) -+#define QTD_STS_ACTIVE (1 << 7) /* HC may execute this */ -+#define QTD_STS_HALT (1 << 6) /* halted on error */ -+#define QTD_STS_DBE (1 << 5) /* data buffer error (in HC) */ -+#define QTD_STS_BABBLE (1 << 4) /* device was babbling (qtd halted) */ -+#define QTD_STS_XACT (1 << 3) /* device gave illegal response */ -+#define QTD_STS_MMF (1 << 2) /* incomplete split transaction */ -+#define QTD_STS_STS (1 << 1) /* split transaction state */ -+#define QTD_STS_PING (1 << 0) /* issue PING? */ -+ -+#define ACTIVE_BIT(fotg210) cpu_to_hc32(fotg210, QTD_STS_ACTIVE) -+#define HALT_BIT(fotg210) cpu_to_hc32(fotg210, QTD_STS_HALT) -+#define STATUS_BIT(fotg210) cpu_to_hc32(fotg210, QTD_STS_STS) -+ -+ __hc32 hw_buf[5]; /* see EHCI 3.5.4 */ -+ __hc32 hw_buf_hi[5]; /* Appendix B */ -+ -+ /* the rest is HCD-private */ -+ dma_addr_t qtd_dma; /* qtd address */ -+ struct list_head qtd_list; /* sw qtd list */ -+ struct urb *urb; /* qtd's urb */ -+ size_t length; /* length of buffer */ -+} __aligned(32); -+ -+/* mask NakCnt+T in qh->hw_alt_next */ -+#define QTD_MASK(fotg210) cpu_to_hc32(fotg210, ~0x1f) -+ -+#define IS_SHORT_READ(token) (QTD_LENGTH(token) != 0 && QTD_PID(token) == 1) -+ -+/*-------------------------------------------------------------------------*/ -+ -+/* type tag from {qh,itd,fstn}->hw_next */ -+#define Q_NEXT_TYPE(fotg210, dma) ((dma) & cpu_to_hc32(fotg210, 3 << 1)) -+ -+/* -+ * Now the following defines are not converted using the -+ * cpu_to_le32() macro anymore, since we have to support -+ * "dynamic" switching between be and le support, so that the driver -+ * can be used on one system with SoC EHCI controller using big-endian -+ * descriptors as well as a normal little-endian PCI EHCI controller. -+ */ -+/* values for that type tag */ -+#define Q_TYPE_ITD (0 << 1) -+#define Q_TYPE_QH (1 << 1) -+#define Q_TYPE_SITD (2 << 1) -+#define Q_TYPE_FSTN (3 << 1) -+ -+/* next async queue entry, or pointer to interrupt/periodic QH */ -+#define QH_NEXT(fotg210, dma) \ -+ (cpu_to_hc32(fotg210, (((u32)dma)&~0x01f)|Q_TYPE_QH)) -+ -+/* for periodic/async schedules and qtd lists, mark end of list */ -+#define FOTG210_LIST_END(fotg210) \ -+ cpu_to_hc32(fotg210, 1) /* "null pointer" to hw */ -+ -+/* -+ * Entries in periodic shadow table are pointers to one of four kinds -+ * of data structure. That's dictated by the hardware; a type tag is -+ * encoded in the low bits of the hardware's periodic schedule. Use -+ * Q_NEXT_TYPE to get the tag. -+ * -+ * For entries in the async schedule, the type tag always says "qh". -+ */ -+union fotg210_shadow { -+ struct fotg210_qh *qh; /* Q_TYPE_QH */ -+ struct fotg210_itd *itd; /* Q_TYPE_ITD */ -+ struct fotg210_fstn *fstn; /* Q_TYPE_FSTN */ -+ __hc32 *hw_next; /* (all types) */ -+ void *ptr; -+}; -+ -+/*-------------------------------------------------------------------------*/ -+ -+/* -+ * EHCI Specification 0.95 Section 3.6 -+ * QH: describes control/bulk/interrupt endpoints -+ * See Fig 3-7 "Queue Head Structure Layout". -+ * -+ * These appear in both the async and (for interrupt) periodic schedules. -+ */ -+ -+/* first part defined by EHCI spec */ -+struct fotg210_qh_hw { -+ __hc32 hw_next; /* see EHCI 3.6.1 */ -+ __hc32 hw_info1; /* see EHCI 3.6.2 */ -+#define QH_CONTROL_EP (1 << 27) /* FS/LS control endpoint */ -+#define QH_HEAD (1 << 15) /* Head of async reclamation list */ -+#define QH_TOGGLE_CTL (1 << 14) /* Data toggle control */ -+#define QH_HIGH_SPEED (2 << 12) /* Endpoint speed */ -+#define QH_LOW_SPEED (1 << 12) -+#define QH_FULL_SPEED (0 << 12) -+#define QH_INACTIVATE (1 << 7) /* Inactivate on next transaction */ -+ __hc32 hw_info2; /* see EHCI 3.6.2 */ -+#define QH_SMASK 0x000000ff -+#define QH_CMASK 0x0000ff00 -+#define QH_HUBADDR 0x007f0000 -+#define QH_HUBPORT 0x3f800000 -+#define QH_MULT 0xc0000000 -+ __hc32 hw_current; /* qtd list - see EHCI 3.6.4 */ -+ -+ /* qtd overlay (hardware parts of a struct fotg210_qtd) */ -+ __hc32 hw_qtd_next; -+ __hc32 hw_alt_next; -+ __hc32 hw_token; -+ __hc32 hw_buf[5]; -+ __hc32 hw_buf_hi[5]; -+} __aligned(32); -+ -+struct fotg210_qh { -+ struct fotg210_qh_hw *hw; /* Must come first */ -+ /* the rest is HCD-private */ -+ dma_addr_t qh_dma; /* address of qh */ -+ union fotg210_shadow qh_next; /* ptr to qh; or periodic */ -+ struct list_head qtd_list; /* sw qtd list */ -+ struct list_head intr_node; /* list of intr QHs */ -+ struct fotg210_qtd *dummy; -+ struct fotg210_qh *unlink_next; /* next on unlink list */ -+ -+ unsigned unlink_cycle; -+ -+ u8 needs_rescan; /* Dequeue during giveback */ -+ u8 qh_state; -+#define QH_STATE_LINKED 1 /* HC sees this */ -+#define QH_STATE_UNLINK 2 /* HC may still see this */ -+#define QH_STATE_IDLE 3 /* HC doesn't see this */ -+#define QH_STATE_UNLINK_WAIT 4 /* LINKED and on unlink q */ -+#define QH_STATE_COMPLETING 5 /* don't touch token.HALT */ -+ -+ u8 xacterrs; /* XactErr retry counter */ -+#define QH_XACTERR_MAX 32 /* XactErr retry limit */ -+ -+ /* periodic schedule info */ -+ u8 usecs; /* intr bandwidth */ -+ u8 gap_uf; /* uframes split/csplit gap */ -+ u8 c_usecs; /* ... split completion bw */ -+ u16 tt_usecs; /* tt downstream bandwidth */ -+ unsigned short period; /* polling interval */ -+ unsigned short start; /* where polling starts */ -+#define NO_FRAME ((unsigned short)~0) /* pick new start */ -+ -+ struct usb_device *dev; /* access to TT */ -+ unsigned is_out:1; /* bulk or intr OUT */ -+ unsigned clearing_tt:1; /* Clear-TT-Buf in progress */ -+}; -+ -+/*-------------------------------------------------------------------------*/ -+ -+/* description of one iso transaction (up to 3 KB data if highspeed) */ -+struct fotg210_iso_packet { -+ /* These will be copied to iTD when scheduling */ -+ u64 bufp; /* itd->hw_bufp{,_hi}[pg] |= */ -+ __hc32 transaction; /* itd->hw_transaction[i] |= */ -+ u8 cross; /* buf crosses pages */ -+ /* for full speed OUT splits */ -+ u32 buf1; -+}; -+ -+/* temporary schedule data for packets from iso urbs (both speeds) -+ * each packet is one logical usb transaction to the device (not TT), -+ * beginning at stream->next_uframe -+ */ -+struct fotg210_iso_sched { -+ struct list_head td_list; -+ unsigned span; -+ struct fotg210_iso_packet packet[]; -+}; -+ -+/* -+ * fotg210_iso_stream - groups all (s)itds for this endpoint. -+ * acts like a qh would, if EHCI had them for ISO. -+ */ -+struct fotg210_iso_stream { -+ /* first field matches fotg210_hq, but is NULL */ -+ struct fotg210_qh_hw *hw; -+ -+ u8 bEndpointAddress; -+ u8 highspeed; -+ struct list_head td_list; /* queued itds */ -+ struct list_head free_list; /* list of unused itds */ -+ struct usb_device *udev; -+ struct usb_host_endpoint *ep; -+ -+ /* output of (re)scheduling */ -+ int next_uframe; -+ __hc32 splits; -+ -+ /* the rest is derived from the endpoint descriptor, -+ * trusting urb->interval == f(epdesc->bInterval) and -+ * including the extra info for hw_bufp[0..2] -+ */ -+ u8 usecs, c_usecs; -+ u16 interval; -+ u16 tt_usecs; -+ u16 maxp; -+ u16 raw_mask; -+ unsigned bandwidth; -+ -+ /* This is used to initialize iTD's hw_bufp fields */ -+ __hc32 buf0; -+ __hc32 buf1; -+ __hc32 buf2; -+ -+ /* this is used to initialize sITD's tt info */ -+ __hc32 address; -+}; -+ -+/*-------------------------------------------------------------------------*/ -+ -+/* -+ * EHCI Specification 0.95 Section 3.3 -+ * Fig 3-4 "Isochronous Transaction Descriptor (iTD)" -+ * -+ * Schedule records for high speed iso xfers -+ */ -+struct fotg210_itd { -+ /* first part defined by EHCI spec */ -+ __hc32 hw_next; /* see EHCI 3.3.1 */ -+ __hc32 hw_transaction[8]; /* see EHCI 3.3.2 */ -+#define FOTG210_ISOC_ACTIVE (1<<31) /* activate transfer this slot */ -+#define FOTG210_ISOC_BUF_ERR (1<<30) /* Data buffer error */ -+#define FOTG210_ISOC_BABBLE (1<<29) /* babble detected */ -+#define FOTG210_ISOC_XACTERR (1<<28) /* XactErr - transaction error */ -+#define FOTG210_ITD_LENGTH(tok) (((tok)>>16) & 0x0fff) -+#define FOTG210_ITD_IOC (1 << 15) /* interrupt on complete */ -+ -+#define ITD_ACTIVE(fotg210) cpu_to_hc32(fotg210, FOTG210_ISOC_ACTIVE) -+ -+ __hc32 hw_bufp[7]; /* see EHCI 3.3.3 */ -+ __hc32 hw_bufp_hi[7]; /* Appendix B */ -+ -+ /* the rest is HCD-private */ -+ dma_addr_t itd_dma; /* for this itd */ -+ union fotg210_shadow itd_next; /* ptr to periodic q entry */ -+ -+ struct urb *urb; -+ struct fotg210_iso_stream *stream; /* endpoint's queue */ -+ struct list_head itd_list; /* list of stream's itds */ -+ -+ /* any/all hw_transactions here may be used by that urb */ -+ unsigned frame; /* where scheduled */ -+ unsigned pg; -+ unsigned index[8]; /* in urb->iso_frame_desc */ -+} __aligned(32); -+ -+/*-------------------------------------------------------------------------*/ -+ -+/* -+ * EHCI Specification 0.96 Section 3.7 -+ * Periodic Frame Span Traversal Node (FSTN) -+ * -+ * Manages split interrupt transactions (using TT) that span frame boundaries -+ * into uframes 0/1; see 4.12.2.2. In those uframes, a "save place" FSTN -+ * makes the HC jump (back) to a QH to scan for fs/ls QH completions until -+ * it hits a "restore" FSTN; then it returns to finish other uframe 0/1 work. -+ */ -+struct fotg210_fstn { -+ __hc32 hw_next; /* any periodic q entry */ -+ __hc32 hw_prev; /* qh or FOTG210_LIST_END */ -+ -+ /* the rest is HCD-private */ -+ dma_addr_t fstn_dma; -+ union fotg210_shadow fstn_next; /* ptr to periodic q entry */ -+} __aligned(32); -+ -+/*-------------------------------------------------------------------------*/ -+ -+/* Prepare the PORTSC wakeup flags during controller suspend/resume */ -+ -+#define fotg210_prepare_ports_for_controller_suspend(fotg210, do_wakeup) \ -+ fotg210_adjust_port_wakeup_flags(fotg210, true, do_wakeup) -+ -+#define fotg210_prepare_ports_for_controller_resume(fotg210) \ -+ fotg210_adjust_port_wakeup_flags(fotg210, false, false) -+ -+/*-------------------------------------------------------------------------*/ -+ -+/* -+ * Some EHCI controllers have a Transaction Translator built into the -+ * root hub. This is a non-standard feature. Each controller will need -+ * to add code to the following inline functions, and call them as -+ * needed (mostly in root hub code). -+ */ -+ -+static inline unsigned int -+fotg210_get_speed(struct fotg210_hcd *fotg210, unsigned int portsc) -+{ -+ return (readl(&fotg210->regs->otgcsr) -+ & OTGCSR_HOST_SPD_TYP) >> 22; -+} -+ -+/* Returns the speed of a device attached to a port on the root hub. */ -+static inline unsigned int -+fotg210_port_speed(struct fotg210_hcd *fotg210, unsigned int portsc) -+{ -+ switch (fotg210_get_speed(fotg210, portsc)) { -+ case 0: -+ return 0; -+ case 1: -+ return USB_PORT_STAT_LOW_SPEED; -+ case 2: -+ default: -+ return USB_PORT_STAT_HIGH_SPEED; -+ } -+} -+ -+/*-------------------------------------------------------------------------*/ -+ -+#define fotg210_has_fsl_portno_bug(e) (0) -+ -+/* -+ * While most USB host controllers implement their registers in -+ * little-endian format, a minority (celleb companion chip) implement -+ * them in big endian format. -+ * -+ * This attempts to support either format at compile time without a -+ * runtime penalty, or both formats with the additional overhead -+ * of checking a flag bit. -+ * -+ */ -+ -+#define fotg210_big_endian_mmio(e) 0 -+#define fotg210_big_endian_capbase(e) 0 -+ -+static inline unsigned int fotg210_readl(const struct fotg210_hcd *fotg210, -+ __u32 __iomem *regs) -+{ -+ return readl(regs); -+} -+ -+static inline void fotg210_writel(const struct fotg210_hcd *fotg210, -+ const unsigned int val, __u32 __iomem *regs) -+{ -+ writel(val, regs); -+} -+ -+/* cpu to fotg210 */ -+static inline __hc32 cpu_to_hc32(const struct fotg210_hcd *fotg210, const u32 x) -+{ -+ return cpu_to_le32(x); -+} -+ -+/* fotg210 to cpu */ -+static inline u32 hc32_to_cpu(const struct fotg210_hcd *fotg210, const __hc32 x) -+{ -+ return le32_to_cpu(x); -+} -+ -+static inline u32 hc32_to_cpup(const struct fotg210_hcd *fotg210, -+ const __hc32 *x) -+{ -+ return le32_to_cpup(x); -+} -+ -+/*-------------------------------------------------------------------------*/ -+ -+static inline unsigned fotg210_read_frame_index(struct fotg210_hcd *fotg210) -+{ -+ return fotg210_readl(fotg210, &fotg210->regs->frame_index); -+} -+ -+/*-------------------------------------------------------------------------*/ -+ -+#endif /* __LINUX_FOTG210_H */ ---- /dev/null -+++ b/drivers/usb/fotg210/fotg210-udc.h -@@ -0,0 +1,249 @@ -+// SPDX-License-Identifier: GPL-2.0+ -+/* -+ * Faraday FOTG210 USB OTG controller -+ * -+ * Copyright (C) 2013 Faraday Technology Corporation -+ * Author: Yuan-Hsin Chen -+ */ -+ -+#include -+ -+#define FOTG210_MAX_NUM_EP 5 /* ep0...ep4 */ -+#define FOTG210_MAX_FIFO_NUM 4 /* fifo0...fifo4 */ -+ -+/* Global Mask of HC/OTG/DEV interrupt Register(0xC4) */ -+#define FOTG210_GMIR 0xC4 -+#define GMIR_INT_POLARITY 0x8 /*Active High*/ -+#define GMIR_MHC_INT 0x4 -+#define GMIR_MOTG_INT 0x2 -+#define GMIR_MDEV_INT 0x1 -+ -+/* Device Main Control Register(0x100) */ -+#define FOTG210_DMCR 0x100 -+#define DMCR_HS_EN (1 << 6) -+#define DMCR_CHIP_EN (1 << 5) -+#define DMCR_SFRST (1 << 4) -+#define DMCR_GOSUSP (1 << 3) -+#define DMCR_GLINT_EN (1 << 2) -+#define DMCR_HALF_SPEED (1 << 1) -+#define DMCR_CAP_RMWAKUP (1 << 0) -+ -+/* Device Address Register(0x104) */ -+#define FOTG210_DAR 0x104 -+#define DAR_AFT_CONF (1 << 7) -+ -+/* Device Test Register(0x108) */ -+#define FOTG210_DTR 0x108 -+#define DTR_TST_CLRFF (1 << 0) -+ -+/* PHY Test Mode Selector register(0x114) */ -+#define FOTG210_PHYTMSR 0x114 -+#define PHYTMSR_TST_PKT (1 << 4) -+#define PHYTMSR_TST_SE0NAK (1 << 3) -+#define PHYTMSR_TST_KSTA (1 << 2) -+#define PHYTMSR_TST_JSTA (1 << 1) -+#define PHYTMSR_UNPLUG (1 << 0) -+ -+/* Cx configuration and FIFO Empty Status register(0x120) */ -+#define FOTG210_DCFESR 0x120 -+#define DCFESR_FIFO_EMPTY(fifo) (1 << 8 << (fifo)) -+#define DCFESR_CX_EMP (1 << 5) -+#define DCFESR_CX_CLR (1 << 3) -+#define DCFESR_CX_STL (1 << 2) -+#define DCFESR_TST_PKDONE (1 << 1) -+#define DCFESR_CX_DONE (1 << 0) -+ -+/* Device IDLE Counter Register(0x124) */ -+#define FOTG210_DICR 0x124 -+ -+/* Device Mask of Interrupt Group Register (0x130) */ -+#define FOTG210_DMIGR 0x130 -+#define DMIGR_MINT_G0 (1 << 0) -+ -+/* Device Mask of Interrupt Source Group 0(0x134) */ -+#define FOTG210_DMISGR0 0x134 -+#define DMISGR0_MCX_COMEND (1 << 3) -+#define DMISGR0_MCX_OUT_INT (1 << 2) -+#define DMISGR0_MCX_IN_INT (1 << 1) -+#define DMISGR0_MCX_SETUP_INT (1 << 0) -+ -+/* Device Mask of Interrupt Source Group 1 Register(0x138)*/ -+#define FOTG210_DMISGR1 0x138 -+#define DMISGR1_MF3_IN_INT (1 << 19) -+#define DMISGR1_MF2_IN_INT (1 << 18) -+#define DMISGR1_MF1_IN_INT (1 << 17) -+#define DMISGR1_MF0_IN_INT (1 << 16) -+#define DMISGR1_MF_IN_INT(fifo) (1 << (16 + (fifo))) -+#define DMISGR1_MF3_SPK_INT (1 << 7) -+#define DMISGR1_MF3_OUT_INT (1 << 6) -+#define DMISGR1_MF2_SPK_INT (1 << 5) -+#define DMISGR1_MF2_OUT_INT (1 << 4) -+#define DMISGR1_MF1_SPK_INT (1 << 3) -+#define DMISGR1_MF1_OUT_INT (1 << 2) -+#define DMISGR1_MF0_SPK_INT (1 << 1) -+#define DMISGR1_MF0_OUT_INT (1 << 0) -+#define DMISGR1_MF_OUTSPK_INT(fifo) (0x3 << (fifo) * 2) -+ -+/* Device Mask of Interrupt Source Group 2 Register (0x13C) */ -+#define FOTG210_DMISGR2 0x13C -+#define DMISGR2_MDMA_ERROR (1 << 8) -+#define DMISGR2_MDMA_CMPLT (1 << 7) -+ -+/* Device Interrupt group Register (0x140) */ -+#define FOTG210_DIGR 0x140 -+#define DIGR_INT_G2 (1 << 2) -+#define DIGR_INT_G1 (1 << 1) -+#define DIGR_INT_G0 (1 << 0) -+ -+/* Device Interrupt Source Group 0 Register (0x144) */ -+#define FOTG210_DISGR0 0x144 -+#define DISGR0_CX_COMABT_INT (1 << 5) -+#define DISGR0_CX_COMFAIL_INT (1 << 4) -+#define DISGR0_CX_COMEND_INT (1 << 3) -+#define DISGR0_CX_OUT_INT (1 << 2) -+#define DISGR0_CX_IN_INT (1 << 1) -+#define DISGR0_CX_SETUP_INT (1 << 0) -+ -+/* Device Interrupt Source Group 1 Register (0x148) */ -+#define FOTG210_DISGR1 0x148 -+#define DISGR1_OUT_INT(fifo) (1 << ((fifo) * 2)) -+#define DISGR1_SPK_INT(fifo) (1 << 1 << ((fifo) * 2)) -+#define DISGR1_IN_INT(fifo) (1 << 16 << (fifo)) -+ -+/* Device Interrupt Source Group 2 Register (0x14C) */ -+#define FOTG210_DISGR2 0x14C -+#define DISGR2_DMA_ERROR (1 << 8) -+#define DISGR2_DMA_CMPLT (1 << 7) -+#define DISGR2_RX0BYTE_INT (1 << 6) -+#define DISGR2_TX0BYTE_INT (1 << 5) -+#define DISGR2_ISO_SEQ_ABORT_INT (1 << 4) -+#define DISGR2_ISO_SEQ_ERR_INT (1 << 3) -+#define DISGR2_RESM_INT (1 << 2) -+#define DISGR2_SUSP_INT (1 << 1) -+#define DISGR2_USBRST_INT (1 << 0) -+ -+/* Device Receive Zero-Length Data Packet Register (0x150)*/ -+#define FOTG210_RX0BYTE 0x150 -+#define RX0BYTE_EP8 (1 << 7) -+#define RX0BYTE_EP7 (1 << 6) -+#define RX0BYTE_EP6 (1 << 5) -+#define RX0BYTE_EP5 (1 << 4) -+#define RX0BYTE_EP4 (1 << 3) -+#define RX0BYTE_EP3 (1 << 2) -+#define RX0BYTE_EP2 (1 << 1) -+#define RX0BYTE_EP1 (1 << 0) -+ -+/* Device Transfer Zero-Length Data Packet Register (0x154)*/ -+#define FOTG210_TX0BYTE 0x154 -+#define TX0BYTE_EP8 (1 << 7) -+#define TX0BYTE_EP7 (1 << 6) -+#define TX0BYTE_EP6 (1 << 5) -+#define TX0BYTE_EP5 (1 << 4) -+#define TX0BYTE_EP4 (1 << 3) -+#define TX0BYTE_EP3 (1 << 2) -+#define TX0BYTE_EP2 (1 << 1) -+#define TX0BYTE_EP1 (1 << 0) -+ -+/* Device IN Endpoint x MaxPacketSize Register(0x160+4*(x-1)) */ -+#define FOTG210_INEPMPSR(ep) (0x160 + 4 * ((ep) - 1)) -+#define INOUTEPMPSR_MPS(mps) ((mps) & 0x2FF) -+#define INOUTEPMPSR_STL_EP (1 << 11) -+#define INOUTEPMPSR_RESET_TSEQ (1 << 12) -+ -+/* Device OUT Endpoint x MaxPacketSize Register(0x180+4*(x-1)) */ -+#define FOTG210_OUTEPMPSR(ep) (0x180 + 4 * ((ep) - 1)) -+ -+/* Device Endpoint 1~4 Map Register (0x1A0) */ -+#define FOTG210_EPMAP 0x1A0 -+#define EPMAP_FIFONO(ep, dir) \ -+ ((((ep) - 1) << ((ep) - 1) * 8) << ((dir) ? 0 : 4)) -+#define EPMAP_FIFONOMSK(ep, dir) \ -+ ((3 << ((ep) - 1) * 8) << ((dir) ? 0 : 4)) -+ -+/* Device FIFO Map Register (0x1A8) */ -+#define FOTG210_FIFOMAP 0x1A8 -+#define FIFOMAP_DIROUT(fifo) (0x0 << 4 << (fifo) * 8) -+#define FIFOMAP_DIRIN(fifo) (0x1 << 4 << (fifo) * 8) -+#define FIFOMAP_BIDIR(fifo) (0x2 << 4 << (fifo) * 8) -+#define FIFOMAP_NA(fifo) (0x3 << 4 << (fifo) * 8) -+#define FIFOMAP_EPNO(ep) ((ep) << ((ep) - 1) * 8) -+#define FIFOMAP_EPNOMSK(ep) (0xF << ((ep) - 1) * 8) -+ -+/* Device FIFO Confuguration Register (0x1AC) */ -+#define FOTG210_FIFOCF 0x1AC -+#define FIFOCF_TYPE(type, fifo) ((type) << (fifo) * 8) -+#define FIFOCF_BLK_SIN(fifo) (0x0 << (fifo) * 8 << 2) -+#define FIFOCF_BLK_DUB(fifo) (0x1 << (fifo) * 8 << 2) -+#define FIFOCF_BLK_TRI(fifo) (0x2 << (fifo) * 8 << 2) -+#define FIFOCF_BLKSZ_512(fifo) (0x0 << (fifo) * 8 << 4) -+#define FIFOCF_BLKSZ_1024(fifo) (0x1 << (fifo) * 8 << 4) -+#define FIFOCF_FIFO_EN(fifo) (0x1 << (fifo) * 8 << 5) -+ -+/* Device FIFO n Instruction and Byte Count Register (0x1B0+4*n) */ -+#define FOTG210_FIBCR(fifo) (0x1B0 + (fifo) * 4) -+#define FIBCR_BCFX 0x7FF -+#define FIBCR_FFRST (1 << 12) -+ -+/* Device DMA Target FIFO Number Register (0x1C0) */ -+#define FOTG210_DMATFNR 0x1C0 -+#define DMATFNR_ACC_CXF (1 << 4) -+#define DMATFNR_ACC_F3 (1 << 3) -+#define DMATFNR_ACC_F2 (1 << 2) -+#define DMATFNR_ACC_F1 (1 << 1) -+#define DMATFNR_ACC_F0 (1 << 0) -+#define DMATFNR_ACC_FN(fifo) (1 << (fifo)) -+#define DMATFNR_DISDMA 0 -+ -+/* Device DMA Controller Parameter setting 1 Register (0x1C8) */ -+#define FOTG210_DMACPSR1 0x1C8 -+#define DMACPSR1_DMA_LEN(len) (((len) & 0xFFFF) << 8) -+#define DMACPSR1_DMA_ABORT (1 << 3) -+#define DMACPSR1_DMA_TYPE(dir_in) (((dir_in) ? 1 : 0) << 1) -+#define DMACPSR1_DMA_START (1 << 0) -+ -+/* Device DMA Controller Parameter setting 2 Register (0x1CC) */ -+#define FOTG210_DMACPSR2 0x1CC -+ -+/* Device DMA Controller Parameter setting 3 Register (0x1CC) */ -+#define FOTG210_CXPORT 0x1D0 -+ -+struct fotg210_request { -+ struct usb_request req; -+ struct list_head queue; -+}; -+ -+struct fotg210_ep { -+ struct usb_ep ep; -+ struct fotg210_udc *fotg210; -+ -+ struct list_head queue; -+ unsigned stall:1; -+ unsigned wedged:1; -+ unsigned use_dma:1; -+ -+ unsigned char epnum; -+ unsigned char type; -+ unsigned char dir_in; -+ unsigned int maxp; -+ const struct usb_endpoint_descriptor *desc; -+}; -+ -+struct fotg210_udc { -+ spinlock_t lock; /* protect the struct */ -+ void __iomem *reg; -+ -+ unsigned long irq_trigger; -+ -+ struct usb_gadget gadget; -+ struct usb_gadget_driver *driver; -+ -+ struct fotg210_ep *ep[FOTG210_MAX_NUM_EP]; -+ -+ struct usb_request *ep0_req; /* for internal request */ -+ __le16 ep0_data; -+ u8 ep0_dir; /* 0/0x80 out/in */ -+ -+ u8 reenum; /* if re-enumeration */ -+}; -+ -+#define gadget_to_fotg210(g) container_of((g), struct fotg210_udc, gadget) ---- a/drivers/usb/gadget/udc/fotg210.h -+++ /dev/null -@@ -1,249 +0,0 @@ --// SPDX-License-Identifier: GPL-2.0+ --/* -- * Faraday FOTG210 USB OTG controller -- * -- * Copyright (C) 2013 Faraday Technology Corporation -- * Author: Yuan-Hsin Chen -- */ -- --#include -- --#define FOTG210_MAX_NUM_EP 5 /* ep0...ep4 */ --#define FOTG210_MAX_FIFO_NUM 4 /* fifo0...fifo4 */ -- --/* Global Mask of HC/OTG/DEV interrupt Register(0xC4) */ --#define FOTG210_GMIR 0xC4 --#define GMIR_INT_POLARITY 0x8 /*Active High*/ --#define GMIR_MHC_INT 0x4 --#define GMIR_MOTG_INT 0x2 --#define GMIR_MDEV_INT 0x1 -- --/* Device Main Control Register(0x100) */ --#define FOTG210_DMCR 0x100 --#define DMCR_HS_EN (1 << 6) --#define DMCR_CHIP_EN (1 << 5) --#define DMCR_SFRST (1 << 4) --#define DMCR_GOSUSP (1 << 3) --#define DMCR_GLINT_EN (1 << 2) --#define DMCR_HALF_SPEED (1 << 1) --#define DMCR_CAP_RMWAKUP (1 << 0) -- --/* Device Address Register(0x104) */ --#define FOTG210_DAR 0x104 --#define DAR_AFT_CONF (1 << 7) -- --/* Device Test Register(0x108) */ --#define FOTG210_DTR 0x108 --#define DTR_TST_CLRFF (1 << 0) -- --/* PHY Test Mode Selector register(0x114) */ --#define FOTG210_PHYTMSR 0x114 --#define PHYTMSR_TST_PKT (1 << 4) --#define PHYTMSR_TST_SE0NAK (1 << 3) --#define PHYTMSR_TST_KSTA (1 << 2) --#define PHYTMSR_TST_JSTA (1 << 1) --#define PHYTMSR_UNPLUG (1 << 0) -- --/* Cx configuration and FIFO Empty Status register(0x120) */ --#define FOTG210_DCFESR 0x120 --#define DCFESR_FIFO_EMPTY(fifo) (1 << 8 << (fifo)) --#define DCFESR_CX_EMP (1 << 5) --#define DCFESR_CX_CLR (1 << 3) --#define DCFESR_CX_STL (1 << 2) --#define DCFESR_TST_PKDONE (1 << 1) --#define DCFESR_CX_DONE (1 << 0) -- --/* Device IDLE Counter Register(0x124) */ --#define FOTG210_DICR 0x124 -- --/* Device Mask of Interrupt Group Register (0x130) */ --#define FOTG210_DMIGR 0x130 --#define DMIGR_MINT_G0 (1 << 0) -- --/* Device Mask of Interrupt Source Group 0(0x134) */ --#define FOTG210_DMISGR0 0x134 --#define DMISGR0_MCX_COMEND (1 << 3) --#define DMISGR0_MCX_OUT_INT (1 << 2) --#define DMISGR0_MCX_IN_INT (1 << 1) --#define DMISGR0_MCX_SETUP_INT (1 << 0) -- --/* Device Mask of Interrupt Source Group 1 Register(0x138)*/ --#define FOTG210_DMISGR1 0x138 --#define DMISGR1_MF3_IN_INT (1 << 19) --#define DMISGR1_MF2_IN_INT (1 << 18) --#define DMISGR1_MF1_IN_INT (1 << 17) --#define DMISGR1_MF0_IN_INT (1 << 16) --#define DMISGR1_MF_IN_INT(fifo) (1 << (16 + (fifo))) --#define DMISGR1_MF3_SPK_INT (1 << 7) --#define DMISGR1_MF3_OUT_INT (1 << 6) --#define DMISGR1_MF2_SPK_INT (1 << 5) --#define DMISGR1_MF2_OUT_INT (1 << 4) --#define DMISGR1_MF1_SPK_INT (1 << 3) --#define DMISGR1_MF1_OUT_INT (1 << 2) --#define DMISGR1_MF0_SPK_INT (1 << 1) --#define DMISGR1_MF0_OUT_INT (1 << 0) --#define DMISGR1_MF_OUTSPK_INT(fifo) (0x3 << (fifo) * 2) -- --/* Device Mask of Interrupt Source Group 2 Register (0x13C) */ --#define FOTG210_DMISGR2 0x13C --#define DMISGR2_MDMA_ERROR (1 << 8) --#define DMISGR2_MDMA_CMPLT (1 << 7) -- --/* Device Interrupt group Register (0x140) */ --#define FOTG210_DIGR 0x140 --#define DIGR_INT_G2 (1 << 2) --#define DIGR_INT_G1 (1 << 1) --#define DIGR_INT_G0 (1 << 0) -- --/* Device Interrupt Source Group 0 Register (0x144) */ --#define FOTG210_DISGR0 0x144 --#define DISGR0_CX_COMABT_INT (1 << 5) --#define DISGR0_CX_COMFAIL_INT (1 << 4) --#define DISGR0_CX_COMEND_INT (1 << 3) --#define DISGR0_CX_OUT_INT (1 << 2) --#define DISGR0_CX_IN_INT (1 << 1) --#define DISGR0_CX_SETUP_INT (1 << 0) -- --/* Device Interrupt Source Group 1 Register (0x148) */ --#define FOTG210_DISGR1 0x148 --#define DISGR1_OUT_INT(fifo) (1 << ((fifo) * 2)) --#define DISGR1_SPK_INT(fifo) (1 << 1 << ((fifo) * 2)) --#define DISGR1_IN_INT(fifo) (1 << 16 << (fifo)) -- --/* Device Interrupt Source Group 2 Register (0x14C) */ --#define FOTG210_DISGR2 0x14C --#define DISGR2_DMA_ERROR (1 << 8) --#define DISGR2_DMA_CMPLT (1 << 7) --#define DISGR2_RX0BYTE_INT (1 << 6) --#define DISGR2_TX0BYTE_INT (1 << 5) --#define DISGR2_ISO_SEQ_ABORT_INT (1 << 4) --#define DISGR2_ISO_SEQ_ERR_INT (1 << 3) --#define DISGR2_RESM_INT (1 << 2) --#define DISGR2_SUSP_INT (1 << 1) --#define DISGR2_USBRST_INT (1 << 0) -- --/* Device Receive Zero-Length Data Packet Register (0x150)*/ --#define FOTG210_RX0BYTE 0x150 --#define RX0BYTE_EP8 (1 << 7) --#define RX0BYTE_EP7 (1 << 6) --#define RX0BYTE_EP6 (1 << 5) --#define RX0BYTE_EP5 (1 << 4) --#define RX0BYTE_EP4 (1 << 3) --#define RX0BYTE_EP3 (1 << 2) --#define RX0BYTE_EP2 (1 << 1) --#define RX0BYTE_EP1 (1 << 0) -- --/* Device Transfer Zero-Length Data Packet Register (0x154)*/ --#define FOTG210_TX0BYTE 0x154 --#define TX0BYTE_EP8 (1 << 7) --#define TX0BYTE_EP7 (1 << 6) --#define TX0BYTE_EP6 (1 << 5) --#define TX0BYTE_EP5 (1 << 4) --#define TX0BYTE_EP4 (1 << 3) --#define TX0BYTE_EP3 (1 << 2) --#define TX0BYTE_EP2 (1 << 1) --#define TX0BYTE_EP1 (1 << 0) -- --/* Device IN Endpoint x MaxPacketSize Register(0x160+4*(x-1)) */ --#define FOTG210_INEPMPSR(ep) (0x160 + 4 * ((ep) - 1)) --#define INOUTEPMPSR_MPS(mps) ((mps) & 0x2FF) --#define INOUTEPMPSR_STL_EP (1 << 11) --#define INOUTEPMPSR_RESET_TSEQ (1 << 12) -- --/* Device OUT Endpoint x MaxPacketSize Register(0x180+4*(x-1)) */ --#define FOTG210_OUTEPMPSR(ep) (0x180 + 4 * ((ep) - 1)) -- --/* Device Endpoint 1~4 Map Register (0x1A0) */ --#define FOTG210_EPMAP 0x1A0 --#define EPMAP_FIFONO(ep, dir) \ -- ((((ep) - 1) << ((ep) - 1) * 8) << ((dir) ? 0 : 4)) --#define EPMAP_FIFONOMSK(ep, dir) \ -- ((3 << ((ep) - 1) * 8) << ((dir) ? 0 : 4)) -- --/* Device FIFO Map Register (0x1A8) */ --#define FOTG210_FIFOMAP 0x1A8 --#define FIFOMAP_DIROUT(fifo) (0x0 << 4 << (fifo) * 8) --#define FIFOMAP_DIRIN(fifo) (0x1 << 4 << (fifo) * 8) --#define FIFOMAP_BIDIR(fifo) (0x2 << 4 << (fifo) * 8) --#define FIFOMAP_NA(fifo) (0x3 << 4 << (fifo) * 8) --#define FIFOMAP_EPNO(ep) ((ep) << ((ep) - 1) * 8) --#define FIFOMAP_EPNOMSK(ep) (0xF << ((ep) - 1) * 8) -- --/* Device FIFO Confuguration Register (0x1AC) */ --#define FOTG210_FIFOCF 0x1AC --#define FIFOCF_TYPE(type, fifo) ((type) << (fifo) * 8) --#define FIFOCF_BLK_SIN(fifo) (0x0 << (fifo) * 8 << 2) --#define FIFOCF_BLK_DUB(fifo) (0x1 << (fifo) * 8 << 2) --#define FIFOCF_BLK_TRI(fifo) (0x2 << (fifo) * 8 << 2) --#define FIFOCF_BLKSZ_512(fifo) (0x0 << (fifo) * 8 << 4) --#define FIFOCF_BLKSZ_1024(fifo) (0x1 << (fifo) * 8 << 4) --#define FIFOCF_FIFO_EN(fifo) (0x1 << (fifo) * 8 << 5) -- --/* Device FIFO n Instruction and Byte Count Register (0x1B0+4*n) */ --#define FOTG210_FIBCR(fifo) (0x1B0 + (fifo) * 4) --#define FIBCR_BCFX 0x7FF --#define FIBCR_FFRST (1 << 12) -- --/* Device DMA Target FIFO Number Register (0x1C0) */ --#define FOTG210_DMATFNR 0x1C0 --#define DMATFNR_ACC_CXF (1 << 4) --#define DMATFNR_ACC_F3 (1 << 3) --#define DMATFNR_ACC_F2 (1 << 2) --#define DMATFNR_ACC_F1 (1 << 1) --#define DMATFNR_ACC_F0 (1 << 0) --#define DMATFNR_ACC_FN(fifo) (1 << (fifo)) --#define DMATFNR_DISDMA 0 -- --/* Device DMA Controller Parameter setting 1 Register (0x1C8) */ --#define FOTG210_DMACPSR1 0x1C8 --#define DMACPSR1_DMA_LEN(len) (((len) & 0xFFFF) << 8) --#define DMACPSR1_DMA_ABORT (1 << 3) --#define DMACPSR1_DMA_TYPE(dir_in) (((dir_in) ? 1 : 0) << 1) --#define DMACPSR1_DMA_START (1 << 0) -- --/* Device DMA Controller Parameter setting 2 Register (0x1CC) */ --#define FOTG210_DMACPSR2 0x1CC -- --/* Device DMA Controller Parameter setting 3 Register (0x1CC) */ --#define FOTG210_CXPORT 0x1D0 -- --struct fotg210_request { -- struct usb_request req; -- struct list_head queue; --}; -- --struct fotg210_ep { -- struct usb_ep ep; -- struct fotg210_udc *fotg210; -- -- struct list_head queue; -- unsigned stall:1; -- unsigned wedged:1; -- unsigned use_dma:1; -- -- unsigned char epnum; -- unsigned char type; -- unsigned char dir_in; -- unsigned int maxp; -- const struct usb_endpoint_descriptor *desc; --}; -- --struct fotg210_udc { -- spinlock_t lock; /* protect the struct */ -- void __iomem *reg; -- -- unsigned long irq_trigger; -- -- struct usb_gadget gadget; -- struct usb_gadget_driver *driver; -- -- struct fotg210_ep *ep[FOTG210_MAX_NUM_EP]; -- -- struct usb_request *ep0_req; /* for internal request */ -- __le16 ep0_data; -- u8 ep0_dir; /* 0/0x80 out/in */ -- -- u8 reenum; /* if re-enumeration */ --}; -- --#define gadget_to_fotg210(g) container_of((g), struct fotg210_udc, gadget) ---- a/drivers/usb/host/fotg210.h -+++ /dev/null -@@ -1,688 +0,0 @@ --/* SPDX-License-Identifier: GPL-2.0 */ --#ifndef __LINUX_FOTG210_H --#define __LINUX_FOTG210_H -- --#include -- --/* definitions used for the EHCI driver */ -- --/* -- * __hc32 and __hc16 are "Host Controller" types, they may be equivalent to -- * __leXX (normally) or __beXX (given FOTG210_BIG_ENDIAN_DESC), depending on -- * the host controller implementation. -- * -- * To facilitate the strongest possible byte-order checking from "sparse" -- * and so on, we use __leXX unless that's not practical. -- */ --#define __hc32 __le32 --#define __hc16 __le16 -- --/* statistics can be kept for tuning/monitoring */ --struct fotg210_stats { -- /* irq usage */ -- unsigned long normal; -- unsigned long error; -- unsigned long iaa; -- unsigned long lost_iaa; -- -- /* termination of urbs from core */ -- unsigned long complete; -- unsigned long unlink; --}; -- --/* fotg210_hcd->lock guards shared data against other CPUs: -- * fotg210_hcd: async, unlink, periodic (and shadow), ... -- * usb_host_endpoint: hcpriv -- * fotg210_qh: qh_next, qtd_list -- * fotg210_qtd: qtd_list -- * -- * Also, hold this lock when talking to HC registers or -- * when updating hw_* fields in shared qh/qtd/... structures. -- */ -- --#define FOTG210_MAX_ROOT_PORTS 1 /* see HCS_N_PORTS */ -- --/* -- * fotg210_rh_state values of FOTG210_RH_RUNNING or above mean that the -- * controller may be doing DMA. Lower values mean there's no DMA. -- */ --enum fotg210_rh_state { -- FOTG210_RH_HALTED, -- FOTG210_RH_SUSPENDED, -- FOTG210_RH_RUNNING, -- FOTG210_RH_STOPPING --}; -- --/* -- * Timer events, ordered by increasing delay length. -- * Always update event_delays_ns[] and event_handlers[] (defined in -- * ehci-timer.c) in parallel with this list. -- */ --enum fotg210_hrtimer_event { -- FOTG210_HRTIMER_POLL_ASS, /* Poll for async schedule off */ -- FOTG210_HRTIMER_POLL_PSS, /* Poll for periodic schedule off */ -- FOTG210_HRTIMER_POLL_DEAD, /* Wait for dead controller to stop */ -- FOTG210_HRTIMER_UNLINK_INTR, /* Wait for interrupt QH unlink */ -- FOTG210_HRTIMER_FREE_ITDS, /* Wait for unused iTDs and siTDs */ -- FOTG210_HRTIMER_ASYNC_UNLINKS, /* Unlink empty async QHs */ -- FOTG210_HRTIMER_IAA_WATCHDOG, /* Handle lost IAA interrupts */ -- FOTG210_HRTIMER_DISABLE_PERIODIC, /* Wait to disable periodic sched */ -- FOTG210_HRTIMER_DISABLE_ASYNC, /* Wait to disable async sched */ -- FOTG210_HRTIMER_IO_WATCHDOG, /* Check for missing IRQs */ -- FOTG210_HRTIMER_NUM_EVENTS /* Must come last */ --}; --#define FOTG210_HRTIMER_NO_EVENT 99 -- --struct fotg210_hcd { /* one per controller */ -- /* timing support */ -- enum fotg210_hrtimer_event next_hrtimer_event; -- unsigned enabled_hrtimer_events; -- ktime_t hr_timeouts[FOTG210_HRTIMER_NUM_EVENTS]; -- struct hrtimer hrtimer; -- -- int PSS_poll_count; -- int ASS_poll_count; -- int died_poll_count; -- -- /* glue to PCI and HCD framework */ -- struct fotg210_caps __iomem *caps; -- struct fotg210_regs __iomem *regs; -- struct ehci_dbg_port __iomem *debug; -- -- __u32 hcs_params; /* cached register copy */ -- spinlock_t lock; -- enum fotg210_rh_state rh_state; -- -- /* general schedule support */ -- bool scanning:1; -- bool need_rescan:1; -- bool intr_unlinking:1; -- bool async_unlinking:1; -- bool shutdown:1; -- struct fotg210_qh *qh_scan_next; -- -- /* async schedule support */ -- struct fotg210_qh *async; -- struct fotg210_qh *dummy; /* For AMD quirk use */ -- struct fotg210_qh *async_unlink; -- struct fotg210_qh *async_unlink_last; -- struct fotg210_qh *async_iaa; -- unsigned async_unlink_cycle; -- unsigned async_count; /* async activity count */ -- -- /* periodic schedule support */ --#define DEFAULT_I_TDPS 1024 /* some HCs can do less */ -- unsigned periodic_size; -- __hc32 *periodic; /* hw periodic table */ -- dma_addr_t periodic_dma; -- struct list_head intr_qh_list; -- unsigned i_thresh; /* uframes HC might cache */ -- -- union fotg210_shadow *pshadow; /* mirror hw periodic table */ -- struct fotg210_qh *intr_unlink; -- struct fotg210_qh *intr_unlink_last; -- unsigned intr_unlink_cycle; -- unsigned now_frame; /* frame from HC hardware */ -- unsigned next_frame; /* scan periodic, start here */ -- unsigned intr_count; /* intr activity count */ -- unsigned isoc_count; /* isoc activity count */ -- unsigned periodic_count; /* periodic activity count */ -- /* max periodic time per uframe */ -- unsigned uframe_periodic_max; -- -- -- /* list of itds completed while now_frame was still active */ -- struct list_head cached_itd_list; -- struct fotg210_itd *last_itd_to_free; -- -- /* per root hub port */ -- unsigned long reset_done[FOTG210_MAX_ROOT_PORTS]; -- -- /* bit vectors (one bit per port) -- * which ports were already suspended at the start of a bus suspend -- */ -- unsigned long bus_suspended; -- -- /* which ports are edicated to the companion controller */ -- unsigned long companion_ports; -- -- /* which ports are owned by the companion during a bus suspend */ -- unsigned long owned_ports; -- -- /* which ports have the change-suspend feature turned on */ -- unsigned long port_c_suspend; -- -- /* which ports are suspended */ -- unsigned long suspended_ports; -- -- /* which ports have started to resume */ -- unsigned long resuming_ports; -- -- /* per-HC memory pools (could be per-bus, but ...) */ -- struct dma_pool *qh_pool; /* qh per active urb */ -- struct dma_pool *qtd_pool; /* one or more per qh */ -- struct dma_pool *itd_pool; /* itd per iso urb */ -- -- unsigned random_frame; -- unsigned long next_statechange; -- ktime_t last_periodic_enable; -- u32 command; -- -- /* SILICON QUIRKS */ -- unsigned need_io_watchdog:1; -- unsigned fs_i_thresh:1; /* Intel iso scheduling */ -- -- u8 sbrn; /* packed release number */ -- -- /* irq statistics */ --#ifdef FOTG210_STATS -- struct fotg210_stats stats; --# define INCR(x) ((x)++) --#else --# define INCR(x) do {} while (0) --#endif -- -- /* silicon clock */ -- struct clk *pclk; --}; -- --/* convert between an HCD pointer and the corresponding FOTG210_HCD */ --static inline struct fotg210_hcd *hcd_to_fotg210(struct usb_hcd *hcd) --{ -- return (struct fotg210_hcd *)(hcd->hcd_priv); --} --static inline struct usb_hcd *fotg210_to_hcd(struct fotg210_hcd *fotg210) --{ -- return container_of((void *) fotg210, struct usb_hcd, hcd_priv); --} -- --/*-------------------------------------------------------------------------*/ -- --/* EHCI register interface, corresponds to EHCI Revision 0.95 specification */ -- --/* Section 2.2 Host Controller Capability Registers */ --struct fotg210_caps { -- /* these fields are specified as 8 and 16 bit registers, -- * but some hosts can't perform 8 or 16 bit PCI accesses. -- * some hosts treat caplength and hciversion as parts of a 32-bit -- * register, others treat them as two separate registers, this -- * affects the memory map for big endian controllers. -- */ -- u32 hc_capbase; --#define HC_LENGTH(fotg210, p) (0x00ff&((p) >> /* bits 7:0 / offset 00h */ \ -- (fotg210_big_endian_capbase(fotg210) ? 24 : 0))) --#define HC_VERSION(fotg210, p) (0xffff&((p) >> /* bits 31:16 / offset 02h */ \ -- (fotg210_big_endian_capbase(fotg210) ? 0 : 16))) -- u32 hcs_params; /* HCSPARAMS - offset 0x4 */ --#define HCS_N_PORTS(p) (((p)>>0)&0xf) /* bits 3:0, ports on HC */ -- -- u32 hcc_params; /* HCCPARAMS - offset 0x8 */ --#define HCC_CANPARK(p) ((p)&(1 << 2)) /* true: can park on async qh */ --#define HCC_PGM_FRAMELISTLEN(p) ((p)&(1 << 1)) /* true: periodic_size changes*/ -- u8 portroute[8]; /* nibbles for routing - offset 0xC */ --}; -- -- --/* Section 2.3 Host Controller Operational Registers */ --struct fotg210_regs { -- -- /* USBCMD: offset 0x00 */ -- u32 command; -- --/* EHCI 1.1 addendum */ --/* 23:16 is r/w intr rate, in microframes; default "8" == 1/msec */ --#define CMD_PARK (1<<11) /* enable "park" on async qh */ --#define CMD_PARK_CNT(c) (((c)>>8)&3) /* how many transfers to park for */ --#define CMD_IAAD (1<<6) /* "doorbell" interrupt async advance */ --#define CMD_ASE (1<<5) /* async schedule enable */ --#define CMD_PSE (1<<4) /* periodic schedule enable */ --/* 3:2 is periodic frame list size */ --#define CMD_RESET (1<<1) /* reset HC not bus */ --#define CMD_RUN (1<<0) /* start/stop HC */ -- -- /* USBSTS: offset 0x04 */ -- u32 status; --#define STS_ASS (1<<15) /* Async Schedule Status */ --#define STS_PSS (1<<14) /* Periodic Schedule Status */ --#define STS_RECL (1<<13) /* Reclamation */ --#define STS_HALT (1<<12) /* Not running (any reason) */ --/* some bits reserved */ -- /* these STS_* flags are also intr_enable bits (USBINTR) */ --#define STS_IAA (1<<5) /* Interrupted on async advance */ --#define STS_FATAL (1<<4) /* such as some PCI access errors */ --#define STS_FLR (1<<3) /* frame list rolled over */ --#define STS_PCD (1<<2) /* port change detect */ --#define STS_ERR (1<<1) /* "error" completion (overflow, ...) */ --#define STS_INT (1<<0) /* "normal" completion (short, ...) */ -- -- /* USBINTR: offset 0x08 */ -- u32 intr_enable; -- -- /* FRINDEX: offset 0x0C */ -- u32 frame_index; /* current microframe number */ -- /* CTRLDSSEGMENT: offset 0x10 */ -- u32 segment; /* address bits 63:32 if needed */ -- /* PERIODICLISTBASE: offset 0x14 */ -- u32 frame_list; /* points to periodic list */ -- /* ASYNCLISTADDR: offset 0x18 */ -- u32 async_next; /* address of next async queue head */ -- -- u32 reserved1; -- /* PORTSC: offset 0x20 */ -- u32 port_status; --/* 31:23 reserved */ --#define PORT_USB11(x) (((x)&(3<<10)) == (1<<10)) /* USB 1.1 device */ --#define PORT_RESET (1<<8) /* reset port */ --#define PORT_SUSPEND (1<<7) /* suspend port */ --#define PORT_RESUME (1<<6) /* resume it */ --#define PORT_PEC (1<<3) /* port enable change */ --#define PORT_PE (1<<2) /* port enable */ --#define PORT_CSC (1<<1) /* connect status change */ --#define PORT_CONNECT (1<<0) /* device connected */ --#define PORT_RWC_BITS (PORT_CSC | PORT_PEC) -- u32 reserved2[19]; -- -- /* OTGCSR: offet 0x70 */ -- u32 otgcsr; --#define OTGCSR_HOST_SPD_TYP (3 << 22) --#define OTGCSR_A_BUS_DROP (1 << 5) --#define OTGCSR_A_BUS_REQ (1 << 4) -- -- /* OTGISR: offset 0x74 */ -- u32 otgisr; --#define OTGISR_OVC (1 << 10) -- -- u32 reserved3[15]; -- -- /* GMIR: offset 0xB4 */ -- u32 gmir; --#define GMIR_INT_POLARITY (1 << 3) /*Active High*/ --#define GMIR_MHC_INT (1 << 2) --#define GMIR_MOTG_INT (1 << 1) --#define GMIR_MDEV_INT (1 << 0) --}; -- --/*-------------------------------------------------------------------------*/ -- --#define QTD_NEXT(fotg210, dma) cpu_to_hc32(fotg210, (u32)dma) -- --/* -- * EHCI Specification 0.95 Section 3.5 -- * QTD: describe data transfer components (buffer, direction, ...) -- * See Fig 3-6 "Queue Element Transfer Descriptor Block Diagram". -- * -- * These are associated only with "QH" (Queue Head) structures, -- * used with control, bulk, and interrupt transfers. -- */ --struct fotg210_qtd { -- /* first part defined by EHCI spec */ -- __hc32 hw_next; /* see EHCI 3.5.1 */ -- __hc32 hw_alt_next; /* see EHCI 3.5.2 */ -- __hc32 hw_token; /* see EHCI 3.5.3 */ --#define QTD_TOGGLE (1 << 31) /* data toggle */ --#define QTD_LENGTH(tok) (((tok)>>16) & 0x7fff) --#define QTD_IOC (1 << 15) /* interrupt on complete */ --#define QTD_CERR(tok) (((tok)>>10) & 0x3) --#define QTD_PID(tok) (((tok)>>8) & 0x3) --#define QTD_STS_ACTIVE (1 << 7) /* HC may execute this */ --#define QTD_STS_HALT (1 << 6) /* halted on error */ --#define QTD_STS_DBE (1 << 5) /* data buffer error (in HC) */ --#define QTD_STS_BABBLE (1 << 4) /* device was babbling (qtd halted) */ --#define QTD_STS_XACT (1 << 3) /* device gave illegal response */ --#define QTD_STS_MMF (1 << 2) /* incomplete split transaction */ --#define QTD_STS_STS (1 << 1) /* split transaction state */ --#define QTD_STS_PING (1 << 0) /* issue PING? */ -- --#define ACTIVE_BIT(fotg210) cpu_to_hc32(fotg210, QTD_STS_ACTIVE) --#define HALT_BIT(fotg210) cpu_to_hc32(fotg210, QTD_STS_HALT) --#define STATUS_BIT(fotg210) cpu_to_hc32(fotg210, QTD_STS_STS) -- -- __hc32 hw_buf[5]; /* see EHCI 3.5.4 */ -- __hc32 hw_buf_hi[5]; /* Appendix B */ -- -- /* the rest is HCD-private */ -- dma_addr_t qtd_dma; /* qtd address */ -- struct list_head qtd_list; /* sw qtd list */ -- struct urb *urb; /* qtd's urb */ -- size_t length; /* length of buffer */ --} __aligned(32); -- --/* mask NakCnt+T in qh->hw_alt_next */ --#define QTD_MASK(fotg210) cpu_to_hc32(fotg210, ~0x1f) -- --#define IS_SHORT_READ(token) (QTD_LENGTH(token) != 0 && QTD_PID(token) == 1) -- --/*-------------------------------------------------------------------------*/ -- --/* type tag from {qh,itd,fstn}->hw_next */ --#define Q_NEXT_TYPE(fotg210, dma) ((dma) & cpu_to_hc32(fotg210, 3 << 1)) -- --/* -- * Now the following defines are not converted using the -- * cpu_to_le32() macro anymore, since we have to support -- * "dynamic" switching between be and le support, so that the driver -- * can be used on one system with SoC EHCI controller using big-endian -- * descriptors as well as a normal little-endian PCI EHCI controller. -- */ --/* values for that type tag */ --#define Q_TYPE_ITD (0 << 1) --#define Q_TYPE_QH (1 << 1) --#define Q_TYPE_SITD (2 << 1) --#define Q_TYPE_FSTN (3 << 1) -- --/* next async queue entry, or pointer to interrupt/periodic QH */ --#define QH_NEXT(fotg210, dma) \ -- (cpu_to_hc32(fotg210, (((u32)dma)&~0x01f)|Q_TYPE_QH)) -- --/* for periodic/async schedules and qtd lists, mark end of list */ --#define FOTG210_LIST_END(fotg210) \ -- cpu_to_hc32(fotg210, 1) /* "null pointer" to hw */ -- --/* -- * Entries in periodic shadow table are pointers to one of four kinds -- * of data structure. That's dictated by the hardware; a type tag is -- * encoded in the low bits of the hardware's periodic schedule. Use -- * Q_NEXT_TYPE to get the tag. -- * -- * For entries in the async schedule, the type tag always says "qh". -- */ --union fotg210_shadow { -- struct fotg210_qh *qh; /* Q_TYPE_QH */ -- struct fotg210_itd *itd; /* Q_TYPE_ITD */ -- struct fotg210_fstn *fstn; /* Q_TYPE_FSTN */ -- __hc32 *hw_next; /* (all types) */ -- void *ptr; --}; -- --/*-------------------------------------------------------------------------*/ -- --/* -- * EHCI Specification 0.95 Section 3.6 -- * QH: describes control/bulk/interrupt endpoints -- * See Fig 3-7 "Queue Head Structure Layout". -- * -- * These appear in both the async and (for interrupt) periodic schedules. -- */ -- --/* first part defined by EHCI spec */ --struct fotg210_qh_hw { -- __hc32 hw_next; /* see EHCI 3.6.1 */ -- __hc32 hw_info1; /* see EHCI 3.6.2 */ --#define QH_CONTROL_EP (1 << 27) /* FS/LS control endpoint */ --#define QH_HEAD (1 << 15) /* Head of async reclamation list */ --#define QH_TOGGLE_CTL (1 << 14) /* Data toggle control */ --#define QH_HIGH_SPEED (2 << 12) /* Endpoint speed */ --#define QH_LOW_SPEED (1 << 12) --#define QH_FULL_SPEED (0 << 12) --#define QH_INACTIVATE (1 << 7) /* Inactivate on next transaction */ -- __hc32 hw_info2; /* see EHCI 3.6.2 */ --#define QH_SMASK 0x000000ff --#define QH_CMASK 0x0000ff00 --#define QH_HUBADDR 0x007f0000 --#define QH_HUBPORT 0x3f800000 --#define QH_MULT 0xc0000000 -- __hc32 hw_current; /* qtd list - see EHCI 3.6.4 */ -- -- /* qtd overlay (hardware parts of a struct fotg210_qtd) */ -- __hc32 hw_qtd_next; -- __hc32 hw_alt_next; -- __hc32 hw_token; -- __hc32 hw_buf[5]; -- __hc32 hw_buf_hi[5]; --} __aligned(32); -- --struct fotg210_qh { -- struct fotg210_qh_hw *hw; /* Must come first */ -- /* the rest is HCD-private */ -- dma_addr_t qh_dma; /* address of qh */ -- union fotg210_shadow qh_next; /* ptr to qh; or periodic */ -- struct list_head qtd_list; /* sw qtd list */ -- struct list_head intr_node; /* list of intr QHs */ -- struct fotg210_qtd *dummy; -- struct fotg210_qh *unlink_next; /* next on unlink list */ -- -- unsigned unlink_cycle; -- -- u8 needs_rescan; /* Dequeue during giveback */ -- u8 qh_state; --#define QH_STATE_LINKED 1 /* HC sees this */ --#define QH_STATE_UNLINK 2 /* HC may still see this */ --#define QH_STATE_IDLE 3 /* HC doesn't see this */ --#define QH_STATE_UNLINK_WAIT 4 /* LINKED and on unlink q */ --#define QH_STATE_COMPLETING 5 /* don't touch token.HALT */ -- -- u8 xacterrs; /* XactErr retry counter */ --#define QH_XACTERR_MAX 32 /* XactErr retry limit */ -- -- /* periodic schedule info */ -- u8 usecs; /* intr bandwidth */ -- u8 gap_uf; /* uframes split/csplit gap */ -- u8 c_usecs; /* ... split completion bw */ -- u16 tt_usecs; /* tt downstream bandwidth */ -- unsigned short period; /* polling interval */ -- unsigned short start; /* where polling starts */ --#define NO_FRAME ((unsigned short)~0) /* pick new start */ -- -- struct usb_device *dev; /* access to TT */ -- unsigned is_out:1; /* bulk or intr OUT */ -- unsigned clearing_tt:1; /* Clear-TT-Buf in progress */ --}; -- --/*-------------------------------------------------------------------------*/ -- --/* description of one iso transaction (up to 3 KB data if highspeed) */ --struct fotg210_iso_packet { -- /* These will be copied to iTD when scheduling */ -- u64 bufp; /* itd->hw_bufp{,_hi}[pg] |= */ -- __hc32 transaction; /* itd->hw_transaction[i] |= */ -- u8 cross; /* buf crosses pages */ -- /* for full speed OUT splits */ -- u32 buf1; --}; -- --/* temporary schedule data for packets from iso urbs (both speeds) -- * each packet is one logical usb transaction to the device (not TT), -- * beginning at stream->next_uframe -- */ --struct fotg210_iso_sched { -- struct list_head td_list; -- unsigned span; -- struct fotg210_iso_packet packet[]; --}; -- --/* -- * fotg210_iso_stream - groups all (s)itds for this endpoint. -- * acts like a qh would, if EHCI had them for ISO. -- */ --struct fotg210_iso_stream { -- /* first field matches fotg210_hq, but is NULL */ -- struct fotg210_qh_hw *hw; -- -- u8 bEndpointAddress; -- u8 highspeed; -- struct list_head td_list; /* queued itds */ -- struct list_head free_list; /* list of unused itds */ -- struct usb_device *udev; -- struct usb_host_endpoint *ep; -- -- /* output of (re)scheduling */ -- int next_uframe; -- __hc32 splits; -- -- /* the rest is derived from the endpoint descriptor, -- * trusting urb->interval == f(epdesc->bInterval) and -- * including the extra info for hw_bufp[0..2] -- */ -- u8 usecs, c_usecs; -- u16 interval; -- u16 tt_usecs; -- u16 maxp; -- u16 raw_mask; -- unsigned bandwidth; -- -- /* This is used to initialize iTD's hw_bufp fields */ -- __hc32 buf0; -- __hc32 buf1; -- __hc32 buf2; -- -- /* this is used to initialize sITD's tt info */ -- __hc32 address; --}; -- --/*-------------------------------------------------------------------------*/ -- --/* -- * EHCI Specification 0.95 Section 3.3 -- * Fig 3-4 "Isochronous Transaction Descriptor (iTD)" -- * -- * Schedule records for high speed iso xfers -- */ --struct fotg210_itd { -- /* first part defined by EHCI spec */ -- __hc32 hw_next; /* see EHCI 3.3.1 */ -- __hc32 hw_transaction[8]; /* see EHCI 3.3.2 */ --#define FOTG210_ISOC_ACTIVE (1<<31) /* activate transfer this slot */ --#define FOTG210_ISOC_BUF_ERR (1<<30) /* Data buffer error */ --#define FOTG210_ISOC_BABBLE (1<<29) /* babble detected */ --#define FOTG210_ISOC_XACTERR (1<<28) /* XactErr - transaction error */ --#define FOTG210_ITD_LENGTH(tok) (((tok)>>16) & 0x0fff) --#define FOTG210_ITD_IOC (1 << 15) /* interrupt on complete */ -- --#define ITD_ACTIVE(fotg210) cpu_to_hc32(fotg210, FOTG210_ISOC_ACTIVE) -- -- __hc32 hw_bufp[7]; /* see EHCI 3.3.3 */ -- __hc32 hw_bufp_hi[7]; /* Appendix B */ -- -- /* the rest is HCD-private */ -- dma_addr_t itd_dma; /* for this itd */ -- union fotg210_shadow itd_next; /* ptr to periodic q entry */ -- -- struct urb *urb; -- struct fotg210_iso_stream *stream; /* endpoint's queue */ -- struct list_head itd_list; /* list of stream's itds */ -- -- /* any/all hw_transactions here may be used by that urb */ -- unsigned frame; /* where scheduled */ -- unsigned pg; -- unsigned index[8]; /* in urb->iso_frame_desc */ --} __aligned(32); -- --/*-------------------------------------------------------------------------*/ -- --/* -- * EHCI Specification 0.96 Section 3.7 -- * Periodic Frame Span Traversal Node (FSTN) -- * -- * Manages split interrupt transactions (using TT) that span frame boundaries -- * into uframes 0/1; see 4.12.2.2. In those uframes, a "save place" FSTN -- * makes the HC jump (back) to a QH to scan for fs/ls QH completions until -- * it hits a "restore" FSTN; then it returns to finish other uframe 0/1 work. -- */ --struct fotg210_fstn { -- __hc32 hw_next; /* any periodic q entry */ -- __hc32 hw_prev; /* qh or FOTG210_LIST_END */ -- -- /* the rest is HCD-private */ -- dma_addr_t fstn_dma; -- union fotg210_shadow fstn_next; /* ptr to periodic q entry */ --} __aligned(32); -- --/*-------------------------------------------------------------------------*/ -- --/* Prepare the PORTSC wakeup flags during controller suspend/resume */ -- --#define fotg210_prepare_ports_for_controller_suspend(fotg210, do_wakeup) \ -- fotg210_adjust_port_wakeup_flags(fotg210, true, do_wakeup) -- --#define fotg210_prepare_ports_for_controller_resume(fotg210) \ -- fotg210_adjust_port_wakeup_flags(fotg210, false, false) -- --/*-------------------------------------------------------------------------*/ -- --/* -- * Some EHCI controllers have a Transaction Translator built into the -- * root hub. This is a non-standard feature. Each controller will need -- * to add code to the following inline functions, and call them as -- * needed (mostly in root hub code). -- */ -- --static inline unsigned int --fotg210_get_speed(struct fotg210_hcd *fotg210, unsigned int portsc) --{ -- return (readl(&fotg210->regs->otgcsr) -- & OTGCSR_HOST_SPD_TYP) >> 22; --} -- --/* Returns the speed of a device attached to a port on the root hub. */ --static inline unsigned int --fotg210_port_speed(struct fotg210_hcd *fotg210, unsigned int portsc) --{ -- switch (fotg210_get_speed(fotg210, portsc)) { -- case 0: -- return 0; -- case 1: -- return USB_PORT_STAT_LOW_SPEED; -- case 2: -- default: -- return USB_PORT_STAT_HIGH_SPEED; -- } --} -- --/*-------------------------------------------------------------------------*/ -- --#define fotg210_has_fsl_portno_bug(e) (0) -- --/* -- * While most USB host controllers implement their registers in -- * little-endian format, a minority (celleb companion chip) implement -- * them in big endian format. -- * -- * This attempts to support either format at compile time without a -- * runtime penalty, or both formats with the additional overhead -- * of checking a flag bit. -- * -- */ -- --#define fotg210_big_endian_mmio(e) 0 --#define fotg210_big_endian_capbase(e) 0 -- --static inline unsigned int fotg210_readl(const struct fotg210_hcd *fotg210, -- __u32 __iomem *regs) --{ -- return readl(regs); --} -- --static inline void fotg210_writel(const struct fotg210_hcd *fotg210, -- const unsigned int val, __u32 __iomem *regs) --{ -- writel(val, regs); --} -- --/* cpu to fotg210 */ --static inline __hc32 cpu_to_hc32(const struct fotg210_hcd *fotg210, const u32 x) --{ -- return cpu_to_le32(x); --} -- --/* fotg210 to cpu */ --static inline u32 hc32_to_cpu(const struct fotg210_hcd *fotg210, const __hc32 x) --{ -- return le32_to_cpu(x); --} -- --static inline u32 hc32_to_cpup(const struct fotg210_hcd *fotg210, -- const __hc32 *x) --{ -- return le32_to_cpup(x); --} -- --/*-------------------------------------------------------------------------*/ -- --static inline unsigned fotg210_read_frame_index(struct fotg210_hcd *fotg210) --{ -- return fotg210_readl(fotg210, &fotg210->regs->frame_index); --} -- --/*-------------------------------------------------------------------------*/ -- --#endif /* __LINUX_FOTG210_H */ diff --git a/target/linux/gemini/patches-6.1/0003-usb-fotg210-Compile-into-one-module.patch b/target/linux/gemini/patches-6.1/0003-usb-fotg210-Compile-into-one-module.patch deleted file mode 100644 index 5c7b4ff9c7..0000000000 --- a/target/linux/gemini/patches-6.1/0003-usb-fotg210-Compile-into-one-module.patch +++ /dev/null @@ -1,332 +0,0 @@ -From 0dbc77a99267a5efef0603a4b49ac02ece6a3f23 Mon Sep 17 00:00:00 2001 -From: Linus Walleij -Date: Sun, 23 Oct 2022 16:47:07 +0200 -Subject: [PATCH 03/29] usb: fotg210: Compile into one module - -It is since ages perfectly possible to compile both of these -modules into the same kernel, which makes no sense since it -is one piece of hardware. - -Compile one module named "fotg210.ko" for both HCD and UDC -drivers by collecting the init calls into a fotg210-core.c -file and start to centralize things handling one and the same -piece of hardware. - -Stub out the initcalls if one or the other part of the driver -was not selected. - -Tested by compiling one or the other or both of the drivers -into the kernel and as modules. - -Cc: Fabian Vogt -Cc: Yuan-Hsin Chen -Cc: Felipe Balbi -Signed-off-by: Linus Walleij -Link: https://lore.kernel.org/r/20221023144708.3596563-2-linus.walleij@linaro.org -Signed-off-by: Greg Kroah-Hartman ---- ---- a/drivers/usb/fotg210/Kconfig -+++ b/drivers/usb/fotg210/Kconfig -@@ -12,7 +12,7 @@ config USB_FOTG210 - if USB_FOTG210 - - config USB_FOTG210_HCD -- tristate "Faraday FOTG210 USB Host Controller support" -+ bool "Faraday FOTG210 USB Host Controller support" - depends on USB - help - Faraday FOTG210 is an OTG controller which can be configured as -@@ -24,7 +24,7 @@ config USB_FOTG210_HCD - - config USB_FOTG210_UDC - depends on USB_GADGET -- tristate "Faraday FOTG210 USB Peripheral Controller support" -+ bool "Faraday FOTG210 USB Peripheral Controller support" - help - Faraday USB2.0 OTG controller which can be configured as - high speed or full speed USB device. This driver suppports ---- a/drivers/usb/fotg210/Makefile -+++ b/drivers/usb/fotg210/Makefile -@@ -1,3 +1,10 @@ - # SPDX-License-Identifier: GPL-2.0 --obj-$(CONFIG_USB_FOTG210_HCD) += fotg210-hcd.o --obj-$(CONFIG_USB_FOTG210_UDC) += fotg210-udc.o -+ -+# This setup links the different object files into one single -+# module so we don't have to EXPORT() a lot of internal symbols -+# or create unnecessary submodules. -+fotg210-objs-y += fotg210-core.o -+fotg210-objs-$(CONFIG_USB_FOTG210_HCD) += fotg210-hcd.o -+fotg210-objs-$(CONFIG_USB_FOTG210_UDC) += fotg210-udc.o -+fotg210-objs := $(fotg210-objs-y) -+obj-$(CONFIG_USB_FOTG210) += fotg210.o ---- /dev/null -+++ b/drivers/usb/fotg210/fotg210-core.c -@@ -0,0 +1,79 @@ -+// SPDX-License-Identifier: GPL-2.0+ -+/* -+ * Central probing code for the FOTG210 dual role driver -+ * We register one driver for the hardware and then we decide -+ * whether to proceed with probing the host or the peripheral -+ * driver. -+ */ -+#include -+#include -+#include -+#include -+#include -+ -+#include "fotg210.h" -+ -+static int fotg210_probe(struct platform_device *pdev) -+{ -+ int ret; -+ -+ if (IS_ENABLED(CONFIG_USB_FOTG210_HCD)) { -+ ret = fotg210_hcd_probe(pdev); -+ if (ret) -+ return ret; -+ } -+ if (IS_ENABLED(CONFIG_USB_FOTG210_UDC)) -+ ret = fotg210_udc_probe(pdev); -+ -+ return ret; -+} -+ -+static int fotg210_remove(struct platform_device *pdev) -+{ -+ if (IS_ENABLED(CONFIG_USB_FOTG210_HCD)) -+ fotg210_hcd_remove(pdev); -+ if (IS_ENABLED(CONFIG_USB_FOTG210_UDC)) -+ fotg210_udc_remove(pdev); -+ -+ return 0; -+} -+ -+#ifdef CONFIG_OF -+static const struct of_device_id fotg210_of_match[] = { -+ { .compatible = "faraday,fotg210" }, -+ {}, -+}; -+MODULE_DEVICE_TABLE(of, fotg210_of_match); -+#endif -+ -+static struct platform_driver fotg210_driver = { -+ .driver = { -+ .name = "fotg210", -+ .of_match_table = of_match_ptr(fotg210_of_match), -+ }, -+ .probe = fotg210_probe, -+ .remove = fotg210_remove, -+}; -+ -+static int __init fotg210_init(void) -+{ -+ if (usb_disabled()) -+ return -ENODEV; -+ -+ if (IS_ENABLED(CONFIG_USB_FOTG210_HCD)) -+ fotg210_hcd_init(); -+ return platform_driver_register(&fotg210_driver); -+} -+module_init(fotg210_init); -+ -+static void __exit fotg210_cleanup(void) -+{ -+ platform_driver_unregister(&fotg210_driver); -+ if (IS_ENABLED(CONFIG_USB_FOTG210_HCD)) -+ fotg210_hcd_cleanup(); -+} -+module_exit(fotg210_cleanup); -+ -+MODULE_AUTHOR("Yuan-Hsin Chen, Feng-Hsin Chiang"); -+MODULE_LICENSE("GPL"); -+MODULE_DESCRIPTION("FOTG210 Dual Role Controller Driver"); ---- a/drivers/usb/fotg210/fotg210-hcd.c -+++ b/drivers/usb/fotg210/fotg210-hcd.c -@@ -39,8 +39,8 @@ - #include - #include - --#define DRIVER_AUTHOR "Yuan-Hsin Chen" --#define DRIVER_DESC "FOTG210 Host Controller (EHCI) Driver" -+#include "fotg210.h" -+ - static const char hcd_name[] = "fotg210_hcd"; - - #undef FOTG210_URB_TRACE -@@ -5490,9 +5490,6 @@ static int fotg210_get_frame(struct usb_ - * functions and in order to facilitate role switching we cannot - * give the fotg210 driver exclusive access to those. - */ --MODULE_DESCRIPTION(DRIVER_DESC); --MODULE_AUTHOR(DRIVER_AUTHOR); --MODULE_LICENSE("GPL"); - - static const struct hc_driver fotg210_fotg210_hc_driver = { - .description = hcd_name, -@@ -5560,7 +5557,7 @@ static void fotg210_init(struct fotg210_ - * then invokes the start() method for the HCD associated with it - * through the hotplug entry's driver_data. - */ --static int fotg210_hcd_probe(struct platform_device *pdev) -+int fotg210_hcd_probe(struct platform_device *pdev) - { - struct device *dev = &pdev->dev; - struct usb_hcd *hcd; -@@ -5652,7 +5649,7 @@ fail_create_hcd: - * @dev: USB Host Controller being removed - * - */ --static int fotg210_hcd_remove(struct platform_device *pdev) -+int fotg210_hcd_remove(struct platform_device *pdev) - { - struct usb_hcd *hcd = platform_get_drvdata(pdev); - struct fotg210_hcd *fotg210 = hcd_to_fotg210(hcd); -@@ -5668,27 +5665,8 @@ static int fotg210_hcd_remove(struct pla - return 0; - } - --#ifdef CONFIG_OF --static const struct of_device_id fotg210_of_match[] = { -- { .compatible = "faraday,fotg210" }, -- {}, --}; --MODULE_DEVICE_TABLE(of, fotg210_of_match); --#endif -- --static struct platform_driver fotg210_hcd_driver = { -- .driver = { -- .name = "fotg210-hcd", -- .of_match_table = of_match_ptr(fotg210_of_match), -- }, -- .probe = fotg210_hcd_probe, -- .remove = fotg210_hcd_remove, --}; -- --static int __init fotg210_hcd_init(void) -+int __init fotg210_hcd_init(void) - { -- int retval = 0; -- - if (usb_disabled()) - return -ENODEV; - -@@ -5704,24 +5682,11 @@ static int __init fotg210_hcd_init(void) - - fotg210_debug_root = debugfs_create_dir("fotg210", usb_debug_root); - -- retval = platform_driver_register(&fotg210_hcd_driver); -- if (retval < 0) -- goto clean; -- return retval; -- --clean: -- debugfs_remove(fotg210_debug_root); -- fotg210_debug_root = NULL; -- -- clear_bit(USB_EHCI_LOADED, &usb_hcds_loaded); -- return retval; -+ return 0; - } --module_init(fotg210_hcd_init); - --static void __exit fotg210_hcd_cleanup(void) -+void __exit fotg210_hcd_cleanup(void) - { -- platform_driver_unregister(&fotg210_hcd_driver); - debugfs_remove(fotg210_debug_root); - clear_bit(USB_EHCI_LOADED, &usb_hcds_loaded); - } --module_exit(fotg210_hcd_cleanup); ---- a/drivers/usb/fotg210/fotg210-udc.c -+++ b/drivers/usb/fotg210/fotg210-udc.c -@@ -16,6 +16,7 @@ - #include - #include - -+#include "fotg210.h" - #include "fotg210-udc.h" - - #define DRIVER_DESC "FOTG210 USB Device Controller Driver" -@@ -1081,7 +1082,7 @@ static const struct usb_gadget_ops fotg2 - .udc_stop = fotg210_udc_stop, - }; - --static int fotg210_udc_remove(struct platform_device *pdev) -+int fotg210_udc_remove(struct platform_device *pdev) - { - struct fotg210_udc *fotg210 = platform_get_drvdata(pdev); - int i; -@@ -1098,7 +1099,7 @@ static int fotg210_udc_remove(struct pla - return 0; - } - --static int fotg210_udc_probe(struct platform_device *pdev) -+int fotg210_udc_probe(struct platform_device *pdev) - { - struct resource *res, *ires; - struct fotg210_udc *fotg210 = NULL; -@@ -1223,17 +1224,3 @@ err_alloc: - err: - return ret; - } -- --static struct platform_driver fotg210_driver = { -- .driver = { -- .name = udc_name, -- }, -- .probe = fotg210_udc_probe, -- .remove = fotg210_udc_remove, --}; -- --module_platform_driver(fotg210_driver); -- --MODULE_AUTHOR("Yuan-Hsin Chen, Feng-Hsin Chiang "); --MODULE_LICENSE("GPL"); --MODULE_DESCRIPTION(DRIVER_DESC); ---- /dev/null -+++ b/drivers/usb/fotg210/fotg210.h -@@ -0,0 +1,42 @@ -+/* SPDX-License-Identifier: GPL-2.0 */ -+#ifndef __FOTG210_H -+#define __FOTG210_H -+ -+#ifdef CONFIG_USB_FOTG210_HCD -+int fotg210_hcd_probe(struct platform_device *pdev); -+int fotg210_hcd_remove(struct platform_device *pdev); -+int fotg210_hcd_init(void); -+void fotg210_hcd_cleanup(void); -+#else -+static inline int fotg210_hcd_probe(struct platform_device *pdev) -+{ -+ return 0; -+} -+static inline int fotg210_hcd_remove(struct platform_device *pdev) -+{ -+ return 0; -+} -+static inline int fotg210_hcd_init(void) -+{ -+ return 0; -+} -+static inline void fotg210_hcd_cleanup(void) -+{ -+} -+#endif -+ -+#ifdef CONFIG_USB_FOTG210_UDC -+int fotg210_udc_probe(struct platform_device *pdev); -+int fotg210_udc_remove(struct platform_device *pdev); -+#else -+static inline int fotg210_udc_probe(struct platform_device *pdev) -+{ -+ return 0; -+} -+static inline int fotg210_udc_remove(struct platform_device *pdev) -+{ -+ return 0; -+} -+#endif -+ -+#endif /* __FOTG210_H */ diff --git a/target/linux/gemini/patches-6.1/0004-usb-fotg210-Select-subdriver-by-mode.patch b/target/linux/gemini/patches-6.1/0004-usb-fotg210-Select-subdriver-by-mode.patch deleted file mode 100644 index 6a19a0aa4d..0000000000 --- a/target/linux/gemini/patches-6.1/0004-usb-fotg210-Select-subdriver-by-mode.patch +++ /dev/null @@ -1,68 +0,0 @@ -From 7c0b661926097e935f2711857596fc2277b2304a Mon Sep 17 00:00:00 2001 -From: Linus Walleij -Date: Sun, 23 Oct 2022 16:47:08 +0200 -Subject: [PATCH 04/29] usb: fotg210: Select subdriver by mode - -Check which mode the hardware is in, and selecte the peripheral -driver if the hardware is in explicit peripheral mode, otherwise -select host mode. - -This should solve the immediate problem that both subdrivers -can get probed. - -Cc: Fabian Vogt -Cc: Yuan-Hsin Chen -Cc: Felipe Balbi -Signed-off-by: Linus Walleij -Link: https://lore.kernel.org/r/20221023144708.3596563-3-linus.walleij@linaro.org -Signed-off-by: Greg Kroah-Hartman ---- ---- a/drivers/usb/fotg210/fotg210-core.c -+++ b/drivers/usb/fotg210/fotg210-core.c -@@ -10,30 +10,37 @@ - #include - #include - #include -+#include - - #include "fotg210.h" - - static int fotg210_probe(struct platform_device *pdev) - { -+ struct device *dev = &pdev->dev; -+ enum usb_dr_mode mode; - int ret; - -- if (IS_ENABLED(CONFIG_USB_FOTG210_HCD)) { -- ret = fotg210_hcd_probe(pdev); -- if (ret) -- return ret; -- } -- if (IS_ENABLED(CONFIG_USB_FOTG210_UDC)) -+ mode = usb_get_dr_mode(dev); -+ -+ if (mode == USB_DR_MODE_PERIPHERAL) - ret = fotg210_udc_probe(pdev); -+ else -+ ret = fotg210_hcd_probe(pdev); - - return ret; - } - - static int fotg210_remove(struct platform_device *pdev) - { -- if (IS_ENABLED(CONFIG_USB_FOTG210_HCD)) -- fotg210_hcd_remove(pdev); -- if (IS_ENABLED(CONFIG_USB_FOTG210_UDC)) -+ struct device *dev = &pdev->dev; -+ enum usb_dr_mode mode; -+ -+ mode = usb_get_dr_mode(dev); -+ -+ if (mode == USB_DR_MODE_PERIPHERAL) - fotg210_udc_remove(pdev); -+ else -+ fotg210_hcd_remove(pdev); - - return 0; - } diff --git a/target/linux/gemini/patches-6.1/0005-usb-fotg2-add-Gemini-specific-handling.patch b/target/linux/gemini/patches-6.1/0005-usb-fotg2-add-Gemini-specific-handling.patch deleted file mode 100644 index daf8d85611..0000000000 --- a/target/linux/gemini/patches-6.1/0005-usb-fotg2-add-Gemini-specific-handling.patch +++ /dev/null @@ -1,135 +0,0 @@ -From f7f6c8aca91093e2f886ec97910b1a7d9a69bf9b Mon Sep 17 00:00:00 2001 -From: Linus Walleij -Date: Wed, 9 Nov 2022 21:05:54 +0100 -Subject: [PATCH 05/29] usb: fotg2: add Gemini-specific handling - -The Cortina Systems Gemini has bolted on a PHY inside the -silicon that can be handled by six bits in a MISC register in -the system controller. - -If we are running on Gemini, look up a syscon regmap through -a phandle and enable VBUS and optionally the Mini-B connector. - -If the device is flagged as "wakeup-source" using the standard -DT bindings, we also enable this in the global controller for -respective port. - -Signed-off-by: Linus Walleij -Link: https://lore.kernel.org/r/20221109200554.1957185-1-linus.walleij@linaro.org -Signed-off-by: Greg Kroah-Hartman ---- ---- a/drivers/usb/fotg210/Kconfig -+++ b/drivers/usb/fotg210/Kconfig -@@ -5,6 +5,7 @@ config USB_FOTG210 - depends on USB || USB_GADGET - depends on HAS_DMA && HAS_IOMEM - default ARCH_GEMINI -+ select MFD_SYSCON - help - Faraday FOTG210 is a dual-mode USB controller that can act - in both host controller and peripheral controller mode. ---- a/drivers/usb/fotg210/fotg210-core.c -+++ b/drivers/usb/fotg210/fotg210-core.c -@@ -5,15 +5,86 @@ - * whether to proceed with probing the host or the peripheral - * driver. - */ -+#include - #include -+#include - #include - #include - #include -+#include - #include - #include - - #include "fotg210.h" - -+/* -+ * Gemini-specific initialization function, only executed on the -+ * Gemini SoC using the global misc control register. -+ * -+ * The gemini USB blocks are connected to either Mini-A (host mode) or -+ * Mini-B (peripheral mode) plugs. There is no role switch support on the -+ * Gemini SoC, just either-or. -+ */ -+#define GEMINI_GLOBAL_MISC_CTRL 0x30 -+#define GEMINI_MISC_USB0_WAKEUP BIT(14) -+#define GEMINI_MISC_USB1_WAKEUP BIT(15) -+#define GEMINI_MISC_USB0_VBUS_ON BIT(22) -+#define GEMINI_MISC_USB1_VBUS_ON BIT(23) -+#define GEMINI_MISC_USB0_MINI_B BIT(29) -+#define GEMINI_MISC_USB1_MINI_B BIT(30) -+ -+static int fotg210_gemini_init(struct device *dev, struct resource *res, -+ enum usb_dr_mode mode) -+{ -+ struct device_node *np = dev->of_node; -+ struct regmap *map; -+ bool wakeup; -+ u32 mask, val; -+ int ret; -+ -+ map = syscon_regmap_lookup_by_phandle(np, "syscon"); -+ if (IS_ERR(map)) { -+ dev_err(dev, "no syscon\n"); -+ return PTR_ERR(map); -+ } -+ wakeup = of_property_read_bool(np, "wakeup-source"); -+ -+ /* -+ * Figure out if this is USB0 or USB1 by simply checking the -+ * physical base address. -+ */ -+ mask = 0; -+ if (res->start == 0x69000000) { -+ mask = GEMINI_MISC_USB1_VBUS_ON | GEMINI_MISC_USB1_MINI_B | -+ GEMINI_MISC_USB1_WAKEUP; -+ if (mode == USB_DR_MODE_HOST) -+ val = GEMINI_MISC_USB1_VBUS_ON; -+ else -+ val = GEMINI_MISC_USB1_MINI_B; -+ if (wakeup) -+ val |= GEMINI_MISC_USB1_WAKEUP; -+ } else { -+ mask = GEMINI_MISC_USB0_VBUS_ON | GEMINI_MISC_USB0_MINI_B | -+ GEMINI_MISC_USB0_WAKEUP; -+ if (mode == USB_DR_MODE_HOST) -+ val = GEMINI_MISC_USB0_VBUS_ON; -+ else -+ val = GEMINI_MISC_USB0_MINI_B; -+ if (wakeup) -+ val |= GEMINI_MISC_USB0_WAKEUP; -+ } -+ -+ ret = regmap_update_bits(map, GEMINI_GLOBAL_MISC_CTRL, mask, val); -+ if (ret) { -+ dev_err(dev, "failed to initialize Gemini PHY\n"); -+ return ret; -+ } -+ -+ dev_info(dev, "initialized Gemini PHY in %s mode\n", -+ (mode == USB_DR_MODE_HOST) ? "host" : "gadget"); -+ return 0; -+} -+ - static int fotg210_probe(struct platform_device *pdev) - { - struct device *dev = &pdev->dev; -@@ -22,6 +93,15 @@ static int fotg210_probe(struct platform - - mode = usb_get_dr_mode(dev); - -+ if (of_device_is_compatible(dev->of_node, "cortina,gemini-usb")) { -+ struct resource *res; -+ -+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); -+ ret = fotg210_gemini_init(dev, res, mode); -+ if (ret) -+ return ret; -+ } -+ - if (mode == USB_DR_MODE_PERIPHERAL) - ret = fotg210_udc_probe(pdev); - else diff --git a/target/linux/gemini/patches-6.1/0006-usb-fotg210-Fix-Kconfig-for-USB-host-modules.patch b/target/linux/gemini/patches-6.1/0006-usb-fotg210-Fix-Kconfig-for-USB-host-modules.patch deleted file mode 100644 index bd3a42415a..0000000000 --- a/target/linux/gemini/patches-6.1/0006-usb-fotg210-Fix-Kconfig-for-USB-host-modules.patch +++ /dev/null @@ -1,51 +0,0 @@ -From 6e002d41889bc52213a26ff91338d340505e0336 Mon Sep 17 00:00:00 2001 -From: Linus Walleij -Date: Fri, 11 Nov 2022 15:48:21 +0100 -Subject: [PATCH 06/29] usb: fotg210: Fix Kconfig for USB host modules - -The kernel robot reports a link failure when activating the -FOTG210 host subdriver with =y on a system where the USB host -core is a module (CONFIG_USB=m). - -This is a bit of special case, so mimic the Kconfig incantations -from DWC3: let the subdrivers for host or peripheral depend -on the host or gadget support being =y or the same as the -FOTG210 core itself. - -This should ensure that either: - -- The host (CONFIG_USB) or gadget (CONFIG_GADGET) is compiled - in and then the FOTG210 can be either module or compiled - in. - -- The host or gadget is modular, and then the FOTG210 module - must be a module too, or we cannot resolve the symbols - at link time. - -Reported-by: kernel test robot -Link: https://lore.kernel.org/linux-usb/202211112132.0BUPGKCd-lkp@intel.com/ -Cc: Arnd Bergmann -Signed-off-by: Linus Walleij -Link: https://lore.kernel.org/r/20221111144821.113665-1-linus.walleij@linaro.org -Signed-off-by: Greg Kroah-Hartman ---- ---- a/drivers/usb/fotg210/Kconfig -+++ b/drivers/usb/fotg210/Kconfig -@@ -14,7 +14,7 @@ if USB_FOTG210 - - config USB_FOTG210_HCD - bool "Faraday FOTG210 USB Host Controller support" -- depends on USB -+ depends on USB=y || USB=USB_FOTG210 - help - Faraday FOTG210 is an OTG controller which can be configured as - an USB2.0 host. It is designed to meet USB2.0 EHCI specification -@@ -24,7 +24,7 @@ config USB_FOTG210_HCD - module will be called fotg210-hcd. - - config USB_FOTG210_UDC -- depends on USB_GADGET -+ depends on USB_GADGET=y || USB_GADGET=USB_FOTG210 - bool "Faraday FOTG210 USB Peripheral Controller support" - help - Faraday USB2.0 OTG controller which can be configured as diff --git a/target/linux/gemini/patches-6.1/0007-usb-USB_FOTG210-should-depend-on-ARCH_GEMINI.patch b/target/linux/gemini/patches-6.1/0007-usb-USB_FOTG210-should-depend-on-ARCH_GEMINI.patch deleted file mode 100644 index 6afef0d820..0000000000 --- a/target/linux/gemini/patches-6.1/0007-usb-USB_FOTG210-should-depend-on-ARCH_GEMINI.patch +++ /dev/null @@ -1,26 +0,0 @@ -From 466b10510add46afd21ca19505b29d35ad853370 Mon Sep 17 00:00:00 2001 -From: Geert Uytterhoeven -Date: Mon, 21 Nov 2022 16:22:19 +0100 -Subject: [PATCH 07/29] usb: USB_FOTG210 should depend on ARCH_GEMINI - -The Faraday Technology FOTG210 USB2 Dual Role Controller is only present -on Cortina Systems Gemini SoCs. Hence add a dependency on ARCH_GEMINI, -to prevent asking the user about its drivers when configuring a kernel -without Cortina Systems Gemini SoC support. - -Fixes: 1dd33a9f1b95ab59 ("usb: fotg210: Collect pieces of dual mode controller") -Signed-off-by: Geert Uytterhoeven -Reviewed-by: Linus Walleij -Link: https://lore.kernel.org/r/a989b3b798ecaf3b45f35160e30e605636d66a77.1669044086.git.geert+renesas@glider.be -Signed-off-by: Greg Kroah-Hartman ---- ---- a/drivers/usb/fotg210/Kconfig -+++ b/drivers/usb/fotg210/Kconfig -@@ -4,6 +4,7 @@ config USB_FOTG210 - tristate "Faraday FOTG210 USB2 Dual Role controller" - depends on USB || USB_GADGET - depends on HAS_DMA && HAS_IOMEM -+ depends on ARCH_GEMINI || COMPILE_TEST - default ARCH_GEMINI - select MFD_SYSCON - help diff --git a/target/linux/gemini/patches-6.1/0008-fotg210-udc-Use-dev-pointer-in-probe-and-dev_message.patch b/target/linux/gemini/patches-6.1/0008-fotg210-udc-Use-dev-pointer-in-probe-and-dev_message.patch deleted file mode 100644 index 2a595e885d..0000000000 --- a/target/linux/gemini/patches-6.1/0008-fotg210-udc-Use-dev-pointer-in-probe-and-dev_message.patch +++ /dev/null @@ -1,61 +0,0 @@ -From 27cd321a365fecac857e41ad1681062994142e4a Mon Sep 17 00:00:00 2001 -From: Linus Walleij -Date: Mon, 14 Nov 2022 12:51:58 +0100 -Subject: [PATCH 08/29] fotg210-udc: Use dev pointer in probe and dev_messages - -Add a local struct device *dev pointer and use dev_err() -etc to report status. - -Signed-off-by: Linus Walleij -Link: https://lore.kernel.org/r/20221114115201.302887-1-linus.walleij@linaro.org -Signed-off-by: Greg Kroah-Hartman ---- ---- a/drivers/usb/fotg210/fotg210-udc.c -+++ b/drivers/usb/fotg210/fotg210-udc.c -@@ -1104,6 +1104,7 @@ int fotg210_udc_probe(struct platform_de - struct resource *res, *ires; - struct fotg210_udc *fotg210 = NULL; - struct fotg210_ep *_ep[FOTG210_MAX_NUM_EP]; -+ struct device *dev = &pdev->dev; - int ret = 0; - int i; - -@@ -1135,7 +1136,7 @@ int fotg210_udc_probe(struct platform_de - - fotg210->reg = ioremap(res->start, resource_size(res)); - if (fotg210->reg == NULL) { -- pr_err("ioremap error.\n"); -+ dev_err(dev, "ioremap error\n"); - goto err_alloc; - } - -@@ -1146,8 +1147,8 @@ int fotg210_udc_probe(struct platform_de - fotg210->gadget.ops = &fotg210_gadget_ops; - - fotg210->gadget.max_speed = USB_SPEED_HIGH; -- fotg210->gadget.dev.parent = &pdev->dev; -- fotg210->gadget.dev.dma_mask = pdev->dev.dma_mask; -+ fotg210->gadget.dev.parent = dev; -+ fotg210->gadget.dev.dma_mask = dev->dma_mask; - fotg210->gadget.name = udc_name; - - INIT_LIST_HEAD(&fotg210->gadget.ep_list); -@@ -1195,15 +1196,15 @@ int fotg210_udc_probe(struct platform_de - ret = request_irq(ires->start, fotg210_irq, IRQF_SHARED, - udc_name, fotg210); - if (ret < 0) { -- pr_err("request_irq error (%d)\n", ret); -+ dev_err(dev, "request_irq error (%d)\n", ret); - goto err_req; - } - -- ret = usb_add_gadget_udc(&pdev->dev, &fotg210->gadget); -+ ret = usb_add_gadget_udc(dev, &fotg210->gadget); - if (ret) - goto err_add_udc; - -- dev_info(&pdev->dev, "version %s\n", DRIVER_VERSION); -+ dev_info(dev, "version %s\n", DRIVER_VERSION); - - return 0; - diff --git a/target/linux/gemini/patches-6.1/0009-fotg210-udc-Support-optional-external-PHY.patch b/target/linux/gemini/patches-6.1/0009-fotg210-udc-Support-optional-external-PHY.patch deleted file mode 100644 index 498875c535..0000000000 --- a/target/linux/gemini/patches-6.1/0009-fotg210-udc-Support-optional-external-PHY.patch +++ /dev/null @@ -1,158 +0,0 @@ -From 03e4b585ac947e2d422bedf03179bbfec3aca3cf Mon Sep 17 00:00:00 2001 -From: Linus Walleij -Date: Mon, 14 Nov 2022 12:51:59 +0100 -Subject: [PATCH 09/29] fotg210-udc: Support optional external PHY - -This adds support for an optional external PHY to the FOTG210 -UDC driver. - -Tested with the GPIO VBUS PHY driver on the Gemini SoC. - -Signed-off-by: Linus Walleij -Link: https://lore.kernel.org/r/20221114115201.302887-2-linus.walleij@linaro.org -Signed-off-by: Greg Kroah-Hartman ---- ---- a/drivers/usb/fotg210/fotg210-udc.c -+++ b/drivers/usb/fotg210/fotg210-udc.c -@@ -15,6 +15,8 @@ - #include - #include - #include -+#include -+#include - - #include "fotg210.h" - #include "fotg210-udc.h" -@@ -1022,10 +1024,18 @@ static int fotg210_udc_start(struct usb_ - { - struct fotg210_udc *fotg210 = gadget_to_fotg210(g); - u32 value; -+ int ret; - - /* hook up the driver */ - fotg210->driver = driver; - -+ if (!IS_ERR_OR_NULL(fotg210->phy)) { -+ ret = otg_set_peripheral(fotg210->phy->otg, -+ &fotg210->gadget); -+ if (ret) -+ dev_err(fotg210->dev, "can't bind to phy\n"); -+ } -+ - /* enable device global interrupt */ - value = ioread32(fotg210->reg + FOTG210_DMCR); - value |= DMCR_GLINT_EN; -@@ -1067,6 +1077,9 @@ static int fotg210_udc_stop(struct usb_g - struct fotg210_udc *fotg210 = gadget_to_fotg210(g); - unsigned long flags; - -+ if (!IS_ERR_OR_NULL(fotg210->phy)) -+ return otg_set_peripheral(fotg210->phy->otg, NULL); -+ - spin_lock_irqsave(&fotg210->lock, flags); - - fotg210_init(fotg210); -@@ -1082,12 +1095,50 @@ static const struct usb_gadget_ops fotg2 - .udc_stop = fotg210_udc_stop, - }; - -+/** -+ * fotg210_phy_event - Called by phy upon VBus event -+ * @nb: notifier block -+ * @action: phy action, is vbus connect or disconnect -+ * @data: the usb_gadget structure in fotg210 -+ * -+ * Called by the USB Phy when a cable connect or disconnect is sensed. -+ * -+ * Returns NOTIFY_OK or NOTIFY_DONE -+ */ -+static int fotg210_phy_event(struct notifier_block *nb, unsigned long action, -+ void *data) -+{ -+ struct usb_gadget *gadget = data; -+ -+ if (!gadget) -+ return NOTIFY_DONE; -+ -+ switch (action) { -+ case USB_EVENT_VBUS: -+ usb_gadget_vbus_connect(gadget); -+ return NOTIFY_OK; -+ case USB_EVENT_NONE: -+ usb_gadget_vbus_disconnect(gadget); -+ return NOTIFY_OK; -+ default: -+ return NOTIFY_DONE; -+ } -+} -+ -+static struct notifier_block fotg210_phy_notifier = { -+ .notifier_call = fotg210_phy_event, -+}; -+ - int fotg210_udc_remove(struct platform_device *pdev) - { - struct fotg210_udc *fotg210 = platform_get_drvdata(pdev); - int i; - - usb_del_gadget_udc(&fotg210->gadget); -+ if (!IS_ERR_OR_NULL(fotg210->phy)) { -+ usb_unregister_notifier(fotg210->phy, &fotg210_phy_notifier); -+ usb_put_phy(fotg210->phy); -+ } - iounmap(fotg210->reg); - free_irq(platform_get_irq(pdev, 0), fotg210); - -@@ -1127,6 +1178,22 @@ int fotg210_udc_probe(struct platform_de - if (fotg210 == NULL) - goto err; - -+ fotg210->dev = dev; -+ -+ fotg210->phy = devm_usb_get_phy_by_phandle(dev->parent, "usb-phy", 0); -+ if (IS_ERR(fotg210->phy)) { -+ ret = PTR_ERR(fotg210->phy); -+ if (ret == -EPROBE_DEFER) -+ goto err; -+ dev_info(dev, "no PHY found\n"); -+ fotg210->phy = NULL; -+ } else { -+ ret = usb_phy_init(fotg210->phy); -+ if (ret) -+ goto err; -+ dev_info(dev, "found and initialized PHY\n"); -+ } -+ - for (i = 0; i < FOTG210_MAX_NUM_EP; i++) { - _ep[i] = kzalloc(sizeof(struct fotg210_ep), GFP_KERNEL); - if (_ep[i] == NULL) -@@ -1200,6 +1267,9 @@ int fotg210_udc_probe(struct platform_de - goto err_req; - } - -+ if (!IS_ERR_OR_NULL(fotg210->phy)) -+ usb_register_notifier(fotg210->phy, &fotg210_phy_notifier); -+ - ret = usb_add_gadget_udc(dev, &fotg210->gadget); - if (ret) - goto err_add_udc; -@@ -1209,6 +1279,8 @@ int fotg210_udc_probe(struct platform_de - return 0; - - err_add_udc: -+ if (!IS_ERR_OR_NULL(fotg210->phy)) -+ usb_unregister_notifier(fotg210->phy, &fotg210_phy_notifier); - free_irq(ires->start, fotg210); - - err_req: ---- a/drivers/usb/fotg210/fotg210-udc.h -+++ b/drivers/usb/fotg210/fotg210-udc.h -@@ -234,6 +234,8 @@ struct fotg210_udc { - - unsigned long irq_trigger; - -+ struct device *dev; -+ struct usb_phy *phy; - struct usb_gadget gadget; - struct usb_gadget_driver *driver; - diff --git a/target/linux/gemini/patches-6.1/0010-fotg210-udc-Handle-PCLK.patch b/target/linux/gemini/patches-6.1/0010-fotg210-udc-Handle-PCLK.patch deleted file mode 100644 index 8da3de3b47..0000000000 --- a/target/linux/gemini/patches-6.1/0010-fotg210-udc-Handle-PCLK.patch +++ /dev/null @@ -1,90 +0,0 @@ -From 772ea3ec2b9363b45ef9a4768ea205f758c3debc Mon Sep 17 00:00:00 2001 -From: Linus Walleij -Date: Mon, 14 Nov 2022 12:52:00 +0100 -Subject: [PATCH 10/29] fotg210-udc: Handle PCLK - -This adds optional handling of the peripheral clock PCLK. - -Signed-off-by: Linus Walleij -Link: https://lore.kernel.org/r/20221114115201.302887-3-linus.walleij@linaro.org -Signed-off-by: Greg Kroah-Hartman ---- ---- a/drivers/usb/fotg210/fotg210-udc.c -+++ b/drivers/usb/fotg210/fotg210-udc.c -@@ -15,6 +15,7 @@ - #include - #include - #include -+#include - #include - #include - -@@ -1145,6 +1146,10 @@ int fotg210_udc_remove(struct platform_d - fotg210_ep_free_request(&fotg210->ep[0]->ep, fotg210->ep0_req); - for (i = 0; i < FOTG210_MAX_NUM_EP; i++) - kfree(fotg210->ep[i]); -+ -+ if (!IS_ERR(fotg210->pclk)) -+ clk_disable_unprepare(fotg210->pclk); -+ - kfree(fotg210); - - return 0; -@@ -1180,17 +1185,34 @@ int fotg210_udc_probe(struct platform_de - - fotg210->dev = dev; - -+ /* It's OK not to supply this clock */ -+ fotg210->pclk = devm_clk_get(dev, "PCLK"); -+ if (!IS_ERR(fotg210->pclk)) { -+ ret = clk_prepare_enable(fotg210->pclk); -+ if (ret) { -+ dev_err(dev, "failed to enable PCLK\n"); -+ return ret; -+ } -+ } else if (PTR_ERR(fotg210->pclk) == -EPROBE_DEFER) { -+ /* -+ * Percolate deferrals, for anything else, -+ * just live without the clocking. -+ */ -+ ret = -EPROBE_DEFER; -+ goto err; -+ } -+ - fotg210->phy = devm_usb_get_phy_by_phandle(dev->parent, "usb-phy", 0); - if (IS_ERR(fotg210->phy)) { - ret = PTR_ERR(fotg210->phy); - if (ret == -EPROBE_DEFER) -- goto err; -+ goto err_pclk; - dev_info(dev, "no PHY found\n"); - fotg210->phy = NULL; - } else { - ret = usb_phy_init(fotg210->phy); - if (ret) -- goto err; -+ goto err_pclk; - dev_info(dev, "found and initialized PHY\n"); - } - -@@ -1292,6 +1314,10 @@ err_map: - err_alloc: - for (i = 0; i < FOTG210_MAX_NUM_EP; i++) - kfree(fotg210->ep[i]); -+err_pclk: -+ if (!IS_ERR(fotg210->pclk)) -+ clk_disable_unprepare(fotg210->pclk); -+ - kfree(fotg210); - - err: ---- a/drivers/usb/fotg210/fotg210-udc.h -+++ b/drivers/usb/fotg210/fotg210-udc.h -@@ -231,6 +231,7 @@ struct fotg210_ep { - struct fotg210_udc { - spinlock_t lock; /* protect the struct */ - void __iomem *reg; -+ struct clk *pclk; - - unsigned long irq_trigger; - diff --git a/target/linux/gemini/patches-6.1/0011-fotg210-udc-Get-IRQ-using-platform_get_irq.patch b/target/linux/gemini/patches-6.1/0011-fotg210-udc-Get-IRQ-using-platform_get_irq.patch deleted file mode 100644 index 9544de7cb0..0000000000 --- a/target/linux/gemini/patches-6.1/0011-fotg210-udc-Get-IRQ-using-platform_get_irq.patch +++ /dev/null @@ -1,69 +0,0 @@ -From eda686d41e298a9d16708d2ec8d12d8e682dd7ca Mon Sep 17 00:00:00 2001 -From: Linus Walleij -Date: Mon, 14 Nov 2022 12:52:01 +0100 -Subject: [PATCH 11/29] fotg210-udc: Get IRQ using platform_get_irq() - -The platform_get_irq() is necessary to use to get dynamic -IRQ resolution when instantiating the device from the -device tree. IRQs are not passed as resources in that -case. - -Signed-off-by: Linus Walleij -Link: https://lore.kernel.org/r/20221114115201.302887-4-linus.walleij@linaro.org -Signed-off-by: Greg Kroah-Hartman ---- ---- a/drivers/usb/fotg210/fotg210-udc.c -+++ b/drivers/usb/fotg210/fotg210-udc.c -@@ -1157,10 +1157,11 @@ int fotg210_udc_remove(struct platform_d - - int fotg210_udc_probe(struct platform_device *pdev) - { -- struct resource *res, *ires; -+ struct resource *res; - struct fotg210_udc *fotg210 = NULL; - struct fotg210_ep *_ep[FOTG210_MAX_NUM_EP]; - struct device *dev = &pdev->dev; -+ int irq; - int ret = 0; - int i; - -@@ -1170,9 +1171,9 @@ int fotg210_udc_probe(struct platform_de - return -ENODEV; - } - -- ires = platform_get_resource(pdev, IORESOURCE_IRQ, 0); -- if (!ires) { -- pr_err("platform_get_resource IORESOURCE_IRQ error.\n"); -+ irq = platform_get_irq(pdev, 0); -+ if (irq < 0) { -+ pr_err("could not get irq\n"); - return -ENODEV; - } - -@@ -1202,7 +1203,7 @@ int fotg210_udc_probe(struct platform_de - goto err; - } - -- fotg210->phy = devm_usb_get_phy_by_phandle(dev->parent, "usb-phy", 0); -+ fotg210->phy = devm_usb_get_phy_by_phandle(dev, "usb-phy", 0); - if (IS_ERR(fotg210->phy)) { - ret = PTR_ERR(fotg210->phy); - if (ret == -EPROBE_DEFER) -@@ -1282,7 +1283,7 @@ int fotg210_udc_probe(struct platform_de - - fotg210_disable_unplug(fotg210); - -- ret = request_irq(ires->start, fotg210_irq, IRQF_SHARED, -+ ret = request_irq(irq, fotg210_irq, IRQF_SHARED, - udc_name, fotg210); - if (ret < 0) { - dev_err(dev, "request_irq error (%d)\n", ret); -@@ -1303,7 +1304,7 @@ int fotg210_udc_probe(struct platform_de - err_add_udc: - if (!IS_ERR_OR_NULL(fotg210->phy)) - usb_unregister_notifier(fotg210->phy, &fotg210_phy_notifier); -- free_irq(ires->start, fotg210); -+ free_irq(irq, fotg210); - - err_req: - fotg210_ep_free_request(&fotg210->ep[0]->ep, fotg210->ep0_req); diff --git a/target/linux/gemini/patches-6.1/0012-usb-fotg210-udc-Remove-a-useless-assignment.patch b/target/linux/gemini/patches-6.1/0012-usb-fotg210-udc-Remove-a-useless-assignment.patch deleted file mode 100644 index 8c33c50b2c..0000000000 --- a/target/linux/gemini/patches-6.1/0012-usb-fotg210-udc-Remove-a-useless-assignment.patch +++ /dev/null @@ -1,39 +0,0 @@ -From 7889a2f0256c55e0184dffd0001d0782f9e4cb83 Mon Sep 17 00:00:00 2001 -From: Christophe JAILLET -Date: Mon, 14 Nov 2022 21:38:04 +0100 -Subject: [PATCH 12/29] usb: fotg210-udc: Remove a useless assignment - -There is no need to use an intermediate array for these memory allocations, -so, axe it. - -While at it, turn a '== NULL' into a shorter '!' when testing memory -allocation failure. - -Signed-off-by: Christophe JAILLET -Reviewed-by: Linus Walleij -Link: https://lore.kernel.org/r/deab9696fc4000499470e7ccbca7c36fca17bd4e.1668458274.git.christophe.jaillet@wanadoo.fr -Signed-off-by: Greg Kroah-Hartman ---- ---- a/drivers/usb/fotg210/fotg210-udc.c -+++ b/drivers/usb/fotg210/fotg210-udc.c -@@ -1159,7 +1159,6 @@ int fotg210_udc_probe(struct platform_de - { - struct resource *res; - struct fotg210_udc *fotg210 = NULL; -- struct fotg210_ep *_ep[FOTG210_MAX_NUM_EP]; - struct device *dev = &pdev->dev; - int irq; - int ret = 0; -@@ -1218,10 +1217,9 @@ int fotg210_udc_probe(struct platform_de - } - - for (i = 0; i < FOTG210_MAX_NUM_EP; i++) { -- _ep[i] = kzalloc(sizeof(struct fotg210_ep), GFP_KERNEL); -- if (_ep[i] == NULL) -+ fotg210->ep[i] = kzalloc(sizeof(struct fotg210_ep), GFP_KERNEL); -+ if (!fotg210->ep[i]) - goto err_alloc; -- fotg210->ep[i] = _ep[i]; - } - - fotg210->reg = ioremap(res->start, resource_size(res)); diff --git a/target/linux/gemini/patches-6.1/0013-usb-fotg210-udc-fix-potential-memory-leak-in-fotg210.patch b/target/linux/gemini/patches-6.1/0013-usb-fotg210-udc-fix-potential-memory-leak-in-fotg210.patch deleted file mode 100644 index 178135662f..0000000000 --- a/target/linux/gemini/patches-6.1/0013-usb-fotg210-udc-fix-potential-memory-leak-in-fotg210.patch +++ /dev/null @@ -1,58 +0,0 @@ -From 7b95ade85ac18eec63e81ac58a482b3e88361ffd Mon Sep 17 00:00:00 2001 -From: Yi Yang -Date: Fri, 2 Dec 2022 09:21:26 +0800 -Subject: [PATCH 13/29] usb: fotg210-udc: fix potential memory leak in - fotg210_udc_probe() -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -In fotg210_udc_probe(), if devm_clk_get() or clk_prepare_enable() -fails, 'fotg210' will not be freed, which will lead to a memory leak. -Fix it by moving kfree() to a proper location. - -In addition,we can use "return -ENOMEM" instead of "goto err" -to simplify the code. - -Fixes: 718a38d092ec ("fotg210-udc: Handle PCLK") -Reviewed-by: Andrzej Pietrasiewicz -Reviewed-by: Linus Walleij -Signed-off-by: Yi Yang -Link: https://lore.kernel.org/r/20221202012126.246953-1-yiyang13@huawei.com -Signed-off-by: Greg Kroah-Hartman ---- ---- a/drivers/usb/fotg210/fotg210-udc.c -+++ b/drivers/usb/fotg210/fotg210-udc.c -@@ -1176,12 +1176,10 @@ int fotg210_udc_probe(struct platform_de - return -ENODEV; - } - -- ret = -ENOMEM; -- - /* initialize udc */ - fotg210 = kzalloc(sizeof(struct fotg210_udc), GFP_KERNEL); - if (fotg210 == NULL) -- goto err; -+ return -ENOMEM; - - fotg210->dev = dev; - -@@ -1191,7 +1189,7 @@ int fotg210_udc_probe(struct platform_de - ret = clk_prepare_enable(fotg210->pclk); - if (ret) { - dev_err(dev, "failed to enable PCLK\n"); -- return ret; -+ goto err; - } - } else if (PTR_ERR(fotg210->pclk) == -EPROBE_DEFER) { - /* -@@ -1317,8 +1315,7 @@ err_pclk: - if (!IS_ERR(fotg210->pclk)) - clk_disable_unprepare(fotg210->pclk); - -- kfree(fotg210); -- - err: -+ kfree(fotg210); - return ret; - } diff --git a/target/linux/gemini/patches-6.1/0014-usb-fotg210-fix-OTG-only-build.patch b/target/linux/gemini/patches-6.1/0014-usb-fotg210-fix-OTG-only-build.patch deleted file mode 100644 index acdf1796f3..0000000000 --- a/target/linux/gemini/patches-6.1/0014-usb-fotg210-fix-OTG-only-build.patch +++ /dev/null @@ -1,39 +0,0 @@ -From d8eed400495029ba551704ff0fae1dad87332291 Mon Sep 17 00:00:00 2001 -From: Arnd Bergmann -Date: Thu, 15 Dec 2022 17:57:20 +0100 -Subject: [PATCH 14/29] usb: fotg210: fix OTG-only build - -The fotg210 module combines the HCD and OTG drivers, which then -fails to build when only the USB gadget support is enabled -in the kernel but host support is not: - -aarch64-linux-ld: drivers/usb/fotg210/fotg210-core.o: in function `fotg210_init': -fotg210-core.c:(.init.text+0xc): undefined reference to `usb_disabled' - -Move the check for usb_disabled() after the check for the HCD module, -and let the OTG driver still be probed in this configuration. - -A nicer approach might be to have the common portion built as a -library module, with the two platform other files registering -their own platform_driver instances separately. - -Fixes: ddacd6ef44ca ("usb: fotg210: Fix Kconfig for USB host modules") -Reviewed-by: Linus Walleij -Signed-off-by: Arnd Bergmann -Link: https://lore.kernel.org/r/20221215165728.2062984-1-arnd@kernel.org -Signed-off-by: Greg Kroah-Hartman ---- ---- a/drivers/usb/fotg210/fotg210-core.c -+++ b/drivers/usb/fotg210/fotg210-core.c -@@ -144,10 +144,7 @@ static struct platform_driver fotg210_dr - - static int __init fotg210_init(void) - { -- if (usb_disabled()) -- return -ENODEV; -- -- if (IS_ENABLED(CONFIG_USB_FOTG210_HCD)) -+ if (IS_ENABLED(CONFIG_USB_FOTG210_HCD) && !usb_disabled()) - fotg210_hcd_init(); - return platform_driver_register(&fotg210_driver); - } diff --git a/target/linux/gemini/patches-6.1/0015-usb-fotg210-udc-fix-error-return-code-in-fotg210_udc.patch b/target/linux/gemini/patches-6.1/0015-usb-fotg210-udc-fix-error-return-code-in-fotg210_udc.patch deleted file mode 100644 index a9bbca58b4..0000000000 --- a/target/linux/gemini/patches-6.1/0015-usb-fotg210-udc-fix-error-return-code-in-fotg210_udc.patch +++ /dev/null @@ -1,28 +0,0 @@ -From eaaa85d907fe27852dd960b2bc5d7bcf11bc3ebd Mon Sep 17 00:00:00 2001 -From: Yang Yingliang -Date: Fri, 30 Dec 2022 14:54:27 +0800 -Subject: [PATCH 15/29] usb: fotg210-udc: fix error return code in - fotg210_udc_probe() - -After commit 5f217ccd520f ("fotg210-udc: Support optional external PHY"), -the error code is re-assigned to 0 in fotg210_udc_probe(), if allocate or -map memory fails after the assignment, it can't return an error code. Set -the error code to -ENOMEM to fix this problem. - -Fixes: 5f217ccd520f ("fotg210-udc: Support optional external PHY") -Signed-off-by: Yang Yingliang -Reviewed-by: Linus Walleij -Link: https://lore.kernel.org/r/20221230065427.944586-1-yangyingliang@huawei.com -Signed-off-by: Greg Kroah-Hartman ---- ---- a/drivers/usb/fotg210/fotg210-udc.c -+++ b/drivers/usb/fotg210/fotg210-udc.c -@@ -1214,6 +1214,8 @@ int fotg210_udc_probe(struct platform_de - dev_info(dev, "found and initialized PHY\n"); - } - -+ ret = -ENOMEM; -+ - for (i = 0; i < FOTG210_MAX_NUM_EP; i++) { - fotg210->ep[i] = kzalloc(sizeof(struct fotg210_ep), GFP_KERNEL); - if (!fotg210->ep[i]) diff --git a/target/linux/gemini/patches-6.1/0016-usb-fotg210-List-different-variants.patch b/target/linux/gemini/patches-6.1/0016-usb-fotg210-List-different-variants.patch deleted file mode 100644 index 6ff6d28ad3..0000000000 --- a/target/linux/gemini/patches-6.1/0016-usb-fotg210-List-different-variants.patch +++ /dev/null @@ -1,25 +0,0 @@ -From 407577548b2fcd41cc72ee05df1f05a430ed30a0 Mon Sep 17 00:00:00 2001 -From: Linus Walleij -Date: Wed, 18 Jan 2023 08:09:16 +0100 -Subject: [PATCH 16/29] usb: fotg210: List different variants - -There are at least two variants of the FOTG: FOTG200 and -FOTG210. Handle them in this driver and let's add -more quirks as we go along. - -Signed-off-by: Linus Walleij -Link: https://lore.kernel.org/r/20230103-gemini-fotg210-usb-v2-2-100388af9810@linaro.org -Signed-off-by: Greg Kroah-Hartman ---- ---- a/drivers/usb/fotg210/fotg210-core.c -+++ b/drivers/usb/fotg210/fotg210-core.c -@@ -127,7 +127,9 @@ static int fotg210_remove(struct platfor - - #ifdef CONFIG_OF - static const struct of_device_id fotg210_of_match[] = { -+ { .compatible = "faraday,fotg200" }, - { .compatible = "faraday,fotg210" }, -+ /* TODO: can we also handle FUSB220? */ - {}, - }; - MODULE_DEVICE_TABLE(of, fotg210_of_match); diff --git a/target/linux/gemini/patches-6.1/0017-usb-fotg210-Acquire-memory-resource-in-core.patch b/target/linux/gemini/patches-6.1/0017-usb-fotg210-Acquire-memory-resource-in-core.patch deleted file mode 100644 index 7dbd511ecb..0000000000 --- a/target/linux/gemini/patches-6.1/0017-usb-fotg210-Acquire-memory-resource-in-core.patch +++ /dev/null @@ -1,245 +0,0 @@ -From fa735ad1afeb5791d5562617b9bbed74574d3e81 Mon Sep 17 00:00:00 2001 -From: Linus Walleij -Date: Wed, 18 Jan 2023 08:09:17 +0100 -Subject: [PATCH 17/29] usb: fotg210: Acquire memory resource in core - -The subdrivers are obtaining and mapping the memory resource -separately. Create a common state container for the shared -resources and start populating this by acquiring the IO -memory resource and remap it and pass this to the subdrivers -for host and peripheral. - -Signed-off-by: Linus Walleij -Link: https://lore.kernel.org/r/20230103-gemini-fotg210-usb-v2-3-100388af9810@linaro.org -Signed-off-by: Greg Kroah-Hartman ---- ---- a/drivers/usb/fotg210/fotg210-core.c -+++ b/drivers/usb/fotg210/fotg210-core.c -@@ -33,9 +33,10 @@ - #define GEMINI_MISC_USB0_MINI_B BIT(29) - #define GEMINI_MISC_USB1_MINI_B BIT(30) - --static int fotg210_gemini_init(struct device *dev, struct resource *res, -+static int fotg210_gemini_init(struct fotg210 *fotg, struct resource *res, - enum usb_dr_mode mode) - { -+ struct device *dev = fotg->dev; - struct device_node *np = dev->of_node; - struct regmap *map; - bool wakeup; -@@ -47,6 +48,7 @@ static int fotg210_gemini_init(struct de - dev_err(dev, "no syscon\n"); - return PTR_ERR(map); - } -+ fotg->map = map; - wakeup = of_property_read_bool(np, "wakeup-source"); - - /* -@@ -55,6 +57,7 @@ static int fotg210_gemini_init(struct de - */ - mask = 0; - if (res->start == 0x69000000) { -+ fotg->port = GEMINI_PORT_1; - mask = GEMINI_MISC_USB1_VBUS_ON | GEMINI_MISC_USB1_MINI_B | - GEMINI_MISC_USB1_WAKEUP; - if (mode == USB_DR_MODE_HOST) -@@ -64,6 +67,7 @@ static int fotg210_gemini_init(struct de - if (wakeup) - val |= GEMINI_MISC_USB1_WAKEUP; - } else { -+ fotg->port = GEMINI_PORT_0; - mask = GEMINI_MISC_USB0_VBUS_ON | GEMINI_MISC_USB0_MINI_B | - GEMINI_MISC_USB0_WAKEUP; - if (mode == USB_DR_MODE_HOST) -@@ -89,23 +93,34 @@ static int fotg210_probe(struct platform - { - struct device *dev = &pdev->dev; - enum usb_dr_mode mode; -+ struct fotg210 *fotg; - int ret; - -+ fotg = devm_kzalloc(dev, sizeof(*fotg), GFP_KERNEL); -+ if (!fotg) -+ return -ENOMEM; -+ fotg->dev = dev; -+ -+ fotg->res = platform_get_resource(pdev, IORESOURCE_MEM, 0); -+ if (!fotg->res) -+ return -ENODEV; -+ -+ fotg->base = devm_ioremap_resource(dev, fotg->res); -+ if (!fotg->base) -+ return -ENOMEM; -+ - mode = usb_get_dr_mode(dev); - - if (of_device_is_compatible(dev->of_node, "cortina,gemini-usb")) { -- struct resource *res; -- -- res = platform_get_resource(pdev, IORESOURCE_MEM, 0); -- ret = fotg210_gemini_init(dev, res, mode); -+ ret = fotg210_gemini_init(fotg, fotg->res, mode); - if (ret) - return ret; - } - - if (mode == USB_DR_MODE_PERIPHERAL) -- ret = fotg210_udc_probe(pdev); -+ ret = fotg210_udc_probe(pdev, fotg); - else -- ret = fotg210_hcd_probe(pdev); -+ ret = fotg210_hcd_probe(pdev, fotg); - - return ret; - } ---- a/drivers/usb/fotg210/fotg210-hcd.c -+++ b/drivers/usb/fotg210/fotg210-hcd.c -@@ -5557,11 +5557,10 @@ static void fotg210_init(struct fotg210_ - * then invokes the start() method for the HCD associated with it - * through the hotplug entry's driver_data. - */ --int fotg210_hcd_probe(struct platform_device *pdev) -+int fotg210_hcd_probe(struct platform_device *pdev, struct fotg210 *fotg) - { - struct device *dev = &pdev->dev; - struct usb_hcd *hcd; -- struct resource *res; - int irq; - int retval; - struct fotg210_hcd *fotg210; -@@ -5585,18 +5584,14 @@ int fotg210_hcd_probe(struct platform_de - - hcd->has_tt = 1; - -- res = platform_get_resource(pdev, IORESOURCE_MEM, 0); -- hcd->regs = devm_ioremap_resource(&pdev->dev, res); -- if (IS_ERR(hcd->regs)) { -- retval = PTR_ERR(hcd->regs); -- goto failed_put_hcd; -- } -+ hcd->regs = fotg->base; - -- hcd->rsrc_start = res->start; -- hcd->rsrc_len = resource_size(res); -+ hcd->rsrc_start = fotg->res->start; -+ hcd->rsrc_len = resource_size(fotg->res); - - fotg210 = hcd_to_fotg210(hcd); - -+ fotg210->fotg = fotg; - fotg210->caps = hcd->regs; - - /* It's OK not to supply this clock */ ---- a/drivers/usb/fotg210/fotg210-hcd.h -+++ b/drivers/usb/fotg210/fotg210-hcd.h -@@ -182,6 +182,7 @@ struct fotg210_hcd { /* one per contro - # define INCR(x) do {} while (0) - #endif - -+ struct fotg210 *fotg; /* Overarching FOTG210 device */ - /* silicon clock */ - struct clk *pclk; - }; ---- a/drivers/usb/fotg210/fotg210-udc.c -+++ b/drivers/usb/fotg210/fotg210-udc.c -@@ -1155,21 +1155,14 @@ int fotg210_udc_remove(struct platform_d - return 0; - } - --int fotg210_udc_probe(struct platform_device *pdev) -+int fotg210_udc_probe(struct platform_device *pdev, struct fotg210 *fotg) - { -- struct resource *res; - struct fotg210_udc *fotg210 = NULL; - struct device *dev = &pdev->dev; - int irq; - int ret = 0; - int i; - -- res = platform_get_resource(pdev, IORESOURCE_MEM, 0); -- if (!res) { -- pr_err("platform_get_resource error.\n"); -- return -ENODEV; -- } -- - irq = platform_get_irq(pdev, 0); - if (irq < 0) { - pr_err("could not get irq\n"); -@@ -1182,6 +1175,7 @@ int fotg210_udc_probe(struct platform_de - return -ENOMEM; - - fotg210->dev = dev; -+ fotg210->fotg = fotg; - - /* It's OK not to supply this clock */ - fotg210->pclk = devm_clk_get(dev, "PCLK"); -@@ -1222,11 +1216,7 @@ int fotg210_udc_probe(struct platform_de - goto err_alloc; - } - -- fotg210->reg = ioremap(res->start, resource_size(res)); -- if (fotg210->reg == NULL) { -- dev_err(dev, "ioremap error\n"); -- goto err_alloc; -- } -+ fotg210->reg = fotg->base; - - spin_lock_init(&fotg210->lock); - ---- a/drivers/usb/fotg210/fotg210-udc.h -+++ b/drivers/usb/fotg210/fotg210-udc.h -@@ -236,6 +236,7 @@ struct fotg210_udc { - unsigned long irq_trigger; - - struct device *dev; -+ struct fotg210 *fotg; - struct usb_phy *phy; - struct usb_gadget gadget; - struct usb_gadget_driver *driver; ---- a/drivers/usb/fotg210/fotg210.h -+++ b/drivers/usb/fotg210/fotg210.h -@@ -2,13 +2,28 @@ - #ifndef __FOTG210_H - #define __FOTG210_H - -+enum gemini_port { -+ GEMINI_PORT_NONE = 0, -+ GEMINI_PORT_0, -+ GEMINI_PORT_1, -+}; -+ -+struct fotg210 { -+ struct device *dev; -+ struct resource *res; -+ void __iomem *base; -+ struct regmap *map; -+ enum gemini_port port; -+}; -+ - #ifdef CONFIG_USB_FOTG210_HCD --int fotg210_hcd_probe(struct platform_device *pdev); -+int fotg210_hcd_probe(struct platform_device *pdev, struct fotg210 *fotg); - int fotg210_hcd_remove(struct platform_device *pdev); - int fotg210_hcd_init(void); - void fotg210_hcd_cleanup(void); - #else --static inline int fotg210_hcd_probe(struct platform_device *pdev) -+static inline int fotg210_hcd_probe(struct platform_device *pdev, -+ struct fotg210 *fotg) - { - return 0; - } -@@ -26,10 +41,11 @@ static inline void fotg210_hcd_cleanup(v - #endif - - #ifdef CONFIG_USB_FOTG210_UDC --int fotg210_udc_probe(struct platform_device *pdev); -+int fotg210_udc_probe(struct platform_device *pdev, struct fotg210 *fotg); - int fotg210_udc_remove(struct platform_device *pdev); - #else --static inline int fotg210_udc_probe(struct platform_device *pdev) -+static inline int fotg210_udc_probe(struct platform_device *pdev, -+ struct fotg210 *fotg) - { - return 0; - } diff --git a/target/linux/gemini/patches-6.1/0018-usb-fotg210-Move-clock-handling-to-core.patch b/target/linux/gemini/patches-6.1/0018-usb-fotg210-Move-clock-handling-to-core.patch deleted file mode 100644 index 9894f4dc66..0000000000 --- a/target/linux/gemini/patches-6.1/0018-usb-fotg210-Move-clock-handling-to-core.patch +++ /dev/null @@ -1,196 +0,0 @@ -From fb8e1e8dbc47e7aff7624b47adaa0a84d2983802 Mon Sep 17 00:00:00 2001 -From: Linus Walleij -Date: Wed, 18 Jan 2023 08:09:18 +0100 -Subject: [PATCH 18/29] usb: fotg210: Move clock handling to core - -Grab the optional silicon block clock, prepare and enable it in -the core before proceeding to prepare the host or peripheral -driver. This saves duplicate code and also uses the simple -devm_clk_get_optional_enabled() to do everything we really -want to do. - -Signed-off-by: Linus Walleij -Link: https://lore.kernel.org/r/20230103-gemini-fotg210-usb-v2-4-100388af9810@linaro.org -Signed-off-by: Greg Kroah-Hartman ---- ---- a/drivers/usb/fotg210/fotg210-core.c -+++ b/drivers/usb/fotg210/fotg210-core.c -@@ -6,6 +6,7 @@ - * driver. - */ - #include -+#include - #include - #include - #include -@@ -109,6 +110,10 @@ static int fotg210_probe(struct platform - if (!fotg->base) - return -ENOMEM; - -+ fotg->pclk = devm_clk_get_optional_enabled(dev, "PCLK"); -+ if (IS_ERR(fotg->pclk)) -+ return PTR_ERR(fotg->pclk); -+ - mode = usb_get_dr_mode(dev); - - if (of_device_is_compatible(dev->of_node, "cortina,gemini-usb")) { ---- a/drivers/usb/fotg210/fotg210-hcd.c -+++ b/drivers/usb/fotg210/fotg210-hcd.c -@@ -33,7 +33,6 @@ - #include - #include - #include --#include - - #include - #include -@@ -5594,44 +5593,22 @@ int fotg210_hcd_probe(struct platform_de - fotg210->fotg = fotg; - fotg210->caps = hcd->regs; - -- /* It's OK not to supply this clock */ -- fotg210->pclk = clk_get(dev, "PCLK"); -- if (!IS_ERR(fotg210->pclk)) { -- retval = clk_prepare_enable(fotg210->pclk); -- if (retval) { -- dev_err(dev, "failed to enable PCLK\n"); -- goto failed_put_hcd; -- } -- } else if (PTR_ERR(fotg210->pclk) == -EPROBE_DEFER) { -- /* -- * Percolate deferrals, for anything else, -- * just live without the clocking. -- */ -- retval = PTR_ERR(fotg210->pclk); -- goto failed_dis_clk; -- } -- - retval = fotg210_setup(hcd); - if (retval) -- goto failed_dis_clk; -+ goto failed_put_hcd; - - fotg210_init(fotg210); - - retval = usb_add_hcd(hcd, irq, IRQF_SHARED); - if (retval) { - dev_err(dev, "failed to add hcd with err %d\n", retval); -- goto failed_dis_clk; -+ goto failed_put_hcd; - } - device_wakeup_enable(hcd->self.controller); - platform_set_drvdata(pdev, hcd); - - return retval; - --failed_dis_clk: -- if (!IS_ERR(fotg210->pclk)) { -- clk_disable_unprepare(fotg210->pclk); -- clk_put(fotg210->pclk); -- } - failed_put_hcd: - usb_put_hcd(hcd); - fail_create_hcd: -@@ -5647,12 +5624,6 @@ fail_create_hcd: - int fotg210_hcd_remove(struct platform_device *pdev) - { - struct usb_hcd *hcd = platform_get_drvdata(pdev); -- struct fotg210_hcd *fotg210 = hcd_to_fotg210(hcd); -- -- if (!IS_ERR(fotg210->pclk)) { -- clk_disable_unprepare(fotg210->pclk); -- clk_put(fotg210->pclk); -- } - - usb_remove_hcd(hcd); - usb_put_hcd(hcd); ---- a/drivers/usb/fotg210/fotg210-udc.c -+++ b/drivers/usb/fotg210/fotg210-udc.c -@@ -15,7 +15,6 @@ - #include - #include - #include --#include - #include - #include - -@@ -1147,9 +1146,6 @@ int fotg210_udc_remove(struct platform_d - for (i = 0; i < FOTG210_MAX_NUM_EP; i++) - kfree(fotg210->ep[i]); - -- if (!IS_ERR(fotg210->pclk)) -- clk_disable_unprepare(fotg210->pclk); -- - kfree(fotg210); - - return 0; -@@ -1177,34 +1173,17 @@ int fotg210_udc_probe(struct platform_de - fotg210->dev = dev; - fotg210->fotg = fotg; - -- /* It's OK not to supply this clock */ -- fotg210->pclk = devm_clk_get(dev, "PCLK"); -- if (!IS_ERR(fotg210->pclk)) { -- ret = clk_prepare_enable(fotg210->pclk); -- if (ret) { -- dev_err(dev, "failed to enable PCLK\n"); -- goto err; -- } -- } else if (PTR_ERR(fotg210->pclk) == -EPROBE_DEFER) { -- /* -- * Percolate deferrals, for anything else, -- * just live without the clocking. -- */ -- ret = -EPROBE_DEFER; -- goto err; -- } -- - fotg210->phy = devm_usb_get_phy_by_phandle(dev, "usb-phy", 0); - if (IS_ERR(fotg210->phy)) { - ret = PTR_ERR(fotg210->phy); - if (ret == -EPROBE_DEFER) -- goto err_pclk; -+ goto err_free; - dev_info(dev, "no PHY found\n"); - fotg210->phy = NULL; - } else { - ret = usb_phy_init(fotg210->phy); - if (ret) -- goto err_pclk; -+ goto err_free; - dev_info(dev, "found and initialized PHY\n"); - } - -@@ -1303,11 +1282,8 @@ err_map: - err_alloc: - for (i = 0; i < FOTG210_MAX_NUM_EP; i++) - kfree(fotg210->ep[i]); --err_pclk: -- if (!IS_ERR(fotg210->pclk)) -- clk_disable_unprepare(fotg210->pclk); - --err: -+err_free: - kfree(fotg210); - return ret; - } ---- a/drivers/usb/fotg210/fotg210-udc.h -+++ b/drivers/usb/fotg210/fotg210-udc.h -@@ -231,7 +231,6 @@ struct fotg210_ep { - struct fotg210_udc { - spinlock_t lock; /* protect the struct */ - void __iomem *reg; -- struct clk *pclk; - - unsigned long irq_trigger; - ---- a/drivers/usb/fotg210/fotg210.h -+++ b/drivers/usb/fotg210/fotg210.h -@@ -12,6 +12,7 @@ struct fotg210 { - struct device *dev; - struct resource *res; - void __iomem *base; -+ struct clk *pclk; - struct regmap *map; - enum gemini_port port; - }; diff --git a/target/linux/gemini/patches-6.1/0019-usb-fotg210-Check-role-register-in-core.patch b/target/linux/gemini/patches-6.1/0019-usb-fotg210-Check-role-register-in-core.patch deleted file mode 100644 index 892b0d31af..0000000000 --- a/target/linux/gemini/patches-6.1/0019-usb-fotg210-Check-role-register-in-core.patch +++ /dev/null @@ -1,54 +0,0 @@ -From b1b07abb598211de3ce7f52abdf8dcb24384341e Mon Sep 17 00:00:00 2001 -From: Linus Walleij -Date: Wed, 18 Jan 2023 08:09:19 +0100 -Subject: [PATCH 19/29] usb: fotg210: Check role register in core - -Read the role register and check that we are in host/peripheral -mode and issue warnings if we're not in the right role when -probing respective driver. - -Signed-off-by: Linus Walleij -Link: https://lore.kernel.org/r/20230103-gemini-fotg210-usb-v2-5-100388af9810@linaro.org -Signed-off-by: Greg Kroah-Hartman ---- ---- a/drivers/usb/fotg210/fotg210-core.c -+++ b/drivers/usb/fotg210/fotg210-core.c -@@ -18,6 +18,11 @@ - - #include "fotg210.h" - -+/* Role Register 0x80 */ -+#define FOTG210_RR 0x80 -+#define FOTG210_RR_ID BIT(21) /* 1 = B-device, 0 = A-device */ -+#define FOTG210_RR_CROLE BIT(20) /* 1 = device, 0 = host */ -+ - /* - * Gemini-specific initialization function, only executed on the - * Gemini SoC using the global misc control register. -@@ -95,6 +100,7 @@ static int fotg210_probe(struct platform - struct device *dev = &pdev->dev; - enum usb_dr_mode mode; - struct fotg210 *fotg; -+ u32 val; - int ret; - - fotg = devm_kzalloc(dev, sizeof(*fotg), GFP_KERNEL); -@@ -122,10 +128,16 @@ static int fotg210_probe(struct platform - return ret; - } - -- if (mode == USB_DR_MODE_PERIPHERAL) -+ val = readl(fotg->base + FOTG210_RR); -+ if (mode == USB_DR_MODE_PERIPHERAL) { -+ if (!(val & FOTG210_RR_CROLE)) -+ dev_err(dev, "block not in device role\n"); - ret = fotg210_udc_probe(pdev, fotg); -- else -+ } else { -+ if (val & FOTG210_RR_CROLE) -+ dev_err(dev, "block not in host role\n"); - ret = fotg210_hcd_probe(pdev, fotg); -+ } - - return ret; - } diff --git a/target/linux/gemini/patches-6.1/0020-usb-fotg210-udc-Assign-of_node-and-speed-on-start.patch b/target/linux/gemini/patches-6.1/0020-usb-fotg210-udc-Assign-of_node-and-speed-on-start.patch deleted file mode 100644 index 20f8f94350..0000000000 --- a/target/linux/gemini/patches-6.1/0020-usb-fotg210-udc-Assign-of_node-and-speed-on-start.patch +++ /dev/null @@ -1,34 +0,0 @@ -From d7c2b0b6da75b86cf5ddbcd51a74d74e19bbf178 Mon Sep 17 00:00:00 2001 -From: Linus Walleij -Date: Wed, 18 Jan 2023 08:09:20 +0100 -Subject: [PATCH 20/29] usb: fotg210-udc: Assign of_node and speed on start - -Follow the example set by other drivers to assign of_node -and speed to the driver when binding, also print bound -info akin to other UDC drivers. - -Signed-off-by: Linus Walleij -Link: https://lore.kernel.org/r/20230103-gemini-fotg210-usb-v2-6-100388af9810@linaro.org -Signed-off-by: Greg Kroah-Hartman ---- ---- a/drivers/usb/fotg210/fotg210-udc.c -+++ b/drivers/usb/fotg210/fotg210-udc.c -@@ -1028,6 +1028,10 @@ static int fotg210_udc_start(struct usb_ - - /* hook up the driver */ - fotg210->driver = driver; -+ fotg210->gadget.dev.of_node = fotg210->dev->of_node; -+ fotg210->gadget.speed = USB_SPEED_UNKNOWN; -+ -+ dev_info(fotg210->dev, "bound driver %s\n", driver->driver.name); - - if (!IS_ERR_OR_NULL(fotg210->phy)) { - ret = otg_set_peripheral(fotg210->phy->otg, -@@ -1084,6 +1088,7 @@ static int fotg210_udc_stop(struct usb_g - - fotg210_init(fotg210); - fotg210->driver = NULL; -+ fotg210->gadget.speed = USB_SPEED_UNKNOWN; - - spin_unlock_irqrestore(&fotg210->lock, flags); - diff --git a/target/linux/gemini/patches-6.1/0021-usb-fotg210-udc-Implement-VBUS-session.patch b/target/linux/gemini/patches-6.1/0021-usb-fotg210-udc-Implement-VBUS-session.patch deleted file mode 100644 index d98561f0d4..0000000000 --- a/target/linux/gemini/patches-6.1/0021-usb-fotg210-udc-Implement-VBUS-session.patch +++ /dev/null @@ -1,96 +0,0 @@ -From 2fbbfb2c556944945639b17b13fcb1e05272b646 Mon Sep 17 00:00:00 2001 -From: Linus Walleij -Date: Wed, 18 Jan 2023 08:09:21 +0100 -Subject: [PATCH 21/29] usb: fotg210-udc: Implement VBUS session - -Implement VBUS session handling for FOTG210. This is -mainly used by the UDC driver which needs to call down to -the FOTG210 core and enable/disable VBUS, as this needs to be -handled outside of the HCD and UDC drivers, by platform -specific glue code. - -The Gemini has a special bit in a system register to turn -VBUS on and off so we implement this in the FOTG210 core. - -Signed-off-by: Linus Walleij -Link: https://lore.kernel.org/r/20230103-gemini-fotg210-usb-v2-7-100388af9810@linaro.org -Signed-off-by: Greg Kroah-Hartman ---- ---- a/drivers/usb/fotg210/fotg210-core.c -+++ b/drivers/usb/fotg210/fotg210-core.c -@@ -95,6 +95,35 @@ static int fotg210_gemini_init(struct fo - return 0; - } - -+/** -+ * fotg210_vbus() - Called by gadget driver to enable/disable VBUS -+ * @enable: true to enable VBUS, false to disable VBUS -+ */ -+void fotg210_vbus(struct fotg210 *fotg, bool enable) -+{ -+ u32 mask; -+ u32 val; -+ int ret; -+ -+ switch (fotg->port) { -+ case GEMINI_PORT_0: -+ mask = GEMINI_MISC_USB0_VBUS_ON; -+ val = enable ? GEMINI_MISC_USB0_VBUS_ON : 0; -+ break; -+ case GEMINI_PORT_1: -+ mask = GEMINI_MISC_USB1_VBUS_ON; -+ val = enable ? GEMINI_MISC_USB1_VBUS_ON : 0; -+ break; -+ default: -+ return; -+ } -+ ret = regmap_update_bits(fotg->map, GEMINI_GLOBAL_MISC_CTRL, mask, val); -+ if (ret) -+ dev_err(fotg->dev, "failed to %s VBUS\n", -+ enable ? "enable" : "disable"); -+ dev_info(fotg->dev, "%s: %s VBUS\n", __func__, enable ? "enable" : "disable"); -+} -+ - static int fotg210_probe(struct platform_device *pdev) - { - struct device *dev = &pdev->dev; ---- a/drivers/usb/fotg210/fotg210-udc.c -+++ b/drivers/usb/fotg210/fotg210-udc.c -@@ -1095,9 +1095,26 @@ static int fotg210_udc_stop(struct usb_g - return 0; - } - -+/** -+ * fotg210_vbus_session - Called by external transceiver to enable/disable udc -+ * @_gadget: usb gadget -+ * @is_active: 0 if should disable UDC VBUS, 1 if should enable -+ * -+ * Returns 0 -+ */ -+static int fotg210_vbus_session(struct usb_gadget *g, int is_active) -+{ -+ struct fotg210_udc *fotg210 = gadget_to_fotg210(g); -+ -+ /* Call down to core integration layer to drive or disable VBUS */ -+ fotg210_vbus(fotg210->fotg, is_active); -+ return 0; -+} -+ - static const struct usb_gadget_ops fotg210_gadget_ops = { - .udc_start = fotg210_udc_start, - .udc_stop = fotg210_udc_stop, -+ .vbus_session = fotg210_vbus_session, - }; - - /** ---- a/drivers/usb/fotg210/fotg210.h -+++ b/drivers/usb/fotg210/fotg210.h -@@ -17,6 +17,8 @@ struct fotg210 { - enum gemini_port port; - }; - -+void fotg210_vbus(struct fotg210 *fotg, bool enable); -+ - #ifdef CONFIG_USB_FOTG210_HCD - int fotg210_hcd_probe(struct platform_device *pdev, struct fotg210 *fotg); - int fotg210_hcd_remove(struct platform_device *pdev); diff --git a/target/linux/gemini/patches-6.1/0022-fotg210-udc-Introduce-and-use-a-fotg210_ack_int-func.patch b/target/linux/gemini/patches-6.1/0022-fotg210-udc-Introduce-and-use-a-fotg210_ack_int-func.patch deleted file mode 100644 index fc5831eb23..0000000000 --- a/target/linux/gemini/patches-6.1/0022-fotg210-udc-Introduce-and-use-a-fotg210_ack_int-func.patch +++ /dev/null @@ -1,134 +0,0 @@ -From f011d1eab23f4c063c5441c0d5a22898adf9145c Mon Sep 17 00:00:00 2001 -From: Fabian Vogt -Date: Mon, 23 Jan 2023 08:35:07 +0100 -Subject: [PATCH 22/29] fotg210-udc: Introduce and use a fotg210_ack_int - function - -This is in preparation of support for devices where interrupts are acked -differently. - -Signed-off-by: Fabian Vogt -Signed-off-by: Linus Walleij -Link: https://lore.kernel.org/r/20230123073508.2350402-3-linus.walleij@linaro.org -Signed-off-by: Greg Kroah-Hartman ---- ---- a/drivers/usb/fotg210/fotg210-udc.c -+++ b/drivers/usb/fotg210/fotg210-udc.c -@@ -28,6 +28,14 @@ static const char udc_name[] = "fotg210_ - static const char * const fotg210_ep_name[] = { - "ep0", "ep1", "ep2", "ep3", "ep4"}; - -+static void fotg210_ack_int(struct fotg210_udc *fotg210, u32 offset, u32 mask) -+{ -+ u32 value = ioread32(fotg210->reg + offset); -+ -+ value &= ~mask; -+ iowrite32(value, fotg210->reg + offset); -+} -+ - static void fotg210_disable_fifo_int(struct fotg210_ep *ep) - { - u32 value = ioread32(ep->fotg210->reg + FOTG210_DMISGR1); -@@ -303,8 +311,7 @@ static void fotg210_wait_dma_done(struct - goto dma_reset; - } while (!(value & DISGR2_DMA_CMPLT)); - -- value &= ~DISGR2_DMA_CMPLT; -- iowrite32(value, ep->fotg210->reg + FOTG210_DISGR2); -+ fotg210_ack_int(ep->fotg210, FOTG210_DISGR2, DISGR2_DMA_CMPLT); - return; - - dma_reset: -@@ -844,14 +851,6 @@ static void fotg210_ep0in(struct fotg210 - } - } - --static void fotg210_clear_comabt_int(struct fotg210_udc *fotg210) --{ -- u32 value = ioread32(fotg210->reg + FOTG210_DISGR0); -- -- value &= ~DISGR0_CX_COMABT_INT; -- iowrite32(value, fotg210->reg + FOTG210_DISGR0); --} -- - static void fotg210_in_fifo_handler(struct fotg210_ep *ep) - { - struct fotg210_request *req = list_entry(ep->queue.next, -@@ -893,60 +892,43 @@ static irqreturn_t fotg210_irq(int irq, - void __iomem *reg = fotg210->reg + FOTG210_DISGR2; - u32 int_grp2 = ioread32(reg); - u32 int_msk2 = ioread32(fotg210->reg + FOTG210_DMISGR2); -- u32 value; - - int_grp2 &= ~int_msk2; - - if (int_grp2 & DISGR2_USBRST_INT) { - usb_gadget_udc_reset(&fotg210->gadget, - fotg210->driver); -- value = ioread32(reg); -- value &= ~DISGR2_USBRST_INT; -- iowrite32(value, reg); -+ fotg210_ack_int(fotg210, FOTG210_DISGR2, DISGR2_USBRST_INT); - pr_info("fotg210 udc reset\n"); - } - if (int_grp2 & DISGR2_SUSP_INT) { -- value = ioread32(reg); -- value &= ~DISGR2_SUSP_INT; -- iowrite32(value, reg); -+ fotg210_ack_int(fotg210, FOTG210_DISGR2, DISGR2_SUSP_INT); - pr_info("fotg210 udc suspend\n"); - } - if (int_grp2 & DISGR2_RESM_INT) { -- value = ioread32(reg); -- value &= ~DISGR2_RESM_INT; -- iowrite32(value, reg); -+ fotg210_ack_int(fotg210, FOTG210_DISGR2, DISGR2_RESM_INT); - pr_info("fotg210 udc resume\n"); - } - if (int_grp2 & DISGR2_ISO_SEQ_ERR_INT) { -- value = ioread32(reg); -- value &= ~DISGR2_ISO_SEQ_ERR_INT; -- iowrite32(value, reg); -+ fotg210_ack_int(fotg210, FOTG210_DISGR2, DISGR2_ISO_SEQ_ERR_INT); - pr_info("fotg210 iso sequence error\n"); - } - if (int_grp2 & DISGR2_ISO_SEQ_ABORT_INT) { -- value = ioread32(reg); -- value &= ~DISGR2_ISO_SEQ_ABORT_INT; -- iowrite32(value, reg); -+ fotg210_ack_int(fotg210, FOTG210_DISGR2, DISGR2_ISO_SEQ_ABORT_INT); - pr_info("fotg210 iso sequence abort\n"); - } - if (int_grp2 & DISGR2_TX0BYTE_INT) { - fotg210_clear_tx0byte(fotg210); -- value = ioread32(reg); -- value &= ~DISGR2_TX0BYTE_INT; -- iowrite32(value, reg); -+ fotg210_ack_int(fotg210, FOTG210_DISGR2, DISGR2_TX0BYTE_INT); - pr_info("fotg210 transferred 0 byte\n"); - } - if (int_grp2 & DISGR2_RX0BYTE_INT) { - fotg210_clear_rx0byte(fotg210); -- value = ioread32(reg); -- value &= ~DISGR2_RX0BYTE_INT; -- iowrite32(value, reg); -+ fotg210_ack_int(fotg210, FOTG210_DISGR2, DISGR2_RX0BYTE_INT); - pr_info("fotg210 received 0 byte\n"); - } - if (int_grp2 & DISGR2_DMA_ERROR) { -- value = ioread32(reg); -- value &= ~DISGR2_DMA_ERROR; -- iowrite32(value, reg); -+ fotg210_ack_int(fotg210, FOTG210_DISGR2, DISGR2_DMA_ERROR); - } - } - -@@ -960,7 +942,7 @@ static irqreturn_t fotg210_irq(int irq, - - /* the highest priority in this source register */ - if (int_grp0 & DISGR0_CX_COMABT_INT) { -- fotg210_clear_comabt_int(fotg210); -+ fotg210_ack_int(fotg210, FOTG210_DISGR0, DISGR0_CX_COMABT_INT); - pr_info("fotg210 CX command abort\n"); - } - diff --git a/target/linux/gemini/patches-6.1/0023-fotg210-udc-Improve-device-initialization.patch b/target/linux/gemini/patches-6.1/0023-fotg210-udc-Improve-device-initialization.patch deleted file mode 100644 index fde17a48b3..0000000000 --- a/target/linux/gemini/patches-6.1/0023-fotg210-udc-Improve-device-initialization.patch +++ /dev/null @@ -1,62 +0,0 @@ -From 367747c7813cecf19b46ef7134691f903ab76dc9 Mon Sep 17 00:00:00 2001 -From: Fabian Vogt -Date: Mon, 23 Jan 2023 08:35:08 +0100 -Subject: [PATCH 23/29] fotg210-udc: Improve device initialization - -Reset the device explicitly to get into a known state and also set the chip -enable bit. Additionally, mask interrupts which aren't handled. - -Signed-off-by: Fabian Vogt -Signed-off-by: Linus Walleij -Link: https://lore.kernel.org/r/20230123073508.2350402-4-linus.walleij@linaro.org -Signed-off-by: Greg Kroah-Hartman ---- ---- a/drivers/usb/fotg210/fotg210-udc.c -+++ b/drivers/usb/fotg210/fotg210-udc.c -@@ -7,6 +7,7 @@ - * Author : Yuan-Hsin Chen - */ - -+#include - #include - #include - #include -@@ -1022,6 +1023,11 @@ static int fotg210_udc_start(struct usb_ - dev_err(fotg210->dev, "can't bind to phy\n"); - } - -+ /* chip enable */ -+ value = ioread32(fotg210->reg + FOTG210_DMCR); -+ value |= DMCR_CHIP_EN; -+ iowrite32(value, fotg210->reg + FOTG210_DMCR); -+ - /* enable device global interrupt */ - value = ioread32(fotg210->reg + FOTG210_DMCR); - value |= DMCR_GLINT_EN; -@@ -1038,6 +1044,15 @@ static void fotg210_init(struct fotg210_ - iowrite32(GMIR_MHC_INT | GMIR_MOTG_INT | GMIR_INT_POLARITY, - fotg210->reg + FOTG210_GMIR); - -+ /* mask interrupts for groups other than 0-2 */ -+ iowrite32(~(DMIGR_MINT_G0 | DMIGR_MINT_G1 | DMIGR_MINT_G2), -+ fotg210->reg + FOTG210_DMIGR); -+ -+ /* udc software reset */ -+ iowrite32(DMCR_SFRST, fotg210->reg + FOTG210_DMCR); -+ /* Better wait a bit, but without a datasheet, no idea how long. */ -+ usleep_range(100, 200); -+ - /* disable device global interrupt */ - value = ioread32(fotg210->reg + FOTG210_DMCR); - value &= ~DMCR_GLINT_EN; ---- a/drivers/usb/fotg210/fotg210-udc.h -+++ b/drivers/usb/fotg210/fotg210-udc.h -@@ -58,6 +58,8 @@ - - /* Device Mask of Interrupt Group Register (0x130) */ - #define FOTG210_DMIGR 0x130 -+#define DMIGR_MINT_G2 (1 << 2) -+#define DMIGR_MINT_G1 (1 << 1) - #define DMIGR_MINT_G0 (1 << 0) - - /* Device Mask of Interrupt Source Group 0(0x134) */ diff --git a/target/linux/gemini/patches-6.1/0024-usb-fotg210-hcd-use-sysfs_emit-to-instead-of-scnprin.patch b/target/linux/gemini/patches-6.1/0024-usb-fotg210-hcd-use-sysfs_emit-to-instead-of-scnprin.patch deleted file mode 100644 index 680836110a..0000000000 --- a/target/linux/gemini/patches-6.1/0024-usb-fotg210-hcd-use-sysfs_emit-to-instead-of-scnprin.patch +++ /dev/null @@ -1,32 +0,0 @@ -From 482830a70408a5d30af264b3d6706f818c78b2b2 Mon Sep 17 00:00:00 2001 -From: Andy Shevchenko -Date: Fri, 20 Jan 2023 17:44:33 +0200 -Subject: [PATCH 24/29] usb: fotg210-hcd: use sysfs_emit() to instead of - scnprintf() - -Follow the advice of the Documentation/filesystems/sysfs.rst and show() -should only use sysfs_emit() or sysfs_emit_at() when formatting the -value to be returned to user space. - -Signed-off-by: Andy Shevchenko -Link: https://lore.kernel.org/r/20230120154437.22025-1-andriy.shevchenko@linux.intel.com -Signed-off-by: Greg Kroah-Hartman ---- ---- a/drivers/usb/fotg210/fotg210-hcd.c -+++ b/drivers/usb/fotg210/fotg210-hcd.c -@@ -4686,14 +4686,11 @@ static ssize_t uframe_periodic_max_show( - struct device_attribute *attr, char *buf) - { - struct fotg210_hcd *fotg210; -- int n; - - fotg210 = hcd_to_fotg210(bus_to_hcd(dev_get_drvdata(dev))); -- n = scnprintf(buf, PAGE_SIZE, "%d\n", fotg210->uframe_periodic_max); -- return n; -+ return sysfs_emit(buf, "%d\n", fotg210->uframe_periodic_max); - } - -- - static ssize_t uframe_periodic_max_store(struct device *dev, - struct device_attribute *attr, const char *buf, size_t count) - { diff --git a/target/linux/gemini/patches-6.1/0025-ARM-dts-gemini-Push-down-flash-address-size-cells.patch b/target/linux/gemini/patches-6.1/0025-ARM-dts-gemini-Push-down-flash-address-size-cells.patch deleted file mode 100644 index 1e031f1d4f..0000000000 --- a/target/linux/gemini/patches-6.1/0025-ARM-dts-gemini-Push-down-flash-address-size-cells.patch +++ /dev/null @@ -1,62 +0,0 @@ -From 6b84aa39a063eec883d410a9893cec70fce56163 Mon Sep 17 00:00:00 2001 -From: Linus Walleij -Date: Sun, 4 Dec 2022 20:02:28 +0100 -Subject: [PATCH 25/29] ARM: dts: gemini: Push down flash address/size cells - -The platforms not defining any OF partions complain like -this: - -../arch/arm/boot/dts/gemini.dtsi:19.25-28.5: Warning - (avoid_unnecessary_addr_size): /soc/flash@30000000: unnecessary - #address-cells/#size-cells without "ranges" or child "reg" property - -Get rid of this by only defining the address-cells and -size-cells where it is actually used by OF partitions. - -Link: https://lore.kernel.org/r/20221204190230.3345590-1-linus.walleij@linaro.org -Signed-off-by: Linus Walleij ---- ---- a/arch/arm/boot/dts/gemini-dlink-dns-313.dts -+++ b/arch/arm/boot/dts/gemini-dlink-dns-313.dts -@@ -164,6 +164,8 @@ - compatible = "cortina,gemini-flash", "jedec-flash"; - status = "okay"; - reg = <0x30000000 0x00080000>; -+ #address-cells = <1>; -+ #size-cells = <1>; - - /* - * This "RedBoot" is the Storlink derivative. ---- a/arch/arm/boot/dts/gemini-wbd111.dts -+++ b/arch/arm/boot/dts/gemini-wbd111.dts -@@ -86,6 +86,8 @@ - status = "okay"; - /* 8MB of flash */ - reg = <0x30000000 0x00800000>; -+ #address-cells = <1>; -+ #size-cells = <1>; - - partition@0 { - label = "RedBoot"; ---- a/arch/arm/boot/dts/gemini-wbd222.dts -+++ b/arch/arm/boot/dts/gemini-wbd222.dts -@@ -90,6 +90,8 @@ - status = "okay"; - /* 8MB of flash */ - reg = <0x30000000 0x00800000>; -+ #address-cells = <1>; -+ #size-cells = <1>; - - partition@0 { - label = "RedBoot"; ---- a/arch/arm/boot/dts/gemini.dtsi -+++ b/arch/arm/boot/dts/gemini.dtsi -@@ -22,8 +22,6 @@ - pinctrl-names = "default"; - pinctrl-0 = <&pflash_default_pins>; - bank-width = <2>; -- #address-cells = <1>; -- #size-cells = <1>; - status = "disabled"; - }; - diff --git a/target/linux/gemini/patches-6.1/0026-ARM-dts-gemini-wbd111-Use-RedBoot-partion-parser.patch b/target/linux/gemini/patches-6.1/0026-ARM-dts-gemini-wbd111-Use-RedBoot-partion-parser.patch deleted file mode 100644 index 1aff23ed1b..0000000000 --- a/target/linux/gemini/patches-6.1/0026-ARM-dts-gemini-wbd111-Use-RedBoot-partion-parser.patch +++ /dev/null @@ -1,54 +0,0 @@ -From 0e733f5af628210f372585e431504a7024e7b571 Mon Sep 17 00:00:00 2001 -From: Linus Walleij -Date: Sun, 4 Dec 2022 20:02:29 +0100 -Subject: [PATCH 26/29] ARM: dts: gemini: wbd111: Use RedBoot partion parser - -This is clearly a RedBoot partitioned device with 0x20000 -sized erase blocks. - -Link: https://lore.kernel.org/r/20221204190230.3345590-2-linus.walleij@linaro.org -Signed-off-by: Linus Walleij ---- ---- a/arch/arm/boot/dts/gemini-wbd111.dts -+++ b/arch/arm/boot/dts/gemini-wbd111.dts -@@ -86,36 +86,11 @@ - status = "okay"; - /* 8MB of flash */ - reg = <0x30000000 0x00800000>; -- #address-cells = <1>; -- #size-cells = <1>; - -- partition@0 { -- label = "RedBoot"; -- reg = <0x00000000 0x00020000>; -- read-only; -- }; -- partition@20000 { -- label = "kernel"; -- reg = <0x00020000 0x00100000>; -- }; -- partition@120000 { -- label = "rootfs"; -- reg = <0x00120000 0x006a0000>; -- }; -- partition@7c0000 { -- label = "VCTL"; -- reg = <0x007c0000 0x00010000>; -- read-only; -- }; -- partition@7d0000 { -- label = "cfg"; -- reg = <0x007d0000 0x00010000>; -- read-only; -- }; -- partition@7e0000 { -- label = "FIS"; -- reg = <0x007e0000 0x00010000>; -- read-only; -+ partitions { -+ compatible = "redboot-fis"; -+ /* Eraseblock at 0x7e0000 */ -+ fis-index-block = <0x3f>; - }; - }; - diff --git a/target/linux/gemini/patches-6.1/0027-ARM-dts-gemini-wbd222-Use-RedBoot-partion-parser.patch b/target/linux/gemini/patches-6.1/0027-ARM-dts-gemini-wbd222-Use-RedBoot-partion-parser.patch deleted file mode 100644 index 8cafeaa0df..0000000000 --- a/target/linux/gemini/patches-6.1/0027-ARM-dts-gemini-wbd222-Use-RedBoot-partion-parser.patch +++ /dev/null @@ -1,54 +0,0 @@ -From 8558e2e1110a5daa4ac9e1c5b5c15e1651a8fb94 Mon Sep 17 00:00:00 2001 -From: Linus Walleij -Date: Sun, 4 Dec 2022 20:02:30 +0100 -Subject: [PATCH 27/29] ARM: dts: gemini: wbd222: Use RedBoot partion parser - -This is clearly a RedBoot partitioned device with 0x20000 -sized erase blocks. - -Link: https://lore.kernel.org/r/20221204190230.3345590-3-linus.walleij@linaro.org -Signed-off-by: Linus Walleij ---- ---- a/arch/arm/boot/dts/gemini-wbd222.dts -+++ b/arch/arm/boot/dts/gemini-wbd222.dts -@@ -90,36 +90,11 @@ - status = "okay"; - /* 8MB of flash */ - reg = <0x30000000 0x00800000>; -- #address-cells = <1>; -- #size-cells = <1>; - -- partition@0 { -- label = "RedBoot"; -- reg = <0x00000000 0x00020000>; -- read-only; -- }; -- partition@20000 { -- label = "kernel"; -- reg = <0x00020000 0x00100000>; -- }; -- partition@120000 { -- label = "rootfs"; -- reg = <0x00120000 0x006a0000>; -- }; -- partition@7c0000 { -- label = "VCTL"; -- reg = <0x007c0000 0x00010000>; -- read-only; -- }; -- partition@7d0000 { -- label = "cfg"; -- reg = <0x007d0000 0x00010000>; -- read-only; -- }; -- partition@7e0000 { -- label = "FIS"; -- reg = <0x007e0000 0x00010000>; -- read-only; -+ partitions { -+ compatible = "redboot-fis"; -+ /* Eraseblock at 0x7e0000 */ -+ fis-index-block = <0x3f>; - }; - }; - diff --git a/target/linux/gemini/patches-6.1/0028-ARM-dts-gemini-Fix-USB-block-version.patch b/target/linux/gemini/patches-6.1/0028-ARM-dts-gemini-Fix-USB-block-version.patch deleted file mode 100644 index fb93b70a31..0000000000 --- a/target/linux/gemini/patches-6.1/0028-ARM-dts-gemini-Fix-USB-block-version.patch +++ /dev/null @@ -1,31 +0,0 @@ -From d5c01ce4a1016507c69682894cf6b66301abca3d Mon Sep 17 00:00:00 2001 -From: Linus Walleij -Date: Mon, 23 Jan 2023 08:39:15 +0100 -Subject: [PATCH 28/29] ARM: dts: gemini: Fix USB block version - -The FOTG version in the Gemini is the FOTG200, fix this -up. - -Signed-off-by: Linus Walleij -Link: https://lore.kernel.org/r/20230123073916.2350839-1-linus.walleij@linaro.org ---- ---- a/arch/arm/boot/dts/gemini.dtsi -+++ b/arch/arm/boot/dts/gemini.dtsi -@@ -439,7 +439,7 @@ - }; - - usb0: usb@68000000 { -- compatible = "cortina,gemini-usb", "faraday,fotg210"; -+ compatible = "cortina,gemini-usb", "faraday,fotg200"; - reg = <0x68000000 0x1000>; - interrupts = <10 IRQ_TYPE_LEVEL_HIGH>; - resets = <&syscon GEMINI_RESET_USB0>; -@@ -460,7 +460,7 @@ - }; - - usb1: usb@69000000 { -- compatible = "cortina,gemini-usb", "faraday,fotg210"; -+ compatible = "cortina,gemini-usb", "faraday,fotg200"; - reg = <0x69000000 0x1000>; - interrupts = <11 IRQ_TYPE_LEVEL_HIGH>; - resets = <&syscon GEMINI_RESET_USB1>; diff --git a/target/linux/gemini/patches-6.1/0029-ARM-dts-gemini-Enable-DNS313-FOTG210-as-periph.patch b/target/linux/gemini/patches-6.1/0029-ARM-dts-gemini-Enable-DNS313-FOTG210-as-periph.patch deleted file mode 100644 index 667878170b..0000000000 --- a/target/linux/gemini/patches-6.1/0029-ARM-dts-gemini-Enable-DNS313-FOTG210-as-periph.patch +++ /dev/null @@ -1,54 +0,0 @@ -From 296184694ae7a4e388603c95499e98d30b21cc09 Mon Sep 17 00:00:00 2001 -From: Linus Walleij -Date: Mon, 23 Jan 2023 08:39:16 +0100 -Subject: [PATCH 29/29] ARM: dts: gemini: Enable DNS313 FOTG210 as periph - -Add the GPIO-based VBUS phy, and enable the FOTG210 -USB1 block for use as peripheral. - -Signed-off-by: Linus Walleij -Link: https://lore.kernel.org/r/20230123073916.2350839-2-linus.walleij@linaro.org ---- ---- a/arch/arm/boot/dts/gemini-dlink-dns-313.dts -+++ b/arch/arm/boot/dts/gemini-dlink-dns-313.dts -@@ -80,6 +80,15 @@ - #cooling-cells = <2>; - }; - -+ /* -+ * This is the type B USB connector on the device, -+ * a GPIO-controlled USB VBUS detect -+ */ -+ usb1_phy: phy { -+ compatible = "gpio-usb-b-connector", "usb-b-connector"; -+ #phy-cells = <0>; -+ vbus-gpios = <&gpio0 18 GPIO_ACTIVE_LOW>; -+ }; - - /* Global Mixed-Mode Technology G751 mounted on GPIO I2C */ - i2c { -@@ -302,5 +311,13 @@ - ide@63000000 { - status = "okay"; - }; -+ -+ usb@69000000 { -+ status = "okay"; -+ dr_mode = "peripheral"; -+ usb-phy = <&usb1_phy>; -+ pinctrl-names = "default"; -+ pinctrl-0 = <&usb_default_pins>; -+ }; - }; - }; ---- a/arch/arm/boot/dts/gemini.dtsi -+++ b/arch/arm/boot/dts/gemini.dtsi -@@ -455,6 +455,8 @@ - */ - pinctrl-names = "default"; - pinctrl-0 = <&usb_default_pins>; -+ /* Default to host mode */ -+ dr_mode = "host"; - syscon = <&syscon>; - status = "disabled"; - }; diff --git a/target/linux/gemini/patches-6.6/0001-net-ethernet-cortina-Drop-TSO-support.patch b/target/linux/gemini/patches-6.6/0001-net-ethernet-cortina-Drop-TSO-support.patch new file mode 100644 index 0000000000..43e0cb283a --- /dev/null +++ b/target/linux/gemini/patches-6.6/0001-net-ethernet-cortina-Drop-TSO-support.patch @@ -0,0 +1,78 @@ +From f8001196455311eb128fcafd98cb2050a70218df Mon Sep 17 00:00:00 2001 +From: Linus Walleij +Date: Sat, 6 Jan 2024 01:12:22 +0100 +Subject: [PATCH 4/4] net: ethernet: cortina: Drop TSO support + +The recent change to allow large frames without hardware checksumming +slotted in software checksumming in the driver if hardware could not +do it. + +This will however upset TSO (TCP Segment Offloading). Typical +error dumps includes this: + +skb len=2961 headroom=222 headlen=66 tailroom=0 +(...) +WARNING: CPU: 0 PID: 956 at net/core/dev.c:3259 skb_warn_bad_offload+0x7c/0x108 +gemini-ethernet-port: caps=(0x0000010000154813, 0x00002007ffdd7889) + +And the packets do not go through. + +The TSO implementation is bogus: a TSO enabled driver must propagate +the skb_shinfo(skb)->gso_size value to the TSO engine on the NIC. + +Drop the size check and TSO offloading features for now: this +needs to be fixed up properly. + +After this ethernet works fine on Gemini devices with a direct connected +PHY such as D-Link DNS-313. + +Also tested to still be working with a DSA switch using the Gemini +ethernet as conduit interface. + +Link: https://lore.kernel.org/netdev/CANn89iJLfxng1sYL5Zk0mknXpyYQPCp83m3KgD2KJ2_hKCpEUg@mail.gmail.com/ +Suggested-by: Eric Dumazet +Fixes: d4d0c5b4d279 ("net: ethernet: cortina: Handle large frames") +Signed-off-by: Linus Walleij +Reviewed-by: Eric Dumazet +Signed-off-by: David S. Miller +--- + drivers/net/ethernet/cortina/gemini.c | 15 ++------------- + 1 file changed, 2 insertions(+), 13 deletions(-) + +--- a/drivers/net/ethernet/cortina/gemini.c ++++ b/drivers/net/ethernet/cortina/gemini.c +@@ -79,8 +79,7 @@ MODULE_PARM_DESC(debug, "Debug level (0= + #define GMAC0_IRQ4_8 (GMAC0_MIB_INT_BIT | GMAC0_RX_OVERRUN_INT_BIT) + + #define GMAC_OFFLOAD_FEATURES (NETIF_F_SG | NETIF_F_IP_CSUM | \ +- NETIF_F_IPV6_CSUM | NETIF_F_RXCSUM | \ +- NETIF_F_TSO | NETIF_F_TSO_ECN | NETIF_F_TSO6) ++ NETIF_F_IPV6_CSUM | NETIF_F_RXCSUM) + + /** + * struct gmac_queue_page - page buffer per-page info +@@ -1143,23 +1142,13 @@ static int gmac_map_tx_bufs(struct net_d + struct gmac_txdesc *txd; + skb_frag_t *skb_frag; + dma_addr_t mapping; +- unsigned short mtu; + void *buffer; + int ret; + +- mtu = ETH_HLEN; +- mtu += netdev->mtu; +- if (skb->protocol == htons(ETH_P_8021Q)) +- mtu += VLAN_HLEN; +- ++ /* TODO: implement proper TSO using MTU in word3 */ + word1 = skb->len; + word3 = SOF_BIT; + +- if (word1 > mtu) { +- word1 |= TSS_MTU_ENABLE_BIT; +- word3 |= mtu; +- } +- + if (skb->len >= ETH_FRAME_LEN) { + /* Hardware offloaded checksumming isn't working on frames + * bigger than 1514 bytes. A hypothesis about this is that the diff --git a/target/linux/gemini/patches-6.6/0002-ARM-dts-gemini-Map-reset-keys-to-KEY_RESTART.patch b/target/linux/gemini/patches-6.6/0002-ARM-dts-gemini-Map-reset-keys-to-KEY_RESTART.patch new file mode 100644 index 0000000000..1d1ee2b46c --- /dev/null +++ b/target/linux/gemini/patches-6.6/0002-ARM-dts-gemini-Map-reset-keys-to-KEY_RESTART.patch @@ -0,0 +1,103 @@ +From 091cde88b5ff2a2ca5739ce41f9cf5640a95222f Mon Sep 17 00:00:00 2001 +From: Linus Walleij +Date: Sun, 11 Feb 2024 22:24:25 +0100 +Subject: [PATCH] ARM: dts: gemini: Map reset keys to KEY_RESTART + +This maps the misc "reset", "setup" and "facory reset" keys to the +only key a standard userspace is likely to understand: KEY_RESTART. +On OpenWrt this will simply restart the system under controlled +forms. + +Signed-off-by: Linus Walleij +Link: https://lore.kernel.org/r/20240211-gemini-dts-v1-3-6c09adeb4c2e@linaro.org +--- + arch/arm/boot/dts/gemini/gemini-dlink-dir-685.dts | 4 ++-- + arch/arm/boot/dts/gemini/gemini-dlink-dns-313.dts | 4 ++-- + arch/arm/boot/dts/gemini/gemini-sl93512r.dts | 2 +- + arch/arm/boot/dts/gemini/gemini-sq201.dts | 2 +- + arch/arm/boot/dts/gemini/gemini-wbd111.dts | 4 ++-- + arch/arm/boot/dts/gemini/gemini-wbd222.dts | 4 ++-- + 6 files changed, 10 insertions(+), 10 deletions(-) + +--- a/arch/arm/boot/dts/gemini/gemini-dlink-dir-685.dts ++++ b/arch/arm/boot/dts/gemini/gemini-dlink-dir-685.dts +@@ -27,10 +27,10 @@ + gpio_keys { + compatible = "gpio-keys"; + +- button-esc { ++ button-reset { + debounce-interval = <100>; + wakeup-source; +- linux,code = ; ++ linux,code = ; + label = "reset"; + /* Collides with LPC_LAD[0], UART DCD, SSP 97RST */ + gpios = <&gpio0 8 GPIO_ACTIVE_LOW>; +--- a/arch/arm/boot/dts/gemini/gemini-dlink-dns-313.dts ++++ b/arch/arm/boot/dts/gemini/gemini-dlink-dns-313.dts +@@ -33,10 +33,10 @@ + gpio_keys { + compatible = "gpio-keys"; + +- button-esc { ++ button-reset { + debounce-interval = <100>; + wakeup-source; +- linux,code = ; ++ linux,code = ; + label = "reset"; + gpios = <&gpio1 31 GPIO_ACTIVE_LOW>; + }; +--- a/arch/arm/boot/dts/gemini/gemini-sl93512r.dts ++++ b/arch/arm/boot/dts/gemini/gemini-sl93512r.dts +@@ -43,7 +43,7 @@ + button-setup { + debounce-interval = <50>; + wakeup-source; +- linux,code = ; ++ linux,code = ; + label = "factory reset"; + /* Conflict with NAND flash */ + gpios = <&gpio0 18 GPIO_ACTIVE_LOW>; +--- a/arch/arm/boot/dts/gemini/gemini-sq201.dts ++++ b/arch/arm/boot/dts/gemini/gemini-sq201.dts +@@ -30,7 +30,7 @@ + button-setup { + debounce-interval = <100>; + wakeup-source; +- linux,code = ; ++ linux,code = ; + label = "factory reset"; + /* Conflict with NAND flash */ + gpios = <&gpio0 18 GPIO_ACTIVE_LOW>; +--- a/arch/arm/boot/dts/gemini/gemini-wbd111.dts ++++ b/arch/arm/boot/dts/gemini/gemini-wbd111.dts +@@ -28,10 +28,10 @@ + gpio_keys { + compatible = "gpio-keys"; + +- button-setup { ++ button-reset { + debounce-interval = <100>; + wakeup-source; +- linux,code = ; ++ linux,code = ; + label = "reset"; + /* Conflict with ICE */ + gpios = <&gpio0 5 GPIO_ACTIVE_LOW>; +--- a/arch/arm/boot/dts/gemini/gemini-wbd222.dts ++++ b/arch/arm/boot/dts/gemini/gemini-wbd222.dts +@@ -27,10 +27,10 @@ + gpio_keys { + compatible = "gpio-keys"; + +- button-setup { ++ button-reset { + debounce-interval = <100>; + wakeup-source; +- linux,code = ; ++ linux,code = ; + label = "reset"; + /* Conflict with ICE */ + gpios = <&gpio0 5 GPIO_ACTIVE_LOW>; diff --git a/target/linux/gemini/patches-6.1/300-ARM-dts-Augment-DIR-685-partition-table-for-OpenWrt.patch b/target/linux/gemini/patches-6.6/300-ARM-dts-Augment-DIR-685-partition-table-for-OpenWrt.patch similarity index 70% rename from target/linux/gemini/patches-6.1/300-ARM-dts-Augment-DIR-685-partition-table-for-OpenWrt.patch rename to target/linux/gemini/patches-6.6/300-ARM-dts-Augment-DIR-685-partition-table-for-OpenWrt.patch index 99e0d2731d..613c3a842f 100644 --- a/target/linux/gemini/patches-6.1/300-ARM-dts-Augment-DIR-685-partition-table-for-OpenWrt.patch +++ b/target/linux/gemini/patches-6.6/300-ARM-dts-Augment-DIR-685-partition-table-for-OpenWrt.patch @@ -1,7 +1,7 @@ -From 36ee838bf83c01cff7cb47c7b07be278d2950ac0 Mon Sep 17 00:00:00 2001 +From c1aa34cd568bc7b86b82353034070c32b6ebe6db Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Mon, 11 Mar 2019 15:44:29 +0100 -Subject: [PATCH 2/2] ARM: dts: Augment DIR-685 partition table for OpenWrt +Subject: [PATCH] ARM: dts: Augment DIR-685 partition table for OpenWrt Rename the firmware partition so that the firmware MTD splitter will do its job, drop the rootfs arguments as @@ -9,8 +9,11 @@ the MTD splitter will set this up automatically. Signed-off-by: Linus Walleij --- ---- a/arch/arm/boot/dts/gemini-dlink-dir-685.dts -+++ b/arch/arm/boot/dts/gemini-dlink-dir-685.dts + arch/arm/boot/dts/gemini/gemini-dlink-dir-685.dts | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +--- a/arch/arm/boot/dts/gemini/gemini-dlink-dir-685.dts ++++ b/arch/arm/boot/dts/gemini/gemini-dlink-dir-685.dts @@ -20,7 +20,7 @@ }; diff --git a/target/linux/generic/backport-6.1/301-v6.9-kernel.h-removed-REPEAT_BYTE-from-kernel.h.patch b/target/linux/generic/backport-6.1/301-v6.9-kernel.h-removed-REPEAT_BYTE-from-kernel.h.patch new file mode 100644 index 0000000000..239adff1af --- /dev/null +++ b/target/linux/generic/backport-6.1/301-v6.9-kernel.h-removed-REPEAT_BYTE-from-kernel.h.patch @@ -0,0 +1,161 @@ +From 66a5c40f60f5d88ad8d47ba6a4ba05892853fa1f Mon Sep 17 00:00:00 2001 +From: Tanzir Hasan +Date: Tue, 26 Dec 2023 18:00:00 +0000 +Subject: [PATCH] kernel.h: removed REPEAT_BYTE from kernel.h + +This patch creates wordpart.h and includes it in asm/word-at-a-time.h +for all architectures. WORD_AT_A_TIME_CONSTANTS depends on kernel.h +because of REPEAT_BYTE. Moving this to another header and including it +where necessary allows us to not include the bloated kernel.h. Making +this implicit dependency on REPEAT_BYTE explicit allows for later +improvements in the lib/string.c inclusion list. + +Suggested-by: Al Viro +Suggested-by: Andy Shevchenko +Signed-off-by: Tanzir Hasan +Reviewed-by: Andy Shevchenko +Link: https://lore.kernel.org/r/20231226-libstringheader-v6-1-80aa08c7652c@google.com +Signed-off-by: Kees Cook +--- + arch/arm/include/asm/word-at-a-time.h | 3 ++- + arch/arm64/include/asm/word-at-a-time.h | 3 ++- + arch/powerpc/include/asm/word-at-a-time.h | 4 ++-- + arch/riscv/include/asm/word-at-a-time.h | 3 ++- + arch/s390/include/asm/word-at-a-time.h | 3 ++- + arch/sh/include/asm/word-at-a-time.h | 2 ++ + arch/x86/include/asm/word-at-a-time.h | 3 ++- + arch/x86/kvm/mmu/mmu.c | 1 + + fs/namei.c | 2 +- + include/asm-generic/word-at-a-time.h | 3 ++- + include/linux/kernel.h | 8 -------- + include/linux/wordpart.h | 13 +++++++++++++ + 12 files changed, 31 insertions(+), 17 deletions(-) + create mode 100644 include/linux/wordpart.h + +--- a/arch/arm/include/asm/word-at-a-time.h ++++ b/arch/arm/include/asm/word-at-a-time.h +@@ -8,7 +8,8 @@ + * Little-endian word-at-a-time zero byte handling. + * Heavily based on the x86 algorithm. + */ +-#include ++#include ++#include + + struct word_at_a_time { + const unsigned long one_bits, high_bits; +--- a/arch/arm64/include/asm/word-at-a-time.h ++++ b/arch/arm64/include/asm/word-at-a-time.h +@@ -9,7 +9,8 @@ + + #ifndef __AARCH64EB__ + +-#include ++#include ++#include + + struct word_at_a_time { + const unsigned long one_bits, high_bits; +--- a/arch/powerpc/include/asm/word-at-a-time.h ++++ b/arch/powerpc/include/asm/word-at-a-time.h +@@ -4,8 +4,8 @@ + /* + * Word-at-a-time interfaces for PowerPC. + */ +- +-#include ++#include ++#include + #include + #include + +--- a/arch/sh/include/asm/word-at-a-time.h ++++ b/arch/sh/include/asm/word-at-a-time.h +@@ -5,6 +5,8 @@ + #ifdef CONFIG_CPU_BIG_ENDIAN + # include + #else ++#include ++#include + /* + * Little-endian version cribbed from x86. + */ +--- a/arch/x86/include/asm/word-at-a-time.h ++++ b/arch/x86/include/asm/word-at-a-time.h +@@ -2,7 +2,8 @@ + #ifndef _ASM_WORD_AT_A_TIME_H + #define _ASM_WORD_AT_A_TIME_H + +-#include ++#include ++#include + + /* + * This is largely generic for little-endian machines, but the +--- a/arch/x86/kvm/mmu/mmu.c ++++ b/arch/x86/kvm/mmu/mmu.c +@@ -44,6 +44,7 @@ + #include + #include + #include ++#include + + #include + #include +--- a/fs/namei.c ++++ b/fs/namei.c +@@ -17,8 +17,8 @@ + + #include + #include +-#include + #include ++#include + #include + #include + #include +--- a/include/asm-generic/word-at-a-time.h ++++ b/include/asm-generic/word-at-a-time.h +@@ -2,7 +2,8 @@ + #ifndef _ASM_WORD_AT_A_TIME_H + #define _ASM_WORD_AT_A_TIME_H + +-#include ++#include ++#include + #include + + #ifdef __BIG_ENDIAN +--- a/include/linux/kernel.h ++++ b/include/linux/kernel.h +@@ -36,14 +36,6 @@ + + #define STACK_MAGIC 0xdeadbeef + +-/** +- * REPEAT_BYTE - repeat the value @x multiple times as an unsigned long value +- * @x: value to repeat +- * +- * NOTE: @x is not checked for > 0xff; larger values produce odd results. +- */ +-#define REPEAT_BYTE(x) ((~0ul / 0xff) * (x)) +- + /* generic data direction definitions */ + #define READ 0 + #define WRITE 1 +--- /dev/null ++++ b/include/linux/wordpart.h +@@ -0,0 +1,13 @@ ++/* SPDX-License-Identifier: GPL-2.0 */ ++ ++#ifndef _LINUX_WORDPART_H ++#define _LINUX_WORDPART_H ++/** ++ * REPEAT_BYTE - repeat the value @x multiple times as an unsigned long value ++ * @x: value to repeat ++ * ++ * NOTE: @x is not checked for > 0xff; larger values produce odd results. ++ */ ++#define REPEAT_BYTE(x) ((~0ul / 0xff) * (x)) ++ ++#endif // _LINUX_WORDPART_H diff --git a/target/linux/generic/backport-6.1/302-v6.9-kernel.h-Move-upper_-_bits-and-lower_-_bits-to-wordp.patch b/target/linux/generic/backport-6.1/302-v6.9-kernel.h-Move-upper_-_bits-and-lower_-_bits-to-wordp.patch new file mode 100644 index 0000000000..9bbd515852 --- /dev/null +++ b/target/linux/generic/backport-6.1/302-v6.9-kernel.h-Move-upper_-_bits-and-lower_-_bits-to-wordp.patch @@ -0,0 +1,107 @@ +From adeb04362d74188c1e22ccb824b15a0a7b3de2f4 Mon Sep 17 00:00:00 2001 +From: Andy Shevchenko +Date: Wed, 14 Feb 2024 19:26:32 +0200 +Subject: [PATCH] kernel.h: Move upper_*_bits() and lower_*_bits() to + wordpart.h + +The wordpart.h header is collecting APIs related to the handling +parts of the word (usually in byte granularity). The upper_*_bits() +and lower_*_bits() are good candidates to be moved to there. + +This helps to clean up header dependency hell with regard to kernel.h +as the latter gathers completely unrelated stuff together and slows +down compilation (especially when it's included into other header). + +Signed-off-by: Andy Shevchenko +Link: https://lore.kernel.org/r/20240214172752.3605073-1-andriy.shevchenko@linux.intel.com +Reviewed-by: Randy Dunlap +Signed-off-by: Kees Cook +--- + include/linux/kernel.h | 30 ++---------------------------- + include/linux/wordpart.h | 29 +++++++++++++++++++++++++++++ + 2 files changed, 31 insertions(+), 28 deletions(-) + +--- a/include/linux/kernel.h ++++ b/include/linux/kernel.h +@@ -30,6 +30,8 @@ + #include + #include + #include ++#include ++ + #include + + #include +@@ -55,34 +57,6 @@ + } \ + ) + +-/** +- * upper_32_bits - return bits 32-63 of a number +- * @n: the number we're accessing +- * +- * A basic shift-right of a 64- or 32-bit quantity. Use this to suppress +- * the "right shift count >= width of type" warning when that quantity is +- * 32-bits. +- */ +-#define upper_32_bits(n) ((u32)(((n) >> 16) >> 16)) +- +-/** +- * lower_32_bits - return bits 0-31 of a number +- * @n: the number we're accessing +- */ +-#define lower_32_bits(n) ((u32)((n) & 0xffffffff)) +- +-/** +- * upper_16_bits - return bits 16-31 of a number +- * @n: the number we're accessing +- */ +-#define upper_16_bits(n) ((u16)((n) >> 16)) +- +-/** +- * lower_16_bits - return bits 0-15 of a number +- * @n: the number we're accessing +- */ +-#define lower_16_bits(n) ((u16)((n) & 0xffff)) +- + struct completion; + struct user; + +--- a/include/linux/wordpart.h ++++ b/include/linux/wordpart.h +@@ -2,6 +2,35 @@ + + #ifndef _LINUX_WORDPART_H + #define _LINUX_WORDPART_H ++ ++/** ++ * upper_32_bits - return bits 32-63 of a number ++ * @n: the number we're accessing ++ * ++ * A basic shift-right of a 64- or 32-bit quantity. Use this to suppress ++ * the "right shift count >= width of type" warning when that quantity is ++ * 32-bits. ++ */ ++#define upper_32_bits(n) ((u32)(((n) >> 16) >> 16)) ++ ++/** ++ * lower_32_bits - return bits 0-31 of a number ++ * @n: the number we're accessing ++ */ ++#define lower_32_bits(n) ((u32)((n) & 0xffffffff)) ++ ++/** ++ * upper_16_bits - return bits 16-31 of a number ++ * @n: the number we're accessing ++ */ ++#define upper_16_bits(n) ((u16)((n) >> 16)) ++ ++/** ++ * lower_16_bits - return bits 0-15 of a number ++ * @n: the number we're accessing ++ */ ++#define lower_16_bits(n) ((u16)((n) & 0xffff)) ++ + /** + * REPEAT_BYTE - repeat the value @x multiple times as an unsigned long value + * @x: value to repeat diff --git a/target/linux/generic/backport-6.1/790-69-v6.10-net-dsa-mt7530-do-not-set-MT7530_P5_DIS-when-PHY-.patch b/target/linux/generic/backport-6.1/790-69-v6.10-net-dsa-mt7530-do-not-set-MT7530_P5_DIS-when-PHY-.patch new file mode 100644 index 0000000000..29079e03c5 --- /dev/null +++ b/target/linux/generic/backport-6.1/790-69-v6.10-net-dsa-mt7530-do-not-set-MT7530_P5_DIS-when-PHY-.patch @@ -0,0 +1,45 @@ +From 16e6592cd5c5bd74d8890973489f60176c692614 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ar=C4=B1n=C3=A7=20=C3=9CNAL?= +Date: Sun, 28 Apr 2024 12:19:58 +0300 +Subject: [PATCH] net: dsa: mt7530: do not set MT7530_P5_DIS when PHY muxing is + being used +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +DSA initalises the ds->num_ports amount of ports in +dsa_switch_touch_ports(). When the PHY muxing feature is in use, port 5 +won't be defined in the device tree. Because of this, the type member of +the dsa_port structure for this port will be assigned DSA_PORT_TYPE_UNUSED. +The dsa_port_setup() function calls ds->ops->port_disable() when the port +type is DSA_PORT_TYPE_UNUSED. + +The MT7530_P5_DIS bit is unset in mt7530_setup() when PHY muxing is being +used. mt7530_port_disable() which is assigned to ds->ops->port_disable() is +called afterwards. Currently, mt7530_port_disable() sets MT7530_P5_DIS +which breaks network connectivity when PHY muxing is being used. + +Therefore, do not set MT7530_P5_DIS when PHY muxing is being used. + +Fixes: 377174c5760c ("net: dsa: mt7530: move MT753X_MTRAP operations for MT7530") +Reported-by: Daniel Golle +Signed-off-by: Arınç ÜNAL +Reviewed-by: Andrew Lunn +Link: https://lore.kernel.org/r/20240428-for-netnext-mt7530-do-not-disable-port5-when-phy-muxing-v2-1-bb7c37d293f8@arinc9.com +Signed-off-by: Paolo Abeni +--- + drivers/net/dsa/mt7530.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +--- a/drivers/net/dsa/mt7530.c ++++ b/drivers/net/dsa/mt7530.c +@@ -1227,7 +1227,8 @@ mt7530_port_disable(struct dsa_switch *d + if (priv->id != ID_MT7530 && priv->id != ID_MT7621) + return; + +- if (port == 5) ++ /* Do not set MT7530_P5_DIS when port 5 is being used for PHY muxing. */ ++ if (port == 5 && priv->p5_mode == GMAC5) + mt7530_set(priv, MT753X_MTRAP, MT7530_P5_DIS); + else if (port == 6) + mt7530_set(priv, MT753X_MTRAP, MT7530_P6_DIS); diff --git a/target/linux/generic/backport-6.1/790-70-v6.10-796-net-dsa-mt7530-detect-PHY-muxing-when-PHY-is-defined.patch b/target/linux/generic/backport-6.1/790-70-v6.10-796-net-dsa-mt7530-detect-PHY-muxing-when-PHY-is-defined.patch new file mode 100644 index 0000000000..69bbb8e229 --- /dev/null +++ b/target/linux/generic/backport-6.1/790-70-v6.10-796-net-dsa-mt7530-detect-PHY-muxing-when-PHY-is-defined.patch @@ -0,0 +1,45 @@ +From d8dcf5bd6d0eace9f7c1daa14b63b3925b09d033 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ar=C4=B1n=C3=A7=20=C3=9CNAL?= +Date: Tue, 30 Apr 2024 08:01:33 +0300 +Subject: [PATCH] net: dsa: mt7530: detect PHY muxing when PHY is defined on + switch MDIO bus +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Currently, the MT7530 DSA subdriver configures the MT7530 switch to provide +direct access to switch PHYs, meaning, the switch PHYs listen on the MDIO +bus the switch listens on. The PHY muxing feature makes use of this. + +This is problematic as the PHY may be attached before the switch is +initialised, in which case, the PHY will fail to be attached. + +Since commit 91374ba537bd ("net: dsa: mt7530: support OF-based registration +of switch MDIO bus"), we can describe the switch PHYs on the MDIO bus of +the switch on the device tree. Extend the check to detect PHY muxing when +the PHY is defined on the MDIO bus of the switch on the device tree. + +When the PHY is described this way, the switch will be initialised first, +then the switch MDIO bus will be registered. Only after these steps, the +PHY will be attached. + +Signed-off-by: Arınç ÜNAL +Reviewed-by: Daniel Golle +Link: https://lore.kernel.org/r/20240430-b4-for-netnext-mt7530-use-switch-mdio-bus-for-phy-muxing-v2-1-9104d886d0db@arinc9.com +Signed-off-by: Paolo Abeni +--- + drivers/net/dsa/mt7530.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +--- a/drivers/net/dsa/mt7530.c ++++ b/drivers/net/dsa/mt7530.c +@@ -2489,7 +2489,8 @@ mt7530_setup(struct dsa_switch *ds) + if (!phy_node) + continue; + +- if (phy_node->parent == priv->dev->of_node->parent) { ++ if (phy_node->parent == priv->dev->of_node->parent || ++ phy_node->parent->parent == priv->dev->of_node) { + ret = of_get_phy_mode(mac_np, &interface); + if (ret && ret != -ENODEV) { + of_node_put(mac_np); diff --git a/target/linux/generic/pending-6.1/750-net-phy-airoha-Add-the-Airoha-EN8811H-PHY-driver.patch b/target/linux/generic/backport-6.1/798-v6.10-net-phy-air_en8811h-Add-the-Airoha-EN8811H-PHY-drive.patch similarity index 61% rename from target/linux/generic/pending-6.1/750-net-phy-airoha-Add-the-Airoha-EN8811H-PHY-driver.patch rename to target/linux/generic/backport-6.1/798-v6.10-net-phy-air_en8811h-Add-the-Airoha-EN8811H-PHY-drive.patch index 1dfa1366eb..7f963b3cf6 100644 --- a/target/linux/generic/pending-6.1/750-net-phy-airoha-Add-the-Airoha-EN8811H-PHY-driver.patch +++ b/target/linux/generic/backport-6.1/798-v6.10-net-phy-air_en8811h-Add-the-Airoha-EN8811H-PHY-drive.patch @@ -1,88 +1,33 @@ -From patchwork Tue Feb 6 19:47:51 2024 -Content-Type: text/plain; charset="utf-8" -MIME-Version: 1.0 -Content-Transfer-Encoding: 7bit -X-Patchwork-Submitter: Eric Woudstra -X-Patchwork-Id: 13547762 -X-Patchwork-Delegate: kuba@kernel.org +From 71e79430117d56c409c5ea485a263bc0d8083390 Mon Sep 17 00:00:00 2001 From: Eric Woudstra -To: "David S. Miller" , - Eric Dumazet , - Jakub Kicinski , - Paolo Abeni , - Rob Herring , - Krzysztof Kozlowski , - Conor Dooley , - Andrew Lunn , - Heiner Kallweit , - Russell King , - Matthias Brugger , - AngeloGioacchino Del Regno , - "Frank Wunderlich" , - Daniel Golle , - Lucien Jheng , - Zhi-Jun You -Cc: netdev@vger.kernel.org, - devicetree@vger.kernel.org, - Eric Woudstra -Subject: [PATCH net-next 2/2] net: phy: air_en8811h: Add the Airoha EN8811H - PHY driver -Date: Tue, 6 Feb 2024 20:47:51 +0100 -Message-ID: <20240206194751.1901802-3-ericwouds@gmail.com> -X-Mailer: git-send-email 2.42.1 -In-Reply-To: <20240206194751.1901802-1-ericwouds@gmail.com> -References: <20240206194751.1901802-1-ericwouds@gmail.com> -Precedence: bulk -X-Mailing-List: netdev@vger.kernel.org -List-Id: -List-Subscribe: -List-Unsubscribe: -MIME-Version: 1.0 -X-Patchwork-Delegate: kuba@kernel.org +Date: Tue, 26 Mar 2024 17:23:05 +0100 +Subject: [PATCH] net: phy: air_en8811h: Add the Airoha EN8811H PHY driver -* Source originated from airoha's en8811h v1.2.1 driver - * Moved air_en8811h.h to air_en8811h.c - * Removed air_pbus_reg_write() as it writes to another device on mdio-bus - * Load firmware from /lib/firmware/airoha/ instead of /lib/firmware/ - * Added .get_rate_matching() - * Use generic phy_read/write() and phy_read/write_mmd() - * Edited .get_features() to use generic C45 functions - * Edited .config_aneg() and .read_status() to use a mix of generic C22/C45 - * Use led handling functions from mediatek-ge-soc.c - * Simplified led handling by storing led rules - * Cleanup macro definitions - * Cleanup code to pass checkpatch.pl - * General code cleanup +Add the driver for the Airoha EN8811H 2.5 Gigabit PHY. The phy supports +100/1000/2500 Mbps with auto negotiation only. -Changes from original RFC patch: +The driver uses two firmware files, for which updated versions are added to +linux-firmware already. - * Use the correct order in Kconfig and Makefile - * Change some register naming to correspond with datasheet - * Use phy_driver .read_page() and .write_page() - * Use module_phy_driver() - * Use get_unaligned_le16() instead of macro - * In .config_aneg() and .read_status() use genphy_xxx() C22 - * Use another vendor register to read real speed - * Load firmware only once and store firmware version - * Apply 2.5G LPA work-around (firmware before 24011202) - * Read 2.5G LPA from vendor register (firmware 24011202 and later) - -Changes to be committed: - modified: drivers/net/phy/Kconfig - modified: drivers/net/phy/Makefile - new file: drivers/net/phy/air_en8811h.c +Note: At phy-address + 8 there is another device on the mdio bus, that +belongs to the EN881H. While the original driver writes to it, Airoha +has confirmed this is not needed. Therefore, communication with this +device is not included in this driver. Signed-off-by: Eric Woudstra +Reviewed-by: Andrew Lunn +Link: https://lore.kernel.org/r/20240326162305.303598-3-ericwouds@gmail.com +Signed-off-by: Jakub Kicinski --- drivers/net/phy/Kconfig | 5 + drivers/net/phy/Makefile | 1 + - drivers/net/phy/air_en8811h.c | 1006 +++++++++++++++++++++++++++++++++ - 3 files changed, 1012 insertions(+) + drivers/net/phy/air_en8811h.c | 1086 +++++++++++++++++++++++++++++++++ + 3 files changed, 1092 insertions(+) create mode 100644 drivers/net/phy/air_en8811h.c --- a/drivers/net/phy/Kconfig +++ b/drivers/net/phy/Kconfig -@@ -69,6 +69,11 @@ config SFP +@@ -63,6 +63,11 @@ config SFP comment "MII PHY device drivers" @@ -92,7 +37,7 @@ Signed-off-by: Eric Woudstra + Currently supports the Airoha EN8811H PHY. + config AMD_PHY - tristate "AMD and Altima PHYs" + tristate "AMD PHYs" help --- a/drivers/net/phy/Makefile +++ b/drivers/net/phy/Makefile @@ -106,12 +51,10 @@ Signed-off-by: Eric Woudstra obj-$(CONFIG_AX88796B_PHY) += ax88796b.o --- /dev/null +++ b/drivers/net/phy/air_en8811h.c -@@ -0,0 +1,1006 @@ +@@ -0,0 +1,1086 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* -+ * Driver for Airoha Ethernet PHYs -+ * -+ * Currently supporting the EN8811H. ++ * Driver for the Airoha EN8811H 2.5 Gigabit PHY. + * + * Limitations of the EN8811H: + * - Only full duplex supported @@ -125,6 +68,7 @@ Signed-off-by: Eric Woudstra +#include +#include +#include ++#include +#include + +#define EN8811H_PHY_ID 0x03a2a411 @@ -135,12 +79,8 @@ Signed-off-by: Eric Woudstra +#define AIR_FW_ADDR_DM 0x00000000 +#define AIR_FW_ADDR_DSP 0x00100000 + -+/* u32 (DWORD) component macros */ -+#define LOWORD(d) ((u16)(u32)(d)) -+#define HIWORD(d) ((u16)(((u32)(d)) >> 16)) -+ +/* MII Registers */ -+#define AIR_AUX_CTRL_STATUS 0x1d ++#define AIR_AUX_CTRL_STATUS 0x1d +#define AIR_AUX_CTRL_STATUS_SPEED_MASK GENMASK(4, 2) +#define AIR_AUX_CTRL_STATUS_SPEED_100 0x4 +#define AIR_AUX_CTRL_STATUS_SPEED_1000 0x8 @@ -151,32 +91,32 @@ Signed-off-by: Eric Woudstra +#define AIR_PHY_PAGE_EXTENDED_4 0x0004 + +/* MII Registers Page 4*/ -+#define AIR_PBUS_MODE 0x10 -+#define AIR_PBUS_MODE_ADDR_FIXED 0x0000 -+#define AIR_PBUS_MODE_ADDR_INCR BIT(15) -+#define AIR_PBUS_WR_ADDR_HIGH 0x11 -+#define AIR_PBUS_WR_ADDR_LOW 0x12 -+#define AIR_PBUS_WR_DATA_HIGH 0x13 -+#define AIR_PBUS_WR_DATA_LOW 0x14 -+#define AIR_PBUS_RD_ADDR_HIGH 0x15 -+#define AIR_PBUS_RD_ADDR_LOW 0x16 -+#define AIR_PBUS_RD_DATA_HIGH 0x17 -+#define AIR_PBUS_RD_DATA_LOW 0x18 ++#define AIR_BPBUS_MODE 0x10 ++#define AIR_BPBUS_MODE_ADDR_FIXED 0x0000 ++#define AIR_BPBUS_MODE_ADDR_INCR BIT(15) ++#define AIR_BPBUS_WR_ADDR_HIGH 0x11 ++#define AIR_BPBUS_WR_ADDR_LOW 0x12 ++#define AIR_BPBUS_WR_DATA_HIGH 0x13 ++#define AIR_BPBUS_WR_DATA_LOW 0x14 ++#define AIR_BPBUS_RD_ADDR_HIGH 0x15 ++#define AIR_BPBUS_RD_ADDR_LOW 0x16 ++#define AIR_BPBUS_RD_DATA_HIGH 0x17 ++#define AIR_BPBUS_RD_DATA_LOW 0x18 + +/* Registers on MDIO_MMD_VEND1 */ +#define EN8811H_PHY_FW_STATUS 0x8009 +#define EN8811H_PHY_READY 0x02 + -+#define AIR_PHY_HOST_CMD_1 0x800c -+#define AIR_PHY_HOST_CMD_1_MODE1 0x0 -+#define AIR_PHY_HOST_CMD_2 0x800d -+#define AIR_PHY_HOST_CMD_2_MODE1 0x0 -+#define AIR_PHY_HOST_CMD_3 0x800e -+#define AIR_PHY_HOST_CMD_3_MODE1 0x1101 -+#define AIR_PHY_HOST_CMD_3_DOCMD 0x1100 -+#define AIR_PHY_HOST_CMD_4 0x800f -+#define AIR_PHY_HOST_CMD_4_MODE1 0x0002 -+#define AIR_PHY_HOST_CMD_4_INTCLR 0x00e4 ++#define AIR_PHY_MCU_CMD_1 0x800c ++#define AIR_PHY_MCU_CMD_1_MODE1 0x0 ++#define AIR_PHY_MCU_CMD_2 0x800d ++#define AIR_PHY_MCU_CMD_2_MODE1 0x0 ++#define AIR_PHY_MCU_CMD_3 0x800e ++#define AIR_PHY_MCU_CMD_3_MODE1 0x1101 ++#define AIR_PHY_MCU_CMD_3_DOCMD 0x1100 ++#define AIR_PHY_MCU_CMD_4 0x800f ++#define AIR_PHY_MCU_CMD_4_MODE1 0x0002 ++#define AIR_PHY_MCU_CMD_4_INTCLR 0x00e4 + +/* Registers on MDIO_MMD_VEND2 */ +#define AIR_PHY_LED_BCR 0x021 @@ -235,28 +175,21 @@ Signed-off-by: Eric Woudstra +#define EN8811H_FW_CTRL_2 0x800000 +#define EN8811H_FW_CTRL_2_LOADING BIT(11) + ++/* Led definitions */ +#define EN8811H_LED_COUNT 3 + -+/* GPIO5 <-> BASE_T_LED0 -+ * GPIO4 <-> BASE_T_LED1 -+ * GPIO3 <-> BASE_T_LED2 -+ * -+ * Default setup suitable for 2 leds connected: -+ * 100M link up triggers led0, only led0 blinking on traffic -+ * 1000M link up triggers led1, only led1 blinking on traffic -+ * 2500M link up triggers led0 and led1, both blinking on traffic -+ * Also suitable for 1 led connected: -+ * any link up triggers led2 ++/* Default LED setup: ++ * GPIO5 <-> LED0 On: Link detected, blink Rx/Tx ++ * GPIO4 <-> LED1 On: Link detected at 2500 or 1000 Mbps ++ * GPIO3 <-> LED2 On: Link detected at 2500 or 100 Mbps + */ -+#define AIR_DEFAULT_TRIGGER_LED0 (BIT(TRIGGER_NETDEV_LINK_2500) | \ -+ BIT(TRIGGER_NETDEV_LINK_100) | \ ++#define AIR_DEFAULT_TRIGGER_LED0 (BIT(TRIGGER_NETDEV_LINK) | \ + BIT(TRIGGER_NETDEV_RX) | \ + BIT(TRIGGER_NETDEV_TX)) +#define AIR_DEFAULT_TRIGGER_LED1 (BIT(TRIGGER_NETDEV_LINK_2500) | \ -+ BIT(TRIGGER_NETDEV_LINK_1000) | \ -+ BIT(TRIGGER_NETDEV_RX) | \ -+ BIT(TRIGGER_NETDEV_TX)) -+#define AIR_DEFAULT_TRIGGER_LED2 BIT(TRIGGER_NETDEV_LINK) ++ BIT(TRIGGER_NETDEV_LINK_1000)) ++#define AIR_DEFAULT_TRIGGER_LED2 (BIT(TRIGGER_NETDEV_LINK_2500) | \ ++ BIT(TRIGGER_NETDEV_LINK_100)) + +struct led { + unsigned long rules; @@ -265,6 +198,7 @@ Signed-off-by: Eric Woudstra + +struct en8811h_priv { + u32 firmware_version; ++ bool mcu_needs_restart; + struct led led[EN8811H_LED_COUNT]; +}; + @@ -274,12 +208,12 @@ Signed-off-by: Eric Woudstra +}; + +enum { -+ AIR_PHY_LED_DUR_BLINK_32M, -+ AIR_PHY_LED_DUR_BLINK_64M, -+ AIR_PHY_LED_DUR_BLINK_128M, -+ AIR_PHY_LED_DUR_BLINK_256M, -+ AIR_PHY_LED_DUR_BLINK_512M, -+ AIR_PHY_LED_DUR_BLINK_1024M, ++ AIR_PHY_LED_DUR_BLINK_32MS, ++ AIR_PHY_LED_DUR_BLINK_64MS, ++ AIR_PHY_LED_DUR_BLINK_128MS, ++ AIR_PHY_LED_DUR_BLINK_256MS, ++ AIR_PHY_LED_DUR_BLINK_512MS, ++ AIR_PHY_LED_DUR_BLINK_1024MS, +}; + +enum { @@ -298,16 +232,16 @@ Signed-off-by: Eric Woudstra +}; + +#define AIR_PHY_LED_DUR_UNIT 1024 -+#define AIR_PHY_LED_DUR (AIR_PHY_LED_DUR_UNIT << AIR_PHY_LED_DUR_BLINK_64M) ++#define AIR_PHY_LED_DUR (AIR_PHY_LED_DUR_UNIT << AIR_PHY_LED_DUR_BLINK_64MS) + -+static const unsigned long en8811h_led_trig = (BIT(TRIGGER_NETDEV_FULL_DUPLEX) | -+ BIT(TRIGGER_NETDEV_LINK) | -+ BIT(TRIGGER_NETDEV_LINK_10) | -+ BIT(TRIGGER_NETDEV_LINK_100) | -+ BIT(TRIGGER_NETDEV_LINK_1000) | -+ BIT(TRIGGER_NETDEV_LINK_2500) | -+ BIT(TRIGGER_NETDEV_RX) | -+ BIT(TRIGGER_NETDEV_TX)); ++static const unsigned long en8811h_led_trig = BIT(TRIGGER_NETDEV_FULL_DUPLEX) | ++ BIT(TRIGGER_NETDEV_LINK) | ++ BIT(TRIGGER_NETDEV_LINK_10) | ++ BIT(TRIGGER_NETDEV_LINK_100) | ++ BIT(TRIGGER_NETDEV_LINK_1000) | ++ BIT(TRIGGER_NETDEV_LINK_2500) | ++ BIT(TRIGGER_NETDEV_RX) | ++ BIT(TRIGGER_NETDEV_TX); + +static int air_phy_read_page(struct phy_device *phydev) +{ @@ -324,23 +258,27 @@ Signed-off-by: Eric Woudstra +{ + int ret; + -+ ret = __phy_write(phydev, AIR_PBUS_MODE, AIR_PBUS_MODE_ADDR_FIXED); ++ ret = __phy_write(phydev, AIR_BPBUS_MODE, AIR_BPBUS_MODE_ADDR_FIXED); + if (ret < 0) + return ret; + -+ ret = __phy_write(phydev, AIR_PBUS_WR_ADDR_HIGH, HIWORD(pbus_address)); ++ ret = __phy_write(phydev, AIR_BPBUS_WR_ADDR_HIGH, ++ upper_16_bits(pbus_address)); + if (ret < 0) + return ret; + -+ ret = __phy_write(phydev, AIR_PBUS_WR_ADDR_LOW, LOWORD(pbus_address)); ++ ret = __phy_write(phydev, AIR_BPBUS_WR_ADDR_LOW, ++ lower_16_bits(pbus_address)); + if (ret < 0) + return ret; + -+ ret = __phy_write(phydev, AIR_PBUS_WR_DATA_HIGH, HIWORD(pbus_data)); ++ ret = __phy_write(phydev, AIR_BPBUS_WR_DATA_HIGH, ++ upper_16_bits(pbus_data)); + if (ret < 0) + return ret; + -+ ret = __phy_write(phydev, AIR_PBUS_WR_DATA_LOW, LOWORD(pbus_data)); ++ ret = __phy_write(phydev, AIR_BPBUS_WR_DATA_LOW, ++ lower_16_bits(pbus_data)); + if (ret < 0) + return ret; + @@ -350,17 +288,20 @@ Signed-off-by: Eric Woudstra +static int air_buckpbus_reg_write(struct phy_device *phydev, + u32 pbus_address, u32 pbus_data) +{ -+ int ret, saved_page; ++ int saved_page; ++ int ret = 0; + + saved_page = phy_select_page(phydev, AIR_PHY_PAGE_EXTENDED_4); + -+ ret = __air_buckpbus_reg_write(phydev, pbus_address, pbus_data); -+ if (ret < 0) -+ phydev_err(phydev, "%s 0x%08x failed: %d\n", __func__, -+ pbus_address, ret); ++ if (saved_page >= 0) { ++ ret = __air_buckpbus_reg_write(phydev, pbus_address, ++ pbus_data); ++ if (ret < 0) ++ phydev_err(phydev, "%s 0x%08x failed: %d\n", __func__, ++ pbus_address, ret); ++ } + + return phy_restore_page(phydev, saved_page, ret); -+; +} + +static int __air_buckpbus_reg_read(struct phy_device *phydev, @@ -369,41 +310,122 @@ Signed-off-by: Eric Woudstra + int pbus_data_low, pbus_data_high; + int ret; + -+ ret = __phy_write(phydev, AIR_PBUS_MODE, AIR_PBUS_MODE_ADDR_FIXED); ++ ret = __phy_write(phydev, AIR_BPBUS_MODE, AIR_BPBUS_MODE_ADDR_FIXED); + if (ret < 0) + return ret; + -+ ret = __phy_write(phydev, AIR_PBUS_RD_ADDR_HIGH, HIWORD(pbus_address)); ++ ret = __phy_write(phydev, AIR_BPBUS_RD_ADDR_HIGH, ++ upper_16_bits(pbus_address)); + if (ret < 0) + return ret; + -+ ret = __phy_write(phydev, AIR_PBUS_RD_ADDR_LOW, LOWORD(pbus_address)); ++ ret = __phy_write(phydev, AIR_BPBUS_RD_ADDR_LOW, ++ lower_16_bits(pbus_address)); + if (ret < 0) + return ret; + -+ pbus_data_high = __phy_read(phydev, AIR_PBUS_RD_DATA_HIGH); ++ pbus_data_high = __phy_read(phydev, AIR_BPBUS_RD_DATA_HIGH); + if (pbus_data_high < 0) + return ret; + -+ pbus_data_low = __phy_read(phydev, AIR_PBUS_RD_DATA_LOW); ++ pbus_data_low = __phy_read(phydev, AIR_BPBUS_RD_DATA_LOW); + if (pbus_data_low < 0) + return ret; + -+ *pbus_data = (u16)pbus_data_low | ((u32)(u16)pbus_data_high << 16); ++ *pbus_data = pbus_data_low | (pbus_data_high << 16); + return 0; +} + +static int air_buckpbus_reg_read(struct phy_device *phydev, + u32 pbus_address, u32 *pbus_data) +{ -+ int ret, saved_page; ++ int saved_page; ++ int ret = 0; + + saved_page = phy_select_page(phydev, AIR_PHY_PAGE_EXTENDED_4); + -+ ret = __air_buckpbus_reg_read(phydev, pbus_address, pbus_data); ++ if (saved_page >= 0) { ++ ret = __air_buckpbus_reg_read(phydev, pbus_address, pbus_data); ++ if (ret < 0) ++ phydev_err(phydev, "%s 0x%08x failed: %d\n", __func__, ++ pbus_address, ret); ++ } ++ ++ return phy_restore_page(phydev, saved_page, ret); ++} ++ ++static int __air_buckpbus_reg_modify(struct phy_device *phydev, ++ u32 pbus_address, u32 mask, u32 set) ++{ ++ int pbus_data_low, pbus_data_high; ++ u32 pbus_data_old, pbus_data_new; ++ int ret; ++ ++ ret = __phy_write(phydev, AIR_BPBUS_MODE, AIR_BPBUS_MODE_ADDR_FIXED); + if (ret < 0) -+ phydev_err(phydev, "%s 0x%08x failed: %d\n", __func__, -+ pbus_address, ret); ++ return ret; ++ ++ ret = __phy_write(phydev, AIR_BPBUS_RD_ADDR_HIGH, ++ upper_16_bits(pbus_address)); ++ if (ret < 0) ++ return ret; ++ ++ ret = __phy_write(phydev, AIR_BPBUS_RD_ADDR_LOW, ++ lower_16_bits(pbus_address)); ++ if (ret < 0) ++ return ret; ++ ++ pbus_data_high = __phy_read(phydev, AIR_BPBUS_RD_DATA_HIGH); ++ if (pbus_data_high < 0) ++ return ret; ++ ++ pbus_data_low = __phy_read(phydev, AIR_BPBUS_RD_DATA_LOW); ++ if (pbus_data_low < 0) ++ return ret; ++ ++ pbus_data_old = pbus_data_low | (pbus_data_high << 16); ++ pbus_data_new = (pbus_data_old & ~mask) | set; ++ if (pbus_data_new == pbus_data_old) ++ return 0; ++ ++ ret = __phy_write(phydev, AIR_BPBUS_WR_ADDR_HIGH, ++ upper_16_bits(pbus_address)); ++ if (ret < 0) ++ return ret; ++ ++ ret = __phy_write(phydev, AIR_BPBUS_WR_ADDR_LOW, ++ lower_16_bits(pbus_address)); ++ if (ret < 0) ++ return ret; ++ ++ ret = __phy_write(phydev, AIR_BPBUS_WR_DATA_HIGH, ++ upper_16_bits(pbus_data_new)); ++ if (ret < 0) ++ return ret; ++ ++ ret = __phy_write(phydev, AIR_BPBUS_WR_DATA_LOW, ++ lower_16_bits(pbus_data_new)); ++ if (ret < 0) ++ return ret; ++ ++ return 0; ++} ++ ++static int air_buckpbus_reg_modify(struct phy_device *phydev, ++ u32 pbus_address, u32 mask, u32 set) ++{ ++ int saved_page; ++ int ret = 0; ++ ++ saved_page = phy_select_page(phydev, AIR_PHY_PAGE_EXTENDED_4); ++ ++ if (saved_page >= 0) { ++ ret = __air_buckpbus_reg_modify(phydev, pbus_address, mask, ++ set); ++ if (ret < 0) ++ phydev_err(phydev, "%s 0x%08x failed: %d\n", __func__, ++ pbus_address, ret); ++ } + + return phy_restore_page(phydev, saved_page, ret); +} @@ -415,26 +437,28 @@ Signed-off-by: Eric Woudstra + int ret; + u16 val; + -+ ret = __phy_write(phydev, AIR_PBUS_MODE, AIR_PBUS_MODE_ADDR_INCR); ++ ret = __phy_write(phydev, AIR_BPBUS_MODE, AIR_BPBUS_MODE_ADDR_INCR); + if (ret < 0) + return ret; + -+ ret = __phy_write(phydev, AIR_PBUS_WR_ADDR_HIGH, HIWORD(address)); ++ ret = __phy_write(phydev, AIR_BPBUS_WR_ADDR_HIGH, ++ upper_16_bits(address)); + if (ret < 0) + return ret; + -+ ret = __phy_write(phydev, AIR_PBUS_WR_ADDR_LOW, LOWORD(address)); ++ ret = __phy_write(phydev, AIR_BPBUS_WR_ADDR_LOW, ++ lower_16_bits(address)); + if (ret < 0) + return ret; + + for (offset = 0; offset < fw->size; offset += 4) { + val = get_unaligned_le16(&fw->data[offset + 2]); -+ ret = __phy_write(phydev, AIR_PBUS_WR_DATA_HIGH, val); ++ ret = __phy_write(phydev, AIR_BPBUS_WR_DATA_HIGH, val); + if (ret < 0) + return ret; + + val = get_unaligned_le16(&fw->data[offset]); -+ ret = __phy_write(phydev, AIR_PBUS_WR_DATA_LOW, val); ++ ret = __phy_write(phydev, AIR_BPBUS_WR_DATA_LOW, val); + if (ret < 0) + return ret; + } @@ -445,23 +469,43 @@ Signed-off-by: Eric Woudstra +static int air_write_buf(struct phy_device *phydev, u32 address, + const struct firmware *fw) +{ -+ int ret, saved_page; ++ int saved_page; ++ int ret = 0; + + saved_page = phy_select_page(phydev, AIR_PHY_PAGE_EXTENDED_4); + -+ ret = __air_write_buf(phydev, address, fw); -+ if (ret < 0) -+ phydev_err(phydev, "%s 0x%08x failed: %d\n", __func__, -+ address, ret); ++ if (saved_page >= 0) { ++ ret = __air_write_buf(phydev, address, fw); ++ if (ret < 0) ++ phydev_err(phydev, "%s 0x%08x failed: %d\n", __func__, ++ address, ret); ++ } + + return phy_restore_page(phydev, saved_page, ret); +} + ++static int en8811h_wait_mcu_ready(struct phy_device *phydev) ++{ ++ int ret, reg_value; ++ ++ /* Because of mdio-lock, may have to wait for multiple loads */ ++ ret = phy_read_mmd_poll_timeout(phydev, MDIO_MMD_VEND1, ++ EN8811H_PHY_FW_STATUS, reg_value, ++ reg_value == EN8811H_PHY_READY, ++ 20000, 7500000, true); ++ if (ret) { ++ phydev_err(phydev, "MCU not ready: 0x%x\n", reg_value); ++ return -ENODEV; ++ } ++ ++ return 0; ++} ++ +static int en8811h_load_firmware(struct phy_device *phydev) +{ ++ struct en8811h_priv *priv = phydev->priv; + struct device *dev = &phydev->mdio.dev; + const struct firmware *fw1, *fw2; -+ u32 pbus_value; + int ret; + + ret = request_firmware_direct(&fw1, EN8811H_MD32_DM, dev); @@ -477,11 +521,9 @@ Signed-off-by: Eric Woudstra + if (ret < 0) + goto en8811h_load_firmware_out; + -+ ret = air_buckpbus_reg_read(phydev, EN8811H_FW_CTRL_2, &pbus_value); -+ if (ret < 0) -+ goto en8811h_load_firmware_out; -+ pbus_value |= EN8811H_FW_CTRL_2_LOADING; -+ ret = air_buckpbus_reg_write(phydev, EN8811H_FW_CTRL_2, pbus_value); ++ ret = air_buckpbus_reg_modify(phydev, EN8811H_FW_CTRL_2, ++ EN8811H_FW_CTRL_2_LOADING, ++ EN8811H_FW_CTRL_2_LOADING); + if (ret < 0) + goto en8811h_load_firmware_out; + @@ -493,11 +535,8 @@ Signed-off-by: Eric Woudstra + if (ret < 0) + goto en8811h_load_firmware_out; + -+ ret = air_buckpbus_reg_read(phydev, EN8811H_FW_CTRL_2, &pbus_value); -+ if (ret < 0) -+ goto en8811h_load_firmware_out; -+ pbus_value &= ~EN8811H_FW_CTRL_2_LOADING; -+ ret = air_buckpbus_reg_write(phydev, EN8811H_FW_CTRL_2, pbus_value); ++ ret = air_buckpbus_reg_modify(phydev, EN8811H_FW_CTRL_2, ++ EN8811H_FW_CTRL_2_LOADING, 0); + if (ret < 0) + goto en8811h_load_firmware_out; + @@ -506,7 +545,12 @@ Signed-off-by: Eric Woudstra + if (ret < 0) + goto en8811h_load_firmware_out; + -+ ret = 0; ++ ret = en8811h_wait_mcu_ready(phydev); ++ ++ air_buckpbus_reg_read(phydev, EN8811H_FW_VERSION, ++ &priv->firmware_version); ++ phydev_info(phydev, "MD32 firmware version: %08x\n", ++ priv->firmware_version); + +en8811h_load_firmware_out: + release_firmware(fw2); @@ -520,7 +564,7 @@ Signed-off-by: Eric Woudstra + return ret; +} + -+static int en8811h_restart_host(struct phy_device *phydev) ++static int en8811h_restart_mcu(struct phy_device *phydev) +{ + int ret; + @@ -529,8 +573,12 @@ Signed-off-by: Eric Woudstra + if (ret < 0) + return ret; + -+ return air_buckpbus_reg_write(phydev, EN8811H_FW_CTRL_1, ++ ret = air_buckpbus_reg_write(phydev, EN8811H_FW_CTRL_1, + EN8811H_FW_CTRL_1_FINISH); ++ if (ret < 0) ++ return ret; ++ ++ return en8811h_wait_mcu_ready(phydev); +} + +static int air_hw_led_on_set(struct phy_device *phydev, u8 index, bool on) @@ -664,68 +712,46 @@ Signed-off-by: Eric Woudstra + u16 on = 0, blink = 0; + int ret; + -+ priv->led[index].rules = rules; -+ + if (index >= EN8811H_LED_COUNT) + return -EINVAL; + -+ if (rules & (BIT(TRIGGER_NETDEV_LINK_10) | BIT(TRIGGER_NETDEV_LINK))) { -+ on |= AIR_PHY_LED_ON_LINK10; -+ if (rules & BIT(TRIGGER_NETDEV_RX)) -+ blink |= AIR_PHY_LED_BLINK_10RX; -+ if (rules & BIT(TRIGGER_NETDEV_TX)) -+ blink |= AIR_PHY_LED_BLINK_10TX; -+ } -+ -+ if (rules & (BIT(TRIGGER_NETDEV_LINK_100) | BIT(TRIGGER_NETDEV_LINK))) { -+ on |= AIR_PHY_LED_ON_LINK100; -+ if (rules & BIT(TRIGGER_NETDEV_RX)) -+ blink |= AIR_PHY_LED_BLINK_100RX; -+ if (rules & BIT(TRIGGER_NETDEV_TX)) -+ blink |= AIR_PHY_LED_BLINK_100TX; -+ } -+ -+ if (rules & (BIT(TRIGGER_NETDEV_LINK_1000) | BIT(TRIGGER_NETDEV_LINK))) { -+ on |= AIR_PHY_LED_ON_LINK1000; -+ if (rules & BIT(TRIGGER_NETDEV_RX)) -+ blink |= AIR_PHY_LED_BLINK_1000RX; -+ if (rules & BIT(TRIGGER_NETDEV_TX)) -+ blink |= AIR_PHY_LED_BLINK_1000TX; -+ } -+ -+ if (rules & (BIT(TRIGGER_NETDEV_LINK_2500) | BIT(TRIGGER_NETDEV_LINK))) { -+ on |= AIR_PHY_LED_ON_LINK2500; -+ if (rules & BIT(TRIGGER_NETDEV_RX)) -+ blink |= AIR_PHY_LED_BLINK_2500RX; -+ if (rules & BIT(TRIGGER_NETDEV_TX)) -+ blink |= AIR_PHY_LED_BLINK_2500TX; -+ } -+ -+ if (on == 0) { -+ if (rules & BIT(TRIGGER_NETDEV_RX)) { -+ blink |= AIR_PHY_LED_BLINK_10RX | -+ AIR_PHY_LED_BLINK_100RX | -+ AIR_PHY_LED_BLINK_1000RX | -+ AIR_PHY_LED_BLINK_2500RX; -+ } -+ if (rules & BIT(TRIGGER_NETDEV_TX)) { -+ blink |= AIR_PHY_LED_BLINK_10TX | -+ AIR_PHY_LED_BLINK_100TX | -+ AIR_PHY_LED_BLINK_1000TX | -+ AIR_PHY_LED_BLINK_2500TX; -+ } -+ } ++ priv->led[index].rules = rules; + + if (rules & BIT(TRIGGER_NETDEV_FULL_DUPLEX)) + on |= AIR_PHY_LED_ON_FDX; + -+ if (rules & BIT(TRIGGER_NETDEV_HALF_DUPLEX)) -+ on |= AIR_PHY_LED_ON_HDX; ++ if (rules & (BIT(TRIGGER_NETDEV_LINK_10) | BIT(TRIGGER_NETDEV_LINK))) ++ on |= AIR_PHY_LED_ON_LINK10; ++ ++ if (rules & (BIT(TRIGGER_NETDEV_LINK_100) | BIT(TRIGGER_NETDEV_LINK))) ++ on |= AIR_PHY_LED_ON_LINK100; ++ ++ if (rules & (BIT(TRIGGER_NETDEV_LINK_1000) | BIT(TRIGGER_NETDEV_LINK))) ++ on |= AIR_PHY_LED_ON_LINK1000; ++ ++ if (rules & (BIT(TRIGGER_NETDEV_LINK_2500) | BIT(TRIGGER_NETDEV_LINK))) ++ on |= AIR_PHY_LED_ON_LINK2500; ++ ++ if (rules & BIT(TRIGGER_NETDEV_RX)) { ++ blink |= AIR_PHY_LED_BLINK_10RX | ++ AIR_PHY_LED_BLINK_100RX | ++ AIR_PHY_LED_BLINK_1000RX | ++ AIR_PHY_LED_BLINK_2500RX; ++ } ++ ++ if (rules & BIT(TRIGGER_NETDEV_TX)) { ++ blink |= AIR_PHY_LED_BLINK_10TX | ++ AIR_PHY_LED_BLINK_100TX | ++ AIR_PHY_LED_BLINK_1000TX | ++ AIR_PHY_LED_BLINK_2500TX; ++ } + + if (blink || on) { + /* switch hw-control on, so led-on and led-blink are off */ -+ clear_bit(AIR_PHY_LED_STATE_FORCE_ON, &priv->led[index].state); -+ clear_bit(AIR_PHY_LED_STATE_FORCE_BLINK, &priv->led[index].state); ++ clear_bit(AIR_PHY_LED_STATE_FORCE_ON, ++ &priv->led[index].state); ++ clear_bit(AIR_PHY_LED_STATE_FORCE_BLINK, ++ &priv->led[index].state); + } else { + priv->led[index].rules = 0; + } @@ -742,28 +768,26 @@ Signed-off-by: Eric Woudstra + +static int air_led_init(struct phy_device *phydev, u8 index, u8 state, u8 pol) +{ -+ int cl45_data; ++ int val = 0; + int err; + + if (index >= EN8811H_LED_COUNT) + return -EINVAL; + -+ cl45_data = phy_read_mmd(phydev, MDIO_MMD_VEND2, AIR_PHY_LED_ON(index)); -+ if (cl45_data < 0) -+ return cl45_data; -+ + if (state == AIR_LED_ENABLE) -+ cl45_data |= AIR_PHY_LED_ON_ENABLE; ++ val |= AIR_PHY_LED_ON_ENABLE; + else -+ cl45_data &= ~AIR_PHY_LED_ON_ENABLE; ++ val &= ~AIR_PHY_LED_ON_ENABLE; + + if (pol == AIR_ACTIVE_HIGH) -+ cl45_data |= AIR_PHY_LED_ON_POLARITY; ++ val |= AIR_PHY_LED_ON_POLARITY; + else -+ cl45_data &= ~AIR_PHY_LED_ON_POLARITY; ++ val &= ~AIR_PHY_LED_ON_POLARITY; ++ ++ err = phy_modify_mmd(phydev, MDIO_MMD_VEND2, AIR_PHY_LED_ON(index), ++ AIR_PHY_LED_ON_ENABLE | ++ AIR_PHY_LED_ON_POLARITY, val); + -+ err = phy_write_mmd(phydev, MDIO_MMD_VEND2, AIR_PHY_LED_ON(index), -+ cl45_data); + if (err < 0) + return err; + @@ -773,42 +797,40 @@ Signed-off-by: Eric Woudstra +static int air_leds_init(struct phy_device *phydev, int num, int dur, int mode) +{ + struct en8811h_priv *priv = phydev->priv; -+ int cl45_data = dur; + int ret, i; + + ret = phy_write_mmd(phydev, MDIO_MMD_VEND2, AIR_PHY_LED_DUR_BLINK, -+ cl45_data); ++ dur); + if (ret < 0) + return ret; + -+ cl45_data >>= 1; + ret = phy_write_mmd(phydev, MDIO_MMD_VEND2, AIR_PHY_LED_DUR_ON, -+ cl45_data); ++ dur >> 1); + if (ret < 0) + return ret; + -+ cl45_data = phy_read_mmd(phydev, MDIO_MMD_VEND2, AIR_PHY_LED_BCR); -+ if (cl45_data < 0) -+ return cl45_data; -+ + switch (mode) { + case AIR_LED_MODE_DISABLE: -+ cl45_data &= ~AIR_PHY_LED_BCR_EXT_CTRL; -+ cl45_data &= ~AIR_PHY_LED_BCR_MODE_MASK; ++ ret = phy_modify_mmd(phydev, MDIO_MMD_VEND2, AIR_PHY_LED_BCR, ++ AIR_PHY_LED_BCR_EXT_CTRL | ++ AIR_PHY_LED_BCR_MODE_MASK, 0); ++ if (ret < 0) ++ return ret; + break; + case AIR_LED_MODE_USER_DEFINE: -+ cl45_data |= AIR_PHY_LED_BCR_EXT_CTRL; -+ cl45_data |= AIR_PHY_LED_BCR_CLK_EN; ++ ret = phy_modify_mmd(phydev, MDIO_MMD_VEND2, AIR_PHY_LED_BCR, ++ AIR_PHY_LED_BCR_EXT_CTRL | ++ AIR_PHY_LED_BCR_CLK_EN, ++ AIR_PHY_LED_BCR_EXT_CTRL | ++ AIR_PHY_LED_BCR_CLK_EN); ++ if (ret < 0) ++ return ret; + break; + default: + phydev_err(phydev, "LED mode %d is not supported\n", mode); + return -EINVAL; + } + -+ ret = phy_write_mmd(phydev, MDIO_MMD_VEND2, AIR_PHY_LED_BCR, cl45_data); -+ if (ret < 0) -+ return ret; -+ + for (i = 0; i < num; ++i) { + ret = air_led_init(phydev, i, AIR_LED_ENABLE, AIR_ACTIVE_HIGH); + if (ret < 0) { @@ -837,21 +859,42 @@ Signed-off-by: Eric Woudstra +static int en8811h_probe(struct phy_device *phydev) +{ + struct en8811h_priv *priv; ++ int ret; + + priv = devm_kzalloc(&phydev->mdio.dev, sizeof(struct en8811h_priv), + GFP_KERNEL); + if (!priv) + return -ENOMEM; ++ phydev->priv = priv; ++ ++ ret = en8811h_load_firmware(phydev); ++ if (ret < 0) ++ return ret; ++ ++ /* mcu has just restarted after firmware load */ ++ priv->mcu_needs_restart = false; + + priv->led[0].rules = AIR_DEFAULT_TRIGGER_LED0; + priv->led[1].rules = AIR_DEFAULT_TRIGGER_LED1; + priv->led[2].rules = AIR_DEFAULT_TRIGGER_LED2; + -+ phydev->priv = priv; -+ + /* MDIO_DEVS1/2 empty, so set mmds_present bits here */ + phydev->c45_ids.mmds_present |= MDIO_DEVS_PMAPMD | MDIO_DEVS_AN; + ++ ret = air_leds_init(phydev, EN8811H_LED_COUNT, AIR_PHY_LED_DUR, ++ AIR_LED_MODE_DISABLE); ++ if (ret < 0) { ++ phydev_err(phydev, "Failed to disable leds: %d\n", ret); ++ return ret; ++ } ++ ++ /* Configure led gpio pins as output */ ++ ret = air_buckpbus_reg_modify(phydev, EN8811H_GPIO_OUTPUT, ++ EN8811H_GPIO_OUTPUT_345, ++ EN8811H_GPIO_OUTPUT_345); ++ if (ret < 0) ++ return ret; ++ + return 0; +} + @@ -859,58 +902,41 @@ Signed-off-by: Eric Woudstra +{ + struct en8811h_priv *priv = phydev->priv; + struct device *dev = &phydev->mdio.dev; -+ int ret, pollret, reg_value; + u32 pbus_value; ++ int ret; + -+ if (!priv->firmware_version) -+ ret = en8811h_load_firmware(phydev); -+ else -+ ret = en8811h_restart_host(phydev); -+ if (ret < 0) -+ return ret; -+ -+ /* Because of mdio-lock, may have to wait for multiple loads */ -+ pollret = phy_read_mmd_poll_timeout(phydev, MDIO_MMD_VEND1, -+ EN8811H_PHY_FW_STATUS, reg_value, -+ reg_value == EN8811H_PHY_READY, -+ 20000, 7500000, true); -+ -+ ret = air_buckpbus_reg_read(phydev, EN8811H_FW_VERSION, &pbus_value); -+ if (ret < 0) -+ return ret; -+ -+ if (pollret || !pbus_value) { -+ phydev_err(phydev, "Firmware not ready: 0x%x\n", reg_value); -+ return -ENODEV; ++ /* If restart happened in .probe(), no need to restart now */ ++ if (priv->mcu_needs_restart) { ++ ret = en8811h_restart_mcu(phydev); ++ if (ret < 0) ++ return ret; ++ } else { ++ /* Next calls to .config_init() mcu needs to restart */ ++ priv->mcu_needs_restart = true; + } + -+ if (!priv->firmware_version) { -+ phydev_info(phydev, "MD32 firmware version: %08x\n", pbus_value); -+ priv->firmware_version = pbus_value; -+ } -+ -+ /* Select mode 1, the only mode supported */ -+ ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, AIR_PHY_HOST_CMD_1, -+ AIR_PHY_HOST_CMD_1_MODE1); ++ /* Select mode 1, the only mode supported. ++ * Configures the SerDes for 2500Base-X with rate adaptation ++ */ ++ ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, AIR_PHY_MCU_CMD_1, ++ AIR_PHY_MCU_CMD_1_MODE1); + if (ret < 0) + return ret; -+ ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, AIR_PHY_HOST_CMD_2, -+ AIR_PHY_HOST_CMD_2_MODE1); ++ ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, AIR_PHY_MCU_CMD_2, ++ AIR_PHY_MCU_CMD_2_MODE1); + if (ret < 0) + return ret; -+ ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, AIR_PHY_HOST_CMD_3, -+ AIR_PHY_HOST_CMD_3_MODE1); ++ ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, AIR_PHY_MCU_CMD_3, ++ AIR_PHY_MCU_CMD_3_MODE1); + if (ret < 0) + return ret; -+ ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, AIR_PHY_HOST_CMD_4, -+ AIR_PHY_HOST_CMD_4_MODE1); ++ ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, AIR_PHY_MCU_CMD_4, ++ AIR_PHY_MCU_CMD_4_MODE1); + if (ret < 0) + return ret; + + /* Serdes polarity */ -+ ret = air_buckpbus_reg_read(phydev, EN8811H_POLARITY, &pbus_value); -+ if (ret < 0) -+ return ret; ++ pbus_value = 0; + if (device_property_read_bool(dev, "airoha,pnswap-rx")) + pbus_value |= EN8811H_POLARITY_RX_REVERSE; + else @@ -919,7 +945,9 @@ Signed-off-by: Eric Woudstra + pbus_value &= ~EN8811H_POLARITY_TX_NORMAL; + else + pbus_value |= EN8811H_POLARITY_TX_NORMAL; -+ ret = air_buckpbus_reg_write(phydev, EN8811H_POLARITY, pbus_value); ++ ret = air_buckpbus_reg_modify(phydev, EN8811H_POLARITY, ++ EN8811H_POLARITY_RX_REVERSE | ++ EN8811H_POLARITY_TX_NORMAL, pbus_value); + if (ret < 0) + return ret; + @@ -930,14 +958,6 @@ Signed-off-by: Eric Woudstra + return ret; + } + -+ ret = air_buckpbus_reg_read(phydev, EN8811H_GPIO_OUTPUT, &pbus_value); -+ if (ret < 0) -+ return ret; -+ pbus_value |= EN8811H_GPIO_OUTPUT_345; -+ ret = air_buckpbus_reg_write(phydev, EN8811H_GPIO_OUTPUT, pbus_value); -+ if (ret < 0) -+ return ret; -+ + return 0; +} + @@ -959,17 +979,21 @@ Signed-off-by: Eric Woudstra +static int en8811h_config_aneg(struct phy_device *phydev) +{ + bool changed = false; -+ int err, val; ++ int ret; ++ u32 adv; + -+ val = 0; -+ if (linkmode_test_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, -+ phydev->advertising)) -+ val |= MDIO_AN_10GBT_CTRL_ADV2_5G; -+ err = phy_modify_mmd_changed(phydev, MDIO_MMD_AN, MDIO_AN_10GBT_CTRL, -+ MDIO_AN_10GBT_CTRL_ADV2_5G, val); -+ if (err < 0) -+ return err; -+ if (err > 0) ++ if (phydev->autoneg == AUTONEG_DISABLE) { ++ phydev_warn(phydev, "Disabling autoneg is not supported\n"); ++ return -EINVAL; ++ } ++ ++ adv = linkmode_adv_to_mii_10gbt_adv_t(phydev->advertising); ++ ++ ret = phy_modify_mmd_changed(phydev, MDIO_MMD_AN, MDIO_AN_10GBT_CTRL, ++ MDIO_AN_10GBT_CTRL_ADV2_5G, adv); ++ if (ret < 0) ++ return ret; ++ if (ret > 0) + changed = true; + + return __genphy_config_aneg(phydev, changed); @@ -991,6 +1015,7 @@ Signed-off-by: Eric Woudstra + phydev->duplex = DUPLEX_UNKNOWN; + phydev->pause = 0; + phydev->asym_pause = 0; ++ phydev->rate_matching = RATE_MATCH_PAUSE; + + ret = genphy_read_master_slave(phydev); + if (ret < 0) @@ -1008,7 +1033,7 @@ Signed-off-by: Eric Woudstra + phydev->lp_advertising, + pbus_value & EN8811H_2P5G_LPA_2P5G); + -+ if (phydev->autoneg == AUTONEG_ENABLE && phydev->autoneg_complete) ++ if (phydev->autoneg_complete) + phy_resolve_aneg_pause(phydev); + + if (!phydev->link) @@ -1030,8 +1055,7 @@ Signed-off-by: Eric Woudstra + break; + } + -+ /* BUG in PHY firmware: MDIO_AN_10GBT_STAT_LP2_5G does not get set. -+ * Firmware before version 24011202 has no vendor register 2P5G_LPA. ++ /* Firmware before version 24011202 has no vendor register 2P5G_LPA. + * Assume link partner advertised it if connected at 2500Mbps. + */ + if (priv->firmware_version < 0x24011202) { @@ -1050,13 +1074,13 @@ Signed-off-by: Eric Woudstra +{ + int ret; + -+ ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, AIR_PHY_HOST_CMD_3, -+ AIR_PHY_HOST_CMD_3_DOCMD); ++ ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, AIR_PHY_MCU_CMD_3, ++ AIR_PHY_MCU_CMD_3_DOCMD); + if (ret < 0) + return ret; + -+ ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, AIR_PHY_HOST_CMD_4, -+ AIR_PHY_HOST_CMD_4_INTCLR); ++ ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, AIR_PHY_MCU_CMD_4, ++ AIR_PHY_MCU_CMD_4_INTCLR); + if (ret < 0) + return ret; + @@ -1105,6 +1129,7 @@ Signed-off-by: Eric Woudstra + { PHY_ID_MATCH_MODEL(EN8811H_PHY_ID) }, + { } +}; ++ +MODULE_DEVICE_TABLE(mdio, en8811h_tbl); +MODULE_FIRMWARE(EN8811H_MD32_DM); +MODULE_FIRMWARE(EN8811H_MD32_DSP); diff --git a/target/linux/generic/backport-6.1/799-v6.10-net-phy-air_en8811h-fix-some-error-codes.patch b/target/linux/generic/backport-6.1/799-v6.10-net-phy-air_en8811h-fix-some-error-codes.patch new file mode 100644 index 0000000000..1bd0eefe77 --- /dev/null +++ b/target/linux/generic/backport-6.1/799-v6.10-net-phy-air_en8811h-fix-some-error-codes.patch @@ -0,0 +1,47 @@ +From 87c33315af380ca12a2e59ac94edad4fe0481b4c Mon Sep 17 00:00:00 2001 +From: Dan Carpenter +Date: Fri, 5 Apr 2024 13:08:59 +0300 +Subject: [PATCH] net: phy: air_en8811h: fix some error codes + +These error paths accidentally return "ret" which is zero/success +instead of the correct error code. + +Fixes: 71e79430117d ("net: phy: air_en8811h: Add the Airoha EN8811H PHY driver") +Signed-off-by: Dan Carpenter +Reviewed-by: Simon Horman +Link: https://lore.kernel.org/r/7ef2e230-dfb7-4a77-8973-9e5be1a99fc2@moroto.mountain +Signed-off-by: Jakub Kicinski +--- + drivers/net/phy/air_en8811h.c | 8 ++++---- + 1 file changed, 4 insertions(+), 4 deletions(-) + +--- a/drivers/net/phy/air_en8811h.c ++++ b/drivers/net/phy/air_en8811h.c +@@ -272,11 +272,11 @@ static int __air_buckpbus_reg_read(struc + + pbus_data_high = __phy_read(phydev, AIR_BPBUS_RD_DATA_HIGH); + if (pbus_data_high < 0) +- return ret; ++ return pbus_data_high; + + pbus_data_low = __phy_read(phydev, AIR_BPBUS_RD_DATA_LOW); + if (pbus_data_low < 0) +- return ret; ++ return pbus_data_low; + + *pbus_data = pbus_data_low | (pbus_data_high << 16); + return 0; +@@ -323,11 +323,11 @@ static int __air_buckpbus_reg_modify(str + + pbus_data_high = __phy_read(phydev, AIR_BPBUS_RD_DATA_HIGH); + if (pbus_data_high < 0) +- return ret; ++ return pbus_data_high; + + pbus_data_low = __phy_read(phydev, AIR_BPBUS_RD_DATA_LOW); + if (pbus_data_low < 0) +- return ret; ++ return pbus_data_low; + + pbus_data_old = pbus_data_low | (pbus_data_high << 16); + pbus_data_new = (pbus_data_old & ~mask) | set; diff --git a/target/linux/generic/backport-6.1/832-v6.7-net-phy-amd-Support-the-Altima-AMI101L.patch b/target/linux/generic/backport-6.1/832-v6.7-net-phy-amd-Support-the-Altima-AMI101L.patch index 2969462838..e8e73c1e5f 100644 --- a/target/linux/generic/backport-6.1/832-v6.7-net-phy-amd-Support-the-Altima-AMI101L.patch +++ b/target/linux/generic/backport-6.1/832-v6.7-net-phy-amd-Support-the-Altima-AMI101L.patch @@ -16,8 +16,8 @@ Signed-off-by: Linus Walleij --- a/drivers/net/phy/Kconfig +++ b/drivers/net/phy/Kconfig -@@ -72,9 +72,9 @@ config SFP - comment "MII PHY device drivers" +@@ -77,9 +77,9 @@ config AIR_EN8811H_PHY + Currently supports the Airoha EN8811H PHY. config AMD_PHY - tristate "AMD PHYs" diff --git a/target/linux/generic/backport-6.6/301-v6.9-kernel.h-removed-REPEAT_BYTE-from-kernel.h.patch b/target/linux/generic/backport-6.6/301-v6.9-kernel.h-removed-REPEAT_BYTE-from-kernel.h.patch new file mode 100644 index 0000000000..adc924c09a --- /dev/null +++ b/target/linux/generic/backport-6.6/301-v6.9-kernel.h-removed-REPEAT_BYTE-from-kernel.h.patch @@ -0,0 +1,161 @@ +From 66a5c40f60f5d88ad8d47ba6a4ba05892853fa1f Mon Sep 17 00:00:00 2001 +From: Tanzir Hasan +Date: Tue, 26 Dec 2023 18:00:00 +0000 +Subject: [PATCH] kernel.h: removed REPEAT_BYTE from kernel.h + +This patch creates wordpart.h and includes it in asm/word-at-a-time.h +for all architectures. WORD_AT_A_TIME_CONSTANTS depends on kernel.h +because of REPEAT_BYTE. Moving this to another header and including it +where necessary allows us to not include the bloated kernel.h. Making +this implicit dependency on REPEAT_BYTE explicit allows for later +improvements in the lib/string.c inclusion list. + +Suggested-by: Al Viro +Suggested-by: Andy Shevchenko +Signed-off-by: Tanzir Hasan +Reviewed-by: Andy Shevchenko +Link: https://lore.kernel.org/r/20231226-libstringheader-v6-1-80aa08c7652c@google.com +Signed-off-by: Kees Cook +--- + arch/arm/include/asm/word-at-a-time.h | 3 ++- + arch/arm64/include/asm/word-at-a-time.h | 3 ++- + arch/powerpc/include/asm/word-at-a-time.h | 4 ++-- + arch/riscv/include/asm/word-at-a-time.h | 3 ++- + arch/s390/include/asm/word-at-a-time.h | 3 ++- + arch/sh/include/asm/word-at-a-time.h | 2 ++ + arch/x86/include/asm/word-at-a-time.h | 3 ++- + arch/x86/kvm/mmu/mmu.c | 1 + + fs/namei.c | 2 +- + include/asm-generic/word-at-a-time.h | 3 ++- + include/linux/kernel.h | 8 -------- + include/linux/wordpart.h | 13 +++++++++++++ + 12 files changed, 31 insertions(+), 17 deletions(-) + create mode 100644 include/linux/wordpart.h + +--- a/arch/arm/include/asm/word-at-a-time.h ++++ b/arch/arm/include/asm/word-at-a-time.h +@@ -8,7 +8,8 @@ + * Little-endian word-at-a-time zero byte handling. + * Heavily based on the x86 algorithm. + */ +-#include ++#include ++#include + + struct word_at_a_time { + const unsigned long one_bits, high_bits; +--- a/arch/arm64/include/asm/word-at-a-time.h ++++ b/arch/arm64/include/asm/word-at-a-time.h +@@ -9,7 +9,8 @@ + + #ifndef __AARCH64EB__ + +-#include ++#include ++#include + + struct word_at_a_time { + const unsigned long one_bits, high_bits; +--- a/arch/powerpc/include/asm/word-at-a-time.h ++++ b/arch/powerpc/include/asm/word-at-a-time.h +@@ -4,8 +4,8 @@ + /* + * Word-at-a-time interfaces for PowerPC. + */ +- +-#include ++#include ++#include + #include + #include + +--- a/arch/sh/include/asm/word-at-a-time.h ++++ b/arch/sh/include/asm/word-at-a-time.h +@@ -5,6 +5,8 @@ + #ifdef CONFIG_CPU_BIG_ENDIAN + # include + #else ++#include ++#include + /* + * Little-endian version cribbed from x86. + */ +--- a/arch/x86/include/asm/word-at-a-time.h ++++ b/arch/x86/include/asm/word-at-a-time.h +@@ -2,7 +2,8 @@ + #ifndef _ASM_WORD_AT_A_TIME_H + #define _ASM_WORD_AT_A_TIME_H + +-#include ++#include ++#include + + /* + * This is largely generic for little-endian machines, but the +--- a/arch/x86/kvm/mmu/mmu.c ++++ b/arch/x86/kvm/mmu/mmu.c +@@ -47,6 +47,7 @@ + #include + #include + #include ++#include + + #include + #include +--- a/fs/namei.c ++++ b/fs/namei.c +@@ -17,8 +17,8 @@ + + #include + #include +-#include + #include ++#include + #include + #include + #include +--- a/include/asm-generic/word-at-a-time.h ++++ b/include/asm-generic/word-at-a-time.h +@@ -2,7 +2,8 @@ + #ifndef _ASM_WORD_AT_A_TIME_H + #define _ASM_WORD_AT_A_TIME_H + +-#include ++#include ++#include + #include + + #ifdef __BIG_ENDIAN +--- a/include/linux/kernel.h ++++ b/include/linux/kernel.h +@@ -38,14 +38,6 @@ + + #define STACK_MAGIC 0xdeadbeef + +-/** +- * REPEAT_BYTE - repeat the value @x multiple times as an unsigned long value +- * @x: value to repeat +- * +- * NOTE: @x is not checked for > 0xff; larger values produce odd results. +- */ +-#define REPEAT_BYTE(x) ((~0ul / 0xff) * (x)) +- + /* generic data direction definitions */ + #define READ 0 + #define WRITE 1 +--- /dev/null ++++ b/include/linux/wordpart.h +@@ -0,0 +1,13 @@ ++/* SPDX-License-Identifier: GPL-2.0 */ ++ ++#ifndef _LINUX_WORDPART_H ++#define _LINUX_WORDPART_H ++/** ++ * REPEAT_BYTE - repeat the value @x multiple times as an unsigned long value ++ * @x: value to repeat ++ * ++ * NOTE: @x is not checked for > 0xff; larger values produce odd results. ++ */ ++#define REPEAT_BYTE(x) ((~0ul / 0xff) * (x)) ++ ++#endif // _LINUX_WORDPART_H diff --git a/target/linux/generic/backport-6.6/302-v6.9-kernel.h-Move-upper_-_bits-and-lower_-_bits-to-wordp.patch b/target/linux/generic/backport-6.6/302-v6.9-kernel.h-Move-upper_-_bits-and-lower_-_bits-to-wordp.patch new file mode 100644 index 0000000000..b9c40e6206 --- /dev/null +++ b/target/linux/generic/backport-6.6/302-v6.9-kernel.h-Move-upper_-_bits-and-lower_-_bits-to-wordp.patch @@ -0,0 +1,107 @@ +From adeb04362d74188c1e22ccb824b15a0a7b3de2f4 Mon Sep 17 00:00:00 2001 +From: Andy Shevchenko +Date: Wed, 14 Feb 2024 19:26:32 +0200 +Subject: [PATCH] kernel.h: Move upper_*_bits() and lower_*_bits() to + wordpart.h + +The wordpart.h header is collecting APIs related to the handling +parts of the word (usually in byte granularity). The upper_*_bits() +and lower_*_bits() are good candidates to be moved to there. + +This helps to clean up header dependency hell with regard to kernel.h +as the latter gathers completely unrelated stuff together and slows +down compilation (especially when it's included into other header). + +Signed-off-by: Andy Shevchenko +Link: https://lore.kernel.org/r/20240214172752.3605073-1-andriy.shevchenko@linux.intel.com +Reviewed-by: Randy Dunlap +Signed-off-by: Kees Cook +--- + include/linux/kernel.h | 30 ++---------------------------- + include/linux/wordpart.h | 29 +++++++++++++++++++++++++++++ + 2 files changed, 31 insertions(+), 28 deletions(-) + +--- a/include/linux/kernel.h ++++ b/include/linux/kernel.h +@@ -32,6 +32,8 @@ + #include + #include + #include ++#include ++ + #include + + #include +@@ -57,34 +59,6 @@ + } \ + ) + +-/** +- * upper_32_bits - return bits 32-63 of a number +- * @n: the number we're accessing +- * +- * A basic shift-right of a 64- or 32-bit quantity. Use this to suppress +- * the "right shift count >= width of type" warning when that quantity is +- * 32-bits. +- */ +-#define upper_32_bits(n) ((u32)(((n) >> 16) >> 16)) +- +-/** +- * lower_32_bits - return bits 0-31 of a number +- * @n: the number we're accessing +- */ +-#define lower_32_bits(n) ((u32)((n) & 0xffffffff)) +- +-/** +- * upper_16_bits - return bits 16-31 of a number +- * @n: the number we're accessing +- */ +-#define upper_16_bits(n) ((u16)((n) >> 16)) +- +-/** +- * lower_16_bits - return bits 0-15 of a number +- * @n: the number we're accessing +- */ +-#define lower_16_bits(n) ((u16)((n) & 0xffff)) +- + struct completion; + struct user; + +--- a/include/linux/wordpart.h ++++ b/include/linux/wordpart.h +@@ -2,6 +2,35 @@ + + #ifndef _LINUX_WORDPART_H + #define _LINUX_WORDPART_H ++ ++/** ++ * upper_32_bits - return bits 32-63 of a number ++ * @n: the number we're accessing ++ * ++ * A basic shift-right of a 64- or 32-bit quantity. Use this to suppress ++ * the "right shift count >= width of type" warning when that quantity is ++ * 32-bits. ++ */ ++#define upper_32_bits(n) ((u32)(((n) >> 16) >> 16)) ++ ++/** ++ * lower_32_bits - return bits 0-31 of a number ++ * @n: the number we're accessing ++ */ ++#define lower_32_bits(n) ((u32)((n) & 0xffffffff)) ++ ++/** ++ * upper_16_bits - return bits 16-31 of a number ++ * @n: the number we're accessing ++ */ ++#define upper_16_bits(n) ((u16)((n) >> 16)) ++ ++/** ++ * lower_16_bits - return bits 0-15 of a number ++ * @n: the number we're accessing ++ */ ++#define lower_16_bits(n) ((u16)((n) & 0xffff)) ++ + /** + * REPEAT_BYTE - repeat the value @x multiple times as an unsigned long value + * @x: value to repeat diff --git a/target/linux/generic/backport-6.6/790-53-v6.10-net-dsa-mt7530-do-not-set-MT7530_P5_DIS-when-PHY-.patch b/target/linux/generic/backport-6.6/790-53-v6.10-net-dsa-mt7530-do-not-set-MT7530_P5_DIS-when-PHY-.patch new file mode 100644 index 0000000000..cee3d01728 --- /dev/null +++ b/target/linux/generic/backport-6.6/790-53-v6.10-net-dsa-mt7530-do-not-set-MT7530_P5_DIS-when-PHY-.patch @@ -0,0 +1,45 @@ +From 16e6592cd5c5bd74d8890973489f60176c692614 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ar=C4=B1n=C3=A7=20=C3=9CNAL?= +Date: Sun, 28 Apr 2024 12:19:58 +0300 +Subject: [PATCH] net: dsa: mt7530: do not set MT7530_P5_DIS when PHY muxing is + being used +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +DSA initalises the ds->num_ports amount of ports in +dsa_switch_touch_ports(). When the PHY muxing feature is in use, port 5 +won't be defined in the device tree. Because of this, the type member of +the dsa_port structure for this port will be assigned DSA_PORT_TYPE_UNUSED. +The dsa_port_setup() function calls ds->ops->port_disable() when the port +type is DSA_PORT_TYPE_UNUSED. + +The MT7530_P5_DIS bit is unset in mt7530_setup() when PHY muxing is being +used. mt7530_port_disable() which is assigned to ds->ops->port_disable() is +called afterwards. Currently, mt7530_port_disable() sets MT7530_P5_DIS +which breaks network connectivity when PHY muxing is being used. + +Therefore, do not set MT7530_P5_DIS when PHY muxing is being used. + +Fixes: 377174c5760c ("net: dsa: mt7530: move MT753X_MTRAP operations for MT7530") +Reported-by: Daniel Golle +Signed-off-by: Arınç ÜNAL +Reviewed-by: Andrew Lunn +Link: https://lore.kernel.org/r/20240428-for-netnext-mt7530-do-not-disable-port5-when-phy-muxing-v2-1-bb7c37d293f8@arinc9.com +Signed-off-by: Paolo Abeni +--- + drivers/net/dsa/mt7530.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +--- a/drivers/net/dsa/mt7530.c ++++ b/drivers/net/dsa/mt7530.c +@@ -1220,7 +1220,8 @@ mt7530_port_disable(struct dsa_switch *d + if (priv->id != ID_MT7530 && priv->id != ID_MT7621) + return; + +- if (port == 5) ++ /* Do not set MT7530_P5_DIS when port 5 is being used for PHY muxing. */ ++ if (port == 5 && priv->p5_mode == GMAC5) + mt7530_set(priv, MT753X_MTRAP, MT7530_P5_DIS); + else if (port == 6) + mt7530_set(priv, MT753X_MTRAP, MT7530_P6_DIS); diff --git a/target/linux/generic/backport-6.6/790-54-v6.10-796-net-dsa-mt7530-detect-PHY-muxing-when-PHY-is-defined.patch b/target/linux/generic/backport-6.6/790-54-v6.10-796-net-dsa-mt7530-detect-PHY-muxing-when-PHY-is-defined.patch new file mode 100644 index 0000000000..d369c4e05e --- /dev/null +++ b/target/linux/generic/backport-6.6/790-54-v6.10-796-net-dsa-mt7530-detect-PHY-muxing-when-PHY-is-defined.patch @@ -0,0 +1,45 @@ +From d8dcf5bd6d0eace9f7c1daa14b63b3925b09d033 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ar=C4=B1n=C3=A7=20=C3=9CNAL?= +Date: Tue, 30 Apr 2024 08:01:33 +0300 +Subject: [PATCH] net: dsa: mt7530: detect PHY muxing when PHY is defined on + switch MDIO bus +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Currently, the MT7530 DSA subdriver configures the MT7530 switch to provide +direct access to switch PHYs, meaning, the switch PHYs listen on the MDIO +bus the switch listens on. The PHY muxing feature makes use of this. + +This is problematic as the PHY may be attached before the switch is +initialised, in which case, the PHY will fail to be attached. + +Since commit 91374ba537bd ("net: dsa: mt7530: support OF-based registration +of switch MDIO bus"), we can describe the switch PHYs on the MDIO bus of +the switch on the device tree. Extend the check to detect PHY muxing when +the PHY is defined on the MDIO bus of the switch on the device tree. + +When the PHY is described this way, the switch will be initialised first, +then the switch MDIO bus will be registered. Only after these steps, the +PHY will be attached. + +Signed-off-by: Arınç ÜNAL +Reviewed-by: Daniel Golle +Link: https://lore.kernel.org/r/20240430-b4-for-netnext-mt7530-use-switch-mdio-bus-for-phy-muxing-v2-1-9104d886d0db@arinc9.com +Signed-off-by: Paolo Abeni +--- + drivers/net/dsa/mt7530.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +--- a/drivers/net/dsa/mt7530.c ++++ b/drivers/net/dsa/mt7530.c +@@ -2484,7 +2484,8 @@ mt7530_setup(struct dsa_switch *ds) + if (!phy_node) + continue; + +- if (phy_node->parent == priv->dev->of_node->parent) { ++ if (phy_node->parent == priv->dev->of_node->parent || ++ phy_node->parent->parent == priv->dev->of_node) { + ret = of_get_phy_mode(mac_np, &interface); + if (ret && ret != -ENODEV) { + of_node_put(mac_np); diff --git a/target/linux/generic/pending-6.6/750-net-phy-airoha-Add-the-Airoha-EN8811H-PHY-driver.patch b/target/linux/generic/backport-6.6/798-v6.10-net-phy-air_en8811h-Add-the-Airoha-EN8811H-PHY-drive.patch similarity index 61% rename from target/linux/generic/pending-6.6/750-net-phy-airoha-Add-the-Airoha-EN8811H-PHY-driver.patch rename to target/linux/generic/backport-6.6/798-v6.10-net-phy-air_en8811h-Add-the-Airoha-EN8811H-PHY-drive.patch index c811e40d49..5b627cf449 100644 --- a/target/linux/generic/pending-6.6/750-net-phy-airoha-Add-the-Airoha-EN8811H-PHY-driver.patch +++ b/target/linux/generic/backport-6.6/798-v6.10-net-phy-air_en8811h-Add-the-Airoha-EN8811H-PHY-drive.patch @@ -1,83 +1,28 @@ -From patchwork Tue Feb 6 19:47:51 2024 -Content-Type: text/plain; charset="utf-8" -MIME-Version: 1.0 -Content-Transfer-Encoding: 7bit -X-Patchwork-Submitter: Eric Woudstra -X-Patchwork-Id: 13547762 -X-Patchwork-Delegate: kuba@kernel.org +From 71e79430117d56c409c5ea485a263bc0d8083390 Mon Sep 17 00:00:00 2001 From: Eric Woudstra -To: "David S. Miller" , - Eric Dumazet , - Jakub Kicinski , - Paolo Abeni , - Rob Herring , - Krzysztof Kozlowski , - Conor Dooley , - Andrew Lunn , - Heiner Kallweit , - Russell King , - Matthias Brugger , - AngeloGioacchino Del Regno , - "Frank Wunderlich" , - Daniel Golle , - Lucien Jheng , - Zhi-Jun You -Cc: netdev@vger.kernel.org, - devicetree@vger.kernel.org, - Eric Woudstra -Subject: [PATCH net-next 2/2] net: phy: air_en8811h: Add the Airoha EN8811H - PHY driver -Date: Tue, 6 Feb 2024 20:47:51 +0100 -Message-ID: <20240206194751.1901802-3-ericwouds@gmail.com> -X-Mailer: git-send-email 2.42.1 -In-Reply-To: <20240206194751.1901802-1-ericwouds@gmail.com> -References: <20240206194751.1901802-1-ericwouds@gmail.com> -Precedence: bulk -X-Mailing-List: netdev@vger.kernel.org -List-Id: -List-Subscribe: -List-Unsubscribe: -MIME-Version: 1.0 -X-Patchwork-Delegate: kuba@kernel.org +Date: Tue, 26 Mar 2024 17:23:05 +0100 +Subject: [PATCH] net: phy: air_en8811h: Add the Airoha EN8811H PHY driver -* Source originated from airoha's en8811h v1.2.1 driver - * Moved air_en8811h.h to air_en8811h.c - * Removed air_pbus_reg_write() as it writes to another device on mdio-bus - * Load firmware from /lib/firmware/airoha/ instead of /lib/firmware/ - * Added .get_rate_matching() - * Use generic phy_read/write() and phy_read/write_mmd() - * Edited .get_features() to use generic C45 functions - * Edited .config_aneg() and .read_status() to use a mix of generic C22/C45 - * Use led handling functions from mediatek-ge-soc.c - * Simplified led handling by storing led rules - * Cleanup macro definitions - * Cleanup code to pass checkpatch.pl - * General code cleanup +Add the driver for the Airoha EN8811H 2.5 Gigabit PHY. The phy supports +100/1000/2500 Mbps with auto negotiation only. -Changes from original RFC patch: +The driver uses two firmware files, for which updated versions are added to +linux-firmware already. - * Use the correct order in Kconfig and Makefile - * Change some register naming to correspond with datasheet - * Use phy_driver .read_page() and .write_page() - * Use module_phy_driver() - * Use get_unaligned_le16() instead of macro - * In .config_aneg() and .read_status() use genphy_xxx() C22 - * Use another vendor register to read real speed - * Load firmware only once and store firmware version - * Apply 2.5G LPA work-around (firmware before 24011202) - * Read 2.5G LPA from vendor register (firmware 24011202 and later) - -Changes to be committed: - modified: drivers/net/phy/Kconfig - modified: drivers/net/phy/Makefile - new file: drivers/net/phy/air_en8811h.c +Note: At phy-address + 8 there is another device on the mdio bus, that +belongs to the EN881H. While the original driver writes to it, Airoha +has confirmed this is not needed. Therefore, communication with this +device is not included in this driver. Signed-off-by: Eric Woudstra +Reviewed-by: Andrew Lunn +Link: https://lore.kernel.org/r/20240326162305.303598-3-ericwouds@gmail.com +Signed-off-by: Jakub Kicinski --- drivers/net/phy/Kconfig | 5 + drivers/net/phy/Makefile | 1 + - drivers/net/phy/air_en8811h.c | 1006 +++++++++++++++++++++++++++++++++ - 3 files changed, 1012 insertions(+) + drivers/net/phy/air_en8811h.c | 1086 +++++++++++++++++++++++++++++++++ + 3 files changed, 1092 insertions(+) create mode 100644 drivers/net/phy/air_en8811h.c --- a/drivers/net/phy/Kconfig @@ -92,7 +37,7 @@ Signed-off-by: Eric Woudstra + Currently supports the Airoha EN8811H PHY. + config AMD_PHY - tristate "AMD and Altima PHYs" + tristate "AMD PHYs" help --- a/drivers/net/phy/Makefile +++ b/drivers/net/phy/Makefile @@ -106,12 +51,10 @@ Signed-off-by: Eric Woudstra obj-$(CONFIG_AX88796B_PHY) += ax88796b.o --- /dev/null +++ b/drivers/net/phy/air_en8811h.c -@@ -0,0 +1,1006 @@ +@@ -0,0 +1,1086 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* -+ * Driver for Airoha Ethernet PHYs -+ * -+ * Currently supporting the EN8811H. ++ * Driver for the Airoha EN8811H 2.5 Gigabit PHY. + * + * Limitations of the EN8811H: + * - Only full duplex supported @@ -125,6 +68,7 @@ Signed-off-by: Eric Woudstra +#include +#include +#include ++#include +#include + +#define EN8811H_PHY_ID 0x03a2a411 @@ -135,12 +79,8 @@ Signed-off-by: Eric Woudstra +#define AIR_FW_ADDR_DM 0x00000000 +#define AIR_FW_ADDR_DSP 0x00100000 + -+/* u32 (DWORD) component macros */ -+#define LOWORD(d) ((u16)(u32)(d)) -+#define HIWORD(d) ((u16)(((u32)(d)) >> 16)) -+ +/* MII Registers */ -+#define AIR_AUX_CTRL_STATUS 0x1d ++#define AIR_AUX_CTRL_STATUS 0x1d +#define AIR_AUX_CTRL_STATUS_SPEED_MASK GENMASK(4, 2) +#define AIR_AUX_CTRL_STATUS_SPEED_100 0x4 +#define AIR_AUX_CTRL_STATUS_SPEED_1000 0x8 @@ -151,32 +91,32 @@ Signed-off-by: Eric Woudstra +#define AIR_PHY_PAGE_EXTENDED_4 0x0004 + +/* MII Registers Page 4*/ -+#define AIR_PBUS_MODE 0x10 -+#define AIR_PBUS_MODE_ADDR_FIXED 0x0000 -+#define AIR_PBUS_MODE_ADDR_INCR BIT(15) -+#define AIR_PBUS_WR_ADDR_HIGH 0x11 -+#define AIR_PBUS_WR_ADDR_LOW 0x12 -+#define AIR_PBUS_WR_DATA_HIGH 0x13 -+#define AIR_PBUS_WR_DATA_LOW 0x14 -+#define AIR_PBUS_RD_ADDR_HIGH 0x15 -+#define AIR_PBUS_RD_ADDR_LOW 0x16 -+#define AIR_PBUS_RD_DATA_HIGH 0x17 -+#define AIR_PBUS_RD_DATA_LOW 0x18 ++#define AIR_BPBUS_MODE 0x10 ++#define AIR_BPBUS_MODE_ADDR_FIXED 0x0000 ++#define AIR_BPBUS_MODE_ADDR_INCR BIT(15) ++#define AIR_BPBUS_WR_ADDR_HIGH 0x11 ++#define AIR_BPBUS_WR_ADDR_LOW 0x12 ++#define AIR_BPBUS_WR_DATA_HIGH 0x13 ++#define AIR_BPBUS_WR_DATA_LOW 0x14 ++#define AIR_BPBUS_RD_ADDR_HIGH 0x15 ++#define AIR_BPBUS_RD_ADDR_LOW 0x16 ++#define AIR_BPBUS_RD_DATA_HIGH 0x17 ++#define AIR_BPBUS_RD_DATA_LOW 0x18 + +/* Registers on MDIO_MMD_VEND1 */ +#define EN8811H_PHY_FW_STATUS 0x8009 +#define EN8811H_PHY_READY 0x02 + -+#define AIR_PHY_HOST_CMD_1 0x800c -+#define AIR_PHY_HOST_CMD_1_MODE1 0x0 -+#define AIR_PHY_HOST_CMD_2 0x800d -+#define AIR_PHY_HOST_CMD_2_MODE1 0x0 -+#define AIR_PHY_HOST_CMD_3 0x800e -+#define AIR_PHY_HOST_CMD_3_MODE1 0x1101 -+#define AIR_PHY_HOST_CMD_3_DOCMD 0x1100 -+#define AIR_PHY_HOST_CMD_4 0x800f -+#define AIR_PHY_HOST_CMD_4_MODE1 0x0002 -+#define AIR_PHY_HOST_CMD_4_INTCLR 0x00e4 ++#define AIR_PHY_MCU_CMD_1 0x800c ++#define AIR_PHY_MCU_CMD_1_MODE1 0x0 ++#define AIR_PHY_MCU_CMD_2 0x800d ++#define AIR_PHY_MCU_CMD_2_MODE1 0x0 ++#define AIR_PHY_MCU_CMD_3 0x800e ++#define AIR_PHY_MCU_CMD_3_MODE1 0x1101 ++#define AIR_PHY_MCU_CMD_3_DOCMD 0x1100 ++#define AIR_PHY_MCU_CMD_4 0x800f ++#define AIR_PHY_MCU_CMD_4_MODE1 0x0002 ++#define AIR_PHY_MCU_CMD_4_INTCLR 0x00e4 + +/* Registers on MDIO_MMD_VEND2 */ +#define AIR_PHY_LED_BCR 0x021 @@ -235,28 +175,21 @@ Signed-off-by: Eric Woudstra +#define EN8811H_FW_CTRL_2 0x800000 +#define EN8811H_FW_CTRL_2_LOADING BIT(11) + ++/* Led definitions */ +#define EN8811H_LED_COUNT 3 + -+/* GPIO5 <-> BASE_T_LED0 -+ * GPIO4 <-> BASE_T_LED1 -+ * GPIO3 <-> BASE_T_LED2 -+ * -+ * Default setup suitable for 2 leds connected: -+ * 100M link up triggers led0, only led0 blinking on traffic -+ * 1000M link up triggers led1, only led1 blinking on traffic -+ * 2500M link up triggers led0 and led1, both blinking on traffic -+ * Also suitable for 1 led connected: -+ * any link up triggers led2 ++/* Default LED setup: ++ * GPIO5 <-> LED0 On: Link detected, blink Rx/Tx ++ * GPIO4 <-> LED1 On: Link detected at 2500 or 1000 Mbps ++ * GPIO3 <-> LED2 On: Link detected at 2500 or 100 Mbps + */ -+#define AIR_DEFAULT_TRIGGER_LED0 (BIT(TRIGGER_NETDEV_LINK_2500) | \ -+ BIT(TRIGGER_NETDEV_LINK_100) | \ ++#define AIR_DEFAULT_TRIGGER_LED0 (BIT(TRIGGER_NETDEV_LINK) | \ + BIT(TRIGGER_NETDEV_RX) | \ + BIT(TRIGGER_NETDEV_TX)) +#define AIR_DEFAULT_TRIGGER_LED1 (BIT(TRIGGER_NETDEV_LINK_2500) | \ -+ BIT(TRIGGER_NETDEV_LINK_1000) | \ -+ BIT(TRIGGER_NETDEV_RX) | \ -+ BIT(TRIGGER_NETDEV_TX)) -+#define AIR_DEFAULT_TRIGGER_LED2 BIT(TRIGGER_NETDEV_LINK) ++ BIT(TRIGGER_NETDEV_LINK_1000)) ++#define AIR_DEFAULT_TRIGGER_LED2 (BIT(TRIGGER_NETDEV_LINK_2500) | \ ++ BIT(TRIGGER_NETDEV_LINK_100)) + +struct led { + unsigned long rules; @@ -265,6 +198,7 @@ Signed-off-by: Eric Woudstra + +struct en8811h_priv { + u32 firmware_version; ++ bool mcu_needs_restart; + struct led led[EN8811H_LED_COUNT]; +}; + @@ -274,12 +208,12 @@ Signed-off-by: Eric Woudstra +}; + +enum { -+ AIR_PHY_LED_DUR_BLINK_32M, -+ AIR_PHY_LED_DUR_BLINK_64M, -+ AIR_PHY_LED_DUR_BLINK_128M, -+ AIR_PHY_LED_DUR_BLINK_256M, -+ AIR_PHY_LED_DUR_BLINK_512M, -+ AIR_PHY_LED_DUR_BLINK_1024M, ++ AIR_PHY_LED_DUR_BLINK_32MS, ++ AIR_PHY_LED_DUR_BLINK_64MS, ++ AIR_PHY_LED_DUR_BLINK_128MS, ++ AIR_PHY_LED_DUR_BLINK_256MS, ++ AIR_PHY_LED_DUR_BLINK_512MS, ++ AIR_PHY_LED_DUR_BLINK_1024MS, +}; + +enum { @@ -298,16 +232,16 @@ Signed-off-by: Eric Woudstra +}; + +#define AIR_PHY_LED_DUR_UNIT 1024 -+#define AIR_PHY_LED_DUR (AIR_PHY_LED_DUR_UNIT << AIR_PHY_LED_DUR_BLINK_64M) ++#define AIR_PHY_LED_DUR (AIR_PHY_LED_DUR_UNIT << AIR_PHY_LED_DUR_BLINK_64MS) + -+static const unsigned long en8811h_led_trig = (BIT(TRIGGER_NETDEV_FULL_DUPLEX) | -+ BIT(TRIGGER_NETDEV_LINK) | -+ BIT(TRIGGER_NETDEV_LINK_10) | -+ BIT(TRIGGER_NETDEV_LINK_100) | -+ BIT(TRIGGER_NETDEV_LINK_1000) | -+ BIT(TRIGGER_NETDEV_LINK_2500) | -+ BIT(TRIGGER_NETDEV_RX) | -+ BIT(TRIGGER_NETDEV_TX)); ++static const unsigned long en8811h_led_trig = BIT(TRIGGER_NETDEV_FULL_DUPLEX) | ++ BIT(TRIGGER_NETDEV_LINK) | ++ BIT(TRIGGER_NETDEV_LINK_10) | ++ BIT(TRIGGER_NETDEV_LINK_100) | ++ BIT(TRIGGER_NETDEV_LINK_1000) | ++ BIT(TRIGGER_NETDEV_LINK_2500) | ++ BIT(TRIGGER_NETDEV_RX) | ++ BIT(TRIGGER_NETDEV_TX); + +static int air_phy_read_page(struct phy_device *phydev) +{ @@ -324,23 +258,27 @@ Signed-off-by: Eric Woudstra +{ + int ret; + -+ ret = __phy_write(phydev, AIR_PBUS_MODE, AIR_PBUS_MODE_ADDR_FIXED); ++ ret = __phy_write(phydev, AIR_BPBUS_MODE, AIR_BPBUS_MODE_ADDR_FIXED); + if (ret < 0) + return ret; + -+ ret = __phy_write(phydev, AIR_PBUS_WR_ADDR_HIGH, HIWORD(pbus_address)); ++ ret = __phy_write(phydev, AIR_BPBUS_WR_ADDR_HIGH, ++ upper_16_bits(pbus_address)); + if (ret < 0) + return ret; + -+ ret = __phy_write(phydev, AIR_PBUS_WR_ADDR_LOW, LOWORD(pbus_address)); ++ ret = __phy_write(phydev, AIR_BPBUS_WR_ADDR_LOW, ++ lower_16_bits(pbus_address)); + if (ret < 0) + return ret; + -+ ret = __phy_write(phydev, AIR_PBUS_WR_DATA_HIGH, HIWORD(pbus_data)); ++ ret = __phy_write(phydev, AIR_BPBUS_WR_DATA_HIGH, ++ upper_16_bits(pbus_data)); + if (ret < 0) + return ret; + -+ ret = __phy_write(phydev, AIR_PBUS_WR_DATA_LOW, LOWORD(pbus_data)); ++ ret = __phy_write(phydev, AIR_BPBUS_WR_DATA_LOW, ++ lower_16_bits(pbus_data)); + if (ret < 0) + return ret; + @@ -350,17 +288,20 @@ Signed-off-by: Eric Woudstra +static int air_buckpbus_reg_write(struct phy_device *phydev, + u32 pbus_address, u32 pbus_data) +{ -+ int ret, saved_page; ++ int saved_page; ++ int ret = 0; + + saved_page = phy_select_page(phydev, AIR_PHY_PAGE_EXTENDED_4); + -+ ret = __air_buckpbus_reg_write(phydev, pbus_address, pbus_data); -+ if (ret < 0) -+ phydev_err(phydev, "%s 0x%08x failed: %d\n", __func__, -+ pbus_address, ret); ++ if (saved_page >= 0) { ++ ret = __air_buckpbus_reg_write(phydev, pbus_address, ++ pbus_data); ++ if (ret < 0) ++ phydev_err(phydev, "%s 0x%08x failed: %d\n", __func__, ++ pbus_address, ret); ++ } + + return phy_restore_page(phydev, saved_page, ret); -+; +} + +static int __air_buckpbus_reg_read(struct phy_device *phydev, @@ -369,41 +310,122 @@ Signed-off-by: Eric Woudstra + int pbus_data_low, pbus_data_high; + int ret; + -+ ret = __phy_write(phydev, AIR_PBUS_MODE, AIR_PBUS_MODE_ADDR_FIXED); ++ ret = __phy_write(phydev, AIR_BPBUS_MODE, AIR_BPBUS_MODE_ADDR_FIXED); + if (ret < 0) + return ret; + -+ ret = __phy_write(phydev, AIR_PBUS_RD_ADDR_HIGH, HIWORD(pbus_address)); ++ ret = __phy_write(phydev, AIR_BPBUS_RD_ADDR_HIGH, ++ upper_16_bits(pbus_address)); + if (ret < 0) + return ret; + -+ ret = __phy_write(phydev, AIR_PBUS_RD_ADDR_LOW, LOWORD(pbus_address)); ++ ret = __phy_write(phydev, AIR_BPBUS_RD_ADDR_LOW, ++ lower_16_bits(pbus_address)); + if (ret < 0) + return ret; + -+ pbus_data_high = __phy_read(phydev, AIR_PBUS_RD_DATA_HIGH); ++ pbus_data_high = __phy_read(phydev, AIR_BPBUS_RD_DATA_HIGH); + if (pbus_data_high < 0) + return ret; + -+ pbus_data_low = __phy_read(phydev, AIR_PBUS_RD_DATA_LOW); ++ pbus_data_low = __phy_read(phydev, AIR_BPBUS_RD_DATA_LOW); + if (pbus_data_low < 0) + return ret; + -+ *pbus_data = (u16)pbus_data_low | ((u32)(u16)pbus_data_high << 16); ++ *pbus_data = pbus_data_low | (pbus_data_high << 16); + return 0; +} + +static int air_buckpbus_reg_read(struct phy_device *phydev, + u32 pbus_address, u32 *pbus_data) +{ -+ int ret, saved_page; ++ int saved_page; ++ int ret = 0; + + saved_page = phy_select_page(phydev, AIR_PHY_PAGE_EXTENDED_4); + -+ ret = __air_buckpbus_reg_read(phydev, pbus_address, pbus_data); ++ if (saved_page >= 0) { ++ ret = __air_buckpbus_reg_read(phydev, pbus_address, pbus_data); ++ if (ret < 0) ++ phydev_err(phydev, "%s 0x%08x failed: %d\n", __func__, ++ pbus_address, ret); ++ } ++ ++ return phy_restore_page(phydev, saved_page, ret); ++} ++ ++static int __air_buckpbus_reg_modify(struct phy_device *phydev, ++ u32 pbus_address, u32 mask, u32 set) ++{ ++ int pbus_data_low, pbus_data_high; ++ u32 pbus_data_old, pbus_data_new; ++ int ret; ++ ++ ret = __phy_write(phydev, AIR_BPBUS_MODE, AIR_BPBUS_MODE_ADDR_FIXED); + if (ret < 0) -+ phydev_err(phydev, "%s 0x%08x failed: %d\n", __func__, -+ pbus_address, ret); ++ return ret; ++ ++ ret = __phy_write(phydev, AIR_BPBUS_RD_ADDR_HIGH, ++ upper_16_bits(pbus_address)); ++ if (ret < 0) ++ return ret; ++ ++ ret = __phy_write(phydev, AIR_BPBUS_RD_ADDR_LOW, ++ lower_16_bits(pbus_address)); ++ if (ret < 0) ++ return ret; ++ ++ pbus_data_high = __phy_read(phydev, AIR_BPBUS_RD_DATA_HIGH); ++ if (pbus_data_high < 0) ++ return ret; ++ ++ pbus_data_low = __phy_read(phydev, AIR_BPBUS_RD_DATA_LOW); ++ if (pbus_data_low < 0) ++ return ret; ++ ++ pbus_data_old = pbus_data_low | (pbus_data_high << 16); ++ pbus_data_new = (pbus_data_old & ~mask) | set; ++ if (pbus_data_new == pbus_data_old) ++ return 0; ++ ++ ret = __phy_write(phydev, AIR_BPBUS_WR_ADDR_HIGH, ++ upper_16_bits(pbus_address)); ++ if (ret < 0) ++ return ret; ++ ++ ret = __phy_write(phydev, AIR_BPBUS_WR_ADDR_LOW, ++ lower_16_bits(pbus_address)); ++ if (ret < 0) ++ return ret; ++ ++ ret = __phy_write(phydev, AIR_BPBUS_WR_DATA_HIGH, ++ upper_16_bits(pbus_data_new)); ++ if (ret < 0) ++ return ret; ++ ++ ret = __phy_write(phydev, AIR_BPBUS_WR_DATA_LOW, ++ lower_16_bits(pbus_data_new)); ++ if (ret < 0) ++ return ret; ++ ++ return 0; ++} ++ ++static int air_buckpbus_reg_modify(struct phy_device *phydev, ++ u32 pbus_address, u32 mask, u32 set) ++{ ++ int saved_page; ++ int ret = 0; ++ ++ saved_page = phy_select_page(phydev, AIR_PHY_PAGE_EXTENDED_4); ++ ++ if (saved_page >= 0) { ++ ret = __air_buckpbus_reg_modify(phydev, pbus_address, mask, ++ set); ++ if (ret < 0) ++ phydev_err(phydev, "%s 0x%08x failed: %d\n", __func__, ++ pbus_address, ret); ++ } + + return phy_restore_page(phydev, saved_page, ret); +} @@ -415,26 +437,28 @@ Signed-off-by: Eric Woudstra + int ret; + u16 val; + -+ ret = __phy_write(phydev, AIR_PBUS_MODE, AIR_PBUS_MODE_ADDR_INCR); ++ ret = __phy_write(phydev, AIR_BPBUS_MODE, AIR_BPBUS_MODE_ADDR_INCR); + if (ret < 0) + return ret; + -+ ret = __phy_write(phydev, AIR_PBUS_WR_ADDR_HIGH, HIWORD(address)); ++ ret = __phy_write(phydev, AIR_BPBUS_WR_ADDR_HIGH, ++ upper_16_bits(address)); + if (ret < 0) + return ret; + -+ ret = __phy_write(phydev, AIR_PBUS_WR_ADDR_LOW, LOWORD(address)); ++ ret = __phy_write(phydev, AIR_BPBUS_WR_ADDR_LOW, ++ lower_16_bits(address)); + if (ret < 0) + return ret; + + for (offset = 0; offset < fw->size; offset += 4) { + val = get_unaligned_le16(&fw->data[offset + 2]); -+ ret = __phy_write(phydev, AIR_PBUS_WR_DATA_HIGH, val); ++ ret = __phy_write(phydev, AIR_BPBUS_WR_DATA_HIGH, val); + if (ret < 0) + return ret; + + val = get_unaligned_le16(&fw->data[offset]); -+ ret = __phy_write(phydev, AIR_PBUS_WR_DATA_LOW, val); ++ ret = __phy_write(phydev, AIR_BPBUS_WR_DATA_LOW, val); + if (ret < 0) + return ret; + } @@ -445,23 +469,43 @@ Signed-off-by: Eric Woudstra +static int air_write_buf(struct phy_device *phydev, u32 address, + const struct firmware *fw) +{ -+ int ret, saved_page; ++ int saved_page; ++ int ret = 0; + + saved_page = phy_select_page(phydev, AIR_PHY_PAGE_EXTENDED_4); + -+ ret = __air_write_buf(phydev, address, fw); -+ if (ret < 0) -+ phydev_err(phydev, "%s 0x%08x failed: %d\n", __func__, -+ address, ret); ++ if (saved_page >= 0) { ++ ret = __air_write_buf(phydev, address, fw); ++ if (ret < 0) ++ phydev_err(phydev, "%s 0x%08x failed: %d\n", __func__, ++ address, ret); ++ } + + return phy_restore_page(phydev, saved_page, ret); +} + ++static int en8811h_wait_mcu_ready(struct phy_device *phydev) ++{ ++ int ret, reg_value; ++ ++ /* Because of mdio-lock, may have to wait for multiple loads */ ++ ret = phy_read_mmd_poll_timeout(phydev, MDIO_MMD_VEND1, ++ EN8811H_PHY_FW_STATUS, reg_value, ++ reg_value == EN8811H_PHY_READY, ++ 20000, 7500000, true); ++ if (ret) { ++ phydev_err(phydev, "MCU not ready: 0x%x\n", reg_value); ++ return -ENODEV; ++ } ++ ++ return 0; ++} ++ +static int en8811h_load_firmware(struct phy_device *phydev) +{ ++ struct en8811h_priv *priv = phydev->priv; + struct device *dev = &phydev->mdio.dev; + const struct firmware *fw1, *fw2; -+ u32 pbus_value; + int ret; + + ret = request_firmware_direct(&fw1, EN8811H_MD32_DM, dev); @@ -477,11 +521,9 @@ Signed-off-by: Eric Woudstra + if (ret < 0) + goto en8811h_load_firmware_out; + -+ ret = air_buckpbus_reg_read(phydev, EN8811H_FW_CTRL_2, &pbus_value); -+ if (ret < 0) -+ goto en8811h_load_firmware_out; -+ pbus_value |= EN8811H_FW_CTRL_2_LOADING; -+ ret = air_buckpbus_reg_write(phydev, EN8811H_FW_CTRL_2, pbus_value); ++ ret = air_buckpbus_reg_modify(phydev, EN8811H_FW_CTRL_2, ++ EN8811H_FW_CTRL_2_LOADING, ++ EN8811H_FW_CTRL_2_LOADING); + if (ret < 0) + goto en8811h_load_firmware_out; + @@ -493,11 +535,8 @@ Signed-off-by: Eric Woudstra + if (ret < 0) + goto en8811h_load_firmware_out; + -+ ret = air_buckpbus_reg_read(phydev, EN8811H_FW_CTRL_2, &pbus_value); -+ if (ret < 0) -+ goto en8811h_load_firmware_out; -+ pbus_value &= ~EN8811H_FW_CTRL_2_LOADING; -+ ret = air_buckpbus_reg_write(phydev, EN8811H_FW_CTRL_2, pbus_value); ++ ret = air_buckpbus_reg_modify(phydev, EN8811H_FW_CTRL_2, ++ EN8811H_FW_CTRL_2_LOADING, 0); + if (ret < 0) + goto en8811h_load_firmware_out; + @@ -506,7 +545,12 @@ Signed-off-by: Eric Woudstra + if (ret < 0) + goto en8811h_load_firmware_out; + -+ ret = 0; ++ ret = en8811h_wait_mcu_ready(phydev); ++ ++ air_buckpbus_reg_read(phydev, EN8811H_FW_VERSION, ++ &priv->firmware_version); ++ phydev_info(phydev, "MD32 firmware version: %08x\n", ++ priv->firmware_version); + +en8811h_load_firmware_out: + release_firmware(fw2); @@ -520,7 +564,7 @@ Signed-off-by: Eric Woudstra + return ret; +} + -+static int en8811h_restart_host(struct phy_device *phydev) ++static int en8811h_restart_mcu(struct phy_device *phydev) +{ + int ret; + @@ -529,8 +573,12 @@ Signed-off-by: Eric Woudstra + if (ret < 0) + return ret; + -+ return air_buckpbus_reg_write(phydev, EN8811H_FW_CTRL_1, ++ ret = air_buckpbus_reg_write(phydev, EN8811H_FW_CTRL_1, + EN8811H_FW_CTRL_1_FINISH); ++ if (ret < 0) ++ return ret; ++ ++ return en8811h_wait_mcu_ready(phydev); +} + +static int air_hw_led_on_set(struct phy_device *phydev, u8 index, bool on) @@ -664,68 +712,46 @@ Signed-off-by: Eric Woudstra + u16 on = 0, blink = 0; + int ret; + -+ priv->led[index].rules = rules; -+ + if (index >= EN8811H_LED_COUNT) + return -EINVAL; + -+ if (rules & (BIT(TRIGGER_NETDEV_LINK_10) | BIT(TRIGGER_NETDEV_LINK))) { -+ on |= AIR_PHY_LED_ON_LINK10; -+ if (rules & BIT(TRIGGER_NETDEV_RX)) -+ blink |= AIR_PHY_LED_BLINK_10RX; -+ if (rules & BIT(TRIGGER_NETDEV_TX)) -+ blink |= AIR_PHY_LED_BLINK_10TX; -+ } -+ -+ if (rules & (BIT(TRIGGER_NETDEV_LINK_100) | BIT(TRIGGER_NETDEV_LINK))) { -+ on |= AIR_PHY_LED_ON_LINK100; -+ if (rules & BIT(TRIGGER_NETDEV_RX)) -+ blink |= AIR_PHY_LED_BLINK_100RX; -+ if (rules & BIT(TRIGGER_NETDEV_TX)) -+ blink |= AIR_PHY_LED_BLINK_100TX; -+ } -+ -+ if (rules & (BIT(TRIGGER_NETDEV_LINK_1000) | BIT(TRIGGER_NETDEV_LINK))) { -+ on |= AIR_PHY_LED_ON_LINK1000; -+ if (rules & BIT(TRIGGER_NETDEV_RX)) -+ blink |= AIR_PHY_LED_BLINK_1000RX; -+ if (rules & BIT(TRIGGER_NETDEV_TX)) -+ blink |= AIR_PHY_LED_BLINK_1000TX; -+ } -+ -+ if (rules & (BIT(TRIGGER_NETDEV_LINK_2500) | BIT(TRIGGER_NETDEV_LINK))) { -+ on |= AIR_PHY_LED_ON_LINK2500; -+ if (rules & BIT(TRIGGER_NETDEV_RX)) -+ blink |= AIR_PHY_LED_BLINK_2500RX; -+ if (rules & BIT(TRIGGER_NETDEV_TX)) -+ blink |= AIR_PHY_LED_BLINK_2500TX; -+ } -+ -+ if (on == 0) { -+ if (rules & BIT(TRIGGER_NETDEV_RX)) { -+ blink |= AIR_PHY_LED_BLINK_10RX | -+ AIR_PHY_LED_BLINK_100RX | -+ AIR_PHY_LED_BLINK_1000RX | -+ AIR_PHY_LED_BLINK_2500RX; -+ } -+ if (rules & BIT(TRIGGER_NETDEV_TX)) { -+ blink |= AIR_PHY_LED_BLINK_10TX | -+ AIR_PHY_LED_BLINK_100TX | -+ AIR_PHY_LED_BLINK_1000TX | -+ AIR_PHY_LED_BLINK_2500TX; -+ } -+ } ++ priv->led[index].rules = rules; + + if (rules & BIT(TRIGGER_NETDEV_FULL_DUPLEX)) + on |= AIR_PHY_LED_ON_FDX; + -+ if (rules & BIT(TRIGGER_NETDEV_HALF_DUPLEX)) -+ on |= AIR_PHY_LED_ON_HDX; ++ if (rules & (BIT(TRIGGER_NETDEV_LINK_10) | BIT(TRIGGER_NETDEV_LINK))) ++ on |= AIR_PHY_LED_ON_LINK10; ++ ++ if (rules & (BIT(TRIGGER_NETDEV_LINK_100) | BIT(TRIGGER_NETDEV_LINK))) ++ on |= AIR_PHY_LED_ON_LINK100; ++ ++ if (rules & (BIT(TRIGGER_NETDEV_LINK_1000) | BIT(TRIGGER_NETDEV_LINK))) ++ on |= AIR_PHY_LED_ON_LINK1000; ++ ++ if (rules & (BIT(TRIGGER_NETDEV_LINK_2500) | BIT(TRIGGER_NETDEV_LINK))) ++ on |= AIR_PHY_LED_ON_LINK2500; ++ ++ if (rules & BIT(TRIGGER_NETDEV_RX)) { ++ blink |= AIR_PHY_LED_BLINK_10RX | ++ AIR_PHY_LED_BLINK_100RX | ++ AIR_PHY_LED_BLINK_1000RX | ++ AIR_PHY_LED_BLINK_2500RX; ++ } ++ ++ if (rules & BIT(TRIGGER_NETDEV_TX)) { ++ blink |= AIR_PHY_LED_BLINK_10TX | ++ AIR_PHY_LED_BLINK_100TX | ++ AIR_PHY_LED_BLINK_1000TX | ++ AIR_PHY_LED_BLINK_2500TX; ++ } + + if (blink || on) { + /* switch hw-control on, so led-on and led-blink are off */ -+ clear_bit(AIR_PHY_LED_STATE_FORCE_ON, &priv->led[index].state); -+ clear_bit(AIR_PHY_LED_STATE_FORCE_BLINK, &priv->led[index].state); ++ clear_bit(AIR_PHY_LED_STATE_FORCE_ON, ++ &priv->led[index].state); ++ clear_bit(AIR_PHY_LED_STATE_FORCE_BLINK, ++ &priv->led[index].state); + } else { + priv->led[index].rules = 0; + } @@ -742,28 +768,26 @@ Signed-off-by: Eric Woudstra + +static int air_led_init(struct phy_device *phydev, u8 index, u8 state, u8 pol) +{ -+ int cl45_data; ++ int val = 0; + int err; + + if (index >= EN8811H_LED_COUNT) + return -EINVAL; + -+ cl45_data = phy_read_mmd(phydev, MDIO_MMD_VEND2, AIR_PHY_LED_ON(index)); -+ if (cl45_data < 0) -+ return cl45_data; -+ + if (state == AIR_LED_ENABLE) -+ cl45_data |= AIR_PHY_LED_ON_ENABLE; ++ val |= AIR_PHY_LED_ON_ENABLE; + else -+ cl45_data &= ~AIR_PHY_LED_ON_ENABLE; ++ val &= ~AIR_PHY_LED_ON_ENABLE; + + if (pol == AIR_ACTIVE_HIGH) -+ cl45_data |= AIR_PHY_LED_ON_POLARITY; ++ val |= AIR_PHY_LED_ON_POLARITY; + else -+ cl45_data &= ~AIR_PHY_LED_ON_POLARITY; ++ val &= ~AIR_PHY_LED_ON_POLARITY; ++ ++ err = phy_modify_mmd(phydev, MDIO_MMD_VEND2, AIR_PHY_LED_ON(index), ++ AIR_PHY_LED_ON_ENABLE | ++ AIR_PHY_LED_ON_POLARITY, val); + -+ err = phy_write_mmd(phydev, MDIO_MMD_VEND2, AIR_PHY_LED_ON(index), -+ cl45_data); + if (err < 0) + return err; + @@ -773,42 +797,40 @@ Signed-off-by: Eric Woudstra +static int air_leds_init(struct phy_device *phydev, int num, int dur, int mode) +{ + struct en8811h_priv *priv = phydev->priv; -+ int cl45_data = dur; + int ret, i; + + ret = phy_write_mmd(phydev, MDIO_MMD_VEND2, AIR_PHY_LED_DUR_BLINK, -+ cl45_data); ++ dur); + if (ret < 0) + return ret; + -+ cl45_data >>= 1; + ret = phy_write_mmd(phydev, MDIO_MMD_VEND2, AIR_PHY_LED_DUR_ON, -+ cl45_data); ++ dur >> 1); + if (ret < 0) + return ret; + -+ cl45_data = phy_read_mmd(phydev, MDIO_MMD_VEND2, AIR_PHY_LED_BCR); -+ if (cl45_data < 0) -+ return cl45_data; -+ + switch (mode) { + case AIR_LED_MODE_DISABLE: -+ cl45_data &= ~AIR_PHY_LED_BCR_EXT_CTRL; -+ cl45_data &= ~AIR_PHY_LED_BCR_MODE_MASK; ++ ret = phy_modify_mmd(phydev, MDIO_MMD_VEND2, AIR_PHY_LED_BCR, ++ AIR_PHY_LED_BCR_EXT_CTRL | ++ AIR_PHY_LED_BCR_MODE_MASK, 0); ++ if (ret < 0) ++ return ret; + break; + case AIR_LED_MODE_USER_DEFINE: -+ cl45_data |= AIR_PHY_LED_BCR_EXT_CTRL; -+ cl45_data |= AIR_PHY_LED_BCR_CLK_EN; ++ ret = phy_modify_mmd(phydev, MDIO_MMD_VEND2, AIR_PHY_LED_BCR, ++ AIR_PHY_LED_BCR_EXT_CTRL | ++ AIR_PHY_LED_BCR_CLK_EN, ++ AIR_PHY_LED_BCR_EXT_CTRL | ++ AIR_PHY_LED_BCR_CLK_EN); ++ if (ret < 0) ++ return ret; + break; + default: + phydev_err(phydev, "LED mode %d is not supported\n", mode); + return -EINVAL; + } + -+ ret = phy_write_mmd(phydev, MDIO_MMD_VEND2, AIR_PHY_LED_BCR, cl45_data); -+ if (ret < 0) -+ return ret; -+ + for (i = 0; i < num; ++i) { + ret = air_led_init(phydev, i, AIR_LED_ENABLE, AIR_ACTIVE_HIGH); + if (ret < 0) { @@ -837,21 +859,42 @@ Signed-off-by: Eric Woudstra +static int en8811h_probe(struct phy_device *phydev) +{ + struct en8811h_priv *priv; ++ int ret; + + priv = devm_kzalloc(&phydev->mdio.dev, sizeof(struct en8811h_priv), + GFP_KERNEL); + if (!priv) + return -ENOMEM; ++ phydev->priv = priv; ++ ++ ret = en8811h_load_firmware(phydev); ++ if (ret < 0) ++ return ret; ++ ++ /* mcu has just restarted after firmware load */ ++ priv->mcu_needs_restart = false; + + priv->led[0].rules = AIR_DEFAULT_TRIGGER_LED0; + priv->led[1].rules = AIR_DEFAULT_TRIGGER_LED1; + priv->led[2].rules = AIR_DEFAULT_TRIGGER_LED2; + -+ phydev->priv = priv; -+ + /* MDIO_DEVS1/2 empty, so set mmds_present bits here */ + phydev->c45_ids.mmds_present |= MDIO_DEVS_PMAPMD | MDIO_DEVS_AN; + ++ ret = air_leds_init(phydev, EN8811H_LED_COUNT, AIR_PHY_LED_DUR, ++ AIR_LED_MODE_DISABLE); ++ if (ret < 0) { ++ phydev_err(phydev, "Failed to disable leds: %d\n", ret); ++ return ret; ++ } ++ ++ /* Configure led gpio pins as output */ ++ ret = air_buckpbus_reg_modify(phydev, EN8811H_GPIO_OUTPUT, ++ EN8811H_GPIO_OUTPUT_345, ++ EN8811H_GPIO_OUTPUT_345); ++ if (ret < 0) ++ return ret; ++ + return 0; +} + @@ -859,58 +902,41 @@ Signed-off-by: Eric Woudstra +{ + struct en8811h_priv *priv = phydev->priv; + struct device *dev = &phydev->mdio.dev; -+ int ret, pollret, reg_value; + u32 pbus_value; ++ int ret; + -+ if (!priv->firmware_version) -+ ret = en8811h_load_firmware(phydev); -+ else -+ ret = en8811h_restart_host(phydev); -+ if (ret < 0) -+ return ret; -+ -+ /* Because of mdio-lock, may have to wait for multiple loads */ -+ pollret = phy_read_mmd_poll_timeout(phydev, MDIO_MMD_VEND1, -+ EN8811H_PHY_FW_STATUS, reg_value, -+ reg_value == EN8811H_PHY_READY, -+ 20000, 7500000, true); -+ -+ ret = air_buckpbus_reg_read(phydev, EN8811H_FW_VERSION, &pbus_value); -+ if (ret < 0) -+ return ret; -+ -+ if (pollret || !pbus_value) { -+ phydev_err(phydev, "Firmware not ready: 0x%x\n", reg_value); -+ return -ENODEV; ++ /* If restart happened in .probe(), no need to restart now */ ++ if (priv->mcu_needs_restart) { ++ ret = en8811h_restart_mcu(phydev); ++ if (ret < 0) ++ return ret; ++ } else { ++ /* Next calls to .config_init() mcu needs to restart */ ++ priv->mcu_needs_restart = true; + } + -+ if (!priv->firmware_version) { -+ phydev_info(phydev, "MD32 firmware version: %08x\n", pbus_value); -+ priv->firmware_version = pbus_value; -+ } -+ -+ /* Select mode 1, the only mode supported */ -+ ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, AIR_PHY_HOST_CMD_1, -+ AIR_PHY_HOST_CMD_1_MODE1); ++ /* Select mode 1, the only mode supported. ++ * Configures the SerDes for 2500Base-X with rate adaptation ++ */ ++ ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, AIR_PHY_MCU_CMD_1, ++ AIR_PHY_MCU_CMD_1_MODE1); + if (ret < 0) + return ret; -+ ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, AIR_PHY_HOST_CMD_2, -+ AIR_PHY_HOST_CMD_2_MODE1); ++ ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, AIR_PHY_MCU_CMD_2, ++ AIR_PHY_MCU_CMD_2_MODE1); + if (ret < 0) + return ret; -+ ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, AIR_PHY_HOST_CMD_3, -+ AIR_PHY_HOST_CMD_3_MODE1); ++ ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, AIR_PHY_MCU_CMD_3, ++ AIR_PHY_MCU_CMD_3_MODE1); + if (ret < 0) + return ret; -+ ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, AIR_PHY_HOST_CMD_4, -+ AIR_PHY_HOST_CMD_4_MODE1); ++ ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, AIR_PHY_MCU_CMD_4, ++ AIR_PHY_MCU_CMD_4_MODE1); + if (ret < 0) + return ret; + + /* Serdes polarity */ -+ ret = air_buckpbus_reg_read(phydev, EN8811H_POLARITY, &pbus_value); -+ if (ret < 0) -+ return ret; ++ pbus_value = 0; + if (device_property_read_bool(dev, "airoha,pnswap-rx")) + pbus_value |= EN8811H_POLARITY_RX_REVERSE; + else @@ -919,7 +945,9 @@ Signed-off-by: Eric Woudstra + pbus_value &= ~EN8811H_POLARITY_TX_NORMAL; + else + pbus_value |= EN8811H_POLARITY_TX_NORMAL; -+ ret = air_buckpbus_reg_write(phydev, EN8811H_POLARITY, pbus_value); ++ ret = air_buckpbus_reg_modify(phydev, EN8811H_POLARITY, ++ EN8811H_POLARITY_RX_REVERSE | ++ EN8811H_POLARITY_TX_NORMAL, pbus_value); + if (ret < 0) + return ret; + @@ -930,14 +958,6 @@ Signed-off-by: Eric Woudstra + return ret; + } + -+ ret = air_buckpbus_reg_read(phydev, EN8811H_GPIO_OUTPUT, &pbus_value); -+ if (ret < 0) -+ return ret; -+ pbus_value |= EN8811H_GPIO_OUTPUT_345; -+ ret = air_buckpbus_reg_write(phydev, EN8811H_GPIO_OUTPUT, pbus_value); -+ if (ret < 0) -+ return ret; -+ + return 0; +} + @@ -959,17 +979,21 @@ Signed-off-by: Eric Woudstra +static int en8811h_config_aneg(struct phy_device *phydev) +{ + bool changed = false; -+ int err, val; ++ int ret; ++ u32 adv; + -+ val = 0; -+ if (linkmode_test_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, -+ phydev->advertising)) -+ val |= MDIO_AN_10GBT_CTRL_ADV2_5G; -+ err = phy_modify_mmd_changed(phydev, MDIO_MMD_AN, MDIO_AN_10GBT_CTRL, -+ MDIO_AN_10GBT_CTRL_ADV2_5G, val); -+ if (err < 0) -+ return err; -+ if (err > 0) ++ if (phydev->autoneg == AUTONEG_DISABLE) { ++ phydev_warn(phydev, "Disabling autoneg is not supported\n"); ++ return -EINVAL; ++ } ++ ++ adv = linkmode_adv_to_mii_10gbt_adv_t(phydev->advertising); ++ ++ ret = phy_modify_mmd_changed(phydev, MDIO_MMD_AN, MDIO_AN_10GBT_CTRL, ++ MDIO_AN_10GBT_CTRL_ADV2_5G, adv); ++ if (ret < 0) ++ return ret; ++ if (ret > 0) + changed = true; + + return __genphy_config_aneg(phydev, changed); @@ -991,6 +1015,7 @@ Signed-off-by: Eric Woudstra + phydev->duplex = DUPLEX_UNKNOWN; + phydev->pause = 0; + phydev->asym_pause = 0; ++ phydev->rate_matching = RATE_MATCH_PAUSE; + + ret = genphy_read_master_slave(phydev); + if (ret < 0) @@ -1008,7 +1033,7 @@ Signed-off-by: Eric Woudstra + phydev->lp_advertising, + pbus_value & EN8811H_2P5G_LPA_2P5G); + -+ if (phydev->autoneg == AUTONEG_ENABLE && phydev->autoneg_complete) ++ if (phydev->autoneg_complete) + phy_resolve_aneg_pause(phydev); + + if (!phydev->link) @@ -1030,8 +1055,7 @@ Signed-off-by: Eric Woudstra + break; + } + -+ /* BUG in PHY firmware: MDIO_AN_10GBT_STAT_LP2_5G does not get set. -+ * Firmware before version 24011202 has no vendor register 2P5G_LPA. ++ /* Firmware before version 24011202 has no vendor register 2P5G_LPA. + * Assume link partner advertised it if connected at 2500Mbps. + */ + if (priv->firmware_version < 0x24011202) { @@ -1050,13 +1074,13 @@ Signed-off-by: Eric Woudstra +{ + int ret; + -+ ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, AIR_PHY_HOST_CMD_3, -+ AIR_PHY_HOST_CMD_3_DOCMD); ++ ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, AIR_PHY_MCU_CMD_3, ++ AIR_PHY_MCU_CMD_3_DOCMD); + if (ret < 0) + return ret; + -+ ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, AIR_PHY_HOST_CMD_4, -+ AIR_PHY_HOST_CMD_4_INTCLR); ++ ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, AIR_PHY_MCU_CMD_4, ++ AIR_PHY_MCU_CMD_4_INTCLR); + if (ret < 0) + return ret; + @@ -1105,6 +1129,7 @@ Signed-off-by: Eric Woudstra + { PHY_ID_MATCH_MODEL(EN8811H_PHY_ID) }, + { } +}; ++ +MODULE_DEVICE_TABLE(mdio, en8811h_tbl); +MODULE_FIRMWARE(EN8811H_MD32_DM); +MODULE_FIRMWARE(EN8811H_MD32_DSP); diff --git a/target/linux/generic/backport-6.6/799-v6.10-net-phy-air_en8811h-fix-some-error-codes.patch b/target/linux/generic/backport-6.6/799-v6.10-net-phy-air_en8811h-fix-some-error-codes.patch new file mode 100644 index 0000000000..1bd0eefe77 --- /dev/null +++ b/target/linux/generic/backport-6.6/799-v6.10-net-phy-air_en8811h-fix-some-error-codes.patch @@ -0,0 +1,47 @@ +From 87c33315af380ca12a2e59ac94edad4fe0481b4c Mon Sep 17 00:00:00 2001 +From: Dan Carpenter +Date: Fri, 5 Apr 2024 13:08:59 +0300 +Subject: [PATCH] net: phy: air_en8811h: fix some error codes + +These error paths accidentally return "ret" which is zero/success +instead of the correct error code. + +Fixes: 71e79430117d ("net: phy: air_en8811h: Add the Airoha EN8811H PHY driver") +Signed-off-by: Dan Carpenter +Reviewed-by: Simon Horman +Link: https://lore.kernel.org/r/7ef2e230-dfb7-4a77-8973-9e5be1a99fc2@moroto.mountain +Signed-off-by: Jakub Kicinski +--- + drivers/net/phy/air_en8811h.c | 8 ++++---- + 1 file changed, 4 insertions(+), 4 deletions(-) + +--- a/drivers/net/phy/air_en8811h.c ++++ b/drivers/net/phy/air_en8811h.c +@@ -272,11 +272,11 @@ static int __air_buckpbus_reg_read(struc + + pbus_data_high = __phy_read(phydev, AIR_BPBUS_RD_DATA_HIGH); + if (pbus_data_high < 0) +- return ret; ++ return pbus_data_high; + + pbus_data_low = __phy_read(phydev, AIR_BPBUS_RD_DATA_LOW); + if (pbus_data_low < 0) +- return ret; ++ return pbus_data_low; + + *pbus_data = pbus_data_low | (pbus_data_high << 16); + return 0; +@@ -323,11 +323,11 @@ static int __air_buckpbus_reg_modify(str + + pbus_data_high = __phy_read(phydev, AIR_BPBUS_RD_DATA_HIGH); + if (pbus_data_high < 0) +- return ret; ++ return pbus_data_high; + + pbus_data_low = __phy_read(phydev, AIR_BPBUS_RD_DATA_LOW); + if (pbus_data_low < 0) +- return ret; ++ return pbus_data_low; + + pbus_data_old = pbus_data_low | (pbus_data_high << 16); + pbus_data_new = (pbus_data_old & ~mask) | set; diff --git a/target/linux/generic/backport-6.6/832-v6.7-net-phy-amd-Support-the-Altima-AMI101L.patch b/target/linux/generic/backport-6.6/832-v6.7-net-phy-amd-Support-the-Altima-AMI101L.patch index fa2056b69a..07287206f6 100644 --- a/target/linux/generic/backport-6.6/832-v6.7-net-phy-amd-Support-the-Altima-AMI101L.patch +++ b/target/linux/generic/backport-6.6/832-v6.7-net-phy-amd-Support-the-Altima-AMI101L.patch @@ -16,8 +16,8 @@ Signed-off-by: Linus Walleij --- a/drivers/net/phy/Kconfig +++ b/drivers/net/phy/Kconfig -@@ -69,9 +69,9 @@ config SFP - comment "MII PHY device drivers" +@@ -74,9 +74,9 @@ config AIR_EN8811H_PHY + Currently supports the Airoha EN8811H PHY. config AMD_PHY - tristate "AMD PHYs" diff --git a/target/linux/generic/config-6.6 b/target/linux/generic/config-6.6 index 8a30901594..1b4625a40c 100644 --- a/target/linux/generic/config-6.6 +++ b/target/linux/generic/config-6.6 @@ -414,6 +414,7 @@ CONFIG_ARM64_SW_TTBR0_PAN=y # CONFIG_ARM_CCI_PMU is not set # CONFIG_ARM_CCN is not set # CONFIG_ARM_CMN is not set +# CONFIG_ARM_CORESIGHT_PMU_ARCH_SYSTEM_PMU is not set # CONFIG_ARM_CPUIDLE is not set CONFIG_ARM_CPU_TOPOLOGY=y # CONFIG_ARM_CRYPTO is not set @@ -465,6 +466,7 @@ CONFIG_ARM_MODULE_PLTS=y # CONFIG_ARM_SDE_INTERFACE is not set # CONFIG_ARM_SMCCC_SOC_ID is not set # CONFIG_ARM_SMC_WATCHDOG is not set +# CONFIG_ARM_SMMU_V3_PMU is not set # CONFIG_ARM_SP805_WATCHDOG is not set # CONFIG_ARM_SPE_PMU is not set # CONFIG_ARM_THUMBEE is not set diff --git a/target/linux/generic/hack-6.6/200-tools_portability.patch b/target/linux/generic/hack-6.6/200-tools_portability.patch new file mode 100644 index 0000000000..2ea8a68537 --- /dev/null +++ b/target/linux/generic/hack-6.6/200-tools_portability.patch @@ -0,0 +1,90 @@ +--- a/tools/scripts/Makefile.include ++++ b/tools/scripts/Makefile.include +@@ -72,8 +72,6 @@ $(call allow-override,CXX,$(CROSS_COMPIL + $(call allow-override,STRIP,$(CROSS_COMPILE)strip) + endif + +-CC_NO_CLANG := $(shell $(CC) -dM -E -x c /dev/null | grep -Fq "__clang__"; echo $$?) +- + ifneq ($(LLVM),) + HOSTAR ?= $(LLVM_PREFIX)llvm-ar$(LLVM_SUFFIX) + HOSTCC ?= $(LLVM_PREFIX)clang$(LLVM_SUFFIX) +@@ -84,6 +82,9 @@ HOSTCC ?= gcc + HOSTLD ?= ld + endif + ++CC_NO_CLANG := $(shell $(CC) -dM -E -x c /dev/null | grep -Fq "__clang__"; echo $$?) ++HOSTCC_NO_CLANG := $(shell $(HOSTCC) -dM -E -x c /dev/null | grep -Fq "__clang__"; echo $$?) ++ + # Some tools require Clang, LLC and/or LLVM utils + CLANG ?= clang + LLC ?= llc +@@ -92,8 +93,9 @@ LLVM_OBJCOPY ?= llvm-objcopy + LLVM_STRIP ?= llvm-strip + + ifeq ($(CC_NO_CLANG), 1) +-EXTRA_WARNINGS += -Wstrict-aliasing=3 +- ++ ifeq ($(HOSTCC_NO_CLANG), 1) ++ EXTRA_WARNINGS += -Wstrict-aliasing=3 ++ endif + else ifneq ($(CROSS_COMPILE),) + # Allow userspace to override CLANG_CROSS_FLAGS to specify their own + # sysroots and flags or to avoid the GCC call in pure Clang builds. +--- a/tools/include/linux/types.h ++++ b/tools/include/linux/types.h +@@ -56,6 +56,7 @@ typedef __s8 s8; + #define __user + #endif + #define __must_check ++#undef __cold + #define __cold + + typedef __u16 __bitwise __le16; +--- a/tools/objtool/include/objtool/objtool.h ++++ b/tools/objtool/include/objtool/objtool.h +@@ -12,6 +12,7 @@ + + #include + ++#undef __weak + #define __weak __attribute__((weak)) + + struct pv_state { +--- a/tools/include/asm-generic/bitops/fls.h ++++ b/tools/include/asm-generic/bitops/fls.h +@@ -2,6 +2,8 @@ + #ifndef _ASM_GENERIC_BITOPS_FLS_H_ + #define _ASM_GENERIC_BITOPS_FLS_H_ + ++#include ++ + /** + * fls - find last (most-significant) bit set + * @x: the word to search +@@ -10,6 +12,7 @@ + * Note fls(0) = 0, fls(1) = 1, fls(0x80000000) = 32. + */ + ++#define fls __linux_fls + static __always_inline int fls(unsigned int x) + { + int r = 32; +--- a/tools/lib/string.c ++++ b/tools/lib/string.c +@@ -96,6 +96,7 @@ int strtobool(const char *s, bool *res) + * If libc has strlcpy() then that version will override this + * implementation: + */ ++#ifndef __APPLE__ + #ifdef __clang__ + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wignored-attributes" +@@ -114,6 +115,7 @@ size_t __weak strlcpy(char *dest, const + #ifdef __clang__ + #pragma clang diagnostic pop + #endif ++#endif + + /** + * skip_spaces - Removes leading whitespace from @str. diff --git a/target/linux/generic/pending-6.1/742-net-phy-air_en8811h-reset-netdev-rules-when-LED-is-s.patch b/target/linux/generic/pending-6.1/742-net-phy-air_en8811h-reset-netdev-rules-when-LED-is-s.patch new file mode 100644 index 0000000000..500567b4ed --- /dev/null +++ b/target/linux/generic/pending-6.1/742-net-phy-air_en8811h-reset-netdev-rules-when-LED-is-s.patch @@ -0,0 +1,45 @@ +From 9be9a00adfac8118b6d685e71696f83187308c66 Mon Sep 17 00:00:00 2001 +Message-ID: <9be9a00adfac8118b6d685e71696f83187308c66.1715125851.git.daniel@makrotopia.org> +From: Daniel Golle +Date: Tue, 7 May 2024 22:43:30 +0100 +Subject: [PATCH net] net: phy: air_en8811h: reset netdev rules when LED is set + manually +To: Andrew Lunn , + Heiner Kallweit , + Russell King , + David S. Miller , + Eric Dumazet , + Jakub Kicinski , + Paolo Abeni , + SkyLake Huang , + Eric Woudstra , + netdev@vger.kernel.org, + linux-kernel@vger.kernel.org + +Setting LED_OFF via the brightness_set should deactivate hw control, +so make sure netdev trigger rules also get cleared in that case. + +Fixes: 71e79430117d ("net: phy: air_en8811h: Add the Airoha EN8811H PHY driver") +Signed-off-by: Daniel Golle +--- +This is basically a stop-gap measure until unified LED handling has +been implemented accross all MediaTek and Airoha PHYs. +See also +https://patchwork.kernel.org/project/netdevbpf/patch/20240425023325.15586-3-SkyLake.Huang@mediatek.com/ + + drivers/net/phy/air_en8811h.c | 4 ++++ + 1 file changed, 4 insertions(+) + +--- a/drivers/net/phy/air_en8811h.c ++++ b/drivers/net/phy/air_en8811h.c +@@ -544,6 +544,10 @@ static int air_hw_led_on_set(struct phy_ + + changed |= (priv->led[index].rules != 0); + ++ /* clear netdev trigger rules in case LED_OFF has been set */ ++ if (!on) ++ priv->led[index].rules = 0; ++ + if (changed) + return phy_modify_mmd(phydev, MDIO_MMD_VEND2, + AIR_PHY_LED_ON(index), diff --git a/target/linux/generic/pending-6.1/795-01-net-dsa-mt7530-do-not-set-MT7530_P5_DIS-when-PHY-.patch b/target/linux/generic/pending-6.1/795-01-net-dsa-mt7530-do-not-set-MT7530_P5_DIS-when-PHY-.patch deleted file mode 100644 index 04d8c8760f..0000000000 --- a/target/linux/generic/pending-6.1/795-01-net-dsa-mt7530-do-not-set-MT7530_P5_DIS-when-PHY-.patch +++ /dev/null @@ -1,79 +0,0 @@ -From patchwork Sat Apr 27 11:24:42 2024 -Content-Type: text/plain; charset="utf-8" -MIME-Version: 1.0 -Content-Transfer-Encoding: 8bit -X-Patchwork-Submitter: =?utf-8?b?QXLEsW7DpyDDnE5BTCB2aWEgQjQgUmVsYXk=?= - -X-Patchwork-Id: 13645655 -From: =?utf-8?b?QXLEsW7DpyDDnE5BTCB2aWEgQjQgUmVsYXk=?= - -Date: Sat, 27 Apr 2024 14:24:42 +0300 -Subject: [PATCH net-next] net: dsa: mt7530: do not set MT7530_P5_DIS when - PHY muxing is being used -Precedence: bulk -X-Mailing-List: netdev@vger.kernel.org -List-Id: -List-Subscribe: -List-Unsubscribe: -MIME-Version: 1.0 -Message-Id: - <20240427-for-netnext-mt7530-do-not-disable-port5-when-phy-muxing-v1-1-793cdf9d7707@arinc9.com> -To: Daniel Golle , DENG Qingfang , - Sean Wang , Andrew Lunn , - Florian Fainelli , - Vladimir Oltean , - "David S. Miller" , Eric Dumazet , - Jakub Kicinski , Paolo Abeni , - Matthias Brugger , - AngeloGioacchino Del Regno -Cc: netdev@vger.kernel.org, linux-kernel@vger.kernel.org, - linux-arm-kernel@lists.infradead.org, linux-mediatek@lists.infradead.org, - =?utf-8?b?QXLEsW7DpyDDnE5BTA==?= -X-Mailer: b4 0.13.0 -X-Patchwork-Delegate: kuba@kernel.org - -From: Arınç ÜNAL - -When the PHY muxing feature is in use, port 5 won't be defined in the -device tree. Because of this, the type member of the dsa_port structure for -this port will be assigned DSA_PORT_TYPE_UNUSED. The dsa_port_setup() -function calls ds->ops->port_disable() when the port type is -DSA_PORT_TYPE_UNUSED. - -The MT7530_P5_DIS bit is unset when PHY muxing is being used. -mt7530_port_disable() which is assigned to ds->ops->port_disable() is -called afterwards. Currently, mt7530_port_disable() sets MT7530_P5_DIS -which breaks network connectivity when PHY muxing is being used. - -Therefore, do not set MT7530_P5_DIS when PHY muxing is being used. - -Fixes: 377174c5760c ("net: dsa: mt7530: move MT753X_MTRAP operations for MT7530") -Reported-by: Daniel Golle -Signed-off-by: Arınç ÜNAL ---- -Hello. - -I've sent this to net-next as the patch it fixes is on the current -development cycle. ---- - drivers/net/dsa/mt7530.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - - ---- -base-commit: 5c4c0edca68a5841a8d53ccd49596fe199c8334c -change-id: 20240427-for-netnext-mt7530-do-not-disable-port5-when-phy-muxing-7ff5fd0995d7 - -Best regards, - ---- a/drivers/net/dsa/mt7530.c -+++ b/drivers/net/dsa/mt7530.c -@@ -1227,7 +1227,7 @@ mt7530_port_disable(struct dsa_switch *d - if (priv->id != ID_MT7530 && priv->id != ID_MT7621) - return; - -- if (port == 5) -+ if (port == 5 && priv->p5_mode == GMAC5) - mt7530_set(priv, MT753X_MTRAP, MT7530_P5_DIS); - else if (port == 6) - mt7530_set(priv, MT753X_MTRAP, MT7530_P6_DIS); diff --git a/target/linux/generic/pending-6.1/796-net-dsa-mt7530-detect-PHY-muxing-when-PHY-is-defined-on-switch-MDIO-bus.patch b/target/linux/generic/pending-6.1/796-net-dsa-mt7530-detect-PHY-muxing-when-PHY-is-defined-on-switch-MDIO-bus.patch deleted file mode 100644 index 5db834e23f..0000000000 --- a/target/linux/generic/pending-6.1/796-net-dsa-mt7530-detect-PHY-muxing-when-PHY-is-defined-on-switch-MDIO-bus.patch +++ /dev/null @@ -1,136 +0,0 @@ -From patchwork Tue Apr 30 05:01:33 2024 -Content-Type: text/plain; charset="utf-8" -MIME-Version: 1.0 -Content-Transfer-Encoding: 8bit -X-Patchwork-Submitter: =?utf-8?b?QXLEsW7DpyDDnE5BTCB2aWEgQjQgUmVsYXk=?= - -X-Patchwork-Id: 13648264 -X-Patchwork-Delegate: kuba@kernel.org -Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org - [10.30.226.201]) - (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) - (No client certificate requested) - by smtp.subspace.kernel.org (Postfix) with ESMTPS id 4C9C114A89; - Tue, 30 Apr 2024 05:01:39 +0000 (UTC) -Authentication-Results: smtp.subspace.kernel.org; - arc=none smtp.client-ip=10.30.226.201 -ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; - t=1714453299; cv=none; - b=gV4Z0elIASLrrICjPPmDeR0kBaXtdjeqbz/cnj3/0V74cRGmjd5sMQ4PtMYq5iPdJkWbhn4mzf/WX9xcqituDcVV7Vj68zrsE5d6NavvrMK9kf7Ef3Yyr8gEbekALfL9fKuF6ul7TeVFQiFoGQyAJNFzB9YAiQGJlWzw98bldMQ= -ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; - s=arc-20240116; t=1714453299; c=relaxed/simple; - bh=GGkybB3RbZ4yacytPZCe3ceKcaWca6ygWTw/PJtmpsk=; - h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:To:Cc; - b=QRWD6k4Qg1t5nZj6oj2xdwWDCGHQWHG2xj0lkYcEMm3dMkvPpLbCIOptpZBJtSq06TMxRjVJhgVJ9ATDTIYGKwCHJTx3JTxspI+YkxLsXsfnz9jNxMyQ/+CO3xzRjTuKg0mGP3fl1Q1xznm/8cenWMDUOrv/p1Wlg1XZ8s01edY= -ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; - dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org - header.b=LTwGL2cB; arc=none smtp.client-ip=10.30.226.201 -Authentication-Results: smtp.subspace.kernel.org; - dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org - header.b="LTwGL2cB" -Received: by smtp.kernel.org (Postfix) with ESMTPS id 1A3AEC2BBFC; - Tue, 30 Apr 2024 05:01:39 +0000 (UTC) -DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; - s=k20201202; t=1714453299; - bh=GGkybB3RbZ4yacytPZCe3ceKcaWca6ygWTw/PJtmpsk=; - h=From:Date:Subject:To:Cc:Reply-To:From; - b=LTwGL2cBtvmG8vpW/5yPEkA2A4EWbBIHkpxGRp6NhmQcwKx6T+Q4Gt/MKTUdGZ6pp - FHxkNOtF/KeqTZc814r9H7gtR+6rzRBCcQfWYl2TIdj+1edX/UrwUARQa8CQYwWK3V - jqfD9pCOCm+hptOHs6o0+j5FaW5TtN6QJTG/1GpftEfJkQYpsp/jEL28MY35u99DBK - yZErlS77MlNQEMScOR7McNtMj0pYnTvgrZLefdORzeWQhX6REODGKFL2xoSWjtg9jw - QeQUp07wKwtuwHpKI07IBsFwIsclZYD3/oXrjBSSZmvwHCCvAYT+PXRiH0moLzHERn - aa8XczXBSlBVw== -Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org - (localhost.localdomain [127.0.0.1]) - by smtp.lore.kernel.org (Postfix) with ESMTP id 04CA2C25B10; - Tue, 30 Apr 2024 05:01:39 +0000 (UTC) -From: =?utf-8?b?QXLEsW7DpyDDnE5BTCB2aWEgQjQgUmVsYXk=?= - -Date: Tue, 30 Apr 2024 08:01:33 +0300 -Subject: [PATCH net-next v2] net: dsa: mt7530: detect PHY muxing when PHY - is defined on switch MDIO bus -Precedence: bulk -X-Mailing-List: netdev@vger.kernel.org -List-Id: -List-Subscribe: -List-Unsubscribe: -MIME-Version: 1.0 -Message-Id: - <20240430-b4-for-netnext-mt7530-use-switch-mdio-bus-for-phy-muxing-v2-1-9104d886d0db@arinc9.com> -X-B4-Tracking: v=1; b=H4sIACx7MGYC/6WOSw6DMBBDr1LNulNBIHy66j0qFhAGmAUJSgIFI - e7eNFeovLJsPfsER5bJwfN2gqWNHRsdjLjfQE2tHgm5Dx5EIvIkFzV2OQ7Goiavafc4+1JmCa6 - O0H3Yqwnnng12q4u1ZTpwXnfWI8qqEEWdlamSEgJ+sTTwHqffEHD440ETkomdN/aIn7Y05v/Pb - ykGDWUp6yqjVMpXa1mr+qHMDM11XV8dryM7CwEAAA== -To: Daniel Golle , DENG Qingfang , - Sean Wang , Andrew Lunn , - Florian Fainelli , - Vladimir Oltean , - "David S. Miller" , Eric Dumazet , - Jakub Kicinski , Paolo Abeni , - Matthias Brugger , - AngeloGioacchino Del Regno -Cc: Bartel Eerdekens , - mithat.guner@xeront.com, erkin.bozoglu@xeront.com, netdev@vger.kernel.org, - linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, - linux-mediatek@lists.infradead.org, - =?utf-8?b?QXLEsW7DpyDDnE5BTA==?= -X-Mailer: b4 0.13.0 -X-Developer-Signature: v=1; a=ed25519-sha256; t=1714453297; l=1949; - i=arinc.unal@arinc9.com; s=arinc9-PC; h=from:subject:message-id; - bh=c3IRARdnxa6x5otHszH4xrnla2RxJAal1114ej/d2wE=; - b=FL4WEHh4zYu1gBE7wbaN+X2OMCOIMJVsYBkXurHM0IC3CnI6XfpKE1V5QLUSXby75WZfvQ0se - lrMQos/eOAaCNbkyxUkmwb3opbC915iywMECA0lv/g0IAo6snRYzMae -X-Developer-Key: i=arinc.unal@arinc9.com; a=ed25519; - pk=Bd1s2kQtNfZAWyeLHg39jaWBDqt8Ud1WJXLFh7gxl20= -X-Endpoint-Received: by B4 Relay for arinc.unal@arinc9.com/arinc9-PC with - auth_id=158 -X-Original-From: =?utf-8?b?QXLEsW7DpyDDnE5BTA==?= -Reply-To: arinc.unal@arinc9.com -X-Patchwork-Delegate: kuba@kernel.org - -From: Arınç ÜNAL - -Currently, the MT7530 DSA subdriver configures the MT7530 switch to provide -direct access to switch PHYs, meaning, the switch PHYs listen on the MDIO -bus the switch listens on. The PHY muxing feature makes use of this. - -This is problematic as the PHY may be attached before the switch is -initialised, in which case, the PHY will fail to be attached. - -Since commit 91374ba537bd ("net: dsa: mt7530: support OF-based registration -of switch MDIO bus"), we can describe the switch PHYs on the MDIO bus of -the switch on the device tree. Extend the check to detect PHY muxing when -the PHY is defined on the MDIO bus of the switch on the device tree. - -When the PHY is described this way, the switch will be initialised first, -then the switch MDIO bus will be registered. Only after these steps, the -PHY will be attached. - -Signed-off-by: Arınç ÜNAL ---- -Changes in v2: -- Address the terminology on the patch log. -- Link to v1: https://lore.kernel.org/r/20240429-b4-for-netnext-mt7530-use-switch-mdio-bus-for-phy-muxing-v1-1-1f775983e155@arinc9.com ---- - drivers/net/dsa/mt7530.c | 3 ++- - 1 file changed, 2 insertions(+), 1 deletion(-) - - ---- -base-commit: 5c4c0edca68a5841a8d53ccd49596fe199c8334c -change-id: 20240429-b4-for-netnext-mt7530-use-switch-mdio-bus-for-phy-muxing-586269371c55 - -Best regards, - ---- a/drivers/net/dsa/mt7530.c -+++ b/drivers/net/dsa/mt7530.c -@@ -2488,7 +2488,8 @@ mt7530_setup(struct dsa_switch *ds) - if (!phy_node) - continue; - -- if (phy_node->parent == priv->dev->of_node->parent) { -+ if (phy_node->parent == priv->dev->of_node->parent || -+ phy_node->parent->parent == priv->dev->of_node) { - ret = of_get_phy_mode(mac_np, &interface); - if (ret && ret != -ENODEV) { - of_node_put(mac_np); diff --git a/target/linux/generic/pending-6.6/360-selftests-bpf-portability-of-unprivileged-tests.patch b/target/linux/generic/pending-6.6/360-selftests-bpf-portability-of-unprivileged-tests.patch new file mode 100644 index 0000000000..0f28834d48 --- /dev/null +++ b/target/linux/generic/pending-6.6/360-selftests-bpf-portability-of-unprivileged-tests.patch @@ -0,0 +1,26 @@ +From ecb8f9a7d69698ce20fc6f4d107718d56fa861df Mon Sep 17 00:00:00 2001 +From: Tony Ambardar +Date: Sat, 9 Mar 2024 16:44:53 -0800 +Subject: [PATCH] selftests/bpf: Improve portability of unprivileged tests + +The addition of general support for unprivileged tests in test_loader.c +breaks building test_verifier on non-glibc (e.g. musl) systems, due to the +inclusion of glibc extension '' in 'unpriv_helpers.c'. However, +the header is actually not needed, so remove it to restore building. + +Fixes: 1d56ade032a4 ("selftests/bpf: Unprivileged tests for test_loader.c") +Signed-off-by: Tony Ambardar +--- + tools/testing/selftests/bpf/unpriv_helpers.c | 1 - + 1 file changed, 1 deletion(-) + +--- a/tools/testing/selftests/bpf/unpriv_helpers.c ++++ b/tools/testing/selftests/bpf/unpriv_helpers.c +@@ -2,7 +2,6 @@ + + #include + #include +-#include + #include + + #include "unpriv_helpers.h" diff --git a/target/linux/generic/pending-6.6/742-net-phy-air_en8811h-reset-netdev-rules-when-LED-is-s.patch b/target/linux/generic/pending-6.6/742-net-phy-air_en8811h-reset-netdev-rules-when-LED-is-s.patch new file mode 100644 index 0000000000..500567b4ed --- /dev/null +++ b/target/linux/generic/pending-6.6/742-net-phy-air_en8811h-reset-netdev-rules-when-LED-is-s.patch @@ -0,0 +1,45 @@ +From 9be9a00adfac8118b6d685e71696f83187308c66 Mon Sep 17 00:00:00 2001 +Message-ID: <9be9a00adfac8118b6d685e71696f83187308c66.1715125851.git.daniel@makrotopia.org> +From: Daniel Golle +Date: Tue, 7 May 2024 22:43:30 +0100 +Subject: [PATCH net] net: phy: air_en8811h: reset netdev rules when LED is set + manually +To: Andrew Lunn , + Heiner Kallweit , + Russell King , + David S. Miller , + Eric Dumazet , + Jakub Kicinski , + Paolo Abeni , + SkyLake Huang , + Eric Woudstra , + netdev@vger.kernel.org, + linux-kernel@vger.kernel.org + +Setting LED_OFF via the brightness_set should deactivate hw control, +so make sure netdev trigger rules also get cleared in that case. + +Fixes: 71e79430117d ("net: phy: air_en8811h: Add the Airoha EN8811H PHY driver") +Signed-off-by: Daniel Golle +--- +This is basically a stop-gap measure until unified LED handling has +been implemented accross all MediaTek and Airoha PHYs. +See also +https://patchwork.kernel.org/project/netdevbpf/patch/20240425023325.15586-3-SkyLake.Huang@mediatek.com/ + + drivers/net/phy/air_en8811h.c | 4 ++++ + 1 file changed, 4 insertions(+) + +--- a/drivers/net/phy/air_en8811h.c ++++ b/drivers/net/phy/air_en8811h.c +@@ -544,6 +544,10 @@ static int air_hw_led_on_set(struct phy_ + + changed |= (priv->led[index].rules != 0); + ++ /* clear netdev trigger rules in case LED_OFF has been set */ ++ if (!on) ++ priv->led[index].rules = 0; ++ + if (changed) + return phy_modify_mmd(phydev, MDIO_MMD_VEND2, + AIR_PHY_LED_ON(index), diff --git a/target/linux/generic/pending-6.6/795-01-net-dsa-mt7530-do-not-set-MT7530_P5_DIS-when-PHY-.patch b/target/linux/generic/pending-6.6/795-01-net-dsa-mt7530-do-not-set-MT7530_P5_DIS-when-PHY-.patch deleted file mode 100644 index 286c96e43b..0000000000 --- a/target/linux/generic/pending-6.6/795-01-net-dsa-mt7530-do-not-set-MT7530_P5_DIS-when-PHY-.patch +++ /dev/null @@ -1,79 +0,0 @@ -From patchwork Sat Apr 27 11:24:42 2024 -Content-Type: text/plain; charset="utf-8" -MIME-Version: 1.0 -Content-Transfer-Encoding: 8bit -X-Patchwork-Submitter: =?utf-8?b?QXLEsW7DpyDDnE5BTCB2aWEgQjQgUmVsYXk=?= - -X-Patchwork-Id: 13645655 -From: =?utf-8?b?QXLEsW7DpyDDnE5BTCB2aWEgQjQgUmVsYXk=?= - -Date: Sat, 27 Apr 2024 14:24:42 +0300 -Subject: [PATCH net-next] net: dsa: mt7530: do not set MT7530_P5_DIS when - PHY muxing is being used -Precedence: bulk -X-Mailing-List: netdev@vger.kernel.org -List-Id: -List-Subscribe: -List-Unsubscribe: -MIME-Version: 1.0 -Message-Id: - <20240427-for-netnext-mt7530-do-not-disable-port5-when-phy-muxing-v1-1-793cdf9d7707@arinc9.com> -To: Daniel Golle , DENG Qingfang , - Sean Wang , Andrew Lunn , - Florian Fainelli , - Vladimir Oltean , - "David S. Miller" , Eric Dumazet , - Jakub Kicinski , Paolo Abeni , - Matthias Brugger , - AngeloGioacchino Del Regno -Cc: netdev@vger.kernel.org, linux-kernel@vger.kernel.org, - linux-arm-kernel@lists.infradead.org, linux-mediatek@lists.infradead.org, - =?utf-8?b?QXLEsW7DpyDDnE5BTA==?= -X-Mailer: b4 0.13.0 -X-Patchwork-Delegate: kuba@kernel.org - -From: Arınç ÜNAL - -When the PHY muxing feature is in use, port 5 won't be defined in the -device tree. Because of this, the type member of the dsa_port structure for -this port will be assigned DSA_PORT_TYPE_UNUSED. The dsa_port_setup() -function calls ds->ops->port_disable() when the port type is -DSA_PORT_TYPE_UNUSED. - -The MT7530_P5_DIS bit is unset when PHY muxing is being used. -mt7530_port_disable() which is assigned to ds->ops->port_disable() is -called afterwards. Currently, mt7530_port_disable() sets MT7530_P5_DIS -which breaks network connectivity when PHY muxing is being used. - -Therefore, do not set MT7530_P5_DIS when PHY muxing is being used. - -Fixes: 377174c5760c ("net: dsa: mt7530: move MT753X_MTRAP operations for MT7530") -Reported-by: Daniel Golle -Signed-off-by: Arınç ÜNAL ---- -Hello. - -I've sent this to net-next as the patch it fixes is on the current -development cycle. ---- - drivers/net/dsa/mt7530.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - - ---- -base-commit: 5c4c0edca68a5841a8d53ccd49596fe199c8334c -change-id: 20240427-for-netnext-mt7530-do-not-disable-port5-when-phy-muxing-7ff5fd0995d7 - -Best regards, - ---- a/drivers/net/dsa/mt7530.c -+++ b/drivers/net/dsa/mt7530.c -@@ -1220,7 +1220,7 @@ mt7530_port_disable(struct dsa_switch *d - if (priv->id != ID_MT7530 && priv->id != ID_MT7621) - return; - -- if (port == 5) -+ if (port == 5 && priv->p5_mode == GMAC5) - mt7530_set(priv, MT753X_MTRAP, MT7530_P5_DIS); - else if (port == 6) - mt7530_set(priv, MT753X_MTRAP, MT7530_P6_DIS); diff --git a/target/linux/generic/pending-6.6/796-net-dsa-mt7530-detect-PHY-muxing-when-PHY-is-defined-on-switch-MDIO-bus.patch b/target/linux/generic/pending-6.6/796-net-dsa-mt7530-detect-PHY-muxing-when-PHY-is-defined-on-switch-MDIO-bus.patch deleted file mode 100644 index f181be933a..0000000000 --- a/target/linux/generic/pending-6.6/796-net-dsa-mt7530-detect-PHY-muxing-when-PHY-is-defined-on-switch-MDIO-bus.patch +++ /dev/null @@ -1,136 +0,0 @@ -From patchwork Tue Apr 30 05:01:33 2024 -Content-Type: text/plain; charset="utf-8" -MIME-Version: 1.0 -Content-Transfer-Encoding: 8bit -X-Patchwork-Submitter: =?utf-8?b?QXLEsW7DpyDDnE5BTCB2aWEgQjQgUmVsYXk=?= - -X-Patchwork-Id: 13648264 -X-Patchwork-Delegate: kuba@kernel.org -Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org - [10.30.226.201]) - (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) - (No client certificate requested) - by smtp.subspace.kernel.org (Postfix) with ESMTPS id 4C9C114A89; - Tue, 30 Apr 2024 05:01:39 +0000 (UTC) -Authentication-Results: smtp.subspace.kernel.org; - arc=none smtp.client-ip=10.30.226.201 -ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; - t=1714453299; cv=none; - b=gV4Z0elIASLrrICjPPmDeR0kBaXtdjeqbz/cnj3/0V74cRGmjd5sMQ4PtMYq5iPdJkWbhn4mzf/WX9xcqituDcVV7Vj68zrsE5d6NavvrMK9kf7Ef3Yyr8gEbekALfL9fKuF6ul7TeVFQiFoGQyAJNFzB9YAiQGJlWzw98bldMQ= -ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; - s=arc-20240116; t=1714453299; c=relaxed/simple; - bh=GGkybB3RbZ4yacytPZCe3ceKcaWca6ygWTw/PJtmpsk=; - h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:To:Cc; - b=QRWD6k4Qg1t5nZj6oj2xdwWDCGHQWHG2xj0lkYcEMm3dMkvPpLbCIOptpZBJtSq06TMxRjVJhgVJ9ATDTIYGKwCHJTx3JTxspI+YkxLsXsfnz9jNxMyQ/+CO3xzRjTuKg0mGP3fl1Q1xznm/8cenWMDUOrv/p1Wlg1XZ8s01edY= -ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; - dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org - header.b=LTwGL2cB; arc=none smtp.client-ip=10.30.226.201 -Authentication-Results: smtp.subspace.kernel.org; - dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org - header.b="LTwGL2cB" -Received: by smtp.kernel.org (Postfix) with ESMTPS id 1A3AEC2BBFC; - Tue, 30 Apr 2024 05:01:39 +0000 (UTC) -DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; - s=k20201202; t=1714453299; - bh=GGkybB3RbZ4yacytPZCe3ceKcaWca6ygWTw/PJtmpsk=; - h=From:Date:Subject:To:Cc:Reply-To:From; - b=LTwGL2cBtvmG8vpW/5yPEkA2A4EWbBIHkpxGRp6NhmQcwKx6T+Q4Gt/MKTUdGZ6pp - FHxkNOtF/KeqTZc814r9H7gtR+6rzRBCcQfWYl2TIdj+1edX/UrwUARQa8CQYwWK3V - jqfD9pCOCm+hptOHs6o0+j5FaW5TtN6QJTG/1GpftEfJkQYpsp/jEL28MY35u99DBK - yZErlS77MlNQEMScOR7McNtMj0pYnTvgrZLefdORzeWQhX6REODGKFL2xoSWjtg9jw - QeQUp07wKwtuwHpKI07IBsFwIsclZYD3/oXrjBSSZmvwHCCvAYT+PXRiH0moLzHERn - aa8XczXBSlBVw== -Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org - (localhost.localdomain [127.0.0.1]) - by smtp.lore.kernel.org (Postfix) with ESMTP id 04CA2C25B10; - Tue, 30 Apr 2024 05:01:39 +0000 (UTC) -From: =?utf-8?b?QXLEsW7DpyDDnE5BTCB2aWEgQjQgUmVsYXk=?= - -Date: Tue, 30 Apr 2024 08:01:33 +0300 -Subject: [PATCH net-next v2] net: dsa: mt7530: detect PHY muxing when PHY - is defined on switch MDIO bus -Precedence: bulk -X-Mailing-List: netdev@vger.kernel.org -List-Id: -List-Subscribe: -List-Unsubscribe: -MIME-Version: 1.0 -Message-Id: - <20240430-b4-for-netnext-mt7530-use-switch-mdio-bus-for-phy-muxing-v2-1-9104d886d0db@arinc9.com> -X-B4-Tracking: v=1; b=H4sIACx7MGYC/6WOSw6DMBBDr1LNulNBIHy66j0qFhAGmAUJSgIFI - e7eNFeovLJsPfsER5bJwfN2gqWNHRsdjLjfQE2tHgm5Dx5EIvIkFzV2OQ7Goiavafc4+1JmCa6 - O0H3Yqwnnng12q4u1ZTpwXnfWI8qqEEWdlamSEgJ+sTTwHqffEHD440ETkomdN/aIn7Y05v/Pb - ykGDWUp6yqjVMpXa1mr+qHMDM11XV8dryM7CwEAAA== -To: Daniel Golle , DENG Qingfang , - Sean Wang , Andrew Lunn , - Florian Fainelli , - Vladimir Oltean , - "David S. Miller" , Eric Dumazet , - Jakub Kicinski , Paolo Abeni , - Matthias Brugger , - AngeloGioacchino Del Regno -Cc: Bartel Eerdekens , - mithat.guner@xeront.com, erkin.bozoglu@xeront.com, netdev@vger.kernel.org, - linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, - linux-mediatek@lists.infradead.org, - =?utf-8?b?QXLEsW7DpyDDnE5BTA==?= -X-Mailer: b4 0.13.0 -X-Developer-Signature: v=1; a=ed25519-sha256; t=1714453297; l=1949; - i=arinc.unal@arinc9.com; s=arinc9-PC; h=from:subject:message-id; - bh=c3IRARdnxa6x5otHszH4xrnla2RxJAal1114ej/d2wE=; - b=FL4WEHh4zYu1gBE7wbaN+X2OMCOIMJVsYBkXurHM0IC3CnI6XfpKE1V5QLUSXby75WZfvQ0se - lrMQos/eOAaCNbkyxUkmwb3opbC915iywMECA0lv/g0IAo6snRYzMae -X-Developer-Key: i=arinc.unal@arinc9.com; a=ed25519; - pk=Bd1s2kQtNfZAWyeLHg39jaWBDqt8Ud1WJXLFh7gxl20= -X-Endpoint-Received: by B4 Relay for arinc.unal@arinc9.com/arinc9-PC with - auth_id=158 -X-Original-From: =?utf-8?b?QXLEsW7DpyDDnE5BTA==?= -Reply-To: arinc.unal@arinc9.com -X-Patchwork-Delegate: kuba@kernel.org - -From: Arınç ÜNAL - -Currently, the MT7530 DSA subdriver configures the MT7530 switch to provide -direct access to switch PHYs, meaning, the switch PHYs listen on the MDIO -bus the switch listens on. The PHY muxing feature makes use of this. - -This is problematic as the PHY may be attached before the switch is -initialised, in which case, the PHY will fail to be attached. - -Since commit 91374ba537bd ("net: dsa: mt7530: support OF-based registration -of switch MDIO bus"), we can describe the switch PHYs on the MDIO bus of -the switch on the device tree. Extend the check to detect PHY muxing when -the PHY is defined on the MDIO bus of the switch on the device tree. - -When the PHY is described this way, the switch will be initialised first, -then the switch MDIO bus will be registered. Only after these steps, the -PHY will be attached. - -Signed-off-by: Arınç ÜNAL ---- -Changes in v2: -- Address the terminology on the patch log. -- Link to v1: https://lore.kernel.org/r/20240429-b4-for-netnext-mt7530-use-switch-mdio-bus-for-phy-muxing-v1-1-1f775983e155@arinc9.com ---- - drivers/net/dsa/mt7530.c | 3 ++- - 1 file changed, 2 insertions(+), 1 deletion(-) - - ---- -base-commit: 5c4c0edca68a5841a8d53ccd49596fe199c8334c -change-id: 20240429-b4-for-netnext-mt7530-use-switch-mdio-bus-for-phy-muxing-586269371c55 - -Best regards, - ---- a/drivers/net/dsa/mt7530.c -+++ b/drivers/net/dsa/mt7530.c -@@ -2483,7 +2483,8 @@ mt7530_setup(struct dsa_switch *ds) - if (!phy_node) - continue; - -- if (phy_node->parent == priv->dev->of_node->parent) { -+ if (phy_node->parent == priv->dev->of_node->parent || -+ phy_node->parent->parent == priv->dev->of_node) { - ret = of_get_phy_mode(mac_np, &interface); - if (ret && ret != -ENODEV) { - of_node_put(mac_np); diff --git a/target/linux/ipq40xx/base-files/etc/board.d/02_network b/target/linux/ipq40xx/base-files/etc/board.d/02_network index e86d24fab5..df0fca7544 100644 --- a/target/linux/ipq40xx/base-files/etc/board.d/02_network +++ b/target/linux/ipq40xx/base-files/etc/board.d/02_network @@ -52,6 +52,7 @@ ipq40xx_setup_interfaces() aruba,ap-365|\ avm,fritzrepeater-1200|\ dlink,dap-2610|\ + engenius,eap1300|\ extreme-networks,ws-ap3915i|\ meraki,mr33|\ meraki,mr74|\ diff --git a/target/linux/ipq40xx/config-6.6 b/target/linux/ipq40xx/config-6.6 index 22101d7df0..52ac1a585e 100644 --- a/target/linux/ipq40xx/config-6.6 +++ b/target/linux/ipq40xx/config-6.6 @@ -330,6 +330,7 @@ CONFIG_NVMEM=y CONFIG_NVMEM_QCOM_QFPROM=y # CONFIG_NVMEM_QCOM_SEC_QFPROM is not set # CONFIG_NVMEM_SPMI_SDAM is not set +CONFIG_NVMEM_U_BOOT_ENV=y CONFIG_NVMEM_SYSFS=y CONFIG_OF=y CONFIG_OF_ADDRESS=y diff --git a/target/linux/ipq40xx/files-6.6/arch/arm/boot/dts/qcom/qcom-ipq4018-ap120c-ac.dts b/target/linux/ipq40xx/files-6.6/arch/arm/boot/dts/qcom/qcom-ipq4018-ap120c-ac.dts index ceaa1edd45..07262f9ed5 100644 --- a/target/linux/ipq40xx/files-6.6/arch/arm/boot/dts/qcom/qcom-ipq4018-ap120c-ac.dts +++ b/target/linux/ipq40xx/files-6.6/arch/arm/boot/dts/qcom/qcom-ipq4018-ap120c-ac.dts @@ -98,19 +98,6 @@ qcom,wifi_noc_memtype_m0_m2 = ; }; - usb2@60f8800 { - status = "okay"; - }; - - usb3@8af8800 { - status = "okay"; - - dwc3@8a00000 { - phys = <&usb3_hs_phy>; - phy-names = "usb2-phy"; - }; - }; - crypto@8e3a000 { status = "okay"; }; @@ -323,10 +310,23 @@ status = "okay"; }; +&usb2 { + status = "okay"; +}; + &usb3_hs_phy { status = "okay"; }; +&usb3 { + status = "okay"; +}; + +&usb3_dwc { + phys = <&usb3_hs_phy>; + phy-names = "usb2-phy"; +}; + &gmac { status = "okay"; }; diff --git a/target/linux/ipq40xx/files-6.6/arch/arm/boot/dts/qcom/qcom-ipq4018-ea6350v3.dts b/target/linux/ipq40xx/files-6.6/arch/arm/boot/dts/qcom/qcom-ipq4018-ea6350v3.dts index 50e7f3d4e0..396d4e5fea 100644 --- a/target/linux/ipq40xx/files-6.6/arch/arm/boot/dts/qcom/qcom-ipq4018-ea6350v3.dts +++ b/target/linux/ipq40xx/files-6.6/arch/arm/boot/dts/qcom/qcom-ipq4018-ea6350v3.dts @@ -50,14 +50,6 @@ qcom,wifi_noc_memtype_m0_m2 = ; }; - usb2@60f8800 { - status = "okay"; - }; - - usb3@8af8800 { - status = "okay"; - }; - crypto@8e3a000 { status = "okay"; }; @@ -307,6 +299,14 @@ status = "okay"; }; +&usb3 { + status = "okay"; +}; + &usb2_hs_phy { status = "okay"; }; + +&usb2 { + status = "okay"; +}; diff --git a/target/linux/ipq40xx/files-6.6/arch/arm/boot/dts/qcom/qcom-ipq4018-eap1300.dts b/target/linux/ipq40xx/files-6.6/arch/arm/boot/dts/qcom/qcom-ipq4018-eap1300.dts index e9d4775fd8..bfbed30b90 100644 --- a/target/linux/ipq40xx/files-6.6/arch/arm/boot/dts/qcom/qcom-ipq4018-eap1300.dts +++ b/target/linux/ipq40xx/files-6.6/arch/arm/boot/dts/qcom/qcom-ipq4018-eap1300.dts @@ -200,8 +200,13 @@ reg = <0x190000 0x1dc0000>; }; partition9@1f50000 { + compatible = "u-boot,env"; label = "u-boot-env"; reg = <0x01f50000 0x00010000>; + + macaddr_ubootenv_ethaddr: ethaddr { + #nvmem-cell-cells = <1>; + }; }; partition10@1f60000 { label = "userconfig"; @@ -221,16 +226,35 @@ status = "okay"; }; +&switch { + status = "okay"; +}; + +&swport5 { + status = "okay"; + label = "lan"; + nvmem-cell-names = "mac-address"; + nvmem-cells = <&macaddr_ubootenv_ethaddr 0>; +}; + +&gmac { + status = "okay"; +}; + +&mdio { + status = "okay"; +}; + &wifi0 { status = "okay"; - nvmem-cell-names = "pre-calibration"; - nvmem-cells = <&precal_art_1000>; + nvmem-cell-names = "pre-calibration", "mac-address"; + nvmem-cells = <&precal_art_1000>, <&macaddr_ubootenv_ethaddr 1>; qcom,ath10k-calibration-variant = "EnGenius-EAP1300"; }; &wifi1 { status = "okay"; - nvmem-cell-names = "pre-calibration"; - nvmem-cells = <&precal_art_5000>; + nvmem-cell-names = "pre-calibration", "mac-address"; + nvmem-cells = <&precal_art_5000>, <&macaddr_ubootenv_ethaddr 2>; qcom,ath10k-calibration-variant = "EnGenius-EAP1300"; }; diff --git a/target/linux/ipq40xx/files-6.6/arch/arm/boot/dts/qcom/qcom-ipq4018-ecw5211.dts b/target/linux/ipq40xx/files-6.6/arch/arm/boot/dts/qcom/qcom-ipq4018-ecw5211.dts index e74d110b3d..4f3d00c678 100644 --- a/target/linux/ipq40xx/files-6.6/arch/arm/boot/dts/qcom/qcom-ipq4018-ecw5211.dts +++ b/target/linux/ipq40xx/files-6.6/arch/arm/boot/dts/qcom/qcom-ipq4018-ecw5211.dts @@ -89,19 +89,6 @@ qcom,wifi_noc_memtype_m0_m2 = ; }; - usb2@60f8800 { - status = "okay"; - }; - - usb3@8af8800 { - status = "okay"; - - dwc3@8a00000 { - phys = <&usb3_hs_phy>; - phy-names = "usb2-phy"; - }; - }; - crypto@8e3a000 { status = "okay"; }; @@ -325,10 +312,23 @@ qcom,ath10k-calibration-variant = "ALFA-Network-AP120C-AC"; }; +&usb2_hs_phy { + status = "okay"; +}; + +&usb2 { + status = "okay"; +}; + &usb3_hs_phy { status = "okay"; }; -&usb2_hs_phy { +&usb3 { status = "okay"; }; + +&usb3_dwc { + phys = <&usb3_hs_phy>; + phy-names = "usb2-phy"; +}; diff --git a/target/linux/ipq40xx/files-6.6/arch/arm/boot/dts/qcom/qcom-ipq4018-emr3500.dts b/target/linux/ipq40xx/files-6.6/arch/arm/boot/dts/qcom/qcom-ipq4018-emr3500.dts index 701dc936f1..8f108a3f9f 100644 --- a/target/linux/ipq40xx/files-6.6/arch/arm/boot/dts/qcom/qcom-ipq4018-emr3500.dts +++ b/target/linux/ipq40xx/files-6.6/arch/arm/boot/dts/qcom/qcom-ipq4018-emr3500.dts @@ -37,10 +37,6 @@ qcom,wifi_noc_memtype_m0_m2 = ; }; - usb2_hs_phy: hsphy@a8000 { - status = "okay"; - }; - crypto@8e3a000 { status = "okay"; }; diff --git a/target/linux/ipq40xx/files-6.6/arch/arm/boot/dts/qcom/qcom-ipq4018-fritzbox-4040.dts b/target/linux/ipq40xx/files-6.6/arch/arm/boot/dts/qcom/qcom-ipq4018-fritzbox-4040.dts index 524bcbcb2b..572e6545c4 100644 --- a/target/linux/ipq40xx/files-6.6/arch/arm/boot/dts/qcom/qcom-ipq4018-fritzbox-4040.dts +++ b/target/linux/ipq40xx/files-6.6/arch/arm/boot/dts/qcom/qcom-ipq4018-fritzbox-4040.dts @@ -51,14 +51,6 @@ qcom,wifi_noc_memtype_m0_m2 = ; }; - usb2@60f8800 { - status = "okay"; - }; - - usb3@8af8800 { - status = "okay"; - }; - crypto@8e3a000 { status = "okay"; }; @@ -315,10 +307,18 @@ status = "okay"; }; +&usb3 { + status = "okay"; +}; + &usb2_hs_phy { status = "okay"; }; +&usb2 { + status = "okay"; +}; + &wifi0 { status = "okay"; qcom,ath10k-calibration-variant = "AVM-FRITZBox-4040"; diff --git a/target/linux/ipq40xx/files-6.6/arch/arm/boot/dts/qcom/qcom-ipq4018-gl-ap1300.dts b/target/linux/ipq40xx/files-6.6/arch/arm/boot/dts/qcom/qcom-ipq4018-gl-ap1300.dts index 5fc97d7bb2..fb0175aeca 100644 --- a/target/linux/ipq40xx/files-6.6/arch/arm/boot/dts/qcom/qcom-ipq4018-gl-ap1300.dts +++ b/target/linux/ipq40xx/files-6.6/arch/arm/boot/dts/qcom/qcom-ipq4018-gl-ap1300.dts @@ -61,14 +61,6 @@ qcom,wifi_noc_memtype_m0_m2 = ; }; - usb2@60f8800 { - status = "okay"; - }; - - usb3@8af8800 { - status = "okay"; - }; - crypto@8e3a000 { status = "okay"; }; @@ -261,6 +253,10 @@ status = "okay"; }; +&usb2 { + status = "okay"; +}; + &usb3_hs_phy { status = "okay"; }; @@ -269,6 +265,10 @@ status = "okay"; }; +&usb3 { + status = "okay"; +}; + &gmac { status = "okay"; }; diff --git a/target/linux/ipq40xx/files-6.6/arch/arm/boot/dts/qcom/qcom-ipq4018-hap-ac2.dts b/target/linux/ipq40xx/files-6.6/arch/arm/boot/dts/qcom/qcom-ipq4018-hap-ac2.dts index fa3ed8b054..deee82b1a6 100644 --- a/target/linux/ipq40xx/files-6.6/arch/arm/boot/dts/qcom/qcom-ipq4018-hap-ac2.dts +++ b/target/linux/ipq40xx/files-6.6/arch/arm/boot/dts/qcom/qcom-ipq4018-hap-ac2.dts @@ -63,15 +63,6 @@ qcom,wifi_noc_memtype_m0_m2 = ; }; - usb3@8af8800 { - status = "okay"; - - dwc3@8a00000 { - phys = <&usb3_hs_phy>; - phy-names = "usb2-phy"; - }; - }; - crypto@8e3a000 { status = "okay"; }; @@ -219,6 +210,15 @@ status = "okay"; }; +&usb3 { + status = "okay"; +}; + +&usb3_dwc { + phys = <&usb3_hs_phy>; + phy-names = "usb2-phy"; +}; + &mdio { status = "okay"; }; diff --git a/target/linux/ipq40xx/files-6.6/arch/arm/boot/dts/qcom/qcom-ipq4018-nbg6617.dts b/target/linux/ipq40xx/files-6.6/arch/arm/boot/dts/qcom/qcom-ipq4018-nbg6617.dts index a9e9683592..cb40b87e56 100644 --- a/target/linux/ipq40xx/files-6.6/arch/arm/boot/dts/qcom/qcom-ipq4018-nbg6617.dts +++ b/target/linux/ipq40xx/files-6.6/arch/arm/boot/dts/qcom/qcom-ipq4018-nbg6617.dts @@ -60,39 +60,6 @@ qcom,wifi_noc_memtype_m0_m2 = ; }; - usb2@60f8800 { - status = "okay"; - - dwc3@6000000 { - #address-cells = <1>; - #size-cells = <0>; - - usb2_port1: port@1 { - reg = <1>; - #trigger-source-cells = <0>; - }; - }; - }; - - usb3@8af8800 { - status = "okay"; - - dwc3@8a00000 { - #address-cells = <1>; - #size-cells = <0>; - - usb3_port1: port@1 { - reg = <1>; - #trigger-source-cells = <0>; - }; - - usb3_port2: port@2 { - reg = <2>; - #trigger-source-cells = <0>; - }; - }; - }; - crypto@8e3a000 { status = "okay"; }; @@ -360,6 +327,39 @@ status = "okay"; }; +&usb3 { + status = "okay"; +}; + +&usb3_dwc { + #address-cells = <1>; + #size-cells = <0>; + + usb3_port1: port@1 { + reg = <1>; + #trigger-source-cells = <0>; + }; + + usb3_port2: port@2 { + reg = <2>; + #trigger-source-cells = <0>; + }; +}; + &usb2_hs_phy { status = "okay"; }; + +&usb2 { + status = "okay"; + + usb@6000000 { + #address-cells = <1>; + #size-cells = <0>; + + usb2_port1: port@1 { + reg = <1>; + #trigger-source-cells = <0>; + }; + }; +}; diff --git a/target/linux/ipq40xx/files-6.6/arch/arm/boot/dts/qcom/qcom-ipq4018-rt-ac58u.dts b/target/linux/ipq40xx/files-6.6/arch/arm/boot/dts/qcom/qcom-ipq4018-rt-ac58u.dts index 38158fbfa7..ecf652368c 100644 --- a/target/linux/ipq40xx/files-6.6/arch/arm/boot/dts/qcom/qcom-ipq4018-rt-ac58u.dts +++ b/target/linux/ipq40xx/files-6.6/arch/arm/boot/dts/qcom/qcom-ipq4018-rt-ac58u.dts @@ -55,25 +55,6 @@ qcom,wifi_noc_memtype_m0_m2 = ; }; - usb3@8af8800 { - status = "okay"; - - dwc3@8a00000 { - #address-cells = <1>; - #size-cells = <0>; - - usb3_port1: port@1 { - reg = <1>; - #trigger-source-cells = <0>; - }; - - usb3_port2: port@2 { - reg = <2>; - #trigger-source-cells = <0>; - }; - }; - }; - crypto@8e3a000 { status = "okay"; }; @@ -291,6 +272,25 @@ status = "okay"; }; +&usb3 { + status = "okay"; +}; + +&usb3_dwc { + #address-cells = <1>; + #size-cells = <0>; + + usb3_port1: port@1 { + reg = <1>; + #trigger-source-cells = <0>; + }; + + usb3_port2: port@2 { + reg = <2>; + #trigger-source-cells = <0>; + }; +}; + &gmac { status = "okay"; }; diff --git a/target/linux/ipq40xx/files-6.6/arch/arm/boot/dts/qcom/qcom-ipq4018-wap-ac-lte.dts b/target/linux/ipq40xx/files-6.6/arch/arm/boot/dts/qcom/qcom-ipq4018-wap-ac-lte.dts index 8ff18d92b7..5cb103b321 100644 --- a/target/linux/ipq40xx/files-6.6/arch/arm/boot/dts/qcom/qcom-ipq4018-wap-ac-lte.dts +++ b/target/linux/ipq40xx/files-6.6/arch/arm/boot/dts/qcom/qcom-ipq4018-wap-ac-lte.dts @@ -15,15 +15,6 @@ qcom,usb-hsphy-mode-select = ; status = "okay"; }; - - usb3@8af8800 { - status = "okay"; - - dwc3@8a00000 { - phys = <&usb3_hs_phy>; - phy-names = "usb2-phy"; - }; - }; }; }; @@ -43,3 +34,8 @@ &usb3 { status = "okay"; }; + +&usb3_dwc { + phys = <&usb3_hs_phy>; + phy-names = "usb2-phy"; +}; diff --git a/target/linux/ipq40xx/files-6.6/arch/arm/boot/dts/qcom/qcom-ipq4018-wap-r-ac.dts b/target/linux/ipq40xx/files-6.6/arch/arm/boot/dts/qcom/qcom-ipq4018-wap-r-ac.dts index e7f28f23cf..bf50ebfc54 100644 --- a/target/linux/ipq40xx/files-6.6/arch/arm/boot/dts/qcom/qcom-ipq4018-wap-r-ac.dts +++ b/target/linux/ipq40xx/files-6.6/arch/arm/boot/dts/qcom/qcom-ipq4018-wap-r-ac.dts @@ -15,15 +15,6 @@ qcom,usb-hsphy-mode-select = ; status = "okay"; }; - - usb3@8af8800 { - status = "okay"; - - dwc3@8a00000 { - phys = <&usb3_hs_phy>; - phy-names = "usb2-phy"; - }; - }; }; }; @@ -43,3 +34,8 @@ &usb3 { status = "okay"; }; + +&usb3_dwc { + phys = <&usb3_hs_phy>; + phy-names = "usb2-phy"; +}; diff --git a/target/linux/ipq40xx/files-6.6/arch/arm/boot/dts/qcom/qcom-ipq4018-wrtq-329acn.dts b/target/linux/ipq40xx/files-6.6/arch/arm/boot/dts/qcom/qcom-ipq4018-wrtq-329acn.dts index f3c6f34bf4..26c8c0244a 100644 --- a/target/linux/ipq40xx/files-6.6/arch/arm/boot/dts/qcom/qcom-ipq4018-wrtq-329acn.dts +++ b/target/linux/ipq40xx/files-6.6/arch/arm/boot/dts/qcom/qcom-ipq4018-wrtq-329acn.dts @@ -69,14 +69,6 @@ qcom,wifi_noc_memtype_m0_m2 = ; }; - usb2@60f8800 { - status = "okay"; - }; - - usb3@8af8800 { - status = "okay"; - }; - crypto@8e3a000 { status = "okay"; }; @@ -282,6 +274,10 @@ status = "okay"; }; +&usb2 { + status = "okay"; +}; + &usb3_hs_phy { status = "okay"; }; @@ -290,6 +286,10 @@ status = "okay"; }; +&usb3 { + status = "okay"; +}; + &wifi0 { status = "okay"; nvmem-cell-names = "pre-calibration"; diff --git a/target/linux/ipq40xx/files-6.6/arch/arm/boot/dts/qcom/qcom-ipq4019-cm520-79f.dts b/target/linux/ipq40xx/files-6.6/arch/arm/boot/dts/qcom/qcom-ipq4019-cm520-79f.dts index d1c8d798f9..b4f536fdb8 100644 --- a/target/linux/ipq40xx/files-6.6/arch/arm/boot/dts/qcom/qcom-ipq4019-cm520-79f.dts +++ b/target/linux/ipq40xx/files-6.6/arch/arm/boot/dts/qcom/qcom-ipq4019-cm520-79f.dts @@ -54,39 +54,6 @@ qcom,wifi_noc_memtype_m0_m2 = ; }; - usb2@60f8800 { - status = "okay"; - - dwc3@6000000 { - #address-cells = <1>; - #size-cells = <0>; - - usb2_port1: port@1 { - reg = <1>; - #trigger-source-cells = <0>; - }; - }; - }; - - usb3@8af8800 { - status = "okay"; - - dwc3@8a00000 { - #address-cells = <1>; - #size-cells = <0>; - - usb3_port1: port@1 { - reg = <1>; - #trigger-source-cells = <0>; - }; - - usb3_port2: port@2 { - reg = <2>; - #trigger-source-cells = <0>; - }; - }; - }; - crypto@8e3a000 { status = "okay"; }; @@ -344,10 +311,43 @@ status = "okay"; }; +&usb3 { + status = "okay"; +}; + +&usb3_dwc { + #address-cells = <1>; + #size-cells = <0>; + + usb3_port1: port@1 { + reg = <1>; + #trigger-source-cells = <0>; + }; + + usb3_port2: port@2 { + reg = <2>; + #trigger-source-cells = <0>; + }; +}; + &usb2_hs_phy { status = "okay"; }; +&usb2 { + status = "okay"; + + usb@6000000 { + #address-cells = <1>; + #size-cells = <0>; + + usb2_port1: port@1 { + reg = <1>; + #trigger-source-cells = <0>; + }; + }; +}; + &gmac { status = "okay"; diff --git a/target/linux/ipq40xx/files-6.6/arch/arm/boot/dts/qcom/qcom-ipq4019-e2600ac.dtsi b/target/linux/ipq40xx/files-6.6/arch/arm/boot/dts/qcom/qcom-ipq4019-e2600ac.dtsi index 9216a7c9f8..6ea0eb2b55 100644 --- a/target/linux/ipq40xx/files-6.6/arch/arm/boot/dts/qcom/qcom-ipq4019-e2600ac.dtsi +++ b/target/linux/ipq40xx/files-6.6/arch/arm/boot/dts/qcom/qcom-ipq4019-e2600ac.dtsi @@ -202,20 +202,20 @@ &usb3 { status = "okay"; +}; - dwc3@8a00000 { - #address-cells = <1>; - #size-cells = <0>; +&usb3_dwc { + #address-cells = <1>; + #size-cells = <0>; - usb3_port1: port@1 { - reg = <1>; - #trigger-source-cells = <0>; - }; + usb3_port1: port@1 { + reg = <1>; + #trigger-source-cells = <0>; + }; - usb3_port2: port@2 { - reg = <2>; - #trigger-source-cells = <0>; - }; + usb3_port2: port@2 { + reg = <2>; + #trigger-source-cells = <0>; }; }; @@ -230,7 +230,7 @@ &usb2 { status = "okay"; - dwc3@6000000 { + usb@6000000 { #address-cells = <1>; #size-cells = <0>; diff --git a/target/linux/ipq40xx/files-6.6/arch/arm/boot/dts/qcom/qcom-ipq4019-fritzbox-7530.dts b/target/linux/ipq40xx/files-6.6/arch/arm/boot/dts/qcom/qcom-ipq4019-fritzbox-7530.dts index a118bdf26b..2c86cd57bc 100644 --- a/target/linux/ipq40xx/files-6.6/arch/arm/boot/dts/qcom/qcom-ipq4019-fritzbox-7530.dts +++ b/target/linux/ipq40xx/files-6.6/arch/arm/boot/dts/qcom/qcom-ipq4019-fritzbox-7530.dts @@ -54,10 +54,6 @@ qcom,wifi_noc_memtype_m0_m2 = ; }; - usb3@8af8800 { - status = "okay"; - }; - crypto@8e3a000 { status = "okay"; }; @@ -266,6 +262,10 @@ status = "okay"; }; +&usb3 { + status = "okay"; +}; + &qpic_bam { status = "okay"; }; diff --git a/target/linux/ipq40xx/files-6.6/arch/arm/boot/dts/qcom/qcom-ipq4019-le1.dts b/target/linux/ipq40xx/files-6.6/arch/arm/boot/dts/qcom/qcom-ipq4019-le1.dts index c4e7d0b207..1577ed58bb 100644 --- a/target/linux/ipq40xx/files-6.6/arch/arm/boot/dts/qcom/qcom-ipq4019-le1.dts +++ b/target/linux/ipq40xx/files-6.6/arch/arm/boot/dts/qcom/qcom-ipq4019-le1.dts @@ -269,7 +269,7 @@ &usb2 { status = "okay"; - dwc3@6000000 { + usb@6000000 { #address-cells = <1>; #size-cells = <0>; @@ -286,20 +286,20 @@ &usb3 { status = "okay"; +}; - dwc3@8a00000 { - #address-cells = <1>; - #size-cells = <0>; +&usb3_dwc { + #address-cells = <1>; + #size-cells = <0>; - usb3_port1: port@1 { - reg = <1>; - #trigger-source-cells = <0>; - }; + usb3_port1: port@1 { + reg = <1>; + #trigger-source-cells = <0>; + }; - usb3_port2: port@2 { - reg = <2>; - #trigger-source-cells = <0>; - }; + usb3_port2: port@2 { + reg = <2>; + #trigger-source-cells = <0>; }; }; diff --git a/target/linux/ipq40xx/files-6.6/arch/arm/boot/dts/qcom/qcom-ipq4019-map-ac2200.dts b/target/linux/ipq40xx/files-6.6/arch/arm/boot/dts/qcom/qcom-ipq4019-map-ac2200.dts index 32f0473fb1..30101b0c3a 100644 --- a/target/linux/ipq40xx/files-6.6/arch/arm/boot/dts/qcom/qcom-ipq4019-map-ac2200.dts +++ b/target/linux/ipq40xx/files-6.6/arch/arm/boot/dts/qcom/qcom-ipq4019-map-ac2200.dts @@ -45,10 +45,6 @@ qcom,wifi_noc_memtype_m0_m2 = ; }; - usb2@60f8800 { - status = "okay"; - }; - crypto@8e3a000 { status = "okay"; }; @@ -233,6 +229,10 @@ status = "okay"; }; +&usb2 { + status = "okay"; +}; + &blsp1_i2c3 { pinctrl-0 = <&i2c_0_pins>; pinctrl-names = "default"; diff --git a/target/linux/ipq40xx/files-6.6/arch/arm/boot/dts/qcom/qcom-ipq4019-mf18a.dts b/target/linux/ipq40xx/files-6.6/arch/arm/boot/dts/qcom/qcom-ipq4019-mf18a.dts index 6987515720..9bc12be8ef 100644 --- a/target/linux/ipq40xx/files-6.6/arch/arm/boot/dts/qcom/qcom-ipq4019-mf18a.dts +++ b/target/linux/ipq40xx/files-6.6/arch/arm/boot/dts/qcom/qcom-ipq4019-mf18a.dts @@ -135,14 +135,6 @@ qcom,wifi_noc_memtype_m0_m2 = ; }; - usb2@60f8800 { - status = "okay"; - }; - - usb3@8af8800 { - status = "okay"; - }; - crypto@8e3a000 { status = "okay"; }; @@ -449,6 +441,10 @@ status = "okay"; }; +&usb2 { + status = "okay"; +}; + &usb3_ss_phy { status = "okay"; }; @@ -457,6 +453,10 @@ status = "okay"; }; +&usb3 { + status = "okay"; +}; + &wifi0 { status = "okay"; nvmem-cell-names = "pre-calibration", "mac-address"; diff --git a/target/linux/ipq40xx/files-6.6/arch/arm/boot/dts/qcom/qcom-ipq4019-mf282plus.dts b/target/linux/ipq40xx/files-6.6/arch/arm/boot/dts/qcom/qcom-ipq4019-mf282plus.dts index 54353cac58..ede92f7e22 100644 --- a/target/linux/ipq40xx/files-6.6/arch/arm/boot/dts/qcom/qcom-ipq4019-mf282plus.dts +++ b/target/linux/ipq40xx/files-6.6/arch/arm/boot/dts/qcom/qcom-ipq4019-mf282plus.dts @@ -118,14 +118,6 @@ qcom,wifi_noc_memtype_m0_m2 = ; }; - usb2@60f8800 { - status = "okay"; - }; - - usb3@8af8800 { - status = "okay"; - }; - crypto@8e3a000 { status = "okay"; }; @@ -423,6 +415,10 @@ status = "okay"; }; +&usb2 { + status = "okay"; +}; + &usb3_ss_phy { status = "okay"; }; @@ -431,6 +427,10 @@ status = "okay"; }; +&usb3 { + status = "okay"; +}; + /* * The MD5 sum of the board file of the MF286D is identical to the board * file in the OEM firmware diff --git a/target/linux/ipq40xx/files-6.6/arch/arm/boot/dts/qcom/qcom-ipq4019-mf286d.dts b/target/linux/ipq40xx/files-6.6/arch/arm/boot/dts/qcom/qcom-ipq4019-mf286d.dts index 61cbdba0d1..cd1c3c732f 100644 --- a/target/linux/ipq40xx/files-6.6/arch/arm/boot/dts/qcom/qcom-ipq4019-mf286d.dts +++ b/target/linux/ipq40xx/files-6.6/arch/arm/boot/dts/qcom/qcom-ipq4019-mf286d.dts @@ -111,14 +111,6 @@ qcom,wifi_noc_memtype_m0_m2 = ; }; - usb2@60f8800 { - status = "okay"; - }; - - usb3@8af8800 { - status = "okay"; - }; - crypto@8e3a000 { status = "okay"; }; @@ -430,6 +422,10 @@ status = "okay"; }; +&usb2 { + status = "okay"; +}; + &usb3_ss_phy { status = "okay"; }; @@ -438,6 +434,10 @@ status = "okay"; }; +&usb3 { + status = "okay"; +}; + &wifi0 { status = "okay"; nvmem-cell-names = "pre-calibration", "mac-address"; diff --git a/target/linux/ipq40xx/files-6.6/arch/arm/boot/dts/qcom/qcom-ipq4019-ncp-hg100-cellular.dts b/target/linux/ipq40xx/files-6.6/arch/arm/boot/dts/qcom/qcom-ipq4019-ncp-hg100-cellular.dts index ea27defea3..a23654e4c8 100644 --- a/target/linux/ipq40xx/files-6.6/arch/arm/boot/dts/qcom/qcom-ipq4019-ncp-hg100-cellular.dts +++ b/target/linux/ipq40xx/files-6.6/arch/arm/boot/dts/qcom/qcom-ipq4019-ncp-hg100-cellular.dts @@ -582,15 +582,15 @@ pinctrl-0 = <&usb3_pins>, <<e_pins>; pinctrl-names = "default"; +}; - dwc3@8a00000 { - #address-cells = <1>; - #size-cells = <0>; +&usb3_dwc { + #address-cells = <1>; + #size-cells = <0>; - device@1 { - compatible = "usb1bc7,1900"; - reg = <1>; - }; + device@1 { + compatible = "usb1bc7,1900"; + reg = <1>; }; }; diff --git a/target/linux/ipq40xx/files-6.6/arch/arm/boot/dts/qcom/qcom-ipq4019-oap100.dts b/target/linux/ipq40xx/files-6.6/arch/arm/boot/dts/qcom/qcom-ipq4019-oap100.dts index 2080a34e2f..f3f9395b41 100644 --- a/target/linux/ipq40xx/files-6.6/arch/arm/boot/dts/qcom/qcom-ipq4019-oap100.dts +++ b/target/linux/ipq40xx/files-6.6/arch/arm/boot/dts/qcom/qcom-ipq4019-oap100.dts @@ -53,39 +53,6 @@ status = "okay"; }; - usb2@60f8800 { - status = "okay"; - - dwc3@6000000 { - #address-cells = <1>; - #size-cells = <0>; - - usb2_port1: port@1 { - reg = <1>; - #trigger-source-cells = <0>; - }; - }; - }; - - usb3@8af8800 { - status = "okay"; - - dwc3@8a00000 { - #address-cells = <1>; - #size-cells = <0>; - - usb3_port1: port@1 { - reg = <1>; - #trigger-source-cells = <0>; - }; - - usb3_port2: port@2 { - reg = <2>; - #trigger-source-cells = <0>; - }; - }; - }; - crypto@8e3a000 { status = "okay"; }; @@ -337,6 +304,40 @@ status = "okay"; }; +&usb3 { + status = "okay"; +}; + +&usb3_dwc { + #address-cells = <1>; + #size-cells = <0>; + + usb3_port1: port@1 { + reg = <1>; + #trigger-source-cells = <0>; + }; + + usb3_port2: port@2 { + reg = <2>; + #trigger-source-cells = <0>; + }; +}; + + &usb2_hs_phy { status = "okay"; }; + +&usb2 { + status = "okay"; + + usb@6000000 { + #address-cells = <1>; + #size-cells = <0>; + + usb2_port1: port@1 { + reg = <1>; + #trigger-source-cells = <0>; + }; + }; +}; diff --git a/target/linux/ipq40xx/files-6.6/arch/arm/boot/dts/qcom/qcom-ipq4019-r619ac.dtsi b/target/linux/ipq40xx/files-6.6/arch/arm/boot/dts/qcom/qcom-ipq4019-r619ac.dtsi index 90e5455b25..0abc3aff55 100644 --- a/target/linux/ipq40xx/files-6.6/arch/arm/boot/dts/qcom/qcom-ipq4019-r619ac.dtsi +++ b/target/linux/ipq40xx/files-6.6/arch/arm/boot/dts/qcom/qcom-ipq4019-r619ac.dtsi @@ -53,14 +53,6 @@ qcom,wifi_noc_memtype_m0_m2 = ; }; - usb2@60f8800 { - status = "okay"; - }; - - usb3@8af8800 { - status = "okay"; - }; - crypto@8e3a000 { status = "okay"; }; @@ -364,10 +356,18 @@ status = "okay"; }; +&usb3 { + status = "okay"; +}; + &usb2_hs_phy { status = "okay"; }; +&usb2 { + status = "okay"; +}; + &vqmmc { status = "okay"; }; diff --git a/target/linux/ipq40xx/files-6.6/arch/arm/boot/dts/qcom/qcom-ipq4019-rbr50.dts b/target/linux/ipq40xx/files-6.6/arch/arm/boot/dts/qcom/qcom-ipq4019-rbr50.dts index a803999804..f83f75b464 100644 --- a/target/linux/ipq40xx/files-6.6/arch/arm/boot/dts/qcom/qcom-ipq4019-rbr50.dts +++ b/target/linux/ipq40xx/files-6.6/arch/arm/boot/dts/qcom/qcom-ipq4019-rbr50.dts @@ -9,22 +9,20 @@ chosen { bootargs = "root=/dev/mmcblk0p20 blkdevparts=mmcblk0:512K@17K(0:SBL1)ro,512K(0:BOOTCONFIG)ro,512K(0:QSEE)ro,512K(0:QSEE_ALT)ro,256K(0:CDT)ro,256K(0:CDT_ALT)ro,256K(0:DDRPARAMS)ro,256K(0:APPSBLENV)ro,1M(0:APPSBL)ro,1M(0:APPSBL_ALT)ro,256K(0:ART)ro,256K(ARTMTD)ro,2M(language)ro,256K(config)ro,256K(pot)ro,256K(traffic_meter)ro,256K(pot_bak)ro,256K(traffic_meter.bak)ro,3840K(kernel),31488K(rootfs),35328K@9233K(firmware),256K(mtdoops)ro,1457651200(reserved)ro,-(unallocated) rootfstype=squashfs,ext4 rootwait"; }; - - soc { - usb2@60f8800 { - status = "okay"; - }; - - usb3@8af8800 { - status = "okay"; - }; - }; }; &usb3_hs_phy { status = "okay"; }; +&usb3 { + status = "okay"; +}; + &usb2_hs_phy { status = "okay"; }; + +&usb2 { + status = "okay"; +}; diff --git a/target/linux/ipq40xx/files-6.6/arch/arm/boot/dts/qcom/qcom-ipq4019-rbs50.dts b/target/linux/ipq40xx/files-6.6/arch/arm/boot/dts/qcom/qcom-ipq4019-rbs50.dts index 4d0a9132c6..9151c5d33c 100644 --- a/target/linux/ipq40xx/files-6.6/arch/arm/boot/dts/qcom/qcom-ipq4019-rbs50.dts +++ b/target/linux/ipq40xx/files-6.6/arch/arm/boot/dts/qcom/qcom-ipq4019-rbs50.dts @@ -9,22 +9,20 @@ chosen { bootargs = "root=/dev/mmcblk0p20 blkdevparts=mmcblk0:512K@17K(0:SBL1)ro,512K(0:BOOTCONFIG)ro,512K(0:QSEE)ro,512K(0:QSEE_ALT)ro,256K(0:CDT)ro,256K(0:CDT_ALT)ro,256K(0:DDRPARAMS)ro,256K(0:APPSBLENV)ro,1M(0:APPSBL)ro,1M(0:APPSBL_ALT)ro,256K(0:ART)ro,256K(ARTMTD)ro,2M(language)ro,256K(config)ro,256K(pot)ro,256K(traffic_meter)ro,256K(pot_bak)ro,256K(traffic_meter.bak)ro,3840K(kernel),31488K(rootfs),35328K@9233K(firmware),256K(mtdoops)ro,1457651200(reserved)ro,-(unallocated) rootfstype=squashfs,ext4 rootwait"; }; - - soc { - usb2@60f8800 { - status = "okay"; - }; - - usb3@8af8800 { - status = "okay"; - }; - }; }; &usb3_hs_phy { status = "okay"; }; +&usb3 { + status = "okay"; +}; + &usb2_hs_phy { status = "okay"; }; + +&usb2 { + status = "okay"; +}; diff --git a/target/linux/ipq40xx/files-6.6/arch/arm/boot/dts/qcom/qcom-ipq4019-rt-ac42u.dts b/target/linux/ipq40xx/files-6.6/arch/arm/boot/dts/qcom/qcom-ipq4019-rt-ac42u.dts index 70849d71d6..2854809bfe 100644 --- a/target/linux/ipq40xx/files-6.6/arch/arm/boot/dts/qcom/qcom-ipq4019-rt-ac42u.dts +++ b/target/linux/ipq40xx/files-6.6/arch/arm/boot/dts/qcom/qcom-ipq4019-rt-ac42u.dts @@ -55,25 +55,6 @@ qcom,wifi_noc_memtype_m0_m2 = ; }; - usb3@8af8800 { - status = "okay"; - - dwc3@8a00000 { - #address-cells = <1>; - #size-cells = <0>; - - usb3_port1: port@1 { - reg = <1>; - #trigger-source-cells = <0>; - }; - - usb3_port2: port@2 { - reg = <2>; - #trigger-source-cells = <0>; - }; - }; - }; - crypto@8e3a000 { status = "okay"; }; @@ -269,6 +250,25 @@ status = "okay"; }; +&usb3 { + status = "okay"; +}; + +&usb3_dwc { + #address-cells = <1>; + #size-cells = <0>; + + usb3_port1: port@1 { + reg = <1>; + #trigger-source-cells = <0>; + }; + + usb3_port2: port@2 { + reg = <2>; + #trigger-source-cells = <0>; + }; +}; + &gmac { status = "okay"; }; diff --git a/target/linux/ipq40xx/files-6.6/arch/arm/boot/dts/qcom/qcom-ipq4019-rtl30vw.dts b/target/linux/ipq40xx/files-6.6/arch/arm/boot/dts/qcom/qcom-ipq4019-rtl30vw.dts index e2df1d1997..e2d435133b 100644 --- a/target/linux/ipq40xx/files-6.6/arch/arm/boot/dts/qcom/qcom-ipq4019-rtl30vw.dts +++ b/target/linux/ipq40xx/files-6.6/arch/arm/boot/dts/qcom/qcom-ipq4019-rtl30vw.dts @@ -172,14 +172,6 @@ qcom,wifi_noc_memtype_m0_m2 = ; }; - usb2@60f8800 { - status = "okay"; - }; - - usb3@8af8800 { - status = "okay"; - }; - crypto@8e3a000 { status = "okay"; }; @@ -358,6 +350,10 @@ status = "okay"; }; +&usb2 { + status = "okay"; +}; + &usb3_ss_phy { status = "okay"; }; @@ -366,6 +362,10 @@ status = "okay"; }; +&usb3 { + status = "okay"; +}; + &wifi0 { status = "okay"; qcom,ath10k-calibration-variant = "cellc,rtl30vw"; diff --git a/target/linux/ipq40xx/files-6.6/arch/arm/boot/dts/qcom/qcom-ipq4019-u4019.dtsi b/target/linux/ipq40xx/files-6.6/arch/arm/boot/dts/qcom/qcom-ipq4019-u4019.dtsi index c7439b87ec..e31f10f9d3 100644 --- a/target/linux/ipq40xx/files-6.6/arch/arm/boot/dts/qcom/qcom-ipq4019-u4019.dtsi +++ b/target/linux/ipq40xx/files-6.6/arch/arm/boot/dts/qcom/qcom-ipq4019-u4019.dtsi @@ -46,39 +46,6 @@ qcom,wifi_noc_memtype_m0_m2 = ; }; - usb2@60f8800 { - status = "okay"; - - dwc3@6000000 { - #address-cells = <1>; - #size-cells = <0>; - - port@1 { - reg = <1>; - #trigger-source-cells = <0>; - }; - }; - }; - - usb3@8af8800 { - status = "okay"; - - dwc3@8a00000 { - #address-cells = <1>; - #size-cells = <0>; - - port@1 { - reg = <1>; - #trigger-source-cells = <0>; - }; - - port@2 { - reg = <2>; - #trigger-source-cells = <0>; - }; - }; - }; - watchdog@b017000 { status = "okay"; }; @@ -199,10 +166,44 @@ status = "okay"; }; +&usb3 { + status = "okay"; +}; + +&usb3_dwc { + #address-cells = <1>; + #size-cells = <0>; + + usb3_port1: port@1 { + reg = <1>; + #trigger-source-cells = <0>; + }; + + usb3_port2: port@2 { + reg = <2>; + #trigger-source-cells = <0>; + }; +}; + + &usb2_hs_phy { status = "okay"; }; +&usb2 { + status = "okay"; + + usb@6000000 { + #address-cells = <1>; + #size-cells = <0>; + + usb2_port1: port@1 { + reg = <1>; + #trigger-source-cells = <0>; + }; + }; +}; + &wifi0 { status = "okay"; nvmem-cell-names = "pre-calibration"; diff --git a/target/linux/ipq40xx/files-6.6/arch/arm/boot/dts/qcom/qcom-ipq4019-wpj419.dts b/target/linux/ipq40xx/files-6.6/arch/arm/boot/dts/qcom/qcom-ipq4019-wpj419.dts index 2dc4544433..d99cbdb608 100644 --- a/target/linux/ipq40xx/files-6.6/arch/arm/boot/dts/qcom/qcom-ipq4019-wpj419.dts +++ b/target/linux/ipq40xx/files-6.6/arch/arm/boot/dts/qcom/qcom-ipq4019-wpj419.dts @@ -300,26 +300,6 @@ status = "okay"; }; - usb3_ss_phy: ssphy@9a000 { - status = "okay"; - }; - - usb3_hs_phy: hsphy@a6000 { - status = "okay"; - }; - - usb3: usb3@8af8800 { - status = "okay"; - }; - - usb2_hs_phy: hsphy@a8000 { - status = "okay"; - }; - - usb2: usb2@60f8800 { - status = "okay"; - }; - cryptobam: dma@8e04000 { status = "okay"; }; @@ -354,6 +334,26 @@ }; }; +&usb3_ss_phy { + status = "okay"; +}; + +&usb3_hs_phy { + status = "okay"; +}; + +&usb3 { + status = "okay"; +}; + +&usb2_hs_phy { + status = "okay"; +}; + +&usb2 { + status = "okay"; +}; + &nand { pinctrl-0 = <&nand_pins>; pinctrl-names = "default"; diff --git a/target/linux/ipq40xx/files-6.6/arch/arm/boot/dts/qcom/qcom-ipq4019-wtr-m2133hp.dts b/target/linux/ipq40xx/files-6.6/arch/arm/boot/dts/qcom/qcom-ipq4019-wtr-m2133hp.dts index 00b5897b7d..37383b015d 100644 --- a/target/linux/ipq40xx/files-6.6/arch/arm/boot/dts/qcom/qcom-ipq4019-wtr-m2133hp.dts +++ b/target/linux/ipq40xx/files-6.6/arch/arm/boot/dts/qcom/qcom-ipq4019-wtr-m2133hp.dts @@ -72,10 +72,6 @@ watchdog@b017000 { status = "okay"; }; - - usb3@8af8800 { - status = "okay"; - }; }; leds { @@ -470,3 +466,7 @@ &usb3_hs_phy { status = "okay"; }; + +&usb3 { + status = "okay"; +}; diff --git a/target/linux/ipq40xx/files-6.6/arch/arm/boot/dts/qcom/qcom-ipq4019-x1pro.dtsi b/target/linux/ipq40xx/files-6.6/arch/arm/boot/dts/qcom/qcom-ipq4019-x1pro.dtsi index fe3650ca58..8f0fd9a89f 100644 --- a/target/linux/ipq40xx/files-6.6/arch/arm/boot/dts/qcom/qcom-ipq4019-x1pro.dtsi +++ b/target/linux/ipq40xx/files-6.6/arch/arm/boot/dts/qcom/qcom-ipq4019-x1pro.dtsi @@ -56,39 +56,6 @@ qcom,wifi_noc_memtype_m0_m2 = ; }; - usb2@60f8800 { - status = "okay"; - - dwc3@6000000 { - #address-cells = <1>; - #size-cells = <0>; - - port@1 { - reg = <1>; - #trigger-source-cells = <0>; - }; - }; - }; - - usb3@8af8800 { - status = "okay"; - - dwc3@8a00000 { - #address-cells = <1>; - #size-cells = <0>; - - port@1 { - reg = <1>; - #trigger-source-cells = <0>; - }; - - port@2 { - reg = <2>; - #trigger-source-cells = <0>; - }; - }; - }; - watchdog@b017000 { status = "okay"; }; @@ -201,10 +168,43 @@ status = "okay"; }; +&usb3 { + status = "okay"; +}; + +&usb3_dwc { + #address-cells = <1>; + #size-cells = <0>; + + port@1 { + reg = <1>; + #trigger-source-cells = <0>; + }; + + port@2 { + reg = <2>; + #trigger-source-cells = <0>; + }; +}; + &usb2_hs_phy { status = "okay"; }; +&usb2 { + status = "okay"; + + usb@6000000 { + #address-cells = <1>; + #size-cells = <0>; + + port@1 { + reg = <1>; + #trigger-source-cells = <0>; + }; + }; +}; + &wifi0 { status = "okay"; nvmem-cell-names = "pre-calibration"; diff --git a/target/linux/ipq40xx/files-6.6/arch/arm/boot/dts/qcom/qcom-ipq4019-xx8300.dtsi b/target/linux/ipq40xx/files-6.6/arch/arm/boot/dts/qcom/qcom-ipq4019-xx8300.dtsi index 141ea60442..83574f89c7 100644 --- a/target/linux/ipq40xx/files-6.6/arch/arm/boot/dts/qcom/qcom-ipq4019-xx8300.dtsi +++ b/target/linux/ipq40xx/files-6.6/arch/arm/boot/dts/qcom/qcom-ipq4019-xx8300.dtsi @@ -59,39 +59,6 @@ qcom,wifi_noc_memtype_m0_m2 = ; }; - usb2@60f8800 { - status = "okay"; - - dwc3@6000000 { - #address-cells = <1>; - #size-cells = <0>; - - usb2_port1: port@1 { - reg = <1>; - #trigger-source-cells = <0>; - }; - }; - }; - - usb3@8af8800 { - status = "okay"; - - dwc3@8a00000 { - #address-cells = <1>; - #size-cells = <0>; - - usb3_port1: port@1 { - reg = <1>; - #trigger-source-cells = <0>; - }; - - usb3_port2: port@2 { - reg = <2>; - #trigger-source-cells = <0>; - }; - }; - }; - crypto@8e3a000 { status = "okay"; }; @@ -289,6 +256,20 @@ status = "okay"; }; +&usb2 { + status = "okay"; + + usb@6000000 { + #address-cells = <1>; + #size-cells = <0>; + + usb2_port1: port@1 { + reg = <1>; + #trigger-source-cells = <0>; + }; + }; +}; + &usb3_hs_phy { status = "okay"; }; @@ -297,6 +278,25 @@ status = "okay"; }; +&usb3 { + status = "okay"; +}; + +&usb3_dwc { + #address-cells = <1>; + #size-cells = <0>; + + usb3_port1: port@1 { + reg = <1>; + #trigger-source-cells = <0>; + }; + + usb3_port2: port@2 { + reg = <2>; + #trigger-source-cells = <0>; + }; +}; + &gmac { status = "okay"; }; diff --git a/target/linux/ipq40xx/files-6.6/arch/arm/boot/dts/qcom/qcom-ipq4029-gl-b1300.dts b/target/linux/ipq40xx/files-6.6/arch/arm/boot/dts/qcom/qcom-ipq4029-gl-b1300.dts index 13ed26d5d6..e424a021b7 100644 --- a/target/linux/ipq40xx/files-6.6/arch/arm/boot/dts/qcom/qcom-ipq4029-gl-b1300.dts +++ b/target/linux/ipq40xx/files-6.6/arch/arm/boot/dts/qcom/qcom-ipq4029-gl-b1300.dts @@ -72,14 +72,6 @@ qcom,wifi_noc_memtype_m0_m2 = ; }; - usb2@60f8800 { - status = "okay"; - }; - - usb3@8af8800 { - status = "okay"; - }; - crypto@8e3a000 { status = "okay"; }; @@ -275,6 +267,10 @@ status = "okay"; }; +&usb2 { + status = "okay"; +}; + &usb3_hs_phy { status = "okay"; }; @@ -283,6 +279,10 @@ status = "okay"; }; +&usb3 { + status = "okay"; +}; + &gmac { status = "okay"; }; diff --git a/target/linux/ipq40xx/files-6.6/arch/arm/boot/dts/qcom/qcom-ipq4029-gl-s1300.dts b/target/linux/ipq40xx/files-6.6/arch/arm/boot/dts/qcom/qcom-ipq4029-gl-s1300.dts index e7236824aa..b7ff970b25 100644 --- a/target/linux/ipq40xx/files-6.6/arch/arm/boot/dts/qcom/qcom-ipq4029-gl-s1300.dts +++ b/target/linux/ipq40xx/files-6.6/arch/arm/boot/dts/qcom/qcom-ipq4029-gl-s1300.dts @@ -57,14 +57,6 @@ qcom,wifi_noc_memtype_m0_m2 = ; }; - usb2@60f8800 { - status = "okay"; - }; - - usb3@8af8800 { - status = "okay"; - }; - crypto@8e3a000 { status = "okay"; }; @@ -340,6 +332,10 @@ status = "okay"; }; +&usb2 { + status = "okay"; +}; + &usb3_hs_phy { status = "okay"; }; @@ -348,6 +344,10 @@ status = "okay"; }; +&usb3 { + status = "okay"; +}; + &wifi0 { status = "okay"; nvmem-cell-names = "pre-calibration"; diff --git a/target/linux/ipq40xx/image/generic.mk b/target/linux/ipq40xx/image/generic.mk index 7af777248d..444035ffe5 100644 --- a/target/linux/ipq40xx/image/generic.mk +++ b/target/linux/ipq40xx/image/generic.mk @@ -454,8 +454,7 @@ define Device/engenius_eap1300 IMAGE_SIZE := 25344k IMAGE/sysupgrade.bin := append-kernel | append-rootfs | pad-rootfs | append-metadata endef -# Missing DSA Setup -#TARGET_DEVICES += engenius_eap1300 +TARGET_DEVICES += engenius_eap1300 define Device/engenius_eap2200 $(call Device/FitImage) diff --git a/target/linux/ipq806x/files-6.6/arch/arm/boot/dts/qcom/qcom-ipq8068-mr52.dts b/target/linux/ipq806x/files-6.6/arch/arm/boot/dts/qcom/qcom-ipq8068-mr52.dts index 7512bfb74f..0d3230e6de 100644 --- a/target/linux/ipq806x/files-6.6/arch/arm/boot/dts/qcom/qcom-ipq8068-mr52.dts +++ b/target/linux/ipq806x/files-6.6/arch/arm/boot/dts/qcom/qcom-ipq8068-mr52.dts @@ -46,13 +46,13 @@ gpios = <&qcom_pinmux 19 GPIO_ACTIVE_HIGH>; }; - lan2_green { - label = "green:lan2"; + lan1_green { + label = "green:lan1"; gpios = <&qcom_pinmux 23 GPIO_ACTIVE_HIGH>; }; - lan1_green { - label = "green:lan1"; + lan2_green { + label = "green:lan2"; gpios = <&qcom_pinmux 24 GPIO_ACTIVE_HIGH>; }; @@ -61,13 +61,13 @@ gpios = <&qcom_pinmux 26 GPIO_ACTIVE_LOW>; }; - lan2_orange { - label = "orange:lan2"; + lan1_orange { + label = "orange:lan1"; gpios = <&qcom_pinmux 60 GPIO_ACTIVE_HIGH>; }; - lan1_orange { - label = "orange:lan1"; + lan2_orange { + label = "orange:lan2"; gpios = <&qcom_pinmux 62 GPIO_ACTIVE_HIGH>; }; }; diff --git a/target/linux/mediatek/base-files/etc/uci-defaults/99_fwenv-store-ethaddr.sh b/target/linux/mediatek/base-files/etc/uci-defaults/99_fwenv-store-ethaddr.sh index 0afb434c97..e9cb4f921d 100644 --- a/target/linux/mediatek/base-files/etc/uci-defaults/99_fwenv-store-ethaddr.sh +++ b/target/linux/mediatek/base-files/etc/uci-defaults/99_fwenv-store-ethaddr.sh @@ -11,7 +11,8 @@ unielec,u7623-02) ;; bananapi,bpi-r3|\ bananapi,bpi-r3-mini|\ -bananapi,bpi-4) +bananapi,bpi-r4|\ +bananapi,bpi-r4-poe) [ -z "$(fw_printenv -n ethaddr 2>/dev/null)" ] && fw_setenv ethaddr "$(cat /sys/class/net/eth0/address)" [ -z "$(fw_printenv -n eth1addr 2>/dev/null)" ] && diff --git a/target/linux/mediatek/dts/mt7986a-asus-tuf-ax6000.dts b/target/linux/mediatek/dts/mt7986a-asus-tuf-ax6000.dts index 1cdfb5f155..891d56853b 100644 --- a/target/linux/mediatek/dts/mt7986a-asus-tuf-ax6000.dts +++ b/target/linux/mediatek/dts/mt7986a-asus-tuf-ax6000.dts @@ -296,26 +296,26 @@ #size-cells = <0>; port@1 { - reg = <4>; - label = "lan1"; + reg = <1>; + label = "lan4"; phy-handle = <&swphy1>; }; port@2 { - reg = <3>; - label = "lan2"; + reg = <2>; + label = "lan3"; phy-handle = <&swphy2>; }; port@3 { - reg = <2>; - label = "lan3"; + reg = <3>; + label = "lan2"; phy-handle = <&swphy3>; }; port@4 { - reg = <1>; - label = "lan4"; + reg = <4>; + label = "lan1"; phy-handle = <&swphy4>; }; diff --git a/target/linux/mediatek/files-6.6/arch/arm64/boot/dts/mediatek/mt7988a-bananapi-bpi-r4.dtsi b/target/linux/mediatek/files-6.6/arch/arm64/boot/dts/mediatek/mt7988a-bananapi-bpi-r4.dtsi index 49d7edcd6b..c4c05fd419 100644 --- a/target/linux/mediatek/files-6.6/arch/arm64/boot/dts/mediatek/mt7988a-bananapi-bpi-r4.dtsi +++ b/target/linux/mediatek/files-6.6/arch/arm64/boot/dts/mediatek/mt7988a-bananapi-bpi-r4.dtsi @@ -17,16 +17,18 @@ "mediatek,mt7988a"; aliases { - serial0 = &uart0; + ethernet0 = &gmac0; + ethernet1 = &gmac1; led-boot = &led_green; led-failsafe = &led_green; led-running = &led_green; led-upgrade = &led_green; + serial0 = &uart0; }; chosen { stdout-path = &uart0; - bootargs = "console=ttyS0,115200n1 loglevel=8 pci=pcie_bus_perf ubi.block=0,fit root=/dev/fit0"; + bootargs = "console=ttyS0,115200n1 loglevel=8 pci=pcie_bus_perf ubi.block=0,fit root=/dev/fit0 rootwait"; rootdisk-spim-nand = <&ubi_rootfs>; }; diff --git a/target/linux/mediatek/files-6.6/arch/arm64/boot/dts/mediatek/mt7988a.dtsi b/target/linux/mediatek/files-6.6/arch/arm64/boot/dts/mediatek/mt7988a.dtsi index caad6e5577..af4dcb3581 100644 --- a/target/linux/mediatek/files-6.6/arch/arm64/boot/dts/mediatek/mt7988a.dtsi +++ b/target/linux/mediatek/files-6.6/arch/arm64/boot/dts/mediatek/mt7988a.dtsi @@ -159,7 +159,7 @@ pmu { compatible = "arm,cortex-a73-pmu"; interrupt-parent = <&gic>; - interrupt = ; + interrupts = ; }; psci { @@ -1282,6 +1282,7 @@ gsw_phy0: ethernet-phy@0 { compatible = "ethernet-phy-ieee802.3-c22"; reg = <0>; + interrupts = <0>; phy-mode = "internal"; nvmem-cells = <&phy_calibration_p0>; nvmem-cell-names = "phy-cal-data"; @@ -1307,6 +1308,7 @@ gsw_phy1: ethernet-phy@1 { compatible = "ethernet-phy-ieee802.3-c22"; reg = <1>; + interrupts = <1>; phy-mode = "internal"; nvmem-cells = <&phy_calibration_p1>; nvmem-cell-names = "phy-cal-data"; @@ -1332,6 +1334,7 @@ gsw_phy2: ethernet-phy@2 { compatible = "ethernet-phy-ieee802.3-c22"; reg = <2>; + interrupts = <2>; phy-mode = "internal"; nvmem-cells = <&phy_calibration_p2>; nvmem-cell-names = "phy-cal-data"; @@ -1357,6 +1360,7 @@ gsw_phy3: ethernet-phy@3 { compatible = "ethernet-phy-ieee802.3-c22"; reg = <3>; + interrupts = <3>; phy-mode = "internal"; nvmem-cells = <&phy_calibration_p3>; nvmem-cell-names = "phy-cal-data"; diff --git a/target/linux/mediatek/patches-6.6/164-dts-mt7623-bpi-r2-rootdisk-for-fitblk.patch b/target/linux/mediatek/patches-6.6/164-dts-mt7623-bpi-r2-rootdisk-for-fitblk.patch index f1a182b044..fac14b4d82 100644 --- a/target/linux/mediatek/patches-6.6/164-dts-mt7623-bpi-r2-rootdisk-for-fitblk.patch +++ b/target/linux/mediatek/patches-6.6/164-dts-mt7623-bpi-r2-rootdisk-for-fitblk.patch @@ -5,7 +5,7 @@ chosen { stdout-path = "serial2:115200n8"; - bootargs = "earlycon=uart8250,mmio32,0x11004000 console=ttyS2,115200n8 console=tty1"; -+ bootargs = "root=/dev/fit0 earlycon=uart8250,mmio32,0x11004000 console=ttyS2,115200n8 console=tty1"; ++ bootargs = "root=/dev/fit0 rootwait earlycon=uart8250,mmio32,0x11004000 console=ttyS2,115200n8 console=tty1"; + rootdisk-emmc = <&emmc_rootdisk>; + rootdisk-sd = <&sd_rootdisk>; }; diff --git a/target/linux/realtek/files-5.15/drivers/net/ethernet/rtl838x_eth.c b/target/linux/realtek/files-5.15/drivers/net/ethernet/rtl838x_eth.c index 54e592aeaa..71e7937336 100644 --- a/target/linux/realtek/files-5.15/drivers/net/ethernet/rtl838x_eth.c +++ b/target/linux/realtek/files-5.15/drivers/net/ethernet/rtl838x_eth.c @@ -1658,7 +1658,7 @@ static int rtl839x_mdio_read_paged(struct mii_bus *bus, int mii_id, u16 page, in int err; struct rtl838x_eth_priv *priv = bus->priv; - if (mii_id >= 48 && mii_id <= 49 && priv->id == 0x8393) + if (priv->phy_is_internal[mii_id]) return rtl839x_read_sds_phy(mii_id, regnum); if (regnum & (MII_ADDR_C45 | MII_ADDR_C22_MMD)) { @@ -1797,7 +1797,7 @@ static int rtl839x_mdio_write_paged(struct mii_bus *bus, int mii_id, u16 page, struct rtl838x_eth_priv *priv = bus->priv; int err; - if (mii_id >= 48 && mii_id <= 49 && priv->id == 0x8393) + if (priv->phy_is_internal[mii_id]) return rtl839x_write_sds_phy(mii_id, regnum, value); if (regnum & (MII_ADDR_C45 | MII_ADDR_C22_MMD)) { diff --git a/target/linux/realtek/files-5.15/drivers/net/phy/rtl83xx-phy.c b/target/linux/realtek/files-5.15/drivers/net/phy/rtl83xx-phy.c index 56e8a7f49d..490020989f 100644 --- a/target/linux/realtek/files-5.15/drivers/net/phy/rtl83xx-phy.c +++ b/target/linux/realtek/files-5.15/drivers/net/phy/rtl83xx-phy.c @@ -46,6 +46,8 @@ extern struct mutex smi_lock; /* external RTL821X PHY uses register 0x1e to select media page */ #define RTL821XEXT_MEDIA_PAGE_SELECT 0x1e +#define RTL821X_CHIP_ID 0x6276 + #define RTL821X_MEDIA_PAGE_AUTO 0 #define RTL821X_MEDIA_PAGE_COPPER 1 #define RTL821X_MEDIA_PAGE_FIBRE 3 @@ -834,7 +836,7 @@ static int rtl8380_configure_ext_rtl8218b(struct phy_device *phydev) /* Read internal PHY ID */ phy_write_paged(phydev, 31, 27, 0x0002); val = phy_read_paged(phydev, 31, 28); - if (val != 0x6276) { + if (val != RTL821X_CHIP_ID) { phydev_err(phydev, "Expected external RTL8218B, found PHY-ID %x\n", val); return -1; } @@ -1331,7 +1333,7 @@ static int rtl8380_configure_rtl8214fc(struct phy_device *phydev) phy_write_paged(phydev, 0, RTL821XEXT_MEDIA_PAGE_SELECT, RTL821X_MEDIA_PAGE_COPPER); phy_write_paged(phydev, 0x1f, 0x1b, 0x0002); val = phy_read_paged(phydev, 0x1f, 0x1c); - if (val != 0x6276) { + if (val != RTL821X_CHIP_ID) { phydev_err(phydev, "Expected external RTL8214FC, found PHY-ID %x\n", val); return -1; } diff --git a/tools/include/asm/bitsperlong.h b/tools/include/asm/bitsperlong.h new file mode 100644 index 0000000000..75f320fa91 --- /dev/null +++ b/tools/include/asm/bitsperlong.h @@ -0,0 +1,27 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef __ASM_GENERIC_BITS_PER_LONG +#define __ASM_GENERIC_BITS_PER_LONG + +#ifndef __BITS_PER_LONG +/* + * In order to keep safe and avoid regression, only unify uapi + * bitsperlong.h for some archs which are using newer toolchains + * that have the definitions of __CHAR_BIT__ and __SIZEOF_LONG__. + * See the following link for more info: + * https://lore.kernel.org/linux-arch/b9624545-2c80-49a1-ac3c-39264a591f7b@app.fastmail.com/ + */ +#if defined(__CHAR_BIT__) && defined(__SIZEOF_LONG__) +#define __BITS_PER_LONG (__CHAR_BIT__ * __SIZEOF_LONG__) +#else +/* + * There seems to be no way of detecting this automatically from user + * space, so 64 bit architectures should override this in their + * bitsperlong.h. In particular, an architecture that supports + * both 32 and 64 bit user space must not rely on CONFIG_64BIT + * to decide it, but rather check a compiler provided macro. + */ +#define __BITS_PER_LONG 32 +#endif +#endif + +#endif /* __ASM_GENERIC_BITS_PER_LONG */ diff --git a/tools/include/asm/byteorder.h b/tools/include/asm/byteorder.h new file mode 100644 index 0000000000..8e7d779d7d --- /dev/null +++ b/tools/include/asm/byteorder.h @@ -0,0 +1,12 @@ +#ifndef __ASM_BYTEORDER_H +#define __ASM_BYTEORDER_H + +#include + +#if __BYTE_ORDER == __LITTLE_ENDIAN +#include +#else +#include +#endif + +#endif diff --git a/tools/include/asm/errno-base.h b/tools/include/asm/errno-base.h new file mode 100644 index 0000000000..9653140bff --- /dev/null +++ b/tools/include/asm/errno-base.h @@ -0,0 +1,40 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef _ASM_GENERIC_ERRNO_BASE_H +#define _ASM_GENERIC_ERRNO_BASE_H + +#define EPERM 1 /* Operation not permitted */ +#define ENOENT 2 /* No such file or directory */ +#define ESRCH 3 /* No such process */ +#define EINTR 4 /* Interrupted system call */ +#define EIO 5 /* I/O error */ +#define ENXIO 6 /* No such device or address */ +#define E2BIG 7 /* Argument list too long */ +#define ENOEXEC 8 /* Exec format error */ +#define EBADF 9 /* Bad file number */ +#define ECHILD 10 /* No child processes */ +#define EAGAIN 11 /* Try again */ +#define ENOMEM 12 /* Out of memory */ +#define EACCES 13 /* Permission denied */ +#define EFAULT 14 /* Bad address */ +#define ENOTBLK 15 /* Block device required */ +#define EBUSY 16 /* Device or resource busy */ +#define EEXIST 17 /* File exists */ +#define EXDEV 18 /* Cross-device link */ +#define ENODEV 19 /* No such device */ +#define ENOTDIR 20 /* Not a directory */ +#define EISDIR 21 /* Is a directory */ +#define EINVAL 22 /* Invalid argument */ +#define ENFILE 23 /* File table overflow */ +#define EMFILE 24 /* Too many open files */ +#define ENOTTY 25 /* Not a typewriter */ +#define ETXTBSY 26 /* Text file busy */ +#define EFBIG 27 /* File too large */ +#define ENOSPC 28 /* No space left on device */ +#define ESPIPE 29 /* Illegal seek */ +#define EROFS 30 /* Read-only file system */ +#define EMLINK 31 /* Too many links */ +#define EPIPE 32 /* Broken pipe */ +#define EDOM 33 /* Math argument out of domain of func */ +#define ERANGE 34 /* Math result not representable */ + +#endif diff --git a/tools/include/asm/errno.h b/tools/include/asm/errno.h new file mode 100644 index 0000000000..a96d525330 --- /dev/null +++ b/tools/include/asm/errno.h @@ -0,0 +1,123 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef _ASM_GENERIC_ERRNO_H +#define _ASM_GENERIC_ERRNO_H + +#include + +#define EDEADLK 35 /* Resource deadlock would occur */ +#define ENAMETOOLONG 36 /* File name too long */ +#define ENOLCK 37 /* No record locks available */ + +/* + * This error code is special: arch syscall entry code will return + * -ENOSYS if users try to call a syscall that doesn't exist. To keep + * failures of syscalls that really do exist distinguishable from + * failures due to attempts to use a nonexistent syscall, syscall + * implementations should refrain from returning -ENOSYS. + */ +#define ENOSYS 38 /* Invalid system call number */ + +#define ENOTEMPTY 39 /* Directory not empty */ +#define ELOOP 40 /* Too many symbolic links encountered */ +#define EWOULDBLOCK EAGAIN /* Operation would block */ +#define ENOMSG 42 /* No message of desired type */ +#define EIDRM 43 /* Identifier removed */ +#define ECHRNG 44 /* Channel number out of range */ +#define EL2NSYNC 45 /* Level 2 not synchronized */ +#define EL3HLT 46 /* Level 3 halted */ +#define EL3RST 47 /* Level 3 reset */ +#define ELNRNG 48 /* Link number out of range */ +#define EUNATCH 49 /* Protocol driver not attached */ +#define ENOCSI 50 /* No CSI structure available */ +#define EL2HLT 51 /* Level 2 halted */ +#define EBADE 52 /* Invalid exchange */ +#define EBADR 53 /* Invalid request descriptor */ +#define EXFULL 54 /* Exchange full */ +#define ENOANO 55 /* No anode */ +#define EBADRQC 56 /* Invalid request code */ +#define EBADSLT 57 /* Invalid slot */ + +#define EDEADLOCK EDEADLK + +#define EBFONT 59 /* Bad font file format */ +#define ENOSTR 60 /* Device not a stream */ +#define ENODATA 61 /* No data available */ +#define ETIME 62 /* Timer expired */ +#define ENOSR 63 /* Out of streams resources */ +#define ENONET 64 /* Machine is not on the network */ +#define ENOPKG 65 /* Package not installed */ +#define EREMOTE 66 /* Object is remote */ +#define ENOLINK 67 /* Link has been severed */ +#define EADV 68 /* Advertise error */ +#define ESRMNT 69 /* Srmount error */ +#define ECOMM 70 /* Communication error on send */ +#define EPROTO 71 /* Protocol error */ +#define EMULTIHOP 72 /* Multihop attempted */ +#define EDOTDOT 73 /* RFS specific error */ +#define EBADMSG 74 /* Not a data message */ +#define EOVERFLOW 75 /* Value too large for defined data type */ +#define ENOTUNIQ 76 /* Name not unique on network */ +#define EBADFD 77 /* File descriptor in bad state */ +#define EREMCHG 78 /* Remote address changed */ +#define ELIBACC 79 /* Can not access a needed shared library */ +#define ELIBBAD 80 /* Accessing a corrupted shared library */ +#define ELIBSCN 81 /* .lib section in a.out corrupted */ +#define ELIBMAX 82 /* Attempting to link in too many shared libraries */ +#define ELIBEXEC 83 /* Cannot exec a shared library directly */ +#define EILSEQ 84 /* Illegal byte sequence */ +#define ERESTART 85 /* Interrupted system call should be restarted */ +#define ESTRPIPE 86 /* Streams pipe error */ +#define EUSERS 87 /* Too many users */ +#define ENOTSOCK 88 /* Socket operation on non-socket */ +#define EDESTADDRREQ 89 /* Destination address required */ +#define EMSGSIZE 90 /* Message too long */ +#define EPROTOTYPE 91 /* Protocol wrong type for socket */ +#define ENOPROTOOPT 92 /* Protocol not available */ +#define EPROTONOSUPPORT 93 /* Protocol not supported */ +#define ESOCKTNOSUPPORT 94 /* Socket type not supported */ +#define EOPNOTSUPP 95 /* Operation not supported on transport endpoint */ +#define EPFNOSUPPORT 96 /* Protocol family not supported */ +#define EAFNOSUPPORT 97 /* Address family not supported by protocol */ +#define EADDRINUSE 98 /* Address already in use */ +#define EADDRNOTAVAIL 99 /* Cannot assign requested address */ +#define ENETDOWN 100 /* Network is down */ +#define ENETUNREACH 101 /* Network is unreachable */ +#define ENETRESET 102 /* Network dropped connection because of reset */ +#define ECONNABORTED 103 /* Software caused connection abort */ +#define ECONNRESET 104 /* Connection reset by peer */ +#define ENOBUFS 105 /* No buffer space available */ +#define EISCONN 106 /* Transport endpoint is already connected */ +#define ENOTCONN 107 /* Transport endpoint is not connected */ +#define ESHUTDOWN 108 /* Cannot send after transport endpoint shutdown */ +#define ETOOMANYREFS 109 /* Too many references: cannot splice */ +#define ETIMEDOUT 110 /* Connection timed out */ +#define ECONNREFUSED 111 /* Connection refused */ +#define EHOSTDOWN 112 /* Host is down */ +#define EHOSTUNREACH 113 /* No route to host */ +#define EALREADY 114 /* Operation already in progress */ +#define EINPROGRESS 115 /* Operation now in progress */ +#define ESTALE 116 /* Stale file handle */ +#define EUCLEAN 117 /* Structure needs cleaning */ +#define ENOTNAM 118 /* Not a XENIX named type file */ +#define ENAVAIL 119 /* No XENIX semaphores available */ +#define EISNAM 120 /* Is a named type file */ +#define EREMOTEIO 121 /* Remote I/O error */ +#define EDQUOT 122 /* Quota exceeded */ + +#define ENOMEDIUM 123 /* No medium found */ +#define EMEDIUMTYPE 124 /* Wrong medium type */ +#define ECANCELED 125 /* Operation Canceled */ +#define ENOKEY 126 /* Required key not available */ +#define EKEYEXPIRED 127 /* Key has expired */ +#define EKEYREVOKED 128 /* Key has been revoked */ +#define EKEYREJECTED 129 /* Key was rejected by service */ + +/* for robust mutexes */ +#define EOWNERDEAD 130 /* Owner died */ +#define ENOTRECOVERABLE 131 /* State not recoverable */ + +#define ERFKILL 132 /* Operation not possible due to RF-kill */ + +#define EHWPOISON 133 /* Memory page has hardware error */ + +#endif diff --git a/tools/include/asm/posix_types.h b/tools/include/asm/posix_types.h new file mode 100644 index 0000000000..10f5e6ef4a --- /dev/null +++ b/tools/include/asm/posix_types.h @@ -0,0 +1,100 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef __ASM_GENERIC_POSIX_TYPES_H +#define __ASM_GENERIC_POSIX_TYPES_H + +#include + +/* + * This file is generally used by user-level software, so you need to + * be a little careful about namespace pollution etc. + * + * First the types that are often defined in different ways across + * architectures, so that you can override them. + */ + +#ifndef __kernel_long_t +typedef long __kernel_long_t; +typedef unsigned long __kernel_ulong_t; +#endif + +#ifndef __kernel_ino_t +typedef __kernel_ulong_t __kernel_ino_t; +#endif + +#ifndef __kernel_mode_t +typedef unsigned int __kernel_mode_t; +#endif + +#ifndef __kernel_pid_t +typedef int __kernel_pid_t; +#endif + +#ifndef __kernel_ipc_pid_t +typedef int __kernel_ipc_pid_t; +#endif + +#ifndef __kernel_uid_t +typedef unsigned int __kernel_uid_t; +typedef unsigned int __kernel_gid_t; +#endif + +#ifndef __kernel_suseconds_t +typedef __kernel_long_t __kernel_suseconds_t; +#endif + +#ifndef __kernel_daddr_t +typedef int __kernel_daddr_t; +#endif + +#ifndef __kernel_uid32_t +typedef unsigned int __kernel_uid32_t; +typedef unsigned int __kernel_gid32_t; +#endif + +#ifndef __kernel_old_uid_t +typedef __kernel_uid_t __kernel_old_uid_t; +typedef __kernel_gid_t __kernel_old_gid_t; +#endif + +#ifndef __kernel_old_dev_t +typedef unsigned int __kernel_old_dev_t; +#endif + +/* + * Most 32 bit architectures use "unsigned int" size_t, + * and all 64 bit architectures use "unsigned long" size_t. + */ +#ifndef __kernel_size_t +#if __BITS_PER_LONG != 64 +typedef unsigned int __kernel_size_t; +typedef int __kernel_ssize_t; +typedef int __kernel_ptrdiff_t; +#else +typedef __kernel_ulong_t __kernel_size_t; +typedef __kernel_long_t __kernel_ssize_t; +typedef __kernel_long_t __kernel_ptrdiff_t; +#endif +#endif + +#ifndef __kernel_fsid_t +typedef struct { + int val[2]; +} __kernel_fsid_t; +#endif + +/* + * anything below here should be completely generic + */ +typedef __kernel_long_t __kernel_off_t; +typedef long long __kernel_loff_t; +typedef __kernel_long_t __kernel_old_time_t; +typedef __kernel_long_t __kernel_time_t; +typedef long long __kernel_time64_t; +typedef __kernel_long_t __kernel_clock_t; +typedef int __kernel_timer_t; +typedef int __kernel_clockid_t; +typedef char * __kernel_caddr_t; +typedef unsigned short __kernel_uid16_t; +typedef unsigned short __kernel_gid16_t; + +#endif /* __ASM_GENERIC_POSIX_TYPES_H */ diff --git a/tools/include/asm/swab.h b/tools/include/asm/swab.h new file mode 100644 index 0000000000..f2da4e4fd4 --- /dev/null +++ b/tools/include/asm/swab.h @@ -0,0 +1,19 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef _ASM_GENERIC_SWAB_H +#define _ASM_GENERIC_SWAB_H + +#include + +/* + * 32 bit architectures typically (but not always) want to + * set __SWAB_64_THRU_32__. In user space, this is only + * valid if the compiler supports 64 bit data types. + */ + +#if __BITS_PER_LONG == 32 +#if defined(__GNUC__) && !defined(__STRICT_ANSI__) || defined(__KERNEL__) +#define __SWAB_64_THRU_32__ +#endif +#endif + +#endif /* _ASM_GENERIC_SWAB_H */ diff --git a/tools/include/linux/big_endian.h b/tools/include/linux/big_endian.h new file mode 100644 index 0000000000..3bb87c573c --- /dev/null +++ b/tools/include/linux/big_endian.h @@ -0,0 +1,107 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef _LINUX_BYTEORDER_BIG_ENDIAN_H +#define _LINUX_BYTEORDER_BIG_ENDIAN_H + +#ifndef __BIG_ENDIAN +#define __BIG_ENDIAN 4321 +#endif +#ifndef __BIG_ENDIAN_BITFIELD +#define __BIG_ENDIAN_BITFIELD +#endif + +#include +#include +#include + +#define __constant_htonl(x) ((__be32)(__u32)(x)) +#define __constant_ntohl(x) ((__u32)(__be32)(x)) +#define __constant_htons(x) ((__be16)(__u16)(x)) +#define __constant_ntohs(x) ((__u16)(__be16)(x)) +#define __constant_cpu_to_le64(x) ((__le64)___constant_swab64((x))) +#define __constant_le64_to_cpu(x) ___constant_swab64((__u64)(__le64)(x)) +#define __constant_cpu_to_le32(x) ((__le32)___constant_swab32((x))) +#define __constant_le32_to_cpu(x) ___constant_swab32((__u32)(__le32)(x)) +#define __constant_cpu_to_le16(x) ((__le16)___constant_swab16((x))) +#define __constant_le16_to_cpu(x) ___constant_swab16((__u16)(__le16)(x)) +#define __constant_cpu_to_be64(x) ((__be64)(__u64)(x)) +#define __constant_be64_to_cpu(x) ((__u64)(__be64)(x)) +#define __constant_cpu_to_be32(x) ((__be32)(__u32)(x)) +#define __constant_be32_to_cpu(x) ((__u32)(__be32)(x)) +#define __constant_cpu_to_be16(x) ((__be16)(__u16)(x)) +#define __constant_be16_to_cpu(x) ((__u16)(__be16)(x)) +#define __cpu_to_le64(x) ((__le64)__swab64((x))) +#define __le64_to_cpu(x) __swab64((__u64)(__le64)(x)) +#define __cpu_to_le32(x) ((__le32)__swab32((x))) +#define __le32_to_cpu(x) __swab32((__u32)(__le32)(x)) +#define __cpu_to_le16(x) ((__le16)__swab16((x))) +#define __le16_to_cpu(x) __swab16((__u16)(__le16)(x)) +#define __cpu_to_be64(x) ((__be64)(__u64)(x)) +#define __be64_to_cpu(x) ((__u64)(__be64)(x)) +#define __cpu_to_be32(x) ((__be32)(__u32)(x)) +#define __be32_to_cpu(x) ((__u32)(__be32)(x)) +#define __cpu_to_be16(x) ((__be16)(__u16)(x)) +#define __be16_to_cpu(x) ((__u16)(__be16)(x)) + +static __always_inline __le64 __cpu_to_le64p(const __u64 *p) +{ + return (__le64)__swab64p(p); +} +static __always_inline __u64 __le64_to_cpup(const __le64 *p) +{ + return __swab64p((__u64 *)p); +} +static __always_inline __le32 __cpu_to_le32p(const __u32 *p) +{ + return (__le32)__swab32p(p); +} +static __always_inline __u32 __le32_to_cpup(const __le32 *p) +{ + return __swab32p((__u32 *)p); +} +static __always_inline __le16 __cpu_to_le16p(const __u16 *p) +{ + return (__le16)__swab16p(p); +} +static __always_inline __u16 __le16_to_cpup(const __le16 *p) +{ + return __swab16p((__u16 *)p); +} +static __always_inline __be64 __cpu_to_be64p(const __u64 *p) +{ + return (__be64)*p; +} +static __always_inline __u64 __be64_to_cpup(const __be64 *p) +{ + return (__u64)*p; +} +static __always_inline __be32 __cpu_to_be32p(const __u32 *p) +{ + return (__be32)*p; +} +static __always_inline __u32 __be32_to_cpup(const __be32 *p) +{ + return (__u32)*p; +} +static __always_inline __be16 __cpu_to_be16p(const __u16 *p) +{ + return (__be16)*p; +} +static __always_inline __u16 __be16_to_cpup(const __be16 *p) +{ + return (__u16)*p; +} +#define __cpu_to_le64s(x) __swab64s((x)) +#define __le64_to_cpus(x) __swab64s((x)) +#define __cpu_to_le32s(x) __swab32s((x)) +#define __le32_to_cpus(x) __swab32s((x)) +#define __cpu_to_le16s(x) __swab16s((x)) +#define __le16_to_cpus(x) __swab16s((x)) +#define __cpu_to_be64s(x) do { (void)(x); } while (0) +#define __be64_to_cpus(x) do { (void)(x); } while (0) +#define __cpu_to_be32s(x) do { (void)(x); } while (0) +#define __be32_to_cpus(x) do { (void)(x); } while (0) +#define __cpu_to_be16s(x) do { (void)(x); } while (0) +#define __be16_to_cpus(x) do { (void)(x); } while (0) + + +#endif /* _LINUX_BYTEORDER_BIG_ENDIAN_H */ diff --git a/tools/include/linux/errno.h b/tools/include/linux/errno.h new file mode 100644 index 0000000000..70f2bd34e3 --- /dev/null +++ b/tools/include/linux/errno.h @@ -0,0 +1 @@ +#include diff --git a/tools/include/linux/little_endian.h b/tools/include/linux/little_endian.h new file mode 100644 index 0000000000..ba6c199c9b --- /dev/null +++ b/tools/include/linux/little_endian.h @@ -0,0 +1,107 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef _LINUX_BYTEORDER_LITTLE_ENDIAN_H +#define _LINUX_BYTEORDER_LITTLE_ENDIAN_H + +#ifndef __LITTLE_ENDIAN +#define __LITTLE_ENDIAN 1234 +#endif +#ifndef __LITTLE_ENDIAN_BITFIELD +#define __LITTLE_ENDIAN_BITFIELD +#endif + +#include +#include +#include + +#define __constant_htonl(x) ((__be32)___constant_swab32((x))) +#define __constant_ntohl(x) ___constant_swab32((__be32)(x)) +#define __constant_htons(x) ((__be16)___constant_swab16((x))) +#define __constant_ntohs(x) ___constant_swab16((__be16)(x)) +#define __constant_cpu_to_le64(x) ((__le64)(__u64)(x)) +#define __constant_le64_to_cpu(x) ((__u64)(__le64)(x)) +#define __constant_cpu_to_le32(x) ((__le32)(__u32)(x)) +#define __constant_le32_to_cpu(x) ((__u32)(__le32)(x)) +#define __constant_cpu_to_le16(x) ((__le16)(__u16)(x)) +#define __constant_le16_to_cpu(x) ((__u16)(__le16)(x)) +#define __constant_cpu_to_be64(x) ((__be64)___constant_swab64((x))) +#define __constant_be64_to_cpu(x) ___constant_swab64((__u64)(__be64)(x)) +#define __constant_cpu_to_be32(x) ((__be32)___constant_swab32((x))) +#define __constant_be32_to_cpu(x) ___constant_swab32((__u32)(__be32)(x)) +#define __constant_cpu_to_be16(x) ((__be16)___constant_swab16((x))) +#define __constant_be16_to_cpu(x) ___constant_swab16((__u16)(__be16)(x)) +#define __cpu_to_le64(x) ((__le64)(__u64)(x)) +#define __le64_to_cpu(x) ((__u64)(__le64)(x)) +#define __cpu_to_le32(x) ((__le32)(__u32)(x)) +#define __le32_to_cpu(x) ((__u32)(__le32)(x)) +#define __cpu_to_le16(x) ((__le16)(__u16)(x)) +#define __le16_to_cpu(x) ((__u16)(__le16)(x)) +#define __cpu_to_be64(x) ((__be64)__swab64((x))) +#define __be64_to_cpu(x) __swab64((__u64)(__be64)(x)) +#define __cpu_to_be32(x) ((__be32)__swab32((x))) +#define __be32_to_cpu(x) __swab32((__u32)(__be32)(x)) +#define __cpu_to_be16(x) ((__be16)__swab16((x))) +#define __be16_to_cpu(x) __swab16((__u16)(__be16)(x)) + +static __always_inline __le64 __cpu_to_le64p(const __u64 *p) +{ + return (__le64)*p; +} +static __always_inline __u64 __le64_to_cpup(const __le64 *p) +{ + return (__u64)*p; +} +static __always_inline __le32 __cpu_to_le32p(const __u32 *p) +{ + return (__le32)*p; +} +static __always_inline __u32 __le32_to_cpup(const __le32 *p) +{ + return (__u32)*p; +} +static __always_inline __le16 __cpu_to_le16p(const __u16 *p) +{ + return (__le16)*p; +} +static __always_inline __u16 __le16_to_cpup(const __le16 *p) +{ + return (__u16)*p; +} +static __always_inline __be64 __cpu_to_be64p(const __u64 *p) +{ + return (__be64)__swab64p(p); +} +static __always_inline __u64 __be64_to_cpup(const __be64 *p) +{ + return __swab64p((__u64 *)p); +} +static __always_inline __be32 __cpu_to_be32p(const __u32 *p) +{ + return (__be32)__swab32p(p); +} +static __always_inline __u32 __be32_to_cpup(const __be32 *p) +{ + return __swab32p((__u32 *)p); +} +static __always_inline __be16 __cpu_to_be16p(const __u16 *p) +{ + return (__be16)__swab16p(p); +} +static __always_inline __u16 __be16_to_cpup(const __be16 *p) +{ + return __swab16p((__u16 *)p); +} +#define __cpu_to_le64s(x) do { (void)(x); } while (0) +#define __le64_to_cpus(x) do { (void)(x); } while (0) +#define __cpu_to_le32s(x) do { (void)(x); } while (0) +#define __le32_to_cpus(x) do { (void)(x); } while (0) +#define __cpu_to_le16s(x) do { (void)(x); } while (0) +#define __le16_to_cpus(x) do { (void)(x); } while (0) +#define __cpu_to_be64s(x) __swab64s((x)) +#define __be64_to_cpus(x) __swab64s((x)) +#define __cpu_to_be32s(x) __swab32s((x)) +#define __be32_to_cpus(x) __swab32s((x)) +#define __cpu_to_be16s(x) __swab16s((x)) +#define __be16_to_cpus(x) __swab16s((x)) + + +#endif /* _LINUX_BYTEORDER_LITTLE_ENDIAN_H */ diff --git a/tools/include/linux/stddef.h b/tools/include/linux/stddef.h new file mode 100644 index 0000000000..e3d20e76b1 --- /dev/null +++ b/tools/include/linux/stddef.h @@ -0,0 +1,56 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef _UAPI_LINUX_STDDEF_H +#define _UAPI_LINUX_STDDEF_H + +#ifndef __always_inline +#define __always_inline inline +#endif + +/** + * __struct_group() - Create a mirrored named and anonyomous struct + * + * @TAG: The tag name for the named sub-struct (usually empty) + * @NAME: The identifier name of the mirrored sub-struct + * @ATTRS: Any struct attributes (usually empty) + * @MEMBERS: The member declarations for the mirrored structs + * + * Used to create an anonymous union of two structs with identical layout + * and size: one anonymous and one named. The former's members can be used + * normally without sub-struct naming, and the latter can be used to + * reason about the start, end, and size of the group of struct members. + * The named struct can also be explicitly tagged for layer reuse, as well + * as both having struct attributes appended. + */ +#define __struct_group(TAG, NAME, ATTRS, MEMBERS...) \ + union { \ + struct { MEMBERS } ATTRS; \ + struct TAG { MEMBERS } ATTRS NAME; \ + } ATTRS + +#ifdef __cplusplus +/* sizeof(struct{}) is 1 in C++, not 0, can't use C version of the macro. */ +#define __DECLARE_FLEX_ARRAY(T, member) \ + T member[0] +#else +/** + * __DECLARE_FLEX_ARRAY() - Declare a flexible array usable in a union + * + * @TYPE: The type of each flexible array element + * @NAME: The name of the flexible array member + * + * In order to have a flexible array member in a union or alone in a + * struct, it needs to be wrapped in an anonymous struct with at least 1 + * named member, but that member can be empty. + */ +#define __DECLARE_FLEX_ARRAY(TYPE, NAME) \ + struct { \ + struct { } __empty_ ## NAME; \ + TYPE NAME[]; \ + } +#endif + +#ifndef __counted_by +#define __counted_by(m) +#endif + +#endif /* _UAPI_LINUX_STDDEF_H */ diff --git a/tools/include/linux/swab.h b/tools/include/linux/swab.h new file mode 100644 index 0000000000..7e3bad56cf --- /dev/null +++ b/tools/include/linux/swab.h @@ -0,0 +1,305 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef _LINUX_SWAB_H +#define _LINUX_SWAB_H + +#include +#include +#include +#include + +/* + * casts are necessary for constants, because we never know how for sure + * how U/UL/ULL map to __u16, __u32, __u64. At least not in a portable way. + */ +#define ___constant_swab16(x) ((__u16)( \ + (((__u16)(x) & (__u16)0x00ffU) << 8) | \ + (((__u16)(x) & (__u16)0xff00U) >> 8))) + +#define ___constant_swab32(x) ((__u32)( \ + (((__u32)(x) & (__u32)0x000000ffUL) << 24) | \ + (((__u32)(x) & (__u32)0x0000ff00UL) << 8) | \ + (((__u32)(x) & (__u32)0x00ff0000UL) >> 8) | \ + (((__u32)(x) & (__u32)0xff000000UL) >> 24))) + +#define ___constant_swab64(x) ((__u64)( \ + (((__u64)(x) & (__u64)0x00000000000000ffULL) << 56) | \ + (((__u64)(x) & (__u64)0x000000000000ff00ULL) << 40) | \ + (((__u64)(x) & (__u64)0x0000000000ff0000ULL) << 24) | \ + (((__u64)(x) & (__u64)0x00000000ff000000ULL) << 8) | \ + (((__u64)(x) & (__u64)0x000000ff00000000ULL) >> 8) | \ + (((__u64)(x) & (__u64)0x0000ff0000000000ULL) >> 24) | \ + (((__u64)(x) & (__u64)0x00ff000000000000ULL) >> 40) | \ + (((__u64)(x) & (__u64)0xff00000000000000ULL) >> 56))) + +#define ___constant_swahw32(x) ((__u32)( \ + (((__u32)(x) & (__u32)0x0000ffffUL) << 16) | \ + (((__u32)(x) & (__u32)0xffff0000UL) >> 16))) + +#define ___constant_swahb32(x) ((__u32)( \ + (((__u32)(x) & (__u32)0x00ff00ffUL) << 8) | \ + (((__u32)(x) & (__u32)0xff00ff00UL) >> 8))) + +/* + * Implement the following as inlines, but define the interface using + * macros to allow constant folding when possible: + * ___swab16, ___swab32, ___swab64, ___swahw32, ___swahb32 + */ + +static __inline__ __u16 __fswab16(__u16 val) +{ +#if defined (__arch_swab16) + return __arch_swab16(val); +#else + return ___constant_swab16(val); +#endif +} + +static __inline__ __u32 __fswab32(__u32 val) +{ +#if defined(__arch_swab32) + return __arch_swab32(val); +#else + return ___constant_swab32(val); +#endif +} + +static __inline__ __u64 __fswab64(__u64 val) +{ +#if defined (__arch_swab64) + return __arch_swab64(val); +#elif defined(__SWAB_64_THRU_32__) + __u32 h = val >> 32; + __u32 l = val & ((1ULL << 32) - 1); + return (((__u64)__fswab32(l)) << 32) | ((__u64)(__fswab32(h))); +#else + return ___constant_swab64(val); +#endif +} + +static __inline__ __u32 __fswahw32(__u32 val) +{ +#ifdef __arch_swahw32 + return __arch_swahw32(val); +#else + return ___constant_swahw32(val); +#endif +} + +static __inline__ __u32 __fswahb32(__u32 val) +{ +#ifdef __arch_swahb32 + return __arch_swahb32(val); +#else + return ___constant_swahb32(val); +#endif +} + +/** + * __swab16 - return a byteswapped 16-bit value + * @x: value to byteswap + */ +#ifdef __HAVE_BUILTIN_BSWAP16__ +#define __swab16(x) (__u16)__builtin_bswap16((__u16)(x)) +#else +#define __swab16(x) \ + (__u16)(__builtin_constant_p(x) ? \ + ___constant_swab16(x) : \ + __fswab16(x)) +#endif + +/** + * __swab32 - return a byteswapped 32-bit value + * @x: value to byteswap + */ +#ifdef __HAVE_BUILTIN_BSWAP32__ +#define __swab32(x) (__u32)__builtin_bswap32((__u32)(x)) +#else +#define __swab32(x) \ + (__u32)(__builtin_constant_p(x) ? \ + ___constant_swab32(x) : \ + __fswab32(x)) +#endif + +/** + * __swab64 - return a byteswapped 64-bit value + * @x: value to byteswap + */ +#ifdef __HAVE_BUILTIN_BSWAP64__ +#define __swab64(x) (__u64)__builtin_bswap64((__u64)(x)) +#else +#define __swab64(x) \ + (__u64)(__builtin_constant_p(x) ? \ + ___constant_swab64(x) : \ + __fswab64(x)) +#endif + +static __always_inline unsigned long __swab(const unsigned long y) +{ +#if __BITS_PER_LONG == 64 + return __swab64(y); +#else /* __BITS_PER_LONG == 32 */ + return __swab32(y); +#endif +} + +/** + * __swahw32 - return a word-swapped 32-bit value + * @x: value to wordswap + * + * __swahw32(0x12340000) is 0x00001234 + */ +#define __swahw32(x) \ + (__builtin_constant_p((__u32)(x)) ? \ + ___constant_swahw32(x) : \ + __fswahw32(x)) + +/** + * __swahb32 - return a high and low byte-swapped 32-bit value + * @x: value to byteswap + * + * __swahb32(0x12345678) is 0x34127856 + */ +#define __swahb32(x) \ + (__builtin_constant_p((__u32)(x)) ? \ + ___constant_swahb32(x) : \ + __fswahb32(x)) + +/** + * __swab16p - return a byteswapped 16-bit value from a pointer + * @p: pointer to a naturally-aligned 16-bit value + */ +static __always_inline __u16 __swab16p(const __u16 *p) +{ +#ifdef __arch_swab16p + return __arch_swab16p(p); +#else + return __swab16(*p); +#endif +} + +/** + * __swab32p - return a byteswapped 32-bit value from a pointer + * @p: pointer to a naturally-aligned 32-bit value + */ +static __always_inline __u32 __swab32p(const __u32 *p) +{ +#ifdef __arch_swab32p + return __arch_swab32p(p); +#else + return __swab32(*p); +#endif +} + +/** + * __swab64p - return a byteswapped 64-bit value from a pointer + * @p: pointer to a naturally-aligned 64-bit value + */ +static __always_inline __u64 __swab64p(const __u64 *p) +{ +#ifdef __arch_swab64p + return __arch_swab64p(p); +#else + return __swab64(*p); +#endif +} + +/** + * __swahw32p - return a wordswapped 32-bit value from a pointer + * @p: pointer to a naturally-aligned 32-bit value + * + * See __swahw32() for details of wordswapping. + */ +static __inline__ __u32 __swahw32p(const __u32 *p) +{ +#ifdef __arch_swahw32p + return __arch_swahw32p(p); +#else + return __swahw32(*p); +#endif +} + +/** + * __swahb32p - return a high and low byteswapped 32-bit value from a pointer + * @p: pointer to a naturally-aligned 32-bit value + * + * See __swahb32() for details of high/low byteswapping. + */ +static __inline__ __u32 __swahb32p(const __u32 *p) +{ +#ifdef __arch_swahb32p + return __arch_swahb32p(p); +#else + return __swahb32(*p); +#endif +} + +/** + * __swab16s - byteswap a 16-bit value in-place + * @p: pointer to a naturally-aligned 16-bit value + */ +static __inline__ void __swab16s(__u16 *p) +{ +#ifdef __arch_swab16s + __arch_swab16s(p); +#else + *p = __swab16p(p); +#endif +} +/** + * __swab32s - byteswap a 32-bit value in-place + * @p: pointer to a naturally-aligned 32-bit value + */ +static __always_inline void __swab32s(__u32 *p) +{ +#ifdef __arch_swab32s + __arch_swab32s(p); +#else + *p = __swab32p(p); +#endif +} + +/** + * __swab64s - byteswap a 64-bit value in-place + * @p: pointer to a naturally-aligned 64-bit value + */ +static __always_inline void __swab64s(__u64 *p) +{ +#ifdef __arch_swab64s + __arch_swab64s(p); +#else + *p = __swab64p(p); +#endif +} + +/** + * __swahw32s - wordswap a 32-bit value in-place + * @p: pointer to a naturally-aligned 32-bit value + * + * See __swahw32() for details of wordswapping + */ +static __inline__ void __swahw32s(__u32 *p) +{ +#ifdef __arch_swahw32s + __arch_swahw32s(p); +#else + *p = __swahw32p(p); +#endif +} + +/** + * __swahb32s - high and low byteswap a 32-bit value in-place + * @p: pointer to a naturally-aligned 32-bit value + * + * See __swahb32() for details of high and low byte swapping + */ +static __inline__ void __swahb32s(__u32 *p) +{ +#ifdef __arch_swahb32s + __arch_swahb32s(p); +#else + *p = __swahb32p(p); +#endif +} + + +#endif /* _LINUX_SWAB_H */