From: Linus Walleij Date: Wed, 31 May 2023 21:21:05 +0000 (+0200) Subject: gemini: Add kernel v6.1 patches X-Git-Url: http://git.openwrt.org/?p=openwrt%2Fopenwrt.git;a=commitdiff_plain;h=58acb1dd2c050660ae6dd0985823ef0d2c47bece gemini: Add kernel v6.1 patches 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 Signed-off-by: Christian Lamparter --- 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 index 0000000000..943b166d7e --- /dev/null +++ b/target/linux/gemini/patches-6.1/0001-usb-phy-phy-gpio-vbus-usb-Add-device-tree-probing.patch @@ -0,0 +1,67 @@ +From d5a026cc8306ccd3e99e1455c87e38f8e6fa18df Mon Sep 17 00:00:00 2001 +From: Linus Walleij +Date: Mon, 7 Nov 2022 00:05:06 +0100 +Subject: [PATCH 01/29] usb: phy: phy-gpio-vbus-usb: Add device tree probing + +Make it possible to probe the GPIO VBUS detection driver +from the device tree compatible for GPIO USB B connectors. + +Since this driver is using the "gpio-usb-b-connector" +compatible, it is important to discern it from the role +switch connector driver (which does not provide a phy), +so we add some Kconfig text and depend on !USB_CONN_GPIO. + +Cc: Rob Herring +Cc: Prashant Malani +Cc: Felipe Balbi +Signed-off-by: Linus Walleij +Link: https://lore.kernel.org/r/20221106230506.1646101-1-linus.walleij@linaro.org +Signed-off-by: Greg Kroah-Hartman +--- +--- a/drivers/usb/phy/Kconfig ++++ b/drivers/usb/phy/Kconfig +@@ -93,12 +93,16 @@ config USB_GPIO_VBUS + tristate "GPIO based peripheral-only VBUS sensing 'transceiver'" + depends on GPIOLIB || COMPILE_TEST + depends on USB_GADGET || !USB_GADGET # if USB_GADGET=m, this can't be 'y' ++ depends on !USB_CONN_GPIO + select USB_PHY + help + Provides simple GPIO VBUS sensing for controllers with an + internal transceiver via the usb_phy interface, and + optionally control of a D+ pullup GPIO as well as a VBUS +- current limit regulator. ++ current limit regulator. This driver is for devices that do ++ NOT support role switch. OTG devices that can do role switch ++ (master/peripheral) shall use the USB based connection ++ detection driver USB_CONN_GPIO. + + config OMAP_OTG + tristate "OMAP USB OTG controller driver" +--- a/drivers/usb/phy/phy-gpio-vbus-usb.c ++++ b/drivers/usb/phy/phy-gpio-vbus-usb.c +@@ -366,12 +366,24 @@ static const struct dev_pm_ops gpio_vbus + + MODULE_ALIAS("platform:gpio-vbus"); + ++/* ++ * NOTE: this driver matches against "gpio-usb-b-connector" for ++ * devices that do NOT support role switch. ++ */ ++static const struct of_device_id gpio_vbus_of_match[] = { ++ { ++ .compatible = "gpio-usb-b-connector", ++ }, ++ {}, ++}; ++ + static struct platform_driver gpio_vbus_driver = { + .driver = { + .name = "gpio-vbus", + #ifdef CONFIG_PM + .pm = &gpio_vbus_dev_pm_ops, + #endif ++ .of_match_table = gpio_vbus_of_match, + }, + .probe = gpio_vbus_probe, + .remove = gpio_vbus_remove, diff --git a/target/linux/gemini/patches-6.1/0002-usb-fotg210-Collect-pieces-of-dual-mode-controller.patch b/target/linux/gemini/patches-6.1/0002-usb-fotg210-Collect-pieces-of-dual-mode-controller.patch new file mode 100644 index 0000000000..902bf4c68f --- /dev/null +++ b/target/linux/gemini/patches-6.1/0002-usb-fotg210-Collect-pieces-of-dual-mode-controller.patch @@ -0,0 +1,15993 @@ +From 30367636930864f71b2bd462adedcf8484313864 Mon Sep 17 00:00:00 2001 +From: Linus Walleij +Date: Sun, 23 Oct 2022 16:47:06 +0200 +Subject: [PATCH 02/29] usb: fotg210: Collect pieces of dual mode controller + +The Faraday FOTG210 is a dual-mode OTG USB controller that can +act as host, peripheral or both. To be able to probe from one +hardware description and to follow the pattern of other dual- +mode controllers such as MUSB or MTU3 we need to collect the +two, currently completely separate drivers in the same +directory. + +After this, users need to select the main symbol USB_FOTG210 +and then each respective subdriver. We pave the road to +compile both drivers into the same kernel and select the +one we want to use at probe() time, and possibly add OTG +support in the end. + +This patch doesn't do much more than create the new symbol +and collect the drivers in one place. We also add a comment +for the section of dual-mode controllers in the Kconfig +file so people can see what these selections are about. + +Also add myself as maintainer as there has been little +response on my patches to these drivers. + +Cc: Fabian Vogt +Cc: Yuan-Hsin Chen +Cc: Felipe Balbi +Signed-off-by: Linus Walleij +Link: https://lore.kernel.org/r/20221023144708.3596563-1-linus.walleij@linaro.org +Signed-off-by: Greg Kroah-Hartman +--- +--- a/drivers/usb/Kconfig ++++ b/drivers/usb/Kconfig +@@ -111,8 +111,12 @@ source "drivers/usb/usbip/Kconfig" + + endif + ++comment "USB dual-mode controller drivers" ++ + source "drivers/usb/cdns3/Kconfig" + ++source "drivers/usb/fotg210/Kconfig" ++ + source "drivers/usb/mtu3/Kconfig" + + source "drivers/usb/musb/Kconfig" +--- a/drivers/usb/Makefile ++++ b/drivers/usb/Makefile +@@ -17,6 +17,8 @@ obj-$(CONFIG_USB_CDNS_SUPPORT) += cdns3/ + obj-$(CONFIG_USB_CDNS3) += cdns3/ + obj-$(CONFIG_USB_CDNSP_PCI) += cdns3/ + ++obj-$(CONFIG_USB_FOTG210) += fotg210/ ++ + obj-$(CONFIG_USB_MON) += mon/ + obj-$(CONFIG_USB_MTU3) += mtu3/ + +--- /dev/null ++++ b/drivers/usb/fotg210/Kconfig +@@ -0,0 +1,36 @@ ++# SPDX-License-Identifier: GPL-2.0 ++ ++config USB_FOTG210 ++ tristate "Faraday FOTG210 USB2 Dual Role controller" ++ depends on USB || USB_GADGET ++ depends on HAS_DMA && HAS_IOMEM ++ default ARCH_GEMINI ++ help ++ Faraday FOTG210 is a dual-mode USB controller that can act ++ in both host controller and peripheral controller mode. ++ ++if USB_FOTG210 ++ ++config USB_FOTG210_HCD ++ tristate "Faraday FOTG210 USB Host Controller support" ++ depends on USB ++ help ++ Faraday FOTG210 is an OTG controller which can be configured as ++ an USB2.0 host. It is designed to meet USB2.0 EHCI specification ++ with minor modification. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called fotg210-hcd. ++ ++config USB_FOTG210_UDC ++ depends on USB_GADGET ++ tristate "Faraday FOTG210 USB Peripheral Controller support" ++ help ++ Faraday USB2.0 OTG controller which can be configured as ++ high speed or full speed USB device. This driver suppports ++ Bulk Transfer so far. ++ ++ Say "y" to link the driver statically, or "m" to build a ++ dynamically linked module called "fotg210-udc". ++ ++endif +--- /dev/null ++++ b/drivers/usb/fotg210/Makefile +@@ -0,0 +1,3 @@ ++# SPDX-License-Identifier: GPL-2.0 ++obj-$(CONFIG_USB_FOTG210_HCD) += fotg210-hcd.o ++obj-$(CONFIG_USB_FOTG210_UDC) += fotg210-udc.o +--- a/drivers/usb/host/fotg210-hcd.c ++++ /dev/null +@@ -1,5727 +0,0 @@ +-// SPDX-License-Identifier: GPL-2.0+ +-/* Faraday FOTG210 EHCI-like driver +- * +- * Copyright (c) 2013 Faraday Technology Corporation +- * +- * Author: Yuan-Hsin Chen +- * Feng-Hsin Chiang +- * Po-Yu Chuang +- * +- * Most of code borrowed from the Linux-3.7 EHCI driver +- */ +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +- +-#include +-#include +-#include +- +-#define DRIVER_AUTHOR "Yuan-Hsin Chen" +-#define DRIVER_DESC "FOTG210 Host Controller (EHCI) Driver" +-static const char hcd_name[] = "fotg210_hcd"; +- +-#undef FOTG210_URB_TRACE +-#define FOTG210_STATS +- +-/* magic numbers that can affect system performance */ +-#define FOTG210_TUNE_CERR 3 /* 0-3 qtd retries; 0 == don't stop */ +-#define FOTG210_TUNE_RL_HS 4 /* nak throttle; see 4.9 */ +-#define FOTG210_TUNE_RL_TT 0 +-#define FOTG210_TUNE_MULT_HS 1 /* 1-3 transactions/uframe; 4.10.3 */ +-#define FOTG210_TUNE_MULT_TT 1 +- +-/* Some drivers think it's safe to schedule isochronous transfers more than 256 +- * ms into the future (partly as a result of an old bug in the scheduling +- * code). In an attempt to avoid trouble, we will use a minimum scheduling +- * length of 512 frames instead of 256. +- */ +-#define FOTG210_TUNE_FLS 1 /* (medium) 512-frame schedule */ +- +-/* Initial IRQ latency: faster than hw default */ +-static int log2_irq_thresh; /* 0 to 6 */ +-module_param(log2_irq_thresh, int, S_IRUGO); +-MODULE_PARM_DESC(log2_irq_thresh, "log2 IRQ latency, 1-64 microframes"); +- +-/* initial park setting: slower than hw default */ +-static unsigned park; +-module_param(park, uint, S_IRUGO); +-MODULE_PARM_DESC(park, "park setting; 1-3 back-to-back async packets"); +- +-/* for link power management(LPM) feature */ +-static unsigned int hird; +-module_param(hird, int, S_IRUGO); +-MODULE_PARM_DESC(hird, "host initiated resume duration, +1 for each 75us"); +- +-#define INTR_MASK (STS_IAA | STS_FATAL | STS_PCD | STS_ERR | STS_INT) +- +-#include "fotg210.h" +- +-#define fotg210_dbg(fotg210, fmt, args...) \ +- dev_dbg(fotg210_to_hcd(fotg210)->self.controller, fmt, ## args) +-#define fotg210_err(fotg210, fmt, args...) \ +- dev_err(fotg210_to_hcd(fotg210)->self.controller, fmt, ## args) +-#define fotg210_info(fotg210, fmt, args...) \ +- dev_info(fotg210_to_hcd(fotg210)->self.controller, fmt, ## args) +-#define fotg210_warn(fotg210, fmt, args...) \ +- dev_warn(fotg210_to_hcd(fotg210)->self.controller, fmt, ## args) +- +-/* check the values in the HCSPARAMS register (host controller _Structural_ +- * parameters) see EHCI spec, Table 2-4 for each value +- */ +-static void dbg_hcs_params(struct fotg210_hcd *fotg210, char *label) +-{ +- u32 params = fotg210_readl(fotg210, &fotg210->caps->hcs_params); +- +- fotg210_dbg(fotg210, "%s hcs_params 0x%x ports=%d\n", label, params, +- HCS_N_PORTS(params)); +-} +- +-/* check the values in the HCCPARAMS register (host controller _Capability_ +- * parameters) see EHCI Spec, Table 2-5 for each value +- */ +-static void dbg_hcc_params(struct fotg210_hcd *fotg210, char *label) +-{ +- u32 params = fotg210_readl(fotg210, &fotg210->caps->hcc_params); +- +- fotg210_dbg(fotg210, "%s hcc_params %04x uframes %s%s\n", label, +- params, +- HCC_PGM_FRAMELISTLEN(params) ? "256/512/1024" : "1024", +- HCC_CANPARK(params) ? " park" : ""); +-} +- +-static void __maybe_unused +-dbg_qtd(const char *label, struct fotg210_hcd *fotg210, struct fotg210_qtd *qtd) +-{ +- fotg210_dbg(fotg210, "%s td %p n%08x %08x t%08x p0=%08x\n", label, qtd, +- hc32_to_cpup(fotg210, &qtd->hw_next), +- hc32_to_cpup(fotg210, &qtd->hw_alt_next), +- hc32_to_cpup(fotg210, &qtd->hw_token), +- hc32_to_cpup(fotg210, &qtd->hw_buf[0])); +- if (qtd->hw_buf[1]) +- fotg210_dbg(fotg210, " p1=%08x p2=%08x p3=%08x p4=%08x\n", +- hc32_to_cpup(fotg210, &qtd->hw_buf[1]), +- hc32_to_cpup(fotg210, &qtd->hw_buf[2]), +- hc32_to_cpup(fotg210, &qtd->hw_buf[3]), +- hc32_to_cpup(fotg210, &qtd->hw_buf[4])); +-} +- +-static void __maybe_unused +-dbg_qh(const char *label, struct fotg210_hcd *fotg210, struct fotg210_qh *qh) +-{ +- struct fotg210_qh_hw *hw = qh->hw; +- +- fotg210_dbg(fotg210, "%s qh %p n%08x info %x %x qtd %x\n", label, qh, +- hw->hw_next, hw->hw_info1, hw->hw_info2, +- hw->hw_current); +- +- dbg_qtd("overlay", fotg210, (struct fotg210_qtd *) &hw->hw_qtd_next); +-} +- +-static void __maybe_unused +-dbg_itd(const char *label, struct fotg210_hcd *fotg210, struct fotg210_itd *itd) +-{ +- fotg210_dbg(fotg210, "%s[%d] itd %p, next %08x, urb %p\n", label, +- itd->frame, itd, hc32_to_cpu(fotg210, itd->hw_next), +- itd->urb); +- +- fotg210_dbg(fotg210, +- " trans: %08x %08x %08x %08x %08x %08x %08x %08x\n", +- hc32_to_cpu(fotg210, itd->hw_transaction[0]), +- hc32_to_cpu(fotg210, itd->hw_transaction[1]), +- hc32_to_cpu(fotg210, itd->hw_transaction[2]), +- hc32_to_cpu(fotg210, itd->hw_transaction[3]), +- hc32_to_cpu(fotg210, itd->hw_transaction[4]), +- hc32_to_cpu(fotg210, itd->hw_transaction[5]), +- hc32_to_cpu(fotg210, itd->hw_transaction[6]), +- hc32_to_cpu(fotg210, itd->hw_transaction[7])); +- +- fotg210_dbg(fotg210, +- " buf: %08x %08x %08x %08x %08x %08x %08x\n", +- hc32_to_cpu(fotg210, itd->hw_bufp[0]), +- hc32_to_cpu(fotg210, itd->hw_bufp[1]), +- hc32_to_cpu(fotg210, itd->hw_bufp[2]), +- hc32_to_cpu(fotg210, itd->hw_bufp[3]), +- hc32_to_cpu(fotg210, itd->hw_bufp[4]), +- hc32_to_cpu(fotg210, itd->hw_bufp[5]), +- hc32_to_cpu(fotg210, itd->hw_bufp[6])); +- +- fotg210_dbg(fotg210, " index: %d %d %d %d %d %d %d %d\n", +- itd->index[0], itd->index[1], itd->index[2], +- itd->index[3], itd->index[4], itd->index[5], +- itd->index[6], itd->index[7]); +-} +- +-static int __maybe_unused +-dbg_status_buf(char *buf, unsigned len, const char *label, u32 status) +-{ +- return scnprintf(buf, len, "%s%sstatus %04x%s%s%s%s%s%s%s%s%s%s", +- label, label[0] ? " " : "", status, +- (status & STS_ASS) ? " Async" : "", +- (status & STS_PSS) ? " Periodic" : "", +- (status & STS_RECL) ? " Recl" : "", +- (status & STS_HALT) ? " Halt" : "", +- (status & STS_IAA) ? " IAA" : "", +- (status & STS_FATAL) ? " FATAL" : "", +- (status & STS_FLR) ? " FLR" : "", +- (status & STS_PCD) ? " PCD" : "", +- (status & STS_ERR) ? " ERR" : "", +- (status & STS_INT) ? " INT" : ""); +-} +- +-static int __maybe_unused +-dbg_intr_buf(char *buf, unsigned len, const char *label, u32 enable) +-{ +- return scnprintf(buf, len, "%s%sintrenable %02x%s%s%s%s%s%s", +- label, label[0] ? " " : "", enable, +- (enable & STS_IAA) ? " IAA" : "", +- (enable & STS_FATAL) ? " FATAL" : "", +- (enable & STS_FLR) ? " FLR" : "", +- (enable & STS_PCD) ? " PCD" : "", +- (enable & STS_ERR) ? " ERR" : "", +- (enable & STS_INT) ? " INT" : ""); +-} +- +-static const char *const fls_strings[] = { "1024", "512", "256", "??" }; +- +-static int dbg_command_buf(char *buf, unsigned len, const char *label, +- u32 command) +-{ +- return scnprintf(buf, len, +- "%s%scommand %07x %s=%d ithresh=%d%s%s%s period=%s%s %s", +- label, label[0] ? " " : "", command, +- (command & CMD_PARK) ? " park" : "(park)", +- CMD_PARK_CNT(command), +- (command >> 16) & 0x3f, +- (command & CMD_IAAD) ? " IAAD" : "", +- (command & CMD_ASE) ? " Async" : "", +- (command & CMD_PSE) ? " Periodic" : "", +- fls_strings[(command >> 2) & 0x3], +- (command & CMD_RESET) ? " Reset" : "", +- (command & CMD_RUN) ? "RUN" : "HALT"); +-} +- +-static char *dbg_port_buf(char *buf, unsigned len, const char *label, int port, +- u32 status) +-{ +- char *sig; +- +- /* signaling state */ +- switch (status & (3 << 10)) { +- case 0 << 10: +- sig = "se0"; +- break; +- case 1 << 10: +- sig = "k"; +- break; /* low speed */ +- case 2 << 10: +- sig = "j"; +- break; +- default: +- sig = "?"; +- break; +- } +- +- scnprintf(buf, len, "%s%sport:%d status %06x %d sig=%s%s%s%s%s%s%s%s", +- label, label[0] ? " " : "", port, status, +- status >> 25, /*device address */ +- sig, +- (status & PORT_RESET) ? " RESET" : "", +- (status & PORT_SUSPEND) ? " SUSPEND" : "", +- (status & PORT_RESUME) ? " RESUME" : "", +- (status & PORT_PEC) ? " PEC" : "", +- (status & PORT_PE) ? " PE" : "", +- (status & PORT_CSC) ? " CSC" : "", +- (status & PORT_CONNECT) ? " CONNECT" : ""); +- +- return buf; +-} +- +-/* functions have the "wrong" filename when they're output... */ +-#define dbg_status(fotg210, label, status) { \ +- char _buf[80]; \ +- dbg_status_buf(_buf, sizeof(_buf), label, status); \ +- fotg210_dbg(fotg210, "%s\n", _buf); \ +-} +- +-#define dbg_cmd(fotg210, label, command) { \ +- char _buf[80]; \ +- dbg_command_buf(_buf, sizeof(_buf), label, command); \ +- fotg210_dbg(fotg210, "%s\n", _buf); \ +-} +- +-#define dbg_port(fotg210, label, port, status) { \ +- char _buf[80]; \ +- fotg210_dbg(fotg210, "%s\n", \ +- dbg_port_buf(_buf, sizeof(_buf), label, port, status));\ +-} +- +-/* troubleshooting help: expose state in debugfs */ +-static int debug_async_open(struct inode *, struct file *); +-static int debug_periodic_open(struct inode *, struct file *); +-static int debug_registers_open(struct inode *, struct file *); +-static int debug_async_open(struct inode *, struct file *); +- +-static ssize_t debug_output(struct file*, char __user*, size_t, loff_t*); +-static int debug_close(struct inode *, struct file *); +- +-static const struct file_operations debug_async_fops = { +- .owner = THIS_MODULE, +- .open = debug_async_open, +- .read = debug_output, +- .release = debug_close, +- .llseek = default_llseek, +-}; +-static const struct file_operations debug_periodic_fops = { +- .owner = THIS_MODULE, +- .open = debug_periodic_open, +- .read = debug_output, +- .release = debug_close, +- .llseek = default_llseek, +-}; +-static const struct file_operations debug_registers_fops = { +- .owner = THIS_MODULE, +- .open = debug_registers_open, +- .read = debug_output, +- .release = debug_close, +- .llseek = default_llseek, +-}; +- +-static struct dentry *fotg210_debug_root; +- +-struct debug_buffer { +- ssize_t (*fill_func)(struct debug_buffer *); /* fill method */ +- struct usb_bus *bus; +- struct mutex mutex; /* protect filling of buffer */ +- size_t count; /* number of characters filled into buffer */ +- char *output_buf; +- size_t alloc_size; +-}; +- +-static inline char speed_char(u32 scratch) +-{ +- switch (scratch & (3 << 12)) { +- case QH_FULL_SPEED: +- return 'f'; +- +- case QH_LOW_SPEED: +- return 'l'; +- +- case QH_HIGH_SPEED: +- return 'h'; +- +- default: +- return '?'; +- } +-} +- +-static inline char token_mark(struct fotg210_hcd *fotg210, __hc32 token) +-{ +- __u32 v = hc32_to_cpu(fotg210, token); +- +- if (v & QTD_STS_ACTIVE) +- return '*'; +- if (v & QTD_STS_HALT) +- return '-'; +- if (!IS_SHORT_READ(v)) +- return ' '; +- /* tries to advance through hw_alt_next */ +- return '/'; +-} +- +-static void qh_lines(struct fotg210_hcd *fotg210, struct fotg210_qh *qh, +- char **nextp, unsigned *sizep) +-{ +- u32 scratch; +- u32 hw_curr; +- struct fotg210_qtd *td; +- unsigned temp; +- unsigned size = *sizep; +- char *next = *nextp; +- char mark; +- __le32 list_end = FOTG210_LIST_END(fotg210); +- struct fotg210_qh_hw *hw = qh->hw; +- +- if (hw->hw_qtd_next == list_end) /* NEC does this */ +- mark = '@'; +- else +- mark = token_mark(fotg210, hw->hw_token); +- if (mark == '/') { /* qh_alt_next controls qh advance? */ +- if ((hw->hw_alt_next & QTD_MASK(fotg210)) == +- fotg210->async->hw->hw_alt_next) +- mark = '#'; /* blocked */ +- else if (hw->hw_alt_next == list_end) +- mark = '.'; /* use hw_qtd_next */ +- /* else alt_next points to some other qtd */ +- } +- scratch = hc32_to_cpup(fotg210, &hw->hw_info1); +- hw_curr = (mark == '*') ? hc32_to_cpup(fotg210, &hw->hw_current) : 0; +- temp = scnprintf(next, size, +- "qh/%p dev%d %cs ep%d %08x %08x(%08x%c %s nak%d)", +- qh, scratch & 0x007f, +- speed_char(scratch), +- (scratch >> 8) & 0x000f, +- scratch, hc32_to_cpup(fotg210, &hw->hw_info2), +- hc32_to_cpup(fotg210, &hw->hw_token), mark, +- (cpu_to_hc32(fotg210, QTD_TOGGLE) & hw->hw_token) +- ? "data1" : "data0", +- (hc32_to_cpup(fotg210, &hw->hw_alt_next) >> 1) & 0x0f); +- size -= temp; +- next += temp; +- +- /* hc may be modifying the list as we read it ... */ +- list_for_each_entry(td, &qh->qtd_list, qtd_list) { +- scratch = hc32_to_cpup(fotg210, &td->hw_token); +- mark = ' '; +- if (hw_curr == td->qtd_dma) +- mark = '*'; +- else if (hw->hw_qtd_next == cpu_to_hc32(fotg210, td->qtd_dma)) +- mark = '+'; +- else if (QTD_LENGTH(scratch)) { +- if (td->hw_alt_next == fotg210->async->hw->hw_alt_next) +- mark = '#'; +- else if (td->hw_alt_next != list_end) +- mark = '/'; +- } +- temp = snprintf(next, size, +- "\n\t%p%c%s len=%d %08x urb %p", +- td, mark, ({ char *tmp; +- switch ((scratch>>8)&0x03) { +- case 0: +- tmp = "out"; +- break; +- case 1: +- tmp = "in"; +- break; +- case 2: +- tmp = "setup"; +- break; +- default: +- tmp = "?"; +- break; +- } tmp; }), +- (scratch >> 16) & 0x7fff, +- scratch, +- td->urb); +- if (size < temp) +- temp = size; +- size -= temp; +- next += temp; +- 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 ++ * Feng-Hsin Chiang ++ * Po-Yu Chuang ++ * ++ * Most of code borrowed from the Linux-3.7 EHCI driver ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++#define DRIVER_AUTHOR "Yuan-Hsin Chen" ++#define DRIVER_DESC "FOTG210 Host Controller (EHCI) Driver" ++static const char hcd_name[] = "fotg210_hcd"; ++ ++#undef FOTG210_URB_TRACE ++#define FOTG210_STATS ++ ++/* magic numbers that can affect system performance */ ++#define FOTG210_TUNE_CERR 3 /* 0-3 qtd retries; 0 == don't stop */ ++#define FOTG210_TUNE_RL_HS 4 /* nak throttle; see 4.9 */ ++#define FOTG210_TUNE_RL_TT 0 ++#define FOTG210_TUNE_MULT_HS 1 /* 1-3 transactions/uframe; 4.10.3 */ ++#define FOTG210_TUNE_MULT_TT 1 ++ ++/* Some drivers think it's safe to schedule isochronous transfers more than 256 ++ * ms into the future (partly as a result of an old bug in the scheduling ++ * code). In an attempt to avoid trouble, we will use a minimum scheduling ++ * length of 512 frames instead of 256. ++ */ ++#define FOTG210_TUNE_FLS 1 /* (medium) 512-frame schedule */ ++ ++/* Initial IRQ latency: faster than hw default */ ++static int log2_irq_thresh; /* 0 to 6 */ ++module_param(log2_irq_thresh, int, S_IRUGO); ++MODULE_PARM_DESC(log2_irq_thresh, "log2 IRQ latency, 1-64 microframes"); ++ ++/* initial park setting: slower than hw default */ ++static unsigned park; ++module_param(park, uint, S_IRUGO); ++MODULE_PARM_DESC(park, "park setting; 1-3 back-to-back async packets"); ++ ++/* for link power management(LPM) feature */ ++static unsigned int hird; ++module_param(hird, int, S_IRUGO); ++MODULE_PARM_DESC(hird, "host initiated resume duration, +1 for each 75us"); ++ ++#define INTR_MASK (STS_IAA | STS_FATAL | STS_PCD | STS_ERR | STS_INT) ++ ++#include "fotg210-hcd.h" ++ ++#define fotg210_dbg(fotg210, fmt, args...) \ ++ dev_dbg(fotg210_to_hcd(fotg210)->self.controller, fmt, ## args) ++#define fotg210_err(fotg210, fmt, args...) \ ++ dev_err(fotg210_to_hcd(fotg210)->self.controller, fmt, ## args) ++#define fotg210_info(fotg210, fmt, args...) \ ++ dev_info(fotg210_to_hcd(fotg210)->self.controller, fmt, ## args) ++#define fotg210_warn(fotg210, fmt, args...) \ ++ dev_warn(fotg210_to_hcd(fotg210)->self.controller, fmt, ## args) ++ ++/* check the values in the HCSPARAMS register (host controller _Structural_ ++ * parameters) see EHCI spec, Table 2-4 for each value ++ */ ++static void dbg_hcs_params(struct fotg210_hcd *fotg210, char *label) ++{ ++ u32 params = fotg210_readl(fotg210, &fotg210->caps->hcs_params); ++ ++ fotg210_dbg(fotg210, "%s hcs_params 0x%x ports=%d\n", label, params, ++ HCS_N_PORTS(params)); ++} ++ ++/* check the values in the HCCPARAMS register (host controller _Capability_ ++ * parameters) see EHCI Spec, Table 2-5 for each value ++ */ ++static void dbg_hcc_params(struct fotg210_hcd *fotg210, char *label) ++{ ++ u32 params = fotg210_readl(fotg210, &fotg210->caps->hcc_params); ++ ++ fotg210_dbg(fotg210, "%s hcc_params %04x uframes %s%s\n", label, ++ params, ++ HCC_PGM_FRAMELISTLEN(params) ? "256/512/1024" : "1024", ++ HCC_CANPARK(params) ? " park" : ""); ++} ++ ++static void __maybe_unused ++dbg_qtd(const char *label, struct fotg210_hcd *fotg210, struct fotg210_qtd *qtd) ++{ ++ fotg210_dbg(fotg210, "%s td %p n%08x %08x t%08x p0=%08x\n", label, qtd, ++ hc32_to_cpup(fotg210, &qtd->hw_next), ++ hc32_to_cpup(fotg210, &qtd->hw_alt_next), ++ hc32_to_cpup(fotg210, &qtd->hw_token), ++ hc32_to_cpup(fotg210, &qtd->hw_buf[0])); ++ if (qtd->hw_buf[1]) ++ fotg210_dbg(fotg210, " p1=%08x p2=%08x p3=%08x p4=%08x\n", ++ hc32_to_cpup(fotg210, &qtd->hw_buf[1]), ++ hc32_to_cpup(fotg210, &qtd->hw_buf[2]), ++ hc32_to_cpup(fotg210, &qtd->hw_buf[3]), ++ hc32_to_cpup(fotg210, &qtd->hw_buf[4])); ++} ++ ++static void __maybe_unused ++dbg_qh(const char *label, struct fotg210_hcd *fotg210, struct fotg210_qh *qh) ++{ ++ struct fotg210_qh_hw *hw = qh->hw; ++ ++ fotg210_dbg(fotg210, "%s qh %p n%08x info %x %x qtd %x\n", label, qh, ++ hw->hw_next, hw->hw_info1, hw->hw_info2, ++ hw->hw_current); ++ ++ dbg_qtd("overlay", fotg210, (struct fotg210_qtd *) &hw->hw_qtd_next); ++} ++ ++static void __maybe_unused ++dbg_itd(const char *label, struct fotg210_hcd *fotg210, struct fotg210_itd *itd) ++{ ++ fotg210_dbg(fotg210, "%s[%d] itd %p, next %08x, urb %p\n", label, ++ itd->frame, itd, hc32_to_cpu(fotg210, itd->hw_next), ++ itd->urb); ++ ++ fotg210_dbg(fotg210, ++ " trans: %08x %08x %08x %08x %08x %08x %08x %08x\n", ++ hc32_to_cpu(fotg210, itd->hw_transaction[0]), ++ hc32_to_cpu(fotg210, itd->hw_transaction[1]), ++ hc32_to_cpu(fotg210, itd->hw_transaction[2]), ++ hc32_to_cpu(fotg210, itd->hw_transaction[3]), ++ hc32_to_cpu(fotg210, itd->hw_transaction[4]), ++ hc32_to_cpu(fotg210, itd->hw_transaction[5]), ++ hc32_to_cpu(fotg210, itd->hw_transaction[6]), ++ hc32_to_cpu(fotg210, itd->hw_transaction[7])); ++ ++ fotg210_dbg(fotg210, ++ " buf: %08x %08x %08x %08x %08x %08x %08x\n", ++ hc32_to_cpu(fotg210, itd->hw_bufp[0]), ++ hc32_to_cpu(fotg210, itd->hw_bufp[1]), ++ hc32_to_cpu(fotg210, itd->hw_bufp[2]), ++ hc32_to_cpu(fotg210, itd->hw_bufp[3]), ++ hc32_to_cpu(fotg210, itd->hw_bufp[4]), ++ hc32_to_cpu(fotg210, itd->hw_bufp[5]), ++ hc32_to_cpu(fotg210, itd->hw_bufp[6])); ++ ++ fotg210_dbg(fotg210, " index: %d %d %d %d %d %d %d %d\n", ++ itd->index[0], itd->index[1], itd->index[2], ++ itd->index[3], itd->index[4], itd->index[5], ++ itd->index[6], itd->index[7]); ++} ++ ++static int __maybe_unused ++dbg_status_buf(char *buf, unsigned len, const char *label, u32 status) ++{ ++ return scnprintf(buf, len, "%s%sstatus %04x%s%s%s%s%s%s%s%s%s%s", ++ label, label[0] ? " " : "", status, ++ (status & STS_ASS) ? " Async" : "", ++ (status & STS_PSS) ? " Periodic" : "", ++ (status & STS_RECL) ? " Recl" : "", ++ (status & STS_HALT) ? " Halt" : "", ++ (status & STS_IAA) ? " IAA" : "", ++ (status & STS_FATAL) ? " FATAL" : "", ++ (status & STS_FLR) ? " FLR" : "", ++ (status & STS_PCD) ? " PCD" : "", ++ (status & STS_ERR) ? " ERR" : "", ++ (status & STS_INT) ? " INT" : ""); ++} ++ ++static int __maybe_unused ++dbg_intr_buf(char *buf, unsigned len, const char *label, u32 enable) ++{ ++ return scnprintf(buf, len, "%s%sintrenable %02x%s%s%s%s%s%s", ++ label, label[0] ? " " : "", enable, ++ (enable & STS_IAA) ? " IAA" : "", ++ (enable & STS_FATAL) ? " FATAL" : "", ++ (enable & STS_FLR) ? " FLR" : "", ++ (enable & STS_PCD) ? " PCD" : "", ++ (enable & STS_ERR) ? " ERR" : "", ++ (enable & STS_INT) ? " INT" : ""); ++} ++ ++static const char *const fls_strings[] = { "1024", "512", "256", "??" }; ++ ++static int dbg_command_buf(char *buf, unsigned len, const char *label, ++ u32 command) ++{ ++ return scnprintf(buf, len, ++ "%s%scommand %07x %s=%d ithresh=%d%s%s%s period=%s%s %s", ++ label, label[0] ? " " : "", command, ++ (command & CMD_PARK) ? " park" : "(park)", ++ CMD_PARK_CNT(command), ++ (command >> 16) & 0x3f, ++ (command & CMD_IAAD) ? " IAAD" : "", ++ (command & CMD_ASE) ? " Async" : "", ++ (command & CMD_PSE) ? " Periodic" : "", ++ fls_strings[(command >> 2) & 0x3], ++ (command & CMD_RESET) ? " Reset" : "", ++ (command & CMD_RUN) ? "RUN" : "HALT"); ++} ++ ++static char *dbg_port_buf(char *buf, unsigned len, const char *label, int port, ++ u32 status) ++{ ++ char *sig; ++ ++ /* signaling state */ ++ switch (status & (3 << 10)) { ++ case 0 << 10: ++ sig = "se0"; ++ break; ++ case 1 << 10: ++ sig = "k"; ++ break; /* low speed */ ++ case 2 << 10: ++ sig = "j"; ++ break; ++ default: ++ sig = "?"; ++ break; ++ } ++ ++ scnprintf(buf, len, "%s%sport:%d status %06x %d sig=%s%s%s%s%s%s%s%s", ++ label, label[0] ? " " : "", port, status, ++ status >> 25, /*device address */ ++ sig, ++ (status & PORT_RESET) ? " RESET" : "", ++ (status & PORT_SUSPEND) ? " SUSPEND" : "", ++ (status & PORT_RESUME) ? " RESUME" : "", ++ (status & PORT_PEC) ? " PEC" : "", ++ (status & PORT_PE) ? " PE" : "", ++ (status & PORT_CSC) ? " CSC" : "", ++ (status & PORT_CONNECT) ? " CONNECT" : ""); ++ ++ return buf; ++} ++ ++/* functions have the "wrong" filename when they're output... */ ++#define dbg_status(fotg210, label, status) { \ ++ char _buf[80]; \ ++ dbg_status_buf(_buf, sizeof(_buf), label, status); \ ++ fotg210_dbg(fotg210, "%s\n", _buf); \ ++} ++ ++#define dbg_cmd(fotg210, label, command) { \ ++ char _buf[80]; \ ++ dbg_command_buf(_buf, sizeof(_buf), label, command); \ ++ fotg210_dbg(fotg210, "%s\n", _buf); \ ++} ++ ++#define dbg_port(fotg210, label, port, status) { \ ++ char _buf[80]; \ ++ fotg210_dbg(fotg210, "%s\n", \ ++ dbg_port_buf(_buf, sizeof(_buf), label, port, status));\ ++} ++ ++/* troubleshooting help: expose state in debugfs */ ++static int debug_async_open(struct inode *, struct file *); ++static int debug_periodic_open(struct inode *, struct file *); ++static int debug_registers_open(struct inode *, struct file *); ++static int debug_async_open(struct inode *, struct file *); ++ ++static ssize_t debug_output(struct file*, char __user*, size_t, loff_t*); ++static int debug_close(struct inode *, struct file *); ++ ++static const struct file_operations debug_async_fops = { ++ .owner = THIS_MODULE, ++ .open = debug_async_open, ++ .read = debug_output, ++ .release = debug_close, ++ .llseek = default_llseek, ++}; ++static const struct file_operations debug_periodic_fops = { ++ .owner = THIS_MODULE, ++ .open = debug_periodic_open, ++ .read = debug_output, ++ .release = debug_close, ++ .llseek = default_llseek, ++}; ++static const struct file_operations debug_registers_fops = { ++ .owner = THIS_MODULE, ++ .open = debug_registers_open, ++ .read = debug_output, ++ .release = debug_close, ++ .llseek = default_llseek, ++}; ++ ++static struct dentry *fotg210_debug_root; ++ ++struct debug_buffer { ++ ssize_t (*fill_func)(struct debug_buffer *); /* fill method */ ++ struct usb_bus *bus; ++ struct mutex mutex; /* protect filling of buffer */ ++ size_t count; /* number of characters filled into buffer */ ++ char *output_buf; ++ size_t alloc_size; ++}; ++ ++static inline char speed_char(u32 scratch) ++{ ++ switch (scratch & (3 << 12)) { ++ case QH_FULL_SPEED: ++ return 'f'; ++ ++ case QH_LOW_SPEED: ++ return 'l'; ++ ++ case QH_HIGH_SPEED: ++ return 'h'; ++ ++ default: ++ return '?'; ++ } ++} ++ ++static inline char token_mark(struct fotg210_hcd *fotg210, __hc32 token) ++{ ++ __u32 v = hc32_to_cpu(fotg210, token); ++ ++ if (v & QTD_STS_ACTIVE) ++ return '*'; ++ if (v & QTD_STS_HALT) ++ return '-'; ++ if (!IS_SHORT_READ(v)) ++ return ' '; ++ /* tries to advance through hw_alt_next */ ++ return '/'; ++} ++ ++static void qh_lines(struct fotg210_hcd *fotg210, struct fotg210_qh *qh, ++ char **nextp, unsigned *sizep) ++{ ++ u32 scratch; ++ u32 hw_curr; ++ struct fotg210_qtd *td; ++ unsigned temp; ++ unsigned size = *sizep; ++ char *next = *nextp; ++ char mark; ++ __le32 list_end = FOTG210_LIST_END(fotg210); ++ struct fotg210_qh_hw *hw = qh->hw; ++ ++ if (hw->hw_qtd_next == list_end) /* NEC does this */ ++ mark = '@'; ++ else ++ mark = token_mark(fotg210, hw->hw_token); ++ if (mark == '/') { /* qh_alt_next controls qh advance? */ ++ if ((hw->hw_alt_next & QTD_MASK(fotg210)) == ++ fotg210->async->hw->hw_alt_next) ++ mark = '#'; /* blocked */ ++ else if (hw->hw_alt_next == list_end) ++ mark = '.'; /* use hw_qtd_next */ ++ /* else alt_next points to some other qtd */ ++ } ++ scratch = hc32_to_cpup(fotg210, &hw->hw_info1); ++ hw_curr = (mark == '*') ? hc32_to_cpup(fotg210, &hw->hw_current) : 0; ++ temp = scnprintf(next, size, ++ "qh/%p dev%d %cs ep%d %08x %08x(%08x%c %s nak%d)", ++ qh, scratch & 0x007f, ++ speed_char(scratch), ++ (scratch >> 8) & 0x000f, ++ scratch, hc32_to_cpup(fotg210, &hw->hw_info2), ++ hc32_to_cpup(fotg210, &hw->hw_token), mark, ++ (cpu_to_hc32(fotg210, QTD_TOGGLE) & hw->hw_token) ++ ? "data1" : "data0", ++ (hc32_to_cpup(fotg210, &hw->hw_alt_next) >> 1) & 0x0f); ++ size -= temp; ++ next += temp; ++ ++ /* hc may be modifying the list as we read it ... */ ++ list_for_each_entry(td, &qh->qtd_list, qtd_list) { ++ scratch = hc32_to_cpup(fotg210, &td->hw_token); ++ mark = ' '; ++ if (hw_curr == td->qtd_dma) ++ mark = '*'; ++ else if (hw->hw_qtd_next == cpu_to_hc32(fotg210, td->qtd_dma)) ++ mark = '+'; ++ else if (QTD_LENGTH(scratch)) { ++ if (td->hw_alt_next == fotg210->async->hw->hw_alt_next) ++ mark = '#'; ++ else if (td->hw_alt_next != list_end) ++ mark = '/'; ++ } ++ temp = snprintf(next, size, ++ "\n\t%p%c%s len=%d %08x urb %p", ++ td, mark, ({ char *tmp; ++ switch ((scratch>>8)&0x03) { ++ case 0: ++ tmp = "out"; ++ break; ++ case 1: ++ tmp = "in"; ++ break; ++ case 2: ++ tmp = "setup"; ++ break; ++ default: ++ tmp = "?"; ++ break; ++ } tmp; }), ++ (scratch >> 16) & 0x7fff, ++ scratch, ++ td->urb); ++ if (size < temp) ++ temp = size; ++ size -= temp; ++ next += temp; ++ if (temp == size) ++ goto done; ++ } ++ ++ temp = snprintf(next, size, "\n"); ++ if (size < temp) ++ temp = size; ++ ++ size -= temp; ++ next += temp; ++ ++done: ++ *sizep = size; ++ *nextp = next; ++} ++ ++static ssize_t fill_async_buffer(struct debug_buffer *buf) ++{ ++ struct usb_hcd *hcd; ++ struct fotg210_hcd *fotg210; ++ unsigned long flags; ++ unsigned temp, size; ++ char *next; ++ struct fotg210_qh *qh; ++ ++ hcd = bus_to_hcd(buf->bus); ++ fotg210 = hcd_to_fotg210(hcd); ++ next = buf->output_buf; ++ size = buf->alloc_size; ++ ++ *next = 0; ++ ++ /* dumps a snapshot of the async schedule. ++ * usually empty except for long-term bulk reads, or head. ++ * one QH per line, and TDs we know about ++ */ ++ spin_lock_irqsave(&fotg210->lock, flags); ++ for (qh = fotg210->async->qh_next.qh; size > 0 && qh; ++ qh = qh->qh_next.qh) ++ qh_lines(fotg210, qh, &next, &size); ++ if (fotg210->async_unlink && size > 0) { ++ temp = scnprintf(next, size, "\nunlink =\n"); ++ size -= temp; ++ next += temp; ++ ++ for (qh = fotg210->async_unlink; size > 0 && qh; ++ qh = qh->unlink_next) ++ qh_lines(fotg210, qh, &next, &size); ++ } ++ spin_unlock_irqrestore(&fotg210->lock, flags); ++ ++ return strlen(buf->output_buf); ++} ++ ++/* count tds, get ep direction */ ++static unsigned output_buf_tds_dir(char *buf, struct fotg210_hcd *fotg210, ++ struct fotg210_qh_hw *hw, struct fotg210_qh *qh, unsigned size) ++{ ++ u32 scratch = hc32_to_cpup(fotg210, &hw->hw_info1); ++ struct fotg210_qtd *qtd; ++ char *type = ""; ++ unsigned temp = 0; ++ ++ /* count tds, get ep direction */ ++ list_for_each_entry(qtd, &qh->qtd_list, qtd_list) { ++ temp++; ++ switch ((hc32_to_cpu(fotg210, qtd->hw_token) >> 8) & 0x03) { ++ case 0: ++ type = "out"; ++ continue; ++ case 1: ++ type = "in"; ++ continue; ++ } ++ } ++ ++ return scnprintf(buf, size, "(%c%d ep%d%s [%d/%d] q%d p%d)", ++ speed_char(scratch), scratch & 0x007f, ++ (scratch >> 8) & 0x000f, type, qh->usecs, ++ qh->c_usecs, temp, (scratch >> 16) & 0x7ff); ++} ++ ++#define DBG_SCHED_LIMIT 64 ++static ssize_t fill_periodic_buffer(struct debug_buffer *buf) ++{ ++ struct usb_hcd *hcd; ++ struct fotg210_hcd *fotg210; ++ unsigned long flags; ++ union fotg210_shadow p, *seen; ++ unsigned temp, size, seen_count; ++ char *next; ++ unsigned i; ++ __hc32 tag; ++ ++ seen = kmalloc_array(DBG_SCHED_LIMIT, sizeof(*seen), GFP_ATOMIC); ++ if (!seen) ++ return 0; ++ ++ seen_count = 0; ++ ++ hcd = bus_to_hcd(buf->bus); ++ fotg210 = hcd_to_fotg210(hcd); ++ next = buf->output_buf; ++ size = buf->alloc_size; ++ ++ temp = scnprintf(next, size, "size = %d\n", fotg210->periodic_size); ++ size -= temp; ++ next += temp; ++ ++ /* dump a snapshot of the periodic schedule. ++ * iso changes, interrupt usually doesn't. ++ */ ++ spin_lock_irqsave(&fotg210->lock, flags); ++ for (i = 0; i < fotg210->periodic_size; i++) { ++ p = fotg210->pshadow[i]; ++ if (likely(!p.ptr)) ++ continue; ++ ++ tag = Q_NEXT_TYPE(fotg210, fotg210->periodic[i]); ++ ++ temp = scnprintf(next, size, "%4d: ", i); ++ size -= temp; ++ next += temp; ++ ++ do { ++ struct fotg210_qh_hw *hw; ++ ++ switch (hc32_to_cpu(fotg210, tag)) { ++ case Q_TYPE_QH: ++ hw = p.qh->hw; ++ temp = scnprintf(next, size, " qh%d-%04x/%p", ++ p.qh->period, ++ hc32_to_cpup(fotg210, ++ &hw->hw_info2) ++ /* uframe masks */ ++ & (QH_CMASK | QH_SMASK), ++ p.qh); ++ size -= temp; ++ next += temp; ++ /* don't repeat what follows this qh */ ++ for (temp = 0; temp < seen_count; temp++) { ++ if (seen[temp].ptr != p.ptr) ++ continue; ++ if (p.qh->qh_next.ptr) { ++ temp = scnprintf(next, size, ++ " ..."); ++ size -= temp; ++ next += temp; ++ } ++ break; ++ } ++ /* show more info the first time around */ ++ if (temp == seen_count) { ++ temp = output_buf_tds_dir(next, ++ fotg210, hw, ++ p.qh, size); ++ ++ if (seen_count < DBG_SCHED_LIMIT) ++ seen[seen_count++].qh = p.qh; ++ } else ++ temp = 0; ++ tag = Q_NEXT_TYPE(fotg210, hw->hw_next); ++ p = p.qh->qh_next; ++ break; ++ case Q_TYPE_FSTN: ++ temp = scnprintf(next, size, ++ " fstn-%8x/%p", ++ p.fstn->hw_prev, p.fstn); ++ tag = Q_NEXT_TYPE(fotg210, p.fstn->hw_next); ++ p = p.fstn->fstn_next; ++ break; ++ case Q_TYPE_ITD: ++ temp = scnprintf(next, size, ++ " itd/%p", p.itd); ++ tag = Q_NEXT_TYPE(fotg210, p.itd->hw_next); ++ p = p.itd->itd_next; ++ break; ++ } ++ size -= temp; ++ next += temp; ++ } while (p.ptr); ++ ++ temp = scnprintf(next, size, "\n"); ++ size -= temp; ++ next += temp; ++ } ++ spin_unlock_irqrestore(&fotg210->lock, flags); ++ kfree(seen); ++ ++ return buf->alloc_size - size; ++} ++#undef DBG_SCHED_LIMIT ++ ++static const char *rh_state_string(struct fotg210_hcd *fotg210) ++{ ++ switch (fotg210->rh_state) { ++ case FOTG210_RH_HALTED: ++ return "halted"; ++ case FOTG210_RH_SUSPENDED: ++ return "suspended"; ++ case FOTG210_RH_RUNNING: ++ return "running"; ++ case FOTG210_RH_STOPPING: ++ return "stopping"; ++ } ++ return "?"; ++} ++ ++static ssize_t fill_registers_buffer(struct debug_buffer *buf) ++{ ++ struct usb_hcd *hcd; ++ struct fotg210_hcd *fotg210; ++ unsigned long flags; ++ unsigned temp, size, i; ++ char *next, scratch[80]; ++ static const char fmt[] = "%*s\n"; ++ static const char label[] = ""; ++ ++ hcd = bus_to_hcd(buf->bus); ++ fotg210 = hcd_to_fotg210(hcd); ++ next = buf->output_buf; ++ size = buf->alloc_size; ++ ++ spin_lock_irqsave(&fotg210->lock, flags); ++ ++ if (!HCD_HW_ACCESSIBLE(hcd)) { ++ size = scnprintf(next, size, ++ "bus %s, device %s\n" ++ "%s\n" ++ "SUSPENDED(no register access)\n", ++ hcd->self.controller->bus->name, ++ dev_name(hcd->self.controller), ++ hcd->product_desc); ++ goto done; ++ } ++ ++ /* Capability Registers */ ++ i = HC_VERSION(fotg210, fotg210_readl(fotg210, ++ &fotg210->caps->hc_capbase)); ++ temp = scnprintf(next, size, ++ "bus %s, device %s\n" ++ "%s\n" ++ "EHCI %x.%02x, rh state %s\n", ++ hcd->self.controller->bus->name, ++ dev_name(hcd->self.controller), ++ hcd->product_desc, ++ i >> 8, i & 0x0ff, rh_state_string(fotg210)); ++ size -= temp; ++ next += temp; ++ ++ /* FIXME interpret both types of params */ ++ i = fotg210_readl(fotg210, &fotg210->caps->hcs_params); ++ temp = scnprintf(next, size, "structural params 0x%08x\n", i); ++ size -= temp; ++ next += temp; ++ ++ i = fotg210_readl(fotg210, &fotg210->caps->hcc_params); ++ temp = scnprintf(next, size, "capability params 0x%08x\n", i); ++ size -= temp; ++ next += temp; ++ ++ /* Operational Registers */ ++ temp = dbg_status_buf(scratch, sizeof(scratch), label, ++ fotg210_readl(fotg210, &fotg210->regs->status)); ++ temp = scnprintf(next, size, fmt, temp, scratch); ++ size -= temp; ++ next += temp; ++ ++ temp = dbg_command_buf(scratch, sizeof(scratch), label, ++ fotg210_readl(fotg210, &fotg210->regs->command)); ++ temp = scnprintf(next, size, fmt, temp, scratch); ++ size -= temp; ++ next += temp; ++ ++ temp = dbg_intr_buf(scratch, sizeof(scratch), label, ++ fotg210_readl(fotg210, &fotg210->regs->intr_enable)); ++ temp = scnprintf(next, size, fmt, temp, scratch); ++ size -= temp; ++ next += temp; ++ ++ temp = scnprintf(next, size, "uframe %04x\n", ++ fotg210_read_frame_index(fotg210)); ++ size -= temp; ++ next += temp; ++ ++ if (fotg210->async_unlink) { ++ temp = scnprintf(next, size, "async unlink qh %p\n", ++ fotg210->async_unlink); ++ size -= temp; ++ next += temp; ++ } ++ ++#ifdef FOTG210_STATS ++ temp = scnprintf(next, size, ++ "irq normal %ld err %ld iaa %ld(lost %ld)\n", ++ fotg210->stats.normal, fotg210->stats.error, ++ fotg210->stats.iaa, fotg210->stats.lost_iaa); ++ size -= temp; ++ next += temp; ++ ++ temp = scnprintf(next, size, "complete %ld unlink %ld\n", ++ fotg210->stats.complete, fotg210->stats.unlink); ++ size -= temp; ++ next += temp; ++#endif ++ ++done: ++ spin_unlock_irqrestore(&fotg210->lock, flags); ++ ++ return buf->alloc_size - size; ++} ++ ++static struct debug_buffer ++*alloc_buffer(struct usb_bus *bus, ssize_t (*fill_func)(struct debug_buffer *)) ++{ ++ struct debug_buffer *buf; ++ ++ buf = kzalloc(sizeof(struct debug_buffer), GFP_KERNEL); ++ ++ if (buf) { ++ buf->bus = bus; ++ buf->fill_func = fill_func; ++ mutex_init(&buf->mutex); ++ buf->alloc_size = PAGE_SIZE; ++ } ++ ++ return buf; ++} ++ ++static int fill_buffer(struct debug_buffer *buf) ++{ ++ int ret = 0; ++ ++ if (!buf->output_buf) ++ buf->output_buf = vmalloc(buf->alloc_size); ++ ++ if (!buf->output_buf) { ++ ret = -ENOMEM; ++ goto out; ++ } ++ ++ ret = buf->fill_func(buf); ++ ++ if (ret >= 0) { ++ buf->count = ret; ++ ret = 0; ++ } ++ ++out: ++ return ret; ++} ++ ++static ssize_t debug_output(struct file *file, char __user *user_buf, ++ size_t len, loff_t *offset) ++{ ++ struct debug_buffer *buf = file->private_data; ++ int ret = 0; ++ ++ mutex_lock(&buf->mutex); ++ if (buf->count == 0) { ++ ret = fill_buffer(buf); ++ if (ret != 0) { ++ mutex_unlock(&buf->mutex); ++ goto out; ++ } ++ } ++ mutex_unlock(&buf->mutex); ++ ++ ret = simple_read_from_buffer(user_buf, len, offset, ++ buf->output_buf, buf->count); ++ ++out: ++ return ret; ++ ++} ++ ++static int debug_close(struct inode *inode, struct file *file) ++{ ++ struct debug_buffer *buf = file->private_data; ++ ++ if (buf) { ++ vfree(buf->output_buf); ++ kfree(buf); ++ } ++ ++ return 0; ++} ++static int debug_async_open(struct inode *inode, struct file *file) ++{ ++ file->private_data = alloc_buffer(inode->i_private, fill_async_buffer); ++ ++ return file->private_data ? 0 : -ENOMEM; ++} ++ ++static int debug_periodic_open(struct inode *inode, struct file *file) ++{ ++ struct debug_buffer *buf; ++ ++ buf = alloc_buffer(inode->i_private, fill_periodic_buffer); ++ if (!buf) ++ return -ENOMEM; ++ ++ buf->alloc_size = (sizeof(void *) == 4 ? 6 : 8)*PAGE_SIZE; ++ file->private_data = buf; ++ return 0; ++} ++ ++static int debug_registers_open(struct inode *inode, struct file *file) ++{ ++ file->private_data = alloc_buffer(inode->i_private, ++ fill_registers_buffer); ++ ++ return file->private_data ? 0 : -ENOMEM; ++} ++ ++static inline void create_debug_files(struct fotg210_hcd *fotg210) ++{ ++ struct usb_bus *bus = &fotg210_to_hcd(fotg210)->self; ++ struct dentry *root; ++ ++ root = debugfs_create_dir(bus->bus_name, fotg210_debug_root); ++ ++ debugfs_create_file("async", S_IRUGO, root, bus, &debug_async_fops); ++ debugfs_create_file("periodic", S_IRUGO, root, bus, ++ &debug_periodic_fops); ++ debugfs_create_file("registers", S_IRUGO, root, bus, ++ &debug_registers_fops); ++} ++ ++static inline void remove_debug_files(struct fotg210_hcd *fotg210) ++{ ++ struct usb_bus *bus = &fotg210_to_hcd(fotg210)->self; ++ ++ debugfs_lookup_and_remove(bus->bus_name, fotg210_debug_root); ++} ++ ++/* handshake - spin reading hc until handshake completes or fails ++ * @ptr: address of hc register to be read ++ * @mask: bits to look at in result of read ++ * @done: value of those bits when handshake succeeds ++ * @usec: timeout in microseconds ++ * ++ * Returns negative errno, or zero on success ++ * ++ * Success happens when the "mask" bits have the specified value (hardware ++ * handshake done). There are two failure modes: "usec" have passed (major ++ * hardware flakeout), or the register reads as all-ones (hardware removed). ++ * ++ * That last failure should_only happen in cases like physical cardbus eject ++ * before driver shutdown. But it also seems to be caused by bugs in cardbus ++ * bridge shutdown: shutting down the bridge before the devices using it. ++ */ ++static int handshake(struct fotg210_hcd *fotg210, void __iomem *ptr, ++ u32 mask, u32 done, int usec) ++{ ++ u32 result; ++ int ret; ++ ++ ret = readl_poll_timeout_atomic(ptr, result, ++ ((result & mask) == done || ++ result == U32_MAX), 1, usec); ++ if (result == U32_MAX) /* card removed */ ++ return -ENODEV; ++ ++ return ret; ++} ++ ++/* Force HC to halt state from unknown (EHCI spec section 2.3). ++ * Must be called with interrupts enabled and the lock not held. ++ */ ++static int fotg210_halt(struct fotg210_hcd *fotg210) ++{ ++ u32 temp; ++ ++ spin_lock_irq(&fotg210->lock); ++ ++ /* disable any irqs left enabled by previous code */ ++ fotg210_writel(fotg210, 0, &fotg210->regs->intr_enable); ++ ++ /* ++ * This routine gets called during probe before fotg210->command ++ * has been initialized, so we can't rely on its value. ++ */ ++ fotg210->command &= ~CMD_RUN; ++ temp = fotg210_readl(fotg210, &fotg210->regs->command); ++ temp &= ~(CMD_RUN | CMD_IAAD); ++ fotg210_writel(fotg210, temp, &fotg210->regs->command); ++ ++ spin_unlock_irq(&fotg210->lock); ++ synchronize_irq(fotg210_to_hcd(fotg210)->irq); ++ ++ return handshake(fotg210, &fotg210->regs->status, ++ STS_HALT, STS_HALT, 16 * 125); ++} ++ ++/* Reset a non-running (STS_HALT == 1) controller. ++ * Must be called with interrupts enabled and the lock not held. ++ */ ++static int fotg210_reset(struct fotg210_hcd *fotg210) ++{ ++ int retval; ++ u32 command = fotg210_readl(fotg210, &fotg210->regs->command); ++ ++ /* If the EHCI debug controller is active, special care must be ++ * taken before and after a host controller reset ++ */ ++ if (fotg210->debug && !dbgp_reset_prep(fotg210_to_hcd(fotg210))) ++ fotg210->debug = NULL; ++ ++ command |= CMD_RESET; ++ dbg_cmd(fotg210, "reset", command); ++ fotg210_writel(fotg210, command, &fotg210->regs->command); ++ fotg210->rh_state = FOTG210_RH_HALTED; ++ fotg210->next_statechange = jiffies; ++ retval = handshake(fotg210, &fotg210->regs->command, ++ CMD_RESET, 0, 250 * 1000); ++ ++ if (retval) ++ return retval; ++ ++ if (fotg210->debug) ++ dbgp_external_startup(fotg210_to_hcd(fotg210)); ++ ++ fotg210->port_c_suspend = fotg210->suspended_ports = ++ fotg210->resuming_ports = 0; ++ return retval; ++} ++ ++/* Idle the controller (turn off the schedules). ++ * Must be called with interrupts enabled and the lock not held. ++ */ ++static void fotg210_quiesce(struct fotg210_hcd *fotg210) ++{ ++ u32 temp; ++ ++ if (fotg210->rh_state != FOTG210_RH_RUNNING) ++ return; ++ ++ /* wait for any schedule enables/disables to take effect */ ++ temp = (fotg210->command << 10) & (STS_ASS | STS_PSS); ++ handshake(fotg210, &fotg210->regs->status, STS_ASS | STS_PSS, temp, ++ 16 * 125); ++ ++ /* then disable anything that's still active */ ++ spin_lock_irq(&fotg210->lock); ++ fotg210->command &= ~(CMD_ASE | CMD_PSE); ++ fotg210_writel(fotg210, fotg210->command, &fotg210->regs->command); ++ spin_unlock_irq(&fotg210->lock); ++ ++ /* hardware can take 16 microframes to turn off ... */ ++ handshake(fotg210, &fotg210->regs->status, STS_ASS | STS_PSS, 0, ++ 16 * 125); ++} ++ ++static void end_unlink_async(struct fotg210_hcd *fotg210); ++static void unlink_empty_async(struct fotg210_hcd *fotg210); ++static void fotg210_work(struct fotg210_hcd *fotg210); ++static void start_unlink_intr(struct fotg210_hcd *fotg210, ++ struct fotg210_qh *qh); ++static void end_unlink_intr(struct fotg210_hcd *fotg210, struct fotg210_qh *qh); ++ ++/* Set a bit in the USBCMD register */ ++static void fotg210_set_command_bit(struct fotg210_hcd *fotg210, u32 bit) ++{ ++ fotg210->command |= bit; ++ fotg210_writel(fotg210, fotg210->command, &fotg210->regs->command); ++ ++ /* unblock posted write */ ++ fotg210_readl(fotg210, &fotg210->regs->command); ++} ++ ++/* Clear a bit in the USBCMD register */ ++static void fotg210_clear_command_bit(struct fotg210_hcd *fotg210, u32 bit) ++{ ++ fotg210->command &= ~bit; ++ fotg210_writel(fotg210, fotg210->command, &fotg210->regs->command); ++ ++ /* unblock posted write */ ++ fotg210_readl(fotg210, &fotg210->regs->command); ++} ++ ++/* EHCI timer support... Now using hrtimers. ++ * ++ * Lots of different events are triggered from fotg210->hrtimer. Whenever ++ * the timer routine runs, it checks each possible event; events that are ++ * currently enabled and whose expiration time has passed get handled. ++ * The set of enabled events is stored as a collection of bitflags in ++ * fotg210->enabled_hrtimer_events, and they are numbered in order of ++ * increasing delay values (ranging between 1 ms and 100 ms). ++ * ++ * Rather than implementing a sorted list or tree of all pending events, ++ * we keep track only of the lowest-numbered pending event, in ++ * fotg210->next_hrtimer_event. Whenever fotg210->hrtimer gets restarted, its ++ * expiration time is set to the timeout value for this event. ++ * ++ * As a result, events might not get handled right away; the actual delay ++ * could be anywhere up to twice the requested delay. This doesn't ++ * matter, because none of the events are especially time-critical. The ++ * ones that matter most all have a delay of 1 ms, so they will be ++ * handled after 2 ms at most, which is okay. In addition to this, we ++ * allow for an expiration range of 1 ms. ++ */ ++ ++/* Delay lengths for the hrtimer event types. ++ * Keep this list sorted by delay length, in the same order as ++ * the event types indexed by enum fotg210_hrtimer_event in fotg210.h. ++ */ ++static unsigned event_delays_ns[] = { ++ 1 * NSEC_PER_MSEC, /* FOTG210_HRTIMER_POLL_ASS */ ++ 1 * NSEC_PER_MSEC, /* FOTG210_HRTIMER_POLL_PSS */ ++ 1 * NSEC_PER_MSEC, /* FOTG210_HRTIMER_POLL_DEAD */ ++ 1125 * NSEC_PER_USEC, /* FOTG210_HRTIMER_UNLINK_INTR */ ++ 2 * NSEC_PER_MSEC, /* FOTG210_HRTIMER_FREE_ITDS */ ++ 6 * NSEC_PER_MSEC, /* FOTG210_HRTIMER_ASYNC_UNLINKS */ ++ 10 * NSEC_PER_MSEC, /* FOTG210_HRTIMER_IAA_WATCHDOG */ ++ 10 * NSEC_PER_MSEC, /* FOTG210_HRTIMER_DISABLE_PERIODIC */ ++ 15 * NSEC_PER_MSEC, /* FOTG210_HRTIMER_DISABLE_ASYNC */ ++ 100 * NSEC_PER_MSEC, /* FOTG210_HRTIMER_IO_WATCHDOG */ ++}; ++ ++/* Enable a pending hrtimer event */ ++static void fotg210_enable_event(struct fotg210_hcd *fotg210, unsigned event, ++ bool resched) ++{ ++ ktime_t *timeout = &fotg210->hr_timeouts[event]; ++ ++ if (resched) ++ *timeout = ktime_add(ktime_get(), event_delays_ns[event]); ++ fotg210->enabled_hrtimer_events |= (1 << event); ++ ++ /* Track only the lowest-numbered pending event */ ++ if (event < fotg210->next_hrtimer_event) { ++ fotg210->next_hrtimer_event = event; ++ hrtimer_start_range_ns(&fotg210->hrtimer, *timeout, ++ NSEC_PER_MSEC, HRTIMER_MODE_ABS); ++ } ++} ++ ++ ++/* Poll the STS_ASS status bit; see when it agrees with CMD_ASE */ ++static void fotg210_poll_ASS(struct fotg210_hcd *fotg210) ++{ ++ unsigned actual, want; ++ ++ /* Don't enable anything if the controller isn't running (e.g., died) */ ++ if (fotg210->rh_state != FOTG210_RH_RUNNING) ++ return; ++ ++ want = (fotg210->command & CMD_ASE) ? STS_ASS : 0; ++ actual = fotg210_readl(fotg210, &fotg210->regs->status) & STS_ASS; ++ ++ if (want != actual) { ++ ++ /* Poll again later, but give up after about 20 ms */ ++ if (fotg210->ASS_poll_count++ < 20) { ++ fotg210_enable_event(fotg210, FOTG210_HRTIMER_POLL_ASS, ++ true); ++ return; ++ } ++ fotg210_dbg(fotg210, "Waited too long for the async schedule status (%x/%x), giving up\n", ++ want, actual); ++ } ++ fotg210->ASS_poll_count = 0; ++ ++ /* The status is up-to-date; restart or stop the schedule as needed */ ++ if (want == 0) { /* Stopped */ ++ if (fotg210->async_count > 0) ++ fotg210_set_command_bit(fotg210, CMD_ASE); ++ ++ } else { /* Running */ ++ if (fotg210->async_count == 0) { ++ ++ /* Turn off the schedule after a while */ ++ fotg210_enable_event(fotg210, ++ FOTG210_HRTIMER_DISABLE_ASYNC, ++ true); ++ } ++ } ++} ++ ++/* Turn off the async schedule after a brief delay */ ++static void fotg210_disable_ASE(struct fotg210_hcd *fotg210) ++{ ++ fotg210_clear_command_bit(fotg210, CMD_ASE); ++} ++ ++ ++/* Poll the STS_PSS status bit; see when it agrees with CMD_PSE */ ++static void fotg210_poll_PSS(struct fotg210_hcd *fotg210) ++{ ++ unsigned actual, want; ++ ++ /* Don't do anything if the controller isn't running (e.g., died) */ ++ if (fotg210->rh_state != FOTG210_RH_RUNNING) ++ return; ++ ++ want = (fotg210->command & CMD_PSE) ? STS_PSS : 0; ++ actual = fotg210_readl(fotg210, &fotg210->regs->status) & STS_PSS; ++ ++ if (want != actual) { ++ ++ /* Poll again later, but give up after about 20 ms */ ++ if (fotg210->PSS_poll_count++ < 20) { ++ fotg210_enable_event(fotg210, FOTG210_HRTIMER_POLL_PSS, ++ true); ++ return; ++ } ++ fotg210_dbg(fotg210, "Waited too long for the periodic schedule status (%x/%x), giving up\n", ++ want, actual); ++ } ++ fotg210->PSS_poll_count = 0; ++ ++ /* The status is up-to-date; restart or stop the schedule as needed */ ++ if (want == 0) { /* Stopped */ ++ if (fotg210->periodic_count > 0) ++ fotg210_set_command_bit(fotg210, CMD_PSE); ++ ++ } else { /* Running */ ++ if (fotg210->periodic_count == 0) { ++ ++ /* Turn off the schedule after a while */ ++ fotg210_enable_event(fotg210, ++ FOTG210_HRTIMER_DISABLE_PERIODIC, ++ true); ++ } ++ } ++} ++ ++/* Turn off the periodic schedule after a brief delay */ ++static void fotg210_disable_PSE(struct fotg210_hcd *fotg210) ++{ ++ fotg210_clear_command_bit(fotg210, CMD_PSE); ++} ++ ++ ++/* Poll the STS_HALT status bit; see when a dead controller stops */ ++static void fotg210_handle_controller_death(struct fotg210_hcd *fotg210) ++{ ++ if (!(fotg210_readl(fotg210, &fotg210->regs->status) & STS_HALT)) { ++ ++ /* Give up after a few milliseconds */ ++ if (fotg210->died_poll_count++ < 5) { ++ /* Try again later */ ++ fotg210_enable_event(fotg210, ++ FOTG210_HRTIMER_POLL_DEAD, true); ++ return; ++ } ++ fotg210_warn(fotg210, "Waited too long for the controller to stop, giving up\n"); ++ } ++ ++ /* Clean up the mess */ ++ fotg210->rh_state = FOTG210_RH_HALTED; ++ fotg210_writel(fotg210, 0, &fotg210->regs->intr_enable); ++ fotg210_work(fotg210); ++ end_unlink_async(fotg210); ++ ++ /* Not in process context, so don't try to reset the controller */ ++} ++ ++ ++/* Handle unlinked interrupt QHs once they are gone from the hardware */ ++static void fotg210_handle_intr_unlinks(struct fotg210_hcd *fotg210) ++{ ++ bool stopped = (fotg210->rh_state < FOTG210_RH_RUNNING); ++ ++ /* ++ * Process all the QHs on the intr_unlink list that were added ++ * before the current unlink cycle began. The list is in ++ * temporal order, so stop when we reach the first entry in the ++ * current cycle. But if the root hub isn't running then ++ * process all the QHs on the list. ++ */ ++ fotg210->intr_unlinking = true; ++ while (fotg210->intr_unlink) { ++ struct fotg210_qh *qh = fotg210->intr_unlink; ++ ++ if (!stopped && qh->unlink_cycle == fotg210->intr_unlink_cycle) ++ break; ++ fotg210->intr_unlink = qh->unlink_next; ++ qh->unlink_next = NULL; ++ end_unlink_intr(fotg210, qh); ++ } ++ ++ /* Handle remaining entries later */ ++ if (fotg210->intr_unlink) { ++ fotg210_enable_event(fotg210, FOTG210_HRTIMER_UNLINK_INTR, ++ true); ++ ++fotg210->intr_unlink_cycle; ++ } ++ fotg210->intr_unlinking = false; ++} ++ ++ ++/* Start another free-iTDs/siTDs cycle */ ++static void start_free_itds(struct fotg210_hcd *fotg210) ++{ ++ if (!(fotg210->enabled_hrtimer_events & ++ BIT(FOTG210_HRTIMER_FREE_ITDS))) { ++ fotg210->last_itd_to_free = list_entry( ++ fotg210->cached_itd_list.prev, ++ struct fotg210_itd, itd_list); ++ fotg210_enable_event(fotg210, FOTG210_HRTIMER_FREE_ITDS, true); ++ } ++} ++ ++/* Wait for controller to stop using old iTDs and siTDs */ ++static void end_free_itds(struct fotg210_hcd *fotg210) ++{ ++ struct fotg210_itd *itd, *n; ++ ++ if (fotg210->rh_state < FOTG210_RH_RUNNING) ++ fotg210->last_itd_to_free = NULL; ++ ++ list_for_each_entry_safe(itd, n, &fotg210->cached_itd_list, itd_list) { ++ list_del(&itd->itd_list); ++ dma_pool_free(fotg210->itd_pool, itd, itd->itd_dma); ++ if (itd == fotg210->last_itd_to_free) ++ break; ++ } ++ ++ if (!list_empty(&fotg210->cached_itd_list)) ++ start_free_itds(fotg210); ++} ++ ++ ++/* Handle lost (or very late) IAA interrupts */ ++static void fotg210_iaa_watchdog(struct fotg210_hcd *fotg210) ++{ ++ if (fotg210->rh_state != FOTG210_RH_RUNNING) ++ return; ++ ++ /* ++ * Lost IAA irqs wedge things badly; seen first with a vt8235. ++ * So we need this watchdog, but must protect it against both ++ * (a) SMP races against real IAA firing and retriggering, and ++ * (b) clean HC shutdown, when IAA watchdog was pending. ++ */ ++ if (fotg210->async_iaa) { ++ u32 cmd, status; ++ ++ /* If we get here, IAA is *REALLY* late. It's barely ++ * conceivable that the system is so busy that CMD_IAAD ++ * is still legitimately set, so let's be sure it's ++ * clear before we read STS_IAA. (The HC should clear ++ * CMD_IAAD when it sets STS_IAA.) ++ */ ++ cmd = fotg210_readl(fotg210, &fotg210->regs->command); ++ ++ /* ++ * If IAA is set here it either legitimately triggered ++ * after the watchdog timer expired (_way_ late, so we'll ++ * still count it as lost) ... or a silicon erratum: ++ * - VIA seems to set IAA without triggering the IRQ; ++ * - IAAD potentially cleared without setting IAA. ++ */ ++ status = fotg210_readl(fotg210, &fotg210->regs->status); ++ if ((status & STS_IAA) || !(cmd & CMD_IAAD)) { ++ INCR(fotg210->stats.lost_iaa); ++ fotg210_writel(fotg210, STS_IAA, ++ &fotg210->regs->status); ++ } ++ ++ fotg210_dbg(fotg210, "IAA watchdog: status %x cmd %x\n", ++ status, cmd); ++ end_unlink_async(fotg210); ++ } ++} ++ ++ ++/* Enable the I/O watchdog, if appropriate */ ++static void turn_on_io_watchdog(struct fotg210_hcd *fotg210) ++{ ++ /* Not needed if the controller isn't running or it's already enabled */ ++ if (fotg210->rh_state != FOTG210_RH_RUNNING || ++ (fotg210->enabled_hrtimer_events & ++ BIT(FOTG210_HRTIMER_IO_WATCHDOG))) ++ return; ++ ++ /* ++ * Isochronous transfers always need the watchdog. ++ * For other sorts we use it only if the flag is set. ++ */ ++ if (fotg210->isoc_count > 0 || (fotg210->need_io_watchdog && ++ fotg210->async_count + fotg210->intr_count > 0)) ++ fotg210_enable_event(fotg210, FOTG210_HRTIMER_IO_WATCHDOG, ++ true); ++} ++ ++ ++/* Handler functions for the hrtimer event types. ++ * Keep this array in the same order as the event types indexed by ++ * enum fotg210_hrtimer_event in fotg210.h. ++ */ ++static void (*event_handlers[])(struct fotg210_hcd *) = { ++ fotg210_poll_ASS, /* FOTG210_HRTIMER_POLL_ASS */ ++ fotg210_poll_PSS, /* FOTG210_HRTIMER_POLL_PSS */ ++ fotg210_handle_controller_death, /* FOTG210_HRTIMER_POLL_DEAD */ ++ fotg210_handle_intr_unlinks, /* FOTG210_HRTIMER_UNLINK_INTR */ ++ end_free_itds, /* FOTG210_HRTIMER_FREE_ITDS */ ++ unlink_empty_async, /* FOTG210_HRTIMER_ASYNC_UNLINKS */ ++ fotg210_iaa_watchdog, /* FOTG210_HRTIMER_IAA_WATCHDOG */ ++ fotg210_disable_PSE, /* FOTG210_HRTIMER_DISABLE_PERIODIC */ ++ fotg210_disable_ASE, /* FOTG210_HRTIMER_DISABLE_ASYNC */ ++ fotg210_work, /* FOTG210_HRTIMER_IO_WATCHDOG */ ++}; ++ ++static enum hrtimer_restart fotg210_hrtimer_func(struct hrtimer *t) ++{ ++ struct fotg210_hcd *fotg210 = ++ container_of(t, struct fotg210_hcd, hrtimer); ++ ktime_t now; ++ unsigned long events; ++ unsigned long flags; ++ unsigned e; ++ ++ spin_lock_irqsave(&fotg210->lock, flags); ++ ++ events = fotg210->enabled_hrtimer_events; ++ fotg210->enabled_hrtimer_events = 0; ++ fotg210->next_hrtimer_event = FOTG210_HRTIMER_NO_EVENT; ++ ++ /* ++ * Check each pending event. If its time has expired, handle ++ * the event; otherwise re-enable it. ++ */ ++ now = ktime_get(); ++ for_each_set_bit(e, &events, FOTG210_HRTIMER_NUM_EVENTS) { ++ if (ktime_compare(now, fotg210->hr_timeouts[e]) >= 0) ++ event_handlers[e](fotg210); ++ else ++ fotg210_enable_event(fotg210, e, false); ++ } ++ ++ spin_unlock_irqrestore(&fotg210->lock, flags); ++ return HRTIMER_NORESTART; ++} ++ ++#define fotg210_bus_suspend NULL ++#define fotg210_bus_resume NULL ++ ++static int check_reset_complete(struct fotg210_hcd *fotg210, int index, ++ u32 __iomem *status_reg, int port_status) ++{ ++ if (!(port_status & PORT_CONNECT)) ++ return port_status; ++ ++ /* if reset finished and it's still not enabled -- handoff */ ++ if (!(port_status & PORT_PE)) ++ /* with integrated TT, there's nobody to hand it to! */ ++ fotg210_dbg(fotg210, "Failed to enable port %d on root hub TT\n", ++ index + 1); ++ else ++ fotg210_dbg(fotg210, "port %d reset complete, port enabled\n", ++ index + 1); ++ ++ return port_status; ++} ++ ++ ++/* build "status change" packet (one or two bytes) from HC registers */ ++ ++static int fotg210_hub_status_data(struct usb_hcd *hcd, char *buf) ++{ ++ struct fotg210_hcd *fotg210 = hcd_to_fotg210(hcd); ++ u32 temp, status; ++ u32 mask; ++ int retval = 1; ++ unsigned long flags; ++ ++ /* init status to no-changes */ ++ buf[0] = 0; ++ ++ /* Inform the core about resumes-in-progress by returning ++ * a non-zero value even if there are no status changes. ++ */ ++ status = fotg210->resuming_ports; ++ ++ mask = PORT_CSC | PORT_PEC; ++ /* PORT_RESUME from hardware ~= PORT_STAT_C_SUSPEND */ ++ ++ /* no hub change reports (bit 0) for now (power, ...) */ ++ ++ /* port N changes (bit N)? */ ++ spin_lock_irqsave(&fotg210->lock, flags); ++ ++ temp = fotg210_readl(fotg210, &fotg210->regs->port_status); ++ ++ /* ++ * Return status information even for ports with OWNER set. ++ * Otherwise hub_wq wouldn't see the disconnect event when a ++ * high-speed device is switched over to the companion ++ * controller by the user. ++ */ ++ ++ if ((temp & mask) != 0 || test_bit(0, &fotg210->port_c_suspend) || ++ (fotg210->reset_done[0] && ++ time_after_eq(jiffies, fotg210->reset_done[0]))) { ++ buf[0] |= 1 << 1; ++ status = STS_PCD; ++ } ++ /* FIXME autosuspend idle root hubs */ ++ spin_unlock_irqrestore(&fotg210->lock, flags); ++ return status ? retval : 0; ++} ++ ++static void fotg210_hub_descriptor(struct fotg210_hcd *fotg210, ++ struct usb_hub_descriptor *desc) ++{ ++ int ports = HCS_N_PORTS(fotg210->hcs_params); ++ u16 temp; ++ ++ desc->bDescriptorType = USB_DT_HUB; ++ desc->bPwrOn2PwrGood = 10; /* fotg210 1.0, 2.3.9 says 20ms max */ ++ desc->bHubContrCurrent = 0; ++ ++ desc->bNbrPorts = ports; ++ temp = 1 + (ports / 8); ++ desc->bDescLength = 7 + 2 * temp; ++ ++ /* two bitmaps: ports removable, and usb 1.0 legacy PortPwrCtrlMask */ ++ memset(&desc->u.hs.DeviceRemovable[0], 0, temp); ++ memset(&desc->u.hs.DeviceRemovable[temp], 0xff, temp); ++ ++ temp = HUB_CHAR_INDV_PORT_OCPM; /* per-port overcurrent reporting */ ++ temp |= HUB_CHAR_NO_LPSM; /* no power switching */ ++ desc->wHubCharacteristics = cpu_to_le16(temp); ++} ++ ++static int fotg210_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, ++ u16 wIndex, char *buf, u16 wLength) ++{ ++ struct fotg210_hcd *fotg210 = hcd_to_fotg210(hcd); ++ int ports = HCS_N_PORTS(fotg210->hcs_params); ++ u32 __iomem *status_reg = &fotg210->regs->port_status; ++ u32 temp, temp1, status; ++ unsigned long flags; ++ int retval = 0; ++ unsigned selector; ++ ++ /* ++ * FIXME: support SetPortFeatures USB_PORT_FEAT_INDICATOR. ++ * HCS_INDICATOR may say we can change LEDs to off/amber/green. ++ * (track current state ourselves) ... blink for diagnostics, ++ * power, "this is the one", etc. EHCI spec supports this. ++ */ ++ ++ spin_lock_irqsave(&fotg210->lock, flags); ++ switch (typeReq) { ++ case ClearHubFeature: ++ switch (wValue) { ++ case C_HUB_LOCAL_POWER: ++ case C_HUB_OVER_CURRENT: ++ /* no hub-wide feature/status flags */ ++ break; ++ default: ++ goto error; ++ } ++ break; ++ case ClearPortFeature: ++ if (!wIndex || wIndex > ports) ++ goto error; ++ wIndex--; ++ temp = fotg210_readl(fotg210, status_reg); ++ temp &= ~PORT_RWC_BITS; ++ ++ /* ++ * Even if OWNER is set, so the port is owned by the ++ * companion controller, hub_wq needs to be able to clear ++ * the port-change status bits (especially ++ * USB_PORT_STAT_C_CONNECTION). ++ */ ++ ++ switch (wValue) { ++ case USB_PORT_FEAT_ENABLE: ++ fotg210_writel(fotg210, temp & ~PORT_PE, status_reg); ++ break; ++ case USB_PORT_FEAT_C_ENABLE: ++ fotg210_writel(fotg210, temp | PORT_PEC, status_reg); ++ break; ++ case USB_PORT_FEAT_SUSPEND: ++ if (temp & PORT_RESET) ++ goto error; ++ if (!(temp & PORT_SUSPEND)) ++ break; ++ if ((temp & PORT_PE) == 0) ++ goto error; ++ ++ /* resume signaling for 20 msec */ ++ fotg210_writel(fotg210, temp | PORT_RESUME, status_reg); ++ fotg210->reset_done[wIndex] = jiffies ++ + msecs_to_jiffies(USB_RESUME_TIMEOUT); ++ break; ++ case USB_PORT_FEAT_C_SUSPEND: ++ clear_bit(wIndex, &fotg210->port_c_suspend); ++ break; ++ case USB_PORT_FEAT_C_CONNECTION: ++ fotg210_writel(fotg210, temp | PORT_CSC, status_reg); ++ break; ++ case USB_PORT_FEAT_C_OVER_CURRENT: ++ fotg210_writel(fotg210, temp | OTGISR_OVC, ++ &fotg210->regs->otgisr); ++ break; ++ case USB_PORT_FEAT_C_RESET: ++ /* GetPortStatus clears reset */ ++ break; ++ default: ++ goto error; ++ } ++ fotg210_readl(fotg210, &fotg210->regs->command); ++ break; ++ case GetHubDescriptor: ++ fotg210_hub_descriptor(fotg210, (struct usb_hub_descriptor *) ++ buf); ++ break; ++ case GetHubStatus: ++ /* no hub-wide feature/status flags */ ++ memset(buf, 0, 4); ++ /*cpu_to_le32s ((u32 *) buf); */ ++ break; ++ case GetPortStatus: ++ if (!wIndex || wIndex > ports) ++ goto error; ++ wIndex--; ++ status = 0; ++ temp = fotg210_readl(fotg210, status_reg); ++ ++ /* wPortChange bits */ ++ if (temp & PORT_CSC) ++ status |= USB_PORT_STAT_C_CONNECTION << 16; ++ if (temp & PORT_PEC) ++ status |= USB_PORT_STAT_C_ENABLE << 16; ++ ++ temp1 = fotg210_readl(fotg210, &fotg210->regs->otgisr); ++ if (temp1 & OTGISR_OVC) ++ status |= USB_PORT_STAT_C_OVERCURRENT << 16; ++ ++ /* whoever resumes must GetPortStatus to complete it!! */ ++ if (temp & PORT_RESUME) { ++ ++ /* Remote Wakeup received? */ ++ if (!fotg210->reset_done[wIndex]) { ++ /* resume signaling for 20 msec */ ++ fotg210->reset_done[wIndex] = jiffies ++ + msecs_to_jiffies(20); ++ /* check the port again */ ++ mod_timer(&fotg210_to_hcd(fotg210)->rh_timer, ++ fotg210->reset_done[wIndex]); ++ } ++ ++ /* resume completed? */ ++ else if (time_after_eq(jiffies, ++ fotg210->reset_done[wIndex])) { ++ clear_bit(wIndex, &fotg210->suspended_ports); ++ set_bit(wIndex, &fotg210->port_c_suspend); ++ fotg210->reset_done[wIndex] = 0; ++ ++ /* stop resume signaling */ ++ temp = fotg210_readl(fotg210, status_reg); ++ fotg210_writel(fotg210, temp & ++ ~(PORT_RWC_BITS | PORT_RESUME), ++ status_reg); ++ clear_bit(wIndex, &fotg210->resuming_ports); ++ retval = handshake(fotg210, status_reg, ++ PORT_RESUME, 0, 2000);/* 2ms */ ++ if (retval != 0) { ++ fotg210_err(fotg210, ++ "port %d resume error %d\n", ++ wIndex + 1, retval); ++ goto error; ++ } ++ temp &= ~(PORT_SUSPEND|PORT_RESUME|(3<<10)); ++ } ++ } ++ ++ /* whoever resets must GetPortStatus to complete it!! */ ++ if ((temp & PORT_RESET) && time_after_eq(jiffies, ++ fotg210->reset_done[wIndex])) { ++ status |= USB_PORT_STAT_C_RESET << 16; ++ fotg210->reset_done[wIndex] = 0; ++ clear_bit(wIndex, &fotg210->resuming_ports); ++ ++ /* force reset to complete */ ++ fotg210_writel(fotg210, ++ temp & ~(PORT_RWC_BITS | PORT_RESET), ++ status_reg); ++ /* REVISIT: some hardware needs 550+ usec to clear ++ * this bit; seems too long to spin routinely... ++ */ ++ retval = handshake(fotg210, status_reg, ++ PORT_RESET, 0, 1000); ++ if (retval != 0) { ++ fotg210_err(fotg210, "port %d reset error %d\n", ++ wIndex + 1, retval); ++ goto error; ++ } ++ ++ /* see what we found out */ ++ temp = check_reset_complete(fotg210, wIndex, status_reg, ++ fotg210_readl(fotg210, status_reg)); ++ ++ /* restart schedule */ ++ fotg210->command |= CMD_RUN; ++ fotg210_writel(fotg210, fotg210->command, &fotg210->regs->command); ++ } ++ ++ if (!(temp & (PORT_RESUME|PORT_RESET))) { ++ fotg210->reset_done[wIndex] = 0; ++ clear_bit(wIndex, &fotg210->resuming_ports); ++ } ++ ++ /* transfer dedicated ports to the companion hc */ ++ if ((temp & PORT_CONNECT) && ++ test_bit(wIndex, &fotg210->companion_ports)) { ++ temp &= ~PORT_RWC_BITS; ++ fotg210_writel(fotg210, temp, status_reg); ++ fotg210_dbg(fotg210, "port %d --> companion\n", ++ wIndex + 1); ++ temp = fotg210_readl(fotg210, status_reg); ++ } ++ ++ /* ++ * Even if OWNER is set, there's no harm letting hub_wq ++ * see the wPortStatus values (they should all be 0 except ++ * for PORT_POWER anyway). ++ */ ++ ++ if (temp & PORT_CONNECT) { ++ status |= USB_PORT_STAT_CONNECTION; ++ status |= fotg210_port_speed(fotg210, temp); ++ } ++ if (temp & PORT_PE) ++ status |= USB_PORT_STAT_ENABLE; ++ ++ /* maybe the port was unsuspended without our knowledge */ ++ if (temp & (PORT_SUSPEND|PORT_RESUME)) { ++ status |= USB_PORT_STAT_SUSPEND; ++ } else if (test_bit(wIndex, &fotg210->suspended_ports)) { ++ clear_bit(wIndex, &fotg210->suspended_ports); ++ clear_bit(wIndex, &fotg210->resuming_ports); ++ fotg210->reset_done[wIndex] = 0; ++ if (temp & PORT_PE) ++ set_bit(wIndex, &fotg210->port_c_suspend); ++ } ++ ++ temp1 = fotg210_readl(fotg210, &fotg210->regs->otgisr); ++ if (temp1 & OTGISR_OVC) ++ status |= USB_PORT_STAT_OVERCURRENT; ++ if (temp & PORT_RESET) ++ status |= USB_PORT_STAT_RESET; ++ if (test_bit(wIndex, &fotg210->port_c_suspend)) ++ status |= USB_PORT_STAT_C_SUSPEND << 16; ++ ++ if (status & ~0xffff) /* only if wPortChange is interesting */ ++ dbg_port(fotg210, "GetStatus", wIndex + 1, temp); ++ put_unaligned_le32(status, buf); ++ break; ++ case SetHubFeature: ++ switch (wValue) { ++ case C_HUB_LOCAL_POWER: ++ case C_HUB_OVER_CURRENT: ++ /* no hub-wide feature/status flags */ ++ break; ++ default: ++ goto error; ++ } ++ break; ++ case SetPortFeature: ++ selector = wIndex >> 8; ++ wIndex &= 0xff; ++ ++ if (!wIndex || wIndex > ports) ++ goto error; ++ wIndex--; ++ temp = fotg210_readl(fotg210, status_reg); ++ temp &= ~PORT_RWC_BITS; ++ switch (wValue) { ++ case USB_PORT_FEAT_SUSPEND: ++ if ((temp & PORT_PE) == 0 ++ || (temp & PORT_RESET) != 0) ++ goto error; ++ ++ /* After above check the port must be connected. ++ * Set appropriate bit thus could put phy into low power ++ * mode if we have hostpc feature ++ */ ++ fotg210_writel(fotg210, temp | PORT_SUSPEND, ++ status_reg); ++ set_bit(wIndex, &fotg210->suspended_ports); ++ break; ++ case USB_PORT_FEAT_RESET: ++ if (temp & PORT_RESUME) ++ goto error; ++ /* line status bits may report this as low speed, ++ * which can be fine if this root hub has a ++ * transaction translator built in. ++ */ ++ fotg210_dbg(fotg210, "port %d reset\n", wIndex + 1); ++ temp |= PORT_RESET; ++ temp &= ~PORT_PE; ++ ++ /* ++ * caller must wait, then call GetPortStatus ++ * usb 2.0 spec says 50 ms resets on root ++ */ ++ fotg210->reset_done[wIndex] = jiffies ++ + msecs_to_jiffies(50); ++ fotg210_writel(fotg210, temp, status_reg); ++ break; ++ ++ /* For downstream facing ports (these): one hub port is put ++ * into test mode according to USB2 11.24.2.13, then the hub ++ * must be reset (which for root hub now means rmmod+modprobe, ++ * or else system reboot). See EHCI 2.3.9 and 4.14 for info ++ * about the EHCI-specific stuff. ++ */ ++ case USB_PORT_FEAT_TEST: ++ if (!selector || selector > 5) ++ goto error; ++ spin_unlock_irqrestore(&fotg210->lock, flags); ++ fotg210_quiesce(fotg210); ++ spin_lock_irqsave(&fotg210->lock, flags); ++ ++ /* Put all enabled ports into suspend */ ++ temp = fotg210_readl(fotg210, status_reg) & ++ ~PORT_RWC_BITS; ++ if (temp & PORT_PE) ++ fotg210_writel(fotg210, temp | PORT_SUSPEND, ++ status_reg); ++ ++ spin_unlock_irqrestore(&fotg210->lock, flags); ++ fotg210_halt(fotg210); ++ spin_lock_irqsave(&fotg210->lock, flags); ++ ++ temp = fotg210_readl(fotg210, status_reg); ++ temp |= selector << 16; ++ fotg210_writel(fotg210, temp, status_reg); ++ break; ++ ++ default: ++ goto error; ++ } ++ fotg210_readl(fotg210, &fotg210->regs->command); ++ break; ++ ++ default: ++error: ++ /* "stall" on error */ ++ retval = -EPIPE; ++ } ++ spin_unlock_irqrestore(&fotg210->lock, flags); ++ return retval; ++} ++ ++static void __maybe_unused fotg210_relinquish_port(struct usb_hcd *hcd, ++ int portnum) ++{ ++ return; ++} ++ ++static int __maybe_unused fotg210_port_handed_over(struct usb_hcd *hcd, ++ int portnum) ++{ ++ return 0; ++} ++ ++/* There's basically three types of memory: ++ * - data used only by the HCD ... kmalloc is fine ++ * - async and periodic schedules, shared by HC and HCD ... these ++ * need to use dma_pool or dma_alloc_coherent ++ * - driver buffers, read/written by HC ... single shot DMA mapped ++ * ++ * There's also "register" data (e.g. PCI or SOC), which is memory mapped. ++ * No memory seen by this driver is pageable. ++ */ ++ ++/* Allocate the key transfer structures from the previously allocated pool */ ++static inline void fotg210_qtd_init(struct fotg210_hcd *fotg210, ++ struct fotg210_qtd *qtd, dma_addr_t dma) ++{ ++ memset(qtd, 0, sizeof(*qtd)); ++ qtd->qtd_dma = dma; ++ qtd->hw_token = cpu_to_hc32(fotg210, QTD_STS_HALT); ++ qtd->hw_next = FOTG210_LIST_END(fotg210); ++ qtd->hw_alt_next = FOTG210_LIST_END(fotg210); ++ INIT_LIST_HEAD(&qtd->qtd_list); ++} ++ ++static struct fotg210_qtd *fotg210_qtd_alloc(struct fotg210_hcd *fotg210, ++ gfp_t flags) ++{ ++ struct fotg210_qtd *qtd; ++ dma_addr_t dma; ++ ++ qtd = dma_pool_alloc(fotg210->qtd_pool, flags, &dma); ++ if (qtd != NULL) ++ fotg210_qtd_init(fotg210, qtd, dma); ++ ++ return qtd; ++} ++ ++static inline void fotg210_qtd_free(struct fotg210_hcd *fotg210, ++ struct fotg210_qtd *qtd) ++{ ++ dma_pool_free(fotg210->qtd_pool, qtd, qtd->qtd_dma); ++} ++ ++ ++static void qh_destroy(struct fotg210_hcd *fotg210, struct fotg210_qh *qh) ++{ ++ /* clean qtds first, and know this is not linked */ ++ if (!list_empty(&qh->qtd_list) || qh->qh_next.ptr) { ++ fotg210_dbg(fotg210, "unused qh not empty!\n"); ++ BUG(); ++ } ++ if (qh->dummy) ++ fotg210_qtd_free(fotg210, qh->dummy); ++ dma_pool_free(fotg210->qh_pool, qh->hw, qh->qh_dma); ++ kfree(qh); ++} ++ ++static struct fotg210_qh *fotg210_qh_alloc(struct fotg210_hcd *fotg210, ++ gfp_t flags) ++{ ++ struct fotg210_qh *qh; ++ dma_addr_t dma; ++ ++ qh = kzalloc(sizeof(*qh), GFP_ATOMIC); ++ if (!qh) ++ goto done; ++ qh->hw = (struct fotg210_qh_hw *) ++ dma_pool_zalloc(fotg210->qh_pool, flags, &dma); ++ if (!qh->hw) ++ goto fail; ++ qh->qh_dma = dma; ++ INIT_LIST_HEAD(&qh->qtd_list); ++ ++ /* dummy td enables safe urb queuing */ ++ qh->dummy = fotg210_qtd_alloc(fotg210, flags); ++ if (qh->dummy == NULL) { ++ fotg210_dbg(fotg210, "no dummy td\n"); ++ goto fail1; ++ } ++done: ++ return qh; ++fail1: ++ dma_pool_free(fotg210->qh_pool, qh->hw, qh->qh_dma); ++fail: ++ kfree(qh); ++ return NULL; ++} ++ ++/* The queue heads and transfer descriptors are managed from pools tied ++ * to each of the "per device" structures. ++ * This is the initialisation and cleanup code. ++ */ ++ ++static void fotg210_mem_cleanup(struct fotg210_hcd *fotg210) ++{ ++ if (fotg210->async) ++ qh_destroy(fotg210, fotg210->async); ++ fotg210->async = NULL; ++ ++ if (fotg210->dummy) ++ qh_destroy(fotg210, fotg210->dummy); ++ fotg210->dummy = NULL; ++ ++ /* DMA consistent memory and pools */ ++ dma_pool_destroy(fotg210->qtd_pool); ++ fotg210->qtd_pool = NULL; ++ ++ dma_pool_destroy(fotg210->qh_pool); ++ fotg210->qh_pool = NULL; ++ ++ dma_pool_destroy(fotg210->itd_pool); ++ fotg210->itd_pool = NULL; ++ ++ if (fotg210->periodic) ++ dma_free_coherent(fotg210_to_hcd(fotg210)->self.controller, ++ fotg210->periodic_size * sizeof(u32), ++ fotg210->periodic, fotg210->periodic_dma); ++ fotg210->periodic = NULL; ++ ++ /* shadow periodic table */ ++ kfree(fotg210->pshadow); ++ fotg210->pshadow = NULL; ++} ++ ++/* remember to add cleanup code (above) if you add anything here */ ++static int fotg210_mem_init(struct fotg210_hcd *fotg210, gfp_t flags) ++{ ++ int i; ++ ++ /* QTDs for control/bulk/intr transfers */ ++ fotg210->qtd_pool = dma_pool_create("fotg210_qtd", ++ fotg210_to_hcd(fotg210)->self.controller, ++ sizeof(struct fotg210_qtd), ++ 32 /* byte alignment (for hw parts) */, ++ 4096 /* can't cross 4K */); ++ if (!fotg210->qtd_pool) ++ goto fail; ++ ++ /* QHs for control/bulk/intr transfers */ ++ fotg210->qh_pool = dma_pool_create("fotg210_qh", ++ fotg210_to_hcd(fotg210)->self.controller, ++ sizeof(struct fotg210_qh_hw), ++ 32 /* byte alignment (for hw parts) */, ++ 4096 /* can't cross 4K */); ++ if (!fotg210->qh_pool) ++ goto fail; ++ ++ fotg210->async = fotg210_qh_alloc(fotg210, flags); ++ if (!fotg210->async) ++ goto fail; ++ ++ /* ITD for high speed ISO transfers */ ++ fotg210->itd_pool = dma_pool_create("fotg210_itd", ++ fotg210_to_hcd(fotg210)->self.controller, ++ sizeof(struct fotg210_itd), ++ 64 /* byte alignment (for hw parts) */, ++ 4096 /* can't cross 4K */); ++ if (!fotg210->itd_pool) ++ goto fail; ++ ++ /* Hardware periodic table */ ++ fotg210->periodic = ++ dma_alloc_coherent(fotg210_to_hcd(fotg210)->self.controller, ++ fotg210->periodic_size * sizeof(__le32), ++ &fotg210->periodic_dma, 0); ++ if (fotg210->periodic == NULL) ++ goto fail; ++ ++ for (i = 0; i < fotg210->periodic_size; i++) ++ fotg210->periodic[i] = FOTG210_LIST_END(fotg210); ++ ++ /* software shadow of hardware table */ ++ fotg210->pshadow = kcalloc(fotg210->periodic_size, sizeof(void *), ++ flags); ++ if (fotg210->pshadow != NULL) ++ return 0; ++ ++fail: ++ fotg210_dbg(fotg210, "couldn't init memory\n"); ++ fotg210_mem_cleanup(fotg210); ++ return -ENOMEM; ++} ++/* EHCI hardware queue manipulation ... the core. QH/QTD manipulation. ++ * ++ * Control, bulk, and interrupt traffic all use "qh" lists. They list "qtd" ++ * entries describing USB transactions, max 16-20kB/entry (with 4kB-aligned ++ * buffers needed for the larger number). We use one QH per endpoint, queue ++ * multiple urbs (all three types) per endpoint. URBs may need several qtds. ++ * ++ * ISO traffic uses "ISO TD" (itd) records, and (along with ++ * interrupts) needs careful scheduling. Performance improvements can be ++ * an ongoing challenge. That's in "ehci-sched.c". ++ * ++ * USB 1.1 devices are handled (a) by "companion" OHCI or UHCI root hubs, ++ * or otherwise through transaction translators (TTs) in USB 2.0 hubs using ++ * (b) special fields in qh entries or (c) split iso entries. TTs will ++ * buffer low/full speed data so the host collects it at high speed. ++ */ ++ ++/* fill a qtd, returning how much of the buffer we were able to queue up */ ++static int qtd_fill(struct fotg210_hcd *fotg210, struct fotg210_qtd *qtd, ++ dma_addr_t buf, size_t len, int token, int maxpacket) ++{ ++ int i, count; ++ u64 addr = buf; ++ ++ /* one buffer entry per 4K ... first might be short or unaligned */ ++ qtd->hw_buf[0] = cpu_to_hc32(fotg210, (u32)addr); ++ qtd->hw_buf_hi[0] = cpu_to_hc32(fotg210, (u32)(addr >> 32)); ++ count = 0x1000 - (buf & 0x0fff); /* rest of that page */ ++ if (likely(len < count)) /* ... iff needed */ ++ count = len; ++ else { ++ buf += 0x1000; ++ buf &= ~0x0fff; ++ ++ /* per-qtd limit: from 16K to 20K (best alignment) */ ++ for (i = 1; count < len && i < 5; i++) { ++ addr = buf; ++ qtd->hw_buf[i] = cpu_to_hc32(fotg210, (u32)addr); ++ qtd->hw_buf_hi[i] = cpu_to_hc32(fotg210, ++ (u32)(addr >> 32)); ++ buf += 0x1000; ++ if ((count + 0x1000) < len) ++ count += 0x1000; ++ else ++ count = len; ++ } ++ ++ /* short packets may only terminate transfers */ ++ if (count != len) ++ count -= (count % maxpacket); ++ } ++ qtd->hw_token = cpu_to_hc32(fotg210, (count << 16) | token); ++ qtd->length = count; ++ ++ return count; ++} ++ ++static inline void qh_update(struct fotg210_hcd *fotg210, ++ struct fotg210_qh *qh, struct fotg210_qtd *qtd) ++{ ++ struct fotg210_qh_hw *hw = qh->hw; ++ ++ /* writes to an active overlay are unsafe */ ++ BUG_ON(qh->qh_state != QH_STATE_IDLE); ++ ++ hw->hw_qtd_next = QTD_NEXT(fotg210, qtd->qtd_dma); ++ hw->hw_alt_next = FOTG210_LIST_END(fotg210); ++ ++ /* Except for control endpoints, we make hardware maintain data ++ * toggle (like OHCI) ... here (re)initialize the toggle in the QH, ++ * and set the pseudo-toggle in udev. Only usb_clear_halt() will ++ * ever clear it. ++ */ ++ if (!(hw->hw_info1 & cpu_to_hc32(fotg210, QH_TOGGLE_CTL))) { ++ unsigned is_out, epnum; ++ ++ is_out = qh->is_out; ++ epnum = (hc32_to_cpup(fotg210, &hw->hw_info1) >> 8) & 0x0f; ++ if (unlikely(!usb_gettoggle(qh->dev, epnum, is_out))) { ++ hw->hw_token &= ~cpu_to_hc32(fotg210, QTD_TOGGLE); ++ usb_settoggle(qh->dev, epnum, is_out, 1); ++ } ++ } ++ ++ hw->hw_token &= cpu_to_hc32(fotg210, QTD_TOGGLE | QTD_STS_PING); ++} ++ ++/* if it weren't for a common silicon quirk (writing the dummy into the qh ++ * overlay, so qh->hw_token wrongly becomes inactive/halted), only fault ++ * recovery (including urb dequeue) would need software changes to a QH... ++ */ ++static void qh_refresh(struct fotg210_hcd *fotg210, struct fotg210_qh *qh) ++{ ++ struct fotg210_qtd *qtd; ++ ++ if (list_empty(&qh->qtd_list)) ++ qtd = qh->dummy; ++ else { ++ qtd = list_entry(qh->qtd_list.next, ++ struct fotg210_qtd, qtd_list); ++ /* ++ * first qtd may already be partially processed. ++ * If we come here during unlink, the QH overlay region ++ * might have reference to the just unlinked qtd. The ++ * qtd is updated in qh_completions(). Update the QH ++ * overlay here. ++ */ ++ if (cpu_to_hc32(fotg210, qtd->qtd_dma) == qh->hw->hw_current) { ++ qh->hw->hw_qtd_next = qtd->hw_next; ++ qtd = NULL; ++ } ++ } ++ ++ if (qtd) ++ qh_update(fotg210, qh, qtd); ++} ++ ++static void qh_link_async(struct fotg210_hcd *fotg210, struct fotg210_qh *qh); ++ ++static void fotg210_clear_tt_buffer_complete(struct usb_hcd *hcd, ++ struct usb_host_endpoint *ep) ++{ ++ struct fotg210_hcd *fotg210 = hcd_to_fotg210(hcd); ++ struct fotg210_qh *qh = ep->hcpriv; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&fotg210->lock, flags); ++ qh->clearing_tt = 0; ++ if (qh->qh_state == QH_STATE_IDLE && !list_empty(&qh->qtd_list) ++ && fotg210->rh_state == FOTG210_RH_RUNNING) ++ qh_link_async(fotg210, qh); ++ spin_unlock_irqrestore(&fotg210->lock, flags); ++} ++ ++static void fotg210_clear_tt_buffer(struct fotg210_hcd *fotg210, ++ struct fotg210_qh *qh, struct urb *urb, u32 token) ++{ ++ ++ /* If an async split transaction gets an error or is unlinked, ++ * the TT buffer may be left in an indeterminate state. We ++ * have to clear the TT buffer. ++ * ++ * Note: this routine is never called for Isochronous transfers. ++ */ ++ if (urb->dev->tt && !usb_pipeint(urb->pipe) && !qh->clearing_tt) { ++ struct usb_device *tt = urb->dev->tt->hub; ++ ++ dev_dbg(&tt->dev, ++ "clear tt buffer port %d, a%d ep%d t%08x\n", ++ urb->dev->ttport, urb->dev->devnum, ++ usb_pipeendpoint(urb->pipe), token); ++ ++ if (urb->dev->tt->hub != ++ fotg210_to_hcd(fotg210)->self.root_hub) { ++ if (usb_hub_clear_tt_buffer(urb) == 0) ++ qh->clearing_tt = 1; ++ } ++ } ++} ++ ++static int qtd_copy_status(struct fotg210_hcd *fotg210, struct urb *urb, ++ size_t length, u32 token) ++{ ++ int status = -EINPROGRESS; ++ ++ /* count IN/OUT bytes, not SETUP (even short packets) */ ++ if (likely(QTD_PID(token) != 2)) ++ urb->actual_length += length - QTD_LENGTH(token); ++ ++ /* don't modify error codes */ ++ if (unlikely(urb->unlinked)) ++ return status; ++ ++ /* force cleanup after short read; not always an error */ ++ if (unlikely(IS_SHORT_READ(token))) ++ status = -EREMOTEIO; ++ ++ /* serious "can't proceed" faults reported by the hardware */ ++ if (token & QTD_STS_HALT) { ++ if (token & QTD_STS_BABBLE) { ++ /* FIXME "must" disable babbling device's port too */ ++ status = -EOVERFLOW; ++ /* CERR nonzero + halt --> stall */ ++ } else if (QTD_CERR(token)) { ++ status = -EPIPE; ++ ++ /* In theory, more than one of the following bits can be set ++ * since they are sticky and the transaction is retried. ++ * Which to test first is rather arbitrary. ++ */ ++ } else if (token & QTD_STS_MMF) { ++ /* fs/ls interrupt xfer missed the complete-split */ ++ status = -EPROTO; ++ } else if (token & QTD_STS_DBE) { ++ status = (QTD_PID(token) == 1) /* IN ? */ ++ ? -ENOSR /* hc couldn't read data */ ++ : -ECOMM; /* hc couldn't write data */ ++ } else if (token & QTD_STS_XACT) { ++ /* timeout, bad CRC, wrong PID, etc */ ++ fotg210_dbg(fotg210, "devpath %s ep%d%s 3strikes\n", ++ urb->dev->devpath, ++ usb_pipeendpoint(urb->pipe), ++ usb_pipein(urb->pipe) ? "in" : "out"); ++ status = -EPROTO; ++ } else { /* unknown */ ++ status = -EPROTO; ++ } ++ ++ fotg210_dbg(fotg210, ++ "dev%d ep%d%s qtd token %08x --> status %d\n", ++ usb_pipedevice(urb->pipe), ++ usb_pipeendpoint(urb->pipe), ++ usb_pipein(urb->pipe) ? "in" : "out", ++ token, status); ++ } ++ ++ return status; ++} ++ ++static void fotg210_urb_done(struct fotg210_hcd *fotg210, struct urb *urb, ++ int status) ++__releases(fotg210->lock) ++__acquires(fotg210->lock) ++{ ++ if (likely(urb->hcpriv != NULL)) { ++ struct fotg210_qh *qh = (struct fotg210_qh *) urb->hcpriv; ++ ++ /* S-mask in a QH means it's an interrupt urb */ ++ if ((qh->hw->hw_info2 & cpu_to_hc32(fotg210, QH_SMASK)) != 0) { ++ ++ /* ... update hc-wide periodic stats (for usbfs) */ ++ fotg210_to_hcd(fotg210)->self.bandwidth_int_reqs--; ++ } ++ } ++ ++ if (unlikely(urb->unlinked)) { ++ INCR(fotg210->stats.unlink); ++ } else { ++ /* report non-error and short read status as zero */ ++ if (status == -EINPROGRESS || status == -EREMOTEIO) ++ status = 0; ++ INCR(fotg210->stats.complete); ++ } ++ ++#ifdef FOTG210_URB_TRACE ++ fotg210_dbg(fotg210, ++ "%s %s urb %p ep%d%s status %d len %d/%d\n", ++ __func__, urb->dev->devpath, urb, ++ usb_pipeendpoint(urb->pipe), ++ usb_pipein(urb->pipe) ? "in" : "out", ++ status, ++ urb->actual_length, urb->transfer_buffer_length); ++#endif ++ ++ /* complete() can reenter this HCD */ ++ usb_hcd_unlink_urb_from_ep(fotg210_to_hcd(fotg210), urb); ++ spin_unlock(&fotg210->lock); ++ usb_hcd_giveback_urb(fotg210_to_hcd(fotg210), urb, status); ++ spin_lock(&fotg210->lock); ++} ++ ++static int qh_schedule(struct fotg210_hcd *fotg210, struct fotg210_qh *qh); ++ ++/* Process and free completed qtds for a qh, returning URBs to drivers. ++ * Chases up to qh->hw_current. Returns number of completions called, ++ * indicating how much "real" work we did. ++ */ ++static unsigned qh_completions(struct fotg210_hcd *fotg210, ++ struct fotg210_qh *qh) ++{ ++ struct fotg210_qtd *last, *end = qh->dummy; ++ struct fotg210_qtd *qtd, *tmp; ++ int last_status; ++ int stopped; ++ unsigned count = 0; ++ u8 state; ++ struct fotg210_qh_hw *hw = qh->hw; ++ ++ if (unlikely(list_empty(&qh->qtd_list))) ++ return count; ++ ++ /* completions (or tasks on other cpus) must never clobber HALT ++ * till we've gone through and cleaned everything up, even when ++ * they add urbs to this qh's queue or mark them for unlinking. ++ * ++ * NOTE: unlinking expects to be done in queue order. ++ * ++ * It's a bug for qh->qh_state to be anything other than ++ * QH_STATE_IDLE, unless our caller is scan_async() or ++ * scan_intr(). ++ */ ++ state = qh->qh_state; ++ qh->qh_state = QH_STATE_COMPLETING; ++ stopped = (state == QH_STATE_IDLE); ++ ++rescan: ++ last = NULL; ++ last_status = -EINPROGRESS; ++ qh->needs_rescan = 0; ++ ++ /* remove de-activated QTDs from front of queue. ++ * after faults (including short reads), cleanup this urb ++ * then let the queue advance. ++ * if queue is stopped, handles unlinks. ++ */ ++ list_for_each_entry_safe(qtd, tmp, &qh->qtd_list, qtd_list) { ++ struct urb *urb; ++ u32 token = 0; ++ ++ urb = qtd->urb; ++ ++ /* clean up any state from previous QTD ...*/ ++ if (last) { ++ if (likely(last->urb != urb)) { ++ fotg210_urb_done(fotg210, last->urb, ++ last_status); ++ count++; ++ last_status = -EINPROGRESS; ++ } ++ fotg210_qtd_free(fotg210, last); ++ last = NULL; ++ } ++ ++ /* ignore urbs submitted during completions we reported */ ++ if (qtd == end) ++ break; ++ ++ /* hardware copies qtd out of qh overlay */ ++ rmb(); ++ token = hc32_to_cpu(fotg210, qtd->hw_token); ++ ++ /* always clean up qtds the hc de-activated */ ++retry_xacterr: ++ if ((token & QTD_STS_ACTIVE) == 0) { ++ ++ /* Report Data Buffer Error: non-fatal but useful */ ++ if (token & QTD_STS_DBE) ++ fotg210_dbg(fotg210, ++ "detected DataBufferErr for urb %p ep%d%s len %d, qtd %p [qh %p]\n", ++ urb, usb_endpoint_num(&urb->ep->desc), ++ usb_endpoint_dir_in(&urb->ep->desc) ++ ? "in" : "out", ++ urb->transfer_buffer_length, qtd, qh); ++ ++ /* on STALL, error, and short reads this urb must ++ * complete and all its qtds must be recycled. ++ */ ++ if ((token & QTD_STS_HALT) != 0) { ++ ++ /* retry transaction errors until we ++ * reach the software xacterr limit ++ */ ++ if ((token & QTD_STS_XACT) && ++ QTD_CERR(token) == 0 && ++ ++qh->xacterrs < QH_XACTERR_MAX && ++ !urb->unlinked) { ++ fotg210_dbg(fotg210, ++ "detected XactErr len %zu/%zu retry %d\n", ++ qtd->length - QTD_LENGTH(token), ++ qtd->length, ++ qh->xacterrs); ++ ++ /* reset the token in the qtd and the ++ * qh overlay (which still contains ++ * the qtd) so that we pick up from ++ * where we left off ++ */ ++ token &= ~QTD_STS_HALT; ++ token |= QTD_STS_ACTIVE | ++ (FOTG210_TUNE_CERR << 10); ++ qtd->hw_token = cpu_to_hc32(fotg210, ++ token); ++ wmb(); ++ hw->hw_token = cpu_to_hc32(fotg210, ++ token); ++ goto retry_xacterr; ++ } ++ stopped = 1; ++ ++ /* magic dummy for some short reads; qh won't advance. ++ * that silicon quirk can kick in with this dummy too. ++ * ++ * other short reads won't stop the queue, including ++ * control transfers (status stage handles that) or ++ * most other single-qtd reads ... the queue stops if ++ * URB_SHORT_NOT_OK was set so the driver submitting ++ * the urbs could clean it up. ++ */ ++ } else if (IS_SHORT_READ(token) && ++ !(qtd->hw_alt_next & ++ FOTG210_LIST_END(fotg210))) { ++ stopped = 1; ++ } ++ ++ /* stop scanning when we reach qtds the hc is using */ ++ } else if (likely(!stopped ++ && fotg210->rh_state >= FOTG210_RH_RUNNING)) { ++ break; ++ ++ /* scan the whole queue for unlinks whenever it stops */ ++ } else { ++ stopped = 1; ++ ++ /* cancel everything if we halt, suspend, etc */ ++ if (fotg210->rh_state < FOTG210_RH_RUNNING) ++ last_status = -ESHUTDOWN; ++ ++ /* this qtd is active; skip it unless a previous qtd ++ * for its urb faulted, or its urb was canceled. ++ */ ++ else if (last_status == -EINPROGRESS && !urb->unlinked) ++ continue; ++ ++ /* qh unlinked; token in overlay may be most current */ ++ if (state == QH_STATE_IDLE && ++ cpu_to_hc32(fotg210, qtd->qtd_dma) ++ == hw->hw_current) { ++ token = hc32_to_cpu(fotg210, hw->hw_token); ++ ++ /* An unlink may leave an incomplete ++ * async transaction in the TT buffer. ++ * We have to clear it. ++ */ ++ fotg210_clear_tt_buffer(fotg210, qh, urb, ++ token); ++ } ++ } ++ ++ /* unless we already know the urb's status, collect qtd status ++ * and update count of bytes transferred. in common short read ++ * cases with only one data qtd (including control transfers), ++ * queue processing won't halt. but with two or more qtds (for ++ * example, with a 32 KB transfer), when the first qtd gets a ++ * short read the second must be removed by hand. ++ */ ++ if (last_status == -EINPROGRESS) { ++ last_status = qtd_copy_status(fotg210, urb, ++ qtd->length, token); ++ if (last_status == -EREMOTEIO && ++ (qtd->hw_alt_next & ++ FOTG210_LIST_END(fotg210))) ++ last_status = -EINPROGRESS; ++ ++ /* As part of low/full-speed endpoint-halt processing ++ * we must clear the TT buffer (11.17.5). ++ */ ++ if (unlikely(last_status != -EINPROGRESS && ++ last_status != -EREMOTEIO)) { ++ /* The TT's in some hubs malfunction when they ++ * receive this request following a STALL (they ++ * stop sending isochronous packets). Since a ++ * STALL can't leave the TT buffer in a busy ++ * state (if you believe Figures 11-48 - 11-51 ++ * in the USB 2.0 spec), we won't clear the TT ++ * buffer in this case. Strictly speaking this ++ * is a violation of the spec. ++ */ ++ if (last_status != -EPIPE) ++ fotg210_clear_tt_buffer(fotg210, qh, ++ urb, token); ++ } ++ } ++ ++ /* if we're removing something not at the queue head, ++ * patch the hardware queue pointer. ++ */ ++ if (stopped && qtd->qtd_list.prev != &qh->qtd_list) { ++ last = list_entry(qtd->qtd_list.prev, ++ struct fotg210_qtd, qtd_list); ++ last->hw_next = qtd->hw_next; ++ } ++ ++ /* remove qtd; it's recycled after possible urb completion */ ++ list_del(&qtd->qtd_list); ++ last = qtd; ++ ++ /* reinit the xacterr counter for the next qtd */ ++ qh->xacterrs = 0; ++ } ++ ++ /* last urb's completion might still need calling */ ++ if (likely(last != NULL)) { ++ fotg210_urb_done(fotg210, last->urb, last_status); ++ count++; ++ fotg210_qtd_free(fotg210, last); ++ } ++ ++ /* Do we need to rescan for URBs dequeued during a giveback? */ ++ if (unlikely(qh->needs_rescan)) { ++ /* If the QH is already unlinked, do the rescan now. */ ++ if (state == QH_STATE_IDLE) ++ goto rescan; ++ ++ /* Otherwise we have to wait until the QH is fully unlinked. ++ * Our caller will start an unlink if qh->needs_rescan is ++ * set. But if an unlink has already started, nothing needs ++ * to be done. ++ */ ++ if (state != QH_STATE_LINKED) ++ qh->needs_rescan = 0; ++ } ++ ++ /* restore original state; caller must unlink or relink */ ++ qh->qh_state = state; ++ ++ /* be sure the hardware's done with the qh before refreshing ++ * it after fault cleanup, or recovering from silicon wrongly ++ * overlaying the dummy qtd (which reduces DMA chatter). ++ */ ++ if (stopped != 0 || hw->hw_qtd_next == FOTG210_LIST_END(fotg210)) { ++ switch (state) { ++ case QH_STATE_IDLE: ++ qh_refresh(fotg210, qh); ++ break; ++ case QH_STATE_LINKED: ++ /* We won't refresh a QH that's linked (after the HC ++ * stopped the queue). That avoids a race: ++ * - HC reads first part of QH; ++ * - CPU updates that first part and the token; ++ * - HC reads rest of that QH, including token ++ * Result: HC gets an inconsistent image, and then ++ * DMAs to/from the wrong memory (corrupting it). ++ * ++ * That should be rare for interrupt transfers, ++ * except maybe high bandwidth ... ++ */ ++ ++ /* Tell the caller to start an unlink */ ++ qh->needs_rescan = 1; ++ break; ++ /* otherwise, unlink already started */ ++ } ++ } ++ ++ return count; ++} ++ ++/* reverse of qh_urb_transaction: free a list of TDs. ++ * used for cleanup after errors, before HC sees an URB's TDs. ++ */ ++static void qtd_list_free(struct fotg210_hcd *fotg210, struct urb *urb, ++ struct list_head *head) ++{ ++ struct fotg210_qtd *qtd, *temp; ++ ++ list_for_each_entry_safe(qtd, temp, head, qtd_list) { ++ list_del(&qtd->qtd_list); ++ fotg210_qtd_free(fotg210, qtd); ++ } ++} ++ ++/* create a list of filled qtds for this URB; won't link into qh. ++ */ ++static struct list_head *qh_urb_transaction(struct fotg210_hcd *fotg210, ++ struct urb *urb, struct list_head *head, gfp_t flags) ++{ ++ struct fotg210_qtd *qtd, *qtd_prev; ++ dma_addr_t buf; ++ int len, this_sg_len, maxpacket; ++ int is_input; ++ u32 token; ++ int i; ++ struct scatterlist *sg; ++ ++ /* ++ * URBs map to sequences of QTDs: one logical transaction ++ */ ++ qtd = fotg210_qtd_alloc(fotg210, flags); ++ if (unlikely(!qtd)) ++ return NULL; ++ list_add_tail(&qtd->qtd_list, head); ++ qtd->urb = urb; ++ ++ token = QTD_STS_ACTIVE; ++ token |= (FOTG210_TUNE_CERR << 10); ++ /* for split transactions, SplitXState initialized to zero */ ++ ++ len = urb->transfer_buffer_length; ++ is_input = usb_pipein(urb->pipe); ++ if (usb_pipecontrol(urb->pipe)) { ++ /* SETUP pid */ ++ qtd_fill(fotg210, qtd, urb->setup_dma, ++ sizeof(struct usb_ctrlrequest), ++ token | (2 /* "setup" */ << 8), 8); ++ ++ /* ... and always at least one more pid */ ++ token ^= QTD_TOGGLE; ++ qtd_prev = qtd; ++ qtd = fotg210_qtd_alloc(fotg210, flags); ++ if (unlikely(!qtd)) ++ goto cleanup; ++ qtd->urb = urb; ++ qtd_prev->hw_next = QTD_NEXT(fotg210, qtd->qtd_dma); ++ list_add_tail(&qtd->qtd_list, head); ++ ++ /* for zero length DATA stages, STATUS is always IN */ ++ if (len == 0) ++ token |= (1 /* "in" */ << 8); ++ } ++ ++ /* ++ * data transfer stage: buffer setup ++ */ ++ i = urb->num_mapped_sgs; ++ if (len > 0 && i > 0) { ++ sg = urb->sg; ++ buf = sg_dma_address(sg); ++ ++ /* urb->transfer_buffer_length may be smaller than the ++ * size of the scatterlist (or vice versa) ++ */ ++ this_sg_len = min_t(int, sg_dma_len(sg), len); ++ } else { ++ sg = NULL; ++ buf = urb->transfer_dma; ++ this_sg_len = len; ++ } ++ ++ if (is_input) ++ token |= (1 /* "in" */ << 8); ++ /* else it's already initted to "out" pid (0 << 8) */ ++ ++ maxpacket = usb_maxpacket(urb->dev, urb->pipe); ++ ++ /* ++ * buffer gets wrapped in one or more qtds; ++ * last one may be "short" (including zero len) ++ * and may serve as a control status ack ++ */ ++ for (;;) { ++ int this_qtd_len; ++ ++ this_qtd_len = qtd_fill(fotg210, qtd, buf, this_sg_len, token, ++ maxpacket); ++ this_sg_len -= this_qtd_len; ++ len -= this_qtd_len; ++ buf += this_qtd_len; ++ ++ /* ++ * short reads advance to a "magic" dummy instead of the next ++ * qtd ... that forces the queue to stop, for manual cleanup. ++ * (this will usually be overridden later.) ++ */ ++ if (is_input) ++ qtd->hw_alt_next = fotg210->async->hw->hw_alt_next; ++ ++ /* qh makes control packets use qtd toggle; maybe switch it */ ++ if ((maxpacket & (this_qtd_len + (maxpacket - 1))) == 0) ++ token ^= QTD_TOGGLE; ++ ++ if (likely(this_sg_len <= 0)) { ++ if (--i <= 0 || len <= 0) ++ break; ++ sg = sg_next(sg); ++ buf = sg_dma_address(sg); ++ this_sg_len = min_t(int, sg_dma_len(sg), len); ++ } ++ ++ qtd_prev = qtd; ++ qtd = fotg210_qtd_alloc(fotg210, flags); ++ if (unlikely(!qtd)) ++ goto cleanup; ++ qtd->urb = urb; ++ qtd_prev->hw_next = QTD_NEXT(fotg210, qtd->qtd_dma); ++ list_add_tail(&qtd->qtd_list, head); ++ } ++ ++ /* ++ * unless the caller requires manual cleanup after short reads, ++ * have the alt_next mechanism keep the queue running after the ++ * last data qtd (the only one, for control and most other cases). ++ */ ++ if (likely((urb->transfer_flags & URB_SHORT_NOT_OK) == 0 || ++ usb_pipecontrol(urb->pipe))) ++ qtd->hw_alt_next = FOTG210_LIST_END(fotg210); ++ ++ /* ++ * control requests may need a terminating data "status" ack; ++ * other OUT ones may need a terminating short packet ++ * (zero length). ++ */ ++ if (likely(urb->transfer_buffer_length != 0)) { ++ int one_more = 0; ++ ++ if (usb_pipecontrol(urb->pipe)) { ++ one_more = 1; ++ token ^= 0x0100; /* "in" <--> "out" */ ++ token |= QTD_TOGGLE; /* force DATA1 */ ++ } else if (usb_pipeout(urb->pipe) ++ && (urb->transfer_flags & URB_ZERO_PACKET) ++ && !(urb->transfer_buffer_length % maxpacket)) { ++ one_more = 1; ++ } ++ if (one_more) { ++ qtd_prev = qtd; ++ qtd = fotg210_qtd_alloc(fotg210, flags); ++ if (unlikely(!qtd)) ++ goto cleanup; ++ qtd->urb = urb; ++ qtd_prev->hw_next = QTD_NEXT(fotg210, qtd->qtd_dma); ++ list_add_tail(&qtd->qtd_list, head); ++ ++ /* never any data in such packets */ ++ qtd_fill(fotg210, qtd, 0, 0, token, 0); ++ } ++ } ++ ++ /* by default, enable interrupt on urb completion */ ++ if (likely(!(urb->transfer_flags & URB_NO_INTERRUPT))) ++ qtd->hw_token |= cpu_to_hc32(fotg210, QTD_IOC); ++ return head; ++ ++cleanup: ++ qtd_list_free(fotg210, urb, head); ++ return NULL; ++} ++ ++/* Would be best to create all qh's from config descriptors, ++ * when each interface/altsetting is established. Unlink ++ * any previous qh and cancel its urbs first; endpoints are ++ * implicitly reset then (data toggle too). ++ * That'd mean updating how usbcore talks to HCDs. (2.7?) ++ */ ++ ++ ++/* Each QH holds a qtd list; a QH is used for everything except iso. ++ * ++ * For interrupt urbs, the scheduler must set the microframe scheduling ++ * mask(s) each time the QH gets scheduled. For highspeed, that's ++ * just one microframe in the s-mask. For split interrupt transactions ++ * there are additional complications: c-mask, maybe FSTNs. ++ */ ++static struct fotg210_qh *qh_make(struct fotg210_hcd *fotg210, struct urb *urb, ++ gfp_t flags) ++{ ++ struct fotg210_qh *qh = fotg210_qh_alloc(fotg210, flags); ++ struct usb_host_endpoint *ep; ++ u32 info1 = 0, info2 = 0; ++ int is_input, type; ++ int maxp = 0; ++ int mult; ++ struct usb_tt *tt = urb->dev->tt; ++ struct fotg210_qh_hw *hw; ++ ++ if (!qh) ++ return qh; ++ ++ /* ++ * init endpoint/device data for this QH ++ */ ++ info1 |= usb_pipeendpoint(urb->pipe) << 8; ++ info1 |= usb_pipedevice(urb->pipe) << 0; ++ ++ is_input = usb_pipein(urb->pipe); ++ type = usb_pipetype(urb->pipe); ++ ep = usb_pipe_endpoint(urb->dev, urb->pipe); ++ maxp = usb_endpoint_maxp(&ep->desc); ++ mult = usb_endpoint_maxp_mult(&ep->desc); ++ ++ /* 1024 byte maxpacket is a hardware ceiling. High bandwidth ++ * acts like up to 3KB, but is built from smaller packets. ++ */ ++ if (maxp > 1024) { ++ fotg210_dbg(fotg210, "bogus qh maxpacket %d\n", maxp); ++ goto done; ++ } ++ ++ /* Compute interrupt scheduling parameters just once, and save. ++ * - allowing for high bandwidth, how many nsec/uframe are used? ++ * - split transactions need a second CSPLIT uframe; same question ++ * - splits also need a schedule gap (for full/low speed I/O) ++ * - qh has a polling interval ++ * ++ * For control/bulk requests, the HC or TT handles these. ++ */ ++ if (type == PIPE_INTERRUPT) { ++ qh->usecs = NS_TO_US(usb_calc_bus_time(USB_SPEED_HIGH, ++ is_input, 0, mult * maxp)); ++ qh->start = NO_FRAME; ++ ++ if (urb->dev->speed == USB_SPEED_HIGH) { ++ qh->c_usecs = 0; ++ qh->gap_uf = 0; ++ ++ qh->period = urb->interval >> 3; ++ if (qh->period == 0 && urb->interval != 1) { ++ /* NOTE interval 2 or 4 uframes could work. ++ * But interval 1 scheduling is simpler, and ++ * includes high bandwidth. ++ */ ++ urb->interval = 1; ++ } else if (qh->period > fotg210->periodic_size) { ++ qh->period = fotg210->periodic_size; ++ urb->interval = qh->period << 3; ++ } ++ } else { ++ int think_time; ++ ++ /* gap is f(FS/LS transfer times) */ ++ qh->gap_uf = 1 + usb_calc_bus_time(urb->dev->speed, ++ is_input, 0, maxp) / (125 * 1000); ++ ++ /* FIXME this just approximates SPLIT/CSPLIT times */ ++ if (is_input) { /* SPLIT, gap, CSPLIT+DATA */ ++ qh->c_usecs = qh->usecs + HS_USECS(0); ++ qh->usecs = HS_USECS(1); ++ } else { /* SPLIT+DATA, gap, CSPLIT */ ++ qh->usecs += HS_USECS(1); ++ qh->c_usecs = HS_USECS(0); ++ } ++ ++ think_time = tt ? tt->think_time : 0; ++ qh->tt_usecs = NS_TO_US(think_time + ++ usb_calc_bus_time(urb->dev->speed, ++ is_input, 0, maxp)); ++ qh->period = urb->interval; ++ if (qh->period > fotg210->periodic_size) { ++ qh->period = fotg210->periodic_size; ++ urb->interval = qh->period; ++ } ++ } ++ } ++ ++ /* support for tt scheduling, and access to toggles */ ++ qh->dev = urb->dev; ++ ++ /* using TT? */ ++ switch (urb->dev->speed) { ++ case USB_SPEED_LOW: ++ info1 |= QH_LOW_SPEED; ++ fallthrough; ++ ++ case USB_SPEED_FULL: ++ /* EPS 0 means "full" */ ++ if (type != PIPE_INTERRUPT) ++ info1 |= (FOTG210_TUNE_RL_TT << 28); ++ if (type == PIPE_CONTROL) { ++ info1 |= QH_CONTROL_EP; /* for TT */ ++ info1 |= QH_TOGGLE_CTL; /* toggle from qtd */ ++ } ++ info1 |= maxp << 16; ++ ++ info2 |= (FOTG210_TUNE_MULT_TT << 30); ++ ++ /* Some Freescale processors have an erratum in which the ++ * port number in the queue head was 0..N-1 instead of 1..N. ++ */ ++ if (fotg210_has_fsl_portno_bug(fotg210)) ++ info2 |= (urb->dev->ttport-1) << 23; ++ else ++ info2 |= urb->dev->ttport << 23; ++ ++ /* set the address of the TT; for TDI's integrated ++ * root hub tt, leave it zeroed. ++ */ ++ if (tt && tt->hub != fotg210_to_hcd(fotg210)->self.root_hub) ++ info2 |= tt->hub->devnum << 16; ++ ++ /* NOTE: if (PIPE_INTERRUPT) { scheduler sets c-mask } */ ++ ++ break; ++ ++ case USB_SPEED_HIGH: /* no TT involved */ ++ info1 |= QH_HIGH_SPEED; ++ if (type == PIPE_CONTROL) { ++ info1 |= (FOTG210_TUNE_RL_HS << 28); ++ info1 |= 64 << 16; /* usb2 fixed maxpacket */ ++ info1 |= QH_TOGGLE_CTL; /* toggle from qtd */ ++ info2 |= (FOTG210_TUNE_MULT_HS << 30); ++ } else if (type == PIPE_BULK) { ++ info1 |= (FOTG210_TUNE_RL_HS << 28); ++ /* The USB spec says that high speed bulk endpoints ++ * always use 512 byte maxpacket. But some device ++ * vendors decided to ignore that, and MSFT is happy ++ * to help them do so. So now people expect to use ++ * such nonconformant devices with Linux too; sigh. ++ */ ++ info1 |= maxp << 16; ++ info2 |= (FOTG210_TUNE_MULT_HS << 30); ++ } else { /* PIPE_INTERRUPT */ ++ info1 |= maxp << 16; ++ info2 |= mult << 30; ++ } ++ break; ++ default: ++ fotg210_dbg(fotg210, "bogus dev %p speed %d\n", urb->dev, ++ urb->dev->speed); ++done: ++ qh_destroy(fotg210, qh); ++ return NULL; ++ } ++ ++ /* NOTE: if (PIPE_INTERRUPT) { scheduler sets s-mask } */ ++ ++ /* init as live, toggle clear, advance to dummy */ ++ qh->qh_state = QH_STATE_IDLE; ++ hw = qh->hw; ++ hw->hw_info1 = cpu_to_hc32(fotg210, info1); ++ hw->hw_info2 = cpu_to_hc32(fotg210, info2); ++ qh->is_out = !is_input; ++ usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe), !is_input, 1); ++ qh_refresh(fotg210, qh); ++ return qh; ++} ++ ++static void enable_async(struct fotg210_hcd *fotg210) ++{ ++ if (fotg210->async_count++) ++ return; ++ ++ /* Stop waiting to turn off the async schedule */ ++ fotg210->enabled_hrtimer_events &= ~BIT(FOTG210_HRTIMER_DISABLE_ASYNC); ++ ++ /* Don't start the schedule until ASS is 0 */ ++ fotg210_poll_ASS(fotg210); ++ turn_on_io_watchdog(fotg210); ++} ++ ++static void disable_async(struct fotg210_hcd *fotg210) ++{ ++ if (--fotg210->async_count) ++ return; ++ ++ /* The async schedule and async_unlink list are supposed to be empty */ ++ WARN_ON(fotg210->async->qh_next.qh || fotg210->async_unlink); ++ ++ /* Don't turn off the schedule until ASS is 1 */ ++ fotg210_poll_ASS(fotg210); ++} ++ ++/* move qh (and its qtds) onto async queue; maybe enable queue. */ ++ ++static void qh_link_async(struct fotg210_hcd *fotg210, struct fotg210_qh *qh) ++{ ++ __hc32 dma = QH_NEXT(fotg210, qh->qh_dma); ++ struct fotg210_qh *head; ++ ++ /* Don't link a QH if there's a Clear-TT-Buffer pending */ ++ if (unlikely(qh->clearing_tt)) ++ return; ++ ++ WARN_ON(qh->qh_state != QH_STATE_IDLE); ++ ++ /* clear halt and/or toggle; and maybe recover from silicon quirk */ ++ qh_refresh(fotg210, qh); ++ ++ /* splice right after start */ ++ head = fotg210->async; ++ qh->qh_next = head->qh_next; ++ qh->hw->hw_next = head->hw->hw_next; ++ wmb(); ++ ++ head->qh_next.qh = qh; ++ head->hw->hw_next = dma; ++ ++ qh->xacterrs = 0; ++ qh->qh_state = QH_STATE_LINKED; ++ /* qtd completions reported later by interrupt */ ++ ++ enable_async(fotg210); ++} ++ ++/* For control/bulk/interrupt, return QH with these TDs appended. ++ * Allocates and initializes the QH if necessary. ++ * Returns null if it can't allocate a QH it needs to. ++ * If the QH has TDs (urbs) already, that's great. ++ */ ++static struct fotg210_qh *qh_append_tds(struct fotg210_hcd *fotg210, ++ struct urb *urb, struct list_head *qtd_list, ++ int epnum, void **ptr) ++{ ++ struct fotg210_qh *qh = NULL; ++ __hc32 qh_addr_mask = cpu_to_hc32(fotg210, 0x7f); ++ ++ qh = (struct fotg210_qh *) *ptr; ++ if (unlikely(qh == NULL)) { ++ /* can't sleep here, we have fotg210->lock... */ ++ qh = qh_make(fotg210, urb, GFP_ATOMIC); ++ *ptr = qh; ++ } ++ if (likely(qh != NULL)) { ++ struct fotg210_qtd *qtd; ++ ++ if (unlikely(list_empty(qtd_list))) ++ qtd = NULL; ++ else ++ qtd = list_entry(qtd_list->next, struct fotg210_qtd, ++ qtd_list); ++ ++ /* control qh may need patching ... */ ++ if (unlikely(epnum == 0)) { ++ /* usb_reset_device() briefly reverts to address 0 */ ++ if (usb_pipedevice(urb->pipe) == 0) ++ qh->hw->hw_info1 &= ~qh_addr_mask; ++ } ++ ++ /* just one way to queue requests: swap with the dummy qtd. ++ * only hc or qh_refresh() ever modify the overlay. ++ */ ++ if (likely(qtd != NULL)) { ++ struct fotg210_qtd *dummy; ++ dma_addr_t dma; ++ __hc32 token; ++ ++ /* to avoid racing the HC, use the dummy td instead of ++ * the first td of our list (becomes new dummy). both ++ * tds stay deactivated until we're done, when the ++ * HC is allowed to fetch the old dummy (4.10.2). ++ */ ++ token = qtd->hw_token; ++ qtd->hw_token = HALT_BIT(fotg210); ++ ++ dummy = qh->dummy; ++ ++ dma = dummy->qtd_dma; ++ *dummy = *qtd; ++ dummy->qtd_dma = dma; ++ ++ list_del(&qtd->qtd_list); ++ list_add(&dummy->qtd_list, qtd_list); ++ list_splice_tail(qtd_list, &qh->qtd_list); ++ ++ fotg210_qtd_init(fotg210, qtd, qtd->qtd_dma); ++ qh->dummy = qtd; ++ ++ /* hc must see the new dummy at list end */ ++ dma = qtd->qtd_dma; ++ qtd = list_entry(qh->qtd_list.prev, ++ struct fotg210_qtd, qtd_list); ++ qtd->hw_next = QTD_NEXT(fotg210, dma); ++ ++ /* let the hc process these next qtds */ ++ wmb(); ++ dummy->hw_token = token; ++ ++ urb->hcpriv = qh; ++ } ++ } ++ return qh; ++} ++ ++static int submit_async(struct fotg210_hcd *fotg210, struct urb *urb, ++ struct list_head *qtd_list, gfp_t mem_flags) ++{ ++ int epnum; ++ unsigned long flags; ++ struct fotg210_qh *qh = NULL; ++ int rc; ++ ++ epnum = urb->ep->desc.bEndpointAddress; ++ ++#ifdef FOTG210_URB_TRACE ++ { ++ struct fotg210_qtd *qtd; ++ ++ qtd = list_entry(qtd_list->next, struct fotg210_qtd, qtd_list); ++ fotg210_dbg(fotg210, ++ "%s %s urb %p ep%d%s len %d, qtd %p [qh %p]\n", ++ __func__, urb->dev->devpath, urb, ++ epnum & 0x0f, (epnum & USB_DIR_IN) ++ ? "in" : "out", ++ urb->transfer_buffer_length, ++ qtd, urb->ep->hcpriv); ++ } ++#endif ++ ++ spin_lock_irqsave(&fotg210->lock, flags); ++ if (unlikely(!HCD_HW_ACCESSIBLE(fotg210_to_hcd(fotg210)))) { ++ rc = -ESHUTDOWN; ++ goto done; ++ } ++ rc = usb_hcd_link_urb_to_ep(fotg210_to_hcd(fotg210), urb); ++ if (unlikely(rc)) ++ goto done; ++ ++ qh = qh_append_tds(fotg210, urb, qtd_list, epnum, &urb->ep->hcpriv); ++ if (unlikely(qh == NULL)) { ++ usb_hcd_unlink_urb_from_ep(fotg210_to_hcd(fotg210), urb); ++ rc = -ENOMEM; ++ goto done; ++ } ++ ++ /* Control/bulk operations through TTs don't need scheduling, ++ * the HC and TT handle it when the TT has a buffer ready. ++ */ ++ if (likely(qh->qh_state == QH_STATE_IDLE)) ++ qh_link_async(fotg210, qh); ++done: ++ spin_unlock_irqrestore(&fotg210->lock, flags); ++ if (unlikely(qh == NULL)) ++ qtd_list_free(fotg210, urb, qtd_list); ++ return rc; ++} ++ ++static void single_unlink_async(struct fotg210_hcd *fotg210, ++ struct fotg210_qh *qh) ++{ ++ struct fotg210_qh *prev; ++ ++ /* Add to the end of the list of QHs waiting for the next IAAD */ ++ qh->qh_state = QH_STATE_UNLINK; ++ if (fotg210->async_unlink) ++ fotg210->async_unlink_last->unlink_next = qh; ++ else ++ fotg210->async_unlink = qh; ++ fotg210->async_unlink_last = qh; ++ ++ /* Unlink it from the schedule */ ++ prev = fotg210->async; ++ while (prev->qh_next.qh != qh) ++ prev = prev->qh_next.qh; ++ ++ prev->hw->hw_next = qh->hw->hw_next; ++ prev->qh_next = qh->qh_next; ++ if (fotg210->qh_scan_next == qh) ++ fotg210->qh_scan_next = qh->qh_next.qh; ++} ++ ++static void start_iaa_cycle(struct fotg210_hcd *fotg210, bool nested) ++{ ++ /* ++ * Do nothing if an IAA cycle is already running or ++ * if one will be started shortly. ++ */ ++ if (fotg210->async_iaa || fotg210->async_unlinking) ++ return; ++ ++ /* Do all the waiting QHs at once */ ++ fotg210->async_iaa = fotg210->async_unlink; ++ fotg210->async_unlink = NULL; ++ ++ /* If the controller isn't running, we don't have to wait for it */ ++ if (unlikely(fotg210->rh_state < FOTG210_RH_RUNNING)) { ++ if (!nested) /* Avoid recursion */ ++ end_unlink_async(fotg210); ++ ++ /* Otherwise start a new IAA cycle */ ++ } else if (likely(fotg210->rh_state == FOTG210_RH_RUNNING)) { ++ /* Make sure the unlinks are all visible to the hardware */ ++ wmb(); ++ ++ fotg210_writel(fotg210, fotg210->command | CMD_IAAD, ++ &fotg210->regs->command); ++ fotg210_readl(fotg210, &fotg210->regs->command); ++ fotg210_enable_event(fotg210, FOTG210_HRTIMER_IAA_WATCHDOG, ++ true); ++ } ++} ++ ++/* the async qh for the qtds being unlinked are now gone from the HC */ ++ ++static void end_unlink_async(struct fotg210_hcd *fotg210) ++{ ++ struct fotg210_qh *qh; ++ ++ /* Process the idle QHs */ ++restart: ++ fotg210->async_unlinking = true; ++ while (fotg210->async_iaa) { ++ qh = fotg210->async_iaa; ++ fotg210->async_iaa = qh->unlink_next; ++ qh->unlink_next = NULL; ++ ++ qh->qh_state = QH_STATE_IDLE; ++ qh->qh_next.qh = NULL; ++ ++ qh_completions(fotg210, qh); ++ if (!list_empty(&qh->qtd_list) && ++ fotg210->rh_state == FOTG210_RH_RUNNING) ++ qh_link_async(fotg210, qh); ++ disable_async(fotg210); ++ } ++ fotg210->async_unlinking = false; ++ ++ /* Start a new IAA cycle if any QHs are waiting for it */ ++ if (fotg210->async_unlink) { ++ start_iaa_cycle(fotg210, true); ++ if (unlikely(fotg210->rh_state < FOTG210_RH_RUNNING)) ++ goto restart; ++ } ++} ++ ++static void unlink_empty_async(struct fotg210_hcd *fotg210) ++{ ++ struct fotg210_qh *qh, *next; ++ bool stopped = (fotg210->rh_state < FOTG210_RH_RUNNING); ++ bool check_unlinks_later = false; ++ ++ /* Unlink all the async QHs that have been empty for a timer cycle */ ++ next = fotg210->async->qh_next.qh; ++ while (next) { ++ qh = next; ++ next = qh->qh_next.qh; ++ ++ if (list_empty(&qh->qtd_list) && ++ qh->qh_state == QH_STATE_LINKED) { ++ if (!stopped && qh->unlink_cycle == ++ fotg210->async_unlink_cycle) ++ check_unlinks_later = true; ++ else ++ single_unlink_async(fotg210, qh); ++ } ++ } ++ ++ /* Start a new IAA cycle if any QHs are waiting for it */ ++ if (fotg210->async_unlink) ++ start_iaa_cycle(fotg210, false); ++ ++ /* QHs that haven't been empty for long enough will be handled later */ ++ if (check_unlinks_later) { ++ fotg210_enable_event(fotg210, FOTG210_HRTIMER_ASYNC_UNLINKS, ++ true); ++ ++fotg210->async_unlink_cycle; ++ } ++} ++ ++/* makes sure the async qh will become idle */ ++/* caller must own fotg210->lock */ ++ ++static void start_unlink_async(struct fotg210_hcd *fotg210, ++ struct fotg210_qh *qh) ++{ ++ /* ++ * If the QH isn't linked then there's nothing we can do ++ * unless we were called during a giveback, in which case ++ * qh_completions() has to deal with it. ++ */ ++ if (qh->qh_state != QH_STATE_LINKED) { ++ if (qh->qh_state == QH_STATE_COMPLETING) ++ qh->needs_rescan = 1; ++ return; ++ } ++ ++ single_unlink_async(fotg210, qh); ++ start_iaa_cycle(fotg210, false); ++} ++ ++static void scan_async(struct fotg210_hcd *fotg210) ++{ ++ struct fotg210_qh *qh; ++ bool check_unlinks_later = false; ++ ++ fotg210->qh_scan_next = fotg210->async->qh_next.qh; ++ while (fotg210->qh_scan_next) { ++ qh = fotg210->qh_scan_next; ++ fotg210->qh_scan_next = qh->qh_next.qh; ++rescan: ++ /* clean any finished work for this qh */ ++ if (!list_empty(&qh->qtd_list)) { ++ int temp; ++ ++ /* ++ * Unlinks could happen here; completion reporting ++ * drops the lock. That's why fotg210->qh_scan_next ++ * always holds the next qh to scan; if the next qh ++ * gets unlinked then fotg210->qh_scan_next is adjusted ++ * in single_unlink_async(). ++ */ ++ temp = qh_completions(fotg210, qh); ++ if (qh->needs_rescan) { ++ start_unlink_async(fotg210, qh); ++ } else if (list_empty(&qh->qtd_list) ++ && qh->qh_state == QH_STATE_LINKED) { ++ qh->unlink_cycle = fotg210->async_unlink_cycle; ++ check_unlinks_later = true; ++ } else if (temp != 0) ++ goto rescan; ++ } ++ } ++ ++ /* ++ * Unlink empty entries, reducing DMA usage as well ++ * as HCD schedule-scanning costs. Delay for any qh ++ * we just scanned, there's a not-unusual case that it ++ * doesn't stay idle for long. ++ */ ++ if (check_unlinks_later && fotg210->rh_state == FOTG210_RH_RUNNING && ++ !(fotg210->enabled_hrtimer_events & ++ BIT(FOTG210_HRTIMER_ASYNC_UNLINKS))) { ++ fotg210_enable_event(fotg210, ++ FOTG210_HRTIMER_ASYNC_UNLINKS, true); ++ ++fotg210->async_unlink_cycle; ++ } ++} ++/* EHCI scheduled transaction support: interrupt, iso, split iso ++ * These are called "periodic" transactions in the EHCI spec. ++ * ++ * Note that for interrupt transfers, the QH/QTD manipulation is shared ++ * with the "asynchronous" transaction support (control/bulk transfers). ++ * The only real difference is in how interrupt transfers are scheduled. ++ * ++ * For ISO, we make an "iso_stream" head to serve the same role as a QH. ++ * It keeps track of every ITD (or SITD) that's linked, and holds enough ++ * pre-calculated schedule data to make appending to the queue be quick. ++ */ ++static int fotg210_get_frame(struct usb_hcd *hcd); ++ ++/* periodic_next_shadow - return "next" pointer on shadow list ++ * @periodic: host pointer to qh/itd ++ * @tag: hardware tag for type of this record ++ */ ++static union fotg210_shadow *periodic_next_shadow(struct fotg210_hcd *fotg210, ++ union fotg210_shadow *periodic, __hc32 tag) ++{ ++ switch (hc32_to_cpu(fotg210, tag)) { ++ case Q_TYPE_QH: ++ return &periodic->qh->qh_next; ++ case Q_TYPE_FSTN: ++ return &periodic->fstn->fstn_next; ++ default: ++ return &periodic->itd->itd_next; ++ } ++} ++ ++static __hc32 *shadow_next_periodic(struct fotg210_hcd *fotg210, ++ union fotg210_shadow *periodic, __hc32 tag) ++{ ++ switch (hc32_to_cpu(fotg210, tag)) { ++ /* our fotg210_shadow.qh is actually software part */ ++ case Q_TYPE_QH: ++ return &periodic->qh->hw->hw_next; ++ /* others are hw parts */ ++ default: ++ return periodic->hw_next; ++ } ++} ++ ++/* caller must hold fotg210->lock */ ++static void periodic_unlink(struct fotg210_hcd *fotg210, unsigned frame, ++ void *ptr) ++{ ++ union fotg210_shadow *prev_p = &fotg210->pshadow[frame]; ++ __hc32 *hw_p = &fotg210->periodic[frame]; ++ union fotg210_shadow here = *prev_p; ++ ++ /* find predecessor of "ptr"; hw and shadow lists are in sync */ ++ while (here.ptr && here.ptr != ptr) { ++ prev_p = periodic_next_shadow(fotg210, prev_p, ++ Q_NEXT_TYPE(fotg210, *hw_p)); ++ hw_p = shadow_next_periodic(fotg210, &here, ++ Q_NEXT_TYPE(fotg210, *hw_p)); ++ here = *prev_p; ++ } ++ /* an interrupt entry (at list end) could have been shared */ ++ if (!here.ptr) ++ return; ++ ++ /* update shadow and hardware lists ... the old "next" pointers ++ * from ptr may still be in use, the caller updates them. ++ */ ++ *prev_p = *periodic_next_shadow(fotg210, &here, ++ Q_NEXT_TYPE(fotg210, *hw_p)); ++ ++ *hw_p = *shadow_next_periodic(fotg210, &here, ++ Q_NEXT_TYPE(fotg210, *hw_p)); ++} ++ ++/* how many of the uframe's 125 usecs are allocated? */ ++static unsigned short periodic_usecs(struct fotg210_hcd *fotg210, ++ unsigned frame, unsigned uframe) ++{ ++ __hc32 *hw_p = &fotg210->periodic[frame]; ++ union fotg210_shadow *q = &fotg210->pshadow[frame]; ++ unsigned usecs = 0; ++ struct fotg210_qh_hw *hw; ++ ++ while (q->ptr) { ++ switch (hc32_to_cpu(fotg210, Q_NEXT_TYPE(fotg210, *hw_p))) { ++ case Q_TYPE_QH: ++ hw = q->qh->hw; ++ /* is it in the S-mask? */ ++ if (hw->hw_info2 & cpu_to_hc32(fotg210, 1 << uframe)) ++ usecs += q->qh->usecs; ++ /* ... or C-mask? */ ++ if (hw->hw_info2 & cpu_to_hc32(fotg210, ++ 1 << (8 + uframe))) ++ usecs += q->qh->c_usecs; ++ hw_p = &hw->hw_next; ++ q = &q->qh->qh_next; ++ break; ++ /* case Q_TYPE_FSTN: */ ++ default: ++ /* for "save place" FSTNs, count the relevant INTR ++ * bandwidth from the previous frame ++ */ ++ if (q->fstn->hw_prev != FOTG210_LIST_END(fotg210)) ++ fotg210_dbg(fotg210, "ignoring FSTN cost ...\n"); ++ ++ hw_p = &q->fstn->hw_next; ++ q = &q->fstn->fstn_next; ++ break; ++ case Q_TYPE_ITD: ++ if (q->itd->hw_transaction[uframe]) ++ usecs += q->itd->stream->usecs; ++ hw_p = &q->itd->hw_next; ++ q = &q->itd->itd_next; ++ break; ++ } ++ } ++ if (usecs > fotg210->uframe_periodic_max) ++ fotg210_err(fotg210, "uframe %d sched overrun: %d usecs\n", ++ frame * 8 + uframe, usecs); ++ return usecs; ++} ++ ++static int same_tt(struct usb_device *dev1, struct usb_device *dev2) ++{ ++ if (!dev1->tt || !dev2->tt) ++ return 0; ++ if (dev1->tt != dev2->tt) ++ return 0; ++ if (dev1->tt->multi) ++ return dev1->ttport == dev2->ttport; ++ else ++ return 1; ++} ++ ++/* return true iff the device's transaction translator is available ++ * for a periodic transfer starting at the specified frame, using ++ * all the uframes in the mask. ++ */ ++static int tt_no_collision(struct fotg210_hcd *fotg210, unsigned period, ++ struct usb_device *dev, unsigned frame, u32 uf_mask) ++{ ++ if (period == 0) /* error */ ++ return 0; ++ ++ /* note bandwidth wastage: split never follows csplit ++ * (different dev or endpoint) until the next uframe. ++ * calling convention doesn't make that distinction. ++ */ ++ for (; frame < fotg210->periodic_size; frame += period) { ++ union fotg210_shadow here; ++ __hc32 type; ++ struct fotg210_qh_hw *hw; ++ ++ here = fotg210->pshadow[frame]; ++ type = Q_NEXT_TYPE(fotg210, fotg210->periodic[frame]); ++ while (here.ptr) { ++ switch (hc32_to_cpu(fotg210, type)) { ++ case Q_TYPE_ITD: ++ type = Q_NEXT_TYPE(fotg210, here.itd->hw_next); ++ here = here.itd->itd_next; ++ continue; ++ case Q_TYPE_QH: ++ hw = here.qh->hw; ++ if (same_tt(dev, here.qh->dev)) { ++ u32 mask; ++ ++ mask = hc32_to_cpu(fotg210, ++ hw->hw_info2); ++ /* "knows" no gap is needed */ ++ mask |= mask >> 8; ++ if (mask & uf_mask) ++ break; ++ } ++ type = Q_NEXT_TYPE(fotg210, hw->hw_next); ++ here = here.qh->qh_next; ++ continue; ++ /* case Q_TYPE_FSTN: */ ++ default: ++ fotg210_dbg(fotg210, ++ "periodic frame %d bogus type %d\n", ++ frame, type); ++ } ++ ++ /* collision or error */ ++ return 0; ++ } ++ } ++ ++ /* no collision */ ++ return 1; ++} ++ ++static void enable_periodic(struct fotg210_hcd *fotg210) ++{ ++ if (fotg210->periodic_count++) ++ return; ++ ++ /* Stop waiting to turn off the periodic schedule */ ++ fotg210->enabled_hrtimer_events &= ++ ~BIT(FOTG210_HRTIMER_DISABLE_PERIODIC); ++ ++ /* Don't start the schedule until PSS is 0 */ ++ fotg210_poll_PSS(fotg210); ++ turn_on_io_watchdog(fotg210); ++} ++ ++static void disable_periodic(struct fotg210_hcd *fotg210) ++{ ++ if (--fotg210->periodic_count) ++ return; ++ ++ /* Don't turn off the schedule until PSS is 1 */ ++ fotg210_poll_PSS(fotg210); ++} ++ ++/* periodic schedule slots have iso tds (normal or split) first, then a ++ * sparse tree for active interrupt transfers. ++ * ++ * this just links in a qh; caller guarantees uframe masks are set right. ++ * no FSTN support (yet; fotg210 0.96+) ++ */ ++static void qh_link_periodic(struct fotg210_hcd *fotg210, struct fotg210_qh *qh) ++{ ++ unsigned i; ++ unsigned period = qh->period; ++ ++ dev_dbg(&qh->dev->dev, ++ "link qh%d-%04x/%p start %d [%d/%d us]\n", period, ++ hc32_to_cpup(fotg210, &qh->hw->hw_info2) & ++ (QH_CMASK | QH_SMASK), qh, qh->start, qh->usecs, ++ qh->c_usecs); ++ ++ /* high bandwidth, or otherwise every microframe */ ++ if (period == 0) ++ period = 1; ++ ++ for (i = qh->start; i < fotg210->periodic_size; i += period) { ++ union fotg210_shadow *prev = &fotg210->pshadow[i]; ++ __hc32 *hw_p = &fotg210->periodic[i]; ++ union fotg210_shadow here = *prev; ++ __hc32 type = 0; ++ ++ /* skip the iso nodes at list head */ ++ while (here.ptr) { ++ type = Q_NEXT_TYPE(fotg210, *hw_p); ++ if (type == cpu_to_hc32(fotg210, Q_TYPE_QH)) ++ break; ++ prev = periodic_next_shadow(fotg210, prev, type); ++ hw_p = shadow_next_periodic(fotg210, &here, type); ++ here = *prev; ++ } ++ ++ /* sorting each branch by period (slow-->fast) ++ * enables sharing interior tree nodes ++ */ ++ while (here.ptr && qh != here.qh) { ++ if (qh->period > here.qh->period) ++ break; ++ prev = &here.qh->qh_next; ++ hw_p = &here.qh->hw->hw_next; ++ here = *prev; ++ } ++ /* link in this qh, unless some earlier pass did that */ ++ if (qh != here.qh) { ++ qh->qh_next = here; ++ if (here.qh) ++ qh->hw->hw_next = *hw_p; ++ wmb(); ++ prev->qh = qh; ++ *hw_p = QH_NEXT(fotg210, qh->qh_dma); ++ } ++ } ++ qh->qh_state = QH_STATE_LINKED; ++ qh->xacterrs = 0; ++ ++ /* update per-qh bandwidth for usbfs */ ++ fotg210_to_hcd(fotg210)->self.bandwidth_allocated += qh->period ++ ? ((qh->usecs + qh->c_usecs) / qh->period) ++ : (qh->usecs * 8); ++ ++ list_add(&qh->intr_node, &fotg210->intr_qh_list); ++ ++ /* maybe enable periodic schedule processing */ ++ ++fotg210->intr_count; ++ enable_periodic(fotg210); ++} ++ ++static void qh_unlink_periodic(struct fotg210_hcd *fotg210, ++ struct fotg210_qh *qh) ++{ ++ unsigned i; ++ unsigned period; ++ ++ /* ++ * If qh is for a low/full-speed device, simply unlinking it ++ * could interfere with an ongoing split transaction. To unlink ++ * it safely would require setting the QH_INACTIVATE bit and ++ * waiting at least one frame, as described in EHCI 4.12.2.5. ++ * ++ * We won't bother with any of this. Instead, we assume that the ++ * only reason for unlinking an interrupt QH while the current URB ++ * is still active is to dequeue all the URBs (flush the whole ++ * endpoint queue). ++ * ++ * If rebalancing the periodic schedule is ever implemented, this ++ * approach will no longer be valid. ++ */ ++ ++ /* high bandwidth, or otherwise part of every microframe */ ++ period = qh->period; ++ if (!period) ++ period = 1; ++ ++ for (i = qh->start; i < fotg210->periodic_size; i += period) ++ periodic_unlink(fotg210, i, qh); ++ ++ /* update per-qh bandwidth for usbfs */ ++ fotg210_to_hcd(fotg210)->self.bandwidth_allocated -= qh->period ++ ? ((qh->usecs + qh->c_usecs) / qh->period) ++ : (qh->usecs * 8); ++ ++ dev_dbg(&qh->dev->dev, ++ "unlink qh%d-%04x/%p start %d [%d/%d us]\n", ++ qh->period, hc32_to_cpup(fotg210, &qh->hw->hw_info2) & ++ (QH_CMASK | QH_SMASK), qh, qh->start, qh->usecs, ++ qh->c_usecs); ++ ++ /* qh->qh_next still "live" to HC */ ++ qh->qh_state = QH_STATE_UNLINK; ++ qh->qh_next.ptr = NULL; ++ ++ if (fotg210->qh_scan_next == qh) ++ fotg210->qh_scan_next = list_entry(qh->intr_node.next, ++ struct fotg210_qh, intr_node); ++ list_del(&qh->intr_node); ++} ++ ++static void start_unlink_intr(struct fotg210_hcd *fotg210, ++ struct fotg210_qh *qh) ++{ ++ /* If the QH isn't linked then there's nothing we can do ++ * unless we were called during a giveback, in which case ++ * qh_completions() has to deal with it. ++ */ ++ if (qh->qh_state != QH_STATE_LINKED) { ++ if (qh->qh_state == QH_STATE_COMPLETING) ++ qh->needs_rescan = 1; ++ return; ++ } ++ ++ qh_unlink_periodic(fotg210, qh); ++ ++ /* Make sure the unlinks are visible before starting the timer */ ++ wmb(); ++ ++ /* ++ * The EHCI spec doesn't say how long it takes the controller to ++ * stop accessing an unlinked interrupt QH. The timer delay is ++ * 9 uframes; presumably that will be long enough. ++ */ ++ qh->unlink_cycle = fotg210->intr_unlink_cycle; ++ ++ /* New entries go at the end of the intr_unlink list */ ++ if (fotg210->intr_unlink) ++ fotg210->intr_unlink_last->unlink_next = qh; ++ else ++ fotg210->intr_unlink = qh; ++ fotg210->intr_unlink_last = qh; ++ ++ if (fotg210->intr_unlinking) ++ ; /* Avoid recursive calls */ ++ else if (fotg210->rh_state < FOTG210_RH_RUNNING) ++ fotg210_handle_intr_unlinks(fotg210); ++ else if (fotg210->intr_unlink == qh) { ++ fotg210_enable_event(fotg210, FOTG210_HRTIMER_UNLINK_INTR, ++ true); ++ ++fotg210->intr_unlink_cycle; ++ } ++} ++ ++static void end_unlink_intr(struct fotg210_hcd *fotg210, struct fotg210_qh *qh) ++{ ++ struct fotg210_qh_hw *hw = qh->hw; ++ int rc; ++ ++ qh->qh_state = QH_STATE_IDLE; ++ hw->hw_next = FOTG210_LIST_END(fotg210); ++ ++ qh_completions(fotg210, qh); ++ ++ /* reschedule QH iff another request is queued */ ++ if (!list_empty(&qh->qtd_list) && ++ fotg210->rh_state == FOTG210_RH_RUNNING) { ++ rc = qh_schedule(fotg210, qh); ++ ++ /* An error here likely indicates handshake failure ++ * or no space left in the schedule. Neither fault ++ * should happen often ... ++ * ++ * FIXME kill the now-dysfunctional queued urbs ++ */ ++ if (rc != 0) ++ fotg210_err(fotg210, "can't reschedule qh %p, err %d\n", ++ qh, rc); ++ } ++ ++ /* maybe turn off periodic schedule */ ++ --fotg210->intr_count; ++ disable_periodic(fotg210); ++} ++ ++static int check_period(struct fotg210_hcd *fotg210, unsigned frame, ++ unsigned uframe, unsigned period, unsigned usecs) ++{ ++ int claimed; ++ ++ /* complete split running into next frame? ++ * given FSTN support, we could sometimes check... ++ */ ++ if (uframe >= 8) ++ return 0; ++ ++ /* convert "usecs we need" to "max already claimed" */ ++ usecs = fotg210->uframe_periodic_max - usecs; ++ ++ /* we "know" 2 and 4 uframe intervals were rejected; so ++ * for period 0, check _every_ microframe in the schedule. ++ */ ++ if (unlikely(period == 0)) { ++ do { ++ for (uframe = 0; uframe < 7; uframe++) { ++ claimed = periodic_usecs(fotg210, frame, ++ uframe); ++ if (claimed > usecs) ++ return 0; ++ } ++ } while ((frame += 1) < fotg210->periodic_size); ++ ++ /* just check the specified uframe, at that period */ ++ } else { ++ do { ++ claimed = periodic_usecs(fotg210, frame, uframe); ++ if (claimed > usecs) ++ return 0; ++ } while ((frame += period) < fotg210->periodic_size); ++ } ++ ++ /* success! */ ++ return 1; ++} ++ ++static int check_intr_schedule(struct fotg210_hcd *fotg210, unsigned frame, ++ unsigned uframe, const struct fotg210_qh *qh, __hc32 *c_maskp) ++{ ++ int retval = -ENOSPC; ++ u8 mask = 0; ++ ++ if (qh->c_usecs && uframe >= 6) /* FSTN territory? */ ++ goto done; ++ ++ if (!check_period(fotg210, frame, uframe, qh->period, qh->usecs)) ++ goto done; ++ if (!qh->c_usecs) { ++ retval = 0; ++ *c_maskp = 0; ++ goto done; ++ } ++ ++ /* Make sure this tt's buffer is also available for CSPLITs. ++ * We pessimize a bit; probably the typical full speed case ++ * doesn't need the second CSPLIT. ++ * ++ * NOTE: both SPLIT and CSPLIT could be checked in just ++ * one smart pass... ++ */ ++ mask = 0x03 << (uframe + qh->gap_uf); ++ *c_maskp = cpu_to_hc32(fotg210, mask << 8); ++ ++ mask |= 1 << uframe; ++ if (tt_no_collision(fotg210, qh->period, qh->dev, frame, mask)) { ++ if (!check_period(fotg210, frame, uframe + qh->gap_uf + 1, ++ qh->period, qh->c_usecs)) ++ goto done; ++ if (!check_period(fotg210, frame, uframe + qh->gap_uf, ++ qh->period, qh->c_usecs)) ++ goto done; ++ retval = 0; ++ } ++done: ++ return retval; ++} ++ ++/* "first fit" scheduling policy used the first time through, ++ * or when the previous schedule slot can't be re-used. ++ */ ++static int qh_schedule(struct fotg210_hcd *fotg210, struct fotg210_qh *qh) ++{ ++ int status; ++ unsigned uframe; ++ __hc32 c_mask; ++ unsigned frame; /* 0..(qh->period - 1), or NO_FRAME */ ++ struct fotg210_qh_hw *hw = qh->hw; ++ ++ qh_refresh(fotg210, qh); ++ hw->hw_next = FOTG210_LIST_END(fotg210); ++ frame = qh->start; ++ ++ /* reuse the previous schedule slots, if we can */ ++ if (frame < qh->period) { ++ uframe = ffs(hc32_to_cpup(fotg210, &hw->hw_info2) & QH_SMASK); ++ status = check_intr_schedule(fotg210, frame, --uframe, ++ qh, &c_mask); ++ } else { ++ uframe = 0; ++ c_mask = 0; ++ status = -ENOSPC; ++ } ++ ++ /* else scan the schedule to find a group of slots such that all ++ * uframes have enough periodic bandwidth available. ++ */ ++ if (status) { ++ /* "normal" case, uframing flexible except with splits */ ++ if (qh->period) { ++ int i; ++ ++ for (i = qh->period; status && i > 0; --i) { ++ frame = ++fotg210->random_frame % qh->period; ++ for (uframe = 0; uframe < 8; uframe++) { ++ status = check_intr_schedule(fotg210, ++ frame, uframe, qh, ++ &c_mask); ++ if (status == 0) ++ break; ++ } ++ } ++ ++ /* qh->period == 0 means every uframe */ ++ } else { ++ frame = 0; ++ status = check_intr_schedule(fotg210, 0, 0, qh, ++ &c_mask); ++ } ++ if (status) ++ goto done; ++ qh->start = frame; ++ ++ /* reset S-frame and (maybe) C-frame masks */ ++ hw->hw_info2 &= cpu_to_hc32(fotg210, ~(QH_CMASK | QH_SMASK)); ++ hw->hw_info2 |= qh->period ++ ? cpu_to_hc32(fotg210, 1 << uframe) ++ : cpu_to_hc32(fotg210, QH_SMASK); ++ hw->hw_info2 |= c_mask; ++ } else ++ fotg210_dbg(fotg210, "reused qh %p schedule\n", qh); ++ ++ /* stuff into the periodic schedule */ ++ qh_link_periodic(fotg210, qh); ++done: ++ return status; ++} ++ ++static int intr_submit(struct fotg210_hcd *fotg210, struct urb *urb, ++ struct list_head *qtd_list, gfp_t mem_flags) ++{ ++ unsigned epnum; ++ unsigned long flags; ++ struct fotg210_qh *qh; ++ int status; ++ struct list_head empty; ++ ++ /* get endpoint and transfer/schedule data */ ++ epnum = urb->ep->desc.bEndpointAddress; ++ ++ spin_lock_irqsave(&fotg210->lock, flags); ++ ++ if (unlikely(!HCD_HW_ACCESSIBLE(fotg210_to_hcd(fotg210)))) { ++ status = -ESHUTDOWN; ++ goto done_not_linked; ++ } ++ status = usb_hcd_link_urb_to_ep(fotg210_to_hcd(fotg210), urb); ++ if (unlikely(status)) ++ goto done_not_linked; ++ ++ /* get qh and force any scheduling errors */ ++ INIT_LIST_HEAD(&empty); ++ qh = qh_append_tds(fotg210, urb, &empty, epnum, &urb->ep->hcpriv); ++ if (qh == NULL) { ++ status = -ENOMEM; ++ goto done; ++ } ++ if (qh->qh_state == QH_STATE_IDLE) { ++ status = qh_schedule(fotg210, qh); ++ if (status) ++ goto done; ++ } ++ ++ /* then queue the urb's tds to the qh */ ++ qh = qh_append_tds(fotg210, urb, qtd_list, epnum, &urb->ep->hcpriv); ++ BUG_ON(qh == NULL); ++ ++ /* ... update usbfs periodic stats */ ++ fotg210_to_hcd(fotg210)->self.bandwidth_int_reqs++; ++ ++done: ++ if (unlikely(status)) ++ usb_hcd_unlink_urb_from_ep(fotg210_to_hcd(fotg210), urb); ++done_not_linked: ++ spin_unlock_irqrestore(&fotg210->lock, flags); ++ if (status) ++ qtd_list_free(fotg210, urb, qtd_list); ++ ++ return status; ++} ++ ++static void scan_intr(struct fotg210_hcd *fotg210) ++{ ++ struct fotg210_qh *qh; ++ ++ list_for_each_entry_safe(qh, fotg210->qh_scan_next, ++ &fotg210->intr_qh_list, intr_node) { ++rescan: ++ /* clean any finished work for this qh */ ++ if (!list_empty(&qh->qtd_list)) { ++ int temp; ++ ++ /* ++ * Unlinks could happen here; completion reporting ++ * drops the lock. That's why fotg210->qh_scan_next ++ * always holds the next qh to scan; if the next qh ++ * gets unlinked then fotg210->qh_scan_next is adjusted ++ * in qh_unlink_periodic(). ++ */ ++ temp = qh_completions(fotg210, qh); ++ if (unlikely(qh->needs_rescan || ++ (list_empty(&qh->qtd_list) && ++ qh->qh_state == QH_STATE_LINKED))) ++ start_unlink_intr(fotg210, qh); ++ else if (temp != 0) ++ goto rescan; ++ } ++ } ++} ++ ++/* fotg210_iso_stream ops work with both ITD and SITD */ ++ ++static struct fotg210_iso_stream *iso_stream_alloc(gfp_t mem_flags) ++{ ++ struct fotg210_iso_stream *stream; ++ ++ stream = kzalloc(sizeof(*stream), mem_flags); ++ if (likely(stream != NULL)) { ++ INIT_LIST_HEAD(&stream->td_list); ++ INIT_LIST_HEAD(&stream->free_list); ++ stream->next_uframe = -1; ++ } ++ return stream; ++} ++ ++static void iso_stream_init(struct fotg210_hcd *fotg210, ++ struct fotg210_iso_stream *stream, struct usb_device *dev, ++ int pipe, unsigned interval) ++{ ++ u32 buf1; ++ unsigned epnum, maxp; ++ int is_input; ++ long bandwidth; ++ unsigned multi; ++ struct usb_host_endpoint *ep; ++ ++ /* ++ * this might be a "high bandwidth" highspeed endpoint, ++ * as encoded in the ep descriptor's wMaxPacket field ++ */ ++ epnum = usb_pipeendpoint(pipe); ++ is_input = usb_pipein(pipe) ? USB_DIR_IN : 0; ++ ep = usb_pipe_endpoint(dev, pipe); ++ maxp = usb_endpoint_maxp(&ep->desc); ++ if (is_input) ++ buf1 = (1 << 11); ++ else ++ buf1 = 0; ++ ++ multi = usb_endpoint_maxp_mult(&ep->desc); ++ buf1 |= maxp; ++ maxp *= multi; ++ ++ stream->buf0 = cpu_to_hc32(fotg210, (epnum << 8) | dev->devnum); ++ stream->buf1 = cpu_to_hc32(fotg210, buf1); ++ stream->buf2 = cpu_to_hc32(fotg210, multi); ++ ++ /* usbfs wants to report the average usecs per frame tied up ++ * when transfers on this endpoint are scheduled ... ++ */ ++ if (dev->speed == USB_SPEED_FULL) { ++ interval <<= 3; ++ stream->usecs = NS_TO_US(usb_calc_bus_time(dev->speed, ++ is_input, 1, maxp)); ++ stream->usecs /= 8; ++ } else { ++ stream->highspeed = 1; ++ stream->usecs = HS_USECS_ISO(maxp); ++ } ++ bandwidth = stream->usecs * 8; ++ bandwidth /= interval; ++ ++ stream->bandwidth = bandwidth; ++ stream->udev = dev; ++ stream->bEndpointAddress = is_input | epnum; ++ stream->interval = interval; ++ stream->maxp = maxp; ++} ++ ++static struct fotg210_iso_stream *iso_stream_find(struct fotg210_hcd *fotg210, ++ struct urb *urb) ++{ ++ unsigned epnum; ++ struct fotg210_iso_stream *stream; ++ struct usb_host_endpoint *ep; ++ unsigned long flags; ++ ++ epnum = usb_pipeendpoint(urb->pipe); ++ if (usb_pipein(urb->pipe)) ++ ep = urb->dev->ep_in[epnum]; ++ else ++ ep = urb->dev->ep_out[epnum]; ++ ++ spin_lock_irqsave(&fotg210->lock, flags); ++ stream = ep->hcpriv; ++ ++ if (unlikely(stream == NULL)) { ++ stream = iso_stream_alloc(GFP_ATOMIC); ++ if (likely(stream != NULL)) { ++ ep->hcpriv = stream; ++ stream->ep = ep; ++ iso_stream_init(fotg210, stream, urb->dev, urb->pipe, ++ urb->interval); ++ } ++ ++ /* if dev->ep[epnum] is a QH, hw is set */ ++ } else if (unlikely(stream->hw != NULL)) { ++ fotg210_dbg(fotg210, "dev %s ep%d%s, not iso??\n", ++ urb->dev->devpath, epnum, ++ usb_pipein(urb->pipe) ? "in" : "out"); ++ stream = NULL; ++ } ++ ++ spin_unlock_irqrestore(&fotg210->lock, flags); ++ return stream; ++} ++ ++/* fotg210_iso_sched ops can be ITD-only or SITD-only */ ++ ++static struct fotg210_iso_sched *iso_sched_alloc(unsigned packets, ++ gfp_t mem_flags) ++{ ++ struct fotg210_iso_sched *iso_sched; ++ ++ iso_sched = kzalloc(struct_size(iso_sched, packet, packets), mem_flags); ++ if (likely(iso_sched != NULL)) ++ INIT_LIST_HEAD(&iso_sched->td_list); ++ ++ return iso_sched; ++} ++ ++static inline void itd_sched_init(struct fotg210_hcd *fotg210, ++ struct fotg210_iso_sched *iso_sched, ++ struct fotg210_iso_stream *stream, struct urb *urb) ++{ ++ unsigned i; ++ dma_addr_t dma = urb->transfer_dma; ++ ++ /* how many uframes are needed for these transfers */ ++ iso_sched->span = urb->number_of_packets * stream->interval; ++ ++ /* figure out per-uframe itd fields that we'll need later ++ * when we fit new itds into the schedule. ++ */ ++ for (i = 0; i < urb->number_of_packets; i++) { ++ struct fotg210_iso_packet *uframe = &iso_sched->packet[i]; ++ unsigned length; ++ dma_addr_t buf; ++ u32 trans; ++ ++ length = urb->iso_frame_desc[i].length; ++ buf = dma + urb->iso_frame_desc[i].offset; ++ ++ trans = FOTG210_ISOC_ACTIVE; ++ trans |= buf & 0x0fff; ++ if (unlikely(((i + 1) == urb->number_of_packets)) ++ && !(urb->transfer_flags & URB_NO_INTERRUPT)) ++ trans |= FOTG210_ITD_IOC; ++ trans |= length << 16; ++ uframe->transaction = cpu_to_hc32(fotg210, trans); ++ ++ /* might need to cross a buffer page within a uframe */ ++ uframe->bufp = (buf & ~(u64)0x0fff); ++ buf += length; ++ if (unlikely((uframe->bufp != (buf & ~(u64)0x0fff)))) ++ uframe->cross = 1; ++ } ++} ++ ++static void iso_sched_free(struct fotg210_iso_stream *stream, ++ struct fotg210_iso_sched *iso_sched) ++{ ++ if (!iso_sched) ++ return; ++ /* caller must hold fotg210->lock!*/ ++ list_splice(&iso_sched->td_list, &stream->free_list); ++ kfree(iso_sched); ++} ++ ++static int itd_urb_transaction(struct fotg210_iso_stream *stream, ++ struct fotg210_hcd *fotg210, struct urb *urb, gfp_t mem_flags) ++{ ++ struct fotg210_itd *itd; ++ dma_addr_t itd_dma; ++ int i; ++ unsigned num_itds; ++ struct fotg210_iso_sched *sched; ++ unsigned long flags; ++ ++ sched = iso_sched_alloc(urb->number_of_packets, mem_flags); ++ if (unlikely(sched == NULL)) ++ return -ENOMEM; ++ ++ itd_sched_init(fotg210, sched, stream, urb); ++ ++ if (urb->interval < 8) ++ num_itds = 1 + (sched->span + 7) / 8; ++ else ++ num_itds = urb->number_of_packets; ++ ++ /* allocate/init ITDs */ ++ spin_lock_irqsave(&fotg210->lock, flags); ++ for (i = 0; i < num_itds; i++) { ++ ++ /* ++ * Use iTDs from the free list, but not iTDs that may ++ * still be in use by the hardware. ++ */ ++ if (likely(!list_empty(&stream->free_list))) { ++ itd = list_first_entry(&stream->free_list, ++ struct fotg210_itd, itd_list); ++ if (itd->frame == fotg210->now_frame) ++ goto alloc_itd; ++ list_del(&itd->itd_list); ++ itd_dma = itd->itd_dma; ++ } else { ++alloc_itd: ++ spin_unlock_irqrestore(&fotg210->lock, flags); ++ itd = dma_pool_alloc(fotg210->itd_pool, mem_flags, ++ &itd_dma); ++ spin_lock_irqsave(&fotg210->lock, flags); ++ if (!itd) { ++ iso_sched_free(stream, sched); ++ spin_unlock_irqrestore(&fotg210->lock, flags); ++ return -ENOMEM; ++ } ++ } ++ ++ memset(itd, 0, sizeof(*itd)); ++ itd->itd_dma = itd_dma; ++ list_add(&itd->itd_list, &sched->td_list); ++ } ++ spin_unlock_irqrestore(&fotg210->lock, flags); ++ ++ /* temporarily store schedule info in hcpriv */ ++ urb->hcpriv = sched; ++ urb->error_count = 0; ++ return 0; ++} ++ ++static inline int itd_slot_ok(struct fotg210_hcd *fotg210, u32 mod, u32 uframe, ++ u8 usecs, u32 period) ++{ ++ uframe %= period; ++ do { ++ /* can't commit more than uframe_periodic_max usec */ ++ if (periodic_usecs(fotg210, uframe >> 3, uframe & 0x7) ++ > (fotg210->uframe_periodic_max - usecs)) ++ return 0; ++ ++ /* we know urb->interval is 2^N uframes */ ++ uframe += period; ++ } while (uframe < mod); ++ return 1; ++} ++ ++/* This scheduler plans almost as far into the future as it has actual ++ * periodic schedule slots. (Affected by TUNE_FLS, which defaults to ++ * "as small as possible" to be cache-friendlier.) That limits the size ++ * transfers you can stream reliably; avoid more than 64 msec per urb. ++ * Also avoid queue depths of less than fotg210's worst irq latency (affected ++ * by the per-urb URB_NO_INTERRUPT hint, the log2_irq_thresh module parameter, ++ * and other factors); or more than about 230 msec total (for portability, ++ * given FOTG210_TUNE_FLS and the slop). Or, write a smarter scheduler! ++ */ ++ ++#define SCHEDULE_SLOP 80 /* microframes */ ++ ++static int iso_stream_schedule(struct fotg210_hcd *fotg210, struct urb *urb, ++ struct fotg210_iso_stream *stream) ++{ ++ u32 now, next, start, period, span; ++ int status; ++ unsigned mod = fotg210->periodic_size << 3; ++ struct fotg210_iso_sched *sched = urb->hcpriv; ++ ++ period = urb->interval; ++ span = sched->span; ++ ++ if (span > mod - SCHEDULE_SLOP) { ++ fotg210_dbg(fotg210, "iso request %p too long\n", urb); ++ status = -EFBIG; ++ goto fail; ++ } ++ ++ now = fotg210_read_frame_index(fotg210) & (mod - 1); ++ ++ /* Typical case: reuse current schedule, stream is still active. ++ * Hopefully there are no gaps from the host falling behind ++ * (irq delays etc), but if there are we'll take the next ++ * slot in the schedule, implicitly assuming URB_ISO_ASAP. ++ */ ++ if (likely(!list_empty(&stream->td_list))) { ++ u32 excess; ++ ++ /* For high speed devices, allow scheduling within the ++ * isochronous scheduling threshold. For full speed devices ++ * and Intel PCI-based controllers, don't (work around for ++ * Intel ICH9 bug). ++ */ ++ if (!stream->highspeed && fotg210->fs_i_thresh) ++ next = now + fotg210->i_thresh; ++ else ++ next = now; ++ ++ /* Fell behind (by up to twice the slop amount)? ++ * We decide based on the time of the last currently-scheduled ++ * slot, not the time of the next available slot. ++ */ ++ excess = (stream->next_uframe - period - next) & (mod - 1); ++ if (excess >= mod - 2 * SCHEDULE_SLOP) ++ start = next + excess - mod + period * ++ DIV_ROUND_UP(mod - excess, period); ++ else ++ start = next + excess + period; ++ if (start - now >= mod) { ++ fotg210_dbg(fotg210, "request %p would overflow (%d+%d >= %d)\n", ++ urb, start - now - period, period, ++ mod); ++ status = -EFBIG; ++ goto fail; ++ } ++ } ++ ++ /* need to schedule; when's the next (u)frame we could start? ++ * this is bigger than fotg210->i_thresh allows; scheduling itself ++ * isn't free, the slop should handle reasonably slow cpus. it ++ * can also help high bandwidth if the dma and irq loads don't ++ * jump until after the queue is primed. ++ */ ++ else { ++ int done = 0; ++ ++ start = SCHEDULE_SLOP + (now & ~0x07); ++ ++ /* NOTE: assumes URB_ISO_ASAP, to limit complexity/bugs */ ++ ++ /* find a uframe slot with enough bandwidth. ++ * Early uframes are more precious because full-speed ++ * iso IN transfers can't use late uframes, ++ * and therefore they should be allocated last. ++ */ ++ next = start; ++ start += period; ++ do { ++ start--; ++ /* check schedule: enough space? */ ++ if (itd_slot_ok(fotg210, mod, start, ++ stream->usecs, period)) ++ done = 1; ++ } while (start > next && !done); ++ ++ /* no room in the schedule */ ++ if (!done) { ++ fotg210_dbg(fotg210, "iso resched full %p (now %d max %d)\n", ++ urb, now, now + mod); ++ status = -ENOSPC; ++ goto fail; ++ } ++ } ++ ++ /* Tried to schedule too far into the future? */ ++ if (unlikely(start - now + span - period >= ++ mod - 2 * SCHEDULE_SLOP)) { ++ fotg210_dbg(fotg210, "request %p would overflow (%d+%d >= %d)\n", ++ urb, start - now, span - period, ++ mod - 2 * SCHEDULE_SLOP); ++ status = -EFBIG; ++ goto fail; ++ } ++ ++ stream->next_uframe = start & (mod - 1); ++ ++ /* report high speed start in uframes; full speed, in frames */ ++ urb->start_frame = stream->next_uframe; ++ if (!stream->highspeed) ++ urb->start_frame >>= 3; ++ ++ /* Make sure scan_isoc() sees these */ ++ if (fotg210->isoc_count == 0) ++ fotg210->next_frame = now >> 3; ++ return 0; ++ ++fail: ++ iso_sched_free(stream, sched); ++ urb->hcpriv = NULL; ++ return status; ++} ++ ++static inline void itd_init(struct fotg210_hcd *fotg210, ++ struct fotg210_iso_stream *stream, struct fotg210_itd *itd) ++{ ++ int i; ++ ++ /* it's been recently zeroed */ ++ itd->hw_next = FOTG210_LIST_END(fotg210); ++ itd->hw_bufp[0] = stream->buf0; ++ itd->hw_bufp[1] = stream->buf1; ++ itd->hw_bufp[2] = stream->buf2; ++ ++ for (i = 0; i < 8; i++) ++ itd->index[i] = -1; ++ ++ /* All other fields are filled when scheduling */ ++} ++ ++static inline void itd_patch(struct fotg210_hcd *fotg210, ++ struct fotg210_itd *itd, struct fotg210_iso_sched *iso_sched, ++ unsigned index, u16 uframe) ++{ ++ struct fotg210_iso_packet *uf = &iso_sched->packet[index]; ++ unsigned pg = itd->pg; ++ ++ uframe &= 0x07; ++ itd->index[uframe] = index; ++ ++ itd->hw_transaction[uframe] = uf->transaction; ++ itd->hw_transaction[uframe] |= cpu_to_hc32(fotg210, pg << 12); ++ itd->hw_bufp[pg] |= cpu_to_hc32(fotg210, uf->bufp & ~(u32)0); ++ itd->hw_bufp_hi[pg] |= cpu_to_hc32(fotg210, (u32)(uf->bufp >> 32)); ++ ++ /* iso_frame_desc[].offset must be strictly increasing */ ++ if (unlikely(uf->cross)) { ++ u64 bufp = uf->bufp + 4096; ++ ++ itd->pg = ++pg; ++ itd->hw_bufp[pg] |= cpu_to_hc32(fotg210, bufp & ~(u32)0); ++ itd->hw_bufp_hi[pg] |= cpu_to_hc32(fotg210, (u32)(bufp >> 32)); ++ } ++} ++ ++static inline void itd_link(struct fotg210_hcd *fotg210, unsigned frame, ++ struct fotg210_itd *itd) ++{ ++ union fotg210_shadow *prev = &fotg210->pshadow[frame]; ++ __hc32 *hw_p = &fotg210->periodic[frame]; ++ union fotg210_shadow here = *prev; ++ __hc32 type = 0; ++ ++ /* skip any iso nodes which might belong to previous microframes */ ++ while (here.ptr) { ++ type = Q_NEXT_TYPE(fotg210, *hw_p); ++ if (type == cpu_to_hc32(fotg210, Q_TYPE_QH)) ++ break; ++ prev = periodic_next_shadow(fotg210, prev, type); ++ hw_p = shadow_next_periodic(fotg210, &here, type); ++ here = *prev; ++ } ++ ++ itd->itd_next = here; ++ itd->hw_next = *hw_p; ++ prev->itd = itd; ++ itd->frame = frame; ++ wmb(); ++ *hw_p = cpu_to_hc32(fotg210, itd->itd_dma | Q_TYPE_ITD); ++} ++ ++/* fit urb's itds into the selected schedule slot; activate as needed */ ++static void itd_link_urb(struct fotg210_hcd *fotg210, struct urb *urb, ++ unsigned mod, struct fotg210_iso_stream *stream) ++{ ++ int packet; ++ unsigned next_uframe, uframe, frame; ++ struct fotg210_iso_sched *iso_sched = urb->hcpriv; ++ struct fotg210_itd *itd; ++ ++ next_uframe = stream->next_uframe & (mod - 1); ++ ++ if (unlikely(list_empty(&stream->td_list))) { ++ fotg210_to_hcd(fotg210)->self.bandwidth_allocated ++ += stream->bandwidth; ++ fotg210_dbg(fotg210, ++ "schedule devp %s ep%d%s-iso period %d start %d.%d\n", ++ urb->dev->devpath, stream->bEndpointAddress & 0x0f, ++ (stream->bEndpointAddress & USB_DIR_IN) ? "in" : "out", ++ urb->interval, ++ next_uframe >> 3, next_uframe & 0x7); ++ } ++ ++ /* fill iTDs uframe by uframe */ ++ for (packet = 0, itd = NULL; packet < urb->number_of_packets;) { ++ if (itd == NULL) { ++ /* ASSERT: we have all necessary itds */ ++ ++ /* ASSERT: no itds for this endpoint in this uframe */ ++ ++ itd = list_entry(iso_sched->td_list.next, ++ struct fotg210_itd, itd_list); ++ list_move_tail(&itd->itd_list, &stream->td_list); ++ itd->stream = stream; ++ itd->urb = urb; ++ itd_init(fotg210, stream, itd); ++ } ++ ++ uframe = next_uframe & 0x07; ++ frame = next_uframe >> 3; ++ ++ itd_patch(fotg210, itd, iso_sched, packet, uframe); ++ ++ next_uframe += stream->interval; ++ next_uframe &= mod - 1; ++ packet++; ++ ++ /* link completed itds into the schedule */ ++ if (((next_uframe >> 3) != frame) ++ || packet == urb->number_of_packets) { ++ itd_link(fotg210, frame & (fotg210->periodic_size - 1), ++ itd); ++ itd = NULL; ++ } ++ } ++ stream->next_uframe = next_uframe; ++ ++ /* don't need that schedule data any more */ ++ iso_sched_free(stream, iso_sched); ++ urb->hcpriv = NULL; ++ ++ ++fotg210->isoc_count; ++ enable_periodic(fotg210); ++} ++ ++#define ISO_ERRS (FOTG210_ISOC_BUF_ERR | FOTG210_ISOC_BABBLE |\ ++ FOTG210_ISOC_XACTERR) ++ ++/* Process and recycle a completed ITD. Return true iff its urb completed, ++ * and hence its completion callback probably added things to the hardware ++ * schedule. ++ * ++ * Note that we carefully avoid recycling this descriptor until after any ++ * completion callback runs, so that it won't be reused quickly. That is, ++ * assuming (a) no more than two urbs per frame on this endpoint, and also ++ * (b) only this endpoint's completions submit URBs. It seems some silicon ++ * corrupts things if you reuse completed descriptors very quickly... ++ */ ++static bool itd_complete(struct fotg210_hcd *fotg210, struct fotg210_itd *itd) ++{ ++ struct urb *urb = itd->urb; ++ struct usb_iso_packet_descriptor *desc; ++ u32 t; ++ unsigned uframe; ++ int urb_index = -1; ++ struct fotg210_iso_stream *stream = itd->stream; ++ struct usb_device *dev; ++ bool retval = false; ++ ++ /* for each uframe with a packet */ ++ for (uframe = 0; uframe < 8; uframe++) { ++ if (likely(itd->index[uframe] == -1)) ++ continue; ++ urb_index = itd->index[uframe]; ++ desc = &urb->iso_frame_desc[urb_index]; ++ ++ t = hc32_to_cpup(fotg210, &itd->hw_transaction[uframe]); ++ itd->hw_transaction[uframe] = 0; ++ ++ /* report transfer status */ ++ if (unlikely(t & ISO_ERRS)) { ++ urb->error_count++; ++ if (t & FOTG210_ISOC_BUF_ERR) ++ desc->status = usb_pipein(urb->pipe) ++ ? -ENOSR /* hc couldn't read */ ++ : -ECOMM; /* hc couldn't write */ ++ else if (t & FOTG210_ISOC_BABBLE) ++ desc->status = -EOVERFLOW; ++ else /* (t & FOTG210_ISOC_XACTERR) */ ++ desc->status = -EPROTO; ++ ++ /* HC need not update length with this error */ ++ if (!(t & FOTG210_ISOC_BABBLE)) { ++ desc->actual_length = FOTG210_ITD_LENGTH(t); ++ urb->actual_length += desc->actual_length; ++ } ++ } else if (likely((t & FOTG210_ISOC_ACTIVE) == 0)) { ++ desc->status = 0; ++ desc->actual_length = FOTG210_ITD_LENGTH(t); ++ urb->actual_length += desc->actual_length; ++ } else { ++ /* URB was too late */ ++ desc->status = -EXDEV; ++ } ++ } ++ ++ /* handle completion now? */ ++ if (likely((urb_index + 1) != urb->number_of_packets)) ++ goto done; ++ ++ /* ASSERT: it's really the last itd for this urb ++ * list_for_each_entry (itd, &stream->td_list, itd_list) ++ * BUG_ON (itd->urb == urb); ++ */ ++ ++ /* give urb back to the driver; completion often (re)submits */ ++ dev = urb->dev; ++ fotg210_urb_done(fotg210, urb, 0); ++ retval = true; ++ urb = NULL; ++ ++ --fotg210->isoc_count; ++ disable_periodic(fotg210); ++ ++ if (unlikely(list_is_singular(&stream->td_list))) { ++ fotg210_to_hcd(fotg210)->self.bandwidth_allocated ++ -= stream->bandwidth; ++ fotg210_dbg(fotg210, ++ "deschedule devp %s ep%d%s-iso\n", ++ dev->devpath, stream->bEndpointAddress & 0x0f, ++ (stream->bEndpointAddress & USB_DIR_IN) ? "in" : "out"); ++ } ++ ++done: ++ itd->urb = NULL; ++ ++ /* Add to the end of the free list for later reuse */ ++ list_move_tail(&itd->itd_list, &stream->free_list); ++ ++ /* Recycle the iTDs when the pipeline is empty (ep no longer in use) */ ++ if (list_empty(&stream->td_list)) { ++ list_splice_tail_init(&stream->free_list, ++ &fotg210->cached_itd_list); ++ start_free_itds(fotg210); ++ } ++ ++ return retval; ++} ++ ++static int itd_submit(struct fotg210_hcd *fotg210, struct urb *urb, ++ gfp_t mem_flags) ++{ ++ int status = -EINVAL; ++ unsigned long flags; ++ struct fotg210_iso_stream *stream; ++ ++ /* Get iso_stream head */ ++ stream = iso_stream_find(fotg210, urb); ++ if (unlikely(stream == NULL)) { ++ fotg210_dbg(fotg210, "can't get iso stream\n"); ++ return -ENOMEM; ++ } ++ if (unlikely(urb->interval != stream->interval && ++ fotg210_port_speed(fotg210, 0) == ++ USB_PORT_STAT_HIGH_SPEED)) { ++ fotg210_dbg(fotg210, "can't change iso interval %d --> %d\n", ++ stream->interval, urb->interval); ++ goto done; ++ } ++ ++#ifdef FOTG210_URB_TRACE ++ fotg210_dbg(fotg210, ++ "%s %s urb %p ep%d%s len %d, %d pkts %d uframes[%p]\n", ++ __func__, urb->dev->devpath, urb, ++ usb_pipeendpoint(urb->pipe), ++ usb_pipein(urb->pipe) ? "in" : "out", ++ urb->transfer_buffer_length, ++ urb->number_of_packets, urb->interval, ++ stream); ++#endif ++ ++ /* allocate ITDs w/o locking anything */ ++ status = itd_urb_transaction(stream, fotg210, urb, mem_flags); ++ if (unlikely(status < 0)) { ++ fotg210_dbg(fotg210, "can't init itds\n"); ++ goto done; ++ } ++ ++ /* schedule ... need to lock */ ++ spin_lock_irqsave(&fotg210->lock, flags); ++ if (unlikely(!HCD_HW_ACCESSIBLE(fotg210_to_hcd(fotg210)))) { ++ status = -ESHUTDOWN; ++ goto done_not_linked; ++ } ++ status = usb_hcd_link_urb_to_ep(fotg210_to_hcd(fotg210), urb); ++ if (unlikely(status)) ++ goto done_not_linked; ++ status = iso_stream_schedule(fotg210, urb, stream); ++ if (likely(status == 0)) ++ itd_link_urb(fotg210, urb, fotg210->periodic_size << 3, stream); ++ else ++ usb_hcd_unlink_urb_from_ep(fotg210_to_hcd(fotg210), urb); ++done_not_linked: ++ spin_unlock_irqrestore(&fotg210->lock, flags); ++done: ++ return status; ++} ++ ++static inline int scan_frame_queue(struct fotg210_hcd *fotg210, unsigned frame, ++ unsigned now_frame, bool live) ++{ ++ unsigned uf; ++ bool modified; ++ union fotg210_shadow q, *q_p; ++ __hc32 type, *hw_p; ++ ++ /* scan each element in frame's queue for completions */ ++ q_p = &fotg210->pshadow[frame]; ++ hw_p = &fotg210->periodic[frame]; ++ q.ptr = q_p->ptr; ++ type = Q_NEXT_TYPE(fotg210, *hw_p); ++ modified = false; ++ ++ while (q.ptr) { ++ switch (hc32_to_cpu(fotg210, type)) { ++ case Q_TYPE_ITD: ++ /* If this ITD is still active, leave it for ++ * later processing ... check the next entry. ++ * No need to check for activity unless the ++ * frame is current. ++ */ ++ if (frame == now_frame && live) { ++ rmb(); ++ for (uf = 0; uf < 8; uf++) { ++ if (q.itd->hw_transaction[uf] & ++ ITD_ACTIVE(fotg210)) ++ break; ++ } ++ if (uf < 8) { ++ q_p = &q.itd->itd_next; ++ hw_p = &q.itd->hw_next; ++ type = Q_NEXT_TYPE(fotg210, ++ q.itd->hw_next); ++ q = *q_p; ++ break; ++ } ++ } ++ ++ /* Take finished ITDs out of the schedule ++ * and process them: recycle, maybe report ++ * URB completion. HC won't cache the ++ * pointer for much longer, if at all. ++ */ ++ *q_p = q.itd->itd_next; ++ *hw_p = q.itd->hw_next; ++ type = Q_NEXT_TYPE(fotg210, q.itd->hw_next); ++ wmb(); ++ modified = itd_complete(fotg210, q.itd); ++ q = *q_p; ++ break; ++ default: ++ fotg210_dbg(fotg210, "corrupt type %d frame %d shadow %p\n", ++ type, frame, q.ptr); ++ fallthrough; ++ case Q_TYPE_QH: ++ case Q_TYPE_FSTN: ++ /* End of the iTDs and siTDs */ ++ q.ptr = NULL; ++ break; ++ } ++ ++ /* assume completion callbacks modify the queue */ ++ if (unlikely(modified && fotg210->isoc_count > 0)) ++ return -EINVAL; ++ } ++ return 0; ++} ++ ++static void scan_isoc(struct fotg210_hcd *fotg210) ++{ ++ unsigned uf, now_frame, frame, ret; ++ unsigned fmask = fotg210->periodic_size - 1; ++ bool live; ++ ++ /* ++ * When running, scan from last scan point up to "now" ++ * else clean up by scanning everything that's left. ++ * Touches as few pages as possible: cache-friendly. ++ */ ++ if (fotg210->rh_state >= FOTG210_RH_RUNNING) { ++ uf = fotg210_read_frame_index(fotg210); ++ now_frame = (uf >> 3) & fmask; ++ live = true; ++ } else { ++ now_frame = (fotg210->next_frame - 1) & fmask; ++ live = false; ++ } ++ fotg210->now_frame = now_frame; ++ ++ frame = fotg210->next_frame; ++ for (;;) { ++ ret = 1; ++ while (ret != 0) ++ ret = scan_frame_queue(fotg210, frame, ++ now_frame, live); ++ ++ /* Stop when we have reached the current frame */ ++ if (frame == now_frame) ++ break; ++ frame = (frame + 1) & fmask; ++ } ++ fotg210->next_frame = now_frame; ++} ++ ++/* Display / Set uframe_periodic_max ++ */ ++static ssize_t uframe_periodic_max_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct fotg210_hcd *fotg210; ++ int n; ++ ++ fotg210 = hcd_to_fotg210(bus_to_hcd(dev_get_drvdata(dev))); ++ n = scnprintf(buf, PAGE_SIZE, "%d\n", fotg210->uframe_periodic_max); ++ return n; ++} ++ ++ ++static ssize_t uframe_periodic_max_store(struct device *dev, ++ struct device_attribute *attr, const char *buf, size_t count) ++{ ++ struct fotg210_hcd *fotg210; ++ unsigned uframe_periodic_max; ++ unsigned frame, uframe; ++ unsigned short allocated_max; ++ unsigned long flags; ++ ssize_t ret; ++ ++ fotg210 = hcd_to_fotg210(bus_to_hcd(dev_get_drvdata(dev))); ++ if (kstrtouint(buf, 0, &uframe_periodic_max) < 0) ++ return -EINVAL; ++ ++ if (uframe_periodic_max < 100 || uframe_periodic_max >= 125) { ++ fotg210_info(fotg210, "rejecting invalid request for uframe_periodic_max=%u\n", ++ uframe_periodic_max); ++ return -EINVAL; ++ } ++ ++ ret = -EINVAL; ++ ++ /* ++ * lock, so that our checking does not race with possible periodic ++ * bandwidth allocation through submitting new urbs. ++ */ ++ spin_lock_irqsave(&fotg210->lock, flags); ++ ++ /* ++ * for request to decrease max periodic bandwidth, we have to check ++ * every microframe in the schedule to see whether the decrease is ++ * possible. ++ */ ++ if (uframe_periodic_max < fotg210->uframe_periodic_max) { ++ allocated_max = 0; ++ ++ for (frame = 0; frame < fotg210->periodic_size; ++frame) ++ for (uframe = 0; uframe < 7; ++uframe) ++ allocated_max = max(allocated_max, ++ periodic_usecs(fotg210, frame, ++ uframe)); ++ ++ if (allocated_max > uframe_periodic_max) { ++ fotg210_info(fotg210, ++ "cannot decrease uframe_periodic_max because periodic bandwidth is already allocated (%u > %u)\n", ++ allocated_max, uframe_periodic_max); ++ goto out_unlock; ++ } ++ } ++ ++ /* increasing is always ok */ ++ ++ fotg210_info(fotg210, ++ "setting max periodic bandwidth to %u%% (== %u usec/uframe)\n", ++ 100 * uframe_periodic_max/125, uframe_periodic_max); ++ ++ if (uframe_periodic_max != 100) ++ fotg210_warn(fotg210, "max periodic bandwidth set is non-standard\n"); ++ ++ fotg210->uframe_periodic_max = uframe_periodic_max; ++ ret = count; ++ ++out_unlock: ++ spin_unlock_irqrestore(&fotg210->lock, flags); ++ return ret; ++} ++ ++static DEVICE_ATTR_RW(uframe_periodic_max); ++ ++static inline int create_sysfs_files(struct fotg210_hcd *fotg210) ++{ ++ struct device *controller = fotg210_to_hcd(fotg210)->self.controller; ++ ++ return device_create_file(controller, &dev_attr_uframe_periodic_max); ++} ++ ++static inline void remove_sysfs_files(struct fotg210_hcd *fotg210) ++{ ++ struct device *controller = fotg210_to_hcd(fotg210)->self.controller; ++ ++ device_remove_file(controller, &dev_attr_uframe_periodic_max); ++} ++/* On some systems, leaving remote wakeup enabled prevents system shutdown. ++ * The firmware seems to think that powering off is a wakeup event! ++ * This routine turns off remote wakeup and everything else, on all ports. ++ */ ++static void fotg210_turn_off_all_ports(struct fotg210_hcd *fotg210) ++{ ++ u32 __iomem *status_reg = &fotg210->regs->port_status; ++ ++ fotg210_writel(fotg210, PORT_RWC_BITS, status_reg); ++} ++ ++/* Halt HC, turn off all ports, and let the BIOS use the companion controllers. ++ * Must be called with interrupts enabled and the lock not held. ++ */ ++static void fotg210_silence_controller(struct fotg210_hcd *fotg210) ++{ ++ fotg210_halt(fotg210); ++ ++ spin_lock_irq(&fotg210->lock); ++ fotg210->rh_state = FOTG210_RH_HALTED; ++ fotg210_turn_off_all_ports(fotg210); ++ spin_unlock_irq(&fotg210->lock); ++} ++ ++/* fotg210_shutdown kick in for silicon on any bus (not just pci, etc). ++ * This forcibly disables dma and IRQs, helping kexec and other cases ++ * where the next system software may expect clean state. ++ */ ++static void fotg210_shutdown(struct usb_hcd *hcd) ++{ ++ struct fotg210_hcd *fotg210 = hcd_to_fotg210(hcd); ++ ++ spin_lock_irq(&fotg210->lock); ++ fotg210->shutdown = true; ++ fotg210->rh_state = FOTG210_RH_STOPPING; ++ fotg210->enabled_hrtimer_events = 0; ++ spin_unlock_irq(&fotg210->lock); ++ ++ fotg210_silence_controller(fotg210); ++ ++ hrtimer_cancel(&fotg210->hrtimer); ++} ++ ++/* fotg210_work is called from some interrupts, timers, and so on. ++ * it calls driver completion functions, after dropping fotg210->lock. ++ */ ++static void fotg210_work(struct fotg210_hcd *fotg210) ++{ ++ /* another CPU may drop fotg210->lock during a schedule scan while ++ * it reports urb completions. this flag guards against bogus ++ * attempts at re-entrant schedule scanning. ++ */ ++ if (fotg210->scanning) { ++ fotg210->need_rescan = true; ++ return; ++ } ++ fotg210->scanning = true; ++ ++rescan: ++ fotg210->need_rescan = false; ++ if (fotg210->async_count) ++ scan_async(fotg210); ++ if (fotg210->intr_count > 0) ++ scan_intr(fotg210); ++ if (fotg210->isoc_count > 0) ++ scan_isoc(fotg210); ++ if (fotg210->need_rescan) ++ goto rescan; ++ fotg210->scanning = false; ++ ++ /* the IO watchdog guards against hardware or driver bugs that ++ * misplace IRQs, and should let us run completely without IRQs. ++ * such lossage has been observed on both VT6202 and VT8235. ++ */ ++ turn_on_io_watchdog(fotg210); ++} ++ ++/* Called when the fotg210_hcd module is removed. ++ */ ++static void fotg210_stop(struct usb_hcd *hcd) ++{ ++ struct fotg210_hcd *fotg210 = hcd_to_fotg210(hcd); ++ ++ fotg210_dbg(fotg210, "stop\n"); ++ ++ /* no more interrupts ... */ ++ ++ spin_lock_irq(&fotg210->lock); ++ fotg210->enabled_hrtimer_events = 0; ++ spin_unlock_irq(&fotg210->lock); ++ ++ fotg210_quiesce(fotg210); ++ fotg210_silence_controller(fotg210); ++ fotg210_reset(fotg210); ++ ++ hrtimer_cancel(&fotg210->hrtimer); ++ remove_sysfs_files(fotg210); ++ remove_debug_files(fotg210); ++ ++ /* root hub is shut down separately (first, when possible) */ ++ spin_lock_irq(&fotg210->lock); ++ end_free_itds(fotg210); ++ spin_unlock_irq(&fotg210->lock); ++ fotg210_mem_cleanup(fotg210); ++ ++#ifdef FOTG210_STATS ++ fotg210_dbg(fotg210, "irq normal %ld err %ld iaa %ld (lost %ld)\n", ++ fotg210->stats.normal, fotg210->stats.error, ++ fotg210->stats.iaa, fotg210->stats.lost_iaa); ++ fotg210_dbg(fotg210, "complete %ld unlink %ld\n", ++ fotg210->stats.complete, fotg210->stats.unlink); ++#endif ++ ++ dbg_status(fotg210, "fotg210_stop completed", ++ fotg210_readl(fotg210, &fotg210->regs->status)); ++} ++ ++/* one-time init, only for memory state */ ++static int hcd_fotg210_init(struct usb_hcd *hcd) ++{ ++ struct fotg210_hcd *fotg210 = hcd_to_fotg210(hcd); ++ u32 temp; ++ int retval; ++ u32 hcc_params; ++ struct fotg210_qh_hw *hw; ++ ++ spin_lock_init(&fotg210->lock); ++ ++ /* ++ * keep io watchdog by default, those good HCDs could turn off it later ++ */ ++ fotg210->need_io_watchdog = 1; ++ ++ hrtimer_init(&fotg210->hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS); ++ fotg210->hrtimer.function = fotg210_hrtimer_func; ++ fotg210->next_hrtimer_event = FOTG210_HRTIMER_NO_EVENT; ++ ++ hcc_params = fotg210_readl(fotg210, &fotg210->caps->hcc_params); ++ ++ /* ++ * by default set standard 80% (== 100 usec/uframe) max periodic ++ * bandwidth as required by USB 2.0 ++ */ ++ fotg210->uframe_periodic_max = 100; ++ ++ /* ++ * hw default: 1K periodic list heads, one per frame. ++ * periodic_size can shrink by USBCMD update if hcc_params allows. ++ */ ++ fotg210->periodic_size = DEFAULT_I_TDPS; ++ INIT_LIST_HEAD(&fotg210->intr_qh_list); ++ INIT_LIST_HEAD(&fotg210->cached_itd_list); ++ ++ if (HCC_PGM_FRAMELISTLEN(hcc_params)) { ++ /* periodic schedule size can be smaller than default */ ++ switch (FOTG210_TUNE_FLS) { ++ case 0: ++ fotg210->periodic_size = 1024; ++ break; ++ case 1: ++ fotg210->periodic_size = 512; ++ break; ++ case 2: ++ fotg210->periodic_size = 256; ++ break; ++ default: ++ BUG(); ++ } ++ } ++ retval = fotg210_mem_init(fotg210, GFP_KERNEL); ++ if (retval < 0) ++ return retval; ++ ++ /* controllers may cache some of the periodic schedule ... */ ++ fotg210->i_thresh = 2; ++ ++ /* ++ * dedicate a qh for the async ring head, since we couldn't unlink ++ * a 'real' qh without stopping the async schedule [4.8]. use it ++ * as the 'reclamation list head' too. ++ * its dummy is used in hw_alt_next of many tds, to prevent the qh ++ * from automatically advancing to the next td after short reads. ++ */ ++ fotg210->async->qh_next.qh = NULL; ++ hw = fotg210->async->hw; ++ hw->hw_next = QH_NEXT(fotg210, fotg210->async->qh_dma); ++ hw->hw_info1 = cpu_to_hc32(fotg210, QH_HEAD); ++ hw->hw_token = cpu_to_hc32(fotg210, QTD_STS_HALT); ++ hw->hw_qtd_next = FOTG210_LIST_END(fotg210); ++ fotg210->async->qh_state = QH_STATE_LINKED; ++ hw->hw_alt_next = QTD_NEXT(fotg210, fotg210->async->dummy->qtd_dma); ++ ++ /* clear interrupt enables, set irq latency */ ++ if (log2_irq_thresh < 0 || log2_irq_thresh > 6) ++ log2_irq_thresh = 0; ++ temp = 1 << (16 + log2_irq_thresh); ++ if (HCC_CANPARK(hcc_params)) { ++ /* HW default park == 3, on hardware that supports it (like ++ * NVidia and ALI silicon), maximizes throughput on the async ++ * schedule by avoiding QH fetches between transfers. ++ * ++ * With fast usb storage devices and NForce2, "park" seems to ++ * make problems: throughput reduction (!), data errors... ++ */ ++ if (park) { ++ park = min_t(unsigned, park, 3); ++ temp |= CMD_PARK; ++ temp |= park << 8; ++ } ++ fotg210_dbg(fotg210, "park %d\n", park); ++ } ++ if (HCC_PGM_FRAMELISTLEN(hcc_params)) { ++ /* periodic schedule size can be smaller than default */ ++ temp &= ~(3 << 2); ++ temp |= (FOTG210_TUNE_FLS << 2); ++ } ++ fotg210->command = temp; ++ ++ /* Accept arbitrarily long scatter-gather lists */ ++ if (!hcd->localmem_pool) ++ hcd->self.sg_tablesize = ~0; ++ return 0; ++} ++ ++/* start HC running; it's halted, hcd_fotg210_init() has been run (once) */ ++static int fotg210_run(struct usb_hcd *hcd) ++{ ++ struct fotg210_hcd *fotg210 = hcd_to_fotg210(hcd); ++ u32 temp; ++ ++ hcd->uses_new_polling = 1; ++ ++ /* EHCI spec section 4.1 */ ++ ++ fotg210_writel(fotg210, fotg210->periodic_dma, ++ &fotg210->regs->frame_list); ++ fotg210_writel(fotg210, (u32)fotg210->async->qh_dma, ++ &fotg210->regs->async_next); ++ ++ /* ++ * hcc_params controls whether fotg210->regs->segment must (!!!) ++ * be used; it constrains QH/ITD/SITD and QTD locations. ++ * dma_pool consistent memory always uses segment zero. ++ * streaming mappings for I/O buffers, like dma_map_single(), ++ * can return segments above 4GB, if the device allows. ++ * ++ * NOTE: the dma mask is visible through dev->dma_mask, so ++ * drivers can pass this info along ... like NETIF_F_HIGHDMA, ++ * Scsi_Host.highmem_io, and so forth. It's readonly to all ++ * host side drivers though. ++ */ ++ fotg210_readl(fotg210, &fotg210->caps->hcc_params); ++ ++ /* ++ * Philips, Intel, and maybe others need CMD_RUN before the ++ * root hub will detect new devices (why?); NEC doesn't ++ */ ++ fotg210->command &= ~(CMD_IAAD|CMD_PSE|CMD_ASE|CMD_RESET); ++ fotg210->command |= CMD_RUN; ++ fotg210_writel(fotg210, fotg210->command, &fotg210->regs->command); ++ dbg_cmd(fotg210, "init", fotg210->command); ++ ++ /* ++ * Start, enabling full USB 2.0 functionality ... usb 1.1 devices ++ * are explicitly handed to companion controller(s), so no TT is ++ * involved with the root hub. (Except where one is integrated, ++ * and there's no companion controller unless maybe for USB OTG.) ++ * ++ * Turning on the CF flag will transfer ownership of all ports ++ * from the companions to the EHCI controller. If any of the ++ * companions are in the middle of a port reset at the time, it ++ * could cause trouble. Write-locking ehci_cf_port_reset_rwsem ++ * guarantees that no resets are in progress. After we set CF, ++ * a short delay lets the hardware catch up; new resets shouldn't ++ * be started before the port switching actions could complete. ++ */ ++ down_write(&ehci_cf_port_reset_rwsem); ++ fotg210->rh_state = FOTG210_RH_RUNNING; ++ /* unblock posted writes */ ++ fotg210_readl(fotg210, &fotg210->regs->command); ++ usleep_range(5000, 10000); ++ up_write(&ehci_cf_port_reset_rwsem); ++ fotg210->last_periodic_enable = ktime_get_real(); ++ ++ temp = HC_VERSION(fotg210, ++ fotg210_readl(fotg210, &fotg210->caps->hc_capbase)); ++ fotg210_info(fotg210, ++ "USB %x.%x started, EHCI %x.%02x\n", ++ ((fotg210->sbrn & 0xf0) >> 4), (fotg210->sbrn & 0x0f), ++ temp >> 8, temp & 0xff); ++ ++ fotg210_writel(fotg210, INTR_MASK, ++ &fotg210->regs->intr_enable); /* Turn On Interrupts */ ++ ++ /* GRR this is run-once init(), being done every time the HC starts. ++ * So long as they're part of class devices, we can't do it init() ++ * since the class device isn't created that early. ++ */ ++ create_debug_files(fotg210); ++ create_sysfs_files(fotg210); ++ ++ return 0; ++} ++ ++static int fotg210_setup(struct usb_hcd *hcd) ++{ ++ struct fotg210_hcd *fotg210 = hcd_to_fotg210(hcd); ++ int retval; ++ ++ fotg210->regs = (void __iomem *)fotg210->caps + ++ HC_LENGTH(fotg210, ++ fotg210_readl(fotg210, &fotg210->caps->hc_capbase)); ++ dbg_hcs_params(fotg210, "reset"); ++ dbg_hcc_params(fotg210, "reset"); ++ ++ /* cache this readonly data; minimize chip reads */ ++ fotg210->hcs_params = fotg210_readl(fotg210, ++ &fotg210->caps->hcs_params); ++ ++ fotg210->sbrn = HCD_USB2; ++ ++ /* data structure init */ ++ retval = hcd_fotg210_init(hcd); ++ if (retval) ++ return retval; ++ ++ retval = fotg210_halt(fotg210); ++ if (retval) ++ return retval; ++ ++ fotg210_reset(fotg210); ++ ++ return 0; ++} ++ ++static irqreturn_t fotg210_irq(struct usb_hcd *hcd) ++{ ++ struct fotg210_hcd *fotg210 = hcd_to_fotg210(hcd); ++ u32 status, masked_status, pcd_status = 0, cmd; ++ int bh; ++ ++ spin_lock(&fotg210->lock); ++ ++ status = fotg210_readl(fotg210, &fotg210->regs->status); ++ ++ /* e.g. cardbus physical eject */ ++ if (status == ~(u32) 0) { ++ fotg210_dbg(fotg210, "device removed\n"); ++ goto dead; ++ } ++ ++ /* ++ * We don't use STS_FLR, but some controllers don't like it to ++ * remain on, so mask it out along with the other status bits. ++ */ ++ masked_status = status & (INTR_MASK | STS_FLR); ++ ++ /* Shared IRQ? */ ++ if (!masked_status || ++ unlikely(fotg210->rh_state == FOTG210_RH_HALTED)) { ++ spin_unlock(&fotg210->lock); ++ return IRQ_NONE; ++ } ++ ++ /* clear (just) interrupts */ ++ fotg210_writel(fotg210, masked_status, &fotg210->regs->status); ++ cmd = fotg210_readl(fotg210, &fotg210->regs->command); ++ bh = 0; ++ ++ /* unrequested/ignored: Frame List Rollover */ ++ dbg_status(fotg210, "irq", status); ++ ++ /* INT, ERR, and IAA interrupt rates can be throttled */ ++ ++ /* normal [4.15.1.2] or error [4.15.1.1] completion */ ++ if (likely((status & (STS_INT|STS_ERR)) != 0)) { ++ if (likely((status & STS_ERR) == 0)) ++ INCR(fotg210->stats.normal); ++ else ++ INCR(fotg210->stats.error); ++ bh = 1; ++ } ++ ++ /* complete the unlinking of some qh [4.15.2.3] */ ++ if (status & STS_IAA) { ++ ++ /* Turn off the IAA watchdog */ ++ fotg210->enabled_hrtimer_events &= ++ ~BIT(FOTG210_HRTIMER_IAA_WATCHDOG); ++ ++ /* ++ * Mild optimization: Allow another IAAD to reset the ++ * hrtimer, if one occurs before the next expiration. ++ * In theory we could always cancel the hrtimer, but ++ * tests show that about half the time it will be reset ++ * for some other event anyway. ++ */ ++ if (fotg210->next_hrtimer_event == FOTG210_HRTIMER_IAA_WATCHDOG) ++ ++fotg210->next_hrtimer_event; ++ ++ /* guard against (alleged) silicon errata */ ++ if (cmd & CMD_IAAD) ++ fotg210_dbg(fotg210, "IAA with IAAD still set?\n"); ++ if (fotg210->async_iaa) { ++ INCR(fotg210->stats.iaa); ++ end_unlink_async(fotg210); ++ } else ++ fotg210_dbg(fotg210, "IAA with nothing unlinked?\n"); ++ } ++ ++ /* remote wakeup [4.3.1] */ ++ if (status & STS_PCD) { ++ int pstatus; ++ u32 __iomem *status_reg = &fotg210->regs->port_status; ++ ++ /* kick root hub later */ ++ pcd_status = status; ++ ++ /* resume root hub? */ ++ if (fotg210->rh_state == FOTG210_RH_SUSPENDED) ++ usb_hcd_resume_root_hub(hcd); ++ ++ pstatus = fotg210_readl(fotg210, status_reg); ++ ++ if (test_bit(0, &fotg210->suspended_ports) && ++ ((pstatus & PORT_RESUME) || ++ !(pstatus & PORT_SUSPEND)) && ++ (pstatus & PORT_PE) && ++ fotg210->reset_done[0] == 0) { ++ ++ /* start 20 msec resume signaling from this port, ++ * and make hub_wq collect PORT_STAT_C_SUSPEND to ++ * stop that signaling. Use 5 ms extra for safety, ++ * like usb_port_resume() does. ++ */ ++ fotg210->reset_done[0] = jiffies + msecs_to_jiffies(25); ++ set_bit(0, &fotg210->resuming_ports); ++ fotg210_dbg(fotg210, "port 1 remote wakeup\n"); ++ mod_timer(&hcd->rh_timer, fotg210->reset_done[0]); ++ } ++ } ++ ++ /* PCI errors [4.15.2.4] */ ++ if (unlikely((status & STS_FATAL) != 0)) { ++ fotg210_err(fotg210, "fatal error\n"); ++ dbg_cmd(fotg210, "fatal", cmd); ++ dbg_status(fotg210, "fatal", status); ++dead: ++ usb_hc_died(hcd); ++ ++ /* Don't let the controller do anything more */ ++ fotg210->shutdown = true; ++ fotg210->rh_state = FOTG210_RH_STOPPING; ++ fotg210->command &= ~(CMD_RUN | CMD_ASE | CMD_PSE); ++ fotg210_writel(fotg210, fotg210->command, ++ &fotg210->regs->command); ++ fotg210_writel(fotg210, 0, &fotg210->regs->intr_enable); ++ fotg210_handle_controller_death(fotg210); ++ ++ /* Handle completions when the controller stops */ ++ bh = 0; ++ } ++ ++ if (bh) ++ fotg210_work(fotg210); ++ spin_unlock(&fotg210->lock); ++ if (pcd_status) ++ usb_hcd_poll_rh_status(hcd); ++ return IRQ_HANDLED; ++} ++ ++/* non-error returns are a promise to giveback() the urb later ++ * we drop ownership so next owner (or urb unlink) can get it ++ * ++ * urb + dev is in hcd.self.controller.urb_list ++ * we're queueing TDs onto software and hardware lists ++ * ++ * hcd-specific init for hcpriv hasn't been done yet ++ * ++ * NOTE: control, bulk, and interrupt share the same code to append TDs ++ * to a (possibly active) QH, and the same QH scanning code. ++ */ ++static int fotg210_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, ++ gfp_t mem_flags) ++{ ++ struct fotg210_hcd *fotg210 = hcd_to_fotg210(hcd); ++ struct list_head qtd_list; ++ ++ INIT_LIST_HEAD(&qtd_list); ++ ++ switch (usb_pipetype(urb->pipe)) { ++ case PIPE_CONTROL: ++ /* qh_completions() code doesn't handle all the fault cases ++ * in multi-TD control transfers. Even 1KB is rare anyway. ++ */ ++ if (urb->transfer_buffer_length > (16 * 1024)) ++ return -EMSGSIZE; ++ fallthrough; ++ /* case PIPE_BULK: */ ++ default: ++ if (!qh_urb_transaction(fotg210, urb, &qtd_list, mem_flags)) ++ return -ENOMEM; ++ return submit_async(fotg210, urb, &qtd_list, mem_flags); ++ ++ case PIPE_INTERRUPT: ++ if (!qh_urb_transaction(fotg210, urb, &qtd_list, mem_flags)) ++ return -ENOMEM; ++ return intr_submit(fotg210, urb, &qtd_list, mem_flags); ++ ++ case PIPE_ISOCHRONOUS: ++ return itd_submit(fotg210, urb, mem_flags); ++ } ++} ++ ++/* remove from hardware lists ++ * completions normally happen asynchronously ++ */ ++ ++static int fotg210_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) ++{ ++ struct fotg210_hcd *fotg210 = hcd_to_fotg210(hcd); ++ struct fotg210_qh *qh; ++ unsigned long flags; ++ int rc; ++ ++ spin_lock_irqsave(&fotg210->lock, flags); ++ rc = usb_hcd_check_unlink_urb(hcd, urb, status); ++ if (rc) ++ goto done; ++ ++ switch (usb_pipetype(urb->pipe)) { ++ /* case PIPE_CONTROL: */ ++ /* case PIPE_BULK:*/ ++ default: ++ qh = (struct fotg210_qh *) urb->hcpriv; ++ if (!qh) ++ break; ++ switch (qh->qh_state) { ++ case QH_STATE_LINKED: ++ case QH_STATE_COMPLETING: ++ start_unlink_async(fotg210, qh); ++ break; ++ case QH_STATE_UNLINK: ++ case QH_STATE_UNLINK_WAIT: ++ /* already started */ ++ break; ++ case QH_STATE_IDLE: ++ /* QH might be waiting for a Clear-TT-Buffer */ ++ qh_completions(fotg210, qh); ++ break; ++ } ++ break; ++ ++ case PIPE_INTERRUPT: ++ qh = (struct fotg210_qh *) urb->hcpriv; ++ if (!qh) ++ break; ++ switch (qh->qh_state) { ++ case QH_STATE_LINKED: ++ case QH_STATE_COMPLETING: ++ start_unlink_intr(fotg210, qh); ++ break; ++ case QH_STATE_IDLE: ++ qh_completions(fotg210, qh); ++ break; ++ default: ++ fotg210_dbg(fotg210, "bogus qh %p state %d\n", ++ qh, qh->qh_state); ++ goto done; ++ } ++ break; ++ ++ case PIPE_ISOCHRONOUS: ++ /* itd... */ ++ ++ /* wait till next completion, do it then. */ ++ /* completion irqs can wait up to 1024 msec, */ ++ break; ++ } ++done: ++ spin_unlock_irqrestore(&fotg210->lock, flags); ++ return rc; ++} ++ ++/* bulk qh holds the data toggle */ ++ ++static void fotg210_endpoint_disable(struct usb_hcd *hcd, ++ struct usb_host_endpoint *ep) ++{ ++ struct fotg210_hcd *fotg210 = hcd_to_fotg210(hcd); ++ unsigned long flags; ++ struct fotg210_qh *qh, *tmp; ++ ++ /* ASSERT: any requests/urbs are being unlinked */ ++ /* ASSERT: nobody can be submitting urbs for this any more */ ++ ++rescan: ++ spin_lock_irqsave(&fotg210->lock, flags); ++ qh = ep->hcpriv; ++ if (!qh) ++ goto done; ++ ++ /* endpoints can be iso streams. for now, we don't ++ * accelerate iso completions ... so spin a while. ++ */ ++ if (qh->hw == NULL) { ++ struct fotg210_iso_stream *stream = ep->hcpriv; ++ ++ if (!list_empty(&stream->td_list)) ++ goto idle_timeout; ++ ++ /* BUG_ON(!list_empty(&stream->free_list)); */ ++ kfree(stream); ++ goto done; ++ } ++ ++ if (fotg210->rh_state < FOTG210_RH_RUNNING) ++ qh->qh_state = QH_STATE_IDLE; ++ switch (qh->qh_state) { ++ case QH_STATE_LINKED: ++ case QH_STATE_COMPLETING: ++ for (tmp = fotg210->async->qh_next.qh; ++ tmp && tmp != qh; ++ tmp = tmp->qh_next.qh) ++ continue; ++ /* periodic qh self-unlinks on empty, and a COMPLETING qh ++ * may already be unlinked. ++ */ ++ if (tmp) ++ start_unlink_async(fotg210, qh); ++ fallthrough; ++ case QH_STATE_UNLINK: /* wait for hw to finish? */ ++ case QH_STATE_UNLINK_WAIT: ++idle_timeout: ++ spin_unlock_irqrestore(&fotg210->lock, flags); ++ schedule_timeout_uninterruptible(1); ++ goto rescan; ++ case QH_STATE_IDLE: /* fully unlinked */ ++ if (qh->clearing_tt) ++ goto idle_timeout; ++ if (list_empty(&qh->qtd_list)) { ++ qh_destroy(fotg210, qh); ++ break; ++ } ++ fallthrough; ++ default: ++ /* caller was supposed to have unlinked any requests; ++ * that's not our job. just leak this memory. ++ */ ++ fotg210_err(fotg210, "qh %p (#%02x) state %d%s\n", ++ qh, ep->desc.bEndpointAddress, qh->qh_state, ++ list_empty(&qh->qtd_list) ? "" : "(has tds)"); ++ break; ++ } ++done: ++ ep->hcpriv = NULL; ++ spin_unlock_irqrestore(&fotg210->lock, flags); ++} ++ ++static void fotg210_endpoint_reset(struct usb_hcd *hcd, ++ struct usb_host_endpoint *ep) ++{ ++ struct fotg210_hcd *fotg210 = hcd_to_fotg210(hcd); ++ struct fotg210_qh *qh; ++ int eptype = usb_endpoint_type(&ep->desc); ++ int epnum = usb_endpoint_num(&ep->desc); ++ int is_out = usb_endpoint_dir_out(&ep->desc); ++ unsigned long flags; ++ ++ if (eptype != USB_ENDPOINT_XFER_BULK && eptype != USB_ENDPOINT_XFER_INT) ++ return; ++ ++ spin_lock_irqsave(&fotg210->lock, flags); ++ qh = ep->hcpriv; ++ ++ /* For Bulk and Interrupt endpoints we maintain the toggle state ++ * in the hardware; the toggle bits in udev aren't used at all. ++ * When an endpoint is reset by usb_clear_halt() we must reset ++ * the toggle bit in the QH. ++ */ ++ if (qh) { ++ usb_settoggle(qh->dev, epnum, is_out, 0); ++ if (!list_empty(&qh->qtd_list)) { ++ WARN_ONCE(1, "clear_halt for a busy endpoint\n"); ++ } else if (qh->qh_state == QH_STATE_LINKED || ++ qh->qh_state == QH_STATE_COMPLETING) { ++ ++ /* The toggle value in the QH can't be updated ++ * while the QH is active. Unlink it now; ++ * re-linking will call qh_refresh(). ++ */ ++ if (eptype == USB_ENDPOINT_XFER_BULK) ++ start_unlink_async(fotg210, qh); ++ else ++ start_unlink_intr(fotg210, qh); ++ } ++ } ++ spin_unlock_irqrestore(&fotg210->lock, flags); ++} ++ ++static int fotg210_get_frame(struct usb_hcd *hcd) ++{ ++ struct fotg210_hcd *fotg210 = hcd_to_fotg210(hcd); ++ ++ return (fotg210_read_frame_index(fotg210) >> 3) % ++ fotg210->periodic_size; ++} ++ ++/* The EHCI in ChipIdea HDRC cannot be a separate module or device, ++ * because its registers (and irq) are shared between host/gadget/otg ++ * functions and in order to facilitate role switching we cannot ++ * give the fotg210 driver exclusive access to those. ++ */ ++MODULE_DESCRIPTION(DRIVER_DESC); ++MODULE_AUTHOR(DRIVER_AUTHOR); ++MODULE_LICENSE("GPL"); ++ ++static const struct hc_driver fotg210_fotg210_hc_driver = { ++ .description = hcd_name, ++ .product_desc = "Faraday USB2.0 Host Controller", ++ .hcd_priv_size = sizeof(struct fotg210_hcd), ++ ++ /* ++ * generic hardware linkage ++ */ ++ .irq = fotg210_irq, ++ .flags = HCD_MEMORY | HCD_DMA | HCD_USB2, ++ ++ /* ++ * basic lifecycle operations ++ */ ++ .reset = hcd_fotg210_init, ++ .start = fotg210_run, ++ .stop = fotg210_stop, ++ .shutdown = fotg210_shutdown, ++ ++ /* ++ * managing i/o requests and associated device resources ++ */ ++ .urb_enqueue = fotg210_urb_enqueue, ++ .urb_dequeue = fotg210_urb_dequeue, ++ .endpoint_disable = fotg210_endpoint_disable, ++ .endpoint_reset = fotg210_endpoint_reset, ++ ++ /* ++ * scheduling support ++ */ ++ .get_frame_number = fotg210_get_frame, ++ ++ /* ++ * root hub support ++ */ ++ .hub_status_data = fotg210_hub_status_data, ++ .hub_control = fotg210_hub_control, ++ .bus_suspend = fotg210_bus_suspend, ++ .bus_resume = fotg210_bus_resume, ++ ++ .relinquish_port = fotg210_relinquish_port, ++ .port_handed_over = fotg210_port_handed_over, ++ ++ .clear_tt_buffer_complete = fotg210_clear_tt_buffer_complete, ++}; ++ ++static void fotg210_init(struct fotg210_hcd *fotg210) ++{ ++ u32 value; ++ ++ iowrite32(GMIR_MDEV_INT | GMIR_MOTG_INT | GMIR_INT_POLARITY, ++ &fotg210->regs->gmir); ++ ++ value = ioread32(&fotg210->regs->otgcsr); ++ value &= ~OTGCSR_A_BUS_DROP; ++ value |= OTGCSR_A_BUS_REQ; ++ iowrite32(value, &fotg210->regs->otgcsr); ++} ++ ++/* ++ * fotg210_hcd_probe - initialize faraday FOTG210 HCDs ++ * ++ * Allocates basic resources for this USB host controller, and ++ * then invokes the start() method for the HCD associated with it ++ * through the hotplug entry's driver_data. ++ */ ++static int fotg210_hcd_probe(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ struct usb_hcd *hcd; ++ struct resource *res; ++ int irq; ++ int retval; ++ struct fotg210_hcd *fotg210; ++ ++ if (usb_disabled()) ++ return -ENODEV; ++ ++ pdev->dev.power.power_state = PMSG_ON; ++ ++ irq = platform_get_irq(pdev, 0); ++ if (irq < 0) ++ return irq; ++ ++ hcd = usb_create_hcd(&fotg210_fotg210_hc_driver, dev, ++ dev_name(dev)); ++ if (!hcd) { ++ dev_err(dev, "failed to create hcd\n"); ++ retval = -ENOMEM; ++ goto fail_create_hcd; ++ } ++ ++ hcd->has_tt = 1; ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ hcd->regs = devm_ioremap_resource(&pdev->dev, res); ++ if (IS_ERR(hcd->regs)) { ++ retval = PTR_ERR(hcd->regs); ++ goto failed_put_hcd; ++ } ++ ++ hcd->rsrc_start = res->start; ++ hcd->rsrc_len = resource_size(res); ++ ++ fotg210 = hcd_to_fotg210(hcd); ++ ++ fotg210->caps = hcd->regs; ++ ++ /* It's OK not to supply this clock */ ++ fotg210->pclk = clk_get(dev, "PCLK"); ++ if (!IS_ERR(fotg210->pclk)) { ++ retval = clk_prepare_enable(fotg210->pclk); ++ if (retval) { ++ dev_err(dev, "failed to enable PCLK\n"); ++ goto failed_put_hcd; ++ } ++ } else if (PTR_ERR(fotg210->pclk) == -EPROBE_DEFER) { ++ /* ++ * Percolate deferrals, for anything else, ++ * just live without the clocking. ++ */ ++ retval = PTR_ERR(fotg210->pclk); ++ goto failed_dis_clk; ++ } ++ ++ retval = fotg210_setup(hcd); ++ if (retval) ++ goto failed_dis_clk; ++ ++ fotg210_init(fotg210); ++ ++ retval = usb_add_hcd(hcd, irq, IRQF_SHARED); ++ if (retval) { ++ dev_err(dev, "failed to add hcd with err %d\n", retval); ++ goto failed_dis_clk; ++ } ++ device_wakeup_enable(hcd->self.controller); ++ platform_set_drvdata(pdev, hcd); ++ ++ return retval; ++ ++failed_dis_clk: ++ if (!IS_ERR(fotg210->pclk)) { ++ clk_disable_unprepare(fotg210->pclk); ++ clk_put(fotg210->pclk); ++ } ++failed_put_hcd: ++ usb_put_hcd(hcd); ++fail_create_hcd: ++ dev_err(dev, "init %s fail, %d\n", dev_name(dev), retval); ++ return retval; ++} ++ ++/* ++ * fotg210_hcd_remove - shutdown processing for EHCI HCDs ++ * @dev: USB Host Controller being removed ++ * ++ */ ++static int fotg210_hcd_remove(struct platform_device *pdev) ++{ ++ struct usb_hcd *hcd = platform_get_drvdata(pdev); ++ struct fotg210_hcd *fotg210 = hcd_to_fotg210(hcd); ++ ++ if (!IS_ERR(fotg210->pclk)) { ++ clk_disable_unprepare(fotg210->pclk); ++ clk_put(fotg210->pclk); ++ } ++ ++ usb_remove_hcd(hcd); ++ usb_put_hcd(hcd); ++ ++ return 0; ++} ++ ++#ifdef CONFIG_OF ++static const struct of_device_id fotg210_of_match[] = { ++ { .compatible = "faraday,fotg210" }, ++ {}, ++}; ++MODULE_DEVICE_TABLE(of, fotg210_of_match); ++#endif ++ ++static struct platform_driver fotg210_hcd_driver = { ++ .driver = { ++ .name = "fotg210-hcd", ++ .of_match_table = of_match_ptr(fotg210_of_match), ++ }, ++ .probe = fotg210_hcd_probe, ++ .remove = fotg210_hcd_remove, ++}; ++ ++static int __init fotg210_hcd_init(void) ++{ ++ int retval = 0; ++ ++ if (usb_disabled()) ++ return -ENODEV; ++ ++ set_bit(USB_EHCI_LOADED, &usb_hcds_loaded); ++ if (test_bit(USB_UHCI_LOADED, &usb_hcds_loaded) || ++ test_bit(USB_OHCI_LOADED, &usb_hcds_loaded)) ++ pr_warn("Warning! fotg210_hcd should always be loaded before uhci_hcd and ohci_hcd, not after\n"); ++ ++ pr_debug("%s: block sizes: qh %zd qtd %zd itd %zd\n", ++ hcd_name, sizeof(struct fotg210_qh), ++ sizeof(struct fotg210_qtd), ++ sizeof(struct fotg210_itd)); ++ ++ fotg210_debug_root = debugfs_create_dir("fotg210", usb_debug_root); ++ ++ retval = platform_driver_register(&fotg210_hcd_driver); ++ if (retval < 0) ++ goto clean; ++ return retval; ++ ++clean: ++ debugfs_remove(fotg210_debug_root); ++ fotg210_debug_root = NULL; ++ ++ clear_bit(USB_EHCI_LOADED, &usb_hcds_loaded); ++ return retval; ++} ++module_init(fotg210_hcd_init); ++ ++static void __exit fotg210_hcd_cleanup(void) ++{ ++ platform_driver_unregister(&fotg210_hcd_driver); ++ debugfs_remove(fotg210_debug_root); ++ clear_bit(USB_EHCI_LOADED, &usb_hcds_loaded); ++} ++module_exit(fotg210_hcd_cleanup); +--- a/drivers/usb/gadget/udc/fotg210-udc.c ++++ /dev/null +@@ -1,1239 +0,0 @@ +-// SPDX-License-Identifier: GPL-2.0 +-/* +- * FOTG210 UDC Driver supports Bulk transfer so far +- * +- * Copyright (C) 2013 Faraday Technology Corporation +- * +- * Author : Yuan-Hsin Chen +- */ +- +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +- +-#include "fotg210.h" +- +-#define DRIVER_DESC "FOTG210 USB Device Controller Driver" +-#define DRIVER_VERSION "30-April-2013" +- +-static const char udc_name[] = "fotg210_udc"; +-static const char * const fotg210_ep_name[] = { +- "ep0", "ep1", "ep2", "ep3", "ep4"}; +- +-static void fotg210_disable_fifo_int(struct fotg210_ep *ep) +-{ +- u32 value = ioread32(ep->fotg210->reg + FOTG210_DMISGR1); +- +- if (ep->dir_in) +- value |= DMISGR1_MF_IN_INT(ep->epnum - 1); +- else +- value |= DMISGR1_MF_OUTSPK_INT(ep->epnum - 1); +- iowrite32(value, ep->fotg210->reg + FOTG210_DMISGR1); +-} +- +-static void fotg210_enable_fifo_int(struct fotg210_ep *ep) +-{ +- u32 value = ioread32(ep->fotg210->reg + FOTG210_DMISGR1); +- +- if (ep->dir_in) +- value &= ~DMISGR1_MF_IN_INT(ep->epnum - 1); +- else +- value &= ~DMISGR1_MF_OUTSPK_INT(ep->epnum - 1); +- iowrite32(value, ep->fotg210->reg + FOTG210_DMISGR1); +-} +- +-static void fotg210_set_cxdone(struct fotg210_udc *fotg210) +-{ +- u32 value = ioread32(fotg210->reg + FOTG210_DCFESR); +- +- value |= DCFESR_CX_DONE; +- iowrite32(value, fotg210->reg + FOTG210_DCFESR); +-} +- +-static void fotg210_done(struct fotg210_ep *ep, struct fotg210_request *req, +- int status) +-{ +- list_del_init(&req->queue); +- +- /* don't modify queue heads during completion callback */ +- if (ep->fotg210->gadget.speed == USB_SPEED_UNKNOWN) +- req->req.status = -ESHUTDOWN; +- else +- req->req.status = status; +- +- spin_unlock(&ep->fotg210->lock); +- usb_gadget_giveback_request(&ep->ep, &req->req); +- spin_lock(&ep->fotg210->lock); +- +- if (ep->epnum) { +- if (list_empty(&ep->queue)) +- fotg210_disable_fifo_int(ep); +- } else { +- fotg210_set_cxdone(ep->fotg210); +- } +-} +- +-static void fotg210_fifo_ep_mapping(struct fotg210_ep *ep, u32 epnum, +- u32 dir_in) +-{ +- struct fotg210_udc *fotg210 = ep->fotg210; +- u32 val; +- +- /* Driver should map an ep to a fifo and then map the fifo +- * to the ep. What a brain-damaged design! +- */ +- +- /* map a fifo to an ep */ +- val = ioread32(fotg210->reg + FOTG210_EPMAP); +- val &= ~EPMAP_FIFONOMSK(epnum, dir_in); +- val |= EPMAP_FIFONO(epnum, dir_in); +- iowrite32(val, fotg210->reg + FOTG210_EPMAP); +- +- /* map the ep to the fifo */ +- val = ioread32(fotg210->reg + FOTG210_FIFOMAP); +- val &= ~FIFOMAP_EPNOMSK(epnum); +- val |= FIFOMAP_EPNO(epnum); +- iowrite32(val, fotg210->reg + FOTG210_FIFOMAP); +- +- /* enable fifo */ +- val = ioread32(fotg210->reg + FOTG210_FIFOCF); +- val |= FIFOCF_FIFO_EN(epnum - 1); +- iowrite32(val, fotg210->reg + FOTG210_FIFOCF); +-} +- +-static void fotg210_set_fifo_dir(struct fotg210_ep *ep, u32 epnum, u32 dir_in) +-{ +- struct fotg210_udc *fotg210 = ep->fotg210; +- u32 val; +- +- val = ioread32(fotg210->reg + FOTG210_FIFOMAP); +- val |= (dir_in ? FIFOMAP_DIRIN(epnum - 1) : FIFOMAP_DIROUT(epnum - 1)); +- iowrite32(val, fotg210->reg + FOTG210_FIFOMAP); +-} +- +-static void fotg210_set_tfrtype(struct fotg210_ep *ep, u32 epnum, u32 type) +-{ +- struct fotg210_udc *fotg210 = ep->fotg210; +- u32 val; +- +- val = ioread32(fotg210->reg + FOTG210_FIFOCF); +- val |= FIFOCF_TYPE(type, epnum - 1); +- iowrite32(val, fotg210->reg + FOTG210_FIFOCF); +-} +- +-static void fotg210_set_mps(struct fotg210_ep *ep, u32 epnum, u32 mps, +- u32 dir_in) +-{ +- struct fotg210_udc *fotg210 = ep->fotg210; +- u32 val; +- u32 offset = dir_in ? FOTG210_INEPMPSR(epnum) : +- FOTG210_OUTEPMPSR(epnum); +- +- val = ioread32(fotg210->reg + offset); +- val |= INOUTEPMPSR_MPS(mps); +- iowrite32(val, fotg210->reg + offset); +-} +- +-static int fotg210_config_ep(struct fotg210_ep *ep, +- const struct usb_endpoint_descriptor *desc) +-{ +- struct fotg210_udc *fotg210 = ep->fotg210; +- +- fotg210_set_fifo_dir(ep, ep->epnum, ep->dir_in); +- fotg210_set_tfrtype(ep, ep->epnum, ep->type); +- fotg210_set_mps(ep, ep->epnum, ep->ep.maxpacket, ep->dir_in); +- fotg210_fifo_ep_mapping(ep, ep->epnum, ep->dir_in); +- +- fotg210->ep[ep->epnum] = ep; +- +- return 0; +-} +- +-static int fotg210_ep_enable(struct usb_ep *_ep, +- const struct usb_endpoint_descriptor *desc) +-{ +- struct fotg210_ep *ep; +- +- ep = container_of(_ep, struct fotg210_ep, ep); +- +- ep->desc = desc; +- ep->epnum = usb_endpoint_num(desc); +- ep->type = usb_endpoint_type(desc); +- ep->dir_in = usb_endpoint_dir_in(desc); +- ep->ep.maxpacket = usb_endpoint_maxp(desc); +- +- return fotg210_config_ep(ep, desc); +-} +- +-static void fotg210_reset_tseq(struct fotg210_udc *fotg210, u8 epnum) +-{ +- struct fotg210_ep *ep = fotg210->ep[epnum]; +- u32 value; +- void __iomem *reg; +- +- reg = (ep->dir_in) ? +- fotg210->reg + FOTG210_INEPMPSR(epnum) : +- fotg210->reg + FOTG210_OUTEPMPSR(epnum); +- +- /* Note: Driver needs to set and clear INOUTEPMPSR_RESET_TSEQ +- * bit. Controller wouldn't clear this bit. WTF!!! +- */ +- +- value = ioread32(reg); +- value |= INOUTEPMPSR_RESET_TSEQ; +- iowrite32(value, reg); +- +- value = ioread32(reg); +- value &= ~INOUTEPMPSR_RESET_TSEQ; +- iowrite32(value, reg); +-} +- +-static int fotg210_ep_release(struct fotg210_ep *ep) +-{ +- if (!ep->epnum) +- return 0; +- ep->epnum = 0; +- ep->stall = 0; +- ep->wedged = 0; +- +- fotg210_reset_tseq(ep->fotg210, ep->epnum); +- +- return 0; +-} +- +-static int fotg210_ep_disable(struct usb_ep *_ep) +-{ +- struct fotg210_ep *ep; +- struct fotg210_request *req; +- unsigned long flags; +- +- BUG_ON(!_ep); +- +- ep = container_of(_ep, struct fotg210_ep, ep); +- +- while (!list_empty(&ep->queue)) { +- req = list_entry(ep->queue.next, +- struct fotg210_request, queue); +- spin_lock_irqsave(&ep->fotg210->lock, flags); +- fotg210_done(ep, req, -ECONNRESET); +- spin_unlock_irqrestore(&ep->fotg210->lock, flags); +- } +- +- return fotg210_ep_release(ep); +-} +- +-static struct usb_request *fotg210_ep_alloc_request(struct usb_ep *_ep, +- gfp_t gfp_flags) +-{ +- struct fotg210_request *req; +- +- req = kzalloc(sizeof(struct fotg210_request), gfp_flags); +- if (!req) +- return NULL; +- +- INIT_LIST_HEAD(&req->queue); +- +- return &req->req; +-} +- +-static void fotg210_ep_free_request(struct usb_ep *_ep, +- struct usb_request *_req) +-{ +- struct fotg210_request *req; +- +- req = container_of(_req, struct fotg210_request, req); +- kfree(req); +-} +- +-static void fotg210_enable_dma(struct fotg210_ep *ep, +- dma_addr_t d, u32 len) +-{ +- u32 value; +- struct fotg210_udc *fotg210 = ep->fotg210; +- +- /* set transfer length and direction */ +- value = ioread32(fotg210->reg + FOTG210_DMACPSR1); +- value &= ~(DMACPSR1_DMA_LEN(0xFFFF) | DMACPSR1_DMA_TYPE(1)); +- value |= DMACPSR1_DMA_LEN(len) | DMACPSR1_DMA_TYPE(ep->dir_in); +- iowrite32(value, fotg210->reg + FOTG210_DMACPSR1); +- +- /* set device DMA target FIFO number */ +- value = ioread32(fotg210->reg + FOTG210_DMATFNR); +- if (ep->epnum) +- value |= DMATFNR_ACC_FN(ep->epnum - 1); +- else +- value |= DMATFNR_ACC_CXF; +- iowrite32(value, fotg210->reg + FOTG210_DMATFNR); +- +- /* set DMA memory address */ +- iowrite32(d, fotg210->reg + FOTG210_DMACPSR2); +- +- /* enable MDMA_EROR and MDMA_CMPLT interrupt */ +- value = ioread32(fotg210->reg + FOTG210_DMISGR2); +- value &= ~(DMISGR2_MDMA_CMPLT | DMISGR2_MDMA_ERROR); +- iowrite32(value, fotg210->reg + FOTG210_DMISGR2); +- +- /* start DMA */ +- value = ioread32(fotg210->reg + FOTG210_DMACPSR1); +- value |= DMACPSR1_DMA_START; +- iowrite32(value, fotg210->reg + FOTG210_DMACPSR1); +-} +- +-static void fotg210_disable_dma(struct fotg210_ep *ep) +-{ +- iowrite32(DMATFNR_DISDMA, ep->fotg210->reg + FOTG210_DMATFNR); +-} +- +-static void fotg210_wait_dma_done(struct fotg210_ep *ep) +-{ +- u32 value; +- +- do { +- value = ioread32(ep->fotg210->reg + FOTG210_DISGR2); +- if ((value & DISGR2_USBRST_INT) || +- (value & DISGR2_DMA_ERROR)) +- goto dma_reset; +- } while (!(value & DISGR2_DMA_CMPLT)); +- +- value &= ~DISGR2_DMA_CMPLT; +- iowrite32(value, ep->fotg210->reg + FOTG210_DISGR2); +- return; +- +-dma_reset: +- value = ioread32(ep->fotg210->reg + FOTG210_DMACPSR1); +- value |= DMACPSR1_DMA_ABORT; +- iowrite32(value, ep->fotg210->reg + FOTG210_DMACPSR1); +- +- /* reset fifo */ +- if (ep->epnum) { +- value = ioread32(ep->fotg210->reg + +- FOTG210_FIBCR(ep->epnum - 1)); +- value |= FIBCR_FFRST; +- iowrite32(value, ep->fotg210->reg + +- FOTG210_FIBCR(ep->epnum - 1)); +- } else { +- value = ioread32(ep->fotg210->reg + FOTG210_DCFESR); +- value |= DCFESR_CX_CLR; +- iowrite32(value, ep->fotg210->reg + FOTG210_DCFESR); +- } +-} +- +-static void fotg210_start_dma(struct fotg210_ep *ep, +- struct fotg210_request *req) +-{ +- struct device *dev = &ep->fotg210->gadget.dev; +- dma_addr_t d; +- u8 *buffer; +- u32 length; +- +- if (ep->epnum) { +- if (ep->dir_in) { +- buffer = req->req.buf; +- length = req->req.length; +- } else { +- buffer = req->req.buf + req->req.actual; +- length = ioread32(ep->fotg210->reg + +- FOTG210_FIBCR(ep->epnum - 1)) & FIBCR_BCFX; +- if (length > req->req.length - req->req.actual) +- length = req->req.length - req->req.actual; +- } +- } else { +- buffer = req->req.buf + req->req.actual; +- if (req->req.length - req->req.actual > ep->ep.maxpacket) +- length = ep->ep.maxpacket; +- else +- length = req->req.length - req->req.actual; +- } +- +- d = dma_map_single(dev, buffer, length, +- ep->dir_in ? DMA_TO_DEVICE : DMA_FROM_DEVICE); +- +- if (dma_mapping_error(dev, d)) { +- pr_err("dma_mapping_error\n"); +- return; +- } +- +- fotg210_enable_dma(ep, d, length); +- +- /* check if dma is done */ +- fotg210_wait_dma_done(ep); +- +- fotg210_disable_dma(ep); +- +- /* update actual transfer length */ +- req->req.actual += length; +- +- dma_unmap_single(dev, d, length, DMA_TO_DEVICE); +-} +- +-static void fotg210_ep0_queue(struct fotg210_ep *ep, +- struct fotg210_request *req) +-{ +- if (!req->req.length) { +- fotg210_done(ep, req, 0); +- return; +- } +- if (ep->dir_in) { /* if IN */ +- fotg210_start_dma(ep, req); +- if (req->req.length == req->req.actual) +- fotg210_done(ep, req, 0); +- } else { /* OUT */ +- u32 value = ioread32(ep->fotg210->reg + FOTG210_DMISGR0); +- +- value &= ~DMISGR0_MCX_OUT_INT; +- iowrite32(value, ep->fotg210->reg + FOTG210_DMISGR0); +- } +-} +- +-static int fotg210_ep_queue(struct usb_ep *_ep, struct usb_request *_req, +- gfp_t gfp_flags) +-{ +- struct fotg210_ep *ep; +- struct fotg210_request *req; +- unsigned long flags; +- int request = 0; +- +- ep = container_of(_ep, struct fotg210_ep, ep); +- req = container_of(_req, struct fotg210_request, req); +- +- if (ep->fotg210->gadget.speed == USB_SPEED_UNKNOWN) +- return -ESHUTDOWN; +- +- spin_lock_irqsave(&ep->fotg210->lock, flags); +- +- if (list_empty(&ep->queue)) +- request = 1; +- +- list_add_tail(&req->queue, &ep->queue); +- +- req->req.actual = 0; +- req->req.status = -EINPROGRESS; +- +- if (!ep->epnum) /* ep0 */ +- fotg210_ep0_queue(ep, req); +- else if (request && !ep->stall) +- fotg210_enable_fifo_int(ep); +- +- spin_unlock_irqrestore(&ep->fotg210->lock, flags); +- +- return 0; +-} +- +-static int fotg210_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req) +-{ +- struct fotg210_ep *ep; +- struct fotg210_request *req; +- unsigned long flags; +- +- ep = container_of(_ep, struct fotg210_ep, ep); +- req = container_of(_req, struct fotg210_request, req); +- +- spin_lock_irqsave(&ep->fotg210->lock, flags); +- if (!list_empty(&ep->queue)) +- fotg210_done(ep, req, -ECONNRESET); +- spin_unlock_irqrestore(&ep->fotg210->lock, flags); +- +- return 0; +-} +- +-static void fotg210_set_epnstall(struct fotg210_ep *ep) +-{ +- struct fotg210_udc *fotg210 = ep->fotg210; +- u32 value; +- void __iomem *reg; +- +- /* check if IN FIFO is empty before stall */ +- if (ep->dir_in) { +- do { +- value = ioread32(fotg210->reg + FOTG210_DCFESR); +- } while (!(value & DCFESR_FIFO_EMPTY(ep->epnum - 1))); +- } +- +- reg = (ep->dir_in) ? +- fotg210->reg + FOTG210_INEPMPSR(ep->epnum) : +- fotg210->reg + FOTG210_OUTEPMPSR(ep->epnum); +- value = ioread32(reg); +- value |= INOUTEPMPSR_STL_EP; +- iowrite32(value, reg); +-} +- +-static void fotg210_clear_epnstall(struct fotg210_ep *ep) +-{ +- struct fotg210_udc *fotg210 = ep->fotg210; +- u32 value; +- void __iomem *reg; +- +- reg = (ep->dir_in) ? +- fotg210->reg + FOTG210_INEPMPSR(ep->epnum) : +- fotg210->reg + FOTG210_OUTEPMPSR(ep->epnum); +- value = ioread32(reg); +- value &= ~INOUTEPMPSR_STL_EP; +- iowrite32(value, reg); +-} +- +-static int fotg210_set_halt_and_wedge(struct usb_ep *_ep, int value, int wedge) +-{ +- struct fotg210_ep *ep; +- struct fotg210_udc *fotg210; +- unsigned long flags; +- +- ep = container_of(_ep, struct fotg210_ep, ep); +- +- fotg210 = ep->fotg210; +- +- spin_lock_irqsave(&ep->fotg210->lock, flags); +- +- if (value) { +- fotg210_set_epnstall(ep); +- ep->stall = 1; +- if (wedge) +- ep->wedged = 1; +- } else { +- fotg210_reset_tseq(fotg210, ep->epnum); +- fotg210_clear_epnstall(ep); +- ep->stall = 0; +- ep->wedged = 0; +- if (!list_empty(&ep->queue)) +- fotg210_enable_fifo_int(ep); +- } +- +- spin_unlock_irqrestore(&ep->fotg210->lock, flags); +- return 0; +-} +- +-static int fotg210_ep_set_halt(struct usb_ep *_ep, int value) +-{ +- return fotg210_set_halt_and_wedge(_ep, value, 0); +-} +- +-static int fotg210_ep_set_wedge(struct usb_ep *_ep) +-{ +- return fotg210_set_halt_and_wedge(_ep, 1, 1); +-} +- +-static void fotg210_ep_fifo_flush(struct usb_ep *_ep) +-{ +-} +- +-static const struct usb_ep_ops fotg210_ep_ops = { +- .enable = fotg210_ep_enable, +- .disable = fotg210_ep_disable, +- +- .alloc_request = fotg210_ep_alloc_request, +- .free_request = fotg210_ep_free_request, +- +- .queue = fotg210_ep_queue, +- .dequeue = fotg210_ep_dequeue, +- +- .set_halt = fotg210_ep_set_halt, +- .fifo_flush = fotg210_ep_fifo_flush, +- .set_wedge = fotg210_ep_set_wedge, +-}; +- +-static void fotg210_clear_tx0byte(struct fotg210_udc *fotg210) +-{ +- u32 value = ioread32(fotg210->reg + FOTG210_TX0BYTE); +- +- value &= ~(TX0BYTE_EP1 | TX0BYTE_EP2 | TX0BYTE_EP3 +- | TX0BYTE_EP4); +- iowrite32(value, fotg210->reg + FOTG210_TX0BYTE); +-} +- +-static void fotg210_clear_rx0byte(struct fotg210_udc *fotg210) +-{ +- u32 value = ioread32(fotg210->reg + FOTG210_RX0BYTE); +- +- value &= ~(RX0BYTE_EP1 | RX0BYTE_EP2 | RX0BYTE_EP3 +- | RX0BYTE_EP4); +- iowrite32(value, fotg210->reg + FOTG210_RX0BYTE); +-} +- +-/* read 8-byte setup packet only */ +-static void fotg210_rdsetupp(struct fotg210_udc *fotg210, +- u8 *buffer) +-{ +- int i = 0; +- u8 *tmp = buffer; +- u32 data; +- u32 length = 8; +- +- iowrite32(DMATFNR_ACC_CXF, fotg210->reg + FOTG210_DMATFNR); +- +- for (i = (length >> 2); i > 0; i--) { +- data = ioread32(fotg210->reg + FOTG210_CXPORT); +- *tmp = data & 0xFF; +- *(tmp + 1) = (data >> 8) & 0xFF; +- *(tmp + 2) = (data >> 16) & 0xFF; +- *(tmp + 3) = (data >> 24) & 0xFF; +- tmp = tmp + 4; +- } +- +- switch (length % 4) { +- case 1: +- data = ioread32(fotg210->reg + FOTG210_CXPORT); +- *tmp = data & 0xFF; +- break; +- case 2: +- data = ioread32(fotg210->reg + FOTG210_CXPORT); +- *tmp = data & 0xFF; +- *(tmp + 1) = (data >> 8) & 0xFF; +- break; +- case 3: +- data = ioread32(fotg210->reg + FOTG210_CXPORT); +- *tmp = data & 0xFF; +- *(tmp + 1) = (data >> 8) & 0xFF; +- *(tmp + 2) = (data >> 16) & 0xFF; +- break; +- default: +- break; +- } +- +- iowrite32(DMATFNR_DISDMA, fotg210->reg + FOTG210_DMATFNR); +-} +- +-static void fotg210_set_configuration(struct fotg210_udc *fotg210) +-{ +- u32 value = ioread32(fotg210->reg + FOTG210_DAR); +- +- value |= DAR_AFT_CONF; +- iowrite32(value, fotg210->reg + FOTG210_DAR); +-} +- +-static void fotg210_set_dev_addr(struct fotg210_udc *fotg210, u32 addr) +-{ +- u32 value = ioread32(fotg210->reg + FOTG210_DAR); +- +- value |= (addr & 0x7F); +- iowrite32(value, fotg210->reg + FOTG210_DAR); +-} +- +-static void fotg210_set_cxstall(struct fotg210_udc *fotg210) +-{ +- u32 value = ioread32(fotg210->reg + FOTG210_DCFESR); +- +- value |= DCFESR_CX_STL; +- iowrite32(value, fotg210->reg + FOTG210_DCFESR); +-} +- +-static void fotg210_request_error(struct fotg210_udc *fotg210) +-{ +- fotg210_set_cxstall(fotg210); +- pr_err("request error!!\n"); +-} +- +-static void fotg210_set_address(struct fotg210_udc *fotg210, +- struct usb_ctrlrequest *ctrl) +-{ +- if (le16_to_cpu(ctrl->wValue) >= 0x0100) { +- fotg210_request_error(fotg210); +- } else { +- fotg210_set_dev_addr(fotg210, le16_to_cpu(ctrl->wValue)); +- fotg210_set_cxdone(fotg210); +- } +-} +- +-static void fotg210_set_feature(struct fotg210_udc *fotg210, +- struct usb_ctrlrequest *ctrl) +-{ +- switch (ctrl->bRequestType & USB_RECIP_MASK) { +- case USB_RECIP_DEVICE: +- fotg210_set_cxdone(fotg210); +- break; +- case USB_RECIP_INTERFACE: +- fotg210_set_cxdone(fotg210); +- break; +- case USB_RECIP_ENDPOINT: { +- u8 epnum; +- epnum = le16_to_cpu(ctrl->wIndex) & USB_ENDPOINT_NUMBER_MASK; +- if (epnum) +- fotg210_set_epnstall(fotg210->ep[epnum]); +- else +- fotg210_set_cxstall(fotg210); +- fotg210_set_cxdone(fotg210); +- } +- break; +- default: +- fotg210_request_error(fotg210); +- break; +- } +-} +- +-static void fotg210_clear_feature(struct fotg210_udc *fotg210, +- struct usb_ctrlrequest *ctrl) +-{ +- struct fotg210_ep *ep = +- fotg210->ep[ctrl->wIndex & USB_ENDPOINT_NUMBER_MASK]; +- +- switch (ctrl->bRequestType & USB_RECIP_MASK) { +- case USB_RECIP_DEVICE: +- fotg210_set_cxdone(fotg210); +- break; +- case USB_RECIP_INTERFACE: +- fotg210_set_cxdone(fotg210); +- break; +- case USB_RECIP_ENDPOINT: +- if (ctrl->wIndex & USB_ENDPOINT_NUMBER_MASK) { +- if (ep->wedged) { +- fotg210_set_cxdone(fotg210); +- break; +- } +- if (ep->stall) +- fotg210_set_halt_and_wedge(&ep->ep, 0, 0); +- } +- fotg210_set_cxdone(fotg210); +- break; +- default: +- fotg210_request_error(fotg210); +- break; +- } +-} +- +-static int fotg210_is_epnstall(struct fotg210_ep *ep) +-{ +- struct fotg210_udc *fotg210 = ep->fotg210; +- u32 value; +- void __iomem *reg; +- +- reg = (ep->dir_in) ? +- fotg210->reg + FOTG210_INEPMPSR(ep->epnum) : +- fotg210->reg + FOTG210_OUTEPMPSR(ep->epnum); +- value = ioread32(reg); +- return value & INOUTEPMPSR_STL_EP ? 1 : 0; +-} +- +-/* For EP0 requests triggered by this driver (currently GET_STATUS response) */ +-static void fotg210_ep0_complete(struct usb_ep *_ep, struct usb_request *req) +-{ +- struct fotg210_ep *ep; +- struct fotg210_udc *fotg210; +- +- ep = container_of(_ep, struct fotg210_ep, ep); +- fotg210 = ep->fotg210; +- +- if (req->status || req->actual != req->length) { +- dev_warn(&fotg210->gadget.dev, "EP0 request failed: %d\n", req->status); +- } +-} +- +-static void fotg210_get_status(struct fotg210_udc *fotg210, +- struct usb_ctrlrequest *ctrl) +-{ +- u8 epnum; +- +- switch (ctrl->bRequestType & USB_RECIP_MASK) { +- case USB_RECIP_DEVICE: +- fotg210->ep0_data = cpu_to_le16(1 << USB_DEVICE_SELF_POWERED); +- break; +- case USB_RECIP_INTERFACE: +- fotg210->ep0_data = cpu_to_le16(0); +- break; +- case USB_RECIP_ENDPOINT: +- epnum = ctrl->wIndex & USB_ENDPOINT_NUMBER_MASK; +- if (epnum) +- fotg210->ep0_data = +- cpu_to_le16(fotg210_is_epnstall(fotg210->ep[epnum]) +- << USB_ENDPOINT_HALT); +- else +- fotg210_request_error(fotg210); +- break; +- +- default: +- fotg210_request_error(fotg210); +- return; /* exit */ +- } +- +- fotg210->ep0_req->buf = &fotg210->ep0_data; +- fotg210->ep0_req->length = 2; +- +- spin_unlock(&fotg210->lock); +- fotg210_ep_queue(fotg210->gadget.ep0, fotg210->ep0_req, GFP_ATOMIC); +- spin_lock(&fotg210->lock); +-} +- +-static int fotg210_setup_packet(struct fotg210_udc *fotg210, +- struct usb_ctrlrequest *ctrl) +-{ +- u8 *p = (u8 *)ctrl; +- u8 ret = 0; +- +- fotg210_rdsetupp(fotg210, p); +- +- fotg210->ep[0]->dir_in = ctrl->bRequestType & USB_DIR_IN; +- +- if (fotg210->gadget.speed == USB_SPEED_UNKNOWN) { +- u32 value = ioread32(fotg210->reg + FOTG210_DMCR); +- fotg210->gadget.speed = value & DMCR_HS_EN ? +- USB_SPEED_HIGH : USB_SPEED_FULL; +- } +- +- /* check request */ +- if ((ctrl->bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD) { +- switch (ctrl->bRequest) { +- case USB_REQ_GET_STATUS: +- fotg210_get_status(fotg210, ctrl); +- break; +- case USB_REQ_CLEAR_FEATURE: +- fotg210_clear_feature(fotg210, ctrl); +- break; +- case USB_REQ_SET_FEATURE: +- fotg210_set_feature(fotg210, ctrl); +- break; +- case USB_REQ_SET_ADDRESS: +- fotg210_set_address(fotg210, ctrl); +- break; +- case USB_REQ_SET_CONFIGURATION: +- fotg210_set_configuration(fotg210); +- ret = 1; +- break; +- default: +- ret = 1; +- break; +- } +- } else { +- ret = 1; +- } +- +- return ret; +-} +- +-static void fotg210_ep0out(struct fotg210_udc *fotg210) +-{ +- struct fotg210_ep *ep = fotg210->ep[0]; +- +- if (!list_empty(&ep->queue) && !ep->dir_in) { +- struct fotg210_request *req; +- +- req = list_first_entry(&ep->queue, +- struct fotg210_request, queue); +- +- if (req->req.length) +- fotg210_start_dma(ep, req); +- +- if ((req->req.length - req->req.actual) < ep->ep.maxpacket) +- fotg210_done(ep, req, 0); +- } else { +- pr_err("%s : empty queue\n", __func__); +- } +-} +- +-static void fotg210_ep0in(struct fotg210_udc *fotg210) +-{ +- struct fotg210_ep *ep = fotg210->ep[0]; +- +- if ((!list_empty(&ep->queue)) && (ep->dir_in)) { +- struct fotg210_request *req; +- +- req = list_entry(ep->queue.next, +- struct fotg210_request, queue); +- +- if (req->req.length) +- fotg210_start_dma(ep, req); +- +- if (req->req.actual == req->req.length) +- fotg210_done(ep, req, 0); +- } else { +- fotg210_set_cxdone(fotg210); +- } +-} +- +-static void fotg210_clear_comabt_int(struct fotg210_udc *fotg210) +-{ +- u32 value = ioread32(fotg210->reg + FOTG210_DISGR0); +- +- value &= ~DISGR0_CX_COMABT_INT; +- iowrite32(value, fotg210->reg + FOTG210_DISGR0); +-} +- +-static void fotg210_in_fifo_handler(struct fotg210_ep *ep) +-{ +- struct fotg210_request *req = list_entry(ep->queue.next, +- struct fotg210_request, queue); +- +- if (req->req.length) +- fotg210_start_dma(ep, req); +- fotg210_done(ep, req, 0); +-} +- +-static void fotg210_out_fifo_handler(struct fotg210_ep *ep) +-{ +- struct fotg210_request *req = list_entry(ep->queue.next, +- struct fotg210_request, queue); +- int disgr1 = ioread32(ep->fotg210->reg + FOTG210_DISGR1); +- +- fotg210_start_dma(ep, req); +- +- /* Complete the request when it's full or a short packet arrived. +- * Like other drivers, short_not_ok isn't handled. +- */ +- +- if (req->req.length == req->req.actual || +- (disgr1 & DISGR1_SPK_INT(ep->epnum - 1))) +- fotg210_done(ep, req, 0); +-} +- +-static irqreturn_t fotg210_irq(int irq, void *_fotg210) +-{ +- struct fotg210_udc *fotg210 = _fotg210; +- u32 int_grp = ioread32(fotg210->reg + FOTG210_DIGR); +- u32 int_msk = ioread32(fotg210->reg + FOTG210_DMIGR); +- +- int_grp &= ~int_msk; +- +- spin_lock(&fotg210->lock); +- +- if (int_grp & DIGR_INT_G2) { +- void __iomem *reg = fotg210->reg + FOTG210_DISGR2; +- u32 int_grp2 = ioread32(reg); +- u32 int_msk2 = ioread32(fotg210->reg + FOTG210_DMISGR2); +- u32 value; +- +- int_grp2 &= ~int_msk2; +- +- if (int_grp2 & DISGR2_USBRST_INT) { +- usb_gadget_udc_reset(&fotg210->gadget, +- fotg210->driver); +- value = ioread32(reg); +- value &= ~DISGR2_USBRST_INT; +- iowrite32(value, reg); +- pr_info("fotg210 udc reset\n"); +- } +- if (int_grp2 & DISGR2_SUSP_INT) { +- value = ioread32(reg); +- value &= ~DISGR2_SUSP_INT; +- iowrite32(value, reg); +- pr_info("fotg210 udc suspend\n"); +- } +- if (int_grp2 & DISGR2_RESM_INT) { +- value = ioread32(reg); +- value &= ~DISGR2_RESM_INT; +- iowrite32(value, reg); +- pr_info("fotg210 udc resume\n"); +- } +- if (int_grp2 & DISGR2_ISO_SEQ_ERR_INT) { +- value = ioread32(reg); +- value &= ~DISGR2_ISO_SEQ_ERR_INT; +- iowrite32(value, reg); +- pr_info("fotg210 iso sequence error\n"); +- } +- if (int_grp2 & DISGR2_ISO_SEQ_ABORT_INT) { +- value = ioread32(reg); +- value &= ~DISGR2_ISO_SEQ_ABORT_INT; +- iowrite32(value, reg); +- pr_info("fotg210 iso sequence abort\n"); +- } +- if (int_grp2 & DISGR2_TX0BYTE_INT) { +- fotg210_clear_tx0byte(fotg210); +- value = ioread32(reg); +- value &= ~DISGR2_TX0BYTE_INT; +- iowrite32(value, reg); +- pr_info("fotg210 transferred 0 byte\n"); +- } +- if (int_grp2 & DISGR2_RX0BYTE_INT) { +- fotg210_clear_rx0byte(fotg210); +- value = ioread32(reg); +- value &= ~DISGR2_RX0BYTE_INT; +- iowrite32(value, reg); +- pr_info("fotg210 received 0 byte\n"); +- } +- if (int_grp2 & DISGR2_DMA_ERROR) { +- value = ioread32(reg); +- value &= ~DISGR2_DMA_ERROR; +- iowrite32(value, reg); +- } +- } +- +- if (int_grp & DIGR_INT_G0) { +- void __iomem *reg = fotg210->reg + FOTG210_DISGR0; +- u32 int_grp0 = ioread32(reg); +- u32 int_msk0 = ioread32(fotg210->reg + FOTG210_DMISGR0); +- struct usb_ctrlrequest ctrl; +- +- int_grp0 &= ~int_msk0; +- +- /* the highest priority in this source register */ +- if (int_grp0 & DISGR0_CX_COMABT_INT) { +- fotg210_clear_comabt_int(fotg210); +- pr_info("fotg210 CX command abort\n"); +- } +- +- if (int_grp0 & DISGR0_CX_SETUP_INT) { +- if (fotg210_setup_packet(fotg210, &ctrl)) { +- spin_unlock(&fotg210->lock); +- if (fotg210->driver->setup(&fotg210->gadget, +- &ctrl) < 0) +- fotg210_set_cxstall(fotg210); +- spin_lock(&fotg210->lock); +- } +- } +- if (int_grp0 & DISGR0_CX_COMEND_INT) +- pr_info("fotg210 cmd end\n"); +- +- if (int_grp0 & DISGR0_CX_IN_INT) +- fotg210_ep0in(fotg210); +- +- if (int_grp0 & DISGR0_CX_OUT_INT) +- fotg210_ep0out(fotg210); +- +- if (int_grp0 & DISGR0_CX_COMFAIL_INT) { +- fotg210_set_cxstall(fotg210); +- pr_info("fotg210 ep0 fail\n"); +- } +- } +- +- if (int_grp & DIGR_INT_G1) { +- void __iomem *reg = fotg210->reg + FOTG210_DISGR1; +- u32 int_grp1 = ioread32(reg); +- u32 int_msk1 = ioread32(fotg210->reg + FOTG210_DMISGR1); +- int fifo; +- +- int_grp1 &= ~int_msk1; +- +- for (fifo = 0; fifo < FOTG210_MAX_FIFO_NUM; fifo++) { +- if (int_grp1 & DISGR1_IN_INT(fifo)) +- fotg210_in_fifo_handler(fotg210->ep[fifo + 1]); +- +- if ((int_grp1 & DISGR1_OUT_INT(fifo)) || +- (int_grp1 & DISGR1_SPK_INT(fifo))) +- fotg210_out_fifo_handler(fotg210->ep[fifo + 1]); +- } +- } +- +- spin_unlock(&fotg210->lock); +- +- return IRQ_HANDLED; +-} +- +-static void fotg210_disable_unplug(struct fotg210_udc *fotg210) +-{ +- u32 reg = ioread32(fotg210->reg + FOTG210_PHYTMSR); +- +- reg &= ~PHYTMSR_UNPLUG; +- iowrite32(reg, fotg210->reg + FOTG210_PHYTMSR); +-} +- +-static int fotg210_udc_start(struct usb_gadget *g, +- struct usb_gadget_driver *driver) +-{ +- struct fotg210_udc *fotg210 = gadget_to_fotg210(g); +- u32 value; +- +- /* hook up the driver */ +- fotg210->driver = driver; +- +- /* enable device global interrupt */ +- value = ioread32(fotg210->reg + FOTG210_DMCR); +- value |= DMCR_GLINT_EN; +- iowrite32(value, fotg210->reg + FOTG210_DMCR); +- +- return 0; +-} +- +-static void fotg210_init(struct fotg210_udc *fotg210) +-{ +- u32 value; +- +- /* disable global interrupt and set int polarity to active high */ +- iowrite32(GMIR_MHC_INT | GMIR_MOTG_INT | GMIR_INT_POLARITY, +- fotg210->reg + FOTG210_GMIR); +- +- /* disable device global interrupt */ +- value = ioread32(fotg210->reg + FOTG210_DMCR); +- value &= ~DMCR_GLINT_EN; +- iowrite32(value, fotg210->reg + FOTG210_DMCR); +- +- /* enable only grp2 irqs we handle */ +- iowrite32(~(DISGR2_DMA_ERROR | DISGR2_RX0BYTE_INT | DISGR2_TX0BYTE_INT +- | DISGR2_ISO_SEQ_ABORT_INT | DISGR2_ISO_SEQ_ERR_INT +- | DISGR2_RESM_INT | DISGR2_SUSP_INT | DISGR2_USBRST_INT), +- fotg210->reg + FOTG210_DMISGR2); +- +- /* disable all fifo interrupt */ +- iowrite32(~(u32)0, fotg210->reg + FOTG210_DMISGR1); +- +- /* disable cmd end */ +- value = ioread32(fotg210->reg + FOTG210_DMISGR0); +- value |= DMISGR0_MCX_COMEND; +- iowrite32(value, fotg210->reg + FOTG210_DMISGR0); +-} +- +-static int fotg210_udc_stop(struct usb_gadget *g) +-{ +- struct fotg210_udc *fotg210 = gadget_to_fotg210(g); +- unsigned long flags; +- +- spin_lock_irqsave(&fotg210->lock, flags); +- +- fotg210_init(fotg210); +- fotg210->driver = NULL; +- +- spin_unlock_irqrestore(&fotg210->lock, flags); +- +- return 0; +-} +- +-static const struct usb_gadget_ops fotg210_gadget_ops = { +- .udc_start = fotg210_udc_start, +- .udc_stop = fotg210_udc_stop, +-}; +- +-static int fotg210_udc_remove(struct platform_device *pdev) +-{ +- struct fotg210_udc *fotg210 = platform_get_drvdata(pdev); +- int i; +- +- usb_del_gadget_udc(&fotg210->gadget); +- iounmap(fotg210->reg); +- free_irq(platform_get_irq(pdev, 0), fotg210); +- +- fotg210_ep_free_request(&fotg210->ep[0]->ep, fotg210->ep0_req); +- for (i = 0; i < FOTG210_MAX_NUM_EP; i++) +- kfree(fotg210->ep[i]); +- kfree(fotg210); +- +- return 0; +-} +- +-static int fotg210_udc_probe(struct platform_device *pdev) +-{ +- struct resource *res, *ires; +- struct fotg210_udc *fotg210 = NULL; +- struct fotg210_ep *_ep[FOTG210_MAX_NUM_EP]; +- int ret = 0; +- int i; +- +- res = platform_get_resource(pdev, IORESOURCE_MEM, 0); +- if (!res) { +- pr_err("platform_get_resource error.\n"); +- return -ENODEV; +- } +- +- ires = platform_get_resource(pdev, IORESOURCE_IRQ, 0); +- if (!ires) { +- pr_err("platform_get_resource IORESOURCE_IRQ error.\n"); +- return -ENODEV; +- } +- +- ret = -ENOMEM; +- +- /* initialize udc */ +- fotg210 = kzalloc(sizeof(struct fotg210_udc), GFP_KERNEL); +- if (fotg210 == NULL) +- goto err; +- +- for (i = 0; i < FOTG210_MAX_NUM_EP; i++) { +- _ep[i] = kzalloc(sizeof(struct fotg210_ep), GFP_KERNEL); +- if (_ep[i] == NULL) +- goto err_alloc; +- fotg210->ep[i] = _ep[i]; +- } +- +- fotg210->reg = ioremap(res->start, resource_size(res)); +- if (fotg210->reg == NULL) { +- pr_err("ioremap error.\n"); +- goto err_alloc; +- } +- +- spin_lock_init(&fotg210->lock); +- +- platform_set_drvdata(pdev, fotg210); +- +- fotg210->gadget.ops = &fotg210_gadget_ops; +- +- fotg210->gadget.max_speed = USB_SPEED_HIGH; +- fotg210->gadget.dev.parent = &pdev->dev; +- fotg210->gadget.dev.dma_mask = pdev->dev.dma_mask; +- fotg210->gadget.name = udc_name; +- +- INIT_LIST_HEAD(&fotg210->gadget.ep_list); +- +- for (i = 0; i < FOTG210_MAX_NUM_EP; i++) { +- struct fotg210_ep *ep = fotg210->ep[i]; +- +- if (i) { +- INIT_LIST_HEAD(&fotg210->ep[i]->ep.ep_list); +- list_add_tail(&fotg210->ep[i]->ep.ep_list, +- &fotg210->gadget.ep_list); +- } +- ep->fotg210 = fotg210; +- INIT_LIST_HEAD(&ep->queue); +- ep->ep.name = fotg210_ep_name[i]; +- ep->ep.ops = &fotg210_ep_ops; +- usb_ep_set_maxpacket_limit(&ep->ep, (unsigned short) ~0); +- +- if (i == 0) { +- ep->ep.caps.type_control = true; +- } else { +- ep->ep.caps.type_iso = true; +- ep->ep.caps.type_bulk = true; +- ep->ep.caps.type_int = true; +- } +- +- ep->ep.caps.dir_in = true; +- ep->ep.caps.dir_out = true; +- } +- usb_ep_set_maxpacket_limit(&fotg210->ep[0]->ep, 0x40); +- fotg210->gadget.ep0 = &fotg210->ep[0]->ep; +- INIT_LIST_HEAD(&fotg210->gadget.ep0->ep_list); +- +- fotg210->ep0_req = fotg210_ep_alloc_request(&fotg210->ep[0]->ep, +- GFP_KERNEL); +- if (fotg210->ep0_req == NULL) +- goto err_map; +- +- fotg210->ep0_req->complete = fotg210_ep0_complete; +- +- fotg210_init(fotg210); +- +- fotg210_disable_unplug(fotg210); +- +- ret = request_irq(ires->start, fotg210_irq, IRQF_SHARED, +- udc_name, fotg210); +- if (ret < 0) { +- pr_err("request_irq error (%d)\n", ret); +- goto err_req; +- } +- +- ret = usb_add_gadget_udc(&pdev->dev, &fotg210->gadget); +- if (ret) +- goto err_add_udc; +- +- dev_info(&pdev->dev, "version %s\n", DRIVER_VERSION); +- +- return 0; +- +-err_add_udc: +- free_irq(ires->start, fotg210); +- +-err_req: +- fotg210_ep_free_request(&fotg210->ep[0]->ep, fotg210->ep0_req); +- +-err_map: +- iounmap(fotg210->reg); +- +-err_alloc: +- for (i = 0; i < FOTG210_MAX_NUM_EP; i++) +- kfree(fotg210->ep[i]); +- kfree(fotg210); +- +-err: +- return ret; +-} +- +-static struct platform_driver fotg210_driver = { +- .driver = { +- .name = udc_name, +- }, +- .probe = fotg210_udc_probe, +- .remove = fotg210_udc_remove, +-}; +- +-module_platform_driver(fotg210_driver); +- +-MODULE_AUTHOR("Yuan-Hsin Chen, Feng-Hsin Chiang "); +-MODULE_LICENSE("GPL"); +-MODULE_DESCRIPTION(DRIVER_DESC); +--- /dev/null ++++ b/drivers/usb/fotg210/fotg210-udc.c +@@ -0,0 +1,1239 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * FOTG210 UDC Driver supports Bulk transfer so far ++ * ++ * Copyright (C) 2013 Faraday Technology Corporation ++ * ++ * Author : Yuan-Hsin Chen ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "fotg210-udc.h" ++ ++#define DRIVER_DESC "FOTG210 USB Device Controller Driver" ++#define DRIVER_VERSION "30-April-2013" ++ ++static const char udc_name[] = "fotg210_udc"; ++static const char * const fotg210_ep_name[] = { ++ "ep0", "ep1", "ep2", "ep3", "ep4"}; ++ ++static void fotg210_disable_fifo_int(struct fotg210_ep *ep) ++{ ++ u32 value = ioread32(ep->fotg210->reg + FOTG210_DMISGR1); ++ ++ if (ep->dir_in) ++ value |= DMISGR1_MF_IN_INT(ep->epnum - 1); ++ else ++ value |= DMISGR1_MF_OUTSPK_INT(ep->epnum - 1); ++ iowrite32(value, ep->fotg210->reg + FOTG210_DMISGR1); ++} ++ ++static void fotg210_enable_fifo_int(struct fotg210_ep *ep) ++{ ++ u32 value = ioread32(ep->fotg210->reg + FOTG210_DMISGR1); ++ ++ if (ep->dir_in) ++ value &= ~DMISGR1_MF_IN_INT(ep->epnum - 1); ++ else ++ value &= ~DMISGR1_MF_OUTSPK_INT(ep->epnum - 1); ++ iowrite32(value, ep->fotg210->reg + FOTG210_DMISGR1); ++} ++ ++static void fotg210_set_cxdone(struct fotg210_udc *fotg210) ++{ ++ u32 value = ioread32(fotg210->reg + FOTG210_DCFESR); ++ ++ value |= DCFESR_CX_DONE; ++ iowrite32(value, fotg210->reg + FOTG210_DCFESR); ++} ++ ++static void fotg210_done(struct fotg210_ep *ep, struct fotg210_request *req, ++ int status) ++{ ++ list_del_init(&req->queue); ++ ++ /* don't modify queue heads during completion callback */ ++ if (ep->fotg210->gadget.speed == USB_SPEED_UNKNOWN) ++ req->req.status = -ESHUTDOWN; ++ else ++ req->req.status = status; ++ ++ spin_unlock(&ep->fotg210->lock); ++ usb_gadget_giveback_request(&ep->ep, &req->req); ++ spin_lock(&ep->fotg210->lock); ++ ++ if (ep->epnum) { ++ if (list_empty(&ep->queue)) ++ fotg210_disable_fifo_int(ep); ++ } else { ++ fotg210_set_cxdone(ep->fotg210); ++ } ++} ++ ++static void fotg210_fifo_ep_mapping(struct fotg210_ep *ep, u32 epnum, ++ u32 dir_in) ++{ ++ struct fotg210_udc *fotg210 = ep->fotg210; ++ u32 val; ++ ++ /* Driver should map an ep to a fifo and then map the fifo ++ * to the ep. What a brain-damaged design! ++ */ ++ ++ /* map a fifo to an ep */ ++ val = ioread32(fotg210->reg + FOTG210_EPMAP); ++ val &= ~EPMAP_FIFONOMSK(epnum, dir_in); ++ val |= EPMAP_FIFONO(epnum, dir_in); ++ iowrite32(val, fotg210->reg + FOTG210_EPMAP); ++ ++ /* map the ep to the fifo */ ++ val = ioread32(fotg210->reg + FOTG210_FIFOMAP); ++ val &= ~FIFOMAP_EPNOMSK(epnum); ++ val |= FIFOMAP_EPNO(epnum); ++ iowrite32(val, fotg210->reg + FOTG210_FIFOMAP); ++ ++ /* enable fifo */ ++ val = ioread32(fotg210->reg + FOTG210_FIFOCF); ++ val |= FIFOCF_FIFO_EN(epnum - 1); ++ iowrite32(val, fotg210->reg + FOTG210_FIFOCF); ++} ++ ++static void fotg210_set_fifo_dir(struct fotg210_ep *ep, u32 epnum, u32 dir_in) ++{ ++ struct fotg210_udc *fotg210 = ep->fotg210; ++ u32 val; ++ ++ val = ioread32(fotg210->reg + FOTG210_FIFOMAP); ++ val |= (dir_in ? FIFOMAP_DIRIN(epnum - 1) : FIFOMAP_DIROUT(epnum - 1)); ++ iowrite32(val, fotg210->reg + FOTG210_FIFOMAP); ++} ++ ++static void fotg210_set_tfrtype(struct fotg210_ep *ep, u32 epnum, u32 type) ++{ ++ struct fotg210_udc *fotg210 = ep->fotg210; ++ u32 val; ++ ++ val = ioread32(fotg210->reg + FOTG210_FIFOCF); ++ val |= FIFOCF_TYPE(type, epnum - 1); ++ iowrite32(val, fotg210->reg + FOTG210_FIFOCF); ++} ++ ++static void fotg210_set_mps(struct fotg210_ep *ep, u32 epnum, u32 mps, ++ u32 dir_in) ++{ ++ struct fotg210_udc *fotg210 = ep->fotg210; ++ u32 val; ++ u32 offset = dir_in ? FOTG210_INEPMPSR(epnum) : ++ FOTG210_OUTEPMPSR(epnum); ++ ++ val = ioread32(fotg210->reg + offset); ++ val |= INOUTEPMPSR_MPS(mps); ++ iowrite32(val, fotg210->reg + offset); ++} ++ ++static int fotg210_config_ep(struct fotg210_ep *ep, ++ const struct usb_endpoint_descriptor *desc) ++{ ++ struct fotg210_udc *fotg210 = ep->fotg210; ++ ++ fotg210_set_fifo_dir(ep, ep->epnum, ep->dir_in); ++ fotg210_set_tfrtype(ep, ep->epnum, ep->type); ++ fotg210_set_mps(ep, ep->epnum, ep->ep.maxpacket, ep->dir_in); ++ fotg210_fifo_ep_mapping(ep, ep->epnum, ep->dir_in); ++ ++ fotg210->ep[ep->epnum] = ep; ++ ++ return 0; ++} ++ ++static int fotg210_ep_enable(struct usb_ep *_ep, ++ const struct usb_endpoint_descriptor *desc) ++{ ++ struct fotg210_ep *ep; ++ ++ ep = container_of(_ep, struct fotg210_ep, ep); ++ ++ ep->desc = desc; ++ ep->epnum = usb_endpoint_num(desc); ++ ep->type = usb_endpoint_type(desc); ++ ep->dir_in = usb_endpoint_dir_in(desc); ++ ep->ep.maxpacket = usb_endpoint_maxp(desc); ++ ++ return fotg210_config_ep(ep, desc); ++} ++ ++static void fotg210_reset_tseq(struct fotg210_udc *fotg210, u8 epnum) ++{ ++ struct fotg210_ep *ep = fotg210->ep[epnum]; ++ u32 value; ++ void __iomem *reg; ++ ++ reg = (ep->dir_in) ? ++ fotg210->reg + FOTG210_INEPMPSR(epnum) : ++ fotg210->reg + FOTG210_OUTEPMPSR(epnum); ++ ++ /* Note: Driver needs to set and clear INOUTEPMPSR_RESET_TSEQ ++ * bit. Controller wouldn't clear this bit. WTF!!! ++ */ ++ ++ value = ioread32(reg); ++ value |= INOUTEPMPSR_RESET_TSEQ; ++ iowrite32(value, reg); ++ ++ value = ioread32(reg); ++ value &= ~INOUTEPMPSR_RESET_TSEQ; ++ iowrite32(value, reg); ++} ++ ++static int fotg210_ep_release(struct fotg210_ep *ep) ++{ ++ if (!ep->epnum) ++ return 0; ++ ep->epnum = 0; ++ ep->stall = 0; ++ ep->wedged = 0; ++ ++ fotg210_reset_tseq(ep->fotg210, ep->epnum); ++ ++ return 0; ++} ++ ++static int fotg210_ep_disable(struct usb_ep *_ep) ++{ ++ struct fotg210_ep *ep; ++ struct fotg210_request *req; ++ unsigned long flags; ++ ++ BUG_ON(!_ep); ++ ++ ep = container_of(_ep, struct fotg210_ep, ep); ++ ++ while (!list_empty(&ep->queue)) { ++ req = list_entry(ep->queue.next, ++ struct fotg210_request, queue); ++ spin_lock_irqsave(&ep->fotg210->lock, flags); ++ fotg210_done(ep, req, -ECONNRESET); ++ spin_unlock_irqrestore(&ep->fotg210->lock, flags); ++ } ++ ++ return fotg210_ep_release(ep); ++} ++ ++static struct usb_request *fotg210_ep_alloc_request(struct usb_ep *_ep, ++ gfp_t gfp_flags) ++{ ++ struct fotg210_request *req; ++ ++ req = kzalloc(sizeof(struct fotg210_request), gfp_flags); ++ if (!req) ++ return NULL; ++ ++ INIT_LIST_HEAD(&req->queue); ++ ++ return &req->req; ++} ++ ++static void fotg210_ep_free_request(struct usb_ep *_ep, ++ struct usb_request *_req) ++{ ++ struct fotg210_request *req; ++ ++ req = container_of(_req, struct fotg210_request, req); ++ kfree(req); ++} ++ ++static void fotg210_enable_dma(struct fotg210_ep *ep, ++ dma_addr_t d, u32 len) ++{ ++ u32 value; ++ struct fotg210_udc *fotg210 = ep->fotg210; ++ ++ /* set transfer length and direction */ ++ value = ioread32(fotg210->reg + FOTG210_DMACPSR1); ++ value &= ~(DMACPSR1_DMA_LEN(0xFFFF) | DMACPSR1_DMA_TYPE(1)); ++ value |= DMACPSR1_DMA_LEN(len) | DMACPSR1_DMA_TYPE(ep->dir_in); ++ iowrite32(value, fotg210->reg + FOTG210_DMACPSR1); ++ ++ /* set device DMA target FIFO number */ ++ value = ioread32(fotg210->reg + FOTG210_DMATFNR); ++ if (ep->epnum) ++ value |= DMATFNR_ACC_FN(ep->epnum - 1); ++ else ++ value |= DMATFNR_ACC_CXF; ++ iowrite32(value, fotg210->reg + FOTG210_DMATFNR); ++ ++ /* set DMA memory address */ ++ iowrite32(d, fotg210->reg + FOTG210_DMACPSR2); ++ ++ /* enable MDMA_EROR and MDMA_CMPLT interrupt */ ++ value = ioread32(fotg210->reg + FOTG210_DMISGR2); ++ value &= ~(DMISGR2_MDMA_CMPLT | DMISGR2_MDMA_ERROR); ++ iowrite32(value, fotg210->reg + FOTG210_DMISGR2); ++ ++ /* start DMA */ ++ value = ioread32(fotg210->reg + FOTG210_DMACPSR1); ++ value |= DMACPSR1_DMA_START; ++ iowrite32(value, fotg210->reg + FOTG210_DMACPSR1); ++} ++ ++static void fotg210_disable_dma(struct fotg210_ep *ep) ++{ ++ iowrite32(DMATFNR_DISDMA, ep->fotg210->reg + FOTG210_DMATFNR); ++} ++ ++static void fotg210_wait_dma_done(struct fotg210_ep *ep) ++{ ++ u32 value; ++ ++ do { ++ value = ioread32(ep->fotg210->reg + FOTG210_DISGR2); ++ if ((value & DISGR2_USBRST_INT) || ++ (value & DISGR2_DMA_ERROR)) ++ goto dma_reset; ++ } while (!(value & DISGR2_DMA_CMPLT)); ++ ++ value &= ~DISGR2_DMA_CMPLT; ++ iowrite32(value, ep->fotg210->reg + FOTG210_DISGR2); ++ return; ++ ++dma_reset: ++ value = ioread32(ep->fotg210->reg + FOTG210_DMACPSR1); ++ value |= DMACPSR1_DMA_ABORT; ++ iowrite32(value, ep->fotg210->reg + FOTG210_DMACPSR1); ++ ++ /* reset fifo */ ++ if (ep->epnum) { ++ value = ioread32(ep->fotg210->reg + ++ FOTG210_FIBCR(ep->epnum - 1)); ++ value |= FIBCR_FFRST; ++ iowrite32(value, ep->fotg210->reg + ++ FOTG210_FIBCR(ep->epnum - 1)); ++ } else { ++ value = ioread32(ep->fotg210->reg + FOTG210_DCFESR); ++ value |= DCFESR_CX_CLR; ++ iowrite32(value, ep->fotg210->reg + FOTG210_DCFESR); ++ } ++} ++ ++static void fotg210_start_dma(struct fotg210_ep *ep, ++ struct fotg210_request *req) ++{ ++ struct device *dev = &ep->fotg210->gadget.dev; ++ dma_addr_t d; ++ u8 *buffer; ++ u32 length; ++ ++ if (ep->epnum) { ++ if (ep->dir_in) { ++ buffer = req->req.buf; ++ length = req->req.length; ++ } else { ++ buffer = req->req.buf + req->req.actual; ++ length = ioread32(ep->fotg210->reg + ++ FOTG210_FIBCR(ep->epnum - 1)) & FIBCR_BCFX; ++ if (length > req->req.length - req->req.actual) ++ length = req->req.length - req->req.actual; ++ } ++ } else { ++ buffer = req->req.buf + req->req.actual; ++ if (req->req.length - req->req.actual > ep->ep.maxpacket) ++ length = ep->ep.maxpacket; ++ else ++ length = req->req.length - req->req.actual; ++ } ++ ++ d = dma_map_single(dev, buffer, length, ++ ep->dir_in ? DMA_TO_DEVICE : DMA_FROM_DEVICE); ++ ++ if (dma_mapping_error(dev, d)) { ++ pr_err("dma_mapping_error\n"); ++ return; ++ } ++ ++ fotg210_enable_dma(ep, d, length); ++ ++ /* check if dma is done */ ++ fotg210_wait_dma_done(ep); ++ ++ fotg210_disable_dma(ep); ++ ++ /* update actual transfer length */ ++ req->req.actual += length; ++ ++ dma_unmap_single(dev, d, length, DMA_TO_DEVICE); ++} ++ ++static void fotg210_ep0_queue(struct fotg210_ep *ep, ++ struct fotg210_request *req) ++{ ++ if (!req->req.length) { ++ fotg210_done(ep, req, 0); ++ return; ++ } ++ if (ep->dir_in) { /* if IN */ ++ fotg210_start_dma(ep, req); ++ if (req->req.length == req->req.actual) ++ fotg210_done(ep, req, 0); ++ } else { /* OUT */ ++ u32 value = ioread32(ep->fotg210->reg + FOTG210_DMISGR0); ++ ++ value &= ~DMISGR0_MCX_OUT_INT; ++ iowrite32(value, ep->fotg210->reg + FOTG210_DMISGR0); ++ } ++} ++ ++static int fotg210_ep_queue(struct usb_ep *_ep, struct usb_request *_req, ++ gfp_t gfp_flags) ++{ ++ struct fotg210_ep *ep; ++ struct fotg210_request *req; ++ unsigned long flags; ++ int request = 0; ++ ++ ep = container_of(_ep, struct fotg210_ep, ep); ++ req = container_of(_req, struct fotg210_request, req); ++ ++ if (ep->fotg210->gadget.speed == USB_SPEED_UNKNOWN) ++ return -ESHUTDOWN; ++ ++ spin_lock_irqsave(&ep->fotg210->lock, flags); ++ ++ if (list_empty(&ep->queue)) ++ request = 1; ++ ++ list_add_tail(&req->queue, &ep->queue); ++ ++ req->req.actual = 0; ++ req->req.status = -EINPROGRESS; ++ ++ if (!ep->epnum) /* ep0 */ ++ fotg210_ep0_queue(ep, req); ++ else if (request && !ep->stall) ++ fotg210_enable_fifo_int(ep); ++ ++ spin_unlock_irqrestore(&ep->fotg210->lock, flags); ++ ++ return 0; ++} ++ ++static int fotg210_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req) ++{ ++ struct fotg210_ep *ep; ++ struct fotg210_request *req; ++ unsigned long flags; ++ ++ ep = container_of(_ep, struct fotg210_ep, ep); ++ req = container_of(_req, struct fotg210_request, req); ++ ++ spin_lock_irqsave(&ep->fotg210->lock, flags); ++ if (!list_empty(&ep->queue)) ++ fotg210_done(ep, req, -ECONNRESET); ++ spin_unlock_irqrestore(&ep->fotg210->lock, flags); ++ ++ return 0; ++} ++ ++static void fotg210_set_epnstall(struct fotg210_ep *ep) ++{ ++ struct fotg210_udc *fotg210 = ep->fotg210; ++ u32 value; ++ void __iomem *reg; ++ ++ /* check if IN FIFO is empty before stall */ ++ if (ep->dir_in) { ++ do { ++ value = ioread32(fotg210->reg + FOTG210_DCFESR); ++ } while (!(value & DCFESR_FIFO_EMPTY(ep->epnum - 1))); ++ } ++ ++ reg = (ep->dir_in) ? ++ fotg210->reg + FOTG210_INEPMPSR(ep->epnum) : ++ fotg210->reg + FOTG210_OUTEPMPSR(ep->epnum); ++ value = ioread32(reg); ++ value |= INOUTEPMPSR_STL_EP; ++ iowrite32(value, reg); ++} ++ ++static void fotg210_clear_epnstall(struct fotg210_ep *ep) ++{ ++ struct fotg210_udc *fotg210 = ep->fotg210; ++ u32 value; ++ void __iomem *reg; ++ ++ reg = (ep->dir_in) ? ++ fotg210->reg + FOTG210_INEPMPSR(ep->epnum) : ++ fotg210->reg + FOTG210_OUTEPMPSR(ep->epnum); ++ value = ioread32(reg); ++ value &= ~INOUTEPMPSR_STL_EP; ++ iowrite32(value, reg); ++} ++ ++static int fotg210_set_halt_and_wedge(struct usb_ep *_ep, int value, int wedge) ++{ ++ struct fotg210_ep *ep; ++ struct fotg210_udc *fotg210; ++ unsigned long flags; ++ ++ ep = container_of(_ep, struct fotg210_ep, ep); ++ ++ fotg210 = ep->fotg210; ++ ++ spin_lock_irqsave(&ep->fotg210->lock, flags); ++ ++ if (value) { ++ fotg210_set_epnstall(ep); ++ ep->stall = 1; ++ if (wedge) ++ ep->wedged = 1; ++ } else { ++ fotg210_reset_tseq(fotg210, ep->epnum); ++ fotg210_clear_epnstall(ep); ++ ep->stall = 0; ++ ep->wedged = 0; ++ if (!list_empty(&ep->queue)) ++ fotg210_enable_fifo_int(ep); ++ } ++ ++ spin_unlock_irqrestore(&ep->fotg210->lock, flags); ++ return 0; ++} ++ ++static int fotg210_ep_set_halt(struct usb_ep *_ep, int value) ++{ ++ return fotg210_set_halt_and_wedge(_ep, value, 0); ++} ++ ++static int fotg210_ep_set_wedge(struct usb_ep *_ep) ++{ ++ return fotg210_set_halt_and_wedge(_ep, 1, 1); ++} ++ ++static void fotg210_ep_fifo_flush(struct usb_ep *_ep) ++{ ++} ++ ++static const struct usb_ep_ops fotg210_ep_ops = { ++ .enable = fotg210_ep_enable, ++ .disable = fotg210_ep_disable, ++ ++ .alloc_request = fotg210_ep_alloc_request, ++ .free_request = fotg210_ep_free_request, ++ ++ .queue = fotg210_ep_queue, ++ .dequeue = fotg210_ep_dequeue, ++ ++ .set_halt = fotg210_ep_set_halt, ++ .fifo_flush = fotg210_ep_fifo_flush, ++ .set_wedge = fotg210_ep_set_wedge, ++}; ++ ++static void fotg210_clear_tx0byte(struct fotg210_udc *fotg210) ++{ ++ u32 value = ioread32(fotg210->reg + FOTG210_TX0BYTE); ++ ++ value &= ~(TX0BYTE_EP1 | TX0BYTE_EP2 | TX0BYTE_EP3 ++ | TX0BYTE_EP4); ++ iowrite32(value, fotg210->reg + FOTG210_TX0BYTE); ++} ++ ++static void fotg210_clear_rx0byte(struct fotg210_udc *fotg210) ++{ ++ u32 value = ioread32(fotg210->reg + FOTG210_RX0BYTE); ++ ++ value &= ~(RX0BYTE_EP1 | RX0BYTE_EP2 | RX0BYTE_EP3 ++ | RX0BYTE_EP4); ++ iowrite32(value, fotg210->reg + FOTG210_RX0BYTE); ++} ++ ++/* read 8-byte setup packet only */ ++static void fotg210_rdsetupp(struct fotg210_udc *fotg210, ++ u8 *buffer) ++{ ++ int i = 0; ++ u8 *tmp = buffer; ++ u32 data; ++ u32 length = 8; ++ ++ iowrite32(DMATFNR_ACC_CXF, fotg210->reg + FOTG210_DMATFNR); ++ ++ for (i = (length >> 2); i > 0; i--) { ++ data = ioread32(fotg210->reg + FOTG210_CXPORT); ++ *tmp = data & 0xFF; ++ *(tmp + 1) = (data >> 8) & 0xFF; ++ *(tmp + 2) = (data >> 16) & 0xFF; ++ *(tmp + 3) = (data >> 24) & 0xFF; ++ tmp = tmp + 4; ++ } ++ ++ switch (length % 4) { ++ case 1: ++ data = ioread32(fotg210->reg + FOTG210_CXPORT); ++ *tmp = data & 0xFF; ++ break; ++ case 2: ++ data = ioread32(fotg210->reg + FOTG210_CXPORT); ++ *tmp = data & 0xFF; ++ *(tmp + 1) = (data >> 8) & 0xFF; ++ break; ++ case 3: ++ data = ioread32(fotg210->reg + FOTG210_CXPORT); ++ *tmp = data & 0xFF; ++ *(tmp + 1) = (data >> 8) & 0xFF; ++ *(tmp + 2) = (data >> 16) & 0xFF; ++ break; ++ default: ++ break; ++ } ++ ++ iowrite32(DMATFNR_DISDMA, fotg210->reg + FOTG210_DMATFNR); ++} ++ ++static void fotg210_set_configuration(struct fotg210_udc *fotg210) ++{ ++ u32 value = ioread32(fotg210->reg + FOTG210_DAR); ++ ++ value |= DAR_AFT_CONF; ++ iowrite32(value, fotg210->reg + FOTG210_DAR); ++} ++ ++static void fotg210_set_dev_addr(struct fotg210_udc *fotg210, u32 addr) ++{ ++ u32 value = ioread32(fotg210->reg + FOTG210_DAR); ++ ++ value |= (addr & 0x7F); ++ iowrite32(value, fotg210->reg + FOTG210_DAR); ++} ++ ++static void fotg210_set_cxstall(struct fotg210_udc *fotg210) ++{ ++ u32 value = ioread32(fotg210->reg + FOTG210_DCFESR); ++ ++ value |= DCFESR_CX_STL; ++ iowrite32(value, fotg210->reg + FOTG210_DCFESR); ++} ++ ++static void fotg210_request_error(struct fotg210_udc *fotg210) ++{ ++ fotg210_set_cxstall(fotg210); ++ pr_err("request error!!\n"); ++} ++ ++static void fotg210_set_address(struct fotg210_udc *fotg210, ++ struct usb_ctrlrequest *ctrl) ++{ ++ if (le16_to_cpu(ctrl->wValue) >= 0x0100) { ++ fotg210_request_error(fotg210); ++ } else { ++ fotg210_set_dev_addr(fotg210, le16_to_cpu(ctrl->wValue)); ++ fotg210_set_cxdone(fotg210); ++ } ++} ++ ++static void fotg210_set_feature(struct fotg210_udc *fotg210, ++ struct usb_ctrlrequest *ctrl) ++{ ++ switch (ctrl->bRequestType & USB_RECIP_MASK) { ++ case USB_RECIP_DEVICE: ++ fotg210_set_cxdone(fotg210); ++ break; ++ case USB_RECIP_INTERFACE: ++ fotg210_set_cxdone(fotg210); ++ break; ++ case USB_RECIP_ENDPOINT: { ++ u8 epnum; ++ epnum = le16_to_cpu(ctrl->wIndex) & USB_ENDPOINT_NUMBER_MASK; ++ if (epnum) ++ fotg210_set_epnstall(fotg210->ep[epnum]); ++ else ++ fotg210_set_cxstall(fotg210); ++ fotg210_set_cxdone(fotg210); ++ } ++ break; ++ default: ++ fotg210_request_error(fotg210); ++ break; ++ } ++} ++ ++static void fotg210_clear_feature(struct fotg210_udc *fotg210, ++ struct usb_ctrlrequest *ctrl) ++{ ++ struct fotg210_ep *ep = ++ fotg210->ep[ctrl->wIndex & USB_ENDPOINT_NUMBER_MASK]; ++ ++ switch (ctrl->bRequestType & USB_RECIP_MASK) { ++ case USB_RECIP_DEVICE: ++ fotg210_set_cxdone(fotg210); ++ break; ++ case USB_RECIP_INTERFACE: ++ fotg210_set_cxdone(fotg210); ++ break; ++ case USB_RECIP_ENDPOINT: ++ if (ctrl->wIndex & USB_ENDPOINT_NUMBER_MASK) { ++ if (ep->wedged) { ++ fotg210_set_cxdone(fotg210); ++ break; ++ } ++ if (ep->stall) ++ fotg210_set_halt_and_wedge(&ep->ep, 0, 0); ++ } ++ fotg210_set_cxdone(fotg210); ++ break; ++ default: ++ fotg210_request_error(fotg210); ++ break; ++ } ++} ++ ++static int fotg210_is_epnstall(struct fotg210_ep *ep) ++{ ++ struct fotg210_udc *fotg210 = ep->fotg210; ++ u32 value; ++ void __iomem *reg; ++ ++ reg = (ep->dir_in) ? ++ fotg210->reg + FOTG210_INEPMPSR(ep->epnum) : ++ fotg210->reg + FOTG210_OUTEPMPSR(ep->epnum); ++ value = ioread32(reg); ++ return value & INOUTEPMPSR_STL_EP ? 1 : 0; ++} ++ ++/* For EP0 requests triggered by this driver (currently GET_STATUS response) */ ++static void fotg210_ep0_complete(struct usb_ep *_ep, struct usb_request *req) ++{ ++ struct fotg210_ep *ep; ++ struct fotg210_udc *fotg210; ++ ++ ep = container_of(_ep, struct fotg210_ep, ep); ++ fotg210 = ep->fotg210; ++ ++ if (req->status || req->actual != req->length) { ++ dev_warn(&fotg210->gadget.dev, "EP0 request failed: %d\n", req->status); ++ } ++} ++ ++static void fotg210_get_status(struct fotg210_udc *fotg210, ++ struct usb_ctrlrequest *ctrl) ++{ ++ u8 epnum; ++ ++ switch (ctrl->bRequestType & USB_RECIP_MASK) { ++ case USB_RECIP_DEVICE: ++ fotg210->ep0_data = cpu_to_le16(1 << USB_DEVICE_SELF_POWERED); ++ break; ++ case USB_RECIP_INTERFACE: ++ fotg210->ep0_data = cpu_to_le16(0); ++ break; ++ case USB_RECIP_ENDPOINT: ++ epnum = ctrl->wIndex & USB_ENDPOINT_NUMBER_MASK; ++ if (epnum) ++ fotg210->ep0_data = ++ cpu_to_le16(fotg210_is_epnstall(fotg210->ep[epnum]) ++ << USB_ENDPOINT_HALT); ++ else ++ fotg210_request_error(fotg210); ++ break; ++ ++ default: ++ fotg210_request_error(fotg210); ++ return; /* exit */ ++ } ++ ++ fotg210->ep0_req->buf = &fotg210->ep0_data; ++ fotg210->ep0_req->length = 2; ++ ++ spin_unlock(&fotg210->lock); ++ fotg210_ep_queue(fotg210->gadget.ep0, fotg210->ep0_req, GFP_ATOMIC); ++ spin_lock(&fotg210->lock); ++} ++ ++static int fotg210_setup_packet(struct fotg210_udc *fotg210, ++ struct usb_ctrlrequest *ctrl) ++{ ++ u8 *p = (u8 *)ctrl; ++ u8 ret = 0; ++ ++ fotg210_rdsetupp(fotg210, p); ++ ++ fotg210->ep[0]->dir_in = ctrl->bRequestType & USB_DIR_IN; ++ ++ if (fotg210->gadget.speed == USB_SPEED_UNKNOWN) { ++ u32 value = ioread32(fotg210->reg + FOTG210_DMCR); ++ fotg210->gadget.speed = value & DMCR_HS_EN ? ++ USB_SPEED_HIGH : USB_SPEED_FULL; ++ } ++ ++ /* check request */ ++ if ((ctrl->bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD) { ++ switch (ctrl->bRequest) { ++ case USB_REQ_GET_STATUS: ++ fotg210_get_status(fotg210, ctrl); ++ break; ++ case USB_REQ_CLEAR_FEATURE: ++ fotg210_clear_feature(fotg210, ctrl); ++ break; ++ case USB_REQ_SET_FEATURE: ++ fotg210_set_feature(fotg210, ctrl); ++ break; ++ case USB_REQ_SET_ADDRESS: ++ fotg210_set_address(fotg210, ctrl); ++ break; ++ case USB_REQ_SET_CONFIGURATION: ++ fotg210_set_configuration(fotg210); ++ ret = 1; ++ break; ++ default: ++ ret = 1; ++ break; ++ } ++ } else { ++ ret = 1; ++ } ++ ++ return ret; ++} ++ ++static void fotg210_ep0out(struct fotg210_udc *fotg210) ++{ ++ struct fotg210_ep *ep = fotg210->ep[0]; ++ ++ if (!list_empty(&ep->queue) && !ep->dir_in) { ++ struct fotg210_request *req; ++ ++ req = list_first_entry(&ep->queue, ++ struct fotg210_request, queue); ++ ++ if (req->req.length) ++ fotg210_start_dma(ep, req); ++ ++ if ((req->req.length - req->req.actual) < ep->ep.maxpacket) ++ fotg210_done(ep, req, 0); ++ } else { ++ pr_err("%s : empty queue\n", __func__); ++ } ++} ++ ++static void fotg210_ep0in(struct fotg210_udc *fotg210) ++{ ++ struct fotg210_ep *ep = fotg210->ep[0]; ++ ++ if ((!list_empty(&ep->queue)) && (ep->dir_in)) { ++ struct fotg210_request *req; ++ ++ req = list_entry(ep->queue.next, ++ struct fotg210_request, queue); ++ ++ if (req->req.length) ++ fotg210_start_dma(ep, req); ++ ++ if (req->req.actual == req->req.length) ++ fotg210_done(ep, req, 0); ++ } else { ++ fotg210_set_cxdone(fotg210); ++ } ++} ++ ++static void fotg210_clear_comabt_int(struct fotg210_udc *fotg210) ++{ ++ u32 value = ioread32(fotg210->reg + FOTG210_DISGR0); ++ ++ value &= ~DISGR0_CX_COMABT_INT; ++ iowrite32(value, fotg210->reg + FOTG210_DISGR0); ++} ++ ++static void fotg210_in_fifo_handler(struct fotg210_ep *ep) ++{ ++ struct fotg210_request *req = list_entry(ep->queue.next, ++ struct fotg210_request, queue); ++ ++ if (req->req.length) ++ fotg210_start_dma(ep, req); ++ fotg210_done(ep, req, 0); ++} ++ ++static void fotg210_out_fifo_handler(struct fotg210_ep *ep) ++{ ++ struct fotg210_request *req = list_entry(ep->queue.next, ++ struct fotg210_request, queue); ++ int disgr1 = ioread32(ep->fotg210->reg + FOTG210_DISGR1); ++ ++ fotg210_start_dma(ep, req); ++ ++ /* Complete the request when it's full or a short packet arrived. ++ * Like other drivers, short_not_ok isn't handled. ++ */ ++ ++ if (req->req.length == req->req.actual || ++ (disgr1 & DISGR1_SPK_INT(ep->epnum - 1))) ++ fotg210_done(ep, req, 0); ++} ++ ++static irqreturn_t fotg210_irq(int irq, void *_fotg210) ++{ ++ struct fotg210_udc *fotg210 = _fotg210; ++ u32 int_grp = ioread32(fotg210->reg + FOTG210_DIGR); ++ u32 int_msk = ioread32(fotg210->reg + FOTG210_DMIGR); ++ ++ int_grp &= ~int_msk; ++ ++ spin_lock(&fotg210->lock); ++ ++ if (int_grp & DIGR_INT_G2) { ++ void __iomem *reg = fotg210->reg + FOTG210_DISGR2; ++ u32 int_grp2 = ioread32(reg); ++ u32 int_msk2 = ioread32(fotg210->reg + FOTG210_DMISGR2); ++ u32 value; ++ ++ int_grp2 &= ~int_msk2; ++ ++ if (int_grp2 & DISGR2_USBRST_INT) { ++ usb_gadget_udc_reset(&fotg210->gadget, ++ fotg210->driver); ++ value = ioread32(reg); ++ value &= ~DISGR2_USBRST_INT; ++ iowrite32(value, reg); ++ pr_info("fotg210 udc reset\n"); ++ } ++ if (int_grp2 & DISGR2_SUSP_INT) { ++ value = ioread32(reg); ++ value &= ~DISGR2_SUSP_INT; ++ iowrite32(value, reg); ++ pr_info("fotg210 udc suspend\n"); ++ } ++ if (int_grp2 & DISGR2_RESM_INT) { ++ value = ioread32(reg); ++ value &= ~DISGR2_RESM_INT; ++ iowrite32(value, reg); ++ pr_info("fotg210 udc resume\n"); ++ } ++ if (int_grp2 & DISGR2_ISO_SEQ_ERR_INT) { ++ value = ioread32(reg); ++ value &= ~DISGR2_ISO_SEQ_ERR_INT; ++ iowrite32(value, reg); ++ pr_info("fotg210 iso sequence error\n"); ++ } ++ if (int_grp2 & DISGR2_ISO_SEQ_ABORT_INT) { ++ value = ioread32(reg); ++ value &= ~DISGR2_ISO_SEQ_ABORT_INT; ++ iowrite32(value, reg); ++ pr_info("fotg210 iso sequence abort\n"); ++ } ++ if (int_grp2 & DISGR2_TX0BYTE_INT) { ++ fotg210_clear_tx0byte(fotg210); ++ value = ioread32(reg); ++ value &= ~DISGR2_TX0BYTE_INT; ++ iowrite32(value, reg); ++ pr_info("fotg210 transferred 0 byte\n"); ++ } ++ if (int_grp2 & DISGR2_RX0BYTE_INT) { ++ fotg210_clear_rx0byte(fotg210); ++ value = ioread32(reg); ++ value &= ~DISGR2_RX0BYTE_INT; ++ iowrite32(value, reg); ++ pr_info("fotg210 received 0 byte\n"); ++ } ++ if (int_grp2 & DISGR2_DMA_ERROR) { ++ value = ioread32(reg); ++ value &= ~DISGR2_DMA_ERROR; ++ iowrite32(value, reg); ++ } ++ } ++ ++ if (int_grp & DIGR_INT_G0) { ++ void __iomem *reg = fotg210->reg + FOTG210_DISGR0; ++ u32 int_grp0 = ioread32(reg); ++ u32 int_msk0 = ioread32(fotg210->reg + FOTG210_DMISGR0); ++ struct usb_ctrlrequest ctrl; ++ ++ int_grp0 &= ~int_msk0; ++ ++ /* the highest priority in this source register */ ++ if (int_grp0 & DISGR0_CX_COMABT_INT) { ++ fotg210_clear_comabt_int(fotg210); ++ pr_info("fotg210 CX command abort\n"); ++ } ++ ++ if (int_grp0 & DISGR0_CX_SETUP_INT) { ++ if (fotg210_setup_packet(fotg210, &ctrl)) { ++ spin_unlock(&fotg210->lock); ++ if (fotg210->driver->setup(&fotg210->gadget, ++ &ctrl) < 0) ++ fotg210_set_cxstall(fotg210); ++ spin_lock(&fotg210->lock); ++ } ++ } ++ if (int_grp0 & DISGR0_CX_COMEND_INT) ++ pr_info("fotg210 cmd end\n"); ++ ++ if (int_grp0 & DISGR0_CX_IN_INT) ++ fotg210_ep0in(fotg210); ++ ++ if (int_grp0 & DISGR0_CX_OUT_INT) ++ fotg210_ep0out(fotg210); ++ ++ if (int_grp0 & DISGR0_CX_COMFAIL_INT) { ++ fotg210_set_cxstall(fotg210); ++ pr_info("fotg210 ep0 fail\n"); ++ } ++ } ++ ++ if (int_grp & DIGR_INT_G1) { ++ void __iomem *reg = fotg210->reg + FOTG210_DISGR1; ++ u32 int_grp1 = ioread32(reg); ++ u32 int_msk1 = ioread32(fotg210->reg + FOTG210_DMISGR1); ++ int fifo; ++ ++ int_grp1 &= ~int_msk1; ++ ++ for (fifo = 0; fifo < FOTG210_MAX_FIFO_NUM; fifo++) { ++ if (int_grp1 & DISGR1_IN_INT(fifo)) ++ fotg210_in_fifo_handler(fotg210->ep[fifo + 1]); ++ ++ if ((int_grp1 & DISGR1_OUT_INT(fifo)) || ++ (int_grp1 & DISGR1_SPK_INT(fifo))) ++ fotg210_out_fifo_handler(fotg210->ep[fifo + 1]); ++ } ++ } ++ ++ spin_unlock(&fotg210->lock); ++ ++ return IRQ_HANDLED; ++} ++ ++static void fotg210_disable_unplug(struct fotg210_udc *fotg210) ++{ ++ u32 reg = ioread32(fotg210->reg + FOTG210_PHYTMSR); ++ ++ reg &= ~PHYTMSR_UNPLUG; ++ iowrite32(reg, fotg210->reg + FOTG210_PHYTMSR); ++} ++ ++static int fotg210_udc_start(struct usb_gadget *g, ++ struct usb_gadget_driver *driver) ++{ ++ struct fotg210_udc *fotg210 = gadget_to_fotg210(g); ++ u32 value; ++ ++ /* hook up the driver */ ++ fotg210->driver = driver; ++ ++ /* enable device global interrupt */ ++ value = ioread32(fotg210->reg + FOTG210_DMCR); ++ value |= DMCR_GLINT_EN; ++ iowrite32(value, fotg210->reg + FOTG210_DMCR); ++ ++ return 0; ++} ++ ++static void fotg210_init(struct fotg210_udc *fotg210) ++{ ++ u32 value; ++ ++ /* disable global interrupt and set int polarity to active high */ ++ iowrite32(GMIR_MHC_INT | GMIR_MOTG_INT | GMIR_INT_POLARITY, ++ fotg210->reg + FOTG210_GMIR); ++ ++ /* disable device global interrupt */ ++ value = ioread32(fotg210->reg + FOTG210_DMCR); ++ value &= ~DMCR_GLINT_EN; ++ iowrite32(value, fotg210->reg + FOTG210_DMCR); ++ ++ /* enable only grp2 irqs we handle */ ++ iowrite32(~(DISGR2_DMA_ERROR | DISGR2_RX0BYTE_INT | DISGR2_TX0BYTE_INT ++ | DISGR2_ISO_SEQ_ABORT_INT | DISGR2_ISO_SEQ_ERR_INT ++ | DISGR2_RESM_INT | DISGR2_SUSP_INT | DISGR2_USBRST_INT), ++ fotg210->reg + FOTG210_DMISGR2); ++ ++ /* disable all fifo interrupt */ ++ iowrite32(~(u32)0, fotg210->reg + FOTG210_DMISGR1); ++ ++ /* disable cmd end */ ++ value = ioread32(fotg210->reg + FOTG210_DMISGR0); ++ value |= DMISGR0_MCX_COMEND; ++ iowrite32(value, fotg210->reg + FOTG210_DMISGR0); ++} ++ ++static int fotg210_udc_stop(struct usb_gadget *g) ++{ ++ struct fotg210_udc *fotg210 = gadget_to_fotg210(g); ++ unsigned long flags; ++ ++ spin_lock_irqsave(&fotg210->lock, flags); ++ ++ fotg210_init(fotg210); ++ fotg210->driver = NULL; ++ ++ spin_unlock_irqrestore(&fotg210->lock, flags); ++ ++ return 0; ++} ++ ++static const struct usb_gadget_ops fotg210_gadget_ops = { ++ .udc_start = fotg210_udc_start, ++ .udc_stop = fotg210_udc_stop, ++}; ++ ++static int fotg210_udc_remove(struct platform_device *pdev) ++{ ++ struct fotg210_udc *fotg210 = platform_get_drvdata(pdev); ++ int i; ++ ++ usb_del_gadget_udc(&fotg210->gadget); ++ iounmap(fotg210->reg); ++ free_irq(platform_get_irq(pdev, 0), fotg210); ++ ++ fotg210_ep_free_request(&fotg210->ep[0]->ep, fotg210->ep0_req); ++ for (i = 0; i < FOTG210_MAX_NUM_EP; i++) ++ kfree(fotg210->ep[i]); ++ kfree(fotg210); ++ ++ return 0; ++} ++ ++static int fotg210_udc_probe(struct platform_device *pdev) ++{ ++ struct resource *res, *ires; ++ struct fotg210_udc *fotg210 = NULL; ++ struct fotg210_ep *_ep[FOTG210_MAX_NUM_EP]; ++ int ret = 0; ++ int i; ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ if (!res) { ++ pr_err("platform_get_resource error.\n"); ++ return -ENODEV; ++ } ++ ++ ires = platform_get_resource(pdev, IORESOURCE_IRQ, 0); ++ if (!ires) { ++ pr_err("platform_get_resource IORESOURCE_IRQ error.\n"); ++ return -ENODEV; ++ } ++ ++ ret = -ENOMEM; ++ ++ /* initialize udc */ ++ fotg210 = kzalloc(sizeof(struct fotg210_udc), GFP_KERNEL); ++ if (fotg210 == NULL) ++ goto err; ++ ++ for (i = 0; i < FOTG210_MAX_NUM_EP; i++) { ++ _ep[i] = kzalloc(sizeof(struct fotg210_ep), GFP_KERNEL); ++ if (_ep[i] == NULL) ++ goto err_alloc; ++ fotg210->ep[i] = _ep[i]; ++ } ++ ++ fotg210->reg = ioremap(res->start, resource_size(res)); ++ if (fotg210->reg == NULL) { ++ pr_err("ioremap error.\n"); ++ goto err_alloc; ++ } ++ ++ spin_lock_init(&fotg210->lock); ++ ++ platform_set_drvdata(pdev, fotg210); ++ ++ fotg210->gadget.ops = &fotg210_gadget_ops; ++ ++ fotg210->gadget.max_speed = USB_SPEED_HIGH; ++ fotg210->gadget.dev.parent = &pdev->dev; ++ fotg210->gadget.dev.dma_mask = pdev->dev.dma_mask; ++ fotg210->gadget.name = udc_name; ++ ++ INIT_LIST_HEAD(&fotg210->gadget.ep_list); ++ ++ for (i = 0; i < FOTG210_MAX_NUM_EP; i++) { ++ struct fotg210_ep *ep = fotg210->ep[i]; ++ ++ if (i) { ++ INIT_LIST_HEAD(&fotg210->ep[i]->ep.ep_list); ++ list_add_tail(&fotg210->ep[i]->ep.ep_list, ++ &fotg210->gadget.ep_list); ++ } ++ ep->fotg210 = fotg210; ++ INIT_LIST_HEAD(&ep->queue); ++ ep->ep.name = fotg210_ep_name[i]; ++ ep->ep.ops = &fotg210_ep_ops; ++ usb_ep_set_maxpacket_limit(&ep->ep, (unsigned short) ~0); ++ ++ if (i == 0) { ++ ep->ep.caps.type_control = true; ++ } else { ++ ep->ep.caps.type_iso = true; ++ ep->ep.caps.type_bulk = true; ++ ep->ep.caps.type_int = true; ++ } ++ ++ ep->ep.caps.dir_in = true; ++ ep->ep.caps.dir_out = true; ++ } ++ usb_ep_set_maxpacket_limit(&fotg210->ep[0]->ep, 0x40); ++ fotg210->gadget.ep0 = &fotg210->ep[0]->ep; ++ INIT_LIST_HEAD(&fotg210->gadget.ep0->ep_list); ++ ++ fotg210->ep0_req = fotg210_ep_alloc_request(&fotg210->ep[0]->ep, ++ GFP_KERNEL); ++ if (fotg210->ep0_req == NULL) ++ goto err_map; ++ ++ fotg210->ep0_req->complete = fotg210_ep0_complete; ++ ++ fotg210_init(fotg210); ++ ++ fotg210_disable_unplug(fotg210); ++ ++ ret = request_irq(ires->start, fotg210_irq, IRQF_SHARED, ++ udc_name, fotg210); ++ if (ret < 0) { ++ pr_err("request_irq error (%d)\n", ret); ++ goto err_req; ++ } ++ ++ ret = usb_add_gadget_udc(&pdev->dev, &fotg210->gadget); ++ if (ret) ++ goto err_add_udc; ++ ++ dev_info(&pdev->dev, "version %s\n", DRIVER_VERSION); ++ ++ return 0; ++ ++err_add_udc: ++ free_irq(ires->start, fotg210); ++ ++err_req: ++ fotg210_ep_free_request(&fotg210->ep[0]->ep, fotg210->ep0_req); ++ ++err_map: ++ iounmap(fotg210->reg); ++ ++err_alloc: ++ for (i = 0; i < FOTG210_MAX_NUM_EP; i++) ++ kfree(fotg210->ep[i]); ++ kfree(fotg210); ++ ++err: ++ return ret; ++} ++ ++static struct platform_driver fotg210_driver = { ++ .driver = { ++ .name = udc_name, ++ }, ++ .probe = fotg210_udc_probe, ++ .remove = fotg210_udc_remove, ++}; ++ ++module_platform_driver(fotg210_driver); ++ ++MODULE_AUTHOR("Yuan-Hsin Chen, Feng-Hsin Chiang "); ++MODULE_LICENSE("GPL"); ++MODULE_DESCRIPTION(DRIVER_DESC); +--- a/drivers/usb/gadget/udc/Kconfig ++++ b/drivers/usb/gadget/udc/Kconfig +@@ -108,17 +108,6 @@ config USB_FUSB300 + help + Faraday usb device controller FUSB300 driver + +-config USB_FOTG210_UDC +- depends on HAS_DMA +- tristate "Faraday FOTG210 USB Peripheral Controller" +- help +- Faraday USB2.0 OTG controller which can be configured as +- high speed or full speed USB device. This driver supppors +- Bulk Transfer so far. +- +- Say "y" to link the driver statically, or "m" to build a +- dynamically linked module called "fotg210_udc". +- + config USB_GR_UDC + tristate "Aeroflex Gaisler GRUSBDC USB Peripheral Controller Driver" + depends on HAS_DMA +--- a/drivers/usb/gadget/udc/Makefile ++++ b/drivers/usb/gadget/udc/Makefile +@@ -34,7 +34,6 @@ obj-$(CONFIG_USB_EG20T) += pch_udc.o + obj-$(CONFIG_USB_MV_UDC) += mv_udc.o + mv_udc-y := mv_udc_core.o + obj-$(CONFIG_USB_FUSB300) += fusb300_udc.o +-obj-$(CONFIG_USB_FOTG210_UDC) += fotg210-udc.o + obj-$(CONFIG_USB_MV_U3D) += mv_u3d_core.o + obj-$(CONFIG_USB_GR_UDC) += gr_udc.o + obj-$(CONFIG_USB_GADGET_XILINX) += udc-xilinx.o +--- a/drivers/usb/host/Kconfig ++++ b/drivers/usb/host/Kconfig +@@ -389,17 +389,6 @@ config USB_ISP1362_HCD + To compile this driver as a module, choose M here: the + module will be called isp1362-hcd. + +-config USB_FOTG210_HCD +- tristate "FOTG210 HCD support" +- depends on USB && HAS_DMA && HAS_IOMEM +- help +- Faraday FOTG210 is an OTG controller which can be configured as +- an USB2.0 host. It is designed to meet USB2.0 EHCI specification +- with minor modification. +- +- To compile this driver as a module, choose M here: the +- module will be called fotg210-hcd. +- + config USB_MAX3421_HCD + tristate "MAX3421 HCD (USB-over-SPI) support" + depends on USB && SPI +--- a/drivers/usb/host/Makefile ++++ b/drivers/usb/host/Makefile +@@ -84,6 +84,5 @@ obj-$(CONFIG_USB_EHCI_FSL) += ehci-fsl.o + obj-$(CONFIG_USB_EHCI_MV) += ehci-mv.o + obj-$(CONFIG_USB_HCD_BCMA) += bcma-hcd.o + obj-$(CONFIG_USB_HCD_SSB) += ssb-hcd.o +-obj-$(CONFIG_USB_FOTG210_HCD) += fotg210-hcd.o + obj-$(CONFIG_USB_MAX3421_HCD) += max3421-hcd.o + obj-$(CONFIG_USB_XEN_HCD) += xen-hcd.o +--- /dev/null ++++ b/drivers/usb/fotg210/fotg210-hcd.h +@@ -0,0 +1,688 @@ ++/* SPDX-License-Identifier: GPL-2.0 */ ++#ifndef __LINUX_FOTG210_H ++#define __LINUX_FOTG210_H ++ ++#include ++ ++/* definitions used for the EHCI driver */ ++ ++/* ++ * __hc32 and __hc16 are "Host Controller" types, they may be equivalent to ++ * __leXX (normally) or __beXX (given FOTG210_BIG_ENDIAN_DESC), depending on ++ * the host controller implementation. ++ * ++ * To facilitate the strongest possible byte-order checking from "sparse" ++ * and so on, we use __leXX unless that's not practical. ++ */ ++#define __hc32 __le32 ++#define __hc16 __le16 ++ ++/* statistics can be kept for tuning/monitoring */ ++struct fotg210_stats { ++ /* irq usage */ ++ unsigned long normal; ++ unsigned long error; ++ unsigned long iaa; ++ unsigned long lost_iaa; ++ ++ /* termination of urbs from core */ ++ unsigned long complete; ++ unsigned long unlink; ++}; ++ ++/* fotg210_hcd->lock guards shared data against other CPUs: ++ * fotg210_hcd: async, unlink, periodic (and shadow), ... ++ * usb_host_endpoint: hcpriv ++ * fotg210_qh: qh_next, qtd_list ++ * fotg210_qtd: qtd_list ++ * ++ * Also, hold this lock when talking to HC registers or ++ * when updating hw_* fields in shared qh/qtd/... structures. ++ */ ++ ++#define FOTG210_MAX_ROOT_PORTS 1 /* see HCS_N_PORTS */ ++ ++/* ++ * fotg210_rh_state values of FOTG210_RH_RUNNING or above mean that the ++ * controller may be doing DMA. Lower values mean there's no DMA. ++ */ ++enum fotg210_rh_state { ++ FOTG210_RH_HALTED, ++ FOTG210_RH_SUSPENDED, ++ FOTG210_RH_RUNNING, ++ FOTG210_RH_STOPPING ++}; ++ ++/* ++ * Timer events, ordered by increasing delay length. ++ * Always update event_delays_ns[] and event_handlers[] (defined in ++ * ehci-timer.c) in parallel with this list. ++ */ ++enum fotg210_hrtimer_event { ++ FOTG210_HRTIMER_POLL_ASS, /* Poll for async schedule off */ ++ FOTG210_HRTIMER_POLL_PSS, /* Poll for periodic schedule off */ ++ FOTG210_HRTIMER_POLL_DEAD, /* Wait for dead controller to stop */ ++ FOTG210_HRTIMER_UNLINK_INTR, /* Wait for interrupt QH unlink */ ++ FOTG210_HRTIMER_FREE_ITDS, /* Wait for unused iTDs and siTDs */ ++ FOTG210_HRTIMER_ASYNC_UNLINKS, /* Unlink empty async QHs */ ++ FOTG210_HRTIMER_IAA_WATCHDOG, /* Handle lost IAA interrupts */ ++ FOTG210_HRTIMER_DISABLE_PERIODIC, /* Wait to disable periodic sched */ ++ FOTG210_HRTIMER_DISABLE_ASYNC, /* Wait to disable async sched */ ++ FOTG210_HRTIMER_IO_WATCHDOG, /* Check for missing IRQs */ ++ FOTG210_HRTIMER_NUM_EVENTS /* Must come last */ ++}; ++#define FOTG210_HRTIMER_NO_EVENT 99 ++ ++struct fotg210_hcd { /* one per controller */ ++ /* timing support */ ++ enum fotg210_hrtimer_event next_hrtimer_event; ++ unsigned enabled_hrtimer_events; ++ ktime_t hr_timeouts[FOTG210_HRTIMER_NUM_EVENTS]; ++ struct hrtimer hrtimer; ++ ++ int PSS_poll_count; ++ int ASS_poll_count; ++ int died_poll_count; ++ ++ /* glue to PCI and HCD framework */ ++ struct fotg210_caps __iomem *caps; ++ struct fotg210_regs __iomem *regs; ++ struct ehci_dbg_port __iomem *debug; ++ ++ __u32 hcs_params; /* cached register copy */ ++ spinlock_t lock; ++ enum fotg210_rh_state rh_state; ++ ++ /* general schedule support */ ++ bool scanning:1; ++ bool need_rescan:1; ++ bool intr_unlinking:1; ++ bool async_unlinking:1; ++ bool shutdown:1; ++ struct fotg210_qh *qh_scan_next; ++ ++ /* async schedule support */ ++ struct fotg210_qh *async; ++ struct fotg210_qh *dummy; /* For AMD quirk use */ ++ struct fotg210_qh *async_unlink; ++ struct fotg210_qh *async_unlink_last; ++ struct fotg210_qh *async_iaa; ++ unsigned async_unlink_cycle; ++ unsigned async_count; /* async activity count */ ++ ++ /* periodic schedule support */ ++#define DEFAULT_I_TDPS 1024 /* some HCs can do less */ ++ unsigned periodic_size; ++ __hc32 *periodic; /* hw periodic table */ ++ dma_addr_t periodic_dma; ++ struct list_head intr_qh_list; ++ unsigned i_thresh; /* uframes HC might cache */ ++ ++ union fotg210_shadow *pshadow; /* mirror hw periodic table */ ++ struct fotg210_qh *intr_unlink; ++ struct fotg210_qh *intr_unlink_last; ++ unsigned intr_unlink_cycle; ++ unsigned now_frame; /* frame from HC hardware */ ++ unsigned next_frame; /* scan periodic, start here */ ++ unsigned intr_count; /* intr activity count */ ++ unsigned isoc_count; /* isoc activity count */ ++ unsigned periodic_count; /* periodic activity count */ ++ /* max periodic time per uframe */ ++ unsigned uframe_periodic_max; ++ ++ ++ /* list of itds completed while now_frame was still active */ ++ struct list_head cached_itd_list; ++ struct fotg210_itd *last_itd_to_free; ++ ++ /* per root hub port */ ++ unsigned long reset_done[FOTG210_MAX_ROOT_PORTS]; ++ ++ /* bit vectors (one bit per port) ++ * which ports were already suspended at the start of a bus suspend ++ */ ++ unsigned long bus_suspended; ++ ++ /* which ports are edicated to the companion controller */ ++ unsigned long companion_ports; ++ ++ /* which ports are owned by the companion during a bus suspend */ ++ unsigned long owned_ports; ++ ++ /* which ports have the change-suspend feature turned on */ ++ unsigned long port_c_suspend; ++ ++ /* which ports are suspended */ ++ unsigned long suspended_ports; ++ ++ /* which ports have started to resume */ ++ unsigned long resuming_ports; ++ ++ /* per-HC memory pools (could be per-bus, but ...) */ ++ struct dma_pool *qh_pool; /* qh per active urb */ ++ struct dma_pool *qtd_pool; /* one or more per qh */ ++ struct dma_pool *itd_pool; /* itd per iso urb */ ++ ++ unsigned random_frame; ++ unsigned long next_statechange; ++ ktime_t last_periodic_enable; ++ u32 command; ++ ++ /* SILICON QUIRKS */ ++ unsigned need_io_watchdog:1; ++ unsigned fs_i_thresh:1; /* Intel iso scheduling */ ++ ++ u8 sbrn; /* packed release number */ ++ ++ /* irq statistics */ ++#ifdef FOTG210_STATS ++ struct fotg210_stats stats; ++# define INCR(x) ((x)++) ++#else ++# define INCR(x) do {} while (0) ++#endif ++ ++ /* silicon clock */ ++ struct clk *pclk; ++}; ++ ++/* convert between an HCD pointer and the corresponding FOTG210_HCD */ ++static inline struct fotg210_hcd *hcd_to_fotg210(struct usb_hcd *hcd) ++{ ++ return (struct fotg210_hcd *)(hcd->hcd_priv); ++} ++static inline struct usb_hcd *fotg210_to_hcd(struct fotg210_hcd *fotg210) ++{ ++ return container_of((void *) fotg210, struct usb_hcd, hcd_priv); ++} ++ ++/*-------------------------------------------------------------------------*/ ++ ++/* EHCI register interface, corresponds to EHCI Revision 0.95 specification */ ++ ++/* Section 2.2 Host Controller Capability Registers */ ++struct fotg210_caps { ++ /* these fields are specified as 8 and 16 bit registers, ++ * but some hosts can't perform 8 or 16 bit PCI accesses. ++ * some hosts treat caplength and hciversion as parts of a 32-bit ++ * register, others treat them as two separate registers, this ++ * affects the memory map for big endian controllers. ++ */ ++ u32 hc_capbase; ++#define HC_LENGTH(fotg210, p) (0x00ff&((p) >> /* bits 7:0 / offset 00h */ \ ++ (fotg210_big_endian_capbase(fotg210) ? 24 : 0))) ++#define HC_VERSION(fotg210, p) (0xffff&((p) >> /* bits 31:16 / offset 02h */ \ ++ (fotg210_big_endian_capbase(fotg210) ? 0 : 16))) ++ u32 hcs_params; /* HCSPARAMS - offset 0x4 */ ++#define HCS_N_PORTS(p) (((p)>>0)&0xf) /* bits 3:0, ports on HC */ ++ ++ u32 hcc_params; /* HCCPARAMS - offset 0x8 */ ++#define HCC_CANPARK(p) ((p)&(1 << 2)) /* true: can park on async qh */ ++#define HCC_PGM_FRAMELISTLEN(p) ((p)&(1 << 1)) /* true: periodic_size changes*/ ++ u8 portroute[8]; /* nibbles for routing - offset 0xC */ ++}; ++ ++ ++/* Section 2.3 Host Controller Operational Registers */ ++struct fotg210_regs { ++ ++ /* USBCMD: offset 0x00 */ ++ u32 command; ++ ++/* EHCI 1.1 addendum */ ++/* 23:16 is r/w intr rate, in microframes; default "8" == 1/msec */ ++#define CMD_PARK (1<<11) /* enable "park" on async qh */ ++#define CMD_PARK_CNT(c) (((c)>>8)&3) /* how many transfers to park for */ ++#define CMD_IAAD (1<<6) /* "doorbell" interrupt async advance */ ++#define CMD_ASE (1<<5) /* async schedule enable */ ++#define CMD_PSE (1<<4) /* periodic schedule enable */ ++/* 3:2 is periodic frame list size */ ++#define CMD_RESET (1<<1) /* reset HC not bus */ ++#define CMD_RUN (1<<0) /* start/stop HC */ ++ ++ /* USBSTS: offset 0x04 */ ++ u32 status; ++#define STS_ASS (1<<15) /* Async Schedule Status */ ++#define STS_PSS (1<<14) /* Periodic Schedule Status */ ++#define STS_RECL (1<<13) /* Reclamation */ ++#define STS_HALT (1<<12) /* Not running (any reason) */ ++/* some bits reserved */ ++ /* these STS_* flags are also intr_enable bits (USBINTR) */ ++#define STS_IAA (1<<5) /* Interrupted on async advance */ ++#define STS_FATAL (1<<4) /* such as some PCI access errors */ ++#define STS_FLR (1<<3) /* frame list rolled over */ ++#define STS_PCD (1<<2) /* port change detect */ ++#define STS_ERR (1<<1) /* "error" completion (overflow, ...) */ ++#define STS_INT (1<<0) /* "normal" completion (short, ...) */ ++ ++ /* USBINTR: offset 0x08 */ ++ u32 intr_enable; ++ ++ /* FRINDEX: offset 0x0C */ ++ u32 frame_index; /* current microframe number */ ++ /* CTRLDSSEGMENT: offset 0x10 */ ++ u32 segment; /* address bits 63:32 if needed */ ++ /* PERIODICLISTBASE: offset 0x14 */ ++ u32 frame_list; /* points to periodic list */ ++ /* ASYNCLISTADDR: offset 0x18 */ ++ u32 async_next; /* address of next async queue head */ ++ ++ u32 reserved1; ++ /* PORTSC: offset 0x20 */ ++ u32 port_status; ++/* 31:23 reserved */ ++#define PORT_USB11(x) (((x)&(3<<10)) == (1<<10)) /* USB 1.1 device */ ++#define PORT_RESET (1<<8) /* reset port */ ++#define PORT_SUSPEND (1<<7) /* suspend port */ ++#define PORT_RESUME (1<<6) /* resume it */ ++#define PORT_PEC (1<<3) /* port enable change */ ++#define PORT_PE (1<<2) /* port enable */ ++#define PORT_CSC (1<<1) /* connect status change */ ++#define PORT_CONNECT (1<<0) /* device connected */ ++#define PORT_RWC_BITS (PORT_CSC | PORT_PEC) ++ u32 reserved2[19]; ++ ++ /* OTGCSR: offet 0x70 */ ++ u32 otgcsr; ++#define OTGCSR_HOST_SPD_TYP (3 << 22) ++#define OTGCSR_A_BUS_DROP (1 << 5) ++#define OTGCSR_A_BUS_REQ (1 << 4) ++ ++ /* OTGISR: offset 0x74 */ ++ u32 otgisr; ++#define OTGISR_OVC (1 << 10) ++ ++ u32 reserved3[15]; ++ ++ /* GMIR: offset 0xB4 */ ++ u32 gmir; ++#define GMIR_INT_POLARITY (1 << 3) /*Active High*/ ++#define GMIR_MHC_INT (1 << 2) ++#define GMIR_MOTG_INT (1 << 1) ++#define GMIR_MDEV_INT (1 << 0) ++}; ++ ++/*-------------------------------------------------------------------------*/ ++ ++#define QTD_NEXT(fotg210, dma) cpu_to_hc32(fotg210, (u32)dma) ++ ++/* ++ * EHCI Specification 0.95 Section 3.5 ++ * QTD: describe data transfer components (buffer, direction, ...) ++ * See Fig 3-6 "Queue Element Transfer Descriptor Block Diagram". ++ * ++ * These are associated only with "QH" (Queue Head) structures, ++ * used with control, bulk, and interrupt transfers. ++ */ ++struct fotg210_qtd { ++ /* first part defined by EHCI spec */ ++ __hc32 hw_next; /* see EHCI 3.5.1 */ ++ __hc32 hw_alt_next; /* see EHCI 3.5.2 */ ++ __hc32 hw_token; /* see EHCI 3.5.3 */ ++#define QTD_TOGGLE (1 << 31) /* data toggle */ ++#define QTD_LENGTH(tok) (((tok)>>16) & 0x7fff) ++#define QTD_IOC (1 << 15) /* interrupt on complete */ ++#define QTD_CERR(tok) (((tok)>>10) & 0x3) ++#define QTD_PID(tok) (((tok)>>8) & 0x3) ++#define QTD_STS_ACTIVE (1 << 7) /* HC may execute this */ ++#define QTD_STS_HALT (1 << 6) /* halted on error */ ++#define QTD_STS_DBE (1 << 5) /* data buffer error (in HC) */ ++#define QTD_STS_BABBLE (1 << 4) /* device was babbling (qtd halted) */ ++#define QTD_STS_XACT (1 << 3) /* device gave illegal response */ ++#define QTD_STS_MMF (1 << 2) /* incomplete split transaction */ ++#define QTD_STS_STS (1 << 1) /* split transaction state */ ++#define QTD_STS_PING (1 << 0) /* issue PING? */ ++ ++#define ACTIVE_BIT(fotg210) cpu_to_hc32(fotg210, QTD_STS_ACTIVE) ++#define HALT_BIT(fotg210) cpu_to_hc32(fotg210, QTD_STS_HALT) ++#define STATUS_BIT(fotg210) cpu_to_hc32(fotg210, QTD_STS_STS) ++ ++ __hc32 hw_buf[5]; /* see EHCI 3.5.4 */ ++ __hc32 hw_buf_hi[5]; /* Appendix B */ ++ ++ /* the rest is HCD-private */ ++ dma_addr_t qtd_dma; /* qtd address */ ++ struct list_head qtd_list; /* sw qtd list */ ++ struct urb *urb; /* qtd's urb */ ++ size_t length; /* length of buffer */ ++} __aligned(32); ++ ++/* mask NakCnt+T in qh->hw_alt_next */ ++#define QTD_MASK(fotg210) cpu_to_hc32(fotg210, ~0x1f) ++ ++#define IS_SHORT_READ(token) (QTD_LENGTH(token) != 0 && QTD_PID(token) == 1) ++ ++/*-------------------------------------------------------------------------*/ ++ ++/* type tag from {qh,itd,fstn}->hw_next */ ++#define Q_NEXT_TYPE(fotg210, dma) ((dma) & cpu_to_hc32(fotg210, 3 << 1)) ++ ++/* ++ * Now the following defines are not converted using the ++ * cpu_to_le32() macro anymore, since we have to support ++ * "dynamic" switching between be and le support, so that the driver ++ * can be used on one system with SoC EHCI controller using big-endian ++ * descriptors as well as a normal little-endian PCI EHCI controller. ++ */ ++/* values for that type tag */ ++#define Q_TYPE_ITD (0 << 1) ++#define Q_TYPE_QH (1 << 1) ++#define Q_TYPE_SITD (2 << 1) ++#define Q_TYPE_FSTN (3 << 1) ++ ++/* next async queue entry, or pointer to interrupt/periodic QH */ ++#define QH_NEXT(fotg210, dma) \ ++ (cpu_to_hc32(fotg210, (((u32)dma)&~0x01f)|Q_TYPE_QH)) ++ ++/* for periodic/async schedules and qtd lists, mark end of list */ ++#define FOTG210_LIST_END(fotg210) \ ++ cpu_to_hc32(fotg210, 1) /* "null pointer" to hw */ ++ ++/* ++ * Entries in periodic shadow table are pointers to one of four kinds ++ * of data structure. That's dictated by the hardware; a type tag is ++ * encoded in the low bits of the hardware's periodic schedule. Use ++ * Q_NEXT_TYPE to get the tag. ++ * ++ * For entries in the async schedule, the type tag always says "qh". ++ */ ++union fotg210_shadow { ++ struct fotg210_qh *qh; /* Q_TYPE_QH */ ++ struct fotg210_itd *itd; /* Q_TYPE_ITD */ ++ struct fotg210_fstn *fstn; /* Q_TYPE_FSTN */ ++ __hc32 *hw_next; /* (all types) */ ++ void *ptr; ++}; ++ ++/*-------------------------------------------------------------------------*/ ++ ++/* ++ * EHCI Specification 0.95 Section 3.6 ++ * QH: describes control/bulk/interrupt endpoints ++ * See Fig 3-7 "Queue Head Structure Layout". ++ * ++ * These appear in both the async and (for interrupt) periodic schedules. ++ */ ++ ++/* first part defined by EHCI spec */ ++struct fotg210_qh_hw { ++ __hc32 hw_next; /* see EHCI 3.6.1 */ ++ __hc32 hw_info1; /* see EHCI 3.6.2 */ ++#define QH_CONTROL_EP (1 << 27) /* FS/LS control endpoint */ ++#define QH_HEAD (1 << 15) /* Head of async reclamation list */ ++#define QH_TOGGLE_CTL (1 << 14) /* Data toggle control */ ++#define QH_HIGH_SPEED (2 << 12) /* Endpoint speed */ ++#define QH_LOW_SPEED (1 << 12) ++#define QH_FULL_SPEED (0 << 12) ++#define QH_INACTIVATE (1 << 7) /* Inactivate on next transaction */ ++ __hc32 hw_info2; /* see EHCI 3.6.2 */ ++#define QH_SMASK 0x000000ff ++#define QH_CMASK 0x0000ff00 ++#define QH_HUBADDR 0x007f0000 ++#define QH_HUBPORT 0x3f800000 ++#define QH_MULT 0xc0000000 ++ __hc32 hw_current; /* qtd list - see EHCI 3.6.4 */ ++ ++ /* qtd overlay (hardware parts of a struct fotg210_qtd) */ ++ __hc32 hw_qtd_next; ++ __hc32 hw_alt_next; ++ __hc32 hw_token; ++ __hc32 hw_buf[5]; ++ __hc32 hw_buf_hi[5]; ++} __aligned(32); ++ ++struct fotg210_qh { ++ struct fotg210_qh_hw *hw; /* Must come first */ ++ /* the rest is HCD-private */ ++ dma_addr_t qh_dma; /* address of qh */ ++ union fotg210_shadow qh_next; /* ptr to qh; or periodic */ ++ struct list_head qtd_list; /* sw qtd list */ ++ struct list_head intr_node; /* list of intr QHs */ ++ struct fotg210_qtd *dummy; ++ struct fotg210_qh *unlink_next; /* next on unlink list */ ++ ++ unsigned unlink_cycle; ++ ++ u8 needs_rescan; /* Dequeue during giveback */ ++ u8 qh_state; ++#define QH_STATE_LINKED 1 /* HC sees this */ ++#define QH_STATE_UNLINK 2 /* HC may still see this */ ++#define QH_STATE_IDLE 3 /* HC doesn't see this */ ++#define QH_STATE_UNLINK_WAIT 4 /* LINKED and on unlink q */ ++#define QH_STATE_COMPLETING 5 /* don't touch token.HALT */ ++ ++ u8 xacterrs; /* XactErr retry counter */ ++#define QH_XACTERR_MAX 32 /* XactErr retry limit */ ++ ++ /* periodic schedule info */ ++ u8 usecs; /* intr bandwidth */ ++ u8 gap_uf; /* uframes split/csplit gap */ ++ u8 c_usecs; /* ... split completion bw */ ++ u16 tt_usecs; /* tt downstream bandwidth */ ++ unsigned short period; /* polling interval */ ++ unsigned short start; /* where polling starts */ ++#define NO_FRAME ((unsigned short)~0) /* pick new start */ ++ ++ struct usb_device *dev; /* access to TT */ ++ unsigned is_out:1; /* bulk or intr OUT */ ++ unsigned clearing_tt:1; /* Clear-TT-Buf in progress */ ++}; ++ ++/*-------------------------------------------------------------------------*/ ++ ++/* description of one iso transaction (up to 3 KB data if highspeed) */ ++struct fotg210_iso_packet { ++ /* These will be copied to iTD when scheduling */ ++ u64 bufp; /* itd->hw_bufp{,_hi}[pg] |= */ ++ __hc32 transaction; /* itd->hw_transaction[i] |= */ ++ u8 cross; /* buf crosses pages */ ++ /* for full speed OUT splits */ ++ u32 buf1; ++}; ++ ++/* temporary schedule data for packets from iso urbs (both speeds) ++ * each packet is one logical usb transaction to the device (not TT), ++ * beginning at stream->next_uframe ++ */ ++struct fotg210_iso_sched { ++ struct list_head td_list; ++ unsigned span; ++ struct fotg210_iso_packet packet[]; ++}; ++ ++/* ++ * fotg210_iso_stream - groups all (s)itds for this endpoint. ++ * acts like a qh would, if EHCI had them for ISO. ++ */ ++struct fotg210_iso_stream { ++ /* first field matches fotg210_hq, but is NULL */ ++ struct fotg210_qh_hw *hw; ++ ++ u8 bEndpointAddress; ++ u8 highspeed; ++ struct list_head td_list; /* queued itds */ ++ struct list_head free_list; /* list of unused itds */ ++ struct usb_device *udev; ++ struct usb_host_endpoint *ep; ++ ++ /* output of (re)scheduling */ ++ int next_uframe; ++ __hc32 splits; ++ ++ /* the rest is derived from the endpoint descriptor, ++ * trusting urb->interval == f(epdesc->bInterval) and ++ * including the extra info for hw_bufp[0..2] ++ */ ++ u8 usecs, c_usecs; ++ u16 interval; ++ u16 tt_usecs; ++ u16 maxp; ++ u16 raw_mask; ++ unsigned bandwidth; ++ ++ /* This is used to initialize iTD's hw_bufp fields */ ++ __hc32 buf0; ++ __hc32 buf1; ++ __hc32 buf2; ++ ++ /* this is used to initialize sITD's tt info */ ++ __hc32 address; ++}; ++ ++/*-------------------------------------------------------------------------*/ ++ ++/* ++ * EHCI Specification 0.95 Section 3.3 ++ * Fig 3-4 "Isochronous Transaction Descriptor (iTD)" ++ * ++ * Schedule records for high speed iso xfers ++ */ ++struct fotg210_itd { ++ /* first part defined by EHCI spec */ ++ __hc32 hw_next; /* see EHCI 3.3.1 */ ++ __hc32 hw_transaction[8]; /* see EHCI 3.3.2 */ ++#define FOTG210_ISOC_ACTIVE (1<<31) /* activate transfer this slot */ ++#define FOTG210_ISOC_BUF_ERR (1<<30) /* Data buffer error */ ++#define FOTG210_ISOC_BABBLE (1<<29) /* babble detected */ ++#define FOTG210_ISOC_XACTERR (1<<28) /* XactErr - transaction error */ ++#define FOTG210_ITD_LENGTH(tok) (((tok)>>16) & 0x0fff) ++#define FOTG210_ITD_IOC (1 << 15) /* interrupt on complete */ ++ ++#define ITD_ACTIVE(fotg210) cpu_to_hc32(fotg210, FOTG210_ISOC_ACTIVE) ++ ++ __hc32 hw_bufp[7]; /* see EHCI 3.3.3 */ ++ __hc32 hw_bufp_hi[7]; /* Appendix B */ ++ ++ /* the rest is HCD-private */ ++ dma_addr_t itd_dma; /* for this itd */ ++ union fotg210_shadow itd_next; /* ptr to periodic q entry */ ++ ++ struct urb *urb; ++ struct fotg210_iso_stream *stream; /* endpoint's queue */ ++ struct list_head itd_list; /* list of stream's itds */ ++ ++ /* any/all hw_transactions here may be used by that urb */ ++ unsigned frame; /* where scheduled */ ++ unsigned pg; ++ unsigned index[8]; /* in urb->iso_frame_desc */ ++} __aligned(32); ++ ++/*-------------------------------------------------------------------------*/ ++ ++/* ++ * EHCI Specification 0.96 Section 3.7 ++ * Periodic Frame Span Traversal Node (FSTN) ++ * ++ * Manages split interrupt transactions (using TT) that span frame boundaries ++ * into uframes 0/1; see 4.12.2.2. In those uframes, a "save place" FSTN ++ * makes the HC jump (back) to a QH to scan for fs/ls QH completions until ++ * it hits a "restore" FSTN; then it returns to finish other uframe 0/1 work. ++ */ ++struct fotg210_fstn { ++ __hc32 hw_next; /* any periodic q entry */ ++ __hc32 hw_prev; /* qh or FOTG210_LIST_END */ ++ ++ /* the rest is HCD-private */ ++ dma_addr_t fstn_dma; ++ union fotg210_shadow fstn_next; /* ptr to periodic q entry */ ++} __aligned(32); ++ ++/*-------------------------------------------------------------------------*/ ++ ++/* Prepare the PORTSC wakeup flags during controller suspend/resume */ ++ ++#define fotg210_prepare_ports_for_controller_suspend(fotg210, do_wakeup) \ ++ fotg210_adjust_port_wakeup_flags(fotg210, true, do_wakeup) ++ ++#define fotg210_prepare_ports_for_controller_resume(fotg210) \ ++ fotg210_adjust_port_wakeup_flags(fotg210, false, false) ++ ++/*-------------------------------------------------------------------------*/ ++ ++/* ++ * Some EHCI controllers have a Transaction Translator built into the ++ * root hub. This is a non-standard feature. Each controller will need ++ * to add code to the following inline functions, and call them as ++ * needed (mostly in root hub code). ++ */ ++ ++static inline unsigned int ++fotg210_get_speed(struct fotg210_hcd *fotg210, unsigned int portsc) ++{ ++ return (readl(&fotg210->regs->otgcsr) ++ & OTGCSR_HOST_SPD_TYP) >> 22; ++} ++ ++/* Returns the speed of a device attached to a port on the root hub. */ ++static inline unsigned int ++fotg210_port_speed(struct fotg210_hcd *fotg210, unsigned int portsc) ++{ ++ switch (fotg210_get_speed(fotg210, portsc)) { ++ case 0: ++ return 0; ++ case 1: ++ return USB_PORT_STAT_LOW_SPEED; ++ case 2: ++ default: ++ return USB_PORT_STAT_HIGH_SPEED; ++ } ++} ++ ++/*-------------------------------------------------------------------------*/ ++ ++#define fotg210_has_fsl_portno_bug(e) (0) ++ ++/* ++ * While most USB host controllers implement their registers in ++ * little-endian format, a minority (celleb companion chip) implement ++ * them in big endian format. ++ * ++ * This attempts to support either format at compile time without a ++ * runtime penalty, or both formats with the additional overhead ++ * of checking a flag bit. ++ * ++ */ ++ ++#define fotg210_big_endian_mmio(e) 0 ++#define fotg210_big_endian_capbase(e) 0 ++ ++static inline unsigned int fotg210_readl(const struct fotg210_hcd *fotg210, ++ __u32 __iomem *regs) ++{ ++ return readl(regs); ++} ++ ++static inline void fotg210_writel(const struct fotg210_hcd *fotg210, ++ const unsigned int val, __u32 __iomem *regs) ++{ ++ writel(val, regs); ++} ++ ++/* cpu to fotg210 */ ++static inline __hc32 cpu_to_hc32(const struct fotg210_hcd *fotg210, const u32 x) ++{ ++ return cpu_to_le32(x); ++} ++ ++/* fotg210 to cpu */ ++static inline u32 hc32_to_cpu(const struct fotg210_hcd *fotg210, const __hc32 x) ++{ ++ return le32_to_cpu(x); ++} ++ ++static inline u32 hc32_to_cpup(const struct fotg210_hcd *fotg210, ++ const __hc32 *x) ++{ ++ return le32_to_cpup(x); ++} ++ ++/*-------------------------------------------------------------------------*/ ++ ++static inline unsigned fotg210_read_frame_index(struct fotg210_hcd *fotg210) ++{ ++ return fotg210_readl(fotg210, &fotg210->regs->frame_index); ++} ++ ++/*-------------------------------------------------------------------------*/ ++ ++#endif /* __LINUX_FOTG210_H */ +--- /dev/null ++++ b/drivers/usb/fotg210/fotg210-udc.h +@@ -0,0 +1,249 @@ ++// SPDX-License-Identifier: GPL-2.0+ ++/* ++ * Faraday FOTG210 USB OTG controller ++ * ++ * Copyright (C) 2013 Faraday Technology Corporation ++ * Author: Yuan-Hsin Chen ++ */ ++ ++#include ++ ++#define FOTG210_MAX_NUM_EP 5 /* ep0...ep4 */ ++#define FOTG210_MAX_FIFO_NUM 4 /* fifo0...fifo4 */ ++ ++/* Global Mask of HC/OTG/DEV interrupt Register(0xC4) */ ++#define FOTG210_GMIR 0xC4 ++#define GMIR_INT_POLARITY 0x8 /*Active High*/ ++#define GMIR_MHC_INT 0x4 ++#define GMIR_MOTG_INT 0x2 ++#define GMIR_MDEV_INT 0x1 ++ ++/* Device Main Control Register(0x100) */ ++#define FOTG210_DMCR 0x100 ++#define DMCR_HS_EN (1 << 6) ++#define DMCR_CHIP_EN (1 << 5) ++#define DMCR_SFRST (1 << 4) ++#define DMCR_GOSUSP (1 << 3) ++#define DMCR_GLINT_EN (1 << 2) ++#define DMCR_HALF_SPEED (1 << 1) ++#define DMCR_CAP_RMWAKUP (1 << 0) ++ ++/* Device Address Register(0x104) */ ++#define FOTG210_DAR 0x104 ++#define DAR_AFT_CONF (1 << 7) ++ ++/* Device Test Register(0x108) */ ++#define FOTG210_DTR 0x108 ++#define DTR_TST_CLRFF (1 << 0) ++ ++/* PHY Test Mode Selector register(0x114) */ ++#define FOTG210_PHYTMSR 0x114 ++#define PHYTMSR_TST_PKT (1 << 4) ++#define PHYTMSR_TST_SE0NAK (1 << 3) ++#define PHYTMSR_TST_KSTA (1 << 2) ++#define PHYTMSR_TST_JSTA (1 << 1) ++#define PHYTMSR_UNPLUG (1 << 0) ++ ++/* Cx configuration and FIFO Empty Status register(0x120) */ ++#define FOTG210_DCFESR 0x120 ++#define DCFESR_FIFO_EMPTY(fifo) (1 << 8 << (fifo)) ++#define DCFESR_CX_EMP (1 << 5) ++#define DCFESR_CX_CLR (1 << 3) ++#define DCFESR_CX_STL (1 << 2) ++#define DCFESR_TST_PKDONE (1 << 1) ++#define DCFESR_CX_DONE (1 << 0) ++ ++/* Device IDLE Counter Register(0x124) */ ++#define FOTG210_DICR 0x124 ++ ++/* Device Mask of Interrupt Group Register (0x130) */ ++#define FOTG210_DMIGR 0x130 ++#define DMIGR_MINT_G0 (1 << 0) ++ ++/* Device Mask of Interrupt Source Group 0(0x134) */ ++#define FOTG210_DMISGR0 0x134 ++#define DMISGR0_MCX_COMEND (1 << 3) ++#define DMISGR0_MCX_OUT_INT (1 << 2) ++#define DMISGR0_MCX_IN_INT (1 << 1) ++#define DMISGR0_MCX_SETUP_INT (1 << 0) ++ ++/* Device Mask of Interrupt Source Group 1 Register(0x138)*/ ++#define FOTG210_DMISGR1 0x138 ++#define DMISGR1_MF3_IN_INT (1 << 19) ++#define DMISGR1_MF2_IN_INT (1 << 18) ++#define DMISGR1_MF1_IN_INT (1 << 17) ++#define DMISGR1_MF0_IN_INT (1 << 16) ++#define DMISGR1_MF_IN_INT(fifo) (1 << (16 + (fifo))) ++#define DMISGR1_MF3_SPK_INT (1 << 7) ++#define DMISGR1_MF3_OUT_INT (1 << 6) ++#define DMISGR1_MF2_SPK_INT (1 << 5) ++#define DMISGR1_MF2_OUT_INT (1 << 4) ++#define DMISGR1_MF1_SPK_INT (1 << 3) ++#define DMISGR1_MF1_OUT_INT (1 << 2) ++#define DMISGR1_MF0_SPK_INT (1 << 1) ++#define DMISGR1_MF0_OUT_INT (1 << 0) ++#define DMISGR1_MF_OUTSPK_INT(fifo) (0x3 << (fifo) * 2) ++ ++/* Device Mask of Interrupt Source Group 2 Register (0x13C) */ ++#define FOTG210_DMISGR2 0x13C ++#define DMISGR2_MDMA_ERROR (1 << 8) ++#define DMISGR2_MDMA_CMPLT (1 << 7) ++ ++/* Device Interrupt group Register (0x140) */ ++#define FOTG210_DIGR 0x140 ++#define DIGR_INT_G2 (1 << 2) ++#define DIGR_INT_G1 (1 << 1) ++#define DIGR_INT_G0 (1 << 0) ++ ++/* Device Interrupt Source Group 0 Register (0x144) */ ++#define FOTG210_DISGR0 0x144 ++#define DISGR0_CX_COMABT_INT (1 << 5) ++#define DISGR0_CX_COMFAIL_INT (1 << 4) ++#define DISGR0_CX_COMEND_INT (1 << 3) ++#define DISGR0_CX_OUT_INT (1 << 2) ++#define DISGR0_CX_IN_INT (1 << 1) ++#define DISGR0_CX_SETUP_INT (1 << 0) ++ ++/* Device Interrupt Source Group 1 Register (0x148) */ ++#define FOTG210_DISGR1 0x148 ++#define DISGR1_OUT_INT(fifo) (1 << ((fifo) * 2)) ++#define DISGR1_SPK_INT(fifo) (1 << 1 << ((fifo) * 2)) ++#define DISGR1_IN_INT(fifo) (1 << 16 << (fifo)) ++ ++/* Device Interrupt Source Group 2 Register (0x14C) */ ++#define FOTG210_DISGR2 0x14C ++#define DISGR2_DMA_ERROR (1 << 8) ++#define DISGR2_DMA_CMPLT (1 << 7) ++#define DISGR2_RX0BYTE_INT (1 << 6) ++#define DISGR2_TX0BYTE_INT (1 << 5) ++#define DISGR2_ISO_SEQ_ABORT_INT (1 << 4) ++#define DISGR2_ISO_SEQ_ERR_INT (1 << 3) ++#define DISGR2_RESM_INT (1 << 2) ++#define DISGR2_SUSP_INT (1 << 1) ++#define DISGR2_USBRST_INT (1 << 0) ++ ++/* Device Receive Zero-Length Data Packet Register (0x150)*/ ++#define FOTG210_RX0BYTE 0x150 ++#define RX0BYTE_EP8 (1 << 7) ++#define RX0BYTE_EP7 (1 << 6) ++#define RX0BYTE_EP6 (1 << 5) ++#define RX0BYTE_EP5 (1 << 4) ++#define RX0BYTE_EP4 (1 << 3) ++#define RX0BYTE_EP3 (1 << 2) ++#define RX0BYTE_EP2 (1 << 1) ++#define RX0BYTE_EP1 (1 << 0) ++ ++/* Device Transfer Zero-Length Data Packet Register (0x154)*/ ++#define FOTG210_TX0BYTE 0x154 ++#define TX0BYTE_EP8 (1 << 7) ++#define TX0BYTE_EP7 (1 << 6) ++#define TX0BYTE_EP6 (1 << 5) ++#define TX0BYTE_EP5 (1 << 4) ++#define TX0BYTE_EP4 (1 << 3) ++#define TX0BYTE_EP3 (1 << 2) ++#define TX0BYTE_EP2 (1 << 1) ++#define TX0BYTE_EP1 (1 << 0) ++ ++/* Device IN Endpoint x MaxPacketSize Register(0x160+4*(x-1)) */ ++#define FOTG210_INEPMPSR(ep) (0x160 + 4 * ((ep) - 1)) ++#define INOUTEPMPSR_MPS(mps) ((mps) & 0x2FF) ++#define INOUTEPMPSR_STL_EP (1 << 11) ++#define INOUTEPMPSR_RESET_TSEQ (1 << 12) ++ ++/* Device OUT Endpoint x MaxPacketSize Register(0x180+4*(x-1)) */ ++#define FOTG210_OUTEPMPSR(ep) (0x180 + 4 * ((ep) - 1)) ++ ++/* Device Endpoint 1~4 Map Register (0x1A0) */ ++#define FOTG210_EPMAP 0x1A0 ++#define EPMAP_FIFONO(ep, dir) \ ++ ((((ep) - 1) << ((ep) - 1) * 8) << ((dir) ? 0 : 4)) ++#define EPMAP_FIFONOMSK(ep, dir) \ ++ ((3 << ((ep) - 1) * 8) << ((dir) ? 0 : 4)) ++ ++/* Device FIFO Map Register (0x1A8) */ ++#define FOTG210_FIFOMAP 0x1A8 ++#define FIFOMAP_DIROUT(fifo) (0x0 << 4 << (fifo) * 8) ++#define FIFOMAP_DIRIN(fifo) (0x1 << 4 << (fifo) * 8) ++#define FIFOMAP_BIDIR(fifo) (0x2 << 4 << (fifo) * 8) ++#define FIFOMAP_NA(fifo) (0x3 << 4 << (fifo) * 8) ++#define FIFOMAP_EPNO(ep) ((ep) << ((ep) - 1) * 8) ++#define FIFOMAP_EPNOMSK(ep) (0xF << ((ep) - 1) * 8) ++ ++/* Device FIFO Confuguration Register (0x1AC) */ ++#define FOTG210_FIFOCF 0x1AC ++#define FIFOCF_TYPE(type, fifo) ((type) << (fifo) * 8) ++#define FIFOCF_BLK_SIN(fifo) (0x0 << (fifo) * 8 << 2) ++#define FIFOCF_BLK_DUB(fifo) (0x1 << (fifo) * 8 << 2) ++#define FIFOCF_BLK_TRI(fifo) (0x2 << (fifo) * 8 << 2) ++#define FIFOCF_BLKSZ_512(fifo) (0x0 << (fifo) * 8 << 4) ++#define FIFOCF_BLKSZ_1024(fifo) (0x1 << (fifo) * 8 << 4) ++#define FIFOCF_FIFO_EN(fifo) (0x1 << (fifo) * 8 << 5) ++ ++/* Device FIFO n Instruction and Byte Count Register (0x1B0+4*n) */ ++#define FOTG210_FIBCR(fifo) (0x1B0 + (fifo) * 4) ++#define FIBCR_BCFX 0x7FF ++#define FIBCR_FFRST (1 << 12) ++ ++/* Device DMA Target FIFO Number Register (0x1C0) */ ++#define FOTG210_DMATFNR 0x1C0 ++#define DMATFNR_ACC_CXF (1 << 4) ++#define DMATFNR_ACC_F3 (1 << 3) ++#define DMATFNR_ACC_F2 (1 << 2) ++#define DMATFNR_ACC_F1 (1 << 1) ++#define DMATFNR_ACC_F0 (1 << 0) ++#define DMATFNR_ACC_FN(fifo) (1 << (fifo)) ++#define DMATFNR_DISDMA 0 ++ ++/* Device DMA Controller Parameter setting 1 Register (0x1C8) */ ++#define FOTG210_DMACPSR1 0x1C8 ++#define DMACPSR1_DMA_LEN(len) (((len) & 0xFFFF) << 8) ++#define DMACPSR1_DMA_ABORT (1 << 3) ++#define DMACPSR1_DMA_TYPE(dir_in) (((dir_in) ? 1 : 0) << 1) ++#define DMACPSR1_DMA_START (1 << 0) ++ ++/* Device DMA Controller Parameter setting 2 Register (0x1CC) */ ++#define FOTG210_DMACPSR2 0x1CC ++ ++/* Device DMA Controller Parameter setting 3 Register (0x1CC) */ ++#define FOTG210_CXPORT 0x1D0 ++ ++struct fotg210_request { ++ struct usb_request req; ++ struct list_head queue; ++}; ++ ++struct fotg210_ep { ++ struct usb_ep ep; ++ struct fotg210_udc *fotg210; ++ ++ struct list_head queue; ++ unsigned stall:1; ++ unsigned wedged:1; ++ unsigned use_dma:1; ++ ++ unsigned char epnum; ++ unsigned char type; ++ unsigned char dir_in; ++ unsigned int maxp; ++ const struct usb_endpoint_descriptor *desc; ++}; ++ ++struct fotg210_udc { ++ spinlock_t lock; /* protect the struct */ ++ void __iomem *reg; ++ ++ unsigned long irq_trigger; ++ ++ struct usb_gadget gadget; ++ struct usb_gadget_driver *driver; ++ ++ struct fotg210_ep *ep[FOTG210_MAX_NUM_EP]; ++ ++ struct usb_request *ep0_req; /* for internal request */ ++ __le16 ep0_data; ++ u8 ep0_dir; /* 0/0x80 out/in */ ++ ++ u8 reenum; /* if re-enumeration */ ++}; ++ ++#define gadget_to_fotg210(g) container_of((g), struct fotg210_udc, gadget) +--- a/drivers/usb/gadget/udc/fotg210.h ++++ /dev/null +@@ -1,249 +0,0 @@ +-// SPDX-License-Identifier: GPL-2.0+ +-/* +- * Faraday FOTG210 USB OTG controller +- * +- * Copyright (C) 2013 Faraday Technology Corporation +- * Author: Yuan-Hsin Chen +- */ +- +-#include +- +-#define FOTG210_MAX_NUM_EP 5 /* ep0...ep4 */ +-#define FOTG210_MAX_FIFO_NUM 4 /* fifo0...fifo4 */ +- +-/* Global Mask of HC/OTG/DEV interrupt Register(0xC4) */ +-#define FOTG210_GMIR 0xC4 +-#define GMIR_INT_POLARITY 0x8 /*Active High*/ +-#define GMIR_MHC_INT 0x4 +-#define GMIR_MOTG_INT 0x2 +-#define GMIR_MDEV_INT 0x1 +- +-/* Device Main Control Register(0x100) */ +-#define FOTG210_DMCR 0x100 +-#define DMCR_HS_EN (1 << 6) +-#define DMCR_CHIP_EN (1 << 5) +-#define DMCR_SFRST (1 << 4) +-#define DMCR_GOSUSP (1 << 3) +-#define DMCR_GLINT_EN (1 << 2) +-#define DMCR_HALF_SPEED (1 << 1) +-#define DMCR_CAP_RMWAKUP (1 << 0) +- +-/* Device Address Register(0x104) */ +-#define FOTG210_DAR 0x104 +-#define DAR_AFT_CONF (1 << 7) +- +-/* Device Test Register(0x108) */ +-#define FOTG210_DTR 0x108 +-#define DTR_TST_CLRFF (1 << 0) +- +-/* PHY Test Mode Selector register(0x114) */ +-#define FOTG210_PHYTMSR 0x114 +-#define PHYTMSR_TST_PKT (1 << 4) +-#define PHYTMSR_TST_SE0NAK (1 << 3) +-#define PHYTMSR_TST_KSTA (1 << 2) +-#define PHYTMSR_TST_JSTA (1 << 1) +-#define PHYTMSR_UNPLUG (1 << 0) +- +-/* Cx configuration and FIFO Empty Status register(0x120) */ +-#define FOTG210_DCFESR 0x120 +-#define DCFESR_FIFO_EMPTY(fifo) (1 << 8 << (fifo)) +-#define DCFESR_CX_EMP (1 << 5) +-#define DCFESR_CX_CLR (1 << 3) +-#define DCFESR_CX_STL (1 << 2) +-#define DCFESR_TST_PKDONE (1 << 1) +-#define DCFESR_CX_DONE (1 << 0) +- +-/* Device IDLE Counter Register(0x124) */ +-#define FOTG210_DICR 0x124 +- +-/* Device Mask of Interrupt Group Register (0x130) */ +-#define FOTG210_DMIGR 0x130 +-#define DMIGR_MINT_G0 (1 << 0) +- +-/* Device Mask of Interrupt Source Group 0(0x134) */ +-#define FOTG210_DMISGR0 0x134 +-#define DMISGR0_MCX_COMEND (1 << 3) +-#define DMISGR0_MCX_OUT_INT (1 << 2) +-#define DMISGR0_MCX_IN_INT (1 << 1) +-#define DMISGR0_MCX_SETUP_INT (1 << 0) +- +-/* Device Mask of Interrupt Source Group 1 Register(0x138)*/ +-#define FOTG210_DMISGR1 0x138 +-#define DMISGR1_MF3_IN_INT (1 << 19) +-#define DMISGR1_MF2_IN_INT (1 << 18) +-#define DMISGR1_MF1_IN_INT (1 << 17) +-#define DMISGR1_MF0_IN_INT (1 << 16) +-#define DMISGR1_MF_IN_INT(fifo) (1 << (16 + (fifo))) +-#define DMISGR1_MF3_SPK_INT (1 << 7) +-#define DMISGR1_MF3_OUT_INT (1 << 6) +-#define DMISGR1_MF2_SPK_INT (1 << 5) +-#define DMISGR1_MF2_OUT_INT (1 << 4) +-#define DMISGR1_MF1_SPK_INT (1 << 3) +-#define DMISGR1_MF1_OUT_INT (1 << 2) +-#define DMISGR1_MF0_SPK_INT (1 << 1) +-#define DMISGR1_MF0_OUT_INT (1 << 0) +-#define DMISGR1_MF_OUTSPK_INT(fifo) (0x3 << (fifo) * 2) +- +-/* Device Mask of Interrupt Source Group 2 Register (0x13C) */ +-#define FOTG210_DMISGR2 0x13C +-#define DMISGR2_MDMA_ERROR (1 << 8) +-#define DMISGR2_MDMA_CMPLT (1 << 7) +- +-/* Device Interrupt group Register (0x140) */ +-#define FOTG210_DIGR 0x140 +-#define DIGR_INT_G2 (1 << 2) +-#define DIGR_INT_G1 (1 << 1) +-#define DIGR_INT_G0 (1 << 0) +- +-/* Device Interrupt Source Group 0 Register (0x144) */ +-#define FOTG210_DISGR0 0x144 +-#define DISGR0_CX_COMABT_INT (1 << 5) +-#define DISGR0_CX_COMFAIL_INT (1 << 4) +-#define DISGR0_CX_COMEND_INT (1 << 3) +-#define DISGR0_CX_OUT_INT (1 << 2) +-#define DISGR0_CX_IN_INT (1 << 1) +-#define DISGR0_CX_SETUP_INT (1 << 0) +- +-/* Device Interrupt Source Group 1 Register (0x148) */ +-#define FOTG210_DISGR1 0x148 +-#define DISGR1_OUT_INT(fifo) (1 << ((fifo) * 2)) +-#define DISGR1_SPK_INT(fifo) (1 << 1 << ((fifo) * 2)) +-#define DISGR1_IN_INT(fifo) (1 << 16 << (fifo)) +- +-/* Device Interrupt Source Group 2 Register (0x14C) */ +-#define FOTG210_DISGR2 0x14C +-#define DISGR2_DMA_ERROR (1 << 8) +-#define DISGR2_DMA_CMPLT (1 << 7) +-#define DISGR2_RX0BYTE_INT (1 << 6) +-#define DISGR2_TX0BYTE_INT (1 << 5) +-#define DISGR2_ISO_SEQ_ABORT_INT (1 << 4) +-#define DISGR2_ISO_SEQ_ERR_INT (1 << 3) +-#define DISGR2_RESM_INT (1 << 2) +-#define DISGR2_SUSP_INT (1 << 1) +-#define DISGR2_USBRST_INT (1 << 0) +- +-/* Device Receive Zero-Length Data Packet Register (0x150)*/ +-#define FOTG210_RX0BYTE 0x150 +-#define RX0BYTE_EP8 (1 << 7) +-#define RX0BYTE_EP7 (1 << 6) +-#define RX0BYTE_EP6 (1 << 5) +-#define RX0BYTE_EP5 (1 << 4) +-#define RX0BYTE_EP4 (1 << 3) +-#define RX0BYTE_EP3 (1 << 2) +-#define RX0BYTE_EP2 (1 << 1) +-#define RX0BYTE_EP1 (1 << 0) +- +-/* Device Transfer Zero-Length Data Packet Register (0x154)*/ +-#define FOTG210_TX0BYTE 0x154 +-#define TX0BYTE_EP8 (1 << 7) +-#define TX0BYTE_EP7 (1 << 6) +-#define TX0BYTE_EP6 (1 << 5) +-#define TX0BYTE_EP5 (1 << 4) +-#define TX0BYTE_EP4 (1 << 3) +-#define TX0BYTE_EP3 (1 << 2) +-#define TX0BYTE_EP2 (1 << 1) +-#define TX0BYTE_EP1 (1 << 0) +- +-/* Device IN Endpoint x MaxPacketSize Register(0x160+4*(x-1)) */ +-#define FOTG210_INEPMPSR(ep) (0x160 + 4 * ((ep) - 1)) +-#define INOUTEPMPSR_MPS(mps) ((mps) & 0x2FF) +-#define INOUTEPMPSR_STL_EP (1 << 11) +-#define INOUTEPMPSR_RESET_TSEQ (1 << 12) +- +-/* Device OUT Endpoint x MaxPacketSize Register(0x180+4*(x-1)) */ +-#define FOTG210_OUTEPMPSR(ep) (0x180 + 4 * ((ep) - 1)) +- +-/* Device Endpoint 1~4 Map Register (0x1A0) */ +-#define FOTG210_EPMAP 0x1A0 +-#define EPMAP_FIFONO(ep, dir) \ +- ((((ep) - 1) << ((ep) - 1) * 8) << ((dir) ? 0 : 4)) +-#define EPMAP_FIFONOMSK(ep, dir) \ +- ((3 << ((ep) - 1) * 8) << ((dir) ? 0 : 4)) +- +-/* Device FIFO Map Register (0x1A8) */ +-#define FOTG210_FIFOMAP 0x1A8 +-#define FIFOMAP_DIROUT(fifo) (0x0 << 4 << (fifo) * 8) +-#define FIFOMAP_DIRIN(fifo) (0x1 << 4 << (fifo) * 8) +-#define FIFOMAP_BIDIR(fifo) (0x2 << 4 << (fifo) * 8) +-#define FIFOMAP_NA(fifo) (0x3 << 4 << (fifo) * 8) +-#define FIFOMAP_EPNO(ep) ((ep) << ((ep) - 1) * 8) +-#define FIFOMAP_EPNOMSK(ep) (0xF << ((ep) - 1) * 8) +- +-/* Device FIFO Confuguration Register (0x1AC) */ +-#define FOTG210_FIFOCF 0x1AC +-#define FIFOCF_TYPE(type, fifo) ((type) << (fifo) * 8) +-#define FIFOCF_BLK_SIN(fifo) (0x0 << (fifo) * 8 << 2) +-#define FIFOCF_BLK_DUB(fifo) (0x1 << (fifo) * 8 << 2) +-#define FIFOCF_BLK_TRI(fifo) (0x2 << (fifo) * 8 << 2) +-#define FIFOCF_BLKSZ_512(fifo) (0x0 << (fifo) * 8 << 4) +-#define FIFOCF_BLKSZ_1024(fifo) (0x1 << (fifo) * 8 << 4) +-#define FIFOCF_FIFO_EN(fifo) (0x1 << (fifo) * 8 << 5) +- +-/* Device FIFO n Instruction and Byte Count Register (0x1B0+4*n) */ +-#define FOTG210_FIBCR(fifo) (0x1B0 + (fifo) * 4) +-#define FIBCR_BCFX 0x7FF +-#define FIBCR_FFRST (1 << 12) +- +-/* Device DMA Target FIFO Number Register (0x1C0) */ +-#define FOTG210_DMATFNR 0x1C0 +-#define DMATFNR_ACC_CXF (1 << 4) +-#define DMATFNR_ACC_F3 (1 << 3) +-#define DMATFNR_ACC_F2 (1 << 2) +-#define DMATFNR_ACC_F1 (1 << 1) +-#define DMATFNR_ACC_F0 (1 << 0) +-#define DMATFNR_ACC_FN(fifo) (1 << (fifo)) +-#define DMATFNR_DISDMA 0 +- +-/* Device DMA Controller Parameter setting 1 Register (0x1C8) */ +-#define FOTG210_DMACPSR1 0x1C8 +-#define DMACPSR1_DMA_LEN(len) (((len) & 0xFFFF) << 8) +-#define DMACPSR1_DMA_ABORT (1 << 3) +-#define DMACPSR1_DMA_TYPE(dir_in) (((dir_in) ? 1 : 0) << 1) +-#define DMACPSR1_DMA_START (1 << 0) +- +-/* Device DMA Controller Parameter setting 2 Register (0x1CC) */ +-#define FOTG210_DMACPSR2 0x1CC +- +-/* Device DMA Controller Parameter setting 3 Register (0x1CC) */ +-#define FOTG210_CXPORT 0x1D0 +- +-struct fotg210_request { +- struct usb_request req; +- struct list_head queue; +-}; +- +-struct fotg210_ep { +- struct usb_ep ep; +- struct fotg210_udc *fotg210; +- +- struct list_head queue; +- unsigned stall:1; +- unsigned wedged:1; +- unsigned use_dma:1; +- +- unsigned char epnum; +- unsigned char type; +- unsigned char dir_in; +- unsigned int maxp; +- const struct usb_endpoint_descriptor *desc; +-}; +- +-struct fotg210_udc { +- spinlock_t lock; /* protect the struct */ +- void __iomem *reg; +- +- unsigned long irq_trigger; +- +- struct usb_gadget gadget; +- struct usb_gadget_driver *driver; +- +- struct fotg210_ep *ep[FOTG210_MAX_NUM_EP]; +- +- struct usb_request *ep0_req; /* for internal request */ +- __le16 ep0_data; +- u8 ep0_dir; /* 0/0x80 out/in */ +- +- u8 reenum; /* if re-enumeration */ +-}; +- +-#define gadget_to_fotg210(g) container_of((g), struct fotg210_udc, gadget) +--- a/drivers/usb/host/fotg210.h ++++ /dev/null +@@ -1,688 +0,0 @@ +-/* SPDX-License-Identifier: GPL-2.0 */ +-#ifndef __LINUX_FOTG210_H +-#define __LINUX_FOTG210_H +- +-#include +- +-/* definitions used for the EHCI driver */ +- +-/* +- * __hc32 and __hc16 are "Host Controller" types, they may be equivalent to +- * __leXX (normally) or __beXX (given FOTG210_BIG_ENDIAN_DESC), depending on +- * the host controller implementation. +- * +- * To facilitate the strongest possible byte-order checking from "sparse" +- * and so on, we use __leXX unless that's not practical. +- */ +-#define __hc32 __le32 +-#define __hc16 __le16 +- +-/* statistics can be kept for tuning/monitoring */ +-struct fotg210_stats { +- /* irq usage */ +- unsigned long normal; +- unsigned long error; +- unsigned long iaa; +- unsigned long lost_iaa; +- +- /* termination of urbs from core */ +- unsigned long complete; +- unsigned long unlink; +-}; +- +-/* fotg210_hcd->lock guards shared data against other CPUs: +- * fotg210_hcd: async, unlink, periodic (and shadow), ... +- * usb_host_endpoint: hcpriv +- * fotg210_qh: qh_next, qtd_list +- * fotg210_qtd: qtd_list +- * +- * Also, hold this lock when talking to HC registers or +- * when updating hw_* fields in shared qh/qtd/... structures. +- */ +- +-#define FOTG210_MAX_ROOT_PORTS 1 /* see HCS_N_PORTS */ +- +-/* +- * fotg210_rh_state values of FOTG210_RH_RUNNING or above mean that the +- * controller may be doing DMA. Lower values mean there's no DMA. +- */ +-enum fotg210_rh_state { +- FOTG210_RH_HALTED, +- FOTG210_RH_SUSPENDED, +- FOTG210_RH_RUNNING, +- FOTG210_RH_STOPPING +-}; +- +-/* +- * Timer events, ordered by increasing delay length. +- * Always update event_delays_ns[] and event_handlers[] (defined in +- * ehci-timer.c) in parallel with this list. +- */ +-enum fotg210_hrtimer_event { +- FOTG210_HRTIMER_POLL_ASS, /* Poll for async schedule off */ +- FOTG210_HRTIMER_POLL_PSS, /* Poll for periodic schedule off */ +- FOTG210_HRTIMER_POLL_DEAD, /* Wait for dead controller to stop */ +- FOTG210_HRTIMER_UNLINK_INTR, /* Wait for interrupt QH unlink */ +- FOTG210_HRTIMER_FREE_ITDS, /* Wait for unused iTDs and siTDs */ +- FOTG210_HRTIMER_ASYNC_UNLINKS, /* Unlink empty async QHs */ +- FOTG210_HRTIMER_IAA_WATCHDOG, /* Handle lost IAA interrupts */ +- FOTG210_HRTIMER_DISABLE_PERIODIC, /* Wait to disable periodic sched */ +- FOTG210_HRTIMER_DISABLE_ASYNC, /* Wait to disable async sched */ +- FOTG210_HRTIMER_IO_WATCHDOG, /* Check for missing IRQs */ +- FOTG210_HRTIMER_NUM_EVENTS /* Must come last */ +-}; +-#define FOTG210_HRTIMER_NO_EVENT 99 +- +-struct fotg210_hcd { /* one per controller */ +- /* timing support */ +- enum fotg210_hrtimer_event next_hrtimer_event; +- unsigned enabled_hrtimer_events; +- ktime_t hr_timeouts[FOTG210_HRTIMER_NUM_EVENTS]; +- struct hrtimer hrtimer; +- +- int PSS_poll_count; +- int ASS_poll_count; +- int died_poll_count; +- +- /* glue to PCI and HCD framework */ +- struct fotg210_caps __iomem *caps; +- struct fotg210_regs __iomem *regs; +- struct ehci_dbg_port __iomem *debug; +- +- __u32 hcs_params; /* cached register copy */ +- spinlock_t lock; +- enum fotg210_rh_state rh_state; +- +- /* general schedule support */ +- bool scanning:1; +- bool need_rescan:1; +- bool intr_unlinking:1; +- bool async_unlinking:1; +- bool shutdown:1; +- struct fotg210_qh *qh_scan_next; +- +- /* async schedule support */ +- struct fotg210_qh *async; +- struct fotg210_qh *dummy; /* For AMD quirk use */ +- struct fotg210_qh *async_unlink; +- struct fotg210_qh *async_unlink_last; +- struct fotg210_qh *async_iaa; +- unsigned async_unlink_cycle; +- unsigned async_count; /* async activity count */ +- +- /* periodic schedule support */ +-#define DEFAULT_I_TDPS 1024 /* some HCs can do less */ +- unsigned periodic_size; +- __hc32 *periodic; /* hw periodic table */ +- dma_addr_t periodic_dma; +- struct list_head intr_qh_list; +- unsigned i_thresh; /* uframes HC might cache */ +- +- union fotg210_shadow *pshadow; /* mirror hw periodic table */ +- struct fotg210_qh *intr_unlink; +- struct fotg210_qh *intr_unlink_last; +- unsigned intr_unlink_cycle; +- unsigned now_frame; /* frame from HC hardware */ +- unsigned next_frame; /* scan periodic, start here */ +- unsigned intr_count; /* intr activity count */ +- unsigned isoc_count; /* isoc activity count */ +- unsigned periodic_count; /* periodic activity count */ +- /* max periodic time per uframe */ +- unsigned uframe_periodic_max; +- +- +- /* list of itds completed while now_frame was still active */ +- struct list_head cached_itd_list; +- struct fotg210_itd *last_itd_to_free; +- +- /* per root hub port */ +- unsigned long reset_done[FOTG210_MAX_ROOT_PORTS]; +- +- /* bit vectors (one bit per port) +- * which ports were already suspended at the start of a bus suspend +- */ +- unsigned long bus_suspended; +- +- /* which ports are edicated to the companion controller */ +- unsigned long companion_ports; +- +- /* which ports are owned by the companion during a bus suspend */ +- unsigned long owned_ports; +- +- /* which ports have the change-suspend feature turned on */ +- unsigned long port_c_suspend; +- +- /* which ports are suspended */ +- unsigned long suspended_ports; +- +- /* which ports have started to resume */ +- unsigned long resuming_ports; +- +- /* per-HC memory pools (could be per-bus, but ...) */ +- struct dma_pool *qh_pool; /* qh per active urb */ +- struct dma_pool *qtd_pool; /* one or more per qh */ +- struct dma_pool *itd_pool; /* itd per iso urb */ +- +- unsigned random_frame; +- unsigned long next_statechange; +- ktime_t last_periodic_enable; +- u32 command; +- +- /* SILICON QUIRKS */ +- unsigned need_io_watchdog:1; +- unsigned fs_i_thresh:1; /* Intel iso scheduling */ +- +- u8 sbrn; /* packed release number */ +- +- /* irq statistics */ +-#ifdef FOTG210_STATS +- struct fotg210_stats stats; +-# define INCR(x) ((x)++) +-#else +-# define INCR(x) do {} while (0) +-#endif +- +- /* silicon clock */ +- struct clk *pclk; +-}; +- +-/* convert between an HCD pointer and the corresponding FOTG210_HCD */ +-static inline struct fotg210_hcd *hcd_to_fotg210(struct usb_hcd *hcd) +-{ +- return (struct fotg210_hcd *)(hcd->hcd_priv); +-} +-static inline struct usb_hcd *fotg210_to_hcd(struct fotg210_hcd *fotg210) +-{ +- return container_of((void *) fotg210, struct usb_hcd, hcd_priv); +-} +- +-/*-------------------------------------------------------------------------*/ +- +-/* EHCI register interface, corresponds to EHCI Revision 0.95 specification */ +- +-/* Section 2.2 Host Controller Capability Registers */ +-struct fotg210_caps { +- /* these fields are specified as 8 and 16 bit registers, +- * but some hosts can't perform 8 or 16 bit PCI accesses. +- * some hosts treat caplength and hciversion as parts of a 32-bit +- * register, others treat them as two separate registers, this +- * affects the memory map for big endian controllers. +- */ +- u32 hc_capbase; +-#define HC_LENGTH(fotg210, p) (0x00ff&((p) >> /* bits 7:0 / offset 00h */ \ +- (fotg210_big_endian_capbase(fotg210) ? 24 : 0))) +-#define HC_VERSION(fotg210, p) (0xffff&((p) >> /* bits 31:16 / offset 02h */ \ +- (fotg210_big_endian_capbase(fotg210) ? 0 : 16))) +- u32 hcs_params; /* HCSPARAMS - offset 0x4 */ +-#define HCS_N_PORTS(p) (((p)>>0)&0xf) /* bits 3:0, ports on HC */ +- +- u32 hcc_params; /* HCCPARAMS - offset 0x8 */ +-#define HCC_CANPARK(p) ((p)&(1 << 2)) /* true: can park on async qh */ +-#define HCC_PGM_FRAMELISTLEN(p) ((p)&(1 << 1)) /* true: periodic_size changes*/ +- u8 portroute[8]; /* nibbles for routing - offset 0xC */ +-}; +- +- +-/* Section 2.3 Host Controller Operational Registers */ +-struct fotg210_regs { +- +- /* USBCMD: offset 0x00 */ +- u32 command; +- +-/* EHCI 1.1 addendum */ +-/* 23:16 is r/w intr rate, in microframes; default "8" == 1/msec */ +-#define CMD_PARK (1<<11) /* enable "park" on async qh */ +-#define CMD_PARK_CNT(c) (((c)>>8)&3) /* how many transfers to park for */ +-#define CMD_IAAD (1<<6) /* "doorbell" interrupt async advance */ +-#define CMD_ASE (1<<5) /* async schedule enable */ +-#define CMD_PSE (1<<4) /* periodic schedule enable */ +-/* 3:2 is periodic frame list size */ +-#define CMD_RESET (1<<1) /* reset HC not bus */ +-#define CMD_RUN (1<<0) /* start/stop HC */ +- +- /* USBSTS: offset 0x04 */ +- u32 status; +-#define STS_ASS (1<<15) /* Async Schedule Status */ +-#define STS_PSS (1<<14) /* Periodic Schedule Status */ +-#define STS_RECL (1<<13) /* Reclamation */ +-#define STS_HALT (1<<12) /* Not running (any reason) */ +-/* some bits reserved */ +- /* these STS_* flags are also intr_enable bits (USBINTR) */ +-#define STS_IAA (1<<5) /* Interrupted on async advance */ +-#define STS_FATAL (1<<4) /* such as some PCI access errors */ +-#define STS_FLR (1<<3) /* frame list rolled over */ +-#define STS_PCD (1<<2) /* port change detect */ +-#define STS_ERR (1<<1) /* "error" completion (overflow, ...) */ +-#define STS_INT (1<<0) /* "normal" completion (short, ...) */ +- +- /* USBINTR: offset 0x08 */ +- u32 intr_enable; +- +- /* FRINDEX: offset 0x0C */ +- u32 frame_index; /* current microframe number */ +- /* CTRLDSSEGMENT: offset 0x10 */ +- u32 segment; /* address bits 63:32 if needed */ +- /* PERIODICLISTBASE: offset 0x14 */ +- u32 frame_list; /* points to periodic list */ +- /* ASYNCLISTADDR: offset 0x18 */ +- u32 async_next; /* address of next async queue head */ +- +- u32 reserved1; +- /* PORTSC: offset 0x20 */ +- u32 port_status; +-/* 31:23 reserved */ +-#define PORT_USB11(x) (((x)&(3<<10)) == (1<<10)) /* USB 1.1 device */ +-#define PORT_RESET (1<<8) /* reset port */ +-#define PORT_SUSPEND (1<<7) /* suspend port */ +-#define PORT_RESUME (1<<6) /* resume it */ +-#define PORT_PEC (1<<3) /* port enable change */ +-#define PORT_PE (1<<2) /* port enable */ +-#define PORT_CSC (1<<1) /* connect status change */ +-#define PORT_CONNECT (1<<0) /* device connected */ +-#define PORT_RWC_BITS (PORT_CSC | PORT_PEC) +- u32 reserved2[19]; +- +- /* OTGCSR: offet 0x70 */ +- u32 otgcsr; +-#define OTGCSR_HOST_SPD_TYP (3 << 22) +-#define OTGCSR_A_BUS_DROP (1 << 5) +-#define OTGCSR_A_BUS_REQ (1 << 4) +- +- /* OTGISR: offset 0x74 */ +- u32 otgisr; +-#define OTGISR_OVC (1 << 10) +- +- u32 reserved3[15]; +- +- /* GMIR: offset 0xB4 */ +- u32 gmir; +-#define GMIR_INT_POLARITY (1 << 3) /*Active High*/ +-#define GMIR_MHC_INT (1 << 2) +-#define GMIR_MOTG_INT (1 << 1) +-#define GMIR_MDEV_INT (1 << 0) +-}; +- +-/*-------------------------------------------------------------------------*/ +- +-#define QTD_NEXT(fotg210, dma) cpu_to_hc32(fotg210, (u32)dma) +- +-/* +- * EHCI Specification 0.95 Section 3.5 +- * QTD: describe data transfer components (buffer, direction, ...) +- * See Fig 3-6 "Queue Element Transfer Descriptor Block Diagram". +- * +- * These are associated only with "QH" (Queue Head) structures, +- * used with control, bulk, and interrupt transfers. +- */ +-struct fotg210_qtd { +- /* first part defined by EHCI spec */ +- __hc32 hw_next; /* see EHCI 3.5.1 */ +- __hc32 hw_alt_next; /* see EHCI 3.5.2 */ +- __hc32 hw_token; /* see EHCI 3.5.3 */ +-#define QTD_TOGGLE (1 << 31) /* data toggle */ +-#define QTD_LENGTH(tok) (((tok)>>16) & 0x7fff) +-#define QTD_IOC (1 << 15) /* interrupt on complete */ +-#define QTD_CERR(tok) (((tok)>>10) & 0x3) +-#define QTD_PID(tok) (((tok)>>8) & 0x3) +-#define QTD_STS_ACTIVE (1 << 7) /* HC may execute this */ +-#define QTD_STS_HALT (1 << 6) /* halted on error */ +-#define QTD_STS_DBE (1 << 5) /* data buffer error (in HC) */ +-#define QTD_STS_BABBLE (1 << 4) /* device was babbling (qtd halted) */ +-#define QTD_STS_XACT (1 << 3) /* device gave illegal response */ +-#define QTD_STS_MMF (1 << 2) /* incomplete split transaction */ +-#define QTD_STS_STS (1 << 1) /* split transaction state */ +-#define QTD_STS_PING (1 << 0) /* issue PING? */ +- +-#define ACTIVE_BIT(fotg210) cpu_to_hc32(fotg210, QTD_STS_ACTIVE) +-#define HALT_BIT(fotg210) cpu_to_hc32(fotg210, QTD_STS_HALT) +-#define STATUS_BIT(fotg210) cpu_to_hc32(fotg210, QTD_STS_STS) +- +- __hc32 hw_buf[5]; /* see EHCI 3.5.4 */ +- __hc32 hw_buf_hi[5]; /* Appendix B */ +- +- /* the rest is HCD-private */ +- dma_addr_t qtd_dma; /* qtd address */ +- struct list_head qtd_list; /* sw qtd list */ +- struct urb *urb; /* qtd's urb */ +- size_t length; /* length of buffer */ +-} __aligned(32); +- +-/* mask NakCnt+T in qh->hw_alt_next */ +-#define QTD_MASK(fotg210) cpu_to_hc32(fotg210, ~0x1f) +- +-#define IS_SHORT_READ(token) (QTD_LENGTH(token) != 0 && QTD_PID(token) == 1) +- +-/*-------------------------------------------------------------------------*/ +- +-/* type tag from {qh,itd,fstn}->hw_next */ +-#define Q_NEXT_TYPE(fotg210, dma) ((dma) & cpu_to_hc32(fotg210, 3 << 1)) +- +-/* +- * Now the following defines are not converted using the +- * cpu_to_le32() macro anymore, since we have to support +- * "dynamic" switching between be and le support, so that the driver +- * can be used on one system with SoC EHCI controller using big-endian +- * descriptors as well as a normal little-endian PCI EHCI controller. +- */ +-/* values for that type tag */ +-#define Q_TYPE_ITD (0 << 1) +-#define Q_TYPE_QH (1 << 1) +-#define Q_TYPE_SITD (2 << 1) +-#define Q_TYPE_FSTN (3 << 1) +- +-/* next async queue entry, or pointer to interrupt/periodic QH */ +-#define QH_NEXT(fotg210, dma) \ +- (cpu_to_hc32(fotg210, (((u32)dma)&~0x01f)|Q_TYPE_QH)) +- +-/* for periodic/async schedules and qtd lists, mark end of list */ +-#define FOTG210_LIST_END(fotg210) \ +- cpu_to_hc32(fotg210, 1) /* "null pointer" to hw */ +- +-/* +- * Entries in periodic shadow table are pointers to one of four kinds +- * of data structure. That's dictated by the hardware; a type tag is +- * encoded in the low bits of the hardware's periodic schedule. Use +- * Q_NEXT_TYPE to get the tag. +- * +- * For entries in the async schedule, the type tag always says "qh". +- */ +-union fotg210_shadow { +- struct fotg210_qh *qh; /* Q_TYPE_QH */ +- struct fotg210_itd *itd; /* Q_TYPE_ITD */ +- struct fotg210_fstn *fstn; /* Q_TYPE_FSTN */ +- __hc32 *hw_next; /* (all types) */ +- void *ptr; +-}; +- +-/*-------------------------------------------------------------------------*/ +- +-/* +- * EHCI Specification 0.95 Section 3.6 +- * QH: describes control/bulk/interrupt endpoints +- * See Fig 3-7 "Queue Head Structure Layout". +- * +- * These appear in both the async and (for interrupt) periodic schedules. +- */ +- +-/* first part defined by EHCI spec */ +-struct fotg210_qh_hw { +- __hc32 hw_next; /* see EHCI 3.6.1 */ +- __hc32 hw_info1; /* see EHCI 3.6.2 */ +-#define QH_CONTROL_EP (1 << 27) /* FS/LS control endpoint */ +-#define QH_HEAD (1 << 15) /* Head of async reclamation list */ +-#define QH_TOGGLE_CTL (1 << 14) /* Data toggle control */ +-#define QH_HIGH_SPEED (2 << 12) /* Endpoint speed */ +-#define QH_LOW_SPEED (1 << 12) +-#define QH_FULL_SPEED (0 << 12) +-#define QH_INACTIVATE (1 << 7) /* Inactivate on next transaction */ +- __hc32 hw_info2; /* see EHCI 3.6.2 */ +-#define QH_SMASK 0x000000ff +-#define QH_CMASK 0x0000ff00 +-#define QH_HUBADDR 0x007f0000 +-#define QH_HUBPORT 0x3f800000 +-#define QH_MULT 0xc0000000 +- __hc32 hw_current; /* qtd list - see EHCI 3.6.4 */ +- +- /* qtd overlay (hardware parts of a struct fotg210_qtd) */ +- __hc32 hw_qtd_next; +- __hc32 hw_alt_next; +- __hc32 hw_token; +- __hc32 hw_buf[5]; +- __hc32 hw_buf_hi[5]; +-} __aligned(32); +- +-struct fotg210_qh { +- struct fotg210_qh_hw *hw; /* Must come first */ +- /* the rest is HCD-private */ +- dma_addr_t qh_dma; /* address of qh */ +- union fotg210_shadow qh_next; /* ptr to qh; or periodic */ +- struct list_head qtd_list; /* sw qtd list */ +- struct list_head intr_node; /* list of intr QHs */ +- struct fotg210_qtd *dummy; +- struct fotg210_qh *unlink_next; /* next on unlink list */ +- +- unsigned unlink_cycle; +- +- u8 needs_rescan; /* Dequeue during giveback */ +- u8 qh_state; +-#define QH_STATE_LINKED 1 /* HC sees this */ +-#define QH_STATE_UNLINK 2 /* HC may still see this */ +-#define QH_STATE_IDLE 3 /* HC doesn't see this */ +-#define QH_STATE_UNLINK_WAIT 4 /* LINKED and on unlink q */ +-#define QH_STATE_COMPLETING 5 /* don't touch token.HALT */ +- +- u8 xacterrs; /* XactErr retry counter */ +-#define QH_XACTERR_MAX 32 /* XactErr retry limit */ +- +- /* periodic schedule info */ +- u8 usecs; /* intr bandwidth */ +- u8 gap_uf; /* uframes split/csplit gap */ +- u8 c_usecs; /* ... split completion bw */ +- u16 tt_usecs; /* tt downstream bandwidth */ +- unsigned short period; /* polling interval */ +- unsigned short start; /* where polling starts */ +-#define NO_FRAME ((unsigned short)~0) /* pick new start */ +- +- struct usb_device *dev; /* access to TT */ +- unsigned is_out:1; /* bulk or intr OUT */ +- unsigned clearing_tt:1; /* Clear-TT-Buf in progress */ +-}; +- +-/*-------------------------------------------------------------------------*/ +- +-/* description of one iso transaction (up to 3 KB data if highspeed) */ +-struct fotg210_iso_packet { +- /* These will be copied to iTD when scheduling */ +- u64 bufp; /* itd->hw_bufp{,_hi}[pg] |= */ +- __hc32 transaction; /* itd->hw_transaction[i] |= */ +- u8 cross; /* buf crosses pages */ +- /* for full speed OUT splits */ +- u32 buf1; +-}; +- +-/* temporary schedule data for packets from iso urbs (both speeds) +- * each packet is one logical usb transaction to the device (not TT), +- * beginning at stream->next_uframe +- */ +-struct fotg210_iso_sched { +- struct list_head td_list; +- unsigned span; +- struct fotg210_iso_packet packet[]; +-}; +- +-/* +- * fotg210_iso_stream - groups all (s)itds for this endpoint. +- * acts like a qh would, if EHCI had them for ISO. +- */ +-struct fotg210_iso_stream { +- /* first field matches fotg210_hq, but is NULL */ +- struct fotg210_qh_hw *hw; +- +- u8 bEndpointAddress; +- u8 highspeed; +- struct list_head td_list; /* queued itds */ +- struct list_head free_list; /* list of unused itds */ +- struct usb_device *udev; +- struct usb_host_endpoint *ep; +- +- /* output of (re)scheduling */ +- int next_uframe; +- __hc32 splits; +- +- /* the rest is derived from the endpoint descriptor, +- * trusting urb->interval == f(epdesc->bInterval) and +- * including the extra info for hw_bufp[0..2] +- */ +- u8 usecs, c_usecs; +- u16 interval; +- u16 tt_usecs; +- u16 maxp; +- u16 raw_mask; +- unsigned bandwidth; +- +- /* This is used to initialize iTD's hw_bufp fields */ +- __hc32 buf0; +- __hc32 buf1; +- __hc32 buf2; +- +- /* this is used to initialize sITD's tt info */ +- __hc32 address; +-}; +- +-/*-------------------------------------------------------------------------*/ +- +-/* +- * EHCI Specification 0.95 Section 3.3 +- * Fig 3-4 "Isochronous Transaction Descriptor (iTD)" +- * +- * Schedule records for high speed iso xfers +- */ +-struct fotg210_itd { +- /* first part defined by EHCI spec */ +- __hc32 hw_next; /* see EHCI 3.3.1 */ +- __hc32 hw_transaction[8]; /* see EHCI 3.3.2 */ +-#define FOTG210_ISOC_ACTIVE (1<<31) /* activate transfer this slot */ +-#define FOTG210_ISOC_BUF_ERR (1<<30) /* Data buffer error */ +-#define FOTG210_ISOC_BABBLE (1<<29) /* babble detected */ +-#define FOTG210_ISOC_XACTERR (1<<28) /* XactErr - transaction error */ +-#define FOTG210_ITD_LENGTH(tok) (((tok)>>16) & 0x0fff) +-#define FOTG210_ITD_IOC (1 << 15) /* interrupt on complete */ +- +-#define ITD_ACTIVE(fotg210) cpu_to_hc32(fotg210, FOTG210_ISOC_ACTIVE) +- +- __hc32 hw_bufp[7]; /* see EHCI 3.3.3 */ +- __hc32 hw_bufp_hi[7]; /* Appendix B */ +- +- /* the rest is HCD-private */ +- dma_addr_t itd_dma; /* for this itd */ +- union fotg210_shadow itd_next; /* ptr to periodic q entry */ +- +- struct urb *urb; +- struct fotg210_iso_stream *stream; /* endpoint's queue */ +- struct list_head itd_list; /* list of stream's itds */ +- +- /* any/all hw_transactions here may be used by that urb */ +- unsigned frame; /* where scheduled */ +- unsigned pg; +- unsigned index[8]; /* in urb->iso_frame_desc */ +-} __aligned(32); +- +-/*-------------------------------------------------------------------------*/ +- +-/* +- * EHCI Specification 0.96 Section 3.7 +- * Periodic Frame Span Traversal Node (FSTN) +- * +- * Manages split interrupt transactions (using TT) that span frame boundaries +- * into uframes 0/1; see 4.12.2.2. In those uframes, a "save place" FSTN +- * makes the HC jump (back) to a QH to scan for fs/ls QH completions until +- * it hits a "restore" FSTN; then it returns to finish other uframe 0/1 work. +- */ +-struct fotg210_fstn { +- __hc32 hw_next; /* any periodic q entry */ +- __hc32 hw_prev; /* qh or FOTG210_LIST_END */ +- +- /* the rest is HCD-private */ +- dma_addr_t fstn_dma; +- union fotg210_shadow fstn_next; /* ptr to periodic q entry */ +-} __aligned(32); +- +-/*-------------------------------------------------------------------------*/ +- +-/* Prepare the PORTSC wakeup flags during controller suspend/resume */ +- +-#define fotg210_prepare_ports_for_controller_suspend(fotg210, do_wakeup) \ +- fotg210_adjust_port_wakeup_flags(fotg210, true, do_wakeup) +- +-#define fotg210_prepare_ports_for_controller_resume(fotg210) \ +- fotg210_adjust_port_wakeup_flags(fotg210, false, false) +- +-/*-------------------------------------------------------------------------*/ +- +-/* +- * Some EHCI controllers have a Transaction Translator built into the +- * root hub. This is a non-standard feature. Each controller will need +- * to add code to the following inline functions, and call them as +- * needed (mostly in root hub code). +- */ +- +-static inline unsigned int +-fotg210_get_speed(struct fotg210_hcd *fotg210, unsigned int portsc) +-{ +- return (readl(&fotg210->regs->otgcsr) +- & OTGCSR_HOST_SPD_TYP) >> 22; +-} +- +-/* Returns the speed of a device attached to a port on the root hub. */ +-static inline unsigned int +-fotg210_port_speed(struct fotg210_hcd *fotg210, unsigned int portsc) +-{ +- switch (fotg210_get_speed(fotg210, portsc)) { +- case 0: +- return 0; +- case 1: +- return USB_PORT_STAT_LOW_SPEED; +- case 2: +- default: +- return USB_PORT_STAT_HIGH_SPEED; +- } +-} +- +-/*-------------------------------------------------------------------------*/ +- +-#define fotg210_has_fsl_portno_bug(e) (0) +- +-/* +- * While most USB host controllers implement their registers in +- * little-endian format, a minority (celleb companion chip) implement +- * them in big endian format. +- * +- * This attempts to support either format at compile time without a +- * runtime penalty, or both formats with the additional overhead +- * of checking a flag bit. +- * +- */ +- +-#define fotg210_big_endian_mmio(e) 0 +-#define fotg210_big_endian_capbase(e) 0 +- +-static inline unsigned int fotg210_readl(const struct fotg210_hcd *fotg210, +- __u32 __iomem *regs) +-{ +- return readl(regs); +-} +- +-static inline void fotg210_writel(const struct fotg210_hcd *fotg210, +- const unsigned int val, __u32 __iomem *regs) +-{ +- writel(val, regs); +-} +- +-/* cpu to fotg210 */ +-static inline __hc32 cpu_to_hc32(const struct fotg210_hcd *fotg210, const u32 x) +-{ +- return cpu_to_le32(x); +-} +- +-/* fotg210 to cpu */ +-static inline u32 hc32_to_cpu(const struct fotg210_hcd *fotg210, const __hc32 x) +-{ +- return le32_to_cpu(x); +-} +- +-static inline u32 hc32_to_cpup(const struct fotg210_hcd *fotg210, +- const __hc32 *x) +-{ +- return le32_to_cpup(x); +-} +- +-/*-------------------------------------------------------------------------*/ +- +-static inline unsigned fotg210_read_frame_index(struct fotg210_hcd *fotg210) +-{ +- return fotg210_readl(fotg210, &fotg210->regs->frame_index); +-} +- +-/*-------------------------------------------------------------------------*/ +- +-#endif /* __LINUX_FOTG210_H */ diff --git a/target/linux/gemini/patches-6.1/0003-usb-fotg210-Compile-into-one-module.patch b/target/linux/gemini/patches-6.1/0003-usb-fotg210-Compile-into-one-module.patch new file mode 100644 index 0000000000..5c7b4ff9c7 --- /dev/null +++ b/target/linux/gemini/patches-6.1/0003-usb-fotg210-Compile-into-one-module.patch @@ -0,0 +1,332 @@ +From 0dbc77a99267a5efef0603a4b49ac02ece6a3f23 Mon Sep 17 00:00:00 2001 +From: Linus Walleij +Date: Sun, 23 Oct 2022 16:47:07 +0200 +Subject: [PATCH 03/29] usb: fotg210: Compile into one module + +It is since ages perfectly possible to compile both of these +modules into the same kernel, which makes no sense since it +is one piece of hardware. + +Compile one module named "fotg210.ko" for both HCD and UDC +drivers by collecting the init calls into a fotg210-core.c +file and start to centralize things handling one and the same +piece of hardware. + +Stub out the initcalls if one or the other part of the driver +was not selected. + +Tested by compiling one or the other or both of the drivers +into the kernel and as modules. + +Cc: Fabian Vogt +Cc: Yuan-Hsin Chen +Cc: Felipe Balbi +Signed-off-by: Linus Walleij +Link: https://lore.kernel.org/r/20221023144708.3596563-2-linus.walleij@linaro.org +Signed-off-by: Greg Kroah-Hartman +--- +--- a/drivers/usb/fotg210/Kconfig ++++ b/drivers/usb/fotg210/Kconfig +@@ -12,7 +12,7 @@ config USB_FOTG210 + if USB_FOTG210 + + config USB_FOTG210_HCD +- tristate "Faraday FOTG210 USB Host Controller support" ++ bool "Faraday FOTG210 USB Host Controller support" + depends on USB + help + Faraday FOTG210 is an OTG controller which can be configured as +@@ -24,7 +24,7 @@ config USB_FOTG210_HCD + + config USB_FOTG210_UDC + depends on USB_GADGET +- tristate "Faraday FOTG210 USB Peripheral Controller support" ++ bool "Faraday FOTG210 USB Peripheral Controller support" + help + Faraday USB2.0 OTG controller which can be configured as + high speed or full speed USB device. This driver suppports +--- a/drivers/usb/fotg210/Makefile ++++ b/drivers/usb/fotg210/Makefile +@@ -1,3 +1,10 @@ + # SPDX-License-Identifier: GPL-2.0 +-obj-$(CONFIG_USB_FOTG210_HCD) += fotg210-hcd.o +-obj-$(CONFIG_USB_FOTG210_UDC) += fotg210-udc.o ++ ++# This setup links the different object files into one single ++# module so we don't have to EXPORT() a lot of internal symbols ++# or create unnecessary submodules. ++fotg210-objs-y += fotg210-core.o ++fotg210-objs-$(CONFIG_USB_FOTG210_HCD) += fotg210-hcd.o ++fotg210-objs-$(CONFIG_USB_FOTG210_UDC) += fotg210-udc.o ++fotg210-objs := $(fotg210-objs-y) ++obj-$(CONFIG_USB_FOTG210) += fotg210.o +--- /dev/null ++++ b/drivers/usb/fotg210/fotg210-core.c +@@ -0,0 +1,79 @@ ++// SPDX-License-Identifier: GPL-2.0+ ++/* ++ * Central probing code for the FOTG210 dual role driver ++ * We register one driver for the hardware and then we decide ++ * whether to proceed with probing the host or the peripheral ++ * driver. ++ */ ++#include ++#include ++#include ++#include ++#include ++ ++#include "fotg210.h" ++ ++static int fotg210_probe(struct platform_device *pdev) ++{ ++ int ret; ++ ++ if (IS_ENABLED(CONFIG_USB_FOTG210_HCD)) { ++ ret = fotg210_hcd_probe(pdev); ++ if (ret) ++ return ret; ++ } ++ if (IS_ENABLED(CONFIG_USB_FOTG210_UDC)) ++ ret = fotg210_udc_probe(pdev); ++ ++ return ret; ++} ++ ++static int fotg210_remove(struct platform_device *pdev) ++{ ++ if (IS_ENABLED(CONFIG_USB_FOTG210_HCD)) ++ fotg210_hcd_remove(pdev); ++ if (IS_ENABLED(CONFIG_USB_FOTG210_UDC)) ++ fotg210_udc_remove(pdev); ++ ++ return 0; ++} ++ ++#ifdef CONFIG_OF ++static const struct of_device_id fotg210_of_match[] = { ++ { .compatible = "faraday,fotg210" }, ++ {}, ++}; ++MODULE_DEVICE_TABLE(of, fotg210_of_match); ++#endif ++ ++static struct platform_driver fotg210_driver = { ++ .driver = { ++ .name = "fotg210", ++ .of_match_table = of_match_ptr(fotg210_of_match), ++ }, ++ .probe = fotg210_probe, ++ .remove = fotg210_remove, ++}; ++ ++static int __init fotg210_init(void) ++{ ++ if (usb_disabled()) ++ return -ENODEV; ++ ++ if (IS_ENABLED(CONFIG_USB_FOTG210_HCD)) ++ fotg210_hcd_init(); ++ return platform_driver_register(&fotg210_driver); ++} ++module_init(fotg210_init); ++ ++static void __exit fotg210_cleanup(void) ++{ ++ platform_driver_unregister(&fotg210_driver); ++ if (IS_ENABLED(CONFIG_USB_FOTG210_HCD)) ++ fotg210_hcd_cleanup(); ++} ++module_exit(fotg210_cleanup); ++ ++MODULE_AUTHOR("Yuan-Hsin Chen, Feng-Hsin Chiang"); ++MODULE_LICENSE("GPL"); ++MODULE_DESCRIPTION("FOTG210 Dual Role Controller Driver"); +--- a/drivers/usb/fotg210/fotg210-hcd.c ++++ b/drivers/usb/fotg210/fotg210-hcd.c +@@ -39,8 +39,8 @@ + #include + #include + +-#define DRIVER_AUTHOR "Yuan-Hsin Chen" +-#define DRIVER_DESC "FOTG210 Host Controller (EHCI) Driver" ++#include "fotg210.h" ++ + static const char hcd_name[] = "fotg210_hcd"; + + #undef FOTG210_URB_TRACE +@@ -5490,9 +5490,6 @@ static int fotg210_get_frame(struct usb_ + * functions and in order to facilitate role switching we cannot + * give the fotg210 driver exclusive access to those. + */ +-MODULE_DESCRIPTION(DRIVER_DESC); +-MODULE_AUTHOR(DRIVER_AUTHOR); +-MODULE_LICENSE("GPL"); + + static const struct hc_driver fotg210_fotg210_hc_driver = { + .description = hcd_name, +@@ -5560,7 +5557,7 @@ static void fotg210_init(struct fotg210_ + * then invokes the start() method for the HCD associated with it + * through the hotplug entry's driver_data. + */ +-static int fotg210_hcd_probe(struct platform_device *pdev) ++int fotg210_hcd_probe(struct platform_device *pdev) + { + struct device *dev = &pdev->dev; + struct usb_hcd *hcd; +@@ -5652,7 +5649,7 @@ fail_create_hcd: + * @dev: USB Host Controller being removed + * + */ +-static int fotg210_hcd_remove(struct platform_device *pdev) ++int fotg210_hcd_remove(struct platform_device *pdev) + { + struct usb_hcd *hcd = platform_get_drvdata(pdev); + struct fotg210_hcd *fotg210 = hcd_to_fotg210(hcd); +@@ -5668,27 +5665,8 @@ static int fotg210_hcd_remove(struct pla + return 0; + } + +-#ifdef CONFIG_OF +-static const struct of_device_id fotg210_of_match[] = { +- { .compatible = "faraday,fotg210" }, +- {}, +-}; +-MODULE_DEVICE_TABLE(of, fotg210_of_match); +-#endif +- +-static struct platform_driver fotg210_hcd_driver = { +- .driver = { +- .name = "fotg210-hcd", +- .of_match_table = of_match_ptr(fotg210_of_match), +- }, +- .probe = fotg210_hcd_probe, +- .remove = fotg210_hcd_remove, +-}; +- +-static int __init fotg210_hcd_init(void) ++int __init fotg210_hcd_init(void) + { +- int retval = 0; +- + if (usb_disabled()) + return -ENODEV; + +@@ -5704,24 +5682,11 @@ static int __init fotg210_hcd_init(void) + + fotg210_debug_root = debugfs_create_dir("fotg210", usb_debug_root); + +- retval = platform_driver_register(&fotg210_hcd_driver); +- if (retval < 0) +- goto clean; +- return retval; +- +-clean: +- debugfs_remove(fotg210_debug_root); +- fotg210_debug_root = NULL; +- +- clear_bit(USB_EHCI_LOADED, &usb_hcds_loaded); +- return retval; ++ return 0; + } +-module_init(fotg210_hcd_init); + +-static void __exit fotg210_hcd_cleanup(void) ++void __exit fotg210_hcd_cleanup(void) + { +- platform_driver_unregister(&fotg210_hcd_driver); + debugfs_remove(fotg210_debug_root); + clear_bit(USB_EHCI_LOADED, &usb_hcds_loaded); + } +-module_exit(fotg210_hcd_cleanup); +--- a/drivers/usb/fotg210/fotg210-udc.c ++++ b/drivers/usb/fotg210/fotg210-udc.c +@@ -16,6 +16,7 @@ + #include + #include + ++#include "fotg210.h" + #include "fotg210-udc.h" + + #define DRIVER_DESC "FOTG210 USB Device Controller Driver" +@@ -1081,7 +1082,7 @@ static const struct usb_gadget_ops fotg2 + .udc_stop = fotg210_udc_stop, + }; + +-static int fotg210_udc_remove(struct platform_device *pdev) ++int fotg210_udc_remove(struct platform_device *pdev) + { + struct fotg210_udc *fotg210 = platform_get_drvdata(pdev); + int i; +@@ -1098,7 +1099,7 @@ static int fotg210_udc_remove(struct pla + return 0; + } + +-static int fotg210_udc_probe(struct platform_device *pdev) ++int fotg210_udc_probe(struct platform_device *pdev) + { + struct resource *res, *ires; + struct fotg210_udc *fotg210 = NULL; +@@ -1223,17 +1224,3 @@ err_alloc: + err: + return ret; + } +- +-static struct platform_driver fotg210_driver = { +- .driver = { +- .name = udc_name, +- }, +- .probe = fotg210_udc_probe, +- .remove = fotg210_udc_remove, +-}; +- +-module_platform_driver(fotg210_driver); +- +-MODULE_AUTHOR("Yuan-Hsin Chen, Feng-Hsin Chiang "); +-MODULE_LICENSE("GPL"); +-MODULE_DESCRIPTION(DRIVER_DESC); +--- /dev/null ++++ b/drivers/usb/fotg210/fotg210.h +@@ -0,0 +1,42 @@ ++/* SPDX-License-Identifier: GPL-2.0 */ ++#ifndef __FOTG210_H ++#define __FOTG210_H ++ ++#ifdef CONFIG_USB_FOTG210_HCD ++int fotg210_hcd_probe(struct platform_device *pdev); ++int fotg210_hcd_remove(struct platform_device *pdev); ++int fotg210_hcd_init(void); ++void fotg210_hcd_cleanup(void); ++#else ++static inline int fotg210_hcd_probe(struct platform_device *pdev) ++{ ++ return 0; ++} ++static inline int fotg210_hcd_remove(struct platform_device *pdev) ++{ ++ return 0; ++} ++static inline int fotg210_hcd_init(void) ++{ ++ return 0; ++} ++static inline void fotg210_hcd_cleanup(void) ++{ ++} ++#endif ++ ++#ifdef CONFIG_USB_FOTG210_UDC ++int fotg210_udc_probe(struct platform_device *pdev); ++int fotg210_udc_remove(struct platform_device *pdev); ++#else ++static inline int fotg210_udc_probe(struct platform_device *pdev) ++{ ++ return 0; ++} ++static inline int fotg210_udc_remove(struct platform_device *pdev) ++{ ++ return 0; ++} ++#endif ++ ++#endif /* __FOTG210_H */ diff --git a/target/linux/gemini/patches-6.1/0004-usb-fotg210-Select-subdriver-by-mode.patch b/target/linux/gemini/patches-6.1/0004-usb-fotg210-Select-subdriver-by-mode.patch new file mode 100644 index 0000000000..6a19a0aa4d --- /dev/null +++ b/target/linux/gemini/patches-6.1/0004-usb-fotg210-Select-subdriver-by-mode.patch @@ -0,0 +1,68 @@ +From 7c0b661926097e935f2711857596fc2277b2304a Mon Sep 17 00:00:00 2001 +From: Linus Walleij +Date: Sun, 23 Oct 2022 16:47:08 +0200 +Subject: [PATCH 04/29] usb: fotg210: Select subdriver by mode + +Check which mode the hardware is in, and selecte the peripheral +driver if the hardware is in explicit peripheral mode, otherwise +select host mode. + +This should solve the immediate problem that both subdrivers +can get probed. + +Cc: Fabian Vogt +Cc: Yuan-Hsin Chen +Cc: Felipe Balbi +Signed-off-by: Linus Walleij +Link: https://lore.kernel.org/r/20221023144708.3596563-3-linus.walleij@linaro.org +Signed-off-by: Greg Kroah-Hartman +--- +--- a/drivers/usb/fotg210/fotg210-core.c ++++ b/drivers/usb/fotg210/fotg210-core.c +@@ -10,30 +10,37 @@ + #include + #include + #include ++#include + + #include "fotg210.h" + + static int fotg210_probe(struct platform_device *pdev) + { ++ struct device *dev = &pdev->dev; ++ enum usb_dr_mode mode; + int ret; + +- if (IS_ENABLED(CONFIG_USB_FOTG210_HCD)) { +- ret = fotg210_hcd_probe(pdev); +- if (ret) +- return ret; +- } +- if (IS_ENABLED(CONFIG_USB_FOTG210_UDC)) ++ mode = usb_get_dr_mode(dev); ++ ++ if (mode == USB_DR_MODE_PERIPHERAL) + ret = fotg210_udc_probe(pdev); ++ else ++ ret = fotg210_hcd_probe(pdev); + + return ret; + } + + static int fotg210_remove(struct platform_device *pdev) + { +- if (IS_ENABLED(CONFIG_USB_FOTG210_HCD)) +- fotg210_hcd_remove(pdev); +- if (IS_ENABLED(CONFIG_USB_FOTG210_UDC)) ++ struct device *dev = &pdev->dev; ++ enum usb_dr_mode mode; ++ ++ mode = usb_get_dr_mode(dev); ++ ++ if (mode == USB_DR_MODE_PERIPHERAL) + fotg210_udc_remove(pdev); ++ else ++ fotg210_hcd_remove(pdev); + + return 0; + } diff --git a/target/linux/gemini/patches-6.1/0005-usb-fotg2-add-Gemini-specific-handling.patch b/target/linux/gemini/patches-6.1/0005-usb-fotg2-add-Gemini-specific-handling.patch new file mode 100644 index 0000000000..daf8d85611 --- /dev/null +++ b/target/linux/gemini/patches-6.1/0005-usb-fotg2-add-Gemini-specific-handling.patch @@ -0,0 +1,135 @@ +From f7f6c8aca91093e2f886ec97910b1a7d9a69bf9b Mon Sep 17 00:00:00 2001 +From: Linus Walleij +Date: Wed, 9 Nov 2022 21:05:54 +0100 +Subject: [PATCH 05/29] usb: fotg2: add Gemini-specific handling + +The Cortina Systems Gemini has bolted on a PHY inside the +silicon that can be handled by six bits in a MISC register in +the system controller. + +If we are running on Gemini, look up a syscon regmap through +a phandle and enable VBUS and optionally the Mini-B connector. + +If the device is flagged as "wakeup-source" using the standard +DT bindings, we also enable this in the global controller for +respective port. + +Signed-off-by: Linus Walleij +Link: https://lore.kernel.org/r/20221109200554.1957185-1-linus.walleij@linaro.org +Signed-off-by: Greg Kroah-Hartman +--- +--- a/drivers/usb/fotg210/Kconfig ++++ b/drivers/usb/fotg210/Kconfig +@@ -5,6 +5,7 @@ config USB_FOTG210 + depends on USB || USB_GADGET + depends on HAS_DMA && HAS_IOMEM + default ARCH_GEMINI ++ select MFD_SYSCON + help + Faraday FOTG210 is a dual-mode USB controller that can act + in both host controller and peripheral controller mode. +--- a/drivers/usb/fotg210/fotg210-core.c ++++ b/drivers/usb/fotg210/fotg210-core.c +@@ -5,15 +5,86 @@ + * whether to proceed with probing the host or the peripheral + * driver. + */ ++#include + #include ++#include + #include + #include + #include ++#include + #include + #include + + #include "fotg210.h" + ++/* ++ * Gemini-specific initialization function, only executed on the ++ * Gemini SoC using the global misc control register. ++ * ++ * The gemini USB blocks are connected to either Mini-A (host mode) or ++ * Mini-B (peripheral mode) plugs. There is no role switch support on the ++ * Gemini SoC, just either-or. ++ */ ++#define GEMINI_GLOBAL_MISC_CTRL 0x30 ++#define GEMINI_MISC_USB0_WAKEUP BIT(14) ++#define GEMINI_MISC_USB1_WAKEUP BIT(15) ++#define GEMINI_MISC_USB0_VBUS_ON BIT(22) ++#define GEMINI_MISC_USB1_VBUS_ON BIT(23) ++#define GEMINI_MISC_USB0_MINI_B BIT(29) ++#define GEMINI_MISC_USB1_MINI_B BIT(30) ++ ++static int fotg210_gemini_init(struct device *dev, struct resource *res, ++ enum usb_dr_mode mode) ++{ ++ struct device_node *np = dev->of_node; ++ struct regmap *map; ++ bool wakeup; ++ u32 mask, val; ++ int ret; ++ ++ map = syscon_regmap_lookup_by_phandle(np, "syscon"); ++ if (IS_ERR(map)) { ++ dev_err(dev, "no syscon\n"); ++ return PTR_ERR(map); ++ } ++ wakeup = of_property_read_bool(np, "wakeup-source"); ++ ++ /* ++ * Figure out if this is USB0 or USB1 by simply checking the ++ * physical base address. ++ */ ++ mask = 0; ++ if (res->start == 0x69000000) { ++ mask = GEMINI_MISC_USB1_VBUS_ON | GEMINI_MISC_USB1_MINI_B | ++ GEMINI_MISC_USB1_WAKEUP; ++ if (mode == USB_DR_MODE_HOST) ++ val = GEMINI_MISC_USB1_VBUS_ON; ++ else ++ val = GEMINI_MISC_USB1_MINI_B; ++ if (wakeup) ++ val |= GEMINI_MISC_USB1_WAKEUP; ++ } else { ++ mask = GEMINI_MISC_USB0_VBUS_ON | GEMINI_MISC_USB0_MINI_B | ++ GEMINI_MISC_USB0_WAKEUP; ++ if (mode == USB_DR_MODE_HOST) ++ val = GEMINI_MISC_USB0_VBUS_ON; ++ else ++ val = GEMINI_MISC_USB0_MINI_B; ++ if (wakeup) ++ val |= GEMINI_MISC_USB0_WAKEUP; ++ } ++ ++ ret = regmap_update_bits(map, GEMINI_GLOBAL_MISC_CTRL, mask, val); ++ if (ret) { ++ dev_err(dev, "failed to initialize Gemini PHY\n"); ++ return ret; ++ } ++ ++ dev_info(dev, "initialized Gemini PHY in %s mode\n", ++ (mode == USB_DR_MODE_HOST) ? "host" : "gadget"); ++ return 0; ++} ++ + static int fotg210_probe(struct platform_device *pdev) + { + struct device *dev = &pdev->dev; +@@ -22,6 +93,15 @@ static int fotg210_probe(struct platform + + mode = usb_get_dr_mode(dev); + ++ if (of_device_is_compatible(dev->of_node, "cortina,gemini-usb")) { ++ struct resource *res; ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ ret = fotg210_gemini_init(dev, res, mode); ++ if (ret) ++ return ret; ++ } ++ + if (mode == USB_DR_MODE_PERIPHERAL) + ret = fotg210_udc_probe(pdev); + else diff --git a/target/linux/gemini/patches-6.1/0006-usb-fotg210-Fix-Kconfig-for-USB-host-modules.patch b/target/linux/gemini/patches-6.1/0006-usb-fotg210-Fix-Kconfig-for-USB-host-modules.patch new file mode 100644 index 0000000000..bd3a42415a --- /dev/null +++ b/target/linux/gemini/patches-6.1/0006-usb-fotg210-Fix-Kconfig-for-USB-host-modules.patch @@ -0,0 +1,51 @@ +From 6e002d41889bc52213a26ff91338d340505e0336 Mon Sep 17 00:00:00 2001 +From: Linus Walleij +Date: Fri, 11 Nov 2022 15:48:21 +0100 +Subject: [PATCH 06/29] usb: fotg210: Fix Kconfig for USB host modules + +The kernel robot reports a link failure when activating the +FOTG210 host subdriver with =y on a system where the USB host +core is a module (CONFIG_USB=m). + +This is a bit of special case, so mimic the Kconfig incantations +from DWC3: let the subdrivers for host or peripheral depend +on the host or gadget support being =y or the same as the +FOTG210 core itself. + +This should ensure that either: + +- The host (CONFIG_USB) or gadget (CONFIG_GADGET) is compiled + in and then the FOTG210 can be either module or compiled + in. + +- The host or gadget is modular, and then the FOTG210 module + must be a module too, or we cannot resolve the symbols + at link time. + +Reported-by: kernel test robot +Link: https://lore.kernel.org/linux-usb/202211112132.0BUPGKCd-lkp@intel.com/ +Cc: Arnd Bergmann +Signed-off-by: Linus Walleij +Link: https://lore.kernel.org/r/20221111144821.113665-1-linus.walleij@linaro.org +Signed-off-by: Greg Kroah-Hartman +--- +--- a/drivers/usb/fotg210/Kconfig ++++ b/drivers/usb/fotg210/Kconfig +@@ -14,7 +14,7 @@ if USB_FOTG210 + + config USB_FOTG210_HCD + bool "Faraday FOTG210 USB Host Controller support" +- depends on USB ++ depends on USB=y || USB=USB_FOTG210 + help + Faraday FOTG210 is an OTG controller which can be configured as + an USB2.0 host. It is designed to meet USB2.0 EHCI specification +@@ -24,7 +24,7 @@ config USB_FOTG210_HCD + module will be called fotg210-hcd. + + config USB_FOTG210_UDC +- depends on USB_GADGET ++ depends on USB_GADGET=y || USB_GADGET=USB_FOTG210 + bool "Faraday FOTG210 USB Peripheral Controller support" + help + Faraday USB2.0 OTG controller which can be configured as diff --git a/target/linux/gemini/patches-6.1/0007-usb-USB_FOTG210-should-depend-on-ARCH_GEMINI.patch b/target/linux/gemini/patches-6.1/0007-usb-USB_FOTG210-should-depend-on-ARCH_GEMINI.patch new file mode 100644 index 0000000000..6afef0d820 --- /dev/null +++ b/target/linux/gemini/patches-6.1/0007-usb-USB_FOTG210-should-depend-on-ARCH_GEMINI.patch @@ -0,0 +1,26 @@ +From 466b10510add46afd21ca19505b29d35ad853370 Mon Sep 17 00:00:00 2001 +From: Geert Uytterhoeven +Date: Mon, 21 Nov 2022 16:22:19 +0100 +Subject: [PATCH 07/29] usb: USB_FOTG210 should depend on ARCH_GEMINI + +The Faraday Technology FOTG210 USB2 Dual Role Controller is only present +on Cortina Systems Gemini SoCs. Hence add a dependency on ARCH_GEMINI, +to prevent asking the user about its drivers when configuring a kernel +without Cortina Systems Gemini SoC support. + +Fixes: 1dd33a9f1b95ab59 ("usb: fotg210: Collect pieces of dual mode controller") +Signed-off-by: Geert Uytterhoeven +Reviewed-by: Linus Walleij +Link: https://lore.kernel.org/r/a989b3b798ecaf3b45f35160e30e605636d66a77.1669044086.git.geert+renesas@glider.be +Signed-off-by: Greg Kroah-Hartman +--- +--- a/drivers/usb/fotg210/Kconfig ++++ b/drivers/usb/fotg210/Kconfig +@@ -4,6 +4,7 @@ config USB_FOTG210 + tristate "Faraday FOTG210 USB2 Dual Role controller" + depends on USB || USB_GADGET + depends on HAS_DMA && HAS_IOMEM ++ depends on ARCH_GEMINI || COMPILE_TEST + default ARCH_GEMINI + select MFD_SYSCON + help diff --git a/target/linux/gemini/patches-6.1/0008-fotg210-udc-Use-dev-pointer-in-probe-and-dev_message.patch b/target/linux/gemini/patches-6.1/0008-fotg210-udc-Use-dev-pointer-in-probe-and-dev_message.patch new file mode 100644 index 0000000000..2a595e885d --- /dev/null +++ b/target/linux/gemini/patches-6.1/0008-fotg210-udc-Use-dev-pointer-in-probe-and-dev_message.patch @@ -0,0 +1,61 @@ +From 27cd321a365fecac857e41ad1681062994142e4a Mon Sep 17 00:00:00 2001 +From: Linus Walleij +Date: Mon, 14 Nov 2022 12:51:58 +0100 +Subject: [PATCH 08/29] fotg210-udc: Use dev pointer in probe and dev_messages + +Add a local struct device *dev pointer and use dev_err() +etc to report status. + +Signed-off-by: Linus Walleij +Link: https://lore.kernel.org/r/20221114115201.302887-1-linus.walleij@linaro.org +Signed-off-by: Greg Kroah-Hartman +--- +--- a/drivers/usb/fotg210/fotg210-udc.c ++++ b/drivers/usb/fotg210/fotg210-udc.c +@@ -1104,6 +1104,7 @@ int fotg210_udc_probe(struct platform_de + struct resource *res, *ires; + struct fotg210_udc *fotg210 = NULL; + struct fotg210_ep *_ep[FOTG210_MAX_NUM_EP]; ++ struct device *dev = &pdev->dev; + int ret = 0; + int i; + +@@ -1135,7 +1136,7 @@ int fotg210_udc_probe(struct platform_de + + fotg210->reg = ioremap(res->start, resource_size(res)); + if (fotg210->reg == NULL) { +- pr_err("ioremap error.\n"); ++ dev_err(dev, "ioremap error\n"); + goto err_alloc; + } + +@@ -1146,8 +1147,8 @@ int fotg210_udc_probe(struct platform_de + fotg210->gadget.ops = &fotg210_gadget_ops; + + fotg210->gadget.max_speed = USB_SPEED_HIGH; +- fotg210->gadget.dev.parent = &pdev->dev; +- fotg210->gadget.dev.dma_mask = pdev->dev.dma_mask; ++ fotg210->gadget.dev.parent = dev; ++ fotg210->gadget.dev.dma_mask = dev->dma_mask; + fotg210->gadget.name = udc_name; + + INIT_LIST_HEAD(&fotg210->gadget.ep_list); +@@ -1195,15 +1196,15 @@ int fotg210_udc_probe(struct platform_de + ret = request_irq(ires->start, fotg210_irq, IRQF_SHARED, + udc_name, fotg210); + if (ret < 0) { +- pr_err("request_irq error (%d)\n", ret); ++ dev_err(dev, "request_irq error (%d)\n", ret); + goto err_req; + } + +- ret = usb_add_gadget_udc(&pdev->dev, &fotg210->gadget); ++ ret = usb_add_gadget_udc(dev, &fotg210->gadget); + if (ret) + goto err_add_udc; + +- dev_info(&pdev->dev, "version %s\n", DRIVER_VERSION); ++ dev_info(dev, "version %s\n", DRIVER_VERSION); + + return 0; + diff --git a/target/linux/gemini/patches-6.1/0009-fotg210-udc-Support-optional-external-PHY.patch b/target/linux/gemini/patches-6.1/0009-fotg210-udc-Support-optional-external-PHY.patch new file mode 100644 index 0000000000..498875c535 --- /dev/null +++ b/target/linux/gemini/patches-6.1/0009-fotg210-udc-Support-optional-external-PHY.patch @@ -0,0 +1,158 @@ +From 03e4b585ac947e2d422bedf03179bbfec3aca3cf Mon Sep 17 00:00:00 2001 +From: Linus Walleij +Date: Mon, 14 Nov 2022 12:51:59 +0100 +Subject: [PATCH 09/29] fotg210-udc: Support optional external PHY + +This adds support for an optional external PHY to the FOTG210 +UDC driver. + +Tested with the GPIO VBUS PHY driver on the Gemini SoC. + +Signed-off-by: Linus Walleij +Link: https://lore.kernel.org/r/20221114115201.302887-2-linus.walleij@linaro.org +Signed-off-by: Greg Kroah-Hartman +--- +--- a/drivers/usb/fotg210/fotg210-udc.c ++++ b/drivers/usb/fotg210/fotg210-udc.c +@@ -15,6 +15,8 @@ + #include + #include + #include ++#include ++#include + + #include "fotg210.h" + #include "fotg210-udc.h" +@@ -1022,10 +1024,18 @@ static int fotg210_udc_start(struct usb_ + { + struct fotg210_udc *fotg210 = gadget_to_fotg210(g); + u32 value; ++ int ret; + + /* hook up the driver */ + fotg210->driver = driver; + ++ if (!IS_ERR_OR_NULL(fotg210->phy)) { ++ ret = otg_set_peripheral(fotg210->phy->otg, ++ &fotg210->gadget); ++ if (ret) ++ dev_err(fotg210->dev, "can't bind to phy\n"); ++ } ++ + /* enable device global interrupt */ + value = ioread32(fotg210->reg + FOTG210_DMCR); + value |= DMCR_GLINT_EN; +@@ -1067,6 +1077,9 @@ static int fotg210_udc_stop(struct usb_g + struct fotg210_udc *fotg210 = gadget_to_fotg210(g); + unsigned long flags; + ++ if (!IS_ERR_OR_NULL(fotg210->phy)) ++ return otg_set_peripheral(fotg210->phy->otg, NULL); ++ + spin_lock_irqsave(&fotg210->lock, flags); + + fotg210_init(fotg210); +@@ -1082,12 +1095,50 @@ static const struct usb_gadget_ops fotg2 + .udc_stop = fotg210_udc_stop, + }; + ++/** ++ * fotg210_phy_event - Called by phy upon VBus event ++ * @nb: notifier block ++ * @action: phy action, is vbus connect or disconnect ++ * @data: the usb_gadget structure in fotg210 ++ * ++ * Called by the USB Phy when a cable connect or disconnect is sensed. ++ * ++ * Returns NOTIFY_OK or NOTIFY_DONE ++ */ ++static int fotg210_phy_event(struct notifier_block *nb, unsigned long action, ++ void *data) ++{ ++ struct usb_gadget *gadget = data; ++ ++ if (!gadget) ++ return NOTIFY_DONE; ++ ++ switch (action) { ++ case USB_EVENT_VBUS: ++ usb_gadget_vbus_connect(gadget); ++ return NOTIFY_OK; ++ case USB_EVENT_NONE: ++ usb_gadget_vbus_disconnect(gadget); ++ return NOTIFY_OK; ++ default: ++ return NOTIFY_DONE; ++ } ++} ++ ++static struct notifier_block fotg210_phy_notifier = { ++ .notifier_call = fotg210_phy_event, ++}; ++ + int fotg210_udc_remove(struct platform_device *pdev) + { + struct fotg210_udc *fotg210 = platform_get_drvdata(pdev); + int i; + + usb_del_gadget_udc(&fotg210->gadget); ++ if (!IS_ERR_OR_NULL(fotg210->phy)) { ++ usb_unregister_notifier(fotg210->phy, &fotg210_phy_notifier); ++ usb_put_phy(fotg210->phy); ++ } + iounmap(fotg210->reg); + free_irq(platform_get_irq(pdev, 0), fotg210); + +@@ -1127,6 +1178,22 @@ int fotg210_udc_probe(struct platform_de + if (fotg210 == NULL) + goto err; + ++ fotg210->dev = dev; ++ ++ fotg210->phy = devm_usb_get_phy_by_phandle(dev->parent, "usb-phy", 0); ++ if (IS_ERR(fotg210->phy)) { ++ ret = PTR_ERR(fotg210->phy); ++ if (ret == -EPROBE_DEFER) ++ goto err; ++ dev_info(dev, "no PHY found\n"); ++ fotg210->phy = NULL; ++ } else { ++ ret = usb_phy_init(fotg210->phy); ++ if (ret) ++ goto err; ++ dev_info(dev, "found and initialized PHY\n"); ++ } ++ + for (i = 0; i < FOTG210_MAX_NUM_EP; i++) { + _ep[i] = kzalloc(sizeof(struct fotg210_ep), GFP_KERNEL); + if (_ep[i] == NULL) +@@ -1200,6 +1267,9 @@ int fotg210_udc_probe(struct platform_de + goto err_req; + } + ++ if (!IS_ERR_OR_NULL(fotg210->phy)) ++ usb_register_notifier(fotg210->phy, &fotg210_phy_notifier); ++ + ret = usb_add_gadget_udc(dev, &fotg210->gadget); + if (ret) + goto err_add_udc; +@@ -1209,6 +1279,8 @@ int fotg210_udc_probe(struct platform_de + return 0; + + err_add_udc: ++ if (!IS_ERR_OR_NULL(fotg210->phy)) ++ usb_unregister_notifier(fotg210->phy, &fotg210_phy_notifier); + free_irq(ires->start, fotg210); + + err_req: +--- a/drivers/usb/fotg210/fotg210-udc.h ++++ b/drivers/usb/fotg210/fotg210-udc.h +@@ -234,6 +234,8 @@ struct fotg210_udc { + + unsigned long irq_trigger; + ++ struct device *dev; ++ struct usb_phy *phy; + struct usb_gadget gadget; + struct usb_gadget_driver *driver; + diff --git a/target/linux/gemini/patches-6.1/0010-fotg210-udc-Handle-PCLK.patch b/target/linux/gemini/patches-6.1/0010-fotg210-udc-Handle-PCLK.patch new file mode 100644 index 0000000000..8da3de3b47 --- /dev/null +++ b/target/linux/gemini/patches-6.1/0010-fotg210-udc-Handle-PCLK.patch @@ -0,0 +1,90 @@ +From 772ea3ec2b9363b45ef9a4768ea205f758c3debc Mon Sep 17 00:00:00 2001 +From: Linus Walleij +Date: Mon, 14 Nov 2022 12:52:00 +0100 +Subject: [PATCH 10/29] fotg210-udc: Handle PCLK + +This adds optional handling of the peripheral clock PCLK. + +Signed-off-by: Linus Walleij +Link: https://lore.kernel.org/r/20221114115201.302887-3-linus.walleij@linaro.org +Signed-off-by: Greg Kroah-Hartman +--- +--- a/drivers/usb/fotg210/fotg210-udc.c ++++ b/drivers/usb/fotg210/fotg210-udc.c +@@ -15,6 +15,7 @@ + #include + #include + #include ++#include + #include + #include + +@@ -1145,6 +1146,10 @@ int fotg210_udc_remove(struct platform_d + fotg210_ep_free_request(&fotg210->ep[0]->ep, fotg210->ep0_req); + for (i = 0; i < FOTG210_MAX_NUM_EP; i++) + kfree(fotg210->ep[i]); ++ ++ if (!IS_ERR(fotg210->pclk)) ++ clk_disable_unprepare(fotg210->pclk); ++ + kfree(fotg210); + + return 0; +@@ -1180,17 +1185,34 @@ int fotg210_udc_probe(struct platform_de + + fotg210->dev = dev; + ++ /* It's OK not to supply this clock */ ++ fotg210->pclk = devm_clk_get(dev, "PCLK"); ++ if (!IS_ERR(fotg210->pclk)) { ++ ret = clk_prepare_enable(fotg210->pclk); ++ if (ret) { ++ dev_err(dev, "failed to enable PCLK\n"); ++ return ret; ++ } ++ } else if (PTR_ERR(fotg210->pclk) == -EPROBE_DEFER) { ++ /* ++ * Percolate deferrals, for anything else, ++ * just live without the clocking. ++ */ ++ ret = -EPROBE_DEFER; ++ goto err; ++ } ++ + fotg210->phy = devm_usb_get_phy_by_phandle(dev->parent, "usb-phy", 0); + if (IS_ERR(fotg210->phy)) { + ret = PTR_ERR(fotg210->phy); + if (ret == -EPROBE_DEFER) +- goto err; ++ goto err_pclk; + dev_info(dev, "no PHY found\n"); + fotg210->phy = NULL; + } else { + ret = usb_phy_init(fotg210->phy); + if (ret) +- goto err; ++ goto err_pclk; + dev_info(dev, "found and initialized PHY\n"); + } + +@@ -1292,6 +1314,10 @@ err_map: + err_alloc: + for (i = 0; i < FOTG210_MAX_NUM_EP; i++) + kfree(fotg210->ep[i]); ++err_pclk: ++ if (!IS_ERR(fotg210->pclk)) ++ clk_disable_unprepare(fotg210->pclk); ++ + kfree(fotg210); + + err: +--- a/drivers/usb/fotg210/fotg210-udc.h ++++ b/drivers/usb/fotg210/fotg210-udc.h +@@ -231,6 +231,7 @@ struct fotg210_ep { + struct fotg210_udc { + spinlock_t lock; /* protect the struct */ + void __iomem *reg; ++ struct clk *pclk; + + unsigned long irq_trigger; + diff --git a/target/linux/gemini/patches-6.1/0011-fotg210-udc-Get-IRQ-using-platform_get_irq.patch b/target/linux/gemini/patches-6.1/0011-fotg210-udc-Get-IRQ-using-platform_get_irq.patch new file mode 100644 index 0000000000..9544de7cb0 --- /dev/null +++ b/target/linux/gemini/patches-6.1/0011-fotg210-udc-Get-IRQ-using-platform_get_irq.patch @@ -0,0 +1,69 @@ +From eda686d41e298a9d16708d2ec8d12d8e682dd7ca Mon Sep 17 00:00:00 2001 +From: Linus Walleij +Date: Mon, 14 Nov 2022 12:52:01 +0100 +Subject: [PATCH 11/29] fotg210-udc: Get IRQ using platform_get_irq() + +The platform_get_irq() is necessary to use to get dynamic +IRQ resolution when instantiating the device from the +device tree. IRQs are not passed as resources in that +case. + +Signed-off-by: Linus Walleij +Link: https://lore.kernel.org/r/20221114115201.302887-4-linus.walleij@linaro.org +Signed-off-by: Greg Kroah-Hartman +--- +--- a/drivers/usb/fotg210/fotg210-udc.c ++++ b/drivers/usb/fotg210/fotg210-udc.c +@@ -1157,10 +1157,11 @@ int fotg210_udc_remove(struct platform_d + + int fotg210_udc_probe(struct platform_device *pdev) + { +- struct resource *res, *ires; ++ struct resource *res; + struct fotg210_udc *fotg210 = NULL; + struct fotg210_ep *_ep[FOTG210_MAX_NUM_EP]; + struct device *dev = &pdev->dev; ++ int irq; + int ret = 0; + int i; + +@@ -1170,9 +1171,9 @@ int fotg210_udc_probe(struct platform_de + return -ENODEV; + } + +- ires = platform_get_resource(pdev, IORESOURCE_IRQ, 0); +- if (!ires) { +- pr_err("platform_get_resource IORESOURCE_IRQ error.\n"); ++ irq = platform_get_irq(pdev, 0); ++ if (irq < 0) { ++ pr_err("could not get irq\n"); + return -ENODEV; + } + +@@ -1202,7 +1203,7 @@ int fotg210_udc_probe(struct platform_de + goto err; + } + +- fotg210->phy = devm_usb_get_phy_by_phandle(dev->parent, "usb-phy", 0); ++ fotg210->phy = devm_usb_get_phy_by_phandle(dev, "usb-phy", 0); + if (IS_ERR(fotg210->phy)) { + ret = PTR_ERR(fotg210->phy); + if (ret == -EPROBE_DEFER) +@@ -1282,7 +1283,7 @@ int fotg210_udc_probe(struct platform_de + + fotg210_disable_unplug(fotg210); + +- ret = request_irq(ires->start, fotg210_irq, IRQF_SHARED, ++ ret = request_irq(irq, fotg210_irq, IRQF_SHARED, + udc_name, fotg210); + if (ret < 0) { + dev_err(dev, "request_irq error (%d)\n", ret); +@@ -1303,7 +1304,7 @@ int fotg210_udc_probe(struct platform_de + err_add_udc: + if (!IS_ERR_OR_NULL(fotg210->phy)) + usb_unregister_notifier(fotg210->phy, &fotg210_phy_notifier); +- free_irq(ires->start, fotg210); ++ free_irq(irq, fotg210); + + err_req: + fotg210_ep_free_request(&fotg210->ep[0]->ep, fotg210->ep0_req); diff --git a/target/linux/gemini/patches-6.1/0012-usb-fotg210-udc-Remove-a-useless-assignment.patch b/target/linux/gemini/patches-6.1/0012-usb-fotg210-udc-Remove-a-useless-assignment.patch new file mode 100644 index 0000000000..8c33c50b2c --- /dev/null +++ b/target/linux/gemini/patches-6.1/0012-usb-fotg210-udc-Remove-a-useless-assignment.patch @@ -0,0 +1,39 @@ +From 7889a2f0256c55e0184dffd0001d0782f9e4cb83 Mon Sep 17 00:00:00 2001 +From: Christophe JAILLET +Date: Mon, 14 Nov 2022 21:38:04 +0100 +Subject: [PATCH 12/29] usb: fotg210-udc: Remove a useless assignment + +There is no need to use an intermediate array for these memory allocations, +so, axe it. + +While at it, turn a '== NULL' into a shorter '!' when testing memory +allocation failure. + +Signed-off-by: Christophe JAILLET +Reviewed-by: Linus Walleij +Link: https://lore.kernel.org/r/deab9696fc4000499470e7ccbca7c36fca17bd4e.1668458274.git.christophe.jaillet@wanadoo.fr +Signed-off-by: Greg Kroah-Hartman +--- +--- a/drivers/usb/fotg210/fotg210-udc.c ++++ b/drivers/usb/fotg210/fotg210-udc.c +@@ -1159,7 +1159,6 @@ int fotg210_udc_probe(struct platform_de + { + struct resource *res; + struct fotg210_udc *fotg210 = NULL; +- struct fotg210_ep *_ep[FOTG210_MAX_NUM_EP]; + struct device *dev = &pdev->dev; + int irq; + int ret = 0; +@@ -1218,10 +1217,9 @@ int fotg210_udc_probe(struct platform_de + } + + for (i = 0; i < FOTG210_MAX_NUM_EP; i++) { +- _ep[i] = kzalloc(sizeof(struct fotg210_ep), GFP_KERNEL); +- if (_ep[i] == NULL) ++ fotg210->ep[i] = kzalloc(sizeof(struct fotg210_ep), GFP_KERNEL); ++ if (!fotg210->ep[i]) + goto err_alloc; +- fotg210->ep[i] = _ep[i]; + } + + fotg210->reg = ioremap(res->start, resource_size(res)); diff --git a/target/linux/gemini/patches-6.1/0013-usb-fotg210-udc-fix-potential-memory-leak-in-fotg210.patch b/target/linux/gemini/patches-6.1/0013-usb-fotg210-udc-fix-potential-memory-leak-in-fotg210.patch new file mode 100644 index 0000000000..178135662f --- /dev/null +++ b/target/linux/gemini/patches-6.1/0013-usb-fotg210-udc-fix-potential-memory-leak-in-fotg210.patch @@ -0,0 +1,58 @@ +From 7b95ade85ac18eec63e81ac58a482b3e88361ffd Mon Sep 17 00:00:00 2001 +From: Yi Yang +Date: Fri, 2 Dec 2022 09:21:26 +0800 +Subject: [PATCH 13/29] usb: fotg210-udc: fix potential memory leak in + fotg210_udc_probe() +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +In fotg210_udc_probe(), if devm_clk_get() or clk_prepare_enable() +fails, 'fotg210' will not be freed, which will lead to a memory leak. +Fix it by moving kfree() to a proper location. + +In addition,we can use "return -ENOMEM" instead of "goto err" +to simplify the code. + +Fixes: 718a38d092ec ("fotg210-udc: Handle PCLK") +Reviewed-by: Andrzej Pietrasiewicz +Reviewed-by: Linus Walleij +Signed-off-by: Yi Yang +Link: https://lore.kernel.org/r/20221202012126.246953-1-yiyang13@huawei.com +Signed-off-by: Greg Kroah-Hartman +--- +--- a/drivers/usb/fotg210/fotg210-udc.c ++++ b/drivers/usb/fotg210/fotg210-udc.c +@@ -1176,12 +1176,10 @@ int fotg210_udc_probe(struct platform_de + return -ENODEV; + } + +- ret = -ENOMEM; +- + /* initialize udc */ + fotg210 = kzalloc(sizeof(struct fotg210_udc), GFP_KERNEL); + if (fotg210 == NULL) +- goto err; ++ return -ENOMEM; + + fotg210->dev = dev; + +@@ -1191,7 +1189,7 @@ int fotg210_udc_probe(struct platform_de + ret = clk_prepare_enable(fotg210->pclk); + if (ret) { + dev_err(dev, "failed to enable PCLK\n"); +- return ret; ++ goto err; + } + } else if (PTR_ERR(fotg210->pclk) == -EPROBE_DEFER) { + /* +@@ -1317,8 +1315,7 @@ err_pclk: + if (!IS_ERR(fotg210->pclk)) + clk_disable_unprepare(fotg210->pclk); + +- kfree(fotg210); +- + err: ++ kfree(fotg210); + return ret; + } diff --git a/target/linux/gemini/patches-6.1/0014-usb-fotg210-fix-OTG-only-build.patch b/target/linux/gemini/patches-6.1/0014-usb-fotg210-fix-OTG-only-build.patch new file mode 100644 index 0000000000..acdf1796f3 --- /dev/null +++ b/target/linux/gemini/patches-6.1/0014-usb-fotg210-fix-OTG-only-build.patch @@ -0,0 +1,39 @@ +From d8eed400495029ba551704ff0fae1dad87332291 Mon Sep 17 00:00:00 2001 +From: Arnd Bergmann +Date: Thu, 15 Dec 2022 17:57:20 +0100 +Subject: [PATCH 14/29] usb: fotg210: fix OTG-only build + +The fotg210 module combines the HCD and OTG drivers, which then +fails to build when only the USB gadget support is enabled +in the kernel but host support is not: + +aarch64-linux-ld: drivers/usb/fotg210/fotg210-core.o: in function `fotg210_init': +fotg210-core.c:(.init.text+0xc): undefined reference to `usb_disabled' + +Move the check for usb_disabled() after the check for the HCD module, +and let the OTG driver still be probed in this configuration. + +A nicer approach might be to have the common portion built as a +library module, with the two platform other files registering +their own platform_driver instances separately. + +Fixes: ddacd6ef44ca ("usb: fotg210: Fix Kconfig for USB host modules") +Reviewed-by: Linus Walleij +Signed-off-by: Arnd Bergmann +Link: https://lore.kernel.org/r/20221215165728.2062984-1-arnd@kernel.org +Signed-off-by: Greg Kroah-Hartman +--- +--- a/drivers/usb/fotg210/fotg210-core.c ++++ b/drivers/usb/fotg210/fotg210-core.c +@@ -144,10 +144,7 @@ static struct platform_driver fotg210_dr + + static int __init fotg210_init(void) + { +- if (usb_disabled()) +- return -ENODEV; +- +- if (IS_ENABLED(CONFIG_USB_FOTG210_HCD)) ++ if (IS_ENABLED(CONFIG_USB_FOTG210_HCD) && !usb_disabled()) + fotg210_hcd_init(); + return platform_driver_register(&fotg210_driver); + } diff --git a/target/linux/gemini/patches-6.1/0015-usb-fotg210-udc-fix-error-return-code-in-fotg210_udc.patch b/target/linux/gemini/patches-6.1/0015-usb-fotg210-udc-fix-error-return-code-in-fotg210_udc.patch new file mode 100644 index 0000000000..a9bbca58b4 --- /dev/null +++ b/target/linux/gemini/patches-6.1/0015-usb-fotg210-udc-fix-error-return-code-in-fotg210_udc.patch @@ -0,0 +1,28 @@ +From eaaa85d907fe27852dd960b2bc5d7bcf11bc3ebd Mon Sep 17 00:00:00 2001 +From: Yang Yingliang +Date: Fri, 30 Dec 2022 14:54:27 +0800 +Subject: [PATCH 15/29] usb: fotg210-udc: fix error return code in + fotg210_udc_probe() + +After commit 5f217ccd520f ("fotg210-udc: Support optional external PHY"), +the error code is re-assigned to 0 in fotg210_udc_probe(), if allocate or +map memory fails after the assignment, it can't return an error code. Set +the error code to -ENOMEM to fix this problem. + +Fixes: 5f217ccd520f ("fotg210-udc: Support optional external PHY") +Signed-off-by: Yang Yingliang +Reviewed-by: Linus Walleij +Link: https://lore.kernel.org/r/20221230065427.944586-1-yangyingliang@huawei.com +Signed-off-by: Greg Kroah-Hartman +--- +--- a/drivers/usb/fotg210/fotg210-udc.c ++++ b/drivers/usb/fotg210/fotg210-udc.c +@@ -1214,6 +1214,8 @@ int fotg210_udc_probe(struct platform_de + dev_info(dev, "found and initialized PHY\n"); + } + ++ ret = -ENOMEM; ++ + for (i = 0; i < FOTG210_MAX_NUM_EP; i++) { + fotg210->ep[i] = kzalloc(sizeof(struct fotg210_ep), GFP_KERNEL); + if (!fotg210->ep[i]) diff --git a/target/linux/gemini/patches-6.1/0016-usb-fotg210-List-different-variants.patch b/target/linux/gemini/patches-6.1/0016-usb-fotg210-List-different-variants.patch new file mode 100644 index 0000000000..6ff6d28ad3 --- /dev/null +++ b/target/linux/gemini/patches-6.1/0016-usb-fotg210-List-different-variants.patch @@ -0,0 +1,25 @@ +From 407577548b2fcd41cc72ee05df1f05a430ed30a0 Mon Sep 17 00:00:00 2001 +From: Linus Walleij +Date: Wed, 18 Jan 2023 08:09:16 +0100 +Subject: [PATCH 16/29] usb: fotg210: List different variants + +There are at least two variants of the FOTG: FOTG200 and +FOTG210. Handle them in this driver and let's add +more quirks as we go along. + +Signed-off-by: Linus Walleij +Link: https://lore.kernel.org/r/20230103-gemini-fotg210-usb-v2-2-100388af9810@linaro.org +Signed-off-by: Greg Kroah-Hartman +--- +--- a/drivers/usb/fotg210/fotg210-core.c ++++ b/drivers/usb/fotg210/fotg210-core.c +@@ -127,7 +127,9 @@ static int fotg210_remove(struct platfor + + #ifdef CONFIG_OF + static const struct of_device_id fotg210_of_match[] = { ++ { .compatible = "faraday,fotg200" }, + { .compatible = "faraday,fotg210" }, ++ /* TODO: can we also handle FUSB220? */ + {}, + }; + MODULE_DEVICE_TABLE(of, fotg210_of_match); diff --git a/target/linux/gemini/patches-6.1/0017-usb-fotg210-Acquire-memory-resource-in-core.patch b/target/linux/gemini/patches-6.1/0017-usb-fotg210-Acquire-memory-resource-in-core.patch new file mode 100644 index 0000000000..7dbd511ecb --- /dev/null +++ b/target/linux/gemini/patches-6.1/0017-usb-fotg210-Acquire-memory-resource-in-core.patch @@ -0,0 +1,245 @@ +From fa735ad1afeb5791d5562617b9bbed74574d3e81 Mon Sep 17 00:00:00 2001 +From: Linus Walleij +Date: Wed, 18 Jan 2023 08:09:17 +0100 +Subject: [PATCH 17/29] usb: fotg210: Acquire memory resource in core + +The subdrivers are obtaining and mapping the memory resource +separately. Create a common state container for the shared +resources and start populating this by acquiring the IO +memory resource and remap it and pass this to the subdrivers +for host and peripheral. + +Signed-off-by: Linus Walleij +Link: https://lore.kernel.org/r/20230103-gemini-fotg210-usb-v2-3-100388af9810@linaro.org +Signed-off-by: Greg Kroah-Hartman +--- +--- a/drivers/usb/fotg210/fotg210-core.c ++++ b/drivers/usb/fotg210/fotg210-core.c +@@ -33,9 +33,10 @@ + #define GEMINI_MISC_USB0_MINI_B BIT(29) + #define GEMINI_MISC_USB1_MINI_B BIT(30) + +-static int fotg210_gemini_init(struct device *dev, struct resource *res, ++static int fotg210_gemini_init(struct fotg210 *fotg, struct resource *res, + enum usb_dr_mode mode) + { ++ struct device *dev = fotg->dev; + struct device_node *np = dev->of_node; + struct regmap *map; + bool wakeup; +@@ -47,6 +48,7 @@ static int fotg210_gemini_init(struct de + dev_err(dev, "no syscon\n"); + return PTR_ERR(map); + } ++ fotg->map = map; + wakeup = of_property_read_bool(np, "wakeup-source"); + + /* +@@ -55,6 +57,7 @@ static int fotg210_gemini_init(struct de + */ + mask = 0; + if (res->start == 0x69000000) { ++ fotg->port = GEMINI_PORT_1; + mask = GEMINI_MISC_USB1_VBUS_ON | GEMINI_MISC_USB1_MINI_B | + GEMINI_MISC_USB1_WAKEUP; + if (mode == USB_DR_MODE_HOST) +@@ -64,6 +67,7 @@ static int fotg210_gemini_init(struct de + if (wakeup) + val |= GEMINI_MISC_USB1_WAKEUP; + } else { ++ fotg->port = GEMINI_PORT_0; + mask = GEMINI_MISC_USB0_VBUS_ON | GEMINI_MISC_USB0_MINI_B | + GEMINI_MISC_USB0_WAKEUP; + if (mode == USB_DR_MODE_HOST) +@@ -89,23 +93,34 @@ static int fotg210_probe(struct platform + { + struct device *dev = &pdev->dev; + enum usb_dr_mode mode; ++ struct fotg210 *fotg; + int ret; + ++ fotg = devm_kzalloc(dev, sizeof(*fotg), GFP_KERNEL); ++ if (!fotg) ++ return -ENOMEM; ++ fotg->dev = dev; ++ ++ fotg->res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ if (!fotg->res) ++ return -ENODEV; ++ ++ fotg->base = devm_ioremap_resource(dev, fotg->res); ++ if (!fotg->base) ++ return -ENOMEM; ++ + mode = usb_get_dr_mode(dev); + + if (of_device_is_compatible(dev->of_node, "cortina,gemini-usb")) { +- struct resource *res; +- +- res = platform_get_resource(pdev, IORESOURCE_MEM, 0); +- ret = fotg210_gemini_init(dev, res, mode); ++ ret = fotg210_gemini_init(fotg, fotg->res, mode); + if (ret) + return ret; + } + + if (mode == USB_DR_MODE_PERIPHERAL) +- ret = fotg210_udc_probe(pdev); ++ ret = fotg210_udc_probe(pdev, fotg); + else +- ret = fotg210_hcd_probe(pdev); ++ ret = fotg210_hcd_probe(pdev, fotg); + + return ret; + } +--- a/drivers/usb/fotg210/fotg210-hcd.c ++++ b/drivers/usb/fotg210/fotg210-hcd.c +@@ -5557,11 +5557,10 @@ static void fotg210_init(struct fotg210_ + * then invokes the start() method for the HCD associated with it + * through the hotplug entry's driver_data. + */ +-int fotg210_hcd_probe(struct platform_device *pdev) ++int fotg210_hcd_probe(struct platform_device *pdev, struct fotg210 *fotg) + { + struct device *dev = &pdev->dev; + struct usb_hcd *hcd; +- struct resource *res; + int irq; + int retval; + struct fotg210_hcd *fotg210; +@@ -5585,18 +5584,14 @@ int fotg210_hcd_probe(struct platform_de + + hcd->has_tt = 1; + +- res = platform_get_resource(pdev, IORESOURCE_MEM, 0); +- hcd->regs = devm_ioremap_resource(&pdev->dev, res); +- if (IS_ERR(hcd->regs)) { +- retval = PTR_ERR(hcd->regs); +- goto failed_put_hcd; +- } ++ hcd->regs = fotg->base; + +- hcd->rsrc_start = res->start; +- hcd->rsrc_len = resource_size(res); ++ hcd->rsrc_start = fotg->res->start; ++ hcd->rsrc_len = resource_size(fotg->res); + + fotg210 = hcd_to_fotg210(hcd); + ++ fotg210->fotg = fotg; + fotg210->caps = hcd->regs; + + /* It's OK not to supply this clock */ +--- a/drivers/usb/fotg210/fotg210-hcd.h ++++ b/drivers/usb/fotg210/fotg210-hcd.h +@@ -182,6 +182,7 @@ struct fotg210_hcd { /* one per contro + # define INCR(x) do {} while (0) + #endif + ++ struct fotg210 *fotg; /* Overarching FOTG210 device */ + /* silicon clock */ + struct clk *pclk; + }; +--- a/drivers/usb/fotg210/fotg210-udc.c ++++ b/drivers/usb/fotg210/fotg210-udc.c +@@ -1155,21 +1155,14 @@ int fotg210_udc_remove(struct platform_d + return 0; + } + +-int fotg210_udc_probe(struct platform_device *pdev) ++int fotg210_udc_probe(struct platform_device *pdev, struct fotg210 *fotg) + { +- struct resource *res; + struct fotg210_udc *fotg210 = NULL; + struct device *dev = &pdev->dev; + int irq; + int ret = 0; + int i; + +- res = platform_get_resource(pdev, IORESOURCE_MEM, 0); +- if (!res) { +- pr_err("platform_get_resource error.\n"); +- return -ENODEV; +- } +- + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + pr_err("could not get irq\n"); +@@ -1182,6 +1175,7 @@ int fotg210_udc_probe(struct platform_de + return -ENOMEM; + + fotg210->dev = dev; ++ fotg210->fotg = fotg; + + /* It's OK not to supply this clock */ + fotg210->pclk = devm_clk_get(dev, "PCLK"); +@@ -1222,11 +1216,7 @@ int fotg210_udc_probe(struct platform_de + goto err_alloc; + } + +- fotg210->reg = ioremap(res->start, resource_size(res)); +- if (fotg210->reg == NULL) { +- dev_err(dev, "ioremap error\n"); +- goto err_alloc; +- } ++ fotg210->reg = fotg->base; + + spin_lock_init(&fotg210->lock); + +--- a/drivers/usb/fotg210/fotg210-udc.h ++++ b/drivers/usb/fotg210/fotg210-udc.h +@@ -236,6 +236,7 @@ struct fotg210_udc { + unsigned long irq_trigger; + + struct device *dev; ++ struct fotg210 *fotg; + struct usb_phy *phy; + struct usb_gadget gadget; + struct usb_gadget_driver *driver; +--- a/drivers/usb/fotg210/fotg210.h ++++ b/drivers/usb/fotg210/fotg210.h +@@ -2,13 +2,28 @@ + #ifndef __FOTG210_H + #define __FOTG210_H + ++enum gemini_port { ++ GEMINI_PORT_NONE = 0, ++ GEMINI_PORT_0, ++ GEMINI_PORT_1, ++}; ++ ++struct fotg210 { ++ struct device *dev; ++ struct resource *res; ++ void __iomem *base; ++ struct regmap *map; ++ enum gemini_port port; ++}; ++ + #ifdef CONFIG_USB_FOTG210_HCD +-int fotg210_hcd_probe(struct platform_device *pdev); ++int fotg210_hcd_probe(struct platform_device *pdev, struct fotg210 *fotg); + int fotg210_hcd_remove(struct platform_device *pdev); + int fotg210_hcd_init(void); + void fotg210_hcd_cleanup(void); + #else +-static inline int fotg210_hcd_probe(struct platform_device *pdev) ++static inline int fotg210_hcd_probe(struct platform_device *pdev, ++ struct fotg210 *fotg) + { + return 0; + } +@@ -26,10 +41,11 @@ static inline void fotg210_hcd_cleanup(v + #endif + + #ifdef CONFIG_USB_FOTG210_UDC +-int fotg210_udc_probe(struct platform_device *pdev); ++int fotg210_udc_probe(struct platform_device *pdev, struct fotg210 *fotg); + int fotg210_udc_remove(struct platform_device *pdev); + #else +-static inline int fotg210_udc_probe(struct platform_device *pdev) ++static inline int fotg210_udc_probe(struct platform_device *pdev, ++ struct fotg210 *fotg) + { + return 0; + } diff --git a/target/linux/gemini/patches-6.1/0018-usb-fotg210-Move-clock-handling-to-core.patch b/target/linux/gemini/patches-6.1/0018-usb-fotg210-Move-clock-handling-to-core.patch new file mode 100644 index 0000000000..9894f4dc66 --- /dev/null +++ b/target/linux/gemini/patches-6.1/0018-usb-fotg210-Move-clock-handling-to-core.patch @@ -0,0 +1,196 @@ +From fb8e1e8dbc47e7aff7624b47adaa0a84d2983802 Mon Sep 17 00:00:00 2001 +From: Linus Walleij +Date: Wed, 18 Jan 2023 08:09:18 +0100 +Subject: [PATCH 18/29] usb: fotg210: Move clock handling to core + +Grab the optional silicon block clock, prepare and enable it in +the core before proceeding to prepare the host or peripheral +driver. This saves duplicate code and also uses the simple +devm_clk_get_optional_enabled() to do everything we really +want to do. + +Signed-off-by: Linus Walleij +Link: https://lore.kernel.org/r/20230103-gemini-fotg210-usb-v2-4-100388af9810@linaro.org +Signed-off-by: Greg Kroah-Hartman +--- +--- a/drivers/usb/fotg210/fotg210-core.c ++++ b/drivers/usb/fotg210/fotg210-core.c +@@ -6,6 +6,7 @@ + * driver. + */ + #include ++#include + #include + #include + #include +@@ -109,6 +110,10 @@ static int fotg210_probe(struct platform + if (!fotg->base) + return -ENOMEM; + ++ fotg->pclk = devm_clk_get_optional_enabled(dev, "PCLK"); ++ if (IS_ERR(fotg->pclk)) ++ return PTR_ERR(fotg->pclk); ++ + mode = usb_get_dr_mode(dev); + + if (of_device_is_compatible(dev->of_node, "cortina,gemini-usb")) { +--- a/drivers/usb/fotg210/fotg210-hcd.c ++++ b/drivers/usb/fotg210/fotg210-hcd.c +@@ -33,7 +33,6 @@ + #include + #include + #include +-#include + + #include + #include +@@ -5594,44 +5593,22 @@ int fotg210_hcd_probe(struct platform_de + fotg210->fotg = fotg; + fotg210->caps = hcd->regs; + +- /* It's OK not to supply this clock */ +- fotg210->pclk = clk_get(dev, "PCLK"); +- if (!IS_ERR(fotg210->pclk)) { +- retval = clk_prepare_enable(fotg210->pclk); +- if (retval) { +- dev_err(dev, "failed to enable PCLK\n"); +- goto failed_put_hcd; +- } +- } else if (PTR_ERR(fotg210->pclk) == -EPROBE_DEFER) { +- /* +- * Percolate deferrals, for anything else, +- * just live without the clocking. +- */ +- retval = PTR_ERR(fotg210->pclk); +- goto failed_dis_clk; +- } +- + retval = fotg210_setup(hcd); + if (retval) +- goto failed_dis_clk; ++ goto failed_put_hcd; + + fotg210_init(fotg210); + + retval = usb_add_hcd(hcd, irq, IRQF_SHARED); + if (retval) { + dev_err(dev, "failed to add hcd with err %d\n", retval); +- goto failed_dis_clk; ++ goto failed_put_hcd; + } + device_wakeup_enable(hcd->self.controller); + platform_set_drvdata(pdev, hcd); + + return retval; + +-failed_dis_clk: +- if (!IS_ERR(fotg210->pclk)) { +- clk_disable_unprepare(fotg210->pclk); +- clk_put(fotg210->pclk); +- } + failed_put_hcd: + usb_put_hcd(hcd); + fail_create_hcd: +@@ -5647,12 +5624,6 @@ fail_create_hcd: + int fotg210_hcd_remove(struct platform_device *pdev) + { + struct usb_hcd *hcd = platform_get_drvdata(pdev); +- struct fotg210_hcd *fotg210 = hcd_to_fotg210(hcd); +- +- if (!IS_ERR(fotg210->pclk)) { +- clk_disable_unprepare(fotg210->pclk); +- clk_put(fotg210->pclk); +- } + + usb_remove_hcd(hcd); + usb_put_hcd(hcd); +--- a/drivers/usb/fotg210/fotg210-udc.c ++++ b/drivers/usb/fotg210/fotg210-udc.c +@@ -15,7 +15,6 @@ + #include + #include + #include +-#include + #include + #include + +@@ -1147,9 +1146,6 @@ int fotg210_udc_remove(struct platform_d + for (i = 0; i < FOTG210_MAX_NUM_EP; i++) + kfree(fotg210->ep[i]); + +- if (!IS_ERR(fotg210->pclk)) +- clk_disable_unprepare(fotg210->pclk); +- + kfree(fotg210); + + return 0; +@@ -1177,34 +1173,17 @@ int fotg210_udc_probe(struct platform_de + fotg210->dev = dev; + fotg210->fotg = fotg; + +- /* It's OK not to supply this clock */ +- fotg210->pclk = devm_clk_get(dev, "PCLK"); +- if (!IS_ERR(fotg210->pclk)) { +- ret = clk_prepare_enable(fotg210->pclk); +- if (ret) { +- dev_err(dev, "failed to enable PCLK\n"); +- goto err; +- } +- } else if (PTR_ERR(fotg210->pclk) == -EPROBE_DEFER) { +- /* +- * Percolate deferrals, for anything else, +- * just live without the clocking. +- */ +- ret = -EPROBE_DEFER; +- goto err; +- } +- + fotg210->phy = devm_usb_get_phy_by_phandle(dev, "usb-phy", 0); + if (IS_ERR(fotg210->phy)) { + ret = PTR_ERR(fotg210->phy); + if (ret == -EPROBE_DEFER) +- goto err_pclk; ++ goto err_free; + dev_info(dev, "no PHY found\n"); + fotg210->phy = NULL; + } else { + ret = usb_phy_init(fotg210->phy); + if (ret) +- goto err_pclk; ++ goto err_free; + dev_info(dev, "found and initialized PHY\n"); + } + +@@ -1303,11 +1282,8 @@ err_map: + err_alloc: + for (i = 0; i < FOTG210_MAX_NUM_EP; i++) + kfree(fotg210->ep[i]); +-err_pclk: +- if (!IS_ERR(fotg210->pclk)) +- clk_disable_unprepare(fotg210->pclk); + +-err: ++err_free: + kfree(fotg210); + return ret; + } +--- a/drivers/usb/fotg210/fotg210-udc.h ++++ b/drivers/usb/fotg210/fotg210-udc.h +@@ -231,7 +231,6 @@ struct fotg210_ep { + struct fotg210_udc { + spinlock_t lock; /* protect the struct */ + void __iomem *reg; +- struct clk *pclk; + + unsigned long irq_trigger; + +--- a/drivers/usb/fotg210/fotg210.h ++++ b/drivers/usb/fotg210/fotg210.h +@@ -12,6 +12,7 @@ struct fotg210 { + struct device *dev; + struct resource *res; + void __iomem *base; ++ struct clk *pclk; + struct regmap *map; + enum gemini_port port; + }; diff --git a/target/linux/gemini/patches-6.1/0019-usb-fotg210-Check-role-register-in-core.patch b/target/linux/gemini/patches-6.1/0019-usb-fotg210-Check-role-register-in-core.patch new file mode 100644 index 0000000000..892b0d31af --- /dev/null +++ b/target/linux/gemini/patches-6.1/0019-usb-fotg210-Check-role-register-in-core.patch @@ -0,0 +1,54 @@ +From b1b07abb598211de3ce7f52abdf8dcb24384341e Mon Sep 17 00:00:00 2001 +From: Linus Walleij +Date: Wed, 18 Jan 2023 08:09:19 +0100 +Subject: [PATCH 19/29] usb: fotg210: Check role register in core + +Read the role register and check that we are in host/peripheral +mode and issue warnings if we're not in the right role when +probing respective driver. + +Signed-off-by: Linus Walleij +Link: https://lore.kernel.org/r/20230103-gemini-fotg210-usb-v2-5-100388af9810@linaro.org +Signed-off-by: Greg Kroah-Hartman +--- +--- a/drivers/usb/fotg210/fotg210-core.c ++++ b/drivers/usb/fotg210/fotg210-core.c +@@ -18,6 +18,11 @@ + + #include "fotg210.h" + ++/* Role Register 0x80 */ ++#define FOTG210_RR 0x80 ++#define FOTG210_RR_ID BIT(21) /* 1 = B-device, 0 = A-device */ ++#define FOTG210_RR_CROLE BIT(20) /* 1 = device, 0 = host */ ++ + /* + * Gemini-specific initialization function, only executed on the + * Gemini SoC using the global misc control register. +@@ -95,6 +100,7 @@ static int fotg210_probe(struct platform + struct device *dev = &pdev->dev; + enum usb_dr_mode mode; + struct fotg210 *fotg; ++ u32 val; + int ret; + + fotg = devm_kzalloc(dev, sizeof(*fotg), GFP_KERNEL); +@@ -122,10 +128,16 @@ static int fotg210_probe(struct platform + return ret; + } + +- if (mode == USB_DR_MODE_PERIPHERAL) ++ val = readl(fotg->base + FOTG210_RR); ++ if (mode == USB_DR_MODE_PERIPHERAL) { ++ if (!(val & FOTG210_RR_CROLE)) ++ dev_err(dev, "block not in device role\n"); + ret = fotg210_udc_probe(pdev, fotg); +- else ++ } else { ++ if (val & FOTG210_RR_CROLE) ++ dev_err(dev, "block not in host role\n"); + ret = fotg210_hcd_probe(pdev, fotg); ++ } + + return ret; + } diff --git a/target/linux/gemini/patches-6.1/0020-usb-fotg210-udc-Assign-of_node-and-speed-on-start.patch b/target/linux/gemini/patches-6.1/0020-usb-fotg210-udc-Assign-of_node-and-speed-on-start.patch new file mode 100644 index 0000000000..20f8f94350 --- /dev/null +++ b/target/linux/gemini/patches-6.1/0020-usb-fotg210-udc-Assign-of_node-and-speed-on-start.patch @@ -0,0 +1,34 @@ +From d7c2b0b6da75b86cf5ddbcd51a74d74e19bbf178 Mon Sep 17 00:00:00 2001 +From: Linus Walleij +Date: Wed, 18 Jan 2023 08:09:20 +0100 +Subject: [PATCH 20/29] usb: fotg210-udc: Assign of_node and speed on start + +Follow the example set by other drivers to assign of_node +and speed to the driver when binding, also print bound +info akin to other UDC drivers. + +Signed-off-by: Linus Walleij +Link: https://lore.kernel.org/r/20230103-gemini-fotg210-usb-v2-6-100388af9810@linaro.org +Signed-off-by: Greg Kroah-Hartman +--- +--- a/drivers/usb/fotg210/fotg210-udc.c ++++ b/drivers/usb/fotg210/fotg210-udc.c +@@ -1028,6 +1028,10 @@ static int fotg210_udc_start(struct usb_ + + /* hook up the driver */ + fotg210->driver = driver; ++ fotg210->gadget.dev.of_node = fotg210->dev->of_node; ++ fotg210->gadget.speed = USB_SPEED_UNKNOWN; ++ ++ dev_info(fotg210->dev, "bound driver %s\n", driver->driver.name); + + if (!IS_ERR_OR_NULL(fotg210->phy)) { + ret = otg_set_peripheral(fotg210->phy->otg, +@@ -1084,6 +1088,7 @@ static int fotg210_udc_stop(struct usb_g + + fotg210_init(fotg210); + fotg210->driver = NULL; ++ fotg210->gadget.speed = USB_SPEED_UNKNOWN; + + spin_unlock_irqrestore(&fotg210->lock, flags); + diff --git a/target/linux/gemini/patches-6.1/0021-usb-fotg210-udc-Implement-VBUS-session.patch b/target/linux/gemini/patches-6.1/0021-usb-fotg210-udc-Implement-VBUS-session.patch new file mode 100644 index 0000000000..d98561f0d4 --- /dev/null +++ b/target/linux/gemini/patches-6.1/0021-usb-fotg210-udc-Implement-VBUS-session.patch @@ -0,0 +1,96 @@ +From 2fbbfb2c556944945639b17b13fcb1e05272b646 Mon Sep 17 00:00:00 2001 +From: Linus Walleij +Date: Wed, 18 Jan 2023 08:09:21 +0100 +Subject: [PATCH 21/29] usb: fotg210-udc: Implement VBUS session + +Implement VBUS session handling for FOTG210. This is +mainly used by the UDC driver which needs to call down to +the FOTG210 core and enable/disable VBUS, as this needs to be +handled outside of the HCD and UDC drivers, by platform +specific glue code. + +The Gemini has a special bit in a system register to turn +VBUS on and off so we implement this in the FOTG210 core. + +Signed-off-by: Linus Walleij +Link: https://lore.kernel.org/r/20230103-gemini-fotg210-usb-v2-7-100388af9810@linaro.org +Signed-off-by: Greg Kroah-Hartman +--- +--- a/drivers/usb/fotg210/fotg210-core.c ++++ b/drivers/usb/fotg210/fotg210-core.c +@@ -95,6 +95,35 @@ static int fotg210_gemini_init(struct fo + return 0; + } + ++/** ++ * fotg210_vbus() - Called by gadget driver to enable/disable VBUS ++ * @enable: true to enable VBUS, false to disable VBUS ++ */ ++void fotg210_vbus(struct fotg210 *fotg, bool enable) ++{ ++ u32 mask; ++ u32 val; ++ int ret; ++ ++ switch (fotg->port) { ++ case GEMINI_PORT_0: ++ mask = GEMINI_MISC_USB0_VBUS_ON; ++ val = enable ? GEMINI_MISC_USB0_VBUS_ON : 0; ++ break; ++ case GEMINI_PORT_1: ++ mask = GEMINI_MISC_USB1_VBUS_ON; ++ val = enable ? GEMINI_MISC_USB1_VBUS_ON : 0; ++ break; ++ default: ++ return; ++ } ++ ret = regmap_update_bits(fotg->map, GEMINI_GLOBAL_MISC_CTRL, mask, val); ++ if (ret) ++ dev_err(fotg->dev, "failed to %s VBUS\n", ++ enable ? "enable" : "disable"); ++ dev_info(fotg->dev, "%s: %s VBUS\n", __func__, enable ? "enable" : "disable"); ++} ++ + static int fotg210_probe(struct platform_device *pdev) + { + struct device *dev = &pdev->dev; +--- a/drivers/usb/fotg210/fotg210-udc.c ++++ b/drivers/usb/fotg210/fotg210-udc.c +@@ -1095,9 +1095,26 @@ static int fotg210_udc_stop(struct usb_g + return 0; + } + ++/** ++ * fotg210_vbus_session - Called by external transceiver to enable/disable udc ++ * @_gadget: usb gadget ++ * @is_active: 0 if should disable UDC VBUS, 1 if should enable ++ * ++ * Returns 0 ++ */ ++static int fotg210_vbus_session(struct usb_gadget *g, int is_active) ++{ ++ struct fotg210_udc *fotg210 = gadget_to_fotg210(g); ++ ++ /* Call down to core integration layer to drive or disable VBUS */ ++ fotg210_vbus(fotg210->fotg, is_active); ++ return 0; ++} ++ + static const struct usb_gadget_ops fotg210_gadget_ops = { + .udc_start = fotg210_udc_start, + .udc_stop = fotg210_udc_stop, ++ .vbus_session = fotg210_vbus_session, + }; + + /** +--- a/drivers/usb/fotg210/fotg210.h ++++ b/drivers/usb/fotg210/fotg210.h +@@ -17,6 +17,8 @@ struct fotg210 { + enum gemini_port port; + }; + ++void fotg210_vbus(struct fotg210 *fotg, bool enable); ++ + #ifdef CONFIG_USB_FOTG210_HCD + int fotg210_hcd_probe(struct platform_device *pdev, struct fotg210 *fotg); + int fotg210_hcd_remove(struct platform_device *pdev); diff --git a/target/linux/gemini/patches-6.1/0022-fotg210-udc-Introduce-and-use-a-fotg210_ack_int-func.patch b/target/linux/gemini/patches-6.1/0022-fotg210-udc-Introduce-and-use-a-fotg210_ack_int-func.patch new file mode 100644 index 0000000000..fc5831eb23 --- /dev/null +++ b/target/linux/gemini/patches-6.1/0022-fotg210-udc-Introduce-and-use-a-fotg210_ack_int-func.patch @@ -0,0 +1,134 @@ +From f011d1eab23f4c063c5441c0d5a22898adf9145c Mon Sep 17 00:00:00 2001 +From: Fabian Vogt +Date: Mon, 23 Jan 2023 08:35:07 +0100 +Subject: [PATCH 22/29] fotg210-udc: Introduce and use a fotg210_ack_int + function + +This is in preparation of support for devices where interrupts are acked +differently. + +Signed-off-by: Fabian Vogt +Signed-off-by: Linus Walleij +Link: https://lore.kernel.org/r/20230123073508.2350402-3-linus.walleij@linaro.org +Signed-off-by: Greg Kroah-Hartman +--- +--- a/drivers/usb/fotg210/fotg210-udc.c ++++ b/drivers/usb/fotg210/fotg210-udc.c +@@ -28,6 +28,14 @@ static const char udc_name[] = "fotg210_ + static const char * const fotg210_ep_name[] = { + "ep0", "ep1", "ep2", "ep3", "ep4"}; + ++static void fotg210_ack_int(struct fotg210_udc *fotg210, u32 offset, u32 mask) ++{ ++ u32 value = ioread32(fotg210->reg + offset); ++ ++ value &= ~mask; ++ iowrite32(value, fotg210->reg + offset); ++} ++ + static void fotg210_disable_fifo_int(struct fotg210_ep *ep) + { + u32 value = ioread32(ep->fotg210->reg + FOTG210_DMISGR1); +@@ -303,8 +311,7 @@ static void fotg210_wait_dma_done(struct + goto dma_reset; + } while (!(value & DISGR2_DMA_CMPLT)); + +- value &= ~DISGR2_DMA_CMPLT; +- iowrite32(value, ep->fotg210->reg + FOTG210_DISGR2); ++ fotg210_ack_int(ep->fotg210, FOTG210_DISGR2, DISGR2_DMA_CMPLT); + return; + + dma_reset: +@@ -844,14 +851,6 @@ static void fotg210_ep0in(struct fotg210 + } + } + +-static void fotg210_clear_comabt_int(struct fotg210_udc *fotg210) +-{ +- u32 value = ioread32(fotg210->reg + FOTG210_DISGR0); +- +- value &= ~DISGR0_CX_COMABT_INT; +- iowrite32(value, fotg210->reg + FOTG210_DISGR0); +-} +- + static void fotg210_in_fifo_handler(struct fotg210_ep *ep) + { + struct fotg210_request *req = list_entry(ep->queue.next, +@@ -893,60 +892,43 @@ static irqreturn_t fotg210_irq(int irq, + void __iomem *reg = fotg210->reg + FOTG210_DISGR2; + u32 int_grp2 = ioread32(reg); + u32 int_msk2 = ioread32(fotg210->reg + FOTG210_DMISGR2); +- u32 value; + + int_grp2 &= ~int_msk2; + + if (int_grp2 & DISGR2_USBRST_INT) { + usb_gadget_udc_reset(&fotg210->gadget, + fotg210->driver); +- value = ioread32(reg); +- value &= ~DISGR2_USBRST_INT; +- iowrite32(value, reg); ++ fotg210_ack_int(fotg210, FOTG210_DISGR2, DISGR2_USBRST_INT); + pr_info("fotg210 udc reset\n"); + } + if (int_grp2 & DISGR2_SUSP_INT) { +- value = ioread32(reg); +- value &= ~DISGR2_SUSP_INT; +- iowrite32(value, reg); ++ fotg210_ack_int(fotg210, FOTG210_DISGR2, DISGR2_SUSP_INT); + pr_info("fotg210 udc suspend\n"); + } + if (int_grp2 & DISGR2_RESM_INT) { +- value = ioread32(reg); +- value &= ~DISGR2_RESM_INT; +- iowrite32(value, reg); ++ fotg210_ack_int(fotg210, FOTG210_DISGR2, DISGR2_RESM_INT); + pr_info("fotg210 udc resume\n"); + } + if (int_grp2 & DISGR2_ISO_SEQ_ERR_INT) { +- value = ioread32(reg); +- value &= ~DISGR2_ISO_SEQ_ERR_INT; +- iowrite32(value, reg); ++ fotg210_ack_int(fotg210, FOTG210_DISGR2, DISGR2_ISO_SEQ_ERR_INT); + pr_info("fotg210 iso sequence error\n"); + } + if (int_grp2 & DISGR2_ISO_SEQ_ABORT_INT) { +- value = ioread32(reg); +- value &= ~DISGR2_ISO_SEQ_ABORT_INT; +- iowrite32(value, reg); ++ fotg210_ack_int(fotg210, FOTG210_DISGR2, DISGR2_ISO_SEQ_ABORT_INT); + pr_info("fotg210 iso sequence abort\n"); + } + if (int_grp2 & DISGR2_TX0BYTE_INT) { + fotg210_clear_tx0byte(fotg210); +- value = ioread32(reg); +- value &= ~DISGR2_TX0BYTE_INT; +- iowrite32(value, reg); ++ fotg210_ack_int(fotg210, FOTG210_DISGR2, DISGR2_TX0BYTE_INT); + pr_info("fotg210 transferred 0 byte\n"); + } + if (int_grp2 & DISGR2_RX0BYTE_INT) { + fotg210_clear_rx0byte(fotg210); +- value = ioread32(reg); +- value &= ~DISGR2_RX0BYTE_INT; +- iowrite32(value, reg); ++ fotg210_ack_int(fotg210, FOTG210_DISGR2, DISGR2_RX0BYTE_INT); + pr_info("fotg210 received 0 byte\n"); + } + if (int_grp2 & DISGR2_DMA_ERROR) { +- value = ioread32(reg); +- value &= ~DISGR2_DMA_ERROR; +- iowrite32(value, reg); ++ fotg210_ack_int(fotg210, FOTG210_DISGR2, DISGR2_DMA_ERROR); + } + } + +@@ -960,7 +942,7 @@ static irqreturn_t fotg210_irq(int irq, + + /* the highest priority in this source register */ + if (int_grp0 & DISGR0_CX_COMABT_INT) { +- fotg210_clear_comabt_int(fotg210); ++ fotg210_ack_int(fotg210, FOTG210_DISGR0, DISGR0_CX_COMABT_INT); + pr_info("fotg210 CX command abort\n"); + } + diff --git a/target/linux/gemini/patches-6.1/0023-fotg210-udc-Improve-device-initialization.patch b/target/linux/gemini/patches-6.1/0023-fotg210-udc-Improve-device-initialization.patch new file mode 100644 index 0000000000..fde17a48b3 --- /dev/null +++ b/target/linux/gemini/patches-6.1/0023-fotg210-udc-Improve-device-initialization.patch @@ -0,0 +1,62 @@ +From 367747c7813cecf19b46ef7134691f903ab76dc9 Mon Sep 17 00:00:00 2001 +From: Fabian Vogt +Date: Mon, 23 Jan 2023 08:35:08 +0100 +Subject: [PATCH 23/29] fotg210-udc: Improve device initialization + +Reset the device explicitly to get into a known state and also set the chip +enable bit. Additionally, mask interrupts which aren't handled. + +Signed-off-by: Fabian Vogt +Signed-off-by: Linus Walleij +Link: https://lore.kernel.org/r/20230123073508.2350402-4-linus.walleij@linaro.org +Signed-off-by: Greg Kroah-Hartman +--- +--- a/drivers/usb/fotg210/fotg210-udc.c ++++ b/drivers/usb/fotg210/fotg210-udc.c +@@ -7,6 +7,7 @@ + * Author : Yuan-Hsin Chen + */ + ++#include + #include + #include + #include +@@ -1022,6 +1023,11 @@ static int fotg210_udc_start(struct usb_ + dev_err(fotg210->dev, "can't bind to phy\n"); + } + ++ /* chip enable */ ++ value = ioread32(fotg210->reg + FOTG210_DMCR); ++ value |= DMCR_CHIP_EN; ++ iowrite32(value, fotg210->reg + FOTG210_DMCR); ++ + /* enable device global interrupt */ + value = ioread32(fotg210->reg + FOTG210_DMCR); + value |= DMCR_GLINT_EN; +@@ -1038,6 +1044,15 @@ static void fotg210_init(struct fotg210_ + iowrite32(GMIR_MHC_INT | GMIR_MOTG_INT | GMIR_INT_POLARITY, + fotg210->reg + FOTG210_GMIR); + ++ /* mask interrupts for groups other than 0-2 */ ++ iowrite32(~(DMIGR_MINT_G0 | DMIGR_MINT_G1 | DMIGR_MINT_G2), ++ fotg210->reg + FOTG210_DMIGR); ++ ++ /* udc software reset */ ++ iowrite32(DMCR_SFRST, fotg210->reg + FOTG210_DMCR); ++ /* Better wait a bit, but without a datasheet, no idea how long. */ ++ usleep_range(100, 200); ++ + /* disable device global interrupt */ + value = ioread32(fotg210->reg + FOTG210_DMCR); + value &= ~DMCR_GLINT_EN; +--- a/drivers/usb/fotg210/fotg210-udc.h ++++ b/drivers/usb/fotg210/fotg210-udc.h +@@ -58,6 +58,8 @@ + + /* Device Mask of Interrupt Group Register (0x130) */ + #define FOTG210_DMIGR 0x130 ++#define DMIGR_MINT_G2 (1 << 2) ++#define DMIGR_MINT_G1 (1 << 1) + #define DMIGR_MINT_G0 (1 << 0) + + /* Device Mask of Interrupt Source Group 0(0x134) */ diff --git a/target/linux/gemini/patches-6.1/0024-usb-fotg210-hcd-use-sysfs_emit-to-instead-of-scnprin.patch b/target/linux/gemini/patches-6.1/0024-usb-fotg210-hcd-use-sysfs_emit-to-instead-of-scnprin.patch new file mode 100644 index 0000000000..680836110a --- /dev/null +++ b/target/linux/gemini/patches-6.1/0024-usb-fotg210-hcd-use-sysfs_emit-to-instead-of-scnprin.patch @@ -0,0 +1,32 @@ +From 482830a70408a5d30af264b3d6706f818c78b2b2 Mon Sep 17 00:00:00 2001 +From: Andy Shevchenko +Date: Fri, 20 Jan 2023 17:44:33 +0200 +Subject: [PATCH 24/29] usb: fotg210-hcd: use sysfs_emit() to instead of + scnprintf() + +Follow the advice of the Documentation/filesystems/sysfs.rst and show() +should only use sysfs_emit() or sysfs_emit_at() when formatting the +value to be returned to user space. + +Signed-off-by: Andy Shevchenko +Link: https://lore.kernel.org/r/20230120154437.22025-1-andriy.shevchenko@linux.intel.com +Signed-off-by: Greg Kroah-Hartman +--- +--- a/drivers/usb/fotg210/fotg210-hcd.c ++++ b/drivers/usb/fotg210/fotg210-hcd.c +@@ -4686,14 +4686,11 @@ static ssize_t uframe_periodic_max_show( + struct device_attribute *attr, char *buf) + { + struct fotg210_hcd *fotg210; +- int n; + + fotg210 = hcd_to_fotg210(bus_to_hcd(dev_get_drvdata(dev))); +- n = scnprintf(buf, PAGE_SIZE, "%d\n", fotg210->uframe_periodic_max); +- return n; ++ return sysfs_emit(buf, "%d\n", fotg210->uframe_periodic_max); + } + +- + static ssize_t uframe_periodic_max_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) + { diff --git a/target/linux/gemini/patches-6.1/0025-ARM-dts-gemini-Push-down-flash-address-size-cells.patch b/target/linux/gemini/patches-6.1/0025-ARM-dts-gemini-Push-down-flash-address-size-cells.patch new file mode 100644 index 0000000000..1e031f1d4f --- /dev/null +++ b/target/linux/gemini/patches-6.1/0025-ARM-dts-gemini-Push-down-flash-address-size-cells.patch @@ -0,0 +1,62 @@ +From 6b84aa39a063eec883d410a9893cec70fce56163 Mon Sep 17 00:00:00 2001 +From: Linus Walleij +Date: Sun, 4 Dec 2022 20:02:28 +0100 +Subject: [PATCH 25/29] ARM: dts: gemini: Push down flash address/size cells + +The platforms not defining any OF partions complain like +this: + +../arch/arm/boot/dts/gemini.dtsi:19.25-28.5: Warning + (avoid_unnecessary_addr_size): /soc/flash@30000000: unnecessary + #address-cells/#size-cells without "ranges" or child "reg" property + +Get rid of this by only defining the address-cells and +size-cells where it is actually used by OF partitions. + +Link: https://lore.kernel.org/r/20221204190230.3345590-1-linus.walleij@linaro.org +Signed-off-by: Linus Walleij +--- +--- a/arch/arm/boot/dts/gemini-dlink-dns-313.dts ++++ b/arch/arm/boot/dts/gemini-dlink-dns-313.dts +@@ -164,6 +164,8 @@ + compatible = "cortina,gemini-flash", "jedec-flash"; + status = "okay"; + reg = <0x30000000 0x00080000>; ++ #address-cells = <1>; ++ #size-cells = <1>; + + /* + * This "RedBoot" is the Storlink derivative. +--- a/arch/arm/boot/dts/gemini-wbd111.dts ++++ b/arch/arm/boot/dts/gemini-wbd111.dts +@@ -86,6 +86,8 @@ + status = "okay"; + /* 8MB of flash */ + reg = <0x30000000 0x00800000>; ++ #address-cells = <1>; ++ #size-cells = <1>; + + partition@0 { + label = "RedBoot"; +--- a/arch/arm/boot/dts/gemini-wbd222.dts ++++ b/arch/arm/boot/dts/gemini-wbd222.dts +@@ -90,6 +90,8 @@ + status = "okay"; + /* 8MB of flash */ + reg = <0x30000000 0x00800000>; ++ #address-cells = <1>; ++ #size-cells = <1>; + + partition@0 { + label = "RedBoot"; +--- a/arch/arm/boot/dts/gemini.dtsi ++++ b/arch/arm/boot/dts/gemini.dtsi +@@ -22,8 +22,6 @@ + pinctrl-names = "default"; + pinctrl-0 = <&pflash_default_pins>; + bank-width = <2>; +- #address-cells = <1>; +- #size-cells = <1>; + status = "disabled"; + }; + diff --git a/target/linux/gemini/patches-6.1/0026-ARM-dts-gemini-wbd111-Use-RedBoot-partion-parser.patch b/target/linux/gemini/patches-6.1/0026-ARM-dts-gemini-wbd111-Use-RedBoot-partion-parser.patch new file mode 100644 index 0000000000..1aff23ed1b --- /dev/null +++ b/target/linux/gemini/patches-6.1/0026-ARM-dts-gemini-wbd111-Use-RedBoot-partion-parser.patch @@ -0,0 +1,54 @@ +From 0e733f5af628210f372585e431504a7024e7b571 Mon Sep 17 00:00:00 2001 +From: Linus Walleij +Date: Sun, 4 Dec 2022 20:02:29 +0100 +Subject: [PATCH 26/29] ARM: dts: gemini: wbd111: Use RedBoot partion parser + +This is clearly a RedBoot partitioned device with 0x20000 +sized erase blocks. + +Link: https://lore.kernel.org/r/20221204190230.3345590-2-linus.walleij@linaro.org +Signed-off-by: Linus Walleij +--- +--- a/arch/arm/boot/dts/gemini-wbd111.dts ++++ b/arch/arm/boot/dts/gemini-wbd111.dts +@@ -86,36 +86,11 @@ + status = "okay"; + /* 8MB of flash */ + reg = <0x30000000 0x00800000>; +- #address-cells = <1>; +- #size-cells = <1>; + +- partition@0 { +- label = "RedBoot"; +- reg = <0x00000000 0x00020000>; +- read-only; +- }; +- partition@20000 { +- label = "kernel"; +- reg = <0x00020000 0x00100000>; +- }; +- partition@120000 { +- label = "rootfs"; +- reg = <0x00120000 0x006a0000>; +- }; +- partition@7c0000 { +- label = "VCTL"; +- reg = <0x007c0000 0x00010000>; +- read-only; +- }; +- partition@7d0000 { +- label = "cfg"; +- reg = <0x007d0000 0x00010000>; +- read-only; +- }; +- partition@7e0000 { +- label = "FIS"; +- reg = <0x007e0000 0x00010000>; +- read-only; ++ partitions { ++ compatible = "redboot-fis"; ++ /* Eraseblock at 0x7e0000 */ ++ fis-index-block = <0x3f>; + }; + }; + diff --git a/target/linux/gemini/patches-6.1/0027-ARM-dts-gemini-wbd222-Use-RedBoot-partion-parser.patch b/target/linux/gemini/patches-6.1/0027-ARM-dts-gemini-wbd222-Use-RedBoot-partion-parser.patch new file mode 100644 index 0000000000..8cafeaa0df --- /dev/null +++ b/target/linux/gemini/patches-6.1/0027-ARM-dts-gemini-wbd222-Use-RedBoot-partion-parser.patch @@ -0,0 +1,54 @@ +From 8558e2e1110a5daa4ac9e1c5b5c15e1651a8fb94 Mon Sep 17 00:00:00 2001 +From: Linus Walleij +Date: Sun, 4 Dec 2022 20:02:30 +0100 +Subject: [PATCH 27/29] ARM: dts: gemini: wbd222: Use RedBoot partion parser + +This is clearly a RedBoot partitioned device with 0x20000 +sized erase blocks. + +Link: https://lore.kernel.org/r/20221204190230.3345590-3-linus.walleij@linaro.org +Signed-off-by: Linus Walleij +--- +--- a/arch/arm/boot/dts/gemini-wbd222.dts ++++ b/arch/arm/boot/dts/gemini-wbd222.dts +@@ -90,36 +90,11 @@ + status = "okay"; + /* 8MB of flash */ + reg = <0x30000000 0x00800000>; +- #address-cells = <1>; +- #size-cells = <1>; + +- partition@0 { +- label = "RedBoot"; +- reg = <0x00000000 0x00020000>; +- read-only; +- }; +- partition@20000 { +- label = "kernel"; +- reg = <0x00020000 0x00100000>; +- }; +- partition@120000 { +- label = "rootfs"; +- reg = <0x00120000 0x006a0000>; +- }; +- partition@7c0000 { +- label = "VCTL"; +- reg = <0x007c0000 0x00010000>; +- read-only; +- }; +- partition@7d0000 { +- label = "cfg"; +- reg = <0x007d0000 0x00010000>; +- read-only; +- }; +- partition@7e0000 { +- label = "FIS"; +- reg = <0x007e0000 0x00010000>; +- read-only; ++ partitions { ++ compatible = "redboot-fis"; ++ /* Eraseblock at 0x7e0000 */ ++ fis-index-block = <0x3f>; + }; + }; + diff --git a/target/linux/gemini/patches-6.1/0028-ARM-dts-gemini-Fix-USB-block-version.patch b/target/linux/gemini/patches-6.1/0028-ARM-dts-gemini-Fix-USB-block-version.patch new file mode 100644 index 0000000000..fb93b70a31 --- /dev/null +++ b/target/linux/gemini/patches-6.1/0028-ARM-dts-gemini-Fix-USB-block-version.patch @@ -0,0 +1,31 @@ +From d5c01ce4a1016507c69682894cf6b66301abca3d Mon Sep 17 00:00:00 2001 +From: Linus Walleij +Date: Mon, 23 Jan 2023 08:39:15 +0100 +Subject: [PATCH 28/29] ARM: dts: gemini: Fix USB block version + +The FOTG version in the Gemini is the FOTG200, fix this +up. + +Signed-off-by: Linus Walleij +Link: https://lore.kernel.org/r/20230123073916.2350839-1-linus.walleij@linaro.org +--- +--- a/arch/arm/boot/dts/gemini.dtsi ++++ b/arch/arm/boot/dts/gemini.dtsi +@@ -439,7 +439,7 @@ + }; + + usb0: usb@68000000 { +- compatible = "cortina,gemini-usb", "faraday,fotg210"; ++ compatible = "cortina,gemini-usb", "faraday,fotg200"; + reg = <0x68000000 0x1000>; + interrupts = <10 IRQ_TYPE_LEVEL_HIGH>; + resets = <&syscon GEMINI_RESET_USB0>; +@@ -460,7 +460,7 @@ + }; + + usb1: usb@69000000 { +- compatible = "cortina,gemini-usb", "faraday,fotg210"; ++ compatible = "cortina,gemini-usb", "faraday,fotg200"; + reg = <0x69000000 0x1000>; + interrupts = <11 IRQ_TYPE_LEVEL_HIGH>; + resets = <&syscon GEMINI_RESET_USB1>; diff --git a/target/linux/gemini/patches-6.1/0029-ARM-dts-gemini-Enable-DNS313-FOTG210-as-periph.patch b/target/linux/gemini/patches-6.1/0029-ARM-dts-gemini-Enable-DNS313-FOTG210-as-periph.patch new file mode 100644 index 0000000000..667878170b --- /dev/null +++ b/target/linux/gemini/patches-6.1/0029-ARM-dts-gemini-Enable-DNS313-FOTG210-as-periph.patch @@ -0,0 +1,54 @@ +From 296184694ae7a4e388603c95499e98d30b21cc09 Mon Sep 17 00:00:00 2001 +From: Linus Walleij +Date: Mon, 23 Jan 2023 08:39:16 +0100 +Subject: [PATCH 29/29] ARM: dts: gemini: Enable DNS313 FOTG210 as periph + +Add the GPIO-based VBUS phy, and enable the FOTG210 +USB1 block for use as peripheral. + +Signed-off-by: Linus Walleij +Link: https://lore.kernel.org/r/20230123073916.2350839-2-linus.walleij@linaro.org +--- +--- a/arch/arm/boot/dts/gemini-dlink-dns-313.dts ++++ b/arch/arm/boot/dts/gemini-dlink-dns-313.dts +@@ -80,6 +80,15 @@ + #cooling-cells = <2>; + }; + ++ /* ++ * This is the type B USB connector on the device, ++ * a GPIO-controlled USB VBUS detect ++ */ ++ usb1_phy: phy { ++ compatible = "gpio-usb-b-connector", "usb-b-connector"; ++ #phy-cells = <0>; ++ vbus-gpios = <&gpio0 18 GPIO_ACTIVE_LOW>; ++ }; + + /* Global Mixed-Mode Technology G751 mounted on GPIO I2C */ + i2c { +@@ -302,5 +311,13 @@ + ide@63000000 { + status = "okay"; + }; ++ ++ usb@69000000 { ++ status = "okay"; ++ dr_mode = "peripheral"; ++ usb-phy = <&usb1_phy>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&usb_default_pins>; ++ }; + }; + }; +--- a/arch/arm/boot/dts/gemini.dtsi ++++ b/arch/arm/boot/dts/gemini.dtsi +@@ -455,6 +455,8 @@ + */ + pinctrl-names = "default"; + pinctrl-0 = <&usb_default_pins>; ++ /* Default to host mode */ ++ dr_mode = "host"; + syscon = <&syscon>; + status = "disabled"; + }; diff --git a/target/linux/gemini/patches-6.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 index 0000000000..99e0d2731d --- /dev/null +++ b/target/linux/gemini/patches-6.1/300-ARM-dts-Augment-DIR-685-partition-table-for-OpenWrt.patch @@ -0,0 +1,34 @@ +From 36ee838bf83c01cff7cb47c7b07be278d2950ac0 Mon Sep 17 00:00:00 2001 +From: Linus Walleij +Date: Mon, 11 Mar 2019 15:44:29 +0100 +Subject: [PATCH 2/2] ARM: dts: Augment DIR-685 partition table for OpenWrt + +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 +--- +--- 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 {