gemini: Add kernel v6.1 patches
authorLinus Walleij <linus.walleij@linaro.org>
Wed, 31 May 2023 21:21:05 +0000 (23:21 +0200)
committerChristian Lamparter <chunkeey@gmail.com>
Thu, 1 Jun 2023 21:54:20 +0000 (23:54 +0200)
This adds a bunch of patches for the v6.1 Gemini kernel.

For v5.15 this was down to a single upstream patch, but for
kernel v6.2 I reworked the USB code for FOTG210, so instead of
carrying over the half-baked and incomplete patch from v5.15
I just backported all the v6.2 patches, 31 in total, as it
creates full device USB mode for e.g. D-Link DNS-313.

Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
Signed-off-by: Christian Lamparter <chunkeey@gmail.com>
30 files changed:
target/linux/gemini/patches-6.1/0001-usb-phy-phy-gpio-vbus-usb-Add-device-tree-probing.patch [new file with mode: 0644]
target/linux/gemini/patches-6.1/0002-usb-fotg210-Collect-pieces-of-dual-mode-controller.patch [new file with mode: 0644]
target/linux/gemini/patches-6.1/0003-usb-fotg210-Compile-into-one-module.patch [new file with mode: 0644]
target/linux/gemini/patches-6.1/0004-usb-fotg210-Select-subdriver-by-mode.patch [new file with mode: 0644]
target/linux/gemini/patches-6.1/0005-usb-fotg2-add-Gemini-specific-handling.patch [new file with mode: 0644]
target/linux/gemini/patches-6.1/0006-usb-fotg210-Fix-Kconfig-for-USB-host-modules.patch [new file with mode: 0644]
target/linux/gemini/patches-6.1/0007-usb-USB_FOTG210-should-depend-on-ARCH_GEMINI.patch [new file with mode: 0644]
target/linux/gemini/patches-6.1/0008-fotg210-udc-Use-dev-pointer-in-probe-and-dev_message.patch [new file with mode: 0644]
target/linux/gemini/patches-6.1/0009-fotg210-udc-Support-optional-external-PHY.patch [new file with mode: 0644]
target/linux/gemini/patches-6.1/0010-fotg210-udc-Handle-PCLK.patch [new file with mode: 0644]
target/linux/gemini/patches-6.1/0011-fotg210-udc-Get-IRQ-using-platform_get_irq.patch [new file with mode: 0644]
target/linux/gemini/patches-6.1/0012-usb-fotg210-udc-Remove-a-useless-assignment.patch [new file with mode: 0644]
target/linux/gemini/patches-6.1/0013-usb-fotg210-udc-fix-potential-memory-leak-in-fotg210.patch [new file with mode: 0644]
target/linux/gemini/patches-6.1/0014-usb-fotg210-fix-OTG-only-build.patch [new file with mode: 0644]
target/linux/gemini/patches-6.1/0015-usb-fotg210-udc-fix-error-return-code-in-fotg210_udc.patch [new file with mode: 0644]
target/linux/gemini/patches-6.1/0016-usb-fotg210-List-different-variants.patch [new file with mode: 0644]
target/linux/gemini/patches-6.1/0017-usb-fotg210-Acquire-memory-resource-in-core.patch [new file with mode: 0644]
target/linux/gemini/patches-6.1/0018-usb-fotg210-Move-clock-handling-to-core.patch [new file with mode: 0644]
target/linux/gemini/patches-6.1/0019-usb-fotg210-Check-role-register-in-core.patch [new file with mode: 0644]
target/linux/gemini/patches-6.1/0020-usb-fotg210-udc-Assign-of_node-and-speed-on-start.patch [new file with mode: 0644]
target/linux/gemini/patches-6.1/0021-usb-fotg210-udc-Implement-VBUS-session.patch [new file with mode: 0644]
target/linux/gemini/patches-6.1/0022-fotg210-udc-Introduce-and-use-a-fotg210_ack_int-func.patch [new file with mode: 0644]
target/linux/gemini/patches-6.1/0023-fotg210-udc-Improve-device-initialization.patch [new file with mode: 0644]
target/linux/gemini/patches-6.1/0024-usb-fotg210-hcd-use-sysfs_emit-to-instead-of-scnprin.patch [new file with mode: 0644]
target/linux/gemini/patches-6.1/0025-ARM-dts-gemini-Push-down-flash-address-size-cells.patch [new file with mode: 0644]
target/linux/gemini/patches-6.1/0026-ARM-dts-gemini-wbd111-Use-RedBoot-partion-parser.patch [new file with mode: 0644]
target/linux/gemini/patches-6.1/0027-ARM-dts-gemini-wbd222-Use-RedBoot-partion-parser.patch [new file with mode: 0644]
target/linux/gemini/patches-6.1/0028-ARM-dts-gemini-Fix-USB-block-version.patch [new file with mode: 0644]
target/linux/gemini/patches-6.1/0029-ARM-dts-gemini-Enable-DNS313-FOTG210-as-periph.patch [new file with mode: 0644]
target/linux/gemini/patches-6.1/300-ARM-dts-Augment-DIR-685-partition-table-for-OpenWrt.patch [new file with mode: 0644]

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
new file mode 100644 (file)
index 0000000..943b166
--- /dev/null
@@ -0,0 +1,67 @@
+From d5a026cc8306ccd3e99e1455c87e38f8e6fa18df Mon Sep 17 00:00:00 2001
+From: Linus Walleij <linus.walleij@linaro.org>
+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 <robh@kernel.org>
+Cc: Prashant Malani <pmalani@chromium.org>
+Cc: Felipe Balbi <balbi@kernel.org>
+Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
+Link: https://lore.kernel.org/r/20221106230506.1646101-1-linus.walleij@linaro.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+--- 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
new file mode 100644 (file)
index 0000000..902bf4c
--- /dev/null
@@ -0,0 +1,15993 @@
+From 30367636930864f71b2bd462adedcf8484313864 Mon Sep 17 00:00:00 2001
+From: Linus Walleij <linus.walleij@linaro.org>
+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 <fabian@ritter-vogt.de>
+Cc: Yuan-Hsin Chen <yhchen@faraday-tech.com>
+Cc: Felipe Balbi <balbi@kernel.org>
+Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
+Link: https://lore.kernel.org/r/20221023144708.3596563-1-linus.walleij@linaro.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+--- 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,5727 +0,0 @@
+-// SPDX-License-Identifier: GPL-2.0+
+-/* Faraday FOTG210 EHCI-like driver
+- *
+- * Copyright (c) 2013 Faraday Technology Corporation
+- *
+- * Author: Yuan-Hsin Chen <yhchen@faraday-tech.com>
+- *       Feng-Hsin Chiang <john453@faraday-tech.com>
+- *       Po-Yu Chuang <ratbert.chuang@gmail.com>
+- *
+- * Most of code borrowed from the Linux-3.7 EHCI driver
+- */
+-#include <linux/module.h>
+-#include <linux/of.h>
+-#include <linux/device.h>
+-#include <linux/dmapool.h>
+-#include <linux/kernel.h>
+-#include <linux/delay.h>
+-#include <linux/ioport.h>
+-#include <linux/sched.h>
+-#include <linux/vmalloc.h>
+-#include <linux/errno.h>
+-#include <linux/init.h>
+-#include <linux/hrtimer.h>
+-#include <linux/list.h>
+-#include <linux/interrupt.h>
+-#include <linux/usb.h>
+-#include <linux/usb/hcd.h>
+-#include <linux/moduleparam.h>
+-#include <linux/dma-mapping.h>
+-#include <linux/debugfs.h>
+-#include <linux/slab.h>
+-#include <linux/uaccess.h>
+-#include <linux/platform_device.h>
+-#include <linux/io.h>
+-#include <linux/iopoll.h>
+-#include <linux/clk.h>
+-
+-#include <asm/byteorder.h>
+-#include <asm/irq.h>
+-#include <asm/unaligned.h>
+-
+-#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;
+-              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);
+--- /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 <yhchen@faraday-tech.com>
++ *       Feng-Hsin Chiang <john453@faraday-tech.com>
++ *       Po-Yu Chuang <ratbert.chuang@gmail.com>
++ *
++ * Most of code borrowed from the Linux-3.7 EHCI driver
++ */
++#include <linux/module.h>
++#include <linux/of.h>
++#include <linux/device.h>
++#include <linux/dmapool.h>
++#include <linux/kernel.h>
++#include <linux/delay.h>
++#include <linux/ioport.h>
++#include <linux/sched.h>
++#include <linux/vmalloc.h>
++#include <linux/errno.h>
++#include <linux/init.h>
++#include <linux/hrtimer.h>
++#include <linux/list.h>
++#include <linux/interrupt.h>
++#include <linux/usb.h>
++#include <linux/usb/hcd.h>
++#include <linux/moduleparam.h>
++#include <linux/dma-mapping.h>
++#include <linux/debugfs.h>
++#include <linux/slab.h>
++#include <linux/uaccess.h>
++#include <linux/platform_device.h>
++#include <linux/io.h>
++#include <linux/iopoll.h>
++#include <linux/clk.h>
++
++#include <asm/byteorder.h>
++#include <asm/irq.h>
++#include <asm/unaligned.h>
++
++#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 <yhchen@faraday-tech.com>
+- */
+-
+-#include <linux/dma-mapping.h>
+-#include <linux/err.h>
+-#include <linux/interrupt.h>
+-#include <linux/io.h>
+-#include <linux/module.h>
+-#include <linux/platform_device.h>
+-#include <linux/usb/ch9.h>
+-#include <linux/usb/gadget.h>
+-
+-#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 <john453@faraday-tech.com>");
+-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 <yhchen@faraday-tech.com>
++ */
++
++#include <linux/dma-mapping.h>
++#include <linux/err.h>
++#include <linux/interrupt.h>
++#include <linux/io.h>
++#include <linux/module.h>
++#include <linux/platform_device.h>
++#include <linux/usb/ch9.h>
++#include <linux/usb/gadget.h>
++
++#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 <john453@faraday-tech.com>");
++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 <linux/usb/ehci-dbgp.h>
++
++/* 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 <yhchen@faraday-tech.com>
++ */
++
++#include <linux/kernel.h>
++
++#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 <yhchen@faraday-tech.com>
+- */
+-
+-#include <linux/kernel.h>
+-
+-#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 <linux/usb/ehci-dbgp.h>
+-
+-/* 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
new file mode 100644 (file)
index 0000000..5c7b4ff
--- /dev/null
@@ -0,0 +1,332 @@
+From 0dbc77a99267a5efef0603a4b49ac02ece6a3f23 Mon Sep 17 00:00:00 2001
+From: Linus Walleij <linus.walleij@linaro.org>
+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 <fabian@ritter-vogt.de>
+Cc: Yuan-Hsin Chen <yhchen@faraday-tech.com>
+Cc: Felipe Balbi <balbi@kernel.org>
+Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
+Link: https://lore.kernel.org/r/20221023144708.3596563-2-linus.walleij@linaro.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+--- 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 <linux/device.h>
++#include <linux/module.h>
++#include <linux/of.h>
++#include <linux/platform_device.h>
++#include <linux/usb.h>
++
++#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 <asm/irq.h>
+ #include <asm/unaligned.h>
+-#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 <linux/usb/ch9.h>
+ #include <linux/usb/gadget.h>
++#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 <john453@faraday-tech.com>");
+-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
new file mode 100644 (file)
index 0000000..6a19a0a
--- /dev/null
@@ -0,0 +1,68 @@
+From 7c0b661926097e935f2711857596fc2277b2304a Mon Sep 17 00:00:00 2001
+From: Linus Walleij <linus.walleij@linaro.org>
+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 <fabian@ritter-vogt.de>
+Cc: Yuan-Hsin Chen <yhchen@faraday-tech.com>
+Cc: Felipe Balbi <balbi@kernel.org>
+Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
+Link: https://lore.kernel.org/r/20221023144708.3596563-3-linus.walleij@linaro.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+--- a/drivers/usb/fotg210/fotg210-core.c
++++ b/drivers/usb/fotg210/fotg210-core.c
+@@ -10,30 +10,37 @@
+ #include <linux/of.h>
+ #include <linux/platform_device.h>
+ #include <linux/usb.h>
++#include <linux/usb/otg.h>
+ #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
new file mode 100644 (file)
index 0000000..daf8d85
--- /dev/null
@@ -0,0 +1,135 @@
+From f7f6c8aca91093e2f886ec97910b1a7d9a69bf9b Mon Sep 17 00:00:00 2001
+From: Linus Walleij <linus.walleij@linaro.org>
+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 <linus.walleij@linaro.org>
+Link: https://lore.kernel.org/r/20221109200554.1957185-1-linus.walleij@linaro.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+--- 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 <linux/bitops.h>
+ #include <linux/device.h>
++#include <linux/mfd/syscon.h>
+ #include <linux/module.h>
+ #include <linux/of.h>
+ #include <linux/platform_device.h>
++#include <linux/regmap.h>
+ #include <linux/usb.h>
+ #include <linux/usb/otg.h>
+ #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
new file mode 100644 (file)
index 0000000..bd3a424
--- /dev/null
@@ -0,0 +1,51 @@
+From 6e002d41889bc52213a26ff91338d340505e0336 Mon Sep 17 00:00:00 2001
+From: Linus Walleij <linus.walleij@linaro.org>
+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 <lkp@intel.com>
+Link: https://lore.kernel.org/linux-usb/202211112132.0BUPGKCd-lkp@intel.com/
+Cc: Arnd Bergmann <arnd@arndb.de>
+Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
+Link: https://lore.kernel.org/r/20221111144821.113665-1-linus.walleij@linaro.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+--- 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
new file mode 100644 (file)
index 0000000..6afef0d
--- /dev/null
@@ -0,0 +1,26 @@
+From 466b10510add46afd21ca19505b29d35ad853370 Mon Sep 17 00:00:00 2001
+From: Geert Uytterhoeven <geert+renesas@glider.be>
+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 <geert+renesas@glider.be>
+Reviewed-by: Linus Walleij <linus.walleij@linaro.org>
+Link: https://lore.kernel.org/r/a989b3b798ecaf3b45f35160e30e605636d66a77.1669044086.git.geert+renesas@glider.be
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+--- 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
new file mode 100644 (file)
index 0000000..2a595e8
--- /dev/null
@@ -0,0 +1,61 @@
+From 27cd321a365fecac857e41ad1681062994142e4a Mon Sep 17 00:00:00 2001
+From: Linus Walleij <linus.walleij@linaro.org>
+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 <linus.walleij@linaro.org>
+Link: https://lore.kernel.org/r/20221114115201.302887-1-linus.walleij@linaro.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+--- 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
new file mode 100644 (file)
index 0000000..498875c
--- /dev/null
@@ -0,0 +1,158 @@
+From 03e4b585ac947e2d422bedf03179bbfec3aca3cf Mon Sep 17 00:00:00 2001
+From: Linus Walleij <linus.walleij@linaro.org>
+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 <linus.walleij@linaro.org>
+Link: https://lore.kernel.org/r/20221114115201.302887-2-linus.walleij@linaro.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+--- a/drivers/usb/fotg210/fotg210-udc.c
++++ b/drivers/usb/fotg210/fotg210-udc.c
+@@ -15,6 +15,8 @@
+ #include <linux/platform_device.h>
+ #include <linux/usb/ch9.h>
+ #include <linux/usb/gadget.h>
++#include <linux/usb/otg.h>
++#include <linux/usb/phy.h>
+ #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
new file mode 100644 (file)
index 0000000..8da3de3
--- /dev/null
@@ -0,0 +1,90 @@
+From 772ea3ec2b9363b45ef9a4768ea205f758c3debc Mon Sep 17 00:00:00 2001
+From: Linus Walleij <linus.walleij@linaro.org>
+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 <linus.walleij@linaro.org>
+Link: https://lore.kernel.org/r/20221114115201.302887-3-linus.walleij@linaro.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+--- a/drivers/usb/fotg210/fotg210-udc.c
++++ b/drivers/usb/fotg210/fotg210-udc.c
+@@ -15,6 +15,7 @@
+ #include <linux/platform_device.h>
+ #include <linux/usb/ch9.h>
+ #include <linux/usb/gadget.h>
++#include <linux/clk.h>
+ #include <linux/usb/otg.h>
+ #include <linux/usb/phy.h>
+@@ -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
new file mode 100644 (file)
index 0000000..9544de7
--- /dev/null
@@ -0,0 +1,69 @@
+From eda686d41e298a9d16708d2ec8d12d8e682dd7ca Mon Sep 17 00:00:00 2001
+From: Linus Walleij <linus.walleij@linaro.org>
+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 <linus.walleij@linaro.org>
+Link: https://lore.kernel.org/r/20221114115201.302887-4-linus.walleij@linaro.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+--- 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
new file mode 100644 (file)
index 0000000..8c33c50
--- /dev/null
@@ -0,0 +1,39 @@
+From 7889a2f0256c55e0184dffd0001d0782f9e4cb83 Mon Sep 17 00:00:00 2001
+From: Christophe JAILLET <christophe.jaillet@wanadoo.fr>
+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 <christophe.jaillet@wanadoo.fr>
+Reviewed-by: Linus Walleij <linus.walleij@linaro.org>
+Link: https://lore.kernel.org/r/deab9696fc4000499470e7ccbca7c36fca17bd4e.1668458274.git.christophe.jaillet@wanadoo.fr
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+--- 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
new file mode 100644 (file)
index 0000000..1781356
--- /dev/null
@@ -0,0 +1,58 @@
+From 7b95ade85ac18eec63e81ac58a482b3e88361ffd Mon Sep 17 00:00:00 2001
+From: Yi Yang <yiyang13@huawei.com>
+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 <andrzej.p@collabora.com>
+Reviewed-by: Linus Walleij <linus.walleij@linaro.org>
+Signed-off-by: Yi Yang <yiyang13@huawei.com>
+Link: https://lore.kernel.org/r/20221202012126.246953-1-yiyang13@huawei.com
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+--- 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
new file mode 100644 (file)
index 0000000..acdf179
--- /dev/null
@@ -0,0 +1,39 @@
+From d8eed400495029ba551704ff0fae1dad87332291 Mon Sep 17 00:00:00 2001
+From: Arnd Bergmann <arnd@arndb.de>
+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 <linus.walleij@linaro.org>
+Signed-off-by: Arnd Bergmann <arnd@arndb.de>
+Link: https://lore.kernel.org/r/20221215165728.2062984-1-arnd@kernel.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+--- 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
new file mode 100644 (file)
index 0000000..a9bbca5
--- /dev/null
@@ -0,0 +1,28 @@
+From eaaa85d907fe27852dd960b2bc5d7bcf11bc3ebd Mon Sep 17 00:00:00 2001
+From: Yang Yingliang <yangyingliang@huawei.com>
+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 <yangyingliang@huawei.com>
+Reviewed-by: Linus Walleij <linus.walleij@linaro.org>
+Link: https://lore.kernel.org/r/20221230065427.944586-1-yangyingliang@huawei.com
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+--- 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
new file mode 100644 (file)
index 0000000..6ff6d28
--- /dev/null
@@ -0,0 +1,25 @@
+From 407577548b2fcd41cc72ee05df1f05a430ed30a0 Mon Sep 17 00:00:00 2001
+From: Linus Walleij <linus.walleij@linaro.org>
+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 <linus.walleij@linaro.org>
+Link: https://lore.kernel.org/r/20230103-gemini-fotg210-usb-v2-2-100388af9810@linaro.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+--- 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
new file mode 100644 (file)
index 0000000..7dbd511
--- /dev/null
@@ -0,0 +1,245 @@
+From fa735ad1afeb5791d5562617b9bbed74574d3e81 Mon Sep 17 00:00:00 2001
+From: Linus Walleij <linus.walleij@linaro.org>
+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 <linus.walleij@linaro.org>
+Link: https://lore.kernel.org/r/20230103-gemini-fotg210-usb-v2-3-100388af9810@linaro.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+--- 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
new file mode 100644 (file)
index 0000000..9894f4d
--- /dev/null
@@ -0,0 +1,196 @@
+From fb8e1e8dbc47e7aff7624b47adaa0a84d2983802 Mon Sep 17 00:00:00 2001
+From: Linus Walleij <linus.walleij@linaro.org>
+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 <linus.walleij@linaro.org>
+Link: https://lore.kernel.org/r/20230103-gemini-fotg210-usb-v2-4-100388af9810@linaro.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+--- a/drivers/usb/fotg210/fotg210-core.c
++++ b/drivers/usb/fotg210/fotg210-core.c
+@@ -6,6 +6,7 @@
+  * driver.
+  */
+ #include <linux/bitops.h>
++#include <linux/clk.h>
+ #include <linux/device.h>
+ #include <linux/mfd/syscon.h>
+ #include <linux/module.h>
+@@ -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 <linux/platform_device.h>
+ #include <linux/io.h>
+ #include <linux/iopoll.h>
+-#include <linux/clk.h>
+ #include <asm/byteorder.h>
+ #include <asm/irq.h>
+@@ -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 <linux/platform_device.h>
+ #include <linux/usb/ch9.h>
+ #include <linux/usb/gadget.h>
+-#include <linux/clk.h>
+ #include <linux/usb/otg.h>
+ #include <linux/usb/phy.h>
+@@ -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
new file mode 100644 (file)
index 0000000..892b0d3
--- /dev/null
@@ -0,0 +1,54 @@
+From b1b07abb598211de3ce7f52abdf8dcb24384341e Mon Sep 17 00:00:00 2001
+From: Linus Walleij <linus.walleij@linaro.org>
+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 <linus.walleij@linaro.org>
+Link: https://lore.kernel.org/r/20230103-gemini-fotg210-usb-v2-5-100388af9810@linaro.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+--- 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
new file mode 100644 (file)
index 0000000..20f8f94
--- /dev/null
@@ -0,0 +1,34 @@
+From d7c2b0b6da75b86cf5ddbcd51a74d74e19bbf178 Mon Sep 17 00:00:00 2001
+From: Linus Walleij <linus.walleij@linaro.org>
+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 <linus.walleij@linaro.org>
+Link: https://lore.kernel.org/r/20230103-gemini-fotg210-usb-v2-6-100388af9810@linaro.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+--- 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
new file mode 100644 (file)
index 0000000..d98561f
--- /dev/null
@@ -0,0 +1,96 @@
+From 2fbbfb2c556944945639b17b13fcb1e05272b646 Mon Sep 17 00:00:00 2001
+From: Linus Walleij <linus.walleij@linaro.org>
+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 <linus.walleij@linaro.org>
+Link: https://lore.kernel.org/r/20230103-gemini-fotg210-usb-v2-7-100388af9810@linaro.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+--- 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
new file mode 100644 (file)
index 0000000..fc5831e
--- /dev/null
@@ -0,0 +1,134 @@
+From f011d1eab23f4c063c5441c0d5a22898adf9145c Mon Sep 17 00:00:00 2001
+From: Fabian Vogt <fabian@ritter-vogt.de>
+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 <fabian@ritter-vogt.de>
+Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
+Link: https://lore.kernel.org/r/20230123073508.2350402-3-linus.walleij@linaro.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+--- 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
new file mode 100644 (file)
index 0000000..fde17a4
--- /dev/null
@@ -0,0 +1,62 @@
+From 367747c7813cecf19b46ef7134691f903ab76dc9 Mon Sep 17 00:00:00 2001
+From: Fabian Vogt <fabian@ritter-vogt.de>
+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 <fabian@ritter-vogt.de>
+Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
+Link: https://lore.kernel.org/r/20230123073508.2350402-4-linus.walleij@linaro.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+--- a/drivers/usb/fotg210/fotg210-udc.c
++++ b/drivers/usb/fotg210/fotg210-udc.c
+@@ -7,6 +7,7 @@
+  * Author : Yuan-Hsin Chen <yhchen@faraday-tech.com>
+  */
++#include <linux/delay.h>
+ #include <linux/dma-mapping.h>
+ #include <linux/err.h>
+ #include <linux/interrupt.h>
+@@ -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
new file mode 100644 (file)
index 0000000..6808361
--- /dev/null
@@ -0,0 +1,32 @@
+From 482830a70408a5d30af264b3d6706f818c78b2b2 Mon Sep 17 00:00:00 2001
+From: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
+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 <andriy.shevchenko@linux.intel.com>
+Link: https://lore.kernel.org/r/20230120154437.22025-1-andriy.shevchenko@linux.intel.com
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+--- 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
new file mode 100644 (file)
index 0000000..1e031f1
--- /dev/null
@@ -0,0 +1,62 @@
+From 6b84aa39a063eec883d410a9893cec70fce56163 Mon Sep 17 00:00:00 2001
+From: Linus Walleij <linus.walleij@linaro.org>
+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 <linus.walleij@linaro.org>
+---
+--- 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
new file mode 100644 (file)
index 0000000..1aff23e
--- /dev/null
@@ -0,0 +1,54 @@
+From 0e733f5af628210f372585e431504a7024e7b571 Mon Sep 17 00:00:00 2001
+From: Linus Walleij <linus.walleij@linaro.org>
+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 <linus.walleij@linaro.org>
+---
+--- 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
new file mode 100644 (file)
index 0000000..8cafeaa
--- /dev/null
@@ -0,0 +1,54 @@
+From 8558e2e1110a5daa4ac9e1c5b5c15e1651a8fb94 Mon Sep 17 00:00:00 2001
+From: Linus Walleij <linus.walleij@linaro.org>
+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 <linus.walleij@linaro.org>
+---
+--- 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
new file mode 100644 (file)
index 0000000..fb93b70
--- /dev/null
@@ -0,0 +1,31 @@
+From d5c01ce4a1016507c69682894cf6b66301abca3d Mon Sep 17 00:00:00 2001
+From: Linus Walleij <linus.walleij@linaro.org>
+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 <linus.walleij@linaro.org>
+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
new file mode 100644 (file)
index 0000000..6678781
--- /dev/null
@@ -0,0 +1,54 @@
+From 296184694ae7a4e388603c95499e98d30b21cc09 Mon Sep 17 00:00:00 2001
+From: Linus Walleij <linus.walleij@linaro.org>
+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 <linus.walleij@linaro.org>
+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.1/300-ARM-dts-Augment-DIR-685-partition-table-for-OpenWrt.patch b/target/linux/gemini/patches-6.1/300-ARM-dts-Augment-DIR-685-partition-table-for-OpenWrt.patch
new file mode 100644 (file)
index 0000000..99e0d27
--- /dev/null
@@ -0,0 +1,34 @@
+From 36ee838bf83c01cff7cb47c7b07be278d2950ac0 Mon Sep 17 00:00:00 2001
+From: Linus Walleij <linus.walleij@linaro.org>
+Date: Mon, 11 Mar 2019 15:44:29 +0100
+Subject: [PATCH 2/2] 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
+the MTD splitter will set this up automatically.
+
+Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
+---
+--- a/arch/arm/boot/dts/gemini-dlink-dir-685.dts
++++ b/arch/arm/boot/dts/gemini-dlink-dir-685.dts
+@@ -20,7 +20,7 @@
+       };
+       chosen {
+-              bootargs = "console=ttyS0,19200n8 root=/dev/sda1 rw rootwait consoleblank=300";
++              bootargs = "console=ttyS0,19200n8 consoleblank=300";
+               stdout-path = "uart0:19200n8";
+       };
+@@ -317,9 +317,9 @@
+                                * this is called "upgrade" on the vendor system.
+                                */
+                               partition@40000 {
+-                                      label = "upgrade";
++                                      compatible = "wrg";
++                                      label = "firmware";
+                                       reg = <0x00040000 0x01f40000>;
+-                                      read-only;
+                               };
+                               /* RGDB, Residental Gateway Database? */
+                               partition@1f80000 {